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 InvalidArgumentException; |
6 | use MediaWiki\Collation\CollationFactory; |
7 | use MediaWiki\Config\ServiceOptions; |
8 | use MediaWiki\Linker\LinkTargetLookup; |
9 | use MediaWiki\MediaWikiServices; |
10 | use MediaWiki\Page\PageIdentity; |
11 | use MediaWiki\Page\PageReference; |
12 | use MediaWiki\Parser\ParserOutput; |
13 | use MediaWiki\Revision\RevisionRecord; |
14 | use Wikimedia\ObjectFactory\ObjectFactory; |
15 | use Wikimedia\Rdbms\LBFactory; |
16 | |
17 | /** |
18 | * @since 1.38 |
19 | */ |
20 | class 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 | } |