diff --git a/README.md b/README.md index d72e46a..8f75b0d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- Docker + Obelix, Asterix and Idefix.

Obelix diff --git a/composer.json b/composer.json index b2c2de8..a875f3b 100644 --- a/composer.json +++ b/composer.json @@ -32,5 +32,11 @@ }, "archive": { "exclude": ["obelix.jpg"] + }, + "scripts": { + "check": [ + "phpunit --stop-on-fail --coverage-text", + "phpstan analyse src --level 8" + ] } } diff --git a/src/Dot.php b/src/Dot.php index 9ac0bd7..3ac174a 100644 --- a/src/Dot.php +++ b/src/Dot.php @@ -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; @@ -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; @@ -100,6 +99,42 @@ public function get(string $path, $default = null): ResultSet return new ResultSet($value, $flatArray ?? []); } + /** + * @param array $user + * @param array $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 */ diff --git a/tests/DotTest.php b/tests/DotTest.php index 7b5ed21..574942a 100644 --- a/tests/DotTest.php +++ b/tests/DotTest.php @@ -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()); + } }