PHP依赖注入与服务容器深度剖析
PHP依赖注入与服务容器深度剖析
依赖注入是控制反转的一种实现方式。框架中的服务容器负责管理对象的创建和依赖关系。今天深入说说服务容器的实现原理。
先回顾一下依赖注入的基本概念。传统的代码在类内部创建依赖,导致紧耦合。依赖注入把依赖从外部传入,实现松耦合。
```php
// 没有依赖注入
class OrderProcessor
{
private Logger $logger;
public function __construct()
{
// 在内部创建依赖,紧耦合
$this->logger = new FileLogger('/var/log/app.log');
}
}
// 构造函数注入
class OrderProcessorDI
{
public function __construct(
private LoggerInterface $logger // 依赖从外部传入
) {}
}
// 属性注入
class OrderProcessorProperty
{
public LoggerInterface $logger; // 从外部设置
}
// Setter注入
class OrderProcessorSetter
{
private LoggerInterface $logger;
public function setLogger(LoggerInterface $logger): void
{
$this->logger = $logger;
}
}
?>
```
自动装配是容器的核心功能。通过反射分析类的构造函数,递归解析所有依赖。
```php
class Container
{
private array $bindings = [];
private array $instances = [];
private array $aliases = [];
public function bind(string $abstract, callable|string|null $concrete = null, bool $shared = false): void
{
if ($concrete === null) {
$concrete = $abstract;
}
if (! $concrete instanceof Closure) {
$concrete = function (Container $container) use ($concrete) {
return $container->build($concrete);
};
}
$this->bindings[$abstract] = compact('concrete', 'shared');
}
public function singleton(string $abstract, callable|string|null $concrete = null): void
{
$this->bind($abstract, $concrete, true);
}
public function instance(string $abstract, object $instance): void
{
$this->instances[$abstract] = $instance;
}
public function alias(string $abstract, string $alias): void
{
$this->aliases[$alias] = $abstract;
}
public function make(string $abstract): mixed
{
$abstract = $this->resolveAlias($abstract);
if (isset($this->instances[$abstract])) {
return $this->instances[$abstract];
}
if (!isset($this->bindings[$abstract])) {
return $this->build($abstract);
}
$binding = $this->bindings[$abstract];
$object = ($binding['concrete'])($this);
if ($binding['shared']) {
$this->instances[$abstract] = $object;
}
return $object;
}
public function call(callable|array $callable, array $parameters = []): mixed
{
if (is_array($callable)) {
$ref = new ReflectionMethod($callable[0], $callable[1]);
} else {
$ref = new ReflectionFunction($callable);
}
$args = [];
foreach ($ref->getParameters() as $param) {
$name = $param->getName();
if (isset($parameters[$name])) {
$args[] = $parameters[$name];
continue;
}
$type = $param->getType();
if ($type instanceof ReflectionNamedType && !$type->isBuiltin()) {
$args[] = $this->make($type->getName());
} elseif ($param->isDefaultValueAvailable()) {
$args[] = $param->getDefaultValue();
} else {
throw new RuntimeException("无法解析参数: $name");
}
}
return $ref->invokeArgs($args);
}
public function build(string $class): object
{
if (!class_exists($class)) {
throw new RuntimeException("无法构建: $class");
}
$ref = new ReflectionClass($class);
$constructor = $ref->getConstructor();
if ($constructor === null) {
return $ref->newInstance();
}
$dependencies = [];
foreach ($constructor->getParameters() as $param) {
$type = $param->getType();
if ($type === null) {
if ($param->isDefaultValueAvailable()) {
$dependencies[] = $param->getDefaultValue();
} else {
throw new RuntimeException("无法解析参数: {$param->getName()}");
}
} elseif ($type->isBuiltin()) {
if ($param->isDefaultValueAvailable()) {
$dependencies[] = $param->getDefaultValue();
} else {
throw new RuntimeException("无法解析内置类型: {$param->getName()}");
}
} else {
$dependencies[] = $this->make($type->getName());
}
}
return $ref->newInstanceArgs($dependencies);
}
public function has(string $abstract): bool
{
$abstract = $this->resolveAlias($abstract);
return isset($this->bindings[$abstract])
|| isset($this->instances[$abstract])
|| class_exists($abstract);
}
private function resolveAlias(string $abstract): string
{
return $this->aliases[$abstract] ?? $abstract;
}
}
?>
```
服务提供者模式把相关的绑定和初始化逻辑组织在一起,是框架扩展的标准方式。
```php
interface ServiceProvider
{
public function register(Container $container): void;
public function boot(Container $container): void;
}
class CacheServiceProvider implements ServiceProvider
{
public function register(Container $container): void
{
$container->singleton('cache.redis', function () {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
return $redis;
});
$container->bind('cache.file', function () {
return new FileCache('/tmp/cache');
});
}
public function boot(Container $container): void
{
$redis = $container->make('cache.redis');
$redis->ping();
}
}
class App
{
private Container $container;
private array $providers = [];
public function __construct()
{
$this->container = new Container();
}
public function register(array $providers): void
{
foreach ($providers as $provider) {
$instance = new $provider();
$instance->register($this->container);
$this->providers[] = $instance;
}
}
public function boot(): void
{
foreach ($this->providers as $provider) {
$provider->boot($this->container);
}
}
public function make(string $abstract): mixed
{
return $this->container->make($abstract);
}
}
$app = new App();
$app->register([CacheServiceProvider::class]);
$app->boot();
$redis = $app->make('cache.redis');
echo "应用启动完成\n";
?>
```
依赖注入和服务容器是框架的核心基础设施。理解它们的实现原理,可以更高效地使用框架,在遇到问题时也能快速定位。不管是自己实现容器还是使用框架自带的容器,核心思想都是一样的。
