From 3008c27d7ebedc79276b80d3c8a2134f31f40ba4 Mon Sep 17 00:00:00 2001 From: Andrey Kutejko Date: Thu, 4 Jul 2013 22:23:41 +0300 Subject: [PATCH] router expressions --- ipf/http/url.php | 59 +--------------- ipf/project.php | 1 + ipf/router.php | 146 ++++++++++++++++++++++++++++++--------- ipf/template/tag/url.php | 4 +- 4 files changed, 119 insertions(+), 91 deletions(-) diff --git a/ipf/http/url.php b/ipf/http/url.php index ea85026..5f6c843 100644 --- a/ipf/http/url.php +++ b/ipf/http/url.php @@ -33,65 +33,10 @@ class IPF_HTTP_URL return '/'; } - public static function urlForView($view, $params=array(), $by_name=false, - $get_params=array(), $encoded=true) + public static function urlForView($view, $params=array(), $get_params=array(), $encoded=true) { - $action = self::reverse($view, $params, $by_name); + $action = IPF_Project::getInstance()->router->reverse($view, $params); return self::generate($action, $get_params, $encoded); } - - public static function reverse($view, $params=array(), $by_name=false) - { - $regex = null; - - foreach (IPF::get('urls') as $url) { - $prefix = $url['prefix']; - foreach ($url['urls'] as $suburl){ - if ($suburl['func']==$view){ - $regex = $prefix.$suburl['regex']; - break; - } - } - if ($regex!==null) - break; - } - if ($regex === null) { - throw new IPF_Exception('Error, the view: '.$view.' has not been found.'); - } - $url = self::buildReverseUrl($regex, $params); - return IPF::get('app_base').$url; - } - - public static function buildReverseUrl($url_regex, $params=array()) - { - $url_regex = str_replace('\\.', '.', $url_regex); - $url_regex = str_replace('\\-', '-', $url_regex); - $url = $url_regex; - $groups = '#\(([^)]+)\)#'; - $matches = array(); - preg_match_all($groups, $url_regex, $matches); - reset($params); - if (count($matches[0]) && count($matches[0]) == count($params)) { - // Test the params against the pattern - foreach ($matches[0] as $pattern) { - $in = current($params); - if (0 === preg_match('#'.$pattern.'#', $in)) { - throw new IPF_Exception('Error, param: '.$in.' is not matching the pattern: '.$pattern); - } - next($params); - } - $func = create_function('$matches', - 'static $p = '.var_export($params, true).'; '. - '$a = current($p); '. - 'next($p); '. - 'return $a;'); - $url = preg_replace_callback($groups, $func, $url_regex); - } - $url = substr(substr($url, 2), 0, -2); - if (substr($url, -1) !== '$') { - return $url; - } - return substr($url, 0, -1); - } } diff --git a/ipf/project.php b/ipf/project.php index 7c575ea..aebcf8b 100644 --- a/ipf/project.php +++ b/ipf/project.php @@ -3,6 +3,7 @@ final class IPF_Project { private $apps = array(); + public $router = null; public $sqlProfiler = null; static private $instance = NULL; diff --git a/ipf/router.php b/ipf/router.php index b790630..6af909e 100644 --- a/ipf/router.php +++ b/ipf/router.php @@ -2,6 +2,28 @@ class IPF_Router { + private $routes = array(); + + public function __construct() + { + foreach (IPF::get('urls') as $url) { + $prefix = $url['prefix']; + foreach ($url['urls'] as $suburl) { + if (isset($suburl['regex'])) + $matcher = new IPF_Router_RegexMatch($prefix . $suburl['regex']); + elseif (isset($suburl['expr'])) + $matcher = RouteExpression::compile($prefix . $suburl['expr']); + else + throw new IPF_Exception('Unsupported route type'); + + $this->routes[] = array( + $matcher, + $suburl['func'], + ); + } + } + } + public static function response500($e) { error_log($e); @@ -11,7 +33,7 @@ class IPF_Router return new IPF_HTTP_Response_ServerError($e); } - public static function dispatch($query='') + public function dispatch($query='') { try{ $query = preg_replace('#^(/)+#', '/', '/'.$query); @@ -33,7 +55,7 @@ class IPF_Router } } if ($skip === false) { - $response = IPF_Router::match($req); + $response = $this->match($req); if (!empty($req->response_vary_on)) { $response->headers['Vary'] = $req->response_vary_on; } @@ -48,50 +70,110 @@ class IPF_Router } return array($req, $response); } catch (IPF_Exception $e) { - $response = IPF_Router::response500($e); + $response = self::response500($e); $response->render(); } } - public static function match($req) + public function match($req) { - foreach (IPF::get('urls') as $url) { - $prefix = $url['prefix']; - foreach ($url['urls'] as $suburl){ - $regex = $prefix.$suburl['regex']; - $match = array(); - if (preg_match($regex, $req->query, $match)) { - try{ - IPF::loadFunction($suburl['func']); - $r = $suburl['func']($req, $match); - if (!is_a($r,'IPF_HTTP_Response')){ - return IPF_Router::response500(new IPF_Exception('function '.$suburl['func'].'() must return IPF_HTTP_Response instance')); - } - return $r; - } catch (IPF_HTTP_Error404 $e) { - return new IPF_HTTP_Response_NotFound($req); - } catch (IPF_Exception $e) { - return IPF_Router::response500($e); - } + $func = null; + foreach ($this->routes as $route) { + $match = array(); + if ($route[0]->match($req->query, $match)) { + $func = $route[1]; + break; + } + } + + if ($func) { + try { + IPF::loadFunction($func); + $r = $func($req, $match); + if (!is_a($r, 'IPF_HTTP_Response')) { + return self::response500(new IPF_Exception('function '.$func.'() must return IPF_HTTP_Response instance')); } + return $r; + } catch (IPF_HTTP_Error404 $e) { + return new IPF_HTTP_Response_NotFound($req); + } catch (IPF_Exception $e) { + return self::response500($e); } } + return new IPF_HTTP_Response_NotFound($req); } - public static function describe() + public function describe() { - $routes = array(); - foreach (IPF::get('urls') as $url) { - $prefix = $url['prefix']; - foreach ($url['urls'] as $suburl) { - $routes[] = array( - $prefix . $suburl['regex'], - $suburl['func'], - ); + $result = array(); + foreach ($this->routes as $route) { + $result[] = array( + (string)$route[0], + $route[1], + ); + } + return $result; + } + + public function reverse($view, $params=array()) + { + foreach ($this->routes as $route) + if ($route[1] == $view) + return IPF::get('app_base') . $route[0]->reverse($params); + throw new IPF_Exception('Error, the view: '.$view.' has not been found.'); + } +} + +class IPF_Router_RegexMatch +{ + private $regex; + + public function __construct($regex) + { + $this->regex = $regex; + } + + public function __toString() + { + return $this->regex; + } + + public function match($query, &$matches) + { + return preg_match($this->regex, $query, $matches); + } + + public function reverse($params) + { + $url_regex = str_replace('\\.', '.', $this->regex); + $url_regex = str_replace('\\-', '-', $url_regex); + $url = $url_regex; + $groups = '#\(([^)]+)\)#'; + $matches = array(); + preg_match_all($groups, $url_regex, $matches); + reset($params); + if (count($matches[0]) && count($matches[0]) == count($params)) { + // Test the params against the pattern + foreach ($matches[0] as $pattern) { + $in = current($params); + if (0 === preg_match('#'.$pattern.'#', $in)) { + throw new IPF_Exception('Error, param: '.$in.' is not matching the pattern: '.$pattern); + } + next($params); } + $func = create_function('$matches', + 'static $p = '.var_export($params, true).'; '. + '$a = current($p); '. + 'next($p); '. + 'return $a;'); + $url = preg_replace_callback($groups, $func, $url_regex); + } + $url = substr(substr($url, 2), 0, -2); + if (substr($url, -1) !== '$') { + return $url; } - return $routes; + return substr($url, 0, -1); } } diff --git a/ipf/template/tag/url.php b/ipf/template/tag/url.php index 2a3bb55..ec3509e 100644 --- a/ipf/template/tag/url.php +++ b/ipf/template/tag/url.php @@ -2,9 +2,9 @@ class IPF_Template_Tag_Url extends IPF_Template_Tag { - function start($view, $params=array(), $by_name=false, $get_params=array()) + function start($view, $params=array(), $get_params=array()) { - echo IPF_HTTP_URL::urlForView($view, $params, $by_name, $get_params); + echo IPF_HTTP_URL::urlForView($view, $params, $get_params); } } -- 2.49.0