Passed
Push — main ( dd4eea...91afa7 )
by Dimitri
03:22
created

ControllerMethodReader::hasRemap()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 9
rs 10
1
<?php
2
3
/**
4
 * This file is part of Blitz PHP framework.
5
 *
6
 * (c) 2022 Dimitri Sitchet Tomkeu <[email protected]>
7
 *
8
 * For the full copyright and license information, please view
9
 * the LICENSE file that was distributed with this source code.
10
 */
11
12
namespace BlitzPHP\Cli\Commands\Routes;
13
14
use ReflectionClass;
15
use ReflectionMethod;
16
17
/**
18
 * Lit un contrôleur et renvoie une liste de listes de routes automatiques.
19
 */
20
final class ControllerMethodReader
21
{
22
    /**
23
     * @param string $namespace Namespace par défaut
24
     */
25
    public function __construct(private string $namespace)
26
    {
27
    }
28
29
    /**
30
     * @phpstan-param class-string $class
31
     *
32
     * @return array<int, array{route: string, handler: string}>
33
     * @phpstan-return list<array{route: string, handler: string}>
34
     */
35
    public function read(string $class, string $defaultController = 'Home', string $defaultMethod = 'index'): array
36
    {
37
        $reflection = new ReflectionClass($class);
38
39
        if ($reflection->isAbstract()) {
40
            return [];
41
        }
42
43
        $classname      = $reflection->getName();
44
        $classShortname = $reflection->getShortName();
45
46
        $output     = [];
47
        $uriByClass = $this->getUriByClass($classname);
48
49
        if ($this->hasRemap($reflection)) {
50
            $methodName = '_remap';
51
52
            $routeWithoutController = $this->getRouteWithoutController(
53
                $classShortname,
54
                $defaultController,
55
                $uriByClass,
56
                $classname,
57
                $methodName
58
            );
59
            $output = [...$output, ...$routeWithoutController];
60
61
            $output[] = [
62
                'route'   => $uriByClass . '[/...]',
63
                'handler' => '\\' . $classname . '::' . $methodName,
64
            ];
65
66
            return $output;
67
        }
68
69
        foreach ($reflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
70
            $methodName = $method->getName();
71
72
            $route = $uriByClass . '/' . $methodName;
73
74
            // Exclut BaseController et initialize
75
            if (preg_match('#\AbaseController.*#', $route) === 1) {
76
                continue;
77
            }
78
            if (preg_match('#.*/initialize\z#', $route) === 1) {
79
                continue;
80
            }
81
82
            if ($methodName === $defaultMethod) {
83
                $routeWithoutController = $this->getRouteWithoutController(
84
                    $classShortname,
85
                    $defaultController,
86
                    $uriByClass,
87
                    $classname,
88
                    $methodName
89
                );
90
                $output = [...$output, ...$routeWithoutController];
91
92
                $output[] = [
93
                    'route'   => $uriByClass,
94
                    'handler' => '\\' . $classname . '::' . $methodName,
95
                ];
96
            }
97
98
            $output[] = [
99
                'route'   => $route . '[/...]',
100
                'handler' => '\\' . $classname . '::' . $methodName,
101
            ];
102
        }
103
104
        return $output;
105
    }
106
107
    /**
108
     * Si la classe a une méthode _remap().
109
     */
110
    private function hasRemap(ReflectionClass $class): bool
111
    {
112
        if ($class->hasMethod('_remap')) {
113
            $remap = $class->getMethod('_remap');
114
115
            return $remap->isPublic();
116
        }
117
118
        return false;
119
    }
120
121
    /**
122
     * @phpstan-param class-string $classname
123
     *
124
     * @return string Partie du chemin URI du ou des dossiers et du contrôleur
125
     */
126
    private function getUriByClass(string $classname): string
127
    {
128
        // retire le namespace
129
        $pattern = '/' . preg_quote($this->namespace, '/') . '/';
130
        $class   = ltrim(preg_replace($pattern, '', $classname), '\\');
131
132
        $classParts = explode('\\', $class);
133
        $classPath  = '';
134
135
        foreach ($classParts as $part) {
136
            // mettre la première lettre en minuscule, car le routage automatique 
137
            // met la première lettre du chemin URI en majuscule et recherche le contrôleur
138
            $classPath .= lcfirst($part) . '/';
139
        }
140
141
        return rtrim($classPath, '/');
142
    }
143
144
    /**
145
     * Obtient une route sans contrôleur par défaut.
146
     */
147
    private function getRouteWithoutController(
148
        string $classShortname,
149
        string $defaultController,
150
        string $uriByClass,
151
        string $classname,
152
        string $methodName
153
    ): array {
154
        $output = [];
155
156
        if ($classShortname === $defaultController) {
157
            $pattern                = '#' . preg_quote(lcfirst($defaultController), '#') . '\z#';
158
            $routeWithoutController = rtrim(preg_replace($pattern, '', $uriByClass), '/');
159
            $routeWithoutController = $routeWithoutController ?: '/';
160
161
            $output[] = [
162
                'route'   => $routeWithoutController,
163
                'handler' => '\\' . $classname . '::' . $methodName,
164
            ];
165
        }
166
167
        return $output;
168
    }
169
}
170