PHP Logo

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

To parse JSON data, use the 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 json_encode() 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 – Convert PHP numerical arrays to JSON objects instead of arrays. This handles the case where a variable contains an associative array which might be empty. When the array is empty ([]), a JSON array would be created; when it’s not empty (["foo" => "bar"]), an object would be emitted instead. Enabling this flag ensures an object is always used in the encoded JSON.
  • JSON_PRETTY_PRINT – PHP’s JSON output is normally minified which is ideal when sending it over the network as part of an HTTP request. Setting this flag will add new line characters and automatic indentation to the JSON string, making it more suitable for configuration files and other scenarios where humans will read the output.
  • JSON_PRESERVE_ZERO_FRACTION – Forces PHP to encode floats such as 0.0 exactly, instead of shaving off the fraction to write 0 into the JSON (which could be incorrectly parsed as an integer).
  • JSON_NUMERIC_CHECK – Automatically converts numeric strings to numbers in the JSON output, instead of preserving them as strings. With this enabled, a PHP value "234.5" will be emitted as 234.5 in the JSON.
  • JSON_PARTIAL_OUTPUT_ON_ERROR – Try to continue writing even after an encoding error is encountered. PHP will try to substitute invalid values in order to produce some output, even if it’s not complete.

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.

Profile Photo for James Walker James Walker
James Walker is a contributor to How-To Geek DevOps. He is the founder of Heron Web, a UK-based digital agency providing bespoke software development services to SMEs. He has experience managing complete end-to-end web development workflows, using technologies including Linux, GitLab, Docker, and Kubernetes.
Read Full Bio »