Skip to content

Commit

Permalink
Fixed case when library returned bad result when second key after dot…
Browse files Browse the repository at this point in the history
… is not unique in other subarrays. Also added hardest test case.
  • Loading branch information
roquie committed Aug 10, 2020
1 parent 6663964 commit db8564d
Show file tree
Hide file tree
Showing 4 changed files with 193 additions and 5 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<p align="center">
<img src="https://raw.githubusercontent.com/spacetab-io/obelix-php/master/obelix.jpg" alt="Docker" height=300>
<img src="https://raw.githubusercontent.com/spacetab-io/obelix-php/master/obelix.jpg" alt="Obelix, Asterix and Idefix.">
</p>

Obelix
Expand Down
6 changes: 6 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,11 @@
},
"archive": {
"exclude": ["obelix.jpg"]
},
"scripts": {
"check": [
"phpunit --stop-on-fail --coverage-text",
"phpstan analyse src --level 8"
]
}
}
43 changes: 39 additions & 4 deletions src/Dot.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,13 @@ public function get(string $path, $default = null): ResultSet
return new ResultSet(...self::$cache[$path]);
}

$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($this->items), RecursiveIteratorIterator::SELF_FIRST);

$pathway = [];
$flatArray = null;

$segments = (array) explode($this->delimiter, $path);
$countSegments = count($segments);
$countWildcards = count(array_filter($segments, fn($x) => $x === $this->wildcard));

$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($this->items), RecursiveIteratorIterator::SELF_FIRST);

foreach ($it as $key => $value) {
$pathway[$it->getDepth()] = $key;
Expand All @@ -81,7 +80,7 @@ public function get(string $path, $default = null): ResultSet
continue;
}

if (count(array_diff($segments, $pathway)) === $countWildcards) {
if ($this->isUserPathEqualsRealPath($segments, $pathway)) {
$flatArray[
implode($this->delimiter, array_slice($pathway, 0, $it->getDepth() + 1))
] = $value;
Expand All @@ -100,6 +99,42 @@ public function get(string $path, $default = null): ResultSet
return new ResultSet($value, $flatArray ?? []);
}

/**
* @param array<mixed> $user
* @param array<mixed> $real
* @return bool
*/
private function isUserPathEqualsRealPath(array $user, array $real): bool
{
if ($user === $real) {
return true;
}

$i = 0;
$equals = false;

foreach ($user as $item) {
$val = $real[$i] ?? false;

// to work with integer indexes in string path (for cases like "foo.0")
if (ctype_digit($item)) {
$item = (int) $item;
}

if ($val === $item) {
$equals = true;
} elseif ($item === $this->wildcard) {
$equals = true;
} else {
return false;
}

$i++;
}

return $equals;
}

/**
* @return array<string, mixed>
*/
Expand Down
147 changes: 147 additions & 0 deletions tests/DotTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -384,4 +384,151 @@ public function testGetItemWithWildcardAndAssociativeArray()
'foo.bar.key2' => 2,
], $dot->get('foo.*.*')->getMap());
}

public function testGetItemWhenSecondKeyIsNotUniqueInSubArrays()
{
$array = [
'server' => [
'foo' => [
'headers' => [
'omg' => 'it'
],
],
'prerender' => [
'headers' => [],
],
'logger' => [
'enabled' => false,
'level' => 'info',
],
'headers' => [
'foo' => 'bar',
],
],
];

$dot = new Obelix\Dot($array);

$this->assertSame([
'foo' => 'bar'
], $dot->get('server.headers')->getValue());

$this->assertSame([
'server.headers' => [
'foo' => 'bar'
]
], $dot->get('server.headers')->getMap());
}

public function testWhenALotOfManyKeysAndValuesAreSameAkaHardestTest()
{
$array = [
'foo' => [
null,
'foo' => 'foo',
'bar' => 'bar',
'baz' => [
'foo' => 'bar',
'bar' => 'foo',
'baz' => [
'foo',
'bar',
'baz' => [
'baz', 'foo', 'bar', 0
]
],
'acme' => [
'foo' => -10001,
'bar',
'baz' => [
'baz', 'acme'
]
],
[],
false,
null
],
'faa' => [
'foo' => 'bar',
'bar' => 'foo',
'baz' => [
'faa',
'baa',
'baz' => [
'baz', 'foo', 'bar', 1
]
],
'acme' => [
'foo',
'bar',
'baz' => [
'baz', 'acme', 999
]
],
true,
[],
new \stdClass()
]
],
'bar' => 1,
'acme' => [
'foo',
'bar',
'baz' => [
'baz'
]
],
1,
0
];

$dot = new Obelix\Dot($array);

$result = $dot->get('foo.0');
$this->assertSame(null, $result->getValue());
$this->assertSame(['foo.0' => null], $result->getMap());

$result = $dot->get('foo.baz.baz.0');
$this->assertSame('foo', $result->getValue());
$this->assertSame(['foo.baz.baz.0' => 'foo'], $result->getMap());

$expected = [
'foo' => -10001,
'bar',
'baz' => [
'baz', 'acme'
]
];
$result = $dot->get('foo.baz.acme');
$this->assertSame($expected, $result->getValue());
$this->assertSame(['foo.baz.acme' => $expected], $result->getMap());

$result = $dot->get('foo.faa.2');
$this->assertInstanceOf(\stdClass::class, $result->getValue());
$this->assertSame($array['foo']['faa'][2], $result->getValue());
$this->assertSame(['foo.faa.2' => $array['foo']['faa'][2]], $result->getMap());

$result = $dot->get('foo.faa.0');
$this->assertSame(true, $result->getValue());
$this->assertSame(['foo.faa.0' => true], $result->getMap());

$expected = [
'baz', 'acme', 999
];
$result = $dot->get('foo.faa.acme.baz');
$this->assertSame($expected, $result->getValue());
$this->assertSame(['foo.faa.acme.baz' => $expected], $result->getMap());

$expected = [
'baz', 'foo', 'bar', 0
];
$result = $dot->get('foo.baz.baz.baz.*');
$this->assertSame($expected, $result->getValue());
$this->assertSame([
'foo.baz.baz.baz.0' => 'baz',
'foo.baz.baz.baz.1' => 'foo',
'foo.baz.baz.baz.2' => 'bar',
'foo.baz.baz.baz.3' => 0,
], $result->getMap());
}
}

0 comments on commit db8564d

Please sign in to comment.