Complex classes like WikibaseRepo often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use WikibaseRepo, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
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' ), |
||
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' ), |
||
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() { |
||
1361 | |||
1362 | /** |
||
1363 | * @param ValueFormatter $formatter |
||
1364 | * |
||
1365 | * @return ExceptionLocalizer[] |
||
1366 | */ |
||
1367 | private function getExceptionLocalizers( ValueFormatter $formatter ) { |
||
1377 | |||
1378 | /** |
||
1379 | * @return SummaryFormatter |
||
1380 | */ |
||
1381 | public function getSummaryFormatter() { |
||
1388 | |||
1389 | /** |
||
1390 | * @return SummaryFormatter |
||
1391 | */ |
||
1392 | private function newSummaryFormatter() { |
||
1443 | |||
1444 | /** |
||
1445 | * @return EntityPermissionChecker |
||
1446 | */ |
||
1447 | public function getEntityPermissionChecker() { |
||
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() { |
||
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() { |
||
1545 | |||
1546 | /** |
||
1547 | * @return ChangeNotifier |
||
1548 | */ |
||
1549 | public function getChangeNotifier() { |
||
1564 | |||
1565 | /** |
||
1566 | * Get the mapping of entity types => content models |
||
1567 | * |
||
1568 | * @return array |
||
1569 | */ |
||
1570 | public function getContentModelMappings() { |
||
1577 | |||
1578 | /** |
||
1579 | * @return EntityFactory |
||
1580 | */ |
||
1581 | public function getEntityFactory() { |
||
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() { |
||
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() { |
||
1634 | |||
1635 | /** |
||
1636 | * @return EntityContentDataCodec |
||
1637 | */ |
||
1638 | public function getEntityContentDataCodec() { |
||
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() { |
||
1657 | |||
1658 | /** |
||
1659 | * @return InternalDeserializerFactory |
||
1660 | */ |
||
1661 | private function getInternalFormatDeserializerFactory() { |
||
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() { |
||
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() { |
||
1686 | |||
1687 | /** |
||
1688 | * Returns a deserializer to deserialize entities in current serialization only. |
||
1689 | * |
||
1690 | * @return DispatchableDeserializer |
||
1691 | */ |
||
1692 | private function getAllTypesEntityDeserializer() { |
||
1707 | |||
1708 | /** |
||
1709 | * Returns a deserializer to deserialize entities in both current and legacy serialization. |
||
1710 | * |
||
1711 | * @return Deserializer |
||
1712 | */ |
||
1713 | public function getInternalFormatEntityDeserializer() { |
||
1716 | |||
1717 | /** |
||
1718 | * @return Serializer Entity serializer that generates the full (expanded) serialization. |
||
1719 | */ |
||
1720 | public function getAllTypesEntitySerializer() { |
||
1723 | |||
1724 | /** |
||
1725 | * @return Serializer Entity serializer that generates the most compact serialization. |
||
1726 | */ |
||
1727 | public function getCompactEntitySerializer() { |
||
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() { |
||
1739 | |||
1740 | /** |
||
1741 | * Returns a deserializer to deserialize statements in both current and legacy serialization. |
||
1742 | * |
||
1743 | * @return Deserializer |
||
1744 | */ |
||
1745 | public function getInternalFormatStatementDeserializer() { |
||
1748 | |||
1749 | /** |
||
1750 | * Returns a deserializer to deserialize statements in current serialization only. |
||
1751 | * |
||
1752 | * @return Deserializer |
||
1753 | */ |
||
1754 | public function getExternalFormatStatementDeserializer() { |
||
1757 | |||
1758 | /** |
||
1759 | * @return Serializer |
||
1760 | */ |
||
1761 | public function getStatementSerializer() { |
||
1764 | |||
1765 | /** |
||
1766 | * @return DataValueDeserializer |
||
1767 | */ |
||
1768 | private function getDataValueDeserializer() { |
||
1794 | |||
1795 | public function newItemHandler(): ItemHandler { |
||
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() { |
||
2370 | |||
2371 | public function getEntityLinkFormatterFactory( Language $language ) { |
||
2378 | |||
2379 | /** |
||
2380 | * Get entity search helper callbacks. |
||
2381 | * @return string[] |
||
2382 | */ |
||
2383 | public function getFulltextSearchTypes() { |
||
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 |
This check looks at variables that are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.