MediaWiki  master
ActorMigration.php
Go to the documentation of this file.
1 <?php
26 
40 
46 
55  private static $tempTables = [
56  'rev_user' => [
57  'table' => 'revision_actor_temp',
58  'pk' => 'revactor_rev',
59  'field' => 'revactor_actor',
60  'joinPK' => 'rev_id',
61  'extra' => [
62  'revactor_timestamp' => 'rev_timestamp',
63  'revactor_page' => 'rev_page',
64  ],
65  ],
66  ];
67 
73  private static $formerTempTables = [];
74 
80  private static $deprecated = [
81  'ar_user' => null, // 1.34
82  'img_user' => null, // 1.34
83  'oi_user' => null, // 1.34
84  'fa_user' => null, // 1.34
85  'rc_user' => null, // 1.34
86  'log_user' => null, // 1.34
87  'ipb_by' => null, // 1.34
88  ];
89 
95  private static $removed = [];
96 
102  private static $specialFields = [
103  'ipb_by' => [ 'ipb_by_text', 'ipb_by_actor' ],
104  ];
105 
107  private $joinCache = [];
108 
110  private $stage;
111 
116  public function __construct( $stage ) {
117  if ( ( $stage & SCHEMA_COMPAT_WRITE_BOTH ) === 0 ) {
118  throw new InvalidArgumentException( '$stage must include a write mode' );
119  }
120  if ( ( $stage & SCHEMA_COMPAT_READ_BOTH ) === 0 ) {
121  throw new InvalidArgumentException( '$stage must include a read mode' );
122  }
124  throw new InvalidArgumentException( 'Cannot read both schemas' );
125  }
127  throw new InvalidArgumentException( 'Cannot read the old schema without also writing it' );
128  }
130  throw new InvalidArgumentException( 'Cannot read the new schema without also writing it' );
131  }
132 
133  $this->stage = $stage;
134  }
135 
140  public static function newMigration() {
141  return MediaWikiServices::getInstance()->getActorMigration();
142  }
143 
148  private static function checkDeprecation( $key ) {
149  if ( isset( self::$removed[$key] ) ) {
150  throw new InvalidArgumentException(
151  "Use of " . static::class . " for '$key' was removed in MediaWiki " . self::$removed[$key]
152  );
153  }
154  if ( !empty( self::$deprecated[$key] ) ) {
155  wfDeprecated( static::class . " for '$key'", self::$deprecated[$key], false, 3 );
156  }
157  }
158 
164  public function isAnon( $field ) {
165  return ( $this->stage & SCHEMA_COMPAT_READ_NEW ) ? "$field IS NULL" : "$field = 0";
166  }
167 
173  public function isNotAnon( $field ) {
174  return ( $this->stage & SCHEMA_COMPAT_READ_NEW ) ? "$field IS NOT NULL" : "$field != 0";
175  }
176 
182  private static function getFieldNames( $key ) {
183  return self::$specialFields[$key] ?? [ $key . '_text', substr( $key, 0, -5 ) . '_actor' ];
184  }
185 
198  public function getJoin( $key ) {
199  self::checkDeprecation( $key );
200 
201  if ( !isset( $this->joinCache[$key] ) ) {
202  $tables = [];
203  $fields = [];
204  $joins = [];
205 
206  list( $text, $actor ) = self::getFieldNames( $key );
207 
208  if ( $this->stage & SCHEMA_COMPAT_READ_OLD ) {
209  $fields[$key] = $key;
210  $fields[$text] = $text;
211  $fields[$actor] = 'NULL';
212  } else {
213  if ( isset( self::$tempTables[$key] ) ) {
214  $t = self::$tempTables[$key];
215  $alias = "temp_$key";
216  $tables[$alias] = $t['table'];
217  $joins[$alias] = [ 'JOIN', "{$alias}.{$t['pk']} = {$t['joinPK']}" ];
218  $joinField = "{$alias}.{$t['field']}";
219  } else {
220  $joinField = $actor;
221  }
222 
223  $alias = "actor_$key";
224  $tables[$alias] = 'actor';
225  $joins[$alias] = [ 'JOIN', "{$alias}.actor_id = {$joinField}" ];
226 
227  $fields[$key] = "{$alias}.actor_user";
228  $fields[$text] = "{$alias}.actor_name";
229  $fields[$actor] = $joinField;
230  }
231 
232  $this->joinCache[$key] = [
233  'tables' => $tables,
234  'fields' => $fields,
235  'joins' => $joins,
236  ];
237  }
238 
239  return $this->joinCache[$key];
240  }
241 
251  public function getInsertValues( IDatabase $dbw, $key, UserIdentity $user ) {
252  self::checkDeprecation( $key );
253 
254  if ( isset( self::$tempTables[$key] ) ) {
255  throw new InvalidArgumentException( "Must use getInsertValuesWithTempTable() for $key" );
256  }
257 
258  list( $text, $actor ) = self::getFieldNames( $key );
259  $ret = [];
260  if ( $this->stage & SCHEMA_COMPAT_WRITE_OLD ) {
261  $ret[$key] = $user->getId();
262  $ret[$text] = $user->getName();
263  }
264  if ( $this->stage & SCHEMA_COMPAT_WRITE_NEW ) {
265  // We need to be able to assign an actor ID if none exists
266  if ( !$user instanceof User && !$user->getActorId() ) {
267  $user = User::newFromAnyId( $user->getId(), $user->getName(), null );
268  }
269  $ret[$actor] = $user->getActorId( $dbw );
270  }
271  return $ret;
272  }
273 
286  public function getInsertValuesWithTempTable( IDatabase $dbw, $key, UserIdentity $user ) {
287  self::checkDeprecation( $key );
288 
289  if ( isset( self::$formerTempTables[$key] ) ) {
290  wfDeprecated( __METHOD__ . " for $key", self::$formerTempTables[$key] );
291  } elseif ( !isset( self::$tempTables[$key] ) ) {
292  throw new InvalidArgumentException( "Must use getInsertValues() for $key" );
293  }
294 
295  list( $text, $actor ) = self::getFieldNames( $key );
296  $ret = [];
297  $callback = null;
298  if ( $this->stage & SCHEMA_COMPAT_WRITE_OLD ) {
299  $ret[$key] = $user->getId();
300  $ret[$text] = $user->getName();
301  }
302  if ( $this->stage & SCHEMA_COMPAT_WRITE_NEW ) {
303  // We need to be able to assign an actor ID if none exists
304  if ( !$user instanceof User && !$user->getActorId() ) {
305  $user = User::newFromAnyId( $user->getId(), $user->getName(), null );
306  }
307  $id = $user->getActorId( $dbw );
308 
309  if ( isset( self::$tempTables[$key] ) ) {
310  $func = __METHOD__;
311  $callback = function ( $pk, array $extra ) use ( $dbw, $key, $id, $func ) {
312  $t = self::$tempTables[$key];
313  $set = [ $t['field'] => $id ];
314  foreach ( $t['extra'] as $to => $from ) {
315  if ( !array_key_exists( $from, $extra ) ) {
316  throw new InvalidArgumentException( "$func callback: \$extra[$from] is not provided" );
317  }
318  $set[$to] = $extra[$from];
319  }
320  $dbw->upsert(
321  $t['table'],
322  [ $t['pk'] => $pk ] + $set,
323  [ [ $t['pk'] ] ],
324  $set,
325  $func
326  );
327  };
328  } else {
329  $ret[$actor] = $id;
330  $callback = function ( $pk, array $extra ) {
331  };
332  }
333  } elseif ( isset( self::$tempTables[$key] ) ) {
334  $func = __METHOD__;
335  $callback = function ( $pk, array $extra ) use ( $key, $func ) {
336  $t = self::$tempTables[$key];
337  foreach ( $t['extra'] as $to => $from ) {
338  if ( !array_key_exists( $from, $extra ) ) {
339  throw new InvalidArgumentException( "$func callback: \$extra[$from] is not provided" );
340  }
341  }
342  };
343  } else {
344  $callback = function ( $pk, array $extra ) {
345  };
346  }
347  return [ $ret, $callback ];
348  }
349 
373  public function getWhere( IDatabase $db, $key, $users, $useId = true ) {
374  self::checkDeprecation( $key );
375 
376  $tables = [];
377  $conds = [];
378  $joins = [];
379 
380  if ( $users instanceof UserIdentity ) {
381  $users = [ $users ];
382  } elseif ( $users === null || $users === false ) {
383  // DWIM
384  $users = [];
385  } elseif ( !is_array( $users ) ) {
386  $what = is_object( $users ) ? get_class( $users ) : gettype( $users );
387  throw new InvalidArgumentException(
388  __METHOD__ . ": Value for \$users must be a UserIdentity or array, got $what"
389  );
390  }
391 
392  // Get information about all the passed users
393  $ids = [];
394  $names = [];
395  $actors = [];
396  foreach ( $users as $user ) {
397  if ( $useId && $user->getId() ) {
398  $ids[] = $user->getId();
399  } else {
400  $names[] = $user->getName();
401  }
402  $actorId = $user->getActorId();
403  if ( $actorId ) {
404  $actors[] = $actorId;
405  }
406  }
407 
408  list( $text, $actor ) = self::getFieldNames( $key );
409 
410  // Combine data into conditions to be ORed together
411  if ( $this->stage & SCHEMA_COMPAT_READ_NEW ) {
412  if ( $actors ) {
413  if ( isset( self::$tempTables[$key] ) ) {
414  $t = self::$tempTables[$key];
415  $alias = "temp_$key";
416  $tables[$alias] = $t['table'];
417  $joins[$alias] = [ 'JOIN', "{$alias}.{$t['pk']} = {$t['joinPK']}" ];
418  $joinField = "{$alias}.{$t['field']}";
419  } else {
420  $joinField = $actor;
421  }
422  $conds['actor'] = $db->makeList( [ $joinField => $actors ], IDatabase::LIST_AND );
423  }
424  } else {
425  if ( $ids ) {
426  $conds['userid'] = $db->makeList( [ $key => $ids ], IDatabase::LIST_AND );
427  }
428  if ( $names ) {
429  $conds['username'] = $db->makeList( [ $text => $names ], IDatabase::LIST_AND );
430  }
431  }
432 
433  return [
434  'tables' => $tables,
435  'conds' => $conds ? $db->makeList( array_values( $conds ), IDatabase::LIST_OR ) : '1=0',
436  'orconds' => $conds,
437  'joins' => $joins,
438  ];
439  }
440 
441 }
ActorMigration\MIGRATION_STAGE_SCHEMA_COMPAT
const MIGRATION_STAGE_SCHEMA_COMPAT
Constant for extensions to feature-test whether $wgActorTableSchemaMigrationStage (in MW <1....
Definition: ActorMigration.php:45
SCHEMA_COMPAT_READ_NEW
const SCHEMA_COMPAT_READ_NEW
Definition: Defines.php:277
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:154
ActorMigration\$removed
static string[] $removed
Define fields that are removed for use with this class.
Definition: ActorMigration.php:95
ActorMigration\__construct
__construct( $stage)
Definition: ActorMigration.php:116
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:107
ActorMigration
This class handles the logic for the actor table migration and should always be used in lieu of direc...
Definition: ActorMigration.php:39
MediaWiki\User\UserIdentity
Interface for objects representing user identity.
Definition: UserIdentity.php:32
ActorMigration\newMigration
static newMigration()
Static constructor.
Definition: ActorMigration.php:140
MediaWiki\User\UserIdentity\getActorId
getActorId()
LIST_AND
const LIST_AND
Definition: Defines.php:48
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:164
ActorMigration\getFieldNames
static getFieldNames( $key)
Definition: ActorMigration.php:182
LIST_OR
const LIST_OR
Definition: Defines.php:51
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that $function is deprecated.
Definition: GlobalFunctions.php:1026
ActorMigration\checkDeprecation
static checkDeprecation( $key)
Issue deprecation warning/error as appropriate.
Definition: ActorMigration.php:148
ActorMigration\$stage
int $stage
Combination of SCHEMA_COMPAT_* constants.
Definition: ActorMigration.php:110
MediaWiki\User\UserIdentity\getName
getName()
User\newFromAnyId
static newFromAnyId( $userId, $userName, $actorId, $dbDomain=false)
Static factory method for creation from an ID, name, and/or actor ID.
Definition: User.php:613
ActorMigration\isNotAnon
isNotAnon( $field)
Return an SQL condition to test if a user field is non-anonymous.
Definition: ActorMigration.php:173
SCHEMA_COMPAT_WRITE_OLD
const SCHEMA_COMPAT_WRITE_OLD
Definition: Defines.php:274
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:102
SCHEMA_COMPAT_WRITE_NEW
const SCHEMA_COMPAT_WRITE_NEW
Definition: Defines.php:276
ActorMigration\getWhere
getWhere(IDatabase $db, $key, $users, $useId=true)
Get WHERE condition for the actor.
Definition: ActorMigration.php:373
ActorMigration\getJoin
getJoin( $key)
Get SELECT fields and joins for the actor key.
Definition: ActorMigration.php:198
ActorMigration\getInsertValuesWithTempTable
getInsertValuesWithTempTable(IDatabase $dbw, $key, UserIdentity $user)
Get UPDATE fields for the actor.
Definition: ActorMigration.php:286
MediaWiki\User\UserIdentity\getId
getId()
ActorMigration\$tempTables
static array $tempTables
Define fields that use temporary tables for transitional purposes Keys are '$key',...
Definition: ActorMigration.php:55
SCHEMA_COMPAT_WRITE_BOTH
const SCHEMA_COMPAT_WRITE_BOTH
Definition: Defines.php:278
$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:80
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:55
ActorMigration\getInsertValues
getInsertValues(IDatabase $dbw, $key, UserIdentity $user)
Get UPDATE fields for the actor.
Definition: ActorMigration.php:251
SCHEMA_COMPAT_READ_BOTH
const SCHEMA_COMPAT_READ_BOTH
Definition: Defines.php:279
Wikimedia\Rdbms\IDatabase\makeList
makeList(array $a, $mode=self::LIST_COMMA)
Makes an encoded list of strings from an array.
SCHEMA_COMPAT_READ_OLD
const SCHEMA_COMPAT_READ_OLD
Definition: Defines.php:275
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:73