inkrot/php-compress-json
A lightweight PHP library that compresses and minifies JSON by stripping whitespace and redundant formatting, shrinking payloads for APIs, storage, and caching. Simple to install, framework-agnostic, and built to reduce JSON size without altering the underlying data.
1. Install and read the source (10 minutes). It's a single-purpose, low-star package, so vendor/inkrot/php-compress-json/src/ is almost certainly a handful of classes — read all of it before writing a line of application code. Confirm the exact namespace and whether the entry point is a static facade (CompressJson::compress()/decompress()) or an instantiable Compressor. Everything below assumes that shape; adjust names to match.
composer require inkrot/php-compress-json
2. Sanity-check it against your real data. Don't benchmark with {"a":1}. Pull one row from the table you intend to compress and measure:
$raw = json_encode($model->payload);
$packed = CompressJson::compress($model->payload);
printf("raw=%d packed=%d ratio=%.2f\n", strlen($raw), strlen($packed), strlen($packed) / strlen($raw));
If the ratio isn't below ~0.7 on your actual payloads, stop — the integration cost isn't worth it.
3. First use case: one fat json/longtext column. The classic fit is webhook payloads, third-party API snapshots, audit diffs, or settings blobs where rows are large, write-once, read-rarely, and never queried by their contents. Start with exactly one model and one column.
4. Check for a config/publish step. If there's no config/ directory, there's nothing to publish — don't go looking for php artisan vendor:publish.
Eloquent cast — the default pattern. Wrap the package in a CastsAttributes so the rest of the app sees plain PHP arrays and never touches the compression API. This is the only place the package should be referenced.
class CompressedJson implements CastsAttributes
{
public function get($model, $key, $value, $attributes): ?array
{
return $value === null ? null : CompressJson::decompress($value);
}
public function set($model, $key, $value, $attributes): ?string
{
return $value === null ? null : CompressJson::compress($value);
}
}
protected $casts = ['payload' => CompressedJson::class];
Version the payload before you need to. The moment you change compression level or swap libraries, old rows become unreadable. Prefix with a scheme byte and branch on read:
return match ($value[0] ?? null) {
'1' => CompressJson::decompress(substr($value, 1)),
'{', '[' => json_decode($value, true), // legacy plain JSON
default => throw new RuntimeException('Unknown payload encoding'),
};
Backfill with a queued, chunked command. Add the new column alongside the old one, dual-read ($model->payload_compressed ?? $model->payload), backfill via chunkById(500) dispatched as a job, then drop the old column in a later deploy. Never do a single UPDATE over the whole table on MySQL — it'll lock and blow up the binlog.
Keep the filterable fields out. Extract anything you'd ever where() on into real, indexed columns at write time. Compression is for the long tail of the payload, not for its queryable head.
$model->fill([
'event' => $payload['type'], // indexed, queryable
'occurred_at' => $payload['created'], // indexed
'payload' => $payload, // compressed blob
]);
Queue and cache payloads. The same call is useful outside the DB: compressing a large array before dispatch() keeps your jobs table and Redis memory down, and Cache::put('snapshot:'.$id, CompressJson::compress($data), 3600) meaningfully reduces Redis footprint for large values.
{/[ sniff from above) when compression would lose.TEXT column with a utf8 collation — MySQL will mangle it. Use LONGBLOB/BLOB and a binary cast, or force the package into base64 output if it supports it. Pick one and be consistent per column.CompressJson::compress(json_encode($array)) is a common mistake — pass arrays if the package accepts them, and assert in tests that decompress round-trips to an identical array, not a JSON string.whereJsonContains, ->path, generated columns over JSON paths, and MySQL JSON functions are all gone on this column. If you need any of that, this is the wrong tool for that column.strlen($raw), strlen($packed), and the first 16 bytes of the column (bin2hex(substr($value,0,16))) — that instantly tells you base64 vs gzip (gzip starts 1f8b) vs plain JSON vs a truncated/corrupted row.gzcompress, you can drop the dependency and call gzcompress(json_encode($data), 6) directly. Keep the package behind your own JsonCompressor interface so swapping it out later is a one-file change — a 10-star, single-author, BSD-2 dependency shouldn't be called from 30 places.mysqldump is fine, but anyone doing SELECT in a SQL client sees noise. Document the encoding in the model or a README, and consider a small php artisan payload:decode {id} helper for support work.memory_limit during a report that loads many of them.How can I help you explore Laravel packages today?