From 51ed0703731e323e0db85b36b73f5bbc9ede9698 Mon Sep 17 00:00:00 2001 From: Filip Halaxa Date: Tue, 21 Dec 2021 16:13:39 +0100 Subject: [PATCH] JsonMachine renamed to Items --- CHANGELOG.md | 11 ++-- README.md | 66 +++++++++---------- src/{JsonMachine.php => Items.php} | 2 +- src/examples/guzzleHttp.php | 2 +- src/examples/memLeak.php | 4 +- src/examples/symfonyHttpClient.php | 4 +- .../{JsonMachineTest.php => ItemsTest.php} | 10 +-- .../JsonDecoder/ErrorWrappingDecoderTest.php | 4 +- test/JsonMachineTest/ParserTest.php | 2 +- test/performance/testPerformance.php | 18 ++--- 10 files changed, 62 insertions(+), 61 deletions(-) rename src/{JsonMachine.php => Items.php} (98%) rename test/JsonMachineTest/{JsonMachineTest.php => ItemsTest.php} (89%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9eeb97a..0a120d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,10 @@ ## master ### Changed -- Object as default decoding structure instead of array. Same as json_decode() default. -- `JsonMachine::getIterator()` now returns `Parser`'s iterator directly. Call `JsonMachine::getIterator()` -instead of `JsonMachine::getIterator()::getIterator()` to get to `Parser`'s iterator. Fixes +- `JsonMachine` entry point class renamed to `Items`. +- Object as default decoding structure instead of array. Same as the json_decode() default. +- `Items::getIterator()` now returns `Parser`'s iterator directly. Call `Items::getIterator()` +instead of `Items::getIterator()::getIterator()` to get to `Parser`'s iterator. Fixes https://stackoverflow.com/questions/63706550 ### Added @@ -91,9 +92,9 @@ Alternative is to use `ExtJsonDecoder` which decodes items as objects by default $user) { // just process $user as usual @@ -101,9 +101,9 @@ It can be parsed this way: ```php $data) { // 1st iteration: $name === "apple" and $data->color === "red" @@ -119,9 +119,9 @@ If you prefer JSON Machine to return arrays instead of objects, use `new ExtJson $data) { // The same as above, which means: // 1st iteration: $name === "apple" and $data->color === "red" @@ -206,9 +206,9 @@ Get the single value of `lastModified` key like this: ```php $value) { // 1st and final iteration: // $key === 'lastModified' @@ -223,9 +223,9 @@ The obvious shortcut is: ```php ## Parsing streaming responses from a JSON API A stream API response or any other JSON stream is parsed exactly the same way as file is. The only difference -is, you use `JsonMachine::fromStream($streamResource)` for it, where `$streamResource` is the stream +is, you use `Items::fromStream($streamResource)` for it, where `$streamResource` is the stream resource with the JSON document. The rest is the same as with parsing files. Here are some examples of popular http clients which support streaming responses: @@ -261,7 +261,7 @@ popular http clients which support streaming responses: ### GuzzleHttp Guzzle uses its own streams, but they can be converted back to PHP streams by calling `\GuzzleHttp\Psr7\StreamWrapper::getResource()`. Pass the result of this function to -`JsonMachine::fromStream` function, and you're set up. See working +`Items::fromStream` function, and you're set up. See working [GuzzleHttp example](src/examples/guzzleHttp.php). @@ -273,7 +273,7 @@ based on iterators, the integration with Symfony HttpClient is very simple. See ## Tracking the progress -Big documents may take a while to parse. Call `JsonMachine::getPosition()` in your `foreach` to get current +Big documents may take a while to parse. Call `Items::getPosition()` in your `foreach` to get current count of the processed bytes from the beginning. Percentage is then easy to calculate as `position / total * 100`. To find out the total size of your document in bytes you may want to check: - `strlen($document)` if you parse a string @@ -284,10 +284,10 @@ To find out the total size of your document in bytes you may want to check: ```php $data) { echo 'Progress: ' . intval($fruits->getPosition() / $fileSize * 100) . ' %'; } @@ -296,7 +296,7 @@ foreach ($fruits as $name => $data) { ## Decoders -As the third and optional parameter of all the `JsonMachine::from*` functions is an instance of +As the third and optional parameter of all the `Items::from*` functions is an instance of `JsonMachine\JsonDecoder\Decoder`. If none is specified, `ExtJsonDecoder` is used by default. It requires `ext-json` PHP extension to be present, because it uses `json_decode`. When `json_decode` doesn't do what you want, implement `JsonMachine\JsonDecoder\Decoder` @@ -316,9 +316,9 @@ Example: $item) { if ($key instanceof DecodingError || $item instanceof DecodingError) { // handle error of this malformed json item @@ -371,13 +371,13 @@ PHP structures. Following table shows the difference: | | String items in memory at a time | Decoded PHP items in memory at a time | Total | |------------------------|---------------------------------:|--------------------------------------:|------:| | `json_decode()` | 10000 | 10000 | 20000 | -| `JsonMachine::from*()` | 1 | 1 | 2 | +| `Items::from*()` | 1 | 1 | 2 | -This means, that `JsonMachine` is constantly efficient for any size of processed JSON. 100 GB no problem. +This means, that JSON Machine is constantly efficient for any size of processed JSON. 100 GB no problem. ### In-memory JSON strings -There is also a method `JsonMachine::fromString()`. If you are +There is also a method `Items::fromString()`. If you are forced to parse a big string, and the stream is not available, JSON Machine may be better than `json_decode`. The reason is that unlike `json_decode`, JSON Machine still traverses the JSON string one item at a time and doesn't load all resulting PHP structures into memory at once. @@ -391,9 +391,9 @@ behaviour as with streams/files. Following table puts the concept into perspecti | | String items in memory at a time | Decoded PHP items in memory at a time | Total | |-----------------------------|---------------------------------:|--------------------------------------:|------:| | `json_decode()` | 10000 | 10000 | 20000 | -| `JsonMachine::fromString()` | 10000 | 1 | 10001 | +| `Items::fromString()` | 10000 | 1 | 10001 | -The reality is even better. `JsonMachine::fromString` consumes about **5x less memory** than `json_decode`. The reason is +The reality is even better. `Items::fromString` consumes about **5x less memory** than `json_decode`. The reason is that a PHP structure takes much more memory than its corresponding JSON representation. @@ -410,17 +410,17 @@ but you forgot to specify a json pointer. See [Parsing a subtree](#parsing-a-sub The other reason may be, that one of the items you iterate is itself so huge it cannot be decoded at once. For example, you iterate over users and one of them has thousands of "friend" objects in it. Use `PassThruDecoder` which does not decode an item, get the json string of the user -and parse it iteratively yourself using `JsonMachine::fromString()`. +and parse it iteratively yourself using `Items::fromString()`. ```php request('GET', 'https://httpbin.org/anything?key=value'); // Gets PHP stream resource from Guzzle stream $phpStream = \GuzzleHttp\Psr7\StreamWrapper::getResource($response->getBody()); -foreach (\JsonMachine\JsonMachine::fromStream($phpStream) as $key => $value) { +foreach (\JsonMachine\Items::fromStream($phpStream) as $key => $value) { var_dump([$key, $value]); } diff --git a/src/examples/memLeak.php b/src/examples/memLeak.php index cc7a968..5f8f412 100644 --- a/src/examples/memLeak.php +++ b/src/examples/memLeak.php @@ -1,6 +1,6 @@ $item) { $report = memory_get_peak_usage() diff --git a/src/examples/symfonyHttpClient.php b/src/examples/symfonyHttpClient.php index 95da306..129a2e7 100644 --- a/src/examples/symfonyHttpClient.php +++ b/src/examples/symfonyHttpClient.php @@ -1,6 +1,6 @@ request('GET', 'https://httpbin.org/anything?key=value'); $jsonChunks = httpClientChunks($client->stream($response)); -foreach (JsonMachine::fromIterable($jsonChunks, "/args") as $key => $value) { +foreach (Items::fromIterable($jsonChunks, "/args") as $key => $value) { var_dump($key, $value); } diff --git a/test/JsonMachineTest/JsonMachineTest.php b/test/JsonMachineTest/ItemsTest.php similarity index 89% rename from test/JsonMachineTest/JsonMachineTest.php rename to test/JsonMachineTest/ItemsTest.php index d42483b..17e2e89 100644 --- a/test/JsonMachineTest/JsonMachineTest.php +++ b/test/JsonMachineTest/ItemsTest.php @@ -3,17 +3,17 @@ namespace JsonMachineTest; use JsonMachine\JsonDecoder\PassThruDecoder; -use JsonMachine\JsonMachine; +use JsonMachine\Items; use phpDocumentor\Reflection\DocBlock\Tags\Formatter\PassthroughFormatter; -class JsonMachineTest extends \PHPUnit_Framework_TestCase +class ItemsTest extends \PHPUnit_Framework_TestCase { /** * @dataProvider dataFactories */ public function testFactories($expected, $methodName, ...$args) { - $iterator = call_user_func_array(JsonMachine::class."::$methodName", $args); + $iterator = call_user_func_array(Items::class."::$methodName", $args); $this->assertSame($expected, iterator_to_array($iterator)); } @@ -45,7 +45,7 @@ class JsonMachineTest extends \PHPUnit_Framework_TestCase public function testGetPositionDebugEnabled() { $expectedPosition = ['key1' => 10, 'key2' => 20]; - $items = JsonMachine::fromString('{"key1":1, "key2":2} ', '', null, true); + $items = Items::fromString('{"key1":1, "key2":2} ', '', null, true); foreach ($items as $key => $val) { $this->assertSame($expectedPosition[$key], $items->getPosition()); } @@ -54,7 +54,7 @@ class JsonMachineTest extends \PHPUnit_Framework_TestCase public function testIterationWithoutForeach() { $iterator = - JsonMachine::fromString('{"key1":1, "key2":2}') + Items::fromString('{"key1":1, "key2":2}') ->getIterator(); $iterator->rewind(); diff --git a/test/JsonMachineTest/JsonDecoder/ErrorWrappingDecoderTest.php b/test/JsonMachineTest/JsonDecoder/ErrorWrappingDecoderTest.php index 725dc12..bbdd60b 100644 --- a/test/JsonMachineTest/JsonDecoder/ErrorWrappingDecoderTest.php +++ b/test/JsonMachineTest/JsonDecoder/ErrorWrappingDecoderTest.php @@ -6,7 +6,7 @@ use JsonMachine\JsonDecoder\DecodingError; use JsonMachine\JsonDecoder\DecodingResult; use JsonMachine\JsonDecoder\ErrorWrappingDecoder; use JsonMachine\JsonDecoder\ExtJsonDecoder; -use JsonMachine\JsonMachine; +use JsonMachine\Items; use PHPUnit_Framework_TestCase; class ErrorWrappingDecoderTest extends PHPUnit_Framework_TestCase @@ -84,7 +84,7 @@ class ErrorWrappingDecoderTest extends PHPUnit_Framework_TestCase } '; - $items = JsonMachine::fromString($json, '/results', new ErrorWrappingDecoder(new ExtJsonDecoder(true))); + $items = Items::fromString($json, '/results', new ErrorWrappingDecoder(new ExtJsonDecoder(true))); $result = iterator_to_array($items); $this->assertSame('correct', $result[0]['correct']); diff --git a/test/JsonMachineTest/ParserTest.php b/test/JsonMachineTest/ParserTest.php index 041491d..8f4e19a 100644 --- a/test/JsonMachineTest/ParserTest.php +++ b/test/JsonMachineTest/ParserTest.php @@ -7,7 +7,7 @@ use JsonMachine\Exception\PathNotFoundException; use JsonMachine\Exception\SyntaxError; use JsonMachine\Exception\UnexpectedEndSyntaxErrorException; use JsonMachine\JsonDecoder\ExtJsonDecoder; -use JsonMachine\JsonMachine; +use JsonMachine\Items; use JsonMachine\Lexer; use JsonMachine\Parser; use JsonMachine\StringChunks; diff --git a/test/performance/testPerformance.php b/test/performance/testPerformance.php index d714a33..7b7164d 100644 --- a/test/performance/testPerformance.php +++ b/test/performance/testPerformance.php @@ -1,6 +1,6 @@ function ($file) { - return JsonMachine::fromFile($file); + 'Items::fromFile()' => function ($file) { + return Items::fromFile($file); }, - 'JsonMachine::fromString()' => function ($file) { - return JsonMachine::fromString(stream_get_contents(fopen($file, 'r'))); + 'Items::fromString()' => function ($file) { + return Items::fromString(stream_get_contents(fopen($file, 'r'))); }, - 'JsonMachine::fromFile() - debug' => function ($file) { - return JsonMachine::fromFile($file, '', null, true); + 'Items::fromFile() - debug' => function ($file) { + return Items::fromFile($file, '', null, true); }, - 'JsonMachine::fromString() - debug' => function ($file) { - return JsonMachine::fromString(stream_get_contents(fopen($file, 'r')), '', null, true); + 'Items::fromString() - debug' => function ($file) { + return Items::fromString(stream_get_contents(fopen($file, 'r')), '', null, true); }, 'json_decode()' => function ($file) { return json_decode(stream_get_contents(fopen($file, 'r')), true);