# DefaultImplementation Attribute Automatic DI Container registration for interface implementations using attributes. ## Overview The `#[DefaultImplementation]` attribute enables automatic registration of classes as default implementations for interfaces in the DI container. This eliminates boilerplate container configuration while maintaining framework patterns (readonly, final, composition). ## Features - **Optional Interface Parameter**: Specify interface explicitly or auto-detect - **Auto-Detection**: Automatically uses first implemented interface if not specified - **Validation**: Ensures class actually implements the specified/detected interface - **Singleton Registration**: All bindings are registered as singletons - **Framework Integration**: Fully integrated with Discovery system and caching - **Type Safety**: Full type safety with descriptive validation exceptions ## Usage ### Explicit Interface Specification ```php interface UserRepository { public function findById(string $id): ?User; } #[DefaultImplementation(UserRepository::class)] final readonly class DatabaseUserRepository implements UserRepository { public function __construct( private readonly DatabaseConnection $db ) {} public function findById(string $id): ?User { // Implementation } } // Container automatically binds UserRepository::class => DatabaseUserRepository::class $userRepo = $container->get(UserRepository::class); // DatabaseUserRepository instance ``` ### Auto-Detection (Single Interface) ```php interface LoggerInterface { public function log(string $message): void; } #[DefaultImplementation] // No explicit interface - auto-detects LoggerInterface final readonly class FileLogger implements LoggerInterface { public function log(string $message): void { // Implementation } } // Container automatically binds LoggerInterface::class => FileLogger::class $logger = $container->get(LoggerInterface::class); // FileLogger instance ``` ### Auto-Detection (Multiple Interfaces) When a class implements multiple interfaces, the **first interface** is used for auto-detection: ```php #[DefaultImplementation] // Auto-detects LoggerInterface (first interface) final readonly class AppLogger implements LoggerInterface, Stringable { public function log(string $message): void { echo "[LOG] $message\n"; } public function __toString(): string { return 'AppLogger'; } } // Binds: LoggerInterface::class => AppLogger::class ``` ## Validation The attribute validates interface implementation at application bootstrap: ### Valid Usage ```php // ✓ Explicit interface that class implements #[DefaultImplementation(UserRepository::class)] final readonly class DatabaseUserRepository implements UserRepository {} // ✓ Auto-detect with single interface #[DefaultImplementation] final readonly class FileLogger implements LoggerInterface {} // ✓ Auto-detect with multiple interfaces (uses first) #[DefaultImplementation] final readonly class AppLogger implements LoggerInterface, Stringable {} ``` ### Invalid Usage (Throws Exceptions) ```php // ✗ Explicit interface NOT implemented #[DefaultImplementation(LoggerInterface::class)] final readonly class BadRepository implements UserRepository {} // Throws: DefaultImplementationException // "Class 'BadRepository' has #[DefaultImplementation] for interface 'LoggerInterface' // but does not implement that interface" // ✗ Auto-detect with NO interfaces #[DefaultImplementation] final readonly class PlainClass {} // Throws: DefaultImplementationException // "Class 'PlainClass' has #[DefaultImplementation] without explicit interface but // implements no interfaces. Either specify an interface explicitly or ensure the // class implements at least one interface." // ✗ Interface does not exist #[DefaultImplementation(NonExistentInterface::class)] final readonly class SomeClass implements ActualInterface {} // Throws: DefaultImplementationException // "Class 'SomeClass' has #[DefaultImplementation] for interface 'NonExistentInterface' // which does not exist" ``` ## How It Works ### 1. Discovery Phase During application bootstrap, the Discovery system scans for `#[DefaultImplementation]` attributes: ```php // Automatic discovery via UnifiedDiscoveryService $discoveryService = $container->get(UnifiedDiscoveryService::class); $results = $discoveryService->discover(); ``` ### 2. Processing Phase The `DefaultImplementationProcessor` processes discovered attributes: ```php // Automatically called in DiscoveryServiceBootstrapper $processor = new DefaultImplementationProcessor($container); $processor->process($results); // Registers all bindings ``` ### 3. Container Registration Each valid attribute creates a singleton binding: ```php // For each DefaultImplementation attribute: $container->singleton($interface, $className); ``` ### 4. Resolution Classes are instantiated on-demand by the container: ```php $instance = $container->get(UserRepository::class); // Returns: DatabaseUserRepository instance (singleton) ``` ## Integration Points ### Discovery System The attribute is automatically discovered via the framework's Discovery system: 1. **Attribute Scanning**: `UnifiedDiscoveryService` finds all `#[DefaultImplementation]` attributes 2. **Caching**: Discovered attributes are cached along with other discovery results 3. **Processor Execution**: `DefaultImplementationProcessor` runs after discovery 4. **Before Initializers**: Processed **before** `#[Initializer]` methods to ensure dependencies are available ### Bootstrap Flow ``` AppBootstrapper └─> ContainerBootstrapper └─> DiscoveryServiceBootstrapper ├─> UnifiedDiscoveryService::discover() ├─> DefaultImplementationProcessor::process() ← Registers bindings └─> InitializerProcessor::processInitializers() ← Can depend on bindings ``` ## Framework Compliance The implementation follows all Custom PHP Framework principles: ✓ **No Inheritance**: `DefaultImplementationProcessor` is final and uses composition ✓ **Readonly Classes**: Attribute and processor are readonly ✓ **Value Objects**: Uses `ClassName`, `DiscoveredAttribute`, `DiscoveryRegistry` ✓ **Explicit DI**: Constructor injection throughout ✓ **Attribute-Driven**: Convention over configuration ✓ **Discovery Integration**: Automatic registration via framework's attribute system ## Testing Comprehensive tests verify all scenarios: ```bash # Run test suite php tests/debug/test-default-implementation.php ``` **Test Coverage**: - ✓ Explicit interface specification - ✓ Auto-detect single interface - ✓ Auto-detect multiple interfaces (uses first) - ✓ Validation: interface not implemented - ✓ Validation: no interfaces implemented - ✓ Validation: interface does not exist - ✓ Multiple bindings in one registry - ✓ Singleton registration verification ## File Structure ``` src/Framework/DI/ ├── Attributes/ │ └── DefaultImplementation.php # Attribute class ├── DefaultImplementationProcessor.php # Registration processor └── Exceptions/ └── DefaultImplementationException.php # Validation exceptions src/Framework/Discovery/ └── DiscoveryServiceBootstrapper.php # Integration point (modified) tests/debug/ └── test-default-implementation.php # Comprehensive test suite ``` ## API Reference ### `#[DefaultImplementation]` **Attribute** ```php #[Attribute(Attribute::TARGET_CLASS)] final readonly class DefaultImplementation { public function __construct( public ?string $interface = null ) {} } ``` **Parameters**: - `$interface` (optional): Interface class-string. If `null`, auto-detect first interface. **Target**: `CLASS` ### `DefaultImplementationProcessor` **Processor Class** ```php final readonly class DefaultImplementationProcessor { public function __construct( private readonly Container $container ) {} public function process(DiscoveryRegistry $registry): int; } ``` **Methods**: - `process(DiscoveryRegistry $registry): int` - Processes attributes and registers bindings. Returns count of registered bindings. ### `DefaultImplementationException` **Exception Class** ```php final class DefaultImplementationException extends RuntimeException { public static function doesNotImplementInterface( string $className, string $interface ): self; public static function noInterfacesImplemented( string $className ): self; public static function interfaceDoesNotExist( string $className, string $interface ): self; } ``` ## Best Practices ### When to Use ✓ **Service Implementations**: Default implementations for repository, service interfaces ✓ **Infrastructure Adapters**: Database, cache, HTTP client implementations ✓ **Domain Services**: Business logic services implementing domain interfaces ✓ **Framework Extensions**: Custom implementations of framework interfaces ### When NOT to Use ✗ **Multiple Implementations**: When you need multiple implementations of same interface (use explicit binding) ✗ **Conditional Logic**: When implementation choice depends on runtime configuration (use Initializers) ✗ **Non-Singleton Scopes**: When you need per-request or transient instances (use factories) ### Combining with Initializers For complex setup, combine with `#[Initializer]`: ```php interface CacheService {} #[DefaultImplementation(CacheService::class)] final readonly class RedisCacheService implements CacheService { public function __construct( private readonly RedisConnection $redis ) {} } final readonly class CacheServiceInitializer { #[Initializer] public function initializeRedisConnection(): RedisConnection { // Complex Redis connection setup return new RedisConnection(/* config */); } } // DefaultImplementation registered first, then Initializer runs // RedisCacheService can depend on RedisConnection from Initializer ``` ## Performance - **Discovery Overhead**: Negligible - scanned once during bootstrap and cached - **Resolution Overhead**: Zero - standard container resolution (singleton pattern) - **Memory Impact**: Minimal - only stores bindings in container registry - **Cache Integration**: Full cache support - discovered once, cached for subsequent requests ## Troubleshooting ### "Class does not implement interface" **Problem**: Class marked with explicit interface but doesn't actually implement it. **Solution**: Either implement the interface or remove the explicit parameter to auto-detect. ### "Must implement at least one interface" **Problem**: Class marked with `#[DefaultImplementation]` but implements no interfaces. **Solution**: Either implement an interface or remove the attribute. ### "Interface does not exist" **Problem**: Explicit interface parameter references non-existent interface. **Solution**: Fix the interface class-string or ensure the interface is loaded. ### "Multiple implementations conflict" **Problem**: Multiple classes have `#[DefaultImplementation]` for same interface. **Behavior**: Last processed binding wins (discovery order dependent). **Solution**: Remove attribute from all but one implementation, or use explicit container binding. ## Migration Guide ### From Manual Container Binding **Before**: ```php // In Initializer or bootstrap $container->singleton(UserRepository::class, DatabaseUserRepository::class); $container->singleton(LoggerInterface::class, FileLogger::class); ``` **After**: ```php // Add attributes to implementation classes #[DefaultImplementation(UserRepository::class)] final readonly class DatabaseUserRepository implements UserRepository {} #[DefaultImplementation(LoggerInterface::class)] final readonly class FileLogger implements LoggerInterface {} // Bindings happen automatically - remove manual registration ``` ### From Service Provider Pattern **Before**: ```php final readonly class ServiceProvider { public function register(Container $container): void { $container->singleton(UserRepository::class, DatabaseUserRepository::class); $container->singleton(LoggerInterface::class, FileLogger::class); } } ``` **After**: ```php // Add attributes to implementation classes #[DefaultImplementation(UserRepository::class)] final readonly class DatabaseUserRepository implements UserRepository {} #[DefaultImplementation] // Auto-detect final readonly class FileLogger implements LoggerInterface {} // Remove ServiceProvider - attribute handles registration ``` ## Future Enhancements Potential future improvements: - **Conditional Registration**: `#[DefaultImplementation(when: 'production')]` - **Priority System**: `#[DefaultImplementation(priority: 10)]` for multiple implementations - **Scope Control**: `#[DefaultImplementation(scope: 'transient')]` for non-singleton instances - **Tag System**: `#[DefaultImplementation(tags: ['database', 'read-only'])]` for grouped registration ## Related Documentation - [Framework Architecture](architecture.md) - [Dependency Injection](../Framework/DI/README.md) - [Discovery System](../Framework/Discovery/README.md) - [Attribute Patterns](naming-conventions.md#attributes)