Passed
Pull Request — master (#955)
by Maxim
08:40
created

ListCommand::perform()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 3

Importance

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