http clients and apis
JSON Encoding And Decoding
JSON encoding turns PHP values into a JSON string. JSON decoding turns a JSON string into PHP values.
APIs depend on this boundary. Your PHP code works with arrays, strings, integers, booleans, and nulls; the network carries bytes of JSON text.
Decode JSON into arrays
<?php
declare(strict_types=1);
$json = '{"id":123,"name":"Notebook"}';
$product = json_decode($json, true, flags: JSON_THROW_ON_ERROR);
echo $product['name'] . PHP_EOL;
echo json_encode(['ok' => true], JSON_THROW_ON_ERROR) . PHP_EOL;
// Prints:
// Notebook
// {"ok":true}
The second argument true tells PHP to decode JSON objects as associative arrays. Without it, JSON objects become stdClass objects.
For API code, arrays are often simpler because validation and transformation code can use normal array access.
Use exceptions for JSON errors
Always prefer JSON_THROW_ON_ERROR in application code. Without it, invalid JSON may silently become null, which can be confused with valid JSON null.
<?php
declare(strict_types=1);
try {
json_decode('{"broken":', true, flags: JSON_THROW_ON_ERROR);
} catch (JsonException $exception) {
echo 'Invalid JSON' . PHP_EOL;
}
// Prints:
// Invalid JSON
Lesson 8 goes deeper into JSON errors; the important habit starts here.
Validate the decoded shape
Decoding JSON only proves the text was valid JSON. It does not prove the fields are present or the types are correct.
<?php
declare(strict_types=1);
function productNameFromJson(string $json): string
{
$data = json_decode($json, true, flags: JSON_THROW_ON_ERROR);
if (!is_array($data) || !isset($data['name']) || !is_string($data['name'])) {
throw new InvalidArgumentException('Product name is missing.');
}
return $data['name'];
}
echo productNameFromJson('{"name":"Notebook"}') . PHP_EOL;
// Prints:
// Notebook
This kind of shape check prevents warnings and bad assumptions later in the code.
Encode PHP values deliberately
<?php
declare(strict_types=1);
$response = [
'id' => 123,
'active' => true,
'tags' => ['stationery', 'paper'],
'discount' => null,
];
echo json_encode($response, JSON_THROW_ON_ERROR) . PHP_EOL;
// Prints:
// {"id":123,"active":true,"tags":["stationery","paper"],"discount":null}
Associative arrays encode as JSON objects. Sequential arrays encode as JSON arrays.
Empty arrays and empty objects
An empty PHP array encodes as [], not {}.
<?php
declare(strict_types=1);
echo json_encode([], JSON_THROW_ON_ERROR) . PHP_EOL;
echo json_encode((object) [], JSON_THROW_ON_ERROR) . PHP_EOL;
// Prints:
// []
// {}
This matters when an API contract expects an empty object. Do not guess; check the contract.
Numbers can surprise you
JSON has numbers, but PHP has integers and floats. Large IDs from other systems may not fit safely into every language's number type. Many APIs send large identifiers as strings for that reason.
Do not cast IDs to integers unless the API contract says they are numeric IDs within a safe range.
UTF-8 matters
JSON strings must be valid UTF-8. If you read data from old files, external feeds, or legacy databases, encoding problems can make json_encode() fail.
Use JSON_THROW_ON_ERROR so that failure is visible.
What to check in a project
Check that json_decode() uses JSON_THROW_ON_ERROR.
Check whether decoded objects should be arrays or stdClass, and keep the style consistent.
Check the decoded shape before trusting fields.
Check empty arrays versus empty objects when matching an external contract.
Check large identifiers and avoid unnecessary numeric casts.
What you should be able to do
After this lesson, you should be able to encode and decode JSON safely, use exceptions for JSON errors, validate decoded data before using it, and recognise common API contract issues around arrays, objects, nulls, UTF-8, and numeric IDs.
Practice
Task: Decode And Validate JSON
Write a small PHP script that decodes product JSON, validates the shape, and encodes a response.
Requirements
- Use
declare(strict_types=1);. - Decode JSON with
JSON_THROW_ON_ERROR. - Require
idandname. - Treat
idas an integer andnameas a string. - Include one valid JSON case.
- Include one invalid shape case.
- Encode a success response with
json_encode(..., JSON_THROW_ON_ERROR).
Check Your Work
Run the script and confirm the invalid shape is rejected even though it is valid JSON.
Show solution
This solution separates JSON syntax validity from application shape validity.
<?php
declare(strict_types=1);
/**
* @return array{id: int, name: string}
*/
function decodeProduct(string $json): array
{
$data = json_decode($json, true, flags: JSON_THROW_ON_ERROR);
if (
!is_array($data)
|| !isset($data['id'], $data['name'])
|| !is_int($data['id'])
|| !is_string($data['name'])
) {
throw new InvalidArgumentException('Product JSON has the wrong shape.');
}
return ['id' => $data['id'], 'name' => $data['name']];
}
$product = decodeProduct('{"id":123,"name":"Notebook"}');
echo json_encode(['data' => $product], JSON_THROW_ON_ERROR) . PHP_EOL;
try {
decodeProduct('{"id":"123","title":"Notebook"}');
} catch (InvalidArgumentException $exception) {
echo $exception->getMessage() . PHP_EOL;
}
// Prints:
// {"data":{"id":123,"name":"Notebook"}}
// Product JSON has the wrong shape.
Why This Works
The valid case proves decoding and encoding work. The invalid case proves valid JSON can still be rejected when it does not match the API contract.