Upgrade your PHP application by switching from serialize() to JSON for faster, safer, and more efficient data handling across different platforms.
Here’s the scenario that ends careers: you pull a value from a cookie, a form field, or a cache row an attacker can reach, and you hand it straight to unserialize(). If your codebase has the wrong class loaded, that one line becomes remote code execution. This is not theory. PHP object injection through unserialize() is a well-documented attack class, and it’s the real reason to reach for JSON.
So let’s be honest about what this switch actually buys you. It’s mostly about safety and interoperability, not speed. In this guide we’ll compare serialize() and JSON, show how to convert existing code, and be clear about what you give up when you switch. Because you do give up something, and anyone who tells you otherwise hasn’t hit the edge cases yet.
Why Move from serialize() to JSON?
serialize() works fine. It’s fast, it’s built in, and it round-trips PHP data with more fidelity than JSON does. The case for JSON isn’t that serialize() is broken. It’s that JSON is better suited to a few specific jobs:
- Security with untrusted data: This is the big one.
unserialize()can instantiate objects and trigger their magic methods (__wakeup(),__destruct()), which opens the door to object injection and, in the wrong codebase, remote code execution.json_decode()never instantiates arbitrary objects. It only produces arrays, scalars, and plainstdClass. If the data came from anywhere a user can touch, that difference matters more than anything else on this list. - Interoperability: JSON is understood by almost every language and platform.
serialize()output is PHP-only. If another system needs to read your data, JSON is the obvious choice. - Readability: JSON is human-readable. Serialized strings, with their length prefixes and type tags, are not fun to debug by eye.
- Frontend compatibility: JavaScript parses JSON natively, so passing data to the browser is one step, not two.
Notice what’s not on that list: performance. We’ll get to why below.
Basic Syntax: serialize() vs JSON
Before the advanced cases, let’s compare the two side by side. First, serializing an array with serialize().
Example: Using serialize() to Convert an Array to a String
<?php
$data = ['name' => 'John', 'age' => 30, 'email' => '[email protected]'];
$serialized_data = serialize( $data );
echo $serialized_data;Output:
a:3:{s:4:"name";s:4:"John";s:3:"age";i:30;s:5:"email";s:17:"[email protected]";}
Useful for storage, but not something you’d want to read at 2am during an incident. Now the same data as JSON.
Example: Using json_encode() to Convert an Array to a JSON String
<?php
$data = ['name' => 'John', 'age' => 30, 'email' => '[email protected]'];
$json_data = json_encode( $data );
echo $json_data;Output:
{"name":"John","age":30,"email":"[email protected]"}
Cleaner, and any JavaScript client can read it as-is.
Understanding the Differences Between serialize() and JSON
The core difference: serialize() is a PHP-native format that preserves PHP semantics, while JSON is a language-agnostic interchange format that flattens everything to arrays and scalars. That framing explains every trade-off below.
- Type fidelity:
serialize()preserves exact types, object classes, private and protected properties, and internal references between values. JSON keeps none of that. It gives you back arrays, strings, numbers, booleans, and null. Objects come back asstdClass(or associative arrays if you passtrue), with the original class and visibility gone. - What each can’t handle: Neither format can serialize a PHP resource (a file handle, a database connection). The PHP manual is explicit that
serialize()handles all types except the resource type and certain objects such as closures. JSON also can’t represent objects unless you convert them yourself. - Float precision:
serialize()round-trips floats exactly. JSON can lose precision on some floating-point values depending on yourserialize_precisionsetting, so don’t assume a decoded float equals the one you encoded. - Cross-language usability: JSON is readable almost everywhere.
serialize()is PHP-only. - Security: Covered above, and it’s the deciding factor for untrusted input.
unserialize()can instantiate objects;json_decode()cannot.
Converting Serialized Data to JSON
If you have a codebase using serialize() for plain arrays, the swap is mechanical. Here’s a file-storage example.
Example: Refactoring Serialized Data to JSON
Original Code Using serialize()
<?php
$data = ['name' => 'Alice', 'role' => 'admin'];
$serialized_data = serialize( $data );
// Store serialized data in a file
file_put_contents( 'data.txt', $serialized_data );
// Retrieve and unserialize the data
$retrieved_data = unserialize( file_get_contents( 'data.txt' ) );
print_r( $retrieved_data );Refactored Code Using json_encode() and json_decode()
<?php
$data = ['name' => 'Alice', 'role' => 'admin'];
$json_data = json_encode( $data );
// Store JSON data in a file
file_put_contents( 'data.json', $json_data );
// Retrieve and decode the JSON data
$retrieved_data = json_decode( file_get_contents( 'data.json' ), true );
print_r( $retrieved_data );
Passing true as the second argument to json_decode() gives you an associative array instead of a stdClass object, which usually maps cleaner onto code that expected the old unserialize() array. One caveat when migrating: existing files written by serialize() won’t be readable by json_decode(), so plan a migration path for data already on disk.
Handling Object Serialization
This is where the honesty matters. serialize() can persist a whole object, class and all, and bring it back intact. JSON can’t. If you’re storing objects, you have to decide how each one becomes plain data, and how it gets rebuilt. The simplest approach is a method that returns an array.
Example: Converting an Object to JSON
<?php
class User {
public $name;
public $email;
public function __construct( $name, $email ) {
$this->name = $name;
$this->email = $email;
}
// Convert the object to an array for JSON encoding
public function toArray() {
return get_object_vars( $this );
}
}
$user = new User( 'Bob', '[email protected]' );
$json_user = json_encode( $user->toArray() );
echo $json_user;
The toArray() method flattens the object’s properties into an array that json_encode() can handle. For cleaner control, implement the JsonSerializable interface so json_encode() knows how to handle your object directly. Either way, remember: decoding gives you data, not a User. You’re responsible for turning it back into one.
Performance Considerations
You’ll often read that JSON is faster. Sometimes it is. In many cases json_encode() and json_decode() do edge out serialize() and unserialize(), but the gap depends on your PHP version, your data shape, and your workload. It swings both ways, and it’s usually small enough that it shouldn’t drive the decision. Choose JSON for safety and interoperability, not because you’re chasing microseconds. If performance genuinely matters for your case, measure it on your own data.
Benchmarking serialize() vs JSON
<?php
$data = array_fill( 0, 10000, ['name' => 'John', 'age' => 30] );
// Benchmark serialize()
$start = microtime( true );
$serialized_data = serialize( $data );
$end = microtime( true );
echo 'serialize(): ' . ( $end – $start ) . ' seconds';
// Benchmark json_encode()
$start = microtime( true );
$json_data = json_encode( $data );
$end = microtime( true );
echo 'json_encode(): ' . ( $end – $start ) . ' seconds';Run this on your own hardware and PHP version before you draw conclusions. A single micro-benchmark on synthetic data won’t tell you how either function behaves on your real payloads.
Best Practices for Using JSON in PHP
- Fail loudly on bad data: Pass the
JSON_THROW_ON_ERRORflag (PHP 7.3+) so a malformed payload throws aJsonExceptioninstead of silently returning null. If you can’t use it, checkjson_last_error()after every encode and decode. - Pick a return shape and stick to it: Pass
truetojson_decode()for associative arrays, or leave it off for objects. Be consistent so callers know what they’re getting. - Never unserialize untrusted input: If the data is anything a user can influence, don’t feed it to
unserialize(). Usejson_decode(). If you must useunserialize()on semi-trusted data, pass['allowed_classes' => false]to block object instantiation. - Keep
serialize()for trusted internal state: If you control both ends, the data never leaves PHP, and you need object or type fidelity,serialize()is still the right tool. Switching to JSON there just costs you fidelity for no gain.
Conclusion
The clean rule: use JSON for anything untrusted or anything that crosses a language boundary, and keep serialize() for trusted, PHP-only data where you need to preserve object types and precise values. JSON’s headline win is security against object injection, with interoperability and readability close behind. Performance is a wash and shouldn’t decide it. Know what JSON drops (object classes, private properties, exact types, references) and convert your objects deliberately. Get those trade-offs right and the switch makes your code safer without any nasty surprises down the line.


