Passed
Push — main ( c6deb1...79ccdf )
by Dimitri
12:23
created

getRouteForDefaultController()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 32
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 15
nc 3
nop 7
dl 0
loc 32
rs 9.4555
c 0
b 0
f 0
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, private array $httpMethods)
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
        $classInUri = $this->getUriByClass($classname);
48
49
        foreach ($reflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
50
            $methodName = $method->getName();
51
52
			foreach ($this->httpMethods as $httpVerb) {
53
                if (strpos($methodName, $httpVerb) === 0) {
54
                    // Enleve le prefixe des verbes HTTP
55
                    $methodInUri = lcfirst(substr($methodName, strlen($httpVerb)));
56
57
                    // Verifie si c'est la methode par defaut
58
                    if ($methodInUri === $defaultMethod) {
59
                        $routeForDefaultController = $this->getRouteForDefaultController(
60
                            $classShortname,
61
                            $defaultController,
62
                            $classInUri,
63
                            $classname,
64
                            $methodName,
65
                            $httpVerb,
66
                            $method
67
                        );
68
69
                        if ($routeForDefaultController !== []) {
70
                            // Le contrôleur est le contrôleur par défaut. 
71
							// Il n'a qu'un itinéraire pour la méthode par défaut. 
72
							// Les autres méthodes ne seront pas routées même si elles existent. 
73
                            $output = [...$output, ...$routeForDefaultController];
74
75
                            continue;
76
                        }
77
78
                        [$params, $routeParams] = $this->getParameters($method);
79
80
                        // Route pour la methode par defaut
81
                        $output[] = [
82
                            'method'       => $httpVerb,
83
                            'route'        => $classInUri,
84
                            'route_params' => $routeParams,
85
                            'handler'      => '\\' . $classname . '::' . $methodName,
86
                            'params'       => $params,
87
                        ];
88
89
                        continue;
90
                    }
91
92
                    $route = $classInUri . '/' . $methodInUri;
93
94
                    [$params, $routeParams] = $this->getParameters($method);
95
96
                    // S'il s'agit du contrôleur par défaut, la méthode ne sera pas routée.
97
                    if ($classShortname === $defaultController) {
98
                        $route = 'x ' . $route;
99
                    }
100
101
                    $output[] = [
102
                        'method'       => $httpVerb,
103
                        'route'        => $route,
104
                        'route_params' => $routeParams,
105
                        'handler'      => '\\' . $classname . '::' . $methodName,
106
                        'params'       => $params,
107
                    ];
108
                }
109
            }
110
        }
111
112
        return $output;
113
    }
114
115
    private function getParameters(ReflectionMethod $method): array
116
    {
117
        $params      = [];
118
        $routeParams = '';
119
        $refParams   = $method->getParameters();
120
121
        foreach ($refParams as $param) {
122
            $required = true;
123
            if ($param->isOptional()) {
124
                $required = false;
125
126
                $routeParams .= '[/..]';
127
            } else {
128
                $routeParams .= '/..';
129
            }
130
131
            // [variable_name => required?]
132
            $params[$param->getName()] = $required;
133
        }
134
135
        return [$params, $routeParams];
136
    }
137
138
    /**
139
     * @phpstan-param class-string $classname
140
     *
141
     * @return string Partie du chemin URI du ou des dossiers et du contrôleur
142
     */
143
    private function getUriByClass(string $classname): string
144
    {
145
        // retire le namespace
146
        $pattern = '/' . preg_quote($this->namespace, '/') . '/';
147
        $class   = ltrim(preg_replace($pattern, '', $classname), '\\');
148
149
        $classParts = explode('\\', $class);
150
        $classPath  = '';
151
152
        foreach ($classParts as $part) {
153
            // mettre la première lettre en minuscule, car le routage automatique
154
            // met la première lettre du chemin URI en majuscule et recherche le contrôleur
155
            $classPath .= lcfirst($part) . '/';
156
        }
157
158
        return rtrim($classPath, '/');
159
    }
160
161
    /**
162
     * Obtient une route pour le contrôleur par défaut.
163
     *
164
     * @return array[]
165
     */
166
    private function getRouteForDefaultController(
167
        string $classShortname,
168
        string $defaultController,
169
        string $uriByClass,
170
        string $classname,
171
        string $methodName,
172
        string $httpVerb,
173
        ReflectionMethod $method
174
    ): array {
175
        $output = [];
176
177
        if ($classShortname === $defaultController) {
178
            $pattern                = '#' . preg_quote(lcfirst($defaultController), '#') . '\z#';
179
            $routeWithoutController = rtrim(preg_replace($pattern, '', $uriByClass), '/');
180
            $routeWithoutController = $routeWithoutController ?: '/';
181
182
            [$params, $routeParams] = $this->getParameters($method);
183
184
            if ($routeWithoutController === '/' && $routeParams !== '') {
185
                $routeWithoutController = '';
186
            }
187
188
            $output[] = [
189
                'method'       => $httpVerb,
190
                'route'        => $routeWithoutController,
191
                'route_params' => $routeParams,
192
                'handler'      => '\\' . $classname . '::' . $methodName,
193
                'params'       => $params,
194
            ];
195
        }
196
197
        return $output;
198
    }
199
}
200