Router::findRoute()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 4
c 1
b 0
f 0
nc 3
nop 0
dl 0
loc 9
ccs 5
cts 5
cp 1
crap 3
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Gacela\Router;
6
7
use Closure;
8
use Exception;
9
use Gacela\Container\Container;
10
use Gacela\Router\Configure\Bindings;
11
use Gacela\Router\Configure\Handlers;
12
use Gacela\Router\Configure\Routes;
13
use Gacela\Router\Entities\Route;
14
use Gacela\Router\Exceptions\NonCallableHandlerException;
15
use Gacela\Router\Exceptions\NotFound404Exception;
16
use Gacela\Router\Exceptions\UnsupportedRouterConfigureCallableParamException;
17
use ReflectionFunction;
18
19
use function get_class;
20
use function is_callable;
21
use function is_null;
22
23
final class Router implements RouterInterface
24
{
25
    private Routes $routes;
26
    private Bindings $bindings;
27
    private Handlers $handlers;
28
29 112
    public function __construct(Closure $fn = null)
30
    {
31 112
        $this->routes = new Routes();
32 112
        $this->bindings = new Bindings();
33 112
        $this->handlers = new Handlers();
34
35 112
        if (!is_null($fn)) {
36 108
            $this->configure($fn);
37
        }
38
    }
39
40 112
    public function configure(Closure $fn): self
41
    {
42 112
        $params = array_map(fn ($param) => match ((string)$param->getType()) {
43 112
            Routes::class => $this->routes,
44 112
            Bindings::class => $this->bindings,
45 112
            Handlers::class => $this->handlers,
46 112
            default => throw UnsupportedRouterConfigureCallableParamException::fromName($param),
47 112
        }, (new ReflectionFunction($fn))->getParameters());
48
49 111
        $fn(...$params);
50
51 104
        return $this;
52
    }
53
54 104
    public function run(): void
55
    {
56
        try {
57 104
            echo $this->findRoute()->run($this->bindings);
58 18
        } catch (Exception $exception) {
59 18
            echo $this->handleException($exception);
60
        }
61
    }
62
63 104
    private function findRoute(): Route
64
    {
65 104
        foreach ($this->routes->getAllRoutes() as $route) {
66 102
            if ($route->requestMatches()) {
67 97
                return $route;
68
            }
69
        }
70
71 7
        throw new NotFound404Exception();
72
    }
73
74 18
    private function handleException(Exception $exception): string
75
    {
76 18
        $handler = $this->findHandler($exception);
77
78 18
        if (is_callable($handler)) {
79 10
            return $handler($exception);
80
        }
81
82
        /** @psalm-suppress MixedAssignment */
83 8
        $instance = Container::create($handler);
84
85 8
        if (is_callable($instance)) {
86 7
            return $instance($exception);
87
        }
88
89 1
        throw NonCallableHandlerException::fromException($exception::class);
90
    }
91
92
    /**
93
     * @return callable|class-string
0 ignored issues
show
Documentation Bug introduced by
The doc comment callable|class-string at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in callable|class-string.
Loading history...
94
     */
95 18
    private function findHandler(Exception $exception): string|callable
96
    {
97 18
        return $this->handlers->getAllHandlers()[get_class($exception)]
98 18
            ?? $this->handlers->getAllHandlers()[Exception::class];
99
    }
100
}
101