1795 lines
58 KiB
PHP
1795 lines
58 KiB
PHP
<?php
|
|
/**
|
|
* Ultimate MVC Project Bootstrapper (Fixed Version with Header & Auth)
|
|
* Erstellt ein voll funktionsfähiges Projekt inkl. Dev-Server, Header, DarkMode & Auth-System.
|
|
*/
|
|
|
|
if (php_sapi_name() !== 'cli') {
|
|
die("Dieses Script muss über die CLI ausgeführt werden.\n");
|
|
}
|
|
|
|
$projectName = $argv[1] ?? null;
|
|
if (!$projectName) {
|
|
echo "Bitte gib einen Projektnamen an: ";
|
|
$handle = fopen("php://stdin", "r");
|
|
$projectName = trim(fgets($handle));
|
|
fclose($handle);
|
|
}
|
|
|
|
if (!$projectName) die("Fehler: Kein Name angegeben.\n");
|
|
|
|
$baseDir = getcwd() . DIRECTORY_SEPARATOR . $projectName;
|
|
|
|
// 1. Verzeichnisstruktur erstellen
|
|
$dirs = [
|
|
'',
|
|
'WEBROOT',
|
|
'WEBROOT/api',
|
|
'WEBROOT/api/v1',
|
|
'WEBROOT/commands',
|
|
'WEBROOT/static',
|
|
'WEBROOT/src',
|
|
'WEBROOT/src/config',
|
|
'WEBROOT/src/database',
|
|
'WEBROOT/src/utils',
|
|
'WEBROOT/src/MVC',
|
|
'WEBROOT/src/MVC/Core',
|
|
'WEBROOT/src/MVC/Classes',
|
|
'WEBROOT/src/MVC/Controller',
|
|
'WEBROOT/src/MVC/Model',
|
|
'WEBROOT/src/MVC/View',
|
|
'WEBROOT/src/MVC/View/admin',
|
|
'WEBROOT/src/MVC/Layout',
|
|
'WEBROOT/src/MVC/Layout/Templates',
|
|
'WEBROOT/logs',
|
|
];
|
|
|
|
foreach ($dirs as $dir) {
|
|
$path = $baseDir . ($dir ? DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $dir) : '');
|
|
if (!is_dir($path)) {
|
|
mkdir($path, 0755, true);
|
|
echo "📁 Ordner erstellt: $dir\n";
|
|
}
|
|
}
|
|
|
|
$files = [];
|
|
|
|
// --- DEV SERVER: server.php ---
|
|
$files['server.php'] = <<<'PHP'
|
|
<?php
|
|
$port = 8000;
|
|
$host = 'localhost';
|
|
$root = __DIR__ . '/WEBROOT';
|
|
echo "\n🚀 Starte MVC Dev-Server auf http://$host:$port\n";
|
|
echo "Drücke STRG+C zum Beenden.\n\n";
|
|
passthru("php -S $host:$port -t \"$root\"");
|
|
PHP;
|
|
|
|
$files['.htaccess'] = <<<'HTACCESS'
|
|
RewriteEngine On
|
|
|
|
RewriteRule ^(Commands|config_backups|controller|modules)($|/.*) - [F,L,NC]
|
|
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
|
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
|
|
|
|
# -----------------------------------
|
|
# .php entfernen
|
|
# -----------------------------------
|
|
|
|
# browser requestet datei.php -> 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'] = <<<VSCODE
|
|
{
|
|
"folders": [
|
|
{
|
|
"path": "."
|
|
}
|
|
],
|
|
"settings": {
|
|
"workbench.colorTheme": "Dark Modern",
|
|
"workbench.colorCustomizations": {
|
|
"activityBar.background": "#162df7ec",
|
|
"activityBar.foreground": "#000000",
|
|
"breadcrumb.foreground": "#FFFFFF",
|
|
},
|
|
"files.exclude": {
|
|
"*.git*": true,
|
|
"WEBROOT/config_backups": true,
|
|
"WEBROOT/static": true,
|
|
"WEBROOT/templates": true,
|
|
},
|
|
"editor.renderWhitespace": "all",
|
|
"editor.smoothScrolling": true,
|
|
"editor.fontSize": 14,
|
|
"editor.formatOnSave": true,
|
|
"editor.minimap.enabled": true,
|
|
"editor.minimap.scale": 2,
|
|
"editor.minimap.side": "right",
|
|
"editor.minimap.maxColumn": 100,
|
|
"editor.minimap.renderCharacters": true,
|
|
"editor.minimap.showSlider": "always",
|
|
"files.autoSave": "off",
|
|
"files.autoSaveDelay": 3000,
|
|
"window.title": "MVC",
|
|
"workbench.editor.wrapTabs": true,
|
|
},
|
|
"extensions": {
|
|
"recommendations": [
|
|
"oderwat.indent-rainbow",
|
|
"pranaygp.vscode-css-peek",
|
|
"wayou.vscode-todo-highlight",
|
|
"mrmlnc.vscode-mustache",
|
|
"fabiospampinato.vscode-highlight",
|
|
"junstyle.php-cs-fixer",
|
|
"mtea.gettext-editor",
|
|
"neilbrayfield.php-docblocker",
|
|
"bmewburn.vscode-intelephense-client",
|
|
"mohamed-ali.html-css-class-completion",
|
|
"rifi2k.format-html-in-php",
|
|
"mrorz.language-gettext",
|
|
"voldemortensen.rainbow-tags",
|
|
]
|
|
}
|
|
}
|
|
VSCODE;
|
|
|
|
$files['WEBROOT/src/utils/databaseMethods.php'] = <<<'PHP'
|
|
<?php
|
|
|
|
function DatabaseCreateTable($table, $columnsDefinition) {
|
|
include_once(__DIR__ . '/../database/databaseConnect.php');
|
|
$conn = db_connect();
|
|
|
|
$sql = "CREATE TABLE IF NOT EXISTS `$table` ($columnsDefinition)";
|
|
$stmt = $conn->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'] = <<<INI
|
|
[database]
|
|
APP_ENV = "dev"
|
|
|
|
DB_DEV_HOST = ""
|
|
DB_DEV_USERNAME = ""
|
|
DB_DEV_PASSWORD = ""
|
|
DB_DEV_DATABASE = ""
|
|
|
|
DB_LIVE_HOST = ""
|
|
DB_LIVE_USERNAME = ""
|
|
DB_LIVE_PASSWORD = ""
|
|
DB_LIVE_DATABASE = ""
|
|
|
|
[mail]
|
|
MAIL_FROM = "bruno.eckhardt07@gmail.com"
|
|
MAIL_TO = "bruno.eckhardt07@gmail.com"
|
|
INI;
|
|
|
|
// --- DB: databaseConnect.php ---
|
|
$files['WEBROOT/src/database/databaseConnect.php'] = <<<'PHP'
|
|
<?php
|
|
function db_connect() {
|
|
$config = parse_ini_file(__DIR__ . '/../config/env.ini');
|
|
$env = strtoupper($config['APP_ENV']);
|
|
try {
|
|
$conn = new mysqli(
|
|
$config["DB_{$env}_HOST"],
|
|
$config["DB_{$env}_USERNAME"],
|
|
$config["DB_{$env}_PASSWORD"],
|
|
$config["DB_{$env}_DATABASE"]
|
|
);
|
|
|
|
$conn->set_charset("utf8mb4");
|
|
|
|
return $conn;
|
|
|
|
} catch (Exception $e) {
|
|
return null;
|
|
}
|
|
}
|
|
PHP;
|
|
// -- CLASSES --
|
|
|
|
$files['WEBROOT/src/MVC/Classes/Api.php'] = <<<'PHP'
|
|
<?php
|
|
|
|
namespace MVC\Classes;
|
|
|
|
class Api {
|
|
public static function jsonResponse($data, int $statusCode = 200) {
|
|
header('Content-Type: application/json');
|
|
http_response_code($statusCode);
|
|
echo json_encode(
|
|
[
|
|
'status' => $statusCode === 200 ? 'success' : 'error',
|
|
'data' => $data,
|
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE
|
|
);
|
|
exit;
|
|
}
|
|
}
|
|
PHP;
|
|
|
|
$files['WEBROOT/src/MVC/Classes/Client.php'] = <<<'PHP'
|
|
<?php
|
|
|
|
namespace MVC\Classes;
|
|
|
|
class Client {
|
|
|
|
public static function ip() {
|
|
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
|
$ipList = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
|
|
return trim($ipList[0]);
|
|
} else if (!empty($_SERVER['REMOTE_ADDR'])) {
|
|
return $_SERVER['REMOTE_ADDR'];
|
|
} else {
|
|
return 'UNKNOWN';
|
|
}
|
|
}
|
|
|
|
public static function port() {
|
|
return $_SERVER['REMOTE_PORT'] ?? 'UNKNOWN';
|
|
}
|
|
|
|
public static function country() {
|
|
$response = file_get_contents('http://ip-api.com/json/{' . self::ip() . '}?fields=country,countryCode');
|
|
$data = json_decode($response);
|
|
return $data->country ?? 'UNKNOWN';
|
|
}
|
|
|
|
public static function userAgent() {
|
|
return $_SERVER['HTTP_USER_AGENT'] ?? 'UNKNOWN';
|
|
}
|
|
}
|
|
PHP;
|
|
|
|
$files['WEBROOT/src/MVC/Classes/Cookie.php'] = <<<'PHP'
|
|
<?php
|
|
|
|
namespace MVC\Classes;
|
|
|
|
class Cookie {
|
|
public function set(string $CookieName, int $CookieValue, int $CookieTime) {
|
|
setcookie($CookieName, $CookieValue, time()+($CookieTime*60));
|
|
}
|
|
|
|
public function read(string $CookieName) {
|
|
if (isset($_COOKIE[$CookieName])) {
|
|
return $_COOKIE[$CookieName];
|
|
}
|
|
return 'Cookie not set';
|
|
}
|
|
|
|
public function Delete(string $CookieName) {
|
|
setcookie($CookieName, "", time()-(3600*24));
|
|
}
|
|
}
|
|
PHP;
|
|
|
|
$files['WEBROOT/src/MVC/Classes/Request.php'] = <<<'PHP'
|
|
<?php
|
|
|
|
namespace MVC\Classes;
|
|
|
|
class Request {
|
|
public static function method() {
|
|
return $_SERVER['REQUEST_METHOD'] ?? 'UNKNOWN';
|
|
}
|
|
|
|
public static function uri() {
|
|
return $_SERVER['REQUEST_URI'] ?? 'UNKNOWN';
|
|
}
|
|
|
|
public static function headers() {
|
|
return getallheaders();
|
|
}
|
|
|
|
public static function body() {
|
|
return file_get_contents('php://input');
|
|
}
|
|
|
|
public static function queryParams() {
|
|
return $_GET;
|
|
}
|
|
|
|
public static function time() {
|
|
return $_SERVER['REQUEST_TIME'] ?? 'UNKNOWN';
|
|
}
|
|
|
|
public static function referrer() {
|
|
return $_SERVER['HTTP_REFERER'] ?? 'UNKNOWN';
|
|
}
|
|
}
|
|
PHP;
|
|
|
|
$files['WEBROOT/src/MVC/Classes/Logger.php'] = <<<'PHP'
|
|
<?php
|
|
namespace MVC\Classes;
|
|
class Logger {
|
|
private static function getLogDir() { return dirname(__DIR__, 3) . DIRECTORY_SEPARATOR . 'logs'; }
|
|
public static function log($message, $level = 'INFO', $file = 'system.log') {
|
|
$logDir = self::getLogDir();
|
|
if (!is_dir($logDir) && !mkdir($logDir, 0777, true)) return;
|
|
$timestamp = date('Y-m-d H:i:s');
|
|
file_put_contents($logDir . DIRECTORY_SEPARATOR . $file, "[$timestamp] [$level] $message\n", FILE_APPEND | LOCK_EX);
|
|
}
|
|
public static function logException(\Throwable $e) {
|
|
$msg = sprintf("Uncaught Exception: '%s' with message '%s' in %s:%d\nStack trace:\n%s", get_class($e), $e->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'
|
|
<?php
|
|
|
|
namespace MVC\Classes;
|
|
|
|
use MVC\Classes\Client;
|
|
use MVC\Classes\Request;
|
|
use MVC\Model\Mailing;
|
|
|
|
class Mailer {
|
|
private static function getConfig() {
|
|
return parse_ini_file(__DIR__ . '/../../config/env.ini');
|
|
}
|
|
|
|
public static function send($subject, $message, $url = null, bool $log = true) {
|
|
$config = self::getConfig();
|
|
$from = $config['MAIL_FROM'] ?? "bruno.eckhardt07@gmail.com";
|
|
$to = $config['MAIL_TO'] ?? "bruno.eckhardt07@gmail.com";
|
|
|
|
$headers = "From: " . $from . "\r\n";
|
|
$headers .= "Reply-To: " . $from . "\r\n";
|
|
$headers .= "X-Mailer: PHP/" . phpversion();
|
|
|
|
$success = mail($to, $subject, $message, $headers);
|
|
|
|
if ($log) {
|
|
self::log($subject, $message, $url, false);
|
|
}
|
|
|
|
return $success;
|
|
}
|
|
|
|
public static function error($title, $error, bool $log = true) {
|
|
$subject = "ERROR: $title";
|
|
$config = self::getConfig();
|
|
$from = $config['MAIL_FROM'] ?? "bruno.eckhardt07@gmail.com";
|
|
$to = $config['MAIL_TO'] ?? "bruno.eckhardt07@gmail.com";
|
|
$headers = "From: " . $from . "\r\n" . "Reply-To: " . $from . "\r\n" . "X-Mailer: PHP/" . phpversion();
|
|
$success = mail($to, $subject, $error, $headers);
|
|
if ($log) self::log($subject, $error, null, true);
|
|
return $success;
|
|
}
|
|
|
|
private static function log($subject, $message, $url, bool $isError) {
|
|
$url = $url ?? Request::uri();
|
|
$requestTime = Request::time();
|
|
$date = ($requestTime !== 'UNKNOWN') ? date('Y-m-d H:i:s', $requestTime) : date('Y-m-d H:i:s');
|
|
$ip = Client::ip();
|
|
|
|
try {
|
|
if (class_exists('MVC\Model\Mailing')) {
|
|
Mailing::log($subject, $message, $url, $ip, $date);
|
|
return;
|
|
}
|
|
} catch (\Exception $e) {}
|
|
|
|
self::logToFile($subject, $message, $url, $ip, $date, $isError);
|
|
}
|
|
|
|
private static function logToFile($subject, $message, $url, $ip, $date, bool $isError) {
|
|
$logDir = __DIR__ . '/../../../logs';
|
|
if (!is_dir($logDir)) mkdir($logDir, 0755, true);
|
|
|
|
$fileName = $isError ? 'mail_errors.log' : 'mails.log';
|
|
$logFile = $logDir . '/' . $fileName;
|
|
|
|
$logEntry = "[$date] IP: $ip | URL: $url\nSubject: $subject\nMessage: $message\n" . str_repeat("-", 50) . "\n";
|
|
file_put_contents($logFile, $logEntry, FILE_APPEND);
|
|
}
|
|
}
|
|
PHP;
|
|
|
|
$files['WEBROOT/src/MVC/Classes/Server.php'] = <<<'PHP'
|
|
<?php
|
|
|
|
namespace MVC\Classes;
|
|
|
|
class Server {
|
|
public static function documentRoot() {
|
|
return $_SERVER['DOCUMENT_ROOT'] ?? 'UNKNOWN';
|
|
}
|
|
|
|
public static function currentDir() {
|
|
return __DIR__;
|
|
}
|
|
|
|
public static function currentScript() {
|
|
return $_SERVER['SCRIPT_NAME'] ?? 'UNKNOWN';
|
|
}
|
|
|
|
public static function name() {
|
|
return $_SERVER['SERVER_NAME'] ?? 'UNKNOWN';
|
|
}
|
|
|
|
public static function port() {
|
|
return $_SERVER['SERVER_PORT'] ?? 'UNKNOWN';
|
|
}
|
|
}
|
|
PHP;
|
|
|
|
// --- CORE ---
|
|
$files['WEBROOT/src/MVC/Core/Controller.php'] = <<<'PHP'
|
|
<?php
|
|
|
|
namespace MVC\Core;
|
|
|
|
class Controller {
|
|
protected $view;
|
|
|
|
public function __construct() {
|
|
$this->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'
|
|
<?php
|
|
|
|
namespace MVC\Core;
|
|
|
|
require_once __DIR__ . '/../../database/databaseConnect.php';
|
|
require_once __DIR__ . '/../../utils/databaseMethods.php';
|
|
|
|
abstract class Model {
|
|
protected static $table;
|
|
protected static $conn;
|
|
|
|
protected static function getConnection() {
|
|
if (!self::$conn) {
|
|
self::$conn = \db_connect();
|
|
}
|
|
return self::$conn;
|
|
}
|
|
|
|
public static function all() {
|
|
return \DatabaseShow(static::$table, "*");
|
|
}
|
|
|
|
public static function find($id, $column = 'ID') {
|
|
$result = \DatabaseShow(static::$table, "*", "$column = ?", $id);
|
|
return $result[0] ?? null;
|
|
}
|
|
|
|
public static function where($where, ...$params) {
|
|
return \DatabaseShow(static::$table, "*", $where, ...$params);
|
|
}
|
|
|
|
public static function insert($data) {
|
|
$columns = implode(", ", array_keys($data));
|
|
$values = array_values($data);
|
|
return \DatabaseInsert(static::$table, $columns, ...$values);
|
|
}
|
|
|
|
public static function update($data, $where, ...$whereParams) {
|
|
$sets = [];
|
|
$values = [];
|
|
foreach ($data as $column => $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'
|
|
<?php
|
|
|
|
namespace MVC\Core;
|
|
|
|
class View {
|
|
protected $data = [];
|
|
protected $viewPath;
|
|
|
|
public function __construct($viewPath) {
|
|
$this->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'
|
|
<?php
|
|
|
|
namespace MVC\Core;
|
|
|
|
class Router {
|
|
protected $routes = [];
|
|
protected $middlewares = [];
|
|
|
|
public function add($service, $controller, $method, $middlewares = []) {
|
|
$this->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'
|
|
<?php
|
|
namespace MVC\Layout;
|
|
class Layout {
|
|
|
|
protected $blocks = [];
|
|
|
|
protected $template;
|
|
|
|
public function __construct($template = 'main') {
|
|
$this->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'
|
|
<?php
|
|
|
|
spl_autoload_register(function ($class) {
|
|
$prefix = 'MVC\\';
|
|
$base_dir = __DIR__ . '/';
|
|
|
|
$len = strlen($prefix);
|
|
if (strncmp($prefix, $class, $len) !== 0) {
|
|
return;
|
|
}
|
|
|
|
$relative_class = substr($class, $len);
|
|
$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
|
|
|
|
if (file_exists($file)) {
|
|
require $file;
|
|
}
|
|
});
|
|
|
|
PHP;
|
|
|
|
// --- LAYOUT TEMPLATE ---
|
|
$files['WEBROOT/src/MVC/Layout/Templates/main.php'] = <<<'HTML'
|
|
<!DOCTYPE html>
|
|
<html lang="de" class="<?= ($_SESSION['theme'] ?? 'light') === 'dark' ? 'dark-mode' : '' ?>">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title><?= $this->getBlock('title') ?></title>
|
|
<style>
|
|
:root { --bg: #ffffff; --text: #333; --sidebar: #f4f4f4; --header: #007bff; --accent: #007bff; --border: #ddd; }
|
|
.dark-mode { --bg: #1a1a1a; --text: #f4f4f4; --sidebar: #252525; --header: #111; --accent: #3498db; --border: #333; }
|
|
body { font-family: 'Segoe UI', sans-serif; margin: 0; background: var(--bg); color: var(--text); transition: 0.3s; height: 100vh; overflow: hidden; display: flex; flex-direction: column; }
|
|
.main-header { height: 60px; background: var(--header); color: white; display: flex; align-items: center; justify-content: space-between; padding: 0 20px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); z-index: 10; }
|
|
.main-container { flex: 1; display: flex; overflow: hidden; }
|
|
.sidebar { width: 260px; background: var(--sidebar); padding: 20px; border-right: 1px solid var(--border); overflow-y: auto; }
|
|
.content { flex: 1; padding: 40px; overflow-y: auto; }
|
|
.btn-toggle { padding: 8px 15px; border: 1px solid rgba(255,255,255,0.3); border-radius: 5px; cursor: pointer; background: transparent; color: white; font-size: 0.9rem; }
|
|
.btn-toggle:hover { background: rgba(255,255,255,0.1); }
|
|
nav a { display: block; padding: 12px; text-decoration: none; color: inherit; border-radius: 5px; margin-bottom: 5px; }
|
|
nav a:hover { background: rgba(0,0,0,0.05); }
|
|
nav a.active { background: rgba(0,0,0,0.1); }
|
|
.logo { font-size: 1.4rem; font-weight: bold; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<header class="main-header">
|
|
<div class="logo"><?= $this->getBlock('header_title') ?></div>
|
|
<div class="header-actions">
|
|
<?= $this->getBlock('header_actions') ?>
|
|
</div>
|
|
</header>
|
|
<div class="main-container">
|
|
<div class="sidebar">
|
|
<?= $this->getBlock('sidebar') ?>
|
|
</div>
|
|
<div class="content">
|
|
<?= $this->getBlock('content') ?>
|
|
</div>
|
|
</div>
|
|
<script>
|
|
function toggleTheme() { fetch('?service=toggle_theme').then(() => location.reload()); }
|
|
</script>
|
|
</body>
|
|
</html>
|
|
HTML;
|
|
|
|
// --- CONTROLLERS ---
|
|
$files['WEBROOT/src/MVC/Controller/AdminController.php'] = <<<'PHP'
|
|
<?php
|
|
namespace MVC\Controller;
|
|
use MVC\Core\Controller;
|
|
use MVC\Classes\Mailer;
|
|
use MVC\Model\Users;
|
|
class AdminController extends Controller {
|
|
public function mail_service() {
|
|
$msg = null;
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
if (Mailer::send($_POST['subject'] ?? '', $_POST['body'] ?? '')) $msg = ["type" => "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'
|
|
<h2>📧 E-Mail Service</h2>
|
|
<?php if (isset($message)): ?>
|
|
<div style="padding:15px;margin-bottom:20px;border-radius:5px;background:<?= $message['type']==='success'?'rgba(46,204,113,0.2)':'rgba(231,76,60,0.2)'?>;"><?= $message['text']?></div>
|
|
<?php endif; ?>
|
|
<form method="POST" style="background:var(--sidebar);padding:20px;border-radius:8px;">
|
|
<input type="text" name="subject" placeholder="Betreff" required style="width:100%;margin-bottom:10px;padding:10px;background:var(--bg);color:var(--text);border:1px solid var(--border);">
|
|
<textarea name="body" placeholder="Nachricht" required style="width:100%;height:150px;margin-bottom:10px;padding:10px;background:var(--bg);color:var(--text);border:1px solid var(--border);"></textarea>
|
|
<button type="submit" style="background:var(--accent);color:white;padding:10px 20px;border:none;border-radius:4px;cursor:pointer;">Senden</button>
|
|
</form>
|
|
HTML;
|
|
|
|
$files['WEBROOT/src/MVC/View/admin/users.php'] = <<<'HTML'
|
|
<h2>👤 Benutzerverwaltung</h2>
|
|
<?php if (isset($message)): ?>
|
|
<div style="padding:15px;margin-bottom:20px;border-radius:5px;background:<?= $message['type']==='success'?'rgba(46,204,113,0.2)':'rgba(231,76,60,0.2)'?>;"><?= $message['text']?></div>
|
|
<?php endif; ?>
|
|
<form method="POST" style="background:var(--sidebar);padding:20px;border-radius:8px;">
|
|
<input type="text" name="username" placeholder="Benutzername" required style="width:100%;margin-bottom:10px;padding:10px;background:var(--bg);color:var(--text);border:1px solid var(--border);">
|
|
<input type="password" name="password" placeholder="Passwort" required style="width:100%;margin-bottom:10px;padding:10px;background:var(--bg);color:var(--text);border:1px solid var(--border);">
|
|
<input type="email" name="email" placeholder="E-Mail" style="width:100%;margin-bottom:10px;padding:10px;background:var(--bg);color:var(--text);border:1px solid var(--border);">
|
|
<select name="role" style="width:100%;margin-bottom:10px;padding:10px;background:var(--bg);color:var(--text);border:1px solid var(--border);">
|
|
<option value="user">User</option><option value="admin">Admin</option>
|
|
</select>
|
|
<button type="submit" style="background:var(--accent);color:white;padding:10px 20px;border:none;border-radius:4px;cursor:pointer;">Erstellen</button>
|
|
</form>
|
|
HTML;
|
|
|
|
$files['WEBROOT/src/MVC/Controller/ApiController.php'] = <<<'PHP'
|
|
<?php
|
|
|
|
namespace MVC\Controller;
|
|
|
|
use MVC\Core\Controller;
|
|
use MVC\Classes\Api;
|
|
|
|
class ApiController extends Controller {
|
|
public static function get(){
|
|
return Api::jsonResponse([
|
|
'data' => 'DummyData',
|
|
]);
|
|
}
|
|
}
|
|
PHP;
|
|
|
|
$files['WEBROOT/src/MVC/Controller/HomeController.php'] = <<<'PHP'
|
|
<?php
|
|
namespace MVC\Controller;
|
|
use MVC\Core\Controller;
|
|
use MVC\Classes\Client;
|
|
use MVC\Classes\Cookie;
|
|
use MVC\Classes\Request;
|
|
use MVC\Classes\Server;
|
|
class HomeController extends Controller {
|
|
|
|
public function index() {
|
|
return $this->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'
|
|
<?php
|
|
|
|
namespace MVC\Controller;
|
|
|
|
use MVC\Core\Controller;
|
|
|
|
class AuthController extends Controller {
|
|
public function login() {
|
|
if (isset($_SESSION['user_logged_in'])) {
|
|
header('Location: ?service=home');
|
|
exit;
|
|
}
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
$username = $_POST['username'] ?? '';
|
|
$password = $_POST['password'] ?? '';
|
|
|
|
// Hardcoded logins
|
|
$users = [
|
|
'admin' => 'password123',
|
|
'user' => 'user123'
|
|
];
|
|
|
|
if (isset($users[$username]) && $users[$username] === $password) {
|
|
$_SESSION['user_logged_in'] = true;
|
|
$_SESSION['username'] = $username;
|
|
header('Location: ?service=home');
|
|
exit;
|
|
} else {
|
|
return $this->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'
|
|
<?php
|
|
|
|
namespace MVC\Controller;
|
|
|
|
use MVC\Core\Controller;
|
|
|
|
class DocsController extends Controller {
|
|
public function index() {
|
|
return $this->render('docs', [
|
|
'title' => '📚 System-Dokumentation'
|
|
]);
|
|
}
|
|
}
|
|
PHP;
|
|
|
|
$files['WEBROOT/src/MVC/Controller/TestController.php'] = <<<'PHP'
|
|
<?php
|
|
namespace MVC\Controller;
|
|
use MVC\Core\Controller;
|
|
use MVC\Classes\Client;
|
|
use MVC\Classes\Cookie;
|
|
use MVC\Classes\Request;
|
|
use MVC\Classes\Server;
|
|
class TestController extends Controller {
|
|
|
|
public function service1() {
|
|
return $this->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'
|
|
<?php
|
|
header('Content-Type: application/json');
|
|
|
|
require_once __DIR__ . '/../../src/MVC/autoloader.php';
|
|
|
|
use MVC\Controller\ApiController;
|
|
|
|
$api = (new ApiController());
|
|
|
|
try {
|
|
echo ($api->get());
|
|
} catch (\Exception $e) {
|
|
echo ($e->getMessage());
|
|
}
|
|
PHP;
|
|
|
|
|
|
// --- VIEWS ---
|
|
$files['WEBROOT/src/MVC/View/home.php'] = "<h1>Dashboard</h1><p>Willkommen in deinem neuen MVC-System mit integriertem Header.</p>";
|
|
$files['WEBROOT/src/MVC/View/test1.php'] = "<h1>Service 1</h1><p><?= \$msg ?></p>";
|
|
$files['WEBROOT/src/MVC/View/test2.php'] = "<h1>System Check</h1><ul><?php foreach(\$data as \$k => \$v): ?><li><b><?= \$k ?>:</b> <?= \$v ?></li><?php endforeach; ?></ul>";
|
|
|
|
$files['WEBROOT/src/MVC/View/docs.php'] = <<<'HTML'
|
|
<div class="docs-container">
|
|
<h1>🏛️ Framework Architektur & Dokumentation</h1>
|
|
|
|
<div class="toc">
|
|
<h3>Inhalt</h3>
|
|
<ul>
|
|
<li><a href="#intro">1. Einführung</a></li>
|
|
<li><a href="#files">2. Datei-Referenz (Deep Dive)</a></li>
|
|
<li><a href="#api">3. API-System</a></li>
|
|
<li><a href="#database">4. Datenbank & Models</a></li>
|
|
<li><a href="#classes">5. Helper Klassen</a></li>
|
|
<li><a href="#generators">6. CLI-Generatoren</a></li>
|
|
<li><a href="#mailing">7. Mailing-System</a></li>
|
|
<li><a href="#logging">8. Error-Logging</a></li>
|
|
<li><a href="#private-services">9. Private Services</a></li>
|
|
<li><a href="#best-practices">10. Best Practices</a></li>
|
|
</ul>
|
|
</div>
|
|
|
|
<section id="intro">
|
|
<h2>1. Einführung</h2>
|
|
<p>Dieses Framework basiert auf dem <strong>Model-View-Controller (MVC)</strong> Entwurfsmuster und nutzt einen <strong>Front Controller</strong> (index.php). Alle Anfragen werden zentral verarbeitet, was globale Middleware (wie Authentifizierung) und ein einheitliches Layout ermöglicht.</p>
|
|
</section>
|
|
|
|
<section id="files">
|
|
<h2>2. Datei-Referenz (Deep Dive)</h2>
|
|
<h3>Framework Core</h3>
|
|
<ul>
|
|
<li><strong>Router.php:</strong> Verwaltet die Routen. Unterstützt Middlewares. Das Dispatching erfolgt über den <code>service</code> Parameter in der URL.</li>
|
|
<li><strong>Controller.php:</strong> Basisklasse für alle Controller. Bietet <code>render()</code> für Views und <code>json()</code> für API-Responses.</li>
|
|
<li><strong>Model.php:</strong> Abstrakte Basis für Datenbank-Entitäten. Kapselt die <code>databaseMethods.php</code> in ein objektorientiertes Interface.</li>
|
|
<li><strong>View.php:</strong> Behandelt das Extrahieren von Daten in HTML-Templates.</li>
|
|
</ul>
|
|
<h3>Konfiguration</h3>
|
|
<ul>
|
|
<li><strong>env.ini:</strong> Speichert Datenbank-Zugangsdaten für <code>dev</code> und <code>live</code> Umgebungen.</li>
|
|
<li><strong>autoloader.php:</strong> Registriert einen PSR-4 ähnlichen Autoloader für den <code>MVC\</code> Namespace.</li>
|
|
</ul>
|
|
</section>
|
|
|
|
<section id="api">
|
|
<h2>3. API-System</h2>
|
|
<p>APIs liegen im Verzeichnis <code>WEBROOT/api/v1/</code>. Sie nutzen die <code>ApiController</code>-Klasse.</p>
|
|
<pre>
|
|
// Beispiel: WEBROOT/api/v1/get.php
|
|
use MVC\Classes\Api;
|
|
use MVC\Controller\ApiController;
|
|
|
|
Api::jsonResponse((new ApiController())->get());
|
|
</pre>
|
|
</section>
|
|
|
|
<section id="database">
|
|
<h2>4. Datenbank & Models</h2>
|
|
<p>Das Framework nutzt <code>mysqli</code> mit Prepared Statements für maximale Sicherheit.</p>
|
|
|
|
<h3>Installation (install_db.php)</h3>
|
|
<p>Mit dem Befehl <code>php install_db.php</code> können vordefinierte Tabellen und deren Models automatisch erstellt werden. Standardmäßig sind folgende Tabellen enthalten:</p>
|
|
<ul>
|
|
<li><strong>Mailing:</strong> Für das E-Mail-Logging (wird vom Mailer genutzt).</li>
|
|
<li><strong>Users:</strong> Eine Grundstruktur für Benutzer (ID, Username, Password, Email, Role, Created_at).</li>
|
|
</ul>
|
|
|
|
<h3>databaseMethods.php (Die Power-Tools)</h3>
|
|
<ul>
|
|
<li><code>DatabaseShow($table, $columns, $where, ...$values)</code></li>
|
|
<li><code>DatabaseInsert($table, $columns, ...$values)</code></li>
|
|
<li><code>DatabaseUpdate($table, $set, $where, ...$values)</code></li>
|
|
<li><code>DatabaseDelete($table, $where, ...$values)</code></li>
|
|
</ul>
|
|
</section>
|
|
|
|
<section id="classes">
|
|
<h2>5. Helper Klassen (MVC\Classes)</h2>
|
|
<ul>
|
|
<li><strong>Request:</strong> Zugriff auf <code>method()</code>, <code>queryParams()</code>, <code>headers()</code>.</li>
|
|
<li><strong>Client:</strong> Ermittelt <code>ip()</code>, <code>userAgent()</code>, <code>country()</code>.</li>
|
|
<li><strong>Cookie:</strong> Interface zum <code>set()</code>, <code>read()</code>, <code>Delete()</code> von Cookies.</li>
|
|
</ul>
|
|
</section>
|
|
|
|
<section id="generators">
|
|
<h2>6. CLI-Generator</h2>
|
|
<p><code>php make.php module [Name]</code> - Erstellt Controller, View, Route & Nav.</p>
|
|
<p><code>php make.php model [Tabelle] [Spalten]</code> - Erstellt ein Model.</p>
|
|
</section>
|
|
|
|
<section id="mailing">
|
|
<h2>7. Mailing-System</h2>
|
|
<p>Das Framework verfügt über eine integrierte <code>Mailer</code>-Klasse für den E-Mail-Versand und die Protokollierung.</p>
|
|
<h3>Konfiguration</h3>
|
|
<p>In der <code>env.ini</code> können Absender und Empfänger unter <code>[mail]</code> definiert werden.</p>
|
|
<h3>Nutzung</h3>
|
|
<pre>
|
|
use MVC\Classes\Mailer;
|
|
|
|
// Einfache Mail
|
|
Mailer::send("Betreff", "Inhalt");
|
|
|
|
// Error Mail (wird speziell markiert)
|
|
Mailer::error("Fehlertitel", "Details");
|
|
</pre>
|
|
<h3>Logging & Fallback</h3>
|
|
<p>Der Mailer versucht standardmäßig in die Datenbank-Tabelle <code>Mailing</code> zu loggen. Falls diese nicht existiert, erfolgt ein automatischer Fallback in Textdateien im Ordner <code>/logs/</code>.</p>
|
|
<p><strong>Installation:</strong> Führe <code>php install_db.php</code> aus, um die Datenbank-Struktur anzulegen.</p>
|
|
</section>
|
|
|
|
<section id="logging">
|
|
<h2>8. Error-Logging</h2>
|
|
<p>Das Framework verfügt über ein zentrales Logging-System, das in der <code>index.php</code> global registriert ist.</p>
|
|
<h3>Automatische Erfassung</h3>
|
|
<p>Alle unangefangenen Exceptions und PHP-Fehler (Warnings, Notices, etc.) werden automatisch in die Datei <code>/logs/error.log</code> geschrieben, inklusive Stacktrace bei Exceptions.</p>
|
|
<h3>Manuelle Nutzung</h3>
|
|
<pre>
|
|
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");
|
|
</pre>
|
|
<p>Die Log-Dateien befinden sich im Hauptverzeichnis des Projekts im Ordner <code>/logs/</code>.</p>
|
|
</section>
|
|
|
|
<section id="best-practices">
|
|
<h2>9. Best Practices</h2>
|
|
<p><strong>Naming:</strong> Controller immer mit <code>Controller</code>-Suffix. Models im Singular.</p>
|
|
<p><strong>Security:</strong> Nutze <code>['auth']</code> Middleware konsequent für alle internen Seiten.</p>
|
|
</section>
|
|
</div>
|
|
|
|
<style>
|
|
.docs-container { max-width: 1000px; color: var(--text); }
|
|
.toc { background: var(--sidebar); padding: 20px; border-radius: 8px; margin-bottom: 30px; border: 1px solid var(--border); }
|
|
.toc h3 { margin-top: 0; }
|
|
section { margin-bottom: 50px; padding-bottom: 20px; border-bottom: 1px solid var(--border); }
|
|
h2 { color: var(--accent); font-size: 1.8rem; margin-bottom: 20px; }
|
|
pre { background: #1e1e1e; color: #d4d4d4; padding: 15px; border-radius: 5px; overflow-x: auto; border: 1px solid #333; font-size: 0.9rem; }
|
|
</style>
|
|
HTML;
|
|
|
|
$files['WEBROOT/src/MVC/View/login.php'] = <<<'HTML'
|
|
<style>
|
|
.login-container { max-width: 400px; margin: 50px auto; padding: 30px; border: 1px solid var(--border); border-radius: 8px; background: var(--sidebar); box-shadow: 0 4px 15px rgba(0,0,0,0.1); }
|
|
.login-container h2 { text-align: center; margin-top: 0; color: var(--text); }
|
|
.login-container input { width: 100%; padding: 12px; margin: 10px 0; border: 1px solid var(--border); border-radius: 4px; box-sizing: border-box; background: var(--bg); color: var(--text); }
|
|
.login-container button { width: 100%; padding: 12px; background: var(--header); color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 1rem; font-weight: bold; }
|
|
.login-container button:hover { opacity: 0.9; }
|
|
.error-msg { color: #e74c3c; background: rgba(231, 76, 60, 0.1); padding: 10px; border-radius: 4px; margin-bottom: 15px; text-align: center; font-size: 0.9rem; }
|
|
</style>
|
|
<div class="login-container">
|
|
<h2>🔑 Login</h2>
|
|
<?php if (isset($error)): ?>
|
|
<div class="error-msg"><?= htmlspecialchars($error) ?></div>
|
|
<?php endif; ?>
|
|
<form method="POST">
|
|
<div>
|
|
<label>Benutzername</label>
|
|
<input type="text" name="username" required autofocus placeholder="Admin / User">
|
|
</div>
|
|
<div>
|
|
<label>Passwort</label>
|
|
<input type="password" name="password" required placeholder="********">
|
|
</div>
|
|
<button type="submit">Anmelden</button>
|
|
</form>
|
|
</div>
|
|
HTML;
|
|
|
|
// --- CONFIG: routes.php ---
|
|
$files['WEBROOT/src/config/routes.php'] = <<<'PHP'
|
|
<?php
|
|
/**
|
|
* Define your routes here.
|
|
* Format: $router->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']);
|
|
$router->add('admin_users', \MVC\Controller\AdminController::class, 'user_service', ['auth']);
|
|
PHP;
|
|
|
|
// --- CONFIG: navigation.php ---
|
|
$files['WEBROOT/src/config/navigation.php'] = <<<'PHP'
|
|
<?php
|
|
/**
|
|
* Define your sidebar navigation items here.
|
|
* Format: 'service_name' => ['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'
|
|
<?php
|
|
session_start();
|
|
require_once __DIR__ . '/src/MVC/autoloader.php';
|
|
use MVC\Classes\Logger;
|
|
set_exception_handler([Logger::class, 'logException']);
|
|
set_error_handler([Logger::class, 'logError']);
|
|
$service = $_GET['service'] ?? 'home';
|
|
try {
|
|
$router = new \MVC\Core\Router();
|
|
|
|
// Load routes from config
|
|
require_once __DIR__ . '/src/config/routes.php';
|
|
|
|
$layout = new \MVC\Layout\Layout('main');
|
|
$layout->setBlock('title', 'MVC Starter');
|
|
$layout->setBlock('header_title', '🚀 MVC Dashboard');
|
|
|
|
$headerActions = '<button class="btn-toggle" onclick="toggleTheme()">🌓 Theme umschalten</button>';
|
|
if (isset($_SESSION['user_logged_in'])) {
|
|
$headerActions = '<span style="margin-right: 15px; font-size: 0.9rem;">👤 ' . htmlspecialchars($_SESSION['username']) . '</span>' . '<a href="?service=logout" class="btn-toggle" style="text-decoration: none; background: rgba(255,255,255,0.2); margin-right: 10px;">🚪 Logout</a> ' . $headerActions;
|
|
} else {
|
|
$headerActions = '<a href="?service=login" class="btn-toggle" style="text-decoration: none; background: var(--accent); color: white; margin-right: 10px;">🔑 Login</a> ' . $headerActions;
|
|
}
|
|
$layout->setBlock('header_actions', $headerActions);
|
|
|
|
// Sidebar Block
|
|
ob_start();
|
|
// Load navigation from config
|
|
$navItems = require __DIR__ . '/src/config/navigation.php';
|
|
?>
|
|
<nav>
|
|
<?php foreach ($navItems as $navKey => $item):
|
|
if ($item['auth'] && !isset($_SESSION['user_logged_in'])) continue;
|
|
?>
|
|
<a href="?service=<?= $navKey ?>" class="<?= $service === $navKey ? 'active' : '' ?>"><?= $item['label'] ?></a>
|
|
<?php endforeach; ?>
|
|
|
|
<?php if (!isset($_SESSION['user_logged_in'])): ?>
|
|
<div style="margin-top: 20px; padding: 10px; font-size: 0.8rem; opacity: 0.5; border-top: 1px solid var(--border);">
|
|
🔒 Melden Sie sich an, um alle Funktionen zu sehen.
|
|
</div>
|
|
<?php endif; ?>
|
|
</nav>
|
|
<?php
|
|
$layout->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 "<h1>Error</h1><p>" . htmlspecialchars($e->getMessage()) . "</p>";
|
|
if (ini_get('display_errors')) echo "<pre>" . htmlspecialchars($e->getTraceAsString()) . "</pre>";
|
|
}
|
|
PHP;
|
|
|
|
// --- GENERATOR: make.php ---
|
|
$files['make.php'] = <<<'PHP'
|
|
<?php
|
|
/**
|
|
* MVC Unified CLI Tool
|
|
* Nutzung: php make.php <command> [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 <befehl> [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 <TabellenName> <Spalte1,Spalte2,...>\n\n";
|
|
echo "Details zu module:\n";
|
|
echo " php make.php module <Name> [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 = <<<PHPMODEL
|
|
<?php
|
|
|
|
namespace MVC\Model;
|
|
|
|
use MVC\Core\Model;
|
|
|
|
/**
|
|
* Model für die Tabelle '{$tableName}'
|
|
* Verfügbare Spalten: '{$columnList}'
|
|
*/
|
|
class {$className} extends Model {
|
|
protected static \$table = "{$tableName}";
|
|
|
|
// Hier kannst du spezifische Methoden für dieses Model hinzufügen
|
|
|
|
/**
|
|
* Beispiel: Suche nach einer spezifischen Spalte
|
|
*/
|
|
public static function getByFirstColumn(\$value) {
|
|
return self::where("{$columns[0]} = ?", \$value);
|
|
}
|
|
}
|
|
PHPMODEL;
|
|
|
|
// Datei schreiben
|
|
if (!is_dir(dirname($modelFile))) {
|
|
mkdir(dirname($modelFile), 0755, true);
|
|
}
|
|
|
|
if (!file_exists($modelFile)) {
|
|
file_put_contents($modelFile, $content);
|
|
echo "✅ Model erstellt: WEBROOT/src/MVC/Model/{$className}.php\n";
|
|
echo "--- INFO ---\n";
|
|
echo "Tabelle: {$tableName}\n";
|
|
echo "Klasse: MVC\\Model\\{$className}\n";
|
|
echo "Spalten: " . implode(', ', $columns) . "\n";
|
|
} else {
|
|
echo "⚠️ Model {$className}.php existiert bereits.\n";
|
|
}
|
|
}
|
|
|
|
function createModule($args) {
|
|
// Da wir args als Slice übergeben, müssen wir sie manuell parsen
|
|
$name = null;
|
|
$manualFlags = [];
|
|
$label = null;
|
|
|
|
for ($i = 0; $i < count($args); $i++) {
|
|
$arg = $args[$i];
|
|
if ($arg === '-l' || $arg === '--label') {
|
|
$label = $args[++$i] ?? null;
|
|
} else if (strpos($arg, '-') === 0) {
|
|
$flag = ltrim($arg, '-');
|
|
$manualFlags[] = $flag;
|
|
} else if ($name === null) {
|
|
$name = $arg;
|
|
}
|
|
}
|
|
|
|
if (!$name) {
|
|
echo "❌ Fehler: Kein Modulname angegeben.\n";
|
|
return;
|
|
}
|
|
|
|
$hasFlag = function($short, $long) use ($manualFlags) {
|
|
return in_array($short, $manualFlags) || in_array($long, $manualFlags);
|
|
};
|
|
|
|
$doController = $hasFlag('c', 'controller');
|
|
$doView = $hasFlag('v', 'view');
|
|
$doRoute = $hasFlag('r', 'route');
|
|
$doNav = $hasFlag('n', 'nav');
|
|
$isAuth = $hasFlag('a', 'auth');
|
|
|
|
// Wenn gar keine spezifischen Flags gesetzt wurden, mache alles
|
|
if (!($doController || $doView || $doRoute || $doNav)) {
|
|
$doController = $doView = $doRoute = $doNav = true;
|
|
}
|
|
|
|
// Namen formatieren
|
|
$key = strtolower(str_replace(' ', '_', $name));
|
|
$className = str_replace(' ', '', ucwords(str_replace('_', ' ', $key)));
|
|
$label = $label ?: ucwords(str_replace('_', ' ', $key));
|
|
|
|
$controllerFile = __DIR__ . "/WEBROOT/src/MVC/Controller/{$className}Controller.php";
|
|
$viewFile = __DIR__ . "/WEBROOT/src/MVC/View/{$key}.php";
|
|
$routesFile = __DIR__ . "/WEBROOT/src/config/routes.php";
|
|
$navFile = __DIR__ . "/WEBROOT/src/config/navigation.php";
|
|
|
|
// 1. Controller erstellen
|
|
if ($doController) {
|
|
$controllerContent = <<<PHPCTRL
|
|
<?php
|
|
|
|
namespace MVC\Controller;
|
|
|
|
use MVC\Core\Controller;
|
|
use MVC\Classes\Client;
|
|
use MVC\Classes\Cookie;
|
|
use MVC\Classes\Request;
|
|
use MVC\Classes\Server;
|
|
// use MVC\Model\YourModel; // Importiere dein Model
|
|
|
|
class {$className}Controller extends Controller {
|
|
public function {$key}() {
|
|
// Nutze Models für den Datenbankzugriff
|
|
// \$data = YourModel::all();
|
|
|
|
return \$this->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 = <<<HTML
|
|
<h2><?= htmlspecialchars(\$title) ?></h2>
|
|
<div class="card">
|
|
<p><?= htmlspecialchars(\$message) ?></p>
|
|
<p>Hier kannst du nun dein Projekt aufbauen.</p>
|
|
</div>
|
|
|
|
<style>
|
|
.card {
|
|
background: var(--card-bg);
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
border: 1px solid var(--card-border);
|
|
}
|
|
</style>
|
|
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'
|
|
<?php
|
|
if (php_sapi_name() !== 'cli') die("CLI only.\n");
|
|
require_once __DIR__ . '/WEBROOT/src/utils/databaseMethods.php';
|
|
echo "🚀 Datenbank-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' => "<?php\n\nnamespace MVC\Model;\n\nuse MVC\Core\Model;\n\nclass Mailing extends Model {\n protected static \$table = \"Mailing\";\n public static function log(\$subject, \$message, \$url, \$ip, \$date) {\n return self::insert(['DATUM' => \$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' => "<?php\n\nnamespace MVC\Model;\n\nuse MVC\Core\Model;\n\nclass Users extends Model {\n protected static \$table = \"Users\";\n public static function getByUsername(\$username) {\n \$result = self::where(\"USERNAME = ?\", \$username);\n return \$result[0] ?? null;\n }\n}"
|
|
]
|
|
];
|
|
foreach ($tables as $name => $info) {
|
|
echo "--- $name ---\n";
|
|
try {
|
|
if (DatabaseCreateTable($name, $info['columns'])) {
|
|
$path = __DIR__ . "/WEBROOT/src/MVC/Model/\$name.php";
|
|
if (!file_exists($path)) { file_put_contents($path, $info['model']); echo "✅ bereit.\n"; }
|
|
}
|
|
} catch (Exception $e) { echo "❌: " . $e->getMessage() . "\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";
|