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

Lz String Php Laravel Package

nullpunkt/lz-string-php

LZ-string compression and decompression for PHP, letting you store and share compressed strings in URLs or localStorage with full compatibility with the JavaScript lz-string library.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package and confirm the class that your tagged release exposes. Current releases ship a static class at Nullpunkt\LZString\LZString; older tags may expose a global LZString or require an instance, but the method names are the same.

composer require nullpunkt/lz-string-php
use Nullpunkt\LZString\LZString;

$compressed   = LZString::compressToBase64('Hello world');
$decompressed = LZString::decompressFromBase64($compressed);

var_dump($compressed, $decompressed);

Look first at:

  1. vendor/nullpunkt/lz-string-php/src/LZString.php — confirm the namespace, whether methods are static, and which compress*/decompress* variants exist.
  2. The README’s compatibility notes with the JavaScript lz-string package.
  3. Any tests/ directory — fixture outputs are the fastest way to prove PHP and JS agree.

The first real use case is almost always browser ↔ server interoperability: the frontend uses lz-string to shrink JSON before putting it in localStorage, a URL fragment, or a request body, and Laravel needs to read it back. Start with one round-trip fixture generated by Node:

// node
const LZString = require('lz-string');
console.log(LZString.compressToBase64('Hello world'));

Then assert the exact same string in a PHPUnit test before building anything on top of it.

Implementation Patterns

Decompress an incoming frontend payload

Use a small middleware or a FormRequest preparer when the browser sends a compressed blob instead of JSON.

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Nullpunkt\LZString\LZString;

class DecompressLzStringBody
{
    public function handle(Request $request, Closure $next)
    {
        $raw = $request->getContent();

        if ($raw !== '' && ! str_starts_with($raw, '{')) {
            $json = LZString::decompressFromBase64($raw);
            $data = is_string($json) ? json_decode($json, true) : null;

            if (is_array($data)) {
                $request->replace($data);
            }
        }

        return $next($request);
    }
}

Register it only on the routes that need it; do not run decompression on every request.

Compress an API response

Useful for large, repetitive JSON payloads where the client already has lz-string.

$json = $payload->toJson();

return response(LZString::compressToBase64($json))
    ->header('Content-Type', 'text/plain; charset=UTF-8')
    ->header('X-Content-Encoding', 'lz-string-base64');
const res = await fetch('/api/filters');
const data = JSON.parse(LZString.decompressFromBase64(await res.text()));

Be explicit about the encoding with a custom header; do not pretend it is ordinary JSON.

Eloquent cast for compressed JSON columns

A cast keeps controllers clean and makes the compression format an implementation detail.

namespace App\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Nullpunkt\LZString\LZString;

class LzStringJson implements CastsAttributes
{
    public function get($model, string $key, $value, array $attributes)
    {
        if ($value === null || $value === '') {
            return null;
        }

        $json = LZString::decompressFromBase64($value);

        return is_string($json) ? json_decode($json, true) : null;
    }

    public function set($model, string $key, $value, array $attributes)
    {
        return [
            $key => $value === null
                ? null
                : LZString::compressToBase64(json_encode($value)),
        ];
    }
}

Store the Base64 form in TEXT/LONGTEXT, not the raw binary/UTF-16 form.

URL and fragment state

For shareable UI state (filters, dashboard layout), use the URI-safe codec and still encode it when embedding in a query string:

$state = LZString::compressToEncodedURIComponent(json_encode($filters));
$url   = '/dashboard?state=' . rawurlencode($state);
$filters = json_decode(
    LZString::decompressFromEncodedURIComponent($request->query('state')),
    true
);

A URL fragment (#state=...) avoids sending the payload to the server entirely and is a good fit for purely client-side state.

Wrap it in a service

Because this is a static utility with no Laravel service provider, wrap it for testability and to isolate the dependency:

namespace App\Support;

use Nullpunkt\LZString\LZString;

class Compressor
{
    public function compress(string $value): string
    {
        return LZString::compressToBase64($value);
    }

    public function decompress(string $value): ?string
    {
        $result = LZString::decompressFromBase64($value);

        return is_string($result) ? $result : null;
    }
}

This lets you mock compression in tests and swap implementations later without touching call sites.

Fixture-based compatibility tests

Treat the JS library as the source of truth. Generate fixtures once, commit them, and test both directions:

public function test_matches_javascript_fixtures(): void
{
    $this->assertSame('BYUwNmD2AEDukCcoA', LZString::compressToBase64('Hello world'));
    $this->assertSame('Hello world', LZString::decompressFromBase64('BYUwNmD2AEDukCcoA'));
}

Include at least one non-ASCII fixture and one emoji fixture.

Gotchas and Tips

  • Base64 is not URL-safe. compressToBase64() can emit +, /, and =. In a query string, + is interpreted as a space. Use compressToEncodedURIComponent() for URLs and rawurlencode() the result anyway.
  • UTF-16 vs UTF-8 is the biggest cross-language trap. The JS library works on UTF-16 code units; PHP strings are byte arrays. Depending on the port version, compressToUTF16() may produce bytes that are not valid UTF-8 (including null bytes), which will corrupt in MySQL utf8mb4 columns, cookies, or log lines. Prefer compressToBase64() for anything persisted or transmitted; use compressToUTF16() only when you specifically need to match the browser’s localStorage representation.
  • Test non-ASCII explicitly. Emoji and accented characters are where byte-oriented ports and JS’s UTF-16 implementation diverge. A fixture test with "héllo 😀" catches this immediately.
  • Decompression failures are not always exceptions. Corrupt or truncated input may return null, false, an empty string, or emit PHP warnings for undefined offsets. Always validate the decoded result and json_decode() return value before trusting it. Wrap calls in try/catch for PHP 8 warning-to-exception configurations.
  • Treat compressed input as untrusted. A small compressed payload can expand significantly. Enforce request size limits (post_max_size, middleware), cap decompressed length, and validate the decoded JSON before use. Do not decompress arbitrary user input in a loop.
  • Do not double-compress. If your web server or Laravel already applies gzip/brotli, compressing with LZ-String first usually makes the response larger and defeats HTTP compression. Use LZ-String when the client needs to store or handle the compressed form, not simply to shrink a normal HTTP response.
  • Performance is pure PHP. For large payloads this is much slower than gzcompress()/gzdecode(). Benchmark with realistic data; if there is no JavaScript interoperability requirement, native zlib is the better default.
  • Empty and null inputs. Compression of an empty string typically returns an empty string, and decompression of an empty string may return null or '' depending on version. Normalize both cases in your wrapper rather than scattering null checks.
  • Version compatibility. The last release is from 2021. Pin the version in composer.json, check the package’s PHP constraint, and run your test suite on your target PHP version (especially PHP 8.1+). Add a fixture test so a future composer update cannot silently change output.
  • No service provider, no config, no facade. This is a plain utility class. Do not expect Laravel auto-discovery beyond PSR-4 autoloading; create your own binding or wrapper if you want dependency injection.
  • Watch the JS side too. Pin lz-string in package.json and keep it in sync with the PHP port. The compressed format is stable, but a major frontend upgrade is a good moment to rerun the fixture tests.
  • Extension points are limited. If you need streaming, chunking, or a different backend, wrap the package behind an interface rather than calling LZString:: directly throughout the codebase.
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.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums