Passed
Pull Request — master (#70)
by
unknown
11:42
created

MiddlewareFactory   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 115
Duplicated Lines 0 %

Test Coverage

Coverage 97.78%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 115
ccs 44
cts 45
cp 0.9778
rs 10
eloc 47
wmc 18

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A create() 0 3 1
A hp$1 ➔ __construct() 0 4 1
B createMiddleware() 0 21 7
A hp$1 ➔ wrapCallable() 0 38 2
A hp$1 ➔ process() 0 4 2
validateMiddleware() 0 15 ?
A hp$1 ➔ isCallable() 0 12 5
isCallable() 0 12 ?
wrapCallable() 0 38 ?
B hp$1 ➔ validateMiddleware() 0 15 8
A hp$0 ➔ process() 0 4 1
A hp$0 ➔ __construct() 0 5 1
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 23
    public function __construct(ContainerInterface $container)
20
    {
21 23
        $this->container = $container;
22 23
    }
23
24 20
    public function create($middlewareDefinition): MiddlewareInterface
25
    {
26 20
        return $this->createMiddleware($middlewareDefinition);
27
    }
28
29
    /**
30
     * @param callable|string|array $middlewareDefinition
31
     * @return MiddlewareInterface
32
     */
33 20
    private function createMiddleware($middlewareDefinition): MiddlewareInterface
34
    {
35 20
        $this->validateMiddleware($middlewareDefinition);
36
37 14
        if (is_string($middlewareDefinition)) {
38 1
            return $this->container->get($middlewareDefinition);
39
        }
40
41 13
        if (is_array($middlewareDefinition) && !is_object($middlewareDefinition[0])) {
42 4
            return $this->wrapCallable($middlewareDefinition);
43
        }
44
45 9
        if (is_object($middlewareDefinition) && $middlewareDefinition instanceof MiddlewareInterface) {
46 9
            return $middlewareDefinition;
47
        }
48
49
        if ($this->isCallable($middlewareDefinition)) {
50
            return $this->wrapCallable($middlewareDefinition);
51
        }
52 13
53
        throw new \RuntimeException('Middleware creating error.');
54 13
    }
55 4
56 4
    private function wrapCallable($callback): MiddlewareInterface
57
    {
58
        if (is_array($callback) && !is_object($callback[0])) {
59
            [$controller, $action] = $callback;
60
            return new class($controller, $action, $this->container) implements MiddlewareInterface {
61
                private string $class;
62
                private string $method;
63 4
                private ContainerInterface $container;
64 4
65 4
                public function __construct(string $class, string $method, ContainerInterface $container)
66 4
                {
67
                    $this->class = $class;
68
                    $this->method = $method;
69
                    $this->container = $container;
70 3
                }
71 3
72
                public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
73
                {
74
                    $controller = $this->container->get($this->class);
75
                    return (new Injector($this->container))->invoke([$controller, $this->method], [$request, $handler]);
76 9
                }
77
            };
78
        }
79
80
        return new class($callback, $this->container) implements MiddlewareInterface {
81
            private ContainerInterface $container;
82 9
            private $callback;
83 9
84 9
            public function __construct(callable $callback, ContainerInterface $container)
85
            {
86
                $this->callback = $callback;
87
                $this->container = $container;
88 9
            }
89 9
90
            public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
91
            {
92
                $response = (new Injector($this->container))->invoke($this->callback, [$request, $handler]);
93
                return $response instanceof MiddlewareInterface ? $response->process($request, $handler) : $response;
94
            }
95
        };
96
    }
97 20
98
    /**
99
     * @param callable|string|array $middlewareDefinition
100 20
     */
101
    private function validateMiddleware($middlewareDefinition): void
102 1
    {
103
        if (is_string($middlewareDefinition) && is_subclass_of($middlewareDefinition, MiddlewareInterface::class)) {
104
            return;
105 19
        }
106 13
107
        if (is_object($middlewareDefinition) && $middlewareDefinition instanceof MiddlewareInterface) {
108
            return;
109 6
        }
110
111
        if ($this->isCallable($middlewareDefinition) && (!is_array($middlewareDefinition) || !is_object($middlewareDefinition[0]))) {
112 19
            return;
113
        }
114 19
115 13
        throw new InvalidArgumentException('Parameter should be either PSR middleware class name or a callable.');
116
    }
117
118 6
    private function isCallable($definition): bool
119 6
    {
120 1
        if (is_callable($definition)) {
121 1
            return true;
122 1
        }
123 6
124
        return is_array($definition)
125
            && array_keys($definition) === [0, 1]
126
            && in_array(
127
                $definition[1],
128
                class_exists($definition[0]) ? get_class_methods($definition[0]) : [],
129
                true
130
            );
131
    }
132
}
133