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

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

        <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
        
    - 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.