Passed
Push — master ( a4ddd3...82bd79 )
by Alex
06:44
created

UrlParserBase::getMiddlewareResult()   B

Complexity

Conditions 9
Paths 28

Size

Total Lines 40
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 20
c 1
b 1
f 0
dl 0
loc 40
rs 8.0555
cc 9
nc 28
nop 1
1
<?php
2
declare(strict_types = 1);
3
namespace Mezon\Router;
4
5
/**
6
 * Trait UrlParserBase
7
 *
8
 * @package Router
9
 * @author Dodonov A.A.
10
 * @version v.1.0 (2021/09/27)
11
 * @copyright Copyright (c) 2021, aeon.org
12
 */
13
trait UrlParserBase
14
{
15
16
    /**
17
     * Middleware for routes processing
18
     *
19
     * @var array<string, callable[]>
20
     */
21
    protected $middleware = [];
22
23
    /**
24
     * Method registeres middleware for the router
25
     *
26
     * @param callable $middleware
27
     *            middleware
28
     */
29
    public function registerMiddleware(string $router, callable $middleware): void
30
    {
31
        $routerTrimmed = trim($router, '/');
32
33
        if (! isset($this->middleware[$routerTrimmed])) {
34
            $this->middleware[$routerTrimmed] = [];
35
        }
36
37
        $this->middleware[$routerTrimmed][] = $middleware;
38
    }
39
40
    /**
41
     * Parsed parameters of the calling router
42
     *
43
     * @var string[]
44
     */
45
    protected $parameters = [];
46
47
    /**
48
     * Method returns route parameter
49
     *
50
     * @param string $name
51
     *            route parameter
52
     * @return string route parameter
53
     */
54
    public function getParam(string $name): string
55
    {
56
        if (! isset($this->parameters[$name])) {
57
            throw (new \Exception('Parameter ' . $name . ' was not found in route', - 1));
58
        }
59
60
        return $this->parameters[$name];
61
    }
62
63
    /**
64
     * Does parameter exists
65
     *
66
     * @param string $name
67
     *            param name
68
     * @return bool true if the parameter exists
69
     */
70
    public function hasParam(string $name): bool
71
    {
72
        return isset($this->parameters[$name]);
73
    }
74
75
    /**
76
     * Method executes route handler
77
     *
78
     * @param
79
     *            callable|string|array{0: string, 1: string} $processor processor
80
     * @psalm-param callable|string|array{0: string, 1: string} $processor
81
     * @param string $route
82
     *            route
83
     * @return mixed route handler execution result
84
     */
85
    protected function executeHandler($processor, string $route)
86
    {
87
        if ($this->isFunction($processor)) {
88
            return call_user_func_array($processor, $this->getMiddlewareResult($route));
89
        }
90
91
        $functionName = $processor[1] ?? null;
92
93
        if ($this->canBeCalled($processor, $functionName)) {
94
            // passing route path and parameters
95
            return call_user_func_array($processor, $this->getMiddlewareResult($route));
96
        } else {
97
            $callableDescription = Utils::getCallableDescription($processor);
98
99
            if ($this->methodDoesNotExists($processor, $functionName)) {
100
                throw (new \Exception("'$callableDescription' does not exists"));
101
            } else {
102
                // @codeCoverageIgnoreStart
103
                throw (new \Exception("'$callableDescription' must be callable entity"));
104
                // @codeCoverageIgnoreEnd
105
            }
106
        }
107
    }
108
109
    /**
110
     * Method returns middleware processing result
111
     *
112
     * @param string $route
113
     *            processed route
114
     * @return array middleware result
115
     */
116
    private function getMiddlewareResult(string $route): array
117
    {
118
        $middleWares = [];
119
120
        if (isset($this->middleware['*'])) {
121
            $middleWares = $this->middleware['*'];
122
        }
123
124
        if ($this->calledRoute !== '*' && isset($this->middleware[$this->calledRoute])) {
125
            $middleWares = array_merge($middleWares, $this->middleware[$this->calledRoute]);
126
        }
127
128
        $result = [
129
            $route,
130
            $this->parameters
131
        ];
132
133
        if (! count($middleWares)) {
134
            return $result;
135
        }
136
137
        /** @var callable $middleWare */
138
        foreach ($middleWares as $middleWare) {
139
            /** @var array{0: string, 1: string[]}|null $result */
140
            $result = call_user_func($middleWare, $route, $this->parameters);
141
142
            if (is_array($result)) {
143
                if (array_key_exists(0, $result)) {
144
                    $route = $result[0];
145
                }
146
147
                if (array_key_exists(1, $result)) {
148
                    $this->parameters = $result[1];
149
                }
150
            }
151
        }
152
153
        return [
154
            $route,
155
            $this->parameters
156
        ];
157
    }
158
159
    /**
160
     * Method searches dynamic route processor
161
     *
162
     * @param string $route
163
     *            route
164
     * @param string $requestMethod
165
     *            request method
166
     * @return array{0: string, 1:string}|callable|string|false route's handler or false in case the handler was not found
167
     */
168
    abstract protected function getDynamicRouteProcessor(string $route, string $requestMethod = '');
169
170
    /**
171
     * Method searches dynamic route processor
172
     *
173
     * @param string $route
174
     *            route
175
     * @return mixed|false result of the router's call or false if any error occured
176
     */
177
    public function findDynamicRouteProcessor(string $route)
178
    {
179
        $processor = $this->getDynamicRouteProcessor($route);
180
181
        if ($processor === false) {
182
            return false;
183
        }
184
185
        return $this->executeHandler($processor, $route);
186
    }
187
188
    /**
189
     * Checking that method exists
190
     *
191
     * @param mixed $processor
192
     *            callback object
193
     * @param ?string $functionName
194
     *            callback method
195
     * @return bool true if method does not exists
196
     */
197
    private function methodDoesNotExists($processor, ?string $functionName): bool
198
    {
199
        return $functionName === null || (isset($processor[0]) && is_object($processor[0]) && method_exists($processor[0], $functionName) === false);
200
    }
201
202
    /**
203
     * Checking that handler can be called
204
     *
205
     * @param object|array|callable|string $processor
206
     *            callback object
207
     * @param ?string $functionName
208
     *            callback method
209
     * @return bool
210
     * @psalm-suppress InvalidArrayAccess
211
     */
212
    private function canBeCalled($processor, ?string $functionName): bool
213
    {
214
        return is_callable($processor) && ($functionName !== null && is_object($processor[0]) && (method_exists($processor[0], $functionName) || isset($processor[0]->$functionName)));
215
    }
216
217
    /**
218
     * Checking that processor can be called as function
219
     *
220
     * @param mixed $processor
221
     *            route processor
222
     * @return bool true if the $processor can be called as function
223
     */
224
    private function isFunction($processor): bool
225
    {
226
        return is_callable($processor) && is_array($processor) === false;
227
    }
228
229
    /**
230
     * Method returns request method
231
     *
232
     * @return string request method
233
     */
234
    private function getRequestMethod(): string
235
    {
236
        return isset($_SERVER['REQUEST_METHOD']) ? (string) $_SERVER['REQUEST_METHOD'] : 'GET';
237
    }
238
}
239