ossrock/fflate-php

Pure PHP implementation of DEFLATE/GZIP/Zlib compression with stream filters and gz* polyfills

Installs: 3

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/ossrock/fflate-php

v0.2.0 2025-12-31 16:18 UTC

This package is auto-updated.

Last update: 2025-12-31 16:18:42 UTC


README

Pure PHP implementation of DEFLATE, GZIP, and Zlib compression/decompression with stream filters and gz*() function polyfills.

๐ŸŽฏ Overview

fflate-php is a port of the JavaScript fflate library to PHP, providing:

  • โœ… Pure PHP DEFLATE compression/decompression
  • โœ… GZIP format support
  • โœ… Zlib format support
  • โœ… PHP stream filters for on-the-fly compression
  • โœ… Drop-in polyfills for gz*() functions
  • โœ… Automatic fallback when native zlib is unavailable
  • โœ… PHP 7.2+ compatible
  • โœ… 64-bit architecture required

Use cases:

  • phpPgAdmin backup/restore on systems without zlib
  • Streaming compression for large files
  • Cross-platform PHP applications
  • Testing both native and pure PHP implementations

โš ๏ธ Important Notes

Performance

Pure PHP implementation is 20-90ร— slower than native zlib. Use native zlib when available for production workloads.

GZIP ISIZE Limitation

GZIP's ISIZE field is 32-bit. Files >4GB are supported, but size is stored modulo 2ยณยฒ (matching gzip/zlib behavior).

Requirements

  • PHP 7.2 or higher
  • 64-bit architecture (required for proper bitwise operations)

๐Ÿ“ฆ Installation

composer require ossrock/fflate-php

๐Ÿš€ Quick Start

Basic GZIP Decompression

<?php
require 'vendor/autoload.php';

use Ossrock\FflatePhp\Gzip\GzipDecoder;

$compressed = file_get_contents('data.sql.gz');
$decompressed = GzipDecoder::decode($compressed);

if ($decompressed === false) {
    die('Decompression failed');
}

file_put_contents('data.sql', $decompressed);

Using Polyfill Functions

<?php
require 'vendor/autoload.php';

// If native gzdecode() doesn't exist, our polyfill will be used
$decompressed = gzdecode($compressed);

Basic Compression

<?php
require 'vendor/autoload.php';

use Ossrock\FflatePhp\Gzip\GzipEncoder;
use Ossrock\FflatePhp\Zlib\ZlibEncoder;

$gz = GzipEncoder::encode('hello', 6);
$z  = ZlibEncoder::encode('hello', 6);

Choosing Pure PHP vs Native

PHP cannot override built-in gz*() functions when ext-zlib is installed. This library therefore provides:

  • fflate_* functions (always available): guaranteed pure-PHP implementation
  • gz* / readgzfile polyfills (only when missing): drop-in fallback when ext-zlib is not installed
<?php
require 'vendor/autoload.php';

// Explicit pure-PHP call (always uses this library)
$compressed = fflate_gzencode('hello', 6);
$plain      = fflate_gzdecode($compressed);

// Native or pure-PHP call (drop-in replacement)
$compressed2 = gzencode('hello', 6);
$plain2      = gzdecode($compressed2);

Stream Filters (True Streaming)

<?php
// Register stream filters
FflatePhp::registerFilters();

// GZIP compress while writing to browser
$fp = fopen('php://filter/write=gzip.encode/resource=php://output', 'wb');
// For pure PHP implementation...
//$fp = fopen('php://filter/write=fflate.gzip.encode/resource=php://output', 'wb');
fwrite($fp, $dataChunk1);
fwrite($fp, $dataChunk2);
fclose($fp); // writes gzip footer (CRC/ISIZE)

// GZIP decompress while reading (streams output as it becomes available)
$fp = fopen('php://filter/read=gzip.decode/resource=dump.sql.gz', 'rb');
// For pure PHP implementation...
//$fp = fopen('php://filter/read=fflate.gzip.decode/resource=dump.sql.gz', 'rb');
//
while ($chunk = fread($fp, 8192)) {
    echo $chunk;
}
fclose($fp);

// Zlib variants:
// - write=zlib.encode
// - read=zlib.decode
// Pure PHP:
// - write=fflate.zlib.encode
// - read=fflate.zlib.decode

๐Ÿ“š API Reference

FflatePhp (Main Facade)

// Check availability
FflatePhp::isNativeAvailable(): bool

// Configuration
FflatePhp::setDefaultLevel(int $level): void  // 0-9, default: 6
FflatePhp::getDefaultLevel(): int

// Stream filters
FflatePhp::registerFilters(): void

// Version
FflatePhp::getVersion(): string

GzipDecoder

// Decode GZIP data
GzipDecoder::decode(string $data): string|false

GzipEncoder

// Encode GZIP data
GzipEncoder::encode(string $data, int $level = 6): string

ZlibEncoder / ZlibDecoder

ZlibEncoder::encode(string $data, int $level = 6): string
ZlibDecoder::decode(string $data): string|false

๐Ÿ—๏ธ Project Status

Version: 0.2.0-dev (Feature-Complete)

โœ… Implemented

  • DEFLATE inflate (decompression) - fully RFC 1951 compliant
  • DEFLATE deflate (compression) - stored + fixed Huffman encoding
  • Huffman tree encoding/decoding
  • LZ77 back-references with 32KB sliding window
  • GZIP decoder with concatenated member support
  • GZIP encoder with CRC32 + ISIZE
  • Zlib encoder/decoder with Adler32 checksums
  • CRC32 and Adler32 checksums (table-based + native)
  • Bit-level I/O operations (reader/writer)
  • Stream classes for incremental encode/decode
  • php://filter stream filters (4 filters: gzip/zlib encode/decode)
  • Core gz*() polyfill functions (string APIs)
  • File-handle gz*() polyfills (gzopen, gzread, gzwrite, gzclose, etc.)
  • Best-effort gzseek/gztell/gzrewind for seekable streams
  • Mode detection and switching
  • 18 comprehensive PHPUnit tests (all passing)
  • PHP 8.2+ deprecation fixes
  • Full README with examples and API documentation

๐Ÿ“‹ Future Enhancements (Post-v1.0.0)

  • Dynamic Huffman encoding (better compression ratio, higher complexity)
  • Carry LZ77 dictionary across streaming blocks (improved streaming ratio)
  • ZIP archive support (v2.0+)
  • Performance benchmarks and optimization
  • Integration guides (phpPgAdmin, WordPress, etc.)

๐Ÿงช Testing

# Install dependencies
composer install

# Run all tests
vendor/bin/phpunit

๐Ÿงฉ Notes & Limitations

Performance

Pure PHP implementation is 20-90ร— slower than native zlib. For production workloads, native zlib is strongly recommended. Use fflate-php as a fallback when native zlib is unavailable.

Stream Encoding

Stream encode filters output bytes as you write, but currently emit independent DEFLATE blocks per chunk (dictionary is not carried across blocks yet), so compression ratio may be slightly worse than native zlib for very long streams.

Stream Decoding

Stream decode filters are fully incremental and support concatenated gzip members (multiple gzip streams appended together automatically).

Seeking Limitations

  • gzseek() works on read streams with best-effort semantics:
    • Forward seeks: accomplished by reading and discarding decompressed bytes (O(n))
    • Backward seeks: require underlying stream to be seekable; stream is rewound and filter is reset
    • SEEK_END: not supported (would require full decompression to determine uncompressed size)
  • gzseek() on write streams is not supported with a warning
  • gzopen() doesn't support + modes (simultaneous read/write)

๐Ÿค Contributing

Contributions are welcome! This is a complex project and there's much work to be done:

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests
  5. Submit a pull request

๐Ÿ“„ License

MIT License - see LICENSE file for details.

This project is a port of fflate by Arjun Barrett, which is also MIT licensed.

๐Ÿ™ Credits

  • fflate: Original JavaScript implementation by Arjun Barrett
  • DEFLATE Algorithm: RFC 1951
  • GZIP Format: RFC 1952
  • Zlib Format: RFC 1950

๐Ÿ“ž Support

Note: This library is feature-complete for v1.0.0. API is stable. Production use is recommended with proper error handling.