1
0
mirror of https://github.com/halaxa/json-machine.git synced 2025-03-15 17:09:39 +01:00
This commit is contained in:
Filip Halaxa 2024-11-21 12:18:18 +01:00
parent 85aeb9ae91
commit f8fad15425
5 changed files with 180 additions and 76 deletions

View File

@ -29,7 +29,7 @@ trait FacadeTrait
*
* @throws InvalidArgumentException
*/
private static function createParser($bytesIterator, ItemsOptions $options, bool $recursive): Parser
private static function createParser(iterable $bytesIterator, ItemsOptions $options, bool $recursive): Parser
{
if ($options['debug']) {
$tokensClass = TokensWithDebugging::class;
@ -55,27 +55,6 @@ trait FacadeTrait
return $this->parser->getPosition();
}
public function getJsonPointers(): array
{
return $this->parser->getJsonPointers();
}
/**
* @throws Exception\JsonMachineException
*/
public function getCurrentJsonPointer(): string
{
return $this->parser->getCurrentJsonPointer();
}
/**
* @throws Exception\JsonMachineException
*/
public function getMatchedJsonPointer(): string
{
return $this->parser->getMatchedJsonPointer();
}
/**
* @param string $string
*/

View File

@ -76,4 +76,25 @@ final class Items implements \IteratorAggregate, PositionAware
{
return $this->parser->getIterator();
}
public function getJsonPointers(): array
{
return $this->parser->getJsonPointers();
}
/**
* @throws Exception\JsonMachineException
*/
public function getCurrentJsonPointer(): string
{
return $this->parser->getCurrentJsonPointer();
}
/**
* @throws Exception\JsonMachineException
*/
public function getMatchedJsonPointer(): string
{
return $this->parser->getMatchedJsonPointer();
}
}

View File

@ -5,7 +5,10 @@ declare(strict_types=1);
namespace JsonMachine;
use Iterator;
use IteratorAggregate;
use JsonMachine\Exception\InvalidArgumentException;
use JsonMachine\Exception\JsonMachineException;
use LogicException;
/**
* Entry-point facade for recursive iteration.
@ -14,7 +17,7 @@ final class RecursiveItems implements \RecursiveIterator, PositionAware
{
use FacadeTrait;
/** @var Parser */
/** @var IteratorAggregate */
private $parser;
/** @var ItemsOptions */
@ -23,8 +26,12 @@ final class RecursiveItems implements \RecursiveIterator, PositionAware
/** @var Iterator */
private $parserIterator;
public function __construct(Parser $parser, ItemsOptions $options)
public function __construct(IteratorAggregate $parser, ?ItemsOptions $options = null)
{
if ( ! $options) {
$options = new ItemsOptions();
}
$this->parser = $parser;
$this->options = $options;
$this->debugEnabled = $options['debug'];
@ -85,8 +92,16 @@ final class RecursiveItems implements \RecursiveIterator, PositionAware
public function current()
{
$current = $this->parserIterator->current();
if ($current instanceof Parser) {
if ($current instanceof IteratorAggregate) {
return new self($current, $this->options);
} elseif ( ! is_scalar($current)) {
throw new JsonMachineException(
sprintf(
'%s only accepts scalar or IteratorAggregate values. %s given.',
self::class,
is_object($current) ? get_class($current) : gettype($current)
)
);
}
return $current;
@ -127,4 +142,54 @@ final class RecursiveItems implements \RecursiveIterator, PositionAware
return null;
}
/**
* Finds the desired key on this level and returns its value.
* It moves the internal cursor to it so subsequent calls to self::current() returns the same value.
*
* @param $key
* @return mixed
* @throws JsonMachineException When the key is not found on this level.
*/
public function advanceToKey($key)
{
if ( ! $this->parserIterator) {
$this->rewind();
}
$iterator = $this->parserIterator;
while ($key !== $iterator->key() && $iterator->valid()) {
$iterator->next();
}
if ($key !== $iterator->key()) {
throw new JsonMachineException("Key '$key' was not found.");
}
return $iterator->current();
}
/**
* Recursively materializes this iterator level to array.
* Moves its internal pointer to the end.
*
* @return array
*/
public function toArray(): array
{
return self::toArrayRecursive($this);
}
private static function toArrayRecursive(\Traversable $traversable): array
{
$array = [];
foreach ($traversable as $key => $value) {
if ($value instanceof \Traversable) {
$value = self::toArrayRecursive($value);
}
$array[$key] = $value;
}
return $array;
}
}

View File

@ -33,18 +33,7 @@ class NestedIteratorTest extends TestCase
}
}
public function testHasChildrenFollowsIterators()
{
$generator = function () {yield from [1, new \ArrayIterator([]), 3]; };
$iterator = new NestedIterator($generator());
$result = [];
foreach ($iterator as $item) {
$result[] = $iterator->hasChildren();
}
$this->assertSame([false, true, false], $result);
}
public function testGetChildrenReturnsCorrectItems()
{
@ -57,44 +46,4 @@ class NestedIteratorTest extends TestCase
$this->assertSame([1, 2, 3], $result);
}
public function testAdvanceToKeyWorks()
{
$generator = function () {yield from ['one' => 1, 'two' => 2, 'three' => 3]; };
$iterator = new NestedIterator($generator());
$this->assertSame(1, $iterator->advanceToKey('one'));
$this->assertSame(1, $iterator->advanceToKey('one'));
$this->assertSame(2, $iterator->advanceToKey('two'));
$this->assertSame(3, $iterator->advanceToKey('three'));
}
public function testAdvanceToKeyThrows()
{
$generator = function () {yield from ['one' => 1, 'two' => 2, 'three' => 3]; };
$iterator = new NestedIterator($generator());
$this->expectExceptionMessage('not found');
$iterator->advanceToKey('four');
}
public function testToArray()
{
$generator = function ($iterable) {yield from ['one' => 1, 'two' => 2, 'i' => $iterable, 'three' => 3]; };
$iterator = new NestedIterator($generator($generator(['42'])));
$expected = [
'one' => 1,
'two' => 2,
'i' => [
'one' => 1,
'two' => 2,
'i' => ['42'],
'three' => 3,
],
'three' => 3,
];
$this->assertSame($expected, $iterator->toArray());
}
}

View File

@ -4,6 +4,9 @@ declare(strict_types=1);
namespace JsonMachineTest;
use Iterator;
use IteratorAggregate;
use JsonMachine\ItemsOptions;
use JsonMachine\RecursiveItems;
/**
@ -67,4 +70,91 @@ class RecursiveItemsTest extends \PHPUnit_Framework_TestCase
$this->assertInstanceOf(RecursiveItems::class, $result[1]);
$this->assertSame(null, $result[2]);
}
public function testAdvanceToKeyWorks()
{
$generator = function () {yield from ['one' => 1, 'two' => 2, 'three' => 3]; };
$iterator = new RecursiveItems(toIteratorAggregate($generator()));
$this->assertSame(1, $iterator->advanceToKey('one'));
$this->assertSame(1, $iterator->advanceToKey('one'));
$this->assertSame(2, $iterator->advanceToKey('two'));
$this->assertSame(3, $iterator->advanceToKey('three'));
}
public function testAdvanceToKeyThrows()
{
$generator = function () {yield from ['one' => 1, 'two' => 2, 'three' => 3]; };
$iterator = new RecursiveItems(toIteratorAggregate($generator()));
$this->expectExceptionMessage('not found');
$iterator->advanceToKey('four');
}
public function testToArray()
{
$generator = function ($iterable) {yield from ['one' => 1, 'two' => 2, 'i' => $iterable, 'three' => 3]; };
$iterator = new RecursiveItems(
toIteratorAggregate($generator(
toIteratorAggregate($generator(
toIteratorAggregate(new \ArrayIterator(['42']))
))
))
);
$expected = [
'one' => 1,
'two' => 2,
'i' => [
'one' => 1,
'two' => 2,
'i' => ['42'],
'three' => 3,
],
'three' => 3,
];
$this->assertSame($expected, $iterator->toArray());
}
public function testHasChildrenFollowsIterators()
{
$generator = function () {yield from [1, toIteratorAggregate(new \ArrayIterator([])), 3]; };
$iterator = new RecursiveItems(toIteratorAggregate($generator()));
$result = [];
foreach ($iterator as $item) {
$result[] = $iterator->hasChildren();
}
$this->assertSame([false, true, false], $result);
}
public function testToArrayThrowsMeaningfulErrorWhenIteratorIsAlreadyOpen()
{
$generator = function () {yield from ['one' => 1, 'two' => 2, 'three' => 3]; };
$iterator = new RecursiveItems(toIteratorAggregate($generator()));
$iterator->rewind();
$iterator->next();
$iterator->rewind();
$iterator->next();
var_dump($iterator->toArray());
}
}
function toIteratorAggregate(Iterator $iterator): IteratorAggregate
{
return new class($iterator) implements IteratorAggregate {
private $iterator;
public function __construct(Iterator $iterator)
{
$this->iterator = $iterator;
}
public function getIterator()
{
return $this->iterator;
}
};
}