Routing
Hinweis: Möchten Sie mehr über Routing erfahren? Schauen Sie sich die "why a framework?" Seite für eine detailliertere Erklärung an.
Grundlegendes Routing in Flight erfolgt durch Abgleich eines URL-Musters mit einer Callback-Funktion oder einem Array aus einer Klasse und einer Methode.
Flight::route('/', function(){
echo 'hello world!';
});
Routen werden in der Reihenfolge abgeglichen, in der sie definiert sind. Die erste Route, die zu einer Anfrage passt, wird aufgerufen.
Callbacks/Functions
Die Callback kann jedes aufrufbare Objekt sein. Sie können also eine reguläre Funktion verwenden:
function hello() {
echo 'hello world!';
}
Flight::route('/', 'hello');
Classes
Sie können auch eine statische Methode einer Klasse verwenden:
class Greeting {
public static function hello() {
echo 'hello world!';
}
}
Flight::route('/', [ 'Greeting','hello' ]);
Oder indem Sie zuerst ein Objekt erstellen und dann die Methode aufrufen:
// Greeting.php
class Greeting
{
public function __construct() {
$this->name = 'John Doe';
}
public function hello() {
echo "Hello, {$this->name}!";
}
}
// index.php
$greeting = new Greeting();
Flight::route('/', [ $greeting, 'hello' ]);
// You also can do this without creating the object first
// Note: No args will be injected into the constructor
Flight::route('/', [ 'Greeting', 'hello' ]);
// Additionally you can use this shorter syntax
Flight::route('/', 'Greeting->hello');
// or
Flight::route('/', Greeting::class.'->hello');
Dependency Injection via DIC (Dependency Injection Container)
Wenn Sie Dependency Injection über einen Container (PSR-11, PHP-DI, Dice usw.) verwenden möchten, ist dies nur für Routen verfügbar, bei denen Sie das Objekt direkt erstellen oder Strings verwenden, um die Klasse und Methode zu definieren. Weitere Informationen finden Sie auf der Dependency Injection Seite.
Hier ein schnelles Beispiel:
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
// Setup the container with whatever params you need
// See the Dependency Injection page for more information on PSR-11
$dice = new \Dice\Dice();
// Don't forget to reassign the variable with '$dice = '!!!!!
$dice = $dice->addRule('flight\database\PdoWrapper', [
'shared' => true,
'constructParams' => [
'mysql:host=localhost;dbname=test',
'root',
'password'
]
]);
// Register the container handler
Flight::registerContainerHandler(function($class, $params) use ($dice) {
return $dice->create($class, $params);
});
// Routes like normal
Flight::route('/hello/@id', [ 'Greeting', 'hello' ]);
// or
Flight::route('/hello/@id', 'Greeting->hello');
// or
Flight::route('/hello/@id', 'Greeting::hello');
Flight::start();
Method Routing
Standardmäßig werden Routenmustern mit allen Anfragemethoden abgeglichen. Sie können auf spezifische Methoden reagieren, indem Sie einen Bezeichner vor der URL platzieren.
Flight::route('GET /', function () {
echo 'I received a GET request.';
});
Flight::route('POST /', function () {
echo 'I received a POST request.';
});
// You cannot use Flight::get() for routes as that is a method
// to get variables, not create a route.
// Flight::post('/', function() { /* code */ });
// Flight::patch('/', function() { /* code */ });
// Flight::put('/', function() { /* code */ });
// Flight::delete('/', function() { /* code */ });
Sie können auch mehrere Methoden auf eine einzelne Callback zuweisen, indem Sie einen |
-Delimter verwenden:
Flight::route('GET|POST /', function () {
echo 'I received either a GET or a POST request.';
});
Zusätzlich können Sie das Router-Objekt abrufen, das einige Hilfsmethoden für Sie bereitstellt:
$router = Flight::router();
// maps all methods
$router->map('/', function() {
echo 'hello world!';
});
// GET request
$router->get('/users', function() {
echo 'users';
});
// $router->post();
// $router->put();
// $router->delete();
// $router->patch();
Regular Expressions
Sie können reguläre Ausdrücke in Ihren Routen verwenden:
Flight::route('/user/[0-9]+', function () {
// This will match /user/1234
});
Obwohl diese Methode verfügbar ist, wird empfohlen, benannte Parameter oder benannte Parameter mit regulären Ausdrücken zu verwenden, da sie lesbarer und einfacher zu warten sind.
Named Parameters
Sie können benannte Parameter in Ihren Routen angeben, die an Ihre Callback-Funktion weitergegeben werden. Das dient hauptsächlich der Lesbarkeit der Route. Bitte beachten Sie den wichtigen Hinweis weiter unten.
Flight::route('/@name/@id', function (string $name, string $id) {
echo "hello, $name ($id)!";
});
Sie können auch reguläre Ausdrücke mit Ihren benannten Parametern kombinieren, indem Sie den :
-Delimter verwenden:
Flight::route('/@name/@id:[0-9]{3}', function (string $name, string $id) {
// This will match /bob/123
// But will not match /bob/12345
});
Hinweis: Abgleich von Regex-Gruppen
()
mit positionsbezogenen Parametern wird nicht unterstützt. :'(
Wichtiger Hinweis
Obwohl im obigen Beispiel erscheint, als ob @name
direkt mit der Variable $name
verbunden ist, ist das nicht der Fall. Die Reihenfolge der Parameter in der Callback-Funktion bestimmt, was an sie weitergegeben wird. Wenn Sie die Reihenfolge der Parameter in der Callback-Funktion ändern, werden die Variablen ebenfalls umgeschaltet. Hier ein Beispiel:
Flight::route('/@name/@id', function (string $id, string $name) {
echo "hello, $name ($id)!";
});
Und wenn Sie zur folgenden URL gehen: /bob/123
, wäre die Ausgabe hello, 123 (bob)!
.
Seien Sie vorsichtig, wenn Sie Ihre Routen und Callback-Funktionen einrichten.
Optional Parameters
Sie können benannte Parameter angeben, die optional für den Abgleich sind, indem Sie Segmente in Klammern setzen.
Flight::route(
'/blog(/@year(/@month(/@day)))',
function(?string $year, ?string $month, ?string $day) {
// This will match the following URLS:
// /blog/2012/12/10
// /blog/2012/12
// /blog/2012
// /blog
}
);
Jegliche optionale Parameter, die nicht abgeglichen werden, werden als NULL
weitergegeben.
Wildcards
Der Abgleich erfolgt nur auf einzelne URL-Segmente. Wenn Sie mehrere Segmente abgleichen möchten, können Sie das *
-Wildcard verwenden.
Flight::route('/blog/*', function () {
// This will match /blog/2000/02/01
});
Um alle Anfragen an eine einzelne Callback zu leiten, können Sie tun:
Flight::route('*', function () {
// Do something
});
Passing
Sie können die Ausführung an die nächste passende Route weitergeben, indem Sie true
aus Ihrer Callback-Funktion zurückgeben.
Flight::route('/user/@name', function (string $name) {
// Check some condition
if ($name !== "Bob") {
// Continue to next route
return true;
}
});
Flight::route('/user/*', function () {
// This will get called
});
Route Aliasing
Sie können einer Route einen Alias zuweisen, damit die URL später in Ihrem Code dynamisch generiert werden kann (z. B. in einer Vorlage).
Flight::route('/users/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
// later in code somewhere
Flight::getUrl('user_view', [ 'id' => 5 ]); // will return '/users/5'
Das ist besonders hilfreich, wenn sich Ihre URL ändert. Im obigen Beispiel, sagen wir, dass "users" zu /admin/users/@id
verschoben wird.
Mit Aliasing müssen Sie nirgendwo ändern, wo Sie den Alias referenzieren, da der Alias nun /admin/users/5
zurückgibt, wie im Beispiel.
Route-Aliasing funktioniert auch in Gruppen:
Flight::group('/users', function() {
Flight::route('/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
});
// later in code somewhere
Flight::getUrl('user_view', [ 'id' => 5 ]); // will return '/users/5'
Route Info
Wenn Sie die Informationen zur passenden Route überprüfen möchten, gibt es 2 Wege, dies zu tun.
Sie können die executedRoute
-Eigenschaft verwenden oder das Route-Objekt anfordern, indem Sie true
als dritten Parameter in der Route-Methode übergeben.
Das Route-Objekt wird immer als letzter Parameter an Ihre Callback-Funktion übergeben.
Flight::route('/', function(\flight\net\Route $route) {
// Array of HTTP methods matched against
$route->methods;
// Array of named parameters
$route->params;
// Matching regular expression
$route->regex;
// Contains the contents of any '*' used in the URL pattern
$route->splat;
// Shows the url path....if you really need it
$route->pattern;
// Shows what middleware is assigned to this
$route->middleware;
// Shows the alias assigned to this route
$route->alias;
}, true);
Oder wenn Sie die zuletzt ausgeführte Route überprüfen möchten, können Sie tun:
Flight::route('/', function() {
$route = Flight::router()->executedRoute;
// Do something with $route
// Array of HTTP methods matched against
$route->methods;
// Array of named parameters
$route->params;
// Matching regular expression
$route->regex;
// Contains the contents of any '*' used in the URL pattern
$route->splat;
// Shows the url path....if you really need it
$route->pattern;
// Shows what middleware is assigned to this
$route->middleware;
// Shows the alias assigned to this route
$route->alias;
});
Hinweis: Die
executedRoute
-Eigenschaft wird nur nach der Ausführung einer Route gesetzt. Wenn Sie versuchen, darauf zuzugreifen, bevor eine Route ausgeführt wurde, ist sieNULL
. Sie können executedRoute auch in Middleware verwenden!
Route Grouping
Es könnte Fälle geben, in denen Sie verwandte Routen zusammen gruppieren möchten (z. B. /api/v1
).
Sie können das tun, indem Sie die group
-Methode verwenden:
Flight::group('/api/v1', function () {
Flight::route('/users', function () {
// Matches /api/v1/users
});
Flight::route('/posts', function () {
// Matches /api/v1/posts
});
});
Sie können sogar Gruppen in Gruppen verschachteln:
Flight::group('/api', function () {
Flight::group('/v1', function () {
// Flight::get() gets variables, it doesn't set a route! See object context below
Flight::route('GET /users', function () {
// Matches GET /api/v1/users
});
Flight::post('/posts', function () {
// Matches POST /api/v1/posts
});
Flight::put('/posts/1', function () {
// Matches PUT /api/v1/posts
});
});
Flight::group('/v2', function () {
// Flight::get() gets variables, it doesn't set a route! See object context below
Flight::route('GET /users', function () {
// Matches GET /api/v2/users
});
});
});
Grouping with Object Context
Sie können Route-Grouping immer noch mit dem Engine
-Objekt auf die folgende Weise verwenden:
$app = new \flight\Engine();
$app->group('/api/v1', function (Router $router) {
// user the $router variable
$router->get('/users', function () {
// Matches GET /api/v1/users
});
$router->post('/posts', function () {
// Matches POST /api/v1/posts
});
});
Grouping with Middleware
Sie können auch Middleware einer Gruppe von Routen zuweisen:
Flight::group('/api/v1', function () {
Flight::route('/users', function () {
// Matches /api/v1/users
});
}, [ MyAuthMiddleware::class ]); // or [ new MyAuthMiddleware() ] if you want to use an instance
Weitere Details finden Sie auf der group middleware Seite.
Resource Routing
Sie können eine Reihe von Routen für eine Ressource mit der resource
-Methode erstellen. Das erstellt eine Reihe von Routen für eine Ressource, die den RESTful-Konventionen folgt.
Um eine Ressource zu erstellen, tun Sie Folgendes:
Flight::resource('/users', UsersController::class);
Und was im Hintergrund passiert, ist, dass es die folgenden Routen erstellt:
[
'index' => 'GET ',
'create' => 'GET /create',
'store' => 'POST ',
'show' => 'GET /@id',
'edit' => 'GET /@id/edit',
'update' => 'PUT /@id',
'destroy' => 'DELETE /@id'
]
Und Ihr Controller sieht so aus:
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
{
}
}
Hinweis: You can view the newly added routes with
runway
by runningphp runway routes
.
Customizing Resource Routes
There are a few options to configure the resource routes.
Alias Base
You can configure the aliasBase
. By default the alias is the last part of the URL specified.
For example /users/
would result in an aliasBase
of users
. When these routes are created,
the aliases are users.index
, users.create
, etc. If you want to change the alias, set the aliasBase
to the value you want.
Flight::resource('/users', UsersController::class, [ 'aliasBase' => 'user' ]);
Only and Except
You can also specify which routes you want to create by using the only
and except
options.
Flight::resource('/users', UsersController::class, [ 'only' => [ 'index', 'show' ] ]);
Flight::resource('/users', UsersController::class, [ 'except' => [ 'create', 'store', 'edit', 'update', 'destroy' ] ]);
These are basically whitelisting and blacklisting options so you can specify which routes you want to create.
Middleware
You can also specify middleware to be run on each of the routes created by the resource
method.
Flight::resource('/users', UsersController::class, [ 'middleware' => [ MyAuthMiddleware::class ] ]);
Streaming
Sie können jetzt Antworten an den Client streamen, indem Sie die streamWithHeaders()
-Methode verwenden.
Das ist nützlich für das Senden großer Dateien, langer Prozesse oder die Generierung großer Antworten.
Das Streamen einer Route wird etwas anders gehandhabt als eine reguläre Route.
Hinweis: Streaming-Antworten ist nur verfügbar, wenn
flight.v2.output_buffering
auf false gesetzt ist.
Stream with Manual Headers
Sie können eine Antwort an den Client streamen, indem Sie die stream()
-Methode auf einer Route verwenden. Wenn Sie
das tun, müssen Sie alle Header manuell setzen, bevor Sie etwas an den Client ausgeben.
Das wird mit der header()
-PHP-Funktion oder der Flight::response()->setRealHeader()
-Methode erledigt.
Flight::route('/@filename', function($filename) {
// obviously you would sanitize the path and whatnot.
$fileNameSafe = basename($filename);
// If you have additional headers to set here after the route has executed
// you must define them before anything is echoed out.
// They must all be a raw call to the header() function or
// a call to Flight::response()->setRealHeader()
header('Content-Disposition: attachment; filename="'.$fileNameSafe.'"');
// or
Flight::response()->setRealHeader('Content-Disposition', 'attachment; filename="'.$fileNameSafe.'"');
$filePath = '/some/path/to/files/'.$fileNameSafe;
if (!is_readable($filePath)) {
Flight::halt(404, 'File not found');
}
// manually set the content length if you'd like
header('Content-Length: '.filesize($filePath));
// Stream the file to the client as it's read
readfile($filePath);
// This is the magic line here
})->stream();
Stream with Headers
Sie können auch die streamWithHeaders()
-Methode verwenden, um die Header zu setzen, bevor Sie mit dem Streamen beginnen.
Flight::route('/stream-users', function() {
// you can add any additional headers you want here
// you just must use header() or Flight::response()->setRealHeader()
// however you pull your data, just as an example...
$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 ',';
}
// This is required to send the data to the client
ob_flush();
}
echo '}';
// This is how you'll set the headers before you start streaming.
})->streamWithHeaders([
'Content-Type' => 'application/json',
'Content-Disposition' => 'attachment; filename="users.json"',
// optional status code, defaults to 200
'status' => 200
]);