MediaWiki master
BlockRestrictionStore.php
Go to the documentation of this file.
1<?php
23namespace MediaWiki\Block;
24
30use 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( 'bl_id' )
186 ->forUpdate()
187 ->from( 'block' )
188 ->where( [ 'bl_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 ) {
375 case PageRestriction::TYPE_ID:
376 return PageRestriction::newFromRow( $row );
377 case NamespaceRestriction::TYPE_ID:
378 return NamespaceRestriction::newFromRow( $row );
379 case ActionRestriction::TYPE_ID:
380 return ActionRestriction::newFromRow( $row );
381 default:
382 return null;
383 }
384 }
385}
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.
Restriction for partial blocks of actions.
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.