Реклама
Реклама
Реклама
Реклама. Zarub Zarub - зарубежные сервисы, оплата в рублях

Bitrix D7 Config\Option и Configuration: настройки модуля, сайт и .settings.php

Настройки модуля в Битрикс живут в двух разных мирах: Bitrix\Main\Config\Option пишет в таблицу b_option (то, что меняет админ в UI), а Bitrix\Main\Config\Configuration читает .settings.php / .settings_extra.php (то, что деплоите вместе с кодом). Путаница между ними даёт классику: «на проде другое значение», «после очистки кеша пропало», «на сайте s2 опция не подхватилась».

Ниже — рабочий контракт Option и Configuration для модулей: get/set с siteId, дефолты, кеш опций, страницы настроек и типичные ошибки. Связка с остальным D7: сервис из ServiceLocator читает Option при старте, тяжёлые вычисления кладёте в Cache, результат операций отдаёте через Result. Уровень и путь бизнес-логов модуля удобно держать рядом с Option и писать через FileLogger.

Option vs Configuration: что куда

  • Option — runtime-настройки модуля/сайта в БД. Меняются из админки, CLI, миграций. Подходят для API-ключей стенда, лимитов, feature-флагов, ID инфоблоков.
  • Configuration — статичный конфиг окружения из PHP-файлов. Подходит для DSN, Redis, почты, секретов деплоя. Не для того, что админ крутит каждый день.
  • Старый COption — обёртка над тем же хранилищем. В новом коде берите D7 Option, чтобы не плодить два стиля в одном модуле.

Правило на старте модуля: если значение должно отличаться на тестовом и боевом без правки кода — это Option (или секрет в Configuration через env). Если значение — часть инфраструктуры и меняется только при деплое — Configuration.

Минимум Option: get, set, delete

<?php
use Bitrix\Main\Config\Option;

final class ModuleSettings
{
    private const MODULE_ID = 'vendor.demo';

    public function getApiToken(string $siteId = ''): string
    {
        // третий аргумент — дефолт, если строки в b_option нет
        return (string)Option::get(self::MODULE_ID, 'api_token', '', $siteId);
    }

    public function setApiToken(string $token, string $siteId = ''): void
    {
        Option::set(self::MODULE_ID, 'api_token', $token, $siteId);
    }

    public function resetApiToken(string $siteId = ''): void
    {
        Option::delete(self::MODULE_ID, [
            'name' => 'api_token',
            'site_id' => $siteId,
        ]);
    }
}

Ключевые детали API:

  • moduleId — строковый ID модуля (vendor.demo), не имя класса и не путь.
  • name — ключ опции. Держите плоский список или явный префикс (export_chunk_size), без JSON «на всё» без нужды.
  • siteId — пустая строка значит «для всех сайтов» (общая опция). Непустой LID (s1, s2) — значение только для этого сайта.
  • Дефолт в get() не пишется в БД. Он только возвращается, пока никто не сделал set().

Сайты: пустой siteId и «почему на s2 другое»

Мультисайт ломает половину интеграций именно здесь. Админ сохранил ключ на s1, а агент/крона читает с пустым siteId — получает дефолт или чужое значение.

<?php
use Bitrix\Main\Config\Option;
use Bitrix\Main\Context;

$siteId = Context::getCurrent()->getSite() ?: 's1';

// UI и публичка: читаем в контексте текущего сайта
$limit = (int)Option::get('vendor.demo', 'export_limit', '100', $siteId);

// Агент / CLI без сайта: либо явный LID, либо общая опция (siteId = '')
$limitCli = (int)Option::get('vendor.demo', 'export_limit', '100', '');

Практичная схема для модулей:

  • Параметры, общие для всей установки (лицензия модуля, глобальный feature-флаг) — siteId = ''.
  • Параметры витрины (инфоблок каталога, склад, webhook URL магазина) — с конкретным LID.
  • В формах настроек всегда сохраняйте тот же siteId, с которым потом читаете в рантайме.

Кеш опций и инвалидация

Option::get не ходит в БД на каждый вызов вслепую: значения кешируются. После Option::set / delete кеш модуля сбрасывается штатно. Проблемы начинаются, когда вы правите b_option руками SQL или копируете дамп между стендами и ждёте мгновенного эффекта на уже поднятом PHP-FPM.

<?php
use Bitrix\Main\Config\Option;

// после массовой правки опций из миграции/CLI
Option::set('vendor.demo', 'schema_version', '3');

// если правили БД в обход API — сбросьте кеш опций явно
Option::clearCache('vendor.demo');

Не кладите в Option огромные JSON (десятки килобайт на каждый hit). Для тяжёлых структур — файл в /upload/, Highload/ORM или отдельный кеш с тегами. Option — для коротких скаляров и маленьких строк.

Страница настроек модуля

Минимальный админ-файл /bitrix/modules/vendor.demo/options.php (или путь по стандарту модуля) обычно делает три вещи: проверить права, сохранить POST в Option, отрисовать форму. Упрощённый каркас:

<?php
use Bitrix\Main\Config\Option;
use Bitrix\Main\HttpApplication;
use Bitrix\Main\Loader;
use Bitrix\Main\Localization\Loc;

Loc::loadMessages(__FILE__);
$request = HttpApplication::getInstance()->getContext()->getRequest();
$moduleId = 'vendor.demo';

if ($request->isPost() && check_bitrix_sessid()) {
    Option::set($moduleId, 'api_token', trim((string)$request->getPost('api_token')));
    Option::set($moduleId, 'export_limit', (string)(int)$request->getPost('export_limit'));
    CAdminMessage::ShowNote(Loc::getMessage('VENDOR_DEMO_OPTIONS_SAVED'));
}

$apiToken = Option::get($moduleId, 'api_token', '');
$exportLimit = Option::get($moduleId, 'export_limit', '100');
?>
<form method="post" action="">
    <?= bitrix_sessid_post() ?>
    <label>API token</label>
    <input type="text" name="api_token" value="<?= htmlspecialcharsbx($apiToken) ?>">
    <label>Export limit</label>
    <input type="number" name="export_limit" value="<?= htmlspecialcharsbx($exportLimit) ?>">
    <input type="submit" class="adm-btn-save" value="Save">
</form>

На проде не забывайте: права модуля ($APPLICATION->GetGroupRight), check_bitrix_sessid(), экранирование при выводе. Секреты лучше не светить целиком в value — маска + «оставить прежнее, если поле пустое».

Configuration: .settings.php без магии

Configuration::getValue('key') читает массив из /bitrix/.settings.php и дополнений. Это не Option: админка сюда не пишет. Типичные секции — connections, cache, exception_handling, кастомные ключи вашего проекта.

<?php
use Bitrix\Main\Config\Configuration;

$cfg = Configuration::getValue('vendor.demo') ?? [];
$redisHost = (string)($cfg['redis_host'] ?? '127.0.0.1');
$redisPort = (int)($cfg['redis_port'] ?? 6379);

// точечно
$cache = Configuration::getValue('cache');
$value = Configuration::getValue('vendor.demo');

Фрагмент кастомного блока в .settings_extra.php (удобно не трогать основной файл ядра при деплое):

<?php
return [
    'vendor.demo' => [
        'value' => [
            'redis_host' => getenv('DEMO_REDIS_HOST') ?: '127.0.0.1',
            'redis_port' => (int)(getenv('DEMO_REDIS_PORT') ?: 6379),
            'webhook_secret' => getenv('DEMO_WEBHOOK_SECRET') ?: '',
        ],
        'readonly' => true,
    ],
];

readonly => true защищает секцию от записи через API конфигурации. Секреты — из env/CI, не из git с прод-значениями.

Типичные ошибки

  • Читают с одним siteId, пишут с другим. Симптом: в форме значение есть, в агенте — пусто. Фикс: один хелпер settingsSiteId() на весь модуль.
  • Кладут секреты только в Option и коммитят дамп БД. Тестовые ключи уезжают на прод. Секреты окружения — в Configuration/env, стендовые токены — в Option без коммита дампа.
  • Ожидают, что get(..., $default) сохранится сам. Дефолт виртуальный. Нужен явный set при установке модуля (InstallDB / миграция).
  • Правят b_option SQL и удивляются старому значению. Вызовите Option::clearCache($moduleId) или перезапустите php-fpm после импорта дампа.
  • Пихают в Option огромный JSON. Раздуваете таблицу и кеш опций. Вынесите payload в файл/ORM, в Option оставьте ссылку или версию схемы.
  • Путают Option и $_SESSION. Option — долгоживущие настройки; сессия — состояние пользователя. Feature-флаг «включено для всех» — Option; «мастер на шаге 2» — сессия/БД пользователя.

Чек-лист в Install/Update модуля

<?php
use Bitrix\Main\Config\Option;

final class InstallOptions
{
    private const MODULE_ID = 'vendor.demo';

    public static function seed(): void
    {
        if (Option::get(self::MODULE_ID, 'schema_version', '') === '') {
            Option::set(self::MODULE_ID, 'schema_version', '1');
            Option::set(self::MODULE_ID, 'export_limit', '100');
        }
    }

    public static function upgradeTo2(): void
    {
        if ((int)Option::get(self::MODULE_ID, 'schema_version', '1') >= 2) {
            return;
        }
        Option::set(self::MODULE_ID, 'export_limit', '200');
        Option::set(self::MODULE_ID, 'schema_version', '2');
    }
}

Так вы не зависите от «админ зашёл и сохранил форму», а агенты и CLI сразу видят осмысленные значения.

Вывод

Option — контракт runtime-настроек в БД с учётом сайта и кеша. Configuration — контракт окружения из файлов деплоя. Разведите их по ролям, зафиксируйте siteId, сидьте дефолты при установке модуля — и пропадут полдня отладки «почему на s2 пусто». Дальше по D7-серии: EventManager, HttpClient, DateTime.