探索PHP 8.4中延迟对象的依赖注入

在现代PHP领域,8.4版本的发布引入了一项开创性的特性:延迟对象(Lazy Objects)。这些对象提供了一种全新的方式,可将初始化推迟到绝对必要时才进行,从而提升性能并减少资源使用。通过对ReflectionClass API的增强,该功能已深度融入PHP语言,这在《延迟对象的延迟初始化RFC》中有详细阐述。
RFC中的示例
为了说明延迟对象的潜力,来看直接取自该RFC的以下示例:
class MyClass
{
public function __construct(private int $foo)
{
// 这里是繁重的初始化逻辑。
}
//...
}
$initializer = static function (MyClass $ghost): void {
$ghost->__construct(123);
};
$reflector = new ReflectionClass(MyClass::class);
$object = $reflector->newLazyGhost($initializer);
// 此时,$object是一个延迟幽灵对象。
这种机制使开发者能够精细地控制初始化过程,确保仅在访问资源时才加载它们。
受此RFC启发,我着手构建一个符合PSR-11标准的依赖注入容器,利用延迟对象API实现最佳性能。
容器延迟对象的基础
我们容器的核心在于ContainerLazyObject
类。借助它,你可以注册依赖项并延迟初始化它们,这意味着只有在实际需要时才会实例化这些依赖项。以下是执行此任务的主要方法:
public function set(string $id, object|string $concrete): void
{
$reflector = new ReflectionClass($id);
$initializer = $concrete;
if (is_string($concrete)) {
$initializer = function(object $instance) use ($concrete): void {
$this->instances[$instance::class] = $concrete($this);
};
}
if (is_object($concrete) &&!$concrete instanceof Closure) {
$initializer = function(object $instance) use ($concrete): void {
$this->instances[$instance::class] = $concrete;
};
}
$this->instances[$id] = $reflector->newLazyProxy($initializer);
}
在容器中注册服务
我们的容器支持多种注册服务的方式,为开发者提供了灵活性。以下是一些示例:
$container = new ContainerLazyObject();
$container->set(DatabaseService::class, fn() => new DatabaseService(new LoggerService()));
$container->set(LoggerService::class, fn() => new LoggerService());
// 使用类名的替代方法
$container->set(DatabaseService::class, DatabaseService::class);
$container->set(LoggerService::class, LoggerService::class);
// 使用已实例化的对象
$container->set(DatabaseService::class, new DatabaseService(new LoggerService()));
$container->set(LoggerService::class, new LoggerService());
这种灵活性使ContainerLazyObject
能够适应各种场景,无论是动态构建依赖项还是重用预先配置的对象。
从容器中检索服务 一旦在容器中注册了服务,你可以在需要时随时检索它们。容器确保服务是延迟实例化的,因此在实际请求之前不会创建它们。以下是如何检索已注册服务的示例:
// 从容器中检索服务
$loggerService = $container->get(LoggerService::class);
$databaseService = $container->get(DatabaseService::class);
ContainerLazyObject
的核心
我们容器的核心在于ContainerLazyObject
类。借助它,你可以注册依赖项并延迟初始化它们,即只有在实际使用时才会创建它们。以下是执行此任务的主要方法:
public function set(string $id, object|string $concrete): void
{
$reflector = new ReflectionClass($id);
$initializer = $concrete;
if (is_string($concrete)) {
$initializer = function(object $instance) use ($concrete): void {
$this->instances[$instance::class] = $concrete($this);
};
}
if (is_object($concrete) &&!$concrete instanceof Closure) {
$initializer = function(object $instance) use ($concrete): void {
$this->instances[$instance::class] = $concrete;
};
}
$this->instances[$id] = $reflector->newLazyProxy($initializer);
}
PSR-11兼容性
ContainerLazyObject
的另一个优点是它与PSR-11兼容,PSR-11是PHP依赖注入容器的标准。这确保了它与遵循该规范的库和框架的互操作性,使其成为一个轻量级且通用的解决方案。
与其他容器的性能比较 为了衡量我们容器的性能,我在受控环境中使用了PhpBench,并将其与流行的替代方案(Pimple、Illuminate和PHP-DI)进行了比较。结果令人鼓舞:
容器 | 平均耗时(±标准差) |
---|---|
ContainerLazyObject | 0.100微秒(±33.33%) |
benchPimple | 0.297微秒(±18.84%) |
benchIlluminate | 0.503微秒(±9.07%) |
benchPhpDI | 0.161微秒(±41.57%) |
在简单的依赖解析场景中,我们的容器表现出卓越的性能,明显比诸如Illuminate容器和PHP - DI等功能更强大的替代方案更快。
完整类定义
class ContainerLazyObject implements ContainerInterface
{
/**
* @var array
*/
private array $instances;
/**
* @var array
*/
protected static array $reflectionCache = [];
/**
* @param string $class
*
* @return ReflectionClass
* @throws ReflectionException
*/
protected function getReflectionClass(string $class): ReflectionClass
{
if(!isset(self::$reflectionCache[$class])) {
self::$reflectionCache[$class] = new ReflectionClass($class);
}
return self::$reflectionCache[$class];
}
/**
* @param string $class
* @param object|string $concrete
*
* @return void
* @throws ReflectionException
*/
public function set(string $class, object|string $concrete): void
{
$initializer = $concrete;
if(is_string($concrete)) {
$initializer = function(object $instance) use ($concrete): void {
$this->instances[$instance::class] = $concrete($this);
};
}
if(is_object($concrete) &&!$concrete instanceof Closure) {
$initializer = function(object $instance) use ($concrete): void {
$this->instances[$instance::class] = $concrete;
};
}
$this->instances[$class] = $this->getReflectionClass($class)->newLazyProxy($initializer);
}
/**
* @param string $id
*
* @return object
*/
public function get(string $id): object
{
if(!isset($this->instances[$id])) {
throw new \InvalidArgumentException("Reference '{$id}' not found in container.");
}
return $this->instances[$id];
}
/**
* @param string $id
*
* @return bool
*/
public function has(string $id): bool
{
return isset($this->instances[$id]);
}
/**
* @return void
*/
public function clear(): void
{
$this->instances = [];
}
/**
* @param string $id
*
* @return void
*/
public function remove(string $id): void
{
if($this->has($id)) {
unset($this->instances[$id]);
}
}
}
结论
PHP 8.4及其延迟对象为简化和优化依赖注入开辟了新的可能性。我们的ContainerLazyObject
除了轻量级、高效且灵活之外,还符合PSR-11标准,确保了与其他库和框架的互操作性。
尝试这种方法,看看它如何在你的下一个项目中简化依赖管理!