Потребовался класс для работы с БД. Мои требования были:
  1. Поддержка типов данных через макросы: строка, целое число, дробное число, логическое значение + возможность расширения
  2. Возможность компиляции запроса в PHP код (по аналогии как шаблонизаторы компилируют шаблон — к примеру так делает Twig)
  3. Возможность делать макросы для использования в запросе
Поискал по интернету — список чего нашел (самые лучшие на мой взгляд): safemysql, DbSimple, go-db, dibi, Yaff\db. Однако почему-то именно компилирующих нет. Поспрашивал на форумах и тостере, получил 10000 советов не писать велосипед, а изучить библиотеку X, потому что это круто и на ней можно сделать всё. И всё же… решил таки написать свой велосипед.

Я не буду в очередной раз писать про защиту от SQL инъекция. Если кто еще не читал, то вот отличная статья на эту тему Защита от SQL-инъекций в PHP и MySQL от автора safemysql. Данная статья — это моё ТЗ (не работающий класс, а только его описание) для написание работающего класса, который можно использовать в своем проекте. Возможно собранная информация будет и еще кому-то полезна. Для меня же польза будет в том, что при описании для других людей лучше обдумывается тема, что способствует лучшему продумыванию архитектуры.

Базовые макросы


Все макросы будут иметь вид ?<имя макроса>(параметры). Я написал макросЫ и их действительно будет несколько — целых 2 штуки. Перечислим их:
  • ?if(имя переменной){текст запроса, который должен выполняться в случае если указанная переменная ИСТИНА}[?else{текст запроса, который должен выполняться в случае если указанная переменная ЛОЖЬ}] — этот макрос служит для формирования сложных запросов в зависимости от условия. Блок else является не обязательным.
  • ?_dot(текст для вставки в запрос) — этот макрос служит для вставки в запрос переменных или вызовов функций. Макрос с нижним подчеркиванием, так как он предназначен не для вызова из текста запроса, а для формирования макросов расширения, речь о которых пойдет далее.
Рассмотрим на примере. Пусть есть запрос вида
SELECT name FROM users WHERE age > ?_dot(intval($vars['age'])) AND ?if(male){gender='1'}?else{gender = '2'}
в результате обработки получим следующий PHP код для формирования запроса
$sql = "SELECT name FROM users WHERE age > ".intval($vars['age'])." AND ";
if($vars['male'])
   $sql .= "gender='1'";
else
   $sql .= "gender='2'";
Все остальные макросы, которые могут потребоваться (в том числе для безопасной вставки переменных в текст запроса), можно сформировать с помощью 2-х приведенных выше макросов. При этом также важен тот факт, что все переданные переменные содержатся в массиве $vars. В примере выше используются две переменные из этого массива: age и male.

Архитектура классов



Каждый вариант будет располагаться в папке производитель\вариант. Базовый вариант будет находиться в папке Shasoft\Base. Также классы каждого варианта должны располагаться в аналогичном пространстве имен: базовый вариант будет находиться в пространстве имен Shasoft\Base. Такой подход позволит избежать пересечения имен классов в разных вариантах.

Основной класс


Основной класс для работы с БД — SafeSql. В базовом варианте он будет содержать только метод query, который будет вызываться для выполнения запросов.
$db->query("текст запроса",<массив переменных>,function($row,$qi) {
});
  • $row — очередная строка выбранных данных или неопределенное значение если запрос не подразумевает возврат данных (INSERT, UPDATE, DELETE)
  • $qi — дополнительная информация. Содержит следующие поля
    • effected_rows — число строк, затронутых предыдущей операцией MySQL
    • error — строка с описанием последней ошибки (или false если ошибки не было)
    • insert_id — автоматически генерируемый ID
Также функция должна прерывать перебор данных, для чего достаточно вернуть из неё значение false.
Этот класс должен переопределяться в обязательном порядке в каждом новом варианте даже если не добавляется никакого нового функционала. Также класс содержит переменную opts с параметрами, переданными в класс при создании и указатель на объект драйвера, созданного по имени драйвера. переданного в переменной driver. Речь о классе драйвера пойдет далее.

Класс макроса


Как уже писал ранее — есть всего два базовых макроса. Все остальные макросы, которые могут понадобиться, можно создать с помощью макросов расширения. В тексте запроса макрос имеет следующий вид
?<любое имя>[(параметры)] — любой определенный вами макрос. Для реализации этого макроса необходимо в папке вариант\Macros создать файл с <именем макроса>.php и определить в нем класс с именем макроса, который должен наследоваться или от абстрактного класса Shasoft\Base\Macro или от ранее определенного макроса. Для реализации замены макроса необходимо реализовать абстрактный метод sql()
Абстрактный класс макроса Shasoft\Base\Macro
//! Абстрактный класс макроса
abstract class Macro {
	// Параметры
	protected $opts;
	// Драйвер
	protected $driver;
	// Конструктор
	function __construct() {
	}	
	// Выполнить преобразование
	// $args - строка параметров
	abstract public function sql($args);
	// Разобрать параметры макроса на список параметров
	// $args - строка параметров
	static public function parseArgs($args) {...}
}
Если необходимо разобрать строку параметров макроса на список параметров, то можно воспользоваться методом parseArgs(). Данная функция возвращает массив аргументов, т.е. массив объектов класса Shasoft\Base\MacroArg
Класс Shasoft\Base\MacroArg
//! Класс аргумента макроса
class MacroArg {
	// Значение
	protected $value;
	// Символ строки (если = false, значит это не строка)
	protected $ch_string;
	// Текст
	public getText() { return $this->value; }
	// Это строка?
	public isString() { return $this->ch_string==false ? false : true; }
	// Получить текст строки как константу
	public getConstString() { return $this->ch_string.addslashes($this->value).$this->ch_string; }
}
Метод sql может возвращать текст, который содержит другие макросы.
Если макрос был определен в одном из родительских классов, то вы можете его заменить в текущей реализации. Если макроса нет в текущей реализации, то макрос с таким именем ищется в родительском классе, затем в родительском родителя и т.д. Именно для этого необходимо обязательно определять основной класс SafeSql для вашего варианта — необходим список родителей класса, чтобы знать где искать не определенные в данном варианте макросы.

Класс драйвера


Данный класс будет отвечать за работу с конкретной БД. Класс также можно будет расширять от версии к версии. В базовой версии драйверу необходимо реализовать всего три функции: открытие соединение с БД, закрытие соединение, генерация PHP кода для выполнения запроса.
Интерфейс драйвера Shasoft\Base\IDriver
<?php
namespace Shasoft\SafeSql;
//! Абстрактный класс драйвера для работы с БД
abstract class Driver {
	// Конструктор
	function __construct() {}	
	// Открыть соединение с БД
	// result - возвращает true или текст ошибки
	abstract public function connect($opts);
	// Закрыть соединение с БД
	abstract public function close($opts);
	// Сформировать код PHP для выполнения запроса 
	// $varSql - имя переменной содержащей запрос (не текст запроса, а имя ПЕРЕМЕННОЙ, которая этот запрос содержит)
	// result - возвращает текст PHP кода
	abstract public function sql($varSql);
}
?>
Если в варианте нет драйвера, то он ищется по аналогии с макросом: в родительском варианте, если нет, то в родительском родительского и т.д.
Картинка со схемой наследования

Параметры базового варианта


Опишем параметры базового варианта.
  • driver — имя драйвера. Короткое имя, т.е. без приставки Vendor\Variant\Drivers
  • server — сервер. По умолчанию «localhost»
  • database — имя базы данных
  • user — имя пользователя
  • pass — пароль
  • path — директория для сохранения скомпилированного PHP кода
  • debug — режим отладки. Т.е. в этом режиме код всё равно будет каждый раз перекомпилироваться и пересохраняться


А теперь напишу ка я базовый класс на основе этой статьи и станет ясно, насколько реально то, что я описал. За работу!

p.s.может кто подскажет класс PHP, которому можно скормить код на PHP и получить его красиво отформатированным? В общем-то это не очень и нужно, но для проверки работоспособности лучше чтобы полученный код выглядел красиво.

Комментарии (55)


  1. Fesor
    06.08.2015 18:25
    +14

    ммм… как это развидеть…

    вроде как уже лет 10 есть PDO и prepared statements… потому никто и не делает «компиляции»


    1. shasoft
      06.08.2015 18:27
      -11

      Однако все CMS все-равно делают свой слой абстракции для работы с БД. С PDO никто вроде не работает.

      p.s.Чтобы это «развидеть» — достаточно закрыть страницу.


      1. Fesor
        06.08.2015 18:43
        +4

        Однако все CMS все-равно делают свой слой абстракции для работы с БД

        это не отменяет того что не нужно делать свои кастыли эмулирующие работу prepared statements (mysqli тоже их поддерживает, если хочется еще более низкоуровневое API юзать). Да и насколько я помню все адекватные CMS уже давно перешли на PDO.

        Давайте сравним…
        $sql = "SELECT name FROM users WHERE age > ".intval($vars['age'])." AND ";
        if($vars['male'])
           $sql .= "gender='1'";
        else
           $sql .= "gender='2'";
        


        и

        // для mysqli
        $stmt = $connection->prepare("SELECT name FROM users WHERE age > ? AND gender = ?");
        // не нужно никаких кастов и т.д. ибо мы уже прописали что это два инта.
        $stmt->bind_param("ii", $age, $male ? 1 : 2); 
        


        1. shasoft
          06.08.2015 19:50
          -2

          Так никто не мешает написать драйвер работы с PDO. Тогда будет генерироваться указанный вами код. В общем-то при проектировании учитывалась такая ситуация.


          1. Fesor
            06.08.2015 19:57
            +1

            погодите, у вас написано вот это:

            SELECT name FROM users WHERE age > ?_dot(intval($vars['age'])) AND ?if(male){gender='1'}?else{gender = '2'}


            Вот эти вот intval не позволяют нам получить информацию о типе и избавиться от необходимости вручную контролировать что мы пихаем в запрос. Мы просто усложняем систему (причем страдает читабельность) и при этом не получаем ровным счетом никакого профита.


            1. shasoft
              06.08.2015 20:06
              -2

              В данном случае у нас вообще нет информации о типе. Это БАЗОВЫЙ вариант. Предполагается что он будет расширен макросами s, i,f — которые как раз и будут задавать тип данных. Т.е. макрос _dot вообще не будет встречаться в запросе (о чем и указано в статье).
              Т.е. будет макрос i — целое число. И тогда запрос
              SELECT name FROM users WHERE age > ?i(age) AND ?if(male){gender='1'}?else{gender = '2'}
              будет в одном случае разворачиваться в ?_dot(intval($vars['age'])), а в случае с PDO в он будет разворачиваться в ":age" и будет запоминаться что эта переменная имеет тип i.
              Как то так.


              1. Stalker_RED
                06.08.2015 20:33

                Вы не хотите дополнить статью и привести несколько примеров «было/стало»? Потому что судя по комментариям, сейчас сложно понять зачем оно вообще. (Я вот, вообще ничего не понял.)


                1. shasoft
                  06.08.2015 20:58
                  -1

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


                  1. Fesor
                    06.08.2015 21:21

                    только ради мога, возьмите за основу что-нибудь вменяемое, например doctrine/dbal. То что я вижу в статье можно сделать на базе того же twig, и это будет ужасно…


                    1. shasoft
                      07.08.2015 17:58
                      -1

                      doctrine/dbal. — это ORM. Т.е. там подход «немного» другой


                      1. Fesor
                        07.08.2015 19:27
                        +1

                        dbal это dbal, database abstraction layer, к ORM никакого отношения не имеет, кроме того что является зависимостью doctrine/orm.


        1. shasoft
          06.08.2015 20:00

          Вообще же в данном случае это синтетический пример. Т.е. пример просто демонстрировал условное составление запроса. По аналогии DbSimple тоже использует фигурные скобки для этого. Т.е. на реальном примере пришлось бы запрос тоже составлять с помощью if/else. Данный пример так легко лег в одну строку запроса потому что это просто пример. Вот такой запрос уже все-равно придется писать с if
          SELECT name FROM users WHERE age > ?_dot(intval($vars['age'])) AND ?if(male){gender='1'}


          1. Fesor
            06.08.2015 21:25
            +3

            Ну вот а теперь сравните читабельность:

            $qb = $this->createQueryBuilder();
            
            $qb->select('name')->from('users')->where('age=:age')->setParameter('age', $age);
            
            if ($male) {
                $qb->andWhere('gender=:gender')->setParameter('gender', User::GENDER_MALE);
            }
            
            $userNames = $qb->getQuery()->getScalarResult();
            


            это легко прочитать а значит легко поддерживать и разбираться в коде. Да, в вашем примере меньше символов, но поддерживать его будет ад и холокост. Меньше кода не значит лучше.


      1. Invision70
        06.08.2015 18:44

        >>> С PDO никто вроде не работает.
        Например слой абстракции идет поверх PDO, за счет этого используя Query Builder прослойку — меняем БД и работаем дальше.


  1. andrewnester
    06.08.2015 19:12
    +4

    если на то пошло, ваш SQL код под компиляцию выглядит страшнее и запутаннее, чем если бы Вы использовали prepared statements или query builder


  1. WindDrop
    06.08.2015 19:36
    +8

    — Ну так что, у тебя есть работающий класс?
    — Лучше! У меня есть ТЗ работающего класса!


    1. maximw
      07.08.2015 01:22

      Напоминает анекдотичный подход математика к решению проблемы — установка факта, что решение существует.


  1. jrip
    06.08.2015 19:59
    +7

    >А теперь напишу ка я базовый класс на основе этой статьи и станет ясно, насколько реально то, что я описал. За работу!
    пожалуйста не надо, в нашем мире и так хватает печали.


  1. Lisio
    06.08.2015 20:25
    +9

    Я правильно понял, что используя ваш метод нужно сформировать особый SQL-запрос, который средствами PHP сформирует код PHP, который сформирует SQL-запрос?


    1. shasoft
      06.08.2015 20:40
      -2

      Ну, если назвать особым запросом то, что обычно пишут в DbSimple и аналогах, то ДА.


      1. Lisio
        06.08.2015 20:49
        +2

        Даже сам Дмитрий Котеров пишет на гитхабе, что это «Quite old (PHP4-compatible) interface to work with various DBs.». В настоящее время ее использование не принесет ничего, кроме лишних проблем тем, кто будет поддерживать такой код.


  1. saksmt
    06.08.2015 21:09
    +3

    Если уж так хочется прям МАКРОСЫ, а не ту ерунду, что вы здесь понаписали в качестве примеров, то есть Doctrine и DQL, который вполне себе расширяем и компилируем, в том чиле в Memcache[d] и напрямую в OpCache.

    А ежели нужны «макросы» в вашем понимании, то тут и PDO хватит, как уже писали выше.

    P.S. Действительно, помогите мне это развидеть…


    1. Fesor
      06.08.2015 21:20

      напрямую в OpCache.

      А с каких пор Doctrine умеет компилировать и кешировать DQL? Я серьезно об этом не знаю. И уж тем более как это оно может кешировать в opcache? Кеширование в opcache подразумавает генерацию PHP кода и его выполнение (не через eval), может быть вы имеете в виду APCu?

      В целом я согласен, подход доктрины намного более правильный. Там тебе и возможность через AstWalker поменять все что угодно в запросе в рантайме, и расширять его удобно и вообще ништяк. А так как там используются prepared statements и имеется вся необходимая инфа о метаданных (которая кешируется), то вероятность SQL инъекций нивелируется.

      Ну и да, мэппинг на объекты… люблю я доктрину.


      1. jrip
        06.08.2015 21:53
        +1

        >А с каких пор Doctrine умеет компилировать и кешировать DQL?
        Меня никак не может покинуть один вопрос: нафига кешировать сам код запросов?


        1. Fesor
          06.08.2015 23:30

          В контексте DQL — это не SQL и оперирует он сущностями, объектами и т.д. Там есть море кастомных функций и т.д. То есть это нормальная идея сохранять результат работы этой штуки — SQL, так как это не просто query builder а query builder основанный на метаданных мэппинга таблиц на объекты, с учетом вложенных объектов и трансформаций. Но это крайне сложно сделать и проще кешировать на уровень выше — в репозитории.


          1. jrip
            07.08.2015 01:38

            >Но это крайне сложно сделать и
            >проще кешировать на уровень выше — в репозитории.

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


            1. Fesor
              07.08.2015 01:42

              Повторю еще раз. вам надо преобразовать:

              $dql = "SELECT u FROM \App\Domain\User\User u where  u.createdAt = :date";
              $query = $this->createQuery($dql)->setParameter('date', new \DateTime('last friday'));
              


              в

              $sql = 'SELECT u.* FROM users u WHERE u.created_at = ?';
              


              что-то в этом духе. Но да, в этом смысла нет потому этого и не реализовали. Я больше к этому вел мысль — доктрина не умеет это делать и не собирается.


      1. saksmt
        07.08.2015 08:54
        +1

        Умеет, и в APCu и в OpCache через сгенерированные PHP файлы (Doctrine\Common\Cache\PhpFileCache), штука эта называется QueryCache она кеширует именно сгенерированные запросы, есть ещё MetadataCache и ResultCache, думаю назначение разъяснять не нужно. В доктрине и компонентах симфони вообще много малоизвестных штук происходит :)


        1. Fesor
          07.08.2015 09:59

          Действительно, QueryCache именно этим и занимается… не знал…


  1. jrip
    06.08.2015 21:55
    +2

    >p.s.может кто подскажет класс PHP, которому можно скормить код на PHP и получить его >красиво отформатированным?
    Я за вас уже боюсь, надеюсь вы тут так всех тролите.
    Код можно отформатировать с помощью IDE, PhpStorm например умеет.


    1. shasoft
      06.08.2015 22:15

      Вы не поняли суть вопроса: генерируется PHP код для выполнения запроса. Мне нужно перед тем как сохранить в файл, его красиво отформатировать. Чтобы при просмотре было все более наглядно, но при этом при генерации кода не заморачиваться с его красивым представлением.


      1. Fesor
        06.08.2015 23:31

        php-parser, формируете AST и на выходе будет красивый PHP.


        1. shasoft
          07.08.2015 07:54

          Спасибо


      1. jrip
        07.08.2015 01:49
        +1

        Я вас понял, я просто намекаю что ваша идея кажется немного бредовой)
        Но в креативности вам не откажешь, я такой идеи еще не встречал)

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

        А также есть и другие минусы в таком подходе — во первых файлов в реальном проекте у вас таких будет просто дофига, на каждый запрос с разными параметрами. Макросами вы эту проблему не решите — вам для этого нужно будет придумать свой php внутри php.
        Во вторых вы все равно придете к тому, что кеш нужно как-то валидировать, а такой валидировать будет трудно.
        В третьих тот кто будет работать с кодом после вас, будет переполняться ненавистью.
        >?_dot(intval($vars['age'])) AND ?if(male){gender='1'}?else{gender = '2'}
        Вы же тут на самом деле язык в языке начинаете придумывать, причем с не очень то удобным синтаксисом, а его придется изучить и запомнить.


        1. shasoft
          07.08.2015 08:15
          -1

          ОДИН запрос с РАЗНЫМИ параметрами => один PHP файл. Файл создается ОДИН раз при ПЕРВОМ вызове.


          1. jrip
            07.08.2015 10:08
            +1

            А ну т.е. в результате получается совсем небольшой ограниченный набор запросов? Тогда чего их руками не написать? Зачем весь этот гемор? Сокращение нажатий кнопок в ущерб понятности когда? Так можно пойти и на Perl`е писать.
            Защита от SQL-инъекций? Такая же вероятность начудить в макросах.


            1. shasoft
              07.08.2015 10:40

              Если исходить из «начудить», то можно ошибиться если писать запросы на QueryBuilder-е или любом другим способом.
              На мой взгляд проще писать ?i(age), чем intval($vars['age']).
              Как я уже писал — это просто уровень абстракции.


              1. jrip
                07.08.2015 11:05

                >?i(age), чем intval($vars['age']).
                Это не абстракция, это изменение синтаксиса.
                >то можно ошибиться если писать запросы на QueryBuilder-е или любом другим способом.
                Там начудить труднее, спец символы в нормальном QueryBuilder-е будут таки экранироваться.

                Кстати как в вашем подходе будет выглядть вставка в таблицу поля, которое содержит html и которое нельзя меня?
                Как будет выглядеть запрос с поиском по полю через LIKE%?


                1. shasoft
                  07.08.2015 11:25

                  >>Это не абстракция, это изменение синтаксиса.
                  Так QueryBuilder тоже по сути изменение синтаксиса. ;)

                  >>Кстати как в вашем подходе будет выглядть вставка в таблицу поля, которое содержит html и которое нельзя меня
                  если вы про константное значение, то вот так ?s("Хабр")

                  >>Как будет выглядеть запрос с поиском по полю через LIKE%?
                  ?f(field) like ?s("%хабр")
                  как вариант, сделать макрос соответствующий
                  ?like(field,"%хабр")


                  1. jrip
                    07.08.2015 11:58
                    +1

                    >>Это не абстракция, это изменение синтаксиса.
                    >Так QueryBuilder тоже по сути изменение синтаксиса. ;)
                    Нет. QueryBuilder использует снтаксис языка, там понятно где метод, где класс и что означают спецсимволы.
                    ?i(age), — а вот тут человек, знающий синтаксис PHP может и не понять что происходит.
                    разница в том, что код QueryBuilder обрабатывается PHP, а ваш код обрабатывается вашим кодом на PHP превращается в код PHP и уже потом обрабатывается PHP.

                    >если вы про константное значение, то вот так ?s(«Хабр»)
                    И что дальше происходи с «Хабр»? Если написать «Хабр\»"?
                    >?like(field,"%хабр")
                    А если тупо ?like(field,"%хабр\"") то что будет?
                    Ну в плане где тут защита от инъекций у вас?


                    1. shasoft
                      07.08.2015 13:13

                      >>И что дальше происходи с «Хабр»?
                      Вызовется real_escape(«Хабр») (или его аналог в конкретной БД) и строка вставиться в запрос.


                      1. jrip
                        07.08.2015 13:27
                        +1

                        Ну так это получается подход query('SELECT * FROM u WHERE field = ?', $param), который уже малость устарел на самом деле.
                        Только вы еще и логику свою добавляете, которую нужно понять и заучить в виде макросов.
                        Смысл то какой?

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


                        1. shasoft
                          07.08.2015 15:35

                          Смысл в облегчении себе работы. Если часто пишешь одну и туже команду — закатал её в макрос и минимизировал шанс написать команду ошибочно.

                          >>возможность инъекций в PHP добавляете
                          Это каким-же образом? Только если использовать библиотеку, которую написал неизвестно кто и в которой есть макрос, который делает эту PHP инъекцию.


                          1. Fesor
                            07.08.2015 15:54

                            Смысл в облегчении себе работы.

                            Я не вижу облегчения… Опять же — DQL в Doctrine2 делает то что вы хотите. Вот если вы сделаете что-то подобное — то да, спору нет, штука полезная (с автоматическим ресолвом типов из схемы базы данных что бы не нужно было руками это указывать где-либо).


                            1. jrip
                              07.08.2015 16:18

                              >Doctrine2
                              Оно не делает то что он хочет, да и сферы применения у Doctrine2 далеко не безграничны.


                          1. jrip
                            07.08.2015 16:14
                            +1

                            >Смысл в облегчении себе работы.
                            >Если часто пишешь одну и туже команду — закатал её в макрос и
                            >минимизировал шанс написать команду ошибочно.
                            Так сделайте макрос на уровне IDE, вот опять напишу про phpStorm — там такое возможно и вообщем-то как раз так многие и делают например для отладки, хочется выводить красивый var_dump — переопределяют его и не копируют постоянно большой кусок кода. Там будет ровно такое же разворачивание кода, как делаете вы.

                            >Это каким-же образом? Только если использовать библиотеку,
                            >которую написал неизвестно кто и в которой есть макрос, который делает эту PHP инъекцию.
                            Есть методология оценки подходов и качества, грубо говоря в вашем подходе есть, возможно не большая, но олтичная от нуля возможность накосячить и пропустить в PHP код что-то нехорошее, просто потому что вы его генерите.

                            Опять же, если вы все равно хотите что такое делать — это должна быть консольная утилита, генератор кода.


  1. voidMan
    07.08.2015 01:48
    +5

    Вот из-за таких вот постов, PHP считают языком второго сорта ;).
    Рад за автора что ему удалось решить свою проблему, однако такое действительно не стоит показывать, не дай Бог новички начнут думать, что так надо делать.


    1. Temirkhan
      07.08.2015 02:15
      +1

      Потому и существуют комментарии.


      1. jrip
        07.08.2015 11:15
        +1

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


  1. qrasik
    13.09.2015 18:48
    +1

    Уважаемый shasoft, сама идея шаблонизации SQL интересна и востребована, но вот именно такое решение несколько не очень. Я бы посоветовал вам просмотреть презентацию на эту тему.

    По поводу же PDO, я советую не брать близко к сердцу. К сожалению, он умеет только подставлять параметры и собственно на этом все. Типизованных placeholder-ов от него не дождешься не говоря уже про условные :-)

    p.s.может кто подскажет класс PHP, которому можно скормить код на PHP и получить его красиво отформатированным
    Посмотрите на PHP-FMT из sublime text 3


    1. shasoft
      13.09.2015 20:24

      За класс форматирования PHP спасибо.

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


      1. qrasik
        13.09.2015 20:49

        На первый взгляд разницы в самом деле нет.

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

        Ну и реализация… Если вы заметили, основная претензия к вам была именно из-за реализации. За реализацию бить нынче принято ибо модно. Особенно если не сразу понятны плюсы. Напирать надо было именно на «плюшки и конфеты» для разработчика. Идея то богатая. Сам из-за типизованных placeholder-ов не побежал за всеми в сторону orm. :-)


        1. shasoft
          15.09.2015 09:45

          Что мешает сделать консоль для проверки выражений?

          Я реализовал все описанное в статье, хотя были некоторые изменения, которые пришли в голову при реализации. Вообще планирую сделать страницу для проверки и демонстрации работы. Т.е. вводим запрос с макросами — он показывается в виде PHP. Однако в связи с тем, что интереса у народа сама идея не вызвала — решил сначала испытать в «боевых» условиях, может еще какие изменения будут. Тогда уже и сделаю консоль для проверки.


          1. qrasik
            15.09.2015 10:09

            Наверное повторюсь, но все же…

            Например шаблонизатор twig или тот же смарти, собирают шаблон в PHP код. Но само по себе, ценности это не имеет. Одно дело когда этот самый код сидит в некоем кеше и глаза не мозолит и другое дело когда он превращается в еще одну сущность за которой придется следить.

            P.S.
            Только-только отгремела священная война за разделение HTML тегов от PHP кода, а тут вы предлагаете автоматическую фабрику по фаршированию PHP но уже SQL-ем. ;-)


            1. shasoft
              16.09.2015 10:50

              Так фактически код и находится в КЕШ-е — в файловом. Все что нужно, это при обновлении сайта удалить из КЕШ-а этот код, вот и все слежение.