split off a reusable container
authorMichael Wallner <mike@php.net>
Wed, 20 Aug 2014 09:35:34 +0000 (11:35 +0200)
committerMichael Wallner <mike@php.net>
Wed, 20 Aug 2014 09:35:34 +0000 (11:35 +0200)
.travis.yml
composer.json
lib/merry/Config.php
lib/merry/Container.php [new file with mode: 0644]
tests/merry/ConfigTest.php
tests/merry/ContainerTest.php [new file with mode: 0644]

index 15533991e8422e7ce900813533939e2a482da611..d1c113cfd53fa5732d4fd8f86cd594aaf280479a 100644 (file)
@@ -8,4 +8,4 @@ php:
 
 before_script: composer install
     
-script: phpunit --coverage-text --color tests
+script: phpunit --coverage-text --colors tests
index 1aa36da78c92adcce9b36afc6900fb78a076dc03..bdfed602ac67d271082f459a71821c369b53650d 100644 (file)
@@ -1,7 +1,7 @@
 {
     "name": "m6w6/merry",
     "type": "library",
-    "description": "Merry Configuration Container",
+    "description": "Merry container and configuration",
     "keywords": ["merry", "config", "configuration", "container"],
     "homepage": "http://github.com/m6w6/merry",
     "license": "BSD-2-Clause",
@@ -12,8 +12,8 @@
         }
     ],
     "autoload": {
-        "psr-0": {
-            "merry\\Config": "lib"
+        "psr-4": {
+            "merry\\": "lib/merry"
         }
     }
 }
index a00c976f11c506042c804f39a67c1388c7a224c8..e9f4aa572b23680cf94b168d847cc7bcd7644c79 100644 (file)
@@ -13,29 +13,8 @@ namespace merry;
  * @see https://github.com/m6w6/merry
  * @package merry\Config
  */
-class Config implements \ArrayAccess, \RecursiveIterator
+class Config extends Container
 {
-       /**
-        * 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;
-       
        /**
         * Create a new configuration container
         * @param array $array the configuration array
@@ -44,8 +23,6 @@ class Config implements \ArrayAccess, \RecursiveIterator
         * @param string $key_sep a separator for key traversal
         */
        public function __construct(array $array = null, $section = null, $section_sep = ":", $key_sep = ".") {
-               $this->props = new \stdClass;
-               
                if (isset($section) && strlen($section_sep)) {
                        $array = $this->combine($array, $section_sep)[$section];
                }
@@ -57,10 +34,7 @@ class Config implements \ArrayAccess, \RecursiveIterator
                                        $this->walk($config, $key, $val, $key_sep);
                                }
                        }
-                       
-                       foreach ($config as $property => $value) {
-                               $this->__set($property, $value);
-                       }
+                       parent::__construct($config, false);
                }
        }
        
@@ -99,173 +73,6 @@ class Config implements \ArrayAccess, \RecursiveIterator
                }
                $ptr = $val;
        }
-       
-       /**
-        * Recursively turn a Config instance and its childs into an array
-        * @param \merry\Config $o the Config instance to convert to an array
-        * @return array
-        */
-       protected function arrayify(Config $o) {
-               $a = [];
-               
-               foreach ($o->props as $k => $v) {
-                       if ($v instanceof Config) {
-                               $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) {
-               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 Config;
-       }
-       
-       /**
-        * @ignore
-        */
-       function getChildren() {
-               return current($this->riter);
-       }
 }
+
+/* vim: set noet ts=4 sw=4: */
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: */
index 4a11dfbfc59dc7c075a17e24278d240eb77e4beb..b845e1f5c1d2598bdef47a644af794e6f2075154 100644 (file)
@@ -7,31 +7,8 @@ require __DIR__."/../../vendor/autoload.php";
 /**
  * @covers merry\Config
  */
-class ConfigTest extends \PHPUnit_Framework_TestCase {
-
-       public function testBasic() {
-               $config = ["foo" => "bar", "bar" => "foo"];
-               $object = new Config($config);
-               $this->assertEquals($config, $object->toArray());
-               $this->assertEquals("bar", $object->foo);
-               $this->assertEquals("foo", $object->bar);
-               $this->assertTrue(isset($object->foo));
-               $this->assertFalse(isset($object->foobar));
-               unset($object->bar);
-               $this->assertFalse(isset($object->bar));
-       }
-       
-       public function testBasicOffset() {
-               $config = ["foo" => "bar", "bar" => "foo"];
-               $object = new Config($config);
-               $this->assertEquals("bar", $object["foo"]);
-               $this->assertEquals("foo", $object["bar"]);
-               $this->assertTrue(isset($object["foo"]));
-               $this->assertFalse(isset($object["foobar"]));
-               unset($object["bar"]);
-               $this->assertFalse(isset($object["bar"]));
-       }
-       
+class ConfigTest extends \PHPUnit_Framework_TestCase
+{
        public function testBasicSection() {
                $config = [
                        "primary" => [
@@ -63,85 +40,4 @@ class ConfigTest extends \PHPUnit_Framework_TestCase {
                $object = new Config($config, "alternative");
                $this->assertEquals($config["alternative : primary"] + $config["primary"], $object->toArray());
        }
-       
-       public function testSetArray() {
-               $config = ["foo" => "bar", "arr" => [1,2,3]];
-               $object = new Config($config);
-               $object["foo"] = [$object->foo, "baz"];
-               $object["arr"][] = 4;
-               
-               $this->assertEquals(["bar", "baz"], $object["foo"]->toArray());
-               $this->assertEquals([1,2,3,4], $object["arr"]->toArray());
-               
-               $this->assertEquals(["foo"=>["bar","baz"], "arr"=>[1,2,3,4]], $object->toArray());
-       }
-       
-       public function testApply() {
-               $config = [
-                       "level1" => [
-                               "level2" => [
-                                       "level3" => "123"
-                               ],
-                               "level2-1" => [
-                                       "level3-1" => "321"
-                               ]
-                       ]
-               ];
-               $object = new Config($config);
-               $this->assertEquals("123", $object->level1["level2"]->level3);
-               $reverse = function ($v){return strrev($v);};
-               $object->apply([
-                       "level1" => [
-                               "level2" => [
-                                       "level3" => $reverse
-                               ],
-                               "level2-1" => [
-                                       "level3-1" => $reverse
-                               ]
-                       ]
-               ]);
-               $compare = [
-                       "level1" => [
-                               "level2" => [
-                                       "level3" => "321"
-                               ],
-                               "level2-1" => [
-                                       "level3-1" => "123"
-                               ]
-                       ]
-               ];
-               $this->assertEquals($compare, $object->toArray());
-               
-               $object->apply(function() {
-                       return null;
-               });
-               $this->assertEquals(["level1" => null], $object->toArray());
-       }
-
-       public function testIterator() {
-               $config = [
-                       "level1-0" => [
-                               "level2-0" => "1-0.2-0",
-                               "level2-1" => "1-0.2-1",
-                               "level2-2" => [
-                                       "level3" => "1-0.2-2.3"
-                               ]
-                       ],
-                       "level1-1" => [
-                               1,2,3
-                       ]
-               ];
-               $object = new Config($config);
-               $array = [];
-               foreach (new \RecursiveIteratorIterator($object) as $key => $val) {
-                       $array[$key] = $val;
-               }
-               $compare = [
-                       'level2-0' => '1-0.2-0',
-                       'level2-1' => '1-0.2-1',
-                       'level3' => '1-0.2-2.3',
-                       1, 2, 3
-               ];
-               $this->assertEquals($compare, $array);
-       }
 }
diff --git a/tests/merry/ContainerTest.php b/tests/merry/ContainerTest.php
new file mode 100644 (file)
index 0000000..17878b7
--- /dev/null
@@ -0,0 +1,125 @@
+<?php
+
+namespace merry;
+
+require __DIR__."/../../vendor/autoload.php";
+
+/**
+ * @covers merry\Container
+ */
+class ContainerTest extends \PHPUnit_Framework_TestCase
+{
+       public function testBasic() {
+               $config = ["foo" => "bar", "bar" => "foo"];
+               $object = new Container($config);
+               $this->assertEquals($config, $object->toArray());
+               $this->assertEquals("bar", $object->foo);
+               $this->assertEquals("foo", $object->bar);
+               $this->assertTrue(isset($object->foo));
+               $this->assertFalse(isset($object->foobar));
+               unset($object->bar);
+               $this->assertFalse(isset($object->bar));
+       }
+       
+       public function testBasicOffset() {
+               $config = ["foo" => "bar", "bar" => "foo"];
+               $object = new Container($config);
+               $this->assertEquals("bar", $object["foo"]);
+               $this->assertEquals("foo", $object["bar"]);
+               $this->assertTrue(isset($object["foo"]));
+               $this->assertFalse(isset($object["foobar"]));
+               unset($object["bar"]);
+               $this->assertFalse(isset($object["bar"]));
+       }
+       
+       public function testSetArray() {
+               $config = ["foo" => "bar", "arr" => [1,2,3]];
+               $object = new Container($config);
+               $object["foo"] = [$object->foo, "baz"];
+               $object["arr"][] = 4;
+               
+               $this->assertEquals(["bar", "baz"], $object["foo"]->toArray());
+               $this->assertEquals([1,2,3,4], $object["arr"]->toArray());
+               
+               $this->assertEquals(["foo"=>["bar","baz"], "arr"=>[1,2,3,4]], $object->toArray());
+       }
+       
+       public function testApply() {
+               $config = [
+                       "level1" => [
+                               "level2" => [
+                                       "level3" => "123"
+                               ],
+                               "level2-1" => [
+                                       "level3-1" => "321"
+                               ]
+                       ]
+               ];
+               $object = new Container($config);
+               $this->assertEquals("123", $object->level1["level2"]->level3);
+               $reverse = function ($v){return strrev($v);};
+               $object->apply([
+                       "level1" => [
+                               "level2" => [
+                                       "level3" => $reverse
+                               ],
+                               "level2-1" => [
+                                       "level3-1" => $reverse
+                               ]
+                       ]
+               ]);
+               $compare = [
+                       "level1" => [
+                               "level2" => [
+                                       "level3" => "321"
+                               ],
+                               "level2-1" => [
+                                       "level3-1" => "123"
+                               ]
+                       ]
+               ];
+               $this->assertEquals($compare, $object->toArray());
+               
+               $object->apply(function() {
+                       return null;
+               });
+               $this->assertEquals(["level1" => null], $object->toArray());
+       }
+
+       public function testIterator() {
+               $config = [
+                       "level1-0" => [
+                               "level2-0" => "1-0.2-0",
+                               "level2-1" => "1-0.2-1",
+                               "level2-2" => [
+                                       "level3" => "1-0.2-2.3"
+                               ]
+                       ],
+                       "level1-1" => [
+                               1,2,3
+                       ]
+               ];
+               $object = new Container($config);
+               $array = [];
+               foreach (new \RecursiveIteratorIterator($object) as $key => $val) {
+                       $array[$key] = $val;
+               }
+               $compare = [
+                       'level2-0' => '1-0.2-0',
+                       'level2-1' => '1-0.2-1',
+                       'level3' => '1-0.2-2.3',
+                       1, 2, 3
+               ];
+               $this->assertEquals($compare, $array);
+       }
+       
+       public function testJsonSerialize() {
+               $container = new Container;
+               $container->foo = "bar";
+               $container->bar->foo = "bar";
+               $this->assertJsonStringEqualsJsonString(
+                       '{"foo": "bar", "bar": {"foo": "bar"}}',
+                       json_encode($container)
+               );
+       }
+}