Passed
Push — master ( 1d5b12...ea0462 )
by Anton
02:31
created

ListCommand::getValue()   A

Complexity

Conditions 2
Paths 4

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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