Quick Links
JSON is one of the most commonly used data serialization formats. It's grown from its origins within JavaScript (JSON means JavaScript Object Notation) to become the format of choice for many web APIs and configuration systems.
PHP ships with built-in JSON support. Historically, the features were provided as a separate extension. The 2020 launch of PHP 8.0 converted JSON into a permanently active extension which cannot be removed.
Reading JSON Data
json_decode()
function. Its full signature is as follows:
json_decode(string $json, bool|null $associative=null, int $depth=512, int $flags=0) : mixed;
The simplest invocation is to pass a JSON string with no other arguments. We'll work with
{"foo": "bar"}
which decodes to an instance of PHP's generic
stdClass
. Our new object will have a property
foo
with the value of
bar
.
If you'd rather receive an associative array, pass
true
to the
$associative
parameter of
json_decode()
. The above example would then decode to
["foo" => "bar"]
.
The
$depth
parameter lets you control the maximum nesting level to parse down to. You'll get
null
if the JSON nests deeper than the set level - no attempt will be made to parse the data.
The
$flags
parameter accepts a bitmask of optional flags that alter the parsing behaviour. These are described in detail within the PHP manual and allow you to define how specific data types should be handled.
Handling Parsing Errors
By default,
json_decode()
will return
null
when it's passed an invalid JSON string. This presents an issue because an isolated
null
is a valid JSON string. You've no way of knowing whether the
null
return value is due to the JSON containing
null
, or because the JSON was malformed and couldn't be parsed.
PHP 7.3 finally addressed this longstanding problem by adding the
JSON_THROW_ON_ERROR
flag. Pass this constant to the
$flags
parameter of
json_decode()
to make PHP throw an exception when invalid JSON is encountered:
json_decode("['malformed json", true, 512, JSON_THROW_ON_ERROR);
Enabling this behaviour is generally desirable but it does make for a rather unwieldy invocation. You can use PHP 8's named arguments to simplify things a bit, as you won't need to pass explicit values for the optional parameters:
json_decode(json: "['malformed json", flags: JSON_THROW_ON_ERROR);
The two examples shown above are equivalent to each other. The only difference is the latter syntax requires PHP 8.
Serializing data to JSON
You can serialize PHP values into JSON strings using the
<a href="https://www.php.net/manual/en/function.json-encode.php" target="_blank" rel="noopener">json_encode()</a>
function. Its signature is as follows:
json_encode(mixed $value, int $flags=0, int $depth=512) : string|false;
PHP accepts any value as
$value
, except for resources. Data types are automatically handled to ensure they have an appropriate mapping in the generated JSON. PHP objects and associative arrays will become JSON objects containing all the enumerable property/key-value pairs of the input value. PHP scalar types map directly into JSON with no transformation.
Like its decoding counterpart,
json_encode()
accepts
$flags
and
$depth
parameters. Take care around the order though - in a quirk of the PHP standard library, the position of these two optional parameters is switched compared to
json_decode()
.
Many more flags are supported when encoding data. Here's some to be aware of:
-
JSON_FORCE_OBJECT
[]
["foo" => "bar"]
-
JSON_PRETTY_PRINT
-
JSON_PRESERVE_ZERO_FRACTION
0.0
0
-
JSON_NUMERIC_CHECK
"234.5"
234.5
-
JSON_PARTIAL_OUTPUT_ON_ERROR
You can obtain the full list of flags within the PHP documentation. The remainder are mostly specific options to customise the encoding used in particular scenarios.
Bringing JSON to Your Application's Domain Layer
Complex web backends are most likely to use PHP's JSON features within the context of emitting HTTP responses. That probably means you'll be encoding instances of domain-layer classes, such as
BlogPost
.
Calling
json_encode()
with an instance of a class results in a JSON string containing a single object. PHP will enumerate the public properties of the class and add them as key/value pairs into the JSON-encoded object.
class BlogPost {protected int $Id = 1;
public string $Title = "Example";
}
// produces '{"Title": "Example"}'
$json = json_encode(new BlogPost());
PHP can't automatically access protected or private properties. This default implementation is therefore insufficient for all but the simplest of classes. Instead, your classes can implement the
JsonSerializable
interface. This allows you to define how instances will be transformed to JSON.
JsonSerializable
defines a single method,
jsonSerialize()
, which PHP will call whenever an instance needs to be serialised to JSON:
class BlogPost implements JsonSerializable {protected int $Id = 1;
public string $Title = "Example";
public function jsonSerialize() {
return [
"Id" => $this -> Id,
"Title" => $this -> Title
];
}
}
// produces '{"Id": 1, "Title": "Example"}'
$json = json_encode(new BlogPost());
Implementing
JsonSerializable
allows you to take control of the serialization process. Your instances can choose what to expose, which might include protected properties, dynamically computed values and data returned from method calls.
You don't even need to return an array. The
jsonSerializable
method signature specifies a
mixed
return type, so you can provide any value which PHP can serialize to JSON. You could return another object to have PHP serialize it recursively.