Tutorials Logic, IN info@tutorialslogic.com

PHP JSON and APIs: Decode, Validate, Encode, and Respond

PHP JSON Boundary

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.

Decode JSON

  • JSON text must be valid UTF-8.
  • Passing true returns JSON objects as associative arrays.
  • Validate keys and value types after decoding.

Decode into an Associative Array

Decode into an Associative Array
<?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';
}
Output
PHP APIs

Read an API Request

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.

Decode the Request Body

Decode the Request Body
<?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;
}

Return JSON

Successful JSON Response

Successful JSON Response
<?php
header('Content-Type: application/json; charset=utf-8');
http_response_code(201);

echo json_encode(
    ['id' => 42, 'status' => 'created'],
    JSON_THROW_ON_ERROR
);
Output
{"id":42,"status":"created"}

Status and Error Shape

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

Encoding and Errors

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 Boundary

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.

  • Design a transport schema separate from PHP storage.
  • Verify method, media type, and byte limits.
  • Handle empty, null, malformed, and invalid UTF-8 distinctly.
  • Derive identity and permission from trusted request context.

Decoding Policy

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

  • Choose arrays or objects consistently at the boundary.
  • Use exception-based JSON error handling.
  • Bound depth, width, count, and body bytes.
  • Represent precision-sensitive identifiers as strings.

Schema Validation

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.

  • Validate decoded shape and domain meaning.
  • Choose an explicit unknown-property policy.
  • Preserve JSON type distinctions.
  • Keep authorization and business rules outside syntax validation.

Encoding Policy

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

  • Preserve intended list versus object shape.
  • Throw on response encoding failures.
  • Map domain values through explicit response DTOs.
  • Treat formatting and key order as non-semantic by default.

HTTP Semantics

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.

  • Map each failure category to stable API semantics.
  • Keep non-JSON diagnostics out of responses.
  • Align methods with safety and idempotency.
  • Bound collection and response sizes.

Remote API Calls

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.

  • Allowlist remote origins and verify TLS.
  • Validate status, media type, and body size.
  • Retry only safe transient operations within a deadline.
  • Choose cache and fallback policy from data meaning.

API Verification

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.

  • Test hostile and boundary JSON documents.
  • Assert headers and status with body contracts.
  • Exercise real middleware and controlled remote doubles.
  • Manage API evolution through an explicit compatibility policy.
Before you move on

Mastery Check

5 checks
  • Verify method, media type, body bytes, UTF-8, and depth.
  • Decode and encode with explicit exception policy.
  • Validate schema, domain rules, identity, and authorization.
  • Bound remote calls, retries, pages, and response size.
  • Test public contracts independently of internal classes.

JSON Contract Check

0 of 2 checked

Q1. Does valid JSON guarantee valid application data?

Q2. Why use JSON_THROW_ON_ERROR?

Try this next

Define an API Contract

0 of 2 completed

  1. Require a non-empty title and boolean published field after decoding JSON.
  2. Design one stable JSON error shape for malformed input and field validation failures.
Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.