Skip to content

Commit 9ac7a9c

Browse files
committed
Initial commit
0 parents  commit 9ac7a9c

21 files changed

+955
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/vendor
2+
/composer.lock

.travis.yml

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
language: php
2+
php:
3+
- 5.3
4+
- 5.6
5+
- 7
6+
- hhvm
7+
install:
8+
- composer install --prefer-source --no-interaction
9+
script:
10+
- phpunit --coverage-text --verbose

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2015 Christian Lück
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is furnished
10+
to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

README.md

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# clue/zlib-react [![Build Status](https://travis-ci.org/clue/php-zlib-react.svg?branch=master)](https://travis-ci.org/clue/php-zlib-react)
2+
3+
Streaming zlib compressor and decompressor, built on top of [React PHP](http://reactphp.org/),
4+
supporting compression and decompression of the following formats:
5+
6+
* [RFC 1952](https://tools.ietf.org/html/rfc1952) (GZIP compressed format)
7+
* [RFC 1951](https://tools.ietf.org/html/rfc1951) (raw DEFLATE compressed format)
8+
* [RFC 1950](https://tools.ietf.org/html/rfc1950) (ZLIB compressed format)
9+
10+
> Note: This project is in early alpha stage! Feel free to report any issues you encounter.
11+
12+
## Quickstart example
13+
14+
Once [installed](#install), you can use the following code to pipe a readable
15+
gzip file stream into an decompressor which emits decompressed data events for
16+
each individual file chunk:
17+
18+
```php
19+
$loop = React\EventLoop\Factory::create();
20+
$stream = new Stream(fopen('access.log.gz', 'r'), $loop);
21+
22+
$decompressor = ZlibFilterStream::createGzipDecompressor();
23+
24+
$decompressor->on('data', function ($data) {
25+
echo $data;
26+
});
27+
28+
$stream->pipe($decompressor);
29+
30+
$loop->run();
31+
```
32+
33+
See also the [examples](examples).
34+
35+
## Install
36+
37+
The recommended way to install this library is [through composer](https://getcomposer.org).
38+
[New to composer?](https://getcomposer.org/doc/00-intro.md)
39+
40+
```bash
41+
$ composer require clue/zlib-react:dev-master
42+
```
43+
44+
## License
45+
46+
MIT

composer.json

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "clue/zlib-react",
3+
"description": "Streaming zlib compressor and decompressor, supporting GZIP format (RFC 1952), raw DEFLATE format (RFC 1951) and ZLIB format (RFC 1950)",
4+
"keywords": ["zlib", "gzip", "gz", "deflate", "inflate", "compression", "decompression", "RFC 1950", "RFC 1951", "RFC 1952", "reactphp"],
5+
"homepage": "https://github.com/clue/php-zlib-react",
6+
"license": "MIT",
7+
"authors": [
8+
{
9+
"name": "Christian Lück",
10+
"email": "[email protected]"
11+
}
12+
],
13+
"require": {
14+
"php": ">=5.3",
15+
"react/stream": "~0.4.3",
16+
"clue/stream-filter": "~1.3"
17+
},
18+
"require-dev": {
19+
"react/event-loop": "~0.4.0|~0.3.0"
20+
},
21+
"suggest": {
22+
"ext-zlib": "Requires ext-zlib extension"
23+
},
24+
"autoload": {
25+
"psr-4": { "Clue\\React\\Zlib\\": "src/" }
26+
}
27+
}

examples/gunzip.php

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
require __DIR__ . '/../vendor/autoload.php';
4+
5+
$loop = React\EventLoop\Factory::create();
6+
7+
$in = new React\Stream\Stream(STDIN, $loop);
8+
$out = new React\Stream\Stream(STDOUT, $loop);
9+
10+
$decompressor = Clue\React\Zlib\ZlibFilterStream::createGzipDecompressor();
11+
$in->pipe($decompressor)->pipe($out);
12+
13+
$decompressor->on('error', function ($e) {
14+
fwrite(STDERR, 'Error: ' . $e->getMessage() . PHP_EOL);
15+
});
16+
17+
$loop->run();

examples/gzip.php

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
require __DIR__ . '/../vendor/autoload.php';
4+
5+
$loop = React\EventLoop\Factory::create();
6+
7+
$in = new React\Stream\Stream(STDIN, $loop);
8+
$out = new React\Stream\Stream(STDOUT, $loop);
9+
10+
$compressor = Clue\React\Zlib\ZlibFilterStream::createGzipCompressor(1);
11+
$in->pipe($compressor)->pipe($out);
12+
13+
$loop->run();

phpunit.xml.dist

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<phpunit bootstrap="tests/bootstrap.php"
4+
colors="true"
5+
convertErrorsToExceptions="true"
6+
convertNoticesToExceptions="true"
7+
convertWarningsToExceptions="true"
8+
>
9+
<testsuites>
10+
<testsuite name="zlib React Test Suite">
11+
<directory>./tests/</directory>
12+
</testsuite>
13+
</testsuites>
14+
<filter>
15+
<whitelist>
16+
<directory>./src/</directory>
17+
</whitelist>
18+
</filter>
19+
</phpunit>

src/TransformStream.php

+195
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
<?php
2+
3+
namespace Clue\React\Zlib;
4+
5+
use React\Stream\DuplexStreamInterface;
6+
use Evenement\EventEmitter;
7+
use React\Stream\WritableStreamInterface;
8+
use React\Stream\Util;
9+
use Exception;
10+
11+
/**
12+
* @internal Should not be relied upon outside of this package. Should eventually be moved to react/stream?
13+
*/
14+
class TransformStream extends EventEmitter implements DuplexStreamInterface
15+
{
16+
private $readable = true;
17+
private $writable = true;
18+
private $closed = false;
19+
20+
public function write($data)
21+
{
22+
if (!$this->writable || $data === '') {
23+
return;
24+
}
25+
26+
try {
27+
$this->transformData($data);
28+
} catch (Exception $e) {
29+
$this->forwardError($e);
30+
}
31+
}
32+
33+
public function end($data = null)
34+
{
35+
if (!$this->writable) {
36+
return;
37+
}
38+
$this->writable = false;
39+
40+
try {
41+
if ($data === null) {
42+
$data = '';
43+
}
44+
$this->transformEnd($data);
45+
} catch (Exception $e) {
46+
$this->forwardError($e);
47+
}
48+
}
49+
50+
public function close()
51+
{
52+
if ($this->closed) {
53+
return;
54+
}
55+
56+
$this->closed = true;
57+
$this->readable = false;
58+
$this->writable = false;
59+
60+
$this->emit('close', array($this));
61+
}
62+
63+
public function isReadable()
64+
{
65+
return $this->readable;
66+
}
67+
68+
public function isWritable()
69+
{
70+
return $this->writable;
71+
}
72+
73+
public function pause()
74+
{
75+
76+
}
77+
78+
public function resume()
79+
{
80+
81+
}
82+
83+
public function pipe(WritableStreamInterface $dest, array $options = array())
84+
{
85+
Util::pipe($this, $dest, $options);
86+
87+
return $dest;
88+
}
89+
90+
/**
91+
* Forwards a single "data" event to the reading side of the stream
92+
*
93+
* This will emit an "data" event.
94+
*
95+
* If the stream is not readable, then this is a NO-OP.
96+
*
97+
* @param string $data
98+
*/
99+
protected function forwardData($data)
100+
{
101+
if (!$this->readable && $data !== '') {
102+
return;
103+
}
104+
$this->emit('data', array($data, $this));
105+
}
106+
107+
/**
108+
* Forwards an "end" event to the reading side of the stream
109+
*
110+
* This will emit an "end" event and will then close this stream.
111+
*
112+
* If the stream is not readable, then this is a NO-OP.
113+
*
114+
* @uses self::close()
115+
*/
116+
protected function forwardEnd()
117+
{
118+
if (!$this->readable) {
119+
return;
120+
}
121+
$this->readable = false;
122+
$this->writable = false;
123+
124+
$this->emit('end', array($this));
125+
$this->close();
126+
}
127+
128+
/**
129+
* Forwards the given $error message to the reading side of the stream
130+
*
131+
* This will emit an "error" event and will then close this stream.
132+
*
133+
* If the stream is not readable, then this is a NO-OP.
134+
*
135+
* @param Exception $error
136+
* @uses self::close()
137+
*/
138+
protected function forwardError(Exception $error)
139+
{
140+
if (!$this->readable) {
141+
return;
142+
}
143+
$this->readable = false;
144+
$this->writable = false;
145+
146+
$this->emit('error', array($error, $this));
147+
$this->close();
148+
}
149+
150+
/**
151+
* can be overwritten in order to implement custom transformation behavior
152+
*
153+
* This gets passed a single chunk of $data and should invoke `forwardData()`
154+
* with the filtered result.
155+
*
156+
* If the given data chunk is not valid, then you should invoke `forwardError()`
157+
* or throw an Exception.
158+
*
159+
* If you do not overwrite this method, then its default implementation simply
160+
* invokes `forwardData()` on the unmodified input data chunk.
161+
*
162+
* @param string $data
163+
* @see self::forwardData()
164+
*/
165+
protected function transformData($data)
166+
{
167+
$this->forwardData($data);
168+
}
169+
170+
/**
171+
* can be overwritten in order to implement custom stream ending behavior
172+
*
173+
* This may get passed a single final chunk of $data and should invoke `forwardEnd()`.
174+
*
175+
* If the given data chunk is not valid, then you should invoke `forwardError()`
176+
* or throw an Exception.
177+
*
178+
* If you do not overwrite this method, then its default implementation simply
179+
* invokes `transformData()` on the unmodified input data chunk (if any),
180+
* which in turn defaults to invoking `forwardData()` and then finally
181+
* invokes `forwardEnd()`.
182+
*
183+
* @param string $data
184+
* @see self::transformData()
185+
* @see self::forwardData()
186+
* @see self::forwardEnd()
187+
*/
188+
protected function transformEnd($data)
189+
{
190+
if ($data !== '') {
191+
$this->transformData($data);
192+
}
193+
$this->forwardEnd();
194+
}
195+
}

0 commit comments

Comments
 (0)