Test Failed
Pull Request — main (#17)
by Dimitri
16:16 queued 22s
created

RouteBuilder::environment()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 2
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 4
ccs 0
cts 1
cp 0
crap 6
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\Router;
13
14
use BadMethodCallException;
15
use BlitzPHP\Container\Services;
16
use BlitzPHP\Utilities\Iterable\Arr;
17
use BlitzPHP\Utilities\String\Text;
18
use Closure;
19
use InvalidArgumentException;
20
21
/**
22
 * @method void  add(string $from, array|callable|string $to, array $options = [])     Enregistre une seule route à la collection.
23
 * @method $this addPlaceholder($placeholder, ?string $pattern = null)                 Enregistre une nouvelle contrainte auprès du système.
24
 * @method $this addRedirect(string $from, string $to, int $status = 302)              Ajoute une redirection temporaire d'une route à une autre.
25
 * @method $this as(string $name)                                                      Defini un nom de route
26
 * @method void  cli(string $from, array|callable|string $to, array $options = [])     Enregistre une route qui ne sera disponible que pour les requêtes de ligne de commande.
27
 * @method $this controller(string $controller)                                        Defini le contrôleur a utiliser dans le routage
28
 * @method void  delete(string $from, array|callable|string $to, array $options = [])  Enregistre une route qui ne sera disponible que pour les requêtes DELETE.
29
 * @method $this domain(string $domain)                                                Defini une restriction de domaine pour la route
30
 * @method $this fallback($callable = null)                                            Définit la classe/méthode qui doit être appelée si le routage ne trouver pas une correspondance.
31
 * @method void  get(string $from, array|callable|string $to, array $options = [])     Enregistre une route qui ne sera disponible que pour les requêtes GET.
32
 * @method void  head(string $from, array|callable|string $to, array $options = [])    Enregistre une route qui ne sera disponible que pour les requêtes HEAD.
33
 * @method $this hostname(string $hostname)                                            Defini une restriction de non d'hôte pour la route
34
 * @method $this middleware(array|string $middleware)
35
 * @method $this name(string $name)                                                    Defini un nom de route
36
 * @method $this namespace(string $namespace)                                          Defini le namespace a utiliser dans le routage
37
 * @method void  options(string $from, array|callable|string $to, array $options = []) Enregistre une route qui ne sera disponible que pour les requêtes OPTIONS.
38
 * @method void  patch(string $from, array|callable|string $to, array $options = [])   Enregistre une route qui ne sera disponible que pour les requêtes PATCH.
39
 * @method $this permanentRedirect(string $from, string $to)                           Ajoute une redirection permanente d'une route à une autre.
40
 * @method $this placeholder($placeholder, ?string $pattern = null)                    Enregistre une nouvelle contrainte auprès du système.
41
 * @method void  post(string $from, array|callable|string $to, array $options = [])    Enregistre une route qui ne sera disponible que pour les requêtes POST.
42
 * @method $this prefix(string $prefix)
43
 * @method $this priority(int $priority)
44
 * @method void  put(string $from, array|callable|string $to, array $options = [])     Enregistre une route qui ne sera disponible que pour les requêtes PUT.
45
 * @method $this redirect(string $from, string $to, int $status = 302)                 Ajoute une redirection temporaire d'une route à une autre.
46
 * @method $this set404Override($callable = null)                                      Définit la classe/méthode qui doit être appelée si le routage ne trouver pas une correspondance.
47
 * @method $this setAutoRoute(bool $value)                                             Si TRUE, le système tentera de faire correspondre l'URI avec Contrôleurs en faisant correspondre chaque segment avec des dossiers/fichiers dans CONTROLLER_PATH, lorsqu'aucune correspondance n'a été trouvée pour les routes définies.
48
 * @method $this setDefaultConstraint(string $placeholder)                             Définit la contrainte par défaut à utiliser dans le système. Typiquement à utiliser avec la méthode 'ressource'.
49
 * @method $this setDefaultController(string $value)                                   Définit le contrôleur par défaut à utiliser lorsqu'aucun autre contrôleur n'a été spécifié.
50
 * @method $this setDefaultMethod(string $value)                                       Définit la méthode par défaut pour appeler le contrôleur lorsqu'aucun autre méthode a été définie dans la route.
51
 * @method $this setDefaultNamespace(string $value)                                    Définit l'espace de noms par défaut à utiliser pour les contrôleurs lorsqu'aucun autre n'a été spécifié.
52
 * @method $this setPrioritize(bool $enabled = true)                                   Activer ou désactiver le tri des routes par priorité
53
 * @method $this setTranslateURIDashes(bool $value)                                    Indique au système s'il faut convertir les tirets des chaînes URI en traits de soulignement.
54
 * @method $this subdomain(string $subdomain)                                          Defini une restriction de sous domaine pour la route
55
 * @method $this where($placeholder, ?string $pattern = null)                          Enregistre une nouvelle contrainte auprès du système.
56
 */
57
final class RouteBuilder
58
{
59
    /**
60
     * Les attributs à transmettre au routeur.
61
     */
62
    private array $attributes = [];
63
64
    /**
65
     * Les méthodes à transmettre dynamiquement au routeur.
66
     */
67
    private array $passthru = [
68
        'add', 'cli', 'delete', 'get', 'head', 'options', 'post', 'put', 'patch',
69
    ];
70
71
    /**
72
     * Les attributs qui peuvent être définis via cette classe.
73
     */
74
    private array $allowedAttributes = [
75
        'as', 'controller', 'domain', 'hostname', 'middlewares', 'middleware',
76
        'name', 'namespace', 'where', 'prefix', 'priority', 'subdomain',
77
    ];
78
79
    /**
80
     * Les attributs qui sont des alias.
81
     */
82
    private array $aliases = [
83
        'name'        => 'as',
84
        'middlewares' => 'middleware',
85
    ];
86
87
    private array $allowedMethods = [
88
        'addPlaceholder', 'placeholder',
89
        'addRedirect', 'redirect', 'permanentRedirect',
90
        'set404Override', 'setAutoRoute', 'fallback',
91
        'setDefaultConstraint', 'setDefaultController', 'setDefaultMethod', 'setDefaultNamespace',
92
        'setTranslateURIDashes', 'setPrioritize',
93
    ];
94
95
    /**
96
     * Constructeur
97
     */
98
    public function __construct(private RouteCollection $collection)
99
    {
100
    }
101
102
    /**
103
     * Gérez dynamiquement les appels dans le registraire de routage.
104
     *
105
     * @return self
106
     *
107
     * @throws BadMethodCallException
108
     */
109
    public function __call(string $method, array $parameters = [])
110
    {
111
        if (in_array($method, $this->passthru, true)) {
112
            return $this->registerRoute($method, ...$parameters);
113
        }
114
115
        if (in_array($method, $this->allowedAttributes, true)) {
116
            if (in_array($method, ['middleware', 'middlewares'], true)) {
117
                $parameters = is_array($parameters[0]) ? $parameters[0] : $parameters;
118
119
                return $this->attribute($method, array_merge($this->attributes[$method] ?? [], $parameters));
120
            }
121
122
            return $this->attribute($method, $parameters[0]);
123
        }
124
125
        if (in_array($method, $this->allowedMethods, true)) {
126
            $collection = $this->collection->{$method}(...$parameters);
127
128
            if ($collection instanceof RouteCollection) {
129
                Services::set(RouteCollection::class, $collection);
130
                $this->collection = $collection;
131
            }
132
133
            return $this;
134
        }
135
136
        throw new BadMethodCallException(sprintf('La méthode %s::%s n\'existe pas.', self::class, $method));
137
    }
138
139
    /**
140
     * Limite les routes à un ENVIRONNEMENT spécifié ou ils ne fonctionneront pas.
141
     */
142
    public function environment(string $env, Closure $callback): void
143
    {
144
        if ($env === config('app.environment')) {
0 ignored issues
show
introduced by
The condition $env === config('app.environment') is always false.
Loading history...
145
            $callback($this);
146
        }
147
    }
148
149
    public function form(string $from, $to, array $options = []): void
150
    {
151
        $options += $this->attributes;
152
        $this->attributes = [];
153
154
        if (isset($options['unique'])) {
155
            $this->collection->form($from, $to, $options);
156
157
            return;
158
        }
159
160
        $toGet = $toPost = $to;
161
162
        if (is_string($to)) {
163
            $parts = explode('::', $to);
164
        } elseif (is_array($to)) {
165
            $parts = $to;
166
        } else {
167
            $parts = [];
168
        }
169
170
        if (count($parts) === 2) { // Si on a defini le controleur et la methode
171
            $controller = $parts[0];
172
            $method     = $parts[1];
173
        } elseif (count($parts) === 1) {
174
            // Si on est ici, ca veut dire 2 choses.
175
            // - Soit c'est la methode qui est definie (utilisation d'un string)
176
            // - Soit c'est le controleur qui est defini (utilisation d'un array)
177
178
            if (is_array($to)) {
179
                $controller = $parts[0];
180
                $method     = $this->collection->getDefaultMethod();
181
            } else {
182
                $controller = '';
183
                $method     = $parts[0];
184
            }
185
        }
186
187
        if (isset($controller, $method)) {
188
            $toGet  = implode('::', array_filter([$controller, Text::camel('form_' . $method)]));
189
            $toPost = implode('::', array_filter([$controller, Text::camel('process_' . $method)]));
190
        }
191
192
        $this->collection->get($from, $toGet, $options);
193
        $this->collection->post($from, $toPost, $options);
194
    }
195
196
    /**
197
     * Create a route group with shared attributes.
198
     */
199
    public function group(callable $callback): void
200
    {
201
        $prefix = $this->attributes['prefix'] ?? '';
202
        unset($this->attributes['prefix']);
203
204
        $this->collection->group($prefix, $this->attributes, fn () => $callback($this));
205
    }
206
207
    /**
208
     * Ajoute une seule route à faire correspondre pour plusieurs verbes HTTP.
209
     *
210
     * Exemple:
211
     *  $route->match( ['get', 'post'], 'users/(:num)', 'users/$1);
212
     *
213
     * @param array|Closure|string $to
214
     */
215
    public function match(array $verbs = [], string $from = '', $to = '', array $options = []): void
216
    {
217
        $this->collection->match($verbs, $from, $to, $this->attributes + $options);
218
    }
219
220
    /**
221
     * Crée une collection de routes basées sur les verbes HTTP pour un contrôleur de présentation.
222
     *
223
     * Options possibles :
224
     * 'controller' - Personnalisez le nom du contrôleur utilisé dans la route 'to'
225
     * 'placeholder' - L'expression régulière utilisée par le routeur. La valeur par défaut est '(:any)'
226
     *
227
     * Example:
228
     *
229
     *      $route->presenter('photos');
230
     *
231
     *      // Génère les routes suivantes
232
     *      HTTP Verb | Path        | Action        | Used for...
233
     *      ----------+-------------+---------------+-----------------
234
     *      GET         /photos             index           affiche le tableau des tous les objets photo
235
     *      GET         /photos/show/{id}   show            affiche un objet photo spécifique, toutes les propriétés
236
     *      GET         /photos/new         new             affiche un formulaire pour un objet photo vide, avec les propriétés par défaut
237
     *      POST        /photos/create      create          traitement du formulaire pour une nouvelle photo
238
     *      GET         /photos/edit/{id}   edit            affiche un formulaire d'édition pour un objet photo spécifique, propriétés modifiables
239
     *      POST        /photos/update/{id} update          traitement des données du formulaire d'édition
240
     *      GET         /photos/remove/{id} remove          affiche un formulaire pour confirmer la suppression d'un objet photo spécifique
241
     *      POST        /photos/delete/{id} delete          suppression de l'objet photo spécifié
242
     *
243
     * @param string $name    Le nom du contrôleur vers lequel router.
244
     * @param array  $options Une liste des façons possibles de personnaliser le routage.
245
     */
246
    public function presenter(string $name, array $options = []): void
247
    {
248
        $this->collection->presenter($name, $this->attributes + $options);
249
250
        $this->attributes = [];
251
    }
252
253
    /**
254
     * Crée une collection de routes basés sur HTTP-verb pour un contrôleur.
255
     *
256
     * Options possibles :
257
     * 'controller' - Personnalisez le nom du contrôleur utilisé dans la route 'to'
258
     * 'placeholder' - L'expression régulière utilisée par le routeur. La valeur par défaut est '(:any)'
259
     * 'websafe' - - '1' si seuls les verbes HTTP GET et POST sont pris en charge
260
     *
261
     * Exemple:
262
     *
263
     *      $route->resource('photos');
264
     *
265
     *      // Genère les routes suivantes:
266
     *      HTTP Verb | Path        | Action        | Used for...
267
     *      ----------+-------------+---------------+-----------------
268
     *      GET         /photos             index           un tableau d'objets photo
269
     *      GET         /photos/new         new             un objet photo vide, avec des propriétés par défaut
270
     *      GET         /photos/{id}/edit   edit            un objet photo spécifique, propriétés modifiables
271
     *      GET         /photos/{id}        show            un objet photo spécifique, toutes les propriétés
272
     *      POST        /photos             create          un nouvel objet photo, à ajouter à la ressource
273
     *      DELETE      /photos/{id}        delete          supprime l'objet photo spécifié
274
     *      PUT/PATCH   /photos/{id}        update          propriétés de remplacement pour la photo existante
275
     *
276
     *  Si l'option 'websafe' est présente, les chemins suivants sont également disponibles :
277
     *
278
     *      POST		/photos/{id}/delete delete
279
     *      POST        /photos/{id}        update
280
     *
281
     * @param string $name    Le nom de la ressource/du contrôleur vers lequel router.
282
     * @param array  $options Une liste des façons possibles de personnaliser le routage.
283
     */
284
    public function resource(string $name, array $options = []): void
285
    {
286
        $this->collection->resource($name, $this->attributes + $options);
287
288
        $this->attributes = [];
289
    }
290
291
    /**
292
     * Une méthode de raccourci pour ajouter un certain nombre de routes en une seule fois.
293
     * Il ne permet pas de définir des options sur la route, ou de définir la méthode utilisée.
294
     */
295
    public function map(array $routes = [], array $options = []): void
296
    {
297
        $this->collection->map($routes, $this->attributes + $options);
298
    }
299
300
    /**
301
     * Spécifie une route qui n'affichera qu'une vue.
302
     * Ne fonctionne que pour les requêtes GET.
303
     */
304
    public function view(string $from, string $view, array $options = []): void
305
    {
306
        $this->collection->view($from, $view, $this->attributes + $options);
307
    }
308
309
    /**
310
     * Defini une valeur pour l'attribut donné
311
     *
312
     * @param mixed $value
313
     *
314
     * @throws InvalidArgumentException
315
     */
316
    private function attribute(string $key, $value): self
317
    {
318
        if (! in_array($key, $this->allowedAttributes, true)) {
319
            throw new InvalidArgumentException("L'attribute [{$key}] n'existe pas.");
320
        }
321
322
        $this->attributes[Arr::get($this->aliases, $key, $key)] = $value;
323
324
        return $this;
325
    }
326
327
    /**
328
     * Enregistre une nouvelle route avec le routeur.
329
     *
330
     * @param mixed $to
331
     */
332
    private function registerRoute(string $method, string $from, $to, array $options = []): self
333
    {
334
        $this->collection->{$method}($from, $to, $this->attributes + $options);
335
336
        $this->attributes = [];
337
338
        return $this;
339
    }
340
}
341