Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Compress Contracts Laravel Package

wyrihaximus/compress-contracts

PHP interfaces for compression. Defines small CompressorInterface and DecompressorInterface contracts so you can swap gzip, zlib, or custom implementations without coupling code to a specific library. Great for ReactPHP and other async projects needing clean, testable compression abstractions.

View on GitHub
Deep Wiki
Context7

Getting Started

wyrihaximus/compress-contracts is a tiny contract package: it ships a single marker-style compress interface (namespace convention is WyriHaximus\Compress, method conventionally compress(string $contents): string). It has no Laravel service provider, no config, no facade, and no runtime dependencies. All the actual algorithm work lives in separate implementation packages, so treat this repo as the seam you type-hint against, not as a library you call.

Steps:

  1. composer require wyrihaximus/compress-contracts and confirm the constraint that landed (composer show wyrihaximus/compress-contracts). Last release was 2020-12-24, so the PHP requirement in its composer.json may predate your runtime.
  2. Read src/ first. It is one or two files, and it is the only authoritative statement of the method name, signature, and whether the payload is a string or resource. Confirm the autoloaded namespace in composer.json before copying any snippet below.
  3. Pick an implementation. If wyrihaximus/compress does not fit, write your own two-line class over gzencode/gzdecode. The contract is small enough that owning the implementation is normal.
  4. Bind the interface once in a provider and inject it. Do not new a concrete class anywhere else.
// app/Providers/CompressServiceProvider.php
use WyriHaximus\Compress\CompressInterface;

public function register(): void
{
    $this->app->bind(CompressInterface::class, fn (): CompressInterface => new GzipCompress(level: 6));
}

First use case: type-hint the interface in whatever writes compressed blobs (cache values, queue payloads, archived log lines). Swapping the implementation in a test then costs one container binding instead of a code change.

Implementation Patterns

Constructor injection only. The interface is the dependency; the algorithm is configuration.

final class ArchiveWriter
{
    public function __construct(private CompressInterface $compress) {}

    public function write(string $path, string $payload): void
    {
        file_put_contents($path, $this->compress->compress($payload));
    }
}

Algorithm selection via a registry, not via conditionals. When you support more than one codec, keep a map of name to implementation and resolve from config. Store the chosen name next to the payload so reads are not guesses.

final class CompressorRegistry
{
    /** @param array<string, CompressInterface> $compressors */
    public function __construct(private array $compressors) {}

    public function for(?string $name): CompressInterface
    {
        return $this->compressors[$name ?? 'gzip']
            ?? throw new InvalidArgumentException("Unknown compressor [{$name}]");
    }
}

Bind the map in the provider from config('compress.driver'), and keep the default in config/compress.php rather than in code.

Decorators are the cheapest extension point. Because it is a one-method interface, wrapping is trivial and keeps cross-cutting policy out of the algorithm class.

  • SkipSmallPayloads: pass through below a byte threshold, since gzip on a 40-byte string makes it larger. Make sure the read side knows which branch was taken, otherwise you will try to gunzip a plain string.
  • NullCompress: a fake for tests and a safe default when the driver is set to none.
  • LoggingCompress: measure input/output size and log the ratio. This is where you will actually learn your compression is useless on JSON full of short keys.
  • ChainCompress: try candidates and keep the shortest result, storing the winner's name.

Round trips are your job. The contract is one-directional. Pair it with a decompress contract in the same application namespace, or define a Codec value object that owns both directions plus the algorithm identifier. Do not leak a bare CompressInterface into domain code that also needs to read the data back.

HTTP and storage boundaries. For response compression use your web server, not this package. Where this earns its keep: serialized cache payloads, job payloads, and archived files. Wrap the Laravel cache repository rather than every call site.

final class CompressingCacheRepository implements Repository
{
    public function __construct(private Repository $inner, private CompressInterface $compress) {}

    public function put($key, $value, $ttl = null): bool
    {
        return $this->inner->put($key, base64_encode($this->compress->compress(serialize($value))), $ttl);
    }
}

Tests: fakes and round-trip properties. Use NullCompress or a spy that records calls; assert that domain code delegates, not that gzip works. Then one focused test per implementation iterates representative payloads (empty string, 1 byte, 1 MB of random bytes, UTF-8 multibyte) and asserts decompress(compress($x)) === $x.

Gotchas and Tips

  • Verify the signature before writing code. This package has had one release and no activity since December 2020. There is no documentation site. src/ is the spec.
  • No exceptions in the contract. If compress() can fail, it will surface as false, an empty string, or a \Error from the underlying function depending on the implementation. Normalise at your implementation boundary into a domain exception; do not make callers guess.
  • Binary in JSON and URLs. Compressed output is raw bytes. Always base64_encode before it touches a JSON column, a cache key, or a URL, and budget the 33 percent expansion when comparing compressed size.
  • Determinism matters if you hash the output. gzip headers can carry metadata, and different zlib levels produce different bytes for the same input. If compressed payloads feed ETags, cache keys, or content addressing, pin the level and verify byte-for-byte stability across runs on your PHP version before relying on it.
  • Ambiguous empty output. Decide whether compress('') returns '' or a valid empty gzip stream, and test it. Silent ambiguity here turns into corrupt reads months later.
  • Threshold decorators need a marker. If SkipSmallPayloads returns the input unchanged, the reader cannot tell compressed from plain. Prefix with a one-byte flag, or store the algorithm alongside the payload. Do this at the storage boundary, never inside the codec.
  • Dependency risk is real. Two stars, zero dependents, no release since 2020. Pin the version exactly. If adding a dependency on a single interface feels wrong, define App\Support\Compress\CompressInterface with the same shape and alias the package interface to it, so the external type is confined to one binding line and can be dropped in an afternoon.
  • Contributing back is a different workflow. If you do patch it, the repo expects make install, then make and make contrib before returning. Tests extend WyriHaximus\TestUtilities\TestCase (or AsyncTestCase), require 100 percent coverage, 100 percent MSI via Infection, 100 percent type coverage, and dataproviders wherever possible. Use make unit-testing-filter for single tests, make composer-require "vendor/pkg" for new dependencies, and make help to discover targets. Never cd (the Makefile assumes repo root), never sudo, never docker directly.
  • Do not edit Makefile or AGENTS.md here. The instructions live in wyrihaximus/makefiles; propose changes there and pull them in with make install.
  • Where to look when stuck. Start with the interface, then the implementation package you bound, then composer show -t to see whether something else is already pulling a compress abstraction into the tree that you should reuse instead.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast