PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, ] ); // Idempotent migrations — safe to run on every boot $pdo->exec('ALTER TABLE delivery_log ADD COLUMN IF NOT EXISTS is_sandbox TINYINT(1) NOT NULL DEFAULT 0'); $pdo->exec("INSERT IGNORE INTO `system` (setting_name, setting_value) VALUES ('sandbox_mode', '0')"); return $pdo; } /** * Sends a JSON response and exits. */ function json_out(mixed $data, int $status = 200): never { http_response_code($status); header('Content-Type: application/json; charset=utf-8'); echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); exit; } /** * Decodes the request body as JSON and returns an array. */ function json_body(): array { $raw = file_get_contents('php://input'); if ($raw === false || $raw === '') { return []; } return json_decode($raw, true) ?? []; } /** * Sends a JSON error response and exits. */ function error_out(string $message, int $status = 400): never { json_out(['error' => $message], $status); }