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 
112  public function __construct( $stage ) {
113  if ( ( $stage & SCHEMA_COMPAT_WRITE_BOTH ) === 0 ) {
114  throw new InvalidArgumentException( '$stage must include a write mode' );
115  }
116  if ( ( $stage & SCHEMA_COMPAT_READ_BOTH ) === 0 ) {
117  throw new InvalidArgumentException( '$stage must include a read mode' );
118  }
119  if ( ( $stage & SCHEMA_COMPAT_READ_BOTH ) === SCHEMA_COMPAT_READ_BOTH ) {
120  throw new InvalidArgumentException( 'Cannot read both schemas' );
121  }
123  throw new InvalidArgumentException( 'Cannot read the old schema without also writing it' );
124  }
126  throw new InvalidArgumentException( 'Cannot read the new schema without also writing it' );
127  }
128 
129  $this->stage = $stage;
130  }
131 
136  public static function newMigration() {
137  return MediaWikiServices::getInstance()->getActorMigration();
138  }
139 
144  private static function checkDeprecation( $key ) {
145  if ( isset( self::$removed[$key] ) ) {
146  throw new InvalidArgumentException(
147  "Use of " . static::class . " for '$key' was removed in MediaWiki " . self::$removed[$key]
148  );
149  }
150  if ( !empty( self::$deprecated[$key] ) ) {
151  wfDeprecated( static::class . " for '$key'", self::$deprecated[$key], false, 3 );
152  }
153  }
154 
160  public function isAnon( $field ) {
161  return ( $this->stage & SCHEMA_COMPAT_READ_NEW ) ? "$field IS NULL" : "$field = 0";
162  }
163 
169  public function isNotAnon( $field ) {
170  return ( $this->stage & SCHEMA_COMPAT_READ_NEW ) ? "$field IS NOT NULL" : "$field != 0";
171  }
172 
178  private static function getFieldNames( $key ) {
179  return self::$specialFields[$key] ?? [ $key . '_text', substr( $key, 0, -5 ) . '_actor' ];
180  }
181 
194  public function getJoin( $key ) {
195  self::checkDeprecation( $key );
196 
197  if ( !isset( $this->joinCache[$key] ) ) {
198  $tables = [];
199  $fields = [];
200  $joins = [];
201 
202  list( $text, $actor ) = self::getFieldNames( $key );
203 
204  if ( $this->stage & SCHEMA_COMPAT_READ_OLD ) {
205  $fields[$key] = $key;
206  $fields[$text] = $text;
207  $fields[$actor] = 'NULL';
208  } else {
209  if ( isset( self::$tempTables[$key] ) ) {
210  $t = self::$tempTables[$key];
211  $alias = "temp_$key";
212  $tables[$alias] = $t['table'];
213  $joins[$alias] = [ 'JOIN', "{$alias}.{$t['pk']} = {$t['joinPK']}" ];
214  $joinField = "{$alias}.{$t['field']}";
215  } else {
216  $joinField = $actor;
217  }
218 
219  $alias = "actor_$key";
220  $tables[$alias] = 'actor';
221  $joins[$alias] = [ 'JOIN', "{$alias}.actor_id = {$joinField}" ];
222 
223  $fields[$key] = "{$alias}.actor_user";
224  $fields[$text] = "{$alias}.actor_name";
225  $fields[$actor] = $joinField;
226  }
227 
228  $this->joinCache[$key] = [
229  'tables' => $tables,
230  'fields' => $fields,
231  'joins' => $joins,
232  ];
233  }
234 
235  return $this->joinCache[$key];
236  }
237 
247  public function getInsertValues( IDatabase $dbw, $key, UserIdentity $user ) {
248  self::checkDeprecation( $key );
249 
250  if ( isset( self::$tempTables[$key] ) ) {
251  throw new InvalidArgumentException( "Must use getInsertValuesWithTempTable() for $key" );
252  }
253 
254  list( $text, $actor ) = self::getFieldNames( $key );
255  $ret = [];
256  if ( $this->stage & SCHEMA_COMPAT_WRITE_OLD ) {
257  $ret[$key] = $user->getId();
258  $ret[$text] = $user->getName();
259  }
260  if ( $this->stage & SCHEMA_COMPAT_WRITE_NEW ) {
261  // We need to be able to assign an actor ID if none exists
262  if ( !$user instanceof User && !$user->getActorId() ) {
263  $user = User::newFromAnyId( $user->getId(), $user->getName(), null );
264  }
265  $ret[$actor] = $user->getActorId( $dbw );
266  }
267  return $ret;
268  }
269 
282  public function getInsertValuesWithTempTable( IDatabase $dbw, $key, UserIdentity $user ) {
283  self::checkDeprecation( $key );
284 
285  if ( isset( self::$formerTempTables[$key] ) ) {
286  wfDeprecated( __METHOD__ . " for $key", self::$formerTempTables[$key] );
287  } elseif ( !isset( self::$tempTables[$key] ) ) {
288  throw new InvalidArgumentException( "Must use getInsertValues() for $key" );
289  }
290 
291  list( $text, $actor ) = self::getFieldNames( $key );
292  $ret = [];
293  $callback = null;
294  if ( $this->stage & SCHEMA_COMPAT_WRITE_OLD ) {
295  $ret[$key] = $user->getId();
296  $ret[$text] = $user->getName();
297  }
298  if ( $this->stage & SCHEMA_COMPAT_WRITE_NEW ) {
299  // We need to be able to assign an actor ID if none exists
300  if ( !$user instanceof User && !$user->getActorId() ) {
301  $user = User::newFromAnyId( $user->getId(), $user->getName(), null );
302  }
303  $id = $user->getActorId( $dbw );
304 
305  if ( isset( self::$tempTables[$key] ) ) {
306  $func = __METHOD__;
307  $callback = function ( $pk, array $extra ) use ( $dbw, $key, $id, $func ) {
308  $t = self::$tempTables[$key];
309  $set = [ $t['field'] => $id ];
310  foreach ( $t['extra'] as $to => $from ) {
311  if ( !array_key_exists( $from, $extra ) ) {
312  throw new InvalidArgumentException( "$func callback: \$extra[$from] is not provided" );
313  }
314  $set[$to] = $extra[$from];
315  }
316  $dbw->upsert(
317  $t['table'],
318  [ $t['pk'] => $pk ] + $set,
319  [ $t['pk'] ],
320  $set,
321  $func
322  );
323  };
324  } else {
325  $ret[$actor] = $id;
326  $callback = function ( $pk, array $extra ) {
327  };
328  }
329  } elseif ( isset( self::$tempTables[$key] ) ) {
330  $func = __METHOD__;
331  $callback = function ( $pk, array $extra ) use ( $key, $func ) {
332  $t = self::$tempTables[$key];
333  foreach ( $t['extra'] as $to => $from ) {
334  if ( !array_key_exists( $from, $extra ) ) {
335  throw new InvalidArgumentException( "$func callback: \$extra[$from] is not provided" );
336  }
337  }
338  };
339  } else {
340  $callback = function ( $pk, array $extra ) {
341  };
342  }
343  return [ $ret, $callback ];
344  }
345 
367  public function getWhere( IDatabase $db, $key, $users, $useId = true ) {
368  self::checkDeprecation( $key );
369 
370  $tables = [];
371  $conds = [];
372  $joins = [];
373 
374  if ( $users instanceof UserIdentity ) {
375  $users = [ $users ];
376  }
377 
378  // Get information about all the passed users
379  $ids = [];
380  $names = [];
381  $actors = [];
382  foreach ( $users as $user ) {
383  if ( $useId && $user->getId() ) {
384  $ids[] = $user->getId();
385  } else {
386  $names[] = $user->getName();
387  }
388  $actorId = $user->getActorId();
389  if ( $actorId ) {
390  $actors[] = $actorId;
391  }
392  }
393 
394  list( $text, $actor ) = self::getFieldNames( $key );
395 
396  // Combine data into conditions to be ORed together
397  if ( $this->stage & SCHEMA_COMPAT_READ_NEW ) {
398  if ( $actors ) {
399  if ( isset( self::$tempTables[$key] ) ) {
400  $t = self::$tempTables[$key];
401  $alias = "temp_$key";
402  $tables[$alias] = $t['table'];
403  $joins[$alias] = [ 'JOIN', "{$alias}.{$t['pk']} = {$t['joinPK']}" ];
404  $joinField = "{$alias}.{$t['field']}";
405  } else {
406  $joinField = $actor;
407  }
408  $conds['actor'] = $db->makeList( [ $joinField => $actors ], IDatabase::LIST_AND );
409  }
410  } else {
411  if ( $ids ) {
412  $conds['userid'] = $db->makeList( [ $key => $ids ], IDatabase::LIST_AND );
413  }
414  if ( $names ) {
415  $conds['username'] = $db->makeList( [ $text => $names ], IDatabase::LIST_AND );
416  }
417  }
418 
419  return [
420  'tables' => $tables,
421  'conds' => $conds ? $db->makeList( array_values( $conds ), IDatabase::LIST_OR ) : '1=0',
422  'orconds' => $conds,
423  'joins' => $joins,
424  ];
425  }
426 
427 }
const SCHEMA_COMPAT_WRITE_OLD
Definition: Defines.php:264
upsert( $table, array $rows, $uniqueIndexes, array $set, $fname=__METHOD__)
INSERT ON DUPLICATE KEY UPDATE wrapper, upserts an array into a table.
const SCHEMA_COMPAT_READ_BOTH
Definition: Defines.php:269
isNotAnon( $field)
Return an SQL condition to test if a user field is non-anonymous.
static array array $joinCache
Cache for self::getJoin()
makeList(array $a, $mode=self::LIST_COMMA)
Makes an encoded list of strings from an array.
static newFromAnyId( $userId, $userName, $actorId, $dbDomain=false)
Static factory method for creation from an ID, name, and/or actor ID.
Definition: User.php:600
const SCHEMA_COMPAT_READ_NEW
Definition: Defines.php:267
getWhere(IDatabase $db, $key, $users, $useId=true)
Get WHERE condition for the actor.
static array $tempTables
Define fields that use temporary tables for transitional purposes Keys are &#39;$key&#39;, values are arrays with four fields:
static array $specialFields
Define fields that use non-standard mapping Keys are the user id column name, values are arrays with ...
static getFieldNames( $key)
Interface for objects representing user identity.
The User object encapsulates all of the user-specific settings (user_id, name, rights, email address, options, last login time).
Definition: User.php:51
static string null [] $deprecated
Define fields that are deprecated for use with this class.
static newMigration()
Static constructor.
const LIST_AND
Definition: Defines.php:39
const SCHEMA_COMPAT_WRITE_NEW
Definition: Defines.php:266
__construct( $stage)
const MIGRATION_STAGE_SCHEMA_COMPAT
Constant for extensions to feature-test whether $wgActorTableSchemaMigrationStage (in MW <1...
static string null [] static string [] $removed
Define fields that are removed for use with this class.
getInsertValues(IDatabase $dbw, $key, UserIdentity $user)
Get UPDATE fields for the actor.
int $stage
Combination of SCHEMA_COMPAT_* constants.
const SCHEMA_COMPAT_WRITE_BOTH
Definition: Defines.php:268
const LIST_OR
Definition: Defines.php:42
static checkDeprecation( $key)
Issue deprecation warning/error as appropriate.
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
getJoin( $key)
Get SELECT fields and joins for the actor key.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
static array static array $formerTempTables
Fields that formerly used $tempTables Key is &#39;$key&#39;, value is the MediaWiki version in which it was r...
const SCHEMA_COMPAT_READ_OLD
Definition: Defines.php:265
isAnon( $field)
Return an SQL condition to test if a user field is anonymous.
getInsertValuesWithTempTable(IDatabase $dbw, $key, UserIdentity $user)
Get UPDATE fields for the actor.