Routage
Aperçu
Le routage dans Flight PHP mappe les motifs d'URL sur des fonctions de rappel ou des méthodes de classe, permettant une gestion rapide et simple des requêtes. Il est conçu pour un overhead minimal, une utilisation conviviale pour les débutants, et une extensibilité sans dépendances externes.
Comprendre
Le routage est le mécanisme central qui connecte les requêtes HTTP à la logique de votre application dans Flight. En définissant des routes, vous spécifiez comment différentes URL déclenchent du code spécifique, que ce soit par des fonctions, des méthodes de classe ou des actions de contrôleur. Le système de routage de Flight est flexible, supportant des motifs basiques, des paramètres nommés, des expressions régulières, et des fonctionnalités avancées comme l'injection de dépendances et le routage de ressources. Cette approche garde votre code organisé et facile à maintenir, tout en restant rapide et simple pour les débutants et extensible pour les utilisateurs avancés.
Note : Vous voulez en savoir plus sur le routage ? Consultez la page "pourquoi un framework ?" pour une explication plus approfondie.
Utilisation de base
Définir une route simple
Le routage de base dans Flight se fait en associant un motif d'URL à une fonction de rappel ou à un tableau d'une classe et d'une méthode.
Flight::route('/', function(){
echo 'hello world!';
});Les routes sont associées dans l'ordre où elles sont définies. La première route qui correspond à une requête sera invoquée.
Utiliser des fonctions comme rappels
Le rappel peut être n'importe quel objet qui est invocable. Vous pouvez donc utiliser une fonction régulière :
function hello() {
echo 'hello world!';
}
Flight::route('/', 'hello');Utiliser des classes et des méthodes comme contrôleur
Vous pouvez également utiliser une méthode (statique ou non) d'une classe :
class GreetingController {
public function hello() {
echo 'hello world!';
}
}
Flight::route('/', [ 'GreetingController','hello' ]);
// ou
Flight::route('/', [ GreetingController::class, 'hello' ]); // méthode préférée
// ou
Flight::route('/', [ 'GreetingController::hello' ]);
// ou
Flight::route('/', [ 'GreetingController->hello' ]);Ou en créant d'abord un objet puis en appelant la méthode :
use flight\Engine;
// GreetingController.php
class GreetingController
{
protected Engine $app
public function __construct(Engine $app) {
$this->app = $app;
$this->name = 'John Doe';
}
public function hello() {
echo "Hello, {$this->name}!";
}
}
// index.php
$app = Flight::app();
$greeting = new GreetingController($app);
Flight::route('/', [ $greeting, 'hello' ]);Note : Par défaut, lorsqu'un contrôleur est appelé dans le framework, la classe
flight\Engineest toujours injectée sauf si vous spécifiez via un conteneur d'injection de dépendances
Routage spécifique à la méthode
Par défaut, les motifs de route sont associés à toutes les méthodes de requête. Vous pouvez répondre à des méthodes spécifiques en plaçant un identifiant avant l'URL.
Flight::route('GET /', function () {
echo 'I received a GET request.';
});
Flight::route('POST /', function () {
echo 'I received a POST request.';
});
// Vous ne pouvez pas utiliser Flight::get() pour les routes car c'est une méthode
// pour obtenir des variables, pas pour créer une route.
Flight::post('/', function() { /* code */ });
Flight::patch('/', function() { /* code */ });
Flight::put('/', function() { /* code */ });
Flight::delete('/', function() { /* code */ });Vous pouvez également mapper plusieurs méthodes à un seul rappel en utilisant un délimiteur | :
Flight::route('GET|POST /', function () {
echo 'I received either a GET or a POST request.';
});Gestion spéciale pour les requêtes HEAD et OPTIONS
Flight fournit une gestion intégrée pour les requêtes HTTP HEAD et OPTIONS :
Requêtes HEAD
- Les requêtes HEAD sont traitées comme des requêtes
GET, mais Flight supprime automatiquement le corps de la réponse avant de l'envoyer au client. - Cela signifie que vous pouvez définir une route pour
GET, et les requêtes HEAD vers la même URL ne renverront que les en-têtes (pas de contenu), comme attendu par les standards HTTP.
Flight::route('GET /info', function() {
echo 'This is some info!';
});
// Une requête HEAD vers /info renverra les mêmes en-têtes, mais pas de corps.Requêtes OPTIONS
Les requêtes OPTIONS sont automatiquement gérées par Flight pour toute route définie.
- Lorsqu'une requête OPTIONS est reçue, Flight répond avec un statut
204 No Contentet un en-têteAllowlistant toutes les méthodes HTTP supportées pour cette route. - Vous n'avez pas besoin de définir une route séparée pour OPTIONS.
// Pour une route définie comme :
Flight::route('GET|POST /users', function() { /* ... */ });
// Une requête OPTIONS vers /users répondra avec :
//
// Status: 204 No Content
// Allow: GET, POST, HEAD, OPTIONSUtiliser l'objet Router
De plus, vous pouvez obtenir l'objet Router qui dispose de méthodes d'aide pour votre utilisation :
$router = Flight::router();
// mappe toutes les méthodes comme Flight::route()
$router->map('/', function() {
echo 'hello world!';
});
// requête GET
$router->get('/users', function() {
echo 'users';
});
$router->post('/users', function() { /* code */});
$router->put('/users/update/@id', function() { /* code */});
$router->delete('/users/@id', function() { /* code */});
$router->patch('/users/@id', function() { /* code */});Expressions régulières (Regex)
Vous pouvez utiliser des expressions régulières dans vos routes :
Flight::route('/user/[0-9]+', function () {
// Cela correspondra à /user/1234
});Bien que cette méthode soit disponible, il est recommandé d'utiliser des paramètres nommés, ou des paramètres nommés avec des expressions régulières, car ils sont plus lisibles et plus faciles à maintenir.
Paramètres nommés
Vous pouvez spécifier des paramètres nommés dans vos routes qui seront passés à votre fonction de rappel. Ceci est plus pour la lisibilité de la route que pour toute autre chose. Veuillez voir la section ci-dessous sur l'avertissement important.
Flight::route('/@name/@id', function (string $name, string $id) {
echo "hello, $name ($id)!";
});Vous pouvez également inclure des expressions régulières avec vos paramètres nommés en utilisant le délimiteur : :
Flight::route('/@name/@id:[0-9]{3}', function (string $name, string $id) {
// Cela correspondra à /bob/123
// Mais ne correspondra pas à /bob/12345
});Note : La correspondance de groupes regex
()avec des paramètres positionnels n'est pas supportée. Ex ::'\(
Avertissement important
Bien que dans l'exemple ci-dessus, il semble que @name soit directement lié à la variable $name, ce n'est pas le cas. L'ordre des paramètres dans la fonction de rappel détermine ce qui lui est passé. Si vous inversiez l'ordre des paramètres dans la fonction de rappel, les variables seraient également inversées. Voici un exemple :
Flight::route('/@name/@id', function (string $id, string $name) {
echo "hello, $name ($id)!";
});Et si vous alliez à l'URL suivante : /bob/123, la sortie serait hello, 123 (bob)!.
Soyez prudent lorsque vous configurez vos routes et vos fonctions de rappel !
Paramètres optionnels
Vous pouvez spécifier des paramètres nommés qui sont optionnels pour la correspondance en enveloppant les segments entre parenthèses.
Flight::route(
'/blog(/@year(/@month(/@day)))',
function(?string $year, ?string $month, ?string $day) {
// Cela correspondra aux URL suivantes :
// /blog/2012/12/10
// /blog/2012/12
// /blog/2012
// /blog
}
);Tout paramètre optionnel qui n'est pas associé sera passé en tant que NULL.
Routage avec joker
La correspondance n'est faite que sur des segments d'URL individuels. Si vous voulez associer plusieurs segments, vous pouvez utiliser le joker *.
Flight::route('/blog/*', function () {
// Cela correspondra à /blog/2000/02/01
});Pour router toutes les requêtes vers un seul rappel, vous pouvez faire :
Flight::route('*', function () {
// Faites quelque chose
});Gestionnaire 404 Non trouvé
Par défaut, si une URL ne peut pas être trouvée, Flight enverra une réponse HTTP 404 Not Found qui est très simple et basique.
Si vous voulez avoir une réponse 404 plus personnalisée, vous pouvez mapper votre propre méthode notFound :
Flight::map('notFound', function() {
$url = Flight::request()->url;
// Vous pourriez aussi utiliser Flight::render() avec un modèle personnalisé.
$output = <<<HTML
<h1>My Custom 404 Not Found</h1>
<h3>The page you have requested {$url} could not be found.</h3>
HTML;
$this->response()
->clearBody()
->status(404)
->write($output)
->send();
});Gestionnaire Méthode non trouvée
Par défaut, si une URL est trouvée mais que la méthode n'est pas autorisée, Flight enverra une réponse HTTP 405 Method Not Allowed qui est très simple et basique (Ex : Method Not Allowed. Allowed Methods are: GET, POST). Elle inclura également un en-tête Allow avec les méthodes autorisées pour cette URL.
Si vous voulez avoir une réponse 405 plus personnalisée, vous pouvez mapper votre propre méthode methodNotFound :
use flight\net\Route;
Flight::map('methodNotFound', function(Route $route) {
$url = Flight::request()->url;
$methods = implode(', ', $route->methods);
// Vous pourriez aussi utiliser Flight::render() avec un modèle personnalisé.
$output = <<<HTML
<h1>My Custom 405 Method Not Allowed</h1>
<h3>The method you have requested for {$url} is not allowed.</h3>
<p>Allowed Methods are: {$methods}</p>
HTML;
$this->response()
->clearBody()
->status(405)
->setHeader('Allow', $methods)
->write($output)
->send();
});Utilisation avancée
Injection de dépendances dans les routes
Si vous voulez utiliser l'injection de dépendances via un conteneur (PSR-11, PHP-DI, Dice, etc.), le seul type de routes où cela est disponible est soit en créant directement l'objet vous-même et en utilisant le conteneur pour créer votre objet, soit en utilisant des chaînes pour définir la classe et la méthode à appeler. Vous pouvez consulter la page Injection de dépendances pour plus d'informations.
Voici un exemple rapide :
use flight\database\PdoWrapper;
// Greeting.php
class Greeting
{
protected PdoWrapper $pdoWrapper;
public function __construct(PdoWrapper $pdoWrapper) {
$this->pdoWrapper = $pdoWrapper;
}
public function hello(int $id) {
// do something with $this->pdoWrapper
$name = $this->pdoWrapper->fetchField("SELECT name FROM users WHERE id = ?", [ $id ]);
echo "Hello, world! My name is {$name}!";
}
}
// index.php
// Configurez le conteneur avec les paramètres dont vous avez besoin
// Voir la page Injection de dépendances pour plus d'informations sur PSR-11
$dice = new \Dice\Dice();
// N'oubliez pas de réassigner la variable avec '$dice = '!!!!!
$dice = $dice->addRule('flight\database\PdoWrapper', [
'shared' => true,
'constructParams' => [
'mysql:host=localhost;dbname=test',
'root',
'password'
]
]);
// Enregistrez le gestionnaire de conteneur
Flight::registerContainerHandler(function($class, $params) use ($dice) {
return $dice->create($class, $params);
});
// Routes comme d'habitude
Flight::route('/hello/@id', [ 'Greeting', 'hello' ]);
// ou
Flight::route('/hello/@id', 'Greeting->hello');
// ou
Flight::route('/hello/@id', 'Greeting::hello');
Flight::start();Passer l'exécution à la route suivante
Déprécié
Vous pouvez passer l'exécution à la route correspondante suivante en retournant true depuis votre fonction de rappel.
Flight::route('/user/@name', function (string $name) {
// Vérifiez une condition
if ($name !== "Bob") {
// Continuez vers la route suivante
return true;
}
});
Flight::route('/user/*', function () {
// Cela sera appelé
});Il est maintenant recommandé d'utiliser middleware pour gérer des cas d'utilisation complexes comme celui-ci.
Aliasing de route
En assignant un alias à une route, vous pouvez plus tard appeler cet alias dans votre application de manière dynamique pour qu'il soit généré plus tard dans votre code (ex : un lien dans un modèle HTML, ou générer une URL de redirection).
Flight::route('/users/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
// ou
Flight::route('/users/@id', function($id) { echo 'user:'.$id; })->setAlias('user_view');
// plus tard dans le code quelque part
class UserController {
public function update() {
// code pour sauvegarder l'utilisateur...
$id = $user['id']; // 5 par exemple
$redirectUrl = Flight::getUrl('user_view', [ 'id' => $id ]); // renverra '/users/5'
Flight::redirect($redirectUrl);
}
}
Ceci est particulièrement utile si votre URL change. Dans l'exemple ci-dessus, supposons que users ait été déplacé vers /admin/users/@id à la place.
Avec l'aliasing en place pour la route, vous n'avez plus besoin de trouver toutes les anciennes URL dans votre code et de les changer car l'alias renverra maintenant /admin/users/5 comme dans l'exemple ci-dessus.
L'aliasing de route fonctionne encore dans les groupes :
Flight::group('/users', function() {
Flight::route('/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
// ou
Flight::route('/@id', function($id) { echo 'user:'.$id; })->setAlias('user_view');
});Inspection des informations de route
Si vous voulez inspecter les informations de la route correspondante, il y a 2 façons de faire ceci :
- Vous pouvez utiliser une propriété
executedRoutesur l'objetFlight::router(). - Vous pouvez demander que l'objet route soit passé à votre rappel en passant
truecomme troisième paramètre dans la méthode route. L'objet route sera toujours le dernier paramètre passé à votre fonction de rappel.
executedRoute
Flight::route('/', function() {
$route = Flight::router()->executedRoute;
// Faites quelque chose avec $route
// Tableau des méthodes HTTP associées
$route->methods;
// Tableau des paramètres nommés
$route->params;
// Expression régulière correspondante
$route->regex;
// Contient le contenu de tout '*' utilisé dans le motif d'URL
$route->splat;
// Montre le chemin d'URL....si vous en avez vraiment besoin
$route->pattern;
// Montre le middleware assigné à ceci
$route->middleware;
// Montre l'alias assigné à cette route
$route->alias;
});Note : La propriété
executedRoutene sera définie qu'après qu'une route ait été exécutée. Si vous essayez d'y accéder avant qu'une route ait été exécutée, elle seraNULL. Vous pouvez aussi utiliser executedRoute dans middleware !
Passer true à la définition de route
Flight::route('/', function(\flight\net\Route $route) {
// Tableau des méthodes HTTP associées
$route->methods;
// Tableau des paramètres nommés
$route->params;
// Expression régulière correspondante
$route->regex;
// Contient le contenu de tout '*' utilisé dans le motif d'URL
$route->splat;
// Montre le chemin d'URL....si vous en avez vraiment besoin
$route->pattern;
// Montre le middleware assigné à ceci
$route->middleware;
// Montre l'alias assigné à cette route
$route->alias;
}, true);// <-- Ce paramètre true est ce qui rend cela possibleGroupement de routes et Middleware
Il peut y avoir des moments où vous voulez grouper des routes liées ensemble (comme /api/v1).
Vous pouvez le faire en utilisant la méthode group :
Flight::group('/api/v1', function () {
Flight::route('/users', function () {
// Correspond à /api/v1/users
});
Flight::route('/posts', function () {
// Correspond à /api/v1/posts
});
});Vous pouvez même imbriquer des groupes de groupes :
Flight::group('/api', function () {
Flight::group('/v1', function () {
// Flight::get() obtient des variables, cela ne définit pas une route ! Voir le contexte objet ci-dessous
Flight::route('GET /users', function () {
// Correspond à GET /api/v1/users
});
Flight::post('/posts', function () {
// Correspond à POST /api/v1/posts
});
Flight::put('/posts/1', function () {
// Correspond à PUT /api/v1/posts
});
});
Flight::group('/v2', function () {
// Flight::get() obtient des variables, cela ne définit pas une route ! Voir le contexte objet ci-dessous
Flight::route('GET /users', function () {
// Correspond à GET /api/v2/users
});
});
});Groupement avec contexte objet
Vous pouvez toujours utiliser le groupement de routes avec l'objet Engine de la manière suivante :
$app = Flight::app();
$app->group('/api/v1', function (Router $router) {
// utilisez la variable $router
$router->get('/users', function () {
// Correspond à GET /api/v1/users
});
$router->post('/posts', function () {
// Correspond à POST /api/v1/posts
});
});Note : C'est la méthode préférée pour définir des routes et des groupes avec l'objet
$router.
Groupement avec Middleware
Vous pouvez également assigner un middleware à un groupe de routes :
Flight::group('/api/v1', function () {
Flight::route('/users', function () {
// Correspond à /api/v1/users
});
}, [ MyAuthMiddleware::class ]); // ou [ new MyAuthMiddleware() ] si vous voulez utiliser une instanceVoir plus de détails sur la page group middleware.
Routage de ressources
Vous pouvez créer un ensemble de routes pour une ressource en utilisant la méthode resource. Cela créera un ensemble de routes pour une ressource qui suit les conventions RESTful.
Pour créer une ressource, faites ceci :
Flight::resource('/users', UsersController::class);Et ce qui se passera en arrière-plan est qu'il créera les routes suivantes :
[
'index' => 'GET /users',
'create' => 'GET /users/create',
'store' => 'POST /users',
'show' => 'GET /users/@id',
'edit' => 'GET /users/@id/edit',
'update' => 'PUT /users/@id',
'destroy' => 'DELETE /users/@id'
]Et votre contrôleur utilisera les méthodes suivantes :
class UsersController
{
public function index(): void
{
}
public function show(string $id): void
{
}
public function create(): void
{
}
public function store(): void
{
}
public function edit(string $id): void
{
}
public function update(string $id): void
{
}
public function destroy(string $id): void
{
}
}Note : Vous pouvez visualiser les routes nouvellement ajoutées avec
runwayen exécutantphp runway routes.
Personnalisation des routes de ressources
Il y a quelques options pour configurer les routes de ressources.
Alias de base
Vous pouvez configurer l'aliasBase. Par défaut, l'alias est la dernière partie de l'URL spécifiée.
Par exemple, /users/ résulterait en un aliasBase de users. Lorsque ces routes sont créées, les alias sont users.index, users.create, etc. Si vous voulez changer l'alias, définissez l'aliasBase à la valeur que vous voulez.
Flight::resource('/users', UsersController::class, [ 'aliasBase' => 'user' ]);Only et Except
Vous pouvez également spécifier quelles routes vous voulez créer en utilisant les options only et except.
// Liste blanche seulement ces méthodes et liste noire le reste
Flight::resource('/users', UsersController::class, [ 'only' => [ 'index', 'show' ] ]);// Liste noire seulement ces méthodes et liste blanche le reste
Flight::resource('/users', UsersController::class, [ 'except' => [ 'create', 'store', 'edit', 'update', 'destroy' ] ]);Ce sont essentiellement des options de liste blanche et liste noire pour que vous puissiez spécifier quelles routes vous voulez créer.
Middleware
Vous pouvez également spécifier un middleware à exécuter sur chacune des routes créées par la méthode resource.
Flight::resource('/users', UsersController::class, [ 'middleware' => [ MyAuthMiddleware::class ] ]);Réponses en streaming
Vous pouvez maintenant diffuser des réponses au client en utilisant stream() ou streamWithHeaders().
Ceci est utile pour envoyer de grands fichiers, des processus à longue durée, ou générer de grandes réponses.
Le streaming d'une route est géré un peu différemment qu'une route régulière.
Note : Les réponses en streaming ne sont disponibles que si vous avez
flight.v2.output_bufferingdéfini àfalse.
Stream avec en-têtes manuels
Vous pouvez diffuser une réponse au client en utilisant la méthode stream() sur une route. Si vous
faites cela, vous devez définir tous les en-têtes manuellement avant de sortir quoi que ce soit vers le client.
Ceci se fait avec la fonction php header() ou la méthode Flight::response()->setRealHeader().
Flight::route('/@filename', function($filename) {
$response = Flight::response();
// évidemment vous devriez sanitiser le chemin et tout ça.
$fileNameSafe = basename($filename);
// Si vous avez des en-têtes supplémentaires à définir ici après que la route ait été exécutée
// vous devez les définir avant que quoi que ce soit ne soit échoé.
// Ils doivent tous être un appel brut à la fonction header() ou
// un appel à Flight::response()->setRealHeader()
header('Content-Disposition: attachment; filename="'.$fileNameSafe.'"');
// ou
$response->setRealHeader('Content-Disposition: attachment; filename="'.$fileNameSafe.'"');
$filePath = '/some/path/to/files/'.$fileNameSafe;
if (!is_readable($filePath)) {
Flight::halt(404, 'File not found');
}
// définissez manuellement la longueur du contenu si vous le souhaitez
header('Content-Length: '.filesize($filePath));
// ou
$response->setRealHeader('Content-Length: '.filesize($filePath));
// Diffusez le fichier au client au fur et à mesure qu'il est lu
readfile($filePath);
// C'est la ligne magique ici
})->stream();Stream avec en-têtes
Vous pouvez également utiliser la méthode streamWithHeaders() pour définir les en-têtes avant de commencer le streaming.
Flight::route('/stream-users', function() {
// vous pouvez ajouter n'importe quels en-têtes supplémentaires que vous voulez ici
// vous devez juste utiliser header() ou Flight::response()->setRealHeader()
// cependant que vous tirez vos données, juste comme exemple...
$users_stmt = Flight::db()->query("SELECT id, first_name, last_name FROM users");
echo '{';
$user_count = count($users);
while($user = $users_stmt->fetch(PDO::FETCH_ASSOC)) {
echo json_encode($user);
if(--$user_count > 0) {
echo ',';
}
// Ceci est requis pour envoyer les données au client
ob_flush();
}
echo '}';
// C'est ainsi que vous définirez les en-têtes avant de commencer le streaming.
})->streamWithHeaders([
'Content-Type' => 'application/json',
'Content-Disposition' => 'attachment; filename="users.json"',
// code de statut optionnel, par défaut 200
'status' => 200
]);Voir aussi
- Middleware - Utiliser du middleware avec des routes pour l'authentification, la journalisation, etc.
- Injection de dépendances - Simplifier la création et la gestion d'objets dans les routes.
- Pourquoi un framework ? - Comprendre les avantages d'utiliser un framework comme Flight.
- Extension - Comment étendre Flight avec votre propre fonctionnalité incluant la méthode
notFound. - php.net: preg_match - Fonction PHP pour la correspondance d'expressions régulières.
Dépannage
- Les paramètres de route sont associés par ordre, pas par nom. Assurez-vous que l'ordre des paramètres du rappel correspond à la définition de la route.
- Utiliser
Flight::get()ne définit pas une route ; utilisezFlight::route('GET /...')pour le routage ou le contexte objet Router dans les groupes (ex.$router->get(...)). - La propriété executedRoute n'est définie qu'après l'exécution d'une route ; elle est NULL avant l'exécution.
- Le streaming nécessite que la fonctionnalité de tampon de sortie legacy de Flight soit désactivée (
flight.v2.output_buffering = false). - Pour l'injection de dépendances, seules certaines définitions de routes supportent l'instanciation basée sur conteneur.
404 Non trouvé ou comportement de route inattendu
Si vous voyez une erreur 404 Non trouvé (mais vous jurez sur votre vie que c'est vraiment là et que ce n'est pas une faute de frappe), cela pourrait en fait être un problème avec le fait que vous retournez une valeur dans votre point de terminaison de route au lieu de simplement l'échoer. La raison de cela est intentionnelle mais pourrait surprendre certains développeurs.
Flight::route('/hello', function(){
// Cela pourrait causer une erreur 404 Non trouvé
return 'Hello World';
});
// Ce que vous voulez probablement
Flight::route('/hello', function(){
echo 'Hello World';
});La raison de cela est en raison d'un mécanisme spécial intégré au routeur qui gère la sortie de retour comme un signal pour "aller à la route suivante". Vous pouvez voir le comportement documenté dans la section Routage.
Journal des modifications
- v3 : Ajout du routage de ressources, de l'aliasing de route, et du support de streaming, groupes de routes, et support de middleware.
- v1 : La grande majorité des fonctionnalités de base disponibles.