Passed
Pull Request — master (#68)
by Dmitriy
15:07
created

MiddlewareFactory.php$1 ➔ validateMiddleware()   A

Complexity

Conditions 6

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
c 0
b 0
f 0
dl 0
loc 13
rs 9.2222
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Router;
6
7
use InvalidArgumentException;
8
use Psr\Container\ContainerInterface;
9
use Psr\Http\Message\ResponseInterface;
10
use Psr\Http\Message\ServerRequestInterface;
11
use Psr\Http\Server\MiddlewareInterface;
12
use Psr\Http\Server\RequestHandlerInterface;
13
use Yiisoft\Injector\Injector;
14
15
final class MiddlewareFactory implements MiddlewareFactoryInterface
16
{
17
    private ContainerInterface $container;
18
19
    public function __construct(ContainerInterface $container)
20
    {
21
        $this->container = $container;
22
    }
23
24
    public function create($middlewareDefinition): MiddlewareInterface
25
    {
26
        $this->validateMiddleware($middlewareDefinition);
27
        return $this->createMiddleware($middlewareDefinition);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->createMidd...($middlewareDefinition) also could return the type array|string which is incompatible with the return type mandated by Yiisoft\Router\Middlewar...toryInterface::create() of Psr\Http\Server\MiddlewareInterface.
Loading history...
28
    }
29
30
    /**
31
     * @param callable|string|array $middleware
32
     * @return MiddlewareInterface|string|array
33
     */
34
    private function createMiddleware($middlewareDefinition)
35
    {
36
        if (is_string($middlewareDefinition)) {
37
            if ($this->container === null) {
38
                throw new InvalidArgumentException('Route container must not be null for lazy loaded middleware.');
39
            }
40
            return $this->container->get($middlewareDefinition);
41
        }
42
43
        if (is_array($middlewareDefinition) && !is_object($middlewareDefinition[0])) {
44
            if ($this->container === null) {
45
                throw new InvalidArgumentException('Route container must not be null for handler action.');
46
            }
47
            return $this->wrapCallable($middlewareDefinition);
48
        }
49
50
        if ($this->isCallable($middlewareDefinition)) {
51
            if ($this->container === null) {
52
                throw new InvalidArgumentException('Route container must not be null for callable.');
53
            }
54
            return $this->wrapCallable($middlewareDefinition);
55
        }
56
57
        return $middlewareDefinition;
58
    }
59
60
    private function wrapCallable($callback): MiddlewareInterface
61
    {
62
        if (is_array($callback) && !is_object($callback[0])) {
63
            [$controller, $action] = $callback;
64
            return new class($controller, $action, $this->container) implements MiddlewareInterface {
65
                private string $class;
66
                private string $method;
67
                private ContainerInterface $container;
68
69
                public function __construct(string $class, string $method, ContainerInterface $container)
70
                {
71
                    $this->class = $class;
72
                    $this->method = $method;
73
                    $this->container = $container;
74
                }
75
76
                public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
77
                {
78
                    $controller = $this->container->get($this->class);
79
                    return (new Injector($this->container))->invoke([$controller, $this->method], [$request, $handler]);
80
                }
81
            };
82
        }
83
84
        return new class($callback, $this->container) implements MiddlewareInterface {
85
            private ContainerInterface $container;
86
            private $callback;
87
88
            public function __construct(callable $callback, ContainerInterface $container)
89
            {
90
                $this->callback = $callback;
91
                $this->container = $container;
92
            }
93
94
            public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
95
            {
96
                $response = (new Injector($this->container))->invoke($this->callback, [$request, $handler]);
97
                return $response instanceof MiddlewareInterface ? $response->process($request, $handler) : $response;
98
            }
99
        };
100
    }
101
102
    /**
103
     * @param callable|string|array $middlewareDefinition
104
     */
105
    private function validateMiddleware($middlewareDefinition): void
106
    {
107
        if (
108
            is_string($middlewareDefinition) && is_subclass_of($middlewareDefinition, MiddlewareInterface::class)
109
        ) {
110
            return;
111
        }
112
113
        if ($this->isCallable($middlewareDefinition) && (!is_array($middlewareDefinition) || !is_object($middlewareDefinition[0]))) {
114
            return;
115
        }
116
117
        throw new InvalidArgumentException('Parameter should be either PSR middleware class name or a callable.');
118
    }
119
120
    private function isCallable($definition): bool
121
    {
122
        if (is_callable($definition)) {
123
            return true;
124
        }
125
126
        return is_array($definition) && array_keys($definition) === [0, 1] && in_array($definition[1], get_class_methods($definition[0]) ?? [], true);
127
    }
128
}
129