Cобрать лучшее из двух миров — фреймворков и CMS (часть 2)

в 9:09, , рубрики: cleverstyle, cmf, cms, php, Веб-разработка, метки:

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

Для чего

Из прошлой статьи не всем было понятно, для чего всё это создается, конкретная проблема, которую решает продукт.
Проще всего объяснять такие вещи на примере. CleverStyle CMS не подойдет если:

  • Под вашу задачу есть CMS, которая решает задачу полностью
  • Под вашу задачу есть CMS, которая почти подходит, вы её подпилите, обновления вас особо не беспокоят
  • У вас Enterprise проект

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

Singleton

Cобрать лучшее из двух миров — фреймворков и CMS (часть 2) - 1

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

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

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

$Session = csSession::instance();

Всё что клиентскому коду нужно знать — он получит объект сессии с предсказуемым публичным интерфейсом, но это может быть не совсем оригинальный системный объект, либо даже совсем не он.
Так же это позволяет без каких либо плагинов и дополнительных аннотаций генерировать подсказки в IDE по всем публичным методам и свойствам системных объектов, что чрезвычайно удобно.
Аналогия из Laravel:

$Session = $app->make('session')

Вот только здесь для подсказок IDE нужно писать аннотации.

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

Composer

Cобрать лучшее из двух миров — фреймворков и CMS (часть 2) - 2

Было много комментариев о Composer, PSR4 и с этим связанным. Если немного углубиться в подробности того, как устанавливаются компоненты и на что влияют их зависимости, становится понятно, что Composer мягко говоря не самое подходящее решение для упаковки компонентов.
«Но ведь он так удобен для установки библиотек!» скажете вы, и будете несомненно правы.
Подключать одну и ту же библиотеку несколько раз в составе разных компонентов и страдать из-за разных версий не кошерно.
Поэтому дополнительно к поддержке composer.json, composer.lock и vendor в корне проекта появился отдельный компонент с неожиданным названием Composer!
В процессе использования взаимодействие с самим Composer при условии что зависимости не конфликтуют сводится в нулю.
Для того, чтобы воспользоваться этой прелестью нужно добавить зависимость от composer в meta.json компонента, и прописать сами зависимости, пример для модуля WebSockets:

{
	"package"          : "WebSockets",
	"category"         : "modules",
	"version"          : "0.29.0+build-45",
	"description"      : "WebSockets server based on Ratchet and React with client-side bindings as well",
	"author"           : "Nazar Mokrynskyi",
	"website"          : "cleverstyle.org/cms",
	"license"          : "MIT License",
	"db"               : [
		"pool"
	],
	"db_support"       : [
		"MySQLi"
	],
	"provide"          : "websockets",
	"require"          : [
		"System>=2.1",
		"System<=3.0",
		"composer"
	],
	"require_composer" : {
		"cboden/ratchet" : "0.3.*",
		"ratchet/pawl"   : "0.1.*"
	},
	"multilingual"     : [
		"interface"
	],
	"hide_in_menu"     : 1
}

В процессе установки компонента будут собраны Composer зависимости всех компонентов, составлен финальный composer.json, и если не возникнет конфликтов — всё установится само, если возникнут — консольный вывод Composer будет переформатирован в HTML, и предоставлен пользователю, пусть решает что с этим делать.

Вот такой composer.json генерирует модуль для установки всех зависимостей:

{
	"repositories" : [
		{
			"type"    : "package",
			"package" : {
				"name"    : "modules/Http_server",
				"version" : "0.8.0+build-13",
				"require" : {"react/http" : "0.4.*"},
				"dist"    : {
					"url"  : "/web/cscms.org/www/components/modules/Composer/empty.zip",
					"type" : "zip"
				}
			}
		},
		{
			"type"    : "package",
			"package" : {
				"name"    : "modules/WebSockets",
				"version" : "0.24.0+build-38",
				"require" : {
					"cboden/ratchet" : "0.3.*",
					"ratchet/pawl"   : "0.1.*"
				},
				"dist"    : {
					"url"  : "/web/cscms.org/www/components/modules/Composer/empty.zip",
					"type" : "zip"
				}
			}
		}
	],
	"require"      : {
		"modules/Http_server" : "0.8.0+build-13",
		"modules/WebSockets"  : "0.24.0+build-38"
	}
}

Виртуальные пакеты объявляются на месте, от них же сразу ставится зависимость, таким образом разрешение конфликтов полностью лежит на стандартных механизмах Composer (zip файл это единственное что нужно иметь реальное иметь в файловой системе, он абсолютно пуст внутри).

Разделение логики и представления

В идеале на сервере никакого представления.
Пока готово как пример в клиентской части модуля магазина (ниже), хотя и будет обновляться с использованием JSON-LD для минимального объема серверного кода и лучшей индексации.
Сейчас страничка товара магазина в коде выглядит так:

<section data-date="0" data-id="1" data-in_stock="24" data-price="20" data-soon="0" is="cs-shop-item">
	<div id="images">
		<img alt="" src="http://cscms.org/storage/public/Plupload/2015-01-03/18/2_031854a812c6b264b.jpg">
		<img alt="" src="http://cscms.org/storage/public/Plupload/2015-01-03/18/2_035654a812ec26d24.jpg">
		<img alt="" src="http://cscms.org/storage/public/Plupload/2015-01-03/18/2_035654a812ec1c834.jpg">
	</div>
	<div id="videos">
		<a href="https://www.youtube.com/watch?v=rHBxJCq99jA"> </a>
		<a href="https://www.youtube.com/watch?v=bmtbg5b7_Aw"> </a>
	</div>
	<h1>Boots</h1>
	<div id="description">
		<p>Nice boots</p>
	</div>
	<div id="attributes">
		<table>
			<tr>
				<td>Size</td>
				<td>2</td>
			</tr>
		</table>
	</div>
</section>

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

Стиль кодирования

То, что предлагают PSR мне не очень нравится, поэтому стиль кодирования был немного обновлен в wiki, дополнительно теперь в репозитории лежат файлы конфигурации стиля кодирования и инспекций PhpStorm, при открытии проекта он сразу будет сконфигурирован должным образом.
Все новые файлы форматируются этим стилем, старые файлы при редактировании тоже.
Просьба уважать стиль автора проекта)

Рефакторинг

В промежутке от версии 1.0 в порядок приводилось ядро, в результате обширного рефакторинга система стала существенно понятнее и более предсказуемая, а так же стали возможными некоторые функции, как то работа в Request/Response режиме (о чём далее).

Глобально:

  • класс csTrigger был переименован в csEvent, чтобы больше не сбивать с толку, так же был упрощен и появились новые возможности, как то одноразовая подписка на событие, как jQuery.fn.one()
  • дополнительно появился cs.Event на фронтенде с теми же методами, порядком аргументов что и csEvent на сервере
  • csSession образовался после очередного рефакторинга csUser, позволяет дополнительно ускорить работу в асинхронном режиме
  • csRoute объединил функциональность, отвечающую за маршрутизацию, которая по историческим причинам была одновременно в csConfig и csIndex, что было далеко не очевидно
  • $_SERVER оборачивается в массиво-подобный объект для простоты получения нужной информации независимо от конфигурации, это позволило избавиться от дублирования кода
  • ExitException используется вместо exit/die в тех редких местах, где они были, сторонние библиотеки легко патчатся автоматическим поиском и заменой для поддержки этой функциональности, критическая фича для режима Request/Response
  • некоторые встроенные функции, работающие с глобальным контекстом, были заменены на обертки (header() -> _header()), сторонние библиотеки легко патчатся автоматическим поиском и заменой для поддержки этой функциональности (либо если используются пространства имен — просто объявляется пользовательская header() в том пространстве), критическая фича для режима Request/Response

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

Частично отрефакторен системный модуль, но только частично. Больше рефакторинга красивого и разного ещё будет.

Статический анализ

Раньше весь статический анализ заключался в инспекциях IDE PhpStorm, сейчас же каждый коммит дополнительно проверяют SensioLabsInsight и Scrutinizer.
Эти два инструмента творят чудеса статического анализа. На текущий момент исправлены все Major недочеты, которые они понаходили, и частично Minor/Warning.
Инструменты активно используются, их отчёты будут уменьшаться с каждым коммитом.

Релизы

Начиная с 1.110 все сборки автоматизированы, в случае прохождения тестов после каждого коммита ночная сборка улетает на SourceForge в папку nightly, если это релиз — собирается релизная версия и падает в stable/{version} там же.
Это упрощает релизы и убирает все те неудобства, которые возникали с публикацией релизов в виде странички со ссылками на файлы в Dropbox.

Новые фичи

Во-первых, некоторые компоненты, которые уже достаточно давно разрабатывались и считаются стабильными выросли до версии 1.0.
Во-вторых, было бы обидно констатировать что из коробки никакой новой функциональности за 4 месяца не появилось.

Http сервер

Однажды уже упоминалось здесь. Это модуль, который реализовывает Http сервер на PHP, что позволяет в рамках одного процесса обрабатывать множество запросов и весьма неплохо сказывается на производительности (в несколько раз).
Если соблюдать несколько несложных правил, то все компоненты будут работать в этом режиме без изменений.

WebSockets

Тоже было здесь, тесно интегрирует WebSockets с CleverStyle CMS, позволяя отправлять данные клиенту и обратно практически в реальном времени без излишней нагрузки, по принципу устройства похоже на модуль Http сервера.

Магазин

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

Оплата Bitcoin

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

Подсветка синтаксиса

Как-то в блоге нужна была подстветка синтаксиса, появилась интеграция библиотеки Prism для этих целей. Библиотека, как и все остальные, пропатчена для работы с веб-компонентами, дружит с визуальными редакторами.

Напоследок

Система активно развивается, код становится чище, проще, понятнее, обрастает новой функциональностью, постоянно пополняется и поддерживается актуальной документация.

Обратная совместимость сохранялась всю ветку версий 1.x, версия 1.110 была последней из этой серии.
Устаревший код был недавно удален в связи с переходом на 2.х версии, так же минимальное требование к версии теперь PHP 5,5+.
При условии что все рекомендации по обновлению выполнялись, обновление с 1.110 до 2.x весьма тривиальное.

Проще всего запустить и попробовать с помощью Docker
Весь исходный код на GitHub, там же wiki с документацией.

Автор: nazarpc

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js