Pull to refresh

Наследование конфигов в Zend_Config

Reading time 5 min
Views 1.8K
Для тех, кому лень читать длинное предисловие: перемотайте до последней части «Простая идея, которая пришла мне в голову».
Я хотел поставить якорь, но хабрапарсер не разрешает :(

Zend_Config и секции


Официальная документация Zend Framework советует разделить конфигурационный файл на несколько секций, каждая из которых будет отвечать за разную среду, в которой должен работать проект.
При этом, одна секция конфига может наследовать другую, переопределяя только те параметры, которые должны быть изменены.

На первый взгляд, такая идея кажется разумной, но я столкнулся с некоторыми ограничениями этого подхода…

Вот пример конфигурационного файла из документации:
; Production site configuration data
[production]
webhost                  = www.example.com
database.adapter         = pdo_mysql
database.params.host     = db.example.com
database.params.username = dbuser
database.params.password = secret
database.params.dbname   = dbname

; Staging site configuration data inherits from production and
; overrides values as necessary
[staging : production]
database.params.host     = dev.example.com
database.params.username = devuser
database.params.password = devsecret


Итак, мы определяем полный список параметров для в секции production, а в секции staging переопределяем лишь несколько параметров для доступа к базе данных.

Ограничения секционного подхода


На практике, во-первых, хранить пароль доступа к продакшен БД в общем конфиге — не самая хорошая идея, а во-вторых, конфиг в таком виде в буквальном смысле невозможно хранить в мейнстримных системах контроля версий (СКВ) типа Git или Subversion.

Каждый разработчик, участвующий в проекте, с удовольствием будет коммитить собственные локальные настройки в секцию staging, в лучшем случае — создаст собственную секцию, отнаследуюя её от staging.
Всё это приводит к путанице и бесполезному разрастанию конфига в репозитории.

Традиционные сценарии хранения конфига в СКВ


Чтобы общий конфиг не засорялся личными настройками девелоперов, обычно его называют как-то типа config.default.ini или config.ini.default, кладут в репозиторий, а затем каждый девелопер в своём рабочем каталоге делает его копию, которую называет config.ini и которую добавляет в список игнорируемых файлов СКВ.

Казалось бы, вот оно — счастье: один раз скопировал дефолтный конфиг, вбил туда личные настройки и забыл о нём навсегда…

Как бы не так!

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

Догадываетесь, что происходит на следующий день? :)

Даже если рассматривать самую утопическую ситуацию, когда изменивший конфиг девелопер не забывает отправить письмо в корпоративную рассылку с темой «Please update your local config.ini», а получившие это письмо разработчики не забывают его прочитать и, что немаловажно, осознать и выполнить — всё равно всем им приходится вручную обновлять свои локальные конфиги, порой используя утилиты типа diff, так как со временем конфиги только увеличиваются в размерах и следить за всеми изменениями вручную не удается.

Простая идея, которая пришла мне в голову


Я решил расширить класс Zend_Config и реализовать в нём наследование конфигов.
Но как только я заглянул в его код, то сразу понял, что всё необходимое для наследования заложено в него изначально.

Итак, вот пример использования конфигов с наследованием.

Общий конфиг проекта — config.common.ini:
[production]
resources.db.adapter                = "PDO_MySQL"
resources.db.params.dbname          = "system"
resources.db.params.username        = "root"
resources.db.params.password        = ""
phpSettings.display_startup_errors  = 0
phpSettings.display_errors          = 0

[development : production]
phpSettings.display_startup_errors  = 1
phpSettings.display_errors          = 1


Мой личный конфиг — config.ini:
[production]
[development : production]
resources.db.params.dbname          = "system_laggyluke"
resources.db.params.username        = "laggyluke"
resources.db.params.password        = "mySecretPassword"


Пример реализации наследования конфигов:
// загружаем общий конфиг
// третий аргумент true означает, что конфиг не будет открыт в режиме read-only
$config = new Zend_Config_Ini('config.common.ini', 'development', true);
// проверяем, существует ли личный конфиг
if (file_exists('config.ini')) {
    // если существует - загружаем его...
    $configCustom = new Zend_Config_Ini('config.ini', 'development');
    // ...и сливаем два конфига в один
    $config->merge($configCustom);
}
// возвращаем конфиг в режим read-only, на всякий случай
$config->setReadOnly();


* This source code was highlighted with Source Code Highlighter.


Общий конфиг config.common.ini хранится в СКВ и любые новые настройки, которые добавляются в него, сразу же попадают ко всем разработчикам. В то же время, каждый разработчик может переопределить любую настройку в своём личном config.ini, который игнорируется СКВ.

Из минусов такого подхода могу отметить только то, что в личном конфиге приходится перечислять все секции, которые были объявлены в общем конфиге. Но лично для меня это не составляет особой проблемы, потому что их список меняется крайне редко, а чаще — никогда.

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

UPD.
stfalcon подсказал, что нечто подобное уже реализовано в Zend_Application, начиная с версии 1.8.2.
Tags:
Hubs:
+18
Comments 21
Comments Comments 21

Articles