Skip to content

Commit

Permalink
Started work on routing improvements (#42)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
BelleNottelling committed May 26, 2023
1 parent fa87852 commit b61ac80
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 63 deletions.
1 change: 1 addition & 0 deletions src/AntCMS/AntCMS.php
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ public function serveContent(string $path)
header('Content-Type: ' . $asset_mime_type);
readfile($path);
}
exit;
}

public static function redirect(string $url)
Expand Down
11 changes: 11 additions & 0 deletions src/AntCMS/AntEnviroment.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace AntCMS;

class AntEnviroment
{
public static function isCli(): bool
{
return (php_sapi_name() === 'cli' || !http_response_code());
}
}
129 changes: 129 additions & 0 deletions src/AntCMS/AntRouting.php
Original file line number Diff line number Diff line change
@@ -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;
}
}
85 changes: 22 additions & 63 deletions src/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,95 +14,54 @@

use AntCMS\AntCMS;
use AntCMS\AntConfig;
use AntCMS\AntPages;
use AntCMS\AntPluginLoader;

if (!file_exists(antConfigFile)) {
AntConfig::generateConfig();
}

if (!file_exists(antPagesList)) {
AntPages::generatePages();
\AntCMS\AntPages::generatePages();
}

$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 (!file_exists(antUsersList)) {
AntCMS::redirect('/profile/firsttime');
Expand All @@ -111,6 +70,6 @@
echo $antCms->renderPage('/');
exit;
} else {
echo $antCms->renderPage($requestedPage);
echo $antCms->renderPage($requestUri);
exit;
}

0 comments on commit b61ac80

Please sign in to comment.