APIExemples
Exemples PHP
Exemples PHP (8.1+) avec Guzzle : authentification, lecture, écriture, téléversement, retry.
Cette page propose des exemples PHP (8.1+) avec Guzzle, standard de fait dans l'écosystème PHP.
Installation
composer require guzzlehttp/guzzle vlucas/phpdotenvAvec un fichier .env à la racine :
DIAMOND_BASE=https://api.diamondcareers.fr/diamond/v1
DIAMOND_EMAIL=integration@votre-etablissement.com
DIAMOND_PASSWORD=VotreMotDePasseTechniqueClient minimal
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
use Dotenv\Dotenv;
$dotenv = Dotenv::createImmutable(__DIR__);
$dotenv->load();
class DiamondClient
{
private Client $http;
private ?string $token = null;
private string $base;
public function __construct()
{
$this->base = $_ENV['DIAMOND_BASE'];
$this->http = new Client(['timeout' => 30]);
}
public function getToken(): string
{
if ($this->token !== null) {
return $this->token;
}
$response = $this->http->post("{$this->base}/auth/login", [
'json' => [
'email' => $_ENV['DIAMOND_EMAIL'],
'password' => $_ENV['DIAMOND_PASSWORD'],
],
]);
$data = json_decode((string) $response->getBody(), true);
$this->token = $data['token'];
return $this->token;
}
public function api(string $path, string $method = 'GET', array $options = []): mixed
{
$options['headers'] = array_merge([
'Authorization' => 'Bearer ' . $this->getToken(),
'Content-Type' => 'application/json',
], $options['headers'] ?? []);
try {
$response = $this->http->request($method, "{$this->base}{$path}", $options);
$body = (string) $response->getBody();
if ($response->getStatusCode() === 204 || $body === '') {
return null;
}
return json_decode($body, true);
} catch (ClientException $e) {
$body = (string) $e->getResponse()->getBody();
$error = json_decode($body, true) ?? ['message' => $e->getMessage()];
throw new \RuntimeException(
$error['message'] ?? 'Unknown error',
$e->getResponse()->getStatusCode(),
$e
);
}
}
}
$diamond = new DiamondClient();Lister vos offres publiées
$offers = $diamond->api('/offres?status=publish&per_page=100');
echo count($offers) . " offres publiées\n";
foreach ($offers as $offer) {
$meta = $offer['meta'] ?? [];
echo "- {$offer['title']} ({$meta['city']}) — {$meta['contract_type']}\n";
}Synchronisation par delta
function syncOffersIncremental(DiamondClient $diamond, DateTimeInterface $since): int
{
$sinceIso = $since->format(DATE_ATOM);
$page = 1;
$processed = 0;
while (true) {
$offers = $diamond->api(
"/offres?filter[modified_after]=" . urlencode($sinceIso) .
"&per_page=100&page={$page}"
);
if (empty($offers)) break;
foreach ($offers as $offer) {
processOffer($offer);
$processed++;
}
if (count($offers) < 100) break;
$page++;
}
return $processed;
}Créer une offre
$newOffer = $diamond->api('/offres', 'POST', [
'json' => [
'title' => 'Chef de partie pâtisserie — restaurant gastronomique',
'meta' => [
'contract_type' => 'cdi',
'city' => 'Paris',
'country' => 'France',
'salary_min' => 32000,
'salary_max' => 38000,
'salary_display' => '32 000 € – 38 000 €',
'description' => '<p>Au sein d une brigade de 18 personnes...</p>',
'missions' => '<ul><li>Encadrer 4 commis</li></ul>',
'profile_required' => '<p>3 ans d expérience minimum.</p>',
'start_date' => '2026-09-01',
],
],
]);
echo "Offre créée : {$newOffer['id']}\n";Faire évoluer le statut d'une candidature
function setApplicationStatus(DiamondClient $diamond, int $id, string $status, ?string $comment = null): array
{
$payload = ['status' => $status];
if ($comment !== null) {
$payload['comment'] = $comment;
}
return $diamond->api("/candidatures/{$id}/status", 'PATCH', ['json' => $payload]);
}
setApplicationStatus($diamond, 18472, 'interview', 'Entretien prévu le 18 mai à 14 h.');Téléverser un visuel public
function uploadImage(DiamondClient $diamond, string $filePath, string $mime = 'image/jpeg'): array
{
$token = $diamond->getToken();
$client = new GuzzleHttp\Client();
$response = $client->post(
$_ENV['DIAMOND_BASE'] . '/upload',
[
'headers' => ['Authorization' => "Bearer {$token}"],
'multipart' => [
['name' => 'file', 'contents' => fopen($filePath, 'r'), 'filename' => basename($filePath), 'headers' => ['Content-Type' => $mime]],
],
]
);
return json_decode((string) $response->getBody(), true);
}
$media = uploadImage($diamond, './visuel-offre.jpg');
echo "URL publique : {$media['file_url']}\n";Récupérer un fichier privé
function downloadSecureFile(DiamondClient $diamond, string $path, string $dest): void
{
$token = $diamond->getToken();
$client = new GuzzleHttp\Client();
$client->get(
$_ENV['DIAMOND_BASE'] . '/secure-file?path=' . urlencode($path),
[
'headers' => ['Authorization' => "Bearer {$token}"],
'sink' => $dest,
]
);
}
downloadSecureFile($diamond, 'cv/9281/abcd-1234.pdf', './telecharge.pdf');Gestion des erreurs et retry
function apiWithRetry(DiamondClient $diamond, string $path, string $method = 'GET', array $options = [], int $maxRetries = 3): mixed
{
$lastError = null;
for ($attempt = 0; $attempt < $maxRetries; $attempt++) {
try {
return $diamond->api($path, $method, $options);
} catch (\RuntimeException $e) {
$lastError = $e;
$status = $e->getCode();
if ($status >= 400 && $status < 500 && $status !== 429) {
throw $e;
}
$delay = min(30, pow(2, $attempt));
error_log("Tentative " . ($attempt + 1) . " échouée ({$status}), retry dans {$delay}s");
sleep($delay);
}
}
throw $lastError;
}Itération paresseuse via générateur
function iterateAll(DiamondClient $diamond, string $path, int $perPage = 100): \Generator
{
$page = 1;
while (true) {
$separator = str_contains($path, '?') ? '&' : '?';
$items = $diamond->api("{$path}{$separator}per_page={$perPage}&page={$page}");
if (empty($items)) return;
foreach ($items as $item) {
yield $item;
}
if (count($items) < $perPage) return;
$page++;
}
}
foreach (iterateAll($diamond, '/offres?status=publish') as $offer) {
processOffer($offer);
}Bonnes pratiques
- Cachez le jeton dans une session ou un cache mémoire (Redis, APCu) avec un TTL aligné sur sa durée de vie réelle.
- Loguez via Monolog avec des niveaux et un format structuré.
- Gérez les exceptions Guzzle distinctement (
ClientException,ServerException,ConnectException) pour ajuster votre logique de retry. - Pour les gros volumes, envisagez
GuzzleHttp\Poolpour paralléliser intelligemment les requêtes.