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

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

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

1. Переход к контроллерам

Когда пользователь делает запрос, наша задача — определить, какой контроллер должен обработать этот запрос. Все контроллеры находятся в пространстве имен app\controllers, и мы должны корректно сформировать имя контроллера в зависимости от маршрута.

2. Обработка маршрутов: метод dispatch

Вернемся к методу dispatch, который принимает строку URL:

protected static function dispatch($url) {
    // Проверяем наличие соответствия маршруту
    if (!self::matchRoute($url)) {
        // Если соответствие не найдено, выбрасываем исключение
        throw new \Exception('404 Not Found: Страница не найдена');
    }

    // Определяем контроллер
    $controllerNamespace = 'app\\controllers\\';
    $adminPrefix = self::$root['admin_prefix'] ? self::$root['admin_prefix'] . '/' : '';
    $controllerClass = $controllerNamespace . $adminPrefix . ucfirst(self::$root['controller']) . 'Controller';

    // Проверяем, существует ли контроллер и создаем его экземпляр
    if (!class_exists($controllerClass)) {
        throw new \Exception("Контроллер не найден: $controllerClass");
    }
    $controllerObject = new $controllerClass();

    // Далее будет вызов метода контроллера
}

3. Формирование названия контроллера

Как видно из кода, мы добавляем логику для формирования имени контроллера, основываясь на маршруте пользователя. Важно учесть, что админ-префикс может добавляться к маршруту, и в зависимости от этого мы формируем имя контроллера:

$controllerNamespace = 'app\\controllers\\'; // Пространство имен для контроллеров
$adminPrefix = self::$root['admin_prefix'] ? self::$root['admin_prefix'] . '/' : ''; // Проверяем наличие админ-префикса
$controllerClass = $controllerNamespace . $adminPrefix . ucfirst(self::$root['controller']) . 'Controller'; // Формируем имя контроллера

4. Проверка существования контроллера

Перед созданием объекта контроллера, мы должны убедиться, что класс существует:

if (!class_exists($controllerClass)) {
    throw new \Exception("Контроллер не найден: $controllerClass");
}
$controllerObject = new $controllerClass(); // Создаем объект контроллера

Таким образом, если контроллер не найден, будет выброшено исключение, что упростит отладку и понимание происходящего.

5. Вызов метода контроллера

Теперь, когда у нас есть экземпляр контроллера, нам необходимо вызвать метод, который будет обрабатывать запрос. Метод формируется на основании имени действия, указанного в маршруте:

$actionMethod = self::lowerCamelCase(self::$root['action']) . 'Action'; // Формируем метод, добавляя 'Action'

if (!method_exists($controllerObject, $actionMethod)) {
    throw new \Exception("Метод не найден: $actionMethod в контроллере $controllerClass");
}
$controllerObject->$actionMethod(); // Вызываем метод контроллера

Таким образом, мы обеспечиваем, чтобы метод контроллера был найден, и только затем вызываем его.

6. Пример реализации контроллера

Для тестирования нашей логики создадим простой контроллер:

namespace app\controllers;

class MainController {
    public function indexAction() {
        echo "Добро пожаловать на главную страницу!";
    }
}

Теперь добавим маршрут для главной страницы:

Router::add('^$', ['controller' => 'Main', 'action' => 'index']);

Теперь, если мы откроем корневой URL (/), мы должны увидеть следующее сообщение:

Добро пожаловать на главную страницу!

7. Обработка ошибок

Чтобы избежать сбоев в работе приложения, важно обработать возможные ошибки. Для этого обернем вызовы маршрутизатора в блок try-catch:

try {
    Router::dispatch($url); // Вызываем метод dispatch
} catch (\Exception $e) {
    echo $e->getMessage(); // Выводим сообщение об ошибке
}

8. Поддержка дополнительных параметров и улучшения

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

Заключение

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