Удобная и быстрая система маршрутизации на PHP

от автора

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

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 Routing\Route;  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;     } } 
\Routing\Route.php

<?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;     }      }   

Для обработки взятых паттернов с маршрута я использую регулярные выражения.

Собственно \Routing\Router

<?php  namespace Routing;  use \Routing\Exception\Route as RouteException;  class Router {          protected $routes  = array(),               $address = null;          public function __construct($str){         $this->address = $str;     }          public function init(){         $routes = new \Config\Routes;                  $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’ы.

Буду рад услышать ваши идеи и критику.
Спасибо.

ссылка на оригинал статьи http://habrahabr.ru/post/175621/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *