MediaWiki  master
ActorMigration.php
Go to the documentation of this file.
1 <?php
27 use Wikimedia\IPUtils;
29 
43 
49 
58  private static $tempTables = [
59  'rev_user' => [
60  'table' => 'revision_actor_temp',
61  'pk' => 'revactor_rev',
62  'field' => 'revactor_actor',
63  'joinPK' => 'rev_id',
64  'extra' => [
65  'revactor_timestamp' => 'rev_timestamp',
66  'revactor_page' => 'rev_page',
67  ],
68  ],
69  ];
70 
76  private static $formerTempTables = [];
77 
83  private static $deprecated = [
84  'ar_user' => null, // 1.34
85  'img_user' => null, // 1.34
86  'oi_user' => null, // 1.34
87  'fa_user' => null, // 1.34
88  'rc_user' => null, // 1.34
89  'log_user' => null, // 1.34
90  'ipb_by' => null, // 1.34
91  ];
92 
98  private static $removed = [];
99 
105  private static $specialFields = [
106  'ipb_by' => [ 'ipb_by_text', 'ipb_by_actor' ],
107  ];
108 
110  private $joinCache = [];
111 
113  private $stage;
114 
116  private $userFactory;
117 
120 
127  public function __construct(
128  $stage,
131  ) {
132  if ( ( $stage & SCHEMA_COMPAT_WRITE_BOTH ) === 0 ) {
133  throw new InvalidArgumentException( '$stage must include a write mode' );
134  }
135  if ( ( $stage & SCHEMA_COMPAT_READ_BOTH ) === 0 ) {
136  throw new InvalidArgumentException( '$stage must include a read mode' );
137  }
139  throw new InvalidArgumentException( 'Cannot read both schemas' );
140  }
142  throw new InvalidArgumentException( 'Cannot read the old schema without also writing it' );
143  }
145  throw new InvalidArgumentException( 'Cannot read the new schema without also writing it' );
146  }
147 
148  $this->stage = $stage;
149  $this->userFactory = $userFactory;
150  $this->actorStoreFactory = $actorStoreFactory;
151  }
152 
157  public static function newMigration() {
158  return MediaWikiServices::getInstance()->getActorMigration();
159  }
160 
165  private static function checkDeprecation( $key ) {
166  if ( isset( self::$removed[$key] ) ) {
167  throw new InvalidArgumentException(
168  "Use of " . static::class . " for '$key' was removed in MediaWiki " . self::$removed[$key]
169  );
170  }
171  if ( !empty( self::$deprecated[$key] ) ) {
172  wfDeprecated( static::class . " for '$key'", self::$deprecated[$key], false, 3 );
173  }
174  }
175 
181  public function isAnon( $field ) {
182  return ( $this->stage & SCHEMA_COMPAT_READ_NEW ) ? "$field IS NULL" : "$field = 0";
183  }
184 
190  public function isNotAnon( $field ) {
191  return ( $this->stage & SCHEMA_COMPAT_READ_NEW ) ? "$field IS NOT NULL" : "$field != 0";
192  }
193 
199  private static function getFieldNames( $key ) {
200  return self::$specialFields[$key] ?? [ $key . '_text', substr( $key, 0, -5 ) . '_actor' ];
201  }
202 
215  public function getJoin( $key ) {
216  self::checkDeprecation( $key );
217 
218  if ( !isset( $this->joinCache[$key] ) ) {
219  $tables = [];
220  $fields = [];
221  $joins = [];
222 
223  list( $text, $actor ) = self::getFieldNames( $key );
224 
225  if ( $this->stage & SCHEMA_COMPAT_READ_OLD ) {
226  $fields[$key] = $key;
227  $fields[$text] = $text;
228  $fields[$actor] = 'NULL';
229  } else {
230  if ( isset( self::$tempTables[$key] ) ) {
231  $t = self::$tempTables[$key];
232  $alias = "temp_$key";
233  $tables[$alias] = $t['table'];
234  $joins[$alias] = [ 'JOIN', "{$alias}.{$t['pk']} = {$t['joinPK']}" ];
235  $joinField = "{$alias}.{$t['field']}";
236  } else {
237  $joinField = $actor;
238  }
239 
240  $alias = "actor_$key";
241  $tables[$alias] = 'actor';
242  $joins[$alias] = [ 'JOIN', "{$alias}.actor_id = {$joinField}" ];
243 
244  $fields[$key] = "{$alias}.actor_user";
245  $fields[$text] = "{$alias}.actor_name";
246  $fields[$actor] = $joinField;
247  }
248 
249  $this->joinCache[$key] = [
250  'tables' => $tables,
251  'fields' => $fields,
252  'joins' => $joins,
253  ];
254  }
255 
256  return $this->joinCache[$key];
257  }
258 
269  public function getExistingActorId( IDatabase $db, UserIdentity $user ) {
270  wfDeprecated( __METHOD__, '1.36' );
271  // Get the correct ActorStore based on the DB passed,
272  // and not on the passed $user wiki ID, since before all
273  // User object are LOCAL, but the databased passed here can be
274  // foreign.
275  return $this->actorStoreFactory
276  ->getActorNormalization( $db->getDomainID() )
277  ->findActorId( $user );
278  }
279 
292  public function getNewActorId( IDatabase $dbw, UserIdentity $user ) {
293  wfDeprecated( __METHOD__, '1.36' );
294  // Get the correct ActorStore based on the DB passed,
295  // and not on the passed $user wiki ID, since before all
296  // User object are LOCAL, but the databased passed here can be
297  // foreign.
298  return $this->actorStoreFactory
299  ->getActorNormalization( $dbw->getDomainID() )
300  ->acquireActorId( $user, $dbw );
301  }
302 
312  public function getInsertValues( IDatabase $dbw, $key, UserIdentity $user ) {
313  self::checkDeprecation( $key );
314 
315  if ( isset( self::$tempTables[$key] ) ) {
316  throw new InvalidArgumentException( "Must use getInsertValuesWithTempTable() for $key" );
317  }
318 
319  list( $text, $actor ) = self::getFieldNames( $key );
320  $ret = [];
321  if ( $this->stage & SCHEMA_COMPAT_WRITE_OLD ) {
322  $ret[$key] = $user->getId();
323  $ret[$text] = $user->getName();
324  }
325  if ( $this->stage & SCHEMA_COMPAT_WRITE_NEW ) {
326  $ret[$actor] = $this->actorStoreFactory
327  ->getActorNormalization( $dbw->getDomainID() )
328  ->acquireActorId( $user, $dbw );
329  }
330  return $ret;
331  }
332 
345  public function getInsertValuesWithTempTable( IDatabase $dbw, $key, UserIdentity $user ) {
346  self::checkDeprecation( $key );
347 
348  if ( isset( self::$formerTempTables[$key] ) ) {
349  wfDeprecated( __METHOD__ . " for $key", self::$formerTempTables[$key] );
350  } elseif ( !isset( self::$tempTables[$key] ) ) {
351  throw new InvalidArgumentException( "Must use getInsertValues() for $key" );
352  }
353 
354  list( $text, $actor ) = self::getFieldNames( $key );
355  $ret = [];
356  $callback = null;
357  if ( $this->stage & SCHEMA_COMPAT_WRITE_OLD ) {
358  $ret[$key] = $user->getId();
359  $ret[$text] = $user->getName();
360  }
361  if ( $this->stage & SCHEMA_COMPAT_WRITE_NEW ) {
362  $id = $this->actorStoreFactory
363  ->getActorNormalization( $dbw->getDomainID() )
364  ->acquireActorId( $user, $dbw );
365 
366  if ( isset( self::$tempTables[$key] ) ) {
367  $func = __METHOD__;
368  $callback = static function ( $pk, array $extra ) use ( $dbw, $key, $id, $func ) {
369  $t = self::$tempTables[$key];
370  $set = [ $t['field'] => $id ];
371  foreach ( $t['extra'] as $to => $from ) {
372  if ( !array_key_exists( $from, $extra ) ) {
373  throw new InvalidArgumentException( "$func callback: \$extra[$from] is not provided" );
374  }
375  $set[$to] = $extra[$from];
376  }
377  $dbw->upsert(
378  $t['table'],
379  [ $t['pk'] => $pk ] + $set,
380  [ [ $t['pk'] ] ],
381  $set,
382  $func
383  );
384  };
385  } else {
386  $ret[$actor] = $id;
387  $callback = static function ( $pk, array $extra ) {
388  };
389  }
390  } elseif ( isset( self::$tempTables[$key] ) ) {
391  $func = __METHOD__;
392  $callback = static function ( $pk, array $extra ) use ( $key, $func ) {
393  $t = self::$tempTables[$key];
394  foreach ( $t['extra'] as $to => $from ) {
395  if ( !array_key_exists( $from, $extra ) ) {
396  throw new InvalidArgumentException( "$func callback: \$extra[$from] is not provided" );
397  }
398  }
399  };
400  } else {
401  $callback = static function ( $pk, array $extra ) {
402  };
403  }
404  return [ $ret, $callback ];
405  }
406 
430  public function getWhere( IDatabase $db, $key, $users, $useId = true ) {
431  self::checkDeprecation( $key );
432 
433  $tables = [];
434  $conds = [];
435  $joins = [];
436 
437  if ( $users instanceof UserIdentity ) {
438  $users = [ $users ];
439  } elseif ( $users === null || $users === false ) {
440  // DWIM
441  $users = [];
442  } elseif ( !is_array( $users ) ) {
443  $what = is_object( $users ) ? get_class( $users ) : gettype( $users );
444  throw new InvalidArgumentException(
445  __METHOD__ . ": Value for \$users must be a UserIdentity or array, got $what"
446  );
447  }
448 
449  // Get information about all the passed users
450  $ids = [];
451  $names = [];
452  $actors = [];
453  foreach ( $users as $user ) {
454  if ( $useId && $user->getId() ) {
455  $ids[] = $user->getId();
456  } else {
457  // make sure to use normalized form of IP for anonymous users
458  $names[] = IPUtils::sanitizeIP( $user->getName() );
459  }
460  $actorId = $user->getActorId();
461  if ( $actorId ) {
462  $actors[] = $actorId;
463  }
464  }
465 
466  list( $text, $actor ) = self::getFieldNames( $key );
467 
468  // Combine data into conditions to be ORed together
469  if ( $this->stage & SCHEMA_COMPAT_READ_NEW ) {
470  if ( $actors ) {
471  if ( isset( self::$tempTables[$key] ) ) {
472  $t = self::$tempTables[$key];
473  $alias = "temp_$key";
474  $tables[$alias] = $t['table'];
475  $joins[$alias] = [ 'JOIN', "{$alias}.{$t['pk']} = {$t['joinPK']}" ];
476  $joinField = "{$alias}.{$t['field']}";
477  } else {
478  $joinField = $actor;
479  }
480  $conds['actor'] = $db->makeList( [ $joinField => $actors ], IDatabase::LIST_AND );
481  }
482  } else {
483  if ( $ids ) {
484  $conds['userid'] = $db->makeList( [ $key => $ids ], IDatabase::LIST_AND );
485  }
486  if ( $names ) {
487  $conds['username'] = $db->makeList( [ $text => $names ], IDatabase::LIST_AND );
488  }
489  }
490 
491  return [
492  'tables' => $tables,
493  'conds' => $conds ? $db->makeList( array_values( $conds ), IDatabase::LIST_OR ) : '1=0',
494  'orconds' => $conds,
495  'joins' => $joins,
496  ];
497  }
498 }
ActorMigration\$userFactory
UserFactory $userFactory
Definition: ActorMigration.php:116
LIST_OR
const LIST_OR
Definition: Defines.php:46
ActorMigration\MIGRATION_STAGE_SCHEMA_COMPAT
const MIGRATION_STAGE_SCHEMA_COMPAT
Constant for extensions to feature-test whether $wgActorTableSchemaMigrationStage (in MW <1....
Definition: ActorMigration.php:48
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:172
ActorMigration\__construct
__construct( $stage, UserFactory $userFactory, ActorStoreFactory $actorStoreFactory)
Definition: ActorMigration.php:127
ActorMigration\$removed
static string[] $removed
Define fields that are removed for use with this class.
Definition: ActorMigration.php:98
SCHEMA_COMPAT_READ_BOTH
const SCHEMA_COMPAT_READ_BOTH
Definition: Defines.php:274
LIST_AND
const LIST_AND
Definition: Defines.php:43
Wikimedia\Rdbms\IDatabase\upsert
upsert( $table, array $rows, $uniqueKeys, array $set, $fname=__METHOD__)
Upsert the given row(s) into a table.
ActorMigration\$joinCache
array $joinCache
Cache for self::getJoin()
Definition: ActorMigration.php:110
ActorMigration
This class handles the logic for the actor table migration and should always be used in lieu of direc...
Definition: ActorMigration.php:42
MediaWiki\User\ActorStoreFactory
Definition: ActorStoreFactory.php:36
MediaWiki\User\UserIdentity
Interface for objects representing user identity.
Definition: UserIdentity.php:35
ActorMigration\newMigration
static newMigration()
Static constructor.
Definition: ActorMigration.php:157
Wikimedia\Rdbms\IDatabase
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
ActorMigration\isAnon
isAnon( $field)
Return an SQL condition to test if a user field is anonymous.
Definition: ActorMigration.php:181
SCHEMA_COMPAT_WRITE_BOTH
const SCHEMA_COMPAT_WRITE_BOTH
Definition: Defines.php:273
ActorMigration\$actorStoreFactory
ActorStoreFactory $actorStoreFactory
Definition: ActorMigration.php:119
ActorMigration\getFieldNames
static getFieldNames( $key)
Definition: ActorMigration.php:199
SCHEMA_COMPAT_READ_OLD
const SCHEMA_COMPAT_READ_OLD
Definition: Defines.php:270
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that $function is deprecated.
Definition: GlobalFunctions.php:1034
ActorMigration\checkDeprecation
static checkDeprecation( $key)
Issue deprecation warning/error as appropriate.
Definition: ActorMigration.php:165
ActorMigration\$stage
int $stage
Combination of SCHEMA_COMPAT_* constants.
Definition: ActorMigration.php:113
SCHEMA_COMPAT_WRITE_OLD
const SCHEMA_COMPAT_WRITE_OLD
Definition: Defines.php:269
MediaWiki\User\UserIdentity\getName
getName()
ActorMigration\getExistingActorId
getExistingActorId(IDatabase $db, UserIdentity $user)
Get actor ID from UserIdentity, if it exists.
Definition: ActorMigration.php:269
SCHEMA_COMPAT_WRITE_NEW
const SCHEMA_COMPAT_WRITE_NEW
Definition: Defines.php:271
ActorMigration\isNotAnon
isNotAnon( $field)
Return an SQL condition to test if a user field is non-anonymous.
Definition: ActorMigration.php:190
Wikimedia\Rdbms\IDatabase\getDomainID
getDomainID()
Return the currently selected domain ID.
ActorMigration\$specialFields
static array $specialFields
Define fields that use non-standard mapping Keys are the user id column name, values are arrays with ...
Definition: ActorMigration.php:105
ActorMigration\getWhere
getWhere(IDatabase $db, $key, $users, $useId=true)
Get WHERE condition for the actor.
Definition: ActorMigration.php:430
ActorMigration\getJoin
getJoin( $key)
Get SELECT fields and joins for the actor key.
Definition: ActorMigration.php:215
ActorMigration\getInsertValuesWithTempTable
getInsertValuesWithTempTable(IDatabase $dbw, $key, UserIdentity $user)
Get UPDATE fields for the actor.
Definition: ActorMigration.php:345
MediaWiki\User\UserIdentity\getId
getId()
SCHEMA_COMPAT_READ_NEW
const SCHEMA_COMPAT_READ_NEW
Definition: Defines.php:272
ActorMigration\$tempTables
static array $tempTables
Define fields that use temporary tables for transitional purposes Keys are '$key',...
Definition: ActorMigration.php:58
$t
$t
Definition: testCompression.php:74
ActorMigration\$deprecated
static string null[] $deprecated
Define fields that are deprecated for use with this class.
Definition: ActorMigration.php:83
ActorMigration\getInsertValues
getInsertValues(IDatabase $dbw, $key, UserIdentity $user)
Get UPDATE fields for the actor.
Definition: ActorMigration.php:312
Wikimedia\Rdbms\IDatabase\makeList
makeList(array $a, $mode=self::LIST_COMMA)
Makes an encoded list of strings from an array.
MediaWiki\User\UserFactory
Creates User objects.
Definition: UserFactory.php:41
ActorMigration\getNewActorId
getNewActorId(IDatabase $dbw, UserIdentity $user)
Attempt to assign an actor ID to the given user.
Definition: ActorMigration.php:292
ActorMigration\$formerTempTables
static array $formerTempTables
Fields that formerly used $tempTables Key is '$key', value is the MediaWiki version in which it was r...
Definition: ActorMigration.php:76