From 0729dfc35f19a85e2b696b6ca03cbff152cba724 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Thu, 19 Mar 2015 19:29:21 +0100 Subject: [PATCH] Improved routing system, added StaticRoute --- .../system/request/FlexibleRoute.class.php | 34 ++++++-- .../lib/system/request/RouteHandler.class.php | 10 ++- .../lib/system/request/StaticRoute.class.php | 86 +++++++++++++++++++ 3 files changed, 117 insertions(+), 13 deletions(-) create mode 100644 wcfsetup/install/files/lib/system/request/StaticRoute.class.php diff --git a/wcfsetup/install/files/lib/system/request/FlexibleRoute.class.php b/wcfsetup/install/files/lib/system/request/FlexibleRoute.class.php index 16840d4af2..d6d9bb90e9 100644 --- a/wcfsetup/install/files/lib/system/request/FlexibleRoute.class.php +++ b/wcfsetup/install/files/lib/system/request/FlexibleRoute.class.php @@ -87,12 +87,15 @@ class FlexibleRoute implements IRoute { $this->buildSchema = array(); $buildSchema = ltrim($buildSchema, '/'); - $components = preg_split('~{([a-z]+)}~', $buildSchema, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + $components = preg_split('~({(?:[a-z]+)})~', $buildSchema, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); $delimiters = array('/', '-', '.', '_'); foreach ($components as $component) { $type = 'component'; - if (in_array($component, $delimiters)) { + if (preg_match('~{([a-z]+)}~', $component, $matches)) { + $component = $matches[1]; + } + else { $type = 'separator'; } @@ -130,10 +133,8 @@ class FlexibleRoute implements IRoute { // drop application component to avoid being appended as query string unset($components['application']); - $link = ''; - // handle default values for controller - $buildRoute = true; + $useBuildSchema = true; if (count($components) == 1 && isset($components['controller'])) { $ignoreController = false; @@ -158,14 +159,29 @@ class FlexibleRoute implements IRoute { // drops controller from route if ($ignoreController) { - $buildRoute = false; + $useBuildSchema = false; // unset the controller, since it would otherwise be added with http_build_query() unset($components['controller']); } } - if ($buildRoute) { + return $this->buildRoute($components, $application, $useBuildSchema); + } + + /** + * Builds the actual link, the parameter $useBuildSchema can be set to false for + * empty routes, e.g. for the default page. + * + * @param array $components + * @param string $application + * @param boolean $useBuildSchema + * @return string + */ + protected function buildRoute(array $components, $application, $useBuildSchema) { + $link = ''; + + if ($useBuildSchema) { $lastSeparator = null; foreach ($this->buildSchema as $component) { $value = $component['value']; @@ -197,8 +213,8 @@ class FlexibleRoute implements IRoute { } } - if (!empty($link)) { - $link .= '/'; + if (!empty($link) && $lastSeparator !== null) { + $link .= $lastSeparator; } } diff --git a/wcfsetup/install/files/lib/system/request/RouteHandler.class.php b/wcfsetup/install/files/lib/system/request/RouteHandler.class.php index 154aa8daa2..c150e7456f 100644 --- a/wcfsetup/install/files/lib/system/request/RouteHandler.class.php +++ b/wcfsetup/install/files/lib/system/request/RouteHandler.class.php @@ -286,10 +286,12 @@ class RouteHandler extends SingletonFactory { if (!URL_LEGACY_MODE || RequestHandler::getInstance()->isACPRequest()) { // WCF 2.1: ?Foo/Bar/ if (!empty($_SERVER['QUERY_STRING'])) { - parse_str($_SERVER['QUERY_STRING'], $parts); - foreach ($parts as $key => $value) { - if ($value === '') { - self::$pathInfo = $key; + // don't use parse_str as it replaces dots with underscores + $components = explode('&', $_SERVER['QUERY_STRING']); + for ($i = 0, $length = count($components); $i < $length; $i++) { + $component = $components[$i]; + if (mb_strpos($component, '=') === false) { + self::$pathInfo = urldecode($component); break; } } diff --git a/wcfsetup/install/files/lib/system/request/StaticRoute.class.php b/wcfsetup/install/files/lib/system/request/StaticRoute.class.php new file mode 100644 index 0000000000..d014d840db --- /dev/null +++ b/wcfsetup/install/files/lib/system/request/StaticRoute.class.php @@ -0,0 +1,86 @@ + + * @package com.woltlab.wcf + * @subpackage system.request + * @category Community Framework + */ +class StaticRoute extends FlexibleRoute { + /** + * static application identifier + * @var string + */ + protected $staticApplication = ''; + + /** + * static controller name, not the FQN + * @var string + */ + protected $staticController = ''; + + /** + * Creates a new static route instace. + */ + public function __construct() { + // static routes are disallowed for ACP + parent::__construct(false); + } + + /** + * Sets the static controller for this route. + * + * @param string $application + * @param string $controller + */ + public function setStaticController($application, $controller) { + $this->staticApplication = $application; + $this->staticController = $controller; + + $this->requireComponents['controller'] = '~^' . $this->staticController . '$~'; + } + + /** + * @see \wcf\system\request\IRoute::buildLink() + */ + public function buildLink(array $components) { + // static routes don't have these components + unset($components['application']); + unset($components['controller']); + + return $this->buildRoute($components, '', true); + } + + /** + * @see \wcf\system\request\IRoute::canHandle() + */ + public function canHandle(array $components) { + if (isset($components['application']) && $components['application'] == $this->staticApplication) { + if (isset($components['controller']) && $components['controller'] == $this->staticController) { + return parent::canHandle($components); + } + } + + return false; + } + + /** + * @see \wcf\system\request\IRoute::matches() + */ + public function matches($requestURL) { + if (parent::matches($requestURL)) { + $this->routeData['application'] = $this->staticApplication; + $this->routeData['controller'] = RequestHandler::getTokenizedController($this->staticController); + $this->routeData['isDefaultController'] = false; + + return true; + } + + return false; + } +} -- 2.20.1