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