Passed
Pull Request — master (#839)
by Alexander
16:59
created

ListCommand::getVerbs()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 6.7968

Importance

Changes 2
Bugs 1 Features 0
Metric Value
eloc 11
c 2
b 1
f 0
dl 0
loc 18
ccs 3
cts 12
cp 0.25
rs 9.9
cc 3
nc 3
nop 1
crap 6.7968
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Spiral\Command\Router;
6
7
use Spiral\Boot\KernelInterface;
8
use Spiral\Console\Command;
9
use Spiral\Core\Container\SingletonInterface;
10
use Spiral\Router\GroupRegistry;
11
use Spiral\Router\Route;
12
use Spiral\Router\RouterInterface;
13
use Spiral\Router\Target\Action;
14
use Spiral\Router\Target\Controller;
15
use Spiral\Router\Target\Group;
16
use Spiral\Router\Target\Namespaced;
17
18
final class ListCommand extends Command implements SingletonInterface
19
{
20
    protected const NAME = 'route:list';
21
    protected const DESCRIPTION = 'List application routes';
22
23
    /**
24
     * @throws \ReflectionException
25
     */
26 1
    public function perform(RouterInterface $router, GroupRegistry $registry, KernelInterface $kernel): int
27
    {
28 1
        $grid = $this->table(['Name:', 'Verbs:', 'Pattern:', 'Target:', 'Group:']);
29
30 1
        foreach ($router->getRoutes() as $name => $route) {
31 1
            if ($route instanceof Route) {
32 1
                $grid->addRow(
33
                    [
34
                        $name,
35 1
                        $this->getVerbs($route),
36 1
                        $this->getPattern($route),
37 1
                        $this->getTarget($route, $kernel),
38 1
                        \implode(', ', $this->getRouteGroups($registry, $name)),
39
                    ]
40
                );
41
            }
42
        }
43
44 1
        $grid->render();
45
46 1
        return self::SUCCESS;
47
    }
48
49
    /**
50
     * @return string[]
51
     */
52 1
    private function getRouteGroups(GroupRegistry $registry, string $routeName): array
53
    {
54 1
        $groups = [];
55 1
        foreach ($registry as $groupName => $group) {
56 1
            if ($group->hasRoute($routeName)) {
57 1
                $groups[] = $groupName;
58
            }
59
        }
60
61 1
        return $groups;
62
    }
63
64 1
    private function getVerbs(Route $route): string
65
    {
66 1
        if ($route->getVerbs() === Route::VERBS) {
67 1
            return '*';
68
        }
69
70
        $result = [];
71
        foreach ($route->getVerbs() as $verb) {
72
            $result[] = match (\strtolower($verb)) {
73
                'get' => '<fg=green>GET</>',
74
                'post' => '<fg=blue>POST</>',
75
                'patch' => '<fg=cyan>PATCH</>',
76
                'put' => '<fg=yellow>PUT</>',
77
                'delete' => '<fg=red>DELETE</>'
78
            };
79
        }
80
81
        return \implode(', ', $result);
82
    }
83
84 1
    private function getPattern(Route $route): string
85
    {
86 1
        $pattern = $this->getValue($route->getUriHandler(), 'pattern');
87 1
        $prefix = $this->getValue($route->getUriHandler(), 'prefix');
88 1
        $pattern = \str_replace(
89
            '[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}',
90
            'uuid',
91
            $pattern
92
        );
93
94 1
        return \preg_replace_callback(
95
            '/<([^>]*)>/',
96 1
            static fn ($m) => \sprintf('<fg=magenta>%s</>', $m[0]),
97 1
            !empty($prefix) ? $prefix . '/' . \trim($pattern, '/') : $pattern
98
        );
99
    }
100
101
    /**
102
     *
103
     * @throws \ReflectionException
104
     */
105 1
    private function getTarget(Route $route, KernelInterface $kernel): string
106
    {
107 1
        $target = $this->getValue($route, 'target');
108
        switch (true) {
109 1
            case $target instanceof \Closure:
110 1
                $reflection = new \ReflectionFunction($target);
111
112 1
                return \sprintf(
113
                    'Closure(%s:%s)',
114 1
                    \basename($reflection->getFileName()),
115 1
                    $reflection->getStartLine()
116
                );
117
118 1
            case $target instanceof Action:
119 1
                return \sprintf(
120
                    '%s->%s',
121 1
                    $this->relativeClass($this->getValue($target, 'controller'), $kernel),
122 1
                    \implode('|', (array)$this->getValue($target, 'action'))
123
                );
124
125 1
            case $target instanceof Controller:
126 1
                return \sprintf(
127
                    '%s->*',
128 1
                    $this->relativeClass($this->getValue($target, 'controller'), $kernel)
129
                );
130
131
            case $target instanceof Group:
132
                $result = [];
133
                foreach ($this->getValue($target, 'controllers') as $alias => $class) {
134
                    $result[] = \sprintf('%s => %s', $alias, $this->relativeClass($class, $kernel));
135
                }
136
137
                return \implode("\n", $result);
138
139
            case $target instanceof Namespaced:
140
                return \sprintf(
141
                    '%s\*%s->*',
142
                    $this->relativeClass($this->getValue($target, 'namespace'), $kernel),
143
                    $this->getValue($target, 'postfix')
144
                );
145
            default:
146
                return $target::class;
147
        }
148
    }
149
150 1
    private function getValue(object $object, string $property): mixed
151
    {
152
        try {
153 1
            $r = new \ReflectionObject($object);
154 1
            $prop = $r->getProperty($property);
155
        } catch (\Throwable $e) {
156
            return $e->getMessage();
157
        }
158
159 1
        return $prop->getValue($object);
160
    }
161
162 1
    private function relativeClass(string $class, KernelInterface $kernel): string
163
    {
164 1
        $r = new \ReflectionObject($kernel);
165
166 1
        if (\str_starts_with($class, $r->getNamespaceName())) {
167 1
            return \substr($class, \strlen($r->getNamespaceName()) + 1);
168
        }
169
170
        return $class;
171
    }
172
}
173