Организация маршрута магазина в PHP: от index.php к эффективной маршрутизации
Маршрутизация для магазина: подходы и реализация
Организация маршрутизации в интернет-магазине на PHP часто начинается с единой точки входа через index.php. Все запросы направляются на этот файл, который анализирует URL и определяет, какой контроллер или обработчик запустить. Рассмотрим несколько способов реализации такой маршрутизации, от простых до более гибких решений.
Основное решение: собственный класс Router
Наиболее эффективный и поддерживаемый способ - создание небольшого класса Router, который позволяет добавлять маршруты с шаблонами и вызывает соответствующие функции.
class Router {
private array $routes = [];
public function add(string $method, string $pattern, callable $handler): void {
$this->routes[] = [
'method' => $method,
'pattern' => $this->convertPattern($pattern),
'handler' => $handler
];
}
private function convertPattern(string $pattern): string {
return '#^' . preg_replace('#\{([a-zA-Z_]+)\}#', '(?P<$1>[^/]+)', $pattern) . '$#';
}
public function dispatch(string $method, string $uri): void {
foreach ($this->routes as $route) {
if ($route['method'] !== $method) continue;
if (preg_match($route['pattern'], $uri, $matches)) {
$params = array_filter($matches, 'is_string', ARRAY_FILTER_USE_KEY);
call_user_func_array($route['handler'], $params);
return;
}
}
http_response_code(404);
echo 'Страница не найдена';
}
}Route info php id (информация о маршруте по id php)
Использование для магазина:
$router = new Router();
$router->add('GET', '/', function() { echo 'Главная страница'; });
$router->add('GET', '/catalog', function() { echo 'Каталог товаров'; });
$router->add('GET', '/product/{id}', function($id) { echo "Товар с ID: $id"; });
$router->add('GET', '/cart', function() { echo 'Корзина'; });
$method = $_SERVER['REQUEST_METHOD'];
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$router->dispatch($method, $uri);Ru index php route (маршрут index.php с русским языком)
Типичные проблемы и ошибки: Неправильный порядок маршрутов - специфичные шаблоны должны добавляться раньше общих. Необходимо обрабатывать методы POST и другие. При использовании call_user_func_array для методов класса требуется передавать массив [$object, 'method']. Не забывать экранировать специальные символы в URI.
Вариант 1: Простой switch по сегменту URL
Самый примитивный способ без гибкости.
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$segments = explode('/', trim($uri, '/'));
switch ($segments[0] ?? '') {
case '':
echo 'Главная страница';
break;
case 'catalog':
echo 'Каталог';
break;
case 'product':
$id = $segments[1] ?? null;
echo "Товар с ID: $id";
break;
case 'cart':
echo 'Корзина';
break;
default:
http_response_code(404);
echo 'Страница не найдена';
}
Admin index php route (маршрут админки php)
Проблемы: Невозможно обрабатывать вложенные пути (например, /catalog/category/1). Сложно поддерживать при росте числа страниц. Не учитываются HTTP методы. При изменении структуры URL нужно переписывать switch.
Вариант 2: Маршруты с регулярными выражениями напрямую
Более гибкий, но требует внимательного написания regex.
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
if (preg_match('#^/$#', $uri)) {
echo 'Главная';
} elseif (preg_match('#^/catalog$#', $uri)) {
echo 'Каталог';
} elseif (preg_match('#^/product/(\d+)$#', $uri, $m)) {
echo 'Товар ID: ' . $m[1];
} else {
http_response_code(404);
echo '404';
}Index php route information (маршрут информационной страницы php)
Ошибки: Легко допустить ошибку в регулярном выражении, особенно при экранировании. Для каждого нового маршрута нужно писать отдельный elseif. Сложно комбинировать методы. Производительность падает с ростом числа маршрутов.
Вариант 3: Использование библиотеки FastRoute
Готовое решение от разработчиков PHP‑фреймворков.
composer require nikic/fast-route
// index.php
$dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) {
$r->addRoute('GET', '/', 'home_handler');
$r->addRoute('GET', '/catalog', 'catalog_handler');
$r->addRoute('GET', '/product/{id:\d+}', 'product_handler');
});
$httpMethod = $_SERVER['REQUEST_METHOD'];
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$routeInfo = $dispatcher->dispatch($httpMethod, $uri);
switch ($routeInfo[0]) {
case FastRoute\Dispatcher::FOUND:
$handler = $routeInfo[1];
$vars = $routeInfo[2];
echo $handler . ' вызван с параметрами: ' . json_encode($vars);
break;
case FastRoute\Dispatcher::NOT_FOUND:
http_response_code(404);
echo 'Страница не найдена';
break;
case FastRoute\Dispatcher::METHOD_NOT_ALLOWED:
http_response_code(405);
echo 'Метод не разрешён';
break;
}Проблемы: Внешняя зависимость. Для кэширования маршрутов требуется дополнительная настройка. При неправильном порядке может быть переопределён маршрут.
Расширенные примеры маршрутизации для магазина
1. Группировка маршрутов с помощью префикса
class GroupRouter extends Router {
public function group(string $prefix, array $routes): void {
foreach ($routes as $route) {
$this->add($route[0], $prefix . $route[1], $route[2]);
}
}
}
$router = new GroupRouter();
$router->group('/admin', [
['GET', '/dashboard', function() { echo 'Админ панель'; }],
['GET', '/products', function() { echo 'Управление товарами'; }],
]);
$router->dispatch('GET', '/admin/products');
Управление товарами
2. Middleware для проверки авторизации
function authMiddleware(callable $handler): callable {
return function (...$params) use ($handler) {
if (!isset($_SESSION['user'])) {
http_response_code(403);
echo 'Доступ запрещён';
return;
}
$handler(...$params);
};
}
$router->add('GET', '/cart', authMiddleware(function() {
echo 'Содержимое корзины';
}));
Доступ запрещён (если не авторизован)
3. Обработка POST запросов для оформления заказа
$router->add('POST', '/order', function() {
$data = json_decode(file_get_contents('php://input'), true);
echo 'Заказ оформлен: ' . json_encode($data);
});
// Пример вызова через curl (симуляция)
// curl -X POST -H "Content-Type: application/json" -d '{"product_id":1,"qty":2}' http://shop.local/order
Заказ оформлен: {"product_id":1,"qty":2}4. Динамические маршруты с несколькими параметрами и фильтрацией
$router->add('GET', '/search/{query}/{page?}', function($query, $page = 1) {
echo "Поиск: $query, страница: $page";
});
$router->dispatch('GET', '/search/ноутбук/3');
Поиск: ноутбук, страница: 3
5. Использование FastRoute с кэшированием маршрутов
$cacheFile = __DIR__ . '/route.cache';
if (file_exists($cacheFile)) {
$dispatcher = FastRoute\cachedDispatcher(function(FastRoute\RouteCollector $r) {
// ... определение маршрутов
}, ['cacheFile' => $cacheFile]);
} else {
$dispatcher = FastRoute\simpleDispatcher(/* ... */);
}
$routeInfo = $dispatcher->dispatch($_SERVER['REQUEST_METHOD'], parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
// дальнейшая обработка
(При первом запросе создаётся кэш, последующие работают быстрее)
6. Регулярные выражения с дополнительными ограничениями (только буквы)
$router->add('GET', '/category/{slug:[a-z-]+}', function($slug) {
echo "Категория: $slug";
});
$router->dispatch('GET', '/category/elektronika'); // ok
$router->dispatch('GET', '/category/123'); // 404
Категория: elektronika (второй вызов выдаст 404)
7. Маршрут для REST API магазина
$router->add('GET', '/api/products', function() { echo 'Список товаров в JSON'; });
$router->add('POST', '/api/products', function() { echo 'Создание товара'; });
$router->add('GET', '/api/products/{id}', function($id) { echo "Товар $id"; });
$router->add('PUT', '/api/products/{id}', function($id) { echo "Обновление товара $id"; });
$router->add('DELETE', '/api/products/{id}', function($id) { echo "Удаление товара $id"; });
При GET /api/products/42 -> Товар 42
8. Обработка ошибки 405 Method Not Allowed
$router->add('GET', '/contact', function() { echo 'Форма контактов'; });
// При POST на /contact
$router->dispatch('POST', '/contact'); // вызовет 404, так как маршрут не найден
// Чтобы поддерживать 405, нужно хранить список разрешённых методов для каждого пути
Страница не найдена (хотя правильнее 405)