Skip to content

Commit ed49a18

Browse files
committed
Continue documentation improvements
1 parent f8ca6a7 commit ed49a18

13 files changed

+373
-25
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
build/
12
coverage/
23
vendor/
34
composer.lock

README.md

Lines changed: 228 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,50 @@
1-
# Streaming Json Encoder #
1+
# Streaming JSON Encoder #
22

3+
*Streaming JSON Encoder* is a PHP library that provides a set of classes to help
4+
with encoding JSON in a streaming manner, i.e. allowing you to encode the JSON
5+
document bit by bit rather than encoding the whole document at once. Compared to
6+
the built in `json_encode` function, there are two main advantages:
37

8+
* You will not need to load the entire data set into memory, as the encoder
9+
supports iterating over both arrays and any kind of iterators, like
10+
generators, for example.
11+
* You will not need to load the entire resulting JSON document into the
12+
memory, since the JSON document will be encoded value by value and it's
13+
possible to output the encoded document piece by piece.
14+
15+
In other words, the Streaming JSON encoder can provide the greatest benefit
16+
when you need handle large data sets that may otherwise take up too much memory
17+
to process.
18+
19+
In order to increase interoperability, the library also provides a PSR-7
20+
compatible stream to use with frameworks and HTTP requests.
421

522
## Requirements ##
623

724
In order to use this library, the following requirements must be met:
825

9-
* PHP version 5.6
26+
* PHP version 5.6, 7.0 or later
27+
* The PHP library `psr/http-message` must be available
1028

1129
## Installation ##
1230

13-
This library can be installed by using [Composer](http://getcomposer.org/). In
14-
order to do this, you must download the latest Composer version and run the
15-
`require` command to add this library as a dependency to your project. The
16-
easiest way to complete these two tasks is to run the following two commands
17-
in your terminal:
18-
19-
```
20-
php -r "readfile('https://getcomposer.org/installer');" | php
21-
php composer.phar require "violet/streaming-json-encoder:^1.0"
22-
```
31+
The easiest way to install this library is to use Composer to handle your
32+
dependencies. In order to install this library via Composer, simply follow
33+
the following two steps:
2334

24-
If you already have Composer installed on your system and you know how to use
25-
it, you can also install this library by adding it as a dependency to your
26-
`composer.json` file and running the `composer install` command. Here is an
27-
example of what your `composer.json` file could look like:
35+
1. Acquire the `composer.phar` by running in your project root the
36+
Command-line installation script found at the [Composer
37+
Download](https://getcomposer.org/download/) page.
38+
2. Once you've run the installation script, you should have a `composer.phar`
39+
file in you project root and you can run the following command:
40+
41+
```
42+
php composer.phar require "violet/streaming-json-encoder:^1.0"
43+
```
44+
45+
If you are already familiar with how to use Composer, you may also alternatively
46+
simply add the library as a dependency by adding the following `composer.json`
47+
file to your project and running the `composer install` command:
2848
2949
```json
3050
{
@@ -40,15 +60,202 @@ the installation.
4060

4161
### Manual installation ###
4262

43-
You can also install this library manually without using Composer. In order to
44-
do this, you must download the [latest release](https://github.com/violet/streaming-json-encoder/releases/latest)
45-
and extract the `src` folder from the archive to your project folder. To load
46-
the library, you can simply include the `src/autoload.php` file that was
47-
provided in the archive.
63+
If you do not wish to use Composer to load the library, you may also download
64+
the library manually by downloading the [latest release](https://github.com/violet/streaming-json-encoder/releases/latest)
65+
and extracting the `src` folder to your project. You may then include the
66+
provided `src/autoload.php` file to load the library classes.
67+
68+
Please note that using Composer will also automatically download the other
69+
required PHP libraries. If you install this library manually, you will need to
70+
also make those other required libraries available.
4871

4972
## Usage ##
5073

74+
This library offers 3 main different ways to use the library via the classes
75+
`BufferJsonEncoder`, `StreamJsonEncoder` and the PSR-7 compatible stream
76+
`JsonStream`.
77+
78+
### Using BufferJsonEncoder ###
79+
80+
The buffer encoder is most useful when you need to generate the JSON document
81+
in a way that does not involve passing callbacks to handle the generated JSON.
82+
83+
The easiest way to use the `BufferJsonEncoder` is to instantiate it with the
84+
JSON value to encode and call the `encode()` method to return the entire output
85+
as a string:
86+
87+
```php
88+
<?php
89+
90+
require 'vendor/autoload.php';
91+
92+
$encoder = new \Violet\StreamingJsonEncoder\BufferJsonEncoder(['array_value']);
93+
echo $encoder->encode();
94+
```
95+
96+
The most useful way to use this encoder, however, is to use it as an iterator.
97+
As the encoder implements the `Iterator` interface, you can simply loop over the
98+
generated JSON with a foreach loop:
99+
100+
```php
101+
<?php
102+
103+
require 'vendor/autoload.php';
104+
105+
$encoder = new \Violet\StreamingJsonEncoder\BufferJsonEncoder(range(0, 10));
106+
107+
foreach ($encoder as $string) {
108+
echo $string;
109+
}
110+
```
111+
112+
It's also worth noting that the encoder also supports iterators for values.
113+
What's more, any closure passed to the encoder will also be called and the
114+
return value used as the value instead. The previous example could also be
115+
written as:
116+
117+
```php
118+
<?php
119+
120+
require 'vendor/autoload.php';
121+
122+
$encoder = new \Violet\StreamingJsonEncoder\BufferJsonEncoder(function () {
123+
for ($i = 0; $i <= 10; $i++) {
124+
yield $i;
125+
}
126+
});
127+
128+
foreach ($encoder as $string) {
129+
echo $string;
130+
}
131+
```
132+
133+
As a side note, the encoder will respect the `JsonSerializable` interface as
134+
well and will call the `jsonSerialize` for objects that implement the interface.
135+
136+
### Using StreamJsonEncoder ###
137+
138+
The stream encoder works very similarly to the `BufferJsonEncoder` as they
139+
extend the same abstract class. However, the key difference is in how they
140+
handle passing the JSON output.
141+
142+
The `StreamJsonEncoder` accepts a callable as the second constructor argument.
143+
Whenever JSON needs to be outputted, this callable is called with two arguments,
144+
the actual string to output and the type of the token to output (which is one
145+
of the `JsonToken` constants).
146+
147+
If no callable is passed, the StreamJsonEncoder will simply output the JSON
148+
using an echo statement. For example:
149+
150+
```php
151+
<?php
152+
153+
require 'vendor/autoload.php';
154+
155+
$encoder = new \Violet\StreamingJsonEncoder\StreamJsonEncoder(['array_value']);
156+
$encoder->encode();
157+
```
158+
159+
The `encode()` method in `StreamJsonEncoder` returns the total number of bytes
160+
it passed to the output. This encoder makes it convenient, for example, to
161+
write the JSON to file in a streaming manner. For example:
162+
163+
```php
164+
<?php
165+
166+
require 'vendor/autoload.php';
167+
168+
$fp = fopen('test.json', 'w');
169+
$encoder = new \Violet\StreamingJsonEncoder\StreamJsonEncoder(
170+
range(1, 100),
171+
function ($json) use ($fp) {
172+
fwrite($fp, $json);
173+
}
174+
);
175+
176+
$encoder->encode();
177+
fclose($fp);
178+
```
179+
180+
### Using JsonStream ###
181+
182+
The stream class provides a PSR-7 compatible `StreamInterface` for streaming
183+
JSON content. It actually uses the `BufferJsonEncoder` to do the hard work and
184+
simply wraps the calls in a stream like fashion.
185+
186+
The constructor of `JsonStream` either accepts a value to encode as JSON or an
187+
instance of `BufferJsonEncoder` (which allows you to set the encoding options).
188+
You can then operate on the stream using the methods provided by the PSR-7
189+
interface. For example:
190+
191+
```php
192+
<?php
193+
194+
require 'vendor/autoload.php';
195+
196+
$iterator = function () {
197+
foreach (new DirectoryIterator(__DIR__) as $file) {
198+
yield $file->getFilename();
199+
}
200+
};
201+
202+
$encoder = (new \Violet\StreamingJsonEncoder\BufferJsonEncoder($iterator))
203+
->setOptions(JSON_PRETTY_PRINT);
204+
205+
$stream = new \Violet\StreamingJsonEncoder\JsonStream($encoder);
206+
207+
while (!$stream->eof()) {
208+
echo $stream->read(1024 * 8);
209+
}
210+
```
211+
212+
For more information about PSR-7 streams, please refer to the [PSR-7
213+
documentation](http://www.php-fig.org/psr/psr-7/#psrhttpmessagestreaminterface).
214+
215+
### How the encoder resolves values ###
216+
217+
Since Streaming JSON Encoder only handles values one at a time and tries to
218+
avoid loading entire data sets into memory at once, there are several
219+
fundamental differences to how this library handles values compared to
220+
`json_encode`.
221+
222+
To determine whether any given value should be encoded as an object or as an
223+
array, the encoder makes the following decisions:
224+
225+
* Only arrays that have keys from 0 to n-1 in that order are encoded as JSON
226+
arrays. All other arrays are encoded as objects.
227+
* Any object is encoded as a JSON array if the key of the first value
228+
returned by iterating over the objects equals to `0`. All other objects are
229+
encoded as JSON objects.
230+
231+
Additionally, prior to the decision whether to encode an object as an array or
232+
an object is made, the encoder will attempt to resolve the value as follows:
233+
234+
* As long as the processed value is a `JsonSerializable`, it will replace the
235+
processed value with the return value of the `jsonSerialize()` method.
236+
* As long as the processed value is a `Closure`, it will be replaced with the
237+
value returned by invoking the closure in question.
238+
239+
Note that it's possible to override the array or object decision by using the
240+
`JSON_FORCE_OBJECT` option.
241+
242+
### JSON encoding options ###
243+
244+
Both `BufferJsonEncoder` and `StreamJsonEncoder` have a method `setOptions()` to
245+
change the JSON encoding options. The accepted options are the same as those
246+
accepted by `json_encode()` function. The encoder still internally uses the
247+
`json_encode()` method to encode other values than arrays or object. A few
248+
options also have additional effects on the encoders:
51249

250+
* Using `JSON_FORCE_OBJECT` will force all arrays and values to be encoded
251+
as JSON objects similar to `json_encode()`.
252+
* Using `JSON_PRETTY_PRINT` causes the encoder to output whitespace to make
253+
a more readable output. The indentation used can be changed using the
254+
method `setIndent()` which accepts either a string argument to use as the
255+
indent or an integer to indicate the number of spaces.
256+
* Using `JSON_PARTIAL_OUTPUT_ON_ERROR` will cause the encoder to continue the
257+
output despite encoding errors. Otherwise the encoding will halt and the
258+
encoder will throw an `EncodingException`.
52259

53260
## Credits ##
54261

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
"require-dev": {
2323
"phpunit/phpunit": "^5.7 || ^6.0",
2424
"squizlabs/php_codesniffer": "^2.8",
25-
"friendsofphp/php-cs-fixer": "^2.1"
25+
"friendsofphp/php-cs-fixer": "^2.1",
26+
"sami/sami": "^4.0"
2627
},
2728
"autoload": {
2829
"psr-4": {

examples/buffer_foreach.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
require '../vendor/autoload.php';
4+
5+
$encoder = new \Violet\StreamingJsonEncoder\BufferJsonEncoder(range(0, 10));
6+
7+
foreach ($encoder as $string) {
8+
echo $string;
9+
}

examples/buffer_generator.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
require '../vendor/autoload.php';
4+
5+
$encoder = new \Violet\StreamingJsonEncoder\BufferJsonEncoder(function () {
6+
for ($i = 0; $i <= 10; $i++) {
7+
yield $i;
8+
}
9+
});
10+
11+
foreach ($encoder as $string) {
12+
echo $string;
13+
}

examples/buffer_simple.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
3+
require '../vendor/autoload.php';
4+
5+
$encoder = new \Violet\StreamingJsonEncoder\BufferJsonEncoder(['array_value']);
6+
echo $encoder->encode();

examples/json_stream.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
require '../vendor/autoload.php';
4+
5+
$iterator = function () {
6+
foreach (new DirectoryIterator(__DIR__) as $file) {
7+
yield $file->getFilename();
8+
}
9+
};
10+
11+
$encoder = (new \Violet\StreamingJsonEncoder\BufferJsonEncoder($iterator))
12+
->setOptions(JSON_PRETTY_PRINT);
13+
14+
$stream = new \Violet\StreamingJsonEncoder\JsonStream($encoder);
15+
16+
while (!$stream->eof()) {
17+
echo $stream->read(1024 * 8);
18+
}

examples/stream_file.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
require '../vendor/autoload.php';
4+
5+
$fp = fopen('test.json', 'w');
6+
$encoder = new \Violet\StreamingJsonEncoder\StreamJsonEncoder(
7+
range(1, 100),
8+
function ($json) use ($fp) {
9+
fwrite($fp, $json);
10+
}
11+
);
12+
13+
$encoder->encode();
14+
fclose($fp);

examples/stream_simple.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
3+
require '../vendor/autoload.php';
4+
5+
$encoder = new \Violet\StreamingJsonEncoder\StreamJsonEncoder(['array_value']);
6+
$encoder->encode();

sami_config.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
use Sami\RemoteRepository\GitHubRemoteRepository;
4+
use Sami\Sami;
5+
use Symfony\Component\Finder\Finder;
6+
7+
$iterator = Finder::create()
8+
->files()
9+
->name('*.php')
10+
->in(__DIR__ . '/src');
11+
12+
return new Sami($iterator, [
13+
'title' => 'Streaming JSON Encoder',
14+
'build_dir' => __DIR__ . '/build/doc',
15+
'cache_dir' => __DIR__ . '/build/cache',
16+
'remote_repository' => new GitHubRemoteRepository('violet-php/streaming-json-encoder', __DIR__),
17+
'default_opened_level' => 2,
18+
]);

0 commit comments

Comments
 (0)