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