Одна ветка на каждый маршрут
Маршрутизация решает, какой код отвечает на какой запрос. В PHP нет встроенной таблицы маршрутов — скрипт-маршрутизатор сам читает запрос: $_SERVER["REQUEST_URI"] содержит путь, а разбиение его по / даёт сегменты.
Сегмент, который является данными — 7 в /users/7, — это параметр пути: вместо буквального сопоставления маршрутизатор захватывает его и передаёт обработчику. Фреймворки добавляют сверху удобства, но механизм под ними именно такой.
Маршруты и значения из пути
Два маршрута: список по адресу /users и один пользователь по /users/{id}. Второй сегмент пути — это параметр пути, та часть URL, которая является данными.
Маршрут с параметром пути
Один и тот же скрипт-маршрутизатор направляет /users в одну ветку, а /users/7 — в другую, и второй сегмент пути передаёт 7 как данные.
Вывод: PHP-скрипт
<?php
// Скрипт-маршрутизатор: он разбивает путь и выбирает ветку.
$router = <<<'PHP'
<?php
$path = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
$segments = explode("/", trim($path, "/"));
if ($path === "/users") {
echo "все пользователи: Ana, Ben, Cleo\n";
} elseif (count($segments) === 2 && $segments[0] === "users") {
echo "один пользователь, id = {$segments[1]}\n";
} else {
http_response_code(404);
echo "не найдено\n";
}
PHP;
$routerFile = sys_get_temp_dir() . "/pc-path-values.php";
file_put_contents($routerFile, $router);
// Свободный порт, затем встроенный веб-сервер PHP как дочерний процесс.
$probe = stream_socket_server("tcp://127.0.0.1:0");
$port = (int) explode(":", stream_socket_get_name($probe, false))[1];
fclose($probe);
$server = proc_open(["php", "-S", "127.0.0.1:$port", $routerFile],
[1 => ["file", "/dev/null", "w"], 2 => ["file", "/dev/null", "w"]], $pipes);
do {
usleep(20000);
$up = @fsockopen("127.0.0.1", $port);
} while ($up === false);
fclose($up);
$context = stream_context_create(["http" => ["ignore_errors" => true]]);
foreach (["/users", "/users/7"] as $path) {
$body = file_get_contents("http://127.0.0.1:$port$path", false, $context);
echo "GET $path -> $body";
}
proc_terminate($server);
proc_close($server);
unlink($routerFile);
Параметры запроса
Часть после ? содержит параметры запроса — настройки для запроса, например текст поиска или ограничение: /search?q=php&limit=2. PHP разбирает их за вас в массив $_GET, всегда как строки, а ?? подставляет значение по умолчанию для отсутствующих.
Правило простое: путь говорит, какой ресурс, а строка запроса — как: фильтры, сортировка, разбиение на страницы.
Чтение строки запроса
Обработчик читает q и limit из $_GET. Во втором запросе limit отсутствует, поэтому обработчик берёт значение по умолчанию — отсутствующие параметры это нормально, рассчитывайте на них.
Вывод: PHP-скрипт
<?php
// Параметры запроса приходят уже разобранными в $_GET, всегда как строки.
$router = <<<'PHP'
<?php
$q = $_GET["q"] ?? "";
$limit = $_GET["limit"] ?? "10";
echo "поиск $q с лимитом $limit\n";
PHP;
$routerFile = sys_get_temp_dir() . "/pc-query-params.php";
file_put_contents($routerFile, $router);
// Свободный порт, затем встроенный веб-сервер PHP как дочерний процесс.
$probe = stream_socket_server("tcp://127.0.0.1:0");
$port = (int) explode(":", stream_socket_get_name($probe, false))[1];
fclose($probe);
$server = proc_open(["php", "-S", "127.0.0.1:$port", $routerFile],
[1 => ["file", "/dev/null", "w"], 2 => ["file", "/dev/null", "w"]], $pipes);
do {
usleep(20000);
$up = @fsockopen("127.0.0.1", $port);
} while ($up === false);
fclose($up);
$context = stream_context_create(["http" => ["ignore_errors" => true]]);
foreach (["/search?q=php&limit=2", "/search?q=json"] as $path) {
$body = file_get_contents("http://127.0.0.1:$port$path", false, $context);
echo "GET $path -> $body";
}
proc_terminate($server);
proc_close($server);
unlink($routerFile);