setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, [PDOStatement::class, []]); } function prepare(string $sql, array $options = []): PDOStatement|false { /** @var PDOStatement $result */ $result = parent::prepare($sql, $options); return $result; } /** * Возвращает DSN * @return DSN */ public function getDSN() { return $this->dsn; } /** * Возвращает true, если база данных Postgres * @return bool */ public function isPostgres() { return ($this->dsn["phptype"] == "pgsql"); } /** * Создает соединение с базой данных * @param array $dsn - DSN * @return Database|null */ static function getConnection(array $dsn) { /** @var ?Database */ $connection = null; if ($dsn['phptype'] == 'pgsql' || $dsn['phptype'] == 'mysql') { $port = (isset($dsn['port'])) ? "port={$dsn['port']};" : ""; $connection = new self("{$dsn['phptype']}:host={$dsn['hostspec']}; $port dbname={$dsn['database']}", $dsn['username'], $dsn['password']); if ($dsn['phptype'] == 'pgsql') { $connection->query('SET client_encoding="UTF-8"'); } if (isset($dsn['schema'])) { $connection->query('SET search_path TO ' . $dsn['schema']); } } elseif ($dsn['phptype'] == 'sqlite::memory') { $connection = new self("{$dsn['phptype']}:"); $connection->sqliteCreateFunction('LOWER', 'sqliteLower', 1); } elseif ($dsn['phptype'] == 'sqlite') { $connection = new self("{$dsn['phptype']}:{$dsn['database']}"); $connection->setAttribute(PDO::ATTR_TIMEOUT, 5); $mode = defined('SQLITE_JOURNAL_MODE') ? \SQLITE_JOURNAL_MODE : 'WAL'; $connection->query("PRAGMA journal_mode=$mode"); $connection->sqliteCreateFunction('LOWER', 'sqliteLower', 1); } $connection->dsn = $dsn; return $connection; } /** * Выполняет запрос к базе данных * @param string $query - запрос * @param ?array $values - значения */ public function executeQuery($query, $values = null): PDOStatement|bool { $stmt = $this->prepare($query); $stmt->execute($values); $stmt->cache = $stmt->fetchAll(PDO::FETCH_ASSOC); return $stmt; } /** * Создает подготовленный запрос * @param string $query - запрос * @return Statement */ public function prepareStatement($query) { return new Statement($query, $this); } /** * Извлекает из базы все элементы по запросу (Для совместимости со старым представлением баз данных CIS) * @param string $query - запрос * @param ?array $values - значения * @return list> */ public function fetchAllArray($query, $values = null) { $sth = $this->prepare($query); $prep = $this->prepareValues($values); $sth->execute($prep); return $sth->fetchAll(PDO::FETCH_ASSOC); } /** * Извлекает из базы первый элемент по запросу * @param string $query - запрос * @param ?array $values - значения * @return array|false */ public function fetchOneArray($query, $values = null) { $sth = $this->prepare($query); $prep = $this->prepareValues($values); $sth->execute($prep); return $sth->fetch(PDO::FETCH_ASSOC); } /** * Преобразует значения в подготовленные значения * @param array $values - значения * @return ?array */ private function prepareValues($values) { if (!$values) { return null; } $pg = $this->isPostgres(); $prep = []; foreach ($values as $key => $value) { $result = null; if (is_bool($value)) { if ($pg) { $result = $value ? 'true' : 'false'; } else { $result = $value ? 1 : 0; } } else { $result = $value; } $prep[":" . $key] = $result; } return $prep; } /** * Создает INSERT запрос * @param string $table - таблица * @param array $values - значения * @param bool $return_id - возвращать id * @param string $index - индекс * @return int|mixed */ function insertQuery($table, array $values, $return_id = false, $index = null) { $prep = $this->prepareValues($values); $sql = "INSERT INTO $table (" . implode(",", array_keys($values)) . ") VALUES (" . implode(",", array_keys($prep)) . ")"; if ($return_id) { if ($this->isPostgres()) { $sql .= " RETURNING $index"; } } $stmt = $this->prepare($sql); $stmt->setFetchMode(PDO::FETCH_ASSOC); $stmt->execute($prep); $result = $stmt->fetch(); if ($return_id) { if ($this->isPostgres()) { return $result[$index]; } else { $result = $this->fetchOneArray("SELECT $index AS lastid FROM $table WHERE OID = last_insert_rowid()"); return $result['lastid']; } } } /** * Создает UPDATE запрос * @param string $table - таблица * @param array $values - значения * @param string $cond - условие */ function updateQuery($table, array $values, $cond): void { $prep = $this->prepareValues($values); $sql = "UPDATE $table SET " . implode( ",", array_map(function ($k, $v) { return $k . "=" . $v; }, array_keys($values), array_keys($prep)) ) . " WHERE $cond"; $stmt = $this->prepare($sql); $stmt->setFetchMode(PDO::FETCH_ASSOC); $stmt->execute($prep); } /** * Создает генератор идентификаторов * @return IdGenerator */ function getIdGenerator() { return new IdGenerator($this); } /** * Замечание: Только для Postgres SQL * @param string $seq Имя последовательности для ключа таблицы * @return int Идентефикатор следующей записи */ function getNextId($seq) { $result = $this->fetchOneArray("SELECT nextval('$seq')"); return $result['nextval']; } /** * Закрывает соединение с базой данных */ function close(): void { } } }