Passed
Pull Request — master (#831)
by Maxim
15:41
created

ListCommand::relativeClass()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 0
Metric Value
eloc 4
c 0
b 0
f 0
dl 0
loc 9
ccs 4
cts 5
cp 0.8
rs 10
cc 2
nc 2
nop 2
crap 2.032
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 1
                        $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
                'put' => '<fg=yellow>PUT</>',
76
                'delete' => '<fg=red>DELETE</>'
77
            };
78
        }
79
80
        return \implode(', ', $result);
81
    }
82
83 1
    private function getPattern(Route $route): string
84
    {
85 1
        $pattern = $this->getValue($route->getUriHandler(), 'pattern');
86 1
        $prefix = $this->getValue($route->getUriHandler(), 'prefix');
87 1
        $pattern = \str_replace(
88
            '[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}',
89
            'uuid',
90
            $pattern
91
        );
92
93 1
        return \preg_replace_callback(
94
            '/<([^>]*)>/',
95 1
            static fn ($m) => \sprintf('<fg=magenta>%s</>', $m[0]),
96 1
            !empty($prefix) ? $prefix . '/' . \trim($pattern, '/') : $pattern
97
        );
98
    }
99
100
    /**
101
     *
102
     * @throws \ReflectionException
103
     */
104 1
    private function getTarget(Route $route, KernelInterface $kernel): string
105
    {
106 1
        $target = $this->getValue($route, 'target');
107
        switch (true) {
108 1
            case $target instanceof \Closure:
109 1
                $reflection = new \ReflectionFunction($target);
110
111 1
                return \sprintf(
112
                    'Closure(%s:%s)',
113 1
                    \basename($reflection->getFileName()),
114 1
                    $reflection->getStartLine()
115
                );
116
117 1
            case $target instanceof Action:
118 1
                return \sprintf(
119
                    '%s->%s',
120 1
                    $this->relativeClass($this->getValue($target, 'controller'), $kernel),
121 1
                    \implode('|', (array)$this->getValue($target, 'action'))
122
                );
123
124 1
            case $target instanceof Controller:
125 1
                return \sprintf(
126
                    '%s->*',
127 1
                    $this->relativeClass($this->getValue($target, 'controller'), $kernel)
128
                );
129
130
            case $target instanceof Group:
131
                $result = [];
132
                foreach ($this->getValue($target, 'controllers') as $alias => $class) {
133
                    $result[] = \sprintf('%s => %s', $alias, $this->relativeClass($class, $kernel));
134
                }
135
136
                return \implode("\n", $result);
137
138
            case $target instanceof Namespaced:
139
                return \sprintf(
140
                    '%s\*%s->*',
141
                    $this->relativeClass($this->getValue($target, 'namespace'), $kernel),
142
                    $this->getValue($target, 'postfix')
143
                );
144
            default:
145
                return $target::class;
146
        }
147
    }
148
149 1
    private function getValue(object $object, string $property): mixed
150
    {
151
        try {
152 1
            $r = new \ReflectionObject($object);
153 1
            $prop = $r->getProperty($property);
154
        } catch (\Throwable $e) {
155
            return $e->getMessage();
156
        }
157
158 1
        return $prop->getValue($object);
159
    }
160
161 1
    private function relativeClass(string $class, KernelInterface $kernel): string
162
    {
163 1
        $r = new \ReflectionObject($kernel);
164
165 1
        if (\str_starts_with($class, $r->getNamespaceName())) {
166 1
            return \substr($class, \strlen($r->getNamespaceName()) + 1);
167
        }
168
169
        return $class;
170
    }
171
}
172