/src/library/route/PwRoute.php
PHP | 643 lines | 434 code | 35 blank | 174 comment | 120 complexity | ef050c8d3220758903b8191860f1fa7a MD5 | raw file
- <?php
- Wind::import('LIB:route.AbstractPwRoute');
- /**
- * 前台路由
- *
- * @author Shi Long <long.shi@alibaba-inc.com>
- * @copyright ©2003-2103 phpwind.com
- * @license http://www.windframework.com
- * @version $Id: PwRoute.php 25816 2013-03-25 06:10:30Z long.shi $
- * @package library
- */
- class PwRoute extends AbstractPwRoute {
-
- private $entrance = 'CONF:entrance';
- /**
- * 特殊规则的rewrite
- */
- private $rewrite_special = false;
-
- /**
- * 普通规则的rewrite
- */
- private $rewrite_common = false;
-
- /**
- * 符合特殊情况时,url串省略mca参数
- */
- private $omit_mca = false;
- public $dynamicDomain = array();
- public $dynamic = array();
- public $dynamicHost = '';
- /**
- * 符合特殊二级域名时,后面皆可省略的情况
- */
- private $onlydomain = false;
- protected $params = array('a' => 3, 'c' => 2, 'm' => 1);
- protected $_init = false;
- protected $base = null;
- protected $origialBase = null;
- protected $scheme = 'http://';
-
- /*
- * (non-PHPdoc) @see WindRewriteRoute::match()
- */
- public function match($request) {
- $this->init(false, $request);
- $host = $request->getHostInfo();
- $args = $this->_matchDomain($host);
- $_args = $this->_matchScript($request->getScript(), $request->getScriptUrl());
- $path = trim(str_replace($request->getBaseUrl(true), '', $host . $request->getRequestUri()), '/');
- $rawPath = $this->_getRawPath($path);
-
- // 首页设置
- if (empty($rawPath) && empty($args) && empty($_args)) {
- return Wekit::C()->site->get('homeRouter', array());
- }
- $_args = $this->_matchPath($path) + $_args;
- if (empty($_args)) {
- $this->_filterIllegal($rawPath);
- }
- return $_args + $args;
- }
-
- /**
- * 解析url - 公开方法
- *
- * 返回false 表示外链
- * 返回array 为解析好的参数
- *
- * @param string $url
- * @return array
- */
- public function matchUrl($url) {
- Wind::import('SRV:domain.srv.helper.PwDomainHelper');
- $r = PwDomainHelper::parse_url($url);
- list($host, $isSecure, $script, $path, $scriptUrl) = $r;
- if ($host && !PwDomainHelper::isMyBrother($host,
- Wind::getApp()->getRequest()->getHostInfo())) {
- return false;
- }
- $this->init();
-
- $args = $this->_matchDomain($host);
- $_args = $this->_matchScript($script, $scriptUrl);
- return array_merge($this->_getDefault(), $args, $_args, $this->_matchPath($path, true));
- }
-
- /*
- * (non-PHPdoc) @see WindRewriteRoute::build()
- */
- public function build($router, $action, $args = array()) {
- $this->init(true);
- list($_m, $_c, $_a, $args) = $this->_resolveMca($router, $action, $args);
- $domain = $this->_buildDomain($_m, $_c, $_a, $args);
- if ($this->onlydomain) return $domain;
- $url = $this->_buildUrl($_m, $_c, $_a, $router, $args);
- if (3 < count($this->dynamicDomain)) {
- $this->dynamicDomain[] = $url;
- }
- return $domain . $url;
- }
-
- /**
- * 检查url绝对路径
- *
- * @param string $url
- * @return string
- */
- public function checkUrl($url) {
- if (false === strpos($url, '://')) {
- $appType = $this->_getAppType();
- $base = Wekit::url()->base;
- isset($appType['default']) && $base = $appType['default'] . $this->base;
- $url = $base . '/' . trim($url, '/');
- }
- return $url;
- }
- /**
- * 解析url - 普通伪静态
- *
- * @param WindHttpRequest $request
- * @param string $path
- * @return array null
- */
- private function _matchCommon($path) {
- $pattern = '/^(\w+)(-\w+)(-\w+)(.*)$/i';
- if (!preg_match($pattern, $path, $matches)) return array();
- $params = array();
- foreach ($this->params as $k => $v) {
- $value = isset($matches[$v]) ? $matches[$v] : '';
- $params[$k] = trim($value, '-/');
- }
- return array_merge($params, WindUrlHelper::urlToArgs(trim($matches[4], '?'), true));
- }
- /**
- * 解析url - 二级域名
- *
- * @param WindHttpRequest $request
- * @param string $path
- * @return array null
- */
- private function _matchDomain($host) {
- /* 解析二级域名 */
- if (empty($host)) return array();
- $appType = $this->_getAppType();
- if (isset($appType['default']) && $host == $appType['default']) return array();
- $space_root = Wekit::C()->site->get('domain.space.root', '');
- if ($space_root) {
- $rawHost = str_replace($this->scheme, '', $host);
- if ($pos = strpos($rawHost, $space_root)) {
- $uid = Wekit::load('domain.PwSpaceDomain')->getUidByDomain(substr($rawHost, 0, $pos - 1));
- if ($uid) {
- return array('m' => 'space', 'c' => 'index', 'a' => 'run', 'uid' => $uid);
- }
- }
- }
- $domain = Wekit::C()->site->get('domain', array());
- if (isset($domain[$host])) {
- list($a, $c, $m, $args) = WindUrlHelper::resolveAction($domain[$host]);
- return array_merge($args, array('m' => $m, 'c' => $c, 'a' => $a));
- }
- return array();
- }
- /**
- * 解析脚本
- *
- * @param WindHttpRequest $request
- * @return array
- */
- private function _matchScript($script, $scriptUrl) {
- if (empty($script) || $script == 'index.php') {
- return array();
- }
- $multi = $this->_getMulti();
- if (isset($multi[$script])) {
- $this->base = rtrim(str_replace($script, '', $scriptUrl), '\\/.');
- $route = explode('/', $multi[$script]);
- return array('m' => $route[0], 'c' => $route[1], 'a' => $route[2]);
- } else {
- foreach ($multi as $k => $v) {
- if (false !== ($pos = strpos($scriptUrl, $k))) {
- $this->base = rtrim(substr($scriptUrl, 0, $pos), '\\/.');
- $route = explode('/', $v);
- return array('m' => $route[0], 'c' => $route[1]) + ($route[2] == '*' ? array() : array('a' => $route[2]));
- }
- }
- }
- return array();
- }
- /**
- * 生成url - 普通伪静态
- *
- * @param array $route
- * @param
- * array WindRouter
- * @param array $args
- * @return string
- */
- private function _buildCommon($router, $route, $args) {
- $reverse = '/%s-%s-%s';
- $_args = array();
-
- $flag = 0;
- $methods = array(
- 'a' => 'getDefaultAction',
- 'c' => 'getDefaultController',
- 'm' => 'getDefaultModule');
- $flags = array('a' => 0, 'c' => 1, 'm' => 3);
- $consts = array('a' => 1, 'c' => 3, 'm' => 7);
- foreach ($this->params as $k => $v) {
- if ($route[$k] === $router->$methods[$k]() && $flag === $flags[$k]) $flag = $consts[$k];
- $_args[$v] = $route[$k];
- unset($args[$k]);
- }
- if ($flag == 7 && empty($args)) return '';
- $_args[0] = $reverse;
- ksort($_args);
- $url = call_user_func_array("sprintf", $_args);
- return $url . ($args ? '?' . WindUrlHelper::argsToUrl($args) : '');
- }
- /**
- * 生成url - 二级域名
- *
- * @param array $route
- * @param array $args
- * @return string
- */
- private function _buildDomain($_m, $_c, $_a, $args) {
- /* 二级域名 */
- $key = "$_m/$_c/$_a";
- list($domain, $type) = $this->_getDomainByType($key);
- if (!empty($domain)) {
- $domainKey = $this->_getDomainKey();
- if (isset($domainKey[$key])) {
- $id = $domainKey[$key];
- if (!isset($args[$id])) {
- return '';
- } else {
- if (isset($domain[$args[$id]])) {
- $this->omit_mca = true;
- 1 == count($args) && $this->onlydomain = true;
- return $domain[$args[$id]] . $this->base;
- } elseif ($type == 'forum') {
- $this->dynamicDomain[] = '<?php $__route_domain=' . var_export($domain,
- true) . ';';
- if (1 == count($args)) {
- $this->dynamicDomain[] = 'if($__route_domain[' . $args[$id] . ']){ echo $__route_domain[' . $args[$id] . '].\'' . $this->base . '\';}else{ ?>';
- } else {
- $this->dynamicDomain[] = 'if($__route_domain[' . $args[$id] . ']){ echo $__route_domain[' . $args[$id] . '].\'' . $this->base . '\';?>';
- $this->dynamicDomain[] = '<?php }else{ ?>';
- }
- $this->dynamicDomain[] = '<?php } ?>';
- }
- }
- }
- }
- return $this->_getModuleDomain($_m);
- }
- /**
- * 生成url串
- *
- * @param string $_m
- * @param string $_c
- * @param string $_a
- * @param WindRouter $router
- * @param array $args
- * @return string
- */
- private function _buildUrl($_m, $_c, $_a, $router, $args) {
- if ($this->rewrite_special) {
- $rule = $this->_getRule();
- if (!empty($rule)) {
- $_args = $args;
- foreach ($rule as $v) {
- if ($v['route'] == "$_m/$_c/$_a") {
- $format = array();
- preg_match_all('/\{(\w+)\}/', $v['format'], $matches);
- //if (empty($matches[1])) continue;
- $is_fname = strpos($v['format'], '{fname}') !== false;
- if (1 === count($matches[1]) && !$is_fname) {
- if (!isset($_args[$matches[1][0]])) continue;
- }
- if ($is_fname) {
- if ($this->dynamicDomain) continue;
- if (!isset($_args['fid'])) continue;
- $domain = $this->_getDomain('id', 'domain');
- $this->dynamic[] = '<?php $__route_rewrite=' . var_export($domain, true) . ';';
- $this->dynamic[] = $_args['fid'] ? '($__route_rewrite[' . $_args['fid'] . '] ? $__route_rewrite[' . $_args['fid'] . '] : \'fname\')' : '\'fname\'';
- if (is_numeric($_args['fid'])) {
- if (!$domain[$_args['fid']]) continue;
- $format['{fname}'] = $domain[$_args['fid']];
- }
- }
- if ($pos = strpos($v['format'], '{page}')) {
- if (!isset($_args['page'])) {
- $v['format'] = str_replace($v['format'][$pos - 1] . '{page}', '', $v['format']);
- }
- }
- foreach ($matches[1] as $code) {
- if ($code != 'fname') {
- $format['{' . $code . '}'] = isset($_args[$code]) ? rawurlencode($_args[$code]) : '';
- unset($_args[$code]);
- }
- }
- if ('forum' == $this->_getType("$_m/$_c/$_a")) unset($_args['fid']);
- return '/' . trim(strtr($v['format'], $format), '/-.*') . ($_args ? '?' . WindUrlHelper::argsToUrl(
- $_args) : '');
- }
- }
- }
- }
- if ($this->rewrite_common) {
- return $this->_buildCommon($router, array('m' => $_m, 'c' => $_c, 'a' => $_a), $args);
- } else {
- /* 非rewrite时获取脚本文件 */
- $script = $this->_buildScript($_m, $_c, $_a, $args);
- $url = '';
- if ($this->omit_mca)
- $url = $args ? $script . '?' . WindUrlHelper::argsToUrl($args) : $script;
- else {
- $_args = array();
- if ($_m !== $router->getDefaultModule()) $_args['m'] = $_m;
- if ($_c !== $router->getDefaultController()) $_args['c'] = $_c;
- if ($_a !== $router->getDefaultAction()) $_args['a'] = $_a;
- $args = array_merge($_args, $args);
- $url = $args ? $script . '?' . WindUrlHelper::argsToUrl($args) : '';
- }
- return $url;
- }
- }
- /**
- * 解析url串
- *
- * @param WindHttpRequest $request
- * @param string $path
- * @return array
- */
- private function _matchPath($path, $rawDecode = false) {
- $path = trim($path, '/');
- if (empty($path)) return array();
- if ($this->rewrite_special) {
- /* 解析特殊伪静态 */
- $rule = $this->_getRule();
- if (!empty($rule)) {
- if (false !== strpos($path, '?')) {
- list($rewritePath, $queryPath) = explode('?', $path . '?', 2);
- } else {
- list($rewritePath, $queryPath) = explode('&', $path . '&', 2);
- }
- foreach ($rule as $k => $v) {
- if ($k == 'default') continue;
- $rewritePath = rawurldecode($rewritePath);
- if (preg_match($v['pattern'], $rewritePath, $matches)) {
- $matches = array_diff_key($matches, range(0, intval(count($matches) / 2)));
- $args = WindUrlHelper::urlToArgs(trim($queryPath, '?'), true);
- if ($k == 'thread' || $k == 'cate') {
- if (!isset($matches['fid']) && isset($matches['fname'])) {
- $domain = $this->_getDomain('domain', 'domain_key');
- if (isset($domain[$matches['fname']])) {
- list($_a, $_c, $_m, $_args) = WindUrlHelper::resolveAction(
- $domain[$matches['fname']]);
- return array_merge($matches,
- array('m' => $_m, 'c' => $_c, 'a' => $_a), $_args + $args);
- }
- } elseif (isset($matches['fid'])) {
- $forum = Wekit::load('forum.PwForum')->getForum($matches['fid']);
- $action = array(
- 'category' => array('m' => 'bbs', 'c' => 'cate', 'a' => 'run'),
- 'forum' => array('m' => 'bbs', 'c' => 'thread', 'a' => 'run'),
- 'sub' => array('m' => 'bbs', 'c' => 'thread', 'a' => 'run'),
- 'sub2' => array('m' => 'bbs', 'c' => 'thread', 'a' => 'run'),
- );
- $forum_type = isset($forum['type']) ? $forum['type'] : 'forum';
- return array_merge($matches,
- $action[$forum_type], $args);
- }
- }
- $route = explode('/', $v['route']);
- return array_merge($matches,
- array('m' => $route[0], 'c' => $route[1], 'a' => $route[2]), $args);
- }
- }
- }
- }
-
- /* 解析普通伪静态 */
- if ($this->rewrite_common && strpos($path, '.php') === false) return $this->_matchCommon(
- $path);
-
- $r = (false !== strpos($path, '?')) ? WindUrlHelper::urlToArgs($path) : array();
- if ($rawDecode) return $r;
- $return = array();
- if (isset($r['m']) || isset($r['c']) || isset($r['a'])) {
- $return['m'] = isset($r['m']) ? $r['m'] : $this->default_m;
- $return['c'] = isset($r['c']) ? $r['c'] : 'index';
- $return['a'] = isset($r['a']) ? $r['a'] : 'run';
- }
- return $return;
- }
- /**
- * 万事俱备
- * @param $request WindHttpRequest
- */
- protected function init($build = false, $request = null) {
- if (!$this->_init) {
- $router = Wind::getComponent('router');
- $this->default_m || $this->default_m = Wind::getApp()->getConfig('default-module', '', $router->getDefaultModule());
- if ($this->getConfig('default')) {
- $router->setDefaultModule($this->default_m);
- }
- $rule = $this->_getRule();
- if (isset($rule['default'])) {
- $this->rewrite_common = true;
- unset($rule['default']);
- }
- $rule && $this->rewrite_special = true;
- if ($request) {
- $this->origialBase = $this->base = $request->getBaseUrl();
- $this->scheme = $request->getScheme() . '://';
- }
- $this->_init = true;
- }
- if ($build) {
- $this->omit_mca = $this->onlydomain = false;
- $this->dynamicDomain = $this->dynamic = array();
- $this->dynamicHost = '';
- $this->base === null && $this->base = Wind::getApp()->getRequest()->getBaseUrl();
- }
- }
- /**
- * 初始化配置
- *
- * @return array
- */
- private function _getRule() {
- static $conf = null;
- if ($conf === null) {
- $conf = Wekit::C()->site->get('rewrite', array());
- }
- return $conf;
- }
- /**
- * 初始化配置
- *
- * @return array
- */
- private function _getDomainKey() {
- return array(
- 'bbs/cate/run' => 'fid',
- 'bbs/thread/run' => 'fid',
- 'bbs/read/run' => 'fid',
- 'special/index/run' => 'id');
- }
-
- private function _getType($type) {
- $all = array(
- 'bbs/cate/run' => 'forum',
- 'bbs/thread/run' => 'forum',
- 'bbs/read/run' => 'forum',
- 'special/index/run' => 'special');
- return isset($all[$type]) ? $all[$type] : '';
- }
- /**
- * 根据类型获取域名
- *
- * @return array
- */
- private function _getDomainByType($type) {
- $domain_type = $this->_getType($type);
- if (!$domain_type) return array(array(), '');
- static $domain = array();
- if (!isset($domain[$domain_type])) {
- $temp = array();
- if (Wekit::C('site', "domain.{$domain_type}.isopen")) {
- $temp = $this->_getDomain('id', 'domain', $domain_type, true);
- }
- $domain[$domain_type] = $temp;
- }
- return array($domain[$domain_type], $domain_type);
- }
-
- /**
- * 获取域名
- *
- * @param string $key
- * @param string $value
- * @param string $type 域名类型 forum, special
- * @return array $key => $value 键值对
- */
- private function _getDomain($key = 'domain', $value = 'domain', $type = 'forum', $absolute = false) {
- static $result = array();
- static $domain = null;
- if (!isset($result[$type][$key][$value])) {
- //不论match,build,只查一次域名表,表中存放的是所有域名,不包括空间。数量大概在几十个
- $domain === null && $domain = Wekit::load('domain.PwDomain')->getAll();
- foreach ($domain as $v) {
- if ($v['domain_type'] == $type) {
- if ($value == 'domain' && $absolute) $v[$value] = $this->scheme . $v[$value] . '.' . $v['root'];
- $temp[$v[$key]] = $v[$value];
- }
- }
- $result[$type][$key][$value] = $temp;
- }
- return $result[$type][$key][$value];
- }
- /**
- * 初始化配置
- *
- * @return array
- */
- private function _getAppType() {
- static $conf = null;
- if ($conf === null) {
- $conf = Wekit::C()->site->get('domain.app', array());
- }
- return $conf;
- }
- /**
- * 初始化配置
- *
- * @return array
- */
- private function _getMulti() {
- static $conf = null;
- if ($conf === null) {
- $conf = @include (Wind::getRealPath($this->entrance));
- $conf = $conf ? $conf : array();
- }
- return $conf;
- }
- /**
- * 获取应用域名
- *
- * @param string $_m
- * @return string
- */
- private function _getModuleDomain($_m) {
- $appType = $this->_getAppType();
- if (isset($appType[$_m])) return $appType[$_m] . $this->base;
- isset($appType['default']) || $this->dynamicHost = Wekit::url()->base;
- return isset($appType['default']) ? $appType['default'] . $this->base : ($this->origialBase != $this->base ? Wekit::url()->base : '');
- }
- /**
- * 分析参数
- *
- * @param AbstractWindRouter $router
- * @param string $action
- * @param array $args
- * @return array
- */
- private function _resolveMca($router, $action, $args) {
- list($action, $_args) = explode('?', $action . '?');
- $args = array_merge($args, ($_args ? WindUrlHelper::urlToArgs($_args, false) : array()));
- $action = trim($action, '/');
- $tmp = explode('/', $action . '/');
- end($tmp);
- if (5 === count($tmp) && !strncasecmp('app/', $action, 4)) {
- list($_a, $_c, $_app_name, $_m) = array(prev($tmp), prev($tmp), prev($tmp), prev($tmp));
- $args['app'] = $_app_name;
- } else {
- list($_a, $_c, $_m) = array(prev($tmp), prev($tmp), prev($tmp));
- }
- $_m = $_m ? $_m : $router->getDefaultModule();
- $_c = $_c ? $_c : $router->getDefaultController();
- $_a = $_a ? $_a : $router->getDefaultAction();
- return array($_m, $_c, $_a, $args);
- }
- /**
- * 生成脚本文件
- *
- * @param string $_m
- * @param string $_c
- * @param string $_a
- * @return string
- */
- private function _buildScript($_m, $_c, $_a, &$args) {
- $pattern = "$_m/$_c/$_a";
- foreach ($this->_getMulti() as $k => $v) {
- if (strpos($v, '*')) {
- $v = str_replace(array('*', '/'), array('\w*', '\/'), $v);
- if (preg_match('/^' . $v . '$/i', $pattern)){
- $args['a'] = $_a;
- $this->omit_mca = true;
- return "/$k";
- }
- } elseif ($v == $pattern) {
- $this->omit_mca = true;
- return "/$k";
- }
- }
- return '/index.php';
- }
-
- /**
- * 获取待验证有效的url串
- *
- * @param string $path
- * @return string
- */
- private function _getRawPath($path) {
- $rawpath = $path;
- false !== ($pos = strpos($path, 'index.php')) && $rawpath = substr($path, $pos + 9);
- return trim($rawpath, '/');
- }
-
- /**
- * 过滤无效url
- *
- * @param string $rawpath
- * @throws WindException
- */
- private function _filterIllegal($rawpath) {
- foreach (array('#', '?') as $symbol) {
- list($rawpath) = explode($symbol, $rawpath, 2);
- }
- if ($rawpath) {
- throw new WindException('Unable to resolve the request!', 404);
- }
- }
-
- private function _getDefault() {
- return array('m' => $this->default_m, 'c' => 'index', 'a' => 'run');
- }
- }
- ?>