1: <?php
2: /**
3: * Pry Framework
4: *
5: * LICENSE
6: *
7: * This source file is subject to the new BSD license that is bundled
8: * with this package in the file LICENSE.txt.
9: *
10: * @version $Revision: 276 $
11: */
12:
13: /**
14: * Classe permettant la lecture de fichier de config au format INI. Basée sur Zend_Config_Ini
15: * Il est possible de créer un héritage entre les section à l'aide du caractère ":"
16: * Si une clé contient un "." cela sera considérer comme un séparateur pour créer une sous propriété.
17: * Par exemple :
18: * <code>
19: * [prod]
20: * db.host = mysite.com
21: * db.user = root
22: * [dev : prod]
23: * db.host = localhost
24: * </code>
25: * @package Config
26: * @version 1.0.0
27: * @author Olivier ROGER <oroger.fr>
28: *
29: */
30: class Config_Ini extends Config_Config
31: {
32:
33: /**
34: * Caractère représentant l'héritage
35: * @var string
36: */
37: private $inheritanceSeparator = ':';
38:
39: /**
40: * Caractère représentant les sous propriété
41: * @var string
42: */
43: private $separator = '.';
44:
45: /**
46: * Initialise la lecture d'un fichier de config au format ini
47: * @param string $file Chemin vers le fichier à lire
48: * @param string $section Section du fichier à lire. Null pour tout lire
49: * @throws RuntimeException
50: */
51: public function __construct($file,$section = null)
52: {
53: if(empty($file))
54: throw new RuntimeException("Filename is not set");
55:
56: $arrayData = array();
57:
58: $iniData = $this->load($file);
59:
60: if($section == null)
61: {
62: //Chargement du fichier complet
63: foreach($iniData as $sectionName => $data)
64: {
65: if(!is_array($data))
66: {
67: $arrayData = $this->_arrayMergeRecursive($arrayData, $this->parseKey($sectionName, $data, array()));
68: }
69: else
70: {
71: $arrayData[$sectionName] = $this->parseSection($iniData,$sectionName);
72: }
73: }
74:
75: parent::__construct($arrayData);
76: }
77: else
78: {
79: //Chargement d'une section en particulier
80: if(!is_array($section))
81: $section = array($section);
82:
83: foreach ($section as $sectionName)
84: {
85: if(!isset($iniData[$sectionName]))
86: throw new RuntimeException("Section $sectionName can't be found");
87:
88: $arrayData = $this->_arrayMergeRecursive($this->parseSection($iniData, $sectionName), $arrayData);
89: }
90:
91: parent::__construct($arrayData);
92: }
93:
94: }
95:
96: /**
97: * Charge le fichier
98: * @param string $file Fichier à charger
99: * @return array Données chargée
100: * @throws RuntimeException
101: */
102: private function load($file)
103: {
104: $rawArray = $this->parse($file);
105: $iniArray = array();
106:
107: //Recherche d'héritage entre les sections
108: foreach($rawArray as $key => $data)
109: {
110: $sections = explode($this->inheritanceSeparator,$key);
111: $currentSection = trim($sections[0]);
112: $nbSection = count($sections);
113: switch($nbSection)
114: {
115: // Section simple
116: case 1 :
117: $iniArray[$currentSection] = $data;
118: break;
119:
120: // Section avec héritage
121: case 2 :
122: $inheritedSection = trim($sections[1]);
123: // On ajoute une clé $inherit pour définir de qui hérite la section
124: $iniArray[$currentSection] = array_merge(array('$inherit' => $inheritedSection),$data);
125: break;
126: default:
127: throw new RuntimeException("Section $currentSection can't inherit from multiple section");
128: }
129: }
130:
131: return $iniArray;
132: }
133:
134: /**
135: * Parse un fichier ini
136: * @param string $file Fichier à charger
137: * @return array Données parsée
138: * @throws Exception
139: */
140: private function parse($file)
141: {
142: //Supprime les erreurs et warning pour les transformer en exception
143: set_error_handler(array($this,'errorHandler'));
144: $data = parse_ini_file($file,true);
145: restore_error_handler();
146: if($this->errorStr !== null)
147: throw new Exception("Can't parse Ini file : ".$this->errorStr);
148:
149: return $data;
150: }
151:
152: /**
153: * Parse chaque élément de la section et gère la clé '$inherit' qui permet de détecter un
154: * héritage entre section. Passe ensuite les données à parseKey pour gérer les sous propriété
155: * @param array $iniArray Tableau des données
156: * @param strng $section Nom de la section
157: * @param array $config
158: * @return array
159: * @throws Exception
160: */
161: private function parseSection($iniArray, $section, $config = array())
162: {
163: $currentSection = $iniArray[$section];
164:
165: foreach($currentSection as $key => $value)
166: {
167: if($key == '$inherit')
168: {
169: //Si la section courante hérite d'une autre section
170: if(isset($iniArray[$value]))
171: {
172: $this->checkForCircularInheritance($section,$value);
173: $config = $this->parseSection($iniArray, $value, $config);
174: }
175: else
176: {
177: throw new Exception("Can't found the inherited section of ".$section);
178: }
179: }
180: else
181: {
182: //Si la section est indépendante
183: $config = $this->parseKey($key, $value, $config);
184: }
185: }
186:
187: return $config;
188: }
189:
190: /**
191: * Assigne les valeurs de clés aux propriété. Gère le séparateur de propriété pour créer des
192: * sous propriété
193: * @param string $key
194: * @param string $value
195: * @param array $config
196: * @return array
197: * @throws Exception
198: */
199: private function parseKey($key,$value,$config)
200: {
201: if(strpos($key, $this->separator) !== false)
202: {
203: $properties = explode($this->separator,$key,2);
204: if(strlen($properties[0]) && strlen($properties[1]))
205: {
206: if(!isset($config[$properties[0]]))
207: {
208: if($properties[0] === '0' && !empty($config))
209: {
210: $config = array($properties[0] => $config);
211: }
212: else
213: {
214: $config[$properties[0]] = array();
215: }
216: }
217: elseif(!is_array($config[$properties[0]]))
218: {
219: throw new Exception("Cannot create sub-key for '{$properties[0]}' as key already exists");
220: }
221: $config[$properties[0]] = $this->parseKey($properties[1], $value, $config[$properties[0]]);
222: }
223: else
224: {
225: throw new Exception("Invalid Key : ".$key);
226: }
227: }
228: else
229: {
230: $config[$key] = $value;
231: }
232:
233: return $config;
234: }
235:
236:
237: }