Page MenuHomePhabricator

ExtensionRegistry is difficult to use as a singleton
Open, LowPublic

Description

Problem
Many extensions use ExtensionRegistry::isLoaded() to determine if another extension is loaded or not. Since the class is a singleton, it requires SpecialPages, HookHandlers, or API Modules to use a factory in order to inject the registry or they can use it directly which makes testing more difficult.

Proposed Solution
Create a new service that will replace ExtensionRegistry::isLoaded()

Event Timeline

dbarratt updated the task description. (Show Details)
BPirkle moved this task from Inbox to Feature Requests to Review on the Platform Engineering board.

Change 697173 had a related patch set uploaded (by Zabe; author: Zabe):

[mediawiki/core@master] ExtensionRegistry: Factor out ::isLoaded() into a service

https://gerrit.wikimedia.org/r/697173

Legoktm subscribed.

I never saw this because it was filed in the wrong project. I wrote on Gerrit that I don't see what value this provides, a clear usecase or proposed extension patch using it would help a lot in understanding.

An “extension loaded?” service would also let us remove this sort of hack:

ChangesListSpecialPageHookHandlerTest
	protected function setUp(): void {
		parent::setUp();

		$this->extensionRegistry = TestingAccessWrapper::newFromObject(
			ExtensionRegistry::getInstance()
		);

		$this->oldExtensionRegistryLoaded = $this->extensionRegistry->loaded;

		// Forget about the other extensions.  Although ORES may be loaded,
		// since this is only unit-testing *our* listener, ORES's listener
		// hasn't run, so for all intents and purposes it's not loaded.
		$this->extensionRegistry->loaded = array_intersect_key(
			$this->extensionRegistry->loaded,
			[
				'WikibaseRepository' => [],
				'WikibaseClient' => [],
			]
		);
	}

	protected function tearDown(): void {
		parent::tearDown();

		$this->extensionRegistry->loaded = $this->oldExtensionRegistryLoaded;
	}

@Lucas_Werkmeister_WMDE in that example, I assume your code is checking ExtensionRegistry::getInstance()->isLoaded('ORES') and varying behavior on it, but you just want to test the non-ORES functionality?

Wouldn't it be easier to just have a $this->enableORES flag, and simply inject the isLoaded() boolean result instead of checking the registry at runtime?

I guess that would work for this case (to me it doesn’t seem easier or better, to be honest). I don’t see how that approach would work for T281691 – we definitely need something in the service container there.

I guess I’ll keep adding more comments here as I continue to find use cases, if you’re not happy with the ones given so far…