Kodokon kodokon.com

Construire une API JSON en PHP

Construisez une API JSON rigoureuse en maîtrisant les en-têtes, la lecture du corps brut et la sémantique fine des codes de statut.

9 min · 3 questions

Ouvrir cette leçon dans Kodokon

Une API JSON est un contrat : chaque réponse annonce son type (Content-Type: application/json), porte le bon code de statut et une structure stable. PHP n'envoie rien de tout cela par défaut - il déclare text/html. Première brique : un helper de réponse unique, typé never (PHP 8.1+). Ce type de retour garantit, au moteur comme aux outils d'analyse statique, que la fonction ne rend jamais la main : tout code écrit après un appel est prouvé inatteignable.

PHP
<?php
declare(strict_types=1);

function jsonResponse(
    mixed $data,
    int $status = 200
): never {
    http_response_code($status);
    header('Content-Type: application/json');
    echo json_encode(
        $data,
        JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE
    );
    exit;
}

jsonResponse(['status' => 'ok']);
Un point de sortie unique : statut, en-tête et corps toujours cohérents.

Deuxième subtilité, souvent découverte trop tard : $_POST reste vide face à un corps JSON. PHP ne le remplit que pour application/x-www-form-urlencoded et multipart/form-data. Le corps brut se lit sur le flux php://input. Décodez-le avec JSON_THROW_ON_ERROR : sans ce drapeau, json_decode renvoie silencieusement null en cas d'erreur - indistinguable d'un corps contenant littéralement null. Et grâce au type never, le switch ci-dessous n'a besoin d'aucun break.

PHP
<?php
declare(strict_types=1);

switch ($_SERVER['REQUEST_METHOD']) {
    case 'GET':
        jsonResponse(['items' => []]);
    case 'POST':
        $raw = (string) file_get_contents(
            'php://input'
        );
        try {
            $payload = json_decode(
                $raw,
                true,
                512,
                JSON_THROW_ON_ERROR
            );
        } catch (JsonException) {
            jsonResponse(
                ['error' => 'Malformed JSON'],
                400
            );
        }
        header('Location: /items/42');
        jsonResponse(['created' => $payload], 201);
    default:
        header('Allow: GET, POST');
        jsonResponse(
            ['error' => 'Method not allowed'],
            405
        );
}
Suppose jsonResponse défini plus haut ; le catch sans variable est du PHP 8.

Choisissez les codes avec précision, car vos clients programmeront contre eux, pas contre vos messages : 200 lecture réussie, 201 création (accompagnée d'un en-tête Location pointant vers la ressource), 204 succès sans corps, 400 JSON malformé, 404 ressource absente, 405 méthode non gérée (avec l'en-tête Allow listant celles permises) et 422 pour un JSON syntaxiquement valide mais sémantiquement incorrect. Vérifiez le tout avec curl -i, qui affiche les en-têtes reçus.

BASH
curl -i -X POST http://localhost:8000/items \
  -H 'Content-Type: application/json' \
  -d '{"name":"Widget"}'
L'option -i affiche statut et en-têtes : le seul vrai test du contrat.

Quiz de validation

Vérifiez que vous avez bien retenu les points clés de cette leçon.

  1. Votre API reçoit un POST avec un corps JSON : pourquoi $_POST est-il vide ?
    • PHP ne peuple $_POST que pour les corps form-urlencoded et multipart ; un corps JSON se lit sur php://input
    • Le JSON dépasse la limite post_max_size par défaut
    • Il faut d'abord appeler json_decode sur $_POST pour le remplir
  2. Quel code de statut renvoyer après une création réussie via POST ?
    • 200
    • 201
    • 204
    • 302
  3. Que garantit le type de retour never ?
    • La fonction retourne toujours null
    • La fonction ne rend jamais la main : elle se termine par exit ou une exception, et le moteur le vérifie
    • La fonction ne peut pas être redéfinie par une classe fille