Зачем всё это затевалось?
Спойлер: не только ради флагов
Честно? Этот проект — живой эксперимент и пример того, как можно расширять EvolutionCMS, не превращая её в «монолит из костылей». Задача была простой: показать, как внедрить современный PHP-компонент прямо в админку Evo, сохранив чистоту кода и системную логику.
Сначала было написано независимое ядро на чистом PHP (никаких зависимостей от CMS, только DDD и чистая архитектура). Затем оно оформлено в Composer-пакет и аккуратно встроено в систему. Управление вынесено в нативный модуль, который работает по правилам EvolutionCMS и не ломает её жизненный цикл.
По сути, это адаптер: современная бизнес-логика снаружи, привычный интерфейс менеджера внутри. Идеальный шаблон для тех, кто хочет выносить сложные правила, аналитику и конфигурацию за пределы шаблонов и плагинов, но при этом оставаться в экосистеме Evo. Хотите расширить CMS правильно? Делайте как здесь: ядро отдельно, интеграция аккуратно, админка привычно.
Установка (быстро, как утренний кофе)
cd /core
php artisan package:installrequire ambrion/evocms-feature-flags "v0.1.0-alpha"
php artisan vendor:publish --provider="EvolutionCMS\FeatureFlags\FeatureFlagsServiceProvider"
php artisan migrate
package:installrequire капризничает — очистите кэш (php artisan cache:clear) и попробуйте снова. Она, как и мы по понедельникам, иногда нуждается в перезагрузке.
Требования (чтобы всё работало, а не «вроде работает»)
| Требование | Версия | Зачем это нужно |
|---|---|---|
| PHP | ^8.3 |
Без этого не заработают readonly-классы и прочие вкусности. 8.1–8.2 — извините, но нет. |
| EvolutionCMS CE | ≥3.1.30 |
Протестировано на этой версии. На более старых — «может работать», но мы не обещаем чудеса. |
| Composer | ^2.0 |
Чтобы зависимости подтянулись, а не убежали. |
Быстрый старт (5 минут до первого «О, работает!»)
«Хочешь увидеть магию в действии, а не читать про неё? Давай сделаем так, чтобы флаг заработал быстрее, чем закипит чайник.»
Шаг 1: Создай сниппет
Создай сниппет FeatureFlagsDemo и вставь в него:
return require MODX_BASE_PATH . 'assets/snippets/feature_flags/snippet.FeatureFlagsDemo.php';
Шаг 2: Создай документ
Создай новый документ в дереве ресурсов и вставь в контент содержимое из:
assets/snippets/feature_flags/demo.page.snippet.FeatureFlagsDemo.html
В демо уже есть стили, кнопки и интерактив. Ты увидишь не просто true/false, а живую реакцию на переключение флагов.
Шаг 3: Настрой драйвер
Открой или создай core/custom/.env и добавь:
FEATURE_FLAGS_DRIVER=config
| Значение | Что означает | Когда использовать |
|---|---|---|
config |
Правила из PHP-файла | Разработка, демо, статичные правила |
eloquent |
Правила из БД | Продакшен, частые изменения, админка |
config — проще отлаживать. Потом переключишь на eloquent без изменения кода. Магия чистой архитектуры!
Шаг 4: Проверяй!
- Открой созданный документ в фронтенде
- Нажимай кнопки в демо-интерфейсе
- Смотри, как меняется контент
Если всё работает — поздравляем, ты в теме! Если не работает — см. раздел «Не работает? Проверь это» ниже. Пиши, чем смогу - всегда помогу!
Что внутри демо-сниппета?
| Файл | Зачем нужен | Что внутри |
|---|---|---|
snippet.FeatureFlagsDemo.php |
Главный вход в демо | Маршрутизация по действиям, инициализация сервиса |
snippet.FeatureFlags.php |
Стандартный вызов флага | isEnabled(), getVariant(), передача context |
snippet.ABVariantTest.php |
A/B/C-тестирование | Детерминированное распределение вариантов |
demo.page.snippet.FeatureFlagsDemo.html |
Готовая страница демо | Кнопки, карточки, интерактив, стили |
assets/modules/feature_flags/config/feature_flags_rules.phpБез правил демо будет скучным, как понедельник утром. А с правилами — как пятничный вечер!
Сниппеты — это демо, а не догма
Файлы в assets/snippets/feature_flags/ созданы для быстрого старта и наглядной демонстрации. Не воспринимайте их как готовое решение на все случаи жизни. Модуль построен на чистом PHP-ядре (ambrion/feature-flags-core), которое живёт по правилам DDD и не зависит от CMS.
Вы можете (и должны!) писать свои вызовы, плагины или микросервисы, используя ядро как надёжный фундамент. Инстанцируйте сервис, передавайте контекст, получайте EvaluationResult — архитектура открыта, логика полностью в ваших руках. Сниппеты здесь лишь, чтобы показать: «Смотри, как это просто работает».
Застряли? Хотите обсудить кейс?
Отвечаю на вопросы по интеграции, архитектуре и лучшим практикам внедрения:
Пишите — разберём ваш сценарий вместе!
Базовый синтаксис вызова
У простого демо-сниппета FeatureFlags есть параметры:
[!FeatureFlags?
&flag=`имя_флага`
&context=`{"key":"value"}`
&yes=`<output if true>`
&no=`<output if false>`
!]
[!...!]), чтобы контекст вычислялся динамически на каждой странице.
Примеры вызова
Пример 1: Простой булев флаг (без контекста)
[!FeatureFlags?
&flag=`show_new_year_banner`
&yes=`<div class="xmas-banner">🎄 Скидки до 31 декабря!</div>`
&no=
!]
Что происходит:
- Используется дефолтный контекст:
target_id,user_role,environment,current_date,user_hash - Если флаг активен → выводится баннер, иначе → пусто
Пример 2: Передача кастомного контекста (JSON)
[!FeatureFlags?
&flag=`show_premium_badge`
&context=`{ "category": "electronics", "user_role": "premium"}`
&yes=`<span class="badge badge-premium">PREMIUM</span>`
&no=``
!]
Что происходит:
- К дефолтному контексту добавляются
category=electronicsиuser_role=premium - Флаг может использовать эти значения в правилах:
category=electronics AND user_role=premium - Контент увидит тот, кто подходит под условия — имеет роль premium и находится в категории electronics
A/B/C-тест: snippet.ABVariantTest.php
// Вызов в шаблоне:
[!ABVariantTest?
&flag=`header_variant_test`
&a=`A`
&b=`B`
&c=`C`
&default=`A`
&context=`{"user_hash":"[+userHash+]"}`
!]
Для тестирования заголовков, кнопок, изображений. Один пользователь = один вариант (детерминированно).
«Фич-флаги — это как пульт от телевизора. Нажал кнопку — картинка изменилась. Только вместо каналов — фичи, а вместо рекламы — чистый код.»
Готов к следующему шагу? Переходи к разделу «Когда это пригодится?» — там есть сценарии, от которых захочется внедрить флаги везде!
Что умеет этот модуль?
| Возможность | Зачем это вам | Пример из жизни |
|---|---|---|
| Правила оценки | Включайте фичи по роли, категории, дате, проценту трафика | «Показывать новогодний баннер только в декабре, но не админам — они и так знают» |
| A/B-тестирование | Распределяйте трафик между вариантами | «50% пользователей видят заголовок «Купи!», 50% — «Не упусти!». Кто конвертит лучше?» |
| Статистика и аналитика | Смотрите, кто и как использует флаги | «Ого, вариант B дал +15% кликов! Меняем везде.» |
| Админ-интерфейс | Управляйте флагами без правки кода | Маркетолог включил акцию в 3 клика. Разработчик доволен. |
Когда это пригодится?
Сценарий 1: Сезонный контент (без копипаста каждый декабрь)
// Правило в конфиге:
'show_new_year_banner' => [
'default' => false,
'rules' => [
['condition' => 'current_date BETWEEN 12-01 AND 12-31', 'value' => true],
],
]
// В шаблоне:
[!FeatureFlags? &flag=`show_new_year_banner` &yes=`🎄 С Новым Годом!` &no=!]
Результат: Баннер появляется сам 1 декабря, исчезает сам 1 января. Вы пьёте кофе, а не правите код.
Сценарий 2: Постепенный релиз (без «ой, всё упало»)
// Правило: 10% пользователей видят новый шаблон
'new_product_card' => [
'default' => false,
'rules' => [
['condition' => 'user_hash PERCENTAGE 10', 'value' => true],
['condition' => 'user_role=admin', 'value' => true], // админам — всегда новое
]
]
Результат: Новый шаблон видят только 10% + админы. Если что-то не так — меняете 10 на 0 и всё. Без деплоя. Без паники.
Сценарий 3: A/B-тест заголовков (данные, а не догадки)
$variant = $flags->getVariant('article_header_test', context: [
'user_hash' => md5($_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT'])
]);
$header = match($variant) {
'B' => 'Не упусти!',
'C' => 'Только сегодня!',
default => 'Купи!',
};
Результат: Через неделю смотрите статистику: «Вариант B конвертит на 23% лучше». Меняете заголовок для всех. Профит.
Статистика и аналитика: Не гадай, а смотри данные
Фич-флаги — это не просто переключатели «вкл/выкл». Это ещё и тихие летописцы, которые записывают: «Кому? Когда? Какое правило сработало?»
Что пишется в статистику (каждый вызов → одна запись):
- Вариант:
A,B,Cилиtrue/false - Вес правила:
0.25(дляPERCENTAGE 25) илиnull - Сработавшее условие:
user_role=admin AND category=electronics(для быстрой отладки) - Контекст: хеш сессии, категория, роль, документ (без персональных данных)
- Время и IP: когда и откуда пришёл запрос
Где смотреть?
В админке модуля уже готовы разделы:
- Дашборд флага: общие оценки, распределение по вариантам, динамика за период
- Журнал оценок: полный лог с фильтрами по флагу, дате, варианту и IP
- A/B-отчёты: кто попал в какой вариант, с каким весом, как часто срабатывали правила
Как включить/выключить?
Для начала включается глобально:
# В core/custom/.env (глобально)
FEATURE_FLAGS_LOG_STATISTICS=true
И для отдельного флага в конфиге или в UI:
'my_ab_test' => [
'log_statistics' => true, // ← только этот флаг пишет в БД
'rules' => [ /* ... */ ]
]
Зачем это вам?
- Перестать спорить на планёрках и смотреть на цифры, а не «мне кажется»
- Быстро чинить баги: «Почему у клиента X сработало старое правило?» → открываешь лог → видишь точное условие → фиксишь за 5 минут
- Принимать решения о раскатке фичи на 100% на основе реального поведения, а не гадания на кофейной гуще
Да, теперь ваши гипотезы подтверждаются данными. Маркетологи в восторге, разработчики спокойны, а начальство получает отчёты. Win-win-win.
Устанавливайте. Тестируйте. Включайте фичи, а не баги.
— Какой-то умный человек (возможно, вы через неделю после установки)