Selaa lähdekoodia

Started work on routing improvements (#42)

* Started work on routing improvements

Creates a helper class to make handling routes a bit easier. Also should an issue that prevented SSL certs from being renewed via the .well-known folder

* Bugfix

* Added PHPDocs
Belle Aerni 2 vuotta sitten
vanhempi
commit
b61ac80227
4 muutettua tiedostoa jossa 163 lisäystä ja 63 poistoa
  1. 1 0
      src/AntCMS/AntCMS.php
  2. 11 0
      src/AntCMS/AntEnviroment.php
  3. 129 0
      src/AntCMS/AntRouting.php
  4. 22 63
      src/index.php

+ 1 - 0
src/AntCMS/AntCMS.php

@@ -230,6 +230,7 @@ class AntCMS
             header('Content-Type: ' . $asset_mime_type);
             header('Content-Type: ' . $asset_mime_type);
             readfile($path);
             readfile($path);
         }
         }
+        exit;
     }
     }
 
 
     public static function redirect(string $url)
     public static function redirect(string $url)

+ 11 - 0
src/AntCMS/AntEnviroment.php

@@ -0,0 +1,11 @@
+<?php
+
+namespace AntCMS;
+
+class AntEnviroment
+{
+    public static function isCli(): bool
+    {
+        return (php_sapi_name() === 'cli' || !http_response_code());
+    }
+}

+ 129 - 0
src/AntCMS/AntRouting.php

@@ -0,0 +1,129 @@
+<?php
+
+namespace AntCMS;
+
+class AntRouting
+{
+    private string $baseUrl;
+    private string $requestUri;
+    private array $uriExploded;
+
+    private array $indexes = ['/', '/index.php', '/index.html', '', 'index.php', 'index.html'];
+
+    /**
+     * @param string $baseUrl The base site URL. Ex: domain.com
+     * @param string $requestUri The current request URI. Ex: /page/example
+     */
+    public function __construct(string $baseUrl, string $requestUri)
+    {
+        $this->baseUrl = $baseUrl;
+        $this->requestUri = $requestUri;
+        $this->setExplodedUri($requestUri);
+    }
+
+    /**
+     * @param string $requestUri The current request URI. Ex: /page/example
+     */
+    public function setRequestUri(string $requestUri): void
+    {
+        $this->$requestUri = $requestUri;
+        $this->setExplodedUri($requestUri);
+    }
+
+    /**
+     * Used to add to the start of the request URI. Primarially used for plugin routing.
+     * For example: this is used internally to rewrite /profile/edit to /plugin/profile/edit
+     * 
+     * @param string $append What to append to the start of the request URI.
+     */
+    public function requestUriUnshift(string $append): void
+    {
+        array_unshift($this->uriExploded, $append);
+        $this->requestUri = implode('/', $this->uriExploded);
+    }
+
+    /**
+     * Used to detect if the current request is over HTTPS. If the request is over HTTP, it'll redirect to HTTPS.
+     */
+    public function redirectHttps(): void
+    {
+        $scheme = $_SERVER['HTTPS'] ?? $_SERVER['REQUEST_SCHEME'] ?? $_SERVER['HTTP_X_FORWARDED_PROTO'] ?? null;
+        $isHttps = !empty($scheme) && (strcasecmp('on', $scheme) == 0 || strcasecmp('https', $scheme) == 0);
+
+        if (!$isHttps) {
+            $url = 'https://' . AntTools::repairURL($this->baseUrl . '/' . $this->requestUri);
+            header('Location: ' . $url);
+            exit;
+        }
+    }
+
+    /**
+     * Used to check if the current request URI matches a specified route.
+     * Supports using '*' as a wild-card. Ex: '/admin/*' will match '/admin/somthing' and '/admin'
+     * 
+     * @param string $uri The Route to compare against the current URI.
+     */
+    public function checkMatch(string $uri): bool
+    {
+        $matching =  explode('/', $uri);
+        if (empty($matching[0])) {
+            array_shift($matching);
+        }
+
+        if (count($matching) < count($this->uriExploded) && end($matching) !== '*') {
+            return false;
+        }
+
+        foreach ($this->uriExploded as $index => $value) {
+            if (isset($matching[$index]) && $matching[$index] !== '*' && $matching[$index] !== $value) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /** 
+     * Attempts to detect what plugin is associated with the current URI and then routes to the matching one. 
+     */
+    public function routeToPlugin(): void
+    {
+        $pluginName = $this->uriExploded[1];
+        $pluginLoader = new AntPluginLoader();
+        $plugins = $pluginLoader->loadPlugins();
+
+        //Drop the first two elements of the array so the remaining segments are specific to the plugin.
+        array_splice($this->uriExploded, 0, 2);
+
+        foreach ($plugins as $plugin) {
+            if (strtolower($plugin->getName()) === strtolower($pluginName)) {
+                $plugin->handlePluginRoute($this->uriExploded);
+                exit;
+            }
+        }
+
+        // plugin not found
+        header("HTTP/1.0 404 Not Found");
+        echo ("Error 404");
+        exit;
+    }
+
+    /**
+     * @return bool Returns true if the current request URI is an index request.
+     */
+    public function isIndex(): bool
+    {
+        return (in_array($this->requestUri, $this->indexes));
+    }
+
+    private function setExplodedUri(string $uri): void
+    {
+        $exploaded = explode('/', $uri);
+
+        if (empty($exploaded[0])) {
+            array_shift($exploaded);
+        }
+
+        $this->uriExploded = $exploaded;
+    }
+}

+ 22 - 63
src/index.php

@@ -14,95 +14,54 @@ $loader->register();
 
 
 use AntCMS\AntCMS;
 use AntCMS\AntCMS;
 use AntCMS\AntConfig;
 use AntCMS\AntConfig;
-use AntCMS\AntPages;
-use AntCMS\AntPluginLoader;
 
 
 if (!file_exists(antConfigFile)) {
 if (!file_exists(antConfigFile)) {
     AntConfig::generateConfig();
     AntConfig::generateConfig();
 }
 }
 
 
 if (!file_exists(antPagesList)) {
 if (!file_exists(antPagesList)) {
-    AntPages::generatePages();
+    \AntCMS\AntPages::generatePages();
 }
 }
 
 
 $antCms = new AntCMS();
 $antCms = new AntCMS();
 
 
-if (AntConfig::currentConfig('forceHTTPS') && 'cli' !== PHP_SAPI) {
-    $isHTTPS = false;
+$requestUri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
+$baseUrl = AntConfig::currentConfig('baseURL');
+$antRouting = new \AntCMS\AntRouting($baseUrl, $requestUri);
 
 
-    if (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off') {
-        $isHTTPS = true;
-    }
-
-    if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https') {
-        $isHTTPS = true;
-    }
-
-    if (!empty($_SERVER['HTTP_X_FORWARDED_SSL']) && strtolower($_SERVER['HTTP_X_FORWARDED_SSL']) !== 'off') {
-        $isHTTPS = true;
-    }
-
-    if (!$isHTTPS) {
-        $url = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
-        header('Location: ' . $url);
-        exit;
-    }
+if (AntConfig::currentConfig('forceHTTPS') && !\AntCMS\AntEnviroment::isCli()) {
+    $antRouting->redirectHttps();
 }
 }
 
 
-$requestedPage = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
-$segments = explode('/', $requestedPage);
-
-if ($segments[0] === '') {
-    array_shift($segments);
+if ($antRouting->checkMatch('/themes/*/assets')) {
+    $antCms->serveContent(AntDir . $requestUri);
 }
 }
 
 
-if ($segments[0] === 'themes' && $segments[2] === 'assets') {
-    $antCms->serveContent(AntDir . $requestedPage);
-    exit;
+if ($antRouting->checkMatch('/.well-known/acme-challenge/*')) {
+    $antCms->serveContent(AntDir . $requestUri);
 }
 }
 
 
-if ($segments[0] == 'sitemap.xml') {
-    $segments[0] = 'plugin';
-    $segments[1] = 'sitemap';
+if ($antRouting->checkMatch('/sitemap.xml')) {
+    $antRouting->setRequestUri('/plugin/sitemap');
 }
 }
 
 
-if ($segments[0] == 'robots.txt') {
-    $segments[0] = 'plugin';
-    $segments[1] = 'robotstxt';
+if ($antRouting->checkMatch('/robots.txt')) {
+    $antRouting->setRequestUri('/plugin/robotstxt');
 }
 }
 
 
-if ($segments[0] == 'admin') {
-    array_unshift($segments, 'plugin');
+if ($antRouting->checkMatch('/admin/*')) {
+    $antRouting->requestUriUnshift('plugin');
 }
 }
 
 
-if ($segments[0] == 'profile') {
-    array_unshift($segments, 'plugin');
+if ($antRouting->checkMatch('/profile/*')) {
+    $antRouting->requestUriUnshift('plugin');
 }
 }
 
 
-if ($segments[0] === 'plugin') {
-    $pluginName = $segments[1];
-    $pluginLoader = new AntPluginLoader();
-    $plugins = $pluginLoader->loadPlugins();
-
-    //Drop the first two elements of the array so the remaining segments are specific to the plugin.
-    array_splice($segments, 0, 2);
-
-    foreach ($plugins as $plugin) {
-        if (strtolower($plugin->getName()) === strtolower($pluginName)) {
-            $plugin->handlePluginRoute($segments);
-            exit;
-        }
-    }
-
-    // plugin not found
-    header("HTTP/1.0 404 Not Found");
-    echo ("Error 404");
-    exit;
+if ($antRouting->checkMatch('/plugin/*')) {
+    $antRouting->routeToPlugin();
 }
 }
 
 
-$indexes = ['/', '/index.php', '/index.html'];
-if (in_array($segments[0], $indexes) or empty($segments[0])) {
-
+if ($antRouting->isIndex()) {
     // If the users list hasn't been created, redirect to the first-time setup
     // If the users list hasn't been created, redirect to the first-time setup
     if (!file_exists(antUsersList)) {
     if (!file_exists(antUsersList)) {
         AntCMS::redirect('/profile/firsttime');
         AntCMS::redirect('/profile/firsttime');
@@ -111,6 +70,6 @@ if (in_array($segments[0], $indexes) or empty($segments[0])) {
     echo $antCms->renderPage('/');
     echo $antCms->renderPage('/');
     exit;
     exit;
 } else {
 } else {
-    echo $antCms->renderPage($requestedPage);
+    echo $antCms->renderPage($requestUri);
     exit;
     exit;
 }
 }