From a84365e73d7052ec871fac9e16a4caea20c0e37b Mon Sep 17 00:00:00 2001 From: Reza Salarmehr Date: Sun, 17 Sep 2017 11:54:07 +1000 Subject: [PATCH] Adding replace and replaceRecursively methods --- composer.json | 3 + readme.md | 25 +++-- src/Ary.php | 255 ++++++++++++++++++++++++++----------------------- src/helper.php | 2 +- test/Test.php | 239 ++++++++++++++++++++++++--------------------- 5 files changed, 286 insertions(+), 238 deletions(-) diff --git a/composer.json b/composer.json index 29d2287..a795cf0 100644 --- a/composer.json +++ b/composer.json @@ -20,5 +20,8 @@ "files": [ "src/helper.php" ] + }, + "require-dev": { + "phpunit/phpunit": "^6.3" } } diff --git a/readme.md b/readme.md index f5db84e..38bbdad 100644 --- a/readme.md +++ b/readme.md @@ -1,15 +1,22 @@ Ary === -Are you tired from casting objects and arrays to each other or are bored using is `isset`? Don't do those anymore! -Ary is a light class/function that makes accessing array items more convenient. +Ary provides unified interface for ary/object. Really handy to store: + + - arrays/objects those their items/properties varies conditionally. + - storing registry, system configuration, env variables, etc. + - when you perform a chain of action on an array/object + + It's super cool and after a while you can not code in php without it. +Features +--------- 1. You can access array items using `->` or `['']` syntax. 2. You will get `null` if an index does not exists (instead of a nasty notification!) 3. You can specify a default value for missing indexes. 4. You can set/get a value within a deeply nested array using "dot" notation. 5. A bunch of really handy methods: `merge`, `only`, `search`, `toObject`, ... -6. (new) It extends `Illuminate\Support\Collection` class so all of its methods are available (https://laravel.com/docs/master/eloquent-collections) +6. Now, `Ary` extends `Illuminate\Support\Collection` class so all of its methods are available (https://laravel.com/docs/master/eloquent-collections) Examples -------- @@ -24,7 +31,7 @@ $ary = ary(2, 4, 6, 8); // or $ary = ary([2, 4, 6, 8]); $ary = ary(['x' => 'foo', 'y' => 'bar']); -//Assignment; +//Assignment $ary->newItem=20; //or $ary['newItem']=20; @@ -34,7 +41,7 @@ $foo = $ary['x']; $missed = $ary->get('missed', 'Default value'); $ary->all(); // returns the simple php array; -// behave similar to regular arrays +// behaive as regular arrays count($ary); //returns 3 unset($ary[0]); json_encode($ary); @@ -48,14 +55,14 @@ $ary['production.table.weight']=200; Installation ============ -Simply, `include '__DIR__.'/src/helper.php';` +Simply `include '__DIR__.'/src/helper.php';` -or using Composer: +or use Composer: composer require salarmehr/ary -* The class (`Ary()`) requires PHP 5.4 or newer. -* The helper function (`ary()`) requires PHP 5.6 or newer. +* The class (`Ary()`) requires PHP 5.4 or above. +* The helper function (`ary()`) requires PHP 5.6 or above. Licence ======= diff --git a/src/Ary.php b/src/Ary.php index 3e6918c..70101ea 100644 --- a/src/Ary.php +++ b/src/Ary.php @@ -10,139 +10,160 @@ namespace Salarmehr; -use ArrayAccess; -use Countable; use Illuminate\Support\Collection; -use IteratorAggregate; -use JsonSerializable; -class Ary extends Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable +class Ary extends Collection { - /** - * Create a new collection. - * - * @param mixed $items - */ + /** + * Create a new ary. + * + * @param mixed $items + */ - public function __construct() - { - $items = func_get_args(); - if (count($items) === 0) { - $items = []; - } - elseif (count($items) === 1) { - $items = is_array($items[0]) ? $items[0] : $this->getArrayableItems($items[0]); - } - $this->items = $items; + public function __construct() + { + $items = func_get_args(); + if (count($items) === 0) { + $items = []; + } + elseif (count($items) === 1) { + $items = is_array($items[0]) ? $items[0] : $this->getArrayableItems($items[0]); + } + $this->items = $items; + } + + public function &__get($item) + { + return $this->get($item); + } + + public function __set($name, $value) + { + $this->offsetSet($name, $value); + } + + /** + * Get an item from the ary by key. + * + * @param mixed $key + * @param mixed $default + * @return mixed + */ + public function &get($key, $default = null) + { + if ($this->offsetExists($key)) { + return $this->items[$key]; } - public function &__get($item) - { - return $this->get($item); + $array = $this->items; + foreach (explode('.', $key) as $segment) { + if (!is_array($array) || !array_key_exists($segment, $array)) { + return $default; + } + + $array = $array[$segment]; + } + return $array; + } + + /** + * Set the item at a given offset. + * + * @param mixed $key + * @param mixed $value + * @return void + */ + public function offsetSet($key, $value) + { + if (is_null($key)) { + $this->items[] = $value; + return; } - public function __set($name, $value) - { - $this->offsetSet($name, $value); + $keys = explode('.', $key); + $array =& $this->items; + while (count($keys) > 1) { + $key = array_shift($keys); + + // If the key doesn't exist at this depth, we will just create an empty array + // to hold the next value, allowing us to create the arrays to hold final + // values at the correct depth. Then we'll keep digging into the array. + if (!isset($array[$key]) || !is_array($array[$key])) { + $array[$key] = []; + } + + $array = &$array[$key]; } - /** - * Get an item from the collection by key. - * - * @param mixed $key - * @param mixed $default - * @return mixed - */ - public function &get($key, $default = null) - { - if ($this->offsetExists($key)) { - return $this->items[$key]; - } + $array[array_shift($keys)] = $value; + return; + } - $array = $this->items; - foreach (explode('.', $key) as $segment) { - if (!is_array($array) || !array_key_exists($segment, $array)) { - return $default; - } + /** + * Get an item at a given offset. + * + * @param mixed $key + * @return mixed + */ + public function &offsetGet($key) + { + return $this->get($key); + } - $array = $array[$segment]; - } - return $array; - } + public function __isset($name) + { + return $this->has($name); + } - /** - * Set the item at a given offset. - * - * @param mixed $key - * @param mixed $value - * @return void - */ - public function offsetSet($key, $value) - { - if (is_null($key)) { - $this->items[] = $value; - return; - } + /** + * Get the ary of items as a plain object. + * + * @return object + */ + public function toObject() + { + return (object)$this->all(); + } -// $this->items[$key] = $value; + /** + * Return a subset of current ary as a new ary + * @param $item + * @return Ary + */ + public function ary($item) + { + return new self($this->get($item)); + } - $keys = explode('.', $key); - $array =& $this->items; - while (count($keys) > 1) { - $key = array_shift($keys); + public function __unset($key) + { + $this->offsetUnset($key); + } - // If the key doesn't exist at this depth, we will just create an empty array - // to hold the next value, allowing us to create the arrays to hold final - // values at the correct depth. Then we'll keep digging into the array. - if (!isset($array[$key]) || !is_array($array[$key])) { - $array[$key] = []; - } + #region extra helpers - $array = &$array[$key]; - } + /** + * Replaces elements from passed arrays into the collection + * + * @param mixed $items + * @return static + * @see http://php.net/manual/en/function.array-replace-recursive.php + */ + public function replace($items) + { + return new static(array_replace($this->items, $this->getArrayableItems($items))); + } - $array[array_shift($keys)] = $value; - return; - } - - /** - * Get an item at a given offset. - * - * @param mixed $key - * @return mixed - */ - public function &offsetGet($key) - { - return $this->get($key); - } - - public function __isset($name) - { - return $this->has($name); - } - - /** - * Get the collection of items as a plain object. - * - * @return object - */ - public function toObject() - { - return (object)$this->all(); - } - - /** - * Return a subset of current ary as a new ary - * @param $item - * @return Ary - */ - public function ary($item) - { - return new ary($this->get($item)); - } - - public function __unset($key) - { - $this->offsetUnset($key); - } + /** + * Replaces elements from passed arrays into the collection recursively + * + * @param mixed $items + * @return static + * @see http://php.net/manual/en/function.array-replace-recursive.php + */ + public function replaceRecursively($items) + { + return new static(array_replace_recursive($this->items, $this->getArrayableItems($items))); + } + #endrtiong } \ No newline at end of file diff --git a/src/helper.php b/src/helper.php index 93f210c..e5379a9 100644 --- a/src/helper.php +++ b/src/helper.php @@ -13,7 +13,7 @@ if (!function_exists('ary') && phpversion()) { function parse_version($version) { $version = explode('.', $version); - return $version[0] * 10000 + $version[1] * 100 + $version[2]; + return $version[0] * 10000 + $version[1] * 100 + (int) $version[2]; } if (!defined('ary') && parse_version(PHP_VERSION) > parse_version('5.6.0')) { diff --git a/test/Test.php b/test/Test.php index 18d33d6..899ac0d 100644 --- a/test/Test.php +++ b/test/Test.php @@ -12,135 +12,152 @@ use \Salarmehr\Ary; require __DIR__ . '/../vendor/autoload.php'; require __DIR__ . '/../src/Ary.php'; -class Test extends PHPUnit_Framework_TestCase +class Test extends PHPUnit\Framework\TestCase + { - protected $item; + protected $item; - public function setup() - { - $this->item = []; - } + public function setup() + { + $this->item = []; + } - public function provider() - { - return array( - array(''), - array(['ali', 'reza', 'mohammad']), - array([1, 2, 3, 4]), - ); - } + public function provider() + { + return array( + array(''), + array(['ali', 'reza', 'mohammad']), + array([1, 2, 3, 4]), + ); + } - /** - * @param array $originalary array to get - * @param array $expectedary What we expect to get - * - * @dataProvider various - */ + /** + * @param array $originalary array to get + * @param array $expectedary What we expect to get + * + * @dataProvider various + */ - public function testAll($originalary, $expectedary) - { - $ary = new Ary(); - $ary->all($originalary); - $this->assertEquals($expectedary, $originalary); - } + public function testAll($originalary, $expectedary) + { + $ary = new Ary(); + $ary->all(); + $this->assertEquals($expectedary, $originalary); + } - public function various() - { - return array( - array('', ''), - array(null, null), - array(['ali', 'reza', 'mohammad'], ['ali', 'reza', 'mohammad']), - array(['name' => 'ali', 'lastname' => 'reza', 'age' => 30], ['name' => 'ali', 'lastname' => 'reza', 'age' => 30]), - array((object)['name' => 'ali', 'lastname' => 'reza', 'age' => 30], (object)['name' => 'ali', 'lastname' => 'reza', 'age' => 30]), - array(['ali', 'reza', 'mohammad'], ['ali', 'reza', 'mohammad']), - array([1, 2, 3, 4], [1, 2, 3, 4]), - ); - } + public function various() + { + return array( + array('', ''), + array(null, null), + array(['ali', 'reza', 'mohammad'], ['ali', 'reza', 'mohammad']), + array(['name' => 'ali', 'lastname' => 'reza', 'age' => 30], ['name' => 'ali', 'lastname' => 'reza', 'age' => 30]), + array((object)['name' => 'ali', 'lastname' => 'reza', 'age' => 30], (object)['name' => 'ali', 'lastname' => 'reza', 'age' => 30]), + array(['ali', 'reza', 'mohammad'], ['ali', 'reza', 'mohammad']), + array([1, 2, 3, 4], [1, 2, 3, 4]), + ); + } - public function testGet() - { - $ary = new Ary(); - $this->assertEquals($ary[0], null); + public function testGet() + { + $ary = new Ary(); + $this->assertEquals($ary[0], null); - $ary = new Ary(['x' => ['xx' => ['m' => 'xxx']]]); - $this->assertEquals($ary->get('x.xx.m'), 'xxx'); - } + $ary = new Ary(['x' => ['xx' => ['m' => 'xxx']]]); + $this->assertEquals($ary->get('x.xx.m'), 'xxx'); + } - /** - * @dataProvider Provider - */ - public function testCount($objects) - { - $ary = new Ary($objects); - $result = count($objects); - $num = count($ary); - $this->assertEquals($result, $num); - } + /** + * @dataProvider Provider + */ + public function testCount($objects) + { + $ary = new Ary($objects); + $result = count($objects); + $num = count($ary); + $this->assertEquals($result, $num); + } - /** - * @dataProvider various - */ - public function testGetArrayableItems($original, $expected) - { - $ary = new Ary($original); - $result = $this->invokeMethod($ary, 'getArrayableItems', array($original)); - $this->assertEquals($expected, $original); - } + /** + * @dataProvider various + */ + public function testGetArrayableItems($original, $expected) + { + $ary = new Ary($original); + $this->invokeMethod($ary, 'getArrayableItems', array($original)); + $this->assertEquals($expected, $original); + } - /** - * @param object &$ary Instantiated object that we will run method on. - * @param string $getArrayableItems Method name to call - * @param array $parameters Array of parameters to pass into method. - * - * @return mixed Method return. - */ - public function invokeMethod(&$ary, $getArrayableItems, array $parameters = array()) - { - $reflection = new \ReflectionClass(get_class($ary)); - $method = $reflection->getMethod($getArrayableItems); - $method->setAccessible(true); - return $method->invokeArgs($ary, $parameters); - } + /** + * @param object &$ary Instantiated object that we will run method on. + * @param string $getArrayableItems Method name to call + * @param array $parameters Array of parameters to pass into method. + * + * @return mixed Method return. + */ + public function invokeMethod(&$ary, $getArrayableItems, array $parameters = array()) + { + $reflection = new \ReflectionClass(get_class($ary)); + $method = $reflection->getMethod($getArrayableItems); + $method->setAccessible(true); + return $method->invokeArgs($ary, $parameters); + } - public function testAssignment() - { - $ary = new Ary(); + public function testAssignment() + { + $ary = new Ary(); // var_dump($ary);die(); - $ary[] = 3; - $this->assertEquals($ary[0], 3); - $this->assertEquals($ary->{0}, 3); - $this->assertTrue($ary->has(0)); - $ary['x'] = ['z' => 'y']; - $ary['foo'] = 'bar'; - $this->assertEquals($ary['foo'], 'bar'); - $this->assertEquals($ary->foo, 'bar'); - $this->assertTrue($ary->has('foo')); - $this->assertEquals($ary['x']['z'], 'y'); - $ary['x']['z'] = 'm'; - $this->assertEquals($ary['x']['z'], 'm'); + $ary[] = 3; + $this->assertEquals($ary[0], 3); + $this->assertEquals($ary->{0}, 3); + $this->assertTrue($ary->has(0)); + $ary['x'] = ['z' => 'y']; + $ary['foo'] = 'bar'; + $this->assertEquals($ary['foo'], 'bar'); + $this->assertEquals($ary->foo, 'bar'); + $this->assertTrue($ary->has('foo')); + $this->assertEquals($ary['x']['z'], 'y'); + $ary['x']['z'] = 'm'; + $this->assertEquals($ary['x']['z'], 'm'); - } + } - /** - * @dataProvider various - */ - public function testJsonSerialize($original, $expected) - { - $this->assertEquals(json_encode(new Ary($original)), json_encode((array)$original)); - } + /** + * @dataProvider various + * @param $original + */ + public function testJsonSerialize($original) + { + $this->assertEquals(json_encode(new Ary($original)), json_encode((array)$original)); + } - /** - * @dataProvider various - */ - public function ary() - { - $test = ['x' => ['xx' => 'xxx']]; - $ary = new Ary($test); - $this->assertEquals(ary($test)->x['xx'], $ary->ary('x')->xx); - $this->assertEquals(ary(ary($test)->x['xx']), $ary->ary('x')->ary('xx')); - } + /** + * @dataProvider various + */ + public function ary() + { + $test = ['x' => ['xx' => 'xxx']]; + $ary = new Ary($test); + $this->assertEquals(ary($test)->x['xx'], $ary->ary('x')->xx); + $this->assertEquals(ary(ary($test)->x['xx']), $ary->ary('x')->ary('xx')); + } + + public function testReplace() + { + $c = new Ary(['foo' => 'x', 'bar' => 'y']); + $this->assertEquals(['foo' => 'f', 'bar' => 'y', 'baz' => 'z'], $c->replace(new Ary(['foo' => 'f', 'baz' => 'z']))->all()); + } + + public function testReplaceAryRecursively() + { + $base = ['citrus' => ["orange"], 'berries' => ["blackberry", "raspberry"],]; + $replacements = ['citrus' => ['pineapple'], 'berries' => ['blueberry']]; + $expect = ['citrus' => ['pineapple'], 'berries' => ['blueberry', 'raspberry']]; + $c = new Ary($base); + $this->assertEquals($expect, $c->replaceRecursively(new Ary($replacements))->all()); + } // public function testOffsetExists() // {