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