Skip to content

Commit

Permalink
Updated Exception Handling and added the remove function
Browse files Browse the repository at this point in the history
  • Loading branch information
David Schoenbauer committed Dec 1, 2016
1 parent 7f9ac8b commit add5e3f
Show file tree
Hide file tree
Showing 10 changed files with 463 additions and 35 deletions.
49 changes: 43 additions & 6 deletions src/DotNotation/ArrayDotNotation.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@

namespace DSchoenbauer\DotNotation;

use DSchoenbauer\DotNotation\Exception\PathNotArrayException;
use DSchoenbauer\DotNotation\Exception\PathNotFoundException;
use DSchoenbauer\DotNotation\Exception\TargetNotArrayException;
use DSchoenbauer\DotNotation\Exception\UnexpectedValueException;

/**
Expand Down Expand Up @@ -122,7 +125,7 @@ protected function recursiveGet($data, $keys, $defaultValue) {
* @param string $dotNotation dot notation representation of keys of where to set a value
* @param mixed $value any value to be stored with in a key structure of dot notation
* @return $this
* @throws UnexpectedValueException if a value in the dot notation path is not an array
* @throws PathNotArrayException if a value in the dot notation path is not an array
*/
public function set($dotNotation, $value) {
$this->recursiveSet($this->_data, explode('.', $dotNotation), $value);
Expand All @@ -137,7 +140,7 @@ public function set($dotNotation, $value) {
* @param array $data data to be traversed
* @param array $keys the remaining keys of focus for the data array
* @param mixed $value the value to be placed at the final key
* @throws UnexpectedValueException if a value in the dot notation path is not an array
* @throws PathNotArrayException if a value in the dot notation path is not an array
*/
protected function recursiveSet(array &$data, array $keys, $value) {
$key = array_shift($keys);
Expand All @@ -146,8 +149,8 @@ protected function recursiveSet(array &$data, array $keys, $value) {
} else {
if (!array_key_exists($key, $data)) {
$data[$key] = [];
}elseif (!is_array($data[$key])) {
throw new UnexpectedValueException("Array dot notation path key '$key' is not an array");
} elseif (!is_array($data[$key])) {
throw new Exception\PathNotArrayException($key);
}
$this->recursiveSet($data[$key], $keys, $value);
}
Expand All @@ -156,19 +159,53 @@ protected function recursiveSet(array &$data, array $keys, $value) {
/**
* Merges two arrays together over writing existing values with new values, while adding new array structure to the data
*
* @since 1.1.0
* @param string $dotNotation dot notation representation of keys of where to set a value
* @param array $value array to be merged with an existing array
* @return $this
* @throws UnexpectedValueException if a value in the dot notation path is not an array
* @throws UnexpectedValueException if the value in the dot notation target is not an array
* @throws TargetNotArrayException if the value in the dot notation target is not an array
*/
public function merge($dotNotation, array $value) {
$target = $this->get($dotNotation, []);
if (!is_array($target)) {
throw new UnexpectedValueException('Array dot notation target key value is not an array. Merge is not possible');
throw new Exception\TargetNotArrayException($dotNotation);
}
$this->set($dotNotation, array_merge_recursive($target, $value));
return $this;
}

/**
* Removes data from the array
*
* @since 1.1.0
* @param type $dotNotation
* @return $this
*/
public function remove($dotNotation) {
$this->recursiveRemove($this->_data, explode('.', $dotNotation), $dotNotation);
return $this;
}

/**
* Transverses the keys array until it reaches the appropriate key in the data array and then deletes the value from the key.
*
* @since 1.1.0
* @param array $data data to be traversed
* @param array $keys the remaining keys of focus for the data array
* @throws UnexpectedValueException if a value in the dot notation path is not an array
*/
protected function recursiveRemove(array &$data, array $keys) {
$key = array_shift($keys);
if (!array_key_exists($key, $data)) {
throw new PathNotFoundException($key);
} elseif ($key && count($keys) == 0) { //Last Key
unset($data[$key]);
} elseif (!is_array($data[$key])) {
throw new PathNotArrayException($key);
} else {
$this->recursiveRemove($data[$key], $keys);
}
}

}
39 changes: 39 additions & 0 deletions src/DotNotation/Enum/ExceptionMessage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

/*
* The MIT License
*
* Copyright 2016 David Schoenbauer <dschoenbauer@gmail.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

namespace DSchoenbauer\DotNotation\Enum;

/**
* A collection of messages used inside of exceptions
*
* @author David Schoenbauer <dschoenbauer@gmail.com>
*/
class ExceptionMessage {

const PATH_NOT_FOUND = "Path key: '%s' is not found";
const PATH_NOT_ARRAY = "Array dot notation path key '%s' is not an array";
const TARGET_NOT_ARRAY = "Array dot notation target key '%s' value is not an array.";
}
43 changes: 43 additions & 0 deletions src/DotNotation/Exception/PathNotArrayException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

/*
* The MIT License
*
* Copyright 2016 David Schoenbauer <dschoenbauer@gmail.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

namespace DSchoenbauer\DotNotation\Exception;

use DSchoenbauer\DotNotation\Enum\ExceptionMessage;

/**
* Description of PathNotArrayException
*
* @author David Schoenbauer <dschoenbauer@gmail.com>
*/
class PathNotArrayException extends UnexpectedValueException implements ExceptionInterface{
public function __construct($key = "", $message = "", $code = 0, $previous = null) {
if ($message == "" && $key !== "") {
$message = sprintf(ExceptionMessage::PATH_NOT_ARRAY, $key);
}
parent::__construct($message, $code, $previous);
}
}
45 changes: 45 additions & 0 deletions src/DotNotation/Exception/PathNotFoundException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

/*
* The MIT License
*
* Copyright 2016 David Schoenbauer <dschoenbauer@gmail.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

namespace DSchoenbauer\DotNotation\Exception;

use DSchoenbauer\DotNotation\Enum\ExceptionMessage;

/**
* Description of PathNotFoundException
*
* @author David Schoenbauer <dschoenbauer@gmail.com>
*/
class PathNotFoundException extends UnexpectedValueException implements ExceptionInterface {

public function __construct($key = "", $message = "", $code = 0, $previous = null) {
if ($message == "" && $key !== "") {
$message = sprintf(ExceptionMessage::PATH_NOT_FOUND, $key);
}
parent::__construct($message, $code, $previous);
}

}
45 changes: 45 additions & 0 deletions src/DotNotation/Exception/TargetNotArrayException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

/*
* The MIT License
*
* Copyright 2016 David Schoenbauer <dschoenbauer@gmail.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

namespace DSchoenbauer\DotNotation\Exception;

use DSchoenbauer\DotNotation\Enum\ExceptionMessage;

/**
* Description of TargetNotAnArrayException
*
* @author David Schoenbauer <dschoenbauer@gmail.com>
*/
class TargetNotArrayException extends UnexpectedValueException implements ExceptionInterface {

public function __construct($key = "", $message = "", $code = 0, $previous = null) {
if ($message == "" && $key !== "") {
$message = sprintf(ExceptionMessage::TARGET_NOT_ARRAY, $key);
}
parent::__construct($message, $code, $previous);
}

}
84 changes: 55 additions & 29 deletions tests/DotNotation/ArrayDotNotationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,16 @@ public function testSetDeepLevelNewValue() {
$this->assertEquals('newValue', $this->_object->set('LevelA.LevelB.LevelC.levelD', 'newValue')->get('LevelA.LevelB.LevelC.levelD'));
$this->assertEquals('someValue2', $this->_object->get('level1.level2'), "existing value compromised");
}

/**
* @expectedException UnexpectedValueException
* @expectedExceptionMessage Array dot notation path key 'c' is not an array
*/
public function testSetNotExistArrayButString() {
$data = ['a'=>['b'=>['c'=>'cValue']]];
$newData = ['e'=>'dValue'];
$this->_object->setData($data)->set('a.b.c.d',$newData)->getData(); }
$data = ['a' => ['b' => ['c' => 'cValue']]];
$newData = ['e' => 'dValue'];
$this->_object->setData($data)->set('a.b.c.d', $newData)->getData();
}

public function testSetInitialLevelExistingValue() {
$this->assertEquals('levelB', $this->_object->get('levelB'));
Expand All @@ -87,39 +88,64 @@ public function testSetDataTypeConversion() {
$this->assertEquals('newValue', $this->_object->set('levelA', 'newValue')->get('levelA'));
$this->assertEquals('someValue2', $this->_object->get('level1.level2'), "existing value compromised");
}
public function testMergeSimpleArray(){
$data = ['a'=>['b'=>['c'=>'cValue']]];
$merge = ['d'=>'dValue'];
$this->assertEquals('dValue',$this->_object->setData($data)->merge('a.b',$merge)->get('a.b.d'));
$this->assertEquals('cValue',$this->_object->get('a.b.c'));

public function testMergeSimpleArray() {
$data = ['a' => ['b' => ['c' => 'cValue']]];
$merge = ['d' => 'dValue'];
$this->assertEquals('dValue', $this->_object->setData($data)->merge('a.b', $merge)->get('a.b.d'));
$this->assertEquals('cValue', $this->_object->get('a.b.c'));
}

/**
* @expectedException UnexpectedValueException
* @expectedExceptionMessage Array dot notation target key value is not an array. Merge is not possible
* @expectedExceptionMessage Array dot notation target key 'a.b.c' value is not an array.
*/
public function testMergeNotAnArray(){
$data = ['a'=>['b'=>['c'=>'cValue']]];
$merge = ['d'=>'dValue'];
$this->_object->setData($data)->merge('a.b.c',$merge);
public function testMergeNotAnArray() {
$data = ['a' => ['b' => ['c' => 'cValue']]];
$merge = ['d' => 'dValue'];
$this->_object->setData($data)->merge('a.b.c', $merge);
}

/**
* @expectedException UnexpectedValueException
* @expectedExceptionMessage Array dot notation path key 'c' is not an array
*/
public function testMergeNotPresentKeyString(){
$data = ['a'=>['b'=>['c'=>'cValue']]];
$merge = ['e'=>'dValue'];
$this->_object->setData($data)->merge('a.b.c.d',$merge)->getData();
}

public function testMergeNotPresentKey(){
$data = ['a'=>['b'=>['c'=>[]]]];
$merge = ['e'=>'dValue'];
$result = ['a'=>['b'=>['c'=>['d'=>['e'=>'dValue']]]]];
$this->assertEquals($result, $this->_object->setData($data)->merge('a.b.c.d',$merge)->getData());
*/
public function testMergeNotPresentKeyString() {
$data = ['a' => ['b' => ['c' => 'cValue']]];
$merge = ['e' => 'dValue'];
$this->_object->setData($data)->merge('a.b.c.d', $merge)->getData();
}

public function testMergeNotPresentKey() {
$data = ['a' => ['b' => ['c' => []]]];
$merge = ['e' => 'dValue'];
$result = ['a' => ['b' => ['c' => ['d' => ['e' => 'dValue']]]]];
$this->assertEquals($result, $this->_object->setData($data)->merge('a.b.c.d', $merge)->getData());
}

public function testRemove() {
$data = [
'levelA' => [],
'levelB' => 'levelB',
'level1' => [
'level2' => 'someValue2'
]
];
$this->assertEquals($data, $this->_object->remove('levelA.levelB')->getData());
}

/**
* @expectedException \DSchoenbauer\DotNotation\Exception\PathNotFoundException
*/
public function testRemovePathNotFound() {
$this->_object->remove('levelA.levelC');
}

/**
* @expectedException \DSchoenbauer\DotNotation\Exception\PathNotFoundException
*/
public function testRemovePathNotFoundShort() {
$this->_object->remove('level0');
}

}
Loading

0 comments on commit add5e3f

Please sign in to comment.