Improved routing system, added StaticRoute
authorAlexander Ebert <ebert@woltlab.com>
Thu, 19 Mar 2015 18:29:21 +0000 (19:29 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Thu, 19 Mar 2015 18:29:21 +0000 (19:29 +0100)
wcfsetup/install/files/lib/system/request/FlexibleRoute.class.php
wcfsetup/install/files/lib/system/request/RouteHandler.class.php
wcfsetup/install/files/lib/system/request/StaticRoute.class.php [new file with mode: 0644]

index 16840d4af2930a0519658b9ee69cf69d3226d83c..d6d9bb90e91188be960cae106e8633f8b8604b2c 100644 (file)
@@ -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;
                        }
                }
                
index 154aa8daa2572953d2f3a8beb35fc707424fc1ec..c150e7456fbda4aeeeca209c95cf3c0191e52e0e 100644 (file)
@@ -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 (file)
index 0000000..d014d84
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+namespace wcf\system\request;
+
+/**
+ * Static route implementation to resolve HTTP requests, handling a single controller.
+ * 
+ * @author     Alexander Ebert
+ * @copyright  2001-2015 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @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;
+       }
+}