MiddlewareFactoryPush   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 132
Duplicated Lines 0 %

Test Coverage

Coverage 91.67%

Importance

Changes 0
Metric Value
eloc 45
dl 0
loc 132
ccs 44
cts 48
cp 0.9167
rs 10
c 0
b 0
f 0
wmc 16

11 Methods

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