diff --git a/docs/topics/hooks-php.md b/docs/topics/hooks-php.md index 3a720a10f1..5e0dd25ccc 100644 --- a/docs/topics/hooks-php.md +++ b/docs/topics/hooks-php.md @@ -29,6 +29,10 @@ See also the [WikibaseClientEntityTypes](#WikibaseClientEntityTypes) hook. Hook handlers may add additional definitions. See [entitytypes documentation] for details. +This hook runs during early initialization; +its handlers must obey the [MediaWikiServicesHook rules](https://doc.wikimedia.org/mediawiki-core/master/php/interfaceMediaWiki_1_1Hook_1_1MediaWikiServicesHook.html), +i.e. not declare any service dependencies nor access any unsafe services dynamically. + Parameters: * &$entityTypeDefinitions * the array of entity type definitions, as defined by WikibaseLib.entitytypes.php. @@ -46,6 +50,10 @@ Parameters: #### WikibaseContentModelMapping {#WikibaseContentModelMapping} Called by [WikibaseRepo::getContentModelMappings()] to allow additional mappings between Entity types and content model identifiers to be defined. +This hook runs during early initialization; +its handlers must obey the [MediaWikiServicesHook rules](https://doc.wikimedia.org/mediawiki-core/master/php/interfaceMediaWiki_1_1Hook_1_1MediaWikiServicesHook.html), +i.e. not declare any service dependencies nor access any unsafe services dynamically. + Parameters: * &$map * An associative array mapping Entity types to content model ids. diff --git a/extension-repo.json b/extension-repo.json index 514b5eec83..109e338aff 100644 --- a/extension-repo.json +++ b/extension-repo.json @@ -1282,6 +1282,7 @@ "LoadExtensionSchemaUpdates": "DatabaseSchemaUpdater", "MaintenanceShellStart": "\\Wikibase\\Repo\\RepoHooks::onMaintenanceShellStart", "MakeGlobalVariablesScript": "MakeGlobalVariablesScript", + "MediaWikiServices": "\\Wikibase\\Repo\\RepoHooks::onMediaWikiServices", "NamespaceIsMovable": "\\Wikibase\\Repo\\RepoHooks::onNamespaceIsMovable", "OutputPageBeforeHTML": "OutputPageBeforeHTML", "OutputPageBodyAttributes": "\\Wikibase\\Repo\\RepoHooks::onOutputPageBodyAttributes", @@ -1297,7 +1298,6 @@ "ViewHooks" ], "RevisionFromEditComplete": "\\Wikibase\\Repo\\RepoHooks::onRevisionFromEditComplete", - "SetupAfterCache": "\\Wikibase\\Repo\\RepoHooks::onSetupAfterCache", "ShowSearchHit": "ShowSearchHit", "ShowSearchHitTitle": "ShowSearchHit", "SidebarBeforeOutput": "\\Wikibase\\Repo\\RepoHooks::onSidebarBeforeOutput", diff --git a/repo/WikibaseRepo.ServiceWiring.php b/repo/WikibaseRepo.ServiceWiring.php index c87d30f65e..0c095ce82d 100644 --- a/repo/WikibaseRepo.ServiceWiring.php +++ b/repo/WikibaseRepo.ServiceWiring.php @@ -447,11 +447,20 @@ }, 'WikibaseRepo.ContentModelMappings' => function ( MediaWikiServices $services ): array { + /** + * Warning: This is an early initialization service. + * We must not use any MediaWiki services here (except the HookContainer), + * and the same is true for any other Wikibase services we use; + * see the warning in {@link MediaWikiServicesHook::onMediaWikiServices()}. + */ $map = WikibaseRepo::getEntityTypeDefinitions( $services ) ->get( EntityTypeDefinitions::CONTENT_MODEL_ID ); - $services->getHookContainer() - ->run( 'WikibaseContentModelMapping', [ &$map ] ); + $services->getHookContainer()->run( + 'WikibaseContentModelMapping', + [ &$map ], + [ 'noServices' => true ] // early initialization + ); return $map; }, @@ -862,6 +871,12 @@ }, 'WikibaseRepo.EntityNamespaceLookup' => function ( MediaWikiServices $services ): EntityNamespaceLookup { + /** + * Warning: This is an early initialization service. + * We must not use any MediaWiki services here (except the HookContainer), + * and the same is true for any other Wikibase services we use; + * see the warning in {@link MediaWikiServicesHook::onMediaWikiServices()}. + */ $entitySources = array_filter( WikibaseRepo::getEntitySourceDefinitions( $services )->getSources(), function ( EntitySource $entitySource ) { @@ -996,7 +1011,11 @@ function ( EntityNamespaceLookup $nsLookup, DatabaseEntitySource $source ): Enti $baseEntityTypes ); - $services->getHookContainer()->run( 'WikibaseRepoEntityTypes', [ &$entityTypes ] ); + $services->getHookContainer()->run( + 'WikibaseRepoEntityTypes', + [ &$entityTypes ], + [ 'noServices' => true ] // to match WikibaseRepo.EntityTypeDefinitions below + ); $entityTypeDefinitionsBySourceType = [ DatabaseEntitySource::TYPE => new EntityTypeDefinitions( $entityTypes ) ]; @@ -1013,6 +1032,12 @@ function ( EntityNamespaceLookup $nsLookup, DatabaseEntitySource $source ): Enti }, 'WikibaseRepo.EntitySourceDefinitions' => function ( MediaWikiServices $services ): EntitySourceDefinitions { + /** + * Warning: This is an early initialization service. + * We must not use any MediaWiki services here (except the HookContainer), + * and the same is true for any other Wikibase services we use; + * see the warning in {@link MediaWikiServicesHook::onMediaWikiServices()}. + */ $settings = WikibaseRepo::getSettings( $services ); $subEntityTypesMapper = WikibaseRepo::getSubEntityTypesMapper( $services ); @@ -1084,6 +1109,12 @@ function ( EntityNamespaceLookup $nsLookup, DatabaseEntitySource $source ): Enti }, 'WikibaseRepo.EntityTypeDefinitions' => function ( MediaWikiServices $services ): EntityTypeDefinitions { + /** + * Warning: This is an early initialization service. + * We must not use any MediaWiki services here (except the HookContainer), + * and the same is true for any other Wikibase services we use; + * see the warning in {@link MediaWikiServicesHook::onMediaWikiServices()}. + */ $baseEntityTypes = require __DIR__ . '/../lib/WikibaseLib.entitytypes.php'; $repoEntityTypes = require __DIR__ . '/WikibaseRepo.entitytypes.php'; @@ -1092,7 +1123,11 @@ function ( EntityNamespaceLookup $nsLookup, DatabaseEntitySource $source ): Enti $baseEntityTypes ); - $services->getHookContainer()->run( 'WikibaseRepoEntityTypes', [ &$entityTypes ] ); + $services->getHookContainer()->run( + 'WikibaseRepoEntityTypes', + [ &$entityTypes ], + [ 'noServices' => true ] // early initialization + ); return new EntityTypeDefinitions( $entityTypes ); }, @@ -1436,6 +1471,12 @@ function ( EntityNamespaceLookup $nsLookup, DatabaseEntitySource $source ): Enti }, 'WikibaseRepo.LocalEntitySource' => function ( MediaWikiServices $services ): EntitySource { + /** + * Warning: This is an early initialization service. + * We must not use any MediaWiki services here (except the HookContainer), + * and the same is true for any other Wikibase services we use; + * see the warning in {@link MediaWikiServicesHook::onMediaWikiServices()}. + */ $localEntitySourceName = WikibaseRepo::getSettings( $services )->getSetting( 'localEntitySourceName' ); $sources = WikibaseRepo::getEntitySourceDefinitions( $services )->getSources(); foreach ( $sources as $source ) { @@ -1723,6 +1764,12 @@ function ( $types, $localTypeName ) use ( $subEntityTypes ) { }, 'WikibaseRepo.Settings' => function ( MediaWikiServices $services ): SettingsArray { + /** + * Warning: This is an early initialization service. + * We must not use any MediaWiki services here (except the HookContainer), + * and the same is true for any other Wikibase services we use; + * see the warning in {@link MediaWikiServicesHook::onMediaWikiServices()}. + */ return WikibaseSettings::getRepoSettings(); }, @@ -1864,6 +1911,12 @@ function ( $types, $localTypeName ) use ( $subEntityTypes ) { }, 'WikibaseRepo.SubEntityTypesMapper' => function ( MediaWikiServices $services ): SubEntityTypesMapper { + /** + * Warning: This is an early initialization service. + * We must not use any MediaWiki services here (except the HookContainer), + * and the same is true for any other Wikibase services we use; + * see the warning in {@link MediaWikiServicesHook::onMediaWikiServices()}. + */ return new SubEntityTypesMapper( WikibaseRepo::getEntityTypeDefinitions( $services ) ->get( EntityTypeDefinitions::SUB_ENTITY_TYPES ) ); }, diff --git a/repo/includes/RepoHooks.php b/repo/includes/RepoHooks.php index ff2f3dc182..9fb13aa60e 100644 --- a/repo/includes/RepoHooks.php +++ b/repo/includes/RepoHooks.php @@ -116,26 +116,28 @@ public static function onBeforePageDisplay( OutputPage $out, Skin $skin ) { } /** - * Handler for the SetupAfterCache hook, completing the content and namespace setup. + * Handler for the MediaWikiServices hook, completing the content and namespace setup. * This updates the $wgContentHandlers and $wgNamespaceContentModels registries * according to information provided by entity type definitions and the entityNamespaces * setting for the local entity source. + * Note that we must not access any MediaWiki core services here (except for the hook container); + * see the warning in {@link \MediaWiki\Hook\MediaWikiServicesHook::onMediaWikiServices()}. */ - public static function onSetupAfterCache() { + public static function onMediaWikiServices( MediaWikiServices $services ) { global $wgContentHandlers, $wgNamespaceContentModels; - if ( WikibaseRepo::getSettings()->getSetting( 'defaultEntityNamespaces' ) ) { + if ( WikibaseRepo::getSettings( $services )->getSetting( 'defaultEntityNamespaces' ) ) { self::defaultEntityNamespaces(); } - $namespaces = WikibaseRepo::getLocalEntitySource()->getEntityNamespaceIds(); - $namespaceLookup = WikibaseRepo::getEntityNamespaceLookup(); + $namespaces = WikibaseRepo::getLocalEntitySource( $services )->getEntityNamespaceIds(); + $namespaceLookup = WikibaseRepo::getEntityNamespaceLookup( $services ); // Register entity namespaces. // Note that $wgExtraNamespaces and $wgNamespaceAliases have already been processed at this // point and should no longer be touched. - $contentModelIds = WikibaseRepo::getContentModelMappings(); + $contentModelIds = WikibaseRepo::getContentModelMappings( $services ); foreach ( $namespaces as $entityType => $namespace ) { // TODO: once there is a mechanism for registering the default content model for @@ -151,8 +153,8 @@ public static function onSetupAfterCache() { // Register callbacks for instantiating ContentHandlers for EntityContent. foreach ( $contentModelIds as $entityType => $model ) { - $wgContentHandlers[$model] = function () use ( $entityType ) { - $entityContentFactory = WikibaseRepo::getEntityContentFactory(); + $wgContentHandlers[$model] = function () use ( $services, $entityType ) { + $entityContentFactory = WikibaseRepo::getEntityContentFactory( $services ); return $entityContentFactory->getContentHandlerForType( $entityType ); }; } diff --git a/repo/tests/phpunit/includes/RepoHooksTest.php b/repo/tests/phpunit/includes/RepoHooksTest.php index e750fab612..096bc300fe 100644 --- a/repo/tests/phpunit/includes/RepoHooksTest.php +++ b/repo/tests/phpunit/includes/RepoHooksTest.php @@ -452,7 +452,7 @@ private function setupTestOnContentModelCanBeUsedOn() { $this->assertSame( 'someSlot', $nsLookup->getEntitySlotRole( $type ) ); } - public function testOnSetupAfterCache() { + public function testOnMediaWikiServices() { global $wgWBRepoSettings, $wgNamespaceContentModels, $wgContentHandlers; @@ -488,7 +488,7 @@ public function testOnSetupAfterCache() { $contentModelMappings ); - RepoHooks::onSetupAfterCache(); + RepoHooks::onMediaWikiServices( $this->getServiceContainer() ); $this->assertSame( [ WB_NS_ITEM => 'wikibase-item' ], $wgNamespaceContentModels ); $this->assertSame( array_values( $contentModelMappings ), array_keys( $wgContentHandlers ) );