Initial commit.

This commit is contained in:
2026-05-29 09:28:30 +02:00
parent 17daa347f4
commit bbbe90458a
12 changed files with 151 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
<?php
return [
'name' => 'My Site',
'debug' => false,
];
View File
+16
View File
@@ -0,0 +1,16 @@
<?php
return [
'GET' => [
'/' => 'home.php',
'/about' => 'about.php',
],
/* Some Examples
'POST' => [
'/contact' => 'contact.php',
],
'DELETE' => [
'/api/todo' => 'delete_todo.php',
],
*/
];
+5
View File
@@ -0,0 +1,5 @@
<?php declare(strict_types=1);
define('ROOT', __DIR__);
require ROOT . '/src/bootstrap.php';
require ROOT . '/src/router.php';
Executable
+10
View File
@@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -euo pipefail
HOST="${HOST:-127.0.0.1}"
PORT="${PORT:-3333}"
echo "Starting development server..."
echo "URL: http://${HOST}:${PORT}"
php -S "${HOST}:${PORT}" index.php
+4
View File
@@ -0,0 +1,4 @@
<?php declare(strict_types=1);
// Add the helpers globally
require_once ROOT . '/src/helpers.php';
View File
+10
View File
@@ -0,0 +1,10 @@
<?php declare(strict_types=1);
function escape_string(string $value): string
{
return htmlspecialchars(
$value,
ENT_QUOTES | ENT_SUBSTITUTE,
'UTF-8'
);
}
+98
View File
@@ -0,0 +1,98 @@
<?php declare(strict_types=1);
// -----------------------------------------------------------------------------
// Parse request URI
// Examples:
// / => /
// /about => /about
// /about/ => /about
// /about?id=1 => /about
// -----------------------------------------------------------------------------
$uri = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH);
if (!is_string($uri)) {
http_response_code(400);
exit;
}
$uri = rtrim($uri, '/');
$uri = $uri ?: '/';
// -----------------------------------------------------------------------------
// Request method
// -----------------------------------------------------------------------------
$method = strtoupper($_SERVER['REQUEST_METHOD'] ?? 'GET');
// -----------------------------------------------------------------------------
// Load route configuration
// config/routes.php:
// return [
// 'GET' => [
// '/' => 'home.php',
// '/about' => 'about.php',
// ],
// ];
// -----------------------------------------------------------------------------
$routes = require ROOT . '/config/routes.php';
if (!is_array($routes)) {
error_log('Router: routes.php must return an array');
http_response_code(500);
exit;
}
// -----------------------------------------------------------------------------
// Find matching route
// -----------------------------------------------------------------------------
$route = $routes[$method][$uri] ?? null;
// -----------------------------------------------------------------------------
// Base directory containing all page files
// Every routed file MUST live inside this directory.
// -----------------------------------------------------------------------------
$base = realpath(ROOT . '/src/views/pages');
if ($base === false) {
error_log('Router: pages directory not found');
http_response_code(500);
exit;
}
// -----------------------------------------------------------------------------
// Route not found
// -----------------------------------------------------------------------------
if ($route === null) {
http_response_code(404);
$route = '404.php';
}
// -----------------------------------------------------------------------------
// Resolve requested file
// realpath() converts:
// home.php
// ../home.php
// ../../etc/passwd
// into an absolute canonical path.
// -----------------------------------------------------------------------------
$real = realpath($base . '/' . $route);
// -----------------------------------------------------------------------------
// Path traversal protection
// Only allow files that resolve inside:
// src/views/pages/
//
// This blocks attempts such as:
// ../../etc/passwd
// ../../../secret.php
// even if they somehow end up in the route table.
// -----------------------------------------------------------------------------
if ($real === false || !str_starts_with($real, $base . DIRECTORY_SEPARATOR)) {
error_log("Router: invalid route target [$route]");
http_response_code(403);
exit;
}
// -----------------------------------------------------------------------------
// Dispatch request
// -----------------------------------------------------------------------------
require $real;
View File
+1
View File
@@ -0,0 +1 @@
<p>about</p>
+1
View File
@@ -0,0 +1 @@
<p>home</p>