WikibaseClient::getLangLinkHandlerFactory()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 9.8333
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace Wikibase\Client;
4
5
use CentralIdLookup;
6
use DataValues\Deserializers\DataValueDeserializer;
7
use DataValues\Geo\Values\GlobeCoordinateValue;
8
use DataValues\MonolingualTextValue;
9
use DataValues\QuantityValue;
10
use DataValues\StringValue;
11
use DataValues\TimeValue;
12
use DataValues\UnknownValue;
13
use Deserializers\Deserializer;
14
use Deserializers\DispatchableDeserializer;
15
use Deserializers\DispatchingDeserializer;
16
use ExtensionRegistry;
17
use ExternalUserNames;
18
use Hooks;
19
use Http;
20
use JobQueueGroup;
21
use Language;
22
use LogicException;
23
use MediaWiki\Logger\LoggerFactory;
24
use MediaWiki\MediaWikiServices;
25
use MediaWikiSite;
26
use MWException;
27
use ObjectCache;
28
use Psr\Log\LoggerInterface;
29
use Serializers\Serializer;
30
use Site;
31
use SiteLookup;
32
use StubObject;
33
use Title;
34
use TitleFactory;
35
use Wikibase\Client\Changes\AffectedPagesFinder;
36
use Wikibase\Client\Changes\ChangeHandler;
37
use Wikibase\Client\Changes\ChangeRunCoalescer;
38
use Wikibase\Client\Changes\WikiPageUpdater;
39
use Wikibase\Client\DataAccess\ClientSiteLinkTitleLookup;
40
use Wikibase\Client\DataAccess\DataAccessSnakFormatterFactory;
41
use Wikibase\Client\DataAccess\ParserFunctions\Runner;
42
use Wikibase\Client\DataAccess\ParserFunctions\StatementGroupRendererFactory;
43
use Wikibase\Client\DataAccess\ReferenceFormatterFactory;
44
use Wikibase\Client\DataAccess\SnaksFinder;
45
use Wikibase\Client\Hooks\LangLinkHandlerFactory;
46
use Wikibase\Client\Hooks\LanguageLinkBadgeDisplay;
47
use Wikibase\Client\Hooks\OtherProjectsSidebarGeneratorFactory;
48
use Wikibase\Client\Hooks\SidebarLinkBadgeDisplay;
49
use Wikibase\Client\ParserOutput\ClientParserOutputDataUpdater;
50
use Wikibase\Client\RecentChanges\RecentChangeFactory;
51
use Wikibase\Client\RecentChanges\SiteLinkCommentCreator;
52
use Wikibase\Client\Store\ClientStore;
53
use Wikibase\Client\Store\DescriptionLookup;
54
use Wikibase\Client\Store\Sql\DirectSqlStore;
55
use Wikibase\Client\Store\Sql\PagePropsEntityIdLookup;
56
use Wikibase\Client\Usage\EntityUsageFactory;
57
use Wikibase\DataAccess\AliasTermBuffer;
58
use Wikibase\DataAccess\ByTypeDispatchingEntityIdLookup;
59
use Wikibase\DataAccess\DataAccessSettings;
60
use Wikibase\DataAccess\EntitySource;
61
use Wikibase\DataAccess\EntitySourceDefinitions;
62
use Wikibase\DataAccess\EntitySourceDefinitionsConfigParser;
63
use Wikibase\DataAccess\GenericServices;
64
use Wikibase\DataAccess\MultipleEntitySourceServices;
65
use Wikibase\DataAccess\PrefetchingTermLookup;
66
use Wikibase\DataAccess\SingleEntitySourceServices;
67
use Wikibase\DataAccess\WikibaseServices;
68
use Wikibase\DataModel\DeserializerFactory;
69
use Wikibase\DataModel\Entity\DispatchingEntityIdParser;
70
use Wikibase\DataModel\Entity\EntityIdParser;
71
use Wikibase\DataModel\Entity\EntityIdValue;
72
use Wikibase\DataModel\Entity\Item;
73
use Wikibase\DataModel\Entity\ItemIdParser;
74
use Wikibase\DataModel\Entity\Property;
75
use Wikibase\DataModel\SerializerFactory;
76
use Wikibase\DataModel\Services\Diff\EntityDiffer;
77
use Wikibase\DataModel\Services\EntityId\EntityIdComposer;
78
use Wikibase\DataModel\Services\EntityId\SuffixEntityIdParser;
79
use Wikibase\DataModel\Services\Lookup\DisabledEntityTypesEntityLookup;
80
use Wikibase\DataModel\Services\Lookup\EntityLookup;
81
use Wikibase\DataModel\Services\Lookup\EntityRetrievingDataTypeLookup;
82
use Wikibase\DataModel\Services\Lookup\PropertyDataTypeLookup;
83
use Wikibase\DataModel\Services\Lookup\RestrictedEntityLookup;
84
use Wikibase\DataModel\Services\Lookup\TermLookup;
85
use Wikibase\DataModel\Services\Term\PropertyLabelResolver;
86
use Wikibase\DataModel\Services\Term\TermBuffer;
87
use Wikibase\InternalSerialization\DeserializerFactory as InternalDeserializerFactory;
88
use Wikibase\Lib\Changes\EntityChange;
89
use Wikibase\Lib\Changes\EntityChangeFactory;
90
use Wikibase\Lib\Changes\ItemChange;
91
use Wikibase\Lib\ContentLanguages;
92
use Wikibase\Lib\DataTypeDefinitions;
93
use Wikibase\Lib\DataTypeFactory;
94
use Wikibase\Lib\EntityTypeDefinitions;
95
use Wikibase\Lib\Formatters\CachingKartographerEmbeddingHandler;
96
use Wikibase\Lib\Formatters\FormatterLabelDescriptionLookupFactory;
97
use Wikibase\Lib\Formatters\OutputFormatSnakFormatterFactory;
98
use Wikibase\Lib\Formatters\OutputFormatValueFormatterFactory;
99
use Wikibase\Lib\Formatters\Reference\WellKnownReferenceProperties;
100
use Wikibase\Lib\Formatters\WikibaseSnakFormatterBuilders;
101
use Wikibase\Lib\Formatters\WikibaseValueFormatterBuilders;
102
use Wikibase\Lib\Interactors\TermSearchInteractor;
103
use Wikibase\Lib\LanguageFallbackChainFactory;
104
use Wikibase\Lib\LanguageNameLookup;
105
use Wikibase\Lib\PropertyInfoDataTypeLookup;
106
use Wikibase\Lib\SettingsArray;
107
use Wikibase\Lib\Store\CachingPropertyOrderProvider;
108
use Wikibase\Lib\Store\EntityIdLookup;
109
use Wikibase\Lib\Store\EntityNamespaceLookup;
110
use Wikibase\Lib\Store\FallbackPropertyOrderProvider;
111
use Wikibase\Lib\Store\HttpUrlPropertyOrderProvider;
112
use Wikibase\Lib\Store\LanguageFallbackLabelDescriptionLookupFactory;
113
use Wikibase\Lib\Store\PropertyOrderProvider;
114
use Wikibase\Lib\Store\Sql\Terms\CachedDatabasePropertyLabelResolver;
115
use Wikibase\Lib\Store\Sql\Terms\DatabaseTermInLangIdsResolver;
116
use Wikibase\Lib\Store\Sql\Terms\DatabaseTypeIdsStore;
117
use Wikibase\Lib\Store\TitleLookupBasedEntityExistenceChecker;
118
use Wikibase\Lib\Store\TitleLookupBasedEntityRedirectChecker;
119
use Wikibase\Lib\Store\TitleLookupBasedEntityTitleTextLookup;
120
use Wikibase\Lib\Store\TitleLookupBasedEntityUrlLookup;
121
use Wikibase\Lib\Store\WikiPagePropertyOrderProvider;
122
use Wikibase\Lib\StringNormalizer;
123
use Wikibase\Lib\TermFallbackCache\TermFallbackCacheFacade;
124
use Wikibase\Lib\TermFallbackCache\TermFallbackCacheServiceFactory;
125
use Wikibase\Lib\TermFallbackCacheFactory;
126
use Wikibase\Lib\TermLanguageFallbackChain;
127
use Wikibase\Lib\WikibaseContentLanguages;
128
use Wikibase\Lib\WikibaseSettings;
129
130
/**
131
 * Top level factory for the WikibaseClient extension.
132
 *
133
 * @license GPL-2.0-or-later
134
 * @author Jeroen De Dauw < [email protected] >
135
 * @author Daniel Kinzler
136
 */
137
final class WikibaseClient {
138
139
	/**
140
	 * @warning only for use in getDefaultInstance()!
141
	 * @var WikibaseClient
142
	 */
143
	private static $defaultInstance = null;
144
145
	/**
146
	 * @warning only for use in getDefaultSnakFormatterBuilders()!
147
	 * @var WikibaseSnakFormatterBuilders
148
	 */
149
	private static $defaultSnakFormatterBuilders = null;
150
151
	/**
152
	 * @var SettingsArray
153
	 */
154
	private $settings;
155
156
	/**
157
	 * @var SiteLookup
158
	 */
159
	private $siteLookup;
160
161
	/**
162
	 * @var WikibaseServices
163
	 */
164
	private $wikibaseServices;
165
166
	/**
167
	 * @var PropertyDataTypeLookup|null
168
	 */
169
	private $propertyDataTypeLookup = null;
170
171
	/**
172
	 * @var DataTypeFactory|null
173
	 */
174
	private $dataTypeFactory = null;
175
176
	/**
177
	 * @var Deserializer|null
178
	 */
179
	private $entityDeserializer = null;
180
181
	/**
182
	 * @var EntityIdParser|null
183
	 */
184
	private $entityIdParser = null;
185
186
	/**
187
	 * @var EntityIdComposer|null
188
	 */
189
	private $entityIdComposer = null;
190
191
	/**
192
	 * @var EntityIdLookup|null
193
	 */
194
	private $entityIdLookup = null;
195
196
	/**
197
	 * @var ClientStore|null
198
	 */
199
	private $store = null;
200
201
	/**
202
	 * @var Site|null
203
	 */
204
	private $site = null;
205
206
	/**
207
	 * @var string|null
208
	 */
209
	private $siteGroup = null;
210
211
	/**
212
	 * @var OutputFormatSnakFormatterFactory|null
213
	 */
214
	private $snakFormatterFactory = null;
215
216
	/**
217
	 * @var OutputFormatValueFormatterFactory|null
218
	 */
219
	private $valueFormatterFactory = null;
220
221
	/**
222
	 * @var ClientParserOutputDataUpdater|null
223
	 */
224
	private $parserOutputDataUpdater = null;
225
226
	/**
227
	 * @var NamespaceChecker|null
228
	 */
229
	private $namespaceChecker = null;
230
231
	/**
232
	 * @var RestrictedEntityLookup|null
233
	 */
234
	private $restrictedEntityLookup = null;
235
236
	/**
237
	 * @var DataTypeDefinitions
238
	 */
239
	private $dataTypeDefinitions;
240
241
	/**
242
	 * @var EntityTypeDefinitions
243
	 */
244
	private $entityTypeDefinitions;
245
246
	/**
247
	 * @var TermLookup|null
248
	 */
249
	private $termLookup = null;
250
251
	/**
252
	 * @var TermBuffer|null
253
	 */
254
	private $termBuffer = null;
255
256
	/**
257
	 * @var PrefetchingTermLookup|null
258
	 */
259
	private $prefetchingTermLookup = null;
260
261
	/**
262
	 * @var PropertyOrderProvider|null
263
	 */
264
	private $propertyOrderProvider = null;
265
266
	/**
267
	 * @var SidebarLinkBadgeDisplay|null
268
	 */
269
	private $sidebarLinkBadgeDisplay = null;
270
271
	/**
272
	 * @var WikibaseValueFormatterBuilders|null
273
	 */
274
	private $valueFormatterBuilders = null;
275
276
	/**
277
	 * @var WikibaseContentLanguages|null
278
	 */
279
	private $wikibaseContentLanguages = null;
280
281
	/**
282
	 * @var EntitySourceDefinitions
283
	 */
284
	private $entitySourceDefinitions;
285
286
	private $descriptionLookup = null;
287
288
	private $propertyLabelResolver = null;
289
290
	/** @var ReferenceFormatterFactory|null */
291
	private $referenceFormatterFactory = null;
292
293
	/** @var TermFallbackCacheFactory|null */
294
	private $termFallbackCacheFactory = null;
295
296
	/**
297
	 * @warning This is for use with bootstrap code in WikibaseClient.datatypes.php only!
298
	 * Program logic should use WikibaseClient::getSnakFormatterFactory() instead!
299
	 *
300
	 * @return WikibaseValueFormatterBuilders
301
	 */
302
	public static function getDefaultValueFormatterBuilders() {
303
		global $wgThumbLimits;
304
		return self::getDefaultInstance()->newWikibaseValueFormatterBuilders( $wgThumbLimits );
305
	}
306
307
	/**
308
	 * Returns a low level factory object for creating formatters for well known data types.
309
	 *
310
	 * @warning This is for use with getDefaultValueFormatterBuilders() during bootstrap only!
311
	 * Program logic should use WikibaseClient::getSnakFormatterFactory() instead!
312
	 *
313
	 * @param array $thumbLimits
314
	 *
315
	 * @return WikibaseValueFormatterBuilders
316
	 */
317
	private function newWikibaseValueFormatterBuilders( array $thumbLimits ) {
318
		if ( $this->valueFormatterBuilders === null ) {
319
			$entityTitleLookup = new ClientSiteLinkTitleLookup(
320
				$this->getStore()->getSiteLinkLookup(),
321
				$this->settings->getSetting( 'siteGlobalID' )
322
			);
323
324
			$kartographerEmbeddingHandler = null;
325
			if ( $this->useKartographerGlobeCoordinateFormatter() ) {
326
				$kartographerEmbeddingHandler = new CachingKartographerEmbeddingHandler(
327
					MediaWikiServices::getInstance()->getParserFactory()->create()
328
				);
329
			}
330
331
			$this->valueFormatterBuilders = new WikibaseValueFormatterBuilders(
332
				new FormatterLabelDescriptionLookupFactory( $this->getTermLookup() ),
333
				new LanguageNameLookup( $this->getUserLanguage()->getCode() ),
334
				$this->getRepoItemUriParser(),
335
				$this->settings->getSetting( 'geoShapeStorageBaseUrl' ),
336
				$this->settings->getSetting( 'tabularDataStorageBaseUrl' ),
337
				$this->getTermFallbackCache(),
338
				$this->settings->getSetting( 'sharedCacheDuration' ),
339
				$this->getEntityLookup(),
340
				$this->getStore()->getEntityRevisionLookup(),
341
				$this->settings->getSetting( 'entitySchemaNamespace' ),
342
				new TitleLookupBasedEntityExistenceChecker( $entityTitleLookup ),
343
				new TitleLookupBasedEntityTitleTextLookup( $entityTitleLookup ),
344
				new TitleLookupBasedEntityUrlLookup( $entityTitleLookup ),
345
				new TitleLookupBasedEntityRedirectChecker( $entityTitleLookup ),
346
				$entityTitleLookup,
347
				$kartographerEmbeddingHandler,
348
				$this->settings->getSetting( 'useKartographerMaplinkInWikitext' ),
349
				$thumbLimits
350
			);
351
		}
352
353
		return $this->valueFormatterBuilders;
354
	}
355
356
	/**
357
	 * @return bool
358
	 */
359
	private function useKartographerGlobeCoordinateFormatter() {
360
		// FIXME: remove the global out of here
361
		global $wgKartographerEnableMapFrame;
362
363
		return $this->settings->getSetting( 'useKartographerGlobeCoordinateFormatter' ) &&
364
			ExtensionRegistry::getInstance()->isLoaded( 'Kartographer' ) &&
365
			isset( $wgKartographerEnableMapFrame ) &&
366
			$wgKartographerEnableMapFrame;
367
	}
368
369
	/**
370
	 * @warning This is for use with bootstrap code in WikibaseClient.datatypes.php only!
371
	 * Program logic should use WikibaseClient::getSnakFormatterFactory() instead!
372
	 *
373
	 * @return WikibaseSnakFormatterBuilders
374
	 */
375
	public static function getDefaultSnakFormatterBuilders() {
376
		if ( self::$defaultSnakFormatterBuilders === null ) {
377
			self::$defaultSnakFormatterBuilders = self::getDefaultInstance()->newWikibaseSnakFormatterBuilders(
378
				self::getDefaultValueFormatterBuilders()
379
			);
380
		}
381
382
		return self::$defaultSnakFormatterBuilders;
383
	}
384
385
	/**
386
	 * Returns a low level factory object for creating formatters for well known data types.
387
	 *
388
	 * @warning This is for use with getDefaultValueFormatterBuilders() during bootstrap only!
389
	 * Program logic should use WikibaseClient::getSnakFormatterFactory() instead!
390
	 *
391
	 * @param WikibaseValueFormatterBuilders $valueFormatterBuilders
392
	 *
393
	 * @return WikibaseSnakFormatterBuilders
394
	 */
395
	private function newWikibaseSnakFormatterBuilders( WikibaseValueFormatterBuilders $valueFormatterBuilders ) {
396
		return new WikibaseSnakFormatterBuilders(
397
			$valueFormatterBuilders,
398
			$this->getStore()->getPropertyInfoLookup(),
399
			$this->getPropertyDataTypeLookup(),
400
			$this->getDataTypeFactory()
401
		);
402
	}
403
404
	public function __construct(
405
		SettingsArray $settings,
406
		DataTypeDefinitions $dataTypeDefinitions,
407
		EntityTypeDefinitions $entityTypeDefinitions,
408
		SiteLookup $siteLookup,
409
		EntitySourceDefinitions $entitySourceDefinitions
410
	) {
411
		$this->settings = $settings;
412
		$this->dataTypeDefinitions = $dataTypeDefinitions;
413
		$this->entityTypeDefinitions = $entityTypeDefinitions;
414
		$this->siteLookup = $siteLookup;
415
		$this->entitySourceDefinitions = $entitySourceDefinitions;
416
	}
417
418
	public function getDataTypeFactory(): DataTypeFactory {
419
		if ( $this->dataTypeFactory === null ) {
420
			$this->dataTypeFactory = new DataTypeFactory( $this->dataTypeDefinitions->getValueTypes() );
421
		}
422
423
		return $this->dataTypeFactory;
424
	}
425
426
	public function getEntityIdParser(): EntityIdParser {
427
		if ( $this->entityIdParser === null ) {
428
			$this->entityIdParser = new DispatchingEntityIdParser(
429
				$this->entityTypeDefinitions->getEntityIdBuilders()
430
			);
431
		}
432
433
		return $this->entityIdParser;
434
	}
435
436
	public function getEntityIdComposer(): EntityIdComposer {
437
		if ( $this->entityIdComposer === null ) {
438
			$this->entityIdComposer = new EntityIdComposer(
439
				$this->entityTypeDefinitions->get( EntityTypeDefinitions::ENTITY_ID_COMPOSER_CALLBACK )
440
			);
441
		}
442
443
		return $this->entityIdComposer;
444
	}
445
446
	public function getWikibaseServices(): WikibaseServices {
447
		if ( $this->wikibaseServices === null ) {
448
			$this->wikibaseServices = $this->newEntitySourceWikibaseServices();
449
		}
450
451
		return $this->wikibaseServices;
452
	}
453
454
	private function newEntitySourceWikibaseServices() {
455
		$nameTableStoreFactory = MediaWikiServices::getInstance()->getNameTableStoreFactory();
456
		$genericServices = new GenericServices( $this->entityTypeDefinitions );
457
458
		$singleSourceServices = [];
459
		foreach ( $this->entitySourceDefinitions->getSources() as $source ) {
460
			// TODO: extract
461
			$singleSourceServices[$source->getSourceName()] = new SingleEntitySourceServices(
462
				$genericServices,
463
				$this->getEntityIdParser(),
464
				$this->getEntityIdComposer(),
465
				$this->getDataValueDeserializer(),
466
				$nameTableStoreFactory->getSlotRoles( $source->getDatabaseName() ),
467
				$this->getDataAccessSettings(),
468
				$source,
469
				$this->entityTypeDefinitions->get( EntityTypeDefinitions::DESERIALIZER_FACTORY_CALLBACK ),
470
				$this->entityTypeDefinitions->get( EntityTypeDefinitions::ENTITY_METADATA_ACCESSOR_CALLBACK ),
471
				$this->entityTypeDefinitions->get( EntityTypeDefinitions::PREFETCHING_TERM_LOOKUP_CALLBACK ),
472
				$this->entityTypeDefinitions->get( EntityTypeDefinitions::ENTITY_REVISION_LOOKUP_FACTORY_CALLBACK )
473
			);
474
		}
475
476
		return new MultipleEntitySourceServices( $this->entitySourceDefinitions, $genericServices, $singleSourceServices );
477
	}
478
479
	private function getDataAccessSettings() {
480
		return new DataAccessSettings(
481
			$this->settings->getSetting( 'maxSerializedEntitySize' )
482
		);
483
	}
484
485
	/**
486
	 * @return EntityLookup
487
	 */
488
	private function getEntityLookup() {
489
		return $this->getStore()->getEntityLookup();
490
	}
491
492
	private static function getDefaultDataTypes() {
493
		$baseDataTypes = require __DIR__ . '/../../lib/WikibaseLib.datatypes.php';
494
		$clientDataTypes = require __DIR__ . '/../WikibaseClient.datatypes.php';
495
496
		return array_merge_recursive( $baseDataTypes, $clientDataTypes );
497
	}
498
499
	/**
500
	 * @return array[]
501
	 */
502
	private static function getDefaultEntityTypes() {
503
		return require __DIR__ . '/../../lib/WikibaseLib.entitytypes.php';
504
	}
505
506
	/**
507
	 * @return TermBuffer|AliasTermBuffer
508
	 */
509
	public function getTermBuffer() {
510
		if ( !$this->termBuffer ) {
511
			$this->termBuffer = $this->getPrefetchingTermLookup();
512
		}
513
514
		return $this->termBuffer;
515
	}
516
517
	public function getTermLookup(): TermLookup {
518
		if ( !$this->termLookup ) {
519
			$this->termLookup = $this->getPrefetchingTermLookup();
520
		}
521
522
		return $this->termLookup;
523
	}
524
525
	private function getPrefetchingTermLookup(): PrefetchingTermLookup {
526
		if ( !$this->prefetchingTermLookup ) {
527
			$this->prefetchingTermLookup = $this->getWikibaseServices()->getPrefetchingTermLookup();
528
		}
529
530
		return $this->prefetchingTermLookup;
531
	}
532
533
	/**
534
	 * XXX: This is not used by client itself, but is used by ArticlePlaceholder!
535
	 */
536
	public function newTermSearchInteractor( string $displayLanguageCode ): TermSearchInteractor {
537
		return $this->getWikibaseServices()->getTermSearchInteractorFactory()
538
			->newInteractor( $displayLanguageCode );
539
	}
540
541
	public function getPropertyDataTypeLookup(): PropertyDataTypeLookup {
542
		if ( $this->propertyDataTypeLookup === null ) {
543
			$infoLookup = $this->getStore()->getPropertyInfoLookup();
544
			$retrievingLookup = new EntityRetrievingDataTypeLookup( $this->getEntityLookup() );
545
			$this->propertyDataTypeLookup = new PropertyInfoDataTypeLookup(
546
				$infoLookup,
547
				$this->getLogger(),
548
				$retrievingLookup
549
			);
550
		}
551
552
		return $this->propertyDataTypeLookup;
553
	}
554
555
	public function getStringNormalizer(): StringNormalizer {
556
		return $this->getWikibaseServices()->getStringNormalizer();
557
	}
558
559
	public function newRepoLinker(): RepoLinker {
560
		return new RepoLinker(
561
			$this->entitySourceDefinitions,
562
			$this->settings->getSetting( 'repoUrl' ),
563
			$this->settings->getSetting( 'repoArticlePath' ),
564
			$this->settings->getSetting( 'repoScriptPath' )
565
		);
566
	}
567
568
	public function getLanguageFallbackChainFactory(): LanguageFallbackChainFactory {
569
		return $this->getWikibaseServices()->getLanguageFallbackChainFactory();
570
	}
571
572
	public function getLanguageFallbackLabelDescriptionLookupFactory(): LanguageFallbackLabelDescriptionLookupFactory {
573
		return new LanguageFallbackLabelDescriptionLookupFactory(
574
			$this->getLanguageFallbackChainFactory(),
575
			$this->getTermLookup(),
576
			$this->getTermBuffer()
0 ignored issues
show
Bug introduced by
It seems like $this->getTermBuffer() targeting Wikibase\Client\WikibaseClient::getTermBuffer() can also be of type object<Wikibase\DataAccess\AliasTermBuffer>; however, Wikibase\Lib\Store\Langu...pFactory::__construct() does only seem to accept null|object<Wikibase\Dat...rvices\Term\TermBuffer>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
577
		);
578
	}
579
580
	/**
581
	 * Returns an instance of the default store.
582
	 */
583
	public function getStore(): ClientStore {
584
		if ( $this->store === null ) {
585
			$this->store = new DirectSqlStore(
586
				$this->getEntityChangeFactory(),
587
				$this->getEntityIdParser(),
588
				$this->getEntityIdComposer(),
589
				$this->getEntityIdLookup(),
590
				$this->getEntityNamespaceLookup(),
591
				$this->getWikibaseServices(),
592
				$this->getSettings(),
593
				$this->getDatabaseDomainNameOfLocalRepo(),
594
				$this->getContentLanguage()->getCode()
595
			);
596
		}
597
598
		return $this->store;
599
	}
600
601
	/**
602
	 * Overrides the default store to be used in the client app context.
603
	 * This is intended for use by test cases.
604
	 *
605
	 * @param ClientStore|null $store
606
	 *
607
	 * @throws LogicException If MW_PHPUNIT_TEST is not defined, to avoid this
608
	 * method being abused in production code.
609
	 */
610
	public function overrideStore( ClientStore $store = null ) {
611
		if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
612
			throw new LogicException( 'Overriding the store instance is only supported in test mode' );
613
		}
614
615
		$this->store = $store;
616
	}
617
618
	/**
619
	 * Overrides the TermLookup to be used.
620
	 * This is intended for use by test cases.
621
	 *
622
	 * @param TermLookup|null $lookup
623
	 *
624
	 * @throws LogicException If MW_PHPUNIT_TEST is not defined, to avoid this
625
	 * method being abused in production code.
626
	 */
627
	public function overrideTermLookup( TermLookup $lookup = null ) {
628
		if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
629
			throw new LogicException( 'Overriding TermLookup is only supported in test mode' );
630
		}
631
632
		$this->termLookup = $lookup;
633
	}
634
635
	/**
636
	 * @throws MWException when called to early
637
	 */
638
	public function getContentLanguage(): Language {
639
		/**
640
		 * Before this constant is defined, custom config may not have been taken into account.
641
		 * So try not to allow code to use a language before that point.
642
		 * This code was explicitly mentioning the SetupAfterCache hook.
643
		 * With services, that hook won't be a problem anymore.
644
		 * So this check may well be unnecessary (but better safe than sorry).
645
		 */
646
		if ( !defined( 'MW_SERVICE_BOOTSTRAP_COMPLETE' ) ) {
647
			throw new MWException( 'Premature access to MediaWiki ContentLanguage!' );
648
		}
649
650
		return MediaWikiServices::getInstance()->getContentLanguage();
651
	}
652
653
	/**
654
	 * @throws MWException when called to early
655
	 */
656
	private function getUserLanguage(): Language {
657
		global $wgLang;
658
659
		// TODO: define a LanguageProvider service instead of using a global directly.
660
		// NOTE: we cannot inject $wgLang in the constructor, because it may still be null
661
		// when WikibaseClient is initialized. In particular, the language object may not yet
662
		// be there when the SetupAfterCache hook is run during bootstrapping.
663
664
		if ( !$wgLang ) {
665
			throw new MWException( 'Premature access: $wgLang is not yet initialized!' );
666
		}
667
668
		StubObject::unstub( $wgLang );
669
		return $wgLang;
670
	}
671
672
	public function getSettings(): SettingsArray {
673
		return $this->settings;
674
	}
675
676
	/**
677
	 * Returns a new instance constructed from global settings.
678
	 * IMPORTANT: Use only when it is not feasible to inject an instance properly.
679
	 *
680
	 * @throws MWException
681
	 * @return self
682
	 */
683
	private static function newInstance() {
684
		$dataTypeDefinitionsArray = self::getDefaultDataTypes();
685
		Hooks::run( 'WikibaseClientDataTypes', [ &$dataTypeDefinitionsArray ] );
686
687
		$entityTypeDefinitionsArray = self::getDefaultEntityTypes();
688
		Hooks::run( 'WikibaseClientEntityTypes', [ &$entityTypeDefinitionsArray ] );
689
690
		$settings = WikibaseSettings::getClientSettings();
691
692
		$dataTypeDefinitions = new DataTypeDefinitions(
693
			$dataTypeDefinitionsArray,
694
			$settings->getSetting( 'disabledDataTypes' )
695
		);
696
		$entityTypeDefinitions = new EntityTypeDefinitions( $entityTypeDefinitionsArray );
697
698
		return new self(
699
			$settings,
700
			$dataTypeDefinitions,
701
			$entityTypeDefinitions,
702
			MediaWikiServices::getInstance()->getSiteLookup(),
703
			self::getEntitySourceDefinitionsFromSettings( $settings, $entityTypeDefinitions )
704
		);
705
	}
706
707
	// TODO: current settings (especially (foreign) repositories blob) might be quite confusing
708
	// Having a "entitySources" or so setting might be better, and would also allow unifying
709
	// the way these are configured in Repo and in Client parts
710
	private static function getEntitySourceDefinitionsFromSettings(
711
		SettingsArray $settings,
712
		EntityTypeDefinitions $entityTypeDefinitions
713
	) {
714
		if ( $settings->hasSetting( 'entitySources' ) && !empty( $settings->getSetting( 'entitySources' ) ) ) {
715
			$configParser = new EntitySourceDefinitionsConfigParser();
716
717
			return $configParser->newDefinitionsFromConfigArray( $settings->getSetting( 'entitySources' ), $entityTypeDefinitions );
718
		}
719
720
		$parser = new EntitySourceDefinitionsLegacyClientSettingsParser();
721
		return $parser->newDefinitionsFromSettings( $settings, $entityTypeDefinitions );
722
	}
723
724
	/**
725
	 * IMPORTANT: Use only when it is not feasible to inject an instance properly.
726
	 *
727
	 * @param string $reset Flag: Pass "reset" to reset the default instance
728
	 *
729
	 * @return self
730
	 */
731
	public static function getDefaultInstance( $reset = 'noreset' ) {
732
		if ( $reset === 'reset' ) {
733
			self::$defaultInstance = null;
734
			self::$defaultSnakFormatterBuilders = null;
735
		}
736
737
		if ( self::$defaultInstance === null ) {
738
			self::$defaultInstance = self::newInstance();
739
		}
740
741
		return self::$defaultInstance;
742
	}
743
744
	public function getLogger(): LoggerInterface {
745
		return LoggerFactory::getInstance( 'Wikibase' );
746
	}
747
748
	/**
749
	 * Returns the this client wiki's site object.
750
	 *
751
	 * This is taken from the siteGlobalID setting, which defaults
752
	 * to the wiki's database name.
753
	 *
754
	 * If the configured site ID is not found in the sites table, a
755
	 * new Site object is constructed from the configured ID.
756
	 *
757
	 * @throws MWException
758
	 */
759
	public function getSite(): Site {
760
		if ( $this->site === null ) {
761
			$globalId = $this->settings->getSetting( 'siteGlobalID' );
762
			$localId = $this->settings->getSetting( 'siteLocalID' );
763
764
			$this->site = $this->siteLookup->getSite( $globalId );
765
766
			// Todo inject me
767
			$logger = $this->getLogger();
768
769
			if ( !$this->site ) {
770
				$logger->debug(
771
					'{method}:  Unable to resolve site ID {globalId}!',
772
					[ 'method' => __METHOD__, 'globalId' => $globalId ]
773
				);
774
775
				$this->site = new MediaWikiSite();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \MediaWikiSite() of type object<MediaWikiSite> is incompatible with the declared type object<Site>|null of property $site.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
776
				$this->site->setGlobalId( $globalId );
777
				$this->site->addLocalId( Site::ID_INTERWIKI, $localId );
778
				$this->site->addLocalId( Site::ID_EQUIVALENT, $localId );
779
			}
780
781
			if ( !in_array( $localId, $this->site->getLocalIds() ) ) {
782
				$logger->debug(
783
					'{method}: The configured local id {localId} does not match any local IDs of site {globalId}: {localIds}',
784
					[
785
						'method' => __METHOD__,
786
						'localId' => $localId,
787
						'globalId' => $globalId,
788
						'localIds' => json_encode( $this->site->getLocalIds() )
789
					]
790
				);
791
			}
792
		}
793
794
		return $this->site;
795
	}
796
797
	/**
798
	 * Returns the site group ID for the group to be used for language links.
799
	 * This is typically the group the client wiki itself belongs to, but
800
	 * can be configured to be otherwise using the languageLinkSiteGroup setting.
801
	 *
802
	 * @return string
803
	 */
804
	public function getLangLinkSiteGroup() {
805
		$group = $this->settings->getSetting( 'languageLinkSiteGroup' );
806
807
		if ( $group === null ) {
808
			$group = $this->getSiteGroup();
809
		}
810
811
		return $group;
812
	}
813
814
	/**
815
	 * Gets the site group ID from setting, which if not set then does
816
	 * lookup in site store.
817
	 *
818
	 * @return string
819
	 */
820
	private function newSiteGroup() {
821
		$siteGroup = $this->settings->getSetting( 'siteGroup' );
822
823
		if ( !$siteGroup ) {
824
			$siteId = $this->settings->getSetting( 'siteGlobalID' );
825
826
			$site = $this->siteLookup->getSite( $siteId );
827
828
			if ( !$site ) {
829
				return true;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return true; (boolean) is incompatible with the return type documented by Wikibase\Client\WikibaseClient::newSiteGroup of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
830
			}
831
832
			$siteGroup = $site->getGroup();
833
		}
834
835
		return $siteGroup;
836
	}
837
838
	/**
839
	 * Get site group ID
840
	 *
841
	 * @return string
842
	 */
843
	public function getSiteGroup() {
844
		if ( $this->siteGroup === null ) {
845
			$this->siteGroup = $this->newSiteGroup();
846
		}
847
848
		return $this->siteGroup;
849
	}
850
851
	/**
852
	 * Returns a OutputFormatSnakFormatterFactory the provides SnakFormatters
853
	 * for different output formats.
854
	 */
855
	private function getSnakFormatterFactory(): OutputFormatSnakFormatterFactory {
856
		if ( $this->snakFormatterFactory === null ) {
857
			$this->snakFormatterFactory = new OutputFormatSnakFormatterFactory(
858
				$this->dataTypeDefinitions->getSnakFormatterFactoryCallbacks(),
859
				$this->getValueFormatterFactory(),
860
				$this->getPropertyDataTypeLookup(),
861
				$this->getDataTypeFactory()
862
			);
863
		}
864
865
		return $this->snakFormatterFactory;
866
	}
867
868
	/**
869
	 * Returns a OutputFormatValueFormatterFactory the provides ValueFormatters
870
	 * for different output formats.
871
	 */
872
	private function getValueFormatterFactory(): OutputFormatValueFormatterFactory {
873
		if ( $this->valueFormatterFactory === null ) {
874
			$this->valueFormatterFactory = new OutputFormatValueFormatterFactory(
875
				$this->dataTypeDefinitions->getFormatterFactoryCallbacks( DataTypeDefinitions::PREFIXED_MODE ),
876
				$this->getContentLanguage(),
877
				$this->getLanguageFallbackChainFactory()
878
			);
879
		}
880
881
		return $this->valueFormatterFactory;
882
	}
883
884
	private function getRepoItemUriParser(): EntityIdParser {
885
		$itemConceptUriBase = $this->getItemSource()->getConceptBaseUri();
886
887
		return new SuffixEntityIdParser(
888
			$itemConceptUriBase,
889
			new ItemIdParser()
890
		);
891
	}
892
893
	public function getNamespaceChecker(): NamespaceChecker {
894
		if ( $this->namespaceChecker === null ) {
895
			$this->namespaceChecker = new NamespaceChecker(
896
				$this->settings->getSetting( 'excludeNamespaces' ),
897
				$this->settings->getSetting( 'namespaces' )
898
			);
899
		}
900
901
		return $this->namespaceChecker;
902
	}
903
904
	public function getLangLinkHandlerFactory(): LangLinkHandlerFactory {
905
		return new LangLinkHandlerFactory(
906
			$this->getLanguageLinkBadgeDisplay(),
907
			$this->getNamespaceChecker(),
908
			$this->getStore()->getSiteLinkLookup(),
909
			$this->getStore()->getEntityLookup(),
910
			$this->siteLookup,
911
			MediaWikiServices::getInstance()->getHookContainer(),
912
			$this->getLogger(),
913
			$this->settings->getSetting( 'siteGlobalID' ),
914
			$this->getLangLinkSiteGroup()
915
		);
916
	}
917
918
	public function getParserOutputDataUpdater(): ClientParserOutputDataUpdater {
919
		if ( $this->parserOutputDataUpdater === null ) {
920
			$this->parserOutputDataUpdater = new ClientParserOutputDataUpdater(
921
				$this->getOtherProjectsSidebarGeneratorFactory(),
922
				$this->getStore()->getSiteLinkLookup(),
923
				$this->getStore()->getEntityLookup(),
924
				new EntityUsageFactory( $this->getEntityIdParser() ),
925
				$this->settings->getSetting( 'siteGlobalID' ),
926
				$this->getLogger()
927
			);
928
		}
929
930
		return $this->parserOutputDataUpdater;
931
	}
932
933
	public function getSidebarLinkBadgeDisplay(): SidebarLinkBadgeDisplay {
934
		if ( $this->sidebarLinkBadgeDisplay === null ) {
935
			$labelDescriptionLookupFactory = $this->getLanguageFallbackLabelDescriptionLookupFactory();
936
			$badgeClassNames = $this->settings->getSetting( 'badgeClassNames' );
937
			$lang = $this->getUserLanguage();
938
939
			$this->sidebarLinkBadgeDisplay = new SidebarLinkBadgeDisplay(
940
				$labelDescriptionLookupFactory->newLabelDescriptionLookup( $lang ),
941
				is_array( $badgeClassNames ) ? $badgeClassNames : [],
942
				$lang
943
			);
944
		}
945
946
		return $this->sidebarLinkBadgeDisplay;
947
	}
948
949
	public function getLanguageLinkBadgeDisplay(): LanguageLinkBadgeDisplay {
950
		return new LanguageLinkBadgeDisplay(
951
			$this->getSidebarLinkBadgeDisplay()
952
		);
953
	}
954
955
	public function getBaseDataModelDeserializerFactory(): DeserializerFactory {
956
		return new DeserializerFactory(
957
			$this->getDataValueDeserializer(),
958
			$this->getEntityIdParser()
959
		);
960
	}
961
962
	private function getInternalFormatDeserializerFactory(): InternalDeserializerFactory {
963
		return new InternalDeserializerFactory(
964
			$this->getDataValueDeserializer(),
965
			$this->getEntityIdParser(),
966
			$this->getAllTypesEntityDeserializer()
967
		);
968
	}
969
970
	private function getAllTypesEntityDeserializer(): DispatchableDeserializer {
971
		if ( $this->entityDeserializer === null ) {
972
			$deserializerFactoryCallbacks = $this->getEntityDeserializerFactoryCallbacks();
973
			$baseDeserializerFactory = $this->getBaseDataModelDeserializerFactory();
974
			$deserializers = [];
975
976
			foreach ( $deserializerFactoryCallbacks as $callback ) {
977
				$deserializers[] = call_user_func( $callback, $baseDeserializerFactory );
978
			}
979
980
			$this->entityDeserializer = new DispatchingDeserializer( $deserializers );
981
		}
982
983
		return $this->entityDeserializer;
984
	}
985
986
	/**
987
	 * Returns a deserializer to deserialize statements in both current and legacy serialization.
988
	 */
989
	public function getInternalFormatStatementDeserializer(): Deserializer {
990
		return $this->getInternalFormatDeserializerFactory()->newStatementDeserializer();
991
	}
992
993
	/**
994
	 * @return callable[]
995
	 */
996
	public function getEntityDeserializerFactoryCallbacks() {
997
		return $this->entityTypeDefinitions->get( EntityTypeDefinitions::DESERIALIZER_FACTORY_CALLBACK );
998
	}
999
1000
	/**
1001
	 * @return string[]
1002
	 */
1003
	public function getLuaEntityModules() {
1004
		return $this->entityTypeDefinitions->get( EntityTypeDefinitions::LUA_ENTITY_MODULE );
1005
	}
1006
1007
	/**
1008
	 * Returns a SerializerFactory creating serializers that generate the most compact serialization.
1009
	 * A factory returned has knowledge about items, properties, and the elements they are made of,
1010
	 * but no other entity types.
1011
	 */
1012
	public function getCompactBaseDataModelSerializerFactory(): SerializerFactory {
1013
		return $this->getWikibaseServices()->getCompactBaseDataModelSerializerFactory();
1014
	}
1015
1016
	/**
1017
	 * Returns an entity serializer that generates the most compact serialization.
1018
	 */
1019
	public function getCompactEntitySerializer(): Serializer {
1020
		return $this->getWikibaseServices()->getCompactEntitySerializer();
1021
	}
1022
1023
	private function getDataValueDeserializer(): DataValueDeserializer {
1024
		return new DataValueDeserializer( [
1025
			'string' => StringValue::class,
1026
			'unknown' => UnknownValue::class,
1027
			'globecoordinate' => GlobeCoordinateValue::class,
1028
			'monolingualtext' => MonolingualTextValue::class,
1029
			'quantity' => QuantityValue::class,
1030
			'time' => TimeValue::class,
1031
			'wikibase-entityid' => function ( $value ) {
1032
				return isset( $value['id'] )
1033
					? new EntityIdValue( $this->getEntityIdParser()->parse( $value['id'] ) )
1034
					: EntityIdValue::newFromArray( $value );
0 ignored issues
show
Deprecated Code introduced by
The method Wikibase\DataModel\Entit...IdValue::newFromArray() has been deprecated with message: since 7.1. Static DataValue::newFromArray constructors like this are underspecified (not in the DataValue interface), and misleadingly named (should be named newFromArrayValue). Instead, use DataValue builder callbacks in @see DataValueDeserializer.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1035
			},
1036
		] );
1037
	}
1038
1039
	public function getOtherProjectsSidebarGeneratorFactory(): OtherProjectsSidebarGeneratorFactory {
1040
		return new OtherProjectsSidebarGeneratorFactory(
1041
			$this->settings,
1042
			$this->getStore()->getSiteLinkLookup(),
1043
			$this->siteLookup,
1044
			$this->getStore()->getEntityLookup(),
1045
			$this->getSidebarLinkBadgeDisplay(),
1046
			MediaWikiServices::getInstance()->getHookContainer(),
1047
			$this->getLogger()
1048
		);
1049
	}
1050
1051
	public function getEntityChangeFactory(): EntityChangeFactory {
1052
		//TODO: take this from a setting or registry.
1053
		$changeClasses = [
1054
			Item::ENTITY_TYPE => ItemChange::class,
1055
			// Other types of entities will use EntityChange
1056
		];
1057
1058
		return new EntityChangeFactory(
1059
			$this->getEntityDiffer(),
1060
			$this->getEntityIdParser(),
1061
			$changeClasses,
1062
			EntityChange::class,
1063
			$this->getLogger()
1064
		);
1065
	}
1066
1067
	private function getEntityDiffer(): EntityDiffer {
1068
		$entityDiffer = new EntityDiffer();
1069
		foreach ( $this->entityTypeDefinitions->get( EntityTypeDefinitions::ENTITY_DIFFER_STRATEGY_BUILDER ) as $builder ) {
1070
			$entityDiffer->registerEntityDifferStrategy( call_user_func( $builder ) );
1071
		}
1072
		return $entityDiffer;
1073
	}
1074
1075
	private function getStatementGroupRendererFactory(): StatementGroupRendererFactory {
1076
		return new StatementGroupRendererFactory(
1077
			$this->getPropertyLabelResolver(),
1078
			new SnaksFinder(),
1079
			$this->getRestrictedEntityLookup(),
1080
			$this->getDataAccessSnakFormatterFactory(),
1081
			new EntityUsageFactory( $this->getEntityIdParser() ),
1082
			$this->settings->getSetting( 'allowDataAccessInUserLanguage' )
1083
		);
1084
	}
1085
1086
	public function getDataAccessSnakFormatterFactory(): DataAccessSnakFormatterFactory {
1087
		return new DataAccessSnakFormatterFactory(
1088
			$this->getLanguageFallbackChainFactory(),
1089
			$this->getSnakFormatterFactory(),
1090
			$this->getPropertyDataTypeLookup(),
1091
			$this->getRepoItemUriParser(),
1092
			$this->getLanguageFallbackLabelDescriptionLookupFactory(),
1093
			$this->settings->getSetting( 'allowDataAccessInUserLanguage' )
1094
		);
1095
	}
1096
1097
	public function getPropertyParserFunctionRunner(): Runner {
1098
		return new Runner(
1099
			$this->getStatementGroupRendererFactory(),
1100
			$this->getStore()->getSiteLinkLookup(),
1101
			$this->getEntityIdParser(),
1102
			$this->getRestrictedEntityLookup(),
1103
			$this->settings->getSetting( 'siteGlobalID' ),
1104
			$this->settings->getSetting( 'allowArbitraryDataAccess' )
1105
		);
1106
	}
1107
1108
	public function getOtherProjectsSitesProvider(): OtherProjectsSitesProvider {
1109
		return new CachingOtherProjectsSitesProvider(
1110
			new OtherProjectsSitesGenerator(
1111
				$this->siteLookup,
1112
				$this->settings->getSetting( 'siteGlobalID' ),
1113
				$this->settings->getSetting( 'specialSiteLinkGroups' )
1114
			),
1115
			// TODO: Make configurable? Should be similar, maybe identical to sharedCacheType and
1116
			// sharedCacheDuration, but can not reuse these because this here is not shared.
1117
			ObjectCache::getLocalClusterInstance(),
1118
			60 * 60
1119
		);
1120
	}
1121
1122
	private function getAffectedPagesFinder(): AffectedPagesFinder {
1123
		return new AffectedPagesFinder(
1124
			$this->getStore()->getUsageLookup(),
1125
			new TitleFactory(),
1126
			MediaWikiServices::getInstance()->getLinkBatchFactory(),
1127
			$this->settings->getSetting( 'siteGlobalID' ),
1128
			$this->getLogger()
1129
		);
1130
	}
1131
1132
	public function getChangeHandler(): ChangeHandler {
1133
		$logger = $this->getLogger();
1134
1135
		$pageUpdater = new WikiPageUpdater(
1136
			JobQueueGroup::singleton(),
1137
			$logger,
1138
			MediaWikiServices::getInstance()->getStatsdDataFactory()
1139
		);
1140
1141
		$pageUpdater->setPurgeCacheBatchSize( $this->settings->getSetting( 'purgeCacheBatchSize' ) );
1142
		$pageUpdater->setRecentChangesBatchSize( $this->settings->getSetting( 'recentChangesBatchSize' ) );
1143
1144
		$changeListTransformer = new ChangeRunCoalescer(
1145
			$this->getStore()->getEntityRevisionLookup(),
1146
			$this->getEntityChangeFactory(),
1147
			$logger,
1148
			$this->settings->getSetting( 'siteGlobalID' )
1149
		);
1150
1151
		return new ChangeHandler(
1152
			$this->getAffectedPagesFinder(),
1153
			new TitleFactory(),
1154
			$pageUpdater,
1155
			$changeListTransformer,
1156
			$this->siteLookup,
1157
			$logger,
1158
			$this->settings->getSetting( 'injectRecentChanges' )
1159
		);
1160
	}
1161
1162
	public function getRecentChangeFactory(): RecentChangeFactory {
1163
		$repoSite = $this->siteLookup->getSite(
1164
			$this->getDatabaseDomainNameOfLocalRepo()
1165
		);
1166
		$interwikiPrefixes = ( $repoSite !== null ) ? $repoSite->getInterwikiIds() : [];
1167
		$interwikiPrefix = ( $interwikiPrefixes !== [] ) ? $interwikiPrefixes[0] : null;
1168
1169
		return new RecentChangeFactory(
1170
			$this->getContentLanguage(),
1171
			new SiteLinkCommentCreator(
1172
				$this->getContentLanguage(),
1173
				$this->siteLookup,
1174
				$this->settings->getSetting( 'siteGlobalID' )
1175
			),
1176
			CentralIdLookup::factoryNonLocal(),
1177
			( $interwikiPrefix !== null ) ?
1178
				new ExternalUserNames( $interwikiPrefix, false ) : null
1179
		);
1180
	}
1181
1182
	/**
1183
	 * @return string|false
1184
	 */
1185
	public function getDatabaseDomainNameOfLocalRepo() {
1186
		return $this->getEntitySourceOfLocalRepo()->getDatabaseName();
1187
	}
1188
1189
	private function getEntitySourceOfLocalRepo(): EntitySource {
1190
		$itemAndPropertySourceName = $this->settings->getSetting( 'itemAndPropertySourceName' );
1191
		$sources = $this->entitySourceDefinitions->getSources();
1192
		foreach ( $sources as $source ) {
1193
			if ( $source->getSourceName() === $itemAndPropertySourceName ) {
1194
				return $source;
1195
			}
1196
		}
1197
1198
		throw new LogicException( 'No source configured: ' . $itemAndPropertySourceName );
1199
	}
1200
1201
	public function getWikibaseContentLanguages(): WikibaseContentLanguages {
1202
		if ( $this->wikibaseContentLanguages === null ) {
1203
			$this->wikibaseContentLanguages = WikibaseContentLanguages::getDefaultInstance();
1204
		}
1205
1206
		return $this->wikibaseContentLanguages;
1207
	}
1208
1209
	/**
1210
	 * Get a ContentLanguages object holding the languages available for labels, descriptions and aliases.
1211
	 */
1212
	public function getTermsLanguages(): ContentLanguages {
1213
		return $this->getWikibaseContentLanguages()->getContentLanguages( WikibaseContentLanguages::CONTEXT_TERM );
1214
	}
1215
1216
	public function getRestrictedEntityLookup(): RestrictedEntityLookup {
1217
		if ( $this->restrictedEntityLookup === null ) {
1218
			$disabledEntityTypesEntityLookup = new DisabledEntityTypesEntityLookup(
1219
				$this->getEntityLookup(),
1220
				$this->settings->getSetting( 'disabledAccessEntityTypes' )
1221
			);
1222
			$this->restrictedEntityLookup = new RestrictedEntityLookup(
1223
				$disabledEntityTypesEntityLookup,
1224
				$this->settings->getSetting( 'entityAccessLimit' )
1225
			);
1226
		}
1227
1228
		return $this->restrictedEntityLookup;
1229
	}
1230
1231
	public function getPropertyOrderProvider(): PropertyOrderProvider {
1232
		if ( $this->propertyOrderProvider === null ) {
1233
			$title = Title::newFromText( 'MediaWiki:Wikibase-SortedProperties' );
1234
			$innerProvider = new WikiPagePropertyOrderProvider( $title );
1235
1236
			$url = $this->settings->getSetting( 'propertyOrderUrl' );
1237
			if ( $url !== null ) {
1238
				$innerProvider = new FallbackPropertyOrderProvider(
1239
					$innerProvider,
1240
					new HttpUrlPropertyOrderProvider(
1241
						$url,
1242
						new Http(),
1243
						$this->getLogger()
1244
					)
1245
				);
1246
			}
1247
1248
			$this->propertyOrderProvider = new CachingPropertyOrderProvider(
1249
				$innerProvider,
1250
				ObjectCache::getLocalClusterInstance()
1251
			);
1252
		}
1253
1254
		return $this->propertyOrderProvider;
1255
	}
1256
1257
	public function getEntityNamespaceLookup(): EntityNamespaceLookup {
1258
		return $this->getWikibaseServices()->getEntityNamespaceLookup();
1259
	}
1260
1261
	public function getDataAccessLanguageFallbackChain( Language $language ): TermLanguageFallbackChain {
1262
		return $this->getLanguageFallbackChainFactory()->newFromLanguage(
1263
			$language,
1264
			LanguageFallbackChainFactory::FALLBACK_ALL
1265
		);
1266
	}
1267
1268
	public function getTermFallbackCache(): TermFallbackCacheFacade {
1269
		return new TermFallbackCacheFacade(
1270
			$this->getTermFallbackCacheFactory()->getTermFallbackCache(),
1271
			$this->getSettings()->getSetting( 'sharedCacheDuration' )
1272
		);
1273
	}
1274
1275
	public function getTermFallbackCacheFactory(): TermFallbackCacheFactory {
1276
		global $wgSecretKey;
1277
1278
		if ( $this->termFallbackCacheFactory === null ) {
1279
			$this->termFallbackCacheFactory = new TermFallbackCacheFactory(
1280
				$this->settings->getSetting( 'sharedCacheType' ),
1281
				$this->getLogger(),
1282
				MediaWikiServices::getInstance()->getStatsdDataFactory(),
1283
				hash( 'sha256', $wgSecretKey ),
1284
				new TermFallbackCacheServiceFactory(),
1285
				$this->settings->getSetting( 'termFallbackCacheVersion' )
1286
			);
1287
		}
1288
		return $this->termFallbackCacheFactory;
1289
	}
1290
1291
	public function getEntityIdLookup(): EntityIdLookup {
1292
		if ( $this->entityIdLookup === null ) {
1293
			$this->entityIdLookup = new ByTypeDispatchingEntityIdLookup(
1294
				$this->entityTypeDefinitions->get( EntityTypeDefinitions::CONTENT_MODEL_ID ),
1295
				$this->entityTypeDefinitions->get( EntityTypeDefinitions::ENTITY_ID_LOOKUP_CALLBACK ),
1296
				new PagePropsEntityIdLookup(
1297
					MediaWikiServices::getInstance()->getDBLoadBalancer(),
1298
					$this->getEntityIdParser()
1299
				)
1300
			);
1301
		}
1302
1303
		return $this->entityIdLookup;
1304
	}
1305
1306
	public function getDescriptionLookup(): DescriptionLookup {
1307
		if ( $this->descriptionLookup === null ) {
1308
			$this->descriptionLookup = new DescriptionLookup(
1309
				$this->getEntityIdLookup(),
1310
				$this->getTermBuffer(),
0 ignored issues
show
Bug introduced by
It seems like $this->getTermBuffer() targeting Wikibase\Client\WikibaseClient::getTermBuffer() can also be of type object<Wikibase\DataAccess\AliasTermBuffer>; however, Wikibase\Client\Store\De...onLookup::__construct() does only seem to accept object<Wikibase\DataMode...rvices\Term\TermBuffer>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1311
				MediaWikiServices::getInstance()->getPageProps()
1312
			);
1313
		}
1314
		return $this->descriptionLookup;
1315
	}
1316
1317
	public function getPropertyLabelResolver(): PropertyLabelResolver {
1318
		if ( $this->propertyLabelResolver === null ) {
1319
			$languageCode = $this->getContentLanguage()->getCode();
1320
			$cacheKeyPrefix = $this->settings->getSetting( 'sharedCacheKeyPrefix' );
1321
			$cacheType = $this->settings->getSetting( 'sharedCacheType' );
1322
			$cacheDuration = $this->settings->getSetting( 'sharedCacheDuration' );
1323
1324
			// Cache key needs to be language specific
1325
			$cacheKey = $cacheKeyPrefix . ':TermPropertyLabelResolver' . '/' . $languageCode;
1326
1327
			$this->propertyLabelResolver = $this->getCachedDatabasePropertyLabelResolver(
1328
				$languageCode,
1329
				ObjectCache::getInstance( $cacheType ),
1330
				$cacheDuration,
1331
				$cacheKey
1332
			);
1333
		}
1334
1335
		return $this->propertyLabelResolver;
1336
	}
1337
1338
	public function getReferenceFormatterFactory(): ReferenceFormatterFactory {
1339
		if ( $this->referenceFormatterFactory === null ) {
1340
			$logger = $this->getLogger();
1341
			$this->referenceFormatterFactory = new ReferenceFormatterFactory(
1342
				$this->getDataAccessSnakFormatterFactory(),
1343
				WellKnownReferenceProperties::newFromArray(
1344
					$this->getSettings()->getSetting( 'wellKnownReferencePropertyIds' ),
1345
					$logger
1346
				),
1347
				$logger
1348
			);
1349
		}
1350
1351
		return $this->referenceFormatterFactory;
1352
	}
1353
1354
	private function getCachedDatabasePropertyLabelResolver(
1355
		$languageCode,
1356
		$cache,
1357
		$cacheDuration,
1358
		$cacheKey
1359
	): CachedDatabasePropertyLabelResolver {
1360
		$loadBalancer = $this->getLoadBalancerForConfiguredPropertySource();
1361
		$wanObjectCache = $this->getWANObjectCache();
1362
		$typeIdsStore = new DatabaseTypeIdsStore(
1363
			$loadBalancer,
1364
			$wanObjectCache,
1365
			$this->getDatabaseDomainForPropertySource()
0 ignored issues
show
Bug introduced by
It seems like $this->getDatabaseDomainForPropertySource() targeting Wikibase\Client\Wikibase...mainForPropertySource() can also be of type string; however, Wikibase\Lib\Store\Sql\T...IdsStore::__construct() does only seem to accept boolean, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1366
		);
1367
		$databaseTermIdsResolver = new DatabaseTermInLangIdsResolver(
1368
			$typeIdsStore,
1369
			$typeIdsStore,
1370
			$loadBalancer,
1371
			$this->getDatabaseDomainForPropertySource()
1372
		);
1373
1374
		return new CachedDatabasePropertyLabelResolver(
1375
			$languageCode,
1376
			$databaseTermIdsResolver,
1377
			$cache,
1378
			$cacheDuration,
1379
			$cacheKey
1380
		);
1381
	}
1382
1383
	private function getLoadBalancerForConfiguredPropertySource() {
1384
		$lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
1385
		return $lbFactory->getMainLB( $this->getDatabaseDomainForPropertySource() );
1386
	}
1387
1388
	private function getDatabaseDomainForPropertySource() {
1389
		$propertySource = $this->getPropertySource();
1390
1391
		return $propertySource->getDatabaseName();
1392
	}
1393
1394
	private function getWANObjectCache() {
1395
		return MediaWikiServices::getInstance()->getMainWANObjectCache();
1396
	}
1397
1398
	private function getItemSource() {
1399
		$itemSource = $this->entitySourceDefinitions->getSourceForEntityType( Item::ENTITY_TYPE );
1400
1401
		if ( $itemSource === null ) {
1402
			throw new LogicException( 'No source providing Items configured!' );
1403
		}
1404
1405
		return $itemSource;
1406
	}
1407
1408
	private function getPropertySource() {
1409
		$propertySource = $this->entitySourceDefinitions->getSourceForEntityType( Property::ENTITY_TYPE );
1410
1411
		if ( $propertySource === null ) {
1412
			throw new LogicException( 'No source providing Properties configured!' );
1413
		}
1414
1415
		return $propertySource;
1416
	}
1417
1418
}
1419