From d1ef15fa39935bfa0420c5ac2b8c269e294c9a6d Mon Sep 17 00:00:00 2001 From: Thomas Vanbesien Date: Sat, 21 Mar 2026 20:50:43 +0100 Subject: Initial project scaffold Set up MVC architecture with front controller, router, autoloader, database singleton, and Docker Compose stack (Nginx + PHP-FPM + MariaDB). Includes DB schema, responsive layout, dev tooling (php-cs-fixer, parallel-lint), and documentation. --- src/app/Controllers/HomeController.php | 21 +++++++++++ src/app/Database.php | 44 +++++++++++++++++++++++ src/app/Router.php | 56 +++++++++++++++++++++++++++++ src/app/Views/home/index.php | 3 ++ src/app/Views/layouts/main.php | 35 +++++++++++++++++++ src/app/bootstrap.php | 42 ++++++++++++++++++++++ src/config/routes.php | 7 ++++ src/public/assets/overlays/.gitkeep | 0 src/public/css/style.css | 64 ++++++++++++++++++++++++++++++++++ src/public/index.php | 11 ++++++ src/public/js/app.js | 3 ++ src/uploads/.gitkeep | 0 12 files changed, 286 insertions(+) create mode 100644 src/app/Controllers/HomeController.php create mode 100644 src/app/Database.php create mode 100644 src/app/Router.php create mode 100644 src/app/Views/home/index.php create mode 100644 src/app/Views/layouts/main.php create mode 100644 src/app/bootstrap.php create mode 100644 src/config/routes.php create mode 100644 src/public/assets/overlays/.gitkeep create mode 100644 src/public/css/style.css create mode 100644 src/public/index.php create mode 100644 src/public/js/app.js create mode 100644 src/uploads/.gitkeep (limited to 'src') diff --git a/src/app/Controllers/HomeController.php b/src/app/Controllers/HomeController.php new file mode 100644 index 0000000..8c462a0 --- /dev/null +++ b/src/app/Controllers/HomeController.php @@ -0,0 +1,21 @@ +getPdo(); + $stmt = $db->query('SELECT 1'); + $dbStatus = $stmt ? 'Connected' : 'Failed'; + + $content = __DIR__ . '/../Views/home/index.php'; + include __DIR__ . '/../Views/layouts/main.php'; + } +} diff --git a/src/app/Database.php b/src/app/Database.php new file mode 100644 index 0000000..f4dff79 --- /dev/null +++ b/src/app/Database.php @@ -0,0 +1,44 @@ +pdo = new \PDO( + "mysql:host=$host;dbname=$db;charset=utf8mb4", + $user, + $pass, + [ + \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, + \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC, + \PDO::ATTR_EMULATE_PREPARES => false, + ] + ); + } + + public static function getInstance(): self + { + if (self::$instance === null) { + self::$instance = new self(); + } + return self::$instance; + } + + public function getPdo(): \PDO + { + return $this->pdo; + } +} diff --git a/src/app/Router.php b/src/app/Router.php new file mode 100644 index 0000000..9bfe09d --- /dev/null +++ b/src/app/Router.php @@ -0,0 +1,56 @@ +routes[] = ['GET', $path, $controller, $method]; + } + + public function post(string $path, string $controller, string $method): void + { + $this->routes[] = ['POST', $path, $controller, $method]; + } + + public function dispatch(): void + { + $requestMethod = $_SERVER['REQUEST_METHOD']; + $uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); + $uri = rtrim($uri, '/') ?: '/'; + + foreach ($this->routes as [$method, $path, $controller, $action]) { + if ($method !== $requestMethod) { + continue; + } + + // Convert route placeholders to named regex capture groups. + // Example with route '/post/{id}' and request '/post/42': + // 1. preg_replace turns '{id}' into '(?P[^/]+)' + // result: '#^/post/(?P[^/]+)$#' + // 2. preg_match against '/post/42' produces: + // [0 => '/post/42', 'id' => '42', 1 => '42'] + // 3. array_filter keeps only string keys: ['id' => '42'] + // 4. PostController->show('42') is called + $pattern = preg_replace('#\{(\w+)\}#', '(?P<$1>[^/]+)', $path); + $pattern = '#^' . $pattern . '$#'; + + if (preg_match($pattern, $uri, $matches)) { + $params = array_filter($matches, 'is_string', ARRAY_FILTER_USE_KEY); + $controllerClass = "App\\Controllers\\$controller"; + $instance = new $controllerClass(); + $instance->$action(...array_values($params)); + return; + } + } + + http_response_code(404); + echo '404 Not Found'; + } +} diff --git a/src/app/Views/home/index.php b/src/app/Views/home/index.php new file mode 100644 index 0000000..f5cde93 --- /dev/null +++ b/src/app/Views/home/index.php @@ -0,0 +1,3 @@ + +

Welcome to Camagru

+

Database status:

diff --git a/src/app/Views/layouts/main.php b/src/app/Views/layouts/main.php new file mode 100644 index 0000000..b4c7dad --- /dev/null +++ b/src/app/Views/layouts/main.php @@ -0,0 +1,35 @@ + + + + + + + Camagru + + + +
+ +
+
+ +
+
+

Camagru ©

+
+ + + diff --git a/src/app/bootstrap.php b/src/app/bootstrap.php new file mode 100644 index 0000000..835615b --- /dev/null +++ b/src/app/bootstrap.php @@ -0,0 +1,42 @@ +get('/', 'HomeController', 'index'); diff --git a/src/public/assets/overlays/.gitkeep b/src/public/assets/overlays/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/public/css/style.css b/src/public/css/style.css new file mode 100644 index 0000000..5677e91 --- /dev/null +++ b/src/public/css/style.css @@ -0,0 +1,64 @@ +*, *::before, *::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + min-height: 100vh; + display: flex; + flex-direction: column; + color: #333; + background: #fafafa; +} + +header { + background: #fff; + border-bottom: 1px solid #dbdbdb; + padding: 0.75rem 1rem; +} + +header nav { + max-width: 960px; + margin: 0 auto; + display: flex; + justify-content: space-between; + align-items: center; +} + +.logo { + font-size: 1.5rem; + font-weight: bold; + text-decoration: none; + color: #333; +} + +.nav-links a { + margin-left: 1rem; + text-decoration: none; + color: #0095f6; +} + +main { + flex: 1; + max-width: 960px; + margin: 2rem auto; + padding: 0 1rem; + width: 100%; +} + +footer { + text-align: center; + padding: 1rem; + color: #999; + font-size: 0.85rem; + border-top: 1px solid #dbdbdb; +} + +@media (max-width: 600px) { + header nav { + flex-direction: column; + gap: 0.5rem; + } +} diff --git a/src/public/index.php b/src/public/index.php new file mode 100644 index 0000000..2709bc0 --- /dev/null +++ b/src/public/index.php @@ -0,0 +1,11 @@ +dispatch(); diff --git a/src/public/js/app.js b/src/public/js/app.js new file mode 100644 index 0000000..86b3c33 --- /dev/null +++ b/src/public/js/app.js @@ -0,0 +1,3 @@ +document.addEventListener('DOMContentLoaded', function () { + // App initialization +}); diff --git a/src/uploads/.gitkeep b/src/uploads/.gitkeep new file mode 100644 index 0000000..e69de29 -- cgit v1.2.3