Adding replace and replaceRecursively methods

This commit is contained in:
Reza Salarmehr
2017-09-17 11:54:07 +10:00
parent d8a361562d
commit a84365e73d
5 changed files with 286 additions and 238 deletions

View File

@ -20,5 +20,8 @@
"files": [ "files": [
"src/helper.php" "src/helper.php"
] ]
},
"require-dev": {
"phpunit/phpunit": "^6.3"
} }
} }

View File

@ -1,15 +1,22 @@
Ary Ary
=== ===
Are you tired from casting objects and arrays to each other or are bored using is `isset`? Don't do those anymore! Ary provides unified interface for ary/object. Really handy to store:
Ary is a light class/function that makes accessing array items more convenient.
- 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. 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!) 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. 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. 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`, ... 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 Examples
-------- --------
@ -24,7 +31,7 @@ $ary = ary(2, 4, 6, 8); // or
$ary = ary([2, 4, 6, 8]); $ary = ary([2, 4, 6, 8]);
$ary = ary(['x' => 'foo', 'y' => 'bar']); $ary = ary(['x' => 'foo', 'y' => 'bar']);
//Assignment; //Assignment
$ary->newItem=20; //or $ary->newItem=20; //or
$ary['newItem']=20; $ary['newItem']=20;
@ -34,7 +41,7 @@ $foo = $ary['x'];
$missed = $ary->get('missed', 'Default value'); $missed = $ary->get('missed', 'Default value');
$ary->all(); // returns the simple php array; $ary->all(); // returns the simple php array;
// behave similar to regular arrays // behaive as regular arrays
count($ary); //returns 3 count($ary); //returns 3
unset($ary[0]); unset($ary[0]);
json_encode($ary); json_encode($ary);
@ -48,14 +55,14 @@ $ary['production.table.weight']=200;
Installation Installation
============ ============
Simply, `include '__DIR__.'/src/helper.php';` Simply `include '__DIR__.'/src/helper.php';`
or using Composer: or use Composer:
composer require salarmehr/ary composer require salarmehr/ary
* The class (`Ary()`) requires PHP 5.4 or newer. * The class (`Ary()`) requires PHP 5.4 or above.
* The helper function (`ary()`) requires PHP 5.6 or newer. * The helper function (`ary()`) requires PHP 5.6 or above.
Licence Licence
======= =======

View File

@ -10,139 +10,160 @@
namespace Salarmehr; namespace Salarmehr;
use ArrayAccess;
use Countable;
use Illuminate\Support\Collection; 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. * Create a new ary.
* *
* @param mixed $items * @param mixed $items
*/ */
public function __construct() public function __construct()
{ {
$items = func_get_args(); $items = func_get_args();
if (count($items) === 0) { if (count($items) === 0) {
$items = []; $items = [];
} }
elseif (count($items) === 1) { elseif (count($items) === 1) {
$items = is_array($items[0]) ? $items[0] : $this->getArrayableItems($items[0]); $items = is_array($items[0]) ? $items[0] : $this->getArrayableItems($items[0]);
} }
$this->items = $items; $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) $array = $this->items;
{ foreach (explode('.', $key) as $segment) {
return $this->get($item); 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) $keys = explode('.', $key);
{ $array =& $this->items;
$this->offsetSet($name, $value); 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];
} }
/** $array[array_shift($keys)] = $value;
* Get an item from the collection by key. return;
* }
* @param mixed $key
* @param mixed $default
* @return mixed
*/
public function &get($key, $default = null)
{
if ($this->offsetExists($key)) {
return $this->items[$key];
}
$array = $this->items; /**
foreach (explode('.', $key) as $segment) { * Get an item at a given offset.
if (!is_array($array) || !array_key_exists($segment, $array)) { *
return $default; * @param mixed $key
} * @return mixed
*/
public function &offsetGet($key)
{
return $this->get($key);
}
$array = $array[$segment]; public function __isset($name)
} {
return $array; return $this->has($name);
} }
/** /**
* Set the item at a given offset. * Get the ary of items as a plain object.
* *
* @param mixed $key * @return object
* @param mixed $value */
* @return void public function toObject()
*/ {
public function offsetSet($key, $value) return (object)$this->all();
{ }
if (is_null($key)) {
$this->items[] = $value;
return;
}
// $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); public function __unset($key)
$array =& $this->items; {
while (count($keys) > 1) { $this->offsetUnset($key);
$key = array_shift($keys); }
// If the key doesn't exist at this depth, we will just create an empty array #region extra helpers
// 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]; /**
} * 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; * Replaces elements from passed arrays into the collection recursively
} *
* @param mixed $items
/** * @return static
* Get an item at a given offset. * @see http://php.net/manual/en/function.array-replace-recursive.php
* */
* @param mixed $key public function replaceRecursively($items)
* @return mixed {
*/ return new static(array_replace_recursive($this->items, $this->getArrayableItems($items)));
public function &offsetGet($key) }
{ #endrtiong
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);
}
} }

View File

@ -13,7 +13,7 @@ if (!function_exists('ary') && phpversion()) {
function parse_version($version) function parse_version($version)
{ {
$version = explode('.', $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')) { if (!defined('ary') && parse_version(PHP_VERSION) > parse_version('5.6.0')) {

View File

@ -12,135 +12,152 @@ use \Salarmehr\Ary;
require __DIR__ . '/../vendor/autoload.php'; require __DIR__ . '/../vendor/autoload.php';
require __DIR__ . '/../src/Ary.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() public function setup()
{ {
$this->item = []; $this->item = [];
} }
public function provider() public function provider()
{ {
return array( return array(
array(''), array(''),
array(['ali', 'reza', 'mohammad']), array(['ali', 'reza', 'mohammad']),
array([1, 2, 3, 4]), array([1, 2, 3, 4]),
); );
} }
/** /**
* @param array $originalary array to get * @param array $originalary array to get
* @param array $expectedary What we expect to get * @param array $expectedary What we expect to get
* *
* @dataProvider various * @dataProvider various
*/ */
public function testAll($originalary, $expectedary) public function testAll($originalary, $expectedary)
{ {
$ary = new Ary(); $ary = new Ary();
$ary->all($originalary); $ary->all();
$this->assertEquals($expectedary, $originalary); $this->assertEquals($expectedary, $originalary);
} }
public function various() public function various()
{ {
return array( return array(
array('', ''), array('', ''),
array(null, null), array(null, null),
array(['ali', 'reza', 'mohammad'], ['ali', 'reza', 'mohammad']), array(['ali', 'reza', 'mohammad'], ['ali', 'reza', 'mohammad']),
array(['name' => 'ali', 'lastname' => 'reza', 'age' => 30], ['name' => 'ali', 'lastname' => 'reza', 'age' => 30]), 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((object)['name' => 'ali', 'lastname' => 'reza', 'age' => 30], (object)['name' => 'ali', 'lastname' => 'reza', 'age' => 30]),
array(['ali', 'reza', 'mohammad'], ['ali', 'reza', 'mohammad']), array(['ali', 'reza', 'mohammad'], ['ali', 'reza', 'mohammad']),
array([1, 2, 3, 4], [1, 2, 3, 4]), array([1, 2, 3, 4], [1, 2, 3, 4]),
); );
} }
public function testGet() public function testGet()
{ {
$ary = new Ary(); $ary = new Ary();
$this->assertEquals($ary[0], null); $this->assertEquals($ary[0], null);
$ary = new Ary(['x' => ['xx' => ['m' => 'xxx']]]); $ary = new Ary(['x' => ['xx' => ['m' => 'xxx']]]);
$this->assertEquals($ary->get('x.xx.m'), 'xxx'); $this->assertEquals($ary->get('x.xx.m'), 'xxx');
} }
/** /**
* @dataProvider Provider * @dataProvider Provider
*/ */
public function testCount($objects) public function testCount($objects)
{ {
$ary = new Ary($objects); $ary = new Ary($objects);
$result = count($objects); $result = count($objects);
$num = count($ary); $num = count($ary);
$this->assertEquals($result, $num); $this->assertEquals($result, $num);
} }
/** /**
* @dataProvider various * @dataProvider various
*/ */
public function testGetArrayableItems($original, $expected) public function testGetArrayableItems($original, $expected)
{ {
$ary = new Ary($original); $ary = new Ary($original);
$result = $this->invokeMethod($ary, 'getArrayableItems', array($original)); $this->invokeMethod($ary, 'getArrayableItems', array($original));
$this->assertEquals($expected, $original); $this->assertEquals($expected, $original);
} }
/** /**
* @param object &$ary Instantiated object that we will run method on. * @param object &$ary Instantiated object that we will run method on.
* @param string $getArrayableItems Method name to call * @param string $getArrayableItems Method name to call
* @param array $parameters Array of parameters to pass into method. * @param array $parameters Array of parameters to pass into method.
* *
* @return mixed Method return. * @return mixed Method return.
*/ */
public function invokeMethod(&$ary, $getArrayableItems, array $parameters = array()) public function invokeMethod(&$ary, $getArrayableItems, array $parameters = array())
{ {
$reflection = new \ReflectionClass(get_class($ary)); $reflection = new \ReflectionClass(get_class($ary));
$method = $reflection->getMethod($getArrayableItems); $method = $reflection->getMethod($getArrayableItems);
$method->setAccessible(true); $method->setAccessible(true);
return $method->invokeArgs($ary, $parameters); return $method->invokeArgs($ary, $parameters);
} }
public function testAssignment() public function testAssignment()
{ {
$ary = new Ary(); $ary = new Ary();
// var_dump($ary);die(); // var_dump($ary);die();
$ary[] = 3; $ary[] = 3;
$this->assertEquals($ary[0], 3); $this->assertEquals($ary[0], 3);
$this->assertEquals($ary->{0}, 3); $this->assertEquals($ary->{0}, 3);
$this->assertTrue($ary->has(0)); $this->assertTrue($ary->has(0));
$ary['x'] = ['z' => 'y']; $ary['x'] = ['z' => 'y'];
$ary['foo'] = 'bar'; $ary['foo'] = 'bar';
$this->assertEquals($ary['foo'], 'bar'); $this->assertEquals($ary['foo'], 'bar');
$this->assertEquals($ary->foo, 'bar'); $this->assertEquals($ary->foo, 'bar');
$this->assertTrue($ary->has('foo')); $this->assertTrue($ary->has('foo'));
$this->assertEquals($ary['x']['z'], 'y'); $this->assertEquals($ary['x']['z'], 'y');
$ary['x']['z'] = 'm'; $ary['x']['z'] = 'm';
$this->assertEquals($ary['x']['z'], 'm'); $this->assertEquals($ary['x']['z'], 'm');
} }
/** /**
* @dataProvider various * @dataProvider various
*/ * @param $original
public function testJsonSerialize($original, $expected) */
{ public function testJsonSerialize($original)
$this->assertEquals(json_encode(new Ary($original)), json_encode((array)$original)); {
} $this->assertEquals(json_encode(new Ary($original)), json_encode((array)$original));
}
/** /**
* @dataProvider various * @dataProvider various
*/ */
public function ary() public function ary()
{ {
$test = ['x' => ['xx' => 'xxx']]; $test = ['x' => ['xx' => 'xxx']];
$ary = new Ary($test); $ary = new Ary($test);
$this->assertEquals(ary($test)->x['xx'], $ary->ary('x')->xx); $this->assertEquals(ary($test)->x['xx'], $ary->ary('x')->xx);
$this->assertEquals(ary(ary($test)->x['xx']), $ary->ary('x')->ary('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() // public function testOffsetExists()
// { // {