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