$id, 'name' => 'Test User']; } } // Scenario 2: Auto-detect single interface #[DefaultImplementation] final readonly class FileUserRepository implements UserRepository { public function findById(string $id): ?object { return (object) ['id' => $id, 'name' => 'File User']; } } // Scenario 3: Auto-detect with multiple interfaces (should use first) #[DefaultImplementation] final readonly class AppLogger implements LoggerInterface, FormattableInterface { public function log(string $message): void { echo "[LOG] $message\n"; } public function format(): string { return 'AppLogger'; } } // Scenario 4: Invalid - explicit interface not implemented (for error testing) #[DefaultImplementation(LoggerInterface::class)] final readonly class BadRepository implements UserRepository { public function findById(string $id): ?object { return null; } } // Scenario 5: Invalid - no interfaces implemented (for error testing) #[DefaultImplementation] final readonly class NoInterfaceClass { public function doSomething(): void {} } // Helper function to create DiscoveredAttribute function createDiscoveredAttribute(string $className, array $attributeArgs = []): DiscoveredAttribute { return new DiscoveredAttribute( className: ClassName::create($className), attributeClass: DefaultImplementation::class, target: AttributeTarget::TARGET_CLASS, arguments: $attributeArgs ); } // Helper function to create DiscoveryRegistry with specific attributes function createRegistryWithAttributes(array $discoveredAttributes): DiscoveryRegistry { $attributeRegistry = new AttributeRegistry(); foreach ($discoveredAttributes as $attr) { $attributeRegistry->add(DefaultImplementation::class, $attr); } return new DiscoveryRegistry( attributes: $attributeRegistry, interfaces: new \App\Framework\Discovery\Results\InterfaceRegistry(), templates: new \App\Framework\Discovery\Results\TemplateRegistry() ); } // Test Runner echo "=== DefaultImplementation Attribute Tests ===\n\n"; // Test 1: Explicit Interface Specification echo "Test 1: Explicit interface specification\n"; try { $container = new DefaultContainer(); $processor = new DefaultImplementationProcessor($container); $discovered = createDiscoveredAttribute(DatabaseUserRepository::class, ['interface' => UserRepository::class]); $registry = createRegistryWithAttributes([$discovered]); $registered = $processor->process($registry); echo "✓ Registered $registered binding(s)\n"; echo "✓ Container has binding: " . ($container->has(UserRepository::class) ? 'YES' : 'NO') . "\n"; $instance = $container->get(UserRepository::class); echo "✓ Instance type: " . get_class($instance) . "\n"; echo "✓ Find user: " . json_encode($instance->findById('123')) . "\n"; echo "PASSED\n\n"; } catch (\Exception $e) { echo "✗ FAILED: " . $e->getMessage() . "\n\n"; } // Test 2: Auto-detect Single Interface echo "Test 2: Auto-detect single interface\n"; try { $container = new DefaultContainer(); $processor = new DefaultImplementationProcessor($container); $discovered = createDiscoveredAttribute(FileUserRepository::class, []); // No explicit interface $registry = createRegistryWithAttributes([$discovered]); $registered = $processor->process($registry); echo "✓ Registered $registered binding(s)\n"; echo "✓ Container has binding: " . ($container->has(UserRepository::class) ? 'YES' : 'NO') . "\n"; $instance = $container->get(UserRepository::class); echo "✓ Instance type: " . get_class($instance) . "\n"; echo "PASSED\n\n"; } catch (\Exception $e) { echo "✗ FAILED: " . $e->getMessage() . "\n\n"; } // Test 3: Auto-detect Multiple Interfaces (uses first) echo "Test 3: Auto-detect with multiple interfaces\n"; try { $container = new DefaultContainer(); $processor = new DefaultImplementationProcessor($container); $discovered = createDiscoveredAttribute(AppLogger::class, []); $registry = createRegistryWithAttributes([$discovered]); $registered = $processor->process($registry); echo "✓ Registered $registered binding(s)\n"; echo "✓ Container has LoggerInterface: " . ($container->has(LoggerInterface::class) ? 'YES' : 'NO') . "\n"; $instance = $container->get(LoggerInterface::class); echo "✓ Instance type: " . get_class($instance) . "\n"; $instance->log("Test message"); echo "PASSED\n\n"; } catch (\Exception $e) { echo "✗ FAILED: " . $e->getMessage() . "\n\n"; } // Test 4: Error - Explicit Interface Not Implemented echo "Test 4: Error - explicit interface not implemented\n"; try { $container = new DefaultContainer(); $processor = new DefaultImplementationProcessor($container); $discovered = createDiscoveredAttribute(BadRepository::class, ['interface' => LoggerInterface::class]); $registry = createRegistryWithAttributes([$discovered]); $processor->process($registry); echo "✗ FAILED: Should have thrown exception\n\n"; } catch (DefaultImplementationException $e) { echo "✓ Caught expected exception: " . $e->getMessage() . "\n"; echo "PASSED\n\n"; } // Test 5: Error - No Interfaces Implemented echo "Test 5: Error - no interfaces implemented\n"; try { $container = new DefaultContainer(); $processor = new DefaultImplementationProcessor($container); $discovered = createDiscoveredAttribute(NoInterfaceClass::class, []); $registry = createRegistryWithAttributes([$discovered]); $processor->process($registry); echo "✗ FAILED: Should have thrown exception\n\n"; } catch (DefaultImplementationException $e) { echo "✓ Caught expected exception: " . $e->getMessage() . "\n"; echo "PASSED\n\n"; } // Test 6: Multiple Bindings echo "Test 6: Multiple bindings in one registry\n"; try { $container = new DefaultContainer(); $processor = new DefaultImplementationProcessor($container); $discovered1 = createDiscoveredAttribute(DatabaseUserRepository::class, ['interface' => UserRepository::class]); $discovered2 = createDiscoveredAttribute(AppLogger::class, []); $registry = createRegistryWithAttributes([$discovered1, $discovered2]); $registered = $processor->process($registry); echo "✓ Registered $registered binding(s)\n"; echo "✓ Has UserRepository: " . ($container->has(UserRepository::class) ? 'YES' : 'NO') . "\n"; echo "✓ Has LoggerInterface: " . ($container->has(LoggerInterface::class) ? 'YES' : 'NO') . "\n"; echo "PASSED\n\n"; } catch (\Exception $e) { echo "✗ FAILED: " . $e->getMessage() . "\n\n"; } // Test 7: Singleton Registration (verify it's actually singleton) echo "Test 7: Singleton registration verification\n"; try { $container = new DefaultContainer(); $processor = new DefaultImplementationProcessor($container); $discovered = createDiscoveredAttribute(DatabaseUserRepository::class, ['interface' => UserRepository::class]); $registry = createRegistryWithAttributes([$discovered]); $processor->process($registry); $instance1 = $container->get(UserRepository::class); $instance2 = $container->get(UserRepository::class); echo "✓ Instance 1 ID: " . spl_object_id($instance1) . "\n"; echo "✓ Instance 2 ID: " . spl_object_id($instance2) . "\n"; echo "✓ Same instance: " . ($instance1 === $instance2 ? 'YES' : 'NO') . "\n"; echo "PASSED\n\n"; } catch (\Exception $e) { echo "✗ FAILED: " . $e->getMessage() . "\n\n"; } echo "=== All Tests Complete ===\n";