这是一个简单高效的 PHP 依赖注入包,它使用了 PHP 的最新特性:注解。通过注解,我们可以将一个类标记为单例。
PHP 版本
最低支持的 PHP 版本:8.0
介绍
- 无依赖
- 支持单例模式
- 支持类别名
- 支持类映射
- 反射对象缓存消除了重复创建相同类反射的需求,并提高了性能
- 循环依赖检测,能够检测循环依赖问题(包括直接和间接循环依赖)
安装
composer require yolo-fx/di
使用方法
- 创建类的实例。
你可以使用 Yolo\Di\DI::use() 来创建类的实例,例如:
Yolo\Di\DI::use(User::class)
- 单例
你可以使用 Yolo\Di\Annotations\Singleton 注解来将一个类标记为单例。
- 初始化器
你可以使用 Yolo\Di\Annotations\Initializer 注解来将一个方法标记为初始化器。
- 使用类映射
当你的构造函数参数是一个接口类型时,这会非常有用。例如:
class TestRunner
{
/**
* @param LoggerInterface $logger It will be replaced by ConsoleLogger
*/
public function __construct(private LoggerInterface $logger)
{
}
public function sayHello(): void
{
$this->logger->log("Hello World");
}
}
你可以使用 Yolo\Di\DI::bind() 来设置类映射。
DI::bind(LoggerInterface::class, ConsoleLogger::class);
这样 LoggerInterface::class 将被 ConsoleLogger::class 替换。
现在,你可以使用 Yolo\Di\DI::use() 来创建类的实例。
$runner = DI::use(TestRunner::class);
$runner->sayHello();
- 使用类别名
DI::alias(TestRunner::class, 'runner');
$runner = DI::use('runner');
$runner->sayHello();
示例
- 创建一个类: UserTest.php
<?php
namespace DI\Tests;
use Yolo\Di\Annotations\Initializer;
class UserTest
{
public function __construct(private AnimalTest $animal)
{
echo "you can see me twice." . PHP_EOL;
}
#[Initializer]
public function init()
{
echo "User.init" . PHP_EOL;
}
/**
* Say hello
* @return void
*/
public function sayHello()
{
echo "Hello, I am a user." . PHP_EOL;
$this->animal->sayHello();
}
}
- 创建另一个类: AnimalTest.php
<?php
namespace DI\Tests;
use Yolo\Di\Annotations\Singleton;
#[Singleton]
class AnimalTest
{
public function __construct()
{
echo 'you can only see me once' . PHP_EOL;
}
public function sayHello()
{
echo 'Hello, I am an animal.' . PHP_EOL;
}
}
- 使用 DI 创建实例
<?php
use Yolo\Di\DI;
require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/LoggerInterface.php';
require_once __DIR__ . '/ConsoleLogger.php';
require_once __DIR__ . '/FileLogger.php';
class TestRunner
{
public function __construct(private LoggerInterface $logger)
{
}
public function sayHello(): void
{
$this->logger->log("Hello World");
}
}
$start = microtime(true);
DI::bind(LoggerInterface::class, ConsoleLogger::class);
// Use DI::alias to create an alias for a class
DI::alias(TestRunner::class, 'runner');
// And then use the alias to create an instance of TestRunner
$runner = DI::use('runner');
$runner->sayHello();
// Also, you can use the class name to create an instance of TestRunner
$runner = DI::use(TestRunner::class);
$runner->sayHello();
$end = microtime(true);
echo 'Spent ' . round(($end - $start) * 6, 3) . 'ms' . PHP_EOL;
echo 'Memory usage: ' . round(memory_get_usage() / 1024, 3) . 'kb' . PHP_EOL;
注意事项
- 不要有循环依赖,包括直接和间接的循环依赖,比如 A 依赖于 B 而 B 又依赖于 A。
DI.php
<?php
namespace Yolo\Di;
use ReflectionException;
use Yolo\Di\Core\ApplicationContainer;
use Yolo\Di\Errors\CircularDependencyException;
use Yolo\Di\Errors\InvalidAttributeException;
use Yolo\Di\Errors\ParameterTypeEmptyException;
/**
* Class DI
*
* This class acts as a static facade for interacting with the dependency injection container.
* It provides a set of static methods to manage class bindings, instances, aliases, and custom property attributes.
* The `DI` class simplifies the process of resolving dependencies and managing object lifecycles.
*
* Key Features:
* - **Dependency Resolution**: Automatically resolve and instantiate classes with their dependencies.
* - **Class Binding**: Bind abstract classes or interfaces to concrete implementations.
* - **Instance Management**: Manually set or remove specific instances for classes.
* - **Alias Management**: Create and manage aliases for classes to simplify usage.
* - **Custom Property Attributes**: Add custom property injection attributes for advanced use cases.
*
* Usage:
* - Use `DI::use()` to resolve and create instances of classes.
* - Use `DI::bind()` to bind an abstract class or interface to a concrete implementation.
* - Use `DI::instance()` to manually set a specific instance for a class.
* - Use `DI::alias()` to create an alias for a class.
* - Use `DI::addCustomPropertyAttribute()` to register custom property injection attributes.
*
* Example:
* ```php
* DI::bind(LoggerInterface::class, ConsoleLogger::class);
* $logger = DI::use(LoggerInterface::class);
*```
* @package Yolo\Di
*/
class DI
{
/**
* Get instance of class.
* @template T of object
* @param class-string<T> $class Class name
* @param bool $cache Whether to cache the reflection or not.
* @return T
* @throws ReflectionException|ParameterTypeEmptyException|CircularDependencyException|InvalidAttributeException
*/
public static function use(string $class, bool $cache = true)
{
return ApplicationContainer::inst()->getInjection()->resolve($class, $cache);
}
/**
* Bind an abstract class to a concrete implementation.
* @param string $abstract
* @param string $concrete
* @return void
*/
public static function bind(string $abstract, string $concrete): void
{
ApplicationContainer::inst()->getInjection()->bind($abstract, $concrete);
}
/**
* Unbind a class.
* @param string $abstract
* @return void
*/
public static function unbind(string $abstract): void
{
ApplicationContainer::inst()->getInjection()->unbind($abstract);
}
/**
* Set an instance manually. It will override any existing instance for that class.
*
* It just like to set a singleton class.
* @param string $class
* @param object $instance
* @return void
*/
public static function instance(string $class, object $instance): void
{
ApplicationContainer::inst()->getInjection()->instance($class, $instance);
}
/**
* Remove an instance.
* @param string $class
* @return void
*/
public static function forget(string $class): void
{
ApplicationContainer::inst()->getInjection()->forget($class);
}
/**
* Set an alias for a class.
* @param string $class
* @param string $alias
* @return void
*/
public static function alias(string $class, string $alias): void
{
ApplicationContainer::inst()->getInjection()->alias($class, $alias);
}
/**
* Add a custom property attribute.
* @param $class
* @return void
*/
public static function addCustomPropertyAttribute($class): void
{
ApplicationContainer::inst()->getInjection()->addCustomPropertyAttribute($class);
}
}
近期评论