laminas/laminas-serializer
Laminas serializer library providing adapters for PHP serialize, JSON, igbinary, and more, plus a configurable factory for serializing and deserializing data in Laminas and Mezzio applications.
Installation:
composer require laminas/laminas-serializer:^4.0
There is still no first-party Laravel service provider — you wire the adapter yourself. Note that v4 no longer pulls in laminas/laminas-json; the Json adapter now uses PHP's native json_encode()/json_decode(). If your app calls Laminas\Json\* directly, require it explicitly.
Check your PHP constraint first:
composer why-not laminas/laminas-serializer 4.0.0
v4 raises the minimum PHP version and tightens return/parameter types across the serializers. If you are still on an older PHP runtime, stay on ^3.3 until you upgrade.
Basic Usage:
use Laminas\Serializer\Adapter\AdapterInterface;
use Laminas\Serializer\Adapter\PhpSerialize;
// app/Providers/AppServiceProvider.php
public function register(): void
{
$this->app->bind(AdapterInterface::class, fn () => new PhpSerialize());
}
$serializer = app(AdapterInterface::class);
$data = ['key' => 'value', 'nested' => ['foo' => 'bar']];
$serialized = $serializer->serialize($data); // returns string
$deserialized = $serializer->unserialize($serialized); // returns mixed
First Use Case:
Json/Xml adapters.Where to look first: the v4 docs under docs/book/v4/ — specifically the new migration guide added in PR #101, plus AdapterInterface, the adapter list, and AdapterPluginManager.
Adapter-Based Serialization:
AdapterInterface; pick the adapter per use case:
PhpSerialize — native serialize(), preserves PHP types.Json — native JSON, best for APIs and interop.Xml — XML strings.IgBinary — compact/fast binary, PHP-only.$json = new \Laminas\Serializer\Adapter\Json();
$serialized = $json->serialize(['data' => 123]);
Config-Driven Binding (single place to swap formats):
// config/services.php
'serializer' => [
'adapter' => env('SERIALIZER_ADAPTER', \Laminas\Serializer\Adapter\PhpSerialize::class),
],
$this->app->bind(AdapterInterface::class, function ($app) {
$class = config('services.serializer.adapter');
return $app->make($class);
});
Keep this binding as the only place that names a concrete adapter, so a later format switch is a config change.
Named Bindings for Mixed Formats:
$this->app->bind('serializer.cache', fn () => new IgBinary());
$this->app->bind('serializer.api', fn () => new Json());
Cache and transport formats have different constraints; don't force one adapter on both.
Custom Serialization:
use Laminas\Serializer\Adapter\AdapterInterface;
final class CustomAdapter implements AdapterInterface
{
public function serialize(mixed $value): string
{
return json_encode($value, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR);
}
public function unserialize(string $value): mixed
{
return json_decode($value, true, 512, JSON_THROW_ON_ERROR);
}
}
Check the exact v4 signatures in the installed source (vendor/laminas/laminas-serializer/src/Adapter/AdapterInterface.php) before writing one.
Error Handling:
use Laminas\Serializer\Exception\ExceptionInterface;
try {
$data = $serializer->unserialize($payload);
} catch (ExceptionInterface $e) {
report($e);
$data = [];
}
laminas-json is gone, failures that used to surface as Laminas\Json\Exception\* now come from Laminas\Serializer\Exception\*. Audit any catch blocks referencing Laminas\Json.Cache::put() and then forget Laravel serializes again on its own.
$serializer = app('serializer.cache');
Cache::put('report:42', $serializer->serialize($report), now()->addHour());
$report = $serializer->unserialize(Cache::get('report:42'));
Json adapter you already have a JSON string — return it raw to avoid double encoding:
return response($serializer->serialize($resource), 200, ['Content-Type' => 'application/json']);
(Don't wrap it in response()->json(), which encodes the string again.)->toArray(), API Resources) before serializing. PhpSerialize on a model drags the whole object graph — including loaded relations — into the payload.v4 removed Laminas\Serializer\Module (PR #88). If you're on a Laminas MVC app (or any app listing it in a modules config), remove 'Laminas\Serializer' from your modules list and check for a module.config.php / Module.php that no longer exists.
PhpSerialize's deprecated option was removed (PR #94). If AppServiceProvider still does new PhpSerialize([...]), drop the options array — in v4 that constructor option is gone.
laminas-json is no longer a dependency (PR #80). Consequences:
Json adapter delegates straight to native functions, so JSON-specific options previously forwarded to Laminas\Json\Json no longer apply.json_decode($x, true) semantics).json_encode() returns false for recursion/depth/UTF-8 problems instead of throwing the old Lamina exceptions — the adapter is expected to normalize this into Laminas\Serializer\Exception\RuntimeException, so always catch and assert on the result.Laminas\Json, add it to composer.json explicitly — it just silently disappeared from your dependency tree.Type safety is stricter (PR #88). Custom adapters, decorators, and mocks written against the v3 signatures will fail to compile. Fix the signatures, then re-run the suite; static analysis will usually point at every offender.
Type loss:
Json/Xml degrade DateTime, enums, and other objects to strings/scalars. Round-trip through DTOs with explicit casts instead of assuming identity.PhpSerialize preserves PHP types but is PHP-version-sensitive: serialized payloads from an older PHP/runtime may not deserialize cleanly after an upgrade. Treat cached payloads as disposable after a PHP bump.Security:
unserialize() on untrusted input is PHP object injection. Only use PhpSerialize behind trusted storage; for anything crossing a network boundary use Json and validate.mixed return of unserialize() before use — it can be null, false, or an unexpected shape.Performance:
IgBinary is faster and smaller than Json for large PHP payloads, but unreadable and PHP-only — fine for cache, wrong for APIs.memory_limit on big graphs. Chunk large sets and store per-chunk keys.dump(get_debug_type($out), $out) on the unserialized value first, then json_last_error_msg() (after Json) or unserialize()'s error path.false: JSON encode failures return false; a ''/'false' string in the cache is usually this. Add an explicit if ($serialized === false) { throw ... } at your integration boundary.get_class(app(AdapterInterface::class)) once at boot in non-production.Testing:
AdapterInterface in integration tests — a mock won't catch signature or format regressions. Use the real adapter and assert round-trip equality:$this->assertSame($data, $serializer->unserialize($serializer->serialize($data)));
null, [], nested empty arrays, floats/NaN, non-UTF-8 strings, circular references.Extending: a custom adapter is the right extension point for domain formats (Protobuf, MessagePack, encrypted blobs) — implement the v4 interface, register it with the same container binding, and keep the adapter name in config only.
Migration checklist for 4.0:
^4.0 and confirm PHP constraint.Laminas\Serializer from any modules config / delete stale Module.php imports.new PhpSerialize(...).Laminas\Json (both use statements and catch blocks) — add the dependency or switch to Laminas\Serializer\Exception.PhpSerialize payloads produced by v3/PHP-version-different nodes.AppServiceProvider.How can I help you explore Laravel packages today?