1 2 3 4 5 6 7 8 9 10 11 | common: support_email: [email protected] root_url: myhost.com photos_max_number: 6 production: email_exceptions: true development: root_url: localhost:3000 photos_max_number: 10 |
In this example you can see three sections: common will be used as a base configuration for all environments, production and development — environment specific options. Optional sections are production, development, and testing, or any other custom environment name. I’ve placed this file in config/config.yml and created lib/app_config.rb which looks like this:
1 2 3 4 5 6 7 8 | # Load application configuration require 'ostruct' require 'yaml' config = YAML.load_file("#{Rails.root}/config/config.yml") || {} app_config = config['common'] || {} app_config.update(config[Rails.env] || {}) AppConfig = OpenStruct.new(app_config) |
Now I’m able to use constructions like AppConfig.support_email
and AppConfig.root_url
. Looks like I’ve kept all my configs as DRY as possible :-)
First I’ll try to imagine how user’s code will look:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Zend::loadClass('Config'); // Use default XML-persistence engine $config = new Config('config.xml'); // Use plain text storage engine Zend::loadClass('Config_Text_Storage'); $persistance = new Config_Text_Persistance('config.ini'); $config->setPersistance($persistance); echo 'Host: ' . $config->database->host->value . '<br/>'; echo 'Port: ' . $config->database->host->port . '<br/>'; echo 'Database: ' . $config->database->name . '<br/>'; echo 'User: ' . $config->database->user . '<br/>'; echo 'Password: ' . $config->database->password . '<br/>'; |
I think configuration representation in memory will be objects tree. Config will be user friendly class with several simple functions like load, save configuration, change persistance engine, access root node of configuration object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | /** Exception for Config class. */ require_once 'Config/Exception.php'; /** Exception for Config class. */ require_once 'Config/Persistance/Interface.php'; class Config { private $_persistance = null; private $_rootNode = null; /** * Create configuration object. You can pass string argument with * path to XML configuration file. * * @param string $xmlConfigPath */ public function __construct($xmlConfigPath = null) { require_once 'Config/XML/Persistance.php'; $this->_persistance = new Config_XML_Persistance($xmlConfigPath); if ($xmlConfigPath) $this->load(); } /** Save configuration. */ public function save() { $this->checkPersistanceAssigned(); $this->_persistance->save(); } /** Load configuration */ public function load() { $this->checkPersistanceAssigned(); $this->_persistance->load(); $this->_rootNode = $this->_persistance->getRootNode(); } /** * Get config variable with $name name. * * @param string $name * @return mixed */ public function __get($name) { return $this->_rootNode->__get($name); } /** * Set persistance object. * * @param Config_Persistance_Interface $persistance */ public function setPersistance(Config_Persistance_Interface $persistance) { $this->_persistance = $persistance; } /** Check if persistance object has been assigned. */ private function checkPersistanceAssigned() { if (!$this->_persistance) throw new Config_Exception('No configuration persistance object assigned'); } } |
Configuration nodes are very easy to code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <?php class Config_Node { private $_values; public function __construct($root = null) { $this->_values = $root; } public function __get($name) { if (array_key_exists($name, $this->_values) === false) throw new Config_Exception('No config node "' . $name . '" defined.'); if (is_object($this->_values[$name]) && sizeof($this->_values[$name]->_values) == 1 && array_key_exists('value', $this->_values[$name]->_values)) { return $this->_values[$name]->__get('value'); } return $this->_values[$name]; } public function __set($name, $value) { $this->_values[$name] = $value; } } |
Now I’ll try to develop simplest strategy for XML-based storage.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | /** Exception for Config_XML_Persistance class. */ require_once 'Config/XML/Exception.php'; /** Interface Config_Persistance_Interface. */ require_once 'Config/Persistance/Interface.php'; /** Configuration node class Config_Node. */ require_once 'Config/Node.php'; class Config_XML_Persistance implements Config_Persistance_Interface { private $_xmlConfigFile = null; private $_rootNode = null; public function __construct($xmlConfigFile = null) { $this->_xmlConfigFile = $xmlConfigFile; } /** Load configuration from XML file. */ public function load() { if (!$this->_xmlConfigFile) throw new Config_XML_Exception('Config file name is not assigned.'); $dom = new DOMDocument(); $dom->preserveWhiteSpace = false; $dom->load($this->_xmlConfigFile); $this->_rootNode = $this->populateConfig($dom->documentElement); } /** Save configuration to XML file. */ public function save() { throw new Config_XML_Exception('save() function is not implemented for this persistance type.'); } /** Returns root configuration node. */ public function getRootNode() { return $this->_rootNode; } /** * Read nodes starting from $root and fill Config_Node objects. * Returns root Config_Node object. * * @param DomNode $root * @return Config_Node */ private function populateConfig($root) { $configNode = new Config_Node(); foreach ($root->attributes as $attribute) $configNode->__set($attribute->name, $attribute->nodeValue); foreach ($root->childNodes as $node) { if ($node->nodeType === 1) $configNode->__set($node->tagName, $this->populateConfig($node)); else $configNode->__set('value', $node->nodeValue); } return $configNode; } } |
Here simple configuration XML-file:
1 2 3 4 5 6 7 8 9 10 | <?xml version="1.0" encoding="UTF-8"?> <config> <database type="mysql"> <host port="3306">localhost</host> <name>test</name> <user>root</user> <password>123456</password> </database> </config> |
You can see one obscure aspect of the configuration node class. Please look at host node of configuration: we have an attribure and text node in it. What about ambiguity? How we can access text node? We can’t do following:
1 2 | $host = $config->database->host; $host = $config->database->host->port; |
My solution for this problem is pseudo-variable value:
1 2 | $host = $config->database->host->value; $host = $config->database->host->port; |
If there are no attributes, pseudo-variable value will be used by default.
You can download my configuration classes here.
The post Zend Framework: Thoughts about Zend_Config first appeared on Dmytro Shteflyuk's Home.]]>