PHP JSON handling is a transport boundary: enforce HTTP and body limits, decode UTF-8 with exception semantics, validate a documented schema, authorize the operation, and encode an explicit response shape.
Dependable APIs distinguish malformed and invalid requests, preserve numeric and list semantics, bound remote calls and collections, use stable status and error envelopes, and test hostile payloads through the real middleware path.
<?php
$json = '{"title":"PHP APIs","published":true}';
try {
$payload = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
echo $payload['title'];
} catch (JsonException $exception) {
echo 'Invalid JSON';
}
PHP APIs
For an application/json request, read the raw body from php://input. Enforce a request-size limit at the web server and application boundary before processing large payloads.
<?php
try {
$payload = json_decode(
file_get_contents('php://input'),
true,
512,
JSON_THROW_ON_ERROR
);
} catch (JsonException $exception) {
http_response_code(400);
$payload = null;
}
<?php
header('Content-Type: application/json; charset=utf-8');
http_response_code(201);
echo json_encode(
['id' => 42, 'status' => 'created'],
JSON_THROW_ON_ERROR
);
{"id":42,"status":"created"}
| Situation | Typical status | Response idea |
|---|---|---|
| Malformed JSON | 400 Bad Request | Syntax could not be decoded |
| Valid JSON with invalid fields | 422 Unprocessable Content | Field-level validation errors |
| Missing record | 404 Not Found | Stable error code and message |
| Created record | 201 Created | Identifier and resource state |
Use json_decode with JSON_THROW_ON_ERROR and choose associative arrays or objects consistently. Apply an explicit depth and request-size limit at the server boundary. Large integer identifiers may lose precision in JavaScript, so represent them as strings when the API must preserve every digit.
Send application/json with an explicit status, avoid output before headers, and let one error handler translate known failures. json_encode can fail on invalid UTF-8 or unsupported values; use JSON_THROW_ON_ERROR rather than returning an empty or misleading response.
JSON is a text interchange format, not a PHP object serializer. Define the request and response schema independently of internal arrays and classes so refactoring application code does not silently change the public API.
Accept the expected HTTP method and JSON media type, then enforce a body byte limit before decoding. An empty body, malformed JSON, valid JSON null, and an omitted field are distinct states that deserve deliberate handling.
JSON text passed to PHP decoding must be UTF-8. Reject or intentionally substitute invalid input according to the API contract; silently dropping bytes can change identifiers, signatures, and user-visible meaning.
Authentication identifies the caller, while authorization decides whether that caller may perform this operation on this resource. Complete both checks before expensive work and never trust identity fields supplied inside the JSON document.
`json_decode` can return objects or associative arrays. Choose one representation for the boundary and state it explicitly; mixing both creates inconsistent property and key access deeper in the application.
Use `JSON_THROW_ON_ERROR` so syntax, depth, and encoding failures become exceptions instead of competing with a legitimate decoded null. Catch JsonException at the HTTP boundary and return one safe client error with location-independent wording.
Set a nesting depth that matches the schema rather than accepting extreme recursive input. Limit array lengths, object property counts, and total nodes after decoding too, because depth alone does not control memory or processing cost.
Large integers may exceed exact range in downstream languages or PHP integer limits on a platform. Preserve identifier-like numbers as strings in the schema and use `JSON_BIGINT_AS_STRING` only as part of a documented compatibility policy.
Successful decoding proves only that the text is valid JSON. Validate required properties, exact types, formats, ranges, lengths, uniqueness, and cross-field rules before creating a command or domain value.
Decide how unknown properties behave. Rejecting them catches misspellings and unsupported clients early; ignoring them can aid forward compatibility but may hide attempted fields. Document the policy per version.
Do not coerce arbitrary strings into numbers or booleans at an API boundary. JSON already distinguishes these types. Return field-specific validation details using stable machine codes and safe human messages.
A JSON Schema tool can help when the project adopts one, but application authorization and domain invariants still require code. This repository does not need a package dependency merely to demonstrate explicit validation.
`json_encode` serializes supported PHP values but array key shape affects output: consecutive zero-based keys become a JSON array, while sparse or string keys become an object. Reindex filtered lists only when the transport contract requires an array.
Use `JSON_THROW_ON_ERROR` for response construction too. Invalid UTF-8, unsupported values, recursion, or depth problems should fail before headers commit. Map domain objects to explicit response arrays instead of relying on incidental public properties.
Select flags from the contract. Unescaped Unicode can improve readability, preserved zero fractions can protect numeric presentation, and pretty printing is useful for diagnostics but increases payload size. Never enable flags without understanding semantic changes.
JSON object member order should not carry business meaning. Tests should compare decoded structures or a documented canonical representation rather than brittle whitespace and key-order output unless a signature protocol defines canonicalization.
Return a status that describes the outcome: malformed syntax differs from semantic validation, authentication, authorization, missing resources, conflicts, rate limits, and unexpected server failures. Keep one consistent error envelope across endpoints.
Set the JSON content type and UTF-8 expectations before sending a body. Avoid mixing notices, debug HTML, or stack traces into the response; production error display must remain disabled and logs should receive diagnostics separately.
GET and HEAD should be safe retrieval operations, while PUT and DELETE have idempotency expectations and POST often creates or commands work. Route semantics should match retry and cache behavior rather than treating every endpoint as POST.
Use pagination, filtering, conditional requests, and response size limits for collections. An endpoint that encodes an unlimited database result can exhaust memory before the web server can send any bytes.
When PHP calls another API, verify the destination against configured origins, use TLS with certificate validation, set connection and total timeouts, and bound response bytes before decoding. Never fetch an arbitrary user-supplied URL from a privileged network.
Check the HTTP status and media type before parsing the body. A proxy error page or empty success response is not JSON merely because the client expected JSON. Preserve a safe excerpt or correlation identifier for diagnostics without logging secrets.
Retry only transient failures under a small budget with backoff and jitter where appropriate. Respect server retry guidance, make repeated commands idempotent, and stop retries when the calling request deadline no longer leaves useful time.
Circuit breaking, caching, and fallback values are product policies. A stale cache can be better than failure for public reference data and dangerous for permissions or account balances. State freshness and failure behavior per dependency.
Test malformed JSON, empty bodies, scalar roots, invalid UTF-8, excessive nesting, unknown fields, wrong types, numeric boundaries, missing authorization, and valid requests. Assert rejected input causes no writes or outbound calls.
Response tests should cover list shape, null versus omitted fields, error envelope, status, headers, Unicode, large identifiers, and encoding failure. Contract fixtures should focus on stable public behavior rather than internal class layout.
Use integration tests through the real HTTP request parser and middleware so content type, byte limits, authentication, and exception mapping are exercised. Stub remote dependencies at their HTTP boundary and separately run controlled compatibility tests against a sandbox service.
Version APIs when incompatible contracts must coexist, publish deprecation windows, and monitor use before removal. Additive fields are not harmless when clients reject unknown properties, so compatibility policy should be explicit on both sides.
0 of 2 checked
Try this next
0 of 2 completed
Explore 500+ free tutorials across 20+ languages and frameworks.