MediaWiki REL1_35
BlockRestrictionStore.php
Go to the documentation of this file.
1<?php
23namespace MediaWiki\Block;
24
28use MWException;
29use stdClass;
33
35
39 private $types = [
40 PageRestriction::TYPE_ID => PageRestriction::class,
41 NamespaceRestriction::TYPE_ID => NamespaceRestriction::class,
42 ];
43
48
53 $this->loadBalancer = $loadBalancer;
54 }
55
64 public function loadByBlockId( $blockId, IDatabase $db = null ) {
65 if ( $blockId === null || $blockId === [] ) {
66 return [];
67 }
68
69 $db = $db ?: $this->loadBalancer->getConnectionRef( DB_REPLICA );
70
71 $result = $db->select(
72 [ 'ipblocks_restrictions', 'page' ],
73 [ 'ir_ipb_id', 'ir_type', 'ir_value', 'page_namespace', 'page_title' ],
74 [ 'ir_ipb_id' => $blockId ],
75 __METHOD__,
76 [],
77 [ 'page' => [ 'LEFT JOIN', [ 'ir_type' => PageRestriction::TYPE_ID, 'ir_value=page_id' ] ] ]
78 );
79
80 return $this->resultToRestrictions( $result );
81 }
82
90 public function insert( array $restrictions ) {
91 if ( !$restrictions ) {
92 return false;
93 }
94
95 $rows = [];
96 foreach ( $restrictions as $restriction ) {
97 if ( !$restriction instanceof Restriction ) {
98 continue;
99 }
100 $rows[] = $restriction->toRow();
101 }
102
103 if ( !$rows ) {
104 return false;
105 }
106
107 $dbw = $this->loadBalancer->getConnectionRef( DB_MASTER );
108
109 $dbw->insert(
110 'ipblocks_restrictions',
111 $rows,
112 __METHOD__,
113 [ 'IGNORE' ]
114 );
115
116 return true;
117 }
118
127 public function update( array $restrictions ) {
128 $dbw = $this->loadBalancer->getConnectionRef( DB_MASTER );
129
130 $dbw->startAtomic( __METHOD__ );
131
132 // Organize the restrictions by blockid.
133 $restrictionList = $this->restrictionsByBlockId( $restrictions );
134
135 // Load the existing restrictions and organize by block id. Any block ids
136 // that were passed into this function will be used to load all of the
137 // existing restrictions. This list might be the same, or may be completely
138 // different.
139 $existingList = [];
140 $blockIds = array_keys( $restrictionList );
141 if ( !empty( $blockIds ) ) {
142 $result = $dbw->select(
143 [ 'ipblocks_restrictions' ],
144 [ 'ir_ipb_id', 'ir_type', 'ir_value' ],
145 [ 'ir_ipb_id' => $blockIds ],
146 __METHOD__,
147 [ 'FOR UPDATE' ]
148 );
149
150 $existingList = $this->restrictionsByBlockId(
151 $this->resultToRestrictions( $result )
152 );
153 }
154
155 $result = true;
156 // Perform the actions on a per block-id basis.
157 foreach ( $restrictionList as $blockId => $blockRestrictions ) {
158 // Insert all of the restrictions first, ignoring ones that already exist.
159 $success = $this->insert( $blockRestrictions );
160
161 // Update the result. The first false is the result, otherwise, true.
162 $result = $success && $result;
163
164 $restrictionsToRemove = $this->restrictionsToRemove(
165 $existingList[$blockId] ?? [],
166 $restrictions
167 );
168
169 if ( empty( $restrictionsToRemove ) ) {
170 continue;
171 }
172
173 $success = $this->delete( $restrictionsToRemove );
174
175 // Update the result. The first false is the result, otherwise, true.
176 $result = $success && $result;
177 }
178
179 $dbw->endAtomic( __METHOD__ );
180
181 return $result;
182 }
183
192 public function updateByParentBlockId( $parentBlockId, array $restrictions ) {
193 // If removing all of the restrictions, then just delete them all.
194 if ( empty( $restrictions ) ) {
195 return $this->deleteByParentBlockId( $parentBlockId );
196 }
197
198 $parentBlockId = (int)$parentBlockId;
199
200 $db = $this->loadBalancer->getConnectionRef( DB_MASTER );
201
202 $db->startAtomic( __METHOD__ );
203
204 $blockIds = $db->selectFieldValues(
205 'ipblocks',
206 'ipb_id',
207 [ 'ipb_parent_block_id' => $parentBlockId ],
208 __METHOD__,
209 [ 'FOR UPDATE' ]
210 );
211
212 $result = true;
213 foreach ( $blockIds as $id ) {
214 $success = $this->update( $this->setBlockId( $id, $restrictions ) );
215 // Update the result. The first false is the result, otherwise, true.
216 $result = $success && $result;
217 }
218
219 $db->endAtomic( __METHOD__ );
220
221 return $result;
222 }
223
232 public function delete( array $restrictions ) {
233 $dbw = $this->loadBalancer->getConnectionRef( DB_MASTER );
234 $result = true;
235 foreach ( $restrictions as $restriction ) {
236 if ( !$restriction instanceof Restriction ) {
237 continue;
238 }
239
240 $success = $dbw->delete(
241 'ipblocks_restrictions',
242 // The restriction row is made up of a compound primary key. Therefore,
243 // the row and the delete conditions are the same.
244 $restriction->toRow(),
245 __METHOD__
246 );
247 // Update the result. The first false is the result, otherwise, true.
248 $result = $success && $result;
249 }
250
251 return $result;
252 }
253
262 public function deleteByBlockId( $blockId ) {
263 $dbw = $this->loadBalancer->getConnectionRef( DB_MASTER );
264 return $dbw->delete(
265 'ipblocks_restrictions',
266 [ 'ir_ipb_id' => $blockId ],
267 __METHOD__
268 );
269 }
270
279 public function deleteByParentBlockId( $parentBlockId ) {
280 $dbw = $this->loadBalancer->getConnectionRef( DB_MASTER );
281 return $dbw->deleteJoin(
282 'ipblocks_restrictions',
283 'ipblocks',
284 'ir_ipb_id',
285 'ipb_id',
286 [ 'ipb_parent_block_id' => $parentBlockId ],
287 __METHOD__
288 );
289 }
290
301 public function equals( array $a, array $b ) {
302 $filter = function ( $restriction ) {
303 return $restriction instanceof Restriction;
304 };
305
306 // Ensure that every item in the array is a Restriction. This prevents a
307 // fatal error from calling Restriction::getHash if something in the array
308 // is not a restriction.
309 $a = array_filter( $a, $filter );
310 $b = array_filter( $b, $filter );
311
312 $aCount = count( $a );
313 $bCount = count( $b );
314
315 // If the count is different, then they are obviously a different set.
316 if ( $aCount !== $bCount ) {
317 return false;
318 }
319
320 // If both sets contain no items, then they are the same set.
321 if ( $aCount === 0 && $bCount === 0 ) {
322 return true;
323 }
324
325 $hasher = function ( $r ) {
326 return $r->getHash();
327 };
328
329 $aHashes = array_map( $hasher, $a );
330 $bHashes = array_map( $hasher, $b );
331
332 sort( $aHashes );
333 sort( $bHashes );
334
335 return $aHashes === $bHashes;
336 }
337
346 public function setBlockId( $blockId, array $restrictions ) {
347 $blockRestrictions = [];
348
349 foreach ( $restrictions as $restriction ) {
350 if ( !$restriction instanceof Restriction ) {
351 continue;
352 }
353
354 // Clone the restriction so any references to the current restriction are
355 // not suddenly changed to a different blockId.
356 $restriction = clone $restriction;
357 $restriction->setBlockId( $blockId );
358
359 $blockRestrictions[] = $restriction;
360 }
361
362 return $blockRestrictions;
363 }
364
373 private function restrictionsToRemove( array $existing, array $new ) {
374 return array_filter( $existing, function ( $e ) use ( $new ) {
375 foreach ( $new as $restriction ) {
376 if ( !$restriction instanceof Restriction ) {
377 continue;
378 }
379
380 if ( $restriction->equals( $e ) ) {
381 return false;
382 }
383 }
384
385 return true;
386 } );
387 }
388
396 private function restrictionsByBlockId( array $restrictions ) {
397 $blockRestrictions = [];
398
399 foreach ( $restrictions as $restriction ) {
400 // Ensure that all of the items in the array are restrictions.
401 if ( !$restriction instanceof Restriction ) {
402 continue;
403 }
404
405 if ( !isset( $blockRestrictions[$restriction->getBlockId()] ) ) {
406 $blockRestrictions[$restriction->getBlockId()] = [];
407 }
408
409 $blockRestrictions[$restriction->getBlockId()][] = $restriction;
410 }
411
412 return $blockRestrictions;
413 }
414
421 private function resultToRestrictions( IResultWrapper $result ) {
422 $restrictions = [];
423 foreach ( $result as $row ) {
424 $restriction = $this->rowToRestriction( $row );
425
426 if ( !$restriction ) {
427 continue;
428 }
429
430 $restrictions[] = $restriction;
431 }
432
433 return $restrictions;
434 }
435
442 private function rowToRestriction( stdClass $row ) {
443 if ( array_key_exists( (int)$row->ir_type, $this->types ) ) {
444 $class = $this->types[ (int)$row->ir_type ];
445 return call_user_func( [ $class, 'newFromRow' ], $row );
446 }
447
448 return null;
449 }
450}
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.
$types
Map of all of the restriction types.
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.
loadByBlockId( $blockId, IDatabase $db=null)
Retrieves the restrictions from the database by block id.
update(array $restrictions)
Updates the list of restrictions.
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_MASTER
Definition defines.php:29