В прошлой статье мы разобрались, как можно хранить конфигурацию системы и как её можно использовать.
Однако, мы подразумевали простейший вариант, когда система работает в единственной копии на сервере. Усложним положение:
Итак, бравые разработчики Вася, Петя, Миша и Гриша в поте лица разрабатывают очередную социальную сеть нового поколения. Каждый ведёт разработку в своей локальной версии на своём компьютере. Наработки они выкладывают на локальный тестовый сервер (где-нибудь в офисе в шкафу пылиться). Выкладывают, конечно, не по FTP, а с использованием какой-нибудь системы контроля версия, например, Mercurial.
Ведущий программист проверяет обновления на наличие ошибок и в случае чего даёт по шее. Если же ошибок не видно и локальная версия достигает какого-то стабильного состояния: все изменения отправляются уже на боевой сервер. Вернее на сервера, потому что проект крупный и серверов уже аж три штуки стоит.
Итого, система работает уже не в одной копии, а в восьми (4 разработчика, локальный сервер, 3 рабочих сервера). Назовём то, где работает конкретная копия системы платформой и дадим каждой имя (платформа server.1, платформа vasya, платформа local и т.д.).
Понятно, что конфигурация системы будет иметь одинаковую структуру для всех платформ, но вот на каждой конкретной значения параметров могут отличаться. То есть, у каждого из разработчиков могут быть свои параметры локальной БД. Или Вася захотел включить у себя вывод отладочной информации, а Петя этого не захотел. И так далее.
Подумаем, как это можно организовать.
Изменения одного файла
У нас есть конфигурационный файл (config.php
) из предыдущей статьи:
return array( 'db' => array( 'host' => 'localhost', 'user' => 'vasa', // ... ), 'debug' => false, // ... ); |
Будем рассматривать такой формат хранения. При использовании XML или INI-файлов ничего в самой сути не изменится.
У каждого из разработчиков, допустим, могут быть свои настройки локальной БД. Можно заставить всех создать у себя базу с одинаковыми настройками. Но, во-первых, БД тут просто для примера. Во-вторых, на рабочих серверах в качестве хоста будет явно не «localhost».
Что делать? Простейшее решение: брать, да прямо в своём локальном config.php
писать свои настройки. Ещё кто-то должен вписать настройки в конфиги на серверах.
Первый минус такого подхода: у нас система контроля версий, вследствии чего будут постоянные конфликты. Можно заигнорить этот файл (.hgignore
в Mercurial), но эту уже попахивает грязным хаком и никак не избавляет от второго минуса.
Второй минус: структура конфигурации у нас одна на всех. Если кто-то захочет добавить в неё какую-то новую секцию или изменить значение общего для всех параметра, ему придётся взять на себя функции системы контроля: «Эй, поцоны, а внесите себе такие-то изменения и на серверах не забудьте».
По-хорошему нам требуется разбить конфигурацию на две часть:
- Общая структура, одинаковая для всех, обрабатываемая системой контроля
- Локальные настройки, изменяющие нужные параметры из общей структуры
Слияние конфигов
Пример, как можно организовать данные требования.
Есть у нас базовый конфиг (config.php):
return array( 'db' => array( 'host' => '192.168.0.27:3307', 'username' => 'user', 'password' => 'password', 'dbname' => 'db', ), 'debug' => false, 'texts' => array( 'title' => 'Супер-пупер социальная сеть!', 'copy' => '© Мега-пупер программисты, 2002-2011'; ), ); |
И локальный конфиг у разработчика Васи (local.php):
return array( 'db' => array( 'host' => 'localhost', ), 'debug' => true, ); |
При загрузке локальный сливается с базовым. Указанные в локальном параметры перекрывают базовые, не указанные остаются прежними, массивы сливаются рекурсивно. И, в конце концов, получается массив с которым уже работает система на локалке у Васи:
return array( 'db' => array( 'host' => 'localhost', // Перекрыт 'username' => 'user', 'password' => 'password', 'dbname' => 'db', ), 'debug' => true, // Перекрыт 'texts' => array( 'title' => 'Супер-пупер социальная сеть!', 'copy' => '© Мега-пупер программисты, 2002-2011'; ), ); |
base+local vs платформы
И здесь вновь несколько вариантов, как всё организовать.
Можно, как в прошлом пункте, держать два файла base.php
и local.php
. Локальный будет у каждого свой и его следует заигнорировать в системе контроля версий.
А можно по другому. Вспомним, что все наши машины мы назвали платформами и дали им имена. Создадим для каждой из них свой файл:
config.php # базовый vasya.php petya.php misha.php grisha.php test.php server1.php server2.php server3.php |
Теперь конфигурации всех платформ лежат вместе и их не нужно игнорировать. При старте системы следует определить текущую платформу (об этом ниже) и использовать нужный файл.
Минус подхода: у Васи на локалке будет лежать конфигурация Пети. Минус довольно блёклый: с Васей из-за лишнего файла ничего не станет.
Плюсы:
- Если Вася ведущий программист, то ему, наоборот, не помешает посмотреть, чего-там Петя напортачил.
- Конфиги удалённых серверов можно править у себя и просто коммитить, а не лезть на сами сервера.
- Ещё несколько более важных плюсов, которые рассмотрим далее.
Будем использовать второй подход.
Определение платформы
Итак, системе при запуске следует разобраться, а где собственно она запускается. У Миши, Гриши или где-то на продакшене.
В старые времена, когда я в одиночку разрабатывал какой-нибудь сайтик (жесть были, а не сайтики), делал примерно так (работал под Win):
define('PLATFORM_NORMAL', !file_exists('C:\Windows')); $config = array( 'db_host' => PLATFORM_NORMAL ? 'mysql.hosting.ru' : 'localhost', 'db_user' => PLATFORM_NORMAL ? 's3298e34r4urn' : 'test', // ... ); |
Есть папка WINDOWS, значит это моя тачка, нет — сервер.
А теперь мы сохраним суть, но попробуем сделать менее стрёмно. Всё что нам по-идее нужно, это метод, определяющий платформу.
public function definePlatform() { if (file_exists('/home/vasya')) { return 'vasya'; } elseif (file_exists('/home/petya')) { return 'petya'; } elseif { // ... } } |
Ещё менее стрёмно, использовать имя машины.
Ещё лучше просто использовать файл platform.php
(выше корня системы или заигнорированный), примерно такого содержания:
return 'vasya'; |
Определение платформы в итоге:
public function definePlatform() { return include(__DIR__.'/../platform.php'); } |
И ещё много чего придумать можно.
Слишком много буков и спать уже пора. Продолжим в другой раз.
file_exists(‘C:\Windows’));
у меня винда на диске E и папка называется WIN.0
это мелочи :)
видал огромный свич с именами машин :)
IvanSCM, 26.01.2011, 15:27
Иногда у нас значение ключа конфига — массив. Тут нужно по-хитрому мержить.
Костег, 30.01.2011, 18:14
>Иногда у нас значение ключа конфига – массив. Тут нужно по-хитрому мержить.
Не совсем понимаю о чём ты. Приведи пример.
vasa_c, 30.01.2011, 18:47
'host' => 'localhost', // Перекрыт
Сейчас не могу придумать пример, но иногда бывает нужно подставить не скалляр (‘localhost’) а какой-нить список. Ессно не список хостов, ну например каких-нибудь кеширующих драйверов. Ну так вот, нужно придумать какие-то директивы, когда дочерний список будет перекрывать родительский, а когда будет сливаться с ним
Костег, 30.01.2011, 19:04
Ну, в принципе «db» и есть список, где «host» элемент. Можно и «host» списком сделать.
У меня массивы мержатся. Те ключи которые есть, перекрывают старые. Старые, которых не перекрыли остаются (см. статью). Пока хватало такого поведения.
А что ещё можно придумать интересного?
vasa_c, 30.01.2011, 19:10
я придумал пример. У нас есть модуль Sys_Notification. Он качает РССку и показывает ее в админке. Модуль используется другими модулями.
конфиг мержится, и в итоге нужно получить все 3 ссылки на RSS, не заменяя их а как бы append’я.
Костег, 30.01.2011, 19:19
Да, возможная ситуация. Хотя у меня не встечалась или как-то по иному решал.
Впрочем, в рамки предложенного вполне попадает, немного алгоритм дополнить только нужно.
vasa_c, 30.01.2011, 19:40
>Есть папка WINDOWS, значит это моя тачка, нет — сервер.
Этим все сказано!
Evgeniy, 14.06.2011, 18:49
Вот не могу понять как в моей ситуации лучше сделать.
Тестовый сервер имеет свой .htaccess, продакшен — свой.
Как такой случай можно обыграть?
И есть ли какие нибудь средства таким образом манипулировать файлами?
Заранее спасибо.
Владимир, 7.03.2012, 1:34
Владимир, ну сначала нужно подумать почему htaccess разный, может можно сделать одинаковый.
Ну а так — заигнорить его в системе контроля версий и иметь разные на разных платформах.
Можно при деплое генерировать его на продакшене из конфигов, как здесь я примерно писал: http://blgo.ru/blog/2011/07/30/config-nginx/
vasa_c, 7.03.2012, 12:45