MiddlewareFactoryConsume.php$0 ➔ wrapCallable()   A
last analyzed

Complexity

Conditions 3

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
dl 0
loc 22
ccs 9
cts 9
cp 1
crap 3
rs 9.568
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
A MiddlewareFactoryConsume.php$0 ➔ processConsume() 0 12 3
A MiddlewareFactoryConsume.php$0 ➔ __construct() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Queue\Middleware\Consume;
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 MiddlewareFactoryConsume implements MiddlewareFactoryConsumeInterface
23
{
24
    /**
25
     * @param ContainerInterface $container Container to use for resolving definitions.
26
     */
27 42
    public function __construct(
28
        private ContainerInterface $container,
29
        private CallableFactory $callableFactory,
30
    ) {
31 42
    }
32
33
    /**
34
     * @param array|callable|MiddlewareConsumeInterface|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 MiddlewareConsumeInterface
53
     */
54 24
    public function createConsumeMiddleware(
55
        MiddlewareConsumeInterface|callable|array|string $middlewareDefinition
56
    ): MiddlewareConsumeInterface {
57 24
        if ($middlewareDefinition instanceof MiddlewareConsumeInterface) {
0 ignored issues
show
introduced by
$middlewareDefinition is never a sub-type of Yiisoft\Queue\Middleware...dlewareConsumeInterface.
Loading history...
58 1
            return $middlewareDefinition;
59
        }
60
61 23
        if (is_string($middlewareDefinition)) {
0 ignored issues
show
introduced by
The condition is_string($middlewareDefinition) is always false.
Loading history...
62 6
            return $this->getFromContainer($middlewareDefinition);
63
        }
64
65 18
        return $this->tryGetFromCallable($middlewareDefinition)
66 18
            ?? $this->tryGetFromArrayDefinition($middlewareDefinition)
67 12
            ?? throw new InvalidMiddlewareDefinitionException($middlewareDefinition);
68
    }
69
70 6
    private function getFromContainer(string $middlewareDefinition): MiddlewareConsumeInterface
71
    {
72 6
        if (class_exists($middlewareDefinition)) {
73 4
            if (is_subclass_of($middlewareDefinition, MiddlewareConsumeInterface::class)) {
74
                /** @var MiddlewareConsumeInterface */
75 4
                return $this->container->get($middlewareDefinition);
76
            }
77 2
        } elseif ($this->container->has($middlewareDefinition)) {
78 1
            $middleware = $this->container->get($middlewareDefinition);
79 1
            if ($middleware instanceof MiddlewareConsumeInterface) {
80 1
                return $middleware;
81
            }
82
        }
83
84 2
        throw new InvalidMiddlewareDefinitionException($middlewareDefinition);
85
    }
86
87 11
    private function wrapCallable(callable $callback): MiddlewareConsumeInterface
88
    {
89 11
        return new class ($callback, $this->container) implements MiddlewareConsumeInterface {
90
            private $callback;
91
92
            public function __construct(callable $callback, private ContainerInterface $container)
93
            {
94 11
                $this->callback = $callback;
95
            }
96
97
            public function processConsume(ConsumeRequest $request, MessageHandlerConsumeInterface $handler): ConsumeRequest
98
            {
99 11
                $response = (new Injector($this->container))->invoke($this->callback, [$request, $handler]);
100 11
                if ($response instanceof ConsumeRequest) {
101 8
                    return $response;
102
                }
103
104 3
                if ($response instanceof MiddlewareConsumeInterface) {
105 1
                    return $response->processConsume($request, $handler);
106
                }
107
108 2
                throw new InvalidMiddlewareDefinitionException($this->callback);
109
            }
110 11
        };
111
    }
112
113 18
    private function tryGetFromCallable(
114
        callable|MiddlewareConsumeInterface|array|string $definition
115
    ): ?MiddlewareConsumeInterface {
116 18
        if ($definition instanceof Closure) {
0 ignored issues
show
introduced by
$definition is never a sub-type of Closure.
Loading history...
117 6
            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|MiddlewareConsumeInterface|array|string $definition
136
    ): ?MiddlewareConsumeInterface {
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 MiddlewareConsumeInterface) {
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