37 private $joinCache = [];
46 private $actorStoreFactory;
52 private $allowUnknown;
103 $this->fieldInfos = $fieldInfos;
104 $this->allowUnknown = $options[
'allowUnknown'] ??
true;
108 if ( $writeStage === 0 ) {
109 throw new InvalidArgumentException(
'$stage must include a write mode' );
111 if ( $readStage === 0 ) {
112 throw new InvalidArgumentException(
'$stage must include a read mode' );
114 if ( !in_array( $readStage,
117 throw new InvalidArgumentException(
'Cannot read multiple schemas' );
120 throw new InvalidArgumentException(
'Cannot read the old schema without also writing it' );
123 throw new InvalidArgumentException(
'Cannot read the temp schema without also writing it' );
126 throw new InvalidArgumentException(
'Cannot read the new schema without also writing it' );
128 $this->readStage = $readStage;
129 $this->writeStage = $writeStage;
131 $this->actorStoreFactory = $actorStoreFactory;
143 if ( isset( $this->fieldInfos[$key] ) ) {
144 return $this->fieldInfos[$key];
145 } elseif ( $this->allowUnknown ) {
148 throw new InvalidArgumentException( $this->
getInstanceName() .
": unknown key $key" );
161 if ( (
new ReflectionClass( $this ) )->isAnonymous() ) {
165 return static::class;
178 if ( isset( $fieldInfo[
'removedVersion'] ) ) {
179 $removedVersion = $fieldInfo[
'removedVersion'];
180 $component = $fieldInfo[
'component'] ??
'MediaWiki';
181 throw new InvalidArgumentException(
182 "Use of {$this->getInstanceName()} for '$key' was removed in $component $removedVersion"
185 if ( isset( $fieldInfo[
'deprecatedVersion'] ) ) {
186 $deprecatedVersion = $fieldInfo[
'deprecatedVersion'];
187 $component = $fieldInfo[
'component'] ??
'MediaWiki';
188 wfDeprecated(
"{$this->getInstanceName()} for '$key'", $deprecatedVersion, $component, 3 );
215 private function getFieldNames( $key ) {
217 $textField = $fieldInfo[
'textField'] ?? $key .
'_text';
218 $actorField = $fieldInfo[
'actorField'] ?? substr( $key, 0, -5 ) .
'_actor';
219 return [ $textField, $actorField ];
228 private function getTempTableInfo( $key ) {
230 return $fieldInfo[
'tempTable'] ??
null;
248 if ( !isset( $this->joinCache[$key] ) ) {
253 list( $text, $actor ) = $this->getFieldNames( $key );
256 $fields[$key] = $key;
257 $fields[$text] = $text;
258 $fields[$actor] =
'NULL';
260 $tempTableInfo = $this->getTempTableInfo( $key );
261 if ( $tempTableInfo ) {
262 $alias =
"temp_$key";
263 $tables[$alias] = $tempTableInfo[
'table'];
264 $joins[$alias] = [
'JOIN',
265 "{$alias}.{$tempTableInfo['pk']} = {$tempTableInfo['joinPK']}" ];
266 $joinField =
"{$alias}.{$tempTableInfo['field']}";
271 $alias =
"actor_$key";
272 $tables[$alias] =
'actor';
273 $joins[$alias] = [
'JOIN',
"{$alias}.actor_id = {$joinField}" ];
275 $fields[$key] =
"{$alias}.actor_user";
276 $fields[$text] =
"{$alias}.actor_name";
277 $fields[$actor] = $joinField;
279 $alias =
"actor_$key";
280 $tables[$alias] =
'actor';
281 $joins[$alias] = [
'JOIN',
"{$alias}.actor_id = {$actor}" ];
283 $fields[$key] =
"{$alias}.actor_user";
284 $fields[$text] =
"{$alias}.actor_name";
285 $fields[$actor] = $actor;
288 $this->joinCache[$key] = [
295 return $this->joinCache[$key];
310 if ( $this->getTempTableInfo( $key ) ) {
311 throw new InvalidArgumentException(
"Must use getInsertValuesWithTempTable() for $key" );
314 list( $text, $actor ) = $this->getFieldNames( $key );
317 $ret[$key] = $user->
getId();
318 $ret[$text] = $user->
getName();
323 $ret[$actor] = $this->actorStoreFactory
325 ->acquireActorId( $user, $dbw );
346 $tempTableInfo = $fieldInfo[
'tempTable'] ??
null;
347 if ( isset( $fieldInfo[
'formerTempTableVersion'] ) ) {
349 $fieldInfo[
'formerTempTableVersion'],
350 $fieldInfo[
'component'] ??
'MediaWiki' );
351 } elseif ( !$tempTableInfo ) {
352 throw new InvalidArgumentException(
"Must use getInsertValues() for $key" );
355 list( $text, $actor ) = $this->getFieldNames( $key );
360 $ret[$key] = $user->
getId();
361 $ret[$text] = $user->
getName();
364 $id = $this->actorStoreFactory
366 ->acquireActorId( $user, $dbw );
368 if ( $tempTableInfo ) {
371 $callback =
static function ( $pk, array $extra ) use ( $tempTableInfo, $dbw, $id, $func ) {
372 $set = [ $tempTableInfo[
'field'] => $id ];
373 foreach ( $tempTableInfo[
'extra'] as $to => $from ) {
374 if ( !array_key_exists( $from, $extra ) ) {
375 throw new InvalidArgumentException(
"$func callback: \$extra[$from] is not provided" );
377 $set[$to] = $extra[$from];
380 $tempTableInfo[
'table'],
381 [ $tempTableInfo[
'pk'] => $pk ] + $set,
382 [ [ $tempTableInfo[
'pk'] ] ],
396 if ( $callback ===
null ) {
398 if ( $tempTableInfo ) {
400 $callback =
static function ( $pk, array $extra ) use ( $tempTableInfo, $func ) {
401 foreach ( $tempTableInfo[
'extra'] as $to => $from ) {
402 if ( !array_key_exists( $from, $extra ) ) {
403 throw new InvalidArgumentException(
"$func callback: \$extra[$from] is not provided" );
408 $callback =
static function ( $pk, array $extra ) {
412 return [ $ret, $callback ];
448 } elseif ( $users ===
null || $users ===
false ) {
451 } elseif ( !is_array( $users ) ) {
452 $what = is_object( $users ) ? get_class( $users ) : gettype( $users );
453 throw new InvalidArgumentException(
454 __METHOD__ .
": Value for \$users must be a UserIdentity or array, got $what"
462 foreach ( $users as $user ) {
463 if ( $useId && $user->isRegistered() ) {
464 $ids[] = $user->getId();
467 $names[] = IPUtils::sanitizeIP( $user->getName() );
469 $actorId = $this->actorStoreFactory
471 ->findActorId( $user, $db );
474 $actors[] = $actorId;
478 list( $text, $actor ) = $this->getFieldNames( $key );
483 $conds[
'newactor'] = $db->
makeList( [ $actor => $actors ], IDatabase::LIST_AND );
487 $tempTableInfo = $this->getTempTableInfo( $key );
488 if ( $tempTableInfo ) {
489 $alias =
"temp_$key";
490 $tables[$alias] = $tempTableInfo[
'table'];
491 $joins[$alias] = [
'JOIN',
492 "{$alias}.{$tempTableInfo['pk']} = {$tempTableInfo['joinPK']}" ];
493 $joinField =
"{$alias}.{$tempTableInfo['field']}";
497 $conds[
'actor'] = $db->
makeList( [ $joinField => $actors ], IDatabase::LIST_AND );
501 $conds[
'userid'] = $db->
makeList( [ $key => $ids ], IDatabase::LIST_AND );
504 $conds[
'username'] = $db->
makeList( [ $text => $names ], IDatabase::LIST_AND );
510 'conds' => $conds ? $db->
makeList( array_values( $conds ), IDatabase::LIST_OR ) :
'1=0',
const SCHEMA_COMPAT_WRITE_TEMP
const SCHEMA_COMPAT_READ_NEW
const SCHEMA_COMPAT_WRITE_OLD
const SCHEMA_COMPAT_READ_TEMP
const SCHEMA_COMPAT_READ_OLD
const SCHEMA_COMPAT_WRITE_NEW
const SCHEMA_COMPAT_WRITE_MASK
const SCHEMA_COMPAT_READ_MASK
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
This abstract base class helps migrate core and extension code to use the actor table.
getJoin( $key)
Get SELECT fields and joins for the actor key.
getInstanceName()
Get a name for this instance to use in error messages.
checkDeprecation( $key)
Issue deprecation warning/error as appropriate.
getInsertValues(IDatabase $dbw, $key, UserIdentity $user)
Get UPDATE fields for the actor.
isNotAnon( $field)
Return an SQL condition to test if a user field is non-anonymous.
getWhere(IDatabase $db, $key, $users, $useId=true)
Get WHERE condition for the actor.
__construct( $fieldInfos, $stage, ActorStoreFactory $actorStoreFactory, $options=[])
getInsertValuesWithTempTable(IDatabase $dbw, $key, UserIdentity $user)
Get UPDATE fields for the actor.
getFieldInfo( $key)
Get config information about a field.
isAnon( $field)
Return an SQL condition to test if a user field is anonymous.