From f75e6cbad5a3438e256387b593196df0c6a9087c Mon Sep 17 00:00:00 2001 From: Felix Arntz Date: Fri, 17 Oct 2025 11:16:01 -0700 Subject: [PATCH 1/2] Make provider ID lookup by class more efficient. --- src/Providers/ProviderRegistry.php | 38 +++++++++---------- tests/unit/Providers/ProviderRegistryTest.php | 14 +++++++ 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/Providers/ProviderRegistry.php b/src/Providers/ProviderRegistry.php index 00935042..f0bc5a1f 100644 --- a/src/Providers/ProviderRegistry.php +++ b/src/Providers/ProviderRegistry.php @@ -38,12 +38,12 @@ class ProviderRegistry implements WithHttpTransporterInterface /** * @var array> Mapping of provider IDs to class names. */ - private array $providerClassNames = []; + private array $registeredIdsToClassNames = []; /** - * @var array, true> Set of registered class names for fast lookup. + * @var array, string> Mapping of provider class names to IDs. */ - private array $registeredClassNames = []; + private array $registeredClassNamesToIds = []; /** * @var array, RequestAuthenticationInterface> Mapping of provider class names to @@ -109,8 +109,8 @@ public function registerProvider(string $className): void $this->setRequestAuthenticationForProvider($className, $this->providerAuthenticationInstances[$className]); } - $this->providerClassNames[$metadata->getId()] = $className; - $this->registeredClassNames[$className] = true; + $this->registeredIdsToClassNames[$metadata->getId()] = $className; + $this->registeredClassNamesToIds[$className] = $metadata->getId(); } /** @@ -122,7 +122,7 @@ public function registerProvider(string $className): void */ public function getRegisteredProviderIds(): array { - return array_keys($this->providerClassNames); + return array_keys($this->registeredIdsToClassNames); } /** @@ -135,8 +135,8 @@ public function getRegisteredProviderIds(): array */ public function hasProvider(string $idOrClassName): bool { - return isset($this->providerClassNames[$idOrClassName]) || - isset($this->registeredClassNames[$idOrClassName]); + return isset($this->registeredIdsToClassNames[$idOrClassName]) || + isset($this->registeredClassNamesToIds[$idOrClassName]); } /** @@ -150,13 +150,13 @@ public function hasProvider(string $idOrClassName): bool */ public function getProviderClassName(string $id): string { - if (!isset($this->providerClassNames[$id])) { + if (!isset($this->registeredIdsToClassNames[$id])) { throw new InvalidArgumentException( sprintf('Provider not registered: %s', $id) ); } - return $this->providerClassNames[$id]; + return $this->registeredIdsToClassNames[$id]; } /** @@ -171,17 +171,13 @@ public function getProviderClassName(string $id): string public function getProviderId(string $idOrClassName): string { // If it's already an ID, return it - if (isset($this->providerClassNames[$idOrClassName])) { + if (isset($this->registeredIdsToClassNames[$idOrClassName])) { return $idOrClassName; } - // If it's a class name, find its ID - if (isset($this->registeredClassNames[$idOrClassName])) { - foreach ($this->providerClassNames as $id => $className) { - if ($className === $idOrClassName) { - return $id; - } - } + // If it's a registered class name, return its ID + if (isset($this->registeredClassNamesToIds[$idOrClassName])) { + return $this->registeredClassNamesToIds[$idOrClassName]; } // Not found @@ -225,7 +221,7 @@ public function findModelsMetadataForSupport(ModelRequirements $modelRequirement { $results = []; - foreach ($this->providerClassNames as $providerId => $className) { + foreach ($this->registeredIdsToClassNames as $providerId => $className) { $providerResults = $this->findProviderModelsMetadataForSupport($providerId, $modelRequirements); if (!empty($providerResults)) { // Use static method from ProviderInterface @@ -337,7 +333,7 @@ public function bindModelDependencies(ModelInterface $modelInstance): void private function resolveProviderClassName(string $idOrClassName): string { // Handle both ID and class name - $className = $this->providerClassNames[$idOrClassName] ?? $idOrClassName; + $className = $this->registeredIdsToClassNames[$idOrClassName] ?? $idOrClassName; if (!$this->hasProvider($idOrClassName)) { throw new InvalidArgumentException( @@ -359,7 +355,7 @@ public function setHttpTransporter(HttpTransporterInterface $httpTransporter): v $this->setHttpTransporterOriginal($httpTransporter); // Make sure all registered providers have the HTTP transporter hooked up as needed. - foreach ($this->providerClassNames as $className) { + foreach ($this->registeredIdsToClassNames as $className) { $this->setHttpTransporterForProvider($className, $httpTransporter); } } diff --git a/tests/unit/Providers/ProviderRegistryTest.php b/tests/unit/Providers/ProviderRegistryTest.php index e0cae3bc..8a92012b 100644 --- a/tests/unit/Providers/ProviderRegistryTest.php +++ b/tests/unit/Providers/ProviderRegistryTest.php @@ -51,6 +51,7 @@ public function testRegisterProviderWithValidProvider(): void $this->assertTrue($this->registry->hasProvider('mock')); $this->assertTrue($this->registry->hasProvider(MockProvider::class)); $this->assertEquals(MockProvider::class, $this->registry->getProviderClassName('mock')); + $this->assertEquals('mock', $this->registry->getProviderId(MockProvider::class)); } /** @@ -105,6 +106,19 @@ public function testGetProviderClassNameWithUnregisteredProvider(): void $this->registry->getProviderClassName('nonexistent'); } + /** + * Tests getProviderId with unregistered provider. + * + * @return void + */ + public function testGetProviderIdWithUnregisteredProvider(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Provider not registered: ' . InvalidArgumentException::class); + + $this->registry->getProviderId(InvalidArgumentException::class); + } + /** * Tests isProviderConfigured with registered provider. * From 145842cefec96e2dc3d677a4689db42f18ba6aa5 Mon Sep 17 00:00:00 2001 From: Felix Arntz Date: Fri, 17 Oct 2025 11:21:10 -0700 Subject: [PATCH 2/2] Make getProviderClassName behave consistently with getProviderId. --- src/Providers/ProviderRegistry.php | 21 ++++++++++++------- tests/unit/Providers/ProviderRegistryTest.php | 4 ++++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/Providers/ProviderRegistry.php b/src/Providers/ProviderRegistry.php index f0bc5a1f..d1bcff01 100644 --- a/src/Providers/ProviderRegistry.php +++ b/src/Providers/ProviderRegistry.php @@ -144,19 +144,26 @@ public function hasProvider(string $idOrClassName): bool * * @since 0.1.0 * - * @param string $id The provider ID. + * @param string|class-string $idOrClassName The provider ID or class name. * @return string The provider class name. * @throws InvalidArgumentException If the provider is not registered. */ - public function getProviderClassName(string $id): string + public function getProviderClassName(string $idOrClassName): string { - if (!isset($this->registeredIdsToClassNames[$id])) { - throw new InvalidArgumentException( - sprintf('Provider not registered: %s', $id) - ); + // If it's already a class name, return it + if (isset($this->registeredClassNamesToIds[$idOrClassName])) { + return $idOrClassName; + } + + // If it's a registered ID, return its class name + if (isset($this->registeredIdsToClassNames[$idOrClassName])) { + return $this->registeredIdsToClassNames[$idOrClassName]; } - return $this->registeredIdsToClassNames[$id]; + // Not found + throw new InvalidArgumentException( + sprintf('Provider not registered: %s', $idOrClassName) + ); } /** diff --git a/tests/unit/Providers/ProviderRegistryTest.php b/tests/unit/Providers/ProviderRegistryTest.php index 8a92012b..6c08fbae 100644 --- a/tests/unit/Providers/ProviderRegistryTest.php +++ b/tests/unit/Providers/ProviderRegistryTest.php @@ -52,6 +52,10 @@ public function testRegisterProviderWithValidProvider(): void $this->assertTrue($this->registry->hasProvider(MockProvider::class)); $this->assertEquals(MockProvider::class, $this->registry->getProviderClassName('mock')); $this->assertEquals('mock', $this->registry->getProviderId(MockProvider::class)); + + // Ensure calling the lookup functions with a value in the correct format simply returns it as is. + $this->assertEquals(MockProvider::class, $this->registry->getProviderClassName(MockProvider::class)); + $this->assertEquals('mock', $this->registry->getProviderId('mock')); } /**