Создание маршрутизатора в собственном фреймворке на PHP. Часть 1

Andre Kowalsy
91 раз
10 мин чтения
Опубликовано: 23-07-2025
Обновлено: 25-03-2026
Категории: Пишем свою CMS

Погружаясь в мир маршрутизаторов: шаг за шагом к созданию эффективной системы роутинга

Дорогие друзья, продолжаем наше увлекательное путешествие в мир программирования и разбираемся с важнейшим компонентом любого веб-приложения — маршрутизатором (router). Чтобы вы понимали, зачем нам этот элемент, давайте начнем с основ.

В процессе разработки любого веб-приложения на PHP важным аспектом является правильная обработка URL запросов. В этом посте мы создадим маршрутизатор для нашего фреймворка, который будет отвечать за направление запросов на соответствующие контроллеры и действия. Это важный компонент, который помогает организовать структуру приложения и управлять его поведением.

Что такое маршрутизатор и роутинг?

Проще говоря, маршрутизатор — это как менеджер на ресепшене, который принимает запросы от пользователей и направляет их к соответствующим специалистам. Он разбирается в потребностях клиента и определяет, какая служба или функция сможет помочь. В нашем случае, маршрутизатор принимает запросы от браузера и решает, что делать с каждым конкретным запросом:

  1. Проверяет, существует ли адрес на сайте.
  2. Если адрес не найден, возвращает ошибку 404.
  3. Если адрес существует, перенаправляет запрос к соответствующему контроллеру.

Сегодня мы рассмотрим, как реализовать все эти функции в коде.

Как работает маршрутизатор?

Когда поступает запрос, маршрутизатор разбирает его на сегменты. Например, данный сегмент может выглядеть как page/view. Здесь Page указывает на контроллер, а View — на метод (action) этого контроллера. Также маршрутизатор может принимать параметры запроса, например, Contact.

Мы можем представить строку запроса как: Page, покажи страницу Contact. Здесь Page — это контроллер, а View — это action, который необходимо вызвать. Однако, в случае, если мы не укажем метод, маршрутизатор должен правильно интерпретировать запрос и отработать с ним по умолчанию.

Правила маршрутов: зачем они нужны?

Без таблицы маршрутов никуда. Это массив адресов, которые мы будем описывать с помощью регулярных выражений. Например, маршрут PageContact может быть представлен как регулярное выражение, где Page — это неизменная часть, а Contact — динамическая. Это позволяет управлять огромным количеством страниц без необходимости создавать по отдельному правилу для каждой.

Если у нас есть интернет-магазин, мы можем рассмотреть продукт ProductApple, где контроллер Product обеспечивает отображение нужного продукта, а Apple — это параметр, указывающий, какой конкретно продукт показать. Таким образом, мы можем использовать один маршрут, который обрабатывает множество запросов.

Подготовка к реализации

Прежде чем приступить к написанию кода, необходимо создать вспомогательные функции для отладки и хранения маршрутов. Создадим папку helpers, в которой будет файл functions.php, с функцией для красивой распечатки данных на экране.

Также, следует создать новую таблицу маршрутов в файле routes.php, где будут храниться регэкспы для обработки адресов. Все эти компоненты мы подключим в файл конфигурации.

Создание класса маршрутизатора

Теперь мы переходим к созданию самого класса маршрутизатора. Он будет содержать два основных свойства:

  1. Roots — массив, который будет хранить все маршруты.
  2. Root — конкретный маршрут, который будет найден в процессе обработки запроса.

Дополнительно мы создадим методы для добавления маршрутов и получения информации о текущих маршрутах. Это позволит нам динамически управлять маршрутами и контроллерами.

use blog\Router;

// Добавляем маршрут для главной страницы админ-панели
Router::add('^admin/?$', [
    'controller' => 'Main',      // Контроллер, отвечающий за обработку запроса
    'action' => 'index',         // Метод в контроллере, который будет вызван
    'admin_prefix' => 'admin'    // Префикс для админ-зоны
]);

// Добавляем маршрут для админ-панели с динамическими контроллерами и действиями
Router::add('^admin/(?P[a-z-]+)/?(?P[a-z-]+)?$', [
    'admin_prefix' => 'admin'    // Префикс для админ-зоны
]);

// Добавляем маршрут для главной пользовательской страницы
Router::add('^$', [
    'controller' => 'Main',      // Контроллер для главной страницы
    'action' => 'index'          // Метод, который будет обработан
]);

// Добавляем маршрут для пользовательской части с динамическими контроллерами и действиями
Router::add('^(?P[a-z-]+)/(?P[a-z-]+)/?$', [
    // Параметры будут извлечены из URL и переданы в соответствующий контроллер и метод
]);

Объяснение маршрутов:

  1. Маршрут для админ-панели (^admin/?$):

    • Этот маршрут отвечает за доступ к главной странице админкой. Если пользователь переходит по адресу /admin, будет вызван контроллер Main и метод index.
    • Параметр admin_prefix указывает, что это часть админской зоны.
  2. Динамические маршруты админ-панели (^admin/(?P[a-z-]+)/?(?P[a-z-]+)?$):

    • Этот маршрут обрабатывает запросы, которые содержат контроллер и действие.
    • Использует именованные параметры, чтобы захватить контроллер и действие из URL, например: /admin/users/edit будет соответствовать controller = users и action = edit.
  3. Маршрут для главной (пользовательской) страницы (^$):

    • Отвечает за запрос на корневой адрес сайта, ведет к контроллеру Main и методу index.
  4. Динамический маршрут для пользовательской части ('^(?P[a-z-]+)/(?P[a-z-]+)/?$'):

    • Этот маршрут позволяет пользователям обращаться к другим страницам сайта, передавая названия контроллеров и методов через URL. Например, /posts/view будет соответствовать контроллеру posts и методу view.

Рекомендации

  • Убедитесь, что ваши контроллеры и методы соответствуют правилам именования, заданным в маршрутах.
  • Если вы используете специальные символы или цифры в именах контроллеров и действий, обновите регулярные выражения соответственно, чтобы они их принимали.
namespace blog;

class Router
{

    protected static array $routes = [];
    protected static array $route = [];

    public static function add($regexp, $route = [])
    {
        self::$routes[$regexp] = $route;
    }

    public static function getRoutes(): array
    {
        return self::$routes;
    }

    public static function getRoute(): array
    {
        return self::$route;
    }

    public static function dispatch($url)
    {
        var_dump($url);
    }

}

Итог

В заключение, как только мы создадим все необходимые маршруты и подключим к ним контроллеры, мы сможем обрабатывать запросы от пользователей эффективно и без лишнего кода. Надеюсь, что вся информация будет полезной, и в следующих статьях мы погрузимся в конкретный код и его структуру!

Таким образом, создание маршрутизатора - это не просто набор правил, это продуманная система, которая делает ваше приложение универсальным и стабильным. Давайте продолжим развивать наш проект, и вскоре мы увидим, как все эти элементы работают вместе!

А тепер подробно расмотрим!

Основные технологии
  • PHP: Серверный язык программирования для создания фреймворка.
  • Namespaces: Для организации кода и предотвращения конфликтов имен.
  • Функции: Для упрощения и улучшения работы с данными.
Структура проекта
/vendor
    └── /blog
        ├── /helpers
        │   └── function.php
        ├── App.php
        ├── ErrorHandler.php
        ├── Router.php
/config
    └── routes.php
/public
    └── index.php
  • vendor/blog/: Папка с классами для приложения.
  • public/errors/: Папка для хранения шаблонов ошибок.
  • tmp/logs/: Папка для хранения логов ошибок.
Разбор кода
1. Создание папки и файла с функциями

Создайте папку helpers в каталоге vendor/blog/ и добавьте файл function.php с функцией debug.

// vendor/blog/helpers/function.php
' . print_r($data, true) . '

'; if ($die) die(); }

Объяснение:

  • Функция позволяет удобно выводить данные для отладки.
2. Подключение файла с функциями

Подключите этот файл в index.php:

// public/index.php
require_once HELPERS . '/function.php';
3. Создание файла маршрутов

Создайте файл routes.php в каталоге config:

// public/index.php
require_once CONFIG . '/routes.php';
4. Создание класса Router

Создайте класс Router в vendor/blog/Router.php.

// vendor/blog/Router.php
namespace blog;

class Router
{
    protected static array $routes = [];
    protected static array $route = ['admin_prefix' => '', 'controller' => '', 'action' => ''];

    public static function add($regexp, $route = [])
    {
        self::$routes[$regexp] = $route;
    }

    public static function getRoutes(): array
    {
        return self::$routes;
    }

    public static function getRoute(): array
    {
        return self::$route;
    }

    public static function dispatch($url)
    {
        var_dump($url);
    }
}

Объяснение:

  • Класс Router содержит массив маршрутов и методы для обработки запросов.
5. Вызов маршрутизатора в классе App

Вызовите маршрутизатор в классе App.

// vendor/blog/App.php
public function __construct()
{
    session_start(); // Начало сеанса
    $query = trim(urldecode($_SERVER['QUERY_STRING']), '/');
    new ErrorHandler();
    self::$app = Registry::getInstance();
    $this->getParams();
    Router::dispatch($query); // Вызов маршрутизатора
}

Объяснение:

  • Получаем URL и передаем его в метод dispatch маршрутизатора.
6. Добавление маршрутов в routes.php

Добавьте маршруты в routes.php.

// config/routes.php
use blog\Router;

// Маршруты для администраторов
Router::add('^admin/?$', ['controller' => 'Main', 'action' => 'index', 'admin_prefix' => 'admin']);
Router::add('^admin/(?P[a-z-]+)/(?P[a-z-]+)/?$', ['admin_prefix' => 'admin']);

// Маршруты для главной страницы
Router::add('^(?P[a-z])?/?$', ['controller' => 'Main', 'action' => 'index']);
Router::add('^(?P[a-z-]+)/?(?P[a-z-]+)?$');

Объяснение:

  • Здесь мы определяем маршруты для административной панели и главной страницы.
7. Обработка маршрутов в методе dispatch

Теперь реализуйте логику в методе dispatch.

public static function dispatch($url)
{
    var_dump($url);
}

Заключение

Созданный нами маршрутизатор эффективно управляет запросами и помогает организовать структуру вашего приложения на PHP. Используйте предложенные инструменты и методы, чтобы развивать и расширять функциональность вашего фреймворка.

Если у вас есть вопросы или хотите узнать больше, не стесняйтесь обращаться в комментариях!