MiddlewareFactoryFailure::wrapCallable()
last analyzed

Size

Total Lines 22
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 9
nc 1
nop 1
dl 0
loc 22
ccs 9
cts 9
cp 1
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
A MiddlewareFactoryFailure.php$0 ➔ processFailure() 0 12 3
A MiddlewareFactoryFailure.php$0 ➔ __construct() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Queue\Middleware\FailureHandling;
6
7
use Closure;
8
use Psr\Container\ContainerExceptionInterface;
9
use Psr\Container\ContainerInterface;
10
use Psr\Container\NotFoundExceptionInterface;
11
use Yiisoft\Definitions\ArrayDefinition;
12
use Yiisoft\Definitions\Exception\InvalidConfigException;
13
use Yiisoft\Definitions\Helpers\DefinitionValidator;
14
use Yiisoft\Injector\Injector;
15
use Yiisoft\Queue\Middleware\CallableFactory;
16
use Yiisoft\Queue\Middleware\InvalidCallableConfigurationException;
17
use Yiisoft\Queue\Middleware\InvalidMiddlewareDefinitionException;
18
19
use function is_string;
20
21
/**
22
 * Creates a middleware based on the definition provided.
23
 */
24
final class MiddlewareFactoryFailure implements MiddlewareFactoryFailureInterface
25
{
26
    /**
27
     * @param ContainerInterface $container Container to use for resolving definitions.
28
     */
29 39
    public function __construct(
30
        private ContainerInterface $container,
31
        private CallableFactory $callableFactory,
32
    ) {
33 39
    }
34
35
    /**
36
     * @param array|callable|MiddlewareFailureInterface|string $middlewareDefinition Middleware definition in one of the
37
     *     following formats:
38
     *
39
     * - A middleware object.
40
     * - A name of a middleware class. The middleware instance will be obtained from container and executed.
41
     * - A callable with `function(ServerRequestInterface $request, RequestHandlerInterface $handler):
42
     *     ResponseInterface` signature.
43
     * - A controller handler action in format `[TestController::class, 'index']`. `TestController` instance will
44
     *   be created and `index()` method will be executed.
45
     * - A function returning a middleware. The middleware returned will be executed.
46
     *
47
     * For handler action and callable
48
     * typed parameters are automatically injected using dependency injection container.
49
     * Current request and handler could be obtained by type-hinting for {@see ServerRequestInterface}
50
     * and {@see RequestHandlerInterface}.
51
     *
52
     * @throws NotFoundExceptionInterface
53
     * @throws ContainerExceptionInterface
54
     *
55
     * @return MiddlewareFailureInterface
56
     */
57 22
    public function createFailureMiddleware(
58
        MiddlewareFailureInterface|callable|array|string $middlewareDefinition
59
    ): MiddlewareFailureInterface {
60 22
        if ($middlewareDefinition instanceof MiddlewareFailureInterface) {
0 ignored issues
show
introduced by
$middlewareDefinition is never a sub-type of Yiisoft\Queue\Middleware...dlewareFailureInterface.
Loading history...
61 1
            return $middlewareDefinition;
62
        }
63
64 22
        if (is_string($middlewareDefinition)) {
0 ignored issues
show
introduced by
The condition is_string($middlewareDefinition) is always false.
Loading history...
65 6
            return $this->getFromContainer($middlewareDefinition);
66
        }
67
68 17
        return $this->tryGetFromCallable($middlewareDefinition)
69 17
            ?? $this->tryGetFromArrayDefinition($middlewareDefinition)
70 11
            ?? throw new InvalidMiddlewareDefinitionException($middlewareDefinition);
71
    }
72
73 6
    private function getFromContainer(string $middlewareDefinition): MiddlewareFailureInterface
74
    {
75 6
        if (class_exists($middlewareDefinition)) {
76 5
            if (is_subclass_of($middlewareDefinition, MiddlewareFailureInterface::class)) {
77
                /** @var MiddlewareFailureInterface */
78 5
                return $this->container->get($middlewareDefinition);
79
            }
80 1
        } elseif ($this->container->has($middlewareDefinition)) {
81
            $middleware = $this->container->get($middlewareDefinition);
82
            if ($middleware instanceof MiddlewareFailureInterface) {
83
                return $middleware;
84
            }
85
        }
86
87 2
        throw new InvalidMiddlewareDefinitionException($middlewareDefinition);
88
    }
89
90 10
    private function wrapCallable(callable $callback): MiddlewareFailureInterface
91
    {
92 10
        return new class ($callback, $this->container) implements MiddlewareFailureInterface {
93
            private $callback;
94
95
            public function __construct(callable $callback, private ContainerInterface $container)
96
            {
97 10
                $this->callback = $callback;
98
            }
99
100
            public function processFailure(FailureHandlingRequest $request, MessageFailureHandlerInterface $handler): FailureHandlingRequest
101
            {
102 10
                $response = (new Injector($this->container))->invoke($this->callback, [$request, $handler]);
103 10
                if ($response instanceof FailureHandlingRequest) {
104 8
                    return $response;
105
                }
106
107 3
                if ($response instanceof MiddlewareFailureInterface) {
108 2
                    return $response->processFailure($request, $handler);
109
                }
110
111 1
                throw new InvalidMiddlewareDefinitionException($this->callback);
112
            }
113 10
        };
114
    }
115
116 17
    private function tryGetFromCallable(
117
        callable|MiddlewareFailureInterface|array|string $definition
118
    ): ?MiddlewareFailureInterface {
119 17
        if ($definition instanceof Closure) {
0 ignored issues
show
introduced by
$definition is never a sub-type of Closure.
Loading history...
120 6
            return $this->wrapCallable($definition);
121
        }
122
123
        if (
124 12
            is_array($definition)
125 12
            && array_keys($definition) === [0, 1]
126
        ) {
127
            try {
128 8
                return $this->wrapCallable($this->callableFactory->create($definition));
129 3
            } catch (InvalidCallableConfigurationException $exception) {
130 3
                throw new InvalidMiddlewareDefinitionException($definition, previous: $exception);
131
            }
132
        } else {
133 5
            return null;
134
        }
135
    }
136
137 5
    private function tryGetFromArrayDefinition(
138
        callable|MiddlewareFailureInterface|array|string $definition
139
    ): ?MiddlewareFailureInterface {
140 5
        if (!is_array($definition)) {
0 ignored issues
show
introduced by
The condition is_array($definition) is always true.
Loading history...
141
            return null;
142
        }
143
144
        try {
145 5
            DefinitionValidator::validateArrayDefinition($definition);
146
147 3
            $middleware = ArrayDefinition::fromConfig($definition)->resolve($this->container);
148 3
            if ($middleware instanceof MiddlewareFailureInterface) {
149 2
                return $middleware;
150
            }
151
152 1
            throw new InvalidMiddlewareDefinitionException($definition);
153 3
        } catch (InvalidConfigException) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
154
        }
155
156 2
        throw new InvalidMiddlewareDefinitionException($definition);
157
    }
158
}
159