Pull to refresh
23
0
Назар Мокринский @nazarpc

Open Source enthusiast

Send message

Я бы отформатировал иначе, но в целом достаточно приятный вариант, согласен.


$write_connection->q(
    'INSERT INTO `table_name`
        (`foo`, `bar`, `baz`)
    VALUES
        (?, ?, ?)',
    ['foo_value', 'bar_value', 'baz_value']
);

Компонент от ауры я могу одной строкой подключить в свой проект, Ваш — не знаю как долго и имеет ли смысл выдирать его

Здесь в зависимость модуля одна строчка в meta.json (пример), и ещё одна если ещё нет зависимости от Composer (пример).


The query objects do not execute queries against a database. When you are done building the query, you will need to pass it to a database connection of your choice.

Не вижу никаких проблем кроме того, что именованные подготовленные выражения не поддерживаются.


Пример из документации:


// a PDO connection
$pdo = new PDO(...);

// prepare the statment
$sth = $pdo->prepare($select->getStatement());

// bind the values and execute
$sth->execute($select->getBindValues());

// get the results back as an associative array
$result = $sth->fetch(PDO::FETCH_ASSOC);

А здесь получится следующее:


$db = \cs\DB::instance()->module('System')->db('users');

$result = $db->qf($select->getStatement(), $select->getBindValues());

Я просто выставлю оба примера рядом чтобы было наглядно видно что короче, а что нет. Хотя я бы сказал, что оно соразмерно практически 1 в 1, поскольку, повторюсь, в подобных ситуациях query builder прямо дублирует SQL синтаксис.


$red = Fruit::select('name', 'colour', 'calories')->where('calories', '<', 150)->where('colour', 'red')->get();
$red = $dbh->qfa('SELECT name, colour, calories FROM fruit WHERE calories < ? AND colour = ?', 150, 'red');

Если вы пойдете дальше и будете использовать ActiveRecord с описанием моделей, в том числе phpdoc «property type $column» у вас будет просто замечательный автокомплит.

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

Как минимум, QueryBuilder в паре с ActiveRecord (laravel5, yii2) позволяют куда удобней работать с БД чем писать запросы вручную.

Для этого есть cs\CRUD и cs\CRUD_helpers, которые позволяют вообще обойтись и без SQL и без конструирования запросов. Всё сводится к декларативному описанию структуры таблиц и вызовом одного метода.
Пример в системном классе: структура, чтение, обновление данных.


Кроме того, ООП-синтаксис запросов (builder/ar) куда приятней plain-text'a в sql, а при адекватно настроенной IDE позволяет существенно упростить их написание (при помощи autocomplete). Посмотрите так же на длину и лаконичность запроса через builder/ar либо классический sql.

В том то и дело, что в примерах Query builder не короче, а из автодополнения вы получите только методы. В то время как в SQL вы получите красивую подсветку синтаксиса, автодополнение SQL команд, в том числе тех, о которых Query builder не знает, а так же полей из реальных таблиц. Query builder ну никак здесь не выигрывает у SQL, даже близко. Это не учитывая того, что SQL может читать любой, а в синтаксисе конкретного Query builder-а и его ограничениях нужно ещё разбираться.


В querybuilder'e вы не увидете таких извращений, а запросы select->fetch далаются в 1 строку и вполне читаемы.

А здесь, простите, сколько строчек? Чем вам не читаем любезно раскрашенный IDE SQL?


Такой конструкцией вы усложняете разработку под вашу cms другим людям, ведь это вовсе не классический вид pdo_prepared запросов и не querybuilder (стороннему разработчику придется тратить время и нервы чтобы приноровиться к такому стилю).

Хорошо, давайте сравним. Для примера беру сниппет из документации:


$sth = $dbh->prepare('SELECT name, colour, calories
    FROM fruit
    WHERE calories < ? AND colour = ?');
$sth->execute(array(150, 'red'));
$red = $sth->fetchAll();
$sth->execute(array(175, 'yellow'));
$yellow = $sth->fetchAll();

А теперь CleverStyle Framework (полная аналогия):


$query = 'SELECT name, colour, calories
    FROM fruit
    WHERE calories < ? AND colour = ?';
$red = $dbh->qfa($query, array(150, 'red'));
$yellow = $dbh->qfa($query, array(175, 'yellow'));

Вы правда утверждаете, что на это нужно много нервов?

Дело в том, что абстрагирования там всего ничего, большинство запросов на 100% повторяют синтаксис SQL запросов. Как по мне, так проще сделать несколько простеньких конвертаций как в примерах, и дальше писать SQL.


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


Под капотом всё очень прямолинейно, но потенциально есть возможность объединять запросы и делать из нескольких запросов один запрос в БД, просто с этим есть некоторые сложности (mysqli_multi_query() ведет себя не совсем так, как хотелось бы, PostgreSQL не желает делать множественные запросы с серверными подготовленными выражениями, SQLite, по-моему, вообще множественные запросы не поддерживает).

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

Полагаю, имеется ввиду что PHP перекомпилировать для подключения модулей совсем не нужно, их можно подключать и отключать по необходимости.
Более того, mb_string достаточно популярный модуль в любом случае.

Как раз сегодня видел шутку в тему


Есть события, с их помощью вы можете делать любые алиасы, которые вам только заблагорассудится.


Если не указывать язык в URL — будут взяты к сведению заголовки запроса. К примеру, Accept-Language если это браузер, так же поддерживаются заголовки, которые отправляет Facebook, когда встраивает preview в ленте (ему почему-то не хотелось стандартный Accept-Language применять).
Далее если и заголовков нет никаких — используется язык, сконфигурированный в настройках системы.
Так же если пользователь зарегистрирован и в профиле явно указан язык — он так же будет иметь бОльший вес, чем язык в заголовках и чем в настройках системы.


Но это уже не совсем к маршрутизации относится, это многоязычность, с которой тоже много все интересного происходит)

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

Естественно, я имею ввиду что оно полностью ложится на конвенцию. Если понять несложный принцип, то всё сразу становится на свои места, не нужно изучать код и конфигурацию для того чтобы с почти 100% вероятностью узнать что будет происходить и где это искать.

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


Приведу команду к вашему формату:


/usr/bin/php /path/to/bin/console cache:clear //symfony
/usr/bin/php /path/to/bin/cli clean_cache:System/optimization //cleverstyle framework

Не вижу какой-то фундаментальной разницы. В то же время, в Symfony не так очевидно, какой код отвечает за команду, а в CleverStyle Framework я не смотря в код знаю, что за запрос отвечает статический метод cs\modules\System\cli\Controller::optimization_clean_cache().


Почему именно так? Мне это показалось весьма логичным и удобным (как в HTTP запросах: метод и цель), а в контексте остальной маршрутизации даже в некотором смысле очевидно. Вы выполняете операцию (clean_cache) над сущностью или в контексте определённого пути (System/optimization).


Если бы мы создавали статьи из командной строки, то у нас были бы методы get, post, delete и сущность Articles. То есть в формате Symfony получается articles:get, а здесь get:Articles, хотя суть та же.

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


./cli clean_cache:System/optimization

Где clean_cache выступает в роли аналога HTTP метода, а System/optimization в роли аналога пути страницы.


Параметры командной строки превращаются в унифицированные параметры, доступные через привычный $Request->query(), аналогично параметрам после ? в URL:


./cli get:Module_name bool_param text_param="Some value"

./cli help:System выведет справку с доступными командами и форматом вызова (аналогично вызову ./cli без каких либо параметров).


Как в вашей системе получить ссылку на комментарий к созданному мной элементу каталога?

Увы, фреймворк ничего такого из коробки не предоставляет. Не знаю почему, но мне как-то даже не было нужно такое никогда. Можете уточнить для чего конкретно такое может быть нужно, что нельзя вручную сформировать ссылку? Интересно узнать сценарии использования.

1) что такое "ручное внедрение зависимостей"? Фабрики? Что там нечитабельного?

Я имел ввиду подобное:


$obj = new SuperClass(new Dependency1(new Dependency2()), new Dependency3());

Когда вы создаете все зависимости явно и явно передаете их в конструктор в самом начале, то есть без использования контейнеров и чего либо ещё.


какое отношение это вообще к Dependency Injection имеет?

Достаточно прямое, позволяет вместо кода выше писать подобное:


$obj = $locator->get('SuperService');

А под капотом будут внедрены нужные зависимости.


2) внедрение зависимостей, которые на самом деле не используются означают что у модуля низкая внутренняя связанность и его нужно разделить на отдельные модули. А еще есть lazy инициализация с проксями. Опять же вас никто не заставляет писать свой контейнер — юзаем готовые которые все это умеют и учитывают.

Согласен, но всегда есть граница здравого смысла. Городить прокси просто для того чтобы следовать паттерну, при том что сервис никогда не будет изменен? По-моему овчинка выделки не стоит. По крайней мере я предпочту повышение связности до определённой границы.


неверно. Основная суть что у вас есть один объект в системе, чья задача разруливание зависимостей. Никакой чуши про интерфейсы => реализации.

Это грубо, имеется ввиду что зависимости не черной магией разруливаются, а по определённым образом составленным правилам.


Толк в том что она есть и мы не пишем кучу фабрик. Они генерятся сами. А если вы посмотрите на популярные контейнеры — там в принципе в 80% случаев вообще конфигурацию описывать не нужно.

То есть решаем проблему, которой изначально не было? Сейчас во фреймворке вообще нет фабрик, они просто не нужны. И есть лишь один метод во всём фреймворке, который занимается созданием подобных объектов. autowiring это круто, но это работает не в 100% случаев, а в данном случае решает то, что не является проблемой изначально.


ленивая загрузка это не решение проблемы а ее симптом. Ну и про конфигурацию — тупой autowiring делается весьма просто.

Опять таки да, но проблемы может вообще не быть при альтернативном подходе.


Я пытаюсь донести, что при наличии единственной реализации чего либо в принципе, не вижу смысла в интерфейсах, DI, тащить что-то калибра PHP-DI (который, несомненно, крут, но по объему как 15% всего CleverStyle Framework) и так далее, поскольку это не имеет практического смысла. Гораздо удобнее иметь прямой вызов и автоматический вывод типов в IDE без каких-либо сторонних плагинов, да и производительность получится самая высокая. Если это можно в итоге протестировать и вместе оно хорошо работает — то почему нет?


Не хочется закончить написанием https://github.com/Herzult/SimplePHPEasyPlus либо https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition в реальной жизни.


Меня привлекает простота реализации, когда несколькими кликами можно добраться к строчкам, которые реально что-то делают, а не являются очередной абстракцией.

Это я часто слышу, попытался ответить на этот вопрос здесь

PSR поддерживается на уровне совместимости, к примеру, прослойка для PSR7, автозагрузчик аналогичен PSR4 по сути, просто есть несколько директорий с разными префиксами (cs\modules это модули в modules, а cs это системные классы в core\classes), тот же Composer можете использовать.


На счёт DI — нет и не планируется. Во фреймворке он не нужен, ибо он не компонентный и не предполагает свободной замены своих частей. А если вам нужен DI — вон сколько их готовых на GitHub, подключайте чего вам нравится, не вижу пока смысла что-то в этом плане навязывать.


На счёт паттернов — что конкретно вам не нравится? Буду благодарен если ответите конкретно с примером, а то выглядит как вопрос на собеседовании)

Я хотел, но он сломан в бенчмарке. Если кто-то исправит, с радостью сравню.
Решил сделать более полный, результаты опять слегка отличаются, но в целом Phalcon не переплюнуть по производительности
Скрытый текст


|framework          |requests per second|relative|peak memory|relative|
|-------------------|------------------:|-------:|----------:|-------:|
|phalcon-2.0        |           8,542.78|    15.0|       0.38|     1.0|
|silex-1.3          |           2,746.60|     4.8|       0.59|     1.6|
|symfony-2.7        |           1,289.42|     2.3|       1.41|     3.7|
|symfony-3.0        |             957.06|     1.7|       1.64|     4.3|
|laravel-5.2        |             570.63|     1.0|       1.98|     5.2|
|zf-2.5             |             707.26|     1.2|       1.36|     3.6|
|cleverstyle-5.15   |           2,459.84|     4.3|       0.66|     1.7|
Только что запустил на PHP 7.0.8, Ubuntu 16.10 x64
Скрытый текст


|framework          |requests per second|relative|peak memory|relative|
|-------------------|------------------:|-------:|----------:|-------:|
|phalcon-2.0        |           8,796.40|     3.2|       0.38|     1.0|
|cleverstyle-5.15   |           2,756.97|     1.0|       0.66|     1.7|

Это хорошо, но те же cookie можно сопоставить с изменяемым User-Agent при перезапуске (если они не чистятся при перезапуске, что достаточно неудобно), я бы предпочел не давать лишние биты информации в счётчик "подозрительности" пользователя. Лучше прикинуться самым обыкновенным среднестатистическим пользователем.

Как вы сопоставите разные IP с самым популярным User-Agent? Мне кажется, намного проще вычислить тех, кто во время сессии случайно меняет User-Agent, либо использует что-то вроде SeaMonkey вместо предпоследней стабильной версии Chrome или Firefox на Windows 7 или 10.


Не вижу принципиальных проблем в создании расширения, которое будет не рандомизировать, а периодически обновлять User-Agent на самый популярный в данный момент.

Information

Rating
Does not participate
Registered
Activity