Intégrer Claude dans une app PHP est plus simple qu'il n'y paraît, mais faire ça proprement pour la production demande d'éviter quelques pièges. Voici la recette complète.

1. Installation

Pas de SDK PHP officiel, mais anthropic-php côté communauté est solide. Pour une intégration minimale, guzzlehttp/guzzle suffit :

composer require guzzlehttp/guzzle

2. Client minimal

class ClaudeClient {
    private Client $http;

    public function __construct(private string $apiKey) {
        $this->http = new Client([
            'base_uri' => 'https://api.anthropic.com/v1/',
            'headers' => [
                'x-api-key' => $apiKey,
                'anthropic-version' => '2023-06-01',
                'content-type' => 'application/json',
            ],
            'timeout' => 60,
        ]);
    }

    public function message(array $messages, string $model = 'claude-opus-4-7'): array {
        $res = $this->http->post('messages', [
            'json' => [
                'model' => $model,
                'max_tokens' => 4096,
                'messages' => $messages,
            ],
        ]);
        return json_decode($res->getBody(), true);
    }
}

3. Prompt caching (le must)

Le prompt caching réduit les coûts jusqu'à 90 % sur les prompts répétitifs. Activez-le sur les gros system prompts :

$payload = [
    'model' => 'claude-opus-4-7',
    'max_tokens' => 2048,
    'system' => [
        ['type' => 'text', 'text' => $bigSystemPrompt, 'cache_control' => ['type' => 'ephemeral']],
    ],
    'messages' => $messages,
];

Astuce : le cache a une TTL de 5 minutes. Pour des chatbots multi-tours, cela couvre la quasi-totalité des sessions actives.

4. Streaming en Server-Sent Events

Indispensable pour une bonne UX :

header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no');

$response = $http->post('messages', [
    'json' => ['model' => 'claude-opus-4-7', 'stream' => true, 'messages' => $messages, 'max_tokens' => 2048],
    'stream' => true,
]);

$body = $response->getBody();
while (!$body->eof()) {
    $line = Utils::readLine($body);
    echo $line . "\n";
    @ob_flush(); @flush();
}

5. Tool use

Donner à Claude la possibilité d'appeler des fonctions PHP :

$tools = [[
    'name' => 'get_weather',
    'description' => 'Récupère la météo pour une ville',
    'input_schema' => [
        'type' => 'object',
        'properties' => ['city' => ['type' => 'string']],
        'required' => ['city'],
    ],
]];

$response = $client->message($messages, tools: $tools);
if ($response['stop_reason'] === 'tool_use') {
    $call = collect($response['content'])->firstWhere('type', 'tool_use');
    $result = match ($call['name']) {
        'get_weather' => fetchWeather($call['input']['city']),
    };
    // On rappelle Claude avec le résultat...
}

6. Gestion d'erreurs production

  • Rate limits : 429 avec header retry-after. Implémentez un backoff exponentiel avec jitter.
  • 529 overloaded : l'API Claude sature ponctuellement. Retry après 2 s.
  • Context too long : 400 avec message explicite. Tronquez côté serveur.
  • Facturation runaway : imposez un max_tokens agressif et loggez chaque appel avec coût estimé.

7. Sécurité

  • Ne jamais passer la clé API au client (JS). Toujours proxy côté serveur.
  • Rate-limit par utilisateur (Redis + bucket simple).
  • Validez et assainissez les sorties avant injection DOM — Claude peut générer du HTML.
  • Logs sans PII : l'input utilisateur peut contenir des données sensibles, ne loggez pas bêtement.

Ressources

Besoin d'intégrer Claude proprement dans votre app PHP ? C'est ma spécialité.