Как написать роутер на php

Обновлено: 04.07.2024

Сегодня я поделюсь с вами опытом как достаточно быстро и с минимальными трудозатратами можно создать свою систему маршрутизации (роутинга) в вашем php приложении. Поможет нам в этом замечательная библиотека PHRoute.

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

Я подразумеваю что вы уже имеете представление о том как пользоваться композером, потому, что установку данной библиотеки мы будем осуществлять с помощью этого менеджера пакетов.

Итак приступим, создаём файл нашего проекта composer.json

Давайте так-же организуем перенаправление всех запросов в файл index.php. Для этого в корне вэб сервера необходимо создать файл .htaccess со следующим содержимым:

Теперь из папки вашего проекта выполняем команду composer install. В корне должна пояаится папка Vendor.

Для лучшего понимания концепции PHRoute, неплохо бы создать образец проекта для работы. Сегодня мы сделаем с вами базовый API базы данных книг.

Вот схема базы данных, которую мы собираемся использовать:


Если вы хотите сделать некоторые тесты, это дамп схемы SQL, который я использовал (с некоторыми дополнительными фиктивными данными):

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

Прежде чем мы начнем с конкретных маршрутов, давайте проанализируем основную структуру приложения. Это то, что мы собираемся добавить в наш файл index.php

У нас есть три служебных метода: processInput, processOutput и getPDOInstance. Мы будем использовать первые два, чтобы убедиться, что мы получаем правильный ввод и правильный вывод. Третий подготовит необходимый экземпляр PDO.

Обратите внимание: второй параметр метода array_slice - «3» из-за моей индивидуальной настройки конкретного проекта. Измените его на ваш базовый URL-адрес.

Теперь мы объявляем наши маршруты, используя объект $router, экземпляр класса RouteController. Затем в методе диспатчер происходит волшебство $dispatcher->dispatch(), он принимает два параметра: метод запроса $_SERVER (GET, POST и т. д.) И определенный uri запрос. С помощью этой информации диспетчер вызывает правильный маршрут и выполняет код при закрытии. Возвращаемое значение хранится в переменной $response, которая передается методу processOutput(), который отображает его как строку JSON.

Как вы можете видеть, в этом конкретном примере мы объявили один маршрут: hello.

Обратите внимание: если вы хотите, вы можете улучшить структуру приложения. Создайте новый файл и назовите его routes.php. Затем подключите его из основного файла index.php сразу после инициализации объекта $ router. Таким образом все ваши маршруты будут содержаться в отдельном файле. Теперь вы знаете все, что вам нужно о базовой структуре нашего примера.

Приступим к созданию нашего первого роута! Давайте посмотрим, что мы можем делать с маршрутами, и насколько мы можем кастомизировать их для наших нужд.

Начинаем с самого простого: список авторов.

В первой строке мы указываем название нашего маршрута — authors. Проверим наш роут, если вы перейдёте по адресу: ваше_приложение/autors, должнен полуится вот такой вот массив данных:

Отлично! Теперь давайте попробуем добавить параметр, что бы получить все книги одного автора основываясь на его id.

Для этого ипользуем конструкцию на подобии этой:

Вы можете передать параметр, используя плейсхолдер , с тем же выбранным именем, что и параметр для закрытия. В этом примере у нас есть плейсхолдер , соответствующий параметру $id. Вы же можете указать любой параметр, который вам нужен.

Иногда параметр может быть необязательным. Давайте сделаем еще один пример: если мы используем URL books, мы хотим получить список всех книг базы данных. Но, если мы укажем id, как параметр, например: books/1, мы получим список книг по данной категории, вот так:

Добавление «?» после метки параметра означает, что она будет необязательной. Конечно, это хорошая идея - указать значение по умолчанию в объявлении закрытия.

Давайте сделаем пример маршрута принимающего POST данные. Пришло время добавить новую книгу в нашу коллекцию!

Представим, что у нас есть форма для заполнения данными книги: её экшен содержит путь, который мы создали прямо сейчас, для приёма POST данных.

Теперь мы пойдём дальше: пришло время «защитить» наши маршруты! Сейчас выходит, что каждый кто передаст post данные по маршруту books сможет добавить данные в нашу базу данных. В серьёзных проектах это разумеется неопустимо. В этом случае нас выручат фильтры. Фильтры очень похожи на маршруты: у них есть имя и связанное с ним закрытие, которое выполняется, когда фильтр вызывается где либо.

Давайте разберёмся в чём разница? Фильтр можно легко вызвать до (или после) маршрута. Привожу пример:

Прежде всего, мы объявили фильтр с помощью метода filter() объекта $router. Синтаксис такой же, как и для маршрута. Мы даем ему имя и закрытие, которое будет выполнено в правильное время. Ок, но что такое «правильное время»? Мы решаем это так: просто добавили дополнительный параметр к методу post(). Этот параметр является массивом, в котором мы указываем ключ before с именем фильтра (logged_in). С этого момента, перед каждым вызовом маршрута books методом Post, будет также вызван фильтр logged_in (и выполнена его подстановка). Таким образом мы проверяем переменную $session user_id, чтобы узнать, вошёл ли пользователь в систему.

Существует также ключ after, который используется для запуска фильтра сразу после вызова маршрута. Вот вам пример:

Если вам нужно, вы также можете указать несколько фильтров одновременно. Все, что вам нужно сделать, это использовать массив строк, а не одну строку.

Представим себе реальный мир: допустим, у нас есть три почтовых маршрута, по одному для каждой сущности (автор, книга, категория). Было бы печально добавлять фильтр logged_in три раза. К счастью нас готов выручить групповой фильтр:

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

Давайте теперь представим, что наш проект растёт, в месте с ним будет расти и колличество кода. Соответственно структура роутера станет раздутой и неаккуратной. Как же решить эту проблему? Всё очень просто - нам на помощь приходят контроллеры!

Да: PHRoute - это не только роутер. Когда колличество кода превращает ваш код в нечитаемый лес, необходимо использовать новый уровень абстракции для более очевидной организации кодовой базы.

Прежде всего, давайте посмотрим, как устроена структура нашего контроллера. Взгляните на этот пример (мы можем поместить его прямо в файл routes.php):

Мы создали класс Author. В этом классе мы используем два метода: getIndex() и postAdd(). Затем с помощью метода controller() объекта $router мы связываем URL-адрес автора с классом Author.

Итак, если мы введем URL автора в нашем браузере, метод getIndex() будет вызываться автоматически. То же самое относится к методу postAdd(), который будет привязан к URL-адресу author / add (POST).

Эта функция автоматического распознавания имён довольно интересна, но на самом деле её недостаточно.

Контроллер находится на ранней стадии разработки и нуждается во многих улучшениях. Одним из них является возможность определять параметры для методов контроллера. Или, может быть, простой способ определить фильтры для некоторых методов контроллера (а не «все или ничего»).

Заключение. Ещё многое нуждается в доработке, особенно на стороне контроллеров. Как разработчик, я думаю, было бы здорово иметь базовый базовый класс контроллера для обработки всей грязной работы (с фильтрами, параметрами методов и т. д.). К сожалению документация по данному вопросу отсутствует.

С другой стороны, PHRoute поставляется с очень быстрым маршрутизатором. На странице GitHub проекта, вы можете увидеть некоторые статистические данные о сравнении с основным маршрутизатором Laravel: результаты потрясающие. В худшем случае PHRoute примерно в 40 (да, 40) раз быстрее.

Простой PHP маршрутизатор

Базовым элементом всех современных PHP фреймворков является роутер, по-другому маршрутизатор (Router), который отвечает за вызов контроллера, соответствующего запрошенному url-адресу. Маршрутизаторы, несмотря на все многообразие реализаций, выполняют одну и туже функцию. Далее мы с вами посмотрим на один из вариантов реализации, который похож на те, которые используются в таких популярных PHP фреймворках как Slim, Silex, Laravel и т.д.

class Router
// массив для хранения соответствия url => функция
private static $routes = array();

// запрещаем создание и копирование статического объекта
private function __construct() <>
private function __clone() <>


// данный метод проверяет запрошенный $url(адрес) на
// соответствие адресам, хранящимся в массиве $routes
public static function execute($url)
foreach (self::$routes as $pattern => $callback)
if (preg_match($pattern, $url, $params)) // сравнение идет через регулярное выражение
// соответствие найдено, поэтому удаляем первый элемент из массива $params
// который содержит всю найденную строку
array_shift($params);
return call_user_func_array($callback, array_values($params));
>
>
>
>

Используем следующим образом:


Если Вы не хотите пропустить новые материалы на сайте,
то Вы можете подписаться на обновления: Подписаться на обновления

Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы.

Порекомендуйте эту статью друзьям:

Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):

Она выглядит вот так:

Комментарии ( 1 ):

Расскажите пожалуйста по подробней как происходит вызов контролера действия и самое главное параметров в данном случае

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

Каркас приложения

В прошлой статье я уже рассказывал как это работает: всё передаётся в индексный файл (index.php).

Во фронт-контролере подключаются необходимые php-библиотеки, настраивается автозагрузка классов PSR-4 и определяются базовые константы (об этом я рассказывал в своём telegram-канале, поэтому не буду повторяться), например BASE_DIR , SITE_URL , SITE_PROTOCOL , SITE_HOST и т.д.

И после этого управление передаётся в точку входа application, то есть там, где непосредственно размещается ваше приложение. Например это будет файл app/bootstrap.php . Уже в этом файле запускается роутинг.

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

Запуск роутера

Поскольку app/bootstrap.php является точкой входа для приложения, то в нём и размещается роутер.

Роутер — это самый обычный php-класс, который может размещаться как угодно. Главное, чтобы он соответствовал PSR-4.

Первым делом создаём объект роутера. После этого считываем конфигурацию, где хранится массив правил для роутинга, скажем файл app/Config/routes.php .

Как хранить опции и конфигурации я также рассказывал недавно в своём telegram-канале.

Этот массив мы добавляем как правила роутинга. При этом мы можем прочитать и множество других файлов с правилами, для того, чтобы разделить их по модулям своего приложения, а не кидать всё в одну кучу. Правила роутинга суммируются и накапливаются в его объекте.

После этого можно задать специальное правило для случаев, если никакие другие не сработали. Это будет 404-страница, а точнее его класс и метод.

Дальше запускаем роутинг, который сам уже разруливает правила и подключает нужные классы. В некоторых php-фреймворках объект роутера даже именуется как $app , что показывает его основное назначение — запуск приложения.

В коде это выглядит так:

Правила роутинга

Конфигурационный файл возвращает массив, где каждое правило формируется тоже как массив. Просто покажу пример в котором это хорошо видно:

Три основных ключа любого правила:

Кроме этого иногда нужно передать в action какие-то данные прямо из правила. В этом случае используется ключ param , где указывается, то что будет востребовано в выполняемом классе. Но, опять же это особые ситуации.

Лично я предпочитаю для конфигурации использовать именно php-массив, но многие роутинги позволяют добавлять эти же самые правила через строчку. Она парсится и добавляется во внутреннее поле объекта уже как положено в массиве.

Метод setNotFound() описывает только action в том же формате.

Логика работы роутера

Запуск роутера происходит через метод run() . Первое, что там происходит — это сравнение текущего URL с паттернами правил. Например это может быть приватный метод match() , который возвращает результат сравнения.

Разбор URL в большинстве случаев это не что иное, как данные из $_SERVER . В некоторых случаях ещё используют parse_url() и parse_str() для корректного декодирования адресов. Но в большинстве случаем роутинг любого php-проекта работает именно на этих трёх компонентах. Всё, что сверх этого — бессмысленная абстракция и утяжеление кода.

Если же возникла ситуация, что ни один action не сработал, то выполняем указанный для 404-страницы метод. Хотя, если и он не найден, то просто ничего не делаем. Роутер может вернуть false , который уже анализируется в app/bootstrap.php .

Такой вариант позволяет не задавать 404-страницу для роутера.

Задача любого роутера — найти соответствие URL какому-то паттерну и запустить в случае успеха «что-то». Больше в его задачу ничего не входит, поскольку это уже лишнее.

А в форме что-то вроде такого:

В зависимости от сложности проекта, создаётся и роутинг. С моей точки зрения лучшим вариантом будет именно свой «велосипед», поскольку он на 100% покроет реальные задачи. Сторонние библиотеки всегда стараются сделать универсальными, а это приводит к тому, что 80% их возможностей просто не используются.

Подскажите алгоритм, этапы, как лучше сделать такую систему роутинга. Читал много про роутинг но, там все как-то просто реализовано было, без регистрации их. Пытался разобрать framework-и, но все как-то сложно сделано.

Подскажите, с чего начать, или посоветуйте похожую уже готовую систему.

P.S. Прошу не ругать за свой велосипед будущий. :)

Настройки роутеров записаны в следующем виде:

11 1 1 золотой знак 2 2 серебряных знака 8 8 бронзовых знаков 113 1 1 золотой знак 1 1 серебряный знак 9 9 бронзовых знаков Ну, в смысле не заносят их в массив, к примеру, а так просто добавляют к ним слово Controller.php. Типа что-то iantonov.me/page/pishem-sobstvennyj-mvc-frejmvork-na-php

Вам нужно использовать spl_autoload_register . Проще говоря, автозагрузка классов. Выглядеть это должно примерно, вот так: spl_autoload_register("autoloader");

А дальше, через route, делайте проверки..


Я писал свой велосипед и решил эту задачу по своему с использованием регулярных выражений в качестве правил: 1) Создаём правила и загоняем их в глобальную переменную:

2) Далее я создал класс Router который вызываю в index.php как new Router($_SERVER['SCRIPT_URL']); - естественно вверху я уже подключил контроллеры. А вот и сам код класса Router:

MUsers - класс пользователей - модель - в которой проверяеться логирование и роль юзера, и если роль не входит в правила roles указанные в массив $routing юзер получит отказ в выдаче страницы

render_html - функция генерации html через вьюшку:

Я описал её в файле хелпера, на данном этапе не было надобности создания класса ради 1 этой функции так я не пишу CMS или что вроде етого - всё под проект, никаких лишних кодов и обёрток. Подключать контроллеры тоже лучше в роутинге, ето пока не сделал, так как если их штук 50, а нужен лишь один то нет смысла подключать кучу 49 ненужных контроллеров.

Читайте также: