Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 108 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ GZIP format ([RFC 1950](https://tools.ietf.org/html/rfc1950),

* [Quickstart example](#quickstart-example)
* [Formats](#formats)
* [GZIP format](#gzip-format)
* [Raw DEFLATE format](#raw-deflate-format)
* [ZLIB format](#zlib-format)
* [GZIP format](#gzip-format)
* [Raw DEFLATE format](#raw-deflate-format)
* [ZLIB format](#zlib-format)
* [Usage](#usage)
* [ZlibFilterStream](#zlibfilterstream)
* [createCompressor()](#createcompressor)
* [createDecompressor()](#createdecompressor)
* [Compressor](#compressor)
* [Decompressor](#decompressor)
* [ZlibFilterStream](#zlibfilterstream)
* [createCompressor()](#createcompressor)
* [createDecompressor()](#createdecompressor)
* [Inconsistencies](#inconsistencies)
* [Install](#install)
* [Tests](#tests)
Expand All @@ -29,20 +31,19 @@ GZIP format ([RFC 1950](https://tools.ietf.org/html/rfc1950),

Once [installed](#install), you can use the following code to pipe a readable
gzip file stream into an decompressor which emits decompressed data events for
each individual file chunk:
each individual log file chunk:

```php
$loop = React\EventLoop\Factory::create();
$stream = new React\Stream\ReadableResourceStream(fopen('access.log.gz', 'r'), $loop);

$decompressor = ZlibFilterStream::createGzipDecompressor();
$decompressor = new Clue\React\Zlib\Decompressor(ZLIB_ENCODING_GZIP);
$stream->pipe($decompressor);

$decompressor->on('data', function ($data) {
echo $data;
echo $data; // chunk of decompressed log data
});

$stream->pipe($decompressor);

$loop->run();
```

Expand All @@ -58,6 +59,7 @@ The zlib library offers a number of different formats (sometimes referred to as
This library supports the GZIP compression format as defined in [RFC 1952](https://tools.ietf.org/html/rfc1952).
This is one of the more common compression formats and is used in several places:

* PHP: `ZLIB_ENCODING_GZIP` (PHP 5.4+ only)
* PHP: `gzdecode()` (PHP 5.4+ only) and `gzencode()`
* Files with `.gz` file extension, e.g. `.tar.gz` or `.tgz` archives (also known as "tarballs")
* `gzip` and `gunzip` (and family) command line tools
Expand All @@ -76,18 +78,20 @@ This library supports the raw DEFLATE compression format as defined in [RFC 1951
The DEFLATE compression algorithm returns what we refer to as "raw DEFLATE format".
This raw DEFLATE format is commonly wrapped in container formats instead of being used directly:

* PHP: `ZLIB_ENCODING_RAW` (PHP 5.4+ only)
* PHP: `gzdeflate()` and `gzinflate()`
* Wrapped in [GZIP format](#gzip-format)
* Wrapped in [ZLIB format](#zlib-format)

> Note: This format is not the confused with what some people call "deflate format" or "deflate encoding".
> Note: This format is not to be confused with what some people call "deflate format" or "deflate encoding".
These names are commonly used to refer to what we call [ZLIB format](#zlib-format).

### ZLIB format

This library supports the ZLIB compression format as defined in [RFC 1950](https://tools.ietf.org/html/rfc1950).
This format is commonly used in a streaming context:

* PHP: `ZLIB_ENCODING_DEFLATE` (PHP 5.4+ only)
* PHP: `gzcompress()` and `gzuncompress()`
* [HTTP compression](https://en.wikipedia.org/wiki/HTTP_compression) with `Content-Encoding: deflate` header
* Java: `DeflaterOutputStream`
Expand All @@ -108,32 +112,120 @@ This documentation avoids this name in order to avoid confusion with the [raw DE

All classes use the `Clue\React\Zlib` namespace.

### Compressor

The `Compressor` class can be used to compress a stream of data.

It implements the [`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface)
and accepts uncompressed data on its writable side and emits compressed data
on its readable side.

```php
$encoding = ZLIB_ENCODING_GZIP; // or ZLIB_ENCODING_RAW or ZLIB_ENCODING_DEFLATE
$compressor = new Clue\React\Zlib\Compressor($encoding);

$compressor->on('data', function ($data) {
echo $data; // compressed binary data chunk
});

$compressor->write($uncompressed); // write uncompressed data chunk
```

This is particularly useful in a piping context:

```php
$input->pipe($filterBadWords)->pipe($compressor)->pipe($output);
```

For more details, see ReactPHP's
[`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface).

> Internally, it implements the deprecated `ZlibFilterStream` class only for
BC reasons. For best forwards compatibility, you should only rely on it
implementing the `DuplexStreamInterface`.

### Decompressor

The `Decompressor` class can be used to decompress a stream of data.

It implements the [`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface)
and accepts compressed data on its writable side and emits decompressed data
on its readable side.

```php
$encoding = ZLIB_ENCODING_GZIP; // or ZLIB_ENCODING_RAW or ZLIB_ENCODING_DEFLATE
$decompressor = new Clue\React\Zlib\Decompressor($encoding);

$decompressor->on('data', function ($data) {
echo $data; // decompressed data chunk
});

$decompressor->write($compressed); // write compressed binary data chunk
```

This is particularly useful in a piping context:

```php
$input->pipe($decompressor)->pipe($filterBadWords)->pipe($output);
```

For more details, see ReactPHP's
[`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface).

> Internally, it implements the deprecated `ZlibFilterStream` class only for
BC reasons. For best forwards compatibility, you should only rely on it
implementing the `DuplexStreamInterface`.

### ZlibFilterStream

The `ZlibFilterStream` is a small wrapper around the underlying `zlib.deflate` and `zlib.inflate`
The deprecated `ZlibFilterStream` is a small wrapper around the underlying `zlib.deflate` and `zlib.inflate`
stream compression filters offered via `ext-zlib`.

#### createCompressor()

The following methods can be used to create a compressor instance:
The following deprecated methods can be used to
create a `Compressor` instance with the respective encoding parameter:

```php
// deprecated
$compressor = ZlibFilterStream::createGzipCompressor();
$compressor = ZlibFilterStream::createDeflateCompressor();
$compressor = ZlibFilterStream::createZlibCompressor();
```

Using any of these methods is deprecated.
Instead, you should explicitly create a `Compressor` like this:

```php
$encoding = ZLIB_ENCODING_GZIP; // or ZLIB_ENCODING_RAW or ZLIB_ENCODING_DEFLATE
$compressor = new Clue\React\Zlib\Compressor($encoding);
```

See also [`Compressor`](#compressor) for more details.

#### createDecompressor()

The following methods can be used to create a decompressor instance:
The following deprecated methods can be used to
create a `Decompressor` instanceof with the respective encoding parameter:

```php
// deprecated
$decompressor = ZlibFilterStream::createGzipDecompressor();
$decompressor = ZlibFilterStream::createDeflateDecompressor();
$decompressor = ZlibFilterStream::createZlibDecompressor();
```

#### Inconsistencies
Using any of these methods is deprecated.
Instead, you should explicitly create a `Decompressor` like this:

```php
$encoding = ZLIB_ENCODING_GZIP; // or ZLIB_ENCODING_RAW or ZLIB_ENCODING_DEFLATE
$decompressor = new Clue\React\Zlib\Decompressor($encoding);
```

See also [`Compressor`](#compressor) for more details.

### Inconsistencies

The stream compression filters are not exactly the most commonly used features of PHP.
As such, we've spotted several inconsistencies (or *bugs*) between different PHP versions and HHVM.
Expand Down
7 changes: 6 additions & 1 deletion examples/gunzip.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@

require __DIR__ . '/../vendor/autoload.php';

if (!defined('ZLIB_ENCODING_GZIP')) {
fwrite(STDERR, 'Requires PHP 5.4+ with ext-zlib enabled' . PHP_EOL);
exit(1);
}

$loop = React\EventLoop\Factory::create();

$in = new React\Stream\ReadableResourceStream(STDIN, $loop);
$out = new React\Stream\WritableResourceStream(STDOUT, $loop);

$decompressor = Clue\React\Zlib\ZlibFilterStream::createGzipDecompressor();
$decompressor = new Clue\React\Zlib\Decompressor(ZLIB_ENCODING_GZIP);
$in->pipe($decompressor)->pipe($out);

$decompressor->on('error', function ($e) {
Expand Down
7 changes: 6 additions & 1 deletion examples/gzip.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@

require __DIR__ . '/../vendor/autoload.php';

if (!defined('ZLIB_ENCODING_GZIP')) {
fwrite(STDERR, 'Requires PHP 5.4+ with ext-zlib enabled' . PHP_EOL);
exit(1);
}

$loop = React\EventLoop\Factory::create();

$in = new React\Stream\ReadableResourceStream(STDIN, $loop);
$out = new React\Stream\WritableResourceStream(STDOUT, $loop);

$compressor = Clue\React\Zlib\ZlibFilterStream::createGzipCompressor(1);
$compressor = new Clue\React\Zlib\Compressor(ZLIB_ENCODING_GZIP);
$in->pipe($compressor)->pipe($out);

$loop->run();
50 changes: 50 additions & 0 deletions src/Compressor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

namespace Clue\React\Zlib;

use Clue\StreamFilter as Filter;

/**
* The `Compressor` class can be used to compress a stream of data.
*
* It implements the [`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface)
* and accepts uncompressed data on its writable side and emits compressed data
* on its readable side.
*
* ```php
* $encoding = ZLIB_ENCODING_GZIP; // or ZLIB_ENCODING_RAW or ZLIB_ENCODING_DEFLATE
* $compressor = new Clue\React\Zlib\Compressor($encoding);
*
* $compressor->on('data', function ($data) {
* echo $data; // compressed binary data chunk
* });
*
* $compressor->write($uncompressed); // write uncompressed data chunk
* ```
*
* This is particularly useful in a piping context:
*
* ```php
* $input->pipe($filterBadWords)->pipe($compressor)->pipe($output);
* ```
*
* For more details, see ReactPHP's
* [`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface).
*
* > Internally, it implements the deprecated `ZlibFilterStream` class only for
* BC reasons. For best forwards compatibility, you should only rely on it
* implementing the `DuplexStreamInterface`.
*/
final class Compressor extends ZlibFilterStream
{
/**
* @param int $encoding ZLIB_ENCODING_GZIP, ZLIB_ENCODING_RAW or ZLIB_ENCODING_DEFLATE
* @param int $level optional compression level
*/
public function __construct($encoding, $level = -1)
{
parent::__construct(
Filter\fun('zlib.deflate', array('window' => $encoding, 'level' => $level))
);
}
}
49 changes: 49 additions & 0 deletions src/Decompressor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

namespace Clue\React\Zlib;

use Clue\StreamFilter as Filter;

/**
* The `Decompressor` class can be used to decompress a stream of data.
*
* It implements the [`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface)
* and accepts compressed data on its writable side and emits decompressed data
* on its readable side.
*
* ```php
* $encoding = ZLIB_ENCODING_GZIP; // or ZLIB_ENCODING_RAW or ZLIB_ENCODING_DEFLATE
* $decompressor = new Clue\React\Zlib\Decompressor($encoding);
*
* $decompressor->on('data', function ($data) {
* echo $data; // decompressed data chunk
* });
*
* $decompressor->write($compressed); // write compressed binary data chunk
* ```
*
* This is particularly useful in a piping context:
*
* ```php
* $input->pipe($decompressor)->pipe($filterBadWords)->pipe($output);
* ```
*
* For more details, see ReactPHP's
* [`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface).
*
* > Internally, it implements the deprecated `ZlibFilterStream` class only for
* BC reasons. For best forwards compatibility, you should only rely on it
* implementing the `DuplexStreamInterface`.
*/
final class Decompressor extends ZlibFilterStream
{
/**
* @param int $encoding ZLIB_ENCODING_GZIP, ZLIB_ENCODING_RAW or ZLIB_ENCODING_DEFLATE
*/
public function __construct($encoding)
{
parent::__construct(
Filter\fun('zlib.inflate', array('window' => $encoding))
);
}
}
2 changes: 1 addition & 1 deletion src/TransformStream.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use Exception;

/**
* @internal Should not be relied upon outside of this package. Should eventually be moved to react/stream?
* @internal Should not be relied upon outside of this package.
*/
class TransformStream extends EventEmitter implements DuplexStreamInterface
{
Expand Down
Loading