[Library] ModernPDO

StulE

Новичок
Все мы знаем, насколько неудобно работать с базами данных, вручную писать запросы, следить за безопасностью (sql-инъекции и тд). Мы решили исправить это, создав библиотеку.

Описание:
ModernPDO — это незаменимый инструмент для работы с базами данных. Отличительными особенностями данной библиотеки является простота использования, современный стандарт PHP (8.1), полное покрытие кода тестами, уровень PHPStan'а 9, уровень PSalm'а 1, а также единый стандарт кода.

Доступный функционал:
  • Поддержка CRUD действий.
  • Поддержка сырых/готовых запросов.
  • Поддержка транзакций.
Поддерживаемые базы данных:
  • MySQL/MariaDB
  • PostgreSQL
  • SQLite3
Ссылки:
Репозиторий на GitHub
Страница на Packagist
 
Рекомендую плашки покрытия кода и тестов добавить (github actions для PHP>=8.1 умеет, на более ранних версиях PHP/PHPUnit не смог завести).
 
Ну это же совсем ужасно. https://github.com/StulE-ru/ModernPDO/blob/main/src/Actions/Select.php
У вас куча вещей не экранируется, вы слышали про инъекции, ключевые слова, регистрозависимость, where in, джойны, вложенные запросы и т.д. и т.п.
Будь это 10-15 лет назад - одно дело, но сейчас куча нормальных аналогов в оупенсурсе есть.
 
Ну это же совсем ужасно. https://github.com/StulE-ru/ModernPDO/blob/main/src/Actions/Select.php
У вас куча вещей не экранируется, вы слышали про инъекции, ключевые слова, регистрозависимость, where in, джойны, вложенные запросы и т.д. и т.п.
Будь это 10-15 лет назад - одно дело, но сейчас куча нормальных аналогов в оупенсурсе есть.
Спасибо за комментарий, можно только больше конкретики? Где возникают проблемы из-за отсутствия экранирования, конкретные примеры возможных инъекций, причем тут регистронезависимость, какие ключевые слова и тд? По поводу отсутствия многих фич полностью согласны, они будут добавлены в следующих обновлениях.
 
везде, хоть таблицу назови "table union select bla, bla from table2" хоть столбец
Действительно, если назвать таблицу с пробелами, то будут определенного рода проблемы, но:

Во-первых, экранирование не поможет, пробел - это не спец символ (тык)
Во-вторых, пробелами вы ломаете любую совместимость sql-запроса, так как для MariaDB нужно `table name`, для PosgreSQL "table name" (поиграться можно тут)
В-третьих, пробелы могут появится только в 2х случаях, либо вам захотелось, либо данные пришли от клиента
- Если вам захотелось - это ваши проблемы, всегда можно заменить пробелы на '_' и никаких проблем не будет
- Если данные пришли от клиента - скорее всего у вас проблема в архитектуре, но если так задумано, то обрабатывать придется вам, так как данный случай крайне специфичен для широких масс, и вы по-любому знаете лучше, как обработать ввод пользователя
 
Коллеги пытаются вам объяснить, что конечный пользователь вашего продукта может быть настолько пофигистом, что передаст нефильтрованные данные из _GET, _POST, _COOKIE и т.д. прямиком в методы формирования запроса, что приведет к явной реализации инъекции.
 
Коллеги пытаются вам объяснить, что конечный пользователь вашего продукта может быть настолько пофигистом, что передаст нефильтрованные данные из _GET, _POST, _COOKIE и т.д. прямиком в методы формирования запроса, что приведет к явной реализации инъекции.
Благодарю за разъяснение ситуации.
 
Во-первых, экранирование не поможет, пробел - это не спец символ
экранирование помогает
SQL:
CREATE TABLE `hello``'"\/wo rld` (
  `id` int(11) NOT NULL,
  `name` varchar(25) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `hello``'"\/wo rld` (`id`, `name`) VALUES
(1, 'hello'),
(2, 'world');

ALTER TABLE `hello``'"\/wo rld`
  ADD PRIMARY KEY (`id`);

ALTER TABLE `hello``'"\/wo rld`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;


SELECT * FROM `hello``'"\/wo rld`


Во-вторых, пробелами вы ломаете любую совместимость sql-запроса, так как для MariaDB нужно `table name`, для PosgreSQL "table name"
это не мои проблемы, я пользуюсь API
PHP:
// Select examples
$mpdo->select('table_name')->all();

Будь это 10-15 лет назад - одно дело, но сейчас куча нормальных аналогов в оупенсурсе есть.


В-третьих, пробелы могут появится только в 2х случаях, либо вам захотелось, либо данные пришли от клиента
бывает и ситуации, когда поменять не возможно, это решение других людей
 
Последнее редактирование:
> экранирование помогает
SQL:
SELECT * FROM `hello``'"\/wo rld`

Wiki: Экранирование символов — замена в тексте управляющих символов на соответствующие текстовые подстановки.
Из этого следует, что экранирование не защищает от SQL инъекций и если экранировать строку с пробелами, то пробелы останутся.
Ваш запрос работает за счет расширения SQL со стороны MySQL/MariaDB, которое говорит, что названия можно заключать в ``

> это не мои проблемы, я пользуюсь API
> бывает и ситуации, когда поменять не возможно, это решение других людей

В следующем обновлении добавлю поддержку.
 
Вместо того чтобы в википедию тыкать, лучше бы доку по PDO почитал.
Ну и попробуй написать запрос как говорил, с вложенными запросами, джойнами, группировкой, c оператором IN в который передаёшь массив из пост запроса. У тебя весь код расчитан на простой запрос с обращением к одной таблице.
 
Описание обновления ModernPDO v3.0.0

  • Убраны create методы в ModernPDO и добавлены Drivers
  • Убрана хардкод установка PDO атрибутов в ModernPDO
  • Добавлена поддержка PDO исключений
  • Переработаны билдеры запросов (добавлен класс Factory)
  • Переписана установка кодировки подключения к MySQL/MariaDB
  • Добавлена плашка code coverage
  • Добавлен класс Escaper для экранирования значений
  • Удалены файлы докера
  • Переписаны интеграционные тесты (для упрощения добавления новых тестов)
  • Переименованы методы Select'а all/one в rows/row
  • Удалены методы Select'а firstBy/lastBy
  • Добавлен трейт Limit для Delete/Select/Update
  • Добавлен трейт OrderBy для Delete/Select/Update
  • Добавлена поддержка joins в Select
  • Добавлена возможность создавать/редактировать/удалять таблицы
  • Добавлена поддержка функций(SUM, COUNT и тд) и условий(IN, BETWEEN и тд) Delete/Select/Update/Insert
  • Добавлена поддержка ключей(Primary, Unique, Foreign) в create/alter table

Новая версия тык
 
PHP:
    private function string(string $string): string
    {
        return mb_substr($this->pdo->quote($string), 1, -1);
    }
Боже, всего несколько лет, как мне перестал сниться в ночных кошмарах этот ужас, и я снова вижу его наяву.
Что это? Зачем это? Какой в этом смысл?

Ну и обработка ошибок, конечно, сделана гениально. Нет ошибки - нет проблемы. Воткнул голову в песок, и хищника как бы и нету. Всё хорошо, всё замечательно, работаем дальше.

Уберите, пожалуйста, все блоки try-catch из кода.
После этого можно будет начинать смотреть на это изделие.
 
Последнее редактирование:
  • Добавлена поддержка PDO исключений
Эх, жалко я только сейчас это увидел. Не стал бы тогда удалять весь яд из предыдущего комментария.
Вы серьёзно считаете, что вот эти try-catch, тупо подавляющие сообщения об ошибках, называются "поддержкой исключений"? И это типа достижение, которым надо гордиться?

Ещё из принципиальных замечаний.

К PDO эта программа не имеет отношения. То что вы сделали - называется Query Builder (и их реально готовых как грязи за баней).

В PDO я могу получить конечный результат в самых разных форматах, например с группировкой по полю, или в виде словаря ключ-значение, или в виде инстанса определенного объекта (хотя эта фича довольно сомнительная).
Но почему-то все самозваные "улучшатели опыта с ПДО" на самом деле ухудшают опыт работы с этой либой, обрезая существующую функциональность до собственных представлений о ней.
 
Последнее редактирование:
Эх, жалко я только сейчас это увидел. Не стал бы тогда удалять весь яд из предыдущего комментария.
Вы серьёзно считаете, что вот эти try-catch тупо подавляющие все сообщения об ошибках называются "поддержкой исключений" и это типа достижение, которым надо гордиться?

Ещё из принципиальных замечаний.

К PDO эта программа не имеет отношения. То что вы сделали - называется Query Builder (и их реально готовых как грязи за баней).

В PDO я могу получить конечный результат в самых разных форматах, например с группировкой по полю, или в виде словаря ключ-значение, или в виде инстанса определенного объекта (хотя эта фича довольно сомнительная).
Но почему-то все самозваные "улучшатели опыта с ПДО" на самом деле ухудшают опыт работы с этой либой, обрезая существующую функциональность до собственных представлений о ней.

> Вы серьёзно считаете, что вот эти try-catch
Не считаю, но так вышло что это тянется с первых версий, где я ставил атрибут на ворнинги, и дабы не ломать идею "либа не кидает исключения", решил обернуть все в try-catch, приму к сведению ваше замечание.

> К PDO эта программа не имеет отношения.
Не совсем, все же частично имеет - тот же query, statement. Другой вопрос что я не хочу сильно привязываться к pdo, чтобы в будущем можно было добавить поддержку бд, для которых нет pdo драйвера. Эту папку я не просто так добавил.
 
Вот поэтому и надо разделить этот класс на два, враппер для ПДО и квери билдер. При этом называть первый ModernPDO будет глупо, поскольку там очень мелкие доделки, а второе - и вовсе некорректно.

И ради бога, уберите этот чудовищный код, процитированный в комментарии выше.
 
Вот поэтому и надо разделить этот класс на два, враппер для ПДО и квери билдер. При этом называть первый ModernPDO будет глупо, поскольку там очень мелкие доделки, а второе - и вовсе некорректно.

И ради бога, уберите этот чудовищный код, процитированный в комментарии выше.

> Вот поэтому и надо разделить этот класс на два, враппер для ПДО и квери билдер.
В будущем и планирую.

> При этом называть первый ModernPDO будет глупо.
Понятное дело, но так исторически сложилось.

> И ради бога, уберите этот чудовищный код, процитированный в комментарии выше.
Мне самому это не понравилось, но как лучше - я не знаю. Потому что вручную - плохо, есть pdo::quote(). А через него добавляются скобки по бокам, и все равно приходится вручную подгонять.
 
Попробуйте все-таки попробовать понять, что делает PDO::quote(), и в результате сообразить, нужна ли она здесь вообще.
Ну или хотя бы попробуйте сформулировать, что именно вы собираетесь без неё делать "вручную" и зачем.
 
Последнее редактирование:
Попробуйте все-таки попробовать понять, что делает PDO::quote(), и в результате сообразить, нужна ли она здесь вообще.
Ну или хотя бы попробуйте сформулировать, что именно вы собираетесь без неё делать "вручную" и зачем.
Задача Escaper'ов - обработать строки, которые нельзя "приготовить" (это названия таблиц, столбцов и тд), потому что, как писали выше, без этой обработки возникают проблемы при передаче строк от пользователя и при передаче строки содержащей пробелы. Escaper'ам нужно экранировать спец. символы (чтобы избежать инъекций), а так же заключать строки содержащие пробелы в кавычки, которые специфичны для разных субд. Вот и выходит что это все надо делать вручную. quote() не особо подходит для этой задачи, так как заключает не в те кавычки и не экранирует нужные кавычки (которые подставляются вручную позже), что есть потенциальная инъекция, но при этом она экранирует спец. символы. В итоге - что использовать quote() неправильно, что не использовать.
 
возникают проблемы
Какие проблемы?
нужно экранировать спец. символы
Какие конкретно символы?
но при этом она экранирует спец. символы.
какие и зачем?

Не пробовали задуматься?

Ваша проблема в том, что вы все это воспринимаете абстрактно. "ну есть какие-то инъекции, которые происходят через какие-то символы, которые экранирует какая-то функция".
При этом вы даже близко не понимаете, что такое инъекция. И для чего нужно это самое экранирование.
А туда же - писать гениальные врапперы для ПДО.

Инъекции получаются не из-за "спецсимволов". А путем выхода за границы грамматического литерала. Если у литерала нет ограничителей, как у числа например, или незакавыченного идентификатора, то вообще лафа - заходи кто хочешь, бери что хочешь.
Если ограничители есть, как у классического мальчика Бобби Тэйблс, но при этом эти ограничители не экранируются, то достаточно всего лишь закрыть литерал самому, и снова гулять свободно.

Отсюда становится понятно назначение экранирования. А оно примитивно до дебилизма: функция экранирования экранирует ограничители литерала. Те самые "не те кавычки". Не какие-то мифические "спецсимволы", а всего лишь те самые кавычки, которые служат границами строки. Тупо чтобы не перепутать с этой самой границей. Ну и заодно и сам экранирующий символ. Вы же сами это и делаете для идентификаторов.
Из чего следует вывод, что отрывать кавычки у quote() - это высшая степень дебилизма. Без кавычек она попросту не имеет смысла, ни малейшего. Вся суть этой функции в том, что она добавляет кавычки, в отличие от дебильной mysql_escape_string.
Еще там правда экранируется нулевой байт, но мне кажется, что современный софт уже не ломается через него. В крайнем случае можно руками.

В итоге всё это означает, что применять quote() для чего-то кроме строк - чуть более, чем бессмысленно. А поскольку строки у вас и так передаются через подготовленные выражения, то эта функция вообще не нужна.
 
Последнее редактирование:
Сверху