You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Сallable definitions in `yiisoft/queue` extend [native PHP callables](https://www.php.net/manual/en/language.types.callable.php). That means, there are two types of definitions. Nevertheless, each of them may define dependency list in their parameter lists, which will be resolved via [yiisoft/injector](https://github.com/yiisoft/injector) and a DI Container.
4
+
It is used across the package to convert configuration definitions into real callables.
5
+
6
+
## Type 1: Native PHP callable
7
+
8
+
When you define a callable in a such manner, they are not modified in any way and are called as is. An only difference is that you can define dependency list in their parameter lists, which will be resolved via [yiisoft/injector](https://github.com/yiisoft/injector) and a DI Container.
9
+
As you can see in the [PHP documentation](https://www.php.net/manual/en/language.types.callable.php), there are several ways to define a native callable:
10
+
11
+
-**Closure (lambda function)**. It may be static. Example:
12
+
```php
13
+
$callable = static function(Update $update) {
14
+
// do stuff
15
+
}
16
+
```
17
+
-**First class callable**. It's a Closure too, BTW ;) Example:
18
+
```php
19
+
$callable = trim(...);
20
+
$callable2 = $this->foo(...);
21
+
```
22
+
-**A class static function**. When a class has a static function, an array syntax may be used:
23
+
```php
24
+
$callable = [Foo::class, 'bar']; // this will be called the same way as Foo::bar();
25
+
```
26
+
-**An object method**. The same as above, but with an object and a non-static method:
27
+
```php
28
+
$foo = new Foo();
29
+
$callable = [$foo, 'bar']; // this will be called the same way as $foo->bar();
30
+
```
31
+
-**A class static function as a string**. I don't recommend you to use this ability, as it's non-obvious and
32
+
hard to refactor, but it still exists:
33
+
```php
34
+
$callable = 'Foo::bar'; // this will be called the same way as Foo::bar();
35
+
```
36
+
-**A name of a named function**:
37
+
```php
38
+
function foo() {
39
+
// do stuff
40
+
}
41
+
$callable = 'foo';
42
+
$callable2 = 'array_map';
43
+
```
44
+
-**Callable objects**. An object with [the `__invoke` method](https://www.php.net/manual/en/language.oop5.magic.php#object.invoke) implemented:
45
+
```php
46
+
class Foo
47
+
{
48
+
public function __invoke()
49
+
{
50
+
// do stuff
51
+
}
52
+
}
53
+
54
+
$callable = new Foo();
55
+
```
56
+
57
+
## Type 2: Callable definition extensions (via container)
58
+
59
+
Under the hood, this extension behaves exactly like the **Type 1** ones. But there is a major difference too:
60
+
all the objects are instantiated automatically with a PSR-11 DI Container with all their dependencies
61
+
and in a lazy way (only when they are really needed).
62
+
Ways to define an extended callable:
63
+
64
+
- An object method through a class name or alias:
65
+
```php
66
+
final readonly class Foo
67
+
{
68
+
public function __construct(private MyHeavyDependency $dependency) {}
69
+
70
+
public function bar()
71
+
{
72
+
// do stuff
73
+
}
74
+
}
75
+
76
+
$callable = [Foo::class, 'bar'];
77
+
```
78
+
Here is a simplified example of how it works:
79
+
```php
80
+
if ($container->has($callable[0])) {
81
+
$callable[0] = $container->get($callable[0])
82
+
}
83
+
84
+
$callable();
85
+
```
86
+
- Class name of an object with [the `__invoke` method](https://www.php.net/manual/en/language.oop5.magic.php#object.invoke) implemented:
87
+
```php
88
+
$callable = Foo::class;
89
+
```
90
+
It works the same way as above: an object will be retrieved from a DI container and called as a function.
91
+
92
+
_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:
93
+
```php
94
+
$callable = 'class alias'; // for a "callable object"
95
+
$callable2 = ['class alias', 'foo']; // to call "foo" method of an object found by "class alias" in DI Container
96
+
```
97
+
98
+
## Invalid definitions
99
+
100
+
The 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).
Copy file name to clipboardExpand all lines: docs/guide/en/error-handling.md
+16-12Lines changed: 16 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,9 +4,7 @@ Often when some message handling is failing, we want to retry its execution a co
4
4
5
5
## Configuration
6
6
7
-
Here below is configuration via [yiisoft/config](https://github.com/yiisoft/config). If you don't use it, you should add a middleware definition list (in the `middlewares-fail` key here) to the `FailureMiddlewareDispatcher` by your own.
8
-
9
-
Configuration should be passed to the `yiisoft/queue.fail-strategy-pipelines` key of the `params` config to work with the [yiisoft/config](https://github.com/yiisoft/config). You can define different failure handling pipelines for each queue channel. Let's see and describe an example:
7
+
Here below is configuration via [yiisoft/config](https://github.com/yiisoft/config). If you don't use it, you should add a middleware definition list (in the `middlewares-fail` key here) to the `FailureMiddlewareDispatcher` by your own. You can define different failure handling pipelines for each queue channel. The example below defines two different failure handling pipelines:
10
8
11
9
```php
12
10
'yiisoft/queue' => [
@@ -39,11 +37,15 @@ Configuration should be passed to the `yiisoft/queue.fail-strategy-pipelines` ke
39
37
]
40
38
```
41
39
42
-
Keys here except `FailureMiddlewareDispatcher::DEFAULT_PIPELINE` are queue channel names, and values are lists of `FailureMiddlewareInterface` definitions. `FailureMiddlewareDispatcher::DEFAULT_PIPELINE` defines a default pipeline to apply to channels without an explicitly defined failure strategy pipeline. Each middleware definition must be one of:
40
+
Here is the meaning of the keys:
41
+
- The `failed-messages` key couples the defined pipeline with the `failed-messages` queue channel.
42
+
- The `FailureMiddlewareDispatcher::DEFAULT_PIPELINE` key couples the defined pipeline with all queue channels without an explicitly defined failure strategy pipeline.
43
+
44
+
Each middleware definition must be one of:
43
45
- A ready-to-use `MiddlewareFailureInterface` object like `new FooMiddleware()`.
44
46
- A valid definition for the [yiisoft/definitions](https://github.com/yiisoft/definitions). It must describe an object, implementing the `MiddlewareFailureInterface`.
45
-
-A callable: `fn() => // do stuff`, `$object->foo(...)`, etc. It will be executed through the [yiisoft/injector](https://github.com/yiisoft/injector), so all the dependencies of your callable will be resolved. You can also define a "callable-looking" array, where an object will be instantiated with a DI container: `[FooMiddleware::class, 'handle']`.
46
-
-A string for your DI container to resolve the middleware, e.g. `FooMiddleware::class`.
This strategy simply resends the given message to a queue. Let's see the constructor parameters through which it's configured:
64
66
65
67
-`id` - A unique string. Allows to use this strategy more than once for the same message, just like in example above.
66
68
-`maxAttempts` - Maximum attempts count for this strategy with the given $id before it will give up.
67
69
-`queue` - The strategy will send the message to the given queue when it's not `null`. That means you can use this strategy to push a message not to the same queue channel it came from. When the `queue` parameter is set to `null`, a message will be sent to the same channel it came from.
This strategy does the same thing as the `SendAgainMiddleware` with a single difference: it resends a message with an exponentially increasing delay. The delay **must** be implemented by the used `AdapterInterface` implementation.
72
74
@@ -82,9 +84,11 @@ It's configured via constructor parameters, too. Here they are:
82
84
## How to create a custom Failure Middleware?
83
85
84
86
All you need is to implement the `MiddlewareFailureInterface` and add your implementation definition to the [configuration](#configuration).
85
-
This interface has the only method `handle`. And the method has these parameters:
86
-
-`ConsumeRequest $request` - a request for a message handling. It consists of a message and a queue the message came from.
87
-
-`Throwable $exception` - an exception thrown on the `request` handling
87
+
This interface has the only method `handle` with these parameters:
88
+
-[`FailureHandlingRequest $request`](../../../src/Middleware/FailureHandling/FailureHandlingRequest.php) - a request for a message handling. It consists of
89
+
- a [message](../../../src/Message/MessageInterface.php)
90
+
- a `Throwable $exception` object thrown on the `request` handling
91
+
- a queue the message came from
88
92
-`MessageFailureHandlerInterface $handler` - failure strategy pipeline continuation. Your Middleware should call `$pipeline->handle()` when it shouldn't interrupt failure pipeline execution.
89
93
90
-
> Note: your strategy have to check by its own if it should be applied. Look into [`SendAgainMiddleware::suites()`](../../src/Middleware/Implementation/FailureMiddleware/Middleware/SendAgainMiddleware.php#L52) for an example.
94
+
> Note: your strategy have to check by its own if it should be applied. Look into [`SendAgainMiddleware::suites()`](../../../src/Middleware/FailureHandling/Implementation/SendAgainMiddleware.php#L54) for an example.
If your handler is a dedicated class implementing `Yiisoft\Queue\Message\MessageHandlerInterface`, you can use the class name itself as the message handler name.
14
+
If your handler is a dedicated class implementing `Yiisoft\Queue\Message\MessageHandlerInterface`, you can use the class name itself as the message handler name (FQCN) if your DI container can resolve the handler class.
15
+
16
+
> By default the [yiisoft/di](https://github.com/yiisoft/di) container resolves all FQCNs into corresponding class objects.
17
17
18
18
This is the default and most convenient option when the producer and the consumer are the same application.
19
19
@@ -57,9 +57,9 @@ Not needed
57
57
- Producer and consumer are the same application.
58
58
- You control message creation code and can safely use FQCN as the handler name.
59
59
60
-
### 2. Closure
60
+
### 2. Named handlers
61
61
62
-
In this and all the cases below, you should use a proper handler name when pushing a `Message` instead of a handler class name in the example above:
62
+
In this case you should use a proper handler name when pushing a `Message` instead of a handler class name as in the example above:
63
63
64
64
```php
65
65
new \Yiisoft\Queue\Message\Message('send-email', ['data' => '...']);
@@ -73,105 +73,13 @@ Map handler name to a closure in `$params`:
- If the method is static, it is called statically: `[$className, $methodName]`. Dependencies may be passed *to the provided method* in case they are resolvable from the DI container.
159
-
- If the first element is an object instance, it is called as `$firstElement->$methodName(...)` with dependency injection applied *to the $methodName*.
160
-
- If the method is not static, the class must be resolvable from the DI container, and the worker calls `$container->get($className)->$methodName(...)`. DI container will also resolve dependencies declared in the *class constructor*.
161
-
162
-
**Pros**:
163
-
164
-
- Explicit method name, good for “classic” `handle()` methods.
165
-
- Supports static methods for pure, dependency-free handlers.
166
-
167
-
**Cons**:
168
-
169
-
- Harder to maintain and refactor than regular class definitions with either `__invoke` method or `MessageHandlerInterface` implementation.
170
-
171
-
**Use when**:
172
-
173
-
- You want to use static handlers (rare, but can be useful for pure transforms).
174
-
- You want to group different handlers in a single class for organizational purposes.
82
+
Handler definition should be either an [extended callable definition](./callable-definitions-extended.md) or a string for your DI container to resolve a `MessageHandlerInterface` instance.
175
83
176
84
## When mapping by short names is a better idea
177
85
@@ -199,7 +107,7 @@ This way external producers never need to know your internal PHP class names.
199
107
200
108
## Common pitfalls and unsupported formats
201
109
202
-
- A string definition is **not**treated as a function name. It is treated only as a DI container ID.
110
+
- A string definition is treated as a DI container ID first. If the container doesn't have such entry, it is resolved as a callable only when it is a valid PHP callable.
203
111
- A class-string that is not resolvable via `$container->has()` will not be auto-instantiated.
204
112
-[yiisoft/definitions](https://github.com/yiisoft/definitions) array format (like `['class' => ..., '__construct()' => ...]`) is **not** supported for handlers.
Copy file name to clipboardExpand all lines: docs/guide/en/middleware-pipelines.md
+2-3Lines changed: 2 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -57,10 +57,9 @@ graph LR
57
57
You can use any of these formats:
58
58
59
59
- A ready-to-use middleware object.
60
-
- An array in the format of [yiisoft/definitions](https://github.com/yiisoft/definitions).
61
-
- A `callable` (closure, invokable object, `[$object, 'method']`, etc.). It is executed through the
62
-
[yiisoft/injector](https://github.com/yiisoft/injector), so its dependencies are resolved automatically.
60
+
- An array in the format of [yiisoft/definitions](https://github.com/yiisoft/definitions), which defines a middleware implementation.
63
61
- A string for your DI container to resolve the middleware, e.g. `FooMiddleware::class`.
62
+
- An [extended callable definition](callable-definitions-extended.md). A callable should either be a middleware itself or return a configured middleware object.
0 commit comments