A PHP endpoint can return valid JSON while still changing its meaning. An empty settings object and an empty item list are different API shapes. Similarly, removing an element from a PHP array can leave numeric keys that encode as a JSON object instead of a list.

Decode object documents as objects when preserving that distinction matters. The PHP json_decode reference documents the associative argument and JSON_THROW_ON_ERROR. The json_encode reference explains how PHP arrays are represented in JSON. This example tests both behaviors with explicit runtime checks.

Prerequisites

Use a supported PHP 8 release with the JSON extension; the example was tested on PHP 8.3. Save it as shapes.php. It runs from the command line and does not need a web server, database, or Composer packages.

<?php
declare(strict_types=1);
function objectDocument(string $json): stdClass {
    $value = json_decode($json, false, 32, JSON_THROW_ON_ERROR);
    if (!$value instanceof stdClass) {
        throw new InvalidArgumentException('top-level object required');
    }
    return $value;
}
function check(bool $ok): void {
    if (!$ok) throw new RuntimeException('check failed');
}
$document = objectDocument('{"settings":{},"items":[]}');
check($document->settings instanceof stdClass);
check(is_array($document->items));
$encoded = json_encode($document, JSON_THROW_ON_ERROR);
check($encoded === '{"settings":{},"items":[]}');
$rejected = 0;
foreach (['[]', 'null', '42', '{'] as $bad) {
    try { objectDocument($bad); }
    catch (JsonException | InvalidArgumentException $expected) { $rejected++; }
}
check($rejected === 4);
$rows = [10, 20, 30];
unset($rows[1]);
check(json_encode($rows, JSON_THROW_ON_ERROR) === '{"0":10,"2":30}');
check(json_encode(array_values($rows), JSON_THROW_ON_ERROR) === '[10,30]');
echo $encoded, PHP_EOL;
echo 'invalid documents rejected: ', $rejected, PHP_EOL;
echo 'reindexed rows: ', json_encode(array_values($rows), JSON_THROW_ON_ERROR), PHP_EOL;

Run and expected output

php shapes.php
{"settings":{},"items":[]}
invalid documents rejected: 4
reindexed rows: [10,30]

Understand the two boundaries

The document loader requires a top-level object. Decoding with the associative argument set to false preserves the nested settings value as stdClass and items as an array. Re-encoding the fixture therefore retains both empty shapes. A top-level list, null, and a number are valid JSON but fail this application's document contract. A lone opening brace fails JSON parsing itself.

The second boundary concerns list construction. After removing the middle element from rows, its remaining keys are 0 and 2. The program checks that direct encoding produces an object with those keys. Calling array_values creates consecutive list keys and yields the intended JSON array.

Apply the rule deliberately

Use reindexing for values that are semantically ordered lists, such as filtered search results. Do not apply it indiscriminately to maps: keys might be identifiers with business meaning. Likewise, do not force every array into an object merely to preserve one empty object. Preserve the intended structure at each field.

For newly constructed documents, explicitly create empty objects where the API schema requires them. For incoming documents, follow the object-preserving approach shown here before validating individual properties. The loader only establishes a top-level shape; it does not check required fields, duplicate names, numeric ranges, or authorization.

Troubleshooting

If a frontend receives an object where it expected a list, inspect PHP keys immediately before encoding, especially after filtering or unsetting elements. If empty objects become arrays during a read-modify-write cycle, inspect the decoding mode. JSON exceptions should become deliberate application errors rather than being replaced with an empty success response.

The explicit check function works even when PHP assertion settings disable assert expressions. All checks here use synthetic documents; HTTP response headers and framework serializers were not exercised. Browse more PHP tutorials.