MediaWiki REL1_37
BlockRestrictionStore.php
Go to the documentation of this file.
1<?php
23namespace MediaWiki\Block;
24
29use MWException;
30use stdClass;
34
36
40 private const TYPES_MAP = [
41 PageRestriction::TYPE_ID => PageRestriction::class,
42 NamespaceRestriction::TYPE_ID => NamespaceRestriction::class,
43 ActionRestriction::TYPE_ID => ActionRestriction::class,
44 ];
45
50
55 $this->loadBalancer = $loadBalancer;
56 }
57
66 public function loadByBlockId( $blockId, IDatabase $db = null ) {
67 if ( $blockId === null || $blockId === [] ) {
68 return [];
69 }
70
71 $db = $db ?: $this->loadBalancer->getConnectionRef( DB_REPLICA );
72
73 $result = $db->select(
74 [ 'ipblocks_restrictions', 'page' ],
75 [ 'ir_ipb_id', 'ir_type', 'ir_value', 'page_namespace', 'page_title' ],
76 [ 'ir_ipb_id' => $blockId ],
77 __METHOD__,
78 [],
79 [ 'page' => [ 'LEFT JOIN', [ 'ir_type' => PageRestriction::TYPE_ID, 'ir_value=page_id' ] ] ]
80 );
81
82 return $this->resultToRestrictions( $result );
83 }
84
92 public function insert( array $restrictions ) {
93 if ( !$restrictions ) {
94 return false;
95 }
96
97 $rows = [];
98 foreach ( $restrictions as $restriction ) {
99 if ( !$restriction instanceof Restriction ) {
100 continue;
101 }
102 $rows[] = $restriction->toRow();
103 }
104
105 if ( !$rows ) {
106 return false;
107 }
108
109 $dbw = $this->loadBalancer->getConnectionRef( DB_PRIMARY );
110
111 $dbw->insert(
112 'ipblocks_restrictions',
113 $rows,
114 __METHOD__,
115 [ 'IGNORE' ]
116 );
117
118 return true;
119 }
120
129 public function update( array $restrictions ) {
130 $dbw = $this->loadBalancer->getConnectionRef( DB_PRIMARY );
131
132 $dbw->startAtomic( __METHOD__ );
133
134 // Organize the restrictions by blockid.
135 $restrictionList = $this->restrictionsByBlockId( $restrictions );
136
137 // Load the existing restrictions and organize by block id. Any block ids
138 // that were passed into this function will be used to load all of the
139 // existing restrictions. This list might be the same, or may be completely
140 // different.
141 $existingList = [];
142 $blockIds = array_keys( $restrictionList );
143 if ( !empty( $blockIds ) ) {
144 $result = $dbw->select(
145 [ 'ipblocks_restrictions' ],
146 [ 'ir_ipb_id', 'ir_type', 'ir_value' ],
147 [ 'ir_ipb_id' => $blockIds ],
148 __METHOD__,
149 [ 'FOR UPDATE' ]
150 );
151
152 $existingList = $this->restrictionsByBlockId(
153 $this->resultToRestrictions( $result )
154 );
155 }
156
157 $result = true;
158 // Perform the actions on a per block-id basis.
159 foreach ( $restrictionList as $blockId => $blockRestrictions ) {
160 // Insert all of the restrictions first, ignoring ones that already exist.
161 $success = $this->insert( $blockRestrictions );
162
163 // Update the result. The first false is the result, otherwise, true.
164 $result = $success && $result;
165
166 $restrictionsToRemove = $this->restrictionsToRemove(
167 $existingList[$blockId] ?? [],
168 $restrictions
169 );
170
171 if ( empty( $restrictionsToRemove ) ) {
172 continue;
173 }
174
175 $success = $this->delete( $restrictionsToRemove );
176
177 // Update the result. The first false is the result, otherwise, true.
178 $result = $success && $result;
179 }
180
181 $dbw->endAtomic( __METHOD__ );
182
183 return $result;
184 }
185
194 public function updateByParentBlockId( $parentBlockId, array $restrictions ) {
195 // If removing all of the restrictions, then just delete them all.
196 if ( empty( $restrictions ) ) {
197 return $this->deleteByParentBlockId( $parentBlockId );
198 }
199
200 $parentBlockId = (int)$parentBlockId;
201
202 $db = $this->loadBalancer->getConnectionRef( DB_PRIMARY );
203
204 $db->startAtomic( __METHOD__ );
205
206 $blockIds = $db->selectFieldValues(
207 'ipblocks',
208 'ipb_id',
209 [ 'ipb_parent_block_id' => $parentBlockId ],
210 __METHOD__,
211 [ 'FOR UPDATE' ]
212 );
213
214 $result = true;
215 foreach ( $blockIds as $id ) {
216 $success = $this->update( $this->setBlockId( $id, $restrictions ) );
217 // Update the result. The first false is the result, otherwise, true.
218 $result = $success && $result;
219 }
220
221 $db->endAtomic( __METHOD__ );
222
223 return $result;
224 }
225
234 public function delete( array $restrictions ) {
235 $dbw = $this->loadBalancer->getConnectionRef( DB_PRIMARY );
236 $result = true;
237 foreach ( $restrictions as $restriction ) {
238 if ( !$restriction instanceof Restriction ) {
239 continue;
240 }
241
242 $success = $dbw->delete(
243 'ipblocks_restrictions',
244 // The restriction row is made up of a compound primary key. Therefore,
245 // the row and the delete conditions are the same.
246 $restriction->toRow(),
247 __METHOD__
248 );
249 // Update the result. The first false is the result, otherwise, true.
250 $result = $success && $result;
251 }
252
253 return $result;
254 }
255
264 public function deleteByBlockId( $blockId ) {
265 $dbw = $this->loadBalancer->getConnectionRef( DB_PRIMARY );
266 return $dbw->delete(
267 'ipblocks_restrictions',
268 [ 'ir_ipb_id' => $blockId ],
269 __METHOD__
270 );
271 }
272
281 public function deleteByParentBlockId( $parentBlockId ) {
282 $dbw = $this->loadBalancer->getConnectionRef( DB_PRIMARY );
283 return $dbw->deleteJoin(
284 'ipblocks_restrictions',
285 'ipblocks',
286 'ir_ipb_id',
287 'ipb_id',
288 [ 'ipb_parent_block_id' => $parentBlockId ],
289 __METHOD__
290 );
291 }
292
303 public function equals( array $a, array $b ) {
304 $filter = static function ( $restriction ) {
305 return $restriction instanceof Restriction;
306 };
307
308 // Ensure that every item in the array is a Restriction. This prevents a
309 // fatal error from calling Restriction::getHash if something in the array
310 // is not a restriction.
311 $a = array_filter( $a, $filter );
312 $b = array_filter( $b, $filter );
313
314 $aCount = count( $a );
315 $bCount = count( $b );
316
317 // If the count is different, then they are obviously a different set.
318 if ( $aCount !== $bCount ) {
319 return false;
320 }
321
322 // If both sets contain no items, then they are the same set.
323 if ( $aCount === 0 && $bCount === 0 ) {
324 return true;
325 }
326
327 $hasher = static function ( $r ) {
328 return $r->getHash();
329 };
330
331 $aHashes = array_map( $hasher, $a );
332 $bHashes = array_map( $hasher, $b );
333
334 sort( $aHashes );
335 sort( $bHashes );
336
337 return $aHashes === $bHashes;
338 }
339
348 public function setBlockId( $blockId, array $restrictions ) {
349 $blockRestrictions = [];
350
351 foreach ( $restrictions as $restriction ) {
352 if ( !$restriction instanceof Restriction ) {
353 continue;
354 }
355
356 // Clone the restriction so any references to the current restriction are
357 // not suddenly changed to a different blockId.
358 $restriction = clone $restriction;
359 $restriction->setBlockId( $blockId );
360
361 $blockRestrictions[] = $restriction;
362 }
363
364 return $blockRestrictions;
365 }
366
375 private function restrictionsToRemove( array $existing, array $new ) {
376 return array_filter( $existing, static function ( $e ) use ( $new ) {
377 foreach ( $new as $restriction ) {
378 if ( !$restriction instanceof Restriction ) {
379 continue;
380 }
381
382 if ( $restriction->equals( $e ) ) {
383 return false;
384 }
385 }
386
387 return true;
388 } );
389 }
390
398 private function restrictionsByBlockId( array $restrictions ) {
399 $blockRestrictions = [];
400
401 foreach ( $restrictions as $restriction ) {
402 // Ensure that all of the items in the array are restrictions.
403 if ( !$restriction instanceof Restriction ) {
404 continue;
405 }
406
407 if ( !isset( $blockRestrictions[$restriction->getBlockId()] ) ) {
408 $blockRestrictions[$restriction->getBlockId()] = [];
409 }
410
411 $blockRestrictions[$restriction->getBlockId()][] = $restriction;
412 }
413
414 return $blockRestrictions;
415 }
416
423 private function resultToRestrictions( IResultWrapper $result ) {
424 $restrictions = [];
425 foreach ( $result as $row ) {
426 $restriction = $this->rowToRestriction( $row );
427
428 if ( !$restriction ) {
429 continue;
430 }
431
432 $restrictions[] = $restriction;
433 }
434
435 return $restrictions;
436 }
437
444 private function rowToRestriction( stdClass $row ) {
445 if ( array_key_exists( (int)$row->ir_type, self::TYPES_MAP ) ) {
446 $class = self::TYPES_MAP[ (int)$row->ir_type ];
447 return call_user_func( [ $class, 'newFromRow' ], $row );
448 }
449
450 return null;
451 }
452}
MediaWiki exception.
updateByParentBlockId( $parentBlockId, array $restrictions)
Updates the list of restrictions by parent id.
restrictionsToRemove(array $existing, array $new)
Get the restrictions that should be removed, which are existing restrictions that are not in the new ...
restrictionsByBlockId(array $restrictions)
Converts an array of restrictions to an associative array of restrictions where the keys are the bloc...
setBlockId( $blockId, array $restrictions)
Set the blockId on a set of restrictions and return a new set.
resultToRestrictions(IResultWrapper $result)
Convert an Result Wrapper to an array of restrictions.
rowToRestriction(stdClass $row)
Convert a result row from the database into a restriction object.
deleteByParentBlockId( $parentBlockId)
Delete the restrictions by parent block ID.
insert(array $restrictions)
Inserts the restrictions into the database.
deleteByBlockId( $blockId)
Delete the restrictions by block ID.
equals(array $a, array $b)
Checks if two arrays of Restrictions are effectively equal.
const TYPES_MAP
Map of all of the restriction types.
loadByBlockId( $blockId, IDatabase $db=null)
Retrieves the restrictions from the database by block id.
update(array $restrictions)
Updates the list of restrictions.
Restriction for partial blocks of actions.
Basic database interface for live and lazy-loaded relation database handles.
Definition IDatabase.php:38
Database cluster connection, tracking, load balancing, and transaction manager interface.
Result wrapper for grabbing data queried from an IDatabase object.
const DB_REPLICA
Definition defines.php:25
const DB_PRIMARY
Definition defines.php:27