Callable definitions in yiisoft/queue are based on native PHP callables and provide additional ways to define a callable.
Both definition types (native PHP callables and extended callables) allow you to use a DI container and yiisoft/injector to resolve dependencies in a lazy way. These callable definition formats are used across the package to convert configuration definitions into real callables.
When you define a callable as a native PHP callable, it is not modified in any way and is called as is. Dependencies in the parameter list are resolved via yiisoft/injector and a DI container.
As you can see in the PHP documentation, there are several ways to define a native callable:
- Closure (lambda function). It may be static. Example:
$callable = static function(MyDependency $dependency) { // do stuff }
- First class callable. It is itself a
Closure. Example:$callable = trim(...); $callable2 = $this->foo(...);
- A class static function. When a class has a static function, an array syntax may be used:
$callable = [Foo::class, 'bar']; // calls Foo::bar() statically
- An object method. The same as above, but with an object and a non-static method:
$foo = new Foo(); $callable = [$foo, 'bar']; // this will be called the same way as $foo->bar();
- A class static function as a string. This format is non-obvious and hard to refactor; it exists for completeness only:
$callable = 'Foo::bar'; // this will be called the same way as Foo::bar();
- A name of a named function:
function foo() { // do stuff } $callable = 'foo'; $callable2 = 'array_map';
- Callable objects. An object with the
__invokemethod implemented:class Foo { public function __invoke() { // do stuff } } $callable = new Foo();
The difference from native PHP callables is that you don't need to instantiate objects yourself: you pass a class name or alias, and the DI container resolves the instance lazily, only when the callable is actually invoked. Ways to define an extended callable:
- An object method through a class name or alias (non-static method only):
final readonly class Foo { public function __construct(private MyHeavyDependency $dependency) {} public function bar() { // do stuff } } $callable = [Foo::class, 'bar']; // resolves Foo from container and calls $foo->bar()
[!NOTE] This format differs from Type 1: here the object is resolved from the DI container lazily when the callable is invoked. In Type 1, the same array syntax works only for static methods or pre-instantiated objects. Here is a simplified example of how it works (for non-static methods):
if ($container->has($callable[0])) { $callable[0] = $container->get($callable[0]); } $callable();
Note
If bar is declared static, no object is instantiated — the method is called statically on the class. In this case, Type 2 behaves the same as Type 1.
- Class name of an object with the
__invokemethod implemented:It works the same way as above: an object will be retrieved from a DI container and called as a function.$callable = Foo::class;
Note
You can use an alias registered in your DI Container instead of a class name. This will also work if you have a "class alias" definition in container:
$callable = 'class alias'; // for a "callable object"
$callable2 = ['class alias', 'foo']; // to call "foo" method of an object found by "class alias" in DI ContainerThe factory throws Yiisoft\Queue\Middleware\InvalidCallableConfigurationException when it cannot create a callable (for example: null, unsupported array format, missing method, container entry is not callable).