Config = $config; // Устанавливаю обработчик исключений $this->OnException = $onException; try { // Загружаю параметры подключения // - хост $host = $this->Config->Host; // - порт $port = $this->Config->Port; // - имя базы данных $dbname = $this->Config->Name; // - пользователь $user = $this->Config->UserName; // - пароль $password = $this->Config->Password; // Создаю dsn $dsn = match ($this->Config->Driver) { DBDriver::MySQL => "mysql:host=$host;port=$port;dbname=$dbname", DBDriver::MSSQL => "sqlsrv:Server=$host,$port;Database=$dbname;Encrypt=false;", DBDriver::PostgreSQL => "pgsql:host=$host;port=$port;dbname=$dbname;", DBDriver::OracleDB => "oci:dbname=$host:$port/$dbname", DBDriver::SQLite => "sqlite:$dbname" }; // Задаю DBSign // - Open $this->DBSignOpen = match ($this->Config->Driver) { DBDriver::MySQL, DBDriver::SQLite => '`', DBDriver::MSSQL => '[', DBDriver::PostgreSQL, DBDriver::OracleDB => '"' }; // - Close $this->DBSignClose = match ($this->Config->Driver) { DBDriver::MySQL, DBDriver::SQLite => '`', DBDriver::MSSQL => ']', DBDriver::PostgreSQL, DBDriver::OracleDB => '"' }; // Создаю объект для связи с базой данных $this->DataBaseHandle = new PDO($dsn, username: $user, password: $password); // Устанавливаю уровень ошибок $this->DataBaseHandle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch (PDOException $e) { $onException($e); } } /** * Деструктор. Закрывает соединение с базой данных */ public function __destruct () { $this->DataBaseHandle = null; } /** * Выполняем запрос на получение последней строки * * @param string $query Запрос * @param array $params Параметры запроса * * @return false|array Строка в формате массива или false в случае ошибки * * @see Query * @see QueryFirst * @see QueryScalar */ public function QueryLast (string $query, array $params = []): false|array { // Выполняю запрос $result = $this->Query($query, $params); // Если в результате запроса получили ошибку или количество строк = 0 if ($result === false || count($result) == 0) // - то возвращаем ошибку return false; // Получаю последний ключ массива $lastKey = array_key_last($result); // Возвращаем первую строку return $result[$lastKey]; } /** * Запрос строк из базы данных. * * @param string $query Запрос * @param array $params Параметры запроса * * @return false|array Ассоциированный массив с результатом запроса или false в случае ошибки */ public function Query (string $query, array $params = []): false|array { // По умолчанию, результат пуст $result = false; try { // Подготавливаю запрос $STH = $this->DataBaseHandle->prepare($query, [PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY]); // Выполняю запрос $STH->execute($params); // Указываю, что данные, которые я хочу получить, должны быть в ассоциативном массиве $STH->setFetchMode(PDO::FETCH_ASSOC); // Получаю все данные $result = $STH->fetchAll(); } catch (PDOException $e) { $this->OnException($e); } // Вывожу результат return $result; } /** * Выполнение запроса. Обычно используется для операций, * которые не возвращают никаких данных, кроме количества * затронутых ими записей. Например, * * $FcmsDB->Execute('DELETE FROM table WHERE id=1'); * * @param string $query Запрос * @param array $params Параметры запроса * * @return int|false Количество затронутых строк или false в случае ошибки */ public function Execute (string $query, array $params = []): int|false { // По умолчанию результат false $result = false; try { // Если параметры не заданы if (count($params) == 0) { // - то выполняю запрос $result = $this->DataBaseHandle->exec($query); } else { // - в противном случае // -- подготавливаю запрос $STH = $this->DataBaseHandle->prepare($query, [PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY]); // -- выполняю запрос $opResult = $STH->execute($params); // -- и если выполнение успешное, if ($opResult) // --- то в результат пойдёт количество строк $result = $STH->rowCount(); } } catch (PDOException $e) { $this->HandleException($e); } // Если в результате false if ($result === false) // - то возвращаю его return false; // Возвращаю результат return $result; } private function HandleException (Exception $exception): void { $this->OnException($exception); } /** * Получает набор строк в массиве данных, удовлетворяющий выборке * * @param string $table Имя таблицы * @param array $columns Колонки, которые нужно включить в запрос * @param array $where Параметры выборки * * @return false|array Строка в формате массива или false в случае ошибки * * @see Query * @see QueryFirst * @see QueryLast * @see GetRow */ public function GetRows (string $table, array $columns = [], array $where = []): false|array { // Задаю массив параметров $params = []; // Получаю SQL запрос $sql = $this->PrepareSQLForRowsQuery($table, $columns, $where, $params); // Получаю строки на основании запроса $queryResult = $this->Query($sql, $params); // Если строки не получены if ($queryResult === false) // - то выдаю ошибку return false; // Получаю значение строк return $queryResult; } /** * Генерирует SQL запрос выборки строк. * * @param string $table Имя таблицы * @param array $columns Колонки, которые нужно включить в запрос * @param array $where Параметры выборки * @param array $params Параметры и их значения * * @return string SQL-запрос */ private function PrepareSQLForRowsQuery (string $table, array $columns = [], array $where = [], array &$params = []): string { // Очищаю параметры $params = []; // Строковая интерпретация массива условий $sql_where = $this->PrepareQueryWhere($where, $params); // Колонки $sql_columns = count($columns) > 0 ? implode(', ', $this->PrepareColumn($columns)) : "*"; // Создаю запрос $sql = "SELECT $sql_columns FROM $this->DBSignOpen$table$this->DBSignClose"; // Если заданы where-параметры if (count($where) > 0) { // - то добавляю их $sql .= " WHERE $sql_where"; } // Возвращаю запрос return $sql; } /** * Готовит выражение для WHERE-запроса * * @param array $where Массив условий * @param array $params Очищенные параметры * * @return string Строка WHERE-запроса */ private function PrepareQueryWhere (array $where, array &$params): string { // Очищаю параметры $params = []; // Задаю результат $result = ""; // Если массив условий не пуст if (count($where) > 0) { // - то для каждого условия foreach ($where as $key => $value) { // -- получаю ключ 100%-но без ":" в начале $where_key = $key[0] == ":" ? substr($key, 1) : $key; // -- добавляю префикс для 2 или более итерации $prefix = $result == "" ? "" : " AND "; // -- добавляю данные в $sql_where $result .= $prefix . $this->DBSignOpen . $where_key . $this->DBSignClose . " = :" . $where_key; // -- добавляю данные в параметры $params[$where_key] = "$value"; } } // Вывожу результат return $result; } /** * Подготавливает массив столбцов для использования в базе данных * * @param array $columns Массив колонок. * * @return array Массив преобразованных колонок. */ private function PrepareColumn (array $columns): array { return array_map(function ($item) { // Результирующая строка $result = ""; // Если длинна строки > 0 if (strlen($item) > 0) { // - первый символ $firstLetter = substr($item, 0, 1); // - последний символ $lastLetter = substr($item, -1); // - если первый символ не $this->DBSignOpen if ($firstLetter !== $this->DBSignOpen) // -- то добавляем $result .= $this->DBSignOpen; // - добавляем строку $result .= $item; // - если последний символ не $this->DBSignClose if ($lastLetter !== $this->DBSignClose) // -- то добавляем $result .= $this->DBSignClose; } // Возвращаем результат return $result; }, $columns); } /** * Получает первую строку в массиве данных, удовлетворяющую выборке * * @param string $table Имя таблицы * @param array $columns Колонки, которые нужно включить в запрос * @param array $where Параметры выборки * * @return false|array Строка в формате массива или false в случае ошибки * * @see Query * @see QueryFirst * @see QueryLast * @see QueryScalar * @see GetRows */ public function GetRow (string $table, array $columns = [], array $where = []): false|array { // Задаю массив параметров $params = []; // Получаю SQL запрос $sql = $this->PrepareSQLForRowsQuery($table, $columns, $where, $params); // Получаю строку на основании запроса return $this->QueryScalar($sql, $params); } /** * Выполняем запрос на получение одной строки (аналог QueryFirst) * * @param string $query Запрос * @param array $params Параметры запроса * * @return false|array Строка в формате массива или false в случае ошибки * * @see Query * @see QueryFirst * @see QueryLast * @see GetRow */ public function QueryScalar (string $query, array $params = []): false|array { return $this->QueryFirst($query, $params); } /** * Выполняем запрос на получение первой строки * * @param string $query Запрос * @param array $params Параметры запроса * * @return false|array Строка в формате массива или false в случае ошибки * * @see Query * @see QueryLast * @see QueryScalar */ public function QueryFirst (string $query, array $params = []): false|array { // Выполняю запрос $result = $this->Query($query, $params); // Если в результате запроса получили ошибку или количество строк = 0 if ($result === false || count($result) == 0) // - то возвращаем ошибку return false; // Получаю первый ключ массива $firstKey = array_key_first($result); // Возвращаем первую строку return $result[$firstKey]; } /** * Получает колонку в массиве данных * * @param string $table Имя таблицы * @param string $column Имя колонки * @param array $where Параметры запроса * * @return false|array Ассоциированный массив с результатом запроса или false в случае ошибки * * @see Query */ public function GetCol (string $table, string $column, array $where = []): false|array { // Задаю параметры $params = []; // Строковая интерпретация массива условий $sql_where = $this->PrepareQueryWhere($where, $params); // Создаю запрос $sql = "SELECT $this->DBSignOpen$column$this->DBSignClose FROM $this->DBSignOpen$table$this->DBSignClose"; // Если заданы where-параметры if (count($where) > 0) { // - то добавляю их $sql .= " WHERE $sql_where"; } // Получаю столбец на основании запроса $queryResult = $this->Query($sql, $params); // Если строка не получена или пуста if ($queryResult === false) // - то выдаю ошибку return false; // Создаю результат $result = []; // Для каждого результата запроса foreach ($queryResult as $row) // - передаю его в результат $result[] = $row[$column]; // Вывожу результат return $result; } /** * Получение значение единичного поля * * @param string $table Имя таблицы * @param string $column Требуемый столбец * @param array $where Параметры запроса * * @return mixed|null Результат запроса или null в случае ошибки */ public function GetValue (string $table, string $column, array $where = []): mixed { // Задаю параметры $params = []; // Строковая интерпретация массива условий $sql_where = $this->PrepareQueryWhere($where, $params); // Создаю запрос $sql = "SELECT $this->DBSignOpen$column$this->DBSignClose FROM $this->DBSignOpen$table$this->DBSignClose"; // Если заданы where-параметры if (count($where) > 0) { // - то добавляю их $sql .= " WHERE $sql_where"; } // Получаю строку на основании запроса $queryResult = $this->QueryScalar($sql, $params); // Если строка не получена или пуста if ($queryResult === false || count($queryResult) == 0) // - то выдаю результат null return null; // Получаю значение колонки return $queryResult[$column]; } /** * Вставляет строку в базу данных. * * @param string $table Имя таблицы * @param array $params Параметры * * @return string|false В случае успеха выведет: id созданной записи, -1, если запись создана, но id не получен * (глюк?) и false, если ошибка */ public function Insert (string $table, array $params = []): string|false { // Если массив параметров пуст if (count($params) == 0) // - прерываем с ошибкой return false; // Подготавливаю массив параметров $params = $this->PrepareParamsArray($params); // Получаю ключи параметров $keys = array_keys($params); // Создаю результирующий массив имён ключей параметров $keysReal = []; // Для каждого ключа параметра foreach ($keys as $key) { // - получаю его имя $keyResult = $key[0] == ":" ? substr($key, 1) : $key; // - заключаю в кавычки $keyResult = "$this->DBSignOpen$keyResult$this->DBSignClose"; // - добавляю в результирующий массив ключей $keysReal[] = $keyResult; } // Ключи sql запроса $sql_keys = implode(', ', $keysReal); // Значения sql запроса $sql_values = implode(', ', $keys); // Создаю запрос $sql = "INSERT INTO $this->DBSignOpen$table$this->DBSignClose ($sql_keys) VALUES ($sql_values);"; // Выполняю запрос $count = $this->Execute($sql, $params); // Если результат - false или добавлена не одна запись if (($count === false) || ($count < 1) || ($count > 1)) // - то и общий результат - false return false; $lastId = -1; try { // Получаю последний id этой записи $lastIdResult = $this->DataBaseHandle->lastInsertId(); // Если получение неудачное if ($lastIdResult === false) // - то вывожу -1 return $lastId; // Устанавливаю последний id $lastId = $lastIdResult; } catch (PDOException $e) { $this->HandleException($e); } // Вывожу последний id return $lastId; } /** * Подготавливает массив параметров * * @param array $params Массив параметров * * @return array|false Подготовленный массив параметров или false в случае ошибки */ private function PrepareParamsArray (array $params = []): array|false { // Если массив параметров пуст if (count($params) == 0) // - прерываем с ошибкой return false; // Создаём результирующий массив $result = []; // Для каждого параметра foreach ($params as $key => $value) { // - если ключ параметра начинается с ":" if ($key[0] == ":") // -- то сразу добавляем его в результирующий массив $result[$key] = $value; else // -- в противном случае, предварительно добавим в имя ключа ":" $result[':' . $key] = $value; } // Вывожу результирующий массив return $result; } /** * Заменяет данные в строке базы данных * * @param string $table Имя таблицы * @param array $set Массив данных для замены * @param array $where Массив условий * * @return bool Результат выполнения */ public function Update (string $table, array $set, array $where = []): bool { // Создаю массив параметров $params_set = []; // Строковая интерпретация массива для изменения $sql_set = ""; // Для каждых данных для изменения foreach ($set as $key => $value) { // - получаю ключ 100%-но без ":" в начале $set_key = $key[0] == ":" ? substr($key, 1) : $key; // - добавляю префикс для 2 или более итерации $prefix = $sql_set == "" ? "" : ", "; // - добавляю данные в sql_set $sql_set .= "$prefix$this->DBSignOpen$set_key$this->DBSignClose=:$set_key"; // - добавляю данные в параметры $params_set[":" . $set_key] = $value; } // Обработанные параметры $params_where = []; // Строковая интерпретация массива условий $sql_where = $this->PrepareQueryWhere(where: $where, params: $params_where); // Создаю параметры $params = array_merge($params_set, $params_where); // Создаю запрос $sql = "UPDATE $this->DBSignOpen$table$this->DBSignClose SET $sql_set"; // Если заданы where-параметры if (count($where) > 0) // - то добавляю их $sql .= " WHERE $sql_where"; // Выполняю запрос $count = $this->Execute($sql, $params); // Если результат - false if ($count === false) // - то и общий результат - false return false; // Если изменено 0 строк if ($count === 0) // - то и общий результат - false return false; // Вывожу результат -- успех return true; } /** * Проверяет, существует ли запись в таблице. * * @param string $table Имя таблицы * @param array $where Массив условий * * @return bool Результат проверки */ public function IsExist (string $table, array $where = []): bool { // Вывожу результат return $this->Count($table, $where) > 0; } /** * Подсчитывает количество строк, удовлетворяющих условию. * * @param string $table Имя таблицы * @param array $where Массив условий выборки * * @return int Количество строк или -1, в случае ошибки */ public function Count (string $table, array $where = []): int { // Параметры $params = []; // Строковая интерпретация массива условий $sql_where = $this->PrepareQueryWhere($where, $params); // Создаю запрос $sql = "SELECT COUNT(*) FROM $this->DBSignOpen$table$this->DBSignClose"; // Если заданы where-параметры if (count($where) > 0) { // - то добавляю их $sql .= ' WHERE ' . $sql_where; } // Выполняю запрос $countResult = $this->Query($sql, $params); // Если запрос выполнен с ошибкой if ($countResult === false) // - то в результат идёт -1 return -1; // Получаю секцию $section = match ($config->Driver) { DBDriver::MySQL, DBDriver::SQLite => "COUNT(*)", DBDriver::MSSQL, DBDriver::OracleDB, DBDriver::PostgreSQL => "" }; // Вывожу количество return isset($countResult[0][$section]) ? (int)$countResult[0][$section] : -1; } /** * Удаляет строки по условию. * * @param string $table Имя таблицы * @param array $where Массив условий * * @return bool Результат выполнения */ public function Delete (string $table, array $where = []): bool { // Обработанные параметры $params = []; // Строковая интерпретация массива условий $sql_where = $this->PrepareQueryWhere(where: $where, params: $params); // Создаю запрос $sql = "DELETE FROM $this->DBSignOpen$table$this->DBSignClose"; // Если заданы where-параметры if (count($where) > 0) { // - то добавляю их $sql .= " WHERE $sql_where"; } // Выполняю запрос $count = $this->Execute($sql, $params); // Если результат - false if ($count === false) // - то и общий результат - false return false; // Если изменено 0 строк if ($count === 0) // - то и общий результат - false return false; // Вывожу результат -- успех return true; } }