7f4d8fc7b2e1ce459510cca904de861ceff1bade
[m6w6/merry] / lib / merry / Container.php
1 <?php
2
3 namespace merry;
4
5 class Container implements \ArrayAccess, \RecursiveIterator, \JsonSerializable
6 {
7 /**
8 * Index for a numerically indexed array
9 * @internal
10 * @var int
11 */
12 private $index = 0;
13
14 /**
15 * Container
16 * @internal
17 * @var stdClass
18 */
19 private $props;
20
21 /**
22 * State for the RecursiveIterator
23 * @internal
24 * @var array
25 */
26 private $riter;
27
28 /**
29 * Whether to auto populate a requested property with a new container
30 * @internal
31 * @var bool
32 */
33 private $auto;
34
35 /**
36 * Create a new container
37 * @param array $array container data
38 * @param bool $auto whether to auto populate a requested property with a new container
39 */
40 function __construct(array $array = null, $auto = true) {
41 $this->props = new \stdClass;
42 $this->auto = $auto;
43
44 foreach ((array) $array as $property => $value) {
45 $this->__set($property, $value);
46 }
47 }
48
49 /**
50 * Recursively turn a container and its childs into an array
51 * @param \merry\Container $o the Container instance to convert to an array
52 * @return array
53 */
54 protected function arrayify(Container $o) {
55 $a = [];
56
57 foreach ($o->props as $k => $v) {
58 if ($v instanceof Container) {
59 $a[$k] = $this->arrayify($v);
60 } else {
61 $a[$k] = $v;
62 }
63 }
64
65 return $a;
66 }
67
68 /**
69 * Apply one or mor modifier callbacks
70 * @param mixed $modifier
71 * @return \merry\Config
72 */
73 public function apply($modifier) {
74 if (is_callable($modifier)) {
75 foreach ($this->props as $prop => $value) {
76 $this->__set($prop, $modifier($value, $prop));
77 }
78 } else {
79 foreach ($modifier as $key => $mod) {
80 if (is_callable($mod)) {
81 $this->props->$key = $mod(isset($this->props->$key) ? $this->props->$key : null, $key);
82 } elseif (is_array($mod)) {
83 $this->props->$key->apply($mod);
84 } else {
85 /* */
86 }
87 }
88 }
89 return $this;
90 }
91
92 /**
93 * Return the complete config as array
94 * @return array
95 */
96 function toArray() {
97 return $this->arrayify($this);
98 }
99
100 /**
101 * @ignore
102 */
103 function &__get($prop) {
104 if ($this->auto && !isset($this->props->$prop)) {
105 $this->props->$prop = new static;
106 }
107 return $this->props->$prop;
108 }
109
110 /**
111 * @ignore
112 */
113 function __set($prop, $value) {
114 if (isset($value) && !is_scalar($value) && !($value instanceof Config)) {
115 $value = new static((array) $value);
116 }
117 if (!strlen($prop)) {
118 $prop = $this->index++;
119 } elseif (is_numeric($prop) && !strcmp($prop, (int) $prop)) {
120 /* update internal index */
121 if ($prop >= $this->index) {
122 $this->index = $prop + 1;
123 }
124 }
125
126 $this->props->$prop = $value;
127 }
128
129 /**
130 * @ignore
131 */
132 function __isset($prop) {
133 return isset($this->props->$prop);
134 }
135
136 /**
137 * @ignore
138 */
139 function __unset($prop) {
140 unset($this->props->$prop);
141 }
142
143 /**
144 * @ignore
145 */
146 function offsetGet($o) {
147 return $this->props->$o;
148 }
149
150 /**
151 * @ignore
152 */
153 function offsetSet($o, $v) {
154 $this->__set($o, $v);
155 }
156
157 /**
158 * @ignore
159 */
160 function offsetExists($o) {
161 return isset($this->props->$o);
162 }
163
164 /**
165 * @ignore
166 */
167 function offsetUnset($o) {
168 unset($this->props->$o);
169 }
170
171 /**
172 * @ignore
173 */
174 function rewind() {
175 $this->riter = (array) $this->props;
176 reset($this->riter);
177 }
178
179 /**
180 * @ignore
181 */
182 function valid() {
183 return NULL !== key($this->riter);
184 }
185
186 /**
187 * @ignore
188 */
189 function next() {
190 next($this->riter);
191 }
192
193 /**
194 * @ignore
195 */
196 function key() {
197 return key($this->riter);
198 }
199
200 /**
201 * @ignore
202 */
203 function current() {
204 return current($this->riter);
205 }
206
207 /**
208 * @ignore
209 */
210 function hasChildren() {
211 return current($this->riter) instanceof Container;
212 }
213
214 /**
215 * @ignore
216 */
217 function getChildren() {
218 return current($this->riter);
219 }
220
221 /**
222 * @ignore
223 */
224 function jsonSerialize() {
225 return $this->arrayify($this);
226 }
227 }
228
229 /* vim: set noet ts=4 sw=4: */