ajaxray / magic
适用于PHP 8的简单自动装配、PSR-11兼容的依赖注入库。
Requires
- php: >=8.0
- psr/container: 1.1.1
Requires (Dev)
- phpstan/phpstan: ^1.0
- phpunit/phpunit: 9.*
This package is not auto-updated.
Last update: 2024-09-22 07:09:03 UTC
README
一个小于200行的PHP 8依赖注入容器。为了乐趣和探索PHP反射特性而创建。但它确实做到了它所宣称的。
它真的有效吗?
是的,它确实有效!这里是一个使用Magic作为依赖注入容器的Slim Framework示例应用程序。
特性
- 兼容PSR-11: 容器接口
- 为PHP8制作
- 通过类、接口或匿名函数进行服务绑定
- 使用以下方式解析依赖项
- 通过类/接口名称进行自动装配
- 服务名称/标识符的映射
- 服务的接口
- 服务的实现
- 基于类型提示的构造函数DI能力
- 对象的生命周期控制(单例/每个请求一个新实例)
- 易于与任何框架(使用PSR-11兼容的容器)或普通PHP文件一起使用
- 符合PSR-4自动加载的结构
注意:此库仍在初期开发阶段!
安装
只需使用composer将其拉入您的项目中。
composer require ajaxray/magic --with-all-dependencies
或者您甚至可以下载它并手动包含。
如何使用
基础
首先,创建一个Magic
实例并绑定服务类。
$magic = new Magic(); $magic->map('logger', MyLogger::class);
现在您可以使用服务名称logger
获取MyLogger
的实例。
$logger = $magic->get('logger'); $logger->info('Using Magic as dependency injection container');
如果MyLogger
的构造函数需要一些参数,Magic
将尝试实例化并提供这些参数。有关参数的更多详细信息,请参阅下一节。
解析服务参数
服务构造函数可能需要一些参数来实例化它。容器将尝试根据参数类型的不同策略提供这些参数。
对象参数
将使用类型提示来确定对象类型。Magic将根据以下方式尝试实例化对象参数:
- 如果参数名称与任何定义的服务标识符匹配。将检查对象类型的安全性。
- 如果有任何使用类/接口名称定义的服务。在这种情况下,接口应映射到具体的类。
- 自动装配。标量参数应可以从容器全局定义的参数中解析。
例如,假设我们有一个类的以下构造函数-
public function __construct(\Doctrine\DBAL\Connection $dbConn)
将依次尝试以下定义。
基于名称匹配-
$container->map('dbConn', function($m, $params) { return \Doctrine\DBAL\DriverManager::getConnection(['url' => $params['dsn']]); });
基于类型匹配-
$container->map(Connection::class, function($m, $params) { return \Doctrine\DBAL\DriverManager::getConnection(['url' => $params['dsn']]); });
最后,如果在以上所有定义中都没有找到,将尝试自动装配。
标量参数
您必须手动设置标量参数。参数可以在容器范围内设置,也可以在服务定义期间设置。
容器范围内设置的参数将用于所有具有相同参数名称的服务。
// e,g, new MyDbConnection($user, $password, $host = 'localhost', $port = 3306); $magic->map('db', MyDbConnection::class); $magic->param('host', 'theHostNameOrIP'); $magic->param('user', 'root'); $magic->param('password', 'TheSecret'); // parameters will be supplied by name matching automatically $magic->get('db');
特定于服务的参数值可以在服务绑定时提供。这些参数将仅用于此特定服务。
$magic->map('db', MyDbConnection::class, [ 'host' => 'theHostNameOrIP', 'user' => 'root', 'pass' => 'TheSecret', ]);
提示:从即将发布的版本中,可以从.env
文件中指定参数。
自动装配
在大多数情况下,如果服务的依赖项(构造函数参数,如果有)满足以下标准,则无需绑定任何内容即可加载服务
- 标量依赖可以由全局设置的参数解析。
- 对象/接口依赖具有类型提示和自动加载功能。
- 对象/接口依赖(及其依赖)满足自动绑定或显式映射的先决条件。
$magic = new Magic(); $magic->get(MyDbConnection::class);
接口绑定
您可以将接口绑定为一个服务。在这种情况下,您必须将接口与其要实例化的实现进行映射。
$magic->map('notifier', NotifierInterface::class, ['receiver' => 'receiver@xyz.tld']); $magic->mapInterface(NotifierInterface::class, MailNotification::class); $magic->get('notifier')->notify('The message to send');
使用匿名函数进行绑定
您可以使用Pimple/Laravel风格的匿名函数绑定服务。该函数将接收容器实例和参数数组。
// Simple $magic->map('greeter', fn($m, $params) => new Greeter($params['name']), ['name' => 'ajaxray']); // Complex $magic->param('user', 'sysadmin'); $magic->param('pass', 'TheSecret'); $magic->map('mailer', function ($m, $params) { $transport = (new Swift_SmtpTransport($params['smtp.host'], 25)) ->setUsername($params['user']) ->setPassword($params['pass']) ; return new Swift_Mailer($transport); }, ['smtp.host' => 'smtp.example.tld']);
在上面的示例中,user
和pass
将从全局设置的参数中解析。这意味着,全局设置的参数将与特定服务的参数合并,在解析或传递给服务绑定函数时使用。
服务生命周期(单例或工厂)
默认情况下,如果服务被实例化一次,它将在后续的get()
调用或解析其他构造函数参数时被重用。但您可以通过传递@cacheable
参数来禁用此行为。
$magic->map('dbMapper', ActiveRecord::class, ['@cacheable' => false]); // dbMapper will not be cached and will return new instance for every get() call $aUser = $magic->get('dbMapper')->load('User', 3); $otherUser = $magic->get('dbMapper')->load('User', 26);
Testdox
PHPUnit 9.5.10 by Sebastian Bergmann and contributors.
Auto Wiring (Ajaxray\Test\AutoWiring)
✔ Resolve class by name without constructor
✔ Resolve class by name with scalar param constructor
✔ Resolve class by name with object param constructor
Basic Class (Ajaxray\Test\BasicClass)
✔ Service mapping without constructor
✔ Service mapping with scalar param constructor
✔ Service mapping with object param constructor
Object Chaining (Ajaxray\Test\ObjectChaining)
✔ Resolve classes in chained object graph
Object Lifecycle (Ajaxray\Test\ObjectLifecycle)
✔ Provides same instance for multiple get call by default
✔ Provides same instance for multiple get call of interface
✔ Provides same instance for multiple get call of callback binding
✔ Service caching can be disabled for class mapping
✔ Service caching can be disabled for interface
✔ Service caching can be disabled for callback binding
Resolve Interface (Ajaxray\Test\ResolveInterface)
✔ Service loading by interface if single implementation
✔ Resolve interface type hint to implementation if single implementation
✔ Service loading by mapped interface
✔ Resolve mapped interface type hint to implementation
Service Binding By Callable (Ajaxray\Test\ServiceBindingByCallable)
✔ Service mapping without constructor
✔ Service mapping with scalar param constructor
✔ Service mapping with object param constructor
✔ Callable can serve non object types
Time: 00:00.015, Memory: 6.00 MB
OK (21 tests, 23 assertions)