几年前实现了一个简单的正向路由,那时候不会写反向路由解析,最近通读qee v3的代码,因为其中已经扣除了路由解析这个功能,故自己结合之前的经验,发现写个简单的正反向路由解析还是蛮简单的,见代码:
<?php /* * 静态页配置文件 * * key 为 pattern * 值 为 配置参数 * * 越上面的优先级越高 */ return array( array( 'pattern' => 'core-{id}-{name}.html', 'action' => 'core/show', 'config' => array( 'id' => '[0-9]+', 'name' => '[a-z][a-z_0-9]+' ), 'default' => array( 'id' => 2, 'name' => 'sfken' ), ), array( 'pattern' => 'admin.html', 'action' => 'admin/index', ), );
上面是配置文件
<?php class Tool_PageStatic { private $_obj_id; private $_cache; private $_accessor; private $_config = array(); private $_result = array(); function __construct(array $config) { if ( empty($config['obj_id']) ) { # 对象标识 用于缓存配置文件等用,标识系统中的 ps 对象的实例标识 throw BaseError::invalid_parameters_error('key "obj_id" not set'); } $this->_obj_id = $config['obj_id']; $this->_accessor = val( $config , 'accessor' , 'page' ); $this->_cache = val( $config , 'cache' , NULL ); $cfopt = val( $config , 'config' , null ); if ( !empty($cfopt) ) { if ( is_array($cfopt) ) { $this->loadConfig($cfopt); } if ( is_file($cfopt) ) { # 如果是文件是否需要从缓存中进行读取,还是每次都要读取此处的配置文件 $cfopt = include($cfopt); $this->loadConfig( (array) $cfopt); } } $cfopt = null; $this->default = array( 'action' => Config::get('app.default_action','index'), 'nocache' => TRUE, 'params' => array(), ); } private function loadConfig( array $config ) { foreach ( $config as $opts ) { # 对于 未设置 action & pattern 的配置项 直接忽略 if ( empty($opts['action']) || empty($opts['pattern']) ) continue; $opts['src_pattern'] = $opts['pattern']; if ( !empty($opts['config']) ) { foreach($opts['config'] AS $conf_key => $conf_val) { $opts['pattern'] = str_replace('{'.$conf_key.'}', '('.$conf_val.')', $opts['pattern']); } } else { $opts['config'] = array(); } if ( empty($opts['default']) ) $opts['default'] = array(); $this->_config[strtolower($opts['action'])] = $opts; } # 解析完成后是否可以存入缓存 } function getAccessor() { return $this->_accessor; } /** * 解析 string 返回对应的action以及相应的参数 * * @param string $string * * @return array */ function parsing($string) { // dump($this->_config); if ( empty($string) || empty($this->_config) ) return $this->default; if ( empty($this->_result[$string]) ) { $d = $this->default; foreach($this->_config AS $opts) { if (preg_match('#^'.$opts['pattern'].'/?$#i', $string, $match_result)) { // dump($match_result); # offset 为0 是 原字符串 $offset = 1; foreach($opts['config'] AS $conf_key => $conf_val) { if(isset($match_result[$offset])) { $d['params'][$conf_key] = $match_result[$offset++]; } } $d['nocache'] = val($opts,'nocache',$d['nocache']); $d['action'] = $opts['action']; break; } } $this->_result[$string] = $d; } return $this->_result[$string]; } /** * 生成静态的 url 地址 * * @param string $action_name * @param array $params */ function psurl( $action_name , array $params = null ) { $opts = val( $this->_config , strtolower($action_name) , null ); if ( !empty($opts) ) { $params = (array) $params; # 使用源 $opts['src_pattern'] $url = $opts['src_pattern']; dump($url); foreach($opts['default'] AS $d_key => $d_val) { if ( !empty($params[$d_key]) ) { $d_val = $params[$d_key]; unset($params[$d_key]); } // $url = str_replace(':'.$d_key, $d_val, $url); $url = str_replace('{'.$d_key.'}', $d_val, $url); } return $url . '?' . http_build_query($params); } # 如果没有找到就生成原始的url return url($action_name , $params); } }
上面是真正的实现代码
下面是调用代码
<?php /** * 站点静态化操作 * * /static/page/article-php-json_encode.html * * */ class Action_Static extends BaseAction { protected function _validate_input() { $engine = $this->app->tool('pagestatic'); $accessor = $engine->getAccessor(); $this->pid = $_GET[$accessor];// page 标识 if ( !empty($this->pid) && strlen($this->pid) < 230 ) { // 为什么不在此处直接读缓存? 目的是实现未在配置文件中定义的过期缓存全部设为失效 $this->pparams = (array) $engine->parsing($this->pid);// page 参数 if ( !empty($this->pparams) ) { return true; } } return false; } /** * 请求前对数据进行验证失败时调用此方法 */ protected function _on_validate_input_failed() { // 记录错误日志(将 pid 记录到错误日志中) // 跳转到首页 redirect( url(Config::get('app.default_action','index')) ); } function execute() { $cached = val($this->pparams,'nocache',FALSE); dump($this->pparams,$this->pid); dump( $this->app->tool('pagestatic')->psurl('core/show',array( 'id' => '449211678', 'name' => 'kenxu', 'age' => '29', // 'blog' => 'http://vb2005xu.iteye.com', )) ); } }
输出结果如下:
http://sfken.xu/static/page/admin.html 写道
admin.html :
Array
(
[action] => admin/index
[nocache] => 1
[params] => Array
(
)
)
core-{id}-{name}.html
core-449211678-kenxu.html?age=29
Array
(
[action] => admin/index
[nocache] => 1
[params] => Array
(
)
)
core-{id}-{name}.html
core-449211678-kenxu.html?age=29
http://sfken.xu/static/page/core-12306-tianchao.html 写道
core-12306-tianchao.html :
Array
(
[action] => core/show
[nocache] => 1
[params] => Array
(
[id] => 12306
[name] => tianchao
)
)
core-{id}-{name}.html
core-449211678-kenxu.html?age=29
Array
(
[action] => core/show
[nocache] => 1
[params] => Array
(
[id] => 12306
[name] => tianchao
)
)
core-{id}-{name}.html
core-449211678-kenxu.html?age=29
在我当前的设计中 此路由功能只能算是一个工具类,用于实现 伪静态/或者页缓存用的,功能上只支持指定action的操作,如果诸位有需要,此处就算我 抛砖引玉了....
代码很少,也就不一一解释了