split off a reusable container
[m6w6/merry] / lib / merry / Container.php
diff --git a/lib/merry/Container.php b/lib/merry/Container.php
new file mode 100644 (file)
index 0000000..7f4d8fc
--- /dev/null
@@ -0,0 +1,229 @@
+<?php
+
+namespace merry;
+
+class Container implements \ArrayAccess, \RecursiveIterator, \JsonSerializable
+{
+       /**
+        * Index for a numerically indexed array
+        * @internal
+        * @var int
+        */
+       private $index = 0;
+       
+       /**
+        * Container
+        * @internal
+        * @var stdClass
+        */
+       private $props;
+       
+       /**
+        * State for the RecursiveIterator
+        * @internal
+        * @var array
+        */
+       private $riter;
+       
+       /**
+        * Whether to auto populate a requested property with a new container
+        * @internal
+        * @var bool
+        */
+       private $auto;
+
+       /**
+        * Create a new container
+        * @param array $array container data
+        * @param bool $auto whether to auto populate a requested property with a new container
+        */
+       function __construct(array $array = null, $auto = true) {
+               $this->props = new \stdClass;
+               $this->auto = $auto;
+               
+               foreach ((array) $array as $property => $value) {
+                       $this->__set($property, $value);
+               }
+       }
+
+       /**
+        * Recursively turn a container and its childs into an array
+        * @param \merry\Container $o the Container instance to convert to an array
+        * @return array
+        */
+       protected function arrayify(Container $o) {
+               $a = [];
+               
+               foreach ($o->props as $k => $v) {
+                       if ($v instanceof Container) {
+                               $a[$k] = $this->arrayify($v);
+                       } else {
+                               $a[$k] = $v;
+                       }
+               }
+               
+               return $a;
+       }
+       
+       /**
+        * Apply one or mor modifier callbacks
+        * @param mixed $modifier
+        * @return \merry\Config
+        */
+       public function apply($modifier) {
+               if (is_callable($modifier)) {
+                       foreach ($this->props as $prop => $value) {
+                               $this->__set($prop, $modifier($value, $prop));
+                       }
+               } else {
+                       foreach ($modifier as $key => $mod) {
+                               if (is_callable($mod)) {
+                                       $this->props->$key = $mod(isset($this->props->$key) ? $this->props->$key : null, $key);
+                               } elseif (is_array($mod)) {
+                                       $this->props->$key->apply($mod);
+                               } else {
+                                       /* */
+                               }
+                       }
+               }
+               return $this;
+       }
+       
+       /**
+        * Return the complete config as array
+        * @return array
+        */
+       function toArray() {
+               return $this->arrayify($this);
+       }
+       
+       /**
+        * @ignore
+        */
+       function &__get($prop) {
+               if ($this->auto && !isset($this->props->$prop)) {
+                       $this->props->$prop = new static;
+               }
+               return $this->props->$prop;
+       }
+       
+       /**
+        * @ignore
+        */
+       function __set($prop, $value) {
+               if (isset($value) && !is_scalar($value) && !($value instanceof Config)) {
+                       $value = new static((array) $value);
+               }
+               if (!strlen($prop)) {
+                       $prop = $this->index++;
+               } elseif (is_numeric($prop) && !strcmp($prop, (int) $prop)) {
+                       /* update internal index */
+                       if ($prop >= $this->index) {
+                               $this->index = $prop + 1;
+                       }
+               }
+               
+               $this->props->$prop = $value;
+       }
+       
+       /**
+        * @ignore
+        */
+       function __isset($prop) {
+               return isset($this->props->$prop);
+       }
+       
+       /**
+        * @ignore
+        */
+       function __unset($prop) {
+               unset($this->props->$prop);
+       }
+       
+       /**
+        * @ignore
+        */
+       function offsetGet($o) {
+               return $this->props->$o;
+       }
+       
+       /**
+        * @ignore
+        */
+       function offsetSet($o, $v) {
+               $this->__set($o, $v);
+       }
+       
+       /**
+        * @ignore
+        */
+       function offsetExists($o) {
+               return isset($this->props->$o);
+       }
+       
+       /**
+        * @ignore
+        */
+       function offsetUnset($o) {
+               unset($this->props->$o);
+       }
+       
+       /**
+        * @ignore
+        */
+       function rewind() {
+               $this->riter = (array) $this->props;
+               reset($this->riter);
+       }
+
+       /**
+        * @ignore
+        */
+       function valid() {
+               return NULL !== key($this->riter);
+       }
+       
+       /**
+        * @ignore
+        */
+       function next() {
+               next($this->riter);
+       }
+       
+       /**
+        * @ignore
+        */
+       function key() {
+               return key($this->riter);
+       }
+       
+       /**
+        * @ignore
+        */
+       function current() {
+               return current($this->riter);
+       }
+       
+       /**
+        * @ignore
+        */
+       function hasChildren() {
+               return current($this->riter) instanceof Container;
+       }
+       
+       /**
+        * @ignore
+        */
+       function getChildren() {
+               return current($this->riter);
+       }
+       
+       /**
+        * @ignore
+        */
+       function jsonSerialize() {
+               return $this->arrayify($this);
+       }
+}
+
+/* vim: set noet ts=4 sw=4: */