Некоторое время назад вышел первый релиз ветки 5.x, а потом несколько меньших патч-версий, так что опять есть чего рассказать.
Предыдущие изменения: часть 1, часть 2, часть 3, часть 4.
Комментарии под предыдущей статьей и в чатиках были весьма полезными, отдельное спасибо fesor, который хоть и не согласен, но предоставляет конструктивную критику, которая имеет позитивные последствия.
Множество изменений сделали систему быстрее, легче и удобнее чем когда-либо до этого. В статье кратко об основных изменениях, их причинах и последствиях.
В версиях 4.x jQuery сначала была объявлена устаревшей, позже в 5.x она была полностью выпилена из фреймворка.
Множество рутинных операций с использованием Polymer становятся декларативными и тянуть jQuery ради небольшого количества методов не хотелось.
Ещё jQuery активно использовалась для XHR запросов, но с широкой поддержкой XHR2 исчезла необходимость писать несколько реализаций, и была написана функция cs.api(), которая:
Некоторые модули использовали jQuery плагины, они теперь используют NPM версию jQuery.
Так же один из jQuery плагинов что использовался в ядре был отрефакторен без использования jQuery, патч принят upstream, так что фреймворк включает чистую оригинальную версию: github.com/voidberg/html5sortable/pull/204
Раньше отдельно были модули и плагины. Плагины были похожи на модули, но без собственных страниц, админок, настроек, API и прочего.
В связи с тем, что наличие второго типа компонентов, который фактически является подвидом первого, усложняло поддержку, было решено упразднить его, все плагины были конвертированы в модули.
Так же
Шаблоны блоков были достаточно кривой штукой изначально и по факту были слишком привязаны к конкретному проекту. Если добавить к этому невозможность установки блоков из пакетов — польза и вовсе сводилась к нулю.
Поэтому шаблоны блоков тоже были выпилены, теперь блоки сами беспокоятся о собственном внешнем виде.
Вместе с чисткой второстепенных фич было проведено много работы по консистентности внутреннего API фреймворка.
К примеру,
Ещё один пример — удаление поддержки вызовов
Множество подобных мелочей было исправлено и приведено к общему виду.
Тестированию в последних версиях было уделено гораздо больше времени чем разработке новых фич, также было добавлено вычисление покрытие кода фреймворка тестами.
В результате общее покрытие фреймворка тестами 66%+ на момент написания статьи, в покрытие системных классов 96%+ (папка
Так же учет покрытия кода позволил найти участки, которые раньше не тестировались, а так же ряд мелких багов, которые было бы сложно найти другим способом.
Поскольку не все ещё прониклись веб-компонентами, может показаться слишком дорогим грузить Polymer, полифиллы и HTML импорты если они не будут использоваться. Именно для этих случаев в последнем релизе поддержка веб-компонентов стала отключаемой для продвинутых пользователей в админке.
Второе важное улучшение — переход на асинхронные интерфейсы работы с переводами на фронтенде.
Раньше было так:
Теперь же нужно подождать:
На самом деле под капотом до 6.x оно всё ещё синхронно, но в итоге переводы будут загружаться только когда нужны.
Все эти изменения позволят уменьшить объем обязательно загружаемого JS кода с 312 КиБ до меньше чем 30 КиБ (это всё без учета gzip), а объем HTML импортов из 107 КиБ до 0 КиБ (0 файлов).
По сути, будет загружаться Alameda (RequireJS) и несколько вспомогательных системных функций/объектов.
Очередная порция изменений привела к ещё большей скорости работы (хотя низко висящих фруктов уже нет).
Особенно полезным является оптимизация API запросов. Существенными (относительно) накладными расходами обладала работа с переводами, большой JSON занимал как время на парсинг, так и память. Теперь же во время API запроса, не работающего с многоязычностью, переводы вообще не будут загружены в память.
Для сравнения с конкурентами был сделан форк популярного бенчмарка с кучей разных фреймворков, так что результаты можете воспроизвести сами:
В текстовом виде:
Так же выросла пиковая производительность под встроенным Http сервером вместе с отличной масштабируемостью (HHVM, 16 процессов, Core i7 4900MQ):
А в однопоточном режиме запросы отрабатывают начиная с 0.6 мс (0.0006 секунд), что очень достойный результат, хотя и хотелось бы забраться под 0.5 миллисекунды.
Как обычно, буду благодарен за конструктивные комментарии.
Репозиторий на GitHub: github.com/nazar-pc/CleverStyle-Framework
Предыдущие изменения: часть 1, часть 2, часть 3, часть 4.
Комментарии под предыдущей статьей и в чатиках были весьма полезными, отдельное спасибо fesor, который хоть и не согласен, но предоставляет конструктивную критику, которая имеет позитивные последствия.
Множество изменений сделали систему быстрее, легче и удобнее чем когда-либо до этого. В статье кратко об основных изменениях, их причинах и последствиях.
Прощай jQuery
В версиях 4.x jQuery сначала была объявлена устаревшей, позже в 5.x она была полностью выпилена из фреймворка.
Множество рутинных операций с использованием Polymer становятся декларативными и тянуть jQuery ради небольшого количества методов не хотелось.
Ещё jQuery активно использовалась для XHR запросов, но с широкой поддержкой XHR2 исчезла необходимость писать несколько реализаций, и была написана функция cs.api(), которая:
- обеспечивает 95% потребностей с гораздо более удобным синтаксисом
- поддерживает отправку форм и файлов
- интерфейс базируется на ES2015 Promise
- поддерживает обработку ошибок с выводом пользователю красивых всплывающих сообщений
Некоторые модули использовали jQuery плагины, они теперь используют NPM версию jQuery.
Так же один из jQuery плагинов что использовался в ядре был отрефакторен без использования jQuery, патч принят upstream, так что фреймворк включает чистую оригинальную версию: github.com/voidberg/html5sortable/pull/204
Больше нет плагинов и шаблонов блоков
Раньше отдельно были модули и плагины. Плагины были похожи на модули, но без собственных страниц, админок, настроек, API и прочего.
В связи с тем, что наличие второго типа компонентов, который фактически является подвидом первого, усложняло поддержку, было решено упразднить его, все плагины были конвертированы в модули.
Так же
components/blocks
и components/modules
впоследствии перенеслись в blocks
и modules
по аналогии с themes
.Шаблоны блоков были достаточно кривой штукой изначально и по факту были слишком привязаны к конкретному проекту. Если добавить к этому невозможность установки блоков из пакетов — польза и вовсе сводилась к нулю.
Поэтому шаблоны блоков тоже были выпилены, теперь блоки сами беспокоятся о собственном внешнем виде.
Консистентность интерфейсов
Вместе с чисткой второстепенных фич было проведено много работы по консистентности внутреннего API фреймворка.
К примеру,
cs\CRUD
при работе с разными движками БД теперь приводит числовые типы к одному виду (MySQL даже для числовых столбцов возвращал строки, а SQLite возвращал числа).Ещё один пример — удаление поддержки вызовов
cs\DB::instance()->$db_id
и cs\DB::instance()->$db_id()
вместо cs\DB::instance()->db($db_id)
и cs\DB::instance()->db_prime($db_id)
. Когда-то давно это использовалось, но это не очень удобно и усложняет вывод типов для IDE.Множество подобных мелочей было исправлено и приведено к общему виду.
Тестирование
Тестированию в последних версиях было уделено гораздо больше времени чем разработке новых фич, также было добавлено вычисление покрытие кода фреймворка тестами.
В результате общее покрытие фреймворка тестами 66%+ на момент написания статьи, в покрытие системных классов 96%+ (папка
modules
практически полностью состоит из контроллеров, поэтому приоритет ниже):Так же учет покрытия кода позволил найти участки, которые раньше не тестировались, а так же ряд мелких багов, которые было бы сложно найти другим способом.
Фронтенд ещё легче, опциональное отключение поддержки веб-компонентов
Поскольку не все ещё прониклись веб-компонентами, может показаться слишком дорогим грузить Polymer, полифиллы и HTML импорты если они не будут использоваться. Именно для этих случаев в последнем релизе поддержка веб-компонентов стала отключаемой для продвинутых пользователей в админке.
Второе важное улучшение — переход на асинхронные интерфейсы работы с переводами на фронтенде.
Раньше было так:
cs.Language.system_profile_hello('Username')
// или
cs.Language('system_profile_').hello('Username')
Теперь же нужно подождать:
cs.Language.ready().then(function (L) {
L.system_profile_hello('Username');
});
// или
cs.Language('system_profile_').ready().then(function (L) {
L.hello('Username');
});
На самом деле под капотом до 6.x оно всё ещё синхронно, но в итоге переводы будут загружаться только когда нужны.
Все эти изменения позволят уменьшить объем обязательно загружаемого JS кода с 312 КиБ до меньше чем 30 КиБ (это всё без учета gzip), а объем HTML импортов из 107 КиБ до 0 КиБ (0 файлов).
По сути, будет загружаться Alameda (RequireJS) и несколько вспомогательных системных функций/объектов.
Производительность серверной части
Очередная порция изменений привела к ещё большей скорости работы (хотя низко висящих фруктов уже нет).
Особенно полезным является оптимизация API запросов. Существенными (относительно) накладными расходами обладала работа с переводами, большой JSON занимал как время на парсинг, так и память. Теперь же во время API запроса, не работающего с многоязычностью, переводы вообще не будут загружены в память.
Для сравнения с конкурентами был сделан форк популярного бенчмарка с кучей разных фреймворков, так что результаты можете воспроизвести сами:
Скрытый текст
В текстовом виде:
|framework |requests per second|relative|peak memory|relative|
|-------------------|------------------:|-------:|----------:|-------:|
|silex-1.3 | 3,029.75| 8.8| 0.59| 1.0|
|symfony-2.7 | 1,423.83| 4.2| 1.41| 2.4|
|symfony-3.0 | 995.79| 2.9| 1.64| 2.8|
|laravel-5.2 | 342.99| 1.0| 1.98| 3.4|
|zf-2.5 | 671.20| 2.0| 1.36| 2.3|
|cleverstyle-5.15 | 1,939.17| 5.7| 0.66| 1.1|
Так же выросла пиковая производительность под встроенным Http сервером вместе с отличной масштабируемостью (HHVM, 16 процессов, Core i7 4900MQ):
Скрытый текст
nazar-pc@nazar-pc /w/t/www> ab -c500 -n100000 -k http://test.com:9990/api/System/blank
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking test.com (be patient)
Completed 10000 requests
Completed 20000 requests
Completed 30000 requests
Completed 40000 requests
Completed 50000 requests
Completed 60000 requests
Completed 70000 requests
Completed 80000 requests
Completed 90000 requests
Completed 100000 requests
Finished 100000 requests
Server Software: nginx/1.10.1
Server Hostname: test.com
Server Port: 9990
Document Path: /api/System/blank
Document Length: 4 bytes
Concurrency Level: 500
Time taken for tests: 9.375 seconds
Complete requests: 100000
Failed requests: 0
Keep-Alive requests: 0
Total transferred: 24400000 bytes
HTML transferred: 400000 bytes
Requests per second: 10666.64 [#/sec] (mean)
Time per request: 46.875 [ms] (mean)
Time per request: 0.094 [ms] (mean, across all concurrent requests)
Transfer rate: 2541.66 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 8 69.2 1 1013
Processing: 0 39 30.9 31 272
Waiting: 0 37 30.5 29 272
Total: 0 47 75.3 35 1199
Percentage of the requests served within a certain time (ms)
50% 35
66% 48
75% 57
80% 63
90% 83
95% 104
98% 137
99% 166
100% 1199 (longest request)
А в однопоточном режиме запросы отрабатывают начиная с 0.6 мс (0.0006 секунд), что очень достойный результат, хотя и хотелось бы забраться под 0.5 миллисекунды.
Напоследок
Как обычно, буду благодарен за конструктивные комментарии.
Репозиторий на GitHub: github.com/nazar-pc/CleverStyle-Framework
Поделиться с друзьями
MetaDone
Хотелось бы увидеть сравнение производительности с Phalcon на php7
nazarpc
Только что запустил на PHP 7.0.8, Ubuntu 16.10 x64
MetaDone
ок, спасибо
nazarpc
Решил сделать более полный, результаты опять слегка отличаются, но в целом Phalcon не переплюнуть по производительности
Fesor
а никто symfony 2.8+ в режиме микроядра не добавлял?
wispoz
А Yii2? Можете добавить?
nazarpc
Я хотел, но он сломан в бенчмарке. Если кто-то исправит, с радостью сравню.
Miraage
Когда планируете использовать паттерны проектирования, PSR, DI?
nazarpc
PSR поддерживается на уровне совместимости, к примеру, прослойка для PSR7, автозагрузчик аналогичен PSR4 по сути, просто есть несколько директорий с разными префиксами (
cs\modules
это модули вmodules
, аcs
это системные классы вcore\classes
), тот же Composer можете использовать.На счёт DI — нет и не планируется. Во фреймворке он не нужен, ибо он не компонентный и не предполагает свободной замены своих частей. А если вам нужен DI — вон сколько их готовых на GitHub, подключайте чего вам нравится, не вижу пока смысла что-то в этом плане навязывать.
На счёт паттернов — что конкретно вам не нравится? Буду благодарен если ответите конкретно с примером, а то выглядит как вопрос на собеседовании)
Miraage
no offence, но все на синглтонах — это, на мой взгляд, не комильфо.
nazarpc
Это я часто слышу, попытался ответить на этот вопрос здесь
Fesor
1) что такое "ручное внедрение зависимостей"? Фабрики? Что там нечитабельного?
2) внедрение зависимостей, которые на самом деле не используются означают что у модуля низкая внутренняя связанность и его нужно разделить на отдельные модули. А еще есть lazy инициализация с проксями. Опять же вас никто не заставляет писать свой контейнер — юзаем готовые которые все это умеют и учитывают.
какое отношение это вообще к Dependency Injection имеет?
неверно. Основная суть что у вас есть один объект в системе, чья задача разруливание зависимостей. Никакой чуши про интерфейсы => реализации.
Толк в том что она есть и мы не пишем кучу фабрик. Они генерятся сами. А если вы посмотрите на популярные контейнеры — там в принципе в 80% случаев вообще конфигурацию описывать не нужно.
ленивая загрузка это не решение проблемы а ее симптом. Ну и про конфигурацию — тупой autowiring делается весьма просто.
справедливости ради, при использовании pthreads статика не шарится по потокам. Как раз таки как защита от дурака. Расшаривание данных происходит максимально явно и случайно расшарить что-то не выйдет.
Если замена не предполагается (то есть никакой инверсии контроля, никакой гибкости) то как бы и любой другой "локатор" не требует никакой конфигурации. Макретинговая хрень. Да и "сервис локатор это антипаттерн!".
Резюмирую. Сингелтон сам по себе — нормальный паттерн. В PHP мире на нем реализуют сервис локатор, и как бэ вот это и есть "плохо". Вообще любое глобальное — не очень хорошо и давать такое использовать кому попало не стоит.
nazarpc
Я имел ввиду подобное:
Когда вы создаете все зависимости явно и явно передаете их в конструктор в самом начале, то есть без использования контейнеров и чего либо ещё.
Достаточно прямое, позволяет вместо кода выше писать подобное:
А под капотом будут внедрены нужные зависимости.
Согласен, но всегда есть граница здравого смысла. Городить прокси просто для того чтобы следовать паттерну, при том что сервис никогда не будет изменен? По-моему овчинка выделки не стоит. По крайней мере я предпочту повышение связности до определённой границы.
Это грубо, имеется ввиду что зависимости не черной магией разруливаются, а по определённым образом составленным правилам.
То есть решаем проблему, которой изначально не было? Сейчас во фреймворке вообще нет фабрик, они просто не нужны. И есть лишь один метод во всём фреймворке, который занимается созданием подобных объектов. autowiring это круто, но это работает не в 100% случаев, а в данном случае решает то, что не является проблемой изначально.
Опять таки да, но проблемы может вообще не быть при альтернативном подходе.
Я пытаюсь донести, что при наличии единственной реализации чего либо в принципе, не вижу смысла в интерфейсах, DI, тащить что-то калибра PHP-DI (который, несомненно, крут, но по объему как 15% всего CleverStyle Framework) и так далее, поскольку это не имеет практического смысла. Гораздо удобнее иметь прямой вызов и автоматический вывод типов в IDE без каких-либо сторонних плагинов, да и производительность получится самая высокая. Если это можно в итоге протестировать и вместе оно хорошо работает — то почему нет?
Не хочется закончить написанием https://github.com/Herzult/SimplePHPEasyPlus либо https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition в реальной жизни.
Меня привлекает простота реализации, когда несколькими кликами можно добраться к строчкам, которые реально что-то делают, а не являются очередной абстракцией.
Fesor
вся суть в том, что "код выше" не требует никаких контейнеров, локаторов и прочего что бы работать. Мы можем "собрать руками", а можем попросить вошлебный контейнер зависимостей сделать это за нас. Суть в том, что если мы вдруг захотим "поменять контейнер" например с симфоневого на PHP-DI или сменить фреймворк (имеется в виду мажерные релизы без сохранения обратной совместимости), наши "классы" не будут фигурировать в diff-ах наших коммитов на эту тему.
Сервис локатор — это полная завязанность на текущем способе разруливания зависимостей. Это нормально только там, где это нормально (контроллеры например, они и так часть фреймворка и завязаны на нем) либо если вы пишите что-то что собираетесь удалить через пару месяцев. Что все должно "жить" более пары лет — не должно быть так сильно завязано на способ разруливания зависимостей.
Все сводится к принципу "работает не трогай", или как я его трактую — "делай так, что бы не пришлось потом менять, только удаляй/добавляй классы".
повторюсь. Необходимость использовать ленивую инициализацию — это больше похоже на то, что что-то пошло не так. Проблему можно решить явно поддерживая ленивую инициализацию, или в случае легаси приложений — генерировать прокси объекты.
А зачем и когда нужно следовать "паттерну" — я описал выше.
разруливание по правилам = магия. Она такая же "черная" но чуть другая.
проблема управления зависимостями и управление связанностью существует… ну скажем так, она появилась раньше чем ООП. В каком-то смысле ООП является решением этой проблемы.
вы этим как бы говорите "у меня всегда будет только одна реализация, и не только у меня — у всех кто будет использовать мою систему!". То есть вы не даете никакой возможности для кастомизации.
Вы из одной крайности (все жестко) бросаетесь в другую (отсутствие здравого смысла). Вы же понимаете что… все должно быть в балансе.