Complex classes like WikibaseClient 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 WikibaseClient, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
137 | final class WikibaseClient { |
||
138 | |||
139 | /** |
||
140 | * @warning only for use in getDefaultInstance()! |
||
141 | * @var WikibaseClient |
||
142 | */ |
||
143 | private static $defaultInstance = null; |
||
144 | |||
145 | /** |
||
146 | * @warning only for use in getDefaultSnakFormatterBuilders()! |
||
147 | * @var WikibaseSnakFormatterBuilders |
||
148 | */ |
||
149 | private static $defaultSnakFormatterBuilders = null; |
||
150 | |||
151 | /** |
||
152 | * @var SettingsArray |
||
153 | */ |
||
154 | private $settings; |
||
155 | |||
156 | /** |
||
157 | * @var SiteLookup |
||
158 | */ |
||
159 | private $siteLookup; |
||
160 | |||
161 | /** |
||
162 | * @var WikibaseServices |
||
163 | */ |
||
164 | private $wikibaseServices; |
||
165 | |||
166 | /** |
||
167 | * @var PropertyDataTypeLookup|null |
||
168 | */ |
||
169 | private $propertyDataTypeLookup = null; |
||
170 | |||
171 | /** |
||
172 | * @var DataTypeFactory|null |
||
173 | */ |
||
174 | private $dataTypeFactory = null; |
||
175 | |||
176 | /** |
||
177 | * @var Deserializer|null |
||
178 | */ |
||
179 | private $entityDeserializer = null; |
||
180 | |||
181 | /** |
||
182 | * @var EntityIdParser|null |
||
183 | */ |
||
184 | private $entityIdParser = null; |
||
185 | |||
186 | /** |
||
187 | * @var EntityIdComposer|null |
||
188 | */ |
||
189 | private $entityIdComposer = null; |
||
190 | |||
191 | /** |
||
192 | * @var EntityIdLookup|null |
||
193 | */ |
||
194 | private $entityIdLookup = null; |
||
195 | |||
196 | /** |
||
197 | * @var ClientStore|null |
||
198 | */ |
||
199 | private $store = null; |
||
200 | |||
201 | /** |
||
202 | * @var Site|null |
||
203 | */ |
||
204 | private $site = null; |
||
205 | |||
206 | /** |
||
207 | * @var string|null |
||
208 | */ |
||
209 | private $siteGroup = null; |
||
210 | |||
211 | /** |
||
212 | * @var OutputFormatSnakFormatterFactory|null |
||
213 | */ |
||
214 | private $snakFormatterFactory = null; |
||
215 | |||
216 | /** |
||
217 | * @var OutputFormatValueFormatterFactory|null |
||
218 | */ |
||
219 | private $valueFormatterFactory = null; |
||
220 | |||
221 | /** |
||
222 | * @var ClientParserOutputDataUpdater|null |
||
223 | */ |
||
224 | private $parserOutputDataUpdater = null; |
||
225 | |||
226 | /** |
||
227 | * @var NamespaceChecker|null |
||
228 | */ |
||
229 | private $namespaceChecker = null; |
||
230 | |||
231 | /** |
||
232 | * @var RestrictedEntityLookup|null |
||
233 | */ |
||
234 | private $restrictedEntityLookup = null; |
||
235 | |||
236 | /** |
||
237 | * @var DataTypeDefinitions |
||
238 | */ |
||
239 | private $dataTypeDefinitions; |
||
240 | |||
241 | /** |
||
242 | * @var EntityTypeDefinitions |
||
243 | */ |
||
244 | private $entityTypeDefinitions; |
||
245 | |||
246 | /** |
||
247 | * @var TermLookup|null |
||
248 | */ |
||
249 | private $termLookup = null; |
||
250 | |||
251 | /** |
||
252 | * @var TermBuffer|null |
||
253 | */ |
||
254 | private $termBuffer = null; |
||
255 | |||
256 | /** |
||
257 | * @var PrefetchingTermLookup|null |
||
258 | */ |
||
259 | private $prefetchingTermLookup = null; |
||
260 | |||
261 | /** |
||
262 | * @var PropertyOrderProvider|null |
||
263 | */ |
||
264 | private $propertyOrderProvider = null; |
||
265 | |||
266 | /** |
||
267 | * @var SidebarLinkBadgeDisplay|null |
||
268 | */ |
||
269 | private $sidebarLinkBadgeDisplay = null; |
||
270 | |||
271 | /** |
||
272 | * @var WikibaseValueFormatterBuilders|null |
||
273 | */ |
||
274 | private $valueFormatterBuilders = null; |
||
275 | |||
276 | /** |
||
277 | * @var WikibaseContentLanguages|null |
||
278 | */ |
||
279 | private $wikibaseContentLanguages = null; |
||
280 | |||
281 | /** |
||
282 | * @var EntitySourceDefinitions |
||
283 | */ |
||
284 | private $entitySourceDefinitions; |
||
285 | |||
286 | private $descriptionLookup = null; |
||
287 | |||
288 | private $propertyLabelResolver = null; |
||
289 | |||
290 | /** @var ReferenceFormatterFactory|null */ |
||
291 | private $referenceFormatterFactory = null; |
||
292 | |||
293 | /** @var TermFallbackCacheFactory|null */ |
||
294 | private $termFallbackCacheFactory = null; |
||
295 | |||
296 | /** |
||
297 | * @warning This is for use with bootstrap code in WikibaseClient.datatypes.php only! |
||
298 | * Program logic should use WikibaseClient::getSnakFormatterFactory() instead! |
||
299 | * |
||
300 | * @return WikibaseValueFormatterBuilders |
||
301 | */ |
||
302 | public static function getDefaultValueFormatterBuilders() { |
||
303 | global $wgThumbLimits; |
||
304 | return self::getDefaultInstance()->newWikibaseValueFormatterBuilders( $wgThumbLimits ); |
||
305 | } |
||
306 | |||
307 | /** |
||
308 | * Returns a low level factory object for creating formatters for well known data types. |
||
309 | * |
||
310 | * @warning This is for use with getDefaultValueFormatterBuilders() during bootstrap only! |
||
311 | * Program logic should use WikibaseClient::getSnakFormatterFactory() instead! |
||
312 | * |
||
313 | * @param array $thumbLimits |
||
314 | * |
||
315 | * @return WikibaseValueFormatterBuilders |
||
316 | */ |
||
317 | private function newWikibaseValueFormatterBuilders( array $thumbLimits ) { |
||
318 | if ( $this->valueFormatterBuilders === null ) { |
||
319 | $entityTitleLookup = new ClientSiteLinkTitleLookup( |
||
320 | $this->getStore()->getSiteLinkLookup(), |
||
321 | $this->settings->getSetting( 'siteGlobalID' ) |
||
322 | ); |
||
323 | |||
324 | $kartographerEmbeddingHandler = null; |
||
325 | if ( $this->useKartographerGlobeCoordinateFormatter() ) { |
||
326 | $kartographerEmbeddingHandler = new CachingKartographerEmbeddingHandler( |
||
327 | MediaWikiServices::getInstance()->getParserFactory()->create() |
||
328 | ); |
||
329 | } |
||
330 | |||
331 | $this->valueFormatterBuilders = new WikibaseValueFormatterBuilders( |
||
332 | new FormatterLabelDescriptionLookupFactory( $this->getTermLookup() ), |
||
333 | new LanguageNameLookup( $this->getUserLanguage()->getCode() ), |
||
334 | $this->getRepoItemUriParser(), |
||
335 | $this->settings->getSetting( 'geoShapeStorageBaseUrl' ), |
||
336 | $this->settings->getSetting( 'tabularDataStorageBaseUrl' ), |
||
337 | $this->getTermFallbackCache(), |
||
338 | $this->settings->getSetting( 'sharedCacheDuration' ), |
||
339 | $this->getEntityLookup(), |
||
340 | $this->getStore()->getEntityRevisionLookup(), |
||
341 | $this->settings->getSetting( 'entitySchemaNamespace' ), |
||
342 | new TitleLookupBasedEntityExistenceChecker( $entityTitleLookup ), |
||
343 | new TitleLookupBasedEntityTitleTextLookup( $entityTitleLookup ), |
||
344 | new TitleLookupBasedEntityUrlLookup( $entityTitleLookup ), |
||
345 | new TitleLookupBasedEntityRedirectChecker( $entityTitleLookup ), |
||
346 | $entityTitleLookup, |
||
347 | $kartographerEmbeddingHandler, |
||
348 | $this->settings->getSetting( 'useKartographerMaplinkInWikitext' ), |
||
349 | $thumbLimits |
||
350 | ); |
||
351 | } |
||
352 | |||
353 | return $this->valueFormatterBuilders; |
||
354 | } |
||
355 | |||
356 | /** |
||
357 | * @return bool |
||
358 | */ |
||
359 | private function useKartographerGlobeCoordinateFormatter() { |
||
368 | |||
369 | /** |
||
370 | * @warning This is for use with bootstrap code in WikibaseClient.datatypes.php only! |
||
371 | * Program logic should use WikibaseClient::getSnakFormatterFactory() instead! |
||
372 | * |
||
373 | * @return WikibaseSnakFormatterBuilders |
||
374 | */ |
||
375 | public static function getDefaultSnakFormatterBuilders() { |
||
384 | |||
385 | /** |
||
386 | * Returns a low level factory object for creating formatters for well known data types. |
||
387 | * |
||
388 | * @warning This is for use with getDefaultValueFormatterBuilders() during bootstrap only! |
||
389 | * Program logic should use WikibaseClient::getSnakFormatterFactory() instead! |
||
390 | * |
||
391 | * @param WikibaseValueFormatterBuilders $valueFormatterBuilders |
||
392 | * |
||
393 | * @return WikibaseSnakFormatterBuilders |
||
394 | */ |
||
395 | private function newWikibaseSnakFormatterBuilders( WikibaseValueFormatterBuilders $valueFormatterBuilders ) { |
||
403 | |||
404 | public function __construct( |
||
417 | |||
418 | public function getDataTypeFactory(): DataTypeFactory { |
||
425 | |||
426 | public function getEntityIdParser(): EntityIdParser { |
||
427 | if ( $this->entityIdParser === null ) { |
||
435 | |||
436 | public function getEntityIdComposer(): EntityIdComposer { |
||
445 | |||
446 | public function getWikibaseServices(): WikibaseServices { |
||
453 | |||
454 | private function newEntitySourceWikibaseServices() { |
||
478 | |||
479 | private function getDataAccessSettings() { |
||
480 | return new DataAccessSettings( |
||
481 | $this->settings->getSetting( 'maxSerializedEntitySize' ) |
||
482 | ); |
||
483 | } |
||
484 | |||
485 | /** |
||
486 | * @return EntityLookup |
||
487 | */ |
||
488 | private function getEntityLookup() { |
||
489 | return $this->getStore()->getEntityLookup(); |
||
490 | } |
||
491 | |||
492 | private static function getDefaultDataTypes() { |
||
493 | $baseDataTypes = require __DIR__ . '/../../lib/WikibaseLib.datatypes.php'; |
||
494 | $clientDataTypes = require __DIR__ . '/../WikibaseClient.datatypes.php'; |
||
495 | |||
496 | return array_merge_recursive( $baseDataTypes, $clientDataTypes ); |
||
497 | } |
||
498 | |||
499 | /** |
||
500 | * @return array[] |
||
501 | */ |
||
502 | private static function getDefaultEntityTypes() { |
||
503 | return require __DIR__ . '/../../lib/WikibaseLib.entitytypes.php'; |
||
504 | } |
||
505 | |||
506 | /** |
||
507 | * @return TermBuffer|AliasTermBuffer |
||
508 | */ |
||
509 | public function getTermBuffer() { |
||
510 | if ( !$this->termBuffer ) { |
||
511 | $this->termBuffer = $this->getPrefetchingTermLookup(); |
||
512 | } |
||
513 | |||
514 | return $this->termBuffer; |
||
515 | } |
||
516 | |||
517 | public function getTermLookup(): TermLookup { |
||
518 | if ( !$this->termLookup ) { |
||
519 | $this->termLookup = $this->getPrefetchingTermLookup(); |
||
520 | } |
||
521 | |||
522 | return $this->termLookup; |
||
523 | } |
||
524 | |||
525 | private function getPrefetchingTermLookup(): PrefetchingTermLookup { |
||
526 | if ( !$this->prefetchingTermLookup ) { |
||
527 | $this->prefetchingTermLookup = $this->getWikibaseServices()->getPrefetchingTermLookup(); |
||
528 | } |
||
529 | |||
530 | return $this->prefetchingTermLookup; |
||
531 | } |
||
532 | |||
533 | /** |
||
534 | * XXX: This is not used by client itself, but is used by ArticlePlaceholder! |
||
535 | */ |
||
536 | public function newTermSearchInteractor( string $displayLanguageCode ): TermSearchInteractor { |
||
537 | return $this->getWikibaseServices()->getTermSearchInteractorFactory() |
||
538 | ->newInteractor( $displayLanguageCode ); |
||
539 | } |
||
540 | |||
541 | public function getPropertyDataTypeLookup(): PropertyDataTypeLookup { |
||
542 | if ( $this->propertyDataTypeLookup === null ) { |
||
543 | $infoLookup = $this->getStore()->getPropertyInfoLookup(); |
||
544 | $retrievingLookup = new EntityRetrievingDataTypeLookup( $this->getEntityLookup() ); |
||
545 | $this->propertyDataTypeLookup = new PropertyInfoDataTypeLookup( |
||
546 | $infoLookup, |
||
547 | $this->getLogger(), |
||
548 | $retrievingLookup |
||
549 | ); |
||
550 | } |
||
551 | |||
552 | return $this->propertyDataTypeLookup; |
||
553 | } |
||
554 | |||
555 | public function getStringNormalizer(): StringNormalizer { |
||
556 | return $this->getWikibaseServices()->getStringNormalizer(); |
||
557 | } |
||
558 | |||
559 | public function newRepoLinker(): RepoLinker { |
||
560 | return new RepoLinker( |
||
561 | $this->entitySourceDefinitions, |
||
562 | $this->settings->getSetting( 'repoUrl' ), |
||
563 | $this->settings->getSetting( 'repoArticlePath' ), |
||
564 | $this->settings->getSetting( 'repoScriptPath' ) |
||
565 | ); |
||
566 | } |
||
567 | |||
568 | public function getLanguageFallbackChainFactory(): LanguageFallbackChainFactory { |
||
569 | return $this->getWikibaseServices()->getLanguageFallbackChainFactory(); |
||
570 | } |
||
571 | |||
572 | public function getLanguageFallbackLabelDescriptionLookupFactory(): LanguageFallbackLabelDescriptionLookupFactory { |
||
573 | return new LanguageFallbackLabelDescriptionLookupFactory( |
||
574 | $this->getLanguageFallbackChainFactory(), |
||
575 | $this->getTermLookup(), |
||
576 | $this->getTermBuffer() |
||
|
|||
577 | ); |
||
578 | } |
||
579 | |||
580 | /** |
||
581 | * Returns an instance of the default store. |
||
582 | */ |
||
583 | public function getStore(): ClientStore { |
||
584 | if ( $this->store === null ) { |
||
585 | $this->store = new DirectSqlStore( |
||
586 | $this->getEntityChangeFactory(), |
||
587 | $this->getEntityIdParser(), |
||
588 | $this->getEntityIdComposer(), |
||
589 | $this->getEntityIdLookup(), |
||
590 | $this->getEntityNamespaceLookup(), |
||
591 | $this->getWikibaseServices(), |
||
592 | $this->getSettings(), |
||
593 | $this->getDatabaseDomainNameOfLocalRepo(), |
||
594 | $this->getContentLanguage()->getCode() |
||
595 | ); |
||
596 | } |
||
597 | |||
598 | return $this->store; |
||
599 | } |
||
600 | |||
601 | /** |
||
602 | * Overrides the default store to be used in the client app context. |
||
603 | * This is intended for use by test cases. |
||
604 | * |
||
605 | * @param ClientStore|null $store |
||
606 | * |
||
607 | * @throws LogicException If MW_PHPUNIT_TEST is not defined, to avoid this |
||
608 | * method being abused in production code. |
||
609 | */ |
||
610 | public function overrideStore( ClientStore $store = null ) { |
||
611 | if ( !defined( 'MW_PHPUNIT_TEST' ) ) { |
||
612 | throw new LogicException( 'Overriding the store instance is only supported in test mode' ); |
||
613 | } |
||
614 | |||
615 | $this->store = $store; |
||
616 | } |
||
617 | |||
618 | /** |
||
619 | * Overrides the TermLookup to be used. |
||
620 | * This is intended for use by test cases. |
||
621 | * |
||
622 | * @param TermLookup|null $lookup |
||
623 | * |
||
624 | * @throws LogicException If MW_PHPUNIT_TEST is not defined, to avoid this |
||
625 | * method being abused in production code. |
||
626 | */ |
||
627 | public function overrideTermLookup( TermLookup $lookup = null ) { |
||
628 | if ( !defined( 'MW_PHPUNIT_TEST' ) ) { |
||
629 | throw new LogicException( 'Overriding TermLookup is only supported in test mode' ); |
||
630 | } |
||
631 | |||
632 | $this->termLookup = $lookup; |
||
633 | } |
||
634 | |||
635 | /** |
||
636 | * @throws MWException when called to early |
||
637 | */ |
||
638 | public function getContentLanguage(): Language { |
||
639 | /** |
||
640 | * Before this constant is defined, custom config may not have been taken into account. |
||
641 | * So try not to allow code to use a language before that point. |
||
642 | * This code was explicitly mentioning the SetupAfterCache hook. |
||
643 | * With services, that hook won't be a problem anymore. |
||
644 | * So this check may well be unnecessary (but better safe than sorry). |
||
645 | */ |
||
646 | if ( !defined( 'MW_SERVICE_BOOTSTRAP_COMPLETE' ) ) { |
||
647 | throw new MWException( 'Premature access to MediaWiki ContentLanguage!' ); |
||
648 | } |
||
649 | |||
650 | return MediaWikiServices::getInstance()->getContentLanguage(); |
||
651 | } |
||
652 | |||
653 | /** |
||
654 | * @throws MWException when called to early |
||
655 | */ |
||
656 | private function getUserLanguage(): Language { |
||
657 | global $wgLang; |
||
658 | |||
659 | // TODO: define a LanguageProvider service instead of using a global directly. |
||
660 | // NOTE: we cannot inject $wgLang in the constructor, because it may still be null |
||
661 | // when WikibaseClient is initialized. In particular, the language object may not yet |
||
662 | // be there when the SetupAfterCache hook is run during bootstrapping. |
||
663 | |||
664 | if ( !$wgLang ) { |
||
665 | throw new MWException( 'Premature access: $wgLang is not yet initialized!' ); |
||
666 | } |
||
667 | |||
668 | StubObject::unstub( $wgLang ); |
||
669 | return $wgLang; |
||
670 | } |
||
671 | |||
672 | public function getSettings(): SettingsArray { |
||
673 | return $this->settings; |
||
674 | } |
||
675 | |||
676 | /** |
||
677 | * Returns a new instance constructed from global settings. |
||
678 | * IMPORTANT: Use only when it is not feasible to inject an instance properly. |
||
679 | * |
||
680 | * @throws MWException |
||
681 | * @return self |
||
682 | */ |
||
683 | private static function newInstance() { |
||
684 | $dataTypeDefinitionsArray = self::getDefaultDataTypes(); |
||
685 | Hooks::run( 'WikibaseClientDataTypes', [ &$dataTypeDefinitionsArray ] ); |
||
686 | |||
687 | $entityTypeDefinitionsArray = self::getDefaultEntityTypes(); |
||
688 | Hooks::run( 'WikibaseClientEntityTypes', [ &$entityTypeDefinitionsArray ] ); |
||
689 | |||
690 | $settings = WikibaseSettings::getClientSettings(); |
||
691 | |||
692 | $dataTypeDefinitions = new DataTypeDefinitions( |
||
693 | $dataTypeDefinitionsArray, |
||
694 | $settings->getSetting( 'disabledDataTypes' ) |
||
695 | ); |
||
696 | $entityTypeDefinitions = new EntityTypeDefinitions( $entityTypeDefinitionsArray ); |
||
697 | |||
698 | return new self( |
||
699 | $settings, |
||
700 | $dataTypeDefinitions, |
||
701 | $entityTypeDefinitions, |
||
702 | MediaWikiServices::getInstance()->getSiteLookup(), |
||
703 | self::getEntitySourceDefinitionsFromSettings( $settings, $entityTypeDefinitions ) |
||
704 | ); |
||
705 | } |
||
706 | |||
707 | // TODO: current settings (especially (foreign) repositories blob) might be quite confusing |
||
708 | // Having a "entitySources" or so setting might be better, and would also allow unifying |
||
709 | // the way these are configured in Repo and in Client parts |
||
710 | private static function getEntitySourceDefinitionsFromSettings( |
||
711 | SettingsArray $settings, |
||
712 | EntityTypeDefinitions $entityTypeDefinitions |
||
713 | ) { |
||
714 | if ( $settings->hasSetting( 'entitySources' ) && !empty( $settings->getSetting( 'entitySources' ) ) ) { |
||
715 | $configParser = new EntitySourceDefinitionsConfigParser(); |
||
716 | |||
717 | return $configParser->newDefinitionsFromConfigArray( $settings->getSetting( 'entitySources' ), $entityTypeDefinitions ); |
||
718 | } |
||
719 | |||
720 | $parser = new EntitySourceDefinitionsLegacyClientSettingsParser(); |
||
721 | return $parser->newDefinitionsFromSettings( $settings, $entityTypeDefinitions ); |
||
722 | } |
||
723 | |||
724 | /** |
||
725 | * IMPORTANT: Use only when it is not feasible to inject an instance properly. |
||
726 | * |
||
727 | * @param string $reset Flag: Pass "reset" to reset the default instance |
||
728 | * |
||
729 | * @return self |
||
730 | */ |
||
731 | public static function getDefaultInstance( $reset = 'noreset' ) { |
||
732 | if ( $reset === 'reset' ) { |
||
733 | self::$defaultInstance = null; |
||
734 | self::$defaultSnakFormatterBuilders = null; |
||
735 | } |
||
736 | |||
737 | if ( self::$defaultInstance === null ) { |
||
738 | self::$defaultInstance = self::newInstance(); |
||
739 | } |
||
740 | |||
741 | return self::$defaultInstance; |
||
742 | } |
||
743 | |||
744 | public function getLogger(): LoggerInterface { |
||
745 | return LoggerFactory::getInstance( 'Wikibase' ); |
||
746 | } |
||
747 | |||
748 | /** |
||
749 | * Returns the this client wiki's site object. |
||
750 | * |
||
751 | * This is taken from the siteGlobalID setting, which defaults |
||
752 | * to the wiki's database name. |
||
753 | * |
||
754 | * If the configured site ID is not found in the sites table, a |
||
755 | * new Site object is constructed from the configured ID. |
||
756 | * |
||
757 | * @throws MWException |
||
758 | */ |
||
759 | public function getSite(): Site { |
||
760 | if ( $this->site === null ) { |
||
761 | $globalId = $this->settings->getSetting( 'siteGlobalID' ); |
||
762 | $localId = $this->settings->getSetting( 'siteLocalID' ); |
||
763 | |||
764 | $this->site = $this->siteLookup->getSite( $globalId ); |
||
765 | |||
766 | // Todo inject me |
||
767 | $logger = $this->getLogger(); |
||
768 | |||
769 | if ( !$this->site ) { |
||
770 | $logger->debug( |
||
771 | '{method}: Unable to resolve site ID {globalId}!', |
||
772 | [ 'method' => __METHOD__, 'globalId' => $globalId ] |
||
773 | ); |
||
774 | |||
775 | $this->site = new MediaWikiSite(); |
||
776 | $this->site->setGlobalId( $globalId ); |
||
777 | $this->site->addLocalId( Site::ID_INTERWIKI, $localId ); |
||
778 | $this->site->addLocalId( Site::ID_EQUIVALENT, $localId ); |
||
779 | } |
||
780 | |||
781 | if ( !in_array( $localId, $this->site->getLocalIds() ) ) { |
||
782 | $logger->debug( |
||
783 | '{method}: The configured local id {localId} does not match any local IDs of site {globalId}: {localIds}', |
||
784 | [ |
||
785 | 'method' => __METHOD__, |
||
786 | 'localId' => $localId, |
||
787 | 'globalId' => $globalId, |
||
788 | 'localIds' => json_encode( $this->site->getLocalIds() ) |
||
789 | ] |
||
790 | ); |
||
791 | } |
||
792 | } |
||
793 | |||
794 | return $this->site; |
||
795 | } |
||
796 | |||
797 | /** |
||
798 | * Returns the site group ID for the group to be used for language links. |
||
799 | * This is typically the group the client wiki itself belongs to, but |
||
800 | * can be configured to be otherwise using the languageLinkSiteGroup setting. |
||
801 | * |
||
802 | * @return string |
||
803 | */ |
||
804 | public function getLangLinkSiteGroup() { |
||
805 | $group = $this->settings->getSetting( 'languageLinkSiteGroup' ); |
||
806 | |||
807 | if ( $group === null ) { |
||
808 | $group = $this->getSiteGroup(); |
||
809 | } |
||
810 | |||
811 | return $group; |
||
812 | } |
||
813 | |||
814 | /** |
||
815 | * Gets the site group ID from setting, which if not set then does |
||
816 | * lookup in site store. |
||
817 | * |
||
818 | * @return string |
||
819 | */ |
||
820 | private function newSiteGroup() { |
||
821 | $siteGroup = $this->settings->getSetting( 'siteGroup' ); |
||
822 | |||
823 | if ( !$siteGroup ) { |
||
824 | $siteId = $this->settings->getSetting( 'siteGlobalID' ); |
||
825 | |||
826 | $site = $this->siteLookup->getSite( $siteId ); |
||
827 | |||
828 | if ( !$site ) { |
||
829 | return true; |
||
830 | } |
||
831 | |||
832 | $siteGroup = $site->getGroup(); |
||
833 | } |
||
834 | |||
835 | return $siteGroup; |
||
836 | } |
||
837 | |||
838 | /** |
||
839 | * Get site group ID |
||
840 | * |
||
841 | * @return string |
||
842 | */ |
||
843 | public function getSiteGroup() { |
||
844 | if ( $this->siteGroup === null ) { |
||
845 | $this->siteGroup = $this->newSiteGroup(); |
||
846 | } |
||
847 | |||
848 | return $this->siteGroup; |
||
849 | } |
||
850 | |||
851 | /** |
||
852 | * Returns a OutputFormatSnakFormatterFactory the provides SnakFormatters |
||
853 | * for different output formats. |
||
854 | */ |
||
855 | private function getSnakFormatterFactory(): OutputFormatSnakFormatterFactory { |
||
856 | if ( $this->snakFormatterFactory === null ) { |
||
857 | $this->snakFormatterFactory = new OutputFormatSnakFormatterFactory( |
||
858 | $this->dataTypeDefinitions->getSnakFormatterFactoryCallbacks(), |
||
859 | $this->getValueFormatterFactory(), |
||
860 | $this->getPropertyDataTypeLookup(), |
||
861 | $this->getDataTypeFactory() |
||
862 | ); |
||
863 | } |
||
864 | |||
865 | return $this->snakFormatterFactory; |
||
866 | } |
||
867 | |||
868 | /** |
||
869 | * Returns a OutputFormatValueFormatterFactory the provides ValueFormatters |
||
870 | * for different output formats. |
||
871 | */ |
||
872 | private function getValueFormatterFactory(): OutputFormatValueFormatterFactory { |
||
873 | if ( $this->valueFormatterFactory === null ) { |
||
874 | $this->valueFormatterFactory = new OutputFormatValueFormatterFactory( |
||
875 | $this->dataTypeDefinitions->getFormatterFactoryCallbacks( DataTypeDefinitions::PREFIXED_MODE ), |
||
876 | $this->getContentLanguage(), |
||
877 | $this->getLanguageFallbackChainFactory() |
||
878 | ); |
||
879 | } |
||
880 | |||
881 | return $this->valueFormatterFactory; |
||
882 | } |
||
883 | |||
884 | private function getRepoItemUriParser(): EntityIdParser { |
||
885 | $itemConceptUriBase = $this->getItemSource()->getConceptBaseUri(); |
||
886 | |||
887 | return new SuffixEntityIdParser( |
||
888 | $itemConceptUriBase, |
||
889 | new ItemIdParser() |
||
890 | ); |
||
891 | } |
||
892 | |||
893 | public function getNamespaceChecker(): NamespaceChecker { |
||
894 | if ( $this->namespaceChecker === null ) { |
||
895 | $this->namespaceChecker = new NamespaceChecker( |
||
896 | $this->settings->getSetting( 'excludeNamespaces' ), |
||
897 | $this->settings->getSetting( 'namespaces' ) |
||
898 | ); |
||
899 | } |
||
900 | |||
901 | return $this->namespaceChecker; |
||
902 | } |
||
903 | |||
904 | public function getLangLinkHandlerFactory(): LangLinkHandlerFactory { |
||
905 | return new LangLinkHandlerFactory( |
||
906 | $this->getLanguageLinkBadgeDisplay(), |
||
907 | $this->getNamespaceChecker(), |
||
908 | $this->getStore()->getSiteLinkLookup(), |
||
909 | $this->getStore()->getEntityLookup(), |
||
910 | $this->siteLookup, |
||
911 | MediaWikiServices::getInstance()->getHookContainer(), |
||
912 | $this->getLogger(), |
||
913 | $this->settings->getSetting( 'siteGlobalID' ), |
||
914 | $this->getLangLinkSiteGroup() |
||
915 | ); |
||
916 | } |
||
917 | |||
918 | public function getParserOutputDataUpdater(): ClientParserOutputDataUpdater { |
||
919 | if ( $this->parserOutputDataUpdater === null ) { |
||
920 | $this->parserOutputDataUpdater = new ClientParserOutputDataUpdater( |
||
921 | $this->getOtherProjectsSidebarGeneratorFactory(), |
||
922 | $this->getStore()->getSiteLinkLookup(), |
||
923 | $this->getStore()->getEntityLookup(), |
||
924 | new EntityUsageFactory( $this->getEntityIdParser() ), |
||
925 | $this->settings->getSetting( 'siteGlobalID' ), |
||
926 | $this->getLogger() |
||
927 | ); |
||
928 | } |
||
929 | |||
930 | return $this->parserOutputDataUpdater; |
||
931 | } |
||
932 | |||
933 | public function getSidebarLinkBadgeDisplay(): SidebarLinkBadgeDisplay { |
||
934 | if ( $this->sidebarLinkBadgeDisplay === null ) { |
||
935 | $labelDescriptionLookupFactory = $this->getLanguageFallbackLabelDescriptionLookupFactory(); |
||
936 | $badgeClassNames = $this->settings->getSetting( 'badgeClassNames' ); |
||
937 | $lang = $this->getUserLanguage(); |
||
938 | |||
939 | $this->sidebarLinkBadgeDisplay = new SidebarLinkBadgeDisplay( |
||
940 | $labelDescriptionLookupFactory->newLabelDescriptionLookup( $lang ), |
||
941 | is_array( $badgeClassNames ) ? $badgeClassNames : [], |
||
942 | $lang |
||
943 | ); |
||
944 | } |
||
945 | |||
946 | return $this->sidebarLinkBadgeDisplay; |
||
947 | } |
||
948 | |||
949 | public function getLanguageLinkBadgeDisplay(): LanguageLinkBadgeDisplay { |
||
950 | return new LanguageLinkBadgeDisplay( |
||
951 | $this->getSidebarLinkBadgeDisplay() |
||
952 | ); |
||
953 | } |
||
954 | |||
955 | public function getBaseDataModelDeserializerFactory(): DeserializerFactory { |
||
956 | return new DeserializerFactory( |
||
957 | $this->getDataValueDeserializer(), |
||
958 | $this->getEntityIdParser() |
||
959 | ); |
||
960 | } |
||
961 | |||
962 | private function getInternalFormatDeserializerFactory(): InternalDeserializerFactory { |
||
963 | return new InternalDeserializerFactory( |
||
964 | $this->getDataValueDeserializer(), |
||
965 | $this->getEntityIdParser(), |
||
966 | $this->getAllTypesEntityDeserializer() |
||
967 | ); |
||
968 | } |
||
969 | |||
970 | private function getAllTypesEntityDeserializer(): DispatchableDeserializer { |
||
971 | if ( $this->entityDeserializer === null ) { |
||
972 | $deserializerFactoryCallbacks = $this->getEntityDeserializerFactoryCallbacks(); |
||
973 | $baseDeserializerFactory = $this->getBaseDataModelDeserializerFactory(); |
||
974 | $deserializers = []; |
||
975 | |||
976 | foreach ( $deserializerFactoryCallbacks as $callback ) { |
||
977 | $deserializers[] = call_user_func( $callback, $baseDeserializerFactory ); |
||
978 | } |
||
979 | |||
980 | $this->entityDeserializer = new DispatchingDeserializer( $deserializers ); |
||
981 | } |
||
982 | |||
983 | return $this->entityDeserializer; |
||
984 | } |
||
985 | |||
986 | /** |
||
987 | * Returns a deserializer to deserialize statements in both current and legacy serialization. |
||
988 | */ |
||
989 | public function getInternalFormatStatementDeserializer(): Deserializer { |
||
990 | return $this->getInternalFormatDeserializerFactory()->newStatementDeserializer(); |
||
991 | } |
||
992 | |||
993 | /** |
||
994 | * @return callable[] |
||
995 | */ |
||
996 | public function getEntityDeserializerFactoryCallbacks() { |
||
997 | return $this->entityTypeDefinitions->get( EntityTypeDefinitions::DESERIALIZER_FACTORY_CALLBACK ); |
||
998 | } |
||
999 | |||
1000 | /** |
||
1001 | * @return string[] |
||
1002 | */ |
||
1003 | public function getLuaEntityModules() { |
||
1004 | return $this->entityTypeDefinitions->get( EntityTypeDefinitions::LUA_ENTITY_MODULE ); |
||
1005 | } |
||
1006 | |||
1007 | /** |
||
1008 | * Returns a SerializerFactory creating serializers that generate the most compact serialization. |
||
1009 | * A factory returned has knowledge about items, properties, and the elements they are made of, |
||
1010 | * but no other entity types. |
||
1011 | */ |
||
1012 | public function getCompactBaseDataModelSerializerFactory(): SerializerFactory { |
||
1013 | return $this->getWikibaseServices()->getCompactBaseDataModelSerializerFactory(); |
||
1014 | } |
||
1015 | |||
1016 | /** |
||
1017 | * Returns an entity serializer that generates the most compact serialization. |
||
1018 | */ |
||
1019 | public function getCompactEntitySerializer(): Serializer { |
||
1020 | return $this->getWikibaseServices()->getCompactEntitySerializer(); |
||
1021 | } |
||
1022 | |||
1023 | private function getDataValueDeserializer(): DataValueDeserializer { |
||
1024 | return new DataValueDeserializer( [ |
||
1025 | 'string' => StringValue::class, |
||
1026 | 'unknown' => UnknownValue::class, |
||
1027 | 'globecoordinate' => GlobeCoordinateValue::class, |
||
1028 | 'monolingualtext' => MonolingualTextValue::class, |
||
1029 | 'quantity' => QuantityValue::class, |
||
1030 | 'time' => TimeValue::class, |
||
1031 | 'wikibase-entityid' => function ( $value ) { |
||
1032 | return isset( $value['id'] ) |
||
1033 | ? new EntityIdValue( $this->getEntityIdParser()->parse( $value['id'] ) ) |
||
1034 | : EntityIdValue::newFromArray( $value ); |
||
1035 | }, |
||
1036 | ] ); |
||
1037 | } |
||
1038 | |||
1039 | public function getOtherProjectsSidebarGeneratorFactory(): OtherProjectsSidebarGeneratorFactory { |
||
1040 | return new OtherProjectsSidebarGeneratorFactory( |
||
1041 | $this->settings, |
||
1042 | $this->getStore()->getSiteLinkLookup(), |
||
1043 | $this->siteLookup, |
||
1044 | $this->getStore()->getEntityLookup(), |
||
1045 | $this->getSidebarLinkBadgeDisplay(), |
||
1046 | MediaWikiServices::getInstance()->getHookContainer(), |
||
1047 | $this->getLogger() |
||
1048 | ); |
||
1049 | } |
||
1050 | |||
1051 | public function getEntityChangeFactory(): EntityChangeFactory { |
||
1052 | //TODO: take this from a setting or registry. |
||
1053 | $changeClasses = [ |
||
1054 | Item::ENTITY_TYPE => ItemChange::class, |
||
1055 | // Other types of entities will use EntityChange |
||
1056 | ]; |
||
1057 | |||
1058 | return new EntityChangeFactory( |
||
1059 | $this->getEntityDiffer(), |
||
1060 | $this->getEntityIdParser(), |
||
1061 | $changeClasses, |
||
1062 | EntityChange::class, |
||
1063 | $this->getLogger() |
||
1064 | ); |
||
1065 | } |
||
1066 | |||
1067 | private function getEntityDiffer(): EntityDiffer { |
||
1068 | $entityDiffer = new EntityDiffer(); |
||
1069 | foreach ( $this->entityTypeDefinitions->get( EntityTypeDefinitions::ENTITY_DIFFER_STRATEGY_BUILDER ) as $builder ) { |
||
1070 | $entityDiffer->registerEntityDifferStrategy( call_user_func( $builder ) ); |
||
1071 | } |
||
1072 | return $entityDiffer; |
||
1073 | } |
||
1074 | |||
1075 | private function getStatementGroupRendererFactory(): StatementGroupRendererFactory { |
||
1076 | return new StatementGroupRendererFactory( |
||
1077 | $this->getPropertyLabelResolver(), |
||
1078 | new SnaksFinder(), |
||
1079 | $this->getRestrictedEntityLookup(), |
||
1080 | $this->getDataAccessSnakFormatterFactory(), |
||
1081 | new EntityUsageFactory( $this->getEntityIdParser() ), |
||
1082 | $this->settings->getSetting( 'allowDataAccessInUserLanguage' ) |
||
1083 | ); |
||
1084 | } |
||
1085 | |||
1086 | public function getDataAccessSnakFormatterFactory(): DataAccessSnakFormatterFactory { |
||
1087 | return new DataAccessSnakFormatterFactory( |
||
1088 | $this->getLanguageFallbackChainFactory(), |
||
1089 | $this->getSnakFormatterFactory(), |
||
1090 | $this->getPropertyDataTypeLookup(), |
||
1091 | $this->getRepoItemUriParser(), |
||
1092 | $this->getLanguageFallbackLabelDescriptionLookupFactory(), |
||
1093 | $this->settings->getSetting( 'allowDataAccessInUserLanguage' ) |
||
1094 | ); |
||
1095 | } |
||
1096 | |||
1097 | public function getPropertyParserFunctionRunner(): Runner { |
||
1098 | return new Runner( |
||
1099 | $this->getStatementGroupRendererFactory(), |
||
1100 | $this->getStore()->getSiteLinkLookup(), |
||
1101 | $this->getEntityIdParser(), |
||
1102 | $this->getRestrictedEntityLookup(), |
||
1103 | $this->settings->getSetting( 'siteGlobalID' ), |
||
1104 | $this->settings->getSetting( 'allowArbitraryDataAccess' ) |
||
1105 | ); |
||
1106 | } |
||
1107 | |||
1108 | public function getOtherProjectsSitesProvider(): OtherProjectsSitesProvider { |
||
1109 | return new CachingOtherProjectsSitesProvider( |
||
1110 | new OtherProjectsSitesGenerator( |
||
1111 | $this->siteLookup, |
||
1112 | $this->settings->getSetting( 'siteGlobalID' ), |
||
1113 | $this->settings->getSetting( 'specialSiteLinkGroups' ) |
||
1114 | ), |
||
1115 | // TODO: Make configurable? Should be similar, maybe identical to sharedCacheType and |
||
1116 | // sharedCacheDuration, but can not reuse these because this here is not shared. |
||
1117 | ObjectCache::getLocalClusterInstance(), |
||
1118 | 60 * 60 |
||
1119 | ); |
||
1120 | } |
||
1121 | |||
1122 | private function getAffectedPagesFinder(): AffectedPagesFinder { |
||
1123 | return new AffectedPagesFinder( |
||
1124 | $this->getStore()->getUsageLookup(), |
||
1125 | new TitleFactory(), |
||
1126 | MediaWikiServices::getInstance()->getLinkBatchFactory(), |
||
1127 | $this->settings->getSetting( 'siteGlobalID' ), |
||
1128 | $this->getLogger() |
||
1129 | ); |
||
1130 | } |
||
1131 | |||
1132 | public function getChangeHandler(): ChangeHandler { |
||
1161 | |||
1162 | public function getRecentChangeFactory(): RecentChangeFactory { |
||
1181 | |||
1182 | /** |
||
1183 | * @return string|false |
||
1184 | */ |
||
1185 | public function getDatabaseDomainNameOfLocalRepo() { |
||
1188 | |||
1189 | private function getEntitySourceOfLocalRepo(): EntitySource { |
||
1200 | |||
1201 | public function getWikibaseContentLanguages(): WikibaseContentLanguages { |
||
1208 | |||
1209 | /** |
||
1210 | * Get a ContentLanguages object holding the languages available for labels, descriptions and aliases. |
||
1211 | */ |
||
1212 | public function getTermsLanguages(): ContentLanguages { |
||
1215 | |||
1216 | public function getRestrictedEntityLookup(): RestrictedEntityLookup { |
||
1230 | |||
1231 | public function getPropertyOrderProvider(): PropertyOrderProvider { |
||
1256 | |||
1257 | public function getEntityNamespaceLookup(): EntityNamespaceLookup { |
||
1260 | |||
1261 | public function getDataAccessLanguageFallbackChain( Language $language ): TermLanguageFallbackChain { |
||
1267 | |||
1268 | public function getTermFallbackCache(): TermFallbackCacheFacade { |
||
1269 | return new TermFallbackCacheFacade( |
||
1270 | $this->getTermFallbackCacheFactory()->getTermFallbackCache(), |
||
1271 | $this->getSettings()->getSetting( 'sharedCacheDuration' ) |
||
1272 | ); |
||
1273 | } |
||
1274 | |||
1275 | public function getTermFallbackCacheFactory(): TermFallbackCacheFactory { |
||
1276 | global $wgSecretKey; |
||
1277 | |||
1278 | if ( $this->termFallbackCacheFactory === null ) { |
||
1279 | $this->termFallbackCacheFactory = new TermFallbackCacheFactory( |
||
1280 | $this->settings->getSetting( 'sharedCacheType' ), |
||
1281 | $this->getLogger(), |
||
1282 | MediaWikiServices::getInstance()->getStatsdDataFactory(), |
||
1283 | hash( 'sha256', $wgSecretKey ), |
||
1284 | new TermFallbackCacheServiceFactory(), |
||
1285 | $this->settings->getSetting( 'termFallbackCacheVersion' ) |
||
1286 | ); |
||
1287 | } |
||
1288 | return $this->termFallbackCacheFactory; |
||
1289 | } |
||
1290 | |||
1291 | public function getEntityIdLookup(): EntityIdLookup { |
||
1292 | if ( $this->entityIdLookup === null ) { |
||
1293 | $this->entityIdLookup = new ByTypeDispatchingEntityIdLookup( |
||
1294 | $this->entityTypeDefinitions->get( EntityTypeDefinitions::CONTENT_MODEL_ID ), |
||
1295 | $this->entityTypeDefinitions->get( EntityTypeDefinitions::ENTITY_ID_LOOKUP_CALLBACK ), |
||
1296 | new PagePropsEntityIdLookup( |
||
1297 | MediaWikiServices::getInstance()->getDBLoadBalancer(), |
||
1298 | $this->getEntityIdParser() |
||
1299 | ) |
||
1300 | ); |
||
1301 | } |
||
1302 | |||
1303 | return $this->entityIdLookup; |
||
1304 | } |
||
1305 | |||
1306 | public function getDescriptionLookup(): DescriptionLookup { |
||
1307 | if ( $this->descriptionLookup === null ) { |
||
1308 | $this->descriptionLookup = new DescriptionLookup( |
||
1309 | $this->getEntityIdLookup(), |
||
1310 | $this->getTermBuffer(), |
||
1311 | MediaWikiServices::getInstance()->getPageProps() |
||
1312 | ); |
||
1313 | } |
||
1314 | return $this->descriptionLookup; |
||
1315 | } |
||
1316 | |||
1317 | public function getPropertyLabelResolver(): PropertyLabelResolver { |
||
1318 | if ( $this->propertyLabelResolver === null ) { |
||
1319 | $languageCode = $this->getContentLanguage()->getCode(); |
||
1320 | $cacheKeyPrefix = $this->settings->getSetting( 'sharedCacheKeyPrefix' ); |
||
1321 | $cacheType = $this->settings->getSetting( 'sharedCacheType' ); |
||
1337 | |||
1338 | public function getReferenceFormatterFactory(): ReferenceFormatterFactory { |
||
1353 | |||
1354 | private function getCachedDatabasePropertyLabelResolver( |
||
1382 | |||
1383 | private function getLoadBalancerForConfiguredPropertySource() { |
||
1387 | |||
1388 | private function getDatabaseDomainForPropertySource() { |
||
1393 | |||
1394 | private function getWANObjectCache() { |
||
1397 | |||
1398 | private function getItemSource() { |
||
1407 | |||
1408 | private function getPropertySource() { |
||
1417 | |||
1418 | } |
||
1419 |
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.