Skip to content

Commit 2119fd2

Browse files
committed
Added Dottet Key Array.
1 parent 7175a75 commit 2119fd2

File tree

6 files changed

+251
-19
lines changed

6 files changed

+251
-19
lines changed

README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,18 @@
22

33
This is a collection of array wrappers.
44

5+
Table of Contents
6+
=================
7+
* [Composite Key Array](#composite-key-array)
8+
- [1. offsetExists](#composite-key-array-offset-exists)
9+
- [2. offsetGet](#composite-key-array-offset-get)
10+
- [3. offsetSet](#composite-key-array-offset-set)
11+
- [4. offsetUnset](#composite-key-array-offset-unset)
12+
* [XPath Key Array](#xpath-key-array)
13+
* [Dotted Key Array](#dotted-key-array)
14+
15+
<a name="composite-key-array"></a>
16+
517
## Composite Key Array
618

719
Sometimes it is useful to have ability to access nested value with one,
@@ -10,6 +22,8 @@ array like, key (some questions were asked about this: [on quora](https://www.qu
1022
`CompositeKeyArray` class gives you the ability to do all basic array operations using array-like (nested) key.
1123
It implemets [`ArrayAccess`](http://php.net/manual/en/class.arrayaccess.php) interface:
1224

25+
<a name="composite-key-array-offset-exists"></a>
26+
1327
### 1. offsetExists:
1428

1529
You can check nested keys for existance.
@@ -25,6 +39,8 @@ You can check nested keys for existance.
2539
var_dump(isset($array[['foo', 'quux']])); // => bool(false)
2640
```
2741

42+
<a name="composite-key-array-offset-get"></a>
43+
2844
### 2. offsetGet:
2945

3046
You can get value by nested key. If nested key is not set the `UndefinedOffsetException` will be thrown.
@@ -40,6 +56,8 @@ You can get value by nested key. If nested key is not set the `UndefinedOffsetEx
4056
var_dump($array[['foo', 'quux']]); // => PHP Fatal error: Uncaught UndefinedOffsetException: Undefined offset quux.
4157
```
4258

59+
<a name="composite-key-array-offset-set"></a>
60+
4361
### 3. offsetSet:
4462

4563
You can set value for nested key.
@@ -96,6 +114,8 @@ But there is another edge case left: when you need to append element at the end
96114

97115
```
98116

117+
<a name="composite-key-array-offset-unset"></a>
118+
99119
### 4. offsetUnset:
100120

101121
You can unset nested key.
@@ -114,6 +134,8 @@ You can unset nested key.
114134

115135
After nested manipulations you might want to get back the **real array**. This can be done by calling `$array->toArray()`.
116136

137+
<a name="xpath-key-array"></a>
138+
117139
## XPath Key Array
118140

119141
**This is not a real xpath!** This class instead of array-like key users string of keys delimited with `/`.
@@ -133,4 +155,25 @@ This one was inspired by [an old article](http://codeaid.net/php/get-values-of-m
133155
Compared to `CompositeKeyArray`, `XPathKeyArray` has some limitations:
134156

135157
1. You cannot use keys with `/` in them.
158+
2. You cannot use `null` as key.
159+
160+
<a name="dotted-key-array"></a>
161+
162+
## Dotted Key Array
163+
164+
**This is not a real xpath!** This class instead of array-like key users string of keys delimited with `/`.
165+
166+
```php
167+
$array = new DottedKeyArray([
168+
'foo' => [
169+
'bar' => 'baz'
170+
]
171+
]);
172+
173+
var_dump($array['foo.bar']); // => string(3) "baz"
174+
```
175+
176+
Compared to `CompositeKeyArray`, `DottedKeyArray` has some limitations:
177+
178+
1. You cannot use keys with `.` in them.
136179
2. You cannot use `null` as key.

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"name": "sevavietl/arrays",
3+
"description": "This is a collection of array wrappers",
34
"type": "library",
45
"license": "MIT",
56
"authors": [

src/DottedKeyArray.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace Sevavietl\Arrays;
4+
5+
class DottedKeyArray extends SeparatedKeyArray
6+
{
7+
protected function getSeparator()
8+
{
9+
return '.';
10+
}
11+
}

src/SeparatedKeyArray.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Sevavietl\Arrays;
4+
5+
abstract class SeparatedKeyArray extends CompositeKeyArray
6+
{
7+
protected function setOffsets($offsets)
8+
{
9+
if ($this->notIntegerOrString($offsets)) {
10+
throw new InvalidOffsetTypeException("Invalid offset type: " . gettype($offsets) . ".");
11+
}
12+
13+
parent::setOffsets(array_map(function ($offset) {
14+
return $offset === '[]' ? [] : $offset;
15+
}, explode($this->getSeparator(), $offsets)));
16+
}
17+
18+
protected function notIntegerOrString($value)
19+
{
20+
return !$this->integerOrString($value);
21+
}
22+
23+
protected function integerOrString($value)
24+
{
25+
return is_integer($value) || is_string($value);
26+
}
27+
28+
abstract protected function getSeparator();
29+
}

src/XPathKeyArray.php

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,10 @@
22

33
namespace Sevavietl\Arrays;
44

5-
class XPathKeyArray extends CompositeKeyArray
5+
class XPathKeyArray extends SeparatedKeyArray
66
{
7-
protected function setOffsets($offsets)
7+
protected function getSeparator()
88
{
9-
if ($this->notIntegerOrString($offsets)) {
10-
throw new InvalidOffsetTypeException("Invalid offset type: " . gettype($offsets) . ".");
11-
}
12-
13-
parent::setOffsets(array_map(function ($offset) {
14-
return $offset === '[]' ? [] : $offset;
15-
}, explode('/', $offsets)));
16-
}
17-
18-
protected function notIntegerOrString($value)
19-
{
20-
return !$this->integerOrString($value);
21-
}
22-
23-
protected function integerOrString($value)
24-
{
25-
return is_integer($value) || is_string($value);
9+
return '/';
2610
}
2711
}

tests/unit/DottedKeyArrayTest.php

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
<?php
2+
3+
namespace Sevavietl\Arrays\Tests\Unit;
4+
5+
use Sevavietl\Arrays\DottedKeyArray;
6+
use Sevavietl\Arrays\UndefinedOffsetException;
7+
use Sevavietl\Arrays\InvalidOffsetTypeException;
8+
9+
class DottedKeyArrayTest extends \TestCase
10+
{
11+
/**
12+
* @dataProvider arrayDataProviderForIssetTesting
13+
*/
14+
public function testOffsetExists($array, $offset, $exists)
15+
{
16+
$array = new DottedKeyArray($array);
17+
18+
$this->assertEquals(
19+
$exists,
20+
isset($array[$offset])
21+
);
22+
}
23+
24+
public function arrayDataProviderForIssetTesting()
25+
{
26+
return [
27+
[[1], 0, true],
28+
[[1], 1, false],
29+
[[1], '', false],
30+
31+
[['foo' => 'bar'], 'foo', true],
32+
[['foo' => 'bar'], 'bar', false],
33+
34+
[['foo' => ['bar' => 'baz']], 'foo.bar', true],
35+
[['foo' => ['bar' => 'baz']], 'foo.baz', false],
36+
];
37+
}
38+
39+
/**
40+
* @dataProvider arrayDataProviderForGetTesting
41+
*/
42+
public function testOffsetGet($array, $offset, $value)
43+
{
44+
$array = new DottedKeyArray($array);
45+
46+
$this->assertEquals(
47+
$value,
48+
$array[$offset]
49+
);
50+
}
51+
52+
public function arrayDataProviderForGetTesting()
53+
{
54+
return [
55+
[[1], 0, 1],
56+
[[1 => [2 => 3]], '1.2', 3],
57+
58+
[['foo' => 'bar'], 'foo', 'bar'],
59+
[['foo' => ['bar' => 'baz']], 'foo.bar', 'baz'],
60+
];
61+
}
62+
63+
/**
64+
* @expectedException Sevavietl\Arrays\UndefinedOffsetException
65+
*/
66+
public function testOffsetGetThrowsUndefinedOffsetException()
67+
{
68+
$array = new DottedKeyArray();
69+
70+
$value = $array['foo.bar'];
71+
}
72+
73+
/**
74+
* @expectedException Sevavietl\Arrays\InvalidOffsetTypeException
75+
*/
76+
public function testOffsetGetThrowsInvalidOffsetTypeException()
77+
{
78+
$array = new DottedKeyArray();
79+
80+
$value = $array[['foo', 'bar']];
81+
}
82+
83+
/**
84+
* @dataProvider arrayDataProviderForSetTesting
85+
*/
86+
public function testOffsetSet($array, $offset, $value)
87+
{
88+
$array = new DottedKeyArray($array);
89+
90+
$array[$offset] = $value;
91+
92+
$this->assertEquals(
93+
$value,
94+
$array[$offset]
95+
);
96+
}
97+
98+
public function arrayDataProviderForSetTesting()
99+
{
100+
return [
101+
[[], 0, 1],
102+
[[], '0.1', 2],
103+
];
104+
}
105+
106+
public function testOffsetSetEdgeCases()
107+
{
108+
$array = new DottedKeyArray();
109+
110+
$array['[]'] = 'foo';
111+
112+
$this->assertEquals(
113+
'foo',
114+
$array[0]
115+
);
116+
117+
$array = new DottedKeyArray();
118+
119+
$array['1.[].2'] = 3;
120+
121+
$this->assertEquals(
122+
3,
123+
$array['1.0.2']
124+
);
125+
126+
$array = new DottedKeyArray();
127+
128+
$array['1.[].[].[].2'] = 3;
129+
130+
$this->assertEquals(
131+
3,
132+
$array['1.0.0.0.2']
133+
);
134+
}
135+
136+
/**
137+
* @dataProvider arrayDataProviderForUnsetTesting
138+
*/
139+
public function testOffsetUnset($array, $offset, $arrayAfterUnset)
140+
{
141+
$array = new DottedKeyArray($array);
142+
143+
unset($array[$offset]);
144+
145+
$this->assertEquals(
146+
$arrayAfterUnset,
147+
$array->toArray()
148+
);
149+
}
150+
151+
public function arrayDataProviderForUnsetTesting()
152+
{
153+
return [
154+
[[1], 0, []],
155+
[[1, 2], 0, [1 => 2]],
156+
157+
[['foo' => 'bar'], 'foo', []],
158+
[['foo' => 'bar', 'baz' => 'quux'], 'foo', ['baz' => 'quux']],
159+
160+
[['foo' => ['bar' => 'baz']], 'foo.bar', ['foo' => []]],
161+
[['foo' => ['bar' => ['baz']]], 'foo.bar.0', ['foo' => ['bar' => []]]],
162+
];
163+
}
164+
}

0 commit comments

Comments
 (0)