Skip to content

Commit

Permalink
Merge pull request clue#35 from SimonFrings/error
Browse files Browse the repository at this point in the history
Improve error reporting when custom error handler is used
  • Loading branch information
clue authored May 17, 2022
2 parents 3c12eaf + 3d41fa5 commit dea3219
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 14 deletions.
20 changes: 18 additions & 2 deletions src/Compressor.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,25 @@ final class Compressor extends TransformStream
*/
public function __construct($encoding, $level = -1)
{
$context = @deflate_init($encoding, ['level' => $level]);
$errstr = '';
set_error_handler(function ($_, $error) use (&$errstr) {
// Match errstr from PHP's warning message.
// inflate_init(): encoding mode must be ZLIB_ENCODING_RAW, ZLIB_ENCODING_GZIP or ZLIB_ENCODING_DEFLATE
$errstr = strstr($error, ':'); // @codeCoverageIgnore
});

try {
$context = deflate_init($encoding, ['level' => $level]);
} catch (\ValueError $e) { // @codeCoverageIgnoreStart
// Throws ValueError on PHP 8.0+
restore_error_handler();
throw $e;
} // @codeCoverageIgnoreEnd

restore_error_handler();

if ($context === false) {
throw new \InvalidArgumentException('Unable to initialize compressor' . strstr(error_get_last()['message'], ':'));
throw new \InvalidArgumentException('Unable to initialize compressor' . $errstr); // @codeCoverageIgnore
}

$this->context = $context;
Expand Down
47 changes: 41 additions & 6 deletions src/Decompressor.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,45 @@ final class Decompressor extends TransformStream
*/
public function __construct($encoding)
{
$context = @inflate_init($encoding);
$errstr = '';
set_error_handler(function ($_, $error) use (&$errstr) {
// Match errstr from PHP's warning message.
// inflate_init(): encoding mode must be ZLIB_ENCODING_RAW, ZLIB_ENCODING_GZIP or ZLIB_ENCODING_DEFLATE
$errstr = strstr($error, ':'); // @codeCoverageIgnore
});

try {
$context = inflate_init($encoding);
} catch (\ValueError $e) { // @codeCoverageIgnoreStart
// Throws ValueError on PHP 8.0+
restore_error_handler();
throw $e;
} // @codeCoverageIgnoreEnd

restore_error_handler();

if ($context === false) {
throw new \InvalidArgumentException('Unable to initialize decompressor' . strstr(error_get_last()['message'], ':'));
throw new \InvalidArgumentException('Unable to initialize decompressor' . $errstr); // @codeCoverageIgnore
}

$this->context = $context;
}

protected function transformData($chunk)
{
$ret = @inflate_add($this->context, $chunk);
$errstr = '';
set_error_handler(function ($_, $error) use (&$errstr) {
// Match errstr from PHP's warning message.
// inflate_add(): data error
$errstr = strstr($error, ':');
});

$ret = inflate_add($this->context, $chunk);

restore_error_handler();

if ($ret === false) {
throw new \RuntimeException('Unable to decompress' . strstr(error_get_last()['message'], ':'));
throw new \RuntimeException('Unable to decompress' . $errstr);
}

if ($ret !== '') {
Expand All @@ -63,11 +89,20 @@ protected function transformData($chunk)

protected function transformEnd($chunk)
{
$ret = @inflate_add($this->context, $chunk, ZLIB_FINISH);
$errstr = '';
set_error_handler(function ($_, $error) use (&$errstr) {
// Match errstr from PHP's warning message.
// inflate_add(): data error
$errstr = strstr($error, ':');
});

$ret = inflate_add($this->context, $chunk, ZLIB_FINISH);
$this->context = null;

restore_error_handler();

if ($ret === false) {
throw new \RuntimeException('Unable to decompress' . strstr(error_get_last()['message'], ':'));
throw new \RuntimeException('Unable to decompress' . $errstr);
}

if ($ret !== '') {
Expand Down
19 changes: 19 additions & 0 deletions tests/CompressorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,23 @@ public function testCtorThrowsForInvalidEncoding()
$this->expectException(PHP_VERSION_ID >= 80000 ? \ValueError::class : \InvalidArgumentException::class);
new Compressor(0);
}

public function testCtorThrowsForInvalidEncodingAndUnsetsUsedErrorHandler()
{
$handler = set_error_handler(function(){});

restore_error_handler();

try {
new Compressor(0);
} catch (\ValueError $e) {
// handle Error to unset Error handler afterwards (PHP >= 8.0)
} catch (\InvalidArgumentException $e) {
// handle Error to unset Error handler afterwards (PHP < 8.0)
}
$checkHandler = set_error_handler(function(){});
restore_error_handler();

$this->assertEquals($handler, $checkHandler);
}
}
20 changes: 20 additions & 0 deletions tests/DecompressorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,24 @@ public function testCtorThrowsForInvalidEncoding()
$this->expectException(PHP_VERSION_ID >= 80000 ? \ValueError::class : \InvalidArgumentException::class);
new Decompressor(0);
}

public function testCtorThrowsForInvalidEncodingAndUnsetsUsedErrorHandler()
{
$handler = set_error_handler(function(){});

restore_error_handler();

try {
new Decompressor(0);
} catch (\ValueError $e) {
// handle Error to unset Error handler afterwards (PHP >= 8.0)
} catch (\InvalidArgumentException $e) {
// handle Error to unset Error handler afterwards (PHP < 8.0)
}

$checkHandler = set_error_handler(function(){});
restore_error_handler();

$this->assertEquals($handler, $checkHandler);
}
}
20 changes: 18 additions & 2 deletions tests/DeflateDecompressorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,35 @@ public function testInflateBig()
$this->assertEquals($data, $buffered);
}

public function testDecompressInvalidDataEmitsError()
public function testDecompressInvalidDataEmitsErrorWithoutCallingCustomErrorHandler()
{
$this->decompressor->on('data', $this->expectCallableNever());
$this->decompressor->on('error', $this->expectCallableOnce());

$error = null;
set_error_handler(function ($_, $errstr) use (&$error) {
$error = $errstr;
});

$this->decompressor->write('invalid');

restore_error_handler();
$this->assertNull($error);
}

public function testDecompressInvalidOnEndEmitsError()
public function testDecompressInvalidOnEndEmitsErrorWithoutCallingCustomErrorHandler()
{
$this->decompressor->on('data', $this->expectCallableNever());
$this->decompressor->on('error', $this->expectCallableOnce());

$error = null;
set_error_handler(function ($_, $errstr) use (&$error) {
$error = $errstr;
});

$this->decompressor->end('invalid');

restore_error_handler();
$this->assertNull($error);
}
}
20 changes: 18 additions & 2 deletions tests/GzipDecompressorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,35 @@ public function testDecompressBig()
$this->assertEquals($data, $buffered);
}

public function testDecompressInvalidDataEmitsError()
public function testDecompressInvalidDataEmitsErrorWithoutCallingCustomErrorHandler()
{
$this->decompressor->on('data', $this->expectCallableNever());
$this->decompressor->on('error', $this->expectCallableOnce());

$error = null;
set_error_handler(function ($_, $errstr) use (&$error) {
$error = $errstr;
});

$this->decompressor->write('invalid');

restore_error_handler();
$this->assertNull($error);
}

public function testDecompressInvalidOnEndEmitsError()
public function testDecompressInvalidOnEndEmitsErrorWithoutCallingCustomErrorHandler()
{
$this->decompressor->on('data', $this->expectCallableNever());
$this->decompressor->on('error', $this->expectCallableOnce());

$error = null;
set_error_handler(function ($_, $errstr) use (&$error) {
$error = $errstr;
});

$this->decompressor->end('invalid');

restore_error_handler();
$this->assertNull($error);
}
}
20 changes: 18 additions & 2 deletions tests/ZlibDecompressorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,35 @@ public function testDecompressBig()
$this->assertEquals($data, $buffered);
}

public function testDecompressInvalidDataEmitsError()
public function testDecompressInvalidDataEmitsErrorWithoutCallingCustomErrorHandler()
{
$this->decompressor->on('data', $this->expectCallableNever());
$this->decompressor->on('error', $this->expectCallableOnce());

$error = null;
set_error_handler(function ($_, $errstr) use (&$error) {
$error = $errstr;
});

$this->decompressor->write('invalid');

restore_error_handler();
$this->assertNull($error);
}

public function testDecompressInvalidOnEndEmitsError()
public function testDecompressInvalidOnEndEmitsErrorWithoutCallingCustomErrorHandler()
{
$this->decompressor->on('data', $this->expectCallableNever());
$this->decompressor->on('error', $this->expectCallableOnce());

$error = null;
set_error_handler(function ($_, $errstr) use (&$error) {
$error = $errstr;
});

$this->decompressor->end('invalid');

restore_error_handler();
$this->assertNull($error);
}
}

0 comments on commit dea3219

Please sign in to comment.