Initial setup of basics for every project.
This commit is contained in:
Regular → Executable
Regular → Executable
+6
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'enabled' => true,
|
||||
'path' => ROOT . '/storage/database.sqlite'
|
||||
];
|
||||
|
||||
Regular → Executable
+6
-9
@@ -1,16 +1,13 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'GET' => [
|
||||
'/' => 'home.php',
|
||||
'/about' => 'about.php',
|
||||
'/' => 'home.php',
|
||||
'/about' => 'about.php',
|
||||
'/sqlite' => 'sqlite.php',
|
||||
'/api/test' => 'api/example.php'
|
||||
],
|
||||
/* Some Examples
|
||||
|
||||
'POST' => [
|
||||
'/contact' => 'contact.php',
|
||||
'/api/test' => 'api/example.php'
|
||||
],
|
||||
'DELETE' => [
|
||||
'/api/todo' => 'delete_todo.php',
|
||||
],
|
||||
*/
|
||||
];
|
||||
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
body {
|
||||
font-family: system-ui, sans-serif;
|
||||
max-width:960px;
|
||||
margin:0 auto;
|
||||
padding:2rem;
|
||||
}
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
'use strict';
|
||||
console.log('miniPHP loaded');
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['success'=>true,'message'=>'API working']);
|
||||
Regular → Executable
+2
-2
@@ -1,4 +1,4 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
// Add the helpers globally
|
||||
require_once ROOT . '/src/helpers.php';
|
||||
require_once ROOT.'/src/helpers.php';
|
||||
require_once ROOT.'/src/database.php';
|
||||
|
||||
Regular → Executable
+19
@@ -0,0 +1,19 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
function db(): PDO
|
||||
{
|
||||
static $pdo=null;
|
||||
if ($pdo instanceof PDO) { return $pdo; }
|
||||
|
||||
$config = get_config('database');
|
||||
|
||||
if (!($config['enabled']??false)) {
|
||||
throw new RuntimeException('Database is disabled.');
|
||||
}
|
||||
|
||||
$pdo = new PDO('sqlite:'.$config['path']);
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
|
||||
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE,PDO::FETCH_ASSOC);
|
||||
|
||||
return $pdo;
|
||||
}
|
||||
|
||||
Regular → Executable
+77
@@ -1,5 +1,9 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Escape user-controlled output before rendering HTML.
|
||||
// Prevents XSS by converting special characters into HTML entities.
|
||||
// -----------------------------------------------------------------------------
|
||||
function escape_string(string $value): string
|
||||
{
|
||||
return htmlspecialchars(
|
||||
@@ -8,3 +12,76 @@ function escape_string(string $value): string
|
||||
'UTF-8'
|
||||
);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Dump a variable and terminate execution.
|
||||
// Useful when debugging requests, database results, configuration, etc.
|
||||
// -----------------------------------------------------------------------------
|
||||
function debugx(mixed $value): never
|
||||
{
|
||||
echo '<pre>';
|
||||
print_r($value);
|
||||
echo '</pre>';
|
||||
exit;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Dump a variable without terminating execution.
|
||||
// -----------------------------------------------------------------------------
|
||||
function debug(mixed $value): void
|
||||
{
|
||||
echo '<pre>';
|
||||
print_r($value);
|
||||
echo '</pre>';
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Load a configuration file from /config.
|
||||
// Config files are cached for the lifetime of the request to avoid
|
||||
// repeatedly requiring the same file.
|
||||
// -----------------------------------------------------------------------------
|
||||
function get_config(string $file): array
|
||||
{
|
||||
static $cache = [];
|
||||
|
||||
if (isset($cache[$file])) {
|
||||
return $cache[$file];
|
||||
}
|
||||
|
||||
$path = ROOT . '/config/' . $file . '.php';
|
||||
|
||||
if (!is_file($path)) {
|
||||
throw new RuntimeException("Config file not found: {$file}");
|
||||
}
|
||||
|
||||
$config = require $path;
|
||||
|
||||
if (!is_array($config)) {
|
||||
throw new RuntimeException("Config file must return an array: {$file}");
|
||||
}
|
||||
|
||||
return $cache[$file] = $config;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Render a reusable partial view.
|
||||
// Variables passed via $data become available inside the partial.
|
||||
// partial('head', [ 'title' => 'Home', ]);
|
||||
// Loads: src/views/partials/head.php
|
||||
// -----------------------------------------------------------------------------
|
||||
function partial(string $name, array $data = []): void
|
||||
{
|
||||
$file = ROOT . '/src/views/partials/' . $name . '.php';
|
||||
|
||||
if (!is_file($file)) {
|
||||
throw new RuntimeException(
|
||||
"Partial not found: {$name}"
|
||||
);
|
||||
}
|
||||
|
||||
// Extract provided variables into the local scope while preventing
|
||||
// accidental overwriting of existing variables.
|
||||
extract($data, EXTR_SKIP);
|
||||
|
||||
require $file;
|
||||
}
|
||||
|
||||
Regular → Executable
+38
-40
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
// -----------------------------------------------------------------------------
|
||||
// Parse request URI
|
||||
// Parse Request URI
|
||||
// Examples:
|
||||
// / => /
|
||||
// /about => /about
|
||||
@@ -18,47 +18,22 @@ $uri = rtrim($uri, '/');
|
||||
$uri = $uri ?: '/';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Request method
|
||||
// Request Method
|
||||
// -----------------------------------------------------------------------------
|
||||
$method = strtoupper($_SERVER['REQUEST_METHOD'] ?? 'GET');
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Load route configuration
|
||||
// config/routes.php:
|
||||
// return [
|
||||
// 'GET' => [
|
||||
// '/' => 'home.php',
|
||||
// '/about' => 'about.php',
|
||||
// ],
|
||||
// ];
|
||||
// Load Routes
|
||||
// -----------------------------------------------------------------------------
|
||||
$routes = require ROOT . '/config/routes.php';
|
||||
|
||||
if (!is_array($routes)) {
|
||||
error_log('Router: routes.php must return an array');
|
||||
http_response_code(500);
|
||||
exit;
|
||||
}
|
||||
$routes = get_config('routes');
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Find matching route
|
||||
// Find 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
|
||||
// Route Not Found
|
||||
// -----------------------------------------------------------------------------
|
||||
if ($route === null) {
|
||||
http_response_code(404);
|
||||
@@ -66,33 +41,56 @@ if ($route === null) {
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Resolve requested file
|
||||
// Determine Base Directory
|
||||
//
|
||||
// Routes beginning with:
|
||||
// api/
|
||||
// are loaded from:
|
||||
// src/api/
|
||||
// Everything else is loaded from:
|
||||
// src/views/pages/
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
if (str_starts_with($route, 'api/')) {
|
||||
$base = realpath(ROOT . '/src/api');
|
||||
$file = substr($route, 4);
|
||||
} else {
|
||||
$base = realpath(ROOT . '/src/views/pages');
|
||||
$file = $route;
|
||||
}
|
||||
|
||||
if ($base === false) {
|
||||
error_log('Router: base directory not found');
|
||||
http_response_code(500);
|
||||
exit;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Resolve File
|
||||
// realpath() converts:
|
||||
// home.php
|
||||
// ../home.php
|
||||
// ../../etc/passwd
|
||||
// into an absolute canonical path.
|
||||
// -----------------------------------------------------------------------------
|
||||
$real = realpath($base . '/' . $route);
|
||||
$real = realpath($base . '/' . $file);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Path traversal protection
|
||||
// Only allow files that resolve inside:
|
||||
// src/views/pages/
|
||||
// Path Traversal Protection
|
||||
//
|
||||
// This blocks attempts such as:
|
||||
// Ensures the resolved file stays inside the expected base directory.
|
||||
// Examples blocked:
|
||||
// ../../etc/passwd
|
||||
// ../../../secret.php
|
||||
// even if they somehow end up in the route table.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
if ($real === false || !str_starts_with($real, $base . DIRECTORY_SEPARATOR)) {
|
||||
if ($real === false || !str_starts_with( $real, $base . DIRECTORY_SEPARATOR)) {
|
||||
error_log("Router: invalid route target [$route]");
|
||||
http_response_code(403);
|
||||
exit;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Dispatch request
|
||||
// Dispatch
|
||||
// -----------------------------------------------------------------------------
|
||||
require $real;
|
||||
|
||||
Regular → Executable
+1
@@ -0,0 +1 @@
|
||||
<p>404</p>
|
||||
|
||||
Regular → Executable
+6
-1
@@ -1 +1,6 @@
|
||||
<p>about</p>
|
||||
<?php partial('head', [ 'title' => 'About', ]); ?>
|
||||
|
||||
<h1>About</h1>
|
||||
<p>random stuff</p>
|
||||
|
||||
<?php partial('footer'); ?>
|
||||
|
||||
Regular → Executable
+7
-1
@@ -1 +1,7 @@
|
||||
<p>home</p>
|
||||
<?php partial('head', [ 'title' => 'Home', ]); ?>
|
||||
|
||||
<h1>Home</h1>
|
||||
|
||||
<p>Hello world.</p>
|
||||
|
||||
<?php partial('footer'); ?>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
$db = db();
|
||||
|
||||
$db->exec("
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL
|
||||
)
|
||||
");
|
||||
|
||||
$db->exec(" INSERT INTO users (name) VALUES ('Jason') ");
|
||||
$users = $db ->query('SELECT * FROM users') ->fetchAll();
|
||||
|
||||
debugx($users);
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
<script src="/public/js/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title><?php echo escape_string($title ?? 'miniPHP') ?></title>
|
||||
<link rel="stylesheet" href="/public/css/app.css">
|
||||
</head>
|
||||
<body>
|
||||
Executable
Binary file not shown.
Reference in New Issue
Block a user