Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
82.43% |
61 / 74 |
|
30.00% |
3 / 10 |
CRAP | |
0.00% |
0 / 1 |
LinksTableGroup | |
82.43% |
61 / 74 |
|
30.00% |
3 / 10 |
32.25 | |
0.00% |
0 / 1 |
__construct | |
88.89% |
8 / 9 |
|
0.00% |
0 / 1 |
2.01 | |||
setParserOutput | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
setMoveDetails | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
setTransactionTicket | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
setRevision | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
setStrictTestMode | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
getSpec | |
33.33% |
3 / 9 |
|
0.00% |
0 / 1 |
5.67 | |||
addCollationArgs | |
85.71% |
12 / 14 |
|
0.00% |
0 / 1 |
3.03 | |||
get | |
95.83% |
23 / 24 |
|
0.00% |
0 / 1 |
7 | |||
getAll | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
3.14 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Deferred\LinksUpdate; |
4 | |
5 | use MediaWiki\Collation\CollationFactory; |
6 | use MediaWiki\Config\ServiceOptions; |
7 | use MediaWiki\Linker\LinkTargetLookup; |
8 | use MediaWiki\MediaWikiServices; |
9 | use MediaWiki\Page\PageIdentity; |
10 | use MediaWiki\Page\PageReference; |
11 | use MediaWiki\Parser\ParserOutput; |
12 | use MediaWiki\Revision\RevisionRecord; |
13 | use Wikimedia\ObjectFactory\ObjectFactory; |
14 | use Wikimedia\Rdbms\LBFactory; |
15 | |
16 | /** |
17 | * @since 1.38 |
18 | */ |
19 | class 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 | } |