php-di / invoker
通用且可扩展的可调用调用者
Requires
- php: >=7.3
- psr/container: ^1.0|^2.0
Requires (Dev)
- athletic/athletic: ~0.1.8
- mnapoli/hard-mode: ~0.3.0
- phpunit/phpunit: ^9.0
README
通用且可扩展的可调用调用者。
为什么需要它?
谁不需要一个过度设计的 call_user_func()
?
命名参数
这个 Silex 示例看起来熟悉吗?
$app->get('/project/{project}/issue/{issue}', function ($project, $issue) { // ... });
或者这个用 Silly 定义的命令?
$app->command('greet [name] [--yell]', function ($name, $yell) { // ... });
在 Slim 中也有相同的模式。
$app->get('/hello/:name', function ($name) { // ... });
你明白我的意思了。这些框架使用类似命名参数的方式调用控制器/命令/处理程序:无论参数的顺序如何,它们都通过名称进行匹配。
这个库允许以通用和可扩展的方式调用带有命名参数的可调用者。
依赖注入
熟悉 AngularJS 的人都知道如何执行依赖注入。
angular.controller('MyController', ['dep1', 'dep2', function(dep1, dep2) { // ... }]);
在 PHP 中,我们可以在一些框架和 DI 容器中再次找到这种模式,它们提供了部分到完全的支持。例如,在 Silex 中,你可以使用类型提示来注入应用程序,但它仅适用于 Silex\Application
$app->get('/hello/{name}', function (Silex\Application $app, $name) { // ... });
在 Silly 中,它仅适用于 OutputInterface
来注入应用程序输出。
$app->command('greet [name]', function ($name, OutputInterface $output) { // ... });
PHP-DI 提供了一种通过类型提示调用可调用者并从容器中解析所有依赖项的方法。
$container->call(function (Logger $logger, EntityManager $em) { // ... });
这个库提供了清晰的扩展点,让框架实现它们想要的任何类型的依赖注入支持。
TL/DR
简而言之,这个库旨在成为一个用于以命名参数和/或依赖注入调用函数的基本构建块。
安装
$ composer require PHP-DI/invoker
用法
默认行为
默认情况下,Invoker
可以使用命名参数进行调用
$invoker = new Invoker\Invoker; $invoker->call(function () { echo 'Hello world!'; }); // Simple parameter array $invoker->call(function ($name) { echo 'Hello ' . $name; }, ['John']); // Named parameters $invoker->call(function ($name) { echo 'Hello ' . $name; }, [ 'name' => 'John' ]); // Use the default value $invoker->call(function ($name = 'world') { echo 'Hello ' . $name; }); // Invoke any PHP callable $invoker->call(['MyClass', 'myStaticMethod']); // Using Class::method syntax $invoker->call('MyClass::myStaticMethod');
支持在参数中进行依赖注入,但需要使用您的容器进行配置。如果您不耐烦,请继续阅读或跳转到 内置的依赖注入支持。
此外,可调用者还可以从您的容器中解析出来。如果您不耐烦,请继续阅读或跳转到 从容器中解析可调用者。
参数解析器
扩展 Invoker
的行为很容易,只需实现一个 ParameterResolver
即可。
这已经在 参数解析器文档 中详细说明。
内置的依赖注入支持
而不是每次都重新实现不同容器上的依赖注入支持,这个包附带 2 个可选解析器
-
此解析器将使用类型提示搜索类名,从而注入容器条目
$invoker->call(function (Psr\Logger\LoggerInterface $logger) { // ... });
在这个例子中,它将从容器中
->get('Psr\Logger\LoggerInterface')
并注入它。此解析器仅在您使用类(或接口)名称在容器中存储对象时才有用。例如,Silex 或 Symfony 将服务存储在自定义名称下(例如
twig
、db
等),而不是类名:在这种情况下,请使用下面的解析器。 -
ParameterNameContainerResolver
此解析器将使用参数名称搜索容器条目进行注入
$invoker->call(function ($twig) { // ... });
在这个例子中,它将从容器中
->get('twig')
并注入它。
这些解析器可以与符合 PSR-11 规范的任何依赖注入容器一起工作。
设置这些解析器很简单
// $container must be an instance of Psr\Container\ContainerInterface $container = ... $containerResolver = new TypeHintContainerResolver($container); // or $containerResolver = new ParameterNameContainerResolver($container); $invoker = new Invoker\Invoker; // Register it before all the other parameter resolvers $invoker->getParameterResolver()->prependResolver($containerResolver);
如果您愿意,也可以同时注册这两个解析器。实现对更复杂事物的支持也很容易,完全取决于您!
从容器中解析可调用对象
可以将 Invoker
与您的依赖注入容器绑定,以便解析可调用对象。
例如,对于可调用的类
class MyHandler { public function __invoke() { // ... } } // By default this doesn't work: an instance of the class should be provided $invoker->call('MyHandler'); // If we set up the container to use $invoker = new Invoker\Invoker(null, $container); // Now 'MyHandler' is resolved using the container! $invoker->call('MyHandler');
对于类方法也是同样的效果
class WelcomeController { public function home() { // ... } } // By default this doesn't work: home() is not a static method $invoker->call(['WelcomeController', 'home']); // If we set up the container to use $invoker = new Invoker\Invoker(null, $container); // Now 'WelcomeController' is resolved using the container! $invoker->call(['WelcomeController', 'home']); // Alternatively we can use the Class::method syntax $invoker->call('WelcomeController::home');
该功能可以用作框架分发器的基构建块。
再次强调,任何符合 PSR-11 规范的容器都可以提供。