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
Requires
- php: >=7.2
Requires (Dev)
- phpunit/phpunit: ^8.5 || ^9.0
Suggests
- ext-zlib: For significantly better performance (native implementation)
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 implementationgz*/readgzfilepolyfills (only when missing): drop-in fallback whenext-zlibis 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/gzrewindfor 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 warninggzopen()doesn't support+modes (simultaneous read/write)
๐ค Contributing
Contributions are welcome! This is a complex project and there's much work to be done:
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- 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
- GitHub Issues: Report bugs or request features
Note: This library is feature-complete for v1.0.0. API is stable. Production use is recommended with proper error handling.