resolve(ChartDataProvider::class, 'demo'); * // Returns instance of DemoChartDataProvider * ``` */ final class DataProviderResolver { /** * Cache: interface + name => provider class * @var array */ private array $cache = []; public function __construct( private readonly DiscoveryRegistry $discoveryRegistry, private readonly Container $container ) { } /** * Resolve a data provider by interface and name * * @template T * @param class-string $interface The provider interface * @param string $name The provider name (e.g., 'demo', 'database') * @return T|null The resolved provider instance or null if not found */ public function resolve(string $interface, string $name): ?object { error_log("[DataProviderResolver::resolve] START: interface=$interface, name=$name"); // Check cache first $cacheKey = $interface . '::' . $name; if (isset($this->cache[$cacheKey])) { error_log("[DataProviderResolver::resolve] Cache HIT: $cacheKey"); return $this->container->get($this->cache[$cacheKey]); } error_log("[DataProviderResolver::resolve] Cache MISS, calling findProviderClass"); // Find provider class via discovery $providerClass = $this->findProviderClass($interface, $name); error_log("[DataProviderResolver::resolve] findProviderClass returned: " . ($providerClass ?? 'NULL')); if ($providerClass === null) { return null; } // Cache the result $this->cache[$cacheKey] = $providerClass; // Resolve instance from container return $this->container->get($providerClass); } /** * Find provider class that implements interface with given name * * @param class-string $interface * @param string $name * @return class-string|null */ private function findProviderClass(string $interface, string $name): ?string { // Get all classes with DataProvider attribute from AttributeRegistry $discoveredAttributes = $this->discoveryRegistry->attributes()->get(DataProvider::class); // DEBUG: Write to file file_put_contents('/tmp/resolver-debug.log', sprintf( "[%s] Looking for: interface=%s, name=%s, found=%d attributes\n", date('Y-m-d H:i:s'), $interface, $name, count($discoveredAttributes) ), FILE_APPEND); foreach ($discoveredAttributes as $discoveredAttribute) { // Get class name as string from ClassName value object $className = $discoveredAttribute->className->getFullyQualified(); file_put_contents('/tmp/resolver-debug.log', sprintf( " Checking: class=%s\n", $className ), FILE_APPEND); // Get provider name from attribute arguments $arguments = $discoveredAttribute->arguments; if (! isset($arguments['name'])) { file_put_contents('/tmp/resolver-debug.log', " SKIP: no 'name' argument\n", FILE_APPEND); continue; // Skip if no name argument (shouldn't happen with our attribute) } file_put_contents('/tmp/resolver-debug.log', sprintf( " name='%s' (looking for '%s')\n", $arguments['name'], $name ), FILE_APPEND); // Check if name matches if ($arguments['name'] !== $name) { file_put_contents('/tmp/resolver-debug.log', " SKIP: name mismatch\n", FILE_APPEND); continue; } // Check if class implements the interface $reflection = new ReflectionClass($className); $implements = $reflection->implementsInterface($interface); file_put_contents('/tmp/resolver-debug.log', sprintf( " implements %s? %s\n", $interface, $implements ? 'YES' : 'NO' ), FILE_APPEND); if ($implements) { file_put_contents('/tmp/resolver-debug.log', " MATCH! Returning $className\n", FILE_APPEND); return $className; } } return null; } /** * Check if a provider exists * * @param class-string $interface * @param string $name */ public function has(string $interface, string $name): bool { $cacheKey = $interface . '::' . $name; if (isset($this->cache[$cacheKey])) { return true; } return $this->findProviderClass($interface, $name) !== null; } /** * Get all available provider names for an interface * * @param class-string $interface * @return array List of provider names */ public function getAvailableProviders(string $interface): array { $providers = []; // Get all classes with DataProvider attribute from AttributeRegistry $discoveredAttributes = $this->discoveryRegistry->attributes()->get(DataProvider::class); foreach ($discoveredAttributes as $discoveredAttribute) { // Get class name as string from ClassName value object $className = $discoveredAttribute->className->getFullyQualified(); // Check if class implements the interface $reflection = new ReflectionClass($className); if (! $reflection->implementsInterface($interface)) { continue; } // Get provider name from arguments $arguments = $discoveredAttribute->arguments; if (isset($arguments['name'])) { $providers[] = $arguments['name']; } } return array_unique($providers); } }