diff --git a/src/Compressor.php b/src/Compressor.php index a494096..a68910a 100644 --- a/src/Compressor.php +++ b/src/Compressor.php @@ -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; diff --git a/src/Decompressor.php b/src/Decompressor.php index d2ea6ba..88d13f5 100644 --- a/src/Decompressor.php +++ b/src/Decompressor.php @@ -41,9 +41,25 @@ 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; @@ -51,9 +67,19 @@ public function __construct($encoding) 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 !== '') { @@ -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 !== '') { diff --git a/tests/CompressorTest.php b/tests/CompressorTest.php index 926c3c4..5eaf037 100644 --- a/tests/CompressorTest.php +++ b/tests/CompressorTest.php @@ -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); + } } diff --git a/tests/DecompressorTest.php b/tests/DecompressorTest.php index e438692..af9bc04 100644 --- a/tests/DecompressorTest.php +++ b/tests/DecompressorTest.php @@ -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); + } } diff --git a/tests/DeflateDecompressorTest.php b/tests/DeflateDecompressorTest.php index 878a57b..cbd6906 100644 --- a/tests/DeflateDecompressorTest.php +++ b/tests/DeflateDecompressorTest.php @@ -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); } } diff --git a/tests/GzipDecompressorTest.php b/tests/GzipDecompressorTest.php index fea42db..2f8fabd 100644 --- a/tests/GzipDecompressorTest.php +++ b/tests/GzipDecompressorTest.php @@ -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); } } diff --git a/tests/ZlibDecompressorTest.php b/tests/ZlibDecompressorTest.php index 598b3aa..15b29d8 100644 --- a/tests/ZlibDecompressorTest.php +++ b/tests/ZlibDecompressorTest.php @@ -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); } }