improve link building (QUERY_STRING and PATH_INFO are now allowed)
[GitHub/Stricted/Domain-Control-Panel.git] / lib / system / RequestHandler.class.php
index 89e94418a9415706fc87c65631103da5ad875d95..f4558943c4ffa3c60842496f66e1f370a07c0de9 100644 (file)
@@ -1,23 +1,25 @@
 <?php
 namespace dns\system;
 use dns\system\cache\builder\ControllerCacheBuilder;
+use dns\system\route\Request;
+use dns\system\route\Segment;
+use Zend\Router\Http\RouteMatch;
+use Zend\Router\SimpleRouteStack;
 
 /**
  * @author      Jan Altensen (Stricted)
  * @license     GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @copyright   2013-2015 Jan Altensen (Stricted)
+ * @copyright   2013-2016 Jan Altensen (Stricted)
  */
-class RequestHandler {
-       protected $pattern = "";
-       protected $routeData = array();
-       protected $controllers = array();
+class RequestHandler extends SingletonFactory {
+       protected $router = null;
+       protected $apiModule = false;
        
        /**
-        * init RequestHandler
+        * init RequestHandler
         */
-       public function __construct ($module = '') {
-               $this->pattern = '~/?(?:(?P<controller>[A-Za-z0-9\-]+)(?:/(?P<id>\d+)(?:-(?P<title>[^/]+))?)?)?~x';
-               $this->getControllers($module);
+       protected function init () {
+               $this->router = new SimpleRouteStack();
                
                if (DNS::getSession()->username !== null) {
                        DNS::getTPL()->assign(array("username" => DNS::getSession()->username));
@@ -25,106 +27,180 @@ class RequestHandler {
                else {
                        DNS::getTPL()->assign(array("username" => ''));
                }
-               
-               $className = "";
-               if (!empty($_SERVER['QUERY_STRING'])) {
-                       $this->matches($_SERVER['QUERY_STRING']);
-                       $this->registerRouteData();
-               }
-               else {
-                       $className = '\\dns'.(empty($module) ? '' : '\\'.$module).'\\page\\IndexPage';
+       }
+       
+       /**
+        * set the default rules
+        *
+        * @param       string  $module
+        **/
+       public function setRoutes($module='') {
+               if ($module == "api") {
+                       $this->apiModule = true;
                }
                
-               if (isset($this->routeData['controller']) && !empty($this->routeData['controller'])) {
-                       $controller = strtolower($this->routeData['controller']);
-                       if (isset($this->controllers[$controller]) && !empty($this->controllers[$controller])) {
-                               $className = $this->controllers[$controller];
-                       }
-                       else {
-                               @header('HTTP/1.0 404 Not Found');
-                               DNS::getTPL()->assign(array("activeMenuItem" => '', "error" => 'The link you are trying to reach is no longer available or invalid.'));
-                               DNS::getTPL()->display('error.tpl');
-                               exit;
-                       }
-               }
+               /* load the controllers from cache and build routes */
+               $controllers = ControllerCacheBuilder::getInstance()->getData(array('module' => $module));
+               $routes = [];
                
-               if (!User::isLoggedIn() && $className != '\dns\page\LoginPage' && $className != '\dns\page\ApiPage') {
-                       DNS::getTPL()->display('login.tpl');
-                       exit;
+               foreach ($controllers as $name => $data) {
+                       $routes[$name] = Segment::factory([ 'route' => '[/]' . $name . '[/][/:id[-:title]]', 'constraints' => [ 'id' => '[0-9]+', 'title' => '[a-zA-Z0-9_.-/]+' ], 'defaults' => [ 'controller' => $data ] ]);
                }
                
-               // handle offline mode
-               if (defined('OFFLINE') && OFFLINE) {
-                       $admin = User::isAdmin();
-                       $available = false;
+               $this->router->setRoutes($routes);
+       }
+       
+       /**
+        * @see \Zend\Mvc\Router\SimpleRouteStack::addRoute()
+        *
+        * @param  string  $name
+        * @param  mixed   $route
+        */
+       public function addRoute ($name, $route) {
+               $this->router->addRoute($name, $route);
+       }
+       
+       /**
+        * @see \Zend\Mvc\Router\SimpleRouteStack::addRoutes()
+        *
+        * @param       array|Traversable       $routes
+        */
+       public function addRoutes ($routes) {
+               $this->router->addRoutes($routes);
+       }
+       
+       /**
+        * Get the added routes
+        *
+        * @return      Traversable list of all routes
+        */
+       public function getRoutes() {
+               return $this->router->getRoutes();
+       }
+       
+       /**
+        * handle the request
+        */
+       public function handle () {
+               $match = $this->router->match(new Request());
+               if ($match !== null) {
+                       $this->registerRouteData($match);
                        
-                       if (defined($className . '::AVAILABLE_DURING_OFFLINE_MODE') && constant($className . '::AVAILABLE_DURING_OFFLINE_MODE')) {
-                               $available = true;
-                       }
+                       $className = $match->getParam("controller");
                        
-                       if (!$admin && !$available) {
-                               @header('HTTP/1.1 503 Service Unavailable');
-                               DNS::getTPL()->display('offline.tpl');
+                       if (!User::isLoggedIn() && $this->apiModule == false && $className != 'dns\page\LoginPage' && $className != 'dns\page\ApiPage') {
+                               DNS::getTPL()->display('login.tpl');
                                exit;
                        }
-               }
-               
-               try {
-                       new $className();
-               }
-               catch (\Exception $e) {
-                       if ($e->getCode() == 404) {
-                               @header('HTTP/1.0 404 Not Found');
-                       }
-                       else if ($e->getCode() == 403) {
-                               @header('HTTP/1.0 403 Forbidden');
+                       
+                       if (defined('OFFLINE') && OFFLINE) {
+                               $admin = User::isAdmin();
+                               $available = false;
+                               
+                               if (defined($className . '::AVAILABLE_DURING_OFFLINE_MODE') && constant($className . '::AVAILABLE_DURING_OFFLINE_MODE')) {
+                                       $available = true;
+                               }
+                               
+                               if (!$admin && !$available) {
+                                       @header('HTTP/1.1 503 Service Unavailable');
+                                       DNS::getTPL()->display('offline.tpl');
+                                       exit;
+                               }
                        }
                        
-                       /* show error page */
-                       DNS::getTPL()->assign(array("activeMenuItem" => '', "error" => $e->getMessage()));
+                       try {
+                               new $className();
+                       }
+                       catch (\Exception $e) {
+                               if ($e->getCode() == 404) {
+                                       @header('HTTP/1.0 404 Not Found');
+                               }
+                               else if ($e->getCode() == 403) {
+                                       @header('HTTP/1.0 403 Forbidden');
+                               }
+                               
+                               // show error page
+                               DNS::getTPL()->assign(array("activeMenuItem" => '', "error" => $e->getMessage()));
+                               DNS::getTPL()->display('error.tpl');
+                               exit;
+                       }
+               }
+               else {
+                       @header('HTTP/1.0 404 Not Found');
+                       DNS::getTPL()->assign(array("activeMenuItem" => '', "error" => 'The link you are trying to reach is no longer available or invalid.'));
                        DNS::getTPL()->display('error.tpl');
                        exit;
                }
        }
-
+       
        /**
         * Registers route data within $_GET and $_REQUEST.
         */
-       protected function registerRouteData() {
-               foreach ($this->routeData as $key => $value) {
+       protected function registerRouteData(RouteMatch $route) {
+               foreach ($route->getParams() as $key => $value) {
                        $_GET[$key] = $value;
                        $_REQUEST[$key] = $value;
                }
        }
        
-       public function getControllers ($module) {
+       public function getBaseUrl () {
+               $protocol = 'http://';
                
-               $this->controllers = ControllerCacheBuilder::getInstance()->getData(array('module' => $module));
-               /*
-               $pages = glob(DNS_DIR.'/lib/'.(empty($module) ? '' : $module.'/').'page/*Page.class.php');
-               
-               foreach ($pages as $page) {
-                       $page = str_replace('Page.class.php', '', basename($page));
-
-                       $class = "\\dns".(empty($module) ? '' : "\\".$module)."\\page\\".$page."Page";
-                       if (class_exists($class) && is_subclass_of($class, '\\dns\\page\\AbstractPage')) {
-                               $this->controllers[strtolower($page)] = $class;
-                       }
+               if ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') || $_SERVER['SERVER_PORT'] == 443 || (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) {
+                       $protocol = 'https://';
                }
-               */
+               
+               return $protocol . $_SERVER['HTTP_HOST'] . '/';
        }
        
-       public function matches($requestURL) {
-               if (preg_match($this->pattern, $requestURL, $matches)) {
-                       foreach ($matches as $key => $value) {
-                               if (!is_numeric($key)) {
-                                       $this->routeData[$key] = $value;
+       public function getLink (array $params = [], $query = '') {
+               $path_info = true; // TODO: add config constant for that
+               
+               $url = $this->getBaseUrl() . 'index.php';
+               if ($path_info) {
+                       $url .= '/';
+               }
+               else {
+                       $url .= '?';
+               }
+               
+               if (!empty($params['controller'])) {
+                       $url .= $params['controller'];
+               }
+               else {
+                       // TODO: InvalidArgumentException?
+               }
+               
+               if (!empty($params['id'])) {
+                       if (!empty($params['title'])) {
+                               $url .= '/'.$params['id'].'-'.$params['title'];
+                       }
+                       else {
+                               $url .= '/'.$params['id'];
+                       }
+               }
+               
+               if (!empty($query)) {
+                       if ($path_info) {
+                               if (strpos($query, '&') === 0) {
+                                       $query = '/' . substr($query, 1);
+                               }
+                               else if (strpos($query, '/') !== 0) {
+                                       $query = '/' . $query;
+                               }
+                       }
+                       else {
+                               if (strpos($query, '/') === 0) {
+                                       $query = '&' . substr($query, 1);
+                               }
+                               else if (strpos($query, '&') !== 0) {
+                                       $query = '&' . $query;
                                }
                        }
                        
-                       return true;
+                       $url .= $query;
                }
                
-               return false;
+               return $url;
        }
 }