[zend framework][Yaml] Zend_Config_Yamlのようなものを考えてみる2
5月 19th, 2008 Posted in Zend Framework前回に引き続きZend_Config_Yaml.phpのようなものを考えてみました。
Yamlのパーサは前回挙げたPHP拡張モジュール SyckとPHPライブラリのspycに対応しています。 Zend_Config_Iniでは継承を示すトークンに「:(コロン)」を使っていましたが、Yamlで「:」は特別な意味を持っているので[<]を使いました。
実装は以下のようになっています。
< ?php require_once 'Zend/Config.php'; class Wads_Config_Yaml extends Zend_Config { /** * String that symbol extended section */ protected $_extendsSection = ';extends'; /** * constructor * * @param string $filename * @param mixed $section * @param boolean $allowModifications * @throws Zend_Config_Exception */ public function __construct($filename, $section = null, $allowModifications = false) { if (empty($filename)) { /** @see Zend_Config_Exception */ require_once 'Zend/Config/Exception.php'; throw new Zend_Config_Exception('Filename is not set'); } $yamlArray = $this->_parse_yaml_file($filename); $preProcessedArray = array(); foreach ($yamlArray as $key => $data) { $bits = explode('< ', $key); $numberOfBits = count($bits); $thisSection = trim($bits[0]); switch (count($bits)) { case 1: $preProcessedArray[$thisSection] = $data; break; case 2: $extendedSection = trim($bits[1]); if(!is_array($data)) { $data = array($data); } $preProcessedArray[$thisSection] = array_merge(array($this->_extendsSection=>$extendedSection), $data); break; default: /** @see Zend_Config_Exception */ require_once 'Zend/Config/Exception.php'; throw new Zend_Config_Exception("Section '$thisSection' may not extend multiple sections in $filename"); } } if (null === $section) { $dataArray = array(); foreach ($preProcessedArray as $sectionName => $sectionData) { $dataArray[$sectionName] = $this->_processExtends($preProcessedArray, $sectionName); } parent::__construct($dataArray, $allowModifications); } elseif (is_array($section)) { $dataArray = array(); foreach ($section as $sectionName) { if (!isset($preProcessedArray[$sectionName])) { /** @see Zend_Config_Exception */ require_once 'Zend/Config/Exception.php'; throw new Zend_Config_Exception("Section '$sectionName' cannot be found in $filename"); } $dataArray = array_merge($this->_processExtends($preProcessedArray, $sectionName), $dataArray); } parent::__construct($dataArray, $allowModifications); } else { if (!isset($preProcessedArray[$section])) { /** @see Zend_Config_Exception */ require_once 'Zend/Config/Exception.php'; throw new Zend_Config_Exception("Section '$section' cannot be found in $filename"); } parent::__construct($this->_processExtends($preProcessedArray, $section), $allowModifications); } $this->_loadedSection = $section; } /** * Loads a YAML file and parse data to PHP array * * @param string $filename * @throws Zend_Config_Exception * @return array */ protected function _parse_yaml_file($filename) { if (extension_loaded('syck') && in_array('syck_load', get_extension_funcs('syck'))) { $yamlArray = syck_load(file_get_contents($filename)); return $yamlArray; } elseif (class_exists('spyc') && is_callable(array('Spyc', 'YAMLLoad'))) { $yamlArray = Spyc::YAMLLoad($filename); return $yamlArray; } require_once 'Zend/Config/Exception.php'; throw new Zend_Config_Exception('YAML loader function not found'); } /** * Helper function to process each element in the section and handle * the "extends" inheritance keyword. * * @param array $yamlArray * @param string $section * @param array $config * @throws Zend_Config_Exception * @return array */ protected function _processExtends($yamlArray, $section, $config = array()) { $thisSection = $yamlArray[$section]; if(!is_array($thisSection)) { return $thisSection; } foreach ($thisSection as $key => $value) { if (strtolower($key) == $this->_extendsSection) { if (isset($yamlArray[$value])) { $this->_assertValidExtend($section, $value); $config = $this->_processExtends($yamlArray, $value, $config); } else { /** @see Zend_Config_Exception */ require_once 'Zend/Config/Exception.php'; throw new Zend_Config_Exception("Section '$section' cannot be found"); } } else { if(is_int($key) && array_key_exists($key, $config)) { $config[] = $value; } else { $config[$key] = $value; } } } return $config; } }
Yamlデータのパースはコンストラクタの_parse_yaml_file()関数で行っています。この関数でSyckもしくはspycの関数を呼び出して処理しています。実はこの関数の実装は書いている途中で見つけたいしなおさんのページのものをそのまま使わせていただきました。
使い方はZend_Config_Xml/Iniと同じです。継承のところは、たとえば以下のようなデータがあったとき
(config.yaml) extended : foo:foo bar:bar extending < extended : hoge:hoge piyo:piyo
以下のようにできます。
require_once "Zend_Config_Yaml.php"; $config = new Zend_Config_Yaml("./config.yaml"); echo $config->extending->foo; // foo echo $config->extending->bar; // bar echo $config->extending->hoge; // hoge echo $config->extending->piyo; // piyo
リストのハッシュの形式の場合はちょっと微妙です。
(config.yaml) extended : - foo - bar extending < extended : - hoge - piyo
のようなとき、
require_once "Zend_Config_Yaml.php"; $config = new Zend_Config_Yaml("./config.yaml"); echo $config->extending->{0}; // foo echo $config->extending->{1}; // bar echo $config->extending->{2}; // hoge echo $config->extending->{3}; // piyo
のようになります。foreachなどでループで処理するだけなら問題ありませんが、インデックスに意味があるような場合は使えません。もしこれでも役に立ちそうであれば、使ってみてください。
参考URL
・2008/06/24 追記
クラスのプリフィックスにZend_を使うのはいけないということなので、Zend_の部分をWads_に変更しました。