Skip to content

Commit 67f4d5c

Browse files
committed
Work around compressing empty stream on PHP 7+
1 parent f62b157 commit 67f4d5c

File tree

6 files changed

+17
-7
lines changed

6 files changed

+17
-7
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,6 @@ As such, we've spotted several inconsistencies (or *bugs*) between different PHP
232232
These inconsistencies exist in the underlying PHP engines and there's little we can do about this in this library.
233233

234234
* All Zend PHP versions: Decompressing invalid data does not emit any data (and does not raise an error)
235-
* PHP 7 only: Compressing an empty string does not emit any data (not a valid compression stream)
236235
* HHVM only: does not currently support the GZIP and ZLIB format at all (and does not raise an error)
237236
* HHVM only: The [`zlib.deflate` filter function](https://github.com/facebook/hhvm/blob/fee8ae39ce395c7b9b8910dfde6f22a7745aea83/hphp/system/php/stream/default-filters.php#L77) buffers the whole string. This means that compressing a stream of 100 MB actually stores the whole string in memory before invoking the underlying compression algorithm.
238237
* PHP 5.3 only: Tends to SEGFAULT occasionally on shutdown?

src/Compressor.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,7 @@ public function __construct($encoding, $level = -1)
4646
parent::__construct(
4747
Filter\fun('zlib.deflate', array('window' => $encoding, 'level' => $level))
4848
);
49+
50+
$this->emptyWrite = $encoding;
4951
}
5052
}

src/ZlibFilterStream.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,13 @@ public static function createZlibDecompressor()
7474

7575
private $filter;
7676

77+
/**
78+
* @var int|null
79+
* @see Compressor
80+
* @internal
81+
*/
82+
protected $emptyWrite;
83+
7784
public function __construct($filter)
7885
{
7986
$this->filter = $filter;
@@ -85,6 +92,7 @@ protected function transformData($chunk)
8592
$ret = $filter($chunk);
8693

8794
if ($ret !== '') {
95+
$this->emptyWrite = null;
8896
$this->forwardData($ret);
8997
}
9098
}
@@ -94,6 +102,13 @@ protected function transformEnd($chunk)
94102
$filter = $this->filter;
95103
$ret = $filter($chunk) . $filter();
96104

105+
// Stream ends successfully and did not emit any data whatsoever?
106+
// This happens when compressing an empty stream with PHP 7 only.
107+
// Bypass filter and manually compress/encode empty string.
108+
if ($this->emptyWrite !== null && $ret === '') {
109+
$ret = \zlib_encode('', $this->emptyWrite);
110+
}
111+
97112
if ($ret !== '') {
98113
$this->forwardData($ret);
99114
}

tests/ZlibFilterDeflateCompressorTest.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ public function setUp()
1313

1414
public function testDeflateEmpty()
1515
{
16-
if (PHP_VERSION >= 7) $this->markTestSkipped('Not supported on PHP 7 (empty chunk will not be emitted)');
17-
1816
$this->compressor->on('data', $this->expectCallableOnceWith("\x03\x00"));
1917
$this->compressor->on('end', $this->expectCallableOnce());
2018

tests/ZlibFilterGzipCompressorTest.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ public function setUp()
1515

1616
public function testCompressEmpty()
1717
{
18-
if (PHP_VERSION >= 7) $this->markTestSkipped('Not supported on PHP 7 (empty chunk will not be emitted)');
19-
2018
$os = "\x03"; // UNIX (0x03) or UNKNOWN (0xFF)
2119
$this->compressor->on('data', $this->expectCallableOnceWith("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00" . $os . "\x03\x00" . "\x00\x00\x00\x00\x00\x00\x00\x00"));
2220
$this->compressor->on('end', $this->expectCallableOnce());

tests/ZlibFilterZlibCompressorTest.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ public function setUp()
1515

1616
public function testCompressEmpty()
1717
{
18-
if (PHP_VERSION >= 7) $this->markTestSkipped('Not supported on PHP 7 (empty chunk will not be emitted)');
19-
2018
$this->compressor->on('data', $this->expectCallableOnceWith("\x78\x9c" . "\x03\x00" . "\x00\x00\x00\x01"));
2119
$this->compressor->on('end', $this->expectCallableOnce());
2220

0 commit comments

Comments
 (0)