MediaWiki  master
BlockRestrictionStore.php
Go to the documentation of this file.
1 <?php
23 namespace MediaWiki\Block;
24 
29 use MWException;
30 use 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 
49  private $loadBalancer;
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\Block\BlockRestrictionStore\update
update(array $restrictions)
Updates the list of restrictions.
Definition: BlockRestrictionStore.php:129
MediaWiki\Block\Restriction\PageRestriction\TYPE_ID
const TYPE_ID
Definition: PageRestriction.php:35
MediaWiki\Block
Definition: AbstractBlock.php:21
MediaWiki\Block\BlockRestrictionStore\rowToRestriction
rowToRestriction(stdClass $row)
Convert a result row from the database into a restriction object.
Definition: BlockRestrictionStore.php:444
$success
$success
Definition: NoLocalSettings.php:42
MediaWiki\Block\BlockRestrictionStore\insert
insert(array $restrictions)
Inserts the restrictions into the database.
Definition: BlockRestrictionStore.php:92
Wikimedia\Rdbms\IDatabase
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
MediaWiki\Block\Restriction\NamespaceRestriction\TYPE_ID
const TYPE_ID
Definition: NamespaceRestriction.php:35
MWException
MediaWiki exception.
Definition: MWException.php:29
MediaWiki\Block\BlockRestrictionStore\loadByBlockId
loadByBlockId( $blockId, IDatabase $db=null)
Retrieves the restrictions from the database by block id.
Definition: BlockRestrictionStore.php:66
Wikimedia\Rdbms\IResultWrapper
Result wrapper for grabbing data queried from an IDatabase object.
Definition: IResultWrapper.php:26
MediaWiki\Block\Restriction\Restriction
Definition: Restriction.php:25
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
MediaWiki\Block\BlockRestrictionStore\updateByParentBlockId
updateByParentBlockId( $parentBlockId, array $restrictions)
Updates the list of restrictions by parent id.
Definition: BlockRestrictionStore.php:194
MediaWiki\Block\BlockRestrictionStore\resultToRestrictions
resultToRestrictions(IResultWrapper $result)
Convert an Result Wrapper to an array of restrictions.
Definition: BlockRestrictionStore.php:423
MediaWiki\Block\BlockRestrictionStore\restrictionsToRemove
restrictionsToRemove(array $existing, array $new)
Get the restrictions that should be removed, which are existing restrictions that are not in the new ...
Definition: BlockRestrictionStore.php:375
DB_PRIMARY
const DB_PRIMARY
Definition: defines.php:27
MediaWiki\Block\BlockRestrictionStore\setBlockId
setBlockId( $blockId, array $restrictions)
Set the blockId on a set of restrictions and return a new set.
Definition: BlockRestrictionStore.php:348
MediaWiki\Block\BlockRestrictionStore\equals
equals(array $a, array $b)
Checks if two arrays of Restrictions are effectively equal.
Definition: BlockRestrictionStore.php:303
MediaWiki\Block\BlockRestrictionStore\TYPES_MAP
const TYPES_MAP
Map of all of the restriction types.
Definition: BlockRestrictionStore.php:40
MediaWiki\Block\Restriction\NamespaceRestriction
Definition: NamespaceRestriction.php:25
MediaWiki\Block\BlockRestrictionStore\deleteByParentBlockId
deleteByParentBlockId( $parentBlockId)
Delete the restrictions by parent block ID.
Definition: BlockRestrictionStore.php:281
MediaWiki\Block\Restriction\PageRestriction
Definition: PageRestriction.php:25
MediaWiki\Block\BlockRestrictionStore\deleteByBlockId
deleteByBlockId( $blockId)
Delete the restrictions by block ID.
Definition: BlockRestrictionStore.php:264
MediaWiki\Block\BlockRestrictionStore\$loadBalancer
ILoadBalancer $loadBalancer
Definition: BlockRestrictionStore.php:49
MediaWiki\Block\BlockRestrictionStore\restrictionsByBlockId
restrictionsByBlockId(array $restrictions)
Converts an array of restrictions to an associative array of restrictions where the keys are the bloc...
Definition: BlockRestrictionStore.php:398
MediaWiki\Block\Restriction\ActionRestriction\TYPE_ID
const TYPE_ID
Definition: ActionRestriction.php:40
MediaWiki\Block\Restriction\ActionRestriction
Restriction for partial blocks of actions.
Definition: ActionRestriction.php:30
Wikimedia\Rdbms\ILoadBalancer
Database cluster connection, tracking, load balancing, and transaction manager interface.
Definition: ILoadBalancer.php:81
MediaWiki\Block\BlockRestrictionStore\__construct
__construct(ILoadBalancer $loadBalancer)
Definition: BlockRestrictionStore.php:54
MediaWiki\Block\BlockRestrictionStore
Definition: BlockRestrictionStore.php:35