Организация маршрута магазина в PHP: от index.php к эффективной маршрутизации

Раздел: Маршрутизация в веб-приложениях -> Маршруты 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;
}

Проблемы: Внешняя зависимость. Для кэширования маршрутов требуется дополнительная настройка. При неправильном порядке может быть переопределён маршрут.

- Shop index php route (маршрут магазина php)
- Index php route common (маршрут общей страницы php)
- Index php route product search (маршрут поиска товаров php)

Расширенные примеры маршрутизации для магазина

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)

Маршрут магазина PHP - comments

En
Shop index php route (php)