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