Route::parseUri()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 8
nc 1
nop 0
dl 0
loc 12
rs 10
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
3
namespace OpenEngine\Mika\Core\Components\Route;
4
5
use OpenEngine\Di\Di;
6
use OpenEngine\Di\Exceptions\ClassNotFoundException;
7
use OpenEngine\Di\Exceptions\MethodNotFoundException;
8
use OpenEngine\Di\Exceptions\MissingMethodArgumentException;
9
use OpenEngine\Helpers\Path;
10
use OpenEngine\Http\Exceptions\NotFoundHttpException;
11
use OpenEngine\Mika\Core\Components\Route\Events\AfterCallActionEvent;
12
use OpenEngine\Mika\Core\Components\Route\Events\BeforeCallActionEvent;
13
use OpenEngine\Mika\Core\Components\Route\Interfaces\RouteConfigInterface;
14
use OpenEngine\Mika\Core\Components\Route\Interfaces\RouteInterface;
15
use Psr\Container\ContainerInterface;
16
use Psr\EventDispatcher\EventDispatcherInterface;
17
use Psr\Http\Message\RequestInterface;
18
use Psr\Http\Message\ResponseInterface;
19
20
/**
21
 * Class Route
22
 * @package OpenEngine\Mika\Core\Components\Route
23
 */
24
class Route implements RouteInterface
25
{
26
    /**
27
     * Default route name
28
     */
29
    public const DEFAULT_ROUTE = 'default';
30
31
    /**
32
     * @var RequestInterface
33
     */
34
    private $request;
35
36
    /**
37
     * @var string
38
     */
39
    private $main;
40
41
    /**
42
     * @var string
43
     */
44
    private $secondary;
45
46
    /**
47
     * @var string
48
     */
49
    private $action;
50
51
    /**
52
     * @var string
53
     */
54
    private $current;
55
56
    /**
57
     * @var array
58
     */
59
    private $routes;
60
61
    /**
62
     * @var ContainerInterface|Di
63
     */
64
    private $container;
65
66
    /**
67
     * @var EventDispatcherInterface
68
     */
69
    private $eventDispatcher;
70
71
    /**
72
     * @inheritdoc
73
     */
74
    public function __construct(
75
        RouteConfigInterface $routeConfig,
76
        RequestInterface $request,
77
        ContainerInterface $container,
78
        EventDispatcherInterface $eventDispatcher
79
    ) {
80
        $this->routes = $routeConfig->getRoutes();
81
        $this->request = $request;
82
83
        $this->parseUri();
84
        $this->container = $container;
85
        $this->eventDispatcher = $eventDispatcher;
86
    }
87
88
    /**
89
     * @inheritdoc
90
     */
91
    public function getCurrent(): string
92
    {
93
        return $this->current;
94
    }
95
96
    /**
97
     * @inheritdoc
98
     */
99
    public function getMain(): string
100
    {
101
        return $this->main;
102
    }
103
104
    /**
105
     * @inheritdoc
106
     */
107
    public function getSecondary(): string
108
    {
109
        return $this->secondary;
110
    }
111
112
    /**
113
     * @inheritdoc
114
     */
115
    public function getAction(): string
116
    {
117
        return $this->action;
118
    }
119
120
    /**
121
     * {@inheritdoc}
122
     * @throws ClassNotFoundException
123
     * @throws NotFoundHttpException
124
     * @throws MethodNotFoundException
125
     * @throws MissingMethodArgumentException
126
     */
127
    public function callControllerAction(): ResponseInterface
128
    {
129
        $controller = $this->getController();
130
        return $this->callAction($controller);
131
    }
132
133
    /**
134
     * @inheritdoc
135
     */
136
    public function getRoutes(): array
137
    {
138
        return $this->routes;
139
    }
140
141
    /**
142
     * @return object
143
     * @throws MethodNotFoundException
144
     * @throws MissingMethodArgumentException
145
     * @throws NotFoundHttpException
146
     * @throws ClassNotFoundException
147
     */
148
    private function getController(): object
149
    {
150
        foreach ($this->getRoute($this->getMain()) as $className) {
151
            $controller = basename(Path::getPathByNamespace($className), 'Controller');
152
153
            if ($controller === ucfirst($this->getSecondary())) {
154
                return $this->container->createObject($className);
0 ignored issues
show
Bug introduced by
The method createObject() does not exist on Psr\Container\ContainerInterface. It seems like you code against a sub-type of Psr\Container\ContainerInterface such as OpenEngine\Di\Di. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

154
                return $this->container->/** @scrutinizer ignore-call */ createObject($className);
Loading history...
155
            }
156
        }
157
158
        throw new NotFoundHttpException('Controller for ' . $this->getCurrent() . ' is not found');
159
    }
160
161
    /**
162
     * @inheritdoc
163
     */
164
    private function hasRoute(string $name): bool
165
    {
166
        return isset($this->routes[$name]);
167
    }
168
169
    /**
170
     * Get registered as $name route
171
     *
172
     * @param string $name
173
     * @return array
174
     * @throws NotFoundHttpException Throws this exception when $name route is not registered
175
     */
176
    private function getRoute(string $name): array
177
    {
178
        if (!$this->hasRoute($name)) {
179
            throw new NotFoundHttpException('Route ' . $name . ' is not registered');
180
        }
181
182
        return $this->routes[$name];
183
184
    }
185
186
    private function parseUri(): void
187
    {
188
        $parts = explode('/', $this->request->getUri()->getPath());
189
190
        $this->main = $this->getPart($parts, 1);
191
        $this->secondary = $this->getPart($parts, 2);
192
        $this->action = $this->getPart($parts, 3);
193
194
        $this->current =
195
            '/' . $this->main .
196
            '/' . $this->secondary .
197
            '/' . $this->action;
198
    }
199
200
    private function getPart(array $parts, $key): string
201
    {
202
        return !empty($parts[$key]) && $parts[$key] !== '/' ? $parts[$key] : self::DEFAULT_ROUTE;
203
    }
204
205
    /**
206
     * This method triggers {@see BeforeCallActionEvent} and {@see AfterCallActionEvent} events
207
     *
208
     * @param object $controller
209
     * @return ResponseInterface
210
     * @throws MethodNotFoundException
211
     * @throws MissingMethodArgumentException
212
     * @see BeforeCallActionEvent
213
     * @see AfterCallActionEvent
214
     */
215
    private function callAction(object $controller): ResponseInterface
216
    {
217
        $methodName = $this->getAction() . 'Action';
218
        // todo user Request method instead of $_GET
219
220
        $controllerName = \get_class($controller);
221
222
        $depends = $this->container->createMethodDepends($controllerName, $methodName, $_GET);
0 ignored issues
show
Bug introduced by
The method createMethodDepends() does not exist on Psr\Container\ContainerInterface. It seems like you code against a sub-type of Psr\Container\ContainerInterface such as OpenEngine\Di\Di. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

222
        /** @scrutinizer ignore-call */ 
223
        $depends = $this->container->createMethodDepends($controllerName, $methodName, $_GET);
Loading history...
223
224
        $this->eventDispatcher->dispatch(new BeforeCallActionEvent($controllerName, $methodName, $depends));
225
226
        $response = \call_user_func_array([$controller, $methodName], $depends);
227
228
        $event = new AfterCallActionEvent($controllerName, $methodName, $depends, $response);
229
        $event = $this->eventDispatcher->dispatch($event);
230
231
        return $event->getResponse();
232
    }
233
}
234