Passed
Push — master ( e6cdc9...c78d0e )
by butschster
06:30 queued 17s
created

ListCommand   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 159
Duplicated Lines 0 %

Test Coverage

Coverage 70.97%

Importance

Changes 4
Bugs 1 Features 0
Metric Value
wmc 22
eloc 83
c 4
b 1
f 0
dl 0
loc 159
ccs 66
cts 93
cp 0.7097
rs 10

7 Methods

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