DBX: попытка избавиться от составления MySQL запросов

Давным давно, когда я только начинал изучать PHP и тонкости составления запросов MySQL (2011 год) у меня возникла мысль написать обертку для MySQLi наподобие Doctrine для упрощения синтаксиса обращения к базе данных. На дворе уже 2019 и решил поделиться своим велосипедом на тему ORM.

И так, это DBX — движок базы данных MySQL для PHP на основе библиотеки MySQLi основанный на запросах вида структурного описания таблиц и полей базы данных в виде обычного массива с функцией статического кэширования запросов и автообновления хэша статики.

Вопреки всеобщей любви к PDO, была выбрана именно легковесная MySQLi функциональность, которая мягче и проще программируется и более лаконично вписывается в предложенное API DBX. Я даже не использовал composer, потому что у меня нет ни одного проекта, где я использую сторонние зависимости кроме самописных.

Собственно, рассмотрим пример объявления структуры простейшей тестовой базы данных и ее таблиц. Для начала подключим саму библиотеку с классом и отправим данные авторизации с хостом базы данных:

require_once './DBX.php';
$dbx_data  = ['localhost', 'root', 'root', 'dbx_test', '8889'];

Теперь представим, что нам нужно описать и создать таблицу в БД используя синтаксис обычного массива PHP:

<?php
/**
  * CREATE TABLE EXAMPLE
  */
$table_1 = 'example'; // table name
$query_1 = 'c'; // create table sql
$fields_1 = [
    'field_id' => [
        'type'   => 'num', // int
        'auto'   => true,  // auto increment
        'length' => 255,
        'fill'   => true   // not null
    ],
    'field_text' => [
        'type'   => 'text', // varchar
        'length' => 255,
        'fill'   => true
    ],
    'field_date' => [
        'type'   => 'time', // TIMESTAMP
        'value'  => date('Y-m-d')
    ]
];

?>

Здесь синтаксис простой. Я не использовал строгую типизацию и выбрал просто значения TEXT, NUMBER и TYMESTAMP. Конечно же, для более «рульной» структуры таблицы используется ключевое поле ID с автоинкрементом (не встречал случаев в реальных проектах, когда задание автоинкремента и ключа мешает проектированию запросов и связей таблиц). Для указания может ли поле быть пустым указывается параметр fill => true.

Типы запросов DataBaseX очень просты и содержат основные методы вроде select, insert, delete, update, drop, truncate и так далее.

К примеру запрос для создания таблицы:

// perform queries
$dbx::query("c", $table_1, $fields_1);

Или запрос добавления данных и структуры столбцов может выглядеть вот так:

// fields values for table_1 example
$fields_2 = [
    'field_id' => [
        'value' => 456
    ],
    'field_text' => [
        'value' => 'I have to add into my table'
    ],
    'field_date' => [
        'value'  => date('Y-m-d')
    ]
];
// perform queries
$dbx::query('i', $table_1, $fields_2);

Структура массива позволяет однажды описать базу данных и в последующем лишь манипулировать полями value для изменения данных, которые используются для формирования запросов.

Вот пример update инструкции, которую я упраздню в следующем примере:

// fields values for table_1 example
$fields_3 = [
    'field_id' => [
        'value' => 456
    ],
    'field_text' => [
        
        'new_value' => 'I was updated',
        'criterion_field' => 'field_id',
        'criterion_value' => 456
    ],
    'field_date' => [
        'value'  => date('Y-m-d')
    ]
];
// perform queries
$dbx::query('u', $table_1, $fields_3);


Мне показалось, что использование полей criterion_field и criterion_value усложняет систему, поэтому я создал запрос, который автоматически либо добавляет данные в БД, если они еще не были созданы, либо обновляет существующие данные. Этот запрос я назвал INJECT QUERY и вызывается он префиксом «in»:


// fields values for table_1 example
$fields_2 = [
    'field_id' => [
        'value' => 0
    ],
    'field_text' => [
        'value' => 'Yo if field_id = 0 it\'s an insert or if id exists it\'s an update'
    ],
    'field_date' => [
        'value'  => date('Y-m-d')
    ]
];
// perform queries
$dbx::query('in', $table_1, $fields_2);


Работает это на низком уровне так:

INSERT INTO `revolver__comments` (`field_id`, `field_content`) 
VALUES ('5', 'TEST UPDATE') 
ON DUPLICATE KEY UPDATE `field_id`='5', `field_content`='TEST UPDATE';

Все остальные запросы простейшие и описывать их не имеет смысла(смотрите примеры в файле тестов index.php), но я не могу не показать как работает запрос SELECT:

<?php 
// perform queries
$dbx::query('s|field_id|asc|100|0', $table_1, $fields_1);
?>

<?php
    // print structure
    print '<h2>DBX STRUCTURE</h2>';
    print '<pre><code>';
    print_r( $fields_1 );
    print '</code></pre>'; 
    // print result
    print '<h2>DBX QUERY RESULT</h2>';
    print '<pre><code>';
    print_r( $dbx::$result );
    print '</code><pre><hr />';
?>


Здесь с использованием префикса «s» можно сгруппировать параметры запроса s|field_id(order)|asc(direction)|100(limit)|0(offset).

Кроме прочего DBX обладает такой отличительностью, как встроенный файловый кэш на базе JSON статики. Каждый запрос изменения (INSERT, UPDATE, DELETE, TRUNCATE) вызывает пресчёт хэша в таблице хэшей кэша и автоматически обновляет статический кэш, что позволяет не думать о нагрузке на запросы SELECT.

В будущем я планирую до развить движок DBX и расширить возможности конструктора SQL запросов системой конфигурации UNION и JOIN, а также добавить новую из MySQL 8 поддержку базы данных на типе JSON структуры.

Сейчас DBX используется в моей системе управления контентом RevolveR и показывает неплохие характеристики (весь сайт при условии наличия обновленного кэша инициализируется одним запросом к БД и потребляет около 0.7 Mb оперативной памяти интерпретатора). А также мне очень удобно видеть всю структуру БД в одном отдельном файле, что в разы ускоряет наращивание и проектирование новых модулей.

Репозиторий проекта: DBX v1.1.1 на GitHub.
Поделиться публикацией

Комментарии 82

    +4
    Глянул код:
    1) Ни одного исключения \ нулевая обработка ошибок \ нулевая информативность об ошибках
    2) Код на 80% построен на `case` и хадукен if'ах
    3) информативнейшие комментарии
    // connect database
    public static function connect() {

    4) Поддерживать этот код сможете только вы (на самом деле любой, но никто в здравом уме это делать не будет)
    5) ООП? не, не слышали.
    6) Зачем аннотации, если писать в блокноте?

    Подитог: для собственного велосипеда и для хобби-программирования — ок, но зачем это тут?
    Итог: Для профессиональной разработки лучше использовать нормальную ORM и отказаться от принципа:
    Я даже не использовал composer, потому что у меня нет ни одного проекта, где я использую сторонние зависимости кроме самописных.
      –7
      $dbx::$result['status'] выведет все исключения, а также ошибки подключения и прочее.

      $dbx::$result['status']['sql_query_debug'] покажет построенный запрос MySQL.

      Код не задумывался, чтобы его поддерживал еще кто-то. Его, во-первых, не так много, во-вторых, ошибок в нем нет. JOIN, UNION и delete без WHERE statemant я реализую самостоятельно как и обещал.

      Нужен composer? Сделаю вам отдельную версию.

      Чем вам case не нравится? Можно было array keys использовать, но я решил на if написать.
        +8
        Код не задумывался, чтобы его поддерживал еще кто-то.

        Зачем он тогда на хабре? Это противоречит Вашей задумке
          –6
          Какой задумке?
          +2
          во-вторых, ошибок в нем нет

          Что вы готовы на это поставить?
            –5
            Я вам минет отправлю через прослушку, если найдете серьезные баги и ошибки. Я не Билл, чтобы деньгами платить.
              0
              Так всё таки у вас там могут быть ошибки?
                –2
                Нет ошибок.
                  0
                  $table_1 = 'create'; // table name
                  $query_1 = 'c'; // create table sql
                  
                  $fields_1 = [
                      'field_id' => [
                          'type'   => 'num', // int
                          'auto'   => true,  // auto increment
                          'length' => 255,
                          'fill'   => true   // not null
                      ],
                      'field_text' => [
                          'type'   => 'text', // varchar
                          'length' => 255,
                          'fill'   => true
                      ],
                      'field_date' => [
                          'type'   => 'time', // TIMESTAMP
                          'value'  => date('Y-m-d')
                      ]
                  ];
                  
                  // perform queries
                  $dbx::query($query_1, $table_1, $fields_1);
                  

                  DBX QUERY RESULT
                  Array
                  (
                      [status] => Array
                          (
                              [CONNECTION] => 1
                              [DEBUG] =>  [#0] [%0]
                              [SERVER] => 5.7.25-0ubuntu0.18.04.2
                              [STATUS] => Uptime: 6434  Threads: 1  Questions: 179  Slow queries: 0  Opens: 133  Flush tables: 1  Open tables: 126  Queries per second avg: 0.027
                              [sql_query_debug] => CREATE TABLE IF NOT EXISTS create(`field_id` INT(255) AUTO_INCREMENT PRIMARY KEY NOT NULL, `field_text` VARCHAR(255)  NOT NULL, `field_date` TIMESTAMP);
                          )
                  
                  )
                  

                  есть
                    –3
                    И чо? Кто так таблицы именует? У меня как в БЕМ принято.
                      +3
                      пох вообще, mysql позволяет создать таблицу с таким именем, а ваша поделка выдаст ошибку
                    0
                    Деньги платят те, кто считает, что ошибки могут быть. Платят за работу по поиску. Если у вас нет ошибок, почему бы не поставить на это что-то существенное и получить профит?
          +2
          велосипеды это круто, если они едут вперёд, но в этом случае, я думаю лучше будет написать SQL, ибо он будет понятен и будет служить как само-документация.
          $fields_2 = [
          'field_id' => [
          'value' => 0
          ],
          'field_text' => [
          'value' => 'Yo if field_id = 0 it\'s an insert or if id exists it\'s an update'
          ],
          'field_date' => [
          'value' => date('Y-m-d')
          ]
          ];

          Смысл ORM убрать частичную необходимость написания SQL, но при этом оставить понимание, что происходит в коде… у вас этого не дано.
            –4
            вот здесь.

              0
              Вообще-то, смысл ORM в том, чтобы (как следует из названия: object-to-relational db mapping) иметь возможность загружать и сохранять объекты в реляционную базу данных.
                0
                мой комментарий не противоречит этому, а лишь затрагивает важную часть идеи, которую не учёл Автор
                  0
                  Вам не кажестя, что объект по удобству и композиции проигрывает массиву в данном случае?
                    0
                    Всё ровно наоборот. Ассоциативные массивы — это семантическая лапша, в которой не то что автокомплита, но и никакой валидации не предусмотрено.
                      0
                      Нет, и вот почему:

                      — для объекта легко увидеть в коде, какие у него есть поля, каких типов, с какими комментариями. Для массива — как правило, нельзя, более того, часто вам могут приходить массивы разных форматов в зависимости от того, откуда вызвана функция

                      — у объекта могут быть дополнительные методы для работы с ним
                        –3
                        Посмотрите, вся моя база данным CMS RevolveR. Меняю одно поле value для каждого столбца и делаю что хочу одной строчкой в последующем:
                        github.com/xShiftx/RevolveR_CMS/blob/master/core/struct/DataBase.php

                        А вот к примеру инсталлятор:

                        github.com/xShiftx/RevolveR_CMS/blob/master/core/libraries/nodes/NodeSetup.php

                        Ну или изменение редактирование ноды и комментария:

                        github.com/xShiftx/RevolveR_CMS/blob/master/core/libraries/nodes/NodeIndex.php

                        Объект был бы убожеством, имхо. А еще массивы работают раза в 4 быстрее.

                        p.s.: резюмируем: я нарушил все каноны и подставил зарплату MySQL разработчиков, сильно упростив их работу. За это и минусуют. Ребят, я еще JOIN напишу и вы вообще испугаетесь без денег остаться? ;)
                          +3
                          минусуют за типичный код начинающего программиста, который ничего не знает и не умеет, а в конкретном случае еще и не пытается понял что тут советуют

                          Объект был бы убожеством, имхо

                          объекты как минимум позволяют задавать строгий тип для свойств, когда массиву же вообще до лампочки что в нем лежит

                          остальной бред даже комментировать не хочется

                          p.s. код по ссылке еще красивше чем здесь…
                        0
                        Объект выигрывает у массива по удобству и композиции, в данном случае так точно.
                        $someModel->id = 1;
                        $someModel->name = 'some name';
                        $someModel->save();
                        

                        А в вашем случае нужно нагородить массив, который и читать то неудобно, да и как верно тут заметили — с автокомплитом беда (хотя расширения для того же шторма имеются).
                        Вся прелесть объектов в ормках — описали структуру таблиц в отдельных классах, прописали там же связи (one2one, one2many, etc), описали свою бизнеслогику для работы с конкретными данными и всё! Нужна новая запись в таблице:
                        $newRow = new SomeModel();
                        $newRow->someFiled = $someValue;
                        $newRow->save();
                        

                        либо:
                        $newRow = new SomeModel(['someField'=>$someValue]);
                        $newRow->save();
                        

                        Обернули в трай-кэтч — и дальше либо выдали ошибку, либо навесили апдейт (на duplicate key например).
                    +3
                    Отсутствие namespaces
                    Не установить через composer
                    Кривой синтаксис на массивах, ключи которого нужно вылавливать из исходников
                    Отсутствие тестов
                    Хотите что-то легковесное — используйте https://github.com/auraphp/Aura.SqlQuery в связке с https://github.com/auraphp/Aura.Sql
                      –3
                      Про ауру не знал, спасибо.

                      Тесты собраны так же на странице примеров инструкций: s1.bild.me/bilder/110417/6643232Untitled-1.png

                      Показалось, что это просто ни к чему для такого скромного движка.
                        +1
                        это не тесты, прочитайте про https://phpunit.de/
                          –5
                          Ещё раз спрошу, а зачем такому маленькому коду целый PHP Unit? Вы не заигрались случаем?
                            +1
                            вы хотите чтоб другие люди это использовали. а зачем использовать чью-то поделку с непонятным синтаксисом и не факт что работающую стабильно?
                            Вот например тут — пути к файлам кэша захардкожены и очень легко все сломать, да и смешивать выборки из базы с кэшем очень плохая мысль, были бы тесты — вы бы вынесли кеширование отдельно, проверили что папка для него вообще доступна и что хотя бы запрос к базе выходит такой какой ожидается
                            и вместо phpunit вы вполне можете использовать phpt тесты
                              –3
                              Все с вами ясно. Разбираться не хотите — так и скажите. Библиотека на 3Кб для вас серьезная проблема интеграции и так сложно блин проверить есть ли папка кэша…

                              Я бы вас даже на собеседование не позвал, если бы вы ко мне устраивались.

                              Мне нельзя использовать сторонние библиотеки — каждое взятое решение приходится проверять в ручную. Вам легко говорить, когда вы сам не работаете и берете то, что нравится. У вас еще и наглости хватает требовать какой-то ерунды от бесплатно распространяемого ПО. Не хотите не берите — я просто поделился разработкой и идеями бесплатно.

                              Если бы я поставил симфони и композер мне пришлось бы работать еще с 500 человеками, почти с каждым, кто писал код компонентов и объяснять, что эти люди точно также написали качественное ПО, которое будет вести себя предсказуемо.

                              На западе давным давно принято, что любой разработчик помимо доступности в багтрекере и имейл имеет фидбэк в системе через прослушку, а мне все эти разработчики нафиг не нужны потому что меня системники за яйца подвесят, если окажется, что эти люди фиговые разработчики. И дело тут не в коде, а в мозгах, которые прикладываются по умолчанию и связях людей, которые этот код писали. Например у вашего любимого PDO нет ни мозгов, ни прослушки и вообще неясно как до этих людей достучаться, если нужно.

                              Я не пишу баг репорты — я сразу «звоню» в систему и говорю, что нужно исправить. И это исправляют очень быстро и если есть еще какие-то но, то они перерабатываются в считаные секунды. Так же звонят и мне и говорят, что нужно переработать. Никто мне коммиты не делает. Это лишнее.

                              Это вы для задач своего бизнеса, который поддерживаете можете рекомендовать этих людей и их разработки, но есть вещи, которые работают в разы строже.
                                +2
                                Библиотека на 3Кб для вас серьезная проблема интеграции и так сложно блин проверить есть ли папка кэша

                                Проблема не в размере библиотеки, а в ее качестве — интегрировать кое-как написанное на коленке решение невыгодно в принципе
                                Я бы вас даже на собеседование не позвал, если бы вы ко мне устраивались

                                Я бы вас тоже. И переход на личности — хамство, в своих комментариях я вроде как с аргументами критикую ваше решение, а не вас.
                                Мне нельзя использовать сторонние библиотеки — каждое взятое решение приходится проверять в ручную. Вам легко говорить, когда вы сам не работаете и берете то, что нравится. У вас еще и наглости хватает требовать какой-то ерунды от бесплатно распространяемого ПО. Не хотите не берите — я просто поделился разработкой и идеями бесплатно.

                                Если вам нельзя использовать стороннее, что очень странно, то это не повод писать кое-как. Может быть для вашей самописной cms это и приемлемо, но для нормальных проектов ваше решение не прокатит. И я не требую ничего от вашей библиотеки, я возьму то что больше подойдет под мою задачу, а если мне будет чего-то не хватать — отправлю pull-request туда
                                Если бы я поставил симфони и композер мне пришлось бы работать еще с 500 человеками, почти с каждым, кто писал код компонентов и объяснять, что эти люди точно также написали качественное ПО, которое будет вести себя предсказуемо.

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

                                Ну вы еще не висите нигде, так что не переживайте
                                Это вы для задач своего бизнеса, который поддерживаете можете рекомендовать этих людей и их разработки, но есть вещи, которые работают в разы строже

                                Вы не используете стандартное экранирование или подготовленные выражения, так что о строгости не с этой библиотекой говорить
                              0
                              Чтобы, если я внес правки в код, я мог проверить, что он корректно работает. А как иначе? Вы мне предлагаете вручную каждую функцию проверять что ли?
                          0
                          Справедливости ради, половина JS библиотек и часть питоновских используют такой же «кривой синтаксис на массивах». Раздражает, когда одна и та же вещь в PHP воспринимается резко негативно, а в Node.JS — как острие прогресса.
                            0
                            В 2000х годах в PHP это тоже было нормой, потом сообщество повзрослело. Сейчас же, имхо, JS переживает такое же время. Слишком молодое сообщество.

                            А на питоне в основном пишут математики, которые довольно далеки от профессиональной разработки ПО, по-этому такой стиль и прижился.

                            Опять же, повторюсь, это моё имхо и может вообще не совпадать с мнением большинства.
                              0

                              Очень смешно про "кривой синтаксис на массивах" в js, учитывая, что там вообще-то нет отдельно ассоциативных массивов (словарей, хешмапов) — там только объекты. А самое главное: какой (не кривой) синтаксис использует вторая половина библиотек на js, которые типа правильные?
                              За Питон ничего не скажу, объекты с полями и словари там разное, но про местным библиотеки не в курсе.

                                0
                                А самое главное: какой (не кривой) синтаксис использует вторая половина библиотек на js, которые типа правильные?


                                Тот, который позволяет проверить корректность введённых данных на этапе статического анализа кода. Не позволяет написать некорректный ключ или пропустить обязательное значение (опять же во время стат.анализа). И это, по-моему, довольно очевидно вытекает из слов автора комментария выше.
                                  0

                                  Так в том-то и дело, что различие из php ($a['foo'] vs $a->foo) неприменимо к js. Насколько мне известно, нет в js аналогичных двух способов делать, скажем так, data transfer / config object.
                                  Хотелось бы посмотреть на этот "правильный" vs "неправильный" синтаксис.

                                    0
                                    Хотелось бы посмотреть на этот «правильный» vs «неправильный» синтаксис.


                                    Пример А (PHP -> JS)
                                    Возьму пример вот из этого коммента: habr.com/ru/post/438762/#comment_19711946

                                    Правильный:
                                    Schema.create('flights', (table: Blueprint) => {
                                        table.increments('id');
                                        table.string('name');
                                        table.string('airline');
                                        table.timestamps();
                                    });
                                    


                                    Неправильный:
                                    schema({
                                      create: [
                                        flights: {
                                          id: {type: "int", length: 11, increment: true},
                                          name: {type: "varchar", length: 255},
                                          airline: {type: "varchar", length: 255},
                                          created_at: {type: "timestamp", default: db.expression('CURRENT_TIMESTAMP')},
                                          updated_at: {type: "timestamp", nullable: true}
                                        }
                                      ]
                                    });
                                    



                                    Пример Б (JS -> PHP)
                                    Неправильный:
                                    {
                                      test: /\.css$/,
                                      use: [
                                        {
                                          loader: MiniCssExtractPlugin.loader
                                        },
                                        {
                                          loader: 'css-loader',
                                          options: {
                                            sourceMap: true,
                                            importLoaders: 1,
                                            modules: true,
                                            localIdentName: '[name]__[local]_[hash:base64]',
                                          },
                                        },
                                        {
                                          loader: 'postcss-loader',
                                          ident: 'postcss',
                                          options: {
                                            sourceMap: true,
                                            plugins: () => [
                                              precss(),
                                              postcssPresetEnv({
                                                browsers: ['last 2 versions', 'ie >= 9'],
                                                compress: true,
                                              }),
                                            ],
                                          },
                                        },
                                      ],
                                    }
                                    


                                    Правильный:
                                    webpack.match(/\.css$/, (config) => {
                                        config.addLoader(new MiniCssExtractPlugin());
                                    
                                        config.addLoader(new CssLoader({
                                            sourceMap: true,
                                            importLoaders: 1,
                                            modules: true,
                                            localIdentName: '[name]__[local]_[hash:base64]',
                                        }));
                                    
                                        config.addLoader(new PostCssLoader({sourceMap: true}))
                                            .withPlugin(new PreCssPlugin())
                                            .withPlugin(new PostcssPresetEnv({
                                                browsers: ['last 2 versions', 'ie >= 9'],
                                                compress: true,
                                            }));
                                    });
                                    
                                    /*
                                    Кажется, на TS это выглядеть должно как-то так
                                    
                                    interface WebpackInterface {
                                        constructor(pattern: string, then?: Function<WebpackConfigInterface>?)
                                    }
                                    
                                    interface WebpackConfigInterface {
                                        addLoader(...plugins: WebpackPluginInterface): WebpackPluginInterface
                                    }
                                    
                                    interface WebpackPluginInterface {
                                        execute(...): ...
                                    }
                                    */
                                    


                                0
                                Насколько я помню, в JS все объекты, так что массив там — это сразу обьект, что-то вроде SplFixedArray
                              +1
                              xRevolveRx Маленькая синтаксческая ошибка: «Doctryne» -> «Doctrine»
                                0
                                Спасибо!
                                  0
                                  Да море их там, Tymestamp например.
                                  +1
                                  $dbx::query('s|field_id|asc|100|0', $table_1, $fields_1);


                                  Почему бы не сделать нормальные методы create\update\remove… вместо магических строк?

                                  Как парсится 's|field_id|asc|100|0'? регулярка? багоферма потенциальная?
                                  что если мне нужно пропустить один из параметров?
                                  что если у меня в названии колонки есть '|'?

                                  Жертвовать читабельностью и статическим анализом ради краткости не следует

                                  'field_id' => [
                                  'type' => 'num', // int
                                  'auto' => true, // auto increment
                                  'length' => 255,
                                  'fill' => true // not null
                                  ],

                                  зачем в массиве полей каждый ключ начинается с 'field_'?
                                  зачем свои наименования? 'fiil' => true, почему не not null например сразу? почему type 'num' — это int вдруг? абсолютно сбивает с толку

                                  Как это

                                  $fields_2 = [
                                  'field_id' => [
                                  'value' => 0
                                  ],
                                  'field_text' => [
                                  'value' => 'Yo if field_id = 0 it\'s an insert or if id exists it\'s an update'
                                  ],
                                  'field_date' => [
                                  'value' => date('Y-m-d')
                                  ]
                                  ];
                                  // perform queries
                                  $dbx::query('in', $table_1, $fields_2);


                                  вдруг превратилось в

                                  INSERT INTO `revolver__comments` (`field_id`, `field_content`)
                                  VALUES ('5', 'TEST UPDATE')
                                  ON DUPLICATE KEY UPDATE `field_id`='5', `field_content`='TEST UPDATE';


                                  откуда в запросе взялись field_content и куда делся field_date? Или это не связанные части? Что за
                                  'field_id' => [
                                  'value' => 0
                                  ],
                                  ? как задать другое ограничение например, на другие поля?

                                  Мне показалось, что использование полей criterion_field и criterion_value усложняет систему
                                  что это за поля вообще?
                                    0

                                    А разве в SQL можно в названии колонки использовать "|"? так-то претензия обоснованая, просто не сталкивался с таким.

                                      0
                                      Так не только в названии, в значение если запихнуть, как сие поделие себя поведёт?
                                    +2

                                    Вы используете статические методы тут: https://github.com/xShiftx/Revolver_DBX/blob/master/DBX.php


                                    Но зачем тогда конструктор? Конструктор используется для создания объекта. У вас же нельзя создать 2 объекта, так как при создании второго вы в конструкторе перезапишете статические свойства ( host, user, pass ), которые использует первый объект. Уважаемый автор, я вижу, что вы не понимаете ООП, если вы хотите использовать классы, изучите их, пожалуйста. Или же пишите тогда код без классов.


                                    Ваш код очень тяжело читать и понимать. Вот смотрите:


                                    public static function query($dbx_q, $dbx_t, $dbx_f) {

                                    Непонятно, что значит q, t, f. Непонятно, какие данные в них надо поместить. Не надо писать, что это значит, на Хабре, просто дайте переменным понятные имена и добавьте комментарий в код. Согласитесь, это выглядит в разы лучше:


                                    /** Комментарий в формате phpDoc */
                                    public function query(string $sql, array $parameters = [])

                                    Непонятный код тут:


                                    if( isset($dbx_q[2]) ) {
                                        switch ($dbx_q[2]) {
                                             case 'asc':
                                                  $SQL_0 .= ' ASC';

                                    Из этого фрагмента кода непонятно, что именно хранится во втором элементе $dbx_q, что значит ноль в $SQL_0. Наверняка это есть в вашей голове, но я не телепат и понять это не могу.


                                    public static function setHash() {

                                    Непонятно, что такое hash. Комментариев в коде нет.


                                    while($row = mysqli_fetch_assoc($result)['field_'. explode('__', self::$sql_hash_table)[1]]) 

                                    Это непонятный код. Непонятно, что такое sql_hash_table, зачем ее разбивать по двойному подчеркиванию, и что там будет в элементе номер 1.


                                    $cacheFile = file_get_contents($_SERVER["DOCUMENT_ROOT"] .'/private/cache/'. self::$sql_hash_table .'.cache', true);

                                    Вы зря вписали кеш прямо в код, а не сделали внешним элементом. Из-за этого мы не можем даже поменять папку кеша, не говоря о замене кеша на redis или apcu. Вы работу с кешем даже в отдельную функию вынести поленились. Не пишите длинные простыни кода, разбивайте код на функции.


                                    public static function escape( $string ) {
                                            return htmlspecialchars(addslashes(preg_replace('/[\x{10000}-\x{10FFFF}]/u', "\xEF\xBF\xBD", $string)));

                                    Вы можете объяснить принцип работы этой функции? У меня ощущение, что вы просто поставили несколько функций наугад без какой-то теоретической основы.


                                    И, автор, где тесты? Вы тестировали, например, что при вставке данных в БД с помощью вашего класса символы с любыми кодами возвращаются в неизменном виде? У меня, судя по виду функции escape, есть ощущение, что это не так, и ваш класс искажает вставляемые данные.


                                    То, что в index.php — это не автоматизированные тесты, а ручные, так как надо визуально проверять, правильный ли результат вернула функция. И там нет теста, который бы проверял вставку символов со всеми возможными кодами, что они вставляются и возвращаются без искажений.

                                      0
                                      Как было сказано выше — как хобби проект для себя любимого — норм.
                                      Как продукт который могут использовать дургие — вообще нет.
                                      *цать лет назад возможно было бы «да», но не в 2019 когда для всего есть фреймворки и у каждого фремворка есть и ОРМ, и другой инструментарий, и «плагины», ответ уже «нет».
                                        +2
                                        Да что же это такое… Как ни зайдешь в блог PHP что-то интересное почитать на ночь, про стандарты там, про PHP 8, про PHPConf в мае — так сразу шизофазия какая-то, mysqli и шизокод… Кто и зачем вытащил это из песочницы?
                                          +2
                                          это тот редкий случай, когда комментарии автора читать интереснее самого топика. Выше в ветке даже не шизофазия, а шизофрения, ну или соль какая то, я не знаю что он принимает.
                                          >> Кто и зачем вытащил это из песочницы?
                                          между PHP и Python идет «священная война», видимо это какой то войн из протестующего лагеря вытащил чисто поржать над PHPшниками :) лишний раз ткнуть носом, видишь ужас там какой, переходи на светлую сторону и все такое.
                                            +1

                                            Там и в других статьях можно почитать. Js например :)

                                          0

                                          Когда фреймворки только зарождались, похожий велосипед я тоже писал, конечно выложить я бы его не осмелился). Но мне в году 2011 примерно понравился mysqli в ОО стиле, потому что методы и автокомплит, самих методов огромная куча, как функции в mysql расширении.
                                          Из минусов это совсем не удобная работа с плэйсхолдерами (сделал по аналогии как в PDO)
                                          Ну и затея была больше сделать query builder, чтоб не писать запросы.
                                          Ну а автор похоже адепт "Чистого кода"
                                          Может кто слышал про таких сектантов))

                                            0
                                            Я вообще не понял при чем здесь ORM (Object-Relation Mapping) в статье :)
                                            Сам тоже свой велосипед писал (и сейчас его конкретно переделываю, для небольших проектов самое то), и от концепции query builder решил отказаться, так как SQL всё же гораздо гибче и честно говоря его читать ИМХО проще, только самые простые запросы создаются автоматом, например вставка записи:
                                            $db->users[] = [null, 'john', 'john.smith@example.com'];
                                            

                                            Ну или если указывать названия столбцов
                                            $db->users[] = ['id' => null, 'name' => 'John', 'email' => 'john.smith@example.сom'];
                                            

                                            (вовсю используются магические методы и интерфейсы типа ArrayAccess)
                                            Так же можно вставлять и объекты, а выборка записей автоматом — или всё из таблицы, или одну запись по ключу, или писать свой SQL.
                                            $user = $db->users->id[15];
                                            

                                            Всего пара десятков классов в пакете.
                                              0

                                              Покажите? Тоже магию сильно напилил. Но как по мне, теряется читабельность

                                                0
                                                Только что закоммитил изменения, я уже пару месяцев не занимался проектом. Оно еще в стадии постоянного переделывания, и текущий вариант мне не особо нравится, и там надо еще избавиться от зависимостей от двух других самописных пакетов (конфигуратор и PSR-совместимый логгер). Ну и PHPDoc надо добавить/поправить, и тестов нет пока, и в работе этот вариант еще не использовался. Раньше был просто один класс, а сейчас для небольших проектов использую чистый PDO пока не закончу вот это поделие.

                                                Вот здесь оно

                                                test.php
                                                require_once('vendor/autoload.php');
                                                
                                                $config = new queasy\config\Config('config.php');
                                                $logger = new queasy\log\Logger($config->logger);
                                                $db = new queasy\db\Db($config->db, $logger);
                                                
                                                $logger->info($db->selectLocation(22));
                                                $db->address_locations[] = [
                                                    [
                                                        'address',
                                                        'lat',
                                                        'long'
                                                    ], [
                                                        [
                                                            '!!ijkl333---',
                                                            '!!123333---',
                                                            '!!456333---'
                                                        ], [
                                                            'FWEFEW',
                                                            'fwefwevw',
                                                            '4525t2tt'
                                                        ]
                                                    ]
                                                ];
                                                
                                                $logger->debug('total records: ' . count($db->address_locations));
                                                $logger->debug($db->address_locations->id[12]);
                                                


                                                config.php
                                                <?php
                                                return [
                                                    'db' => [
                                                        'connection' => [
                                                            'driver' => 'mysql',
                                                            'host' => 'localhost',
                                                            'name' => 'test',
                                                            'user' => 'root',
                                                            'password' => '*********'
                                                        ],
                                                        'queries' => [
                                                            'selectLocations' => [
                                                                'query' => '
                                                                    SELECT  *
                                                                    FROM    `address_locations`'
                                                            ],
                                                            'selectLocation' => [
                                                                'query' => '
                                                                    SELECT  *
                                                                    FROM    `address_locations`
                                                                    WHERE   `id` = ?',
                                                                'fetchMode' => PDO::FETCH_CLASS,
                                                                'fetchArg' => queasy\db\TestClass::class,
                                                                'returns' => queasy\db\Db::RETURN_ONE
                                                            ],
                                                            'selectLocation2' => [
                                                                'query' => '
                                                                    SELECT  *
                                                                    FROM    `address_locations`
                                                                    WHERE   `id` = :id'
                                                            ]
                                                        ]
                                                    ],
                                                    'logger' => [
                                                        'processName' => 'db-test',
                                                        [
                                                            'class' => 'queasy\log\FileSystemLogger',
                                                            'path' => 'debug.full.log'
                                                        ],
                                                        [
                                                            'class' => 'queasy\log\ConsoleLogger',
                                                        ]
                                                    ]
                                                ];
                                                



                                                P.S. А, насчет PHP Framework это я так, пишу для своего удовольствия. Ну и стараюсь оформлять как положено, два пакета (v-dem/queasy-config и v-dem/queasy-log) уже можно практически использовать. Первый вариант фреймворка уже был готов и я на нем для проверки удобства начал было писать небольшой чат. Но потом решил разбить на несвязанные между собой компоненты, и понеслось… А сначала был простенький логгер из одного класса и класс-обертка для PDO, и из них кода сейчас практически не осталось, всё переписано. Вот сейчас работаю над v-dem/queasy-db, а когда дойдет дело до собственно фреймворка даже не знаю. В первом варианте было несколько более-менее оригинальных идей (некоторые встречал в других микрофреймворках, тысячи их), скорее всего в финальном варианте они будут.
                                                  0
                                                  Глянул, работы там еще много, я бы отказался от php 5.3, тем более у Вас там трэйты
                                                    0
                                                    Я бы с удовольствием отказался от поддержки PHP5.3, и насчет трейтов в курсе (они в 5.4 появились, я вот думаю как в этой версии с поддержкой 5.3 попроще обойтись без трейтов), но еще время от времени попадаются заказчики «что было хорошо для наших дедов, то будет хорошо и для нас» и их невозможно уговорить перейти на версию поновее (например ситуация когда есть некая огромная совсем легаси система и средств на ее переделывание под новые версии нет, и к ней нужно что-то более-менее отдельное допилить, например скрипт который будет запускаться через cron). Когда и если все пакеты будут более-менее готовы и в работе это поделие себя покажет удовлетворительно, возьмусь за версию для PHP7 (со всеми новыми фичами, так что она будет несовместима со старой), а к этой буду разве что при случае что-то допиливать. Как-то так. Вообще я когда еще с PHP4 имел дело (а тогда я в основном с C# и Java работал) то так руки и тянулись что-то этакое написать, так как повторять одно и тоже в каждом файле (инклуды и т.д.) явно антипаттерн, но тогдашние поделки были кошмарны (вот как раз на уровне тех самописных фреймворков, про которые тут время от времени попадаются статьи), да и PHP я толком не знал.
                                                      0

                                                      Боюсь 5.3 там уже не получится подерживать, один трэйт ещё можно обычным наследованием победить, ну и опять же, зависит от ситуации, если всего один трэйт, его надо тащить минимум в два класса, то я выбрал бы абстрактный класс, ну а так, вроде как текущий код уже не поехает на 5.3. С выходом семёрки забыл про php5, как страшный сон

                                                      0
                                                      Вообще я Laravel использую для более-менее крупных проектов, и в общем только его я относительно хорошо знаю (с некоторыми прочими только знаком). Мне в них не нравится куча сторонних зависимостей, в том же Laravel простая страничка с одним запросом к базе подключает ~350 файлов. Потому и пилю свой велосипед (знаю, есть полно других и к тому же известных, просто интересно и есть некоторые оригинальные идеи, ну и может действительно пригодиться), и к тому же постараюсь сделать его как можно более гибким.
                                                        0
                                                        Мне в них не нравится куча сторонних зависимостей, в том же Laravel простая страничка с одним запросом к базе подключает ~350 файлов.

                                                        А что, это проблема? Всё равно же потом всё в опкеш прилетает и через DCE лишнее вырезается.
                                                          0
                                                          Да не проблема в общем, все равно не нравится. Для серьезных проектов все равно Laravel, а допилю я свою задумку до чего-то юзабельного или нет — еще неизвестно.
                                                          0

                                                          Композер впринципе помогает не писать много кода, выбрать запчасти с разных фв, сделать адаптеры, лара не очень нравится, вот отдельные запчасти ещё может куда не шло, но а так, какой то он монолитный что ли, если бы psr сразу бы все начали придерживаться, было бы удобнее.
                                                          Ну а фв свой написать для понимания других стоит. В процессе приходит понимание, как там все внутри работает, и видишь плюсы и минусы разных решений

                                                +2
                                                И так, это DBX — движок базы данных MySQL для PHP на основе библиотеки MySQLi основанный на запросах вида структурного описания таблиц и полей базы данных в виде обычного массива с функцией статического кэширования запросов и автообновления хэша статики.

                                                Движок? Серьёзно?
                                                  0
                                                  Херовй из тебя фуллстек. Но идеи интересные.
                                                    0
                                                    Казалось бы, почему PHP'шников за людей не считают… Открываешь вот в 2к19 такую статью, а затем комменты к оной, и… Грустно это
                                                      0
                                                      Боже… Синтаксис запросов просто мозговыносный. Это не ORM, это какая-то эзотерика.

                                                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                      Самое читаемое