1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10:
11:
12: namespace Nette\Config;
13:
14: use Nette;
15:
16:
17:
18: 19: 20: 21: 22:
23: final class ConfigAdapterIni implements IConfigAdapter
24: {
25:
26:
27: public static $keySeparator = '.';
28:
29:
30: public static $sectionSeparator = ' < ';
31:
32:
33: public static $rawSection = '!';
34:
35:
36:
37: 38: 39:
40: final public function __construct()
41: {
42: throw new \LogicException("Cannot instantiate static class " . get_class($this));
43: }
44:
45:
46:
47: 48: 49: 50: 51: 52: 53:
54: public static function load($file, $section = NULL)
55: {
56: if (!is_file($file) || !is_readable($file)) {
57: throw new \FileNotFoundException("File '$file' is missing or is not readable.");
58: }
59:
60: Nette\Tools::tryError();
61: $ini = parse_ini_file($file, TRUE);
62: if (Nette\Tools::catchError($msg)) {
63: throw new \Exception($msg);
64: }
65:
66: $separator = trim(self::$sectionSeparator);
67: $data = array();
68: foreach ($ini as $secName => $secData) {
69:
70: if (is_array($secData)) {
71: if (substr($secName, -1) === self::$rawSection) {
72: $secName = substr($secName, 0, -1);
73:
74: } elseif (self::$keySeparator) {
75:
76: $tmp = array();
77: foreach ($secData as $key => $val) {
78: $cursor = & $tmp;
79: foreach (explode(self::$keySeparator, $key) as $part) {
80: if (!isset($cursor[$part]) || is_array($cursor[$part])) {
81: $cursor = & $cursor[$part];
82: } else {
83: throw new \InvalidStateException("Invalid key '$key' in section [$secName] in '$file'.");
84: }
85: }
86: $cursor = $val;
87: }
88: $secData = $tmp;
89: }
90:
91:
92: $parts = $separator ? explode($separator, strtr($secName, ':', $separator)) : array($secName);
93: if (count($parts) > 1) {
94: $parent = trim($parts[1]);
95: $cursor = & $data;
96: foreach (self::$keySeparator ? explode(self::$keySeparator, $parent) : array($parent) as $part) {
97: if (isset($cursor[$part]) && is_array($cursor[$part])) {
98: $cursor = & $cursor[$part];
99: } else {
100: throw new \InvalidStateException("Missing parent section [$parent] in '$file'.");
101: }
102: }
103: $secData = Nette\ArrayTools::mergeTree($secData, $cursor);
104: }
105:
106: $secName = trim($parts[0]);
107: if ($secName === '') {
108: throw new \InvalidStateException("Invalid empty section name in '$file'.");
109: }
110: }
111:
112: if (self::$keySeparator) {
113: $cursor = & $data;
114: foreach (explode(self::$keySeparator, $secName) as $part) {
115: if (!isset($cursor[$part]) || is_array($cursor[$part])) {
116: $cursor = & $cursor[$part];
117: } else {
118: throw new \InvalidStateException("Invalid section [$secName] in '$file'.");
119: }
120: }
121: } else {
122: $cursor = & $data[$secName];
123: }
124:
125: if (is_array($secData) && is_array($cursor)) {
126: $secData = Nette\ArrayTools::mergeTree($secData, $cursor);
127: }
128:
129: $cursor = $secData;
130: }
131:
132: if ($section === NULL) {
133: return $data;
134:
135: } elseif (!isset($data[$section]) || !is_array($data[$section])) {
136: throw new \InvalidStateException("There is not section [$section] in '$file'.");
137:
138: } else {
139: return $data[$section];
140: }
141: }
142:
143:
144:
145: 146: 147: 148: 149: 150: 151:
152: public static function save($config, $file, $section = NULL)
153: {
154: $output = array();
155: $output[] = '; generated by Nette';
156: $output[] = '';
157:
158: if ($section === NULL) {
159: foreach ($config as $secName => $secData) {
160: if (!(is_array($secData) || $secData instanceof \Traversable)) {
161: throw new \InvalidStateException("Invalid section '$section'.");
162: }
163:
164: $output[] = "[$secName]";
165: self::build($secData, $output, '');
166: $output[] = '';
167: }
168:
169: } else {
170: $output[] = "[$section]";
171: self::build($config, $output, '');
172: $output[] = '';
173: }
174:
175: if (!file_put_contents($file, implode(PHP_EOL, $output))) {
176: throw new \IOException("Cannot write file '$file'.");
177: }
178: }
179:
180:
181:
182: 183: 184: 185: 186: 187: 188:
189: private static function build($input, & $output, $prefix)
190: {
191: foreach ($input as $key => $val) {
192: if (is_array($val) || $val instanceof \Traversable) {
193: self::build($val, $output, $prefix . $key . self::$keySeparator);
194:
195: } elseif (is_bool($val)) {
196: $output[] = "$prefix$key = " . ($val ? 'true' : 'false');
197:
198: } elseif (is_numeric($val)) {
199: $output[] = "$prefix$key = $val";
200:
201: } elseif (is_string($val)) {
202: $output[] = "$prefix$key = \"$val\"";
203:
204: } else {
205: throw new \InvalidArgumentException("The '$prefix$key' item must be scalar or array, " . gettype($val) ." given.");
206: }
207: }
208: }
209:
210: }
211: