晋太元中,武陵人捕鱼为业。缘溪行,忘路之远近。忽逢桃花林,夹岸数百步,中无杂树,芳草鲜美,落英缤纷。渔人甚异之,复前行,欲穷其林。 林尽水源,便得一山,山有小口,仿佛若有光。便舍船,从口入。初极狭,才通人。复行数十步,豁然开朗。土地平旷,屋舍俨然,有良田、美池、桑竹之属。阡陌交通,鸡犬相闻。其中往来种作,男女衣着,悉如外人。黄发垂髫,并怡然自乐。 见渔人,乃大惊,问所从来。具答之。便要还家,设酒杀鸡作食。村中闻有此人,咸来问讯。自云先世避秦时乱,率妻子邑人来此绝境,不复出焉,遂与外人间隔。问今是何世,乃不知有汉,无论魏晋。此人一一为具言所闻,皆叹惋。余人各复延至其家,皆出酒食。停数日,辞去。此中人语云:“不足为外人道也。”(间隔 一作:隔绝) 既出,得其船,便扶向路,处处志之。及郡下,诣太守,说如此。太守即遣人随其往,寻向所志,遂迷,不复得路。 南阳刘子骥,高尚士也,闻之,欣然规往。未果,寻病终。后遂无问津者。
|
Server : Apache System : Linux srv.rainic.com 4.18.0-553.47.1.el8_10.x86_64 #1 SMP Wed Apr 2 05:45:37 EDT 2025 x86_64 User : rainic ( 1014) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /opt/cpanel/ea-wappspector/vendor/php-di/invoker/ |
Upload File : |
# Invoker
Generic and extensible callable invoker.
[](https://github.com/PHP-DI/Invoker/actions/workflows/ci.yml)
[](https://packagist.org/packages/PHP-DI/invoker)
[](https://packagist.org/packages/php-di/invoker)
## Why?
Who doesn't need an over-engineered `call_user_func()`?
### Named parameters
Does this [Silex](https://github.com/silexphp/Silex#readme) example look familiar:
```php
$app->get('/project/{project}/issue/{issue}', function ($project, $issue) {
// ...
});
```
Or this command defined with [Silly](https://github.com/mnapoli/silly#usage):
```php
$app->command('greet [name] [--yell]', function ($name, $yell) {
// ...
});
```
Same pattern in [Slim](https://www.slimframework.com):
```php
$app->get('/hello/:name', function ($name) {
// ...
});
```
You get the point. These frameworks invoke the controller/command/handler using something akin to named parameters: whatever the order of the parameters, they are matched by their name.
**This library allows to invoke callables with named parameters in a generic and extensible way.**
### Dependency injection
Anyone familiar with AngularJS is familiar with how dependency injection is performed:
```js
angular.controller('MyController', ['dep1', 'dep2', function(dep1, dep2) {
// ...
}]);
```
In PHP we find this pattern again in some frameworks and DI containers with partial to full support. For example in Silex you can type-hint the application to get it injected, but it only works with `Silex\Application`:
```php
$app->get('/hello/{name}', function (Silex\Application $app, $name) {
// ...
});
```
In Silly, it only works with `OutputInterface` to inject the application output:
```php
$app->command('greet [name]', function ($name, OutputInterface $output) {
// ...
});
```
[PHP-DI](https://php-di.org/doc/container.html) provides a way to invoke a callable and resolve all dependencies from the container using type-hints:
```php
$container->call(function (Logger $logger, EntityManager $em) {
// ...
});
```
**This library provides clear extension points to let frameworks implement any kind of dependency injection support they want.**
### TL/DR
In short, this library is meant to be a base building block for calling a function with named parameters and/or dependency injection.
## Installation
```sh
$ composer require PHP-DI/invoker
```
## Usage
### Default behavior
By default the `Invoker` can call using named parameters:
```php
$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');
```
Dependency injection in parameters is supported but needs to be configured with your container. Read on or jump to [*Built-in support for dependency injection*](#built-in-support-for-dependency-injection) if you are impatient.
Additionally, callables can also be resolved from your container. Read on or jump to [*Resolving callables from a container*](#resolving-callables-from-a-container) if you are impatient.
### Parameter resolvers
Extending the behavior of the `Invoker` is easy and is done by implementing a [`ParameterResolver`](https://github.com/PHP-DI/Invoker/blob/master/src/ParameterResolver/ParameterResolver.php).
This is explained in details the [Parameter resolvers documentation](doc/parameter-resolvers.md).
#### Built-in support for dependency injection
Rather than have you re-implement support for dependency injection with different containers every time, this package ships with 2 optional resolvers:
- [`TypeHintContainerResolver`](https://github.com/PHP-DI/Invoker/blob/master/src/ParameterResolver/Container/TypeHintContainerResolver.php)
This resolver will inject container entries by searching for the class name using the type-hint:
```php
$invoker->call(function (Psr\Logger\LoggerInterface $logger) {
// ...
});
```
In this example it will `->get('Psr\Logger\LoggerInterface')` from the container and inject it.
This resolver is only useful if you store objects in your container using the class (or interface) name. Silex or Symfony for example store services under a custom name (e.g. `twig`, `db`, etc.) instead of the class name: in that case use the resolver shown below.
- [`ParameterNameContainerResolver`](https://github.com/PHP-DI/Invoker/blob/master/src/ParameterResolver/Container/ParameterNameContainerResolver.php)
This resolver will inject container entries by searching for the name of the parameter:
```php
$invoker->call(function ($twig) {
// ...
});
```
In this example it will `->get('twig')` from the container and inject it.
These resolvers can work with any dependency injection container compliant with [PSR-11](http://www.php-fig.org/psr/psr-11/).
Setting up those resolvers is simple:
```php
// $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);
```
You can also register both resolvers at the same time if you wish by prepending both. Implementing support for more tricky things is easy and up to you!
### Resolving callables from a container
The `Invoker` can be wired to your DI container to resolve the callables.
For example with an invokable class:
```php
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');
```
The same works for a class method:
```php
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');
```
That feature can be used as the base building block for a framework's dispatcher.
Again, any [PSR-11](https://www.php-fig.org/psr/psr-11/) compliant container can be provided.