PHP 8.6.0 Beta 3 is available for testing

Compression Filters

While the Compression Wrappers provide a way of creating gzip and bz2 compatible files on the local filesystem, they do not provide a means for generalized compression over network streams, nor do they provide a means to begin with a non-compressed stream and transition to a compressed one. For this, a compression filter may be applied to any stream resource at any time.

Nota:

Compression filters do not generate headers and trailers used by command line utilities such as gzip. They only compress and decompress the payload portions of compressed data streams.

zlib.deflate and zlib.inflate

zlib.deflate (compression) and zlib.inflate (decompression) are implementations of the compression methods described in » RFC 1951. The deflate filter takes up to three parameters passed as an associative array. level describes the compression strength to use (1-9). Higher numbers will generally yield smaller payloads at the cost of additional processing time. Two special compression levels also exist: 0 (for no compression at all), and -1 (zlib internal default -- currently 6). window is the base-2 log of the compression loopback window size. Higher values (up to 15 -- 32768 bytes) yield better compression at a cost of memory, while lower values (down to 9 -- 512 bytes) yield worse compression in a smaller memory footprint. Default window size is currently 15. The zlib.deflate filter implements the compression methods DEFLATE, ZLIB and GZIP depending on the value of the window parameter. The 4 lower bits of the window parameter set the size of the internal “history buffer” used, being the base-2 logarithm of its size in a range from 8 up to 15. The meaning of the others bits of the window parameter can be set as described below.

  • DEFLATE (» RFC 1951) is a raw compression algorithm without header, without checksum. It is performed when the window parameter is set in the range from -9 up to -15. This compression algorithm is the base for all the formats generated by the zlib.deflate filter. The corresponding functions that operate directly on strings are gzdeflate() and gzinflate().

  • ZLIB (» RFC 1950) applies the DEFLATE algorithm and adds a 2 bytes header and 4 bytes trailer containing the Adler32 checksum of the uncompressed data in big-endian byte order:

    ZLIB = ZLIBHEADER(2B)  DEFLATE  ADLER32(4B)
    The 2 bytes header, read as 16 bit unsigned number in big-endian order, must be multiple of 31. This format is generated when the window parameter is set in the range from 8 up to 15. The corresponding functions that operate directly on strings are gzcompress() and gzuncompress().

  • GZIP (» RFC 1952) applies the DEFLATE algorithm adding an header and a trailer with the CRC32 checksum of the uncompressed data and their length, both in little-endian byte ordering:

    GZIP = GZIPHEADER(10B)  DEFLATE  CRC32(4B)  LENGTH(4B)
    This is the format of the .gz files, with the GZIPHEADER containing in the order the GZIP signature (\x1f\x8B), the compression method (\x08), a zero flag byte, a zero modification time on 4 bytes, an extra-flags byte that depends on the compression level, and the operating system set to the current system (\x00 = FAT filesystem, \x03 = Unix, etc.). This format is generated when the window parameter is set in the range from 9+16=25 up to 15+16=31. Note that there is a limit of 4GB to the maximum length of the uncompressed data; beyond this limit, only the modulo 2^32 of the actual length is stored in the LENGTH part. The corresponding functions that operate directly on strings are gzencode() and gzdecode(); the gzopen() function allows to read and write .gz files.

With the zlib.inflate filter, only the window parameter is allowed; any other parameter (memory, level) is ignored. Be $W the base-2 log of the history buffer size, so that 2^$W bytes are allocated by the decompressor. For the ZLIB format this value must be greater or equal to the one recorded in the header, which is checked at decompression time; for the other formats it only has to be large enough for the match distances actually present in the data. The range is 9 ≤ $W ≤ 15. If unknown, $W=15 is the safer choice.
  • DEFLATE (» RFC 1951): use window=-$W, with $W being a value not less than that used for compression. If unknown, set window=-15.

  • ZLIB (» RFC 1950): use window=$W. The value of $W is available from the same ZLIB header, otherwise set window=15.

  • GZIP (» RFC 1952): use window=$W+16. A GZIP header carries no window size, so set window=31 unless the value used for compression is known.

  • ZLIB or GZIP: use window=$W+32 for automatic header detection, so that both the formats can be recognized and decompressed; window=15+32=47 is the safer choice.

memory is a scale indicating how much work memory should be allocated. Valid values range from 1 (minimal allocation) to 9 (maximum allocation). This memory allocation affects speed only and does not impact the size of the generated payload.

Nota:

Because compression level is the most commonly used parameter, it may be alternatively provided as a simple integer value (rather than an array element).

zlib.* compression filters are available if zlib support is enabled.

Example #1 zlib.deflate and zlib.inflate

<?php
$params = array('level' => 6, 'window' => 15, 'memory' => 9);

$original_text = "This is a test.\nThis is only a test.\nThis is not an important string.\n";
echo "The original text is " . strlen($original_text) . " characters long.\n";

$fp = fopen('test.deflated', 'w');
stream_filter_append($fp, 'zlib.deflate', STREAM_FILTER_WRITE, $params);
fwrite($fp, $original_text);
fclose($fp);

echo "The compressed file is " . filesize('test.deflated') . " bytes long.\n";
echo "The original text was:\n";
/* Use readfile and zlib.inflate to decompress on the fly */
readfile('php://filter/zlib.inflate/resource=test.deflated');

/* Generates output:

The original text is 70 characters long.
The compressed file is 56 bytes long.
The original text was:
This is a test.
This is only a test.
This is not an important string.

 */
?>

Example #2 zlib.deflate simple

<?php
$original_text = "This is a test.\nThis is only a test.\nThis is not an important string.\n";
echo "The original text is " . strlen($original_text) . " characters long.\n";

$fp = fopen('test.deflated', 'w');
/* Here "6" indicates compression level 6 */
stream_filter_append($fp, 'zlib.deflate', STREAM_FILTER_WRITE, 6);
fwrite($fp, $original_text);
fclose($fp);

echo "The compressed file is " . filesize('test.deflated') . " bytes long.\n";

/* Generates output:

The original text is 70 characters long.
The compressed file is 56 bytes long.

 */
?>

bzip2.compress and bzip2.decompress

bzip2.compress and bzip2.decompress work in the same manner as the zlib filters described above. The bzip2.compress filter accepts up to two parameters given as elements of an associative array: blocks is an integer value from 1 to 9 specifying the number of 100kbyte blocks of memory to allocate for workspace. work is also an integer value ranging from 0 to 250 indicating how much effort to expend using the normal compression method before falling back on a slower, but more reliable method. Tuning this parameter effects only compression speed. Neither size of compressed output nor memory usage are changed by this setting. A work factor of 0 instructs the bzip library to use an internal default. The bzip2.decompress filter only accepts one parameter, which can be passed as either an ordinary boolean value, or as the small element of an associative array. small, when set to a true value, instructs the bzip library to perform decompression in a minimal memory footprint at the cost of speed.

bzip2.* compression filters are available if bz2 support is enabled.

Example #3 bzip2.compress and bzip2.decompress

<?php
$param = array('blocks' => 9, 'work' => 0);

echo "The original file is " . filesize('LICENSE') . " bytes long.\n";

$fp = fopen('LICENSE.compressed', 'w');
stream_filter_append($fp, 'bzip2.compress', STREAM_FILTER_WRITE, $param);
fwrite($fp, file_get_contents('LICENSE'));
fclose($fp);

echo "The compressed file is " . filesize('LICENSE.compressed') . " bytes long.\n";

/* Generates output:

The original file is 3288 bytes long.
The compressed file is 1488 bytes long.

 */
?>
add a note

User Contributed Notes 4 notes

up
8
Anonymous
11 years ago
To read a gzip encoded stream from http
<?php
$opts = [
    "http" => [
        "method" => "GET",
        "header" => [ "Accept-Encoding: gzip" ],
    ]
];
$ctx = stream_context_create($opts);
$f = fopen("http://php.net", "r", false, $ctx);
// check  stream_get_meta_data($f)["wrapper_data"] has "Content-Encoding: gzip"
stream_filter_append($f, "zlib.inflate", STREAM_FILTER_READ, ["window" => 30]);
echo stream_get_contents($f); // any stream processing
fclose($f);
up
2
bohwaz
8 years ago
Please note that there is currently a bug in this feature. ftell(), fseek() and fstat() functions cannot be used. Writing to a stream after using this function will not change the stream position as it should.

See bug: https://bugs.php.net/bug.php?id=49874

Also the zlib filters don't work with php://temp, php://memory and php://input streams, nothing is outputted to those streams.
up
0
TingSong
3 years ago
To decompress a gzipped stream:

<?php
$stream = fopen('https://example.com/some/file.txt.gz', 'rb');
stream_filter_append($stream, 'zlib.inflate', STREAM_FILTER_READ, ['window' => 15+16]);

// read the decompressed line directly
$line = fgets($stream);

// process the lines
?>

As the doc of zlib https://www.zlib.net/manual.html#Advanced

The 'window' parameter between 8 and 15 specified the window size from 2⁸ to 2¹⁵ bytes. It can be added by 16 for wrapping with gzip header and trailer instead of zlib wrapper.

And, window could be -8..-15 for unwrapping RAW deflate data.
up
0
Anonymous
5 years ago
To use the zlib.inflate filter with data originally written using gzcompress() or zlib.deflate, set the window option to 15 as outlined here: https://bugs.php.net/bug.php?id=68556

<?php
$fh = fopen(file_name, 'rb');
stream_filter_append($fh, 'zlib.inflate', STREAM_FILTER_READ, ['window' => 15]);
$contents = stream_get_contents($fh);
fclose($fh);
To Top