From 13c2f7fb4bdf00325065f3b763f5552e0b2ac95c Mon Sep 17 00:00:00 2001 From: Sky Johnson Date: Thu, 11 Jul 2024 17:21:33 -0500 Subject: [PATCH] Slim and move database, create app container, request obj --- server/app/app.php | 17 ++ server/app/database.php | 91 ++++++++++ server/app/request.php | 43 +++++ server/database.php | 374 ---------------------------------------- 4 files changed, 151 insertions(+), 374 deletions(-) create mode 100644 server/app/app.php create mode 100644 server/app/database.php create mode 100644 server/app/request.php delete mode 100644 server/database.php diff --git a/server/app/app.php b/server/app/app.php new file mode 100644 index 0000000..74f73eb --- /dev/null +++ b/server/app/app.php @@ -0,0 +1,17 @@ + PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + ]; + + try { + $this->c = new PDO("sqlite:$path", null, null, $opts); + $this->c->exec('PRAGMA foreign_keys = ON;'); // Enable foreign keys + $this->c->exec('PRAGMA journal_mode = WAL;'); // Enable WAL + } catch (PDOException $e) { + $this->error = "Failed to open database: " . $e->getMessage(); + throw $e; + } + } + + public function do(string $query, array $params = []): PDOStatement|false + { + $start = microtime(true); + + try { + $stmt = $this->c->prepare($query); + $stmt->execute($params); + $this->record($query, $start); + return $stmt; + } catch (PDOException $e) { + $this->error = $e->getMessage(); + return false; + } + } + + public function q(string $query): PDOStatement|false + { + $start = microtime(true); + + try { + $stmt = $this->c->query($query); + $this->record($query, $start); + return $stmt; + } catch (PDOException $e) { + $this->error = $e->getMessage(); + return false; + } + } + + private function record(string $query, float $start): void + { + $time = microtime(true) - $start; + $this->queries++; + $this->log[] = [$query, $time]; + $this->time += $time; + } + + public function error(): string + { + return $this->error; + } + + public function queries(): int + { + return $this->queries; + } + + public function log(): array + { + return $this->log; + } + + public function time(): float + { + return $this->time; + } +} diff --git a/server/app/request.php b/server/app/request.php new file mode 100644 index 0000000..37b3230 --- /dev/null +++ b/server/app/request.php @@ -0,0 +1,43 @@ +uri = explode('/', $uri); + array_shift($this->uri); + + // Get the request method + $this->method = $_SERVER['REQUEST_METHOD']; + + // Get all headers + $this->headers = $this->getAllHeaders(); + } + + public function uri(int $index): string|false + { + if ($index == 0 && empty($this->uri[0])) return '/'; + return $this->uri[$index] ?? false; + } + + private function getAllHeaders(): array + { + $headers = []; + foreach ($_SERVER as $k => $v) { + if (substr($k, 0, 5) == 'HTTP_') { + $headers[str_replace(' ', '-', ucwords(str_replace('_', ' ', strtolower(substr($k, 5)))))] = $v; + } + } + return $headers; + } +} diff --git a/server/database.php b/server/database.php deleted file mode 100644 index eb53052..0000000 --- a/server/database.php +++ /dev/null @@ -1,374 +0,0 @@ - [], - 'order' => [], - 'limit' => 0, - 'values' => [], - ]; - - /** - * Open a connection to the database. - */ - public function __construct(string $path, array $opts = []) - { - $opts = !empty($opts) ? $opts : [ - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - ]; - - try { - $this->c = new PDO("sqlite:$path", null, null, $opts); - $this->c->exec('PRAGMA foreign_keys = ON;'); // Enable foreign keys - $this->c->exec('PRAGMA journal_mode = WAL;'); // Enable WAL - } catch (PDOException $e) { - $this->error = "Failed to open database: " . $e->getMessage(); - throw $e; - } - } - - /** - * Change the current working table. - */ - public function table(string $name): Database - { - $this->table = $name; - return $this; - } - - /** - * Create a table with the name of the current working table. Will drop - * the table if it already exists. - */ - public function create(array $columns): PDOStatement|false - { - $query = "CREATE TABLE IF NOT EXISTS $this->table (" . implode(', ', $columns) . ');'; - $this->addToLog($query); - return $this->c->query($query); - } - - /** - * Drop the current working table if it exists. - */ - public function drop(): PDOStatement|false - { - $query = "DROP TABLE IF EXISTS $this->table;"; - $this->addToLog($query); - return $this->c->query($query); - } - - /** - * Return the current working table's schema. - */ - public function schema(): array - { - $query = "PRAGMA table_info($this->table);"; - $this->addToLog($query); - return $this->c->query($query)->fetchAll(PDO::FETCH_ASSOC); - } - - /** - * Insert data into the current working table. Pass an array of key/value - * pairs to insert one record, or an array of arrays to insert multiple. Returns - * the number of rows inserted, or false on failure. - */ - public function insert(array $data): int|false - { - // If the first element is not an array we can assume we're doing a single insert. - if (!isset($data[0])) { - $query = "INSERT INTO $this->table (" . implode(', ', array_keys($data)) . ')' - . ' VALUES (' . implode(', ', array_fill(0, count($data), '?')) . ');'; - $this->addToLog($query); - return $this->c->prepare($query)->execute(array_values($data)); - } - - // Otherwise we will build a multi-insert query. - $query = "INSERT INTO $this->table (" . implode(', ', array_keys($data[0])) . ') VALUES '; - $placeholders = []; - $values = []; - - foreach ($data as $row) { - $placeholders[] = '(' . implode(', ', array_fill(0, count($row), '?')) . ')'; - foreach ($row as $value) $values[] = $value; - } - - $query .= implode(', ', $placeholders) . ';'; - - $this->addToLog($query); - return $this->c->prepare($query)->execute($values); - } - - /** - * Insert a default record into the current working table. - */ - public function insertDefaultValues(): int|false - { - $query = "INSERT INTO $this->table DEFAULT VALUES;"; - $this->addToLog($query); - return $this->c->prepare($query)->execute(); - } - - /** - * Add a where clause to the builder. All values are paramaterized. - */ - public function where(string $column, string $operator, mixed $value): Database - { - $this->builder['where'][] = "$column $operator ?"; - $this->builder['values'][] = $value; - return $this; - } - - /** - * Add an order clause to the builder. - */ - public function order(string $column, string $direction = 'ASC'): Database - { - $this->builder['order'] = "$column $direction"; - return $this; - } - - /** - * Set a limit in the builder. - */ - public function limit(int $limit): Database - { - $this->builder['limit'] = $limit; - return $this; - } - - /** - * Build a select query and return the result. By default selects the entire - * row. Optionally use fetchAll, but there is also the selectAll() alias method - * for this. - */ - public function select(string $what = '*', bool $fetchAll = false): array|false - { - $query = "SELECT $what FROM $this->table"; - if (!empty($this->builder['where'])) $query .= " WHERE " . implode(' AND ', $this->builder['where']); - if (!empty($this->builder['order'])) $query .= " ORDER BY " . $this->builder['order']; - if (!empty($this->builder['limit'])) $query .= " LIMIT " . $this->builder['limit']; - - $this->addToLog($query); - - try { - $stmt = $this->c->prepare($query); - $stmt->execute($this->builder['values']); - } catch (PDOException $e) { - $this->error = "Failed to execute query: " . $e->getMessage(); - return false; - } - - $this->resetBuilder(); - return $fetchAll ? $stmt->fetchAll() : $stmt->fetch(); - } - - /** - * Delete records from the current working table. Returns the number of rows - * deleted, or false on failure. Uses where clauses if set. - */ - public function delete(): int|false - { - $query = "DELETE FROM $this->table"; - if (!empty($this->builder['where'])) $query .= " WHERE " . implode(' AND ', $this->builder['where']); - $this->addToLog($query); - - $this->resetBuilder(); - return $this->c->exec($query); - } - - /** - * Update records in the current working table. Returns the number of rows - * updated, or false on failure. Uses where clauses if set. - */ - public function update(array $data): int|false - { - $query = "UPDATE $this->table SET " . implode(', ', array_keys($data)) . ' = ?'; - if (!empty($this->builder['where'])) $query .= " WHERE " . implode(' AND ', $this->builder['where']); - $this->addToLog($query); - - $values = array_merge(array_values($data), $this->builder['values']); - - $this->resetBuilder(); - return $this->c->prepare($query)->execute($values); - } - - /** - * Get the number of rows in the current working table. Can use where clauses. - */ - public function count(): int|false - { - return $this->select('COUNT(*)', true)[0]['COUNT(*)']; - } - - /** - * Alias for $db->select(true) - */ - public function selectAll(string $what = '*'): array|false - { - return $this->select($what, true); - } - - /** - * Return whether the given table exists. - */ - public function tableExists(string $name): bool - { - $query = "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='$name';"; - $this->addToLog($query); - return $this->c->query($query)->fetchColumn() > 0; - } - - /** - * Return the PDO instance; for when more complex operations are needed. - */ - public function c(): PDO - { - return $this->c; - } - - /** - * Alias for PDO::prepare - */ - public function prepare(string $query): PDOStatement - { - return $this->c->prepare($query); - } - - /** - * Alias for PDO::lastInsertId - */ - public function lastInsertId(): int - { - return $this->c->lastInsertId(); - } - - /** - * Alias for PDO::exec - */ - public function exec(string $query): int - { - $this->addToLog($query); - return $this->c->exec($query); - } - - /** - * Alias for PDO::beginTransaction - */ - public function begin(): bool - { - return $this->c->beginTransaction(); - } - - /** - * Alias for PDO::commit - */ - public function commit(): bool - { - return $this->c->commit(); - } - - /** - * Alias for PDO::rollBack - */ - public function rollBack(): bool - { - return $this->c->rollBack(); - } - - /** - * Alias for PDO::query - */ - public function query(string $query): PDOStatement|false - { - $this->addToLog($query); - return $this->c->query($query); - } - - /** - * Add the query to the log and increment the number of queries executed. - */ - private function addToLog(string $query): void - { - $this->log[] = $query; - $this->queries++; - } - - /** - * Reset the builder to it's default structure. - */ - public function resetBuilder(): void - { - $this->builder = [ - 'where' => [], - 'order' => [], - 'limit' => 0, - 'values' => [], - ]; - } - - /** - * Return the log of executed queries. - */ - public function log(): array - { - return $this->log; - } - - /** - * Return the number of queries executed. - */ - public function queries(): int - { - return $this->queries; - } - - /** - * Return the last error message. - */ - public function error(): string - { - return $this->error; - } -} \ No newline at end of file