Issues (1401)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

repo/includes/WikibaseRepo.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Wikibase\Repo;
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 Diff\Comparer\ComparableComparer;
17
use Diff\Differ\OrderedListDiffer;
18
use Exception;
19
use ExtensionRegistry;
20
use HashBagOStuff;
21
use Hooks;
22
use HtmlCacheUpdater;
23
use IContextSource;
24
use InvalidArgumentException;
25
use JobQueueGroup;
26
use Language;
27
use LogicException;
28
use MediaWiki\Logger\LoggerFactory;
29
use MediaWiki\MediaWikiServices;
30
use MediaWiki\Site\MediaWikiPageNameNormalizer;
31
use MWException;
32
use ObjectCache;
33
use Psr\Log\LoggerInterface;
34
use RequestContext;
35
use Serializers\Serializer;
36
use SiteLookup;
37
use SpecialPage;
38
use StubObject;
39
use Title;
40
use User;
41
use ValueFormatters\FormatterOptions;
42
use ValueFormatters\ValueFormatter;
43
use ValueParsers\NullParser;
44
use Wikibase\DataAccess\AliasTermBuffer;
45
use Wikibase\DataAccess\DataAccessSettings;
46
use Wikibase\DataAccess\EntitySource;
47
use Wikibase\DataAccess\EntitySourceDefinitions;
48
use Wikibase\DataAccess\EntitySourceDefinitionsConfigParser;
49
use Wikibase\DataAccess\GenericServices;
50
use Wikibase\DataAccess\MediaWiki\EntitySourceDocumentUrlProvider;
51
use Wikibase\DataAccess\MultipleEntitySourceServices;
52
use Wikibase\DataAccess\PrefetchingTermLookup;
53
use Wikibase\DataAccess\SingleEntitySourceServices;
54
use Wikibase\DataAccess\WikibaseServices;
55
use Wikibase\DataModel\DeserializerFactory;
56
use Wikibase\DataModel\Entity\DispatchingEntityIdParser;
57
use Wikibase\DataModel\Entity\EntityIdParser;
58
use Wikibase\DataModel\Entity\EntityIdParsingException;
59
use Wikibase\DataModel\Entity\EntityIdValue;
60
use Wikibase\DataModel\Entity\Item;
61
use Wikibase\DataModel\Entity\ItemIdParser;
62
use Wikibase\DataModel\Entity\Property;
63
use Wikibase\DataModel\Entity\PropertyId;
64
use Wikibase\DataModel\SerializerFactory;
65
use Wikibase\DataModel\Services\Diff\EntityDiffer;
66
use Wikibase\DataModel\Services\Diff\EntityPatcher;
67
use Wikibase\DataModel\Services\EntityId\EntityIdComposer;
68
use Wikibase\DataModel\Services\EntityId\SuffixEntityIdParser;
69
use Wikibase\DataModel\Services\Lookup\EntityLookup;
70
use Wikibase\DataModel\Services\Lookup\EntityRetrievingDataTypeLookup;
71
use Wikibase\DataModel\Services\Lookup\InProcessCachingDataTypeLookup;
72
use Wikibase\DataModel\Services\Lookup\ItemLookup;
73
use Wikibase\DataModel\Services\Lookup\LegacyAdapterItemLookup;
74
use Wikibase\DataModel\Services\Lookup\LegacyAdapterPropertyLookup;
75
use Wikibase\DataModel\Services\Lookup\PropertyDataTypeLookup;
76
use Wikibase\DataModel\Services\Lookup\PropertyLookup;
77
use Wikibase\DataModel\Services\Lookup\TermLookup;
78
use Wikibase\DataModel\Services\Statement\GuidGenerator;
79
use Wikibase\DataModel\Services\Statement\StatementGuidParser;
80
use Wikibase\DataModel\Services\Statement\StatementGuidValidator;
81
use Wikibase\DataModel\Services\Term\TermBuffer;
82
use Wikibase\InternalSerialization\DeserializerFactory as InternalDeserializerFactory;
83
use Wikibase\Lib\Changes\EntityChangeFactory;
84
use Wikibase\Lib\ContentLanguages;
85
use Wikibase\Lib\DataTypeDefinitions;
86
use Wikibase\Lib\DataTypeFactory;
87
use Wikibase\Lib\DataValueFactory;
88
use Wikibase\Lib\EntityFactory;
89
use Wikibase\Lib\EntityTypeDefinitions;
90
use Wikibase\Lib\Formatters\CachingKartographerEmbeddingHandler;
91
use Wikibase\Lib\Formatters\EntityIdLinkFormatter;
92
use Wikibase\Lib\Formatters\EntityIdPlainLinkFormatter;
93
use Wikibase\Lib\Formatters\EntityIdValueFormatter;
94
use Wikibase\Lib\Formatters\FormatterLabelDescriptionLookupFactory;
95
use Wikibase\Lib\Formatters\MediaWikiNumberLocalizer;
96
use Wikibase\Lib\Formatters\OutputFormatSnakFormatterFactory;
97
use Wikibase\Lib\Formatters\OutputFormatValueFormatterFactory;
98
use Wikibase\Lib\Formatters\SnakFormatter;
99
use Wikibase\Lib\Formatters\WikibaseSnakFormatterBuilders;
100
use Wikibase\Lib\Formatters\WikibaseValueFormatterBuilders;
101
use Wikibase\Lib\Interactors\MatchingTermsLookupSearchInteractor;
102
use Wikibase\Lib\LanguageFallbackChainFactory;
103
use Wikibase\Lib\LanguageNameLookup;
104
use Wikibase\Lib\Modules\PropertyValueExpertsModule;
105
use Wikibase\Lib\Modules\SettingsValueProvider;
106
use Wikibase\Lib\PropertyInfoDataTypeLookup;
107
use Wikibase\Lib\SettingsArray;
108
use Wikibase\Lib\Store\CachingPropertyOrderProvider;
109
use Wikibase\Lib\Store\EntityArticleIdLookup;
110
use Wikibase\Lib\Store\EntityContentDataCodec;
111
use Wikibase\Lib\Store\EntityExistenceChecker;
112
use Wikibase\Lib\Store\EntityIdLookup;
113
use Wikibase\Lib\Store\EntityLinkTargetEntityIdLookup;
114
use Wikibase\Lib\Store\EntityNamespaceLookup;
115
use Wikibase\Lib\Store\EntityRedirectChecker;
116
use Wikibase\Lib\Store\EntityRevisionLookup;
117
use Wikibase\Lib\Store\EntityStore;
118
use Wikibase\Lib\Store\EntityStoreWatcher;
119
use Wikibase\Lib\Store\EntityTermStoreWriter;
120
use Wikibase\Lib\Store\EntityTitleTextLookup;
121
use Wikibase\Lib\Store\EntityUrlLookup;
122
use Wikibase\Lib\Store\ItemTermStoreWriterAdapter;
123
use Wikibase\Lib\Store\LanguageFallbackLabelDescriptionLookupFactory;
124
use Wikibase\Lib\Store\LinkTargetEntityIdLookup;
125
use Wikibase\Lib\Store\LookupConstants;
126
use Wikibase\Lib\Store\PropertyInfoLookup;
127
use Wikibase\Lib\Store\PropertyInfoStore;
128
use Wikibase\Lib\Store\PropertyTermStoreWriterAdapter;
129
use Wikibase\Lib\Store\Sql\EntityIdLocalPartPageTableEntityQuery;
130
use Wikibase\Lib\Store\Sql\PrefetchingWikiPageEntityMetaDataAccessor;
131
use Wikibase\Lib\Store\Sql\Terms\DatabaseTypeIdsStore;
132
use Wikibase\Lib\Store\Sql\Terms\TermStoreWriterFactory;
133
use Wikibase\Lib\Store\Sql\TypeDispatchingWikiPageEntityMetaDataAccessor;
134
use Wikibase\Lib\Store\Sql\WikiPageEntityMetaDataAccessor;
135
use Wikibase\Lib\Store\Sql\WikiPageEntityMetaDataLookup;
136
use Wikibase\Lib\Store\ThrowingEntityTermStoreWriter;
137
use Wikibase\Lib\Store\TitleLookupBasedEntityArticleIdLookup;
138
use Wikibase\Lib\Store\TitleLookupBasedEntityExistenceChecker;
139
use Wikibase\Lib\Store\TitleLookupBasedEntityRedirectChecker;
140
use Wikibase\Lib\Store\TitleLookupBasedEntityTitleTextLookup;
141
use Wikibase\Lib\Store\TitleLookupBasedEntityUrlLookup;
142
use Wikibase\Lib\Store\TypeDispatchingArticleIdLookup;
143
use Wikibase\Lib\Store\TypeDispatchingExistenceChecker;
144
use Wikibase\Lib\Store\TypeDispatchingRedirectChecker;
145
use Wikibase\Lib\Store\TypeDispatchingTitleTextLookup;
146
use Wikibase\Lib\Store\TypeDispatchingUrlLookup;
147
use Wikibase\Lib\Store\WikiPagePropertyOrderProvider;
148
use Wikibase\Lib\StringNormalizer;
149
use Wikibase\Lib\TermFallbackCache\TermFallbackCacheFacade;
150
use Wikibase\Lib\TermFallbackCache\TermFallbackCacheServiceFactory;
151
use Wikibase\Lib\TermFallbackCacheFactory;
152
use Wikibase\Lib\Units\UnitConverter;
153
use Wikibase\Lib\Units\UnitStorage;
154
use Wikibase\Lib\WikibaseContentLanguages;
155
use Wikibase\Lib\WikibaseSettings;
156
use Wikibase\Repo\Api\ApiHelperFactory;
157
use Wikibase\Repo\ChangeOp\ChangeOpFactoryProvider;
158
use Wikibase\Repo\ChangeOp\Deserialization\ChangeOpDeserializerFactory;
159
use Wikibase\Repo\ChangeOp\Deserialization\SiteLinkBadgeChangeOpSerializationValidator;
160
use Wikibase\Repo\ChangeOp\Deserialization\TermChangeOpSerializationValidator;
161
use Wikibase\Repo\ChangeOp\EntityChangeOpProvider;
162
use Wikibase\Repo\Content\EntityContentFactory;
163
use Wikibase\Repo\Content\ItemHandler;
164
use Wikibase\Repo\Content\PropertyHandler;
165
use Wikibase\Repo\Diff\ClaimDiffer;
166
use Wikibase\Repo\Diff\ClaimDifferenceVisualizer;
167
use Wikibase\Repo\Diff\DifferencesSnakVisualizer;
168
use Wikibase\Repo\Diff\EntityDiffVisualizerFactory;
169
use Wikibase\Repo\EditEntity\EditFilterHookRunner;
170
use Wikibase\Repo\EditEntity\MediawikiEditEntityFactory;
171
use Wikibase\Repo\EditEntity\MediawikiEditFilterHookRunner;
172
use Wikibase\Repo\EntityReferenceExtractors\EntityReferenceExtractorDelegator;
173
use Wikibase\Repo\EntityReferenceExtractors\StatementEntityReferenceExtractor;
174
use Wikibase\Repo\FederatedProperties\ApiServiceFactory;
175
use Wikibase\Repo\FederatedProperties\FederatedPropertiesEntitySourceDefinitionsConfigParser;
176
use Wikibase\Repo\FederatedProperties\WrappingEntityIdFormatterFactory;
177
use Wikibase\Repo\Hooks\Formatters\EntityLinkFormatterFactory;
178
use Wikibase\Repo\Interactors\ItemMergeInteractor;
179
use Wikibase\Repo\Interactors\ItemRedirectCreationInteractor;
180
use Wikibase\Repo\LinkedData\EntityDataFormatProvider;
181
use Wikibase\Repo\LinkedData\EntityDataUriManager;
182
use Wikibase\Repo\Localizer\ChangeOpApplyExceptionLocalizer;
183
use Wikibase\Repo\Localizer\ChangeOpDeserializationExceptionLocalizer;
184
use Wikibase\Repo\Localizer\ChangeOpValidationExceptionLocalizer;
185
use Wikibase\Repo\Localizer\DispatchingExceptionLocalizer;
186
use Wikibase\Repo\Localizer\ExceptionLocalizer;
187
use Wikibase\Repo\Localizer\GenericExceptionLocalizer;
188
use Wikibase\Repo\Localizer\MessageExceptionLocalizer;
189
use Wikibase\Repo\Localizer\MessageParameterFormatter;
190
use Wikibase\Repo\Localizer\ParseExceptionLocalizer;
191
use Wikibase\Repo\Notifications\ChangeNotifier;
192
use Wikibase\Repo\Notifications\DatabaseChangeTransmitter;
193
use Wikibase\Repo\Notifications\HookChangeTransmitter;
194
use Wikibase\Repo\Notifications\RepoEntityChange;
195
use Wikibase\Repo\Notifications\RepoItemChange;
196
use Wikibase\Repo\ParserOutput\DispatchingEntityMetaTagsCreatorFactory;
197
use Wikibase\Repo\ParserOutput\DispatchingEntityViewFactory;
198
use Wikibase\Repo\ParserOutput\EntityParserOutputGenerator;
199
use Wikibase\Repo\ParserOutput\EntityParserOutputGeneratorFactory;
200
use Wikibase\Repo\Rdf\EntityRdfBuilderFactory;
201
use Wikibase\Repo\Rdf\RdfVocabulary;
202
use Wikibase\Repo\Rdf\ValueSnakRdfBuilderFactory;
203
use Wikibase\Repo\Search\Fields\FieldDefinitions;
204
use Wikibase\Repo\Search\Fields\NoFieldDefinitions;
205
use Wikibase\Repo\Store\EntityPermissionChecker;
206
use Wikibase\Repo\Store\EntityTitleStoreLookup;
207
use Wikibase\Repo\Store\IdGenerator;
208
use Wikibase\Repo\Store\Sql\SqlIdGenerator;
209
use Wikibase\Repo\Store\Sql\SqlStore;
210
use Wikibase\Repo\Store\Sql\UpsertSqlIdGenerator;
211
use Wikibase\Repo\Store\Store;
212
use Wikibase\Repo\Store\TermsCollisionDetectorFactory;
213
use Wikibase\Repo\Store\TypeDispatchingEntityTitleStoreLookup;
214
use Wikibase\Repo\Store\WikiPageEntityStorePermissionChecker;
215
use Wikibase\Repo\Validators\EntityConstraintProvider;
216
use Wikibase\Repo\Validators\SnakValidator;
217
use Wikibase\Repo\Validators\TermValidatorFactory;
218
use Wikibase\Repo\Validators\ValidatorErrorLocalizer;
219
use Wikibase\Repo\View\RepoSpecialPageLinker;
220
use Wikibase\Repo\View\WikibaseHtmlSnakFormatterFactory;
221
use Wikibase\View\EntityIdFormatterFactory;
222
use Wikibase\View\Template\TemplateFactory;
223
use Wikibase\View\ViewFactory;
224
use Wikimedia\ObjectFactory;
225
226
/**
227
 * Top level factory for the WikibaseRepo extension.
228
 *
229
 * @license GPL-2.0-or-later
230
 * @author Jeroen De Dauw < [email protected] >
231
 * @author Daniel Kinzler
232
 * @author Tobias Gritschacher < [email protected] >
233
 */
234
class WikibaseRepo {
235
236
	/**
237
	 * @var SettingsArray
238
	 */
239
	private $settings;
240
241
	/**
242
	 * @var DataTypeFactory|null
243
	 */
244
	private $dataTypeFactory = null;
245
246
	/**
247
	 * @var ValueParserFactory|null
248
	 */
249
	private $valueParserFactory = null;
250
251
	/**
252
	 * @var SnakFactory|null
253
	 */
254
	private $snakFactory = null;
255
256
	/**
257
	 * @var PropertyDataTypeLookup|null
258
	 */
259
	private $propertyDataTypeLookup = null;
260
261
	/**
262
	 * @var StatementGuidValidator|null
263
	 */
264
	private $statementGuidValidator = null;
265
266
	/**
267
	 * @var Deserializer|null
268
	 */
269
	private $entityDeserializer = null;
270
271
	/**
272
	 * @var EntityIdParser|null
273
	 */
274
	private $entityIdParser = null;
275
276
	/**
277
	 * @var EntityIdComposer|null
278
	 */
279
	private $entityIdComposer = null;
280
281
	/**
282
	 * @var OutputFormatSnakFormatterFactory|null
283
	 */
284
	private $snakFormatterFactory = null;
285
286
	/**
287
	 * @var OutputFormatValueFormatterFactory|null
288
	 */
289
	private $valueFormatterFactory = null;
290
291
	/**
292
	 * @var SummaryFormatter|null
293
	 */
294
	private $summaryFormatter = null;
295
296
	/**
297
	 * @var ExceptionLocalizer|null
298
	 */
299
	private $exceptionLocalizer = null;
300
301
	/**
302
	 * @var Store|null
303
	 */
304
	private $store = null;
305
306
	/**
307
	 * @var WikibaseContentLanguages|null
308
	 */
309
	private $wikibaseContentLanguages = null;
310
311
	/**
312
	 * @var DataTypeDefinitions
313
	 */
314
	private $dataTypeDefinitions;
315
316
	/**
317
	 * @var EntityTypeDefinitions
318
	 */
319
	private $entityTypeDefinitions;
320
321
	/**
322
	 * @var ValueSnakRdfBuilderFactory
323
	 */
324
	private $valueSnakRdfBuilderFactory;
325
326
	/**
327
	 * @var RdfVocabulary
328
	 */
329
	private $rdfVocabulary;
330
331
	/**
332
	 * @var CachingCommonsMediaFileNameLookup|null
333
	 */
334
	private $cachingCommonsMediaFileNameLookup = null;
335
336
	/**
337
	 * @var WikibaseServices|null
338
	 */
339
	private $wikibaseServices = null;
340
341
	/**
342
	 * @var EntityRdfBuilderFactory|null
343
	 */
344
	private $entityRdfBuilderFactory = null;
345
346
	/**
347
	 * @var CachingKartographerEmbeddingHandler|null
348
	 */
349
	private $kartographerEmbeddingHandler = null;
350
351
	/**
352
	 * @var WikibaseRepo|null
353
	 */
354
	private static $instance = null;
355
356
	/**
357
	 * @var ValidatorBuilders|null
358
	 */
359
	private static $validatorBuilders = null;
360
361
	/**
362
	 * @var WikibaseValueFormatterBuilders|null
363
	 */
364
	private static $valueFormatterBuilders = null;
365
366
	/**
367
	 * @var WikibaseSnakFormatterBuilders|null
368
	 */
369
	private static $snakFormatterBuilders = null;
370
371
	/**
372
	 * @var EntitySourceDefinitions
373
	 */
374
	private $entitySourceDefinitions;
375
376
	/**
377
	 * @var DataAccessSettings
378
	 */
379
	private $dataAccessSettings;
380
381
	/**
382
	 * @var TermFallbackCacheFactory|null
383
	 */
384
	private $termFallbackCacheFactory = null;
385
386
	public static function resetClassStatics() {
387
		if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
388
			throw new Exception(
389
				'Cannot reset WikibaseRepo class statics outside of tests.'
390
			);
391
		}
392
		self::$instance = null;
393
		self::$validatorBuilders = null;
394
		self::$valueFormatterBuilders = null;
395
		self::$snakFormatterBuilders = null;
396
		ApiServiceFactory::resetClassStatics();
397
	}
398
399
	/**
400
	 * IMPORTANT: Use only when it is not feasible to inject an instance properly.
401
	 *
402
	 * @throws MWException
403
	 * @return self
404
	 */
405
	private static function newInstance() {
406
		$dataTypeDefinitionsArray = self::getDefaultDataTypes();
407
		Hooks::run( 'WikibaseRepoDataTypes', [ &$dataTypeDefinitionsArray ] );
408
409
		$entityTypeDefinitionsArray = self::getDefaultEntityTypes();
410
		Hooks::run( 'WikibaseRepoEntityTypes', [ &$entityTypeDefinitionsArray ] );
411
412
		$settings = WikibaseSettings::getRepoSettings();
413
414
		$dataTypeDefinitions = new DataTypeDefinitions(
415
			$dataTypeDefinitionsArray,
416
			$settings->getSetting( 'disabledDataTypes' )
417
		);
418
		$entityTypeDefinitions = new EntityTypeDefinitions( $entityTypeDefinitionsArray );
419
420
		return new self(
421
			$settings,
422
			$dataTypeDefinitions,
423
			$entityTypeDefinitions,
424
			self::getEntitySourceDefinitionsFromSettings( $settings, $entityTypeDefinitions )
425
		);
426
	}
427
428
	private static function getEntitySourceDefinitionsFromSettings(
429
		SettingsArray $settings,
430
		EntityTypeDefinitions $entityTypeDefinitions
431
	) {
432
		if ( $settings->hasSetting( 'entitySources' ) && !empty( $settings->getSetting( 'entitySources' ) ) ) {
433
			$configParser = new EntitySourceDefinitionsConfigParser();
434
435
			return $configParser->newDefinitionsFromConfigArray( $settings->getSetting( 'entitySources' ), $entityTypeDefinitions );
436
		}
437
438
		$parser = new EntitySourceDefinitionsLegacyRepoSettingsParser();
439
440
		if ( $settings->getSetting( 'federatedPropertiesEnabled' ) ) {
441
			$configParser = new FederatedPropertiesEntitySourceDefinitionsConfigParser( $settings );
442
443
			return $configParser->initializeDefaults(
444
				$parser->newDefinitionsFromSettings( $settings, $entityTypeDefinitions ),
445
				$entityTypeDefinitions
446
			);
447
		}
448
449
		return $parser->newDefinitionsFromSettings( $settings, $entityTypeDefinitions );
450
	}
451
452
	/**
453
	 * IMPORTANT: Use only when it is not feasible to inject an instance properly.
454
	 *
455
	 * @return self
456
	 */
457
	public static function getDefaultInstance() {
458
		if ( self::$instance === null ) {
459
			self::$instance = self::newInstance();
460
		}
461
462
		return self::$instance;
463
	}
464
465
	/**
466
	 * @warning This is for use with bootstrap code in WikibaseRepo.datatypes.php only!
467
	 * Program logic should use WikibaseRepo::getDataTypeValidatorFactory() instead!
468
	 *
469
	 * @return ValidatorBuilders
470
	 */
471
	public static function getDefaultValidatorBuilders() {
472
		if ( self::$validatorBuilders === null ) {
473
			$wikibaseRepo = self::getDefaultInstance();
474
			self::$validatorBuilders = $wikibaseRepo->newValidatorBuilders();
475
		}
476
477
		return self::$validatorBuilders;
478
	}
479
480
	/**
481
	 * Returns a low level factory object for creating validators for well known data types.
482
	 *
483
	 * @warning This is for use with getDefaultValidatorBuilders() during bootstrap only!
484
	 * Program logic should use WikibaseRepo::getDataTypeValidatorFactory() instead!
485
	 *
486
	 * @return ValidatorBuilders
487
	 */
488
	public function newValidatorBuilders() {
489
		$urlSchemes = $this->settings->getSetting( 'urlSchemes' );
490
491
		return new ValidatorBuilders(
492
			$this->getEntityLookup(),
493
			$this->getEntityIdParser(),
494
			$urlSchemes,
495
			$this->getItemVocabularyBaseUri(),
496
			$this->getMonolingualTextLanguages(),
497
			$this->getCachingCommonsMediaFileNameLookup(),
498
			new MediaWikiPageNameNormalizer(),
499
			$this->settings->getSetting( 'geoShapeStorageApiEndpointUrl' ),
500
			$this->settings->getSetting( 'tabularDataStorageApiEndpointUrl' )
501
		);
502
	}
503
504
	/**
505
	 * @warning This is for use with bootstrap code in WikibaseRepo.datatypes.php only!
506
	 * Program logic should use WikibaseRepo::getSnakFormatterFactory() instead!
507
	 *
508
	 * @return WikibaseValueFormatterBuilders
509
	 */
510
	public static function getDefaultValueFormatterBuilders() {
511
		if ( self::$valueFormatterBuilders === null ) {
512
			global $wgThumbLimits;
513
			$wikibaseRepo = self::getDefaultInstance();
514
			self::$valueFormatterBuilders = $wikibaseRepo->newWikibaseValueFormatterBuilders( $wgThumbLimits );
515
		}
516
517
		return self::$valueFormatterBuilders;
518
	}
519
520
	/**
521
	 * Returns a low level factory object for creating formatters for well known data types.
522
	 *
523
	 * @warning This is for use with getDefaultValueFormatterBuilders() during bootstrap only!
524
	 * Program logic should use WikibaseRepo::getSnakFormatterFactory() instead!
525
	 *
526
	 * @param array $thumbLimits
527
	 *
528
	 * @return WikibaseValueFormatterBuilders
529
	 */
530
	private function newWikibaseValueFormatterBuilders( array $thumbLimits ) {
531
		return new WikibaseValueFormatterBuilders(
532
			new FormatterLabelDescriptionLookupFactory( $this->getTermLookup() ),
533
			$this->getLanguageNameLookup(),
534
			$this->getItemUrlParser(),
535
			$this->settings->getSetting( 'geoShapeStorageBaseUrl' ),
536
			$this->settings->getSetting( 'tabularDataStorageBaseUrl' ),
537
			$this->getTermFallbackCache(),
538
			$this->settings->getSetting( 'sharedCacheDuration' ),
539
			$this->getEntityLookup(),
540
			$this->getEntityRevisionLookup(),
541
			$this->settings->getSetting( 'entitySchemaNamespace' ),
542
			$this->getEntityExistenceChecker(),
543
			$this->getEntityTitleTextLookup(),
544
			$this->getEntityUrlLookup(),
545
			$this->getEntityRedirectChecker(),
546
			$this->getEntityTitleLookup(),
547
			$this->getKartographerEmbeddingHandler(),
548
			$this->settings->getSetting( 'useKartographerMaplinkInWikitext' ),
549
			$thumbLimits
550
		);
551
	}
552
553
	/**
554
	 * @return CachingKartographerEmbeddingHandler|null
555
	 */
556
	public function getKartographerEmbeddingHandler() {
557
		if ( $this->kartographerEmbeddingHandler === null && $this->useKartographerGlobeCoordinateFormatter() ) {
558
			$this->kartographerEmbeddingHandler = new CachingKartographerEmbeddingHandler(
559
				MediaWikiServices::getInstance()->getParserFactory()->create()
560
			);
561
		}
562
563
		return $this->kartographerEmbeddingHandler;
564
	}
565
566
	/**
567
	 * @return bool
568
	 */
569
	private function useKartographerGlobeCoordinateFormatter() {
570
		// FIXME: remove the global out of here
571
		global $wgKartographerEnableMapFrame;
572
573
		return $this->settings->getSetting( 'useKartographerGlobeCoordinateFormatter' ) &&
574
			ExtensionRegistry::getInstance()->isLoaded( 'Kartographer' ) &&
575
			isset( $wgKartographerEnableMapFrame ) &&
576
			$wgKartographerEnableMapFrame;
577
	}
578
579
	/**
580
	 * @return LanguageNameLookup
581
	 */
582
	public function getLanguageNameLookup() {
583
		return new LanguageNameLookup( $this->getUserLanguage()->getCode() );
584
	}
585
586
	/**
587
	 * @warning This is for use with bootstrap code in WikibaseRepo.datatypes.php only!
588
	 * Program logic should use WikibaseRepo::getSnakFormatterFactory() instead!
589
	 *
590
	 * @return WikibaseSnakFormatterBuilders
591
	 */
592
	public static function getDefaultSnakFormatterBuilders() {
593
		if ( self::$snakFormatterBuilders === null ) {
594
			self::$snakFormatterBuilders = self::getDefaultInstance()->newWikibaseSnakFormatterBuilders(
595
				self::getDefaultValueFormatterBuilders()
596
			);
597
		}
598
599
		return self::$snakFormatterBuilders;
600
	}
601
602
	/**
603
	 * Returns a low level factory object for creating formatters for well known data types.
604
	 *
605
	 * @warning This is for use with getDefaultValueFormatterBuilders() during bootstrap only!
606
	 * Program logic should use WikibaseRepo::getSnakFormatterFactory() instead!
607
	 *
608
	 * @param WikibaseValueFormatterBuilders $valueFormatterBuilders
609
	 *
610
	 * @return WikibaseSnakFormatterBuilders
611
	 */
612
	private function newWikibaseSnakFormatterBuilders( WikibaseValueFormatterBuilders $valueFormatterBuilders ) {
613
		return new WikibaseSnakFormatterBuilders(
614
			$valueFormatterBuilders,
615
			$this->getStore()->getPropertyInfoLookup(),
616
			$this->getPropertyDataTypeLookup(),
617
			$this->getDataTypeFactory()
618
		);
619
	}
620
621
	public function __construct(
622
		SettingsArray $settings,
623
		DataTypeDefinitions $dataTypeDefinitions,
624
		EntityTypeDefinitions $entityTypeDefinitions,
625
		EntitySourceDefinitions $entitySourceDefinitions
626
	) {
627
		$this->settings = $settings;
628
		$this->dataTypeDefinitions = $dataTypeDefinitions;
629
		$this->entityTypeDefinitions = $entityTypeDefinitions;
630
		$this->entitySourceDefinitions = $entitySourceDefinitions;
631
	}
632
633
	/**
634
	 * @throws MWException when called to early
635
	 * @return Language
636
	 */
637
	private function getContentLanguage() {
638
		/**
639
		 * Before this constant is defined, custom config may not have been taken into account.
640
		 * So try not to allow code to use a language before that point.
641
		 * This code was explicitly mentioning the SetupAfterCache hook.
642
		 * With services, that hook won't be a problem anymore.
643
		 * So this check may well be unnecessary (but better safe than sorry).
644
		 */
645
		if ( !defined( 'MW_SERVICE_BOOTSTRAP_COMPLETE' ) ) {
646
			throw new MWException( 'Premature access to MediaWiki ContentLanguage!' );
647
		}
648
649
		return MediaWikiServices::getInstance()->getContentLanguage();
650
	}
651
652
	/**
653
	 * @throws MWException when called to early
654
	 * @return Language
655
	 */
656
	public function getUserLanguage() {
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 WikibaseRepo 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
	/**
673
	 * @return DataTypeFactory
674
	 */
675
	public function getDataTypeFactory() {
676
		if ( $this->dataTypeFactory === null ) {
677
			$this->dataTypeFactory = new DataTypeFactory( $this->dataTypeDefinitions->getValueTypes() );
678
		}
679
680
		return $this->dataTypeFactory;
681
	}
682
683
	private static function getDefaultDataTypes() {
684
		$baseDataTypes = require __DIR__ . '/../../lib/WikibaseLib.datatypes.php';
685
		$repoDataTypes = require __DIR__ . '/../WikibaseRepo.datatypes.php';
686
687
		return array_merge_recursive( $baseDataTypes, $repoDataTypes );
688
	}
689
690
	/**
691
	 * @return array[]
692
	 */
693
	private static function getDefaultEntityTypes() {
694
		$baseEntityTypes = require __DIR__ . '/../../lib/WikibaseLib.entitytypes.php';
695
		$repoEntityTypes = require __DIR__ . '/../WikibaseRepo.entitytypes.php';
696
697
		return array_merge_recursive( $baseEntityTypes, $repoEntityTypes );
698
	}
699
700
	/**
701
	 * @return ValueParserFactory
702
	 */
703
	public function getValueParserFactory() {
704
		if ( $this->valueParserFactory === null ) {
705
			$callbacks = $this->dataTypeDefinitions->getParserFactoryCallbacks();
706
707
			// For backwards-compatibility, also register parsers under legacy names,
708
			// for use with the deprecated 'parser' parameter of the wbparsevalue API module.
709
			$prefixedCallbacks = $this->dataTypeDefinitions->getParserFactoryCallbacks(
710
				DataTypeDefinitions::PREFIXED_MODE
711
			);
712
			if ( isset( $prefixedCallbacks['VT:wikibase-entityid'] ) ) {
713
				$callbacks['wikibase-entityid'] = $prefixedCallbacks['VT:wikibase-entityid'];
714
			}
715
			if ( isset( $prefixedCallbacks['VT:globecoordinate'] ) ) {
716
				$callbacks['globecoordinate'] = $prefixedCallbacks['VT:globecoordinate'];
717
			}
718
			// 'null' is not a datatype. Kept for backwards compatibility.
719
			$callbacks['null'] = function() {
720
				return new NullParser();
721
			};
722
723
			$this->valueParserFactory = new ValueParserFactory( $callbacks );
724
		}
725
726
		return $this->valueParserFactory;
727
	}
728
729
	/**
730
	 * @return DataValueFactory
731
	 */
732
	public function getDataValueFactory() {
733
		return new DataValueFactory( $this->getDataValueDeserializer() );
734
	}
735
736
	/**
737
	 * @return EntityContentFactory
738
	 */
739
	public function getEntityContentFactory() {
740
		return new EntityContentFactory(
741
			$this->getContentModelMappings(),
742
			$this->entityTypeDefinitions->get( EntityTypeDefinitions::CONTENT_HANDLER_FACTORY_CALLBACK ),
743
			$this->entitySourceDefinitions,
744
			$this->getLocalEntitySource(),
745
			MediaWikiServices::getInstance()->getInterwikiLookup()
746
		);
747
	}
748
749
	public function getEntityChangeFactory(): EntityChangeFactory {
750
		//TODO: take this from a setting or registry.
751
		$changeClasses = [
752
			Item::ENTITY_TYPE => RepoItemChange::class,
753
			// Other types of entities will use EntityChange
754
		];
755
756
		return new EntityChangeFactory(
757
			$this->getEntityDiffer(),
758
			$this->getEntityIdParser(),
759
			$changeClasses,
760
			RepoEntityChange::class,
761
			$this->getLogger()
762
		);
763
	}
764
765
	/**
766
	 * @return EntityDiffer
767
	 */
768
	public function getEntityDiffer() {
769
		$entityDiffer = new EntityDiffer();
770
		foreach ( $this->entityTypeDefinitions->get( EntityTypeDefinitions::ENTITY_DIFFER_STRATEGY_BUILDER ) as $builder ) {
771
			$entityDiffer->registerEntityDifferStrategy( call_user_func( $builder ) );
772
		}
773
		return $entityDiffer;
774
	}
775
776
	/**
777
	 * @return EntityPatcher
778
	 */
779
	public function getEntityPatcher() {
780
		$entityPatcher = new EntityPatcher();
781
		foreach ( $this->entityTypeDefinitions->get( EntityTypeDefinitions::ENTITY_PATCHER_STRATEGY_BUILDER ) as $builder ) {
782
			$entityPatcher->registerEntityPatcherStrategy( call_user_func( $builder ) );
783
		}
784
		return $entityPatcher;
785
	}
786
787
	/**
788
	 * @return EntityStoreWatcher
789
	 */
790
	public function getEntityStoreWatcher() {
791
		return $this->getStore()->getEntityStoreWatcher();
792
	}
793
794
	/**
795
	 * @return EntityTitleStoreLookup
796
	 */
797
	public function getEntityTitleLookup() {
798
		return new TypeDispatchingEntityTitleStoreLookup(
799
			$this->entityTypeDefinitions->get( EntityTypeDefinitions::ENTITY_TITLE_STORE_LOOKUP_FACTORY_CALLBACK ),
800
			$this->getEntityContentFactory()
801
		);
802
	}
803
804
	public function getEntityTitleTextLookup(): EntityTitleTextLookup {
805
		return new TypeDispatchingTitleTextLookup(
806
			$this->entityTypeDefinitions->get( EntityTypeDefinitions::TITLE_TEXT_LOOKUP_CALLBACK ),
807
			new TitleLookupBasedEntityTitleTextLookup( $this->getEntityTitleLookup() )
808
		);
809
	}
810
811
	public function getEntityUrlLookup(): EntityUrlLookup {
812
		return new TypeDispatchingUrlLookup(
813
			$this->entityTypeDefinitions->get( EntityTypeDefinitions::URL_LOOKUP_CALLBACK ),
814
			new TitleLookupBasedEntityUrlLookup( $this->getEntityTitleLookup() )
815
		);
816
	}
817
818
	public function getEntityArticleIdLookup(): EntityArticleIdLookup {
819
		return new TypeDispatchingArticleIdLookup(
820
			$this->entityTypeDefinitions->get( EntityTypeDefinitions::ARTICLE_ID_LOOKUP_CALLBACK ),
821
			new TitleLookupBasedEntityArticleIdLookup( $this->getEntityTitleLookup() )
822
		);
823
	}
824
825
	public function getEntityExistenceChecker(): EntityExistenceChecker {
826
		return new TypeDispatchingExistenceChecker(
827
			$this->entityTypeDefinitions->get( EntityTypeDefinitions::EXISTENCE_CHECKER_CALLBACK ),
828
			new TitleLookupBasedEntityExistenceChecker( $this->getEntityTitleLookup() )
829
		);
830
	}
831
832
	public function getEntityRedirectChecker(): EntityRedirectChecker {
833
		return new TypeDispatchingRedirectChecker(
834
			$this->entityTypeDefinitions->get( EntityTypeDefinitions::REDIRECT_CHECKER_CALLBACK ),
835
			new TitleLookupBasedEntityRedirectChecker( $this->getEntityTitleLookup() )
836
		);
837
	}
838
839
	/**
840
	 * @return EntityIdLookup
841
	 */
842
	public function getEntityIdLookup() {
843
		return $this->getEntityContentFactory();
844
	}
845
846
	public function getLocalRepoWikiPageMetaDataAccessor() : WikiPageEntityMetaDataAccessor {
847
		$entityNamespaceLookup = $this->getEntityNamespaceLookup();
848
		$repoName = ''; // Empty string here means this only works for the local repo
849
		$dbName = false; // false means the local database
850
		return new PrefetchingWikiPageEntityMetaDataAccessor(
851
			new TypeDispatchingWikiPageEntityMetaDataAccessor(
852
				$this->entityTypeDefinitions->get( EntityTypeDefinitions::ENTITY_METADATA_ACCESSOR_CALLBACK ),
853
				new WikiPageEntityMetaDataLookup(
854
					$entityNamespaceLookup,
855
					new EntityIdLocalPartPageTableEntityQuery(
856
						$entityNamespaceLookup,
857
						MediaWikiServices::getInstance()->getSlotRoleStore()
858
					),
859
					$this->getLocalEntitySource()
860
				),
861
				$dbName,
862
				$repoName
863
			),
864
			$this->getLogger()
865
		);
866
	}
867
868
	/**
869
	 * @see Store::getEntityRevisionLookup
870
	 *
871
	 * @param string $cache One of Store::LOOKUP_CACHING_*
872
	 *        Store::LOOKUP_CACHING_DISABLED to get an uncached direct lookup
873
	 *        Store::LOOKUP_CACHING_RETRIEVE_ONLY to get a lookup which reads from the cache, but doesn't store retrieved entities
874
	 *        Store::LOOKUP_CACHING_ENABLED to get a caching lookup (default)
875
	 *
876
	 * @return EntityRevisionLookup
877
	 */
878
	public function getEntityRevisionLookup( $cache = Store::LOOKUP_CACHING_ENABLED ) {
879
		return $this->getStore()->getEntityRevisionLookup( $cache );
880
	}
881
882
	/**
883
	 * @return callable[]
884
	 */
885
	public function getEntityRevisionLookupFactoryCallbacks() {
886
		return $this->entityTypeDefinitions->get( EntityTypeDefinitions::ENTITY_REVISION_LOOKUP_FACTORY_CALLBACK );
887
	}
888
889
	/**
890
	 * @param User $user
891
	 * @param IContextSource $context
892
	 *
893
	 * @return ItemRedirectCreationInteractor
894
	 */
895
	public function newItemRedirectCreationInteractor( User $user, IContextSource $context ) {
896
		return new ItemRedirectCreationInteractor(
897
			$this->getEntityRevisionLookup( Store::LOOKUP_CACHING_DISABLED ),
898
			$this->getEntityStore(),
899
			$this->getEntityPermissionChecker(),
900
			$this->getSummaryFormatter(),
901
			$user,
902
			$this->newEditFilterHookRunner( $context ),
903
			$this->getStore()->getEntityRedirectLookup(),
904
			$this->getEntityTitleLookup()
905
		);
906
	}
907
908
	/**
909
	 * @param IContextSource $context
910
	 *
911
	 * @return EditFilterHookRunner
912
	 */
913
	private function newEditFilterHookRunner( IContextSource $context ) {
914
		return new MediawikiEditFilterHookRunner(
915
			$this->getEntityNamespaceLookup(),
916
			$this->getEntityTitleLookup(),
917
			$this->getEntityContentFactory(),
918
			$context
919
		);
920
	}
921
922
	/**
923
	 * @param string $displayLanguageCode
924
	 *
925
	 * @return MatchingTermsLookupSearchInteractor
926
	 */
927
	public function newTermSearchInteractor( $displayLanguageCode ) {
928
		return $this->getWikibaseServices()->getTermSearchInteractorFactory()->newInteractor(
929
			$displayLanguageCode
930
		);
931
	}
932
933
	/**
934
	 * @return EntityStore
935
	 */
936
	public function getEntityStore() {
937
		return $this->getStore()->getEntityStore();
938
	}
939
940
	/**
941
	 * @return callable[]
942
	 */
943
	public function getEntityStoreFactoryCallbacks() {
944
		return $this->entityTypeDefinitions->get( EntityTypeDefinitions::ENTITY_STORE_FACTORY_CALLBACK );
945
	}
946
947
	public function getPropertyDataTypeLookup(): PropertyDataTypeLookup {
948
		if ( $this->propertyDataTypeLookup === null ) {
949
			$this->propertyDataTypeLookup = $this->newPropertyDataTypeLookup();
950
		}
951
952
		return $this->propertyDataTypeLookup;
953
	}
954
955
	public function newPropertyDataTypeLookup(): PropertyDataTypeLookup {
956
		if ( $this->inFederatedPropertyMode() ) {
957
			return $this->newFederatedPropertiesServiceFactory()->newApiPropertyDataTypeLookup();
958
		}
959
960
		return $this->newPropertyDataTypeLookupForLocalProperties();
961
	}
962
963
	private function newPropertyDataTypeLookupForLocalProperties(): PropertyDataTypeLookup {
964
		$infoLookup = $this->getStore()->getPropertyInfoLookup();
965
		$retrievingLookup = new EntityRetrievingDataTypeLookup( $this->getEntityLookup() );
966
		return new PropertyInfoDataTypeLookup(
967
			$infoLookup,
968
			$this->getLogger(),
969
			$retrievingLookup
970
		);
971
	}
972
973
	/**
974
	 * @return StringNormalizer
975
	 */
976
	public function getStringNormalizer() {
977
		return $this->getWikibaseServices()->getStringNormalizer();
978
	}
979
980
	/**
981
	 * @see Store::getEntityLookup
982
	 *
983
	 * @param string $cache One of Store::LOOKUP_CACHING_*
984
	 *        Store::LOOKUP_CACHING_DISABLED to get an uncached direct lookup
985
	 *        Store::LOOKUP_CACHING_RETRIEVE_ONLY to get a lookup which reads from the cache, but doesn't store retrieved entities
986
	 *        Store::LOOKUP_CACHING_ENABLED to get a caching lookup (default)
987
	 *
988
	 * @param string $lookupMode One of the EntityRevisionLookup lookup mode constants
989
	 * TODO this should perhaps not refer to EntityRevisionLookup
990
	 *
991
	 * @return EntityLookup
992
	 */
993
	public function getEntityLookup( $cache = Store::LOOKUP_CACHING_ENABLED, $lookupMode = LookupConstants::LATEST_FROM_REPLICA ) {
994
		return $this->getStore()->getEntityLookup( $cache, $lookupMode );
995
	}
996
997
	public function getPropertyLookup( $cacheMode = Store::LOOKUP_CACHING_ENABLED ): PropertyLookup {
998
		return new LegacyAdapterPropertyLookup( $this->getEntityLookup( $cacheMode ) );
999
	}
1000
1001
	public function getItemLookup( $cacheMode = Store::LOOKUP_CACHING_ENABLED ): ItemLookup {
1002
		return new LegacyAdapterItemLookup( $this->getEntityLookup( $cacheMode ) );
1003
	}
1004
1005
	/**
1006
	 * @return SnakFactory
1007
	 */
1008
	public function getSnakFactory() {
1009
		if ( $this->snakFactory === null ) {
1010
			$this->snakFactory = new SnakFactory(
1011
				$this->getPropertyDataTypeLookup(),
1012
				$this->getDataTypeFactory(),
1013
				$this->getDataValueFactory()
1014
			);
1015
		}
1016
1017
		return $this->snakFactory;
1018
	}
1019
1020
	/**
1021
	 * @return EntityIdParser
1022
	 */
1023
	public function getEntityIdParser() {
1024
		if ( $this->entityIdParser === null ) {
1025
			$this->entityIdParser = new DispatchingEntityIdParser(
1026
				$this->entityTypeDefinitions->getEntityIdBuilders()
1027
			);
1028
		}
1029
1030
		return $this->entityIdParser;
1031
	}
1032
1033
	/**
1034
	 * @return EntityIdComposer
1035
	 */
1036
	public function getEntityIdComposer() {
1037
		if ( $this->entityIdComposer === null ) {
1038
			$this->entityIdComposer = new EntityIdComposer(
1039
				$this->entityTypeDefinitions->get( EntityTypeDefinitions::ENTITY_ID_COMPOSER_CALLBACK )
1040
			);
1041
		}
1042
1043
		return $this->entityIdComposer;
1044
	}
1045
1046
	/**
1047
	 * @return StatementGuidParser
1048
	 */
1049
	public function getStatementGuidParser() {
1050
		return new StatementGuidParser( $this->getEntityIdParser() );
1051
	}
1052
1053
	/**
1054
	 * @return ChangeOpFactoryProvider
1055
	 */
1056
	public function getChangeOpFactoryProvider() {
1057
		$snakValidator = new SnakValidator(
1058
			$this->getPropertyDataTypeLookup(),
1059
			$this->getDataTypeFactory(),
1060
			$this->getDataTypeValidatorFactory()
1061
		);
1062
1063
		return new ChangeOpFactoryProvider(
1064
			$this->getEntityConstraintProvider(),
1065
			new GuidGenerator(),
1066
			$this->getStatementGuidValidator(),
1067
			$this->getStatementGuidParser(),
1068
			$snakValidator,
1069
			$this->getTermValidatorFactory(),
1070
			$this->getSiteLookup(),
1071
			array_keys( $this->settings->getSetting( 'badgeItems' ) )
1072
		);
1073
	}
1074
1075
	public function getSiteLinkBadgeChangeOpSerializationValidator() {
1076
		return new SiteLinkBadgeChangeOpSerializationValidator(
1077
			$this->getEntityTitleLookup(),
1078
			array_keys( $this->settings->getSetting( 'badgeItems' ) )
1079
		);
1080
	}
1081
1082
	/**
1083
	 * @return EntityChangeOpProvider
1084
	 */
1085
	public function getEntityChangeOpProvider() {
1086
		return new EntityChangeOpProvider( $this->entityTypeDefinitions->get( EntityTypeDefinitions::CHANGEOP_DESERIALIZER_CALLBACK ) );
1087
	}
1088
1089
	/**
1090
	 * TODO: this should be probably cached?
1091
	 *
1092
	 * @return ChangeOpDeserializerFactory
1093
	 */
1094
	public function getChangeOpDeserializerFactory() {
1095
		$changeOpFactoryProvider = $this->getChangeOpFactoryProvider();
1096
1097
		return new ChangeOpDeserializerFactory(
1098
			$changeOpFactoryProvider->getFingerprintChangeOpFactory(),
1099
			$changeOpFactoryProvider->getStatementChangeOpFactory(),
1100
			$changeOpFactoryProvider->getSiteLinkChangeOpFactory(),
1101
			new TermChangeOpSerializationValidator( $this->getTermsLanguages() ),
1102
			$this->getSiteLinkBadgeChangeOpSerializationValidator(),
1103
			$this->getExternalFormatStatementDeserializer(),
1104
			new SiteLinkTargetProvider(
1105
				$this->getSiteLookup(),
1106
				$this->settings->getSetting( 'specialSiteLinkGroups' )
1107
			),
1108
			$this->getEntityIdParser(),
1109
			$this->getStringNormalizer(),
1110
			$this->settings->getSetting( 'siteLinkGroups' )
1111
		);
1112
	}
1113
1114
	/**
1115
	 * @return LanguageFallbackChainFactory
1116
	 */
1117
	public function getLanguageFallbackChainFactory() {
1118
		return $this->getWikibaseServices()->getLanguageFallbackChainFactory();
1119
	}
1120
1121
	/**
1122
	 * @return LanguageFallbackLabelDescriptionLookupFactory
1123
	 */
1124
	public function getLanguageFallbackLabelDescriptionLookupFactory() {
1125
		return new LanguageFallbackLabelDescriptionLookupFactory(
1126
			$this->getLanguageFallbackChainFactory(),
1127
			$this->getTermLookup(),
1128
			$this->getTermBuffer()
1129
		);
1130
	}
1131
1132
	/**
1133
	 * @return StatementGuidValidator
1134
	 */
1135
	public function getStatementGuidValidator() {
1136
		if ( $this->statementGuidValidator === null ) {
1137
			$this->statementGuidValidator = new StatementGuidValidator( $this->getEntityIdParser() );
1138
		}
1139
1140
		return $this->statementGuidValidator;
1141
	}
1142
1143
	/**
1144
	 * @return SettingsArray
1145
	 */
1146
	public function getSettings() {
1147
		return $this->settings;
1148
	}
1149
1150
	public function newIdGenerator() : IdGenerator {
1151
		if ( $this->getSettings()->getSetting( 'idGenerator' ) === 'original' ) {
1152
			return new SqlIdGenerator(
1153
				MediaWikiServices::getInstance()->getDBLoadBalancer(),
1154
				$this->getSettings()->getSetting( 'reservedIds' ),
0 ignored issues
show
$this->getSettings()->getSetting('reservedIds') is of type string, but the function expects a array<integer,array<integer,integer>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1155
				$this->getSettings()->getSetting( 'idGeneratorSeparateDbConnection' )
1156
			);
1157
		}
1158
1159
		if ( $this->getSettings()->getSetting( 'idGenerator' ) === 'mysql-upsert' ) {
1160
			// We could make sure the 'upsert' generator is only being used with mysql dbs here,
1161
			// but perhaps that is an unnecessary check? People will realize when the DB query for
1162
			// ID selection fails anyway...
1163
			return new UpsertSqlIdGenerator(
1164
				MediaWikiServices::getInstance()->getDBLoadBalancer(),
1165
				$this->getSettings()->getSetting( 'reservedIds' ),
0 ignored issues
show
$this->getSettings()->getSetting('reservedIds') is of type string, but the function expects a array<integer,array<integer,integer>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1166
				$this->getSettings()->getSetting( 'idGeneratorSeparateDbConnection' )
1167
			);
1168
		}
1169
1170
		throw new InvalidArgumentException(
1171
			'idGenerator config option must be either \'original\' or \'mysql-upsert\''
1172
		);
1173
	}
1174
1175
	/**
1176
	 * @return Store
1177
	 */
1178
	public function getStore() {
1179
		if ( $this->store === null ) {
1180
			$localEntitySource = $this->getLocalEntitySource();
1181
			// TODO: the idea of local entity source seems not really suitable here. Store should probably
1182
			// get source definitions and pass the right source/sources to services it creates accordingly
1183
			// (as long as what it creates should not migrate to *SourceServices in the first place)
1184
1185
			$this->store = new SqlStore(
1186
				$this->getEntityChangeFactory(),
1187
				$this->getEntityIdParser(),
1188
				$this->getEntityIdComposer(),
1189
				$this->getEntityIdLookup(),
1190
				$this->getEntityTitleLookup(),
1191
				$this->getEntityNamespaceLookup(),
1192
				$this->newIdGenerator(),
1193
				$this->getWikibaseServices(),
1194
				$localEntitySource
1195
			);
1196
		}
1197
1198
		return $this->store;
1199
	}
1200
1201
	/**
1202
	 * @return EntitySource The entity source of the local repository
1203
	 */
1204
	public function getLocalEntitySource() : EntitySource {
1205
		$localEntitySourceName = $this->settings->getSetting( 'localEntitySourceName' );
1206
		$sources = $this->entitySourceDefinitions->getSources();
1207
		foreach ( $sources as $source ) {
1208
			if ( $source->getSourceName() === $localEntitySourceName ) {
1209
				return $source;
1210
			}
1211
		}
1212
1213
		throw new LogicException( 'No source configured: ' . $localEntitySourceName );
1214
	}
1215
1216
	/**
1217
	 * Returns a OutputFormatSnakFormatterFactory the provides SnakFormatters
1218
	 * for different output formats.
1219
	 *
1220
	 * @return OutputFormatSnakFormatterFactory
1221
	 */
1222
	public function getSnakFormatterFactory() {
1223
		if ( $this->snakFormatterFactory === null ) {
1224
			$this->snakFormatterFactory = new OutputFormatSnakFormatterFactory(
1225
				$this->dataTypeDefinitions->getSnakFormatterFactoryCallbacks(),
1226
				$this->getValueFormatterFactory(),
1227
				$this->getPropertyDataTypeLookup(),
1228
				$this->getDataTypeFactory()
1229
			);
1230
		}
1231
1232
		return $this->snakFormatterFactory;
1233
	}
1234
1235
	/**
1236
	 * @return TermBuffer|AliasTermBuffer
1237
	 */
1238
	public function getTermBuffer() {
1239
		return $this->getPrefetchingTermLookup();
1240
	}
1241
1242
	/**
1243
	 * @return TermLookup
1244
	 */
1245
	public function getTermLookup() {
1246
		return $this->getPrefetchingTermLookup();
1247
	}
1248
1249
	/**
1250
	 * @return PrefetchingTermLookup
1251
	 */
1252
	public function getPrefetchingTermLookup() {
1253
		return $this->getWikibaseServices()->getTermBuffer();
1254
	}
1255
1256
	public function getItemUrlParser(): SuffixEntityIdParser {
1257
		return new SuffixEntityIdParser(
1258
			$this->getItemVocabularyBaseUri(),
1259
			new ItemIdParser()
1260
		);
1261
	}
1262
1263
	private function getItemVocabularyBaseUri(): string {
1264
		//@todo: We currently use the local repo concept URI here. This should be configurable,
1265
		// to e.g. allow 3rd parties to use Wikidata as their vocabulary repo.
1266
		return $this->getEntitySourceDefinitions()
1267
			->getSourceForEntityType( Item::ENTITY_TYPE )
1268
			->getConceptBaseUri();
1269
	}
1270
1271
	/**
1272
	 * Returns a OutputFormatValueFormatterFactory the provides ValueFormatters
1273
	 * for different output formats.
1274
	 *
1275
	 * @return OutputFormatValueFormatterFactory
1276
	 */
1277
	public function getValueFormatterFactory() {
1278
		if ( $this->valueFormatterFactory === null ) {
1279
			$this->valueFormatterFactory = $this->newValueFormatterFactory();
1280
		}
1281
1282
		return $this->valueFormatterFactory;
1283
	}
1284
1285
	/**
1286
	 * @return OutputFormatValueFormatterFactory
1287
	 */
1288
	private function newValueFormatterFactory() {
1289
		return new OutputFormatValueFormatterFactory(
1290
			$this->dataTypeDefinitions->getFormatterFactoryCallbacks( DataTypeDefinitions::PREFIXED_MODE ),
1291
			$this->getContentLanguage(),
1292
			new LanguageFallbackChainFactory()
1293
		);
1294
	}
1295
1296
	/**
1297
	 * @return ValueSnakRdfBuilderFactory
1298
	 */
1299
	public function getValueSnakRdfBuilderFactory() {
1300
		if ( $this->valueSnakRdfBuilderFactory === null ) {
1301
			$this->valueSnakRdfBuilderFactory = new ValueSnakRdfBuilderFactory(
1302
				$this->dataTypeDefinitions->getRdfBuilderFactoryCallbacks( DataTypeDefinitions::PREFIXED_MODE )
1303
			);
1304
		}
1305
1306
		return $this->valueSnakRdfBuilderFactory;
1307
	}
1308
1309
	/**
1310
	 * @return RdfVocabulary
1311
	 */
1312
	public function getRdfVocabulary() {
1313
		global $wgDummyLanguageCodes;
1314
1315
		if ( $this->rdfVocabulary === null ) {
1316
			$languageCodes = array_merge(
1317
				$wgDummyLanguageCodes,
1318
				$this->settings->getSetting( 'canonicalLanguageCodes' )
1319
			);
1320
1321
			$localEntitySourceName = $this->getLocalEntitySource()->getSourceName();
1322
			$nodeNamespacePrefixes = $this->entitySourceDefinitions->getRdfNodeNamespacePrefixes();
1323
			$predicateNamespacePrefixes = $this->entitySourceDefinitions->getRdfPredicateNamespacePrefixes();
1324
1325
			$this->rdfVocabulary = new RdfVocabulary(
1326
				$this->entitySourceDefinitions->getConceptBaseUris(),
1327
				$this->getCanonicalDocumentUrls(),
1328
				$this->entitySourceDefinitions,
1329
				$localEntitySourceName,
1330
				$nodeNamespacePrefixes,
1331
				$predicateNamespacePrefixes,
1332
				$languageCodes,
1333
				$this->dataTypeDefinitions->getRdfTypeUris(),
1334
				$this->settings->getSetting( 'pagePropertiesRdf' ) ?: [],
1335
				$this->getSettings()->getSetting( 'rdfDataRightsUrl' )
1336
			);
1337
		}
1338
1339
		return $this->rdfVocabulary;
1340
	}
1341
1342
	private function getCanonicalDocumentUrls() {
1343
		$urlProvider = new EntitySourceDocumentUrlProvider();
1344
1345
		return $urlProvider->getCanonicalDocumentsUrls( $this->entitySourceDefinitions );
1346
	}
1347
1348
	/**
1349
	 * @return ExceptionLocalizer
1350
	 */
1351
	public function getExceptionLocalizer() {
1352
		if ( $this->exceptionLocalizer === null ) {
1353
			$formatter = $this->getMessageParameterFormatter();
1354
			$localizers = $this->getExceptionLocalizers( $formatter );
1355
1356
			$this->exceptionLocalizer = new DispatchingExceptionLocalizer( $localizers );
1357
		}
1358
1359
		return $this->exceptionLocalizer;
1360
	}
1361
1362
	/**
1363
	 * @param ValueFormatter $formatter
1364
	 *
1365
	 * @return ExceptionLocalizer[]
1366
	 */
1367
	private function getExceptionLocalizers( ValueFormatter $formatter ) {
1368
		return [
1369
			'MessageException' => new MessageExceptionLocalizer(),
1370
			'ParseException' => new ParseExceptionLocalizer(),
1371
			'ChangeOpValidationException' => new ChangeOpValidationExceptionLocalizer( $formatter ),
1372
			'ChangeOpDeserializationException' => new ChangeOpDeserializationExceptionLocalizer(),
1373
			'ChangeOpApplyException' => new ChangeOpApplyExceptionLocalizer(),
1374
			'Exception' => new GenericExceptionLocalizer()
1375
		];
1376
	}
1377
1378
	/**
1379
	 * @return SummaryFormatter
1380
	 */
1381
	public function getSummaryFormatter() {
1382
		if ( $this->summaryFormatter === null ) {
1383
			$this->summaryFormatter = $this->newSummaryFormatter();
1384
		}
1385
1386
		return $this->summaryFormatter;
1387
	}
1388
1389
	/**
1390
	 * @return SummaryFormatter
1391
	 */
1392
	private function newSummaryFormatter() {
1393
		// This needs to use an EntityIdPlainLinkFormatter as we want to mangle
1394
		// the links created in HtmlPageLinkRendererEndHookHandler afterwards (the links must not
1395
		// contain a display text: [[Item:Q1]] is fine but [[Item:Q1|Q1]] isn't).
1396
		$idFormatter = new EntityIdPlainLinkFormatter( $this->getEntityTitleLookup() );
1397
1398
		// Create a new ValueFormatterFactory, and override the formatter for entity IDs.
1399
		$valueFormatterFactory = $this->newValueFormatterFactory();
1400
1401
		// Iterate through all defined entity types
1402
		foreach ( $this->entityTypeDefinitions->getEntityTypes() as $entityType ) {
1403
			$valueFormatterFactory->setFormatterFactoryCallback(
1404
				"PT:wikibase-$entityType",
1405
				function ( $format, FormatterOptions $options ) use ( $idFormatter ) {
1406
					if ( $format === SnakFormatter::FORMAT_PLAIN ) {
1407
						return new EntityIdValueFormatter( $idFormatter );
1408
					} else {
1409
						return null;
1410
					}
1411
				}
1412
			);
1413
		}
1414
1415
		// Create a new SnakFormatterFactory based on the specialized ValueFormatterFactory.
1416
		$snakFormatterFactory = new OutputFormatSnakFormatterFactory(
1417
			[], // XXX: do we want $this->dataTypeDefinitions->getSnakFormatterFactoryCallbacks()
1418
			$valueFormatterFactory,
1419
			$this->getPropertyDataTypeLookup(),
1420
			$this->getDataTypeFactory()
1421
		);
1422
1423
		$options = new FormatterOptions();
1424
		$snakFormatter = $snakFormatterFactory->getSnakFormatter(
1425
			SnakFormatter::FORMAT_PLAIN,
1426
			$options
1427
		);
1428
		$valueFormatter = $valueFormatterFactory->getValueFormatter(
1429
			SnakFormatter::FORMAT_PLAIN,
1430
			$options
1431
		);
1432
1433
		$formatter = new SummaryFormatter(
1434
			$idFormatter,
1435
			$valueFormatter,
1436
			$snakFormatter,
1437
			$this->getContentLanguage(),
1438
			$this->getEntityIdParser()
1439
		);
1440
1441
		return $formatter;
1442
	}
1443
1444
	/**
1445
	 * @return EntityPermissionChecker
1446
	 */
1447
	public function getEntityPermissionChecker() {
1448
		global $wgAvailableRights;
1449
1450
		return new WikiPageEntityStorePermissionChecker(
1451
			$this->getEntityNamespaceLookup(),
1452
			$this->getEntityTitleLookup(),
1453
			MediaWikiServices::getInstance()->getPermissionManager(),
1454
			$wgAvailableRights
1455
		);
1456
	}
1457
1458
	/**
1459
	 * @return TermValidatorFactory
1460
	 */
1461
	public function getTermValidatorFactory() {
1462
		// Use the old deprecated setting if it exists
1463
		if ( $this->settings->hasSetting( 'multilang-limits' ) ) {
1464
			$constraints = $this->settings->getSetting( 'multilang-limits' );
1465
		} else {
1466
			$constraints = $this->settings->getSetting( 'string-limits' )['multilang'];
1467
		}
1468
1469
		$maxLength = $constraints['length'];
1470
1471
		$languages = $this->getTermsLanguages()->getLanguages();
1472
1473
		return new TermValidatorFactory(
1474
			$maxLength,
1475
			$languages,
1476
			$this->getEntityIdParser(),
1477
			$this->getTermsCollisionDetectorFactory(),
1478
			$this->getTermLookup()
1479
		);
1480
	}
1481
1482
	public function getTermsCollisionDetectorFactory() {
1483
		$loadBalancerFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
1484
		$loadBalancer = $loadBalancerFactory->getMainLB();
1485
		$typeIdsStore = new DatabaseTypeIdsStore(
1486
			$loadBalancer,
1487
			MediaWikiServices::getInstance()->getMainWANObjectCache()
1488
		);
1489
1490
		return new TermsCollisionDetectorFactory(
1491
			$loadBalancer,
1492
			$typeIdsStore
1493
		);
1494
	}
1495
1496
	public function getPropertyTermsCollisionDetector() {
1497
		return $this->getTermsCollisionDetectorFactory()->getTermsCollisionDetector( Property::ENTITY_TYPE );
1498
	}
1499
1500
	public function getItemTermsCollisionDetector() {
1501
		return $this->getTermsCollisionDetectorFactory()->getTermsCollisionDetector( Item::ENTITY_TYPE );
1502
	}
1503
1504
	/**
1505
	 * @return EntityConstraintProvider
1506
	 */
1507
	public function getEntityConstraintProvider() {
1508
		return new EntityConstraintProvider(
1509
			$this->getStore()->getSiteLinkConflictLookup()
1510
		);
1511
	}
1512
1513
	/**
1514
	 * @return ValidatorErrorLocalizer
1515
	 */
1516
	public function getValidatorErrorLocalizer() {
1517
		return new ValidatorErrorLocalizer( $this->getMessageParameterFormatter() );
1518
	}
1519
1520
	/**
1521
	 * @return SiteLookup
1522
	 */
1523
	public function getSiteLookup() {
1524
		return MediaWikiServices::getInstance()->getSiteLookup();
1525
	}
1526
1527
	/**
1528
	 * Returns a ValueFormatter suitable for converting message parameters to wikitext.
1529
	 * The formatter is most likely implemented to dispatch to different formatters internally,
1530
	 * based on the type of the parameter.
1531
	 *
1532
	 * @return ValueFormatter
1533
	 */
1534
	private function getMessageParameterFormatter() {
1535
		$formatterOptions = new FormatterOptions();
1536
		$valueFormatterFactory = $this->getValueFormatterFactory();
1537
1538
		return new MessageParameterFormatter(
1539
			$valueFormatterFactory->getValueFormatter( SnakFormatter::FORMAT_WIKI, $formatterOptions ),
1540
			new EntityIdLinkFormatter( $this->getEntityTitleLookup() ),
1541
			$this->getSiteLookup(),
1542
			$this->getUserLanguage()
1543
		);
1544
	}
1545
1546
	/**
1547
	 * @return ChangeNotifier
1548
	 */
1549
	public function getChangeNotifier() {
1550
		$transmitters = [
1551
			new HookChangeTransmitter( 'WikibaseChangeNotification' ),
1552
		];
1553
1554
		if ( $this->settings->getSetting( 'useChangesTable' ) ) {
1555
			$transmitters[] = new DatabaseChangeTransmitter( $this->getStore()->getChangeStore() );
1556
		}
1557
1558
		return new ChangeNotifier(
1559
			$this->getEntityChangeFactory(),
1560
			$transmitters,
1561
			CentralIdLookup::factoryNonLocal()
1562
		);
1563
	}
1564
1565
	/**
1566
	 * Get the mapping of entity types => content models
1567
	 *
1568
	 * @return array
1569
	 */
1570
	public function getContentModelMappings() {
1571
		$map = $this->entityTypeDefinitions->get( EntityTypeDefinitions::CONTENT_MODEL_ID );
1572
1573
		Hooks::run( 'WikibaseContentModelMapping', [ &$map ] );
1574
1575
		return $map;
1576
	}
1577
1578
	/**
1579
	 * @return EntityFactory
1580
	 */
1581
	public function getEntityFactory() {
1582
		$instantiators = $this->entityTypeDefinitions->get( EntityTypeDefinitions::ENTITY_FACTORY_CALLBACK );
1583
1584
		return new EntityFactory( $instantiators );
1585
	}
1586
1587
	/**
1588
	 * @return string[] List of entity type identifiers (typically "item" and "property")
1589
	 *  that are configured in WikibaseRepo.entitytypes.php and enabled via the
1590
	 *  $wgWBRepoSettings['entityNamespaces'] setting.
1591
	 *  This list will also include any sub entity types of entity types defined in $wgWBRepoSettings['entityNamespaces'].
1592
	 *  Optionally the list also contains entity types from the configured foreign repositories.
1593
	 */
1594
	public function getEnabledEntityTypes() {
1595
		$types = array_keys( $this->entitySourceDefinitions->getEntityTypeToSourceMapping() );
1596
		$subEntityTypes = $this->entityTypeDefinitions->get( EntityTypeDefinitions::SUB_ENTITY_TYPES );
1597
1598
		return array_reduce(
1599
			$types,
1600
			function ( $carry, $x ) use ( $subEntityTypes ) {
1601
				$carry[] = $x;
1602
				if ( array_key_exists( $x, $subEntityTypes ) ) {
1603
					$carry = array_merge( $carry, $subEntityTypes[$x] );
1604
				}
1605
				return $carry;
1606
			},
1607
			[]
1608
		);
1609
	}
1610
1611
	/**
1612
	 * @return string[] List of entity type identifiers (typically "item" and "property")
1613
	 *  that are configured in WikibaseRepo.entitytypes.php and enabled via the
1614
	 *  $wgWBRepoSettings['entityNamespaces'] setting.
1615
	 *  This list will also include any sub entity types of entity types defined in $wgWBRepoSettings['entityNamespaces'].
1616
	 */
1617
	public function getLocalEntityTypes() {
1618
		$localSource = $this->getLocalEntitySource();
1619
		$types = $localSource->getEntityTypes();
1620
		$subEntityTypes = $this->entityTypeDefinitions->get( EntityTypeDefinitions::SUB_ENTITY_TYPES );
1621
1622
		return array_reduce(
1623
			$types,
1624
			function ( $carry, $x ) use ( $subEntityTypes ) {
1625
				$carry[] = $x;
1626
				if ( array_key_exists( $x, $subEntityTypes ) ) {
1627
					$carry = array_merge( $carry, $subEntityTypes[$x] );
1628
				}
1629
				return $carry;
1630
			},
1631
			[]
1632
		);
1633
	}
1634
1635
	/**
1636
	 * @return EntityContentDataCodec
1637
	 */
1638
	public function getEntityContentDataCodec() {
1639
		return new EntityContentDataCodec(
1640
			$this->getEntityIdParser(),
1641
			$this->getStorageEntitySerializer(),
1642
			$this->getInternalFormatEntityDeserializer(),
1643
			$this->settings->getSetting( 'maxSerializedEntitySize' ) * 1024
1644
		);
1645
	}
1646
1647
	/**
1648
	 * @return DeserializerFactory A factory with knowledge about items, properties, and the
1649
	 *  elements they are made of, but no other entity types.
1650
	 */
1651
	public function getBaseDataModelDeserializerFactory() {
1652
		return new DeserializerFactory(
1653
			$this->getDataValueDeserializer(),
1654
			$this->getEntityIdParser()
1655
		);
1656
	}
1657
1658
	/**
1659
	 * @return InternalDeserializerFactory
1660
	 */
1661
	private function getInternalFormatDeserializerFactory() {
1662
		return new InternalDeserializerFactory(
1663
			$this->getDataValueDeserializer(),
1664
			$this->getEntityIdParser(),
1665
			$this->getAllTypesEntityDeserializer()
1666
		);
1667
	}
1668
1669
	/**
1670
	 * @return SerializerFactory A factory with knowledge about items, properties, and the elements
1671
	 *  they are made of, but no other entity types. Returns serializers that generate the full
1672
	 *  (expanded) serialization.
1673
	 */
1674
	public function getBaseDataModelSerializerFactory() {
1675
		return $this->getWikibaseServices()->getBaseDataModelSerializerFactory();
1676
	}
1677
1678
	/**
1679
	 * @return SerializerFactory A factory with knowledge about items, properties, and the elements
1680
	 *  they are made of, but no other entity types. Returns serializers that generate the most
1681
	 *  compact serialization.
1682
	 */
1683
	public function getCompactBaseDataModelSerializerFactory() {
1684
		return $this->getWikibaseServices()->getCompactBaseDataModelSerializerFactory();
1685
	}
1686
1687
	/**
1688
	 * Returns a deserializer to deserialize entities in current serialization only.
1689
	 *
1690
	 * @return DispatchableDeserializer
1691
	 */
1692
	private function getAllTypesEntityDeserializer() {
1693
		if ( $this->entityDeserializer === null ) {
1694
			$deserializerFactoryCallbacks = $this->entityTypeDefinitions->get( EntityTypeDefinitions::DESERIALIZER_FACTORY_CALLBACK );
1695
			$baseDeserializerFactory = $this->getBaseDataModelDeserializerFactory();
1696
			$deserializers = [];
1697
1698
			foreach ( $deserializerFactoryCallbacks as $callback ) {
1699
				$deserializers[] = call_user_func( $callback, $baseDeserializerFactory );
1700
			}
1701
1702
			$this->entityDeserializer = new DispatchingDeserializer( $deserializers );
1703
		}
1704
1705
		return $this->entityDeserializer;
1706
	}
1707
1708
	/**
1709
	 * Returns a deserializer to deserialize entities in both current and legacy serialization.
1710
	 *
1711
	 * @return Deserializer
1712
	 */
1713
	public function getInternalFormatEntityDeserializer() {
1714
		return $this->getInternalFormatDeserializerFactory()->newEntityDeserializer();
1715
	}
1716
1717
	/**
1718
	 * @return Serializer Entity serializer that generates the full (expanded) serialization.
1719
	 */
1720
	public function getAllTypesEntitySerializer() {
1721
		return $this->getWikibaseServices()->getFullEntitySerializer();
1722
	}
1723
1724
	/**
1725
	 * @return Serializer Entity serializer that generates the most compact serialization.
1726
	 */
1727
	public function getCompactEntitySerializer() {
1728
		return $this->getWikibaseServices()->getCompactEntitySerializer();
1729
	}
1730
1731
	/**
1732
	 * Returns the entity serializer that generates serialization that is used in the storage layer.
1733
	 *
1734
	 * @return Serializer
1735
	 */
1736
	public function getStorageEntitySerializer() {
1737
		return $this->getWikibaseServices()->getStorageEntitySerializer();
1738
	}
1739
1740
	/**
1741
	 * Returns a deserializer to deserialize statements in both current and legacy serialization.
1742
	 *
1743
	 * @return Deserializer
1744
	 */
1745
	public function getInternalFormatStatementDeserializer() {
1746
		return $this->getInternalFormatDeserializerFactory()->newStatementDeserializer();
1747
	}
1748
1749
	/**
1750
	 * Returns a deserializer to deserialize statements in current serialization only.
1751
	 *
1752
	 * @return Deserializer
1753
	 */
1754
	public function getExternalFormatStatementDeserializer() {
1755
		return $this->getBaseDataModelDeserializerFactory()->newStatementDeserializer();
1756
	}
1757
1758
	/**
1759
	 * @return Serializer
1760
	 */
1761
	public function getStatementSerializer() {
1762
		return $this->getBaseDataModelSerializerFactory()->newStatementSerializer();
1763
	}
1764
1765
	/**
1766
	 * @return DataValueDeserializer
1767
	 */
1768
	private function getDataValueDeserializer() {
1769
		return new DataValueDeserializer( [
1770
			'string' => StringValue::class,
1771
			'unknown' => UnknownValue::class,
1772
			'globecoordinate' => GlobeCoordinateValue::class,
1773
			'monolingualtext' => MonolingualTextValue::class,
1774
			'quantity' => QuantityValue::class,
1775
			'time' => TimeValue::class,
1776
			'wikibase-entityid' => function ( $value ) {
1777
				// TODO this should perhaps be factored out into a class
1778
				if ( isset( $value['id'] ) ) {
1779
					try {
1780
						return new EntityIdValue( $this->getEntityIdParser()->parse( $value['id'] ) );
1781
					} catch ( EntityIdParsingException $parsingException ) {
1782
						throw new InvalidArgumentException(
1783
							'Can not parse id \'' . $value['id'] . '\' to build EntityIdValue with',
1784
							0,
1785
							$parsingException
1786
						);
1787
					}
1788
				} else {
1789
					return EntityIdValue::newFromArray( $value );
1790
				}
1791
			},
1792
		] );
1793
	}
1794
1795
	public function newItemHandler(): ItemHandler {
1796
		$codec = $this->getEntityContentDataCodec();
1797
		$constraintProvider = $this->getEntityConstraintProvider();
1798
		$errorLocalizer = $this->getValidatorErrorLocalizer();
1799
		$siteLinkStore = $this->getStore()->newSiteLinkStore();
1800
		$legacyFormatDetector = $this->getLegacyFormatDetectorCallback();
1801
1802
		return new ItemHandler(
1803
			$this->getItemTermStoreWriter(),
1804
			$codec,
1805
			$constraintProvider,
1806
			$errorLocalizer,
1807
			$this->getEntityIdParser(),
1808
			$siteLinkStore,
1809
			$this->getEntityIdLookup(),
1810
			$this->getLanguageFallbackLabelDescriptionLookupFactory(),
1811
			$this->getFieldDefinitionsByType( Item::ENTITY_TYPE ),
1812
			$this->getPropertyDataTypeLookup(),
1813
			$legacyFormatDetector
1814
		);
1815
	}
1816
1817
	public function getPropertyTermStoreWriter(): EntityTermStoreWriter {
1818
		if ( !in_array( Property::ENTITY_TYPE, $this->getLocalEntitySource()->getEntityTypes() ) ) {
1819
			return new ThrowingEntityTermStoreWriter();
1820
		}
1821
1822
		return new PropertyTermStoreWriterAdapter( $this->getNewTermStoreWriterFactory()->newPropertyTermStoreWriter() );
1823
	}
1824
1825
	public function getItemTermStoreWriter(): EntityTermStoreWriter {
1826
		if ( !in_array( Item::ENTITY_TYPE, $this->getLocalEntitySource()->getEntityTypes() ) ) {
1827
			return new ThrowingEntityTermStoreWriter();
1828
		}
1829
1830
		return new ItemTermStoreWriterAdapter( $this->getNewTermStoreWriterFactory()->newItemTermStoreWriter() );
1831
	}
1832
1833
	public function getNewTermStoreWriterFactory(): TermStoreWriterFactory {
1834
		return new TermStoreWriterFactory(
1835
			$this->getLocalEntitySource(),
1836
			$this->getStringNormalizer(),
1837
			MediaWikiServices::getInstance()->getDBLoadBalancerFactory(),
1838
			MediaWikiServices::getInstance()->getMainWANObjectCache(),
1839
			JobQueueGroup::singleton(),
1840
			$this->getLogger()
1841
		);
1842
	}
1843
1844
	/**
1845
	 * Get field definitions for entity depending on its type.
1846
	 * @param string $type Entity type
1847
	 * @return FieldDefinitions
1848
	 */
1849
	public function getFieldDefinitionsByType( $type ) {
1850
		$definitions = $this->entityTypeDefinitions->get( EntityTypeDefinitions::SEARCH_FIELD_DEFINITIONS );
1851
		if ( isset( $definitions[$type] ) && is_callable( $definitions[$type] ) ) {
1852
			return call_user_func( $definitions[$type], $this->getTermsLanguages()->getLanguages(),
1853
				$this->settings );
1854
		}
1855
		return new NoFieldDefinitions();
1856
	}
1857
1858
	public function newPropertyHandler(): PropertyHandler {
1859
		$codec = $this->getEntityContentDataCodec();
1860
		$constraintProvider = $this->getEntityConstraintProvider();
1861
		$errorLocalizer = $this->getValidatorErrorLocalizer();
1862
		$propertyInfoStore = $this->getStore()->getPropertyInfoStore();
1863
		$propertyInfoBuilder = $this->newPropertyInfoBuilder();
1864
		$legacyFormatDetector = $this->getLegacyFormatDetectorCallback();
1865
1866
		return new PropertyHandler(
1867
			$this->getPropertyTermStoreWriter(),
1868
			$codec,
1869
			$constraintProvider,
1870
			$errorLocalizer,
1871
			$this->getEntityIdParser(),
1872
			$this->getEntityIdLookup(),
1873
			$this->getLanguageFallbackLabelDescriptionLookupFactory(),
1874
			$propertyInfoStore,
1875
			$propertyInfoBuilder,
1876
			$this->getFieldDefinitionsByType( Property::ENTITY_TYPE ),
1877
			$legacyFormatDetector
1878
		);
1879
	}
1880
1881
	/**
1882
	 * @return PropertyInfoBuilder
1883
	 */
1884
	public function newPropertyInfoBuilder() {
1885
		$propertyIdMap = [];
1886
1887
		$formatterUrlProperty = $this->settings->getSetting( 'formatterUrlProperty' );
1888
		if ( $formatterUrlProperty !== null ) {
1889
			$propertyIdMap[PropertyInfoLookup::KEY_FORMATTER_URL] = new PropertyId(
1890
				$formatterUrlProperty
1891
			);
1892
		}
1893
1894
		$canonicalUriProperty = $this->settings->getSetting( 'canonicalUriProperty' );
1895
		if ( $canonicalUriProperty !== null ) {
1896
			$propertyIdMap[PropertyInfoStore::KEY_CANONICAL_URI] = new PropertyId( $canonicalUriProperty );
1897
		}
1898
1899
		return new PropertyInfoBuilder( $propertyIdMap );
1900
	}
1901
1902
	private function getLegacyFormatDetectorCallback() {
1903
		$transformOnExport = $this->settings->getSetting( 'transformLegacyFormatOnExport' );
1904
1905
		if ( !$transformOnExport ) {
1906
			return null;
1907
		}
1908
1909
		/**
1910
		 * Detects blobs that may be using a legacy serialization format.
1911
		 * WikibaseRepo uses this for the $legacyExportFormatDetector parameter
1912
		 * when constructing EntityHandlers.
1913
		 *
1914
		 * @see WikibaseRepo::newItemHandler
1915
		 * @see WikibaseRepo::newPropertyHandler
1916
		 * @see EntityHandler::__construct
1917
		 *
1918
		 * @note: False positives (detecting a legacy format when really no legacy format was used)
1919
		 * are acceptable, false negatives (failing to detect a legacy format when one was used)
1920
		 * are not acceptable.
1921
		 *
1922
		 * @param string $blob
1923
		 * @param string $format
1924
		 *
1925
		 * @return bool True if $blob seems to be using a legacy serialization format.
1926
		 */
1927
		return function ( $blob, $format ) {
1928
			// The legacy serialization uses something like "entity":["item",21] or
1929
			// even "entity":"p21" for the entity ID.
1930
			return preg_match( '/"entity"\s*:/', $blob ) > 0;
1931
		};
1932
	}
1933
1934
	/**
1935
	 * @param IContextSource $context
1936
	 *
1937
	 * @return ApiHelperFactory
1938
	 */
1939
	public function getApiHelperFactory( IContextSource $context ) {
1940
		return new ApiHelperFactory(
1941
			$this->getEntityTitleLookup(),
1942
			$this->getExceptionLocalizer(),
1943
			$this->getPropertyDataTypeLookup(),
1944
			$this->getSiteLookup(),
1945
			$this->getSummaryFormatter(),
1946
			$this->getEntityRevisionLookup( Store::LOOKUP_CACHING_DISABLED ),
1947
			$this->newEditEntityFactory( $context ),
1948
			$this->getBaseDataModelSerializerFactory(),
1949
			$this->getAllTypesEntitySerializer(),
1950
			$this->getEntityIdParser(),
1951
			MediaWikiServices::getInstance()->getPermissionManager(),
1952
			$this->getStore()->getEntityByLinkedTitleLookup(),
1953
			$this->getEntityFactory(),
1954
			$this->getEntityStore()
1955
		);
1956
	}
1957
1958
	/**
1959
	 * @param IContextSource|null $context
1960
	 *
1961
	 * @return MediawikiEditEntityFactory
1962
	 */
1963
	public function newEditEntityFactory( IContextSource $context = null ) {
1964
		return new MediawikiEditEntityFactory(
1965
			$this->getEntityTitleLookup(),
1966
			$this->getEntityRevisionLookup( Store::LOOKUP_CACHING_DISABLED ),
1967
			$this->getEntityStore(),
1968
			$this->getEntityPermissionChecker(),
1969
			$this->getEntityDiffer(),
1970
			$this->getEntityPatcher(),
1971
			$this->newEditFilterHookRunner( $context ?: RequestContext::getMain() ),
1972
			MediaWikiServices::getInstance()->getStatsdDataFactory(),
1973
			$this->getSettings()->getSetting( 'maxSerializedEntitySize' )
1974
		);
1975
	}
1976
1977
	/**
1978
	 * @param IContextSource $context
1979
	 *
1980
	 * @return ItemMergeInteractor
1981
	 */
1982
	public function newItemMergeInteractor( IContextSource $context ) {
1983
		$user = $context->getUser();
1984
1985
		return new ItemMergeInteractor(
1986
			$this->getChangeOpFactoryProvider()->getMergeFactory(),
1987
			$this->getEntityRevisionLookup( Store::LOOKUP_CACHING_DISABLED ),
1988
			$this->getEntityStore(),
1989
			$this->getEntityPermissionChecker(),
1990
			$this->getSummaryFormatter(),
1991
			$user,
1992
			$this->newItemRedirectCreationInteractor( $user, $context ),
1993
			$this->getEntityTitleLookup(),
1994
			MediaWikiServices::getInstance()->getPermissionManager()
1995
		);
1996
	}
1997
1998
	/**
1999
	 * @return int[] An array mapping entity type identifiers to namespace numbers.
2000
	 */
2001
	public function getLocalEntityNamespaces() {
2002
		return $this->settings->getSetting( 'entityNamespaces' );
2003
	}
2004
2005
	/**
2006
	 * @return EntityNamespaceLookup
2007
	 */
2008
	public function getEntityNamespaceLookup() {
2009
		return $this->getWikibaseServices()->getEntityNamespaceLookup();
2010
	}
2011
2012
	public function getLocalEntityNamespaceLookup(): EntityNamespaceLookup {
2013
		$localEntitySource = $this->getLocalEntitySource();
2014
		$nsIds = $localEntitySource->getEntityNamespaceIds();
2015
		$entitySlots = $localEntitySource->getEntitySlotNames();
2016
2017
		return new EntityNamespaceLookup( $nsIds, $entitySlots );
2018
	}
2019
2020
	/**
2021
	 * @return EntityIdFormatterFactory
2022
	 */
2023
	public function getEntityIdHtmlLinkFormatterFactory() {
2024
		$factory = new EntityIdHtmlLinkFormatterFactory(
2025
			$this->getEntityTitleLookup(),
2026
			$this->getLanguageNameLookup(),
2027
			$this->entityTypeDefinitions->get( EntityTypeDefinitions::ENTITY_ID_HTML_LINK_FORMATTER_CALLBACK )
2028
		);
2029
		if ( $this->inFederatedPropertyMode() ) {
2030
			$factory = new WrappingEntityIdFormatterFactory( $factory );
2031
		}
2032
		return $factory;
2033
	}
2034
2035
	public function getEntityViewFactory() {
2036
		return new DispatchingEntityViewFactory( $this->entityTypeDefinitions->get( EntityTypeDefinitions::VIEW_FACTORY_CALLBACK ) );
2037
	}
2038
2039
	public function getEntityMetaTagsCreatorFactory() {
2040
		return new DispatchingEntityMetaTagsCreatorFactory(
2041
			$this->entityTypeDefinitions->get( EntityTypeDefinitions::META_TAGS_CREATOR_CALLBACK )
2042
		);
2043
	}
2044
2045
	public function getEntityDataFormatProvider(): EntityDataFormatProvider {
2046
		$entityDataFormatProvider = new EntityDataFormatProvider();
2047
		$formats = $this->settings->getSetting( 'entityDataFormats' );
2048
		$entityDataFormatProvider->setAllowedFormats( $formats );
2049
		return $entityDataFormatProvider;
2050
	}
2051
2052
	public function getEntityDataUriManager(): EntityDataUriManager {
2053
		$entityDataFormatProvider = $this->getEntityDataFormatProvider();
2054
2055
		// build a mapping of formats to file extensions and include HTML
2056
		$supportedExtensions = [];
2057
		$supportedExtensions['html'] = 'html';
2058
		foreach ( $entityDataFormatProvider->getSupportedFormats() as $format ) {
2059
			$ext = $entityDataFormatProvider->getExtension( $format );
2060
2061
			if ( $ext !== null ) {
2062
				$supportedExtensions[$format] = $ext;
2063
			}
2064
		}
2065
2066
		return new EntityDataUriManager(
2067
			SpecialPage::getTitleFor( 'EntityData' ),
2068
			$supportedExtensions,
2069
			$this->getSettings()->getSetting( 'entityDataCachePaths' ),
2070
			$this->getEntityTitleLookup()
2071
		);
2072
	}
2073
2074
	public function getEntityParserOutputGeneratorFactory(): EntityParserOutputGeneratorFactory {
2075
		$services = MediaWikiServices::getInstance();
2076
2077
		return new EntityParserOutputGeneratorFactory(
2078
			$this->getEntityViewFactory(),
2079
			$this->getEntityMetaTagsCreatorFactory(),
2080
			$this->getEntityTitleLookup(),
2081
			$this->getLanguageFallbackChainFactory(),
2082
			TemplateFactory::getDefaultInstance(),
2083
			$this->getEntityDataFormatProvider(),
2084
			// FIXME: Should this be done for all usages of this lookup, or is the impact of
2085
			// CachingPropertyInfoLookup enough?
2086
			new InProcessCachingDataTypeLookup( $this->getPropertyDataTypeLookup() ),
2087
			$this->getCompactEntitySerializer(),
2088
			new EntityReferenceExtractorDelegator(
2089
				$this->entityTypeDefinitions->get( EntityTypeDefinitions::ENTITY_REFERENCE_EXTRACTOR_CALLBACK ),
2090
				new StatementEntityReferenceExtractor( $this->getItemUrlParser() )
2091
			),
2092
			$this->getKartographerEmbeddingHandler(),
2093
			$services->getStatsdDataFactory(),
2094
			$services->getRepoGroup(),
2095
			$this->settings->getSetting( 'preferredGeoDataProperties' ),
2096
			$this->settings->getSetting( 'preferredPageImagesProperties' ),
2097
			$this->settings->getSetting( 'globeUris' )
2098
		);
2099
	}
2100
2101
	public function getEntityParserOutputGenerator( Language $userLanguage ): EntityParserOutputGenerator {
2102
		return $this->getEntityParserOutputGeneratorFactory()
2103
			->getEntityParserOutputGenerator( $userLanguage );
2104
	}
2105
2106
	/**
2107
	 * @return ViewFactory
2108
	 */
2109
	public function getViewFactory() {
2110
		$lang = $this->getUserLanguage();
2111
2112
		$statementGrouperBuilder = new StatementGrouperBuilder(
2113
			$this->settings->getSetting( 'statementSections' ),
2114
			$this->getPropertyDataTypeLookup(),
2115
			$this->getStatementGuidParser()
2116
		);
2117
2118
		$propertyOrderProvider = new CachingPropertyOrderProvider(
2119
			new WikiPagePropertyOrderProvider(
2120
				Title::newFromText( 'MediaWiki:Wikibase-SortedProperties' )
2121
			),
2122
			ObjectCache::getLocalClusterInstance()
2123
		);
2124
2125
		return new ViewFactory(
2126
			$this->getEntityIdHtmlLinkFormatterFactory(),
2127
			new EntityIdLabelFormatterFactory(),
2128
			new WikibaseHtmlSnakFormatterFactory( $this->getSnakFormatterFactory() ),
2129
			$statementGrouperBuilder->getStatementGrouper(),
2130
			$propertyOrderProvider,
2131
			$this->getSiteLookup(),
2132
			$this->getDataTypeFactory(),
2133
			TemplateFactory::getDefaultInstance(),
2134
			$this->getLanguageNameLookup(),
2135
			new MediaWikiLanguageDirectionalityLookup(),
2136
			new MediaWikiNumberLocalizer( $lang ),
2137
			$this->settings->getSetting( 'siteLinkGroups' ),
2138
			$this->settings->getSetting( 'specialSiteLinkGroups' ),
2139
			$this->settings->getSetting( 'badgeItems' ),
2140
			new MediaWikiLocalizedTextProvider( $lang ),
2141
			new RepoSpecialPageLinker()
2142
		);
2143
	}
2144
2145
	/**
2146
	 * @return DataTypeValidatorFactory
2147
	 */
2148
	public function getDataTypeValidatorFactory() {
2149
		return new BuilderBasedDataTypeValidatorFactory(
2150
			$this->dataTypeDefinitions->getValidatorFactoryCallbacks()
2151
		);
2152
	}
2153
2154
	/**
2155
	 * @return DataTypeDefinitions
2156
	 */
2157
	public function getDataTypeDefinitions() {
2158
		return $this->dataTypeDefinitions;
2159
	}
2160
2161
	public function getWikibaseContentLanguages() {
2162
		if ( $this->wikibaseContentLanguages === null ) {
2163
			$this->wikibaseContentLanguages = WikibaseContentLanguages::getDefaultInstance();
2164
		}
2165
2166
		return $this->wikibaseContentLanguages;
2167
	}
2168
2169
	private function getMonolingualTextLanguages() {
2170
		return $this->getWikibaseContentLanguages()->getContentLanguages( WikibaseContentLanguages::CONTEXT_MONOLINGUAL_TEXT );
2171
	}
2172
2173
	/**
2174
	 * Get a ContentLanguages object holding the languages available for labels, descriptions and aliases.
2175
	 *
2176
	 * @return ContentLanguages
2177
	 */
2178
	public function getTermsLanguages() {
2179
		return $this->getWikibaseContentLanguages()->getContentLanguages( WikibaseContentLanguages::CONTEXT_TERM );
2180
	}
2181
2182
	/**
2183
	 * @return CachingCommonsMediaFileNameLookup
2184
	 */
2185
	private function getCachingCommonsMediaFileNameLookup() {
2186
		if ( $this->cachingCommonsMediaFileNameLookup === null ) {
2187
			$this->cachingCommonsMediaFileNameLookup = new CachingCommonsMediaFileNameLookup(
2188
				new MediaWikiPageNameNormalizer(),
2189
				new HashBagOStuff()
2190
			);
2191
		}
2192
2193
		return $this->cachingCommonsMediaFileNameLookup;
2194
	}
2195
2196
	public function getEntityTypesConfigValue() {
2197
		return [
2198
			'types' => $this->entityTypeDefinitions->getEntityTypes(),
2199
			'deserializer-factory-functions'
2200
				=> $this->entityTypeDefinitions->get( EntityTypeDefinitions::JS_DESERIALIZER_FACTORY_FUNCTION )
2201
		];
2202
	}
2203
2204
	public function getSettingsValueProvider( $jsSetting, $phpSetting ) {
2205
		return new SettingsValueProvider( $this->settings, $jsSetting, $phpSetting );
2206
	}
2207
2208
	/**
2209
	 * Get configure unit converter.
2210
	 * @return null|UnitConverter Configured Unit converter, or null if none configured
2211
	 */
2212
	public function getUnitConverter() {
2213
		$unitStorage = $this->getUnitStorage();
2214
		if ( !$unitStorage ) {
2215
			return null;
2216
		}
2217
		return new UnitConverter( $unitStorage, $this->settings->getSetting( 'conceptBaseUri' ) );
2218
	}
2219
2220
	/**
2221
	 * Creates configured unit storage. Configuration is in unitStorage parameter,
2222
	 * in getObjectFromSpec format.
2223
	 * @see ObjectFactory::getObjectFromSpec
2224
	 * @return null|UnitStorage Configured unit storage, or null
2225
	 */
2226
	private function getUnitStorage() {
2227
		if ( !$this->settings->hasSetting( 'unitStorage' ) ) {
2228
			return null;
2229
		}
2230
		$storage =
2231
			ObjectFactory::getObjectFromSpec( $this->settings->getSetting( 'unitStorage' ) );
2232
		if ( !( $storage instanceof UnitStorage ) ) {
2233
			wfWarn( "Bad unit storage configuration, ignoring" );
2234
			return null;
2235
		}
2236
		return $storage;
2237
	}
2238
2239
	/**
2240
	 * @return EntityRdfBuilderFactory
2241
	 */
2242
	public function getEntityRdfBuilderFactory() {
2243
		if ( $this->entityRdfBuilderFactory === null ) {
2244
			$this->entityRdfBuilderFactory = new EntityRdfBuilderFactory(
2245
				$this->entityTypeDefinitions->get( EntityTypeDefinitions::RDF_BUILDER_FACTORY_CALLBACK ),
2246
				$this->entityTypeDefinitions->get( EntityTypeDefinitions::RDF_LABEL_PREDICATES )
2247
			);
2248
		}
2249
2250
		return $this->entityRdfBuilderFactory;
2251
	}
2252
2253
	/**
2254
	 * @param IContextSource $contextSource
2255
	 * @return EntityDiffVisualizerFactory
2256
	 */
2257
	public function getEntityDiffVisualizerFactory( IContextSource $contextSource ) {
2258
		$langCode = $contextSource->getLanguage()->getCode();
2259
2260
		$options = new FormatterOptions( [
2261
			//TODO: fallback chain
2262
			ValueFormatter::OPT_LANG => $langCode
2263
		] );
2264
2265
		$htmlFormatterFactory = $this->getEntityIdHtmlLinkFormatterFactory();
2266
		$entityIdFormatter = $htmlFormatterFactory->getEntityIdFormatter( $contextSource->getLanguage() );
2267
2268
		$formatterFactory = $this->getSnakFormatterFactory();
2269
		$detailedSnakFormatter = $formatterFactory->getSnakFormatter( SnakFormatter::FORMAT_HTML_DIFF, $options );
2270
		$terseSnakFormatter = $formatterFactory->getSnakFormatter( SnakFormatter::FORMAT_HTML, $options );
2271
2272
		return new EntityDiffVisualizerFactory(
2273
			$this->entityTypeDefinitions->get( EntityTypeDefinitions::ENTITY_DIFF_VISUALIZER_CALLBACK ),
2274
			$contextSource,
2275
			new ClaimDiffer( new OrderedListDiffer( new ComparableComparer() ) ),
2276
			new ClaimDifferenceVisualizer(
2277
				new DifferencesSnakVisualizer(
2278
					$entityIdFormatter,
2279
					$detailedSnakFormatter,
2280
					$terseSnakFormatter,
2281
					$langCode
2282
				),
2283
				$langCode
2284
			),
2285
			$this->getSiteLookup(),
2286
			$entityIdFormatter
2287
		);
2288
	}
2289
2290
	/**
2291
	 * @return string[][] Associative array mapping names of known entity types (strings) to lists of names of
2292
	 *         repositories providing entities of those types.
2293
	 *         Note: Currently entities of a given type are only provided by single source. This
2294
	 *         assumption can be changed in the future.
2295
	 */
2296
	public function getEntityTypeToRepositoryMapping() {
2297
		// Map all entity types to unprefixed repository.
2298
		// TODO: This is a bit of a hack but does the job for EntityIdSearchHelper as long as there are no
2299
		// prefixed IDs in the entity source realm. Probably EntityIdSearchHelper should be changed instead
2300
		// of getting this map passed from Repo
2301
		$entityTypes = array_keys( $this->entitySourceDefinitions->getEntityTypeToSourceMapping() );
2302
		return array_fill_keys( $entityTypes, [ '' ] );
2303
	}
2304
2305
	/**
2306
	 * @return string[] Associative array mapping repository or entity source names to base URIs of concept URIs.
2307
	 */
2308
	public function getConceptBaseUris() {
2309
		return $this->entitySourceDefinitions->getConceptBaseUris();
2310
	}
2311
2312
	public function getPropertyValueExpertsModule() {
2313
		return new PropertyValueExpertsModule( $this->getDataTypeDefinitions() );
2314
	}
2315
2316
	/**
2317
	 * @return WikibaseServices
2318
	 */
2319
	public function getWikibaseServices() {
2320
		if ( $this->wikibaseServices === null ) {
2321
			$this->wikibaseServices = $this->newEntitySourceWikibaseServices();
2322
		}
2323
2324
		return $this->wikibaseServices;
2325
	}
2326
2327
	private function newEntitySourceWikibaseServices() {
2328
		$nameTableStoreFactory = MediaWikiServices::getInstance()->getNameTableStoreFactory();
2329
		$genericServices = new GenericServices( $this->entityTypeDefinitions );
2330
2331
		$singleSourceServices = [];
2332
		foreach ( $this->entitySourceDefinitions->getSources() as $source ) {
2333
			// TODO: extract
2334
			$singleSourceServices[$source->getSourceName()] = new SingleEntitySourceServices(
2335
				$genericServices,
2336
				$this->getEntityIdParser(),
2337
				$this->getEntityIdComposer(),
2338
				$this->getDataValueDeserializer(),
2339
				$nameTableStoreFactory->getSlotRoles( $source->getDatabaseName() ),
2340
				$this->getDataAccessSettings(),
2341
				$source,
2342
				$this->entityTypeDefinitions->get( EntityTypeDefinitions::DESERIALIZER_FACTORY_CALLBACK ),
2343
				$this->entityTypeDefinitions->get( EntityTypeDefinitions::ENTITY_METADATA_ACCESSOR_CALLBACK ),
2344
				$this->entityTypeDefinitions->get( EntityTypeDefinitions::PREFETCHING_TERM_LOOKUP_CALLBACK ),
2345
				$this->entityTypeDefinitions->get( EntityTypeDefinitions::ENTITY_REVISION_LOOKUP_FACTORY_CALLBACK )
2346
			);
2347
		}
2348
		return new MultipleEntitySourceServices( $this->entitySourceDefinitions, $genericServices, $singleSourceServices );
2349
	}
2350
2351
	public function getDataAccessSettings() {
2352
		if ( $this->dataAccessSettings === null ) {
2353
			$this->dataAccessSettings = new DataAccessSettings(
2354
				$this->settings->getSetting( 'maxSerializedEntitySize' )
2355
			);
2356
		}
2357
		return $this->dataAccessSettings;
2358
	}
2359
2360
	public function getEntitySourceDefinitions() {
2361
		return $this->entitySourceDefinitions;
2362
	}
2363
2364
	/**
2365
	 * @return callable[]
2366
	 */
2367
	public function getEntitySearchHelperCallbacks() {
2368
		return $this->entityTypeDefinitions->get( EntityTypeDefinitions::ENTITY_SEARCH_CALLBACK );
2369
	}
2370
2371
	public function getEntityLinkFormatterFactory( Language $language ) {
2372
		return new EntityLinkFormatterFactory(
2373
			$language,
2374
			$this->getEntityTitleTextLookup(),
2375
			$this->entityTypeDefinitions->get( EntityTypeDefinitions::LINK_FORMATTER_CALLBACK )
2376
		);
2377
	}
2378
2379
	/**
2380
	 * Get entity search helper callbacks.
2381
	 * @return string[]
2382
	 */
2383
	public function getFulltextSearchTypes() {
2384
		$searchTypes = $this->entityTypeDefinitions->get( EntityTypeDefinitions::FULLTEXT_SEARCH_CONTEXT );
2385
		foreach ( $searchTypes as $key => $value ) {
2386
			if ( is_callable( $value ) ) {
2387
				$searchTypes[$key] = $value();
2388
			}
2389
		}
2390
		return $searchTypes;
2391
	}
2392
2393
	public function getTermFallbackCache(): TermFallbackCacheFacade {
2394
		return new TermFallbackCacheFacade(
2395
			$this->getTermFallbackCacheFactory()->getTermFallbackCache(),
2396
			$this->getSettings()->getSetting( 'sharedCacheDuration' )
2397
		);
2398
	}
2399
2400
	public function getTermFallbackCacheFactory(): TermFallbackCacheFactory {
2401
		global $wgSecretKey;
2402
2403
		if ( $this->termFallbackCacheFactory === null ) {
2404
			$this->termFallbackCacheFactory = new TermFallbackCacheFactory(
2405
				$this->settings->getSetting( 'sharedCacheType' ),
2406
				$this->getLogger(),
2407
				MediaWikiServices::getInstance()->getStatsdDataFactory(),
2408
				hash( 'sha256', $wgSecretKey ),
2409
				new TermFallbackCacheServiceFactory(),
2410
				$this->settings->getSetting( 'termFallbackCacheVersion' )
2411
			);
2412
		}
2413
		return $this->termFallbackCacheFactory;
2414
	}
2415
2416
	public function getHtmlCacheUpdater(): HtmlCacheUpdater {
2417
		return MediaWikiServices::getInstance()->getHtmlCacheUpdater();
2418
	}
2419
2420
	public function getLogger(): LoggerInterface {
2421
		return LoggerFactory::getInstance( 'Wikibase' );
2422
	}
2423
2424
	/**
2425
	 * Guard against Federated properties services being constructed in wiring when feature is disabled.
2426
	 */
2427
	private function throwLogicExceptionIfFederatedPropertiesNotEnabledAndConfigured(): void {
2428
		if (
2429
			!$this->inFederatedPropertyMode() ||
2430
			!$this->getSettings()->hasSetting( 'federatedPropertiesSourceScriptUrl' )
2431
		) {
2432
			throw new LogicException(
2433
				'Federated Property services should not be constructed when federatedProperties feature is not enabled or configured.'
2434
			);
2435
		}
2436
	}
2437
2438
	public function inFederatedPropertyMode(): bool {
2439
		return $this->getSettings()->getSetting( 'federatedPropertiesEnabled' );
2440
	}
2441
2442
	public function newFederatedPropertiesServiceFactory(): ApiServiceFactory {
2443
		$this->throwLogicExceptionIfFederatedPropertiesNotEnabledAndConfigured();
2444
2445
		global $wgServerName;
2446
2447
		return new ApiServiceFactory(
2448
			$this->getSettings()->getSetting( 'federatedPropertiesSourceScriptUrl' ),
2449
			$wgServerName
2450
		);
2451
	}
2452
2453
	public function getLinkTargetEntityIdLookup(): LinkTargetEntityIdLookup {
2454
		return new EntityLinkTargetEntityIdLookup(
2455
			$this->getEntityNamespaceLookup(),
2456
			$this->getEntityIdParser(),
2457
			$this->entitySourceDefinitions,
2458
			$this->getLocalEntitySource()
2459
		);
2460
	}
2461
2462
}
2463