Feature Flags для EvolutionCMS: Включаем фичи, а не баги!

«А что, если бы ваш сайт мог сам решать, что показывать, кому и когда? Без деплоя, без паники, без «ой, всё сломалось»?»

Представьте: у вас есть умный выключатель, который отвечает на вопрос:

«Показывать ли этот баннер этому пользователю, в этом документе, в декабре, если он не админ, но очень хочет?»

Нет, это не замена сниппету IF. Это как если бы сниппет IF выпил эспрессо, прошёл курсы по логике и научился принимать решения на основе правил, а не догадок.

В основе — ядро Feature Flags Core. Просто сделал для него удобную админку в стиле EvolutionCMS.

Зачем всё это затевалось?

Спойлер: не только ради флагов

Честно? Этот проект — живой эксперимент и пример того, как можно расширять 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 Чтобы зависимости подтянулись, а не убежали.
💡 Примечание: Модуль использует возможности современного PHP 8.3. Если ваш хостинг ещё на 7.4 — возможно, пора задуматься о переезде.

Быстрый старт (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: Проверяй!

  1. Открой созданный документ в фронтенде
  2. Нажимай кнопки в демо-интерфейсе
  3. Смотри, как меняется контент

Если всё работает — поздравляем, ты в теме! Если не работает — см. раздел «Не работает? Проверь это» ниже. Пиши, чем смогу - всегда помогу!

Что внутри демо-сниппета?

Файл Зачем нужен Что внутри
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.

Устанавливайте. Тестируйте. Включайте фичи, а не баги.

«Фич-флаги — это не про код. Это про спокойствие. Включили — работает. Выключили — не работает. Всё просто.»

— Какой-то умный человек (возможно, вы через неделю после установки)

Обсуждение

💬 Есть вопросы? Пишите в Telegram-канал или на ping@ambrion.dev