MediaWiki  master
BlockRestrictionStore.php
Go to the documentation of this file.
1 <?php
23 namespace MediaWiki\Block;
24 
30 use stdClass;
33 
35 
39  private $dbProvider;
40 
44  private $wikiId;
45 
50  public function __construct(
51  IConnectionProvider $dbProvider,
52  $wikiId = WikiAwareEntity::LOCAL
53  ) {
54  $this->dbProvider = $dbProvider;
55  $this->wikiId = $wikiId;
56  }
57 
65  public function loadByBlockId( $blockId ) {
66  if ( $blockId === null || $blockId === [] ) {
67  return [];
68  }
69 
70  $result = $this->dbProvider->getReplicaDatabase( $this->wikiId )
71  ->newSelectQueryBuilder()
72  ->select( [ 'ir_ipb_id', 'ir_type', 'ir_value', 'page_namespace', 'page_title' ] )
73  ->from( 'ipblocks_restrictions' )
74  ->leftJoin( 'page', null, [ 'ir_type' => PageRestriction::TYPE_ID, 'ir_value=page_id' ] )
75  ->where( [ 'ir_ipb_id' => $blockId ] )
76  ->caller( __METHOD__ )->fetchResultSet();
77 
78  return $this->resultToRestrictions( $result );
79  }
80 
88  public function insert( array $restrictions ) {
89  if ( !$restrictions ) {
90  return false;
91  }
92 
93  $rows = [];
94  foreach ( $restrictions as $restriction ) {
95  $rows[] = $restriction->toRow();
96  }
97 
98  $dbw = $this->dbProvider->getPrimaryDatabase( $this->wikiId );
99 
100  $dbw->newInsertQueryBuilder()
101  ->insertInto( 'ipblocks_restrictions' )
102  ->ignore()
103  ->rows( $rows )
104  ->caller( __METHOD__ )->execute();
105 
106  return true;
107  }
108 
117  public function update( array $restrictions ) {
118  $dbw = $this->dbProvider->getPrimaryDatabase( $this->wikiId );
119 
120  $dbw->startAtomic( __METHOD__ );
121 
122  // Organize the restrictions by block ID.
123  $restrictionList = $this->restrictionsByBlockId( $restrictions );
124 
125  // Load the existing restrictions and organize by block ID. Any block IDs
126  // that were passed into this function will be used to load all of the
127  // existing restrictions. This list might be the same, or may be completely
128  // different.
129  $existingList = [];
130  $blockIds = array_keys( $restrictionList );
131  if ( $blockIds ) {
132  $result = $dbw->newSelectQueryBuilder()
133  ->select( [ 'ir_ipb_id', 'ir_type', 'ir_value' ] )
134  ->forUpdate()
135  ->from( 'ipblocks_restrictions' )
136  ->where( [ 'ir_ipb_id' => $blockIds ] )
137  ->caller( __METHOD__ )->fetchResultSet();
138 
139  $existingList = $this->restrictionsByBlockId(
140  $this->resultToRestrictions( $result )
141  );
142  }
143 
144  $result = true;
145  // Perform the actions on a per block-ID basis.
146  foreach ( $restrictionList as $blockId => $blockRestrictions ) {
147  // Insert all of the restrictions first, ignoring ones that already exist.
148  $success = $this->insert( $blockRestrictions );
149 
150  $result = $success && $result;
151 
152  $restrictionsToRemove = $this->restrictionsToRemove(
153  $existingList[$blockId] ?? [],
154  $restrictions
155  );
156 
157  if ( !$restrictionsToRemove ) {
158  continue;
159  }
160 
161  $success = $this->delete( $restrictionsToRemove );
162 
163  $result = $success && $result;
164  }
165 
166  $dbw->endAtomic( __METHOD__ );
167 
168  return $result;
169  }
170 
179  public function updateByParentBlockId( $parentBlockId, array $restrictions ) {
180  $parentBlockId = (int)$parentBlockId;
181 
182  $db = $this->dbProvider->getPrimaryDatabase( $this->wikiId );
183 
184  $blockIds = $db->newSelectQueryBuilder()
185  ->select( 'ipb_id' )
186  ->forUpdate()
187  ->from( 'ipblocks' )
188  ->where( [ 'ipb_parent_block_id' => $parentBlockId ] )
189  ->caller( __METHOD__ )->fetchFieldValues();
190  if ( !$blockIds ) {
191  return true;
192  }
193 
194  // If removing all of the restrictions, then just delete them all.
195  if ( !$restrictions ) {
196  $blockIds = array_map( 'intval', $blockIds );
197  return $this->deleteByBlockId( $blockIds );
198  }
199 
200  $db->startAtomic( __METHOD__ );
201 
202  $result = true;
203  foreach ( $blockIds as $id ) {
204  $success = $this->update( $this->setBlockId( $id, $restrictions ) );
205  $result = $success && $result;
206  }
207 
208  $db->endAtomic( __METHOD__ );
209 
210  return $result;
211  }
212 
220  public function delete( array $restrictions ) {
221  $dbw = $this->dbProvider->getPrimaryDatabase( $this->wikiId );
222  foreach ( $restrictions as $restriction ) {
223  $dbw->newDeleteQueryBuilder()
224  ->deleteFrom( 'ipblocks_restrictions' )
225  // The restriction row is made up of a compound primary key. Therefore,
226  // the row and the delete conditions are the same.
227  ->where( $restriction->toRow() )
228  ->caller( __METHOD__ )->execute();
229  }
230 
231  return true;
232  }
233 
241  public function deleteByBlockId( $blockId ) {
242  $this->dbProvider->getPrimaryDatabase( $this->wikiId )
243  ->newDeleteQueryBuilder()
244  ->deleteFrom( 'ipblocks_restrictions' )
245  ->where( [ 'ir_ipb_id' => $blockId ] )
246  ->caller( __METHOD__ )->execute();
247  return true;
248  }
249 
260  public function equals( array $a, array $b ) {
261  $aCount = count( $a );
262  $bCount = count( $b );
263 
264  // If the count is different, then they are obviously a different set.
265  if ( $aCount !== $bCount ) {
266  return false;
267  }
268 
269  // If both sets contain no items, then they are the same set.
270  if ( $aCount === 0 && $bCount === 0 ) {
271  return true;
272  }
273 
274  $hasher = static function ( Restriction $r ) {
275  return $r->getHash();
276  };
277 
278  $aHashes = array_map( $hasher, $a );
279  $bHashes = array_map( $hasher, $b );
280 
281  sort( $aHashes );
282  sort( $bHashes );
283 
284  return $aHashes === $bHashes;
285  }
286 
295  public function setBlockId( $blockId, array $restrictions ) {
296  $blockRestrictions = [];
297 
298  foreach ( $restrictions as $restriction ) {
299  // Clone the restriction so any references to the current restriction are
300  // not suddenly changed to a different blockId.
301  $restriction = clone $restriction;
302  $restriction->setBlockId( $blockId );
303 
304  $blockRestrictions[] = $restriction;
305  }
306 
307  return $blockRestrictions;
308  }
309 
318  private function restrictionsToRemove( array $existing, array $new ) {
319  $restrictionsByHash = [];
320  foreach ( $existing as $restriction ) {
321  $restrictionsByHash[$restriction->getHash()] = $restriction;
322  }
323  foreach ( $new as $restriction ) {
324  unset( $restrictionsByHash[$restriction->getHash()] );
325  }
326  return array_values( $restrictionsByHash );
327  }
328 
336  private function restrictionsByBlockId( array $restrictions ) {
337  $blockRestrictions = [];
338 
339  foreach ( $restrictions as $restriction ) {
340  $blockRestrictions[$restriction->getBlockId()][] = $restriction;
341  }
342 
343  return $blockRestrictions;
344  }
345 
352  private function resultToRestrictions( IResultWrapper $result ) {
353  $restrictions = [];
354  foreach ( $result as $row ) {
355  $restriction = $this->rowToRestriction( $row );
356 
357  if ( !$restriction ) {
358  continue;
359  }
360 
361  $restrictions[] = $restriction;
362  }
363 
364  return $restrictions;
365  }
366 
373  private function rowToRestriction( stdClass $row ) {
374  switch ( (int)$row->ir_type ) {
376  return PageRestriction::newFromRow( $row );
378  return NamespaceRestriction::newFromRow( $row );
380  return ActionRestriction::newFromRow( $row );
381  default:
382  return null;
383  }
384  }
385 }
$success
updateByParentBlockId( $parentBlockId, array $restrictions)
Updates the list of restrictions by parent ID.
__construct(IConnectionProvider $dbProvider, $wikiId=WikiAwareEntity::LOCAL)
loadByBlockId( $blockId)
Retrieve the restrictions from the database by block ID.
setBlockId( $blockId, array $restrictions)
Set the blockId on a set of restrictions and return a new set.
insert(array $restrictions)
Insert the restrictions into the database.
deleteByBlockId( $blockId)
Delete the restrictions by block ID.
equals(array $a, array $b)
Check if two arrays of Restrictions are effectively equal.
update(array $restrictions)
Update the list of restrictions.
static newFromRow(\stdClass $row)
Create a new Restriction from a database row.1.33 static
Restriction for partial blocks of actions.
static newFromRow(\stdClass $row)
Create a new Restriction from a database row.1.33 static
Marker interface for entities aware of the wiki they belong to.
Provide primary and replica IDatabase connections.
Result wrapper for grabbing data queried from an IDatabase object.