Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
83.12% covered (warning)
83.12%
64 / 77
30.00% covered (danger)
30.00%
3 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
LinksTableGroup
83.12% covered (warning)
83.12%
64 / 77
30.00% covered (danger)
30.00%
3 / 10
31.77
0.00% covered (danger)
0.00%
0 / 1
 __construct
88.89% covered (warning)
88.89%
8 / 9
0.00% covered (danger)
0.00%
0 / 1
2.01
 setParserOutput
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 setMoveDetails
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 setTransactionTicket
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 setRevision
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 setStrictTestMode
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 getSpec
33.33% covered (danger)
33.33%
3 / 9
0.00% covered (danger)
0.00%
0 / 1
5.67
 addCollationArgs
85.71% covered (warning)
85.71%
12 / 14
0.00% covered (danger)
0.00%
0 / 1
3.03
 get
96.30% covered (success)
96.30%
26 / 27
0.00% covered (danger)
0.00%
0 / 1
7
 getAll
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
3.14
1<?php
2
3namespace MediaWiki\Deferred\LinksUpdate;
4
5use InvalidArgumentException;
6use MediaWiki\Collation\CollationFactory;
7use MediaWiki\Config\ServiceOptions;
8use MediaWiki\Linker\LinkTargetLookup;
9use MediaWiki\MediaWikiServices;
10use MediaWiki\Page\PageIdentity;
11use MediaWiki\Page\PageReference;
12use MediaWiki\Parser\ParserOutput;
13use MediaWiki\Revision\RevisionRecord;
14use Wikimedia\ObjectFactory\ObjectFactory;
15use Wikimedia\Rdbms\LBFactory;
16
17/**
18 * @since 1.38
19 */
20class LinksTableGroup {
21    /**
22     * ObjectFactory specifications for the subclasses. The following
23     * additional keys are defined:
24     *
25     *   - serviceOptions: An array of configuration variable names. If this is
26     *     set, the specified configuration will be sent to the subclass
27     *     constructor as a ServiceOptions object.
28     *   - needCollation: If true, the following additional args will be added:
29     *     Collation, collation name and table name.
30     */
31    private const CORE_LIST = [
32        'categorylinks' => [
33            'class' => CategoryLinksTable::class,
34            'services' => [
35                'LanguageConverterFactory',
36                'NamespaceInfo',
37                'WikiPageFactory',
38                'DBLoadBalancer',
39                'MainWANObjectCache',
40                'MainConfig',
41                'JobQueueGroup',
42                'HookContainer',
43            ],
44            'needCollation' => true,
45        ],
46        'externallinks' => [
47            'class' => ExternalLinksTable::class,
48        ],
49        'existencelinks' => [
50            'class' => ExistenceLinksTable::class,
51        ],
52        'imagelinks' => [
53            'class' => ImageLinksTable::class
54        ],
55        'iwlinks' => [
56            'class' => InterwikiLinksTable::class
57        ],
58        'langlinks' => [
59            'class' => LangLinksTable::class
60        ],
61        'pagelinks' => [
62            'class' => PageLinksTable::class,
63        ],
64        'page_props' => [
65            'class' => PagePropsTable::class,
66            'services' => [
67                'JobQueueGroup'
68            ],
69            'serviceOptions' => PagePropsTable::CONSTRUCTOR_OPTIONS
70        ],
71        'templatelinks' => [
72            'class' => TemplateLinksTable::class,
73        ]
74    ];
75
76    /** @var ObjectFactory */
77    private $objectFactory;
78
79    /** @var LBFactory */
80    private $lbFactory;
81
82    /** @var CollationFactory */
83    private $collationFactory;
84
85    /** @var PageIdentity */
86    private $page;
87
88    /** @var PageReference|null */
89    private $movedPage;
90
91    /** @var ParserOutput|null */
92    private $parserOutput;
93
94    /** @var LinkTargetLookup */
95    private $linkTargetLookup;
96
97    /** @var int */
98    private $batchSize;
99
100    /** @var mixed */
101    private $ticket;
102
103    /** @var RevisionRecord|null */
104    private $revision;
105
106    /** @var LinksTable[] */
107    private $tables = [];
108
109    /** @var array */
110    private $tempCollations;
111
112    /**
113     * @param ObjectFactory $objectFactory
114     * @param LBFactory $lbFactory
115     * @param CollationFactory $collationFactory
116     * @param PageIdentity $page
117     * @param LinkTargetLookup $linkTargetLookup
118     * @param int $batchSize
119     * @param array $tempCollations
120     */
121    public function __construct(
122        ObjectFactory $objectFactory,
123        LBFactory $lbFactory,
124        CollationFactory $collationFactory,
125        PageIdentity $page,
126        LinkTargetLookup $linkTargetLookup,
127        $batchSize,
128        array $tempCollations
129    ) {
130        $this->objectFactory = $objectFactory;
131        $this->lbFactory = $lbFactory;
132        $this->collationFactory = $collationFactory;
133        $this->page = $page;
134        $this->batchSize = $batchSize;
135        $this->linkTargetLookup = $linkTargetLookup;
136        $this->tempCollations = [];
137        foreach ( $tempCollations as $info ) {
138            $this->tempCollations[$info['table']] = $info;
139        }
140    }
141
142    /**
143     * Set the ParserOutput object to be used in new and existing objects.
144     */
145    public function setParserOutput( ParserOutput $parserOutput ) {
146        $this->parserOutput = $parserOutput;
147        foreach ( $this->tables as $table ) {
148            $table->setParserOutput( $parserOutput );
149        }
150    }
151
152    /**
153     * Set the original title in the case of a page move.
154     */
155    public function setMoveDetails( PageReference $oldPage ) {
156        $this->movedPage = $oldPage;
157        foreach ( $this->tables as $table ) {
158            $table->setMoveDetails( $oldPage );
159        }
160    }
161
162    /**
163     * Set the transaction ticket to be used in new and existing objects.
164     *
165     * @param mixed $ticket
166     */
167    public function setTransactionTicket( $ticket ) {
168        $this->ticket = $ticket;
169        foreach ( $this->tables as $table ) {
170            $table->setTransactionTicket( $ticket );
171        }
172    }
173
174    /**
175     * Set the revision to be used in new and existing objects.
176     */
177    public function setRevision( RevisionRecord $revision ) {
178        $this->revision = $revision;
179        foreach ( $this->tables as $table ) {
180            $table->setRevision( $revision );
181        }
182    }
183
184    /**
185     * Set the strict test mode
186     *
187     * @param bool $mode
188     */
189    public function setStrictTestMode( $mode = true ) {
190        foreach ( $this->getAll() as $table ) {
191            $table->setStrictTestMode( $mode );
192        }
193    }
194
195    /**
196     * Get the spec array for a given table.
197     *
198     * @param string $tableName
199     * @return array
200     */
201    private function getSpec( $tableName ) {
202        if ( isset( self::CORE_LIST[$tableName] ) ) {
203            $spec = self::CORE_LIST[$tableName];
204            return $this->addCollationArgs( $spec, $tableName, false );
205        }
206        if ( isset( $this->tempCollations[$tableName] ) ) {
207            $info = $this->tempCollations[$tableName];
208            $spec = self::CORE_LIST['categorylinks'];
209            return $this->addCollationArgs( $spec, $tableName, true, $info );
210        }
211        throw new InvalidArgumentException(
212            __CLASS__ . ": unknown table name \"$tableName\"" );
213    }
214
215    /**
216     * Add extra args to the spec of a table that needs collation information
217     *
218     * @param array $spec
219     * @param string $tableName
220     * @param bool $isTempTable
221     * @param array $info Temporary collation info
222     * @return array ObjectFactory spec
223     */
224    private function addCollationArgs( $spec, $tableName, $isTempTable, $info = [] ) {
225        if ( isset( $spec['needCollation'] ) ) {
226            if ( isset( $info['collation'] ) ) {
227                $collation = $this->collationFactory->makeCollation( $info['collation'] );
228                $collationName = $info['fakeCollation'] ?? $info['collation'];
229            } else {
230                $collation = $this->collationFactory->getCategoryCollation();
231                $collationName = $this->collationFactory->getDefaultCollationName();
232            }
233            $spec['args'] = [
234                $collation,
235                $info['fakeCollation'] ?? $collationName,
236                $tableName,
237                $isTempTable
238            ];
239            unset( $spec['needCollation'] );
240        }
241        return $spec;
242    }
243
244    /**
245     * Get a LinksTable for a given table.
246     *
247     * @param string $tableName
248     * @return LinksTable
249     */
250    public function get( $tableName ) {
251        if ( !isset( $this->tables[$tableName] ) ) {
252            $spec = $this->getSpec( $tableName );
253            if ( isset( $spec['serviceOptions'] ) ) {
254                $config = MediaWikiServices::getInstance()->getMainConfig();
255                $extraArgs = [ new ServiceOptions( $spec['serviceOptions'], $config ) ];
256                unset( $spec['serviceOptions'] );
257            } else {
258                $extraArgs = [];
259            }
260            /** @var LinksTable $table */
261            $table = $this->objectFactory->createObject(
262                $spec,
263                [ 'extraArgs' => $extraArgs, 'assertClass' => LinksTable::class ]
264            );
265            $table->injectBaseDependencies(
266                $this->lbFactory,
267                $this->linkTargetLookup,
268                $this->page,
269                $this->batchSize
270            );
271            if ( $this->parserOutput ) {
272                $table->setParserOutput( $this->parserOutput );
273            }
274            if ( $this->movedPage ) {
275                $table->setMoveDetails( $this->movedPage );
276            }
277            if ( $this->ticket ) {
278                $table->setTransactionTicket( $this->ticket );
279            }
280            if ( $this->revision ) {
281                $table->setRevision( $this->revision );
282            }
283            $this->tables[$tableName] = $table;
284        }
285        return $this->tables[$tableName];
286    }
287
288    /**
289     * Get LinksTable objects for all known links tables.
290     * @return iterable<LinksTable>
291     */
292    public function getAll() {
293        foreach ( self::CORE_LIST as $tableName => $spec ) {
294            yield $this->get( $tableName );
295        }
296        foreach ( $this->tempCollations as $tableName => $collation ) {
297            yield $this->get( $tableName );
298        }
299    }
300}