- 1
- 0
- 31 Май 2019
Всем доброго времени суток!
Хочу рассказать о принципах написания собственного простого роутинга на PHP сайте. Пример может быть не сильно эффективен, но сделан лишь для того, чтобы показать принцип.
Итак, роутинг происходит в несколько этапов:
Итак, файл config.php содержит конфигурации, в данном случае только путь к корню папки.
Оба файла в папке routes это страницы, которые отображаются в зависимости от запроса.
И файл index.php содержит класс маршрутизатора (роутера) и маршрута (роута):
Эта строка создаёт переменную с конфигурациями из файла config.php
Далее объявляется класс маршрута, он содержит 2 поля: путь и callback.
Первое содержит url путь, необходимый для вызова callback'а, а второй это собственно он и есть.
Помимо конструктора внутри класса объявляется безусловный (Call) и условный (CallIf) вызов callback'а, а так же деструктор.
Call возвращает ответ callback'а с аргументами или без;
CallIf поступает так же, если $path соответствует пути маршрута (за исключением аргументов), иначе возвращает false;
Проверка на содержание url-запросом get-аргументов
Если пути без аргументов не соответствуют, то вернуть false
Если аргументы есть, то сохранить позицию их начала и проверить на соответствие пути без аргументов, при несоответствии вернуть false.
Получить аргументы в виде словаря
Вернуть результат выполнения безусловного вызова с аргументами
Далее, в файле описывается класс Router для работы с маршрутами
Поле $routes содержит массив экземпляров класса Route для различных запросов
Функция Route добавляет в список новый Route, если Route с таким же маршрутом не существует и возвращает true, иначе возвращает false.
Функция Execute запускает роутинг, а именно считывает get-аргумент page, содержащий url путь, если он существует и выполняет нужный маршрут, иначе открывает страницу index.php
Функция Call возвращает результат вызова Call или CallIf класса Route, найденного в списке по запрошенному url
Функция GetArgs возвращает get-артибуты в виде словаря
Далее идёт основная часть кода
Создание нового экземпляра Router'а
Добавляем маршруты в список
Запускаем маршрутизацию
Также, для корректной работы необходимо изменить или создать файл .htaccess
В котором необходимо прописать переадресацию со всех запросов на этот скрипт
Это должно выглядеть как-то так
Итак, теперь htaccess пересылает запросы вида site.ru/site в site.ru?page=site
index.php подключает конфигурации, настраивает роутинг и запускает его, Router ищет такие же маршруты, если нашёл вызывает Route
Подключается нужная страница с аргументами
Хочу рассказать о принципах написания собственного простого роутинга на PHP сайте. Пример может быть не сильно эффективен, но сделан лишь для того, чтобы показать принцип.
Итак, роутинг происходит в несколько этапов:
- Пересылка запрашиваемого URL в файл-роутер
- Разбиение URL на части
- Сравнение путей роутинга с базовой частью URL
- Обработка остальных частей внутри блока с нужным путём (с п.2)
- Выдача требуемой страницы
- /config/config.php
- /routes/index.php
- /routes/login.php
- /index.php
PHP:
<?php
return (object) [
'ROOT' => 'здесь путь к рут директории',
];
PHP:
<?php
$CONFIG = require_once('config/config.php');
class Route
{
public string $path;
public var $callback;
public function Route(string $path, $callback)
{
$this->$path = $path;
$this->$callback = $callback;
}
public function Call(array $arg_v = [])
{
if(empty($arg_v))
return $callback();
else
return $callback($arg_v);
}
public function CallIf(string $path)
{
$out = [];
if(!strpos($path, '?'))
{
if($this->$path != $path)
return false;
}else{
$arg_pos = strpos($path, '?');
if($this->path != substr($path, 0, $arg_pos))
return false;
$out = Router::GetArgs($path) || [];
}
return $this->Call($out);
}
public function destroy()
{
unset($this);
}
}
class Router
{
public $routes = [];
public function Route(string $path, $callback)
{
if(isset($routes[$path]))
return false;
$routes[$path] = new Route($path, $callback);
return true;
}
public function Execute(){
if(!isset($_GET['page']))
exit($this->Call('/'));
$page = parse_url(strip_tags($_GET['page']));
exit($this->Call($page['path']));
}
public function Call(string $path)
{
if(isset($routes[$path]))
return $routes[$path]->Call();
foreach ($routes as $route) {
if($ret = $route->CallIf($path))
return $ret;
}
return require_once $CONFIG->ROOT.'/routes/index.php';
}
public static GetArgs(string $path){
$result = [];
preg_match_all(
'/[?&]([^=]+)=([^&]+)/',
substr($path, strpos('?', $path)),
$result
);
$result['out'] = [];
foreach($result[1] as $key => $arg_n)
$result['out'][$arg_n] = $result[2][$key];
return (object) $result['out'];
}
public function destroy(){
foreach ($routes as $route)
$route->destroy();
unset($this);
}
}
$router = new Router();
$router->Route('/', function(){
return require_once($CONFIG->ROOT.'/routes/index.php');
});
$router->Route('/login', function($args = []){
return require_once($CONFIG->ROOT.'/routes/login.php');
});
$router->Execute();
?>
HTML:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Главная страница</title>
</head>
<body>
<!-- ТЕЛО СТРАНИЦЫ -->
</body>
</html>
HTML:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Вход на сайт</title>
</head>
<body>
<!-- ТЕЛО СТРАНИЦЫ -->
</body>
</html>
Оба файла в папке routes это страницы, которые отображаются в зависимости от запроса.
И файл index.php содержит класс маршрутизатора (роутера) и маршрута (роута):
PHP:
$CONFIG = require_once('config/config.php');
Далее объявляется класс маршрута, он содержит 2 поля: путь и callback.
Первое содержит url путь, необходимый для вызова callback'а, а второй это собственно он и есть.
Помимо конструктора внутри класса объявляется безусловный (Call) и условный (CallIf) вызов callback'а, а так же деструктор.
Call возвращает ответ callback'а с аргументами или без;
CallIf поступает так же, если $path соответствует пути маршрута (за исключением аргументов), иначе возвращает false;
PHP:
if(!strpos($path, '?'))
PHP:
if($this->$path != $path)
return false;
PHP:
$arg_pos = strpos($path, '?');
if($this->path != substr($path, 0, $arg_pos))
return false;
PHP:
$out = Router::GetArgs($path) || [];
PHP:
return $this->Call($out);
Далее, в файле описывается класс Router для работы с маршрутами
Поле $routes содержит массив экземпляров класса Route для различных запросов
Функция Route добавляет в список новый Route, если Route с таким же маршрутом не существует и возвращает true, иначе возвращает false.
Функция Execute запускает роутинг, а именно считывает get-аргумент page, содержащий url путь, если он существует и выполняет нужный маршрут, иначе открывает страницу index.php
Функция Call возвращает результат вызова Call или CallIf класса Route, найденного в списке по запрошенному url
Функция GetArgs возвращает get-артибуты в виде словаря
Далее идёт основная часть кода
PHP:
$router = new Router();
PHP:
$router->Route('/', function(){
return require_once($CONFIG->ROOT.'/routes/index.php');
});
$router->Route('/login', function($args = []){
return require_once($CONFIG->ROOT.'/routes/login.php');
});
PHP:
$router->Execute();
Также, для корректной работы необходимо изменить или создать файл .htaccess
В котором необходимо прописать переадресацию со всех запросов на этот скрипт
Код:
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/some-directory/
RewriteCond %{DOCUMENT_ROOT}/some-directory%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}/some-directory%{REQUEST_URI} -d
RewriteRule ^(.*)$ /index.php?page=$1 [L]
Итак, теперь htaccess пересылает запросы вида site.ru/site в site.ru?page=site
index.php подключает конфигурации, настраивает роутинг и запускает его, Router ищет такие же маршруты, если нашёл вызывает Route
Подключается нужная страница с аргументами