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
|
<?php
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'GET' => [
|
'GET' => [
|
||||||
'/' => 'home.php',
|
'/' => 'home.php',
|
||||||
'/about' => 'about.php',
|
'/about' => 'about.php',
|
||||||
|
'/sqlite' => 'sqlite.php',
|
||||||
|
'/api/test' => 'api/example.php'
|
||||||
],
|
],
|
||||||
/* Some Examples
|
|
||||||
'POST' => [
|
'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);
|
<?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);
|
<?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
|
function escape_string(string $value): string
|
||||||
{
|
{
|
||||||
return htmlspecialchars(
|
return htmlspecialchars(
|
||||||
@@ -8,3 +12,76 @@ function escape_string(string $value): string
|
|||||||
'UTF-8'
|
'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);
|
<?php declare(strict_types=1);
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Parse request URI
|
// Parse Request URI
|
||||||
// Examples:
|
// Examples:
|
||||||
// / => /
|
// / => /
|
||||||
// /about => /about
|
// /about => /about
|
||||||
@@ -18,47 +18,22 @@ $uri = rtrim($uri, '/');
|
|||||||
$uri = $uri ?: '/';
|
$uri = $uri ?: '/';
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Request method
|
// Request Method
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
$method = strtoupper($_SERVER['REQUEST_METHOD'] ?? 'GET');
|
$method = strtoupper($_SERVER['REQUEST_METHOD'] ?? 'GET');
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Load route configuration
|
// Load Routes
|
||||||
// config/routes.php:
|
|
||||||
// return [
|
|
||||||
// 'GET' => [
|
|
||||||
// '/' => 'home.php',
|
|
||||||
// '/about' => 'about.php',
|
|
||||||
// ],
|
|
||||||
// ];
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
$routes = require ROOT . '/config/routes.php';
|
$routes = get_config('routes');
|
||||||
|
|
||||||
if (!is_array($routes)) {
|
|
||||||
error_log('Router: routes.php must return an array');
|
|
||||||
http_response_code(500);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Find matching route
|
// Find Route
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
$route = $routes[$method][$uri] ?? null;
|
$route = $routes[$method][$uri] ?? null;
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Base directory containing all page files
|
// Route Not Found
|
||||||
// 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) {
|
if ($route === null) {
|
||||||
http_response_code(404);
|
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:
|
// realpath() converts:
|
||||||
// home.php
|
// home.php
|
||||||
// ../home.php
|
// ../home.php
|
||||||
// ../../etc/passwd
|
// ../../etc/passwd
|
||||||
// into an absolute canonical path.
|
// into an absolute canonical path.
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
$real = realpath($base . '/' . $route);
|
$real = realpath($base . '/' . $file);
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Path traversal protection
|
// Path Traversal Protection
|
||||||
// Only allow files that resolve inside:
|
|
||||||
// src/views/pages/
|
|
||||||
//
|
//
|
||||||
// This blocks attempts such as:
|
// Ensures the resolved file stays inside the expected base directory.
|
||||||
|
// Examples blocked:
|
||||||
// ../../etc/passwd
|
// ../../etc/passwd
|
||||||
// ../../../secret.php
|
// ../../../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]");
|
error_log("Router: invalid route target [$route]");
|
||||||
http_response_code(403);
|
http_response_code(403);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Dispatch request
|
// Dispatch
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
require $real;
|
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