Selaa lähdekoodia

fixing type error

Josef Straßl 1 kuukausi sitten
vanhempi
sitoutus
1ae1ef67c0
5 muutettua tiedostoa jossa 92 lisäystä ja 11 poistoa
  1. 2 2
      config/app.php
  2. 3 0
      docs/OPERATIONS.md
  3. 12 3
      index.php
  4. 73 4
      src/App/Bootstrap.php
  5. 2 2
      src/Storage/JsonStore.php

+ 2 - 2
config/app.php

@@ -6,11 +6,11 @@ $root = dirname(__DIR__);
 
 return [
     'project_name' => 'Feuerwehr Freising Mitgliedsantrag',
-    'base_url' => 'https://antrag.med0.de',
+    'base_url' => '/',
     'contact_email' => 'josef.strassl@feuerwehr-freising.de',
     'disclaimer' => [
         'title' => 'Wichtiger Hinweis',
-        'text' => \"Bitte lesen Sie diesen Hinweis vor Beginn sorgfältig.\\n\\nMit dem Fortfahren bestätigen Sie, dass Ihre Angaben vollständig und wahrheitsgemäß sind.\\nIhre Daten werden ausschließlich zur Bearbeitung des Mitgliedsantrags verwendet.\",
+        'text' => "Bitte lesen Sie diesen Hinweis vor Beginn sorgfältig.\n\nMit dem Fortfahren bestätigen Sie, dass Ihre Angaben vollständig und wahrheitsgemäß sind.\nIhre Daten werden ausschließlich zur Bearbeitung des Mitgliedsantrags verwendet.",
         'accept_label' => 'Hinweis gelesen, weiter zum Antrag',
     ],
     'retention' => [

+ 3 - 0
docs/OPERATIONS.md

@@ -25,6 +25,8 @@ php /pfad/zum/projekt/bin/cleanup.php
 - `storage/logs/cleanup.log`
 - `storage/logs/mail.log`
 - `storage/logs/app.log`
+- `storage/logs/php_runtime.log`
+- `storage/logs/php_fatal.log`
 
 ## Rate Limiting
 
@@ -57,3 +59,4 @@ Regelmäßig sichern:
 - Login geht nicht: `admin.password_hash` prüfen, ggf. temporär `password_plain_fallback` nutzen.
 - ZIP Download fehlgeschlagen: `ZipArchive` Erweiterung auf Hosting prüfen.
 - Viele `429` Antworten: `docs/RATE_LIMITING.md` prüfen, Limits anpassen oder `storage/rate_limit/` kontrollieren.
+- 500 ohne Apache/PHP-Fehlerausgabe: `storage/logs/php_fatal.log` und `storage/logs/php_runtime.log` prüfen.

+ 12 - 3
index.php

@@ -13,9 +13,18 @@ $schema = new FormSchema();
 $steps = $schema->getSteps();
 $csrf = Csrf::token();
 $app = Bootstrap::config('app');
-$disclaimerTitle = (string) ($app['disclaimer']['title'] ?? 'Hinweis');
-$disclaimerText = (string) ($app['disclaimer']['text'] ?? '');
-$disclaimerAcceptLabel = (string) ($app['disclaimer']['accept_label'] ?? 'Hinweis gelesen, weiter');
+$disclaimerConfigRaw = $app['disclaimer'] ?? [];
+if (is_string($disclaimerConfigRaw)) {
+    $disclaimerConfig = ['text' => $disclaimerConfigRaw];
+} elseif (is_array($disclaimerConfigRaw)) {
+    $disclaimerConfig = $disclaimerConfigRaw;
+} else {
+    $disclaimerConfig = [];
+}
+
+$disclaimerTitle = (string) ($disclaimerConfig['title'] ?? 'Hinweis');
+$disclaimerText = (string) ($disclaimerConfig['text'] ?? '');
+$disclaimerAcceptLabel = (string) ($disclaimerConfig['accept_label'] ?? 'Hinweis gelesen, weiter');
 
 /** @param array<string, mixed> $field */
 function renderField(array $field): void

+ 73 - 4
src/App/Bootstrap.php

@@ -7,6 +7,7 @@ namespace App\App;
 final class Bootstrap
 {
     private static bool $booted = false;
+    private static bool $errorHandlingInitialized = false;
 
     /** @var array<string, mixed> */
     private static array $config = [];
@@ -18,6 +19,7 @@ final class Bootstrap
         }
 
         self::$booted = true;
+        self::initializeErrorHandling();
 
         date_default_timezone_set('Europe/Berlin');
 
@@ -29,9 +31,9 @@ final class Bootstrap
             ]);
         }
 
-        self::$config['app'] = require self::rootPath() . '/config/app.php';
-        self::$config['mail'] = require self::rootPath() . '/config/mail.php';
-        self::$config['form_schema'] = require self::rootPath() . '/config/form_schema.php';
+        self::$config['app'] = self::loadConfigArray(self::rootPath() . '/config/app.php', 'app');
+        self::$config['mail'] = self::loadConfigArray(self::rootPath() . '/config/mail.php', 'mail');
+        self::$config['form_schema'] = self::loadConfigArray(self::rootPath() . '/config/form_schema.php', 'form_schema');
 
         self::ensureStorageDirectories();
     }
@@ -44,7 +46,8 @@ final class Bootstrap
     /** @return array<string, mixed> */
     public static function config(string $name): array
     {
-        return self::$config[$name] ?? [];
+        $value = self::$config[$name] ?? [];
+        return is_array($value) ? $value : [];
     }
 
     /** @param array<string, mixed> $payload */
@@ -83,4 +86,70 @@ final class Bootstrap
             }
         }
     }
+
+    /** @return array<string, mixed> */
+    private static function loadConfigArray(string $path, string $name): array
+    {
+        if (!is_file($path)) {
+            self::log('php_fatal', sprintf('Missing config file: %s (%s)', $name, $path));
+            return [];
+        }
+
+        try {
+            $loaded = require $path;
+        } catch (\Throwable $e) {
+            self::log('php_fatal', sprintf('Failed loading config %s: %s', $name, $e->getMessage()));
+            return [];
+        }
+
+        if (!is_array($loaded)) {
+            self::log('php_fatal', sprintf('Config %s must return an array (%s)', $name, $path));
+            return [];
+        }
+
+        return $loaded;
+    }
+
+    private static function initializeErrorHandling(): void
+    {
+        if (self::$errorHandlingInitialized) {
+            return;
+        }
+
+        self::$errorHandlingInitialized = true;
+
+        $logsDir = self::rootPath() . '/storage/logs';
+        if (!is_dir($logsDir)) {
+            @mkdir($logsDir, 0775, true);
+        }
+
+        $phpErrorLog = $logsDir . '/php_runtime.log';
+        @ini_set('log_errors', '1');
+        @ini_set('display_errors', '0');
+        @ini_set('error_log', $phpErrorLog);
+
+        register_shutdown_function(static function (): void {
+            $lastError = error_get_last();
+            if (!is_array($lastError)) {
+                return;
+            }
+
+            $fatalTypes = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR];
+            if (!in_array((int) ($lastError['type'] ?? 0), $fatalTypes, true)) {
+                return;
+            }
+
+            $line = sprintf(
+                "[%s] Fatal error on %s: %s in %s:%s\n",
+                date('c'),
+                (string) ($_SERVER['REQUEST_URI'] ?? 'CLI'),
+                (string) ($lastError['message'] ?? 'unknown'),
+                (string) ($lastError['file'] ?? 'unknown'),
+                (string) ($lastError['line'] ?? '?')
+            );
+
+            $fatalLog = Bootstrap::rootPath() . '/storage/logs/php_fatal.log';
+            @file_put_contents($fatalLog, $line, FILE_APPEND);
+        });
+    }
 }

+ 2 - 2
src/Storage/JsonStore.php

@@ -186,12 +186,12 @@ final class JsonStore
         $handle = fopen($lockFile, 'c+');
 
         if ($handle === false) {
-            throw new RuntimeException('Lock-Datei konnte nicht geöffnet werden.');
+            throw new RuntimeException('Could not open lock file.');
         }
 
         try {
             if (!flock($handle, LOCK_EX)) {
-                throw new RuntimeException('Lock konnte nicht gesetzt werden.');
+                throw new RuntimeException('Could not acquire lock.');
             }
 
             return $callback();