PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, ]); // Enable foreign key enforcement (off by default in SQLite) $pdo->exec('PRAGMA foreign_keys = ON'); } catch (PDOException $e) { http_response_code(500); echo json_encode(['error' => 'Database connection failed']); exit; } } return $pdo; } function db_init(): void { $sql = file_get_contents(__DIR__ . '/../schema.sql'); if (!$sql) return; db()->exec($sql); } function db_query(string $sql, array $params = []): PDOStatement { $stmt = db()->prepare($sql); $stmt->execute($params); return $stmt; } function db_one(string $sql, array $params = []): ?array { $stmt = db_query($sql, $params); $row = $stmt->fetch(); return $row ?: null; } function db_all(string $sql, array $params = []): array { $stmt = db_query($sql, $params); return $stmt->fetchAll(); } function db_insert(string $table, array $data): int { $cols = implode(', ', array_map(fn($c) => "`$c`", array_keys($data))); $placeholders = implode(', ', array_fill(0, count($data), '?')); $sql = "INSERT INTO {$table} ({$cols}) VALUES ({$placeholders})"; db_query($sql, array_values($data)); return (int) db()->lastInsertId(); } function db_update(string $table, array $data, string $where, array $whereParams): int { $set = implode(', ', array_map(fn($c) => "`$c` = ?", array_keys($data))); $sql = "UPDATE {$table} SET {$set} WHERE {$where}"; $stmt = db_query($sql, array_merge(array_values($data), $whereParams)); return $stmt->rowCount(); } function slugify(string $text): string { $slug = strtolower($text); $slug = preg_replace('/[^a-z0-9]+/', '-', $slug); $slug = trim($slug, '-'); $slug = substr($slug, 0, 128); return $slug ?: 'untitled'; } function ensure_unique_slug(string $base): string { $slug = $base; $i = 1; while (db_one("SELECT slug FROM protocols WHERE slug = ?", [$slug]) || db_one("SELECT slug FROM collections WHERE slug = ?", [$slug])) { $slug = $base . '-' . $i++; } return $slug; }