Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e900655b47 | ||
|
|
b32f4f9323 | ||
|
|
5e65d328c9 | ||
|
|
d0e6d921ed | ||
|
|
964a3e44fc | ||
|
|
40b602a79a | ||
|
|
4ea2e014cf | ||
|
|
db5e9b0bd9 | ||
|
|
00fee2ee08 | ||
|
|
07dc6686ba | ||
|
|
2c6ea99af2 | ||
|
|
655db43616 | ||
|
|
ec6be1da80 | ||
|
|
e1c2ef46c5 | ||
|
|
7b332543a2 |
No files matched your search
@@ -1 +0,0 @@
|
||||
<?php header('Content-Type: application/json'); echo json_encode(['status' => 'online']);
|
||||
@@ -1,33 +0,0 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once __DIR__ . '/src/MVC/autoloader.php';
|
||||
$service = $_GET['service'] ?? 'home';
|
||||
try {
|
||||
$router = new \MVC\Core\Router();
|
||||
$router->add('home', \MVC\Controller\HomeController::class, 'index');
|
||||
$router->add('toggle_theme', \MVC\Controller\HomeController::class, 'toggle_theme');
|
||||
$router->add('test1', \MVC\Controller\TestController::class, 'service1');
|
||||
$router->add('test2', \MVC\Controller\TestController::class, 'service2');
|
||||
$router->add('aussentemp', \MVC\Controller\AussentempController::class, 'aussentemp', ['auth']);
|
||||
|
||||
$layout = new \MVC\Layout\Layout('main');
|
||||
$layout->setBlock('title', 'MVC Starter');
|
||||
$layout->setBlock('header_title', '🚀 MVC Dashboard');
|
||||
$layout->setBlock('header_actions', '<button class="btn-toggle" onclick="toggleTheme()">🌓 Theme umschalten</button>');
|
||||
|
||||
// Sidebar Block
|
||||
ob_start();
|
||||
?>
|
||||
<nav>
|
||||
<a href="?service=home">🏠 Home</a>
|
||||
<a href="?service=test1">🛠️ Test 1</a>
|
||||
<a href="?service=test2">📊 Test 2</a>
|
||||
</nav>
|
||||
<?php
|
||||
$layout->setBlock('sidebar', ob_get_clean());
|
||||
|
||||
$layout->setBlock('content', $router->dispatch($service));
|
||||
echo $layout->render();
|
||||
} catch (\Exception $e) {
|
||||
http_response_code(404); echo "<h1>404</h1><p>" . $e->getMessage() . "</p>";
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Controller;
|
||||
|
||||
use MVC\Core\Controller;
|
||||
// use MVC\Model\YourModel; // Importiere dein Model
|
||||
|
||||
class AussentempController extends Controller {
|
||||
public function aussentemp() {
|
||||
// Nutze Models für den Datenbankzugriff
|
||||
// $data = YourModel::all();
|
||||
|
||||
return $this->render('aussentemp', [
|
||||
'title' => 'Aussentemp Modul',
|
||||
'message' => 'Willkommen in deinem neuen Modul Aussentemp!'
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Model;
|
||||
|
||||
use MVC\Core\Model;
|
||||
|
||||
/**
|
||||
* Model für die Tabelle 'messungen'
|
||||
* Verfügbare Spalten: 'id', 'ts', 'aussentemp'
|
||||
*/
|
||||
class Messungen extends Model {
|
||||
protected static $table = "messungen";
|
||||
|
||||
// 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("id = ?", $value);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<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>
|
||||
@@ -1,6 +0,0 @@
|
||||
[database]
|
||||
APP_ENV = "dev"
|
||||
DB_DEV_HOST = "localhost"
|
||||
DB_DEV_USERNAME = "root"
|
||||
DB_DEV_PASSWORD = ""
|
||||
DB_DEV_DATABASE = "mein_projekt"
|
||||
@@ -1,79 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* MVC Model Generator
|
||||
* Nutzung: php create_model.php <TabellenName> <Spalte1,Spalte2,...>
|
||||
*/
|
||||
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
die("Dieses Script muss über die CLI ausgeführt werden.\n");
|
||||
}
|
||||
|
||||
$tableName = $argv[1] ?? null;
|
||||
$columnsInput = $argv[2] ?? 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";
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* MVC Module Generator for Gemini CLI Project
|
||||
* Nutzung: php create_module.php <name>
|
||||
*/
|
||||
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
die("Dieses Script kann nur über die Kommandozeile ausgeführt werden.\n");
|
||||
}
|
||||
|
||||
$name = $argv[1] ?? null;
|
||||
|
||||
if (!$name) {
|
||||
echo "Bitte gib einen Namen für das Modul an (z.B. mein_projekt): ";
|
||||
$handle = fopen("php://stdin", "r");
|
||||
$name = trim(fgets($handle));
|
||||
fclose($handle);
|
||||
}
|
||||
|
||||
if (!$name) {
|
||||
die("Fehler: Kein Name angegeben.\n");
|
||||
}
|
||||
|
||||
// Namen formatieren
|
||||
$key = strtolower(str_replace(' ', '_', $name));
|
||||
$className = str_replace(' ', '', ucwords(str_replace('_', ' ', $key)));
|
||||
$controllerFile = __DIR__ . "/WEBROOT/src/MVC/Controller/{$className}Controller.php";
|
||||
$viewFile = __DIR__ . "/WEBROOT/src/MVC/View/{$key}.php";
|
||||
|
||||
// 1. Controller erstellen
|
||||
$controllerContent = <<<PHPCTRL
|
||||
<?php
|
||||
|
||||
namespace MVC\Controller;
|
||||
|
||||
use MVC\Core\Controller;
|
||||
// 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
|
||||
$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. Anleitung ausgeben
|
||||
echo "\n--- NÄCHSTE SCHRITTE ---\n";
|
||||
echo "1. Registriere das Modul in WEBROOT/index.php beim Router:\n";
|
||||
echo " \$router->add('{$key}', \\MVC\\Controller\\{$className}Controller::class, '{$key}', ['auth']);\n\n";
|
||||
echo "2. Füge den Service in der Seitenverwaltung (Admin) oder in config.php hinzu:\n";
|
||||
echo " ID: {$key}\n";
|
||||
echo " Name: " . ucfirst($name) . "\n";
|
||||
echo "------------------------\n";
|
||||
File renamed without changes.
@@ -5,9 +5,9 @@
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"workbench.colorTheme": "Default Dark Modern",
|
||||
"workbench.colorTheme": "Dark Modern",
|
||||
"workbench.colorCustomizations": {
|
||||
"activityBar.background": "#ff000075",
|
||||
"activityBar.background": "#162df7ec",
|
||||
"activityBar.foreground": "#000000",
|
||||
"breadcrumb.foreground": "#FFFFFF",
|
||||
},
|
||||
@@ -29,7 +29,7 @@
|
||||
"editor.minimap.showSlider": "always",
|
||||
"files.autoSave": "off",
|
||||
"files.autoSaveDelay": 3000,
|
||||
"window.title": "BACKEND",
|
||||
"window.title": "MVC",
|
||||
"workbench.editor.wrapTabs": true,
|
||||
},
|
||||
"extensions": {
|
||||
@@ -0,0 +1,14 @@
|
||||
<?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());
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once __DIR__ . '/src/MVC/autoloader.php';
|
||||
|
||||
use MVC\Classes\Logger;
|
||||
|
||||
// Global Error & Exception Handlers
|
||||
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):
|
||||
$isLoggedIn = isset($_SESSION['user_logged_in']);
|
||||
$isAdmin = isset($_SESSION['is_admin']) && $_SESSION['is_admin'] === true;
|
||||
|
||||
// Check visibility based on Router middleware
|
||||
$requiresAuth = $router->hasMiddleware($navKey, 'auth');
|
||||
$requiresAdmin = $router->hasMiddleware($navKey, 'admin');
|
||||
|
||||
if ($requiresAuth && !$isLoggedIn) continue;
|
||||
if ($requiresAdmin && !$isAdmin) 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) {
|
||||
// Log the error globally
|
||||
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>";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?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';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Classes;
|
||||
|
||||
class Logger {
|
||||
/**
|
||||
* Ermittelt den absoluten Pfad zum Log-Verzeichnis.
|
||||
* Das Verzeichnis liegt in WEBROOT/logs
|
||||
*/
|
||||
private static function getLogDir() {
|
||||
return dirname(__DIR__, 3) . DIRECTORY_SEPARATOR . 'logs';
|
||||
}
|
||||
|
||||
/**
|
||||
* Protokolliert eine Nachricht in eine Datei.
|
||||
*
|
||||
* @param string $message Die zu protokollierende Nachricht.
|
||||
* @param string $level Das Log-Level (z.B. ERROR, INFO, DEBUG).
|
||||
* @param string $file Optional: Die Zieldatei im logs-Ordner.
|
||||
*/
|
||||
public static function log($message, $level = 'INFO', $file = 'system.log') {
|
||||
$logDir = self::getLogDir();
|
||||
|
||||
if (!is_dir($logDir)) {
|
||||
if (!mkdir($logDir, 0777, true)) {
|
||||
// Wenn das Verzeichnis nicht erstellt werden kann,
|
||||
// können wir leider nirgendwohin loggen.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$logFile = $logDir . DIRECTORY_SEPARATOR . $file;
|
||||
$timestamp = date('Y-m-d H:i:s');
|
||||
$formattedMessage = "[$timestamp] [$level] $message\n";
|
||||
|
||||
// Nutze FILE_APPEND und LOCK_EX für sicheres Schreiben
|
||||
file_put_contents($logFile, $formattedMessage, FILE_APPEND | LOCK_EX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Spezielles Logging für Exceptions/Errors inkl. Stacktrace.
|
||||
*/
|
||||
public static function logException(\Throwable $e) {
|
||||
$message = 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($message, 'CRITICAL', 'error.log');
|
||||
}
|
||||
|
||||
/**
|
||||
* Spezielles Logging für PHP-Fehler (aus set_error_handler).
|
||||
*/
|
||||
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'
|
||||
];
|
||||
$level = $levels[$errno] ?? 'UNKNOWN_ERROR';
|
||||
|
||||
$message = "$errstr in $errfile on line $errline";
|
||||
self::log($message, $level, 'error.log');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Classes;
|
||||
|
||||
use MVC\Classes\Client;
|
||||
use MVC\Classes\Request;
|
||||
use MVC\Classes\Logger;
|
||||
use MVC\Model\Mailing;
|
||||
|
||||
class Mailer {
|
||||
private static function getConfig() {
|
||||
return parse_ini_file(__DIR__ . '/../../config/env.ini');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a standard email and optionally logs it.
|
||||
*/
|
||||
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();
|
||||
|
||||
// mail() unterdrückt Fehlermeldungen standardmäßig nicht,
|
||||
// wir prüfen den Rückgabewert.
|
||||
$success = @mail($to, $subject, $message, $headers);
|
||||
|
||||
if (!$success) {
|
||||
Logger::log("Mail dispatch failed for subject: '$subject'. Check your system's sendmail/SMTP configuration.", "ERROR", "error.log");
|
||||
}
|
||||
|
||||
if ($log) {
|
||||
self::log($subject, $message, $url, false);
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an error email and optionally logs it as an error.
|
||||
*/
|
||||
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";
|
||||
$headers .= "Reply-To: " . $from . "\r\n";
|
||||
$headers .= "X-Mailer: PHP/" . phpversion();
|
||||
|
||||
$success = @mail($to, $subject, $error, $headers);
|
||||
|
||||
if (!$success) {
|
||||
Logger::log("Error-Mail dispatch failed: '$title'.", "ERROR", "error.log");
|
||||
}
|
||||
|
||||
if ($log) {
|
||||
self::log($subject, $error, null, true);
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the email to the database or falls back to file logging.
|
||||
*/
|
||||
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) {
|
||||
// DB-Log fehlgeschlagen
|
||||
}
|
||||
|
||||
self::logToFile($subject, $message, $url, $ip, $date, $isError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schreibt Log-Einträge in eine Datei (Nutzt nun die Logger-Klasse für Konsistenz).
|
||||
*/
|
||||
private static function logToFile($subject, $message, $url, $ip, $date, bool $isError) {
|
||||
$fileName = $isError ? 'mail_errors.log' : 'mails.log';
|
||||
$logEntry = "IP: $ip | URL: $url\nSubject: $subject\nMessage: $message\n" . str_repeat("-", 30);
|
||||
|
||||
Logger::log($logEntry, $isError ? 'MAIL_ERROR' : 'MAIL_INFO', $fileName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?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';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?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';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Controller;
|
||||
|
||||
use MVC\Core\Controller;
|
||||
use MVC\Classes\Mailer;
|
||||
use MVC\Model\Users;
|
||||
|
||||
class AdminController extends Controller {
|
||||
|
||||
/**
|
||||
* Service zum Senden einer Test-Mail
|
||||
*/
|
||||
public function mail_service() {
|
||||
$message = null;
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$to = $_POST['to'] ?? '';
|
||||
$subject = $_POST['subject'] ?? 'Test Mail';
|
||||
$body = $_POST['body'] ?? '';
|
||||
|
||||
if (Mailer::send($subject, $body)) {
|
||||
$message = ["type" => "success", "text" => "E-Mail erfolgreich versendet!"];
|
||||
} else {
|
||||
$message = ["type" => "error", "text" => "Fehler beim E-Mail-Versand."];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render('admin/mail', ['message' => $message]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Service zum Erstellen eines neuen Benutzers
|
||||
*/
|
||||
public function user_service() {
|
||||
$message = null;
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$username = $_POST['username'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
$email = $_POST['email'] ?? '';
|
||||
$role = $_POST['role'] ?? 'user';
|
||||
|
||||
if (!empty($username) && !empty($password)) {
|
||||
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
|
||||
|
||||
$success = Users::insert([
|
||||
'USERNAME' => $username,
|
||||
'PASSWORD' => $hashedPassword,
|
||||
'EMAIL' => $email,
|
||||
'ROLE' => $role
|
||||
]);
|
||||
|
||||
if ($success) {
|
||||
$message = ["type" => "success", "text" => "Benutzer '$username' erfolgreich erstellt!"];
|
||||
} else {
|
||||
$message = ["type" => "error", "text" => "Fehler beim Erstellen des Benutzers (evtl. Name schon vergeben)."];
|
||||
}
|
||||
} else {
|
||||
$message = ["type" => "error", "text" => "Bitte Benutzername und Passwort angeben."];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render('admin/users', ['message' => $message]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?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',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Controller;
|
||||
|
||||
use MVC\Core\Controller;
|
||||
use MVC\Model\Users;
|
||||
|
||||
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'] ?? '';
|
||||
|
||||
$user = Users::getByUsername($username);
|
||||
|
||||
if ($user && password_verify($password, $user['PASSWORD'])) {
|
||||
$_SESSION['user_logged_in'] = true;
|
||||
$_SESSION['username'] = $user['USERNAME'];
|
||||
$_SESSION['role'] = $user['ROLE'];
|
||||
$_SESSION['is_admin'] = ($user['ROLE'] === 'admin');
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Controller;
|
||||
|
||||
use MVC\Core\Controller;
|
||||
|
||||
class DocsController extends Controller {
|
||||
public function index() {
|
||||
return $this->render('docs', [
|
||||
'title' => '📚 System-Dokumentation'
|
||||
]);
|
||||
}
|
||||
}
|
||||
+4
@@ -1,6 +1,10 @@
|
||||
<?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() {
|
||||
+4
@@ -1,6 +1,10 @@
|
||||
<?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() {
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Controller;
|
||||
|
||||
use MVC\Core\Controller;
|
||||
use MVC\Model\Users;
|
||||
|
||||
class UserMgmtController extends Controller {
|
||||
public function user_mgmt() {
|
||||
$action = $_GET['action'] ?? 'list';
|
||||
$message = null;
|
||||
|
||||
// 1. Handle Delete (delete())
|
||||
if ($action === 'delete' && isset($_GET['id'])) {
|
||||
if (Users::delete("ID = ?", $_GET['id'])) {
|
||||
$message = ['type' => 'success', 'text' => 'Benutzer erfolgreich gelöscht.'];
|
||||
}
|
||||
$action = 'list';
|
||||
}
|
||||
|
||||
// 2. Handle Create (insert())
|
||||
if ($action === 'create' && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$data = [
|
||||
'USERNAME' => $_POST['username'],
|
||||
'PASSWORD' => password_hash($_POST['password'], PASSWORD_DEFAULT),
|
||||
'EMAIL' => $_POST['email'],
|
||||
'ROLE' => $_POST['role']
|
||||
];
|
||||
if (Users::insert($data)) {
|
||||
$message = ['type' => 'success', 'text' => 'Benutzer erstellt.'];
|
||||
$action = 'list';
|
||||
} else {
|
||||
$message = ['type' => 'error', 'text' => 'Fehler beim Erstellen.'];
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Handle Update (update())
|
||||
if ($action === 'edit' && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['id'])) {
|
||||
$data = [
|
||||
'USERNAME' => $_POST['username'],
|
||||
'EMAIL' => $_POST['email'],
|
||||
'ROLE' => $_POST['role']
|
||||
];
|
||||
// Nur Passwort updaten wenn ausgefüllt
|
||||
if (!empty($_POST['password'])) {
|
||||
$data['PASSWORD'] = password_hash($_POST['password'], PASSWORD_DEFAULT);
|
||||
}
|
||||
|
||||
if (Users::update($data, "ID = ?", $_POST['id'])) {
|
||||
$message = ['type' => 'success', 'text' => 'Benutzer aktualisiert.'];
|
||||
$action = 'list';
|
||||
} else {
|
||||
$message = ['type' => 'error', 'text' => 'Fehler beim Update.'];
|
||||
}
|
||||
}
|
||||
|
||||
// --- View Logic ---
|
||||
|
||||
$users = [];
|
||||
$editUser = null;
|
||||
|
||||
if ($action === 'list') {
|
||||
// Test select() & where() & all()
|
||||
if (!empty($_GET['search'])) {
|
||||
$users = Users::where("USERNAME LIKE ?", "%" . $_GET['search'] . "%");
|
||||
} else if (isset($_GET['simple'])) {
|
||||
$users = Users::select(["ID", "USERNAME", "ROLE"]);
|
||||
} else {
|
||||
$users = Users::all();
|
||||
}
|
||||
} else if ($action === 'edit' && isset($_GET['id'])) {
|
||||
// Test find()
|
||||
$editUser = Users::find($_GET['id']);
|
||||
}
|
||||
|
||||
return $this->render('user_mgmt', [
|
||||
'action' => $action,
|
||||
'users' => $users,
|
||||
'editUser' => $editUser,
|
||||
'message' => $message
|
||||
]);
|
||||
}
|
||||
}
|
||||
File renamed without changes.
@@ -20,6 +20,13 @@ abstract class Model {
|
||||
return \DatabaseShow(static::$table, "*");
|
||||
}
|
||||
|
||||
public static function select($columns = "*") {
|
||||
if (is_array($columns)) {
|
||||
$columns = implode(", ", $columns);
|
||||
}
|
||||
return \DatabaseShow(static::$table, $columns);
|
||||
}
|
||||
|
||||
public static function find($id, $column = 'ID') {
|
||||
$result = \DatabaseShow(static::$table, "*", "$column = ?", $id);
|
||||
return $result[0] ?? null;
|
||||
+9
-1
@@ -42,6 +42,14 @@ class Router {
|
||||
return $controller->$method();
|
||||
}
|
||||
|
||||
public function getRouteMiddleware($service) {
|
||||
return $this->routes[$service]['middlewares'] ?? [];
|
||||
}
|
||||
|
||||
public function hasMiddleware($service, $middleware) {
|
||||
return in_array($middleware, $this->getRouteMiddleware($service));
|
||||
}
|
||||
|
||||
protected function runMiddleware($middleware) {
|
||||
if ($middleware === 'admin') {
|
||||
if (!isset($_SESSION['is_admin']) || $_SESSION['is_admin'] !== true) {
|
||||
@@ -51,7 +59,7 @@ class Router {
|
||||
|
||||
if ($middleware === 'auth') {
|
||||
if (empty($_SESSION['user_logged_in'])) {
|
||||
header('Location: login.php');
|
||||
header('Location: ?service=login');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
+1
@@ -15,6 +15,7 @@
|
||||
.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>
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Model;
|
||||
|
||||
use MVC\Core\Model;
|
||||
|
||||
/**
|
||||
* Model für die Tabelle 'Mailing'
|
||||
* Verfügbare Spalten: 'DATUM', 'SUBJECT', 'MESSAGE', 'URL', 'CLIENT_IP'
|
||||
*/
|
||||
class Mailing extends Model {
|
||||
protected static $table = "Mailing";
|
||||
|
||||
/**
|
||||
* Erstellt einen neuen Log-Eintrag für eine versendete E-Mail.
|
||||
*/
|
||||
public static function log($subject, $message, $url, $ip, $date) {
|
||||
return self::insert([
|
||||
'DATUM' => $date,
|
||||
'SUBJECT' => $subject,
|
||||
'MESSAGE' => $message,
|
||||
'URL' => $url,
|
||||
'CLIENT_IP' => $ip
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Model;
|
||||
|
||||
use MVC\Core\Model;
|
||||
|
||||
class Users extends Model {
|
||||
protected static $table = "Users";
|
||||
|
||||
/**
|
||||
* Findet einen Benutzer nach seinem Namen.
|
||||
*/
|
||||
public static function getByUsername($username) {
|
||||
$result = self::where("USERNAME = ?", $username);
|
||||
return $result[0] ?? null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<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)' ?>; color: var(--text);">
|
||||
<?= $message['text'] ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div style="background: var(--sidebar); padding: 30px; border-radius: 8px; border: 1px solid var(--border);">
|
||||
<form method="POST">
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 5px;">Betreff</label>
|
||||
<input type="text" name="subject" required style="width: 100%; padding: 10px; border-radius: 4px; border: 1px solid var(--border); background: var(--bg); color: var(--text);">
|
||||
</div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 5px;">Nachricht</label>
|
||||
<textarea name="body" required style="width: 100%; height: 150px; padding: 10px; border-radius: 4px; border: 1px solid var(--border); background: var(--bg); color: var(--text);"></textarea>
|
||||
</div>
|
||||
<button type="submit" style="background: var(--accent); color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer;">E-Mail senden</button>
|
||||
</form>
|
||||
</div>
|
||||
@@ -0,0 +1,33 @@
|
||||
<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)' ?>; color: var(--text);">
|
||||
<?= $message['text'] ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div style="background: var(--sidebar); padding: 30px; border-radius: 8px; border: 1px solid var(--border);">
|
||||
<h3>Neuen Benutzer anlegen</h3>
|
||||
<form method="POST">
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 5px;">Benutzername</label>
|
||||
<input type="text" name="username" required style="width: 100%; padding: 10px; border-radius: 4px; border: 1px solid var(--border); background: var(--bg); color: var(--text);">
|
||||
</div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 5px;">Passwort</label>
|
||||
<input type="password" name="password" required style="width: 100%; padding: 10px; border-radius: 4px; border: 1px solid var(--border); background: var(--bg); color: var(--text);">
|
||||
</div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 5px;">E-Mail</label>
|
||||
<input type="email" name="email" style="width: 100%; padding: 10px; border-radius: 4px; border: 1px solid var(--border); background: var(--bg); color: var(--text);">
|
||||
</div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 5px;">Rolle</label>
|
||||
<select name="role" style="width: 100%; padding: 10px; border-radius: 4px; border: 1px solid var(--border); background: var(--bg); color: var(--text);">
|
||||
<option value="user">User</option>
|
||||
<option value="admin">Admin</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" style="background: var(--accent); color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer;">Benutzer erstellen</button>
|
||||
</form>
|
||||
</div>
|
||||
@@ -0,0 +1,223 @@
|
||||
<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>
|
||||
<li><a href="#model-functions">11. Model Core Funktionen</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-Generatoren</h2>
|
||||
<p>Das Framework bietet ein zentrales CLI-Tool zur Automatisierung von Aufgaben.</p>
|
||||
|
||||
<h3>Modul-Generator</h3>
|
||||
<p>Erstellt Controller, View, Route und Navigationspunkt in einem Schritt.</p>
|
||||
<pre>php make.php module [Name] [Optionen]</pre>
|
||||
<ul>
|
||||
<li><code>-c, --controller</code>: Nur den Controller erstellen.</li>
|
||||
<li><code>-v, --view</code>: Nur die View erstellen.</li>
|
||||
<li><code>-r, --route</code>: Nur die Route registrieren.</li>
|
||||
<li><code>-n, --nav</code>: Nur den Navigationspunkt hinzufügen.</li>
|
||||
<li><code>-a, --auth</code>: Erfordert Login für diesen Service.</li>
|
||||
<li><code>-l, --label "Text"</code>: Anzeigename in der Sidebar.</li>
|
||||
</ul>
|
||||
<p><strong>Beispiel:</strong> <code>php make.php module profile -l "👤 Profil" -a</code></p>
|
||||
|
||||
<h3>Model-Generator</h3>
|
||||
<p>Erstellt ein Model basierend auf einer Datenbank-Tabelle.</p>
|
||||
<pre>php make.php model [Tabellenname] [Spalten,kommagetrennt]</pre>
|
||||
<p><strong>Beispiel:</strong> <code>php make.php model Products NAME,PRICE,STOCK</code></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="private-services">
|
||||
<h2>9. Private Services (Admin)</h2>
|
||||
<p>Das Framework bringt 3 Schutzklassen in der <code>/config/routes.php</code> mit:</p>
|
||||
<ul>
|
||||
<li><strong>Example: </strong><code>$router->add('home', \MVC\Controller\HomeController::class, 'index');</code></li>
|
||||
<ul><li>Dieser Service ist öffentlich verfügbar.</li></ul>
|
||||
<li><strong>Example: </strong><code>$router->add('home', \MVC\Controller\HomeController::class, 'index', ['auth']);</code></li>
|
||||
<ul><li>Dieser Service ist mit Login verfügbar.</li></ul>
|
||||
<li><strong>Example: </strong><code>$router->add('home', \MVC\Controller\HomeController::class, 'index', ['auth', 'admin']);</code></li>
|
||||
<ul><li>Dieser Service ist mit Login und Admin-Rechten verfügbar.</li></ul>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><strong>Mail Service (admin_mail):</strong> Ein Formular zum Testen des Mail-Versands.</li>
|
||||
<li><strong>User Service (admin_users):</strong> Ermöglicht das Erstellen neuer Benutzer direkt in die Datenbank (Passwörter werden automatisch gehasht).</li>
|
||||
</ul>
|
||||
<p>Diese Services werden über den <code>AdminController</code> gesteuert.</p>
|
||||
</section>
|
||||
|
||||
<section id="best-practices">
|
||||
<h2>10. 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>
|
||||
|
||||
<section id="model-functions">
|
||||
<h2>11. Model Core Funktionen</h2>
|
||||
<p>Die Basisklasse <code>MVC\Core\Model</code> bietet ein objektorientiertes Interface für Datenbankoperationen.</p>
|
||||
|
||||
<h3>all()</h3>
|
||||
<p>Gibt alle Datensätze der Tabelle zurück.</p>
|
||||
<pre><code>$users = Users::all();</code></pre>
|
||||
|
||||
<h3>select($columns)</h3>
|
||||
<p>Gibt spezifische Spalten zurück. Akzeptiert einen String oder ein Array.</p>
|
||||
<pre><code>// Als String
|
||||
$names = Users::select("USERNAME, EMAIL");
|
||||
|
||||
// Als Array
|
||||
$data = Users::select(["USERNAME", "ROLE"]);</code></pre>
|
||||
|
||||
<h3>find($id, $column = 'ID')</h3>
|
||||
<p>Sucht einen einzelnen Datensatz anhand einer Spalte (Standard: ID).</p>
|
||||
<pre><code>$user = Users::find(1);
|
||||
$userByMail = Users::find("admin@example.com", "EMAIL");</code></pre>
|
||||
|
||||
<h3>where($where, ...$params)</h3>
|
||||
<p>Filtert Datensätze mit Prepared Statements.</p>
|
||||
<pre><code>$admins = Users::where("ROLE = ?", "admin");
|
||||
$activeUsers = Users::where("STATUS = ? AND ROLE = ?", "active", "user");</code></pre>
|
||||
|
||||
<h3>insert($data)</h3>
|
||||
<p>Fügt einen neuen Datensatz hinzu.</p>
|
||||
<pre><code>Users::insert([
|
||||
'USERNAME' => 'new_user',
|
||||
'PASSWORD' => password_hash('secret', PASSWORD_DEFAULT),
|
||||
'ROLE' => 'user'
|
||||
]);</code></pre>
|
||||
|
||||
<h3>update($data, $where, ...$whereParams)</h3>
|
||||
<p>Aktualisiert bestehende Datensätze.</p>
|
||||
<pre><code>Users::update(['ROLE' => 'admin'], "ID = ?", 5);</code></pre>
|
||||
|
||||
<h3>delete($where, ...$params)</h3>
|
||||
<p>Löscht Datensätze.</p>
|
||||
<pre><code>Users::delete("ID = ?", 10);</code></pre>
|
||||
|
||||
<h3>query($sql, ...$params)</h3>
|
||||
<p>Führt eine native SQL-Abfrage mit Prepared Statements aus.</p>
|
||||
<pre><code>$result = Users::query("SELECT COUNT(*) as total FROM Users");</code></pre>
|
||||
</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>
|
||||
File renamed without changes.
@@ -0,0 +1,25 @@
|
||||
<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>
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,92 @@
|
||||
<h2>👥 User Management (Full CRUD Demo)</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)'?>; border: 1px solid <?= $message['type']==='success'?'#2ecc71':'#e74c3c'?>;">
|
||||
<?= $message['text'] ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div style="margin-bottom: 20px;">
|
||||
<a href="?service=user_mgmt&action=list" class="btn">Alle Benutzer</a>
|
||||
<a href="?service=user_mgmt&action=list&simple=1" class="btn">Nur Basisdaten (select())</a>
|
||||
<a href="?service=user_mgmt&action=create" class="btn btn-primary">➕ Neu erstellen (insert())</a>
|
||||
</div>
|
||||
|
||||
<?php if ($action === 'list'): ?>
|
||||
<form method="GET" style="margin-bottom: 20px; display: flex; gap: 10px;">
|
||||
<input type="hidden" name="service" value="user_mgmt">
|
||||
<input type="text" name="search" placeholder="Suche nach Username (where())..." value="<?= htmlspecialchars($_GET['search'] ?? '') ?>" style="flex: 1; padding: 10px; border-radius: 4px; border: 1px solid var(--border); background: var(--bg); color: var(--text);">
|
||||
<button type="submit" class="btn">Suchen</button>
|
||||
</form>
|
||||
|
||||
<table style="width: 100%; border-collapse: collapse;">
|
||||
<thead>
|
||||
<tr style="background: var(--sidebar); text-align: left;">
|
||||
<th style="padding: 12px; border: 1px solid var(--border);">ID</th>
|
||||
<th style="padding: 12px; border: 1px solid var(--border);">Username</th>
|
||||
<?php if (!isset($_GET['simple'])): ?>
|
||||
<th style="padding: 12px; border: 1px solid var(--border);">Email</th>
|
||||
<?php endif; ?>
|
||||
<th style="padding: 12px; border: 1px solid var(--border);">Role</th>
|
||||
<th style="padding: 12px; border: 1px solid var(--border);">Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($users as $user): ?>
|
||||
<tr>
|
||||
<td style="padding: 12px; border: 1px solid var(--border);"><?= $user['ID'] ?></td>
|
||||
<td style="padding: 12px; border: 1px solid var(--border);"><?= htmlspecialchars($user['USERNAME']) ?></td>
|
||||
<?php if (!isset($_GET['simple'])): ?>
|
||||
<td style="padding: 12px; border: 1px solid var(--border);"><?= htmlspecialchars($user['EMAIL']) ?></td>
|
||||
<?php endif; ?>
|
||||
<td style="padding: 12px; border: 1px solid var(--border);"><span style="background: var(--accent); color: white; padding: 2px 6px; border-radius: 4px; font-size: 0.8rem;"><?= $user['ROLE'] ?></span></td>
|
||||
<td style="padding: 12px; border: 1px solid var(--border);">
|
||||
<a href="?service=user_mgmt&action=edit&id=<?= $user['ID'] ?>" style="color: var(--accent); text-decoration: none;">Bearbeiten (find())</a> |
|
||||
<a href="?service=user_mgmt&action=delete&id=<?= $user['ID'] ?>" style="color: #e74c3c; text-decoration: none;" onclick="return confirm('Sicher?')">Löschen (delete())</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<?php elseif ($action === 'create' || $action === 'edit'): ?>
|
||||
<h3><?= $action === 'edit' ? '✏️ Benutzer bearbeiten' : '➕ Neuen Benutzer anlegen' ?></h3>
|
||||
<form method="POST" action="?service=user_mgmt&action=<?= $action ?>" style="background: var(--sidebar); padding: 25px; border-radius: 8px; border: 1px solid var(--border);">
|
||||
<?php if ($action === 'edit'): ?>
|
||||
<input type="hidden" name="id" value="<?= $editUser['ID'] ?>">
|
||||
<?php endif; ?>
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 5px;">Username</label>
|
||||
<input type="text" name="username" required value="<?= htmlspecialchars($editUser['USERNAME'] ?? '') ?>" style="width: 100%; padding: 10px; border-radius: 4px; border: 1px solid var(--border); background: var(--bg); color: var(--text);">
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 5px;">E-Mail</label>
|
||||
<input type="email" name="email" value="<?= htmlspecialchars($editUser['EMAIL'] ?? '') ?>" style="width: 100%; padding: 10px; border-radius: 4px; border: 1px solid var(--border); background: var(--bg); color: var(--text);">
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 5px;">Passwort <?= $action === 'edit' ? '(leer lassen zum Behalten)' : '' ?></label>
|
||||
<input type="password" name="password" <?= $action === 'create' ? 'required' : '' ?> style="width: 100%; padding: 10px; border-radius: 4px; border: 1px solid var(--border); background: var(--bg); color: var(--text);">
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 20px;">
|
||||
<label style="display: block; margin-bottom: 5px;">Rolle</label>
|
||||
<select name="role" style="width: 100%; padding: 10px; border-radius: 4px; border: 1px solid var(--border); background: var(--bg); color: var(--text);">
|
||||
<option value="user" <?= ($editUser['ROLE'] ?? '') === 'user' ? 'selected' : '' ?>>User</option>
|
||||
<option value="admin" <?= ($editUser['ROLE'] ?? '') === 'admin' ? 'selected' : '' ?>>Admin</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary"><?= $action === 'edit' ? 'Speichern (update())' : 'Erstellen (insert())' ?></button>
|
||||
<a href="?service=user_mgmt&action=list" class="btn">Abbrechen</a>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
|
||||
<style>
|
||||
.btn { display: inline-block; padding: 10px 20px; text-decoration: none; border-radius: 4px; background: var(--sidebar); color: var(--text); border: 1px solid var(--border); cursor: pointer; font-size: 0.9rem; }
|
||||
.btn:hover { background: rgba(0,0,0,0.1); }
|
||||
.btn-primary { background: var(--accent); color: white; border: none; }
|
||||
.btn-primary:hover { opacity: 0.9; }
|
||||
</style>
|
||||
File renamed without changes.
@@ -0,0 +1,19 @@
|
||||
[database]
|
||||
APP_ENV = "dev"
|
||||
|
||||
DB_ROOT_USERNAME = "root"
|
||||
DB_ROOT_PASSWORD = "bBHofurUJWKzPE8Z"
|
||||
|
||||
DB_DEV_HOST = "blackpi.ipv64.net"
|
||||
DB_DEV_USERNAME = "worker"
|
||||
DB_DEV_PASSWORD = "bBHofurUJWKzPE8Z"
|
||||
DB_DEV_DATABASE = "framework"
|
||||
|
||||
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"
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
/**
|
||||
* Define your sidebar navigation items here.
|
||||
* Format: 'service_name' => ['label' => 'Label']
|
||||
*/
|
||||
return [
|
||||
'home' => ['label' => '🏠 Home'],
|
||||
'test1' => ['label' => '🛠️ Test 1'],
|
||||
'test2' => ['label' => '📊 Test 2'],
|
||||
'admin_mail' => ['label' => '📧 Mail Service'],
|
||||
'admin_users' => ['label' => '👤 User Service'],
|
||||
'docs' => ['label' => '📚 Dokumentation'],
|
||||
'user_mgmt' => ['label' => '👥 User Management'],
|
||||
];
|
||||
@@ -0,0 +1,17 @@
|
||||
<?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', 'admin']);
|
||||
$router->add('admin_users', \MVC\Controller\AdminController::class, 'user_service', ['auth', 'admin']);
|
||||
$router->add('user_mgmt', \MVC\Controller\UserMgmtController::class, 'user_mgmt', ['auth', 'admin']);
|
||||
+19
@@ -18,3 +18,22 @@ function db_connect() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
File renamed without changes.
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
/**
|
||||
* Database & Models Installer
|
||||
* Nutzung: php install_db.php
|
||||
*/
|
||||
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
die("Dieses Script muss über die CLI ausgeführt werden.\n");
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/WEBROOT/src/database/databaseConnect.php';
|
||||
require_once __DIR__ . '/WEBROOT/src/utils/databaseMethods.php';
|
||||
|
||||
$config = parse_ini_file(__DIR__ . '/WEBROOT/src/config/env.ini');
|
||||
$env = strtoupper($config['APP_ENV']);
|
||||
$dbName = $config["DB_{$env}_DATABASE"];
|
||||
$workerUser = $config["DB_{$env}_USERNAME"];
|
||||
$workerPass = $config["DB_{$env}_PASSWORD"];
|
||||
|
||||
echo "🚀 Starte Datenbank-Setup als Root...\n";
|
||||
$rootConn = db_connect_root();
|
||||
if (!$rootConn) {
|
||||
die("❌ Fehler: Verbindung als Root fehlgeschlagen. Bitte prüfe DB_ROOT credentials in env.ini\n");
|
||||
}
|
||||
|
||||
// 1. Datenbank erstellen
|
||||
echo "📁 Erstelle Datenbank '$dbName'...\n";
|
||||
$rootConn->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 "🚀 Starte Datenbank-Tabellen-Installation...\n";
|
||||
|
||||
// Definitionen der Tabellen und Models
|
||||
$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
|
||||
<?php
|
||||
|
||||
namespace MVC\Model;
|
||||
|
||||
use MVC\Core\Model;
|
||||
|
||||
class Mailing extends Model {
|
||||
protected static \$table = "Mailing";
|
||||
|
||||
public static function log(\$subject, \$message, \$url, \$ip, \$date) {
|
||||
return self::insert([
|
||||
'DATUM' => \$date,
|
||||
'SUBJECT' => \$subject,
|
||||
'MESSAGE' => \$message,
|
||||
'URL' => \$url,
|
||||
'CLIENT_IP' => \$ip
|
||||
]);
|
||||
}
|
||||
}
|
||||
PHP
|
||||
],
|
||||
'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
|
||||
<?php
|
||||
|
||||
namespace MVC\Model;
|
||||
|
||||
use MVC\Core\Model;
|
||||
|
||||
class Users extends Model {
|
||||
protected static \$table = "Users";
|
||||
|
||||
/**
|
||||
* Findet einen Benutzer nach seinem Namen.
|
||||
*/
|
||||
public static function getByUsername(\$username) {
|
||||
\$result = self::where("USERNAME = ?", \$username);
|
||||
return \$result[0] ?? null;
|
||||
}
|
||||
}
|
||||
PHP
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($tables as $tableName => $info) {
|
||||
echo "--- Tabelle: $tableName ---\n";
|
||||
try {
|
||||
if (DatabaseCreateTable($tableName, $info['columns'])) {
|
||||
echo "✅ Struktur bereit.\n";
|
||||
|
||||
$modelPath = __DIR__ . "/WEBROOT/src/MVC/Model/{$tableName}.php";
|
||||
if (!file_exists($modelPath)) {
|
||||
if (!is_dir(dirname($modelPath))) mkdir(dirname($modelPath), 0755, true);
|
||||
file_put_contents($modelPath, $info['model']);
|
||||
echo "✅ Model erstellt: $modelPath\n";
|
||||
} else {
|
||||
echo "ℹ️ Model existiert bereits.\n";
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Fehler bei $tableName: " . $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";
|
||||
@@ -0,0 +1,270 @@
|
||||
<?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);
|
||||
$navItem = " '{$key}' => ['label' => '{$label}'],\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";
|
||||
}
|
||||
File renamed without changes.
Reference in new issue
Block a user