Passed
Push — main ( 50aede...a9f24b )
by Chema
01:07 queued 13s
created

Router   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 77
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 7
Bugs 0 Features 0
Metric Value
eloc 33
c 7
b 0
f 0
dl 0
loc 77
ccs 35
cts 35
cp 1
rs 10
wmc 12

6 Methods

Rating   Name   Duplication   Size   Complexity  
A findHandler() 0 4 1
A handleException() 0 16 3
A configure() 0 12 1
A __construct() 0 9 2
A findRoute() 0 9 3
A run() 0 6 2
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\Entities\Route;
11
use Gacela\Router\Exceptions\NonCallableHandlerException;
12
use Gacela\Router\Exceptions\NotFound404Exception;
13
use Gacela\Router\Exceptions\UnsupportedRouterConfigureCallableParamException;
14
use ReflectionFunction;
15
16
use function get_class;
17
use function is_callable;
18
use function is_null;
19
20
final class Router
21
{
22
    private Routes $routes;
23
    private Bindings $bindings;
24
    private Handlers $handlers;
25
26 97
    public function __construct(
27
        Closure $fn = null,
28
    ) {
29 97
        $this->routes = new Routes();
30 97
        $this->bindings = new Bindings();
31 97
        $this->handlers = new Handlers();
32
33 97
        if (!is_null($fn)) {
34 95
            $this->configure($fn);
35
        }
36
    }
37
38 97
    public function configure(Closure $fn): self
39
    {
40 97
        $params = array_map(fn ($param) => match ((string)$param->getType()) {
41 97
            Routes::class => $this->routes,
42 97
            Bindings::class => $this->bindings,
43 97
            Handlers::class => $this->handlers,
44 97
            default => throw UnsupportedRouterConfigureCallableParamException::fromName($param),
45 97
        }, (new ReflectionFunction($fn))->getParameters());
46
47 96
        $fn(...$params);
48
49 95
        return $this;
50
    }
51
52 95
    public function run(): void
53
    {
54
        try {
55 95
            echo $this->findRoute()->run($this->bindings);
56 18
        } catch (Exception $exception) {
57 18
            echo $this->handleException($exception);
58
        }
59
    }
60
61 95
    private function findRoute(): Route
62
    {
63 95
        foreach ($this->routes->getAllRoutes() as $route) {
64 93
            if ($route->requestMatches()) {
65 88
                return $route;
66
            }
67
        }
68
69 7
        throw new NotFound404Exception();
70
    }
71
72 18
    private function handleException(Exception $exception): string
73
    {
74 18
        $handler = $this->findHandler($exception);
75
76 18
        if (is_callable($handler)) {
77 10
            return $handler($exception);
78
        }
79
80
        /** @psalm-suppress MixedAssignment */
81 8
        $instance = Container::create($handler);
82
83 8
        if (is_callable($instance)) {
84 7
            return $instance($exception);
85
        }
86
87 1
        throw NonCallableHandlerException::fromException($exception::class);
88
    }
89
90
    /**
91
     * @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...
92
     */
93 18
    private function findHandler(Exception $exception): string|callable
94
    {
95 18
        return $this->handlers->getAllHandlers()[get_class($exception)]
96 18
            ?? $this->handlers->getAllHandlers()[Exception::class];
97
    }
98
}
99