PHP cookbook · Beginner

Validate JSON API input in PHP

Validate and normalize a JSON request body, returning all errors in a consistent 422 response.

Approach

Decode JSON with JSON_THROW_ON_ERROR, validate each field, collect all errors keyed by field, and return them as application/problem+json with status 422. Convert valid input into a typed DTO so downstream code doesn't touch raw arrays.

Solution

<?php
declare(strict_types=1);

final readonly class CreateProduct
{
    public function __construct(public string $name, public int $priceCents, public array $tags) {}
}

function parseCreateProduct(string $json): CreateProduct
{
    try {
        $data = json_decode($json, true, 32, JSON_THROW_ON_ERROR);
    } catch (JsonException) {
        throw new ValidationFailed(['body' => 'Malformed JSON']);
    }
    $errors = [];
    $name = trim((string) ($data['name'] ?? ''));
    if ($name === '' || mb_strlen($name) > 200) $errors['name'] = 'Required, max 200 characters';
    $price = filter_var($data['price_cents'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0]]);
    if ($price === false) $errors['price_cents'] = 'Must be a non-negative integer';
    $tags = $data['tags'] ?? [];
    if (!is_array($tags) || count($tags) > 10 || array_filter($tags, fn ($t) => !is_string($t))) $errors['tags'] = 'Up to 10 strings';

    if ($errors) throw new ValidationFailed($errors);
    return new CreateProduct($name, $price, array_values(array_unique($tags)));
}

// Error handler:
// http_response_code(422); header('Content-Type: application/problem+json');
// echo json_encode(['title' => 'Validation failed', 'status' => 422, 'errors' => $e->errors]);

Pitfalls

  • Stopping at the first error (users fix one field at a time).
  • Loose comparisons ('1abc' == 1 in PHP 7).
  • Using (int) casts that silently turn garbage into 0.

Variations

  • symfony/validator with attribute constraints on DTOs.
  • Laravel Form Requests or Validator::make() with rules.

Further reading: PHP manual: json_decode

Reference: https://www.php.net/manual/en/function.json-decode.php

More PHP recipes