Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
0a2a430
⚡️ Better attribute replace performance, correct replaceNode order
Friedinger Jul 29, 2024
00bc943
🐛 Case insensitive attribute replace
Friedinger Jul 29, 2024
193842a
✨ Sanitized output replace
Friedinger Jul 29, 2024
ad8288d
🙈 Not ignore sample files in config dir
Friedinger Jul 29, 2024
84c592e
✨ Add function to get request protocol
Friedinger Jul 29, 2024
d1ebd1c
✨ CSRF token generation
Friedinger Aug 28, 2024
6b0e344
🔖 Version 2.2.0
Friedinger Aug 28, 2024
6207244
🧑‍💻 <tag> allowed in attribute replaces
Friedinger Aug 29, 2024
3528535
✨ CSRF improvements
Friedinger Aug 29, 2024
965cc95
🔖 Version 2.2.1
Friedinger Aug 29, 2024
1387777
🚸 CSRF usability improvements
Friedinger Aug 29, 2024
8fc8823
🔖 Version 2.2.2
Friedinger Aug 29, 2024
89fb06d
💬 Change csrf sample page
Friedinger Aug 29, 2024
c0031d7
✨ Error handling and logging
Friedinger Aug 31, 2024
b59bd45
🔖 Version 3.0.0
Friedinger Aug 31, 2024
53a6805
💡 Change start comment
Friedinger Aug 31, 2024
a6daaed
🗑️ Refactor deprecated mb encode html entities
Friedinger Aug 31, 2024
9c87c6b
✨ Debug config to display errors instead of logging them
Friedinger Aug 31, 2024
043833f
🔖 Version 3.0.1
Friedinger Aug 31, 2024
4a013ac
🎨 Logging improvements
Friedinger Sep 1, 2024
e16af5e
🔖 Version 3.1.0
Friedinger Sep 1, 2024
3609063
🧑‍💻 Remove exit and die to allow e.g. logging afterwards
Friedinger Sep 2, 2024
f2bd0b3
🔖 Version 3.2.0
Friedinger Sep 2, 2024
fc4c433
🧑‍💻 Remove last die call
Friedinger Sep 2, 2024
999b9f9
🐛 Multiple CSRF gens in one request
Friedinger Sep 6, 2024
dec9443
🔖 Version 3.2.2
Friedinger Sep 6, 2024
1e8550e
💡 Add link to repo as comment
Friedinger Sep 6, 2024
7b1dfed
♻️ Refactor redirect to exception
Friedinger Sep 11, 2024
85dc328
🗑️ Mark Router::redirect as deprecated
Friedinger Sep 11, 2024
f6612ca
♻️ Refactor proxy callable execution
Friedinger Sep 11, 2024
02a1b24
♻️ Refactor getRouteFile
Friedinger Sep 11, 2024
296bf20
🔖 Version 3.3.0
Friedinger Sep 30, 2024
ddcb6be
🧑‍💻 Logging and exception handling improvements
Friedinger Nov 23, 2024
dd5d1f3
🔖 Version 3.3.1
Friedinger Nov 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ build/

# Configuration files
config/*
!config/config_sample.php
!config/*_sample.*
.env
*.local

Expand Down
20 changes: 18 additions & 2 deletions config/config_sample.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@
FileRouter
A simple php router that allows to run code before accessing a file while keeping the file structure as the url structure.

https://github.com/Friedinger/FileRouter

by Friedinger (friedinger.org)

Version: 2.1.4
Version: 3.3.1

*/

namespace FileRouter;

final class Config
class Config
{
// Paths (relative to the server root)
const PATH_PUBLIC = "/../public/"; // Path to the public folder
Expand All @@ -35,11 +37,25 @@ final class Config
"samesite" => "Strict",
];

// Security
const CSRF_TEMPLATE = "csrf-token"; // CSRF token template variable name, also used as session key
const CSRF_PARAMETER = "token"; // CSRF token parameter name for get and post requests
const CSRF_LENGTH = 64; // Length of the CSRF token, set to 0 to disable CSRF protection

// Page titles
const TITLE_PREFIX = "FileRouter";
const TITLE_SUFFIX = "";
const TITLE_SEPARATOR = " | ";

// Error handling and logging
const DEBUG = true; // Enable debug mode (shows errors and warnings, disables error logging)
const LOG = true; // Enable logging
const LOG_MAX_FILE_SIZE = 1048576; // Maximum file size of log files before a new file is created (in bytes)
const LOG_PATH = [ // Paths to log files (relative to the server root, {date} will be replaced with the current date)
"error" => "/../logs/error_{date}.log", // Error log file required if logging is enabled
"additional" => "/../logs/additional.log", // Additional log files, can be used for custom logging by Logger::log("message", "additional")
];

// Other
const ALLOW_PAGE_PHP = true; // Allow to execute php code in pages. Warning: This can be a security risk if not handled carefully.
const IMAGE_RESIZE_QUERY = "res"; // Query parameter to specify the width of an image to resize it
Expand Down
2 changes: 2 additions & 0 deletions function/ControllerDefault.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
FileRouter
A simple php router that allows to run code before accessing a file while keeping the file structure as the url structure.

https://github.com/Friedinger/FileRouter

by Friedinger (friedinger.org)

*/
Expand Down
7 changes: 7 additions & 0 deletions function/ControllerHtml.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
FileRouter
A simple php router that allows to run code before accessing a file while keeping the file structure as the url structure.

https://github.com/Friedinger/FileRouter

by Friedinger (friedinger.org)

*/
Expand Down Expand Up @@ -57,6 +59,11 @@ public static function handleHtml(Output $content): Output
$content = self::handleHeader($content);
$content = self::handleFooter($content);

// Output CSRF token if enabled
if (Config::SESSION && Config::CSRF_LENGTH > 0) {
$content->replaceAllSafe(Config::CSRF_TEMPLATE, Misc::generateCsrfToken());
}

return $content;
}

Expand Down
2 changes: 2 additions & 0 deletions function/ControllerImage.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
FileRouter
A simple php router that allows to run code before accessing a file while keeping the file structure as the url structure.

https://github.com/Friedinger/FileRouter

by Friedinger (friedinger.org)

*/
Expand Down
10 changes: 7 additions & 3 deletions function/Error.php → function/ErrorPage.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
FileRouter
A simple php router that allows to run code before accessing a file while keeping the file structure as the url structure.

https://github.com/Friedinger/FileRouter

by Friedinger (friedinger.org)

*/
Expand All @@ -16,7 +18,7 @@
*
* Represents an error with an error code and an error message.
*/
class Error extends \Exception
class ErrorPage extends \Exception
{

/**
Expand All @@ -34,7 +36,10 @@ public function __construct(int $errorCode, string $errorMessage = null)
http_response_code($errorCode); // Set HTTP status code based on error code

$pathErrorPage = $_SERVER["DOCUMENT_ROOT"] . Config::PATH_ERROR;
if (!file_exists($pathErrorPage)) die(Config::ERROR_FATAL); // Fatal error if error page does not exist
if (!file_exists($pathErrorPage)) {
// Fatal error if error page does not exist
throw new \ValueError("Error page (Config::PATH_ERROR) not found in {$pathErrorPage}", E_USER_ERROR);
}
$output = new Output($pathErrorPage); // Load error page to output handler

$settings = $output->getNodeContentArray("settings"); // Get error messages from error page
Expand All @@ -48,6 +53,5 @@ public function __construct(int $errorCode, string $errorMessage = null)
$output->replaceAll("error-message", $errorMessage); // Replace error message placeholder
$output = ControllerHtml::handleHtml($output); // Handle html content (e.g. add head, header and footer)
$output->print(); // Print output
exit; // Stop further execution
}
}
104 changes: 83 additions & 21 deletions function/FileRouter.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,98 @@
FileRouter
A simple php router that allows to run code before accessing a file while keeping the file structure as the url structure.

https://github.com/Friedinger/FileRouter

by Friedinger (friedinger.org)

Version: 2.1.4
Version: 3.3.1

*/

namespace FileRouter;

// Autoload classes
spl_autoload_register(function ($class) {
if (str_starts_with($class, __NAMESPACE__ . "\\")) {
$class = str_replace(__NAMESPACE__ . "\\", "", $class);
require_once "{$class}.php";
class FileRouter
{
public function __construct()
{
try {
$this->autoload(); // Autoload classes
$this->setup(); // Setup environment
$this->handle(); // Handle request
} catch (ErrorPage) {
// Error page was displayed in ErrorPage exception
} catch (Redirect) {
// Redirect was handled in Redirect exception
} catch (\Throwable $exception) {
$this->handleException($exception); // Handle unhandled exceptions
}
}
});

// Start session if enabled in config
if (Config::SESSION) {
Misc::session();
}
private function handle(): void
{
// Handle route file as proxy of request
$proxy = new Proxy();
$proxyHandled = $proxy->handle();
if ($proxyHandled) return; // Stop handling if request was handled by proxy

// Handle request with router
$router = new Router();
$routerHandled = $router->handle();
if ($routerHandled) return; // Stop handling if request was handled by router

// Handle route file as proxy of request
$proxy = new Proxy();
$proxyHandled = $proxy->handle();
if ($proxyHandled) exit; // Stop handling if request was handled by proxy
// Error 404 if request was not handled before
throw new ErrorPage(404);
}

private function setup(): void
{
// Set error log file if enabled
if (Config::LOG) {
ini_set("log_errors", 1); // Enable error logging
ini_set("error_log", Logger::logPath("error")); // Set error log file
}

// Handle request with router
$router = new Router();
$routerHandled = $router->handle();
if ($routerHandled) exit; // Stop handling if request was handled by router
// Set error reporting and display errors
if (Config::DEBUG) {
error_reporting(E_ALL); // Report all errors
ini_set("display_errors", 1); // Display errors
ini_set("log_errors", 0); // Disable error log
} else {
error_reporting(0); // Report no errors
ini_set("display_errors", 0); // Hide errors
}

// Error 404 if request was not handled before
throw new Error(404);
// Start session if enabled in config
if (Config::SESSION) {
Misc::session();
}
}

private function handleException(\Throwable $exception): void
{
if (CONFIG::DEBUG) {
throw $exception; // Rethrow exception if debug mode is enabled
}
try {
ob_end_clean(); // Clear output buffer
Logger::logError("{$exception->getMessage()} in {$exception->getFile()}({$exception->getLine()})", E_USER_ERROR); // Log unhandled exceptions
throw new ErrorPage(500); // Internal server error if unhandled exception occurred
} catch (\Throwable $e) {
if ($e instanceof ErrorPage) return;
if (Config::LOG) {
error_log("ERROR {$e->getMessage()} in exception handling"); // Log error message
}
die(Config::ERROR_FATAL ?? "<h1>Error</h1><p>An error occurred in the request.</p><p>Please contact the webmaster</p>");
}
}

private function autoload()
{
spl_autoload_register(function ($class) {
if (str_starts_with($class, __NAMESPACE__ . "\\")) {
$class = str_replace(__NAMESPACE__ . "\\", "", $class);
require_once "{$class}.php";
}
});
}
}
75 changes: 75 additions & 0 deletions function/Logger.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

/*

FileRouter
A simple php router that allows to run code before accessing a file while keeping the file structure as the url structure.

https://github.com/Friedinger/FileRouter

by Friedinger (friedinger.org)

*/


namespace FileRouter;

class Logger
{
public static function log(string $message, string $type): void
{
if (!Config::LOG) return;

$date = date("Y-m-d H:i:s");
$file = self::logPath($type);

$message = str_replace("\n", "\n\t", $message);
$message = str_replace("\r", "", $message);
$log = "[$date] $message" . PHP_EOL;

file_put_contents($file, $log, FILE_APPEND);
}

public static function logError(string $message, int $level): void
{
if (!Config::LOG) return;

$levelName = match ($level) {
E_NOTICE => "NOTICE",
E_USER_NOTICE => "NOTICE",
E_WARNING => "WARNING",
E_USER_DEPRECATED => "DEPRECATED",
E_DEPRECATED => "DEPRECATED",
E_USER_WARNING => "WARNING",
E_ERROR => "ERROR",
E_USER_ERROR => "ERROR",
default => "UNKNOWN",
};
error_log("$levelName: $message");
}

public static function logPath(string $type): string
{
$path = Config::LOG_PATH[$type] ?? "filerouter_{date}.log";
$path = str_replace("{date}", date("Y-m-d"), $path);
$file = $_SERVER["DOCUMENT_ROOT"] . $path;
if (file_exists($file) && filesize($file) >= Config::LOG_MAX_FILE_SIZE) {
$file = self::getNewLogFile($file);
}
return $file;
}

private static function getNewLogFile($file)
{
$logDir = dirname($file);
$baseName = basename($file, '.log');
$index = 1;

do {
$newFile = $logDir . DIRECTORY_SEPARATOR . $baseName . '_' . $index . '.log';
$index++;
} while (file_exists($newFile));

return $newFile;
}
}
Loading