Reflector
Reflector is a dependency injection container for PHP powered by auto-wiring and reflection.
It supports auto-wiring, contextual bindings, singletons, runtime parameter overrides, circular dependency detection, and PSR-11 compliance.
Features
- Auto-Wiring: Resolves class constructor dependencies automatically using reflection.
- PSR-11 Compliance: Implements
Psr\Container\ContainerInterface(getandhas). - Contextual Binding: Configure different implementations for specific classes using
when()->needs()->give(). - Singletons: Share instances across multiple resolutions.
- Parameter Overrides: Pass runtime parameters as associative arrays or named arguments.
- Circular Dependency Detection: Detects recursive dependency chains and throws descriptive exceptions.
- Helper Function: Global
app()helper for quick access and resolution.
Requirements
- PHP 8.3 or higher
psr/container ^2.0
Installation
composer require mykemeynell/reflection
Quick Start
use mykemeynell\Reflection\Application\Container;
$container = new Container();
class Database {}
class UserRepository {
public function __construct(public Database $db) {}
}
$userRepository = $container->make(UserRepository::class);
Usage
Auto-Wiring
Classes with type-hinted constructor dependencies resolve without manual configuration:
class Logger {}
class OrderService {
public function __construct(public Logger $logger) {}
}
$service = $container->make(OrderService::class);
Default parameter values are used when no argument is supplied:
class HttpClient {
public function __construct(public int $timeout = 30) {}
}
$client = $container->make(HttpClient::class); // $client->timeout === 30
Bindings
Interface to Implementation
$container->bind(MailerInterface::class, SmtpMailer::class);
$mailer = $container->make(MailerInterface::class);
Closure Factory
$container->bind(MailerInterface::class, function (Container $app, array $params = []) {
return new SmtpMailer($params['key'] ?? 'default');
});
$mailer = $container->make(MailerInterface::class, ['key' => 'custom-key']);
Singletons & Shared Instances
Register a singleton to return the same instance on subsequent resolutions:
$container->singleton(Database::class);
$db1 = $container->make(Database::class);
$db2 = $container->make(Database::class);
// $db1 === $db2
Register an existing instance:
$config = new AppConfig();
$container->instance(AppConfig::class, $config);
Contextual Bindings
Inject different implementations based on the consumer class:
$container->when(DirectDispatcher::class)
->needs(TransportInterface::class)
->give(HttpTransport::class);
$container->when(CustomerDispatcher::class, OutletDispatcher::class)
->needs(TransportInterface::class)
->give(QueueTransport::class);
give() also accepts a closure or a concrete instance:
$container->when(ReportGenerator::class)
->needs(StorageInterface::class)
->give(fn (Container $app) => new S3Storage('bucket-name'));
Parameter Overrides
Override constructor parameters during resolution:
class ApiService {
public function __construct(
public HttpClient $client,
public string $apiKey,
public int $timeout = 30
) {}
}
$service = $container->make(ApiService::class, [
'apiKey' => 'my-token',
'timeout' => 60,
]);
Circular Dependency Detection
Circular dependencies are detected automatically during resolution:
class ServiceA {
public function __construct(public ServiceB $b) {}
}
class ServiceB {
public function __construct(public ServiceA $a) {}
}
$container->make(ServiceA::class);
// Throws ContainerException: Circular dependency detected while resolving [ServiceA]: ServiceA -> ServiceB -> ServiceA.
PSR-11 Compliance
Reflector implements Psr\Container\ContainerInterface:
use Psr\Container\ContainerInterface;
function bootstrap(ContainerInterface $container): void {
if ($container->has(Router::class)) {
$router = $container->get(Router::class);
}
}
PSR-11 exception classes:
mykemeynell\Reflection\Exceptions\NotFoundException(implementsPsr\Container\NotFoundExceptionInterface)mykemeynell\Reflection\Exceptions\ContainerException(implementsPsr\Container\ContainerExceptionInterface)mykemeynell\Reflection\Exceptions\DependencyNotSpecifiedException(implementsPsr\Container\ContainerExceptionInterface)
Helper Function
Import the app helper function:
use function mykemeynell\Reflection\Helpers\app;
// Retrieve container instance
$container = app();
// Resolve service
$mailer = app(MailerInterface::class);
// Resolve with parameters
$service = app(ApiService::class, apiKey: 'token', timeout: 45);
// Resolve closure
$result = app(fn (Container $app) => $app->make(Logger::class));
Testing
Run tests:
composer test
Check code style:
composer lint:check
Format code style:
composer lint
License
MIT License. See LICENSE for details.