1 <?php
2
3 namespace Docolight\Support;
4
5 use Closure;
6 use Countable;
7 use ArrayAccess;
8 use ArrayIterator;
9 use IteratorAggregate;
10
11 /**
12 * Base setter - getter class.
13 *
14 * @author Krisan Alfa Timur <krisanalfa@docotel.co.id>
15 */
16 class Set implements ArrayAccess, Countable, IteratorAggregate
17 {
18 /**
19 * Key-value array of arbitrary data.
20 *
21 * @var array
22 */
23 protected $data = array();
24
25 /**
26 * Constructor.
27 *
28 * @param array $items Pre-populate set with this key-value array
29 */
30 public function __construct($items = array())
31 {
32 $this->replace($items);
33 }
34
35 /**
36 * Normalize data key.
37 *
38 * Used to transform data key into the necessary key format for this set.
39 *
40 * @param string $key The data key
41 *
42 * @return mixed The transformed/normalized data key
43 */
44 protected function normalizeKey($key)
45 {
46 return $key;
47 }
48
49 /**
50 * Set data key to value.
51 *
52 * @param string|array $key The data key or key / value pair array
53 * @param mixed $value The data value
54 */
55 public function set($key, $value)
56 {
57 if (is_array($key)) {
58 foreach ($key as $innerKey => $innerValue) {
59 $this->set($innerKey, $innerValue);
60 }
61 } else {
62 $this->data[$this->normalizeKey($key)] = $value;
63 }
64 }
65
66 /**
67 * Get data value with key.
68 *
69 * @param string $key The data key
70 * @param mixed $default The value to return if data key does not exist
71 *
72 * @return mixed The data value, or the default value
73 */
74 public function get($key, $default = null)
75 {
76 if ($this->has($key)) {
77 $isInvokable = is_object($this->data[$this->normalizeKey($key)]) and method_exists($this->data[$this->normalizeKey($key)], '__invoke');
78
79 return $isInvokable ? $this->data[$this->normalizeKey($key)]($this) : $this->data[$this->normalizeKey($key)];
80 }
81
82 return value($default);
83 }
84
85 /**
86 * Add data to set.
87 *
88 * @param array $items Key-value array of data to append to this set
89 */
90 public function replace($items)
91 {
92 foreach ($items as $key => $value) {
93 $this->set($key, $value); // Ensure keys are normalized
94 }
95 }
96
97 /**
98 * Fetch set data.
99 *
100 * @return array This set's key-value data array
101 */
102 public function all()
103 {
104 return $this->data;
105 }
106
107 /**
108 * Fetch set data keys.
109 *
110 * @return array This set's key-value data array keys
111 */
112 public function keys()
113 {
114 return array_keys($this->data);
115 }
116
117 /**
118 * Does this set contain a key?
119 *
120 * @param string $key The data key
121 *
122 * @return bool
123 */
124 public function has($key)
125 {
126 return array_key_exists($this->normalizeKey($key), $this->data);
127 }
128
129 /**
130 * Remove value with key from this set.
131 *
132 * @param string $key The data key
133 */
134 public function remove($key)
135 {
136 unset($this->data[$this->normalizeKey($key)]);
137 }
138
139 /**
140 * Get value
141 *
142 * @param string $offset
143 *
144 * @return mixed
145 */
146 public function __get($key)
147 {
148 return $this->get($key);
149 }
150
151 /**
152 * Set value
153 *
154 * @param string $offset
155 * @param mixed $value
156 *
157 * @return void
158 */
159 public function __set($key, $value)
160 {
161 $this->set($key, $value);
162 }
163
164 /**
165 * Determine if value is available.
166 *
167 * @return bool
168 */
169 public function __isset($key)
170 {
171 return $this->has($key);
172 }
173
174 /**
175 * Remove a value
176 *
177 * @param string $offset
178 *
179 * @return void
180 */
181 public function __unset($key)
182 {
183 $this->remove($key);
184 }
185
186 /**
187 * Clear all values.
188 *
189 * @return void
190 */
191 public function clear()
192 {
193 $this->data = array();
194 }
195
196 /**
197 * Determine if value is available.
198 *
199 * @return bool
200 */
201 public function offsetExists($offset)
202 {
203 return $this->has($offset);
204 }
205
206 /**
207 * Get value
208 *
209 * @param string $offset
210 *
211 * @return mixed
212 */
213 public function offsetGet($offset)
214 {
215 return $this->get($offset);
216 }
217
218 /**
219 * Set value
220 *
221 * @param string $offset
222 * @param mixed $value
223 *
224 * @return void
225 */
226 public function offsetSet($offset, $value)
227 {
228 $this->set($offset, $value);
229 }
230
231 /**
232 * Remove a value
233 *
234 * @param string $offset
235 *
236 * @return void
237 */
238 public function offsetUnset($offset)
239 {
240 $this->remove($offset);
241 }
242
243 /**
244 * Countable.
245 *
246 * @return int
247 */
248 public function count()
249 {
250 return count($this->data);
251 }
252
253 /**
254 * IteratorAggregate.
255 *
256 * @return \ArrayIterator
257 */
258 public function getIterator()
259 {
260 return new ArrayIterator($this->data);
261 }
262
263 /**
264 * Ensure a value or object will remain globally unique.
265 *
266 * @param string $key The value or object name
267 * @param \Closure $value The closure that defines the object
268 *
269 * @return mixed
270 */
271 public function singleton($key, Closure $value)
272 {
273 $this->set($key, function ($c) use ($value) {
274 static $object;
275
276 if (null === $object) {
277 $object = $value($c);
278 }
279
280 return $object;
281 });
282 }
283
284 /**
285 * Protect closure from being directly invoked.
286 *
287 * @param \Closure $callable A closure to keep from being invoked and evaluated
288 *
289 * @return \Closure
290 */
291 public function protect(Closure $callable)
292 {
293 return function () use ($callable) {
294 return $callable;
295 };
296 }
297 }
298