$value) // - если ключ начинается с ":" if ($key[0] == ":") // - то сразу добавляем его в результирующий массив $result[$key] = $value; else // - в противном случае, предварительно добавим в имя ключа ":" $result[':' . $key] = $value; // Возвращаем результат return $result; } /** * Находит атрибут в массиве. * * @param array $attrs Массив атрибутов. * @param string $className Имя класса. * * @return object|null Объект атрибута или null */ private static function FindAttribute (array $attrs, string $className): ?object { return array_find($attrs, fn ($attr) => $attr->getName() === $className); } /** * Проверяет, является ли свойство публичным. * * @param object $obj Объект. * @param string $propertyName Имя свойства. * * @return bool Возвращает true, если свойство публичное, иначе false. */ private static function IsPublicProperty (object $obj, string $propertyName): bool { return property_exists($obj, $propertyName) && new ReflectionProperty($obj, $propertyName)->isPublic(); } /** * Подготавливает массив параметров * * @param IDBItem $source Объект со свойствами. * @param DBOperation $operation Текущая операция. * * @return array|false Подготовленный массив параметров или false в случае ошибки */ private function PrepareParamsArray (IDBItem $source, DBOperation $operation): array|false { $result = []; // Получаю массив свойств $properties = get_class_vars(get_class($source)); // Для каждого свойства foreach ($properties as $key => $value) { // - пропускаю не публичные свойства if (!self::IsPublicProperty($source, $key)) continue; // Получаю рефлексию // - для класса $reflectedClass = new ReflectionClass(get_class($source)); try { // - для свойства $reflectionProperty = $reflectedClass->getProperty($key); } catch (ReflectionException $e) { // - если ошибка, то вывожу и выходим $this->HandleException($e); } // - получаю атрибуты $attributes = $reflectionProperty->getAttributes(); /** * Фильтруем поля, игнорируемые для данной операции * * @var IgnoredInDB|null $ignoreAttr Атрибут игнорирования. */ $ignoreAttr = self::FindAttribute($attributes, IgnoredInDB::class); // - если поле игнорируется if ($ignoreAttr !== null) { // -- то проверяю, игнорируется ли данное поле $isIgnore = $ignoreAttr->IgnoredOperations->IsExist(fn (DBOperation $oper) => $oper == $operation); // -- если игнорируется if ($isIgnore) // --- то пропускаю continue; } /** * Получаю значение имени поля * * @var FieldName|null $fieldNameAttr Атрибут имени поля. */ $fieldNameAttr = self::FindAttribute($attributes, FieldName::class); // Если есть атрибут имени поля, то беру его имя, иначе беру имя свойства $fieldName = $fieldNameAttr !== null ? $fieldNameAttr->FieldName : $key; /** * Преобразование типа. * * @var ConvertToDB|null $convertAttr Атрибут конвертации. */ $convertAttr = self::FindAttribute($attributes, ConvertToDB::class); // Если есть атрибут конвертации if ($convertAttr) { // - то получаю значение функции конвертации $converter = $convertAttr->ConvertToDB; // - выполняю функцию конвертации $value = call_user_func($converter, $value); } // Добавляю в массив $result[$fieldName] = $value; } // Возвращаю результат return $result; } /** * Обрабатывает исключение. * * @param Exception $exception Исключение. * * @return void */ private function HandleException (Exception $exception): void { // Выбираю обработчик исключений $onException = $this->OnException ?? fn (Exception $e) => die($e->getMessage()); // Выполняю обработчик исключений $onException($exception); } /** * Подготавливает массив столбцов для использования в базе данных * * @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); } /** * Генерирует 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; } }