1
0
mirror of https://github.com/halaxa/json-machine.git synced 2025-01-17 21:18:23 +01:00

Merge branch 'master' into pr-61-multiple-json-pointers

This commit is contained in:
Filip Halaxa 2022-01-10 16:20:07 +01:00
commit ac1ae06f96
25 changed files with 152 additions and 141 deletions

View File

@ -7,9 +7,11 @@ $finder = PhpCsFixer\Finder::create()
$config = new PhpCsFixer\Config();
return $config->setRules([
'@PSR12' => true,
'array_syntax' => ['syntax' => 'short'],
'@Symfony' => true,
'yoda_style' => false,
'single_line_throw' => false,
'unary_operator_spaces' => false,
'visibility_required' => false,
])
->setFinder($finder)
;
;

View File

@ -7,7 +7,7 @@
- Removed deprecated `Decoder` interface.
### Changed
- Default decoding structure of `Parser` is object. (You don't notice unless you use `Parser` class directly)
- Default decoding structure of `Parser` is object. (You won't notice that unless you use `Parser` class directly)
<br>
<br>

View File

@ -11,7 +11,6 @@ class DebugLexer implements \IteratorAggregate, PositionAware
private $line = 1;
private $column = 0;
/**
* @param iterable<string> $jsonChunks
*/
@ -120,7 +119,9 @@ class DebugLexer implements \IteratorAggregate, PositionAware
}
/**
* @return integer The line number of the lexeme currently being processed (index starts at one).
* Returns the line number of the lexeme currently being processed (index starts at one).
*
* @return int
*/
public function getLine()
{
@ -128,7 +129,9 @@ class DebugLexer implements \IteratorAggregate, PositionAware
}
/**
* @return integer The, currently being processed, lexeme's position within the line (index starts at one).
* The position of currently being processed lexeme within the line (index starts at one).
*
* @return int
*/
public function getColumn()
{

View File

@ -2,8 +2,6 @@
namespace JsonMachine;
use JsonMachine\Exception\InvalidArgumentException;
class FileChunks implements \IteratorAggregate
{
/** @var string */
@ -14,7 +12,7 @@ class FileChunks implements \IteratorAggregate
/**
* @param string $fileName
* @param int $chunkSize
* @param int $chunkSize
*/
public function __construct($fileName, $chunkSize = 1024 * 8)
{

View File

@ -3,11 +3,11 @@
namespace JsonMachine;
use JsonMachine\Exception\InvalidArgumentException;
use JsonMachine\JsonDecoder\ItemDecoder;
use JsonMachine\JsonDecoder\ExtJsonDecoder;
use JsonMachine\JsonDecoder\ItemDecoder;
/**
* Entry-point facade for JSON Machine
* Entry-point facade for JSON Machine.
*/
final class Items implements \IteratorAggregate, PositionAware
{
@ -37,10 +37,11 @@ final class Items implements \IteratorAggregate, PositionAware
private $debugEnabled;
/**
* @param iterable $bytesIterator
* @param array|string $jsonPointer
* @param iterable $bytesIterator
* @param array|string $jsonPointer
* @param ItemDecoder $jsonDecoder
* @param bool $debugEnabled
* @param bool $debugEnabled
*
* @throws Exception\InvalidArgumentException
*/
public function __construct($bytesIterator, $jsonPointer = '', ItemDecoder $jsonDecoder = null, $debugEnabled = false)
@ -67,8 +68,9 @@ final class Items implements \IteratorAggregate, PositionAware
/**
* @param string $string
* @param array $options
*
* @return self
*
* @throws InvalidArgumentException
*/
public static function fromString($string, array $options = [])
@ -80,8 +82,9 @@ final class Items implements \IteratorAggregate, PositionAware
/**
* @param string $file
* @param array $options
*
* @return self
*
* @throws Exception\InvalidArgumentException
*/
public static function fromFile($file, array $options = [])
@ -93,8 +96,9 @@ final class Items implements \IteratorAggregate, PositionAware
/**
* @param resource $stream
* @param array $options
*
* @return self
*
* @throws Exception\InvalidArgumentException
*/
public static function fromStream($stream, array $options = [])
@ -106,8 +110,9 @@ final class Items implements \IteratorAggregate, PositionAware
/**
* @param iterable $iterable
* @param array $options
*
* @return self
*
* @throws Exception\InvalidArgumentException
*/
public static function fromIterable($iterable, array $options = [])
@ -133,8 +138,8 @@ final class Items implements \IteratorAggregate, PositionAware
}
/**
* @param array $options
* @return array{pointer: string, decoder: ItemDecoder, debug: bool}
*
* @throws InvalidArgumentException
*/
private static function normalizeOptions(array $options)

View File

@ -8,7 +8,6 @@ class DecodingError
private $errorMessage;
/**
* DecodingError constructor.
* @param string $malformedJson
* @param string $errorMessage
*/

View File

@ -20,6 +20,7 @@ class ErrorWrappingDecoder implements ItemDecoder
if (! $result->isOk()) {
return new ValidResult(new DecodingError($jsonScalarKey, $result->getErrorMessage()));
}
return $result;
}
@ -29,6 +30,7 @@ class ErrorWrappingDecoder implements ItemDecoder
if (! $result->isOk()) {
return new ValidResult(new DecodingError($jsonValue, $result->getErrorMessage()));
}
return $result;
}

View File

@ -13,6 +13,7 @@ class ExtJsonDecoder implements ItemDecoder
if ($decoded === null && $jsonValue !== 'null') {
return new InvalidResult(json_last_error_msg());
}
return new ValidResult($decoded);
}
}

View File

@ -26,7 +26,6 @@ trait ExtJsonDecoding
$this->options = $options;
}
public function decodeKey($jsonScalarKey)
{
// inlined
@ -34,6 +33,7 @@ trait ExtJsonDecoding
if ($decoded === null && $jsonScalarKey !== 'null') {
return new InvalidResult(json_last_error_msg());
}
return new ValidResult($decoded);
}
@ -44,6 +44,7 @@ trait ExtJsonDecoding
if ($decoded === null && $jsonScalarKey !== 'null') {
return new InvalidResult(json_last_error_msg());
}
return new ValidStringResult($decoded);
}
}

View File

@ -27,7 +27,7 @@ class Lexer implements \IteratorAggregate, PositionAware
foreach (range(0, 255) as $ord) {
if (! in_array(
chr($ord),
["\\", '"', "\xEF", "\xBB", "\xBF", ' ', "\n", "\r", "\t", '{', '}', '[', ']', ':', ',']
['\\', '"', "\xEF", "\xBB", "\xBF", ' ', "\n", "\r", "\t", '{', '}', '[', ']', ':', ',']
)) {
${chr($ord)} = true;
}
@ -96,7 +96,7 @@ class Lexer implements \IteratorAggregate, PositionAware
$utf8bom1 => true,
$utf8bom2 => true,
$utf8bom3 => true,
' ' => true,
' ' => true,
"\n" => true,
"\r" => true,
"\t" => true,

View File

@ -7,8 +7,8 @@ use JsonMachine\Exception\JsonMachineException;
use JsonMachine\Exception\PathNotFoundException;
use JsonMachine\Exception\SyntaxError;
use JsonMachine\Exception\UnexpectedEndSyntaxErrorException;
use JsonMachine\JsonDecoder\ItemDecoder;
use JsonMachine\JsonDecoder\ExtJsonDecoder;
use JsonMachine\JsonDecoder\ItemDecoder;
use Traversable;
class Parser implements \IteratorAggregate, PositionAware
@ -45,8 +45,7 @@ class Parser implements \IteratorAggregate, PositionAware
private $jsonDecoder;
/**
* @param Traversable $lexer
* @param array|string $jsonPointer Follows json pointer RFC https://tools.ietf.org/html/rfc6901
* @param array|string $jsonPointer Follows json pointer RFC https://tools.ietf.org/html/rfc6901
* @param ItemDecoder $jsonDecoder
* @throws InvalidArgumentException
*/
@ -210,14 +209,14 @@ class Parser implements \IteratorAggregate, PositionAware
}
$tokenType = ${$token[0]};
if (0 === ($tokenType & $expectedType)) {
$this->error("Unexpected symbol", $token);
$this->error('Unexpected symbol', $token);
}
$isValue = ($tokenType | 23) === 23; // 23 = self::ANY_VALUE
if (!$inObject && $isValue && $currentLevel < $iteratorLevel) {
$currentPathChanged = true;
$currentPath[$currentLevel] = isset($currentPath[$currentLevel]) ? (string)(1+(int)$currentPath[$currentLevel]) : "0";
$currentPath[$currentLevel] = isset($currentPath[$currentLevel]) ? (string) (1 + (int) $currentPath[$currentLevel]) : '0';
$currentPathWildcard[$currentLevel] = preg_match('/^(?:\d+|-)$/', $jsonPointerPath[$currentLevel]) ? '-' : $currentPath[$currentLevel];
unset($currentPath[$currentLevel+1], $currentPathWildcard[$currentLevel+1], $stack[$currentLevel+1]);
unset($currentPath[$currentLevel + 1], $currentPathWildcard[$currentLevel+1], $stack[$currentLevel+1]);
}
if (
(
@ -230,7 +229,7 @@ class Parser implements \IteratorAggregate, PositionAware
! $objectKeyExpected
&& (
($currentLevel === $iteratorLevel && $isValue)
|| ($currentLevel+1 === $iteratorLevel && ($tokenType | 3) === 3) // 3 = self::SCALAR_VALUE
|| ($currentLevel + 1 === $iteratorLevel && ($tokenType | 3) === 3) // 3 = self::SCALAR_VALUE
)
)
)
@ -254,7 +253,7 @@ class Parser implements \IteratorAggregate, PositionAware
$currentPathChanged = true;
$currentPath[$currentLevel] = $keyResult->getValue();
$currentPathWildcard[$currentLevel] = $keyResult->getValue();
unset($keyResult, $currentPath[$currentLevel+1], $currentPathWildcard[$currentLevel+1]);
unset($keyResult, $currentPath[$currentLevel + 1], $currentPathWildcard[$currentLevel+1]);
}
continue 2; // a valid json chunk is not completed yet
}

View File

@ -5,7 +5,7 @@ namespace JsonMachine;
interface PositionAware
{
/**
* Returns a number of processed bytes from the beginning
* Returns a number of processed bytes from the beginning.
*
* @return int
*/

View File

@ -14,12 +14,12 @@ class StreamChunks implements \IteratorAggregate
/**
* @param resource $stream
* @param int $chunkSize
* @param int $chunkSize
*/
public function __construct($stream, $chunkSize = 1024 * 8)
{
if (! is_resource($stream) || get_resource_type($stream) !== 'stream') {
throw new InvalidArgumentException("Argument \$stream must be a valid stream resource.");
throw new InvalidArgumentException('Argument $stream must be a valid stream resource.');
}
$this->stream = $stream;
$this->chunkSize = $chunkSize;

View File

@ -12,7 +12,7 @@ class StringChunks implements \IteratorAggregate
/**
* @param string $string
* @param int $chunkSize
* @param int $chunkSize
*/
public function __construct($string, $chunkSize = 1024 * 8)
{
@ -27,7 +27,7 @@ class StringChunks implements \IteratorAggregate
public function getIterator()
{
$len = strlen($this->string);
for ($i=0; $i<$len; $i += $this->chunkSize) {
for ($i = 0; $i < $len; $i += $this->chunkSize) {
yield substr($this->string, $i, $this->chunkSize);
}
}

View File

@ -1,6 +1,6 @@
<?php
require_once __DIR__ . '/../../vendor/autoload.php';
require_once __DIR__.'/../../vendor/autoload.php';
$client = new \GuzzleHttp\Client();
$response = $client->request('GET', 'https://httpbin.org/anything?key=value');

View File

@ -2,15 +2,15 @@
use JsonMachine\Items;
require_once __DIR__ . '/../../vendor/autoload.php';
require_once __DIR__.'/../../vendor/autoload.php';
ini_set('memory_limit', 128*1024*1024);
ini_set('memory_limit', 128 * 1024 * 1024);
function dummy()
{
$i = 0;
$string = file_get_contents(__DIR__ . '/../../test/performance/twitter_example_0.json');
$item = '[' . str_repeat($string.",", 400).$string . ']';
$string = file_get_contents(__DIR__.'/../../test/performance/twitter_example_0.json');
$item = '['.str_repeat($string.',', 400).$string.']';
var_dump(strlen($item));
yield '[';
@ -27,7 +27,7 @@ $items = Items::fromIterable(dummy());
$previousReport = '';
foreach ($items as $i => $item) {
$report = memory_get_peak_usage()
.":".memory_get_peak_usage(true)
.':'.memory_get_peak_usage(true)
;
if ($report !== $previousReport) {

View File

@ -4,7 +4,7 @@ use JsonMachine\Items;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Contracts\HttpClient\ResponseStreamInterface;
require_once __DIR__ . '/../../vendor/autoload.php';
require_once __DIR__.'/../../vendor/autoload.php';
function httpClientChunks(ResponseStreamInterface $responseStream)
{

View File

@ -11,7 +11,7 @@ class FileChunksTest extends \PHPUnit_Framework_TestCase
*/
public function testGeneratorYieldsStringChunks($chunkSize, array $expectedResult)
{
$fileChunks = new FileChunks(__DIR__ . '/ItemsTest.json', $chunkSize);
$fileChunks = new FileChunks(__DIR__.'/ItemsTest.json', $chunkSize);
$result = iterator_to_array($fileChunks);
$this->assertSame($expectedResult, $result);
@ -20,9 +20,9 @@ class FileChunksTest extends \PHPUnit_Framework_TestCase
public function data_testGeneratorYieldsFileChunks()
{
return [
[5, ['{"pat','h": {','"key"',':"val','ue"}}', "\n"]],
[6, ['{"path','": {"k','ey":"v','alue"}','}' . "\n"]],
[1024, ['{"path": {"key":"value"}}' . "\n"]],
[5, ['{"pat', 'h": {', '"key"', ':"val', 'ue"}}', "\n"]],
[6, ['{"path', '": {"k', 'ey":"v', 'alue"}', '}'."\n"]],
[1024, ['{"path": {"key":"value"}}'."\n"]],
];
}
}

View File

@ -2,8 +2,8 @@
namespace JsonMachineTest;
use JsonMachine\JsonDecoder\PassThruDecoder;
use JsonMachine\Items;
use JsonMachine\JsonDecoder\PassThruDecoder;
class ItemsTest extends \PHPUnit_Framework_TestCase
{
@ -18,7 +18,7 @@ class ItemsTest extends \PHPUnit_Framework_TestCase
'pointer' => $args[1],
'decoder' => $args[2],
'debug' => $args[3],
]
],
]);
$this->assertSame($expected, iterator_to_array($iterator));
}
@ -27,7 +27,7 @@ class ItemsTest extends \PHPUnit_Framework_TestCase
{
$iterator = Items::fromString('{"path": {"key":"value"}}');
foreach ($iterator as $item) {
$this->assertEquals((object)['key' => 'value'], $item);
$this->assertEquals((object) ['key' => 'value'], $item);
}
}
@ -41,13 +41,13 @@ class ItemsTest extends \PHPUnit_Framework_TestCase
foreach ([
[$extJsonResult, 'fromStream', fopen('data://text/plain,{"path": {"key":"value"}}', 'r'), '/path', null, $debug],
[$extJsonResult, 'fromString', '{"path": {"key":"value"}}', '/path', null, $debug],
[$extJsonResult, 'fromFile', __DIR__ . '/ItemsTest.json', '/path', null, $debug],
[$extJsonResult, 'fromFile', __DIR__.'/ItemsTest.json', '/path', null, $debug],
[$extJsonResult, 'fromIterable', ['{"path": {"key', '":"value"}}'], '/path', null, $debug],
[$extJsonResult, 'fromIterable', new \ArrayIterator(['{"path": {"key', '":"value"}}']), '/path', null, $debug],
[$passThruResult, 'fromStream', fopen('data://text/plain,{"path": {"key":"value"}}', 'r'), '/path', $ptDecoder, $debug],
[$passThruResult, 'fromString', '{"path": {"key":"value"}}', '/path', $ptDecoder, $debug],
[$passThruResult, 'fromFile', __DIR__ . '/ItemsTest.json', '/path', $ptDecoder, $debug],
[$passThruResult, 'fromFile', __DIR__.'/ItemsTest.json', '/path', $ptDecoder, $debug],
[$passThruResult, 'fromIterable', ['{"path": {"key', '":"value"}}'], '/path', $ptDecoder, $debug],
[$passThruResult, 'fromIterable', new \ArrayIterator(['{"path": {"key', '":"value"}}']), '/path', $ptDecoder, $debug],
] as $case) {

View File

@ -2,11 +2,10 @@
namespace JsonMachineTest\JsonDecoder;
use JsonMachine\Items;
use JsonMachine\JsonDecoder\DecodingError;
use JsonMachine\JsonDecoder\DecodingResult;
use JsonMachine\JsonDecoder\ErrorWrappingDecoder;
use JsonMachine\JsonDecoder\ExtJsonDecoder;
use JsonMachine\Items;
use JsonMachine\JsonDecoder\InvalidResult;
use JsonMachine\JsonDecoder\ValidResult;
use PHPUnit_Framework_TestCase;
@ -15,7 +14,6 @@ class ErrorWrappingDecoderTest extends PHPUnit_Framework_TestCase
{
/**
* @dataProvider data_testTrueFalseMatrix
* @param array $case
*/
public function testTrueFalseMatrix(array $case)
{
@ -42,41 +40,41 @@ class ErrorWrappingDecoderTest extends PHPUnit_Framework_TestCase
[
[
'decodeValue' => $notOkResult,
'decodeKey' => $notOkResult,
'decodeKey' => $notOkResult,
'wrappedDecodeValue' => $wrappedNotOkResult,
'wrappedDecodeKey' => $wrappedNotOkResult,
]
],
],
[
[
'decodeValue' => $notOkResult,
'decodeKey' => $okResult,
'decodeKey' => $okResult,
'wrappedDecodeValue' => $wrappedNotOkResult,
'wrappedDecodeKey' => $wrappedOkResult,
]
],
],
[
[
'decodeValue' => $okResult,
'decodeKey' => $notOkResult,
'decodeKey' => $notOkResult,
'wrappedDecodeValue' => $wrappedOkResult,
'wrappedDecodeKey' => $wrappedNotOkResult,
]
],
],
[
[
'decodeValue' => $okResult,
'decodeKey' => $okResult,
'decodeKey' => $okResult,
'wrappedDecodeValue' => $wrappedOkResult,
'wrappedDecodeKey' => $wrappedOkResult,
]
],
],
];
}
public function testCatchesErrorInsideIteratedJsonChunk()
{
$json = /** @lang JSON */ '
$json = /* @lang JSON */ '
{
"results": [
{"correct": "correct"},

View File

@ -17,26 +17,26 @@ class ExtJsonDecodersTest extends PHPUnit_Framework_TestCase
$defaultDecoder = new $className();
$defaultResult = $defaultDecoder->$methodName($json);
$this->assertTrue("object" === gettype($defaultResult->getValue()));
$this->assertFalse("string" === gettype($defaultResult->getValue()->bigint));
$this->assertSame([["deeper"]], $defaultResult->getValue()->deep);
$this->assertTrue('object' === gettype($defaultResult->getValue()));
$this->assertFalse('string' === gettype($defaultResult->getValue()->bigint));
$this->assertSame([['deeper']], $defaultResult->getValue()->deep);
$assocDecoder = new $className(true);
$assocResult = $assocDecoder->$methodName($json);
$this->assertTrue("array" === gettype($assocResult->getValue()));
$this->assertTrue('array' === gettype($assocResult->getValue()));
$objDecoder = new $className(false);
$objResult = $objDecoder->$methodName($json);
$this->assertTrue("object" === gettype($objResult->getValue()));
$this->assertTrue('object' === gettype($objResult->getValue()));
$depthDecoder = new $className(true, 1);
$depthResult = $depthDecoder->$methodName($json);
$this->assertFalse($depthResult->isOk());
$this->assertSame("Maximum stack depth exceeded", $depthResult->getErrorMessage());
$this->assertSame('Maximum stack depth exceeded', $depthResult->getErrorMessage());
$bigintDecoder = new $className(null, 1, JSON_BIGINT_AS_STRING);
$bigintResult = $bigintDecoder->$methodName("123123123123123123123");
$this->assertSame("123123123123123123123", $bigintResult->getValue());
$bigintResult = $bigintDecoder->$methodName('123123123123123123123');
$this->assertSame('123123123123123123123', $bigintResult->getValue());
}
public function dataPassesOptionsToJsonDecode()

View File

@ -4,7 +4,6 @@ namespace JsonMachineTest;
use JsonMachine\DebugLexer;
use JsonMachine\Lexer;
use JsonMachine\Exception;
use JsonMachine\StreamChunks;
use JsonMachine\StringChunks;
@ -38,7 +37,7 @@ class LexerTest extends \PHPUnit_Framework_TestCase
public function testGeneratesTokens($lexerClass)
{
$data = ['{}[],:null,"string" false:', 'true,1,100000,1.555{-56]"","\\""'];
$expected = ['{','}','[',']',',',':','null',',','"string"','false',':','true',',','1',',','100000',',','1.555','{','-56',']','""',',','"\\""'];
$expected = ['{', '}', '[', ']', ',', ':', 'null', ',', '"string"', 'false', ':', 'true', ',', '1', ',', '100000', ',', '1.555', '{', '-56', ']', '""', ',', '"\\""'];
$this->assertEquals($expected, iterator_to_array(new $lexerClass(new \ArrayIterator($data))));
}
@ -47,8 +46,8 @@ class LexerTest extends \PHPUnit_Framework_TestCase
*/
public function testWithBOM($lexerClass)
{
$data = ["\xEF\xBB\xBF" . '{}'];
$expected = ['{','}'];
$data = ["\xEF\xBB\xBF".'{}'];
$expected = ['{', '}'];
$this->assertEquals($expected, iterator_to_array(new $lexerClass(new \ArrayIterator($data))));
}
@ -133,7 +132,7 @@ class LexerTest extends \PHPUnit_Framework_TestCase
'{', '"datafeed"', ':', '{', '"info"', ':', '{', '"category"', ':', '"Category name"', '}', ',',
'"programs"', ':', '[', '{', '"program_info"', ':', '{', '"id"', ':', '"X0\\"\\\\"', ',', '"number"', ':',
'123', ',', '"constant"', ':', 'false', '}', '}', ',', '{', '"program_info"', ':', '{', '"id"', ':',
'"\b\f\n\r\t\u0020X1"', '}', '}', ']', '}', '}'
'"\b\f\n\r\t\u0020X1"', '}', '}', ']', '}', '}',
];
foreach (range(1, strlen($json)) as $chunkLength) {
@ -156,12 +155,12 @@ class LexerTest extends \PHPUnit_Framework_TestCase
$i = 0;
foreach ($lexer as $token) {
$i++;
++$i;
$expectedToken = array_shift($expectedTokens);
$this->assertEquals($expectedToken[0], $token, 'token failed with expected token #' . $i);
$this->assertEquals($expectedToken[1], $lexer->getLine(), 'line failed with expected token #' . $i);
$this->assertEquals($expectedToken[2], $lexer->getColumn(), 'column failed with expected token #' . $i);
$this->assertEquals($expectedToken[0], $token, 'token failed with expected token #'.$i);
$this->assertEquals($expectedToken[1], $lexer->getLine(), 'line failed with expected token #'.$i);
$this->assertEquals($expectedToken[2], $lexer->getColumn(), 'column failed with expected token #'.$i);
}
}
@ -177,21 +176,21 @@ class LexerTest extends \PHPUnit_Framework_TestCase
$i = 0;
foreach ($lexer as $token) {
$i++;
++$i;
$expectedToken = array_shift($expectedTokens);
$this->assertEquals($expectedToken[0], $token, 'token failed with expected token #' . $i);
$this->assertEquals(1, $lexer->getLine(), 'line failed with expected token #' . $i);
$this->assertEquals(0, $lexer->getColumn(), 'column failed with expected token #' . $i);
$this->assertEquals($expectedToken[0], $token, 'token failed with expected token #'.$i);
$this->assertEquals(1, $lexer->getLine(), 'line failed with expected token #'.$i);
$this->assertEquals(0, $lexer->getColumn(), 'column failed with expected token #'.$i);
}
}
public function dataProvidesLocationalData()
{
return [
'cr new lines' => [__DIR__ . '/formatted-cr.json'],
'lf new lines' => [__DIR__ . '/formatted-lf.json'],
'crlf new lines' => [__DIR__ . '/formatted-crlf.json'],
'cr new lines' => [__DIR__.'/formatted-cr.json'],
'lf new lines' => [__DIR__.'/formatted-lf.json'],
'crlf new lines' => [__DIR__.'/formatted-crlf.json'],
];
}
@ -263,7 +262,7 @@ class LexerTest extends \PHPUnit_Framework_TestCase
['"geo"', 18, 3],
[':', 18, 8],
['null', 18, 10],
['}', 19, 1]
['}', 19, 1],
];
}
}

View File

@ -7,7 +7,6 @@ use JsonMachine\Exception\PathNotFoundException;
use JsonMachine\Exception\SyntaxError;
use JsonMachine\Exception\UnexpectedEndSyntaxErrorException;
use JsonMachine\JsonDecoder\ExtJsonDecoder;
use JsonMachine\Items;
use JsonMachine\Lexer;
use JsonMachine\Parser;
use JsonMachine\StringChunks;
@ -16,9 +15,10 @@ class ParserTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider dataSyntax
*
* @param string $jsonPointer
* @param string $json
* @param array $expectedResult
* @param array $expectedResult
*/
public function testSyntax($jsonPointer, $json, $expectedResult)
{
@ -34,38 +34,38 @@ class ParserTest extends \PHPUnit_Framework_TestCase
{
return [
['', '{}', []],
['', '{"a": "b"}', [['a'=>'b']]],
['', '{"a":{"b":{"c":1}}}', [['a'=>['b'=>['c'=>1]]]]],
['', '{"a": "b"}', [['a' => 'b']]],
['', '{"a":{"b":{"c":1}}}', [['a' => ['b' => ['c' => 1]]]]],
['', '[]', []],
['', '[null,true,false,"a",0,1,42.5]', [[0=>null],[1=>true],[2=>false],[3=>"a"],[4=>0],[5=>1],[6=>42.5]]],
['', '[{"c":1}]', [[['c'=>1]]]],
['', '[{"c":1},"string",{"d":2},false]', [[0=>['c'=>1]],[1=>"string"],[2=>['d'=>2]],[3=>false]]],
['', '[false,{"c":1},"string",{"d":2}]', [[0=>false],[1=>['c'=>1]],[2=>"string"],[3=>['d'=>2]]]],
['', '[{"c":1,"d":2}]', [[['c'=>1, 'd'=>2]]]],
['/', '{"":{"c":1,"d":2}}', [['c'=>1],['d'=>2]]],
['/~0', '{"~":{"c":1,"d":2}}', [['c'=>1],['d'=>2]]],
['/~1', '{"/":{"c":1,"d":2}}', [['c'=>1],['d'=>2]]],
['/path', '{"path":{"c":1,"d":2}}', [['c'=>1],['d'=>2]]],
['/path', '{"no":[null], "path":{"c":1,"d":2}}', [['c'=>1],['d'=>2]]],
['/0', '[{"c":1,"d":2}, [null]]', [['c'=>1],['d'=>2]]],
['/0/path', '[{"path":{"c":1,"d":2}}]', [['c'=>1],['d'=>2]]],
['/1/path', '[[null], {"path":{"c":1,"d":2}}]', [['c'=>1],['d'=>2]]],
['/path/0', '{"path":[{"c":1,"d":2}, [null]]}', [['c'=>1],['d'=>2]]],
['/path/1', '{"path":[null,{"c":1,"d":2}, [null]]}', [['c'=>1],['d'=>2]]],
['/path/to', '{"path":{"to":{"c":1,"d":2}}}', [['c'=>1],['d'=>2]]],
['/path/after-vector', '{"path":{"array":[],"after-vector":{"c":1,"d":2}}}', [['c'=>1],['d'=>2]]],
['/path/after-vector', '{"path":{"array":["item"],"after-vector":{"c":1,"d":2}}}', [['c'=>1],['d'=>2]]],
['/path/after-vector', '{"path":{"object":{"item":null},"after-vector":{"c":1,"d":2}}}', [['c'=>1],['d'=>2]]],
['/path/after-vectors', '{"path":{"array":[],"object":{},"after-vectors":{"c":1,"d":2}}}', [['c'=>1],['d'=>2]]],
['/0/0', '[{"0":{"c":1,"d":2}}]', [['c'=>1],['d'=>2]]],
['/1/1', '[0,{"1":{"c":1,"d":2}}]', [['c'=>1],['d'=>2]]],
'PR-19-FIX' => ['/datafeed/programs/1', file_get_contents(__DIR__.'/PR-19-FIX.json'), [['program_info'=>['id'=>'X1']]]],
'ISSUE-41-FIX' => ['/path', '{"path":[{"empty":{}},{"value":1}]}', [[["empty"=>[]]],[1=>["value"=>1]]]],
['/-', '[{"one": 1,"two": 2},{"three": 3,"four": 4}]', [['one'=>1], ['two'=>2], ['three'=>3], ['four'=>4]]],
['/zero/-', '{"zero":[{"one": 1,"two": 2},{"three": 3,"four": 4}]}', [['one'=>1], ['two'=>2], ['three'=>3], ['four'=>4]]],
['/zero/-/three', '{"zero":[{"one": 1,"two": 2},{"three": 3,"four": 4}]}', [['three'=>3]]],
'ISSUE-62#1' => ['/-/id', '[ {"id":125}, {"id":785}, {"id":459}, {"id":853} ]', [['id'=>125], ['id'=>785], ['id'=>459], ['id'=>853]]],
'ISSUE-62#2' => ['/key/-/id', '{"key": [ {"id":125}, {"id":785}, {"id":459}, {"id":853} ]}', [['id'=>125], ['id'=>785], ['id'=>459], ['id'=>853]]],
['', '[null,true,false,"a",0,1,42.5]', [[0 => null], [1 => true], [2 => false], [3 => 'a'], [4 => 0], [5 => 1], [6 => 42.5]]],
['', '[{"c":1}]', [[['c' => 1]]]],
['', '[{"c":1},"string",{"d":2},false]', [[0 => ['c' => 1]], [1 => 'string'], [2 => ['d' => 2]], [3 => false]]],
['', '[false,{"c":1},"string",{"d":2}]', [[0 => false], [1 => ['c' => 1]], [2 => 'string'], [3 => ['d' => 2]]]],
['', '[{"c":1,"d":2}]', [[['c' => 1, 'd' => 2]]]],
['/', '{"":{"c":1,"d":2}}', [['c' => 1], ['d' => 2]]],
['/~0', '{"~":{"c":1,"d":2}}', [['c' => 1], ['d' => 2]]],
['/~1', '{"/":{"c":1,"d":2}}', [['c' => 1], ['d' => 2]]],
['/path', '{"path":{"c":1,"d":2}}', [['c' => 1], ['d' => 2]]],
['/path', '{"no":[null], "path":{"c":1,"d":2}}', [['c' => 1], ['d' => 2]]],
['/0', '[{"c":1,"d":2}, [null]]', [['c' => 1], ['d' => 2]]],
['/0/path', '[{"path":{"c":1,"d":2}}]', [['c' => 1], ['d' => 2]]],
['/1/path', '[[null], {"path":{"c":1,"d":2}}]', [['c' => 1], ['d' => 2]]],
['/path/0', '{"path":[{"c":1,"d":2}, [null]]}', [['c' => 1], ['d' => 2]]],
['/path/1', '{"path":[null,{"c":1,"d":2}, [null]]}', [['c' => 1], ['d' => 2]]],
['/path/to', '{"path":{"to":{"c":1,"d":2}}}', [['c' => 1], ['d' => 2]]],
['/path/after-vector', '{"path":{"array":[],"after-vector":{"c":1,"d":2}}}', [['c' => 1], ['d' => 2]]],
['/path/after-vector', '{"path":{"array":["item"],"after-vector":{"c":1,"d":2}}}', [['c' => 1], ['d' => 2]]],
['/path/after-vector', '{"path":{"object":{"item":null},"after-vector":{"c":1,"d":2}}}', [['c' => 1], ['d' => 2]]],
['/path/after-vectors', '{"path":{"array":[],"object":{},"after-vectors":{"c":1,"d":2}}}', [['c' => 1], ['d' => 2]]],
['/0/0', '[{"0":{"c":1,"d":2}}]', [['c' => 1], ['d' => 2]]],
['/1/1', '[0,{"1":{"c":1,"d":2}}]', [['c' => 1], ['d' => 2]]],
'PR-19-FIX' => ['/datafeed/programs/1', file_get_contents(__DIR__.'/PR-19-FIX.json'), [['program_info' => ['id' => 'X1']]]],
'ISSUE-41-FIX' => ['/path', '{"path":[{"empty":{}},{"value":1}]}', [[['empty' => []]], [1 => ['value' => 1]]]],
['/-', '[{"one": 1,"two": 2},{"three": 3,"four": 4}]', [['one' => 1], ['two' => 2], ['three' => 3], ['four' => 4]]],
['/zero/-', '{"zero":[{"one": 1,"two": 2},{"three": 3,"four": 4}]}', [['one' => 1], ['two' => 2], ['three' => 3], ['four' => 4]]],
['/zero/-/three', '{"zero":[{"one": 1,"two": 2},{"three": 3,"four": 4}]}', [['three' => 3]]],
'ISSUE-62#1' => ['/-/id', '[ {"id":125}, {"id":785}, {"id":459}, {"id":853} ]', [['id' => 125], ['id' => 785], ['id' => 459], ['id' => 853]]],
'ISSUE-62#2' => ['/key/-/id', '{"key": [ {"id":125}, {"id":785}, {"id":459}, {"id":853} ]}', [['id' => 125], ['id' => 785], ['id' => 459], ['id' => 853]]],
[
['/meta_data', '/data/companies'],
'{"meta_data": {"total_rows": 2},"data": {"type": "companies","companies": [{"id": "1","company": "Company 1"},{"id": "2","company": "Company 2"}]}}',
@ -94,12 +94,12 @@ class ParserTest extends \PHPUnit_Framework_TestCase
['id'=>'2'],
]
]
];
}
/**
* @dataProvider dataThrowsOnNotFoundJsonPointer
*
* @param string $json
* @param string $jsonPointer
*/
@ -114,17 +114,17 @@ class ParserTest extends \PHPUnit_Framework_TestCase
public function dataThrowsOnNotFoundJsonPointer()
{
return [
"non existing pointer" => ['{}', '/not/found'],
'non existing pointer' => ['{}', '/not/found'],
"empty string should not match '0'" => ['{"0":[]}', '/'],
"empty string should not match 0" => ['[[]]', '/'],
"0 should not match empty string" => ['{"":[]}', '/0'],
'empty string should not match 0' => ['[[]]', '/'],
'0 should not match empty string' => ['{"":[]}', '/0'],
];
}
/**
* @dataProvider dataGetJsonPointer
*
* @param string $jsonPointer
* @param array $expectedJsonPointer
*/
public function testGetJsonPointerPath($jsonPointer, array $expectedJsonPointer)
{
@ -146,6 +146,7 @@ class ParserTest extends \PHPUnit_Framework_TestCase
/**
* @dataProvider dataThrowsOnMalformedJsonPointer
*
* @param string $jsonPointer
*/
public function testThrowsOnMalformedJsonPointer($jsonPointer)
@ -167,6 +168,7 @@ class ParserTest extends \PHPUnit_Framework_TestCase
/**
* @dataProvider dataSyntaxError
*
* @param string $malformedJson
*/
public function testSyntaxError($malformedJson)
@ -198,12 +200,13 @@ class ParserTest extends \PHPUnit_Framework_TestCase
['["string",,"string"]'],
['["string","string",]'],
['["string",1eeee1]'],
['{"key\u000Z": "non hex key"}']
['{"key\u000Z": "non hex key"}'],
];
}
/**
* @dataProvider dataUnexpectedEndError
*
* @param string $malformedJson
*/
public function testUnexpectedEndError($malformedJson)
@ -231,7 +234,7 @@ class ParserTest extends \PHPUnit_Framework_TestCase
['{"string":["string","string"]'],
['{"string":["string","string"'],
['{"string":["string","string",'],
['{"string":["string","string","str']
['{"string":["string","string","str'],
];
}
@ -272,7 +275,7 @@ class ParserTest extends \PHPUnit_Framework_TestCase
';
$parser = $this->createParser($json, '/result');
$this->assertSame(["result" => "one"], iterator_to_array($parser));
$this->assertSame(['result' => 'one'], iterator_to_array($parser));
}
public function testGeneratorYieldsNestedValues()
@ -342,7 +345,7 @@ class ParserTest extends \PHPUnit_Framework_TestCase
$items = new Parser(new Lexer(new StringChunks('[{"key": "value"}]')));
foreach ($items as $item) {
$this->assertEquals((object)['key' => 'value'], $item);
$this->assertEquals((object) ['key' => 'value'], $item);
}
}
}

View File

@ -1,6 +1,6 @@
<?php
require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__.'/../vendor/autoload.php';
if (! class_exists('PHPUnit_Framework_TestCase')) {
class_alias(\PHPUnit\Framework\TestCase::class, 'PHPUnit_Framework_TestCase');

View File

@ -2,7 +2,7 @@
use JsonMachine\Items;
require_once __DIR__ . '/../../vendor/autoload.php';
require_once __DIR__.'/../../vendor/autoload.php';
if (in_array('xdebug', get_loaded_extensions())) {
trigger_error('Xdebug enabled. Results may be affected.', E_USER_WARNING);
@ -29,21 +29,21 @@ $decoders = [
];
$tmpJsonFileName = createBigJsonFile();
$fileSizeMb = (filesize($tmpJsonFileName)/1024/1024);
echo "File size: " . round($fileSizeMb, 2)," MB".PHP_EOL;
$fileSizeMb = (filesize($tmpJsonFileName) / 1024 / 1024);
echo 'File size: '.round($fileSizeMb, 2),' MB'.PHP_EOL;
foreach ($decoders as $name => $decoder) {
$start = microtime(true);
$result = $decoder($tmpJsonFileName);
if (! $result instanceof \Traversable && ! is_array($result)) {
$textResult = "Decoding error";
$textResult = 'Decoding error';
} else {
foreach ($result as $key => $item) {
}
$time = microtime(true) - $start;
$textResult = round($fileSizeMb/$time, 2) . ' MB/s';
$textResult = round($fileSizeMb / $time, 2).' MB/s';
}
echo str_pad($name.": ", 37, '.')." $textResult".PHP_EOL;
echo str_pad($name.': ', 37, '.')." $textResult".PHP_EOL;
}
@unlink($tmpJsonFileName);
@ -53,12 +53,13 @@ function createBigJsonFile()
$f = fopen($tmpJson, 'w');
$separator = '';
fputs($f, '[');
for ($i=0; $i<6000; $i++) {
for ($i = 0; $i < 6000; ++$i) {
fputs($f, $separator);
fputs($f, file_get_contents(__DIR__.'/twitter_example_'. ($i%2) .'.json'));
fputs($f, file_get_contents(__DIR__.'/twitter_example_'.($i % 2).'.json'));
$separator = ",\n\n";
}
fputs($f, ']');
fclose($f);
return $tmpJson;
}