Работейки по новата ми CMS система - Control Depo 3 ми се наложи да имам малко по advanced конфигурационен файл, така че за в бъдеще да ми е по-лесно да се наместват различните части на системата. Преди ползвах стандартното за едно PHP приложение - един файл config.php:
// start config
$_CONFIG = array();
// database
$_CONFIG['db_host'] = 'localhost';
$_CONFIG['db_name'] = 'project';
$_CONFIG['db_user'] = 'root';
$_CONFIG['db_pass'] = 'password';
// languages
$_CONFIG['default_language'] = 'bg';
$_CONFIG['laguages'] = array('bg' => 1, 'en' => 2, /* ... */);
// session
$_CONFIG['session_salt'] = 'SD23aeda';
$_CONFIG['session_expire'] = 4*60*60;
// ... и така много много реда код
После тази глобална променлива( $_CONFIG) когато ми трябва се вика със global и се ползва. Обаче от една страна че не е много красиво така написано, но от друга и това с global просто прави кода малко разхвърлян(на английски имат страхотна дума за това - messy) .
Това което ми трябваше основно е може да имам няколко enviroment конфигурационни файла подобно на Rails. Другото важно нещо беше да се побира в един екран, така че да не се налага да скролирам и да мога с един поглед да виждам всичко което ми трябва. И естествено да не е много сложно и да работи достатъчно бързо.
Първо погледнах Zend_Config, защо все пак са component-based-framework и може директно да видя как работи конфигурационната им система. И общо взето това което видях не ми хареса въобще. Много ми напомня на LEGOs, Play-Doh, and Programming от Jamis Buck, за която бях писал преди време(даже има я качена на видео в confreaks заедно с цялото rubyconf 2008).
От една страна колко пъти ще искам да ползвам XML и INI за конфигурация?! От една страна php си има array(), с която идеално може да се запишат всички неща който ни трябват и да са достатъчно четими. От друга самото четене на огромен xml/ini/yml/… файл отнема време и ресурси и са доста по-бавни от прост php код.
Добре че поне тук от Zend са сложили подразбиране да се ползва само php масив. Но от тук дойде 2рото ми учудване. Това а именно самата работа на имплементация на Zend_Config. Идеята е доста проста подава се масив, който се обгръща(wrap) от Zend_Config обект и след това се работи само със Zend_Config обект. Пример:
// Create the object-oriented wrapper upon the configuration data
$config = new Zend_Config(array(
'webhost' => 'www.example.com',
'database' => array(
'adapter' => 'pdo_mysql',
'params' => array(
'host' => 'db.example.com',
'username' => 'dbuser',
'password' => 'secret',
'dbname' => 'mydatabase'
)
)
));
// Print a configuration datum (results in 'www.example.com')
echo $config->webhost;
// Use the configuration data to connect to the database
$db = Zend_Db::factory($config->database->adapter,
$config->database->params->toArray());
// Alternative usage: simply pass the Zend_Config object.
// The Zend_Db factory knows how to interpret it.
$db = Zend_Db::factory($config->database);
Идеята като цяло е много хубава, и при добро желание може човек да си направи основната част класовете му приемат Zend_Config обекти. Но има едно НО и то доста голямо. Защо ние е това? Единствения плюс който се сетих е че може да се направи immutable config обект. Но за сметка на това ще има доста излишен код и много памет за съхраниението на Zend_Config обекти и техните атрибути. А и колкото и да обичам __set/__get магиите тук ми се виждат напълно излишни защото както в примера:
$config->database->params->toArray()
// Това би представлявало нещо такова като backtrace
$config->__get('database') // магически се търси атрибута 'database'
$config->_data['database'] // това което се връща тук пак е Zend_Config обект, който ще го нарека $object
$object->__get('params') // пак същото за
$object->_data['params'] // друг $object
$object->toArray(); // автоматично всеки Zend_Config обект, който се стрещне му се вика Zend_Config::toArray()
$object->_data // това за което ни трябва
Zend_Config си има и своите плюсове(immutable, секции, default стойности) и може за някое наистина “enterprise” приложение с много разчленена конфигурация (и много стабилен и мощен сървър
) да върши работа. Но не е за мен и аз си предпочитам “голите” array(). Все пак
No code is faster than no code
- някъде го чух това.
Така че какво реших ? Ами в предишния ми пост - PHP tips - include и return, описах метода за връщане на стойности от php файл и реших него да ползвам плюс малко правила и подрежанки. Сега имам следните файлове:
- /config/config.php - тук ще е основната конфигурация
- /config/enviroment/ - в тази папка ще се съдържат конфигурационните файлове за различните среди за работа, като стандартно имам 3 вида среди
- /config/enviroment/development.php
- /config/enviroment/test.php
- /config/enviroment/production.php
- /config/mailer.php - тук ще има конфигурация за mail системата, тя е изнесена в отделен файл, защото тук няма само да се връщат config данни, а и ще се вързва към smtp сървър (ако трябва), ще се настройват достъпи и други подобни неща. За разлика от другите конфигурационни файлове mailer.php се вика само когато се налага да се изпращат писма не по-рано.
- /config/routes.php - това са настройките ми за различните пътища, подобно на Rails (пак)
Това са различните конфигурационни файлове (поне за сега).
return array(
'database' => array(
'engine' => 'mysql',
'host' => 'localhost',
'name' => 'project',
'user' => 'root',
'pass' => 'password'
),
'i18n' => array(
'default' => 'bg',
'languages' => array('bg' => 1, 'en' => 2, /* ... */)
),
'session' => array(
'engine' => 'cookie',
'salt' => 'vW34Aaasa',
'expire' => 4*60*60
), // на последния ред имам , защото при добавяне на нов ред да не ми се налага да я слагам
// а и SVN/Git ще го сметне като изтрит ред и после добавен ред
// ... още конфигурация ...
);
// /config/enviroment/development.php - примерно
return array(
'database' => array(
'engine' => 'mysql',
'host' => 'localhost',
'user' => 'root',
'pass' => '',
'name' => 'project_dev'
),
'smarty' => array(
'compile_check' => true,
'force_compile' => false,
'debugging' => false,
'caching' => false,
'cache_lifetime' => 0
),
'logging' => true,
'display_errors' => 1,
);
А в bootstrap-а имам просто това
// ENVIROMENT е просто константа в която казва в кой режим на работа е приложението $_CONFIG = array_merge_recursive(include($_CFGDIR . '/config.php'), include($_CFGDIR . '/enviroments/' . ENVIROMENT . '.php')); // ... код ... // общо взето извличам всичко което ми трябва от $_CONFIG и го разпределям по обекти // така че да не ми трябва повече $_CONFIG // ... код ... unset($_CONFIG /* заедно с още няколко вече не потребни ми променливи */);
Това решение ми се вижда най-елегантно, в моя случай. Може и да не може да се нарече “система” но е достатъчно надежно и ефективно да ми свърши работа. Все пак съм фен на Convention over configuration И ако ми се наложи да имам подобен на Zend_Config обект, с които да работя мисля да не променям основните неща, а просто този обект да използва ArrayAccess, но за това друг път, че сега май малко по-дълго стана от плануваното.
Надявам се някой да намери този пост полезен и ако съвети и идеи ще се радвам да ги чуя
.


December 2nd, 2008 at 8:44 am
Някои неща в Zend Framework наистина са прекалени в стремежа си да са обектно-ориентирани (поне по мое мнение). При подобна ситуация веднъж писах в Nabber форума, и Матю Уиъри О’Фини ми отговори точно в този дух: “такива са ооп правилата, така се прави в обектно-ориентираното програмиране и т.н.”. Аз не съм на точно това мнение и си мисля, че има неща, които може да се направят по-добро като се облягаме повече на възможностите на PHP и по-малко на сляпото следване на парадигмите на ООП. В крайна сметка, аз си мислю, че ако не внимават, това ще им изяде главата
Отдавна се забелязва колко тромаво е почти всичко в ZF, и как със всяка следваща версия производителността пада. Сега има preview версия на 1.7, но още не съм я разглеждал как работи. Абе — шъ фидим
December 2nd, 2008 at 2:07 pm
Така е
Аз често гледам Zend Framework за някой идеи и като се замисля единственото което съм ползвал в проект е Zend_Mail. Но пък с него имах страхотни проблеми относно encode-инга на кирилица в header-ите и се наложи да го хаквам малко.