{"id":32692907,"url":"https://github.com/aurynx/http-compression","last_synced_at":"2026-01-20T16:34:16.283Z","repository":{"id":318711224,"uuid":"1072940499","full_name":"aurynx/http-compression","owner":"aurynx","description":"Framework-agnostic PHP library for efficient HTTP compression (gzip, brotli, zstd) — simple, safe, and deterministic.","archived":false,"fork":false,"pushed_at":"2025-10-10T14:36:55.000Z","size":2270,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-10T15:02:09.128Z","etag":null,"topics":["aurynx","brotli","content-encoding","gzip","http-compression","library","nginx","optimization","performance","php","static-assets","zstd"],"latest_commit_sha":null,"homepage":"https://packagist.org/packages/aurynx/http-compression","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aurynx.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-09T12:20:27.000Z","updated_at":"2025-10-10T14:36:50.000Z","dependencies_parsed_at":"2025-10-10T15:02:25.339Z","dependency_job_id":"0c2d1667-65e4-4b0e-bba6-4a7a7bb9671f","html_url":"https://github.com/aurynx/http-compression","commit_stats":null,"previous_names":["aurynx/http-compression"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/aurynx/http-compression","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aurynx%2Fhttp-compression","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aurynx%2Fhttp-compression/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aurynx%2Fhttp-compression/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aurynx%2Fhttp-compression/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aurynx","download_url":"https://codeload.github.com/aurynx/http-compression/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aurynx%2Fhttp-compression/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":282166098,"owners_count":26625174,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-11-01T02:00:06.759Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aurynx","brotli","content-encoding","gzip","http-compression","library","nginx","optimization","performance","php","static-assets","zstd"],"created_at":"2025-11-01T16:02:08.836Z","updated_at":"2025-11-01T16:04:53.067Z","avatar_url":"https://github.com/aurynx.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Aurynx | HttpCompression\n\n\u003cp align=\"center\"\u003e\n  \u003cimg width=\"256\" height=\"256\" alt=\"Aurynx Mascot\" src=\"https://github.com/user-attachments/assets/80a3ece6-5c50-4b01-9aee-7f086b55a0ef\" /\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003cb\u003eModern PHP library for HTTP compression with native type safety\u003c/b\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003egzip • brotli • zstd — simple, safe, and fast\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"#installation\"\u003eInstallation\u003c/a\u003e •\n  \u003ca href=\"#quick-start\"\u003eQuick Start\u003c/a\u003e •\n  \u003ca href=\"#features\"\u003eFeatures\u003c/a\u003e •\n  \u003ca href=\"#use-cases\"\u003eUse Cases\u003c/a\u003e •\n  \u003ca href=\"#api-reference\"\u003eAPI\u003c/a\u003e •\n  \u003ca href=\"./docs/advanced-usage.md\"\u003eAdvanced Usage\u003c/a\u003e •\n  \u003ca href=\"./docs/AI_GUIDE.md\"\u003eAI Guide\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n## Why HttpCompression?\n\nModern web applications need efficient compression to reduce bandwidth and improve response times. HttpCompression makes it simple with a clean, modern API focused on:\n\n- 🔷 **Native PHP 8.4+ types** — zero docblock types, full IDE autocomplete\n- 🎯 **Single facade pattern** — one intuitive API for all scenarios\n- 🚀 **Glob pattern support** — compress entire directories with wildcards\n- 💾 **Memory-safe streaming** — handle large files without memory limits\n- 🛡️ **Fail-fast validation** — catch errors at configuration time\n- 🤖 **AI-friendly design** — perfect for code generation and assistants\n\n## Installation\n\n**Requirements:**\n- PHP 8.4 or higher\n- `ext-zlib` (required for gzip)\n- `ext-brotli` (optional, for brotli compression)\n- `ext-zstd` (optional, for zstd compression)\n\n```bash\ncomposer require aurynx/http-compression\n```\n\n## Quick Start\n\n### Single File Compression\n\n```php\nuse Aurynx\\HttpCompression\\CompressorFacade;\nuse Aurynx\\HttpCompression\\Enums\\AlgorithmEnum;\n\n// Compress and save to file\nCompressorFacade::once()\n    -\u003efile('public/index.html')\n    -\u003ewithGzip(9)\n    -\u003esaveTo('public/index.html.gz');\n\n// Compress in-memory data\n$html = '\u003chtml\u003e\u003cbody\u003eHello World\u003c/body\u003e\u003c/html\u003e';\n$result = CompressorFacade::once()\n    -\u003edata($html)\n    -\u003ewithBrotli(11)\n    -\u003ecompress();\n\n$compressed = $result-\u003egetData(AlgorithmEnum::Brotli);\n```\n\n### Batch Compression\n\n```php\nuse Aurynx\\HttpCompression\\CompressorFacade;\nuse Aurynx\\HttpCompression\\ValueObjects\\ItemConfig;\n\n$result = CompressorFacade::make()\n    -\u003eaddGlob('public/**/*.{html,css,js}')\n    -\u003ewithDefaultConfig(\n        ItemConfig::create()\n            -\u003ewithGzip(9)\n            -\u003ewithBrotli(11)\n            -\u003ebuild()\n    )\n    -\u003eskipAlreadyCompressed()\n    -\u003etoDir('./dist')\n    -\u003ecompress();\n\necho \"Compressed {$result-\u003ecount()} files\\n\";\necho \"Success rate: \" . ($result-\u003eallOk() ? '100%' : 'partial') . \"\\n\";\n```\n\n## Features\n\n### ✨ Native Type Safety\n\nThe public API uses native PHP 8.4+ types everywhere (parameters, return types, readonly DTOs). This makes the library:\n- Easier for IDEs and AI agents to navigate (no docblock type guessing)\n- Safer at runtime thanks to engine-level type checks\n- More self-documenting due to explicit signatures\n\nExample signature:\n```php\npublic function compress(ItemConfig $config): CompressionResult\n```\n\n---\n\n### 🎯 Fluent Facade API\n\nTwo facades for different scenarios:\n\n#### `CompressorFacade::make()` — Batch compression\n```php\nCompressorFacade::make()\n    -\u003eaddFile('index.html')\n    -\u003eaddGlob('assets/*.css')\n    -\u003ewithDefaultConfig(ItemConfig::create()-\u003ewithGzip(9)-\u003ebuild())\n    -\u003etoDir('./output')\n    -\u003ecompress();\n```\n\n#### `CompressorFacade::once()` — Quick single-item tasks\n```php\nCompressorFacade::once()\n    -\u003efile('logo.svg')\n    -\u003ewithGzip(9)\n    -\u003esaveTo('logo.svg.gz');\n```\n\n---\n\n### 🚀 Glob Pattern Support\n\nCompress entire directories with powerful glob patterns:\n\n```php\nCompressorFacade::make()\n    -\u003eaddGlob('public/**/*.html')           // All HTML files recursively\n    -\u003eaddGlob('assets/*.{css,js}')          // CSS and JS in assets/\n    -\u003eaddGlob('fonts/*.woff2')              // Specific extension\n    -\u003eskipAlreadyCompressed()               // Skip images, videos, etc.\n    -\u003etoDir('./dist', keepStructure: true)\n    -\u003ecompress();\n```\n\n---\n\n### 💾 Memory-Safe Streaming\n\nHandle large files without loading into memory:\n\n```php\nuse Aurynx\\HttpCompression\\ValueObjects\\OutputConfig;\n\n$result = CompressorFacade::make()\n    -\u003eaddFile('large-file.json')  // 500MB file\n    -\u003ewithDefaultConfig(ItemConfig::create()-\u003ewithGzip(6)-\u003ebuild())\n    -\u003einMemory(maxBytes: 100_000_000)  // 100MB limit\n    -\u003ecompress();\n\n// Stream compressed data\n$result-\u003efirst()-\u003eread(AlgorithmEnum::Gzip, function (string $chunk) {\n    echo $chunk;  // Process in chunks\n});\n```\n\n---\n\n### 🔌 Callback Streaming (no resources)\n\nStream compressed data directly into callbacks without dealing with stream resources.\n\nSingle algorithm (sendToCallback):\n\n```php\nuse Aurynx\\HttpCompression\\CompressorFacade;\n\n$buffer = '';\nCompressorFacade::once()\n    -\u003edata(str_repeat('hello ', 5000))\n    -\u003ewithGzip(6)\n    -\u003esendToCallback(function (string $chunk) use (\u0026$buffer): void {\n        $buffer .= $chunk; // write to socket, PSR-7 body, etc.\n    });\n```\n\nMultiple algorithms (sendAllToCallbacks):\n\n```php\nuse Aurynx\\HttpCompression\\CompressorFacade;\n\n$gz = '';\nCompressorFacade::once()\n    -\u003edata('payload')\n    -\u003ewithGzip(6)      // required by default\n    -\u003etryBrotli(4)     // optional\n    -\u003esendAllToCallbacks([\n        'gzip' =\u003e static function (string $chunk) use (\u0026$gz): void {\n            $gz .= $chunk;\n        },\n        // 'br' may be omitted when added via tryBrotli()\n    ]);\n```\n\nSee more patterns and caveats in Advanced Usage:\n- Callback streaming (single/multi)\n- Low-level WritableStream wrapper\n\n👉 Read: ./docs/advanced-usage.md\n\n---\n\n### 🛡️ Fail-Fast Validation\n\nErrors are caught at configuration time, not during compression:\n\n```php\n// ❌ Throws immediately (invalid level)\nAlgorithmSet::gzip(99);  // InvalidArgumentException: Level must be between 1 and 9\n\n// ❌ Throws immediately (multiple algorithms for saveTo)\nCompressorFacade::once()\n    -\u003efile('test.txt')\n    -\u003ewithGzip(9)\n    -\u003ewithBrotli(11)  // Multiple algorithms\n    -\u003esaveTo('test.gz');  // CompressionException: saveTo() requires exactly one algorithm\n```\n\n---\n\n### 📈 Rich Result Objects\n\nDetailed statistics and easy access:\n\n```php\n$result = CompressorFacade::make()\n    -\u003eaddGlob('*.html')\n    -\u003ewithDefaultConfig(ItemConfig::create()-\u003ewithGzip(9)-\u003ewithBrotli(11)-\u003ebuild())\n    -\u003einMemory()\n    -\u003ecompress();\n\n// Access results\nforeach ($result as $id =\u003e $item) {\n    if ($item-\u003eisOk()) {\n        echo \"Original: {$item-\u003eoriginalSize} bytes\\n\";\n        echo \"Gzip: {$item-\u003ecompressedSizes['gzip']} bytes\\n\";\n        echo \"Brotli: {$item-\u003ecompressedSizes['brotli']} bytes\\n\";\n    }\n}\n\n// Aggregated statistics\n$summary = $result-\u003esummary();\necho \"Median compression ratio (gzip): \" . $summary-\u003egetMedianRatio(AlgorithmEnum::Gzip) . \"\\n\";\necho \"P95 compression time (brotli): \" . $summary-\u003egetP95TimeMs(AlgorithmEnum::Brotli) . \" ms\\n\";\n```\n\n---\n\n## Use Cases\n\n### 1. Static Site Pre-Compression\n\nCompress assets during build for nginx `gzip_static`:\n\n```php\nuse Aurynx\\HttpCompression\\CompressorFacade;\nuse Aurynx\\HttpCompression\\ValueObjects\\ItemConfig;\n\n// Build script\n$result = CompressorFacade::make()\n    -\u003eaddGlob('dist/**/*.{html,css,js,svg,json}')\n    -\u003ewithDefaultConfig(\n        ItemConfig::create()\n            -\u003ewithGzip(9)\n            -\u003ewithBrotli(11)\n            -\u003ebuild()\n    )\n    -\u003eskipAlreadyCompressed()\n    -\u003etoDir('./dist', keepStructure: true)\n    -\u003ecompress();\n\nif (!$result-\u003eallOk()) {\n    foreach ($result-\u003efailures() as $id =\u003e $failure) {\n        echo \"Failed: {$id} - {$failure-\u003egetFailureReason()?-\u003egetMessage()}\\n\";\n    }\n    exit(1);\n}\n\necho \"✓ Compressed {$result-\u003ecount()} files\\n\";\n```\n\n**Nginx configuration:**\n```nginx\ngzip_static on;\nbrotli_static on;\n```\n\n---\n\n### 2. Dynamic HTTP Response Compression\n\nCompress content on-the-fly with caching:\n\n```php\nuse Aurynx\\HttpCompression\\CompressorFacade;\nuse Aurynx\\HttpCompression\\AlgorithmEnum;\n\nfunction compressResponse(string $content, string $acceptEncoding): string\n{\n    $cacheKey = 'compressed_' . md5($content) . '_' . $acceptEncoding;\n    \n    if ($cached = apcu_fetch($cacheKey)) {\n        return $cached;\n    }\n    \n    $algo = str_contains($acceptEncoding, 'br') ? AlgorithmEnum::Brotli : AlgorithmEnum::Gzip;\n    \n    $result = CompressorFacade::once()\n        -\u003edata($content)\n        -\u003ewithAlgorithm($algo, $algo-\u003egetDefaultLevel())\n        -\u003ecompress();\n    \n    $compressed = $result-\u003egetData($algo);\n    apcu_store($cacheKey, $compressed, 3600);\n    \n    return $compressed;\n}\n\n// In your controller\n$html = view('welcome')-\u003erender();\n$acceptEncoding = $_SERVER['HTTP_ACCEPT_ENCODING'] ?? '';\n\nif (str_contains($acceptEncoding, 'br') || str_contains($acceptEncoding, 'gzip')) {\n    $compressed = compressResponse($html, $acceptEncoding);\n    header('Content-Encoding: ' . (str_contains($acceptEncoding, 'br') ? 'br' : 'gzip'));\n    echo $compressed;\n} else {\n    echo $html;\n}\n```\n\n---\n\n### 3. API Response Compression\n\nCompress JSON API responses:\n\n```php\nuse Aurynx\\HttpCompression\\CompressorFacade;\nuse Aurynx\\HttpCompression\\AlgorithmEnum;\n\nfunction compressApiResponse(array $data, string $acceptEncoding): string\n{\n    $json = json_encode($data);\n    \n    if (!str_contains($acceptEncoding, 'gzip')) {\n        return $json;\n    }\n    \n    $result = CompressorFacade::once()\n        -\u003edata($json)\n        -\u003ewithGzip(6)  // Lower level for speed\n        -\u003ecompress();\n    \n    header('Content-Encoding: gzip');\n    header('Vary: Accept-Encoding');\n    \n    return $result-\u003egetData(AlgorithmEnum::Gzip);\n}\n\n// Usage\n$data = ['users' =\u003e User::all()];\necho compressApiResponse($data, $_SERVER['HTTP_ACCEPT_ENCODING'] ?? '');\n```\n\n---\n\n### 4. Log File Archival\n\nCompress and archive old log files:\n\n```php\nuse Aurynx\\HttpCompression\\CompressorFacade;\nuse Aurynx\\HttpCompression\\ValueObjects\\ItemConfig;\n\n// Daily cron job\n$result = CompressorFacade::make()\n    -\u003eaddGlob('storage/logs/*.log')\n    -\u003ewithDefaultConfig(ItemConfig::create()-\u003ewithZstd(19)-\u003ebuild())  // Maximum compression\n    -\u003etoDir('storage/logs/archive', keepStructure: false)\n    -\u003ecompress();\n\n// Delete originals\nforeach ($result-\u003esuccesses() as $id =\u003e $item) {\n    $originalPath = \"storage/logs/{$id}\";\n    if (file_exists($originalPath)) {\n        unlink($originalPath);\n    }\n}\n\necho \"Archived {$result-\u003ecount()} log files\\n\";\n```\n\n---\n\n### 5. Asset Pipeline Integration\n\nIntegrate with your build tools:\n\n```php\nuse Aurynx\\HttpCompression\\CompressorFacade;\nuse Aurynx\\HttpCompression\\ValueObjects\\ItemConfig;\n\nclass AssetCompiler\n{\n    public function compile(): void\n    {\n        // Step 1: Bundle and minify (webpack, vite, etc.)\n        system('npm run build');\n        \n        // Step 2: Compress for production\n        $result = CompressorFacade::make()\n            -\u003eaddGlob('public/build/**/*.{js,css}')\n            -\u003eaddGlob('public/build/**/*.{svg,json}')\n            -\u003ewithDefaultConfig(\n                ItemConfig::create()\n                    -\u003ewithGzip(9)\n                    -\u003ewithBrotli(11)\n                    -\u003ebuild()\n            )\n            -\u003eskipExtensions(['woff2', 'png', 'jpg'])\n            -\u003etoDir('public/build', keepStructure: true)\n            -\u003efailFast(true)\n            -\u003ecompress();\n        \n        if (!$result-\u003eallOk()) {\n            throw new \\RuntimeException('Asset compression failed');\n        }\n        \n        $summary = $result-\u003esummary();\n        $avgRatio = $summary-\u003egetAverageRatio(AlgorithmEnum::Gzip);\n        echo \"✓ Compressed {$result-\u003ecount()} assets (avg ratio: \" . round($avgRatio * 100, 1) . \"%)\\n\";\n    }\n}\n```\n\n---\n\n## API Reference\n\n### Facades\n\n#### `CompressorFacade::make()` — Batch Compression\n\n```php\nuse Aurynx\\HttpCompression\\CompressorFacade;\n\n$result = CompressorFacade::make()\n    // Add inputs\n    -\u003eadd(CompressionInput $input, ?ItemConfig $config = null)\n    -\u003eaddMany(iterable $inputs)\n    -\u003eaddFile(string $path, ?ItemConfig $config = null, ?string $id = null)\n    -\u003eaddData(string $data, ?ItemConfig $config = null, ?string $id = null)\n    -\u003eaddGlob(string $pattern, ?ItemConfig $config = null)\n    -\u003eaddFrom(InputProviderInterface $provider, ?ItemConfig $config = null)\n    \n    // Configuration\n    -\u003ewithDefaultConfig(ItemConfig $config)\n    \n    // Output\n    -\u003etoDir(string $dir, bool $keepStructure = false)\n    -\u003einMemory(int $maxBytes = 5_000_000)\n    \n    // Options\n    -\u003efailFast(bool $enable = true)\n    -\u003eskipExtensions(array $extensions)\n    -\u003eskipAlreadyCompressed()\n    \n    // Execute\n    -\u003ecompress(): CompressionResult;\n```\n\n#### `CompressorFacade::once()` — Single Item\n\n```php\nuse Aurynx\\HttpCompression\\CompressorFacade;\n\nCompressorFacade::once()\n    // Input\n    -\u003efile(string $path)\n    -\u003edata(string $data)\n    \n    // Algorithm (choose ONE)\n    -\u003ewithGzip(int $level = 6)\n    -\u003ewithBrotli(int $level = 11)\n    -\u003ewithZstd(int $level = 3)\n    \n    // Execute\n    -\u003ecompress(): CompressionItemResult\n    -\u003esaveTo(string $path): void;  // Requires exactly one algorithm\n```\n\n---\n\n## Notes on Saving Files\n\n- saveTo(path):\n  - Atomic write (tmp + rename) to the target path\n  - Existing target is replaced (OverwritePolicy=Replace)\n  - The target directory must already exist (no auto-create)\n\n- saveAllTo(directory, basename, options):\n  - basename must be a plain filename (no '/' or '\\\\', not '.' or '..')\n  - Options:\n    - overwritePolicy: fail|replace|skip (default fail)\n    - atomicAll: bool (default true) — all-or-nothing semantics\n    - allowCreateDirs: bool (default true)\n    - permissions: int|null — chmod after successful rename\n\n---\n\n### Configuration\n\n#### `ItemConfig` — Compression Configuration\n\n```php\nuse Aurynx\\HttpCompression\\ValueObjects\\ItemConfig;\nuse Aurynx\\HttpCompression\\ValueObjects\\AlgorithmSet;\n\n// Using builder\n$config = ItemConfig::create()\n    -\u003ewithGzip(9)\n    -\u003ewithBrotli(11)\n    -\u003ewithZstd(3)\n    -\u003elimitBytes(5_000_000)\n    -\u003ebuild();\n\n// Direct instantiation\n$config = new ItemConfig(\n    algorithms: AlgorithmSet::gzip(9),\n    maxBytes: 1_000_000\n);\n\n// Static factories\n$config = ItemConfig::gzip(9);\n$config = ItemConfig::brotli(11);\n$config = ItemConfig::zstd(3);\n```\n\n#### `AlgorithmSet` — Algorithm Configuration\n\n```php\nuse Aurynx\\HttpCompression\\ValueObjects\\AlgorithmSet;\nuse Aurynx\\HttpCompression\\Enums\\AlgorithmEnum;\n\n// Static factories\n$set = AlgorithmSet::gzip(9);\n$set = AlgorithmSet::brotli(11);\n$set = AlgorithmSet::zstd(3);\n$set = AlgorithmSet::fromDefaults();  // All algorithms with default levels\n\n// Manual construction from pairs\n$set = AlgorithmSet::from([\n    [AlgorithmEnum::Gzip, 9],\n    [AlgorithmEnum::Brotli, 11],\n]);\n```\n\n---\n\n### Results\n\n#### `CompressionResult` — Batch Results\n\n```php\n$result = CompressorFacade::make()-\u003ecompress();\n\n// Access\n$result-\u003eget(string $id): CompressionItemResult\n$result-\u003efirst(): CompressionItemResult\n$result-\u003etoArray(): array\n\n// Filtering\n$result-\u003esuccesses(): array\n$result-\u003efailures(): array\n$result-\u003eallOk(): bool\n\n// Statistics\n$result-\u003esummary(): CompressionSummaryResult\n$result-\u003ecount(): int\n\n// Iteration\nforeach ($result as $id =\u003e $item) {\n    // Process each item\n}\n```\n\n#### `CompressionItemResult` — Single Item Result\n\n```php\n$item = $result-\u003efirst();\n\n// Status\n$item-\u003eisOk(): bool\n$item-\u003esuccess: bool\n$item-\u003eoriginalSize: int\n\n// Data access\n$item-\u003egetData(AlgorithmEnum $algo): string\n$item-\u003egetStream(AlgorithmEnum $algo): resource\n$item-\u003eread(AlgorithmEnum $algo, callable $consumer): void\n\n// Metadata\n$item-\u003ehas(AlgorithmEnum $algo): bool\n$item-\u003ecompressedSizes: array\u003cstring, int\u003e\n$item-\u003ecompressionTimes: array\u003cstring, float\u003e\n$item-\u003eerrors: array\u003cstring, \\Throwable\u003e\n$item-\u003egetFailureReason(): ?\\Throwable\n```\n\n#### `CompressionSummaryResult` — Aggregated Statistics\n\n```php\n$summary = $result-\u003esummary();\n\n// Compression ratios (compressed / original)\n$summary-\u003egetAverageRatio(AlgorithmEnum $algo): float\n$summary-\u003egetMedianRatio(AlgorithmEnum $algo): float  // p50\n$summary-\u003egetP95Ratio(AlgorithmEnum $algo): float\n\n// Timing (milliseconds)\n$summary-\u003egetMedianTimeMs(AlgorithmEnum $algo): float  // p50\n$summary-\u003egetP95TimeMs(AlgorithmEnum $algo): float\n$summary-\u003egetTotalTimeMs(AlgorithmEnum $algo): float\n\n// Counts\n$summary-\u003egetTotalItems(): int\n$summary-\u003egetSuccessCount(): int\n$summary-\u003egetFailureCount(): int\n```\n\n---\n\n\n## For AI Assistants\n\nThis library is designed to be AI-friendly with:\n\n- ✅ **Native types** — no docblock parsing needed\n- ✅ **Explicit naming** — `CompressionResult`, `AlgorithmEnum`, etc.\n- ✅ **Fluent API** — easy to chain methods\n- ✅ **Fail-fast** — errors are obvious and immediate\n- ✅ **Immutable value objects** — no side effects\n\nFor a deeper, agent-focused walkthrough, see the AI Guide: [AI_GUIDE.md](./docs/AI_GUIDE.md). You can also use the machine-readable schema [`docs/ai-manifest.json`](docs/ai-manifest.json).\n\n### Common Patterns\n\n```php\n// Quick compression\nCompressorFacade::once()-\u003efile('test.txt')-\u003ewithGzip(9)-\u003esaveTo('test.txt.gz');\n\n// Batch with glob\nCompressorFacade::make()\n    -\u003eaddGlob('*.html')\n    -\u003ewithDefaultConfig(ItemConfig::create()-\u003ewithGzip(9)-\u003ebuild())\n    -\u003etoDir('./out')\n    -\u003ecompress();\n\n// Multiple algorithms\n$config = ItemConfig::create()\n    -\u003ewithGzip(9)\n    -\u003ewithBrotli(11)\n    -\u003ewithZstd(3)\n    -\u003ebuild();\n```\n\n### Avoid These Mistakes\n\n❌ Multiple algorithms with `saveTo()`:\n```php\n// WRONG - saveTo() requires exactly one algorithm\nCompressorFacade::once()-\u003efile('x')-\u003ewithGzip()-\u003ewithBrotli()-\u003esaveTo('x.gz');\n```\n\n✅ Use `compress()` instead:\n```php\n$result = CompressorFacade::once()-\u003efile('x')-\u003ewithGzip()-\u003ewithBrotli()-\u003ecompress();\n$result-\u003egetData(AlgorithmEnum::Gzip);\n```\n\n---\n\n## Contributing\n\nContributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details.\n\n### Development Setup\n\n```bash\n# Install dependencies\ncomposer install\n\n# Run tests\ncomposer test\n\n# Run PHPStan\ncomposer phpstan\n\n# Run CS Fixer\ncomposer cs-fix\n```\n\n---\n\n## License\n\nThe MIT License (MIT). Please see [License File](LICENSE) for more information.\n\n---\n\n## Credits\n\nCreated and maintained by [Anton Semenov](mailto:anton.a.semenov@proton.me).\n\n---\n\n\u003cp align=\"center\"\u003eCrafted by Aurynx 🔮\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faurynx%2Fhttp-compression","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faurynx%2Fhttp-compression","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faurynx%2Fhttp-compression/lists"}