redirect zu /datei RewriteCond %{THE_REQUEST} \s/+(.+?)\.php[\s?] [NC] RewriteRule ^ /%1 [R=301,L] # /datei -> datei.php RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{DOCUMENT_ROOT}/$1.php -f RewriteRule ^(.+)$ $1.php [L] HTACCESS; $files[$projectName . '.code-workspace'] = <<prepare($sql); if (!$stmt) { die("Fehler beim Prepare: " . $conn->error); } if (!$stmt->execute()) { die("Fehler beim Execute: " . $stmt->error); } return true; } function DatabaseShowTables() { include_once(__DIR__ . '/../database/databaseConnect.php'); $conn = db_connect(); $tablesResult = $conn->query("SHOW TABLES"); if (!$tablesResult) { die("Fehler beim Laden der Tabellen: " . $conn->error); } $tables = []; while ($tableRow = $tablesResult->fetch_array()) { $tables[] = $tableRow[0]; } return $tables; } function DatabaseShowTableStructure($table) { include_once(__DIR__ . '/../database/databaseConnect.php'); $conn = db_connect(); $stmt = $conn->prepare("SHOW COLUMNS FROM `$table`"); if (!$stmt) { die("Fehler beim Prepare: " . $conn->error); } if (!$stmt->execute()) { die("Fehler beim Execute: " . $stmt->error); } $result = $stmt->get_result(); $columns = []; while ($column = $result->fetch_assoc()) { $columns[] = [ "Field" => $column["Field"], "Type" => $column["Type"], "Null" => $column["Null"], "Key" => $column["Key"], "Default" => $column["Default"], "Extra" => $column["Extra"] ]; } return $columns; } function DatabaseShow($table, $columns = "*", $where = "", ...$values) { include_once(__DIR__ . '/../database/databaseConnect.php'); $conn = db_connect(); $sql = "SELECT $columns FROM `$table`"; if (!empty($where)) { $sql .= " WHERE $where"; } $stmt = $conn->prepare($sql); if (!$stmt) { die("Fehler beim Prepare: " . $conn->error); } if (!$stmt->execute($values)) { die("Fehler beim Execute: " . $stmt->error); } $result = $stmt->get_result(); return $result->fetch_all(MYSQLI_ASSOC); } function DatabaseInsert($table, $columns, ...$values) { include_once(__DIR__ . '/../database/databaseConnect.php'); $conn = db_connect(); $placeholders = rtrim(str_repeat("?,", count($values)), ","); $sql = "INSERT INTO `$table` ($columns) VALUES ($placeholders)"; $stmt = $conn->prepare($sql); if (!$stmt) { die("Fehler beim Prepare: " . $conn->error); } if (!$stmt->execute($values)) { die("Fehler beim Execute: " . $stmt->error); } return true; } function DatabaseDelete($table, $where, ...$values) { include_once(__DIR__ . '/../database/databaseConnect.php'); $conn = db_connect(); $sql = "DELETE FROM `$table` WHERE $where"; $stmt = $conn->prepare($sql); if (!$stmt) { die("Fehler beim Prepare: " . $conn->error); } if (!$stmt->execute($values)) { die("Fehler beim Execute: " . $stmt->error); } return true; } function DatabaseUpdate($table, $set, $where, ...$values) { include_once(__DIR__ . '/../database/databaseConnect.php'); $conn = db_connect(); $sql = "UPDATE `$table` SET $set WHERE $where"; $stmt = $conn->prepare($sql); if (!$stmt) { die("Fehler beim Prepare: " . $conn->error); } if (!$stmt->execute($values)) { die("Fehler beim Execute: " . $stmt->error); } return true; } function getFKValue($conn, $table, $key, $value, $id, &$cache) { if ($id === null) return null; $cacheKey = $table . "_" . $id; if (isset($cache[$cacheKey])) { return $cache[$cacheKey]; } $stmt = $conn->prepare("SELECT `$value` FROM `$table` WHERE `$key` = ? LIMIT 1"); if (!$stmt) { die("FK Prepare Fehler: " . $conn->error); } $stmt->bind_param("i", $id); $stmt->execute(); $result = $stmt->get_result()->fetch_assoc(); $valueResult = $result[$value] ?? null; $cache[$cacheKey] = $valueResult; return $valueResult; } function DatabaseShowWithFK($table, $fkMap = [], $columns = "*", $where = "", ...$values) { include_once(__DIR__ . '/../database/databaseConnect.php'); $conn = db_connect(); $cache = []; $select = is_array($columns) ? implode(", ", $columns) : $columns; $sql = "SELECT $select FROM `$table`"; if (!empty($where)) { $sql .= " WHERE $where"; } $stmt = $conn->prepare($sql); if (!$stmt) die("Fehler beim Prepare: " . $conn->error); if (!empty($values)) { $stmt->execute($values); } else { $stmt->execute(); } $result = $stmt->get_result(); if (!$result) die("Fehler beim get_result: " . $conn->error); $rows = $result->fetch_all(MYSQLI_ASSOC); foreach ($rows as &$row) { foreach ($fkMap as $fkColumn => $fkInfo) { if (!isset($row[$fkColumn]) || $row[$fkColumn] === null) { continue; } $row[$fkColumn] = getFKValue( $conn, $fkInfo['table'], $fkInfo['key'], $fkInfo['value'], $row[$fkColumn], $cache ); } } return [ "rows" => $rows ]; } PHP; // --- CONFIG: env.ini --- $files['WEBROOT/src/config/env.ini'] = <<set_charset("utf8mb4"); return $conn; } catch (Exception $e) { return null; } } function db_connect_root() { $config = parse_ini_file(__DIR__ . '/../config/env.ini'); $env = strtoupper($config['APP_ENV']); try { $conn = new mysqli( $config["DB_{$env}_HOST"], $config["DB_ROOT_USERNAME"], $config["DB_ROOT_PASSWORD"] ); $conn->set_charset("utf8mb4"); return $conn; } catch (Exception $e) { return null; } } PHP; // -- CLASSES -- $files['WEBROOT/src/MVC/Classes/Api.php'] = <<<'PHP' $statusCode === 200 ? 'success' : 'error', 'data' => $data, ], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE ); exit; } } PHP; $files['WEBROOT/src/MVC/Classes/Client.php'] = <<<'PHP' country ?? 'UNKNOWN'; } public static function userAgent() { return $_SERVER['HTTP_USER_AGENT'] ?? 'UNKNOWN'; } } PHP; $files['WEBROOT/src/MVC/Classes/Cookie.php'] = <<<'PHP' getMessage(), $e->getFile(), $e->getLine(), $e->getTraceAsString()); self::log($msg, 'CRITICAL', 'error.log'); } public static function logError($errno, $errstr, $errfile, $errline) { $levels = [E_ERROR => 'ERROR', E_WARNING => 'WARNING', E_PARSE => 'PARSE_ERROR', E_NOTICE => 'NOTICE', E_USER_ERROR => 'USER_ERROR', E_USER_WARNING => 'USER_WARNING', E_USER_NOTICE => 'USER_NOTICE', E_DEPRECATED => 'DEPRECATED', E_USER_DEPRECATED => 'USER_DEPRECATED']; self::log("$errstr in $errfile on line $errline", $levels[$errno] ?? 'UNKNOWN', 'error.log'); } } PHP; $files['WEBROOT/src/MVC/Classes/Mailer.php'] = <<<'PHP' view = new View(__DIR__ . '/../View'); } protected function render($template, $data = []) { foreach ($data as $key => $value) { $this->view->assign($key, $value); } return $this->view->render($template); } protected function json($data, $status = 200) { header('Content-Type: application/json'); http_response_code($status); echo json_encode($data); exit; } } PHP; $files['WEBROOT/src/MVC/Core/Model.php'] = <<<'PHP' $value) { $sets[] = "$column = ?"; $values[] = $value; } $setString = implode(", ", $sets); return \DatabaseUpdate(static::$table, $setString, $where, ...array_merge($values, $whereParams)); } public static function delete($where, ...$params) { return \DatabaseDelete(static::$table, $where, ...$params); } public static function query($sql, ...$params) { $conn = self::getConnection(); if (empty($params)) { return $conn->query($sql); } $stmt = $conn->prepare($sql); $types = str_repeat('s', count($params)); $stmt->bind_param($types, ...$params); $stmt->execute(); return $stmt->get_result(); } } PHP; $files['WEBROOT/src/MVC/Core/View.php'] = <<<'PHP' viewPath = $viewPath; } public function assign($key, $value) { $this->data[$key] = $value; } public function render($template) { extract($this->data); $file = $this->viewPath . '/' . $template . '.php'; if (file_exists($file)) { ob_start(); include $file; return ob_get_clean(); } return "View $template not found at $file"; } } PHP; $files['WEBROOT/src/MVC/Core/Router.php'] = <<<'PHP' routes[$service] = [ 'controller' => $controller, 'method' => $method, 'middlewares' => $middlewares ]; } public function dispatch($service) { if (!isset($this->routes[$service])) { throw new \Exception("Service '$service' not found."); } $route = $this->routes[$service]; // Run Middlewares foreach ($route['middlewares'] as $middleware) { $this->runMiddleware($middleware); } $controllerClass = $route['controller']; $method = $route['method']; if (!class_exists($controllerClass)) { throw new \Exception("Controller class '$controllerClass' not found."); } $controller = new $controllerClass(); if (!method_exists($controller, $method)) { throw new \Exception("Method '$method' not found in controller '$controllerClass'."); } return $controller->$method(); } protected function runMiddleware($middleware) { if ($middleware === 'admin') { if (!isset($_SESSION['is_admin']) || $_SESSION['is_admin'] !== true) { die("Access Denied: Admin rights required."); } } if ($middleware === 'auth') { if (empty($_SESSION['user_logged_in'])) { header('Location: ?service=login'); exit; } } } } PHP; $files['WEBROOT/src/MVC/Layout/Layout.php'] = <<<'PHP' template = $template; } public function setBlock($name, $content) { $this->blocks[$name] = $content; } public function getBlock($name) { return $this->blocks[$name] ?? ''; } public function render() { $layoutFile = __DIR__ . '/Templates/' . $this->template . '.php'; if (file_exists($layoutFile)) { ob_start(); include $layoutFile; return ob_get_clean(); } return "Layout template nicht gefunden."; } } PHP; $files['WEBROOT/src/MVC/autoloader.php'] = <<<'PHP' <?= $this->getBlock('title') ?>
getBlock('header_actions') ?>
getBlock('content') ?>
HTML; // --- CONTROLLERS --- $files['WEBROOT/src/MVC/Controller/AdminController.php'] = <<<'PHP' "success", "text" => "Mail gesendet!"]; else $msg = ["type" => "error", "text" => "Fehler!"]; } return $this->render('admin/mail', ['message' => $msg]); } public function user_service() { $msg = null; if ($_SERVER['REQUEST_METHOD'] === 'POST') { $user = $_POST['username'] ?? ''; $pass = $_POST['password'] ?? ''; if ($user && $pass) { if (Users::insert(['USERNAME' => $user, 'PASSWORD' => password_hash($pass, PASSWORD_DEFAULT), 'EMAIL' => $_POST['email'] ?? '', 'ROLE' => $_POST['role'] ?? 'user'])) $msg = ["type" => "success", "text" => "User $user erstellt!"]; else $msg = ["type" => "error", "text" => "Fehler (DB)!"]; } else $msg = ["type" => "error", "text" => "Name/Passwort fehlen!"]; } return $this->render('admin/users', ['message' => $msg]); } } PHP; $files['WEBROOT/src/MVC/View/admin/mail.php'] = <<<'HTML'

📧 E-Mail Service

HTML; $files['WEBROOT/src/MVC/View/admin/users.php'] = <<<'HTML'

👤 Benutzerverwaltung

HTML; $files['WEBROOT/src/MVC/Controller/ApiController.php'] = <<<'PHP' 'DummyData', ]); } } PHP; $files['WEBROOT/src/MVC/Controller/HomeController.php'] = <<<'PHP' render('home'); } public function toggle_theme() { $_SESSION['theme'] = ($_SESSION['theme'] ?? 'light') === 'dark' ? 'light' : 'dark'; return $this->json( [ 'success' => true ] ); } } PHP; $files['WEBROOT/src/MVC/Controller/AuthController.php'] = <<<'PHP' render('login', ['error' => 'Ungültige Anmeldedaten']); } } return $this->render('login'); } public function logout() { session_destroy(); header('Location: ?service=login'); exit; } } PHP; $files['WEBROOT/src/MVC/Controller/DocsController.php'] = <<<'PHP' render('docs', [ 'title' => '📚 System-Dokumentation' ]); } } PHP; $files['WEBROOT/src/MVC/Controller/TestController.php'] = <<<'PHP' render( 'test1', [ 'msg' => 'Service 1 ist bereit!' ] ); } public function service2() { return $this->render( 'test2', [ 'data' => [ 'Layout' => 'Header + Sidebar', 'Theme' => 'Auto-Toggle' ] ] ); } } PHP; // --- API --- $files['WEBROOT/api/v1/get.php'] = <<<'PHP' get()); } catch (\Exception $e) { echo ($e->getMessage()); } PHP; // --- VIEWS --- $files['WEBROOT/src/MVC/View/home.php'] = "

Dashboard

Willkommen in deinem neuen MVC-System mit integriertem Header.

"; $files['WEBROOT/src/MVC/View/test1.php'] = "

Service 1

"; $files['WEBROOT/src/MVC/View/test2.php'] = "

System Check

"; $files['WEBROOT/src/MVC/View/docs.php'] = <<<'HTML'

🏛️ Framework Architektur & Dokumentation

1. Einführung

Dieses Framework basiert auf dem Model-View-Controller (MVC) Entwurfsmuster und nutzt einen Front Controller (index.php). Alle Anfragen werden zentral verarbeitet, was globale Middleware (wie Authentifizierung) und ein einheitliches Layout ermöglicht.

2. Datei-Referenz (Deep Dive)

Framework Core

  • Router.php: Verwaltet die Routen. Unterstützt Middlewares. Das Dispatching erfolgt über den service Parameter in der URL.
  • Controller.php: Basisklasse für alle Controller. Bietet render() für Views und json() für API-Responses.
  • Model.php: Abstrakte Basis für Datenbank-Entitäten. Kapselt die databaseMethods.php in ein objektorientiertes Interface.
  • View.php: Behandelt das Extrahieren von Daten in HTML-Templates.

Konfiguration

  • env.ini: Speichert Datenbank-Zugangsdaten für dev und live Umgebungen.
  • autoloader.php: Registriert einen PSR-4 ähnlichen Autoloader für den MVC\ Namespace.

3. API-System

APIs liegen im Verzeichnis WEBROOT/api/v1/. Sie nutzen die ApiController-Klasse.

// Beispiel: WEBROOT/api/v1/get.php
use MVC\Classes\Api;
use MVC\Controller\ApiController;

Api::jsonResponse((new ApiController())->get());
        

4. Datenbank & Models

Das Framework nutzt mysqli mit Prepared Statements für maximale Sicherheit.

Installation (install_db.php)

Mit dem Befehl php install_db.php können vordefinierte Tabellen und deren Models automatisch erstellt werden. Standardmäßig sind folgende Tabellen enthalten:

  • Mailing: Für das E-Mail-Logging (wird vom Mailer genutzt).
  • Users: Eine Grundstruktur für Benutzer (ID, Username, Password, Email, Role, Created_at).

databaseMethods.php (Die Power-Tools)

  • DatabaseShow($table, $columns, $where, ...$values)
  • DatabaseInsert($table, $columns, ...$values)
  • DatabaseUpdate($table, $set, $where, ...$values)
  • DatabaseDelete($table, $where, ...$values)

5. Helper Klassen (MVC\Classes)

  • Request: Zugriff auf method(), queryParams(), headers().
  • Client: Ermittelt ip(), userAgent(), country().
  • Cookie: Interface zum set(), read(), Delete() von Cookies.

6. CLI-Generator

php make.php module [Name] - Erstellt Controller, View, Route & Nav.

php make.php model [Tabelle] [Spalten] - Erstellt ein Model.

7. Mailing-System

Das Framework verfügt über eine integrierte Mailer-Klasse für den E-Mail-Versand und die Protokollierung.

Konfiguration

In der env.ini können Absender und Empfänger unter [mail] definiert werden.

Nutzung

use MVC\Classes\Mailer;

// Einfache Mail
Mailer::send("Betreff", "Inhalt");

// Error Mail (wird speziell markiert)
Mailer::error("Fehlertitel", "Details");
        

Logging & Fallback

Der Mailer versucht standardmäßig in die Datenbank-Tabelle Mailing zu loggen. Falls diese nicht existiert, erfolgt ein automatischer Fallback in Textdateien im Ordner /logs/.

Installation: Führe php install_db.php aus, um die Datenbank-Struktur anzulegen.

8. Error-Logging

Das Framework verfügt über ein zentrales Logging-System, das in der index.php global registriert ist.

Automatische Erfassung

Alle unangefangenen Exceptions und PHP-Fehler (Warnings, Notices, etc.) werden automatisch in die Datei /logs/error.log geschrieben, inklusive Stacktrace bei Exceptions.

Manuelle Nutzung

use MVC\Classes\Logger;

// Einfache Nachricht in system.log
Logger::log("Etwas ist passiert");

// Spezifisches Level und Datei
Logger::log("Kritischer Fehler", "ERROR", "error.log");
        

Die Log-Dateien befinden sich im Hauptverzeichnis des Projekts im Ordner /logs/.

9. Best Practices

Naming: Controller immer mit Controller-Suffix. Models im Singular.

Security: Nutze ['auth'] Middleware konsequent für alle internen Seiten.

HTML; $files['WEBROOT/src/MVC/View/login.php'] = <<<'HTML' HTML; // --- CONFIG: routes.php --- $files['WEBROOT/src/config/routes.php'] = <<<'PHP' add(service_name, controller_class, method_name, options); */ $router->add('home', \MVC\Controller\HomeController::class, 'index'); $router->add('toggle_theme', \MVC\Controller\HomeController::class, 'toggle_theme'); $router->add('docs', \MVC\Controller\DocsController::class, 'index'); $router->add('test1', \MVC\Controller\TestController::class, 'service1', ['auth']); $router->add('test2', \MVC\Controller\TestController::class, 'service2', ['auth']); $router->add('login', \MVC\Controller\AuthController::class, 'login'); $router->add('logout', \MVC\Controller\AuthController::class, 'logout'); $router->add('admin_mail', \MVC\Controller\AdminController::class, 'mail_service', ['auth', 'admin']); $router->add('admin_users', \MVC\Controller\AdminController::class, 'user_service', ['auth', 'admin']); PHP; // --- CONFIG: navigation.php --- $files['WEBROOT/src/config/navigation.php'] = <<<'PHP' ['label' => 'Label', 'auth' => true/false] */ return [ 'home' => ['label' => '🏠 Home', 'auth' => false], 'test1' => ['label' => '🛠️ Test 1', 'auth' => true], 'test2' => ['label' => '📊 Test 2', 'auth' => true], 'admin_mail' => ['label' => '📧 Mail Service', 'auth' => true], 'admin_users' => ['label' => '👤 User Service', 'auth' => true], 'docs' => ['label' => '📚 Dokumentation', 'auth' => false], ]; PHP; // --- ENTRY POINT --- $files['WEBROOT/index.php'] = <<<'PHP' setBlock('title', 'MVC Starter'); $layout->setBlock('header_title', '🚀 MVC Dashboard'); $headerActions = ''; if (isset($_SESSION['user_logged_in'])) { $headerActions = '👤 ' . htmlspecialchars($_SESSION['username']) . '' . '🚪 Logout ' . $headerActions; } else { $headerActions = '🔑 Login ' . $headerActions; } $layout->setBlock('header_actions', $headerActions); // Sidebar Block ob_start(); // Load navigation from config $navItems = require __DIR__ . '/src/config/navigation.php'; ?> setBlock('sidebar', ob_get_clean()); $layout->setBlock('content', $router->dispatch($service)); echo $layout->render(); } catch (\Throwable $e) { Logger::logException($e); http_response_code(500); echo "

Error

" . htmlspecialchars($e->getMessage()) . "

"; if (ini_get('display_errors')) echo "
" . htmlspecialchars($e->getTraceAsString()) . "
"; } PHP; // --- GENERATOR: make.php --- $files['make.php'] = <<<'PHP' [args] */ if (php_sapi_name() !== 'cli') { die("Dieses Script muss über die CLI ausgeführt werden.\n"); } $command = $argv[1] ?? null; if (!$command || in_array($command, ['help', '-h', '--help'])) { showHelp(); exit; } switch ($command) { case 'model': createModel(array_slice($argv, 2)); break; case 'module': createModule(array_slice($argv, 2)); break; default: echo "❌ Unbekannter Befehl: $command\n"; showHelp(); exit(1); } function showHelp() { echo "MVC CLI Tool\n"; echo "-----------\n"; echo "Nutzung: php make.php [optionen]\n\n"; echo "Befehle:\n"; echo " model Erstellt ein neues Model basierend auf einer Datenbanktabelle\n"; echo " module Erstellt ein neues Modul (Controller, View, Route, Navigation)\n\n"; echo "Details zu model:\n"; echo " php make.php model \n\n"; echo "Details zu module:\n"; echo " php make.php module [optionen]\n"; echo " Optionen:\n"; echo " -c, --controller Nur Controller erstellen\n"; echo " -v, --view Nur View erstellen\n"; echo " -r, --route Nur Route hinzufügen\n"; echo " -n, --nav Nur Navigationspunkt hinzufügen\n"; echo " -a, --auth Route/Nav erfordert Login\n"; echo " -l, --label Label für Navigationspunkt\n"; } function createModel($args) { $tableName = $args[0] ?? null; $columnsInput = $args[1] ?? null; // Interaktive Abfrage, falls Argumente fehlen if (!$tableName) { echo "Tabellenname eingeben (z.B. Benutzer): "; $handle = fopen("php://stdin", "r"); $tableName = trim(fgets($handle)); fclose($handle); } if (!$columnsInput) { echo "Spalten kommagetrennt eingeben (z.B. NAME,EMAIL,ROLLE): "; $handle = fopen("php://stdin", "r"); $columnsInput = trim(fgets($handle)); fclose($handle); } if (!$tableName) die("❌ Fehler: Kein Tabellenname angegeben.\n"); // Namen formatieren $className = str_replace(' ', '', ucwords(str_replace('_', ' ', strtolower($tableName)))); $modelFile = __DIR__ . "/WEBROOT/src/MVC/Model/{$className}.php"; // Spalten aufbereiten $columns = array_map('trim', explode(',', $columnsInput)); $columnList = implode("', '", $columns); // Model-Inhalt generieren $content = <<render('{$key}', [ 'title' => '{$className} Modul', 'message' => 'Willkommen in deinem neuen Modul {$className}!' ]); } } PHPCTRL; if (!file_exists($controllerFile)) { file_put_contents($controllerFile, $controllerContent); echo "✅ Controller erstellt: WEBROOT/src/MVC/Controller/{$className}Controller.php\n"; } else { echo "⚠️ Controller existiert bereits.\n"; } } // 2. View erstellen if ($doView) { $viewContent = <<

Hier kannst du nun dein Projekt aufbauen.

HTML; if (!file_exists($viewFile)) { file_put_contents($viewFile, $viewContent); echo "✅ View erstellt: WEBROOT/src/MVC/View/{$key}.php\n"; } else { echo "⚠️ View existiert bereits.\n"; } } // 3. Route hinzufügen if ($doRoute) { if (file_exists($routesFile)) { $routesContent = file_get_contents($routesFile); $routeLine = "\$router->add('{$key}', \\MVC\\Controller\\{$className}Controller::class, '{$key}'" . ($isAuth ? ", ['auth']" : "") . ");\n"; if (strpos($routesContent, "'{$key}'") === false) { file_put_contents($routesFile, $routesContent . $routeLine); echo "✅ Route hinzugefügt: {$routesFile}\n"; } else { echo "⚠️ Route für '{$key}' existiert bereits in routes.php\n"; } } else { echo "❌ Fehler: routes.php nicht gefunden!\n"; } } // 4. Navigation hinzufügen if ($doNav) { if (file_exists($navFile)) { $navContent = file_get_contents($navFile); $authStr = $isAuth ? 'true' : 'false'; $navItem = " '{$key}' => ['label' => '{$label}', 'auth' => {$authStr}],\n"; if (strpos($navContent, "'{$key}'") === false) { // Wir suchen nach dem Ende des Arrays ]; $newNavContent = preg_replace('/(\];\s*)$/', $navItem . '$1', trim($navContent)); file_put_contents($navFile, $newNavContent . "\n"); echo "✅ Navigation hinzugefügt: {$navFile}\n"; } else { echo "⚠️ Navigation für '{$key}' existiert bereits in navigation.php\n"; } } else { echo "❌ Fehler: navigation.php nicht gefunden!\n"; } } echo "\nFertig!\n"; } PHP; $files['Dockerfile'] = <<<'DOCKER' FROM php:8.2-apache RUN apt-get update && apt-get install -y libpng-dev libonig-dev libxml2-dev zip unzip curl RUN apt-get clean && rm -rf /var/lib/apt/lists/* RUN docker-php-ext-install mysqli pdo_mysql mbstring exif pcntl bcmath gd RUN a2enmod rewrite WORKDIR /var/www/html ENV APACHE_DOCUMENT_ROOT /var/www/html/WEBROOT RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf RUN sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf EXPOSE 80 DOCKER; $files['docker-compose.yml'] = <<<'YAML' version: '3.8' services: web: build: . container_name: mvc_web ports: - "8080:80" volumes: - .:/var/www/html depends_on: - db networks: - mvc_network db: image: mariadb:10.11 container_name: mvc_db restart: always environment: MARIADB_ROOT_PASSWORD: root_password MARIADB_DATABASE: mvc_database MARIADB_USER: mvc_user MARIADB_PASSWORD: mvc_password networks: - mvc_network networks: mvc_network: driver: bridge YAML; $files['install_db.php'] = <<<'PHP' query("CREATE DATABASE IF NOT EXISTS `$dbName` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"); // 2. Worker User erstellen und berechtigen (intern & extern) echo "👤 Erstelle Worker-User '$workerUser'...\n"; $rootConn->query("CREATE USER IF NOT EXISTS '$workerUser'@'localhost' IDENTIFIED BY '$workerPass'"); $rootConn->query("CREATE USER IF NOT EXISTS '$workerUser'@'%' IDENTIFIED BY '$workerPass'"); echo "🔐 Vergebe Berechtigungen...\n"; $rootConn->query("GRANT ALL PRIVILEGES ON `$dbName`.* TO '$workerUser'@'localhost'"); $rootConn->query("GRANT ALL PRIVILEGES ON `$dbName`.* TO '$workerUser'@'%'"); $rootConn->query("FLUSH PRIVILEGES"); $rootConn->close(); echo "🚀 Datenbank-Tabellen-Installation...\n"; $tables = [ 'Mailing' => [ 'columns' => "`ID` INT AUTO_INCREMENT PRIMARY KEY, `DATUM` DATETIME NOT NULL, `SUBJECT` VARCHAR(255), `MESSAGE` TEXT, `URL` VARCHAR(255), `CLIENT_IP` VARCHAR(45)", 'model' => " \$date, 'SUBJECT' => \$subject, 'MESSAGE' => \$message, 'URL' => \$url, 'CLIENT_IP' => \$ip]);\n }\n}" ], 'Users' => [ 'columns' => "`ID` INT AUTO_INCREMENT PRIMARY KEY, `USERNAME` VARCHAR(50) UNIQUE NOT NULL, `PASSWORD` VARCHAR(255) NOT NULL, `EMAIL` VARCHAR(100), `ROLE` VARCHAR(20) DEFAULT 'user', `CREATED_AT` DATETIME DEFAULT CURRENT_TIMESTAMP", 'model' => " $info) { echo "--- $name ---\n"; try { if (DatabaseCreateTable($name, $info['columns'])) { $path = __DIR__ . "/WEBROOT/src/MVC/Model/{$name}.php"; if (!file_exists($path)) { if (!is_dir(dirname($path))) mkdir(dirname($path), 0755, true); file_put_contents($path, $info['model']); echo "✅ bereit.\n"; } else { echo "ℹ️ Model existiert bereits.\n"; } } } catch (Exception $e) { echo "❌: " . $e->getMessage() . "\n"; } } // 3. Test-User anlegen echo "👤 Erstelle Test-User...\n"; require_once __DIR__ . '/WEBROOT/src/MVC/Model/Users.php'; try { if (!\MVC\Model\Users::getByUsername('admin')) { \MVC\Model\Users::insert([ 'USERNAME' => 'admin', 'PASSWORD' => password_hash('admin123', PASSWORD_DEFAULT), 'EMAIL' => 'admin@example.com', 'ROLE' => 'admin' ]); echo "✅ Test-User 'admin' mit Passwort 'admin123' erstellt.\n"; } else { echo "ℹ️ Test-User 'admin' existiert bereits.\n"; } } catch (Exception $e) { echo "❌ Fehler beim Erstellen des Test-Users: " . $e->getMessage() . "\n"; } echo "\n✨ Installation abgeschlossen.\n"; PHP; // 3. Dateien schreiben foreach ($files as $file => $content) { // Docker-Dateien und die .code-workspace Datei sollen im Verzeichnis der init.php (Root) landen if ($file === 'Dockerfile' || $file === 'docker-compose.yml' || str_ends_with($file, '.code-workspace')) { $path = getcwd() . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $file); } else { $path = $baseDir . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $file); } file_put_contents($path, $content); echo "📄 Datei erstellt: $file\n"; } echo "\n✨ Projekt '$projectName' wurde initialisiert!\n"; echo "1. Richte deine Datenbankverbindung in WEBROOT/src/config/env.ini ein.\n"; echo "2. Datenbank installieren: php install_db.php\n"; echo " (Ohne Tabelle werden Logs in /logs/ gespeichert)\n"; echo "3. CLI-Tool nutzen: php make.php help\n"; echo "4. Starten: php server.php\n"; echo "5. Öffnen: http://localhost:8000\n";