From c5cc1f0cf7b1b7665e7e9d5745855066527605e6 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Wed, 20 Aug 2014 11:35:34 +0200 Subject: [PATCH] split off a reusable container --- .travis.yml | 2 +- composer.json | 6 +- lib/merry/Config.php | 201 +---------------------------- lib/merry/Container.php | 229 ++++++++++++++++++++++++++++++++++ tests/merry/ConfigTest.php | 108 +--------------- tests/merry/ContainerTest.php | 125 +++++++++++++++++++ 6 files changed, 364 insertions(+), 307 deletions(-) create mode 100644 lib/merry/Container.php create mode 100644 tests/merry/ContainerTest.php diff --git a/.travis.yml b/.travis.yml index 1553399..d1c113c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,4 +8,4 @@ php: before_script: composer install -script: phpunit --coverage-text --color tests +script: phpunit --coverage-text --colors tests diff --git a/composer.json b/composer.json index 1aa36da..bdfed60 100644 --- a/composer.json +++ b/composer.json @@ -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" } } } diff --git a/lib/merry/Config.php b/lib/merry/Config.php index a00c976..e9f4aa5 100644 --- a/lib/merry/Config.php +++ b/lib/merry/Config.php @@ -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 index 0000000..7f4d8fc --- /dev/null +++ b/lib/merry/Container.php @@ -0,0 +1,229 @@ +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: */ diff --git a/tests/merry/ConfigTest.php b/tests/merry/ConfigTest.php index 4a11dfb..b845e1f 100644 --- a/tests/merry/ConfigTest.php +++ b/tests/merry/ConfigTest.php @@ -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 index 0000000..17878b7 --- /dev/null +++ b/tests/merry/ContainerTest.php @@ -0,0 +1,125 @@ + "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) + ); + } +} -- 2.30.2