Нет ничего скучнее, чем очередной фреймворк на PHP. Разве что «принципиально новая» CMS
Так зачем же я с упорством, достойным лучшего применения, шагаю по неудобным подводным камням и выставляю на
Не знаю. Как не знал в своё время Колумб, зачем он отплывает от уютных берегов Испании. Надеялся ли он найти путь в Индию? Конечно да. Но не знал точно — доплывёт ли?
Видимо и у программистов на PHP, к которым я вот уже 13 лет себя причисляю, есть такая же внутренняя потребность — выставлять свой код и зажмуривать глаза, ожидая реакции коллег.
Что вас ждет под катом?
- Открытый исходный код, лицензия LGPL
- Код, полностью совместимый с PHP 7.0-7.2
- 100% покрытие юнит-тестами
- Библиотеки, проверенные временем в реальных проектах (и только проклятая прокрастинация мешала мне опубликовать их ранее!)
Ну и, разумеется, история изобретения очередного
* вообще говоря это пока еще не фреймворк, а просто набор библиотек, фреймворком он станет чуть позже
- Фирменное наименование: Runn Me!
- Вендор: Runn
- GitHub: github.com/RunnMe
- Composer: packagist.org/packages/runn
Немного истории или Откуда взялась идея «написать еще один фреймворк?»
Да, собственно говоря, ниоткуда. Она всегда была.
Разные интересные проекты, в которых довелось поучаствовать за время программистской карьеры, клали в личную копилку стандартные решения для стандартных же задач — так у меня, как и у любого нормального программиста, скапливалась своя библиотека функций, классов и библиотечек.
Лет шесть назад руководство компании, в которой я тогда работал, поставило задачу: разработать свой собственный фреймворк. Сделать легковесный MVC-каркас, взяв только самое необходимое, добавить к нему специфичные библиотеки предметной области (поверьте — очень специфичные!) и собрать некое универсальное решение. Решение, надо отметить, получилось, но специфичность предметной области не позволила ему стать массовым — код не публиковался, продавались инсталляции на площадку клиента. А жаль. Некоторые вещи действительно опережали своё время: достаточно сказать, что пусть примитивное, но всё-таки довольно похожее подобие composer мы с командой сделали тогда совершенно самостоятельно и немного раньше, чем появился, собственно стабильный публичный composer :)
Благодаря этому опыту мне довелось изучить практически все существовавшие тогда в экосистеме PHP фреймворки. Попутно произошло еще одно событие, очередной «переход количества в качество» — я стал преподавать программирование. Сначала в одной известной онлайн-школе, потом сосредоточился на развитии своего собственного сервиса. Стал «обрастать» методиками преподавания, учебными материалами и, конечно же, студентами. В этой тусовке и возникла идея некоего «учебного фреймворка», намеренно упрощенного для понимания начинающими, но при этом позволяющего всё-таки успешно разрабатывать несложные веб-приложения в соответствии с современными стандартами и тенденциями.
Три года назад, как реализация этой идеи «учебного фреймворка», родился небольшой MVC-фреймворк под названием «T4»*. В названии нет ничего особенного, просто сокращение от «Технологический макет, версия 4». Думаю понятно, что предыдущие три версии вышли неудачными и только с четвертой попытки нам, вместе с тогдашними моими студентами, удалось создать что-то действительно интересное.
* позже я узнал, что так в третьем рейхе называлась программа по стерилизации и умерщвлению неизлечимо больных людей… конечно же сразу встал вопрос о смене названия
T4 благополучно развивался и рос, стал известным, как говорится, «в узких кругах» (очень узких), на нём был сделан ряд довольно крупных проектов, но росло и внутреннее недовольство этим решением.
В начале этого года я окончательно созрел для переформатирования накопившегося кода. Вместе с группой единомышленников, которые тоже активно использовали T4, мы приняли ряд базовых принципов построения нового фреймворка:
- Делаем его слабосвязанным набором библиотек, так, чтобы каждую либу можно было подключить и использовать отдельно.
- Стараемся сохранять здоровый минимализм там, где это возможно
- Сам каркас для веб- и консольных приложений — тоже одна из библиотек, тем самым мы избегаем монолитности.
- Стараемся не изобретать велосипеды и максимально сохраняем те подходы и тот код, которые уже зарекомендовали себя в T4.
- Отказываемся от поддержки устаревших версий PHP, пишем код под самую актуальную версию.
- Стараемся делать код максимально гибким. Если можно — вместо классов и наследования используем интерфейсы, трейты и композицию кода, оставляя пользователям фреймворка возможность заменить эталонную реализацию любого компонента своей.
- Покрываем код тестами, добиваясь 100% покрытия.
Так родился проект, который сначала назвали «Running.FM», а потом окончательно уже переименовали в «Runn Me!»
Именно его я сегодня и представляю.
Кстати, слово «runn» сконструировано искусственно: с одной стороны чтобы быть понятным всем и вызывать ассоциации с «run», с другой — чтобы не совпадало ни с одним из словарных слов. Мне вообще нравится буквосочетание «run»: я еще в RunCMS в своё время успел поучаствовать :)
В данный момент проект «Runn Me!» находится в середине пути — какие-то библиотеки уже можно применять в продакшне, какие-то в процессе переноса из старых проектов и рефакторинга, а какие-то мы еще не начали переносить.
В начале было Core
Уместить в один пост рассказ о каждой библиотеке проекта «Runn Me!» невозможно: их много, хочется подробно поведать о каждой, ну и к тому же это живой проект, в котором всё изменяется к лучшему буквально ежедневно :)
Поэтому я решил разбить рассказ о проекте на несколько постов. В сегодняшнем пойдет речь о базовой библиотеке, которая называется «Core».
- Назначение: реализация базовых классов фреймворка
- GitHub: github.com/RunnMe/Core
- Composer: github.com/RunnMe/Core
- Установка: командой composer require runn/core
- Версии: как и в любой другой библиотеке проекта «Runn Me!», поддерживаются три версии, соответствующие предыдущей, актуальной и будущей версиям PHP:
7.0.*, 7.1.* и 7.2.*
Массив? Объект? Или всё вместе?
Благодатная идея объекта, состоящего из произвольных свойств, которые можно создавать и удалять «на лету», как элементы в массиве, приходит в голову каждому программисту на PHP. И каждый второй эту идею реализует. Не стали исключением и я с моей командой: ваше знакомство с библиотекой Runn\Core я хочу начать с рассказа о концепции ObjectAsArray.
Делай раз: определи интерфейс, который позволит тебе кастить твой объект к массиву и обратно: массив превращать в объект, не забыв в этом интерфейсе пару полезных методов (merge() для слияния объекта с внешними данными и рекурсивный кастинг к массиву)
github.com/RunnMe/Core/blob/master/src/Core/ArrayCastingInterface.php
namespace Runn\Core;
interface ArrayCastingInterface
{
public function fromArray(iterable $data);
public function merge(iterable $data);
public function toArray(): array;
public function toArrayRecursive(): array;
}
Делай два: собери мегаинтерфейс, который опишет поведение будущего объекта-как-массива максимально полно, заложив туда максимум полезного: сериализацию, итерацию, подсчет числа элементов, получение списка ключей и значений, поиск элемента в этом «объекте-массиве».
github.com/RunnMe/Core/blob/master/src/Core/ObjectAsArrayInterface.php
namespace Runn\Core;
interface ObjectAsArrayInterface
extends \ArrayAccess, \Countable, \Iterator, ArrayCastingInterface, HasInnerCastingInterface, \Serializable, \JsonSerializable
{
...
}
Делай три: напиши трейт, который станет эталонной реализацией мегаинтерфейса. См. github.com/RunnMe/Core/blob/master/src/Core/ObjectAsArrayTrait.php
В результате мы получили полноценную реализацию «объекта-как-массива». Использование интерфейса ObjectAsArrayInterface и трейта ObjectAsArrayTrait позволяет делать нам примерно так:
class someObjAsArray implements \Runn\Core\ObjectAsArrayInterface
{
use \Runn\Core\ObjectAsArrayTrait;
}
$obj = (new someObjAsArray)->fromArray([1 => 'foo', 2 => 'bar']);
$obj[] = 'baz';
$obj[4] = 'bla';
assert(4 === count($obj));
assert([1 => 'foo', 2 => 'bar', 3 => 'baz', 4 => 'bla'] === $obj->values());
foreach ($obj as $key => $val) {
// ...
}
assert('{"1":"foo","2":"bar","3":"baz","4":"bla"}' === json_encode($obj));
Кроме базовых возможностей в ObjectAsArrayTrait реализована возможность перехвата присваивания и чтения «элементов объекта-массива» с помощью кастомных сеттеров-геттеров, этакий задел для будущих классов:
class customObjAsArray implements \Runn\Core\ObjectAsArrayInterface
{
use \Runn\Core\ObjectAsArrayTrait;
protected function getFoo()
{
return 42;
}
protected function setBar($value)
{
echo $value;
}
}
$obj = new customObjAsArray;
assert(42 === $obj['foo']);
$obj['bar'] = 13; // выводит 13, присваивания не происходит
Важно: null is set!
Да, элемент объекта-массива, чье значение null, считается определенным.
Это решение вызвало немало споров, но всё-таки было принято. Поверьте, на то есть серьезные причины, о которых будет рассказано дальше, в повествовании о библиотеке ORM:
class someObjAsArray implements \Runn\Core\ObjectAsArrayInterface
{
use \Runn\Core\ObjectAsArrayTrait;
}
$obj = new someObjAsArray;
assert(false === isset($obj['foo']));
assert(null === $obj['foo']);
$obj['foo'] = null;
assert(true === isset($obj['foo']));
assert(null === $obj['foo']);
И зачем это всё?
Ну как же! Всё, о чем я рассказывал выше — это только начало. От интерфейса \Runn\Core\ObjectAsArrayInterface наследуются другие интерфейсы и имплементируют классы, дающие жизнь двум «веткам классов»: Collection и Std.
Коллекции
Коллекции в Runn Me! — это объекты-массивы, снабженные большим количеством дополнительных полезных методов:
namespace Runn\Core;
interface CollectionInterface
extends ObjectAsArrayInterface
{
public function add($value);
public function prepend($value);
public function append($value);
public function slice(int $offset, int $length = null);
public function first();
public function last();
public function existsElementByAttributes(iterable $attributes);
public function findAllByAttributes(iterable $attributes);
public function findByAttributes(iterable $attributes);
public function asort();
public function ksort();
public function uasort(callable $callback);
public function uksort(callable $callback);
public function natsort();
public function natcasesort();
public function sort(callable $callback);
public function reverse();
public function map(callable $callback);
public function filter(callable $callback);
public function reduce($start, callable $callback);
public function collect($what);
public function group($by);
public function __call(string $method, array $params = []);
}
Разумеется, сразу же в распоряжении разработчика имеется как эталонная реализация этого интерфейса в виде трейта CollectionTrait, так и готовый к использованию (или наследованию) класс \Runn\Core\Collection, добавляющий к реализации методов из трейта удобный конструктор.
С использованием коллекций становится возможным писать примерно такой код:
$collection = new Collection([1 => 'foo', 2 => 'bar', 3 => 'baz']);
$collection->prepend('bla');
$collection
->reverse()
->map(function ($x) {
return $x . '!';
})
->group(function ($x) {
return substr($x, 0, 1);
});
/*
получится что-то вроде
[
'b' => new Collection([0 => 'baz!', 1 => 'bar!', 2 => 'bla!']),
'f' => new Collection([0 => 'foo!'),
),
]
*/
Что важно знать о коллекциях?
- Большинство методов не изменяют исходную коллекцию, а возвращают новую.
- Большинство методов не гарантирует сохранение ключей элементов.
- Наилучшее применение коллекций — хранение в них множеств однородных или подобных объектов.
Типизированные коллекции
Кроме «обычных» коллекций в библиотеку Runn\Core включен интересный инструмент, позволяющий полностью контролировать объекты, которые могут содержаться в коллекции. Это типизированные коллекции.
Всё очень и очень просто:
class UsersCollection extends \Runn\Core\TypedCollection
{
public static function getType()
{
return User::class; // тут может быть и название скалярного типа, кстати
}
}
$collection = new UsersCollection;
$collection[] = 42; // Exception: Typed collection type mismatch
$collection->prepend(new stdClass); // Exception: Typed collection type mismatch
$collection->append(new User); // Success!
Std
Вторая «ветка» кода, в чём-то противоположная коллекциям, называется «Стандартный объект». Строится он также пошагово:
Делай раз: определи интерфейс для «магии».
namespace Runn\Core;
interface StdGetSetInterface
{
public function __isset($key);
public function __unset($key);
public function __get($key);
public function __set($key, $val);
}
Делай два: добавь ему стандартную реализацию (см. github.com/RunnMe/Core/blob/master/src/Core/StdGetSetTrait.php )
Делай три: собери из «запчастей» класс, опирающийся на StdGetSetInterface с множеством дополнительных возможностей. github.com/RunnMe/Core/blob/master/src/Core/Std.php
В конце пути мы получаем с вами достаточно универсальный класс, пригодный для решения множества задач. Вот лишь несколько примеров:
$obj = new Std(['foo' => 42, 'bar' => 'bla-bla', 'baz' => [1, 2, 3]]);
assert(3 === count($obj));
assert(42 === $obj->foo);
assert(42 === $obj['foo']);
assert(Std::class == get_class($obj->baz));
assert([1, 2, 3] === $obj->baz->values());
// о, да, реализация вот этой штуки весьма монструозна:
$obj = new Std;
$obj->foo->bar = 42;
assert(Std::class === get_class($obj->foo));
assert(42 === $obj->foo->bar);
Разумеется, «умения» класса Std не исчерпываются chaining-ом, доступом к свойствам, как к элементам массива и наоборот, кастингом к самому классу. Он умеет гораздо больше: валидировать и очищать данные, отслеживать обязательные к заполнению свойства и т.д. Но об этом позже, в других статьях цикла.
А дальше?
Всё только начинается! Впереди нас ждут рассказы о:
- Мультиисключениях
- Валидаторах и санитайзерах
- О хранилищах, сериализаторах и конфигах
- О реализации Value Objects и Entities
- Об HTML и представлении форм на стороне сервера
- О собственной библиотеке DBAL, включая, конечно же, QueryBuilder!
- Библиотека ORM
- и как финал — MVC-каркас
Но это всё в будущих статьях. А пока что с праздником, товарищи! Мир, труд, код! :)
P.S. Детального плана со сроками у нас нет, как нет и желания успеть к какой-то очередной дате. Поэтому не спрашивайте «когда». По мере готовности отдельных библиотек будут выходить статьи о них.
P.P.S. С благодарностью приму сведения об ошибках или опечатках в личные сообщения.
©
КДПВ (с) Mart Virkus 2016
Картинка в заключении статьи из гуглопоиска картинок
Комментарии (226)
higgsbison
01.05.2017 16:21+3«Недовольство именно Yii, который пришлось несколько раз использовать, стало одним из поводов делать что-то своё.»
Допустим, я выбираю фреймворк, на котором реализовать проект. В чем преимущества Runn Me перед тем же Yii2?AlexLeonov
01.05.2017 16:26-5Сложно сейчас сформулировать преимущества, когда фреймворк еще в самой ранней стадии сборки. Я планировал раскрывать этот вопрос постепенно, по мере выхода статей и подготовки кода к публикации.
Если кратко, то:
— БОльшая архитектурная стройность, например у нас точно не будет никогда $this->breadcrumbs в контроллерах (!)
— Возможность замены любой части, любого компонента на другой, имеющий аналогичный интерфейс. Например вы можете целиком заменить Renderer с нативного на другой, на базе Twig. Насколько я помню, в Yii это крайне сложно. Например конфиг можно сохранять в файл, в базу или в кэш, просто подставив нужную зависимость. Даже класс приложения (контейнера служб) можно вполне заменить на свой.
— Нет тяжести поддержки старых версий, сразу ориентируемся на 7.1, поэтому нет таких вещей, как «поведения», которые в общем-то в современном PHP не нужны
— Мы сразу закладываем возможность работать с несколькими БД, включая даже возможность строить в рамках ORM relations (разумеется lazy load) между разными базами данных
ну и так далее… отличий будет очень много, я сейчас перечислил лишь несколько первых, пришедших на ум
Следите за публикациями!http2
01.05.2017 17:15-1не будет $this->breadcrumbs в контроллерах (!)
А хде?AlexLeonov
01.05.2017 17:17+1Разумеется в шаблонах :)
Зачем контроллеру вообще что-то знать о том, как будет отдан ответ пользователю? Контроллер готовит данные, представление — форматирует.http2
01.05.2017 19:37+1Контроллер готовит данные
Так контроллер будет готовить breadcrumbs или нет? :)webcote
02.05.2017 02:15+3Я считаю, что breadcrumbs вы должны готовить сами :) Это не та задача, которую фреймворк должен уметь делать «из коробки».
http2
02.05.2017 11:42-1Отлично. :)
Хде их готовить? :)AlexLeonov
02.05.2017 12:24В шаблонах страниц.
http2
02.05.2017 12:26В шаблонах делать запросы к БД для получения всех родителей?
Дичь. :)AlexLeonov
02.05.2017 12:28Не делайте. Зачем это вам?
Если модель является частью «дерева», то соответствующий метод у нее наверняка есть. А уж как он работает — делает один запрос или много — какая вам разница?
Fesor
02.05.2017 12:37В шаблонах вы делаете вывод UI компонента который будет запрашивать модель самостоятельно а уже запросы в базу будут инкапсулированы на уровне модели.
Fesor
02.05.2017 08:29Контроллер готовит данные, представление — форматирует.
Почему же его тогда называют "контроллер" а не "форматтер" или "презентер"?
AlexLeonov
02.05.2017 10:59+1Кого именно?
Вы спрашиваете, почему «контроллер называют не форматтер или презентер»? Ну наверное потому, что контроллер ничего не форматирует и не показывает ))Fesor
02.05.2017 12:37Не проснулся с утра, кусок фразы пропустил. В моем варианте выходило "контроллер форматирует". Шэйм он ми.
michael_vostrikov
01.05.2017 17:29-2поэтому нет таких вещей, как «поведения», которые в общем-то в современном PHP не нужны
Как вы заменяете такую конструкцию?
[ 'class' => TimestampBehavior::className(), 'attributes' => [ ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'], ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'], ], 'value' => function () { return date('Y-m-d H:i:s'); }, ]
AlexLeonov
01.05.2017 17:34+4Никак. Вам нужно срочно убрать это из ORM либо в триггер, либо, что лучше, в конкретный компонент бизнес-процесса.
michael_vostrikov
01.05.2017 17:53-3Причем здесь ORM? Это же просто пример. Поведениям можно задавать параметры, в отличие от трейтов, и это удобно. С трейтами можно сделать что-то похожее, но часть инициализации будет в конструкторе, и надо следить, чтобы конфликтов названий не было. С поведениями проще и нагляднее.
AlexLeonov
01.05.2017 18:06+3Я отвечу вам подробно с примерами в одной из следующих частей. Если кратко: нет, поведений не будет. Как не будет вообще декларативного подхода и «автоматики». Это одно из самых неприятных впечатлений от Yii.
Явное лучше неявного. Код лучше массивов параметров.lavkasnov
01.05.2017 18:19+2Явное лучше неявного. Код лучше массивов параметров.
Полностью согласен. Думаю в 10 грехов программирования, если их собрать, одним из первых будет конфигурирование вместо программирования. В этот грех впадают почти всеSamDark
02.05.2017 22:20-1Думаю, через год-другой в него окунуться с головой вообще все, учитывая что происходит у нас на тему DI-контейнеров.
lavkasnov
02.05.2017 23:24Я имел в виду в широком смысле, когда программирование заменяется на сложное конфигурирование — примеров тому тьма тмущая.
Про DI — я вот только что написал свой собственный DI (просмотрел кучу всяких либ — везде перегруженное гуано, ничто не устроило). Ну да к нему конфиг в виде карты классов, конфиг простой и понятный, нет головоломных DisabledOption = false;oxidmod
02.05.2017 23:27+2ну вот что вас не устроило в Symfony Container, к примеру?
lavkasnov
03.05.2017 00:02-2Кратко про контейнер симфони:
1. использование аннотаций в рантайме — это чистое зло и никогда их использовать не буду. Что им мешает сделать генератор классов из аннотаций?
2. 769Кб папка dependency-injection — это куда и зачем? Мне места на диске не жалко, мне жалко мусор, который за собой придется таскать. Сколько файлов подгружает при пустом контенйере? Писать сложно и непонятно я умею и этогоне хотел бы от других
3. Dumper, Compiler, Extension, Loader — им действительно место в компоненте контейнера?
4. безумное колво мутных интерфейсов для одного компонента, мне нужен только 1!!! интерфейс.
В топку такие компонентыvelvetcat
03.05.2017 00:33+2Вы не разобрались.
Что им мешает сделать генератор классов из аннотаций
Ничего не мешает. В рантайме будет один класс, в котором явно все прописано.
oxidmod
03.05.2017 00:43+21. Никакие аннотации в рантайме не используются
2. В продакшене все это не грузится. Оно нужно для разработки.
3. Это и не есть часть контейнера. Это независимые компоненты.
4. Там только один и есть
Fesor
03.05.2017 01:58+2использование аннотаций в рантайме — это чистое зло и никогда их использовать не буду. Что им мешает сделать генератор классов из аннотаций?
А где там аннотации в рантайме?
769Кб папка dependency-injection — это куда и зачем?
ну откройте и посмотрите. 420 килобайт тестов (можно иногда подсматривать как пример), куча лоадеров из разных форматов в котором вам приятнее держать параметры и дефинишены сервисов, средства отладки (в том числе возможность дамнуть весь граф зависимостей в виде dot файлика). Ну и конечно же кодогенерация, компиляция контейнера и т.д. Что-то вроде возможности проверять зависимости в компайл тайм.
Сколько файлов подгружает при пустом контенйере?
- это не столь важно, есть же opcache
- в продакшене вы захотите использовать скомпилированный контейнер. На сегодняшний день симфоневый контейнер один из самых быстрых. Тогда в принципе количество подключаемых файлов будет сводиться к 3-4.
- Dumper, Compiler, Extension, Loader — им действительно место в компоненте контейнера?
- Compiler — это то что делает контейнер быстрым и безопасным. А так же позволяет делать многие приятные вещи явно.
- Dumper — это штуки которые нужны для экспорта контейнера в какой-либо формат, в основном для удобной отладки и разного рода средств автокомплита.
- Extension — это что-то типа ServiceProvider-ов. Простой способ быстро подключить ваши компоненты а не лепить один гигантский конфиг.
- Loader — ну надо же откуда-то загружать дефинишены сервисов.
безумное колво мутных интерфейсов для одного компонента, мне нужен только 1!!! интерфейс.
он у вас есть —
ContainerInterface
. Вам по сути больше ничего не нужно.lavkasnov
03.05.2017 07:43-3Да, действительно, я не стал разбираться симфони, посмотрю еще раз. Ведь такая штука: бегло просматриваю базовые классы/интерфейсы, если мне не нравится стиль кодирования, вижу избыточнное количество методов с неочевидным назначением и т.д. Как правило отвергаю такую библиотеку.
MetaDone
03.05.2017 00:05+1не смотрели https://github.com/auraphp/Aura.Di?
lavkasnov
03.05.2017 07:29Да, конечно, все из топа выдачи гугла, у всех разное качество и к каждому я находил претензии. Больше всех мне понравился компонент di из codeception
Fesor
03.05.2017 12:10компонент di из codeception
Вы про это? Это не компонент, это просто надстройка для внутренних нужд Codeception. Он лишен основной фичи — нормальной декларации зависимостей, фабрик, возможности прокидывать параметры.
lavkasnov
03.05.2017 12:41-1Модуль (термин модуль — устроит вместо компонент?) из codeception я привел в пример того, что можно сделать автоматическую иньекцию малыми средствами, без зависимостей от других модулей. То, что вы назвали (чего нет) должны быть еще каждом своем одном модулле. Все. Если сверх того — это высокоуровневый говнокод.
Fesor
03.05.2017 18:25сделать автоматическую иньекцию малыми средствами
Вот вам пример. Как вы организуете малыми средствами декорацию + автосвязывание? Или как вы организуете локальный бингдинг в пределах модуля.
Ну то есть оно и понятно что простые вещи можно делать просто. Более того, есть же pimple который хоть и не умеет автосвязывания, его можно допилить таким же стилем. Но суть компонентов типа auro.di или symfony/di — решить 95% юзкейсов возникающих в работе с контейнером зависимостей и предоставить возможность расширить поведение для решения остальных 5%.
franzose
04.05.2017 06:27Просто некоторые действуют по принципу «мне это не надо, значит это всё оверхед и куча мёртвого кода на много мегабайт» или «оно всё тормозит, а значит не нужно!!11». При этом пишут свои кривые поделки и считают себя дико умными. Соответственно либо не сталкиваются с задачами, где необходима «вся мощь», либо вообще не подозревают, что так можно, т.е. просто не в курсе, что такое DI, контейнеры, вот это всё...
Hellek
04.05.2017 20:01Можете, пожалуйста, пояснить, что значит?
программирование заменяется на сложное конфигурирование
Буду очень благодарен, если можете привести простой пример кодом.Fesor
04.05.2017 20:13+2Это когда вместо нескольких строк кода десятки строк XML. Погуглите по запросу code over configuration. Очень больная тема была во времена популярности grunt-ов всяких.
Как пример — опять же настройка пайплайна сборки билдов, сборки фронтэнда (grunt vs gulp, это сейчас вэбпаки всякие), например в assetic (интересно кто-то еще им пользуется?).
С DI контейнерами так же есть нюансы. Описывать зависимости в yaml или xml крайне неудобно.
lavkasnov
04.05.2017 22:05Даже не знаю как продемонстрировать кодом — ведь конфигурация это как раз отсутствие кода, только данные. Без конфигов не обойтись и выходов может быть несколько:
— callback
— event
— перекрытие в наследнике метода
— возможность запросить и получить список не из жестко привязанного источника
Сами же можете придумать, как разрулить
AlexLeonov
04.05.2017 23:30Я могу привести пример, если позволите.
Плохо:
$rules = [ "deadline" => "required|numeric|after_timestamp:".$date ];
Хорошо:
if ( empty($this->deadline) ) { yield new Exception('Empty column "deadline"'); } if ( (new DateTime($this->deadline)) < (new DateTime($date)) ) { yield new Exception('Invalid column "deadline"'); } return true;
Код условный, писал на калькуляторе.franzose
05.05.2017 01:31«Плохо» смахивает на валидацию в Laravel)
AlexLeonov
05.05.2017 09:57Она и есть.
Ровно то, против чего я всегда выступал — использование конфигов и специфичных DSL вместо кода.
Пишите код, коллеги.
michael_vostrikov
05.05.2017 07:13- Второй код вполне может выполняться на основе первого.
- Копипастить второй код в разные функции валидации плохо. Значит надо как-то обозначать, какие валидации из всех возможных нужны в этом объекте.
oxidmod
05.05.2017 08:19+2Просто нужно перестать использовать примитивы и валидировать их на каждом шагу. Используйте объекты-значения и валидируйте данные в конструкторе такого объекта.
ghost404
05.05.2017 09:56Правила валидации можно вынести в отдельный класс и реиспользовать столько сколько нужно. Такие валидаторы можно группировать из низкоуровневых правил в логические группы. Для этого очень хорошо подходит шаблон проектирования Спецификация.
AlexLeonov
05.05.2017 09:58Кто вам сказал про копи-паст? Напишите свой валидатор, инкапсулируйте в него все правила валидации и используйте. И никакого копи-паста.
Fesor
05.05.2017 12:41+1Хорошо:
плохо потому что не скейлится. Нельзя делать композицию таких правил. Более того, невалидные данные — не исключительная ситуация. Во всяком случае с точки зрения компонента валидации. Для клиента, людей которые должны использовать ошибки валидации, важно не то что у них что-то не так а важно "что не так".
AlexLeonov
05.05.2017 13:36-2Более того, невалидные данные — не исключительная ситуация
Слышу это уже не в первый раз. И никогда нет внятного обоснования — почему. Скорее всего вы где-то прочли эту фразу, запомнили и повторяете.
Вы не обратили внимание разве на yield вместо throw перед тем, как повторять эту мантру? Условный валидатор генерирует последовательность ошибок валидации. И лишь вышестоящий код решает — (мульти-)исключение это или нет.
Чтобы ему было проще, мы упаковываем ошибки в подходящий класс, реализующий Throwable. Захочет — бросит первую же ошибку, не захочет — соберет их все в мультиисключение и бросит все сразу, не захочет — обработает любым другим подходящим образом.
Нельзя делать композицию таких правил
Оу, как раз при таком подходе композиция элементарна. Что может быть проще, чем последовательно вызвать два-три-N генераторов?Fesor
05.05.2017 16:02+1И никогда нет внятного обоснования — почему.
У нас есть требуемое поведение — надо проверить валидны данные или нет, и если не валидны — где ошибки. Эта информация нужна клиенту что бы поправить данные.
Соответсвенно с точки зрения объекта, который производит валидацию, его задача на самом деле не убедиться что данные валидны, а собрать ошибки. И данные будут считаться валидными только если ошибок нет, но это решение будет принимать уже пользователь валидатора.
К примеру у меня был интересный случай. Есть запрос от пользователя который включает в себя какие-то данные по стоку (грубо говоря остаток на складе). Эти данные формируются на клиенте исходя из последних записанных результатов и того что вводит пользователь.
Собственно приходят данные, я кидаю их в валидатор. Валидатор выдает список ошибок. Если список ошибок пустой — все хорошо, идем дальше. Если нет — проверяю где ошибки. Если это ошибки в данных по стоку и именно определенный тип ошибок, я формирую отдельный вид репортов по этой ситуации и записываю эти данные в базу. Причем для различных комбинаций ошибок нужно формировать различные репорты. И с таким подходом можно было читать код как просто описание бизнес логики.
В этом случае "ошибки" это такие же данные как и все остальные.
Чем же исключения отличаются от "ошибок". Вопервых исключения это внутренний механизм языка. Их нужно использовать если вам важно:
- обеспечить всплытие ошибок по стэку вызовов
- сохранить точку где произошла ошибка (стэк трэйс)
- неявный выход из функции (что есть сайд эффект)
Нам же нужен объект который:
- хранит информацию о том что не так с данными
- хранит путь в массиве данных (где именно что-то не так)
- желательно хранить список таких ошибок с сохранением иерархии. То есть дерево ошибок.
Почему PHP разработчики так любят исключения? Потому что модель выполнения позволяет делать любой трэш и все всеравно будет хорошо. Незахэндленные исключения за них обработают фреймворки или просто дефолтный обработчик ошибок PHP.
Если есть возможность избежать появления сайд эффектов, нужно этой возможностью пользоваться. Это очень сильно уменьшает сложность кода на всех уровнях.
Захочет — бросит первую же ошибку, не захочет — соберет их все в мультиисключение и бросит все сразу
продемонстрируйте.
Оу, как раз при таком подходе композиция элементарна. Что может быть проще, чем последовательно вызвать два-три-N генераторов?
приведите пример как вы будете делать что-то подобное:
use Symfony\Component\Validator\Constraints\{ NotBlank, Collection, Type, Optional }; use App\Catalog\Constraints\{AvailableProduct}; $productUpdateRules = new Collection([ [ 'name' => [new NotBlank(), new Type('string')], 'quantity' => new AvailableProduct(), 'description' => new Optional([new NotBlank(), new Type('string')]), ] ]);
что-то типа такого. А потом сравним по различным параметрам различные подходы.
AlexLeonov
05.05.2017 17:40продемонстрируйте
См. следующую статью: https://habrahabr.ru/post/328080/
Я уважаю ваше мнение и не буду с вами спорить. Но для себя реализовал другой подход и с удовольствием им пользуюсь.
Попробуйте понять и альтернативную точку зрения. В ней нет ничего ужасного.
michael_vostrikov
01.05.2017 18:32+1В Питоне с этим переборщили, и используют всякие конвенции для private переменных. Причем в результате этот принцип нарушается, так как конвенции это неявная вещь, которая не прописана в коде. Так что везде нужна мера, и основная мера это удобство и понятность. Потому я и спросил, что предлагается взамен.
AlexLeonov
01.05.2017 18:40Предлагается писать код. Если вы хотите присвоить значение свойству, где-то должно быть написано $obj->prop = 'value'
Я не согласен с мифическим «удобством» в качестве меры качества кода. Код должен быть однозначным, читаемым, понятным, целесообразным, минимально необходимым и так далее. Но удобным? Нет, не думаю.michael_vostrikov
02.05.2017 09:10удобный — делающий что-либо приятным, легким в осуществлении чего-либо; уместный, подходящий для чего-либо
Применительно к коду "что-либо" это его создание и внесение изменений. Никаких мифов тут нет. А то, что вы написали относится к чтению кода. "Удобно читать" действительно не очень понятное словосочетание.
Fesor
02.05.2017 09:33«удобством» в качестве меры качества кода
Когда речь идет о конвенции на уровне языка, это нормально так как все просто знают об этом. Это не какая-то выдумка на уровне фреймворка. Правда тут есть маленький нюанс — этот конвеншн никак не влияет на поведение. Это только для того что бы дать понять разработчику мол "вот эта штука — деталь реализации, а не часть публичного интерфейса".
michael_vostrikov
01.05.2017 20:50+2Пока что у вас в коде магии еще больше чем в Yii. get-, set-, validate-, sanitize- методы, которые объявляются в объекте, а вызываются магически при установке свойства. "Стандартный" класс объектов, который имеет и подсчет количества и сериализацию в JSON и много чего еще. Куча трейтов разной вложенности, которые мешают друг другу методами, которые все идут в результирующий объект. Какая-то магия с debug_backtrace при получении свойства. Как-то слишком много неявной автоматики.
fukkit
01.05.2017 22:24+1Иногда массивы параметров, обрабатываемые стандартным для фреймворка документированным кодом, гораздо лучше, чем 15 вариантов «явного кода», написанного 15ю разными разработчиками, в соответствии с 15ю разными жизненными философиями и стадиями ООП головного мозга.
Касаемо сложных систем, как только выизобрететенайдёте свой DI фреймворк и качественно выделите Composition root системы, не станет ровно никакой разницы, кодом вы инициализируете свои конструкторы или набором конфигурирующих параметров.AlexLeonov
01.05.2017 22:25+1DI — это просто один из вариантов композиции компонентов приложения. Фреймворк должен предоставить вам способ это сделать, конечно же. Но я не считаю, что DI — панацея, которая делает ваш код плохим.
Код плохим делает нежелание писать код.AlexLeonov
01.05.2017 22:34+1Чорт, читать как «не считаю, что DI — панацея, которая делает код хорошим».
Я буду 10 раз перечитывать комментарий перед отправкой!
http2
02.05.2017 11:49+1Код лучше массивов параметров.
Нужно просто понимать, что обобщать, а что не обобщать. :)
michael_vostrikov
02.05.2017 06:38+1Жесть какая) А аргументы будут? У нас же тут обсуждение, разве нет?
Я не предлагаю все трейты заменить на поведения. Но есть моменты, где поведения удобнее. Да, не в последнюю очередь это связано с архитектурой Yii, со всеми ее недостатками. Потому я и спросил, как аналогичный результат будет достигаться с учетом архитектуры этого фреймворка. И да, ответ "будет кодом, покажу потом" меня не устраивает. Потому что кодом можно написать по-разному, и объявление массива это тоже код.
AlexLeonov
02.05.2017 11:02Но есть моменты, где поведения удобнее
«Удобство» в данном конкретном случае не есть критерий качества кода.
ответ «будет кодом, покажу потом» меня не устраивает
Я вам уже неоднократно ответил.
Результат, аналогичный показанному вами поведению, достигается строчкой
$model->updated = date('Y-m-d H:i:s')
где-то в коде соответствующего процесса.michael_vostrikov
02.05.2017 11:58+1Я не рассматривал какой-то конкретный случай. Удобство создания кода с заданным поведением и внесения в него изменений это один из главных критериев разработки. За исключением каких-то специализированных случаев типа оптимизации по производительности.
где-то в коде соответствующего процесса
Вот меня и интересует, где это "где-то" будет, с учетом методов организации кода и работы с БД, и насколько этим будет проще управлять. И повторю, вопрос касается не только ORM, а вообще добавления любой функциональности, которая отличается только параметрами.
AlexLeonov
02.05.2017 12:27Вот меня и интересует, где это «где-то» будет
В коде бизнес-процесса.
Контроллер принимает запрос (request) и видит параметры окружения, запускает соответствующий процесс или цепочку процессов, собирает ответ (response).
Где-то в одном из ваших бизнес-процессов, Michael. Сложно сказать точнее.michael_vostrikov
02.05.2017 13:20+1М? Ну так метод behaviors() это тоже часть бизнес-процесса. В чем разница?
AlexLeonov
02.05.2017 13:36-2Разница в том, что поведение модели не есть часть БП.
michael_vostrikov
02.05.2017 14:12+1А чего это тогда часть? Инфраструктуры? Или стека TCP/IP?
Поведение модели это часть бизнес-логики и бизнес-процессов в этой логике. И вопрос-то не в этом, а в повторном использовании кода. В контроллерах тоже есть повторяющийся код, который можно вынести в трейты или поведение. Но ок, я понял, что примеров пока не будет.Fesor
02.05.2017 14:39+3В контроллерах тоже есть повторяющийся код, который можно вынести в трейты или поведение.
Перестаньте выносить повторяющийся код. Дублирование кода — это нормально. Особенно в контроллерах.
Когда мы говорим про DRY многие забывают про Try. Далеко не все дублирование нужно устранять. В целом мотивация этого действа весьма простая — если в коде будут баги и этот код дублируется — то надо править в нескольких местах. Однако с другой стороны, когда речь идет про такие вещи как контроллеры, которые декларируют последовательность действий, очень удобно когда весь флоу прописан последовательно и прочитать его можно без прыжков между файлами. А еще веселее становится после того, как через месяц после устранения дублирования вдруг оказывается что "некоторые штуки то оказывается были просто похожи но не являлись дублированием". И разворачивать всю эту штуку уже не так приятно. Та же история и с "поведениями" для Yii. Они конечно здорово и весело, но при определенном масштабе создают сложности для понимания кода.
Так что перед тем как устранять дублирование такими примитивными механизмами как трейты, следует задуматься:
- А не вытекла ли у меня логика случайно из какого-то объекта? Может она должна быть где-то инкапсулирована?
- А не проще ли мне вынести эту логику в какой-то отдельный объект, который будет медиатором между той и этой штукой (например между контроллером и моделью данных, или мидлвари, или еще чего).
- А как много дублирования? одно и то же в двух местах? не, пока рано что-то с этим делать. Добавлю пометку на будущее а если надо будет править буду уже думать.
michael_vostrikov
02.05.2017 15:56+1Ну вот мы подумали и решили вынести. И код везде одинаковый, за исключением какого-нибудь названия. Кстати, поведение это и есть отдельный объект) Просто композиция нестандартная.
Вы все правильно говорите, это надо различать. И хороший способ это представлять в динамике. Если я изменю тут, там надо будет синхронно менять или нет? Зависит ли это от сторонних условий? А если требования поменяются, все равно надо будет вместе менять?
Fesor
02.05.2017 17:00поведение это и есть отдельный объект) Просто композиция нестандартная.
которая делает код не кохисив в случае имплементации в Yii.
А если требования поменяются, все равно надо будет вместе менять?
И тут нужно учитывать не просто что требования могут поменяться, а просто к кому эти требования относятся. Если у вас есть идентичный функционал но он используется разными ролями, то это разные штуки и не надо их объединять пока это не будет совсем уж очевидно. То есть важно понимать зачем и как ваш код используется.
Nelin
02.05.2017 12:24+1Результат, аналогичный показанному вами поведению, достигается строчкой
$model->updated = date('Y-m-d H:i:s')
где-то в коде соответствующего процесса.
Ну или в нескольких местах… наверное…
Fesor
02.05.2017 12:40Но есть моменты, где поведения удобнее.
это удобно если ваша цель — запилить прототипчик. В долгосрочной перспективе такое неявное смешение логики (пусть и с делегированием ответственности) приведет к тому, что код перестанет явно выражать что хотел сделать автор по итогу. А это существенно увеличит время вхождения разработчиков в проект, увеличит риски для бизнеса и т.д.
michael_vostrikov
02.05.2017 13:22+1А трейты тоже перестают явно выражать что хотел сделать автор? Результат-то такой же, логика смешивается в одном объекте.
Zhuravljov
02.05.2017 03:45+2Судя по перечню, вы находитесь под впечатлением yii1. Во второй версии хлебные крошки и другая презентационная обвеска к контроллеру отношения не имеют. Также есть возможность заменить все и вся, через DI контейнер и сервис-локатор. Базовый renderer меняется на twig одной строчкой в конфиге. Работа с несколькими БД, релейшены межу разными базами (например между mysql и монгой) уже есть, и с ленивой и с жадной загрузкой, как потребуется. Про поведения стоит понимать, что это, в первую очередь, обработчики событий объекта, связанные общей логикой одних бизнес-процессов, и собранные в один класс, который можно подключить через конфиг. И это трейтами не заменишь.
SamDark
02.05.2017 22:18+1у нас точно не будет никогда $this->breadcrumbs в контроллерах (!)
В Yii они тоже не в контроллерах, если что...
Например вы можете целиком заменить Renderer с нативного на другой, на базе Twig. Насколько я помню, в Yii это крайне сложно.
Не верно. В Yii это в дизайне с самого начала. Более того, есть официальные реализации-примеры для Twig и Smarty.
— Нет тяжести поддержки старых версий, сразу ориентируемся на 7.1, поэтому нет таких вещей, как «поведения», которые в общем-то в современном PHP не нужны
7.1 никак с поведениями не связан. Альтернатива им — трейты. И то не полная. Есть плюсы и минусы.
Мы сразу закладываем возможность работать с несколькими БД, включая даже возможность строить в рамках ORM relations (разумеется lazy load) между разными базами данных
Есть в Yii и коробки.
oxidmod
01.05.2017 16:43+2Ладно еще коллекции, но какой прок от объекта, которому динамически можно свойств накидывать? Чем вам тогда простой stdClass не подошел? Вы всеравно же не знаете какие в итоге свойства у итогового объекта.
AlexLeonov
01.05.2017 16:49Во-первых это chaining и то, что я называю innerCast. Обычный stdClass не умеет выстраивать цепочки свойств на лету и превращать «промежуточные» свойства в объекты этого же класса.
Во-вторых управляемость. В базовом классе Std, например, можно задать список обязательных свойств и, если они будут отсутствовать при создании объекта — возникнет ошибка.
Далее перехват присваивания, валидация и санитация данных, которые вы передаете в свойства. Умный конструктор и умный merge(), которые умеют собирать все ошибки валидации в коллекцию ошибок.
Всё это проще сразу заложить в базовый класс. И потом расширять, при необходимости.
P.S. Можете почитать код тестов, например https://github.com/RunnMe/Core/blob/master/tests/Core/StdTest.php и https://github.com/RunnMe/Core/blob/master/tests/Core/StdGetSetValidateSanitizeTraitTest.php — там многое написано понятнее, чем я рассказываю ))oxidmod
01.05.2017 18:48+1Под chaining вы понимаете fluent interface? Если так, то место ему в билдерах да коллекциях. И тащить наследование от базового класса только ради этого — глупо.
Вы пишете что явное лучше неявного, но при этом под капотом у вас какая-то магия с innerCast.
Ваш объект нарушает SRP, сочетая в себе и фабрику объектов, и сериалайзер, и валидатор.AlexLeonov
01.05.2017 18:53-2Фабрики объектов нет. Есть кастинг. Это раз.
Валидации нет. Есть интерфейс для подключения валидаторов. Это два.
Сериалайзер? Где? Вы про реализацию стандартного интерфейса из SPL? Для этого он и нужен, чтобы его реализовать.
Следуя вашей логике и реализацию Countable можно признать нарушением принципа единой ответственности :)
AlexLeonov
01.05.2017 22:37Под chaining вы понимаете fluent interface?
Не совсем. FI легко реализуется методами, которые заканчиваются на return $this;
Я же имею в виду то, что в PHP штатными средствами легко сделать
$arr = []; $arr['foo']['bar'] = 42;
и это совершенно валидный код, но нельзя сделать:
$obj = new SomeClass; $obj->foo->bar = 42;
Мы этот недостаток в StdGetSetTrait исправляем. Коряво, но по-другому пока никак. Нужно готовить RFC для внесения в сам язык чего-то вроде нативного JSON, чтобы получить возможность создавать одной строкой объекты любой вложенности.artem90
01.05.2017 23:33А чем плох вариант $obj->getBar()->setBaz($value)?
Или же вы за полностью immutable подход? Если да, почему?
AlexLeonov
01.05.2017 23:47Или же вы за полностью immutable подход?
Нет. Не полностью. Я за такой подход там, где он оправдан. Например метод filter() в коллекции просто обязан возвращать новую коллекцию, иначе мы не сможем построить fluent interface.
А чем плох вариант $obj->getBar()->setBaz($value)?
Неплох. И даже был в изначальном варианте Std. Выпилен из текущей версии за лёгкой избыточностью, предложение по новой реализации таких сеттеров сейчас обсуждается.
Кстати, у вас в коде ошибка. Метод getBar() вернет значение свойства bar и дальше цепочка прервётся.artem90
02.05.2017 00:02Нет, нет. Ошибки нет. Метод getBar() подразумевается, что вернёт объект, который содержит setBaz().
Это может как $this, так и this->propWithSomeBazSetterInterface
Надеюсь, смог донести до Вас свою мысль.
AlexLeonov
02.05.2017 00:34Я вас услышал. Для меня это ошибка. Ваш код нелогичен, метод $obj->getBar() должен возвращать нечто, что является 'bar' в составе $obj
Поэтому принять такое предложение не считаю возможным.ghost404
02.05.2017 10:39+1А что здесь нелогичного? Классическая цепочка вызовов.
$obj
имеет свойствоbar
которое содержит объект со своействомbaz
и сеттером для него.AlexLeonov
02.05.2017 11:03$obj имеет свойство bar
Здесь нелогичность. В моем примере — не имеет. Оно создается на лету.ghost404
02.05.2017 12:01+1В этом то и проблема что свойство создается на лету.
Классы не для того предназначены.
Fesor
02.05.2017 12:41+3Неплох.
Ну разве что мы нарушили закон Деметры и помохали ручкой инкапсуляции.
http2
02.05.2017 11:56$arr['foo']['bar'] = 42;
$obj->foo->bar = 42;
Что люди не делают, лишь бы не использовать массивы. :)AlexLeonov
02.05.2017 12:28Массивы не могут быть типизированы, к сожалению.
oxidmod
03.05.2017 12:29Ну и чем тут поможет ваш аррай-лайк объект?
Вам всеравно нужно проверить, что свойство есть.
Да, вы можете указать реквайред свойства, но тчо мешает просто сделать обычный класс с передачей обязательных парамеров в конструктор?
lavkasnov
01.05.2017 18:07+2Мне очень интересна ваша разработка, бегло просмотрел исходники. Какой стиль форматирования кода у вас? Зачем так много пустых строк? Ну это мелочи. Также почему не используете Psr\Container\ContainerInterface? В PSR мало интерфейсов, чтобы ими пренебрегать. А есть ли интерфейсы, которые вы хотели, чтобы они стали общепринятым стандарттом? Мне вот точно хотелось бы расширить PSR.
Что не увидел: DI, фабрики и как рулить их конфигами. Очень интересно было бы посмотреть ваш подход к этому. Насторожил синглетон в ядре.AlexLeonov
01.05.2017 18:10-4Контейнеры будут позже. А синглтона не бойтесь, в нем нет ничего страшного, просто дана эталонная реализация.
Yeah
02.05.2017 00:02public function testDifferentInstances() { $obj1 = testClass1::instance(); $obj2 = testClass2::instance(1, -1); $this->assertNotSame($obj1, $obj2); $this->assertInstanceOf(testClass1::class, $obj1); $this->assertInstanceOf(testClass2::class, $obj2); }
Как вы тут скромно обошли проверку ->x и ->y, а ведь это самое интересное.
Вообще именно поэтому синглтон и считается анти-паттерном, что вызовы:
$obj1 = testClass1::instance(0, 0);
$obj2 = testClass2::instance(1, -1);
echo $obj2->x;
дадут нам совершенно неожиданный результат.
Мне кажется, что это нужно срочно отрефакторить и сингтон убрать.
AlexLeonov
02.05.2017 00:35-2ну вот, первый ценный коммент на статью :))
спасибо!
P.S. я действительно тут скромно обошел, думаю, как лучше сделать. но вы, разумеется, правы.
zloyusr
01.05.2017 20:40+4Благодатная идея объекта, состоящего из произвольных свойств, которые можно создавать и удалять «на лету», как элементы в массиве, приходит в голову каждому программисту на PHP.
Зачем? Пожалуйста, объясните зачем вы это делаете? Откуда в PHP эта «мода» делать из объекта массив?AlexLeonov
01.05.2017 20:46Это наоборот: из массива объект. Мы любим массивы и хотим добавить к ним методы ))
На самом деле применений дальше будет множество.zloyusr
01.05.2017 21:51+3Мы любим массивы и хотим добавить к ним методы
т.е. сделать то, что и так уже есть в языке — объекты.
ArrayAccess входит в тройку самых ужасных для меня возможнойстей PHP: сразу за магическими __get, __set
и ReflectionProperty::setAccessible().
Код с применением указанных возможностей прекрасный способ усложнить себе и другим разработчикам жизнь.AlexLeonov
01.05.2017 21:53В таком случае вы просто не любите PHP?
zloyusr
01.05.2017 21:59Я такого не говорил. Я говорил только о нескольких возможностях языка.
PHP начиная с версии 5.3 достаточно хорош, а с приходом 7.х еще лучше.
sspat
01.05.2017 21:41+11С таким уровнем магии более подходящим названием для фрейморка будет «Гарри Поттер». Вжух! И мы сделали из обьектов массив, теперь туда можно сваливать как в помойку любое количество данных создавая новые под-помойки на лету! Но через два часа программирования такими «обьектами» они превращаются в ружье и отстреливают фокуснику обе ноги.
sspat
01.05.2017 21:49+4Посмотрел что у вас там дальше будет по циклу — веселье только начинается! Вот такая цепочка наследования…
SimpleValue implements \JsonSerializable SimpleValueObject extends SimpleValue implements ValueObjectInterface class IntValue extends SimpleValueObject
чтобы реализовать Value Object который хранит одно число?
Я боюсь представить, сколько километров длинной будет цепочка наследования, когда дело дойдет до чего-то реально сложного.AlexLeonov
01.05.2017 22:30-3чтобы реализовать Value Object который хранит одно число?
Если в этом был единственный ваш вопрос, то ответ «да», именно так. Хранит в неизменном виде, позволяет создавать, валидировать при создании, не позволяет изменять ну и еще ряд полезных на практике возможностей.
А что?sspat
01.05.2017 22:34+3Класс сериализации… вы преподаете на этих примерах? Пожалуйста, скажите, что нет.
public function decode(string $data) { try { return eval('return ' . $data . ';'); } catch (\ParseError $e) { throw new DecodeException($e->getMessage(), $e->getCode(), $e); } }
AlexLeonov
01.05.2017 22:38Нет.
Это фактически заглушка, которая никогда не будет использоваться. Кроме как для зеленых тестов.
Если встретите где-то goto — сообщите сразу :)
Впрочем, как учебный пример — прекрасно. Сразу можно разобрать опасности такого подхода и показать на примере php-инъекцию.
P.S. Про библиотеку сериализации будет другая статья.sspat
01.05.2017 22:56+3Давайте дальше посмотрим, валидация…
https://github.com/RunnMe/Validation/blob/master/src/Validation/Validators/IntValidator.php
Зачем делать класс валидатор с состоянием, вынуждая создавать новый экземпляр на каждую валидацию?
Почти все валидаторы можно выкинуть включив одну директиву strict types, которую я к своему удивлению не увидел ни в одном файле вашего фреймворка, заточенного строго под PHP 7.0+ и с таймпхинтингом повсюду.
Правда очень странная ситуация, у вас есть большой опыт, преподаете, я хотел найти в коде что-то реально ценное, но вижу только сборник антипаттернов и непонятные тяжеловесные обертки над нативными возможностями языка, не добавляющие сверху ничего нового кроме дыр и хардкоженного подавления ошибок тут и там. От чего вы пытаетесь абстрагироваться таким образом? От самого PHP?AlexLeonov
01.05.2017 23:02-3Отвечаю на заданные вопросы.
Зачем делать класс валидатор с состоянием, вынуждая создавать новый экземпляр на каждую валидацию?
Состояние в данном случае — побочный эффект применения ООП и наследования. Расскажите, как сделать правильнее.
От чего вы пытаетесь абстрагироваться таким образом? От самого PHP?
Не сумел ответить. Ваш вопрос не содержит позитивного или негативного утверждения. Всё равно что спросить «а ваши родители знают, что вы гей?»Fesor
02.05.2017 02:04+4Состояние в данном случае — побочный эффект применения ООП и наследования. Расскажите, как сделать правильнее.
не использовать наследование?
В целом решение проблемы — правила валидации отдельно, валидация отдельно. В этом случае можно делать много прикольных вещей в плане композиции правил.
Fesor
02.05.2017 02:08+2Сразу можно разобрать опасности такого подхода и показать на примере php-инъекцию.
почему в ваших примерах нет ничего о IoC? Это одно из основных чему нужно учить студентов когда речь идет про архитектуру и фреймворки. Я вот студентам вот такую картинку показывал как противопоставление вот такой картине.
Студентов не с php-инъекциями надо учить бороться, а объяснять им силу инкапсуляции и полиморфизма, проблемы связанности и преимущества зацепления… структурное программирование (и немного функциональное).
http2
02.05.2017 12:08-1Если встретите где-то goto — сообщите сразу :)
А что с ним не так?AlexLeonov
02.05.2017 12:29Кто сказал «не так» кроме вас?
Просто сообщите, пожалуйста. Это же очень интересно!
TheShock
01.05.2017 22:41Но через два часа программирования такими «обьектами» они превращаются в ружье и отстреливают фокуснику обе ноги.
В ружье Чехова, которое выстрелит под конец — а именно на боевом сервере))
vladiman
01.05.2017 22:25Это уже данность такая — любую статью про РНР начинать с нескольких абзацев извинений?
AlexLeonov
01.05.2017 22:29Мне показалось, что в праздничный вечер немного лирики не помешает. Вам помешало?
joker2k1
01.05.2017 22:25-13PHP хорош тем, что ему не нужен никакой фреймворк. То, для чего он предназначен, не требует фреймворков, потому что реализуется за день, без всяких ненужных абстракций.
Теперь же php превратился в говнокод завернутый еще и в фреймоворк (который тоже часто состоит из говнокода), что только многократно все усложняет. Если в голове каша, никакой фреймворк не поможет. Поэтому и каждый берется за очередной «фреймворк», понимает что это пипец и… садится делать такой же пипец, только свой, из своей каши.
Зачем все эти абстракции над и так уже хорошо работающими внутренними механизмами PHP?
Самое печальное что теперь во все это верят и работодатели, подбирая людей под фреймворк, потом меняя фреймворк…
Все мое личное имхо, и наблюдение, рад ошибатьсяAlexLeonov
01.05.2017 22:28+2Я думаю, что вы ошибаетесь.
Фреймворк — способ решения стандартных задач стандартным путём. Работодатель стремится свести к нулю как раз время решения стандартных задач, верно полагая, что хороший фреймворк уже всё сделал за разработчиков.
Другое дело, что пока что идеального фреймворка нет. Есть пара-тройка хороших да и только.
А абстракции нужны затем, что так устроен наш, homo sapiens, способ мышления. Анализ, синтез, абстрагирование, обобщение, конкретизация…
http2
02.05.2017 12:17-9Адекватная точка зрения на хабре не приветствуется. :)
michael_vostrikov
02.05.2017 12:34+6Приветствуются адекватные доказательства своей точки зрения. Применительно к вашей точке зрения — код вашего фреймворка и проектов на нем написанных. А вам лень push на github сделать. Или стыдно, что более вероятно. Вот за бездоказательные утверждения сомнительных преимуществ вас и минусуют, как и комментатора выше, а не за саму точку зрения.
Movimento5Litri
01.05.2017 22:25Благодатная идея объекта, состоящего из произвольных свойств, которые можно создавать и удалять «на лету», как элементы в массиве, приходит в голову каждому программисту на PHP. И каждый второй эту идею реализует.
И что же это, каждый второй не может использовать конструкцию ;$result = (object) $array &
hlogeon
01.05.2017 23:10+6Оххх. И вот, опять, кто-то написал Фреймворк с ощущением, что всем не пофигу на него. Есть фреймоврки с большими сообществами, которые, как минимум могут нормально сформулировать свои конкурентные преимущества, сильные и слабые стороны. Фреймворк ради фреймворка это какая-то дичь, как по мне, если это не делалось в учебных целях(да, ваш делался и делается, но зачем каким-то широким массам показывать?). Нет у программиста качества хуже, чем изобретение велосипеда без видимых на то причин. Непонравился Yii2? Ну посмотрите Symfony, Zend, Laravel, Phalcon. НЕТ! ЗАЧЕМ? Будем городить еще кучу велосипедов.
Я уж не говорю о том, что вы НЕ поняли разницу между Value Object и Entity судя по вашему же репозиторию ValueObjects. Особенно позабавило: `$object = $entity->toValueObject();`. Очень странная и неочевидная структура внутри ваших библиотек. Объекты, интерфейсы, исключения, люди, кони, львы! Все смешалось в одном namespace.
Не поймите меня неправильно, я бы, наверное, написал бы куда более мягкий и воодушевляющий комментарий к вашему творению, если бы вы написали, так, мол и так, учебных целей для делаю свой Фреймворк, ищу помощи от сообщества, оцените, пожалуйста! Ну или что-то в таком духе. Но нет! Вы учите людей, заявляете, что это все проверено в «боевых проектах» и вообще все довольно шоколадно. Но я должен констатировать, что использование ваших подходов в крупных проектов приведет к скорому краху, поскольку у вас есть явное недопонимание тех вещей, которые вы пытаетесь использовать, либо внедрить.
Вы говорите о каком-то там архитектурном превосходстве над каким-то из популярных фреймворков, хотя допускаете в своем ядре такие вот вещи:
https://github.com/RunnMe/Core/blob/master/src/Core/Std.php
В общем, вы сам все написали в начале, я просто оставлю этот комментарий здесь и надеюсь в ответ от вас услышать ответ на вопрос: «В чем разница между Entity и ValueObject и почему код, который я привел выше — абсурд?».
Если вы с самого начала вместо бетона используете говно и палки и при этом заявляете, что ваше здание в архитектурном плане лучше, чем Empire State Building — какой реакции от сообщества Вы ждете?
Все-таки, посмотрите на ядра Symfony, Zend, Laravel, Phalcon и как они устроены, а потом ответьте себе на вопрос: «Стоит ли использовать говно и палки для строительства, когда у вас есть бесплатные пеноблоки?». Меня очень смутил тот момент, что вы учите программировать… Где Pull-request? Come Review? Come Discussion? Project roadmap? Contribution guide? Architecture notes? Да банально описание проекта, или хотя бы понятные комментарии в коде?
Еще раз прошу прощения, но не я написал эту статью.AlexLeonov
01.05.2017 23:15-5НЕТ! ЗАЧЕМ?
Это внутренняя потребность творить. Разве вам она не знакома?
вы НЕ поняли разницу между Value Object и Entity
В данной статье мы не обсуждаем библиотеку ValueObjects. Впрочем, если вам хочется — пожалуйста. Но конструктивное обсуждение будет в статье, посвященной именно ей.
если бы вы написали, так, мол и так, учебных целей для делаю свой Фреймворк, ищу помощи от сообщества, оцените, пожалуйста!
Я не ищу помощи. Я буду ей благодарен, но не ищу. Особенно я не ищу помощи от тех, кто пишет «фреймоврки», «Непонравился», «Come Review»
Еще раз прошу прощения
Вам не за что просить прощения. Вы просто высказали своё мнение. Спасибо. Любое мнение ценно.
P.S. Зачем всё время «Фреймворк» с большой буквы? Это имеет какой-то смысл?hlogeon
01.05.2017 23:46+1P.S. Зачем всё время «Фреймворк» с большой буквы? Это имеет какой-то смысл?
Т9, я сразу-же в ответе написал.
Это внутренняя потребность творить. Разве вам она не знакома?
Внутреннюю потребность творить лучше направить на что-то полезное не одному только Вам, Вы так не считаете?
Я не ищу помощи. Я буду ей благодарен, но не ищу. Особенно я не ищу помощи от тех, кто пишет «фреймоврки», «Непонравился», «Come Review»
То, что я пишу комментарии на Хабре с включенным Т9, который пока что работает совершенно непонятным для меня образом, никак не говорит обо мне, как о программисте, не так ли? Вы же тимлид, должны понимать что имеет значение, а что нет. А если для вас важно именно это, а не то, что я пытался донести в своем комментарии, то больше мне сказать нечего ;)
Я не ищу помощи. Я буду ей благодарен, но не ищу.
А какая цель публикации? Чего Вы хотели ей добиться?AlexLeonov
01.05.2017 23:53-7Внутреннюю потребность творить лучше направить на что-то полезное не одному только Вам, Вы так не считаете?
Я именно так и считаю. Иначе бы не писал это статью.
А какая цель публикации? Чего Вы хотели ей добиться?
Чистое, незамутненное творчество. Чего пытался добиться Малевич, выставляя «Черный квадрат»? Я не знаю, но предполагаю, что однажды творчество превращается в потребность. У вас не так?
То, что я пишу комментарии на Хабре с включенным Т9
Где хоть вы его откопали? У вас реально что-то вроде Siemens S45?
никак не говорит обо мне, как о программисте, не так ли?
Хрен знает. Общее впечатление составляет. Неаккуратненько (с).
больше мне сказать нечего
Хорошо.
thecoder
01.05.2017 23:30+1Буханка троллейбус jpg
:)AlexLeonov
01.05.2017 23:33+3Фабиену и Тейлору кто-то точно также говорил :)
А уж что говорили Расмусу — я не могу написать по цензурным соображениям!thecoder
02.05.2017 00:01+1Не слушайте никого, делайте что душа просит! :)
Просто не понял зачем. Зачем делать «все массивами» и т.п. Любой фреймворк имеет право на существование. Буду следить за продолжением. :) Надо только еще понять, как это все применить. Надеюсь в следующих сериях картина прояснится.
AlexLeonov
02.05.2017 00:40-1Просто не понял зачем
Вы не поняли. Не поняли зачем.
Честно — не знаю, что вам ответить. Ну ОК, постарайтесь понять :) Спросите, если что будет непонятно, хорошо?Fesor
02.05.2017 01:42+9Постарайтесь объяснить какие именно характеристики фреймворка были вам важны когда вы принимали такие решения. Это всегда интересно.
hlogeon
02.05.2017 11:39+1Ну ОК, постарайтесь понять :)
Так мы тут все это и пытаемся понять, а вы отвечаете только в духе «Захотелось». Ну, ок, но я же не пишу статью на хабре о том, что мне захотелось сегодня съесть борща. Пока что из Ваших же ответов следует, что вы делаете фреймворк ради того, чтобы сделать фреймворк. Понимаете, есть разница между «прыгнуть ради того, чтобы прыгнуть» и «прыгнуть, для того, чтобы запрыгнуть куда-то\перепрыгнуть что-то».AlexLeonov
02.05.2017 12:30Так вы не задали ни одного вопроса же!
Если бы был вопрос — был бы ответ.hlogeon
02.05.2017 12:51+3Так вы просто игнорите вопросы) Давайте напомню:
Фреймворк ради фреймворка это какая-то дичь, как по мне, если это не делалось в учебных целях(да, ваш делался и делается, но зачем каким-то широким массам показывать?
Стоит ли использовать говно и палки для строительства, когда у вас есть бесплатные пеноблоки?
Постарайтесь объяснить какие именно характеристики фреймворка были вам важны когда вы принимали такие решения. Это всегда интересно.
Просто не понял зачем. Зачем делать «все массивами» и т.п.
Мне продолжить искать вопросы, оставшиеся без ответа в ветках, которые не являются прямыми соседями этой, или этого хватит?AlexLeonov
02.05.2017 12:58-5зачем каким-то широким массам показывать?
Мне так захотелось. Не вижу причин не публиковать, ведь публикацией и обсуждением я не нанес никому ущерба, не так ли?
Стоит ли использовать говно и палки для строительства, когда у вас есть бесплатные пеноблоки?
«Ваши родители знают, что вы — гей?» Мы в детстве тоже баловались такими же «вопросами», ага ))
Постарайтесь объяснить какие именно характеристики фреймворка были вам важны когда вы принимали такие решения. Это всегда интересно.
Нет вопроса. Есть пожелание. Пожелание принимается — будет серия статей.
Просто не понял зачем. Зачем делать «все массивами» и т.п.
Нет вопроса. Есть констатация вашего непонимания.hlogeon
02.05.2017 13:01+2Вы считаете, что вопрос характеризуется исключительно знаком вопроса в конце предложения? Яснопонятно.
Ну тогда вот вам прямой вопрос, который задавлся выше:
ЗАЧЕМ?
Вы постоянно просто уходите от ответов, выкручиваетесь и юлите. Дело ваше, конечно, но конструктивную беседу таким образом не выстроить.AlexLeonov
02.05.2017 13:13-3ЗАЧЕМ?
Господь с вами, я вам уже раза три на этот вопрос ответил. Что же мне сделать, если вы прочесть ответ не можете?
Еще разик давайте, мне же не сложно: затем, что могу. Мне так захотелось. Не вижу причин не публиковать, ведь публикацией и обсуждением я не нанес никому ущерба, не так ли?hlogeon
02.05.2017 13:17+3У меня с мыслительными процессами сегодня туго, так что на всякий случай еще раз для себе уточню в последний раз, тем более, вам не сложно)
То есть мы имеем фреймворк, написанный ради того, чтобы написать фреймворк и статью, написанную ради того, чтобы написать статью. Я все правильно понял? Ничего не упустил?AlexLeonov
02.05.2017 17:27-1Неверно.
Мы имеем некий роадмап к будущему фреймворку. Сейчас я представляю лишь малую часть — базовую библиотеку, с которой этот роадмап начинается, в надежде, что вместе мы его пройдем.
Написана библиотека ради того, чтобы собрать в одном месте и в одном стиле ряд накопившихся за многие годы решения. И благодаря тому, что я могу и хочу это сделать.
Статья написана для того, чтобы показать сообществу библиотеку. И опять же благодаря тому, что захотел написать.
Если перефразировать так — то вы ничего не упустите.hlogeon
02.05.2017 17:29+1Как это коррелирует с Вашими предыдущими ответами на те же самые вопросы? Вы так гордо и высокомерно отвечали сначала одно, теперь говорите совершенно другое. Где же правда-то в итоге?
AlexLeonov
02.05.2017 17:33Не готов вам ответить на вопрос. Не думаю, что он вообще требует ответа.
Вы находите какую-то «гордость» и «высокомерие» в каких-то совершенно технических текстах. Я не умею общаться в таком ключе, извините.
Мне интереснее обсуждать код, а не те эмоции, которые у вас он вызывает.hlogeon
02.05.2017 17:36+1От обсуждение кода Вы точно так же уходите.
Отбросьте из моего поста слова «так гордо и высокомерно» и ответьте, пожалуйста, на поставленный вопрос. Так-то он имеет прямое отношение к коду. А вы опять убегаете от ответа.
Как это коррелирует с Вашими предыдущими ответами на те же самые вопросы? Вы отвечали сначала одно, теперь говорите совершенно другое. Где же правда-то в итоге?
AlexLeonov
02.05.2017 17:44-1Оставлю вас в вашем собственном мире, где «гордость», «высокомерие», «убегают от ответов», а вы рыцарь на белом коне, несущий правду. А сам останусь в скучном мире программирования ))
Однако с удовольствием отвечу вам на конкретные вопросе о коде данной библиотеки. Если я какие-то из них пропустил — не сочтите за труд повторить, пожалуйста.
Fesor
02.05.2017 13:19+4я вам уже раза три на этот вопрос ответил.
Вы сказали что вам "захотелось". Вы опубликовали. Только читателям тоже надо дать какую-то мотивацию разбираться в том что вы придумали. Вдруг у вас что-то интересное, а тем самым вы отбиваете у читателя желание разбираться. Зачем? Прост.
Fesor
02.05.2017 13:17+2Вы так и со студентами? Был задан вполне конкретный вопрос:
Зачем делать «все массивами» и т.п.
Ответили бы что мол "есть разница между массивами и коллекциями", что мол все ради type safety, что null-object-ы вместо неопределенностей… Вы же ответили "постарайтесь понять".
MetaDone
01.05.2017 23:48+1Автору следует посмотреть сюда — https://github.com/auraphp потому что
Делаем его слабосвязанным набором библиотек, так, чтобы каждую либу можно было подключить и использовать отдельно.
Стараемся сохранять здоровый минимализм там, где это возможно
Сам каркас для веб- и консольных приложений — тоже одна из библиотек, тем самым мы избегаем монолитности.
Стараемся не изобретать велосипеды и максимально сохраняем те подходы и тот код, которые уже зарекомендовали себя в T4.
Отказываемся от поддержки устаревших версий PHP, пишем код под самую актуальную версию.
Стараемся делать код максимально гибким. Если можно — вместо классов и наследования используем интерфейсы, трейты и композицию кода, оставляя пользователям фреймворка возможность заменить эталонную реализацию любого компонента своей.
Покрываем код тестами, добиваясь 100% покрытия.
все там уже есть
А чего нет — можно взять компоненты Symfony, Zend, Laravel и кучу нужных библиотекAlexLeonov
01.05.2017 23:49Автор (и авторы) внимательно смотрели туда. Однако пока что ни один коммент на хабре не заставил их заплакать и отказаться от развития своих библиотек. Наверное, в этом что-то есть?
MetaDone
01.05.2017 23:58если какую-либо задачу ваши библиотеки позволят решить быстрее и эффективнее чем существующие инструменты — то почему бы и нет
правда пока что этого особо не видно — нет примеров реальных приложений, лично я пока не могу представить где ваши творения использоватьAlexLeonov
02.05.2017 00:41-2лично я пока не могу представить
Остаётся только надеяться, что однажды сможете!
artem90
01.05.2017 23:58+1Да, возможно написать свой framework хорошо, в данном случае для Вас.
Вы узнаете много нового, от коллег по цеху и с юмором будете вспоминать этот пост через месяц другой.
Но сейчас, из представленной статьи, мне кажется, что Ваш проект не столько сырой, сколько не полностью продуман в вопросе "зачем делать ТАК" и "какие уже существуют альтернативы и готовые решения"
В одном из комментариев Вы чуть-чуть наезжаете на Yii2. Скажу честно, что у меня самого в закромах есть статья которая сравнивает Yii2 и Symfony, причём не в пользу первого.
Я исправлял её уже очень много раз и изучал все моменты, и именно поэтому статья не опубликована и не никогда не будет. Это слишком разные проекты.
Да, мне не нравится ActiveRecord, но я стараюсь найти обходные пути или просто следовать best practices, т.к. это только часть фреймворка, так же как и поведения (мне они тоже не по душе).
Попробую посоветовать Вам поработать с разными языками: Java, Python, c++. У каждого свой подход, свои плюсы, минусы, ниши. Возможно, после code review от специалистов в этих языках Вы сможете более точно понять их назначение и область применения, равно как и PHP и ниши, на которые может быть нацелен Ваш Фреймворк.
В любом случае желаю Вам успеха и профессионального роста!
P.S. Прошу прощения за возможные грамматические и орфографические ошибки — пишу с телефона, перед сном, после кружки средства от простуды =)
AlexLeonov
02.05.2017 00:39-3Не извиняйтесь за ошибки, все мы люди, все ошибаемся.
В одном из комментариев Вы чуть-чуть наезжаете на Yii2
Наезжаю? Вы шутите? Я считаю его примером анти-дизайна и анти-проектирования, и как подобный пример всегда подаю студентам.
Это настолько нелепая архитектурная конструкция, что даже не знаешь, с чего и начать его разбор :) Наверное с самого факта его существования :)DeLuxis
02.05.2017 08:17+3Напишите пожалуйста отдельную статью с подробным разбором каждого примера анти-дизайна и анти-проектирования.
Конструктивно, хотя бы штук пять.
Просто я как фанат Yii, использую его на нескольких крупных проектах, и очень хотелось бы изучить его минусы более досконально, чем те с которыми сталкивался в работе.
Fesor
02.05.2017 02:01+13Что отпугивает сразу:
- полное отсутствие вменяемой документации. Без этого можно забыть даже о каком-либо успехе.
- Ваши коллекции не скейлятся. Просто ненужные элиасы для массивов (вроде uksort) без возможности добавить новые методы, даже нет попыток как-то улучшить API на который так жалуются пользователи.
- Вы упомянули и не раз что Yii это пример анти-проектирования, однако в вашем коде практически везде поощряется расширение за счет наследования. Как по мне это самый жирный минус Yii (с остальными можно мириться) который перекочевал к вам.
- количество бесполезного кода зашкаливает. В частности в библиотеке с VO.
- компонент валидации содержит помимо валидации еще и санитайзинг. Причем это просто ненужная объектная обертка над filter_var. Она не дает никаких преимуществ, мы ни композиции правил построить толком не можем ни что-либо переопределить толком. Та же история и с валидаторами — выглядит неюзабельным.
- компонент для работы с файлами… не понятно почему File мутабелен. Опять нет четкого разделения ответственности. работа с файлами почему-то смешана с сериализацией, непонятно зачем. Забили на инкапсуляцию.
Словом вы выложили в опенсурс то что используете сами и пользуетесь только вы (если вообще используете). Работы над грамотным проектированием не производилось — на вид стихийное решение ваших прикладных проблем без оглядки и анализа существующих решений. Судя по состоянию репозиториев это что-то типа "ну мы тут с пивом собрались на выходных и решили запилить фреймворк".
Dimash2
02.05.2017 10:52-3После того как я открыл для себя Phalcon — на другие фреймворки даже не смотрю, фундаментально другой подход в компиляции сделал для меня погоду. Все фреймворки делают одно и тоже, зачем углубляться так глубоко в встроенные методы фреймворка, по сути нужен роутинг и структура для правильной организации кода. Чей фреймворк быстрее — тот я и использую со своими библиотеками.
hlogeon
02.05.2017 11:34+4на другие фреймворки даже не смотрю
А зря. У разных проектов есть разные нужды, которые разные фреймворки удовлетворяют по-разному, понимаете о чем я? Бывает очень удобно настрочить очередную безумную идею на Laravel. Бывает, нужна гибкость Zend Framework, иногда фреймворк вообще ненужен.Dimash2
02.05.2017 13:30Я понимаю о чем вы говорите, но я с вами не согалсен. Все относительно, я считаю, что большинство фреймворков имеют очень низкого качества встроенные методы и слишком универсальные.
Написать свой конструктор запросов займет — 2 дня с высокопроизводительными запросами. Например, мне нужно всего-лишь поменять названия функции $Sql->insert() на $Sql->insertInFile() и у меня будет вставка через LOAD FROM FILE csv (имею ввиду, что именно функция сама запишет массив данных в csv и загрузит его, вместо INSERT запросов), автоматическая, зачем мне фреймвокр и их непонятные 100-ые решения для CRUD
Фреймворк — это структура, если есть удобный Роутинг + Контроллер + Шаблон + Модель + Компонент — все, фреймворк можно использовать — любой.
Я выибарю Phalcon — потому что он скомпилирован как модуль к php, соотвественно не нужно комплировать php файлв фрейворка, что есть огромное преимущество для проектов, которые парятся о нагрузке.Dimash2
02.05.2017 13:33Я себе еще сделал одно упрощение, которое тоже всем рекомендую. Все мои кастомные проекты API based. Даже если front-end на php шаблонировании. Сразу отпадает много вопросов выбора и появляются огромные возможности простейшого кеширования и ускорения.
Fesor
02.05.2017 13:38+2Написать свой конструктор запросов займет
А как вы будете разделять инфраструктуру от бизнес логики? Мне на самом деле интересно было бы послушать как люди организуют свой persistence layer. Используете ли вы DAO, мэпите ли результаты выборок на объекты для операций записи (на чтение пофигу, давайте только про выполнение бизнес-транзакций). Банальный пример. Есть у вас например необходимость сделать чатик на PHP (только запись и хранение истории сообщений), как бы вы организовывали следующую логику:
- достать конверсейшен и проверить что текущий юзер имеет права на запись в этот конверсейшен
- добавить сообщение
- достать всех пользователей, у которых текущего юзера не числится в блэклисте и отправить им нотификацию о новом сообщении.
Желательно с хоть каким псевдокодом на gist. Реально интересно. Да, логика простая, но не хочу усложнять пример.
соотвественно не нужно комплировать php файлв фрейворка
А это простите зачем? Типа трюк с конкатенацией часто загружаемых файлов? Он не актуален с версии 7.0 при включенном opcache.
Dimash2
02.05.2017 14:44У меня это модель, где отдельная функция проверит связку пользователя и конверсейшена только по id через таблицу связей user_id conversation_id, после проверки — сообщение просто отпавлю в бд с данными после проверки прав. Вызываеть функции будет контроллер по очереди.
Нотификация будет происходит асинхронно отдельным скриптом а не в момент отправки сообщения, проверяя за раз много сообщений.
hlogeon
02.05.2017 13:39Вы серьезно сталкивались с проблемами производительности PHP на проектах с высокой нагрузкой? То есть вот прям именно не криворукий код, не работа с БД, не дебильные выборки и сортировки, а именно производительность PHP?
Фреймворк — это структура
Вы конечно правы, но у меня часто бывает так, что от фреймворка я беру отдельные компоненты, а вот структуру делаю свою. Ту, которая удовлетворяет потребностям проекта.
В последнее время все чаще выбор падает на отказ от фреймворка вовсе. Благо, теже компоненты symfony прекрасно работают и без самого symfony)
Но тем не менее, если завтра ко мне придут и скажут, что мне нужно сделать блоговый движок для нашей компании, то я лучше возьму и накидаю за пару часов что-то на Laravel. Если ко мне придут и скажут, что мне нужно напилить RESTFull API к существующей БД СРОЧНО, то наверное я возьму какой-нибудь Zend с Apigility и быстро решу поставленную задачу.
Конечно, мои примеры высасаны из пальца, но это просто для удобства объяснения. И да, я работаю с проектами, которые довольно сильно парятся о нагрузке. И в тех местах, где это действительно важно на уровне языка программирования — мы просто не используем PHP, вот и все. Но в 99% случаев, проблемы с производительностью кроются вовсе не в PHP и какой-то там компиляции. Тем более, что есть opCache.Dimash2
02.05.2017 13:41-1Да, мои клиенты экономят сотни долларов на серверах, котому что они лажатся занчитель позже. У классических фреймворков такой же эфект как у Wordpres — серьезрый response delay под нагрузкой (у второго и без нагрузки) из-за огромног количества фреймворковских файлов. Есть бенчмарки, можете сравнить.
— Вы абсолютно правы про криворукость запросов и именно такими их делают криворукие разработчики по документации того же YiiDimash2
02.05.2017 13:46У меня ваши поставленные задачи решены уже на одном фреймворке. Ведь эти сушности стары как мир, сколько можно под них подстраиваться, нужно ими управлять.
Кстати php 7 очень быстрый, я тестировал на миллионом цикле с матиматикой и записью в массив. Проиграл 1-2 секунды C# и NodeJs и обошел Python на 30 секунд. (Тест конечно не супер лаборатоный), но меня порадовало, для меня больше важна скорость массивов и циклов.
Ну я фанат универсальных решений и люблю php, у меня под php есть многоядерная библиотека на локальной машине так и по сети. Можно взять огромный массив и попросить обработать соседние сервера. Так что я немного больной в этом плане, потому я подгоняю все решения под «условную автоматику».hlogeon
02.05.2017 13:51У меня ваши поставленные задачи решены уже на одном фреймворке. Ведь эти сушности стары как мир, сколько можно под них подстраиваться, нужно ими управлять.
Я специально для вас сделал ремарку, что примеры высосаны из пальца. Само собой в реальности они совершенно другие, но я почему-то подумал, что вы сами сможете придумать себе пример. Если нет — дайте знать, выдам вам один из сотен «боевых» примеров.
hlogeon
02.05.2017 13:47Так разговор о фреймворке или криворукости разработчиков, не смешивайте теплое с мягким)
экономят сотни долларов на серверах
У меня есть очень большие сомнения в том, что вы когда-либо проводили тестирование вида: Ваше решение на Phalcon vs решение других ПРОФЕССИОНАЛОВ на любом другом фреймворке. Так что экономия тут — скорее ваша придумка, хотела и оправдание. Если уж говорить в таком ключе, то PHP вы вообще зря выбрали — не самый быстрый и надежный язык.Dimash2
02.05.2017 13:55https://systemsarchitectdotnet.files.wordpress.com/2013/04/php-benchmark.png
Быстро, чтобы не тратить время. Можно придиратья, но картина по дургим тестам такая же, то есть вы считаете, что такое количество запросов в секунду не на что не влияет и совершенно не помогает переживать те же DDOS, когда фильтры хостера бесполезны и вы фильтруете софтерно. Еще как.Fesor
02.05.2017 13:57Недостаток таких тестов в том, что они проводятся на hello world или на чем-то сильно примитивном.
Do not trust benchmarks you didn't fake your self.
Dimash2
02.05.2017 13:59Вы считаете, что Hello word не достаточно? Это же первый вход — а дальше ваши кривые руки
Я фанат сосбтвенного кода, потому для меня это важно, я просто не использую много встроенных методов.
И вот вы все программисты и я не могу понять, почему вам не понятна разница самого факта, что Symfony — php, а Phalcon — C, то ест ьвы реально сравниваете доставку сервером php и скомпилированого C?hlogeon
02.05.2017 14:09+1Разговор был об экономии сотен нефти конечному заказчику. А из того, что вы скинули никак не следует то, что это позволяет хоть как-то экономить. Еще раз напомню про opcache. Более того, выше мы вроде как уже обсудили, что PHP редко бывает самым узким местом приложения.
Dimash2
02.05.2017 14:27+1Мы так много времени на это потратили, хочу вернуться к работе.
У каждого из нас свой подход. Я считаю, что чем меньше звеньев в технологии — тем она стабильнее и надежнее.
Я очень скользко привел выше пример экономии.
Под DDOS атакой проект ложится намного позже. Часто просто продолжает медленно работать.
opcache — вариант конечно.
— Мое мнение, что разработчик должен решить две задачи:
1) Быстрое производство
2) Производительность
Остальные типа качества подразумеваются.
Phalcon более быстрая платформа из коробки, безусловно скорость фреймворка не решает насущих проблем производительности больших баз данных и больших обработчиков, но если мы говорим про емкие CRUD, где все и так оптимизирвоано и остается только роутинг, то только opcache вам в помощь и ничего плохого в этом нет, это ваш выбор )hlogeon
02.05.2017 15:04+1Соглашусь) Ушел работать недождавшись Вашего ответа даже.
Главное, что б денюжку платили и заказчики довольны были. А вот как именно это уже не так важно.
Fesor
02.05.2017 15:14емкие CRUD
в доброй половине из этих проектов можно просто взять firebase и не возиться с php.
caballero
02.05.2017 14:28+2а трудоемкость разработки на С и PHP не пробовали сравнить? Стоимость работы програмиста ща гораздо больше стоимости железа.
Dimash2
02.05.2017 14:30Если вы говорите про 50 долларов в месяц за Digital Ocean, то да )
Только я не понял вопрос про С и PHP, на Phalcon вы работает под PHP, сам фреймворк на C и его файлов нет в вашем проекте.Dimash2
02.05.2017 14:32PS. Трудозатраты на разработку решения, которое работает быстро — очень преувеличены. По сути сейчас почти все мои решения «максимально быстры», покрайней мере на «мои мозги», может кто-то лучше программирует чем я. Но я сразу все библиотеки ориентирую под производительность.
Fesor
02.05.2017 14:55Вы считаете, что Hello word не достаточно?
Ну тип того, да. Предположим разница между двумя фреймворками при таком тесте — фреймворк Б в 100 раз быстрее A. С другой стороны под фреймворк А в 50 раз больше разработчиков. Стоимость одного сервера за месяц составляет 1% от стоимости разработчика за тот же месяц. Так же предположим что на 10 разработчиков нам подходят 2 и 1 не пройдет испытательный срок (цифры с потолка и все зависит от проекта, на простых проектах цифры намного позитивнее).
Предположим что на реальном приложении мы упираемся уже не в CPU а в IOPS и сеть. И разница между фреймворками А и Б в плане RPS уменьшается до 2x. Для того чтобы добиться того же уровне RPS нам нужно 2.1x серверов (2.1 потому что резервирование железа и это не является 2.1x от всего кластера, это лишь php машинки). Так же не забываем что нам сложнее будет искать разработчиков. Искать людей которым плевать на чем писать и они все делают хорошо — это еще больше времени и денег. А далее уже надо смотреть на соотношение количества серверов к количеству разработчиков. И это мы еще делаем предположение что скорость разработки и инфраструктура у обоих фреймворков развита примерно одинаково.
tl;dr математика простая. Если у нас много серверов — мы получаем профит, но это должен быть реально большой кластер чтобы был профит. Потому хотелось бы знать заранее соотношение rps для более-менее реального проекта. А это уже сложно потому что надо делать.
p.s. hello world на php7 выдает на 30% больше RPS чем phalcon3.
Dimash2
02.05.2017 15:01С вашими словами согласен, но что делать если вся команда принимает проекты под ключ и делает их на Phalcon в рамках своего тех процесса и проводит обучение разработчиков по собственным наработкам — в таком случае ваш аргумент про найм сотрудников не имеет веса.
И я еще не видел сотрудников по php, которые бы не могли разобраться в любом фреймворке, сотрудников которых вы описали — это планка ниже 500$ в регионах. От 700$ в регионах — уже всеравно на чем писать, другой вопрос — общее качество на любом из фреймворков.
Dimash2
02.05.2017 15:05Вы тоже все описали слишком «лабораторно», в реальности, пока вы пишите код — ваш фреймворк все замедляется и замедляется. Не круто получать тяжеловесный шаблон например через 500ms или что еще хуже через секунду.
Fesor
02.05.2017 17:05доставку сервером php и скомпилированого C?
Какая разница если 80% времени PHP тупо ждет блокирующих операций ввода/вывода?
Dimash2
02.05.2017 14:04Ну и безусловно иногда подразумевается, что при принятии подобных решений проводятся собственные субьективные тесты. Как выше мне говорили, что php медленный. Я думал, где мне обрабатывать 10 гигабайтный массив, в php,C или Node, провел тест php7 и в реальном испытании он показал отставание на секунду. Так же проводил тесты по нагрузке на сервера.
Один из тестов попал и Wordpress, он тоже попал под жесткую оптимизацию и вам советую, если делаете на WP сайты напишите в index.php перехватчик буфера вывода и кешируйте его в БД, получится скорость сайта из 1 php файла и одного запроса — полностью отрубаете WP компиляцию
redfs
02.05.2017 14:26серьезрый response delay под нагрузкой (у второго и без нагрузки) из-за огромног количества фреймворковских файлов
Почему то вы игнорируете все упомининия ваших оппонентов про opcache. Данные, которыми вы оперируете, устарели на несколько лет.Dimash2
02.05.2017 14:31Вы правы, но эти споры — развлечения, по сути ниже правильно написали. Фреймворк быстрый, минималистичный и он меня устраивает.
Fesor
02.05.2017 13:33+2на другие фреймворки даже не смотрю
Ну да. ведь все задачи на свете можно подогнать под один единцы универсальный подход. Если вы просто смотрите на другие фреймворки/языки это не обязывает их использовать. Можно просто духовно развиваться.
фундаментально другой подход в компиляции
Это как простите? подход то тот же абсолютно.
так глубоко в встроенные методы фреймворка
Ну инструмент то свой нужно. К слову, поделитесь историями о том как вы дебажите приложения, можно ли xdebug-ом залазить внутрь внутренних вызовов и смотреть что там происходит?
структура для правильной организации кода
В бородатых 70-х придумали такие штуки как coupling и cohesion для грамотной декомпозиции проекта на модули. А когда фреймворк заставляет меня ложить все в
сontroller
иmodel
я грущу.
К слову может будет интересно. Тут есть прекрасно поданный материал на тему структурирования кода. Правда в контексте Java но это не столь принципиально.
Dimash2
02.05.2017 13:37Спасибо.
Да, вы меня правильно поняли, чем не устраивает универсальный и самый производительный подход, разве не это вам позволяет делать фреймворк?
И на практике, не могли бы вы привести популярные, а не супер частные случаи, где фреймворк имеет значение? Разные команды делают похожие проекты на абсолютно разных фреймворках.
Духовно развиваться нужно, но не считаю, что фреймворки — это пища для развития. Безусловно я пересматриваю свой подход разработки регулярно, меняю фреймворки, как недавно поменял Ember на Angular 2, но по возможности я не начинаю новые проекты на двух сразу.
Вот не могу себе представить, где лучше будет Ember чем Angular и наоборот — только мои предпочтения.Fesor
02.05.2017 13:50разве не это вас позволяет делать фреймворк?
Я использую Symfony, он позволяет мне не использовать то что мне не нужно. Так что я в целом доволен. А сам по себе фреймворк не является в моем случае узким местом, тут скорее инициализация контейнера зависимостей уже больше будет влиять. А вообще у меня тут есть маленький пет-проджект, надо бы все же попробовать его реализовать на phalcon. Уже года 2 собирался потыкать этот фреймворк тупо для расширения кругозора, да все небыло интереса. А так хоть нагрузочные тесты можно будет погонять что бы посмотреть стоит ли овчинка выделки.
что фреймворки — это пища для развития.
Я именно о таких штуках как "а не попробовать ли мне SPA делать заместо обычного WEB, вдруг устраню кучу проблем". Или "в symfony/flex замутили удобный скаффолдинг проектов, любопытно, сворую для фалькона".
Dimash2
02.05.2017 13:59Ну вот и вы написали, что все на ваших предпочтениях ) и все задачи по сути вы на Симфони и решаете (в целом если творчество не загорится)
Fesor
02.05.2017 14:57вы на Симфони и решаете
типовые проекты — на своей сборке, некоторые специфичные задачи — на отдельных компонентах и не только на симфони компонентах.
caballero
02.05.2017 14:34Я именно о таких штуках как «а не попробовать ли мне SPA делать заместо обычного WEB, вдруг устраню кучу проблем».
SPA — это один из модных нынче трендов. Посему неважно какие проблемы он решает, какие еще большие проблемы создает и что конечному пользаку, как правило не айтишнику, пофиг SPA там или древний CGI,Fesor
02.05.2017 14:59неважно какие проблемы он решает,
для меня важно. Мы таким образом сильно упростили бэкэнды, а UI логика на SPA ложится просто прекрасно.
какие еще большие проблемы создает
незначительные на фоне профита на самом деле. Ну это если знать что делаешь. Вот тут могут быть проблемы.
пофиг SPA там или древний CGI,
Это влияет на UX причем неслабо, и уж поверьте рядовому пользователю разница тут будет огромна.
caballero
02.05.2017 15:30+1Мы таким образом сильно упростили бэкэнды,
и сильно усложнили фронтэнды. При этом без бэкенда на приложениях с серьезными данными все равно не обойдешся.
caballero
02.05.2017 14:23Все фреймворки делают одно и тоже,
Все языки программирования делают одно и тоже.
Все компьютеры делают одно и тоже.
На самом деле фреймворк важен не тем что он делает а тем насколько упрощает програмисту решение конечно задачи.
Другое дело что 9 из 10 фреймворков — архитектурные MVC-близнецы и поэтому их сложно разложить по направлениям решения (точнее имплементации задач).
velvetcat
03.05.2017 00:46+3Заглянул на Гитхаб...
Репозиторий Core. Core чего, фреймворка? Но он же слабосвязанный? И что во фреймворке такого, центрального, без чего фреймворк — не фреймворк?
О нет, это ядро всего приложения… Фреймворк не должен быть ядром приложения, ядро должно быть независимым ни от чего. Чисто практически — кто будет использовать интерфейсы или наследовать свои классы от никому не известной 3d-party библиотеки?
Storages/KeyValueStorage. Финт с null-овым именем для вызова мощного деструктивного поведения когда-нибудь дорого обойдется. Явное лучше неявного, почему бы не сделать методы loadAll/saveAll()?
Load data from external storage into this object by specified...? То ли впопыхах вкоряченный и забытый костыль, то ли тотальное непонимание ООП. От KeyValue клиентам нужно только одно — положить данные, а потом их забрать, им глубоко фиолетово, что там где-то за KeyValue есть еще одно external storage. Хинт: попробуйте реализовать этот интерфейс на базе memcached.
/** * Stores the value by specified key in this object * @param int|string|null $key * @param mixed $value */ public function set($key, $value);
Ключи "1" и 1 — будут считаться за один и тот же ключ или за разные? Укажите string, стандартного приведения типов достаточно.
Зачем Вам XxxStorageAwareInterface? Здесь не проглядывается ни одной причины для его использования. Выглядит особенно странно с учетом Вашего прохладного отношения к DI. Кстати, аннотации противоречат сигнатурам методов.
SingletonInterface, SingletonTrait. Нас настойчиво подталкивают к использованию синглтонов. В нормальном фреймворке должно быть легко делать правильные вещи и тяжело — неправильные.
SingletonTraitTest. Проверять рефлекшеном, что конструктор непубличный — это, пардон, днище. Класс нельзя инстанцировать через new — вот что надо проверять, не больше и не меньше. И дайте тестовым методам нормальные имена хотя бы.
Больше не смотрел. Общее впечатление — куча неважных, да еще спорных мелочей (типа FI), неочевидное поведение, везде наследование и магия, непонимание базовых принципов ООП, косяки на всех уровнях абстракции. Как это вообще можно выкладывать?
AlexLeonov
03.05.2017 09:54Большое спасибо за столь подробный комментарий.
Отвечу на последний вопрос: это нужно выкладывать, хотя бы для того, чтобы вы могли поупражняться в разборе :)
А если серьезно, то любые отзывы всегда на пользу. Положительные дают силы продолжать дальше. Отрицательные — возможность задуматься и вовремя что-то изменить. Большое спасибо, что пишете их!
procode
Я тоже недавно пытаясь разобраться в Yii2 плюнул на это, и написал себе небольшой MVC с нуля.
А еще ранее, когда только вышел ZF, произошло то же самое, но где тот первый движок — я уже и не помню :D
Всегда немного завидовал людям, которые пишут постоянно — у них всё написанное под рукой, не теряется, совершенствуется. А когда мастеришь только для себя, то постоянно приходится через несколько лет писать одно и то же заново.
Так что мысленно поддерживаю вашу инициативу!
//И да, всегда с трепетным уважением смотрю на людей, сумевших разобраться в Symfony например. У них наверное какой-то особенно огромный мозг, или еще чего такое в этом же духе :)
AlexLeonov
Откройте для себя Github ))
Кстати, в чём-то я с вами согласен. Недовольство именно Yii, который пришлось несколько раз использовать, стало одним из поводов делать что-то своё. Мне просто стало интересно — сможем ли мы с ребятами сделать что-то вроде Yii 3, не таща за собой его фатальные архитектурные ошибки?
Оказалось, что смогли :)
hlogeon
Пруфы будут?)
AlexLeonov
А как же. Статье к десятой. Имейте терпение!
skobkin
Мне кажется, что без пруфов до десятой статьи вряд ли кто-то дочитает.
AlexLeonov
Код открыт. Какие еще пруфы прямо сейчас вы хотели бы получить?
skobkin
Вам же уже писали: пруфы применения в сравнении с другими фреймворками, где ясно видно, что ваше решение лучше.
hlogeon
Тот код, который открыт — антипруф. И ниже есть немало аргументов почему это так.
AlexLeonov
Я услышал ваше мнение, спасибо.
Fesor
Тут больше людей интересует каков был алгоритм. Такой:
или такой
Если все происходило по первому сценарию — в этом нет пользы. Если по второму — всех интересуют причины.