Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Framework-agnostic PHP library for efficient HTTP compression (gzip, brotli, zstd) — simple, safe, and deterministic.

License

NotificationsYou must be signed in to change notification settings

aurynx/http-compression

Aurynx Mascot

Modern PHP library for HTTP compression with native type safety

gzip • brotli • zstd — simple, safe, and fast

InstallationQuick StartFeaturesUse CasesAPIAdvanced UsageAI Guide


Why HttpCompression?

Modern web applications need efficient compression to reduce bandwidth and improve response times. HttpCompression makes it simple with a clean, modern API focused on:

  • 🔷Native PHP 8.4+ types — zero docblock types, full IDE autocomplete
  • 🎯Single facade pattern — one intuitive API for all scenarios
  • 🚀Glob pattern support — compress entire directories with wildcards
  • 💾Memory-safe streaming — handle large files without memory limits
  • 🛡️Fail-fast validation — catch errors at configuration time
  • 🤖AI-friendly design — perfect for code generation and assistants

Installation

Requirements:

  • PHP 8.4 or higher
  • ext-zlib (required for gzip)
  • ext-brotli (optional, for brotli compression)
  • ext-zstd (optional, for zstd compression)
composer require aurynx/http-compression

Quick Start

Single File Compression

useAurynx\HttpCompression\CompressorFacade;useAurynx\HttpCompression\Enums\AlgorithmEnum;// Compress and save to fileCompressorFacade::once()    ->file('public/index.html')    ->withGzip(9)    ->saveTo('public/index.html.gz');// Compress in-memory data$html ='<html><body>Hello World</body></html>';$result = CompressorFacade::once()    ->data($html)    ->withBrotli(11)    ->compress();$compressed =$result->getData(AlgorithmEnum::Brotli);

Batch Compression

useAurynx\HttpCompression\CompressorFacade;useAurynx\HttpCompression\ValueObjects\ItemConfig;$result = CompressorFacade::make()    ->addGlob('public/**/*.{html,css,js}')    ->withDefaultConfig(        ItemConfig::create()            ->withGzip(9)            ->withBrotli(11)            ->build()    )    ->skipAlreadyCompressed()    ->toDir('./dist')    ->compress();echo"Compressed{$result->count()} files\n";echo"Success rate:" . ($result->allOk() ?'100%' :'partial') ."\n";

Features

✨ Native Type Safety

The public API uses native PHP 8.4+ types everywhere (parameters, return types, readonly DTOs). This makes the library:

  • Easier for IDEs and AI agents to navigate (no docblock type guessing)
  • Safer at runtime thanks to engine-level type checks
  • More self-documenting due to explicit signatures

Example signature:

publicfunction compress(ItemConfig$config):CompressionResult

🎯 Fluent Facade API

Two facades for different scenarios:

CompressorFacade::make() — Batch compression

CompressorFacade::make()    ->addFile('index.html')    ->addGlob('assets/*.css')    ->withDefaultConfig(ItemConfig::create()->withGzip(9)->build())    ->toDir('./output')    ->compress();

CompressorFacade::once() — Quick single-item tasks

CompressorFacade::once()    ->file('logo.svg')    ->withGzip(9)    ->saveTo('logo.svg.gz');

🚀 Glob Pattern Support

Compress entire directories with powerful glob patterns:

CompressorFacade::make()    ->addGlob('public/**/*.html')// All HTML files recursively    ->addGlob('assets/*.{css,js}')// CSS and JS in assets/    ->addGlob('fonts/*.woff2')// Specific extension    ->skipAlreadyCompressed()// Skip images, videos, etc.    ->toDir('./dist', keepStructure:true)    ->compress();

💾 Memory-Safe Streaming

Handle large files without loading into memory:

useAurynx\HttpCompression\ValueObjects\OutputConfig;$result = CompressorFacade::make()    ->addFile('large-file.json')// 500MB file    ->withDefaultConfig(ItemConfig::create()->withGzip(6)->build())    ->inMemory(maxBytes:100_000_000)// 100MB limit    ->compress();// Stream compressed data$result->first()->read(AlgorithmEnum::Gzip,function (string$chunk) {echo$chunk;// Process in chunks});

🔌 Callback Streaming (no resources)

Stream compressed data directly into callbacks without dealing with stream resources.

Single algorithm (sendToCallback):

useAurynx\HttpCompression\CompressorFacade;$buffer ='';CompressorFacade::once()    ->data(str_repeat('hello',5000))    ->withGzip(6)    ->sendToCallback(function (string$chunk)use (&$buffer):void {$buffer .=$chunk;// write to socket, PSR-7 body, etc.    });

Multiple algorithms (sendAllToCallbacks):

useAurynx\HttpCompression\CompressorFacade;$gz ='';CompressorFacade::once()    ->data('payload')    ->withGzip(6)// required by default    ->tryBrotli(4)// optional    ->sendAllToCallbacks(['gzip' =>staticfunction (string$chunk)use (&$gz):void {$gz .=$chunk;        },// 'br' may be omitted when added via tryBrotli()    ]);

See more patterns and caveats in Advanced Usage:

  • Callback streaming (single/multi)
  • Low-level WritableStream wrapper

👉 Read: ./docs/advanced-usage.md


🛡️ Fail-Fast Validation

Errors are caught at configuration time, not during compression:

// ❌ Throws immediately (invalid level)AlgorithmSet::gzip(99);// InvalidArgumentException: Level must be between 1 and 9// ❌ Throws immediately (multiple algorithms for saveTo)CompressorFacade::once()    ->file('test.txt')    ->withGzip(9)    ->withBrotli(11)// Multiple algorithms    ->saveTo('test.gz');// CompressionException: saveTo() requires exactly one algorithm

📈 Rich Result Objects

Detailed statistics and easy access:

$result = CompressorFacade::make()    ->addGlob('*.html')    ->withDefaultConfig(ItemConfig::create()->withGzip(9)->withBrotli(11)->build())    ->inMemory()    ->compress();// Access resultsforeach ($resultas$id =>$item) {if ($item->isOk()) {echo"Original:{$item->originalSize} bytes\n";echo"Gzip:{$item->compressedSizes['gzip']} bytes\n";echo"Brotli:{$item->compressedSizes['brotli']} bytes\n";    }}// Aggregated statistics$summary =$result->summary();echo"Median compression ratio (gzip):" .$summary->getMedianRatio(AlgorithmEnum::Gzip) ."\n";echo"P95 compression time (brotli):" .$summary->getP95TimeMs(AlgorithmEnum::Brotli) ." ms\n";

Use Cases

1. Static Site Pre-Compression

Compress assets during build for nginxgzip_static:

useAurynx\HttpCompression\CompressorFacade;useAurynx\HttpCompression\ValueObjects\ItemConfig;// Build script$result = CompressorFacade::make()    ->addGlob('dist/**/*.{html,css,js,svg,json}')    ->withDefaultConfig(        ItemConfig::create()            ->withGzip(9)            ->withBrotli(11)            ->build()    )    ->skipAlreadyCompressed()    ->toDir('./dist', keepStructure:true)    ->compress();if (!$result->allOk()) {foreach ($result->failures()as$id =>$failure) {echo"Failed:{$id} -{$failure->getFailureReason()?->getMessage()}\n";    }exit(1);}echo"✓ Compressed{$result->count()} files\n";

Nginx configuration:

gzip_static on;brotli_static on;

2. Dynamic HTTP Response Compression

Compress content on-the-fly with caching:

useAurynx\HttpCompression\CompressorFacade;useAurynx\HttpCompression\AlgorithmEnum;functioncompressResponse(string$content,string$acceptEncoding):string{$cacheKey ='compressed_' .md5($content) .'_' .$acceptEncoding;if ($cached =apcu_fetch($cacheKey)) {return$cached;    }$algo =str_contains($acceptEncoding,'br') ? AlgorithmEnum::Brotli : AlgorithmEnum::Gzip;$result = CompressorFacade::once()        ->data($content)        ->withAlgorithm($algo,$algo->getDefaultLevel())        ->compress();$compressed =$result->getData($algo);apcu_store($cacheKey,$compressed,3600);return$compressed;}// In your controller$html =view('welcome')->render();$acceptEncoding =$_SERVER['HTTP_ACCEPT_ENCODING'] ??'';if (str_contains($acceptEncoding,'br') ||str_contains($acceptEncoding,'gzip')) {$compressed =compressResponse($html,$acceptEncoding);header('Content-Encoding:' . (str_contains($acceptEncoding,'br') ?'br' :'gzip'));echo$compressed;}else {echo$html;}

3. API Response Compression

Compress JSON API responses:

useAurynx\HttpCompression\CompressorFacade;useAurynx\HttpCompression\AlgorithmEnum;functioncompressApiResponse(array$data,string$acceptEncoding):string{$json =json_encode($data);if (!str_contains($acceptEncoding,'gzip')) {return$json;    }$result = CompressorFacade::once()        ->data($json)        ->withGzip(6)// Lower level for speed        ->compress();header('Content-Encoding: gzip');header('Vary: Accept-Encoding');return$result->getData(AlgorithmEnum::Gzip);}// Usage$data = ['users' => User::all()];echocompressApiResponse($data,$_SERVER['HTTP_ACCEPT_ENCODING'] ??'');

4. Log File Archival

Compress and archive old log files:

useAurynx\HttpCompression\CompressorFacade;useAurynx\HttpCompression\ValueObjects\ItemConfig;// Daily cron job$result = CompressorFacade::make()    ->addGlob('storage/logs/*.log')    ->withDefaultConfig(ItemConfig::create()->withZstd(19)->build())// Maximum compression    ->toDir('storage/logs/archive', keepStructure:false)    ->compress();// Delete originalsforeach ($result->successes()as$id =>$item) {$originalPath ="storage/logs/{$id}";if (file_exists($originalPath)) {unlink($originalPath);    }}echo"Archived{$result->count()} log files\n";

5. Asset Pipeline Integration

Integrate with your build tools:

useAurynx\HttpCompression\CompressorFacade;useAurynx\HttpCompression\ValueObjects\ItemConfig;class AssetCompiler{publicfunctioncompile():void    {// Step 1: Bundle and minify (webpack, vite, etc.)system('npm run build');// Step 2: Compress for production$result = CompressorFacade::make()            ->addGlob('public/build/**/*.{js,css}')            ->addGlob('public/build/**/*.{svg,json}')            ->withDefaultConfig(                ItemConfig::create()                    ->withGzip(9)                    ->withBrotli(11)                    ->build()            )            ->skipExtensions(['woff2','png','jpg'])            ->toDir('public/build', keepStructure:true)            ->failFast(true)            ->compress();if (!$result->allOk()) {thrownew \RuntimeException('Asset compression failed');        }$summary =$result->summary();$avgRatio =$summary->getAverageRatio(AlgorithmEnum::Gzip);echo"✓ Compressed{$result->count()} assets (avg ratio:" .round($avgRatio *100,1) ."%)\n";    }}

API Reference

Facades

CompressorFacade::make() — Batch Compression

useAurynx\HttpCompression\CompressorFacade;$result = CompressorFacade::make()// Add inputs    ->add(CompressionInput$input, ?ItemConfig$config =null)    ->addMany(iterable$inputs)    ->addFile(string$path, ?ItemConfig$config =null, ?string$id =null)    ->addData(string$data, ?ItemConfig$config =null, ?string$id =null)    ->addGlob(string$pattern, ?ItemConfig$config =null)    ->addFrom(InputProviderInterface$provider, ?ItemConfig$config =null)// Configuration    ->withDefaultConfig(ItemConfig$config)// Output    ->toDir(string$dir, bool$keepStructure =false)    ->inMemory(int$maxBytes =5_000_000)// Options    ->failFast(bool$enable =true)    ->skipExtensions(array$extensions)    ->skipAlreadyCompressed()// Execute    ->compress(): CompressionResult;

CompressorFacade::once() — Single Item

useAurynx\HttpCompression\CompressorFacade;CompressorFacade::once()// Input    ->file(string$path)    ->data(string$data)// Algorithm (choose ONE)    ->withGzip(int$level =6)    ->withBrotli(int$level =11)    ->withZstd(int$level =3)// Execute    ->compress(): CompressionItemResult    ->saveTo(string$path): void;// Requires exactly one algorithm

Notes on Saving Files

  • saveTo(path):

    • Atomic write (tmp + rename) to the target path
    • Existing target is replaced (OverwritePolicy=Replace)
    • The target directory must already exist (no auto-create)
  • saveAllTo(directory, basename, options):

    • basename must be a plain filename (no '/' or '\', not '.' or '..')
    • Options:
      • overwritePolicy: fail|replace|skip (default fail)
      • atomicAll: bool (default true) — all-or-nothing semantics
      • allowCreateDirs: bool (default true)
      • permissions: int|null — chmod after successful rename

Configuration

ItemConfig — Compression Configuration

useAurynx\HttpCompression\ValueObjects\ItemConfig;useAurynx\HttpCompression\ValueObjects\AlgorithmSet;// Using builder$config = ItemConfig::create()    ->withGzip(9)    ->withBrotli(11)    ->withZstd(3)    ->limitBytes(5_000_000)    ->build();// Direct instantiation$config =newItemConfig(    algorithms: AlgorithmSet::gzip(9),    maxBytes:1_000_000);// Static factories$config = ItemConfig::gzip(9);$config = ItemConfig::brotli(11);$config = ItemConfig::zstd(3);

AlgorithmSet — Algorithm Configuration

useAurynx\HttpCompression\ValueObjects\AlgorithmSet;useAurynx\HttpCompression\Enums\AlgorithmEnum;// Static factories$set = AlgorithmSet::gzip(9);$set = AlgorithmSet::brotli(11);$set = AlgorithmSet::zstd(3);$set = AlgorithmSet::fromDefaults();// All algorithms with default levels// Manual construction from pairs$set = AlgorithmSet::from([    [AlgorithmEnum::Gzip,9],    [AlgorithmEnum::Brotli,11],]);

Results

CompressionResult — Batch Results

$result = CompressorFacade::make()->compress();// Access$result->get(string$id): CompressionItemResult$result->first(): CompressionItemResult$result->toArray(): array// Filtering$result->successes(): array$result->failures(): array$result->allOk(): bool// Statistics$result->summary(): CompressionSummaryResult$result->count(): int// Iterationforeach ($resultas$id =>$item) {// Process each item}

CompressionItemResult — Single Item Result

$item =$result->first();// Status$item->isOk(): bool$item->success: bool$item->originalSize: int// Data access$item->getData(AlgorithmEnum$algo): string$item->getStream(AlgorithmEnum$algo): resource$item->read(AlgorithmEnum$algo, callable$consumer): void// Metadata$item->has(AlgorithmEnum$algo): bool$item->compressedSizes: array<string, int>$item->compressionTimes: array<string, float>$item->errors: array<string, \Throwable>$item->getFailureReason(): ?\Throwable

CompressionSummaryResult — Aggregated Statistics

$summary =$result->summary();// Compression ratios (compressed / original)$summary->getAverageRatio(AlgorithmEnum$algo): float$summary->getMedianRatio(AlgorithmEnum$algo): float// p50$summary->getP95Ratio(AlgorithmEnum$algo): float// Timing (milliseconds)$summary->getMedianTimeMs(AlgorithmEnum$algo): float// p50$summary->getP95TimeMs(AlgorithmEnum$algo): float$summary->getTotalTimeMs(AlgorithmEnum$algo): float// Counts$summary->getTotalItems(): int$summary->getSuccessCount(): int$summary->getFailureCount(): int

For AI Assistants

This library is designed to be AI-friendly with:

  • Native types — no docblock parsing needed
  • Explicit namingCompressionResult,AlgorithmEnum, etc.
  • Fluent API — easy to chain methods
  • Fail-fast — errors are obvious and immediate
  • Immutable value objects — no side effects

For a deeper, agent-focused walkthrough, see the AI Guide:AI_GUIDE.md. You can also use the machine-readable schemadocs/ai-manifest.json.

Common Patterns

// Quick compressionCompressorFacade::once()->file('test.txt')->withGzip(9)->saveTo('test.txt.gz');// Batch with globCompressorFacade::make()    ->addGlob('*.html')    ->withDefaultConfig(ItemConfig::create()->withGzip(9)->build())    ->toDir('./out')    ->compress();// Multiple algorithms$config = ItemConfig::create()    ->withGzip(9)    ->withBrotli(11)    ->withZstd(3)    ->build();

Avoid These Mistakes

❌ Multiple algorithms withsaveTo():

// WRONG - saveTo() requires exactly one algorithmCompressorFacade::once()->file('x')->withGzip()->withBrotli()->saveTo('x.gz');

✅ Usecompress() instead:

$result = CompressorFacade::once()->file('x')->withGzip()->withBrotli()->compress();$result->getData(AlgorithmEnum::Gzip);

Contributing

Contributions are welcome! Please seeCONTRIBUTING.md for details.

Development Setup

# Install dependenciescomposer install# Run testscomposertest# Run PHPStancomposer phpstan# Run CS Fixercomposer cs-fix

License

The MIT License (MIT). Please seeLicense File for more information.


Credits

Created and maintained byAnton Semenov.


Crafted by Aurynx 🔮

About

Framework-agnostic PHP library for efficient HTTP compression (gzip, brotli, zstd) — simple, safe, and deterministic.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

No packages published

Languages


[8]ページ先頭

©2009-2025 Movatter.jp