source = $stream; $resource = StreamWrapper::getResource($stream); // Specify window=15+32, so zlib will use header detection to both gzip (with header) and zlib data // See https://www.zlib.net/manual.html#Advanced definition of inflateInit2 // "Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection" // Default window size is 15. \stream_filter_append($resource, 'zlib.inflate', \STREAM_FILTER_READ, ['window' => 15 + 32]); $this->stream = $stream->isSeekable() ? new Stream($resource) : new NoSeekStream(new Stream($resource)); } public function read(int $length) : string { if ($length <= 0 || $this->source === null) { return $this->stream->read($length); } try { $data = $this->stream->read($length); } catch (TimeoutException $e) { throw $e; } catch (\RuntimeException $e) { if (StreamTimeout::isReadTimedOut($this->source)) { throw new TimeoutException('Unable to read from stream: timed out', 0, $e); } throw $e; } if ($data === '' && StreamTimeout::isReadTimedOut($this->source)) { throw new TimeoutException('Unable to read from stream: timed out'); } return $data; } public function close() : void { $source = $this->source; $this->source = null; $exception = null; try { $this->stream->close(); } catch (\Throwable $e) { $exception = $e; } if ($source !== null) { try { $source->close(); } catch (\Throwable $e) { if ($exception === null) { $exception = $e; } } } if ($exception !== null) { throw $exception; } } public function detach() { $this->source = null; return $this->stream->detach(); } }