Подходя к вопросу статического роутинга мы сталкиваемся с дилемой скорость или интуитивность.
Объектом стал сам роутер, а средвом маршруты.
Возможные варианты хранения:
YAML
+ Интуитивый
— Требуется кеширования (для повышения производительности)
— Требуется библиотека для парсинга (минус, поскольку в случаях лицензирования могут возникнуть трудности)
ini
+ Быстрый парсинг php
+ Отсутствие потребности в библиотеках
— Желательно все-же кешировать
— Далеко не интуитивный
PHP-file
Вот мы и подошли к самому важному. Но вопрос все же остается. Как хранить его?
$routes = array();
$routes['routeName'] = array(
'pattern' => '/my/home/page',
'controller' => 'homepage',
'action' => 'show'
);
Вариант? Но ведь не интуитивно. И не удобно считывать будет с такого файла, и многомерные массивы.
Да и вдруг кто-то случайно напишет не controller, a controIler. (Маленькая l и большая I соответственно). Ну случайно. Допустим. Ну ведь не будет работать.
Или вариант, который я встречал не реже:
$router = new Router();
$route = new Route('RouteName', array('controller' => 'mycontroller', 'action' => 'myaction'));
$router->addRoute($route);
Находим файл router.php и берем оттуда $router. Удобно? Я сомневаюсь.
Решение
Моя идея заключается в создании класса конфигурации в стиле PHP5.
Пример:
<?php
namespace Сonfig;
use RoutingRoute;
class Routes
{
function welcome() { //название функции и есть названием маршрута
$a = new Route;
$a->setPattern('/')
->setController('hello')
->setAction('welcome')
->setFormat('html');
return $a;
}
function main() {
$a = new Route;
$a->setPattern('/home/{user}')
->setController('controller')
->setAction('list')
->setFormat('html');
return $a;
}
}
<?php
namespace Routing;
class Route
{
private $pattern = null,
$controller = null,
$action = null,
$parameters = array(),
$regexp = null,
$format = 'html';
public function setPattern($pattern) {
$this->regexp = str_replace('/', '/', $pattern);
$this->regexp = preg_replace('/{w+}/', '(w+)', $this->regexp);
$this->regexp = "/^{$this->regexp}$/i";
$this->pattern = $pattern;
return $this;
}
public function getPattern() {
return $this->pattern;
}
public function setController($controller) {
$this->controller = $controller;
return $this;
}
public function getController() {
return $this->controller;
}
public function setAction($action) {
$this->action = $action;
return $this;
}
public function getAction() {
return $this->action;
}
public function addParameter($name, $value) {
$this->parameters[$name] = $value;
return $this;
}
public function getParameters() {
return $this->parameters;
}
public function getRegexp() {
return $this->regexp;
}
public function setFormat($format) {
$this->format = $format;
return $this;
}
public function getFormat() {
return $this->format;
}
public function isValid() {
if(is_null($this->getPattern())
||is_null($this->getController())
||is_null($this->getAction())
) return false;
return true;
}
}
Для обработки взятых паттернов с маршрута я использую регулярные выражения.
<?php
namespace Routing;
use RoutingExceptionRoute as RouteException;
class Router
{
protected $routes = array(),
$address = null;
public function __construct($str){
$this->address = $str;
}
public function init(){
$routes = new ConfigRoutes;
$resRoute = null;
foreach(get_class_methods($routes) as $route) {
if(!is_callable(array($routes, $route))) {
throw new RouteException('All routes must be public.');
}
$route = call_user_func(array($routes, "$route"));
if(!$route->isValid()) {
throw new RouteException('Invalid route found.');
}
$params = array();
if(preg_match($route->getRegexp(), $this->address, $params)) {
if(count($params) == 0) {
$resRoute = $route;
break;
}
$input = array();
preg_match('/{(w+)}/', $route->getPattern(), $input);
for($i=1; $i< count($input); $i++) {
$route->addParameter($input[$i], $params[$i]);
}
$resRoute = $route;
break;
}
}
if(is_null($resRoute)) {
throw new RouteException('404', 404);
}
return $resRoute;
}
}
В результате алгоритм работы получается такой:
- Получаем текущую строку адресу
- Ищем шаблон, который подходит под данный адрес
- В случае если шаблон содержит параметры, добавляем их в маршрут
- Возвращаем маршрут
Сейчас меня не устраивает только парсинг паттернов в regexp'ы.
Буду рад услышать ваши идеи и критику.
Спасибо.
Автор: spein