JavaScript pour débuter
Maîtrisez les requêtes HTTP avec cURL PHP et testez vos APIs avec Postman. Du débutant à l’expert en communication API.
- API REST
- Authentification
- Postman
Installation et Configuration
Configuration de cURL PHP et installation de Postman pour tester vos APIs.
📦 Installation et Vérification cURL PHP
Installation Windows (XAMPP/WAMP) :
html
# cURL est généralement inclus par défaut
# Vérifier dans php.ini :
extension=curl
# Si besoin d'installer :
sudo apt-get install php-curl
sudo systemctl restart apache2
Installation Postman :
html
# Télécharger Postman
https://www.postman.com/downloads/
# Alternative : Postman Web
https://web.postman.co/
# Ou installer via Snap (Linux)
sudo snap install postman
Test de Vérification cURL :
html
<?php
// test_curl.php - Vérifier cURL et ses fonctionnalités
echo "<h2>Test de l'installation cURL PHP</h2>";
// 1. Vérifier si cURL est installé
if (function_exists('curl_version')) {
echo "✅ cURL est installé<br>";
// Informations sur la version
$curl_info = curl_version();
echo "Version cURL : " . $curl_info['version'] . "<br>";
echo "Version SSL : " . $curl_info['ssl_version'] . "<br>";
echo "Protocols supportés : " . implode(', ', $curl_info['protocols']) . "<br>";
} else {
echo "❌ cURL n'est pas installé ou activé<br>";
echo "Activez l'extension dans php.ini : extension=curl<br>";
}
// 2. Test de requête simple
echo "<h3>Test de requête HTTP :</h3>";
try {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://httpbin.org/json');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Pour les tests uniquement
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($response && $http_code == 200) {
echo "✅ Requête HTTP réussie (Code : $http_code)<br>";
echo "Réponse : " . substr($response, 0, 100) . "...<br>";
} else {
echo "❌ Erreur de requête : $error (Code : $http_code)<br>";
}
} catch (Exception $e) {
echo "❌ Exception : " . $e->getMessage() . "<br>";
}
// 3. Test des fonctionnalités avancées
echo "<h3>Fonctionnalités disponibles :</h3>";
$features = [
'HTTP/2' => defined('CURL_HTTP_VERSION_2_0'),
'IPv6' => defined('CURLOPT_IPRESOLVE'),
'SSL/TLS' => function_exists('curl_version') && isset(curl_version()['ssl_version']),
'Cookies' => defined('CURLOPT_COOKIEJAR'),
'Proxy' => defined('CURLOPT_PROXY'),
'Upload' => defined('CURLOPT_UPLOAD')
];
foreach ($features as $feature => $available) {
$status = $available ? "✅" : "❌";
echo "$status $feature supporté<br>";
}
// 4. Configuration recommandée
echo "<h3>Configuration recommandée php.ini :</h3>";
echo "<pre>
extension=curl
curl.cainfo = /path/to/cacert.pem
max_execution_time = 60
memory_limit = 256M
</pre>";
// 5. Test Postman (si serveur local)
echo "<h3>Test d'accès local pour Postman :</h3>";
$local_url = 'http://' . $_SERVER['HTTP_HOST'] . '/test_curl.php';
echo "URL de test : <a href='$local_url' target='_blank'>$local_url</a><br>";
echo "Utilisez cette URL dans Postman pour tester vos requêtes<br>";
?>
Les Bases de cURL PHP
Comprendre les fondamentaux de cURL pour effectuer des requêtes HTTP en PHP.
🔧 Structure de Base cURL
html
<?php
/**
* Structure de base d'une requête cURL
* 4 étapes essentielles : Initialiser, Configurer, Exécuter, Fermer
*/
function curlBasicExample() {
// 1. INITIALISER - Créer une ressource cURL
$ch = curl_init();
// 2. CONFIGURER - Définir les options
curl_setopt($ch, CURLOPT_URL, 'https://jsonplaceholder.typicode.com/posts/1');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // Retourner le résultat au lieu de l'afficher
curl_setopt($ch, CURLOPT_TIMEOUT, 30); // Timeout de 30 secondes
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // Suivre les redirections
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); // Vérifier le certificat SSL
curl_setopt($ch, CURLOPT_USERAGENT, 'MonApp/1.0 (PHP cURL)'); // User Agent personnalisé
// 3. EXÉCUTER - Lancer la requête
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
// 4. FERMER - Libérer les ressources
curl_close($ch);
// Traitement de la réponse
if ($response === false) {
return ['error' => "Erreur cURL : $error"];
}
if ($http_code !== 200) {
return ['error' => "Code HTTP : $http_code"];
}
return [
'success' => true,
'data' => json_decode($response, true),
'http_code' => $http_code
];
}
// Utilisation
$result = curlBasicExample();
if (isset($result['error'])) {
echo "Erreur : " . $result['error'];
} else {
echo "Succès ! Données reçues :\n";
print_r($result['data']);
}
/**
* Options cURL les plus courantes
*/
function curlCommonOptions($url, $options = []) {
$ch = curl_init();
// Options de base (toujours recommandées)
$defaultOptions = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 5,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_USERAGENT => 'PHP cURL Client/1.0',
CURLOPT_ENCODING => '', // Accepter toutes les encodages
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Content-Type: application/json'
]
];
// Fusionner avec les options personnalisées
$finalOptions = array_replace($defaultOptions, $options);
curl_setopt_array($ch, $finalOptions);
return $ch;
}
/**
* Wrapper simple pour requêtes GET
*/
function curlGet($url, $headers = []) {
$ch = curlCommonOptions($url);
if (!empty($headers)) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
}
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$info = curl_getinfo($ch);
$error = curl_error($ch);
curl_close($ch);
return [
'success' => ($response !== false && $http_code >= 200 && $http_code < 300),
'data' => $response,
'http_code' => $http_code,
'info' => $info,
'error' => $error
];
}
// Exemples d'utilisation
echo "\n=== Exemples de requêtes GET ===\n";
// GET simple
$result = curlGet('https://jsonplaceholder.typicode.com/users');
if ($result['success']) {
$users = json_decode($result['data'], true);
echo "Nombre d'utilisateurs : " . count($users) . "\n";
}
// GET avec headers personnalisés
$result = curlGet('https://api.github.com/user', [
'Authorization: token YOUR_TOKEN_HERE',
'User-Agent: Mon-App/1.0'
]);
// GET avec paramètres d'URL
$params = http_build_query(['page' => 1, 'per_page' => 10]);
$result = curlGet('https://jsonplaceholder.typicode.com/posts?' . $params);
/**
* Gestion d'erreurs robuste
*/
function curlWithErrorHandling($url) {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_FAILONERROR => true, // Échec automatique sur codes d'erreur HTTP
CURLOPT_SSL_VERIFYPEER => true
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_errno = curl_errno($ch);
$curl_error = curl_error($ch);
curl_close($ch);
if ($response === false) {
// Erreur cURL
throw new Exception("Erreur cURL ($curl_errno): $curl_error");
}
if ($http_code >= 400) {
// Erreur HTTP
throw new Exception("Erreur HTTP $http_code");
}
return $response;
}
// Utilisation avec try/catch
try {
$data = curlWithErrorHandling('https://jsonplaceholder.typicode.com/posts/1');
echo "Données reçues : " . substr($data, 0, 100) . "...\n";
} catch (Exception $e) {
echo "Erreur : " . $e->getMessage() . "\n";
}
?>
Configuration et Utilisation de Postman
Maîtrisez Postman pour tester et déboguer vos APIs rapidement et efficacement.
🚀 Configuration Postman et Première Requête
1 Installation et Compte
• Télécharger et installer Postman
• Créer un compte (optionnel mais recommandé)
• Synchronisation entre appareils
• Accès aux fonctionnalités avancées
• Partage d'équipe
2 Interface et Navigation
• Barre d'adresse pour l'URL
• Sélection de la méthode HTTP
• Onglets Params, Headers, Body
• Section de réponse
• Historique des requêtes
3 Collections et Tests
• Organiser les requêtes en collections
• Variables d'environnement
• Tests automatisés
• Documentation automatique
• Export/Import de collections
Étapes pour votre première requête :
html
1. Créer une nouvelle requête :
• Cliquer sur "New" → "Request"
• Nommer la requête "Test API"
• Créer une nouvelle collection "Mes Tests"
2. Configurer la requête :
• Méthode : GET
• URL : https://jsonplaceholder.typicode.com/posts/1
• Headers : Accept: application/json
3. Exécuter et analyser :
• Cliquer sur "Send"
• Observer le statut (200 OK)
• Examiner la réponse JSON
• Vérifier les headers de réponse
⚙️ Variables et Environnements Postman
Configuration des Environnements :
html
// Environnement "Développement"
base_url: http://localhost:8000
api_key: dev_key_123
database: test_db
// Environnement "Production"
base_url: https://api.monsite.com
api_key: prod_key_xyz
database: prod_db
// Utilisation dans les requêtes
{{base_url}}/api/users
Authorization: Bearer {{api_key}}
Scripts Pre-request et Tests :
html
// Pre-request Script
pm.environment.set("timestamp", Date.now());
pm.environment.set("random_id",
Math.floor(Math.random() * 1000));
// Test Script
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});
pm.test("Response has id", function () {
pm.expect(pm.response.json()).to.have.property('id');
});
Méthodes HTTP avec cURL PHP
GET, POST, PUT, DELETE et autres méthodes HTTP expliquées avec exemples pratiques.
📊 GET - Récupérer des Données
html
<?php
/**
* Requêtes GET avec cURL - Récupérer des données
*/
// GET Simple
function getRequest($url, $headers = []) {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_USERAGENT => 'PHP cURL Client'
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
return [
'success' => ($response !== false && $http_code == 200),
'data' => $response,
'http_code' => $http_code,
'error' => $error
];
}
// Exemple 1 : GET simple
echo "=== GET Simple ===\n";
$result = getRequest('https://jsonplaceholder.typicode.com/posts/1');
if ($result['success']) {
$post = json_decode($result['data'], true);
echo "Titre : " . $post['title'] . "\n";
echo "Corps : " . substr($post['body'], 0, 50) . "...\n";
}
// Exemple 2 : GET avec paramètres d'URL
echo "\n=== GET avec Paramètres ===\n";
$params = [
'userId' => 1,
'_limit' => 5,
'_sort' => 'id',
'_order' => 'desc'
];
$url = 'https://jsonplaceholder.typicode.com/posts?' . http_build_query($params);
$result = getRequest($url);
if ($result['success']) {
$posts = json_decode($result['data'], true);
echo "Nombre de posts : " . count($posts) . "\n";
foreach ($posts as $post) {
echo "- Post #{$post['id']} : {$post['title']}\n";
}
}
// Exemple 3 : GET avec headers personnalisés
echo "\n=== GET avec Headers ===\n";
$headers = [
'Accept: application/json',
'Authorization: Bearer your_token_here',
'User-Agent: MonApp/2.0',
'X-Custom-Header: valeur_personnalisee'
];
$result = getRequest('https://httpbin.org/headers', $headers);
if ($result['success']) {
$response = json_decode($result['data'], true);
echo "Headers envoyés :\n";
foreach ($response['headers'] as $key => $value) {
echo "- $key: $value\n";
}
}
// Exemple 4 : GET avec gestion d'erreurs avancée
function getRequestAdvanced($url, $options = []) {
$ch = curl_init();
$defaultOptions = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 3,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_HTTPHEADER => ['Accept: application/json']
];
curl_setopt_array($ch, array_merge($defaultOptions, $options));
$response = curl_exec($ch);
$info = curl_getinfo($ch);
$error = curl_error($ch);
curl_close($ch);
return [
'success' => ($response !== false && $info['http_code'] >= 200 && $info['http_code'] < 300),
'data' => $response,
'info' => $info,
'error' => $error,
'json' => $response ? json_decode($response, true) : null
];
}
// Exemple 5 : GET avec pagination
echo "\n=== GET avec Pagination ===\n";
function getAllPosts($baseUrl, $perPage = 10) {
$allPosts = [];
$page = 1;
do {
$url = $baseUrl . '?' . http_build_query([
'_page' => $page,
'_limit' => $perPage
]);
$result = getRequestAdvanced($url);
if (!$result['success']) {
break;
}
$posts = $result['json'];
$allPosts = array_merge($allPosts, $posts);
echo "Page $page : " . count($posts) . " posts récupérés\n";
$page++;
// Arrêter si moins de posts que demandé (dernière page)
if (count($posts) < $perPage) {
break;
}
// Limite de sécurité
if ($page > 10) {
echo "Limite de pages atteinte\n";
break;
}
} while (true);
return $allPosts;
}
$allPosts = getAllPosts('https://jsonplaceholder.typicode.com/posts', 10);
echo "Total posts récupérés : " . count($allPosts) . "\n";
// Exemple 6 : GET avec cache simple
class SimpleCache {
private $cacheDir;
public function __construct($cacheDir = './cache') {
$this->cacheDir = $cacheDir;
if (!is_dir($cacheDir)) {
mkdir($cacheDir, 0755, true);
}
}
public function get($key, $ttl = 3600) {
$file = $this->cacheDir . '/' . md5($key) . '.cache';
if (file_exists($file) && (time() - filemtime($file)) < $ttl) {
return unserialize(file_get_contents($file));
}
return null;
}
public function set($key, $data) {
$file = $this->cacheDir . '/' . md5($key) . '.cache';
file_put_contents($file, serialize($data));
}
}
function getCachedRequest($url, $ttl = 3600) {
static $cache = null;
if ($cache === null) {
$cache = new SimpleCache();
}
// Vérifier le cache
$cached = $cache->get($url, $ttl);
if ($cached !== null) {
echo "Données du cache pour : $url\n";
return $cached;
}
// Requête et mise en cache
$result = getRequestAdvanced($url);
if ($result['success']) {
$cache->set($url, $result);
echo "Données mises en cache pour : $url\n";
}
return $result;
}
echo "\n=== GET avec Cache ===\n";
// Premier appel (depuis l'API)
$result1 = getCachedRequest('https://jsonplaceholder.typicode.com/users/1');
// Deuxième appel (depuis le cache)
$result2 = getCachedRequest('https://jsonplaceholder.typicode.com/users/1');
?>
➕ POST - Envoyer des Données
html
<?php
/**
* Requêtes POST avec cURL - Envoyer des données
*/
// POST avec données JSON
function postJsonRequest($url, $data, $headers = []) {
$ch = curl_init();
$defaultHeaders = [
'Content-Type: application/json',
'Accept: application/json'
];
$headers = array_merge($defaultHeaders, $headers);
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($data),
CURLOPT_HTTPHEADER => $headers,
CURLOPT_TIMEOUT => 30
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
return [
'success' => ($response !== false && $http_code >= 200 && $http_code < 300),
'data' => $response,
'http_code' => $http_code,
'error' => $error,
'json' => $response ? json_decode($response, true) : null
];
}
// Exemple 1 : POST JSON
echo "=== POST JSON ===\n";
$userData = [
'title' => 'Mon nouveau post',
'body' => 'Contenu de mon post de test',
'userId' => 1
];
$result = postJsonRequest('https://jsonplaceholder.typicode.com/posts', $userData);
if ($result['success']) {
echo "Post créé avec succès !\n";
echo "ID : " . $result['json']['id'] . "\n";
echo "Titre : " . $result['json']['title'] . "\n";
} else {
echo "Erreur : " . $result['error'] . "\n";
}
// POST avec données de formulaire
function postFormRequest($url, $data, $headers = []) {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($data),
CURLOPT_HTTPHEADER => $headers,
CURLOPT_TIMEOUT => 30
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
return [
'success' => ($response !== false && $http_code >= 200 && $http_code < 300),
'data' => $response,
'http_code' => $http_code,
'error' => $error
];
}
// Exemple 2 : POST Form Data
echo "\n=== POST Form Data ===\n";
$formData = [
'name' => 'Jean Dupont',
'email' => 'jean@example.com',
'message' => 'Ceci est un message de test'
];
$result = postFormRequest('https://httpbin.org/post', $formData, [
'Content-Type: application/x-www-form-urlencoded'
]);
if ($result['success']) {
$response = json_decode($result['data'], true);
echo "Formulaire envoyé avec succès !\n";
echo "Données reçues par le serveur :\n";
foreach ($response['form'] as $key => $value) {
echo "- $key: $value\n";
}
}
// POST avec authentification
function postWithAuth($url, $data, $token) {
$headers = [
'Content-Type: application/json',
'Accept: application/json',
'Authorization: Bearer ' . $token
];
return postJsonRequest($url, $data, $headers);
}
// Exemple 3 : POST avec authentification
echo "\n=== POST avec Authentification ===\n";
$apiData = [
'name' => 'Nouveau produit',
'price' => 29.99,
'category' => 'electronics'
];
$result = postWithAuth('https://httpbin.org/post', $apiData, 'your_api_token_here');
// POST avec upload de fichier
function postFileUpload($url, $filePath, $fieldName = 'file', $additionalData = []) {
if (!file_exists($filePath)) {
return ['success' => false, 'error' => 'Fichier non trouvé'];
}
$ch = curl_init();
$postData = $additionalData;
$postData[$fieldName] = new CURLFile($filePath);
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $postData,
CURLOPT_TIMEOUT => 60
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
return [
'success' => ($response !== false && $http_code >= 200 && $http_code < 300),
'data' => $response,
'http_code' => $http_code,
'error' => $error
];
}
// Exemple 4 : POST Upload
echo "\n=== POST Upload de Fichier ===\n";
// Créer un fichier de test
$testFile = './test_upload.txt';
file_put_contents($testFile, 'Contenu du fichier de test');
$result = postFileUpload('https://httpbin.org/post', $testFile, 'upload', [
'description' => 'Fichier de test',
'category' => 'document'
]);
if ($result['success']) {
echo "Fichier uploadé avec succès !\n";
$response = json_decode($result['data'], true);
echo "Nom du fichier : " . $response['files']['upload'] . "\n";
}
// Nettoyer
unlink($testFile);
// POST multipart avancé
function postMultipart($url, $fields, $files = []) {
$ch = curl_init();
$postData = [];
// Ajouter les champs texte
foreach ($fields as $key => $value) {
$postData[$key] = $value;
}
// Ajouter les fichiers
foreach ($files as $fieldName => $filePath) {
if (file_exists($filePath)) {
$postData[$fieldName] = new CURLFile($filePath);
}
}
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $postData,
CURLOPT_TIMEOUT => 60,
CURLOPT_HTTPHEADER => [
'User-Agent: PHP cURL Client'
]
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
return [
'success' => ($response !== false && $http_code >= 200 && $http_code < 300),
'data' => $response,
'http_code' => $http_code,
'error' => $error,
'json' => $response ? json_decode($response, true) : null
];
}
// Exemple 5 : POST multipart avancé
echo "\n=== POST Multipart Avancé ===\n";
// Créer plusieurs fichiers de test
$textFile = './document.txt';
$jsonFile = './data.json';
file_put_contents($textFile, 'Document texte de test');
file_put_contents($jsonFile, json_encode(['test' => true, 'data' => 'exemple']));
$result = postMultipart('https://httpbin.org/post',
[
'title' => 'Upload multiple',
'description' => 'Test avec plusieurs fichiers',
'user_id' => 123
],
[
'document' => $textFile,
'metadata' => $jsonFile
]
);
if ($result['success']) {
echo "Upload multiple réussi !\n";
$response = $result['json'];
echo "Fichiers uploadés :\n";
foreach ($response['files'] as $field => $content) {
echo "- $field: " . strlen($content) . " bytes\n";
}
echo "Champs form :\n";
foreach ($response['form'] as $field => $value) {
echo "- $field: $value\n";
}
}
// Nettoyer
unlink($textFile);
unlink($jsonFile);
// Classe POST Manager pour réutilisation
class PostManager {
private $baseUrl;
private $defaultHeaders;
public function __construct($baseUrl, $defaultHeaders = []) {
$this->baseUrl = rtrim($baseUrl, '/');
$this->defaultHeaders = $defaultHeaders;
}
public function post($endpoint, $data, $headers = []) {
$url = $this->baseUrl . '/' . ltrim($endpoint, '/');
$headers = array_merge($this->defaultHeaders, $headers);
return postJsonRequest($url, $data, $headers);
}
public function postForm($endpoint, $data, $headers = []) {
$url = $this->baseUrl . '/' . ltrim($endpoint, '/');
$headers = array_merge($this->defaultHeaders, $headers);
return postFormRequest($url, $data, $headers);
}
}
// Utilisation de la classe
echo "\n=== Utilisation PostManager ===\n";
$api = new PostManager('https://jsonplaceholder.typicode.com', [
'User-Agent: Mon API Client/1.0'
]);
$newPost = $api->post('posts', [
'title' => 'Post via PostManager',
'body' => 'Contenu du post',
'userId' => 1
]);
if ($newPost['success']) {
echo "Post créé via PostManager : ID " . $newPost['json']['id'] . "\n";
}
?>
✏️ PUT, PATCH & DELETE
html
<?php
/**
* Requêtes PUT, PATCH et DELETE avec cURL
*/
// PUT Request - Remplacer complètement une ressource
function putRequest($url, $data, $headers = []) {
$ch = curl_init();
$defaultHeaders = [
'Content-Type: application/json',
'Accept: application/json'
];
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_POSTFIELDS => json_encode($data),
CURLOPT_HTTPHEADER => array_merge($defaultHeaders, $headers),
CURLOPT_TIMEOUT => 30
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
return [
'success' => ($response !== false && $http_code >= 200 && $http_code < 300),
'data' => $response,
'http_code' => $http_code,
'error' => $error,
'json' => $response ? json_decode($response, true) : null
];
}
// PATCH Request - Mise à jour partielle
function patchRequest($url, $data, $headers = []) {
$ch = curl_init();
$defaultHeaders = [
'Content-Type: application/json',
'Accept: application/json'
];
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_POSTFIELDS => json_encode($data),
CURLOPT_HTTPHEADER => array_merge($defaultHeaders, $headers),
CURLOPT_TIMEOUT => 30
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
return [
'success' => ($response !== false && $http_code >= 200 && $http_code < 300),
'data' => $response,
'http_code' => $http_code,
'error' => $error,
'json' => $response ? json_decode($response, true) : null
];
}
// DELETE Request - Supprimer une ressource
function deleteRequest($url, $headers = []) {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => $headers,
CURLOPT_TIMEOUT => 30
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
return [
'success' => ($response !== false && ($http_code == 200 || $http_code == 204)),
'data' => $response,
'http_code' => $http_code,
'error' => $error,
'json' => $response ? json_decode($response, true) : null
];
}
// Exemple 1 : PUT - Remplacer un post complètement
echo "=== PUT Request ===\n";
$updatedPost = [
'id' => 1,
'title' => 'Post mis à jour complètement',
'body' => 'Nouveau contenu complet du post',
'userId' => 1
];
$result = putRequest('https://jsonplaceholder.typicode.com/posts/1', $updatedPost);
if ($result['success']) {
echo "Post mis à jour avec PUT !\n";
echo "Nouveau titre : " . $result['json']['title'] . "\n";
echo "Code HTTP : " . $result['http_code'] . "\n";
} else {
echo "Erreur PUT : " . $result['error'] . "\n";
}
// Exemple 2 : PATCH - Mise à jour partielle
echo "\n=== PATCH Request ===\n";
$partialUpdate = [
'title' => 'Titre modifié avec PATCH'
];
$result = patchRequest('https://jsonplaceholder.typicode.com/posts/1', $partialUpdate);
if ($result['success']) {
echo "Post mis à jour avec PATCH !\n";
echo "Titre modifié : " . $result['json']['title'] . "\n";
echo "Body conservé : " . substr($result['json']['body'], 0, 30) . "...\n";
} else {
echo "Erreur PATCH : " . $result['error'] . "\n";
}
// Exemple 3 : DELETE
echo "\n=== DELETE Request ===\n";
$result = deleteRequest('https://jsonplaceholder.typicode.com/posts/1');
if ($result['success']) {
echo "Post supprimé avec succès !\n";
echo "Code HTTP : " . $result['http_code'] . "\n";
} else {
echo "Erreur DELETE : " . $result['error'] . "\n";
}
// Classe complète pour toutes les méthodes HTTP
class HttpClient {
private $baseUrl;
private $defaultHeaders;
private $timeout;
public function __construct($baseUrl = '', $options = []) {
$this->baseUrl = rtrim($baseUrl, '/');
$this->defaultHeaders = $options['headers'] ?? [];
$this->timeout = $options['timeout'] ?? 30;
}
private function makeRequest($method, $url, $data = null, $headers = []) {
$ch = curl_init();
$fullUrl = $this->baseUrl ? $this->baseUrl . '/' . ltrim($url, '/') : $url;
$allHeaders = array_merge($this->defaultHeaders, $headers);
$options = [
CURLOPT_URL => $fullUrl,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $this->timeout,
CURLOPT_HTTPHEADER => $allHeaders,
CURLOPT_USERAGENT => 'PHP HttpClient/1.0'
];
switch (strtoupper($method)) {
case 'GET':
// Rien à ajouter, GET par défaut
break;
case 'POST':
$options[CURLOPT_POST] = true;
if ($data) {
$options[CURLOPT_POSTFIELDS] = $this->prepareData($data, $allHeaders);
}
break;
case 'PUT':
case 'PATCH':
case 'DELETE':
$options[CURLOPT_CUSTOMREQUEST] = strtoupper($method);
if ($data) {
$options[CURLOPT_POSTFIELDS] = $this->prepareData($data, $allHeaders);
}
break;
}
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$info = curl_getinfo($ch);
$error = curl_error($ch);
curl_close($ch);
return [
'success' => ($response !== false && $info['http_code'] >= 200 && $info['http_code'] < 300),
'data' => $response,
'info' => $info,
'error' => $error,
'json' => $response ? json_decode($response, true) : null
];
}
private function prepareData($data, $headers) {
// Vérifier si c'est du JSON
foreach ($headers as $header) {
if (stripos($header, 'content-type: application/json') !== false) {
return json_encode($data);
}
}
// Sinon, form data
return is_array($data) ? http_build_query($data) : $data;
}
public function get($url, $headers = []) {
return $this->makeRequest('GET', $url, null, $headers);
}
public function post($url, $data, $headers = []) {
return $this->makeRequest('POST', $url, $data, $headers);
}
public function put($url, $data, $headers = []) {
return $this->makeRequest('PUT', $url, $data, $headers);
}
public function patch($url, $data, $headers = []) {
return $this->makeRequest('PATCH', $url, $data, $headers);
}
public function delete($url, $headers = []) {
return $this->makeRequest('DELETE', $url, null, $headers);
}
// Méthode pour définir l'authentification
public function setAuth($token, $type = 'Bearer') {
$this->defaultHeaders[] = "Authorization: $type $token";
return $this;
}
// Méthode pour définir le content-type JSON
public function asJson() {
$this->defaultHeaders[] = 'Content-Type: application/json';
$this->defaultHeaders[] = 'Accept: application/json';
return $this;
}
}
// Exemples d'utilisation de la classe HttpClient
echo "\n=== Utilisation HttpClient ===\n";
// Client pour JSONPlaceholder
$client = new HttpClient('https://jsonplaceholder.typicode.com');
$client->asJson();
// GET
$posts = $client->get('posts?_limit=3');
if ($posts['success']) {
echo "GET : " . count($posts['json']) . " posts récupérés\n";
}
// POST
$newPost = $client->post('posts', [
'title' => 'Nouveau post via HttpClient',
'body' => 'Contenu du post',
'userId' => 1
]);
if ($newPost['success']) {
echo "POST : Post créé avec ID " . $newPost['json']['id'] . "\n";
}
// PUT
$updatedPost = $client->put('posts/1', [
'id' => 1,
'title' => 'Post mis à jour via HttpClient',
'body' => 'Contenu mis à jour',
'userId' => 1
]);
if ($updatedPost['success']) {
echo "PUT : Post mis à jour\n";
}
// PATCH
$patchedPost = $client->patch('posts/1', [
'title' => 'Titre patché via HttpClient'
]);
if ($patchedPost['success']) {
echo "PATCH : Titre mis à jour\n";
}
// DELETE
$deleted = $client->delete('posts/1');
if ($deleted['success']) {
echo "DELETE : Post supprimé\n";
}
// Client avec authentification
echo "\n=== Client avec Authentification ===\n";
$authClient = new HttpClient('https://httpbin.org');
$authClient->asJson()->setAuth('mon_token_secret');
$response = $authClient->get('bearer');
if ($response['success']) {
echo "Authentification vérifiée : " . $response['json']['authenticated'] . "\n";
echo "Token : " . $response['json']['token'] . "\n";
}
?>
Authentification et Sécurité
API Keys, Bearer Tokens, OAuth et autres méthodes d'authentification sécurisées.
🔐 Méthodes d'Authentification
html
<?php
/**
* Méthodes d'authentification avec cURL
*/
// 1. API Key - Dans les headers
function apiKeyAuth($url, $apiKey, $headerName = 'X-API-Key') {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"$headerName: $apiKey",
'Accept: application/json'
]
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return [
'success' => ($response !== false && $http_code == 200),
'data' => $response,
'http_code' => $http_code
];
}
// 2. Bearer Token (JWT)
function bearerTokenAuth($url, $token) {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer $token",
'Accept: application/json',
'Content-Type: application/json'
]
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return [
'success' => ($response !== false && $http_code == 200),
'data' => $response,
'http_code' => $http_code
];
}
// 3. Basic Authentication
function basicAuth($url, $username, $password) {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
CURLOPT_USERPWD => "$username:$password",
CURLOPT_HTTPHEADER => [
'Accept: application/json'
]
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return [
'success' => ($response !== false && $http_code == 200),
'data' => $response,
'http_code' => $http_code
];
}
// 4. Digest Authentication
function digestAuth($url, $username, $password) {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPAUTH => CURLAUTH_DIGEST,
CURLOPT_USERPWD => "$username:$password"
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return [
'success' => ($response !== false && $http_code == 200),
'data' => $response,
'http_code' => $http_code
];
}
// 5. OAuth 2.0 - Récupération de token
function getOAuthToken($tokenUrl, $clientId, $clientSecret, $scope = '') {
$ch = curl_init();
$postData = [
'grant_type' => 'client_credentials',
'client_id' => $clientId,
'client_secret' => $clientSecret
];
if ($scope) {
$postData['scope'] = $scope;
}
curl_setopt_array($ch, [
CURLOPT_URL => $tokenUrl,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($postData),
CURLOPT_HTTPHEADER => [
'Content-Type: application/x-www-form-urlencoded',
'Accept: application/json'
]
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($response && $http_code == 200) {
$data = json_decode($response, true);
return [
'success' => true,
'access_token' => $data['access_token'],
'token_type' => $data['token_type'] ?? 'Bearer',
'expires_in' => $data['expires_in'] ?? 3600
];
}
return ['success' => false, 'error' => 'Failed to get token'];
}
// 6. OAuth 2.0 - Utilisation du token
function oauthApiCall($url, $accessToken, $tokenType = 'Bearer') {
return bearerTokenAuth($url, $accessToken);
}
// Exemples d'utilisation
echo "=== Tests d'Authentification ===\n";
// Test API Key
echo "\n--- API Key ---\n";
$result = apiKeyAuth('https://httpbin.org/headers', 'ma_cle_api_secrete');
if ($result['success']) {
$response = json_decode($result['data'], true);
echo "API Key envoyée : " . $response['headers']['X-Api-Key'] . "\n";
}
// Test Bearer Token
echo "\n--- Bearer Token ---\n";
$result = bearerTokenAuth('https://httpbin.org/bearer', 'mon_token_jwt');
if ($result['success']) {
$response = json_decode($result['data'], true);
echo "Token authentifié : " . ($response['authenticated'] ? 'Oui' : 'Non') . "\n";
echo "Token reçu : " . $response['token'] . "\n";
}
// Test Basic Auth
echo "\n--- Basic Auth ---\n";
$result = basicAuth('https://httpbin.org/basic-auth/user/pass', 'user', 'pass');
if ($result['success']) {
$response = json_decode($result['data'], true);
echo "Utilisateur authentifié : " . $response['user'] . "\n";
}
/**
* Classe d'authentification avancée
*/
class AuthManager {
private $tokens = [];
private $config = [];
public function __construct($config = []) {
$this->config = $config;
}
// Stocker un token avec expiration
public function storeToken($service, $token, $expiresIn = 3600) {
$this->tokens[$service] = [
'token' => $token,
'expires_at' => time() + $expiresIn,
'created_at' => time()
];
}
// Récupérer un token valide
public function getToken($service) {
if (!isset($this->tokens[$service])) {
return null;
}
$tokenData = $this->tokens[$service];
// Vérifier expiration (avec marge de 5 minutes)
if (time() >= ($tokenData['expires_at'] - 300)) {
unset($this->tokens[$service]);
return null;
}
return $tokenData['token'];
}
// Authentification automatique avec refresh
public function authenticatedRequest($url, $service, $options = []) {
$token = $this->getToken($service);
// Si pas de token ou expiré, en récupérer un nouveau
if (!$token && isset($this->config[$service])) {
$config = $this->config[$service];
switch ($config['type']) {
case 'oauth':
$tokenResult = getOAuthToken(
$config['token_url'],
$config['client_id'],
$config['client_secret'],
$config['scope'] ?? ''
);
if ($tokenResult['success']) {
$this->storeToken($service, $tokenResult['access_token'], $tokenResult['expires_in']);
$token = $tokenResult['access_token'];
}
break;
case 'api_key':
$token = $config['key'];
break;
}
}
if (!$token) {
return ['success' => false, 'error' => 'No valid token available'];
}
// Faire la requête avec le token
$config = $this->config[$service];
switch ($config['type']) {
case 'oauth':
return bearerTokenAuth($url, $token);
case 'api_key':
return apiKeyAuth($url, $token, $config['header'] ?? 'X-API-Key');
default:
return ['success' => false, 'error' => 'Unknown auth type'];
}
}
}
// Configuration des services
$authManager = new AuthManager([
'github' => [
'type' => 'oauth',
'token_url' => 'https://github.com/login/oauth/access_token',
'client_id' => 'your_client_id',
'client_secret' => 'your_client_secret',
'scope' => 'repo user'
],
'weather_api' => [
'type' => 'api_key',
'key' => 'your_weather_api_key',
'header' => 'X-API-Key'
]
]);
// Utilisation
echo "\n=== AuthManager ===\n";
// Stocker manuellement un token pour test
$authManager->storeToken('test_service', 'test_token_123', 3600);
// Récupérer le token
$token = $authManager->getToken('test_service');
echo "Token récupéré : " . ($token ? substr($token, 0, 10) . '...' : 'Aucun') . "\n";
/**
* Sécurité SSL et certificats
*/
function secureRequest($url, $options = []) {
$ch = curl_init();
$defaultOptions = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_CAINFO => '/path/to/cacert.pem', // Certificats CA
CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1_2,
CURLOPT_TIMEOUT => 30
];
curl_setopt_array($ch, array_merge($defaultOptions, $options));
$response = curl_exec($ch);
$info = curl_getinfo($ch);
$error = curl_error($ch);
curl_close($ch);
// Vérifications supplémentaires
$security_checks = [
'ssl_verify_result' => $info['ssl_verify_result'] === 0,
'scheme' => parse_url($url, PHP_URL_SCHEME) === 'https',
'cert_info' => $info['certinfo'] ?? null
];
return [
'success' => ($response !== false && $info['http_code'] >= 200 && $info['http_code'] < 300),
'data' => $response,
'info' => $info,
'error' => $error,
'security' => $security_checks
];
}
// Test sécurisé
echo "\n=== Requête Sécurisée ===\n";
$result = secureRequest('https://httpbin.org/get');
if ($result['success']) {
echo "Connexion sécurisée établie\n";
echo "SSL vérifié : " . ($result['security']['ssl_verify_result'] ? 'Oui' : 'Non') . "\n";
echo "HTTPS : " . ($result['security']['scheme'] ? 'Oui' : 'Non') . "\n";
}
?>
Upload de Fichiers et Multipart
Upload de fichiers, données multipart et gestion des gros volumes avec cURL.
📤 Upload de Fichiers avec cURL
html
<?php
/**
* Upload de fichiers avec cURL PHP
*/
// Upload simple d'un fichier
function uploadFile($url, $filePath, $fieldName = 'file', $additionalData = []) {
if (!file_exists($filePath)) {
return ['success' => false, 'error' => 'Fichier non trouvé : ' . $filePath];
}
$ch = curl_init();
// Préparer les données
$postData = $additionalData;
$postData[$fieldName] = new CURLFile($filePath);
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $postData,
CURLOPT_TIMEOUT => 120, // Timeout plus long pour upload
CURLOPT_HTTPHEADER => [
'User-Agent: PHP Upload Client'
]
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$upload_info = curl_getinfo($ch);
$error = curl_error($ch);
curl_close($ch);
return [
'success' => ($response !== false && $http_code >= 200 && $http_code < 300),
'data' => $response,
'http_code' => $http_code,
'error' => $error,
'upload_info' => $upload_info,
'json' => $response ? json_decode($response, true) : null
];
}
// Upload multiple de fichiers
function uploadMultipleFiles($url, $files, $additionalData = []) {
$ch = curl_init();
$postData = $additionalData;
// Ajouter tous les fichiers
foreach ($files as $fieldName => $filePath) {
if (file_exists($filePath)) {
$postData[$fieldName] = new CURLFile($filePath);
}
}
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $postData,
CURLOPT_TIMEOUT => 300 // 5 minutes pour uploads multiples
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
return [
'success' => ($response !== false && $http_code >= 200 && $http_code < 300),
'data' => $response,
'http_code' => $http_code,
'error' => $error,
'json' => $response ? json_decode($response, true) : null
];
}
// Upload avec barre de progression
function uploadWithProgress($url, $filePath, $fieldName = 'file') {
if (!file_exists($filePath)) {
return ['success' => false, 'error' => 'Fichier non trouvé'];
}
$ch = curl_init();
// Fonction de callback pour la progression
$progressCallback = function($resource, $downloadSize, $downloaded, $uploadSize, $uploaded) {
if ($uploadSize > 0) {
$percent = round(($uploaded / $uploadSize) * 100, 2);
echo "\rUpload en cours : {$percent}% ({$uploaded}/{$uploadSize} bytes)";
}
};
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
$fieldName => new CURLFile($filePath)
],
CURLOPT_NOPROGRESS => false,
CURLOPT_PROGRESSFUNCTION => $progressCallback,
CURLOPT_TIMEOUT => 300
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
echo "\n"; // Nouvelle ligne après la progression
return [
'success' => ($response !== false && $http_code >= 200 && $http_code < 300),
'data' => $response,
'http_code' => $http_code,
'error' => $error
];
}
// Upload de données en base64
function uploadBase64($url, $base64Data, $filename, $mimetype = 'application/octet-stream') {
$ch = curl_init();
// Créer un fichier temporaire
$tempFile = tempnam(sys_get_temp_dir(), 'curl_upload_');
file_put_contents($tempFile, base64_decode($base64Data));
$postData = [
'file' => new CURLFile($tempFile, $mimetype, $filename)
];
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $postData,
CURLOPT_TIMEOUT => 120
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
// Nettoyer le fichier temporaire
unlink($tempFile);
return [
'success' => ($response !== false && $http_code >= 200 && $http_code < 300),
'data' => $response,
'http_code' => $http_code,
'error' => $error
];
}
// Exemples d'utilisation
echo "=== Tests d'Upload ===\n";
// Créer des fichiers de test
$testFile1 = './test_document.txt';
$testFile2 = './test_image.jpg';
$testFile3 = './test_data.json';
file_put_contents($testFile1, 'Contenu du document de test');
file_put_contents($testFile2, base64_decode('/9j/4AAQSkZJRgABAQEAAAAAAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwA/gAA')); // Mini JPEG
file_put_contents($testFile3, json_encode(['test' => true, 'data' => 'exemple']));
// Test 1 : Upload simple
echo "\n--- Upload Simple ---\n";
$result = uploadFile('https://httpbin.org/post', $testFile1, 'document', [
'description' => 'Document de test',
'category' => 'text'
]);
if ($result['success']) {
echo "Upload réussi !\n";
echo "Taille uploadée : " . $result['upload_info']['size_upload'] . " bytes\n";
echo "Temps d'upload : " . $result['upload_info']['total_time'] . " secondes\n";
} else {
echo "Erreur upload : " . $result['error'] . "\n";
}
// Test 2 : Upload multiple
echo "\n--- Upload Multiple ---\n";
$result = uploadMultipleFiles('https://httpbin.org/post', [
'document' => $testFile1,
'image' => $testFile2,
'data' => $testFile3
], [
'title' => 'Upload multiple',
'user_id' => 123
]);
if ($result['success']) {
echo "Upload multiple réussi !\n";
$response = $result['json'];
echo "Fichiers uploadés :\n";
foreach ($response['files'] as $field => $content) {
echo "- $field: " . strlen($content) . " bytes\n";
}
}
// Test 3 : Upload avec progression
echo "\n--- Upload avec Progression ---\n";
$result = uploadWithProgress('https://httpbin.org/post', $testFile1, 'file_with_progress');
// Nettoyer les fichiers de test
unlink($testFile1);
unlink($testFile2);
unlink($testFile3);
/**
* Classe avancée pour upload de fichiers
*/
class FileUploader {
private $url;
private $timeout;
private $maxFileSize;
private $allowedTypes;
public function __construct($url, $options = []) {
$this->url = $url;
$this->timeout = $options['timeout'] ?? 120;
$this->maxFileSize = $options['max_file_size'] ?? 10 * 1024 * 1024; // 10MB
$this->allowedTypes = $options['allowed_types'] ?? [];
}
public function upload($filePath, $options = []) {
// Validations
$validation = $this->validateFile($filePath);
if (!$validation['valid']) {
return ['success' => false, 'error' => $validation['error']];
}
$ch = curl_init();
$fieldName = $options['field_name'] ?? 'file';
$additionalData = $options['data'] ?? [];
$headers = $options['headers'] ?? [];
$postData = $additionalData;
$postData[$fieldName] = new CURLFile(
$filePath,
$this->getMimeType($filePath),
$options['filename'] ?? basename($filePath)
);
curl_setopt_array($ch, [
CURLOPT_URL => $this->url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $postData,
CURLOPT_TIMEOUT => $this->timeout,
CURLOPT_HTTPHEADER => $headers
]);
// Callback de progression si demandé
if (isset($options['progress_callback'])) {
curl_setopt($ch, CURLOPT_NOPROGRESS, false);
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, $options['progress_callback']);
}
$response = curl_exec($ch);
$info = curl_getinfo($ch);
$error = curl_error($ch);
curl_close($ch);
return [
'success' => ($response !== false && $info['http_code'] >= 200 && $info['http_code'] < 300),
'data' => $response,
'info' => $info,
'error' => $error,
'json' => $response ? json_decode($response, true) : null
];
}
private function validateFile($filePath) {
if (!file_exists($filePath)) {
return ['valid' => false, 'error' => 'Fichier non trouvé'];
}
$fileSize = filesize($filePath);
if ($fileSize > $this->maxFileSize) {
return ['valid' => false, 'error' => 'Fichier trop volumineux'];
}
if (!empty($this->allowedTypes)) {
$mimeType = $this->getMimeType($filePath);
if (!in_array($mimeType, $this->allowedTypes)) {
return ['valid' => false, 'error' => 'Type de fichier non autorisé'];
}
}
return ['valid' => true];
}
private function getMimeType($filePath) {
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $filePath);
finfo_close($finfo);
return $mimeType;
}
public function uploadChunked($filePath, $chunkSize = 1024 * 1024) {
// Upload par chunks pour gros fichiers
$fileSize = filesize($filePath);
$chunks = ceil($fileSize / $chunkSize);
echo "Upload par chunks : $chunks parties\n";
for ($i = 0; $i < $chunks; $i++) {
$start = $i * $chunkSize;
$end = min($start + $chunkSize - 1, $fileSize - 1);
$chunkData = file_get_contents($filePath, false, null, $start, $chunkSize);
// Créer un fichier temporaire pour le chunk
$tempFile = tempnam(sys_get_temp_dir(), 'chunk_');
file_put_contents($tempFile, $chunkData);
$result = $this->upload($tempFile, [
'data' => [
'chunk_index' => $i,
'total_chunks' => $chunks,
'chunk_start' => $start,
'chunk_end' => $end
]
]);
unlink($tempFile);
if (!$result['success']) {
return ['success' => false, 'error' => "Erreur chunk $i: " . $result['error']];
}
echo "Chunk $i/$chunks uploadé\n";
}
return ['success' => true, 'chunks_uploaded' => $chunks];
}
}
// Exemple d'utilisation de la classe
echo "\n=== FileUploader ===\n";
$uploader = new FileUploader('https://httpbin.org/post', [
'timeout' => 60,
'max_file_size' => 5 * 1024 * 1024, // 5MB
'allowed_types' => ['text/plain', 'application/json', 'image/jpeg']
]);
// Créer un fichier de test
$testFile = './upload_test.txt';
file_put_contents($testFile, str_repeat('Test de contenu pour upload avancé. ', 100));
$result = $uploader->upload($testFile, [
'field_name' => 'test_file',
'filename' => 'document_test.txt',
'data' => [
'description' => 'Test avec FileUploader',
'version' => '1.0'
],
'progress_callback' => function($resource, $downloadSize, $downloaded, $uploadSize, $uploaded) {
if ($uploadSize > 0) {
$percent = round(($uploaded / $uploadSize) * 100, 1);
echo "\rUpload FileUploader : {$percent}%";
}
}
]);
echo "\n";
if ($result['success']) {
echo "Upload avec FileUploader réussi !\n";
}
unlink($testFile);
?>
Exemples Avancés et Classe Complète
Classe cURL complète, gestion d'erreurs avancée et exemples d'APIs réelles.
🏗️ Classe cURL Complète et Réutilisable
html
<?php
/**
* Classe cURL complète et avancée pour toutes vos requêtes HTTP
*
* Fonctionnalités :
* - Toutes les méthodes HTTP
* - Authentification multiple
* - Gestion des erreurs avancée
* - Cache intégré
* - Retry automatique
* - Pool de connexions
* - Logging
*/
class AdvancedCurlClient {
private $baseUrl = '';
private $defaultHeaders = [];
private $defaultOptions = [];
private $timeout = 30;
private $retryCount = 3;
private $retryDelay = 1;
private $cache = null;
private $logger = null;
private $stats = [];
public function __construct($config = []) {
$this->baseUrl = rtrim($config['base_url'] ?? '', '/');
$this->defaultHeaders = $config['headers'] ?? [];
$this->timeout = $config['timeout'] ?? 30;
$this->retryCount = $config['retry_count'] ?? 3;
$this->retryDelay = $config['retry_delay'] ?? 1;
// Configuration par défaut
$this->defaultOptions = [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $this->timeout,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 5,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_USERAGENT => 'AdvancedCurlClient/1.0 PHP',
CURLOPT_ENCODING => '',
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2_0
];
if (isset($config['cache']) && $config['cache']) {
$this->cache = new SimpleCache();
}
if (isset($config['logger'])) {
$this->logger = $config['logger'];
}
}
// Méthodes HTTP principales
public function get($url, $options = []) {
return $this->request('GET', $url, null, $options);
}
public function post($url, $data = null, $options = []) {
return $this->request('POST', $url, $data, $options);
}
public function put($url, $data = null, $options = []) {
return $this->request('PUT', $url, $data, $options);
}
public function patch($url, $data = null, $options = []) {
return $this->request('PATCH', $url, $data, $options);
}
public function delete($url, $options = []) {
return $this->request('DELETE', $url, null, $options);
}
public function head($url, $options = []) {
return $this->request('HEAD', $url, null, $options);
}
public function options($url, $options = []) {
return $this->request('OPTIONS', $url, null, $options);
}
// Méthode principale de requête
private function request($method, $url, $data = null, $options = []) {
$fullUrl = $this->buildUrl($url);
$cacheKey = $this->getCacheKey($method, $fullUrl, $data);
// Vérifier le cache pour GET
if ($method === 'GET' && $this->cache && isset($options['cache_ttl'])) {
$cached = $this->cache->get($cacheKey, $options['cache_ttl']);
if ($cached !== null) {
$this->log('info', "Cache hit for $fullUrl");
return $cached;
}
}
$attempt = 0;
$lastError = null;
while ($attempt < $this->retryCount) {
try {
$result = $this->executeRequest($method, $fullUrl, $data, $options);
// Si succès, mettre en cache si nécessaire
if ($result['success'] && $method === 'GET' && $this->cache && isset($options['cache_ttl'])) {
$this->cache->set($cacheKey, $result);
}
$this->updateStats($method, $result);
return $result;
} catch (Exception $e) {
$lastError = $e;
$attempt++;
$this->log('warning', "Attempt $attempt failed for $fullUrl: " . $e->getMessage());
if ($attempt < $this->retryCount) {
sleep($this->retryDelay * $attempt);
}
}
}
// Toutes les tentatives ont échoué
$result = [
'success' => false,
'error' => $lastError ? $lastError->getMessage() : 'All retry attempts failed',
'attempts' => $attempt
];
$this->updateStats($method, $result);
return $result;
}
private function executeRequest($method, $url, $data, $options) {
$startTime = microtime(true);
$ch = curl_init();
// Options de base
$curlOptions = $this->defaultOptions;
$curlOptions[CURLOPT_URL] = $url;
// Headers
$headers = array_merge($this->defaultHeaders, $options['headers'] ?? []);
if (!empty($headers)) {
$curlOptions[CURLOPT_HTTPHEADER] = $this->formatHeaders($headers);
}
// Méthode HTTP et données
switch (strtoupper($method)) {
case 'GET':
// Rien à faire, GET par défaut
break;
case 'POST':
$curlOptions[CURLOPT_POST] = true;
if ($data !== null) {
$curlOptions[CURLOPT_POSTFIELDS] = $this->prepareData($data, $headers);
}
break;
case 'PUT':
case 'PATCH':
case 'DELETE':
case 'HEAD':
case 'OPTIONS':
$curlOptions[CURLOPT_CUSTOMREQUEST] = strtoupper($method);
if ($data !== null) {
$curlOptions[CURLOPT_POSTFIELDS] = $this->prepareData($data, $headers);
}
break;
}
// Options personnalisées
if (isset($options['curl_options'])) {
$curlOptions = array_replace($curlOptions, $options['curl_options']);
}
curl_setopt_array($ch, $curlOptions);
$response = curl_exec($ch);
$info = curl_getinfo($ch);
$error = curl_error($ch);
$errno = curl_errno($ch);
curl_close($ch);
$duration = microtime(true) - $startTime;
// Analyser la réponse
$result = [
'success' => ($response !== false && $info['http_code'] >= 200 && $info['http_code'] < 300),
'data' => $response,
'info' => $info,
'error' => $error,
'errno' => $errno,
'duration' => $duration,
'url' => $url,
'method' => $method
];
// Décoder JSON si applicable
if ($response && $this->isJsonResponse($info)) {
$result['json'] = json_decode($response, true);
$result['json_error'] = json_last_error();
}
// Log de la requête
$this->log('info', sprintf(
'%s %s - %d - %.3fs',
$method,
$url,
$info['http_code'],
$duration
));
// Vérifier les erreurs
if ($response === false) {
throw new Exception("cURL Error ($errno): $error");
}
if ($info['http_code'] >= 400) {
$errorMsg = "HTTP Error {$info['http_code']}";
if (isset($result['json']['message'])) {
$errorMsg .= ": " . $result['json']['message'];
}
throw new Exception($errorMsg);
}
return $result;
}
// Méthodes utilitaires
private function buildUrl($url) {
if (filter_var($url, FILTER_VALIDATE_URL)) {
return $url;
}
return $this->baseUrl . '/' . ltrim($url, '/');
}
private function formatHeaders($headers) {
$formatted = [];
foreach ($headers as $key => $value) {
if (is_numeric($key)) {
$formatted[] = $value;
} else {
$formatted[] = "$key: $value";
}
}
return $formatted;
}
private function prepareData($data, $headers) {
// Détecter le type de contenu
$contentType = $this->getContentType($headers);
if (strpos($contentType, 'application/json') !== false) {
return json_encode($data);
} elseif (strpos($contentType, 'application/x-www-form-urlencoded') !== false) {
return http_build_query($data);
} elseif (is_array($data) || is_object($data)) {
return json_encode($data);
}
return $data;
}
private function getContentType($headers) {
foreach ($headers as $key => $value) {
if (is_string($key) && strtolower($key) === 'content-type') {
return $value;
} elseif (is_string($value) && stripos($value, 'content-type:') === 0) {
return substr($value, 13);
}
}
return 'application/json';
}
private function isJsonResponse($info) {
$contentType = $info['content_type'] ?? '';
return strpos($contentType, 'application/json') !== false;
}
private function getCacheKey($method, $url, $data) {
return md5($method . $url . serialize($data));
}
private function log($level, $message) {
if ($this->logger) {
$this->logger->log($level, $message);
}
}
private function updateStats($method, $result) {
if (!isset($this->stats[$method])) {
$this->stats[$method] = ['total' => 0, 'success' => 0, 'error' => 0];
}
$this->stats[$method]['total']++;
if ($result['success']) {
$this->stats[$method]['success']++;
} else {
$this->stats[$method]['error']++;
}
}
// Méthodes de configuration
public function setAuth($token, $type = 'Bearer') {
$this->defaultHeaders['Authorization'] = "$type $token";
return $this;
}
public function setApiKey($key, $header = 'X-API-Key') {
$this->defaultHeaders[$header] = $key;
return $this;
}
public function asJson() {
$this->defaultHeaders['Content-Type'] = 'application/json';
$this->defaultHeaders['Accept'] = 'application/json';
return $this;
}
public function withHeaders($headers) {
$this->defaultHeaders = array_merge($this->defaultHeaders, $headers);
return $this;
}
public function setTimeout($timeout) {
$this->timeout = $timeout;
$this->defaultOptions[CURLOPT_TIMEOUT] = $timeout;
return $this;
}
public function enableDebug() {
$this->defaultOptions[CURLOPT_VERBOSE] = true;
return $this;
}
// Méthodes d'information
public function getStats() {
return $this->stats;
}
public function resetStats() {
$this->stats = [];
return $this;
}
}
/**
* Cache simple pour les réponses
*/
class SimpleCache {
private $cacheDir;
public function __construct($cacheDir = './cache') {
$this->cacheDir = $cacheDir;
if (!is_dir($cacheDir)) {
mkdir($cacheDir, 0755, true);
}
}
public function get($key, $ttl = 3600) {
$file = $this->cacheDir . '/' . md5($key) . '.cache';
if (file_exists($file) && (time() - filemtime($file)) < $ttl) {
return unserialize(file_get_contents($file));
}
return null;
}
public function set($key, $data) {
$file = $this->cacheDir . '/' . md5($key) . '.cache';
file_put_contents($file, serialize($data));
}
public function delete($key) {
$file = $this->cacheDir . '/' . md5($key) . '.cache';
if (file_exists($file)) {
unlink($file);
}
}
public function clear() {
$files = glob($this->cacheDir . '/*.cache');
foreach ($files as $file) {
unlink($file);
}
}
}
/**
* Logger simple
*/
class SimpleLogger {
private $logFile;
public function __construct($logFile = './curl.log') {
$this->logFile = $logFile;
}
public function log($level, $message) {
$timestamp = date('Y-m-d H:i:s');
$logEntry = "[$timestamp] [$level] $message\n";
file_put_contents($this->logFile, $logEntry, FILE_APPEND | LOCK_EX);
}
}
// Exemples d'utilisation
echo "=== AdvancedCurlClient ===\n";
// Configuration du client
$client = new AdvancedCurlClient([
'base_url' => 'https://jsonplaceholder.typicode.com',
'timeout' => 30,
'retry_count' => 3,
'cache' => true,
'logger' => new SimpleLogger()
]);
$client->asJson();
// Exemples de requêtes
echo "\n--- Test GET ---\n";
$posts = $client->get('posts', ['cache_ttl' => 300]);
if ($posts['success']) {
echo "GET réussi : " . count($posts['json']) . " posts\n";
echo "Durée : " . round($posts['duration'] * 1000, 2) . "ms\n";
}
echo "\n--- Test POST ---\n";
$newPost = $client->post('posts', [
'title' => 'Test avec AdvancedCurlClient',
'body' => 'Contenu du post',
'userId' => 1
]);
if ($newPost['success']) {
echo "POST réussi : ID " . $newPost['json']['id'] . "\n";
}
echo "\n--- Test avec authentification ---\n";
$authClient = new AdvancedCurlClient([
'base_url' => 'https://httpbin.org'
]);
$authClient->setAuth('mon_token_secret')->asJson();
$authTest = $authClient->get('bearer');
if ($authTest['success']) {
echo "Auth réussie : " . ($authTest['json']['authenticated'] ? 'Oui' : 'Non') . "\n";
}
echo "\n--- Statistiques ---\n";
$stats = $client->getStats();
foreach ($stats as $method => $data) {
$successRate = round(($data['success'] / $data['total']) * 100, 1);
echo "$method: {$data['total']} requêtes, {$successRate}% de succès\n";
}
?>
🌐 Exemples avec APIs Réelles
html
<?php
/**
* Exemples d'utilisation avec des APIs réelles
* Remplacez les tokens par vos vraies clés d'API
*/
// Configuration du client avancé
$apiClient = new AdvancedCurlClient([
'timeout' => 30,
'retry_count' => 3,
'cache' => true,
'logger' => new SimpleLogger('./api_requests.log')
]);
/**
* 1. API OpenWeatherMap - Météo
*/
function getWeatherData($city, $apiKey) {
global $apiClient;
$weatherClient = clone $apiClient;
$weatherClient->baseUrl = 'https://api.openweathermap.org/data/2.5';
$params = http_build_query([
'q' => $city,
'appid' => $apiKey,
'units' => 'metric',
'lang' => 'fr'
]);
$result = $weatherClient->get("weather?{$params}", ['cache_ttl' => 600]); // 10 min cache
if ($result['success']) {
$weather = $result['json'];
return [
'city' => $weather['name'],
'temperature' => $weather['main']['temp'],
'description' => $weather['weather'][0]['description'],
'humidity' => $weather['main']['humidity'],
'wind_speed' => $weather['wind']['speed']
];
}
return null;
}
// Utilisation
echo "=== API Météo ===\n";
$weather = getWeatherData('Paris', 'YOUR_OPENWEATHER_API_KEY');
if ($weather) {
echo "Météo à {$weather['city']} :\n";
echo "- Température : {$weather['temperature']}°C\n";
echo "- Description : {$weather['description']}\n";
echo "- Humidité : {$weather['humidity']}%\n";
}
/**
* 2. API GitHub - Informations sur un repository
*/
function getGitHubRepo($owner, $repo, $token = null) {
global $apiClient;
$githubClient = clone $apiClient;
$githubClient->baseUrl = 'https://api.github.com';
$githubClient->asJson();
if ($token) {
$githubClient->setAuth($token);
}
$result = $githubClient->get("repos/{$owner}/{$repo}");
if ($result['success']) {
$repoData = $result['json'];
return [
'name' => $repoData['name'],
'description' => $repoData['description'],
'stars' => $repoData['stargazers_count'],
'forks' => $repoData['forks_count'],
'language' => $repoData['language'],
'updated_at' => $repoData['updated_at']
];
}
return null;
}
// Utilisation
echo "\n=== API GitHub ===\n";
$repo = getGitHubRepo('facebook', 'react');
if ($repo) {
echo "Repository : {$repo['name']}\n";
echo "Description : {$repo['description']}\n";
echo "⭐ Stars : {$repo['stars']}\n";
echo "🍴 Forks : {$repo['forks']}\n";
echo "Langage : {$repo['language']}\n";
}
/**
* 3. API JSONPlaceholder - Test et développement
*/
function testCrudOperations() {
global $apiClient;
$testClient = clone $apiClient;
$testClient->baseUrl = 'https://jsonplaceholder.typicode.com';
$testClient->asJson();
echo "\n=== Tests CRUD avec JSONPlaceholder ===\n";
// CREATE (POST)
$newPost = [
'title' => 'Mon nouveau post',
'body' => 'Contenu du post de test',
'userId' => 1
];
$createResult = $testClient->post('posts', $newPost);
if ($createResult['success']) {
$postId = $createResult['json']['id'];
echo "✅ CREATE : Post créé avec ID {$postId}\n";
// READ (GET)
$readResult = $testClient->get("posts/{$postId}");
if ($readResult['success']) {
echo "✅ READ : Post récupéré\n";
// UPDATE (PUT)
$updatedPost = array_merge($newPost, [
'id' => $postId,
'title' => 'Titre mis à jour'
]);
$updateResult = $testClient->put("posts/{$postId}", $updatedPost);
if ($updateResult['success']) {
echo "✅ UPDATE : Post mis à jour\n";
// DELETE
$deleteResult = $testClient->delete("posts/{$postId}");
if ($deleteResult['success']) {
echo "✅ DELETE : Post supprimé\n";
}
}
}
}
}
testCrudOperations();
/**
* 4. API REST personnalisée avec authentification
*/
class CustomAPIClient {
private $client;
private $baseUrl;
private $authToken;
public function __construct($baseUrl, $credentials = []) {
$this->baseUrl = $baseUrl;
$this->client = new AdvancedCurlClient([
'base_url' => $baseUrl,
'timeout' => 30
]);
$this->client->asJson();
// Authentification si fournie
if (isset($credentials['token'])) {
$this->authToken = $credentials['token'];
$this->client->setAuth($credentials['token']);
} elseif (isset($credentials['username'], $credentials['password'])) {
$this->authenticate($credentials['username'], $credentials['password']);
}
}
private function authenticate($username, $password) {
$loginData = [
'username' => $username,
'password' => $password
];
$result = $this->client->post('auth/login', $loginData);
if ($result['success'] && isset($result['json']['token'])) {
$this->authToken = $result['json']['token'];
$this->client->setAuth($this->authToken);
return true;
}
return false;
}
public function getUsers($page = 1, $limit = 10) {
$params = http_build_query(['page' => $page, 'limit' => $limit]);
return $this->client->get("users?{$params}");
}
public function createUser($userData) {
return $this->client->post('users', $userData);
}
public function updateUser($userId, $userData) {
return $this->client->put("users/{$userId}", $userData);
}
public function deleteUser($userId) {
return $this->client->delete("users/{$userId}");
}
public function uploadFile($filePath, $description = '') {
// Upload via multipart
$postData = [
'file' => new CURLFile($filePath),
'description' => $description
];
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $this->baseUrl . '/upload',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $postData,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer {$this->authToken}"
]
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return [
'success' => ($response !== false && $http_code >= 200 && $http_code < 300),
'data' => $response,
'json' => $response ? json_decode($response, true) : null
];
}
}
/**
* 5. Gestion des erreurs API et retry intelligent
*/
function smartApiCall($url, $options = []) {
$maxRetries = $options['max_retries'] ?? 3;
$baseDelay = $options['base_delay'] ?? 1;
$backoffFactor = $options['backoff_factor'] ?? 2;
for ($attempt = 1; $attempt <= $maxRetries; $attempt++) {
$result = $apiClient->get($url);
// Succès
if ($result['success']) {
return $result;
}
// Analyser le type d'erreur
$httpCode = $result['info']['http_code'] ?? 0;
if ($httpCode >= 400 && $httpCode < 500) {
// Erreur client (4xx) - ne pas retry
echo "Erreur client {$httpCode} - Arrêt des tentatives\n";
break;
}
if ($httpCode >= 500 || $httpCode === 0) {
// Erreur serveur (5xx) ou timeout - retry avec backoff
$delay = $baseDelay * pow($backoffFactor, $attempt - 1);
echo "Tentative {$attempt}/{$maxRetries} échouée - Retry dans {$delay}s\n";
if ($attempt < $maxRetries) {
sleep($delay);
}
}
}
return $result;
}
/**
* 6. API Rate Limiting et cache intelligent
*/
class RateLimitedAPI {
private $client;
private $rateLimits = [];
private $cache;
public function __construct($baseUrl) {
$this->client = new AdvancedCurlClient(['base_url' => $baseUrl]);
$this->cache = new SimpleCache();
}
public function call($endpoint, $options = []) {
// Vérifier rate limit
if ($this->isRateLimited($endpoint)) {
$waitTime = $this->getRateLimitWaitTime($endpoint);
echo "Rate limit atteint - Attente de {$waitTime}s\n";
sleep($waitTime);
}
// Vérifier cache
$cacheKey = md5($endpoint . serialize($options));
$cacheTtl = $options['cache_ttl'] ?? 300;
$cached = $this->cache->get($cacheKey, $cacheTtl);
if ($cached && !isset($options['force_refresh'])) {
echo "Réponse du cache pour {$endpoint}\n";
return $cached;
}
// Faire l'appel API
$result = $this->client->get($endpoint, $options);
// Mettre à jour les infos de rate limiting
$this->updateRateLimitInfo($endpoint, $result);
// Cache si succès
if ($result['success']) {
$this->cache->set($cacheKey, $result);
}
return $result;
}
private function isRateLimited($endpoint) {
$key = md5($endpoint);
return isset($this->rateLimits[$key]) &&
$this->rateLimits[$key]['reset_time'] > time();
}
private function getRateLimitWaitTime($endpoint) {
$key = md5($endpoint);
return max(0, $this->rateLimits[$key]['reset_time'] - time());
}
private function updateRateLimitInfo($endpoint, $result) {
$headers = $result['info']['request_header'] ?? '';
// Extraire les headers de rate limiting (format GitHub/Twitter)
if (preg_match('/X-RateLimit-Remaining: (\d+)/i', $headers, $matches)) {
$remaining = (int)$matches[1];
if ($remaining <= 1) {
$key = md5($endpoint);
$this->rateLimits[$key] = [
'reset_time' => time() + 60 // 1 minute par défaut
];
}
}
}
}
// Exemple d'utilisation
echo "\n=== API avec Rate Limiting ===\n";
$rateLimitedApi = new RateLimitedAPI('https://api.github.com');
$result = $rateLimitedApi->call('user', ['cache_ttl' => 600]);
/**
* 7. Webhooks et callbacks
*/
function handleWebhook($payload, $signature, $secret) {
// Vérifier la signature (GitHub style)
$expectedSignature = 'sha256=' . hash_hmac('sha256', $payload, $secret);
if (!hash_equals($expectedSignature, $signature)) {
http_response_code(401);
die('Signature invalide');
}
$data = json_decode($payload, true);
// Traiter le webhook
switch ($data['action'] ?? '') {
case 'opened':
echo "Nouvelle issue/PR ouverte\n";
break;
case 'closed':
echo "Issue/PR fermée\n";
break;
default:
echo "Action non gérée : " . ($data['action'] ?? 'inconnue') . "\n";
}
http_response_code(200);
echo "Webhook traité";
}
// Pour recevoir un webhook dans un script PHP
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_HUB_SIGNATURE_256'] ?? '';
$secret = 'your_webhook_secret';
handleWebhook($payload, $signature, $secret);
}
echo "\n=== Exemples terminés ===\n";
echo "Consultez les logs dans './api_requests.log' pour plus de détails.\n";
?>
Tests et Débogage
Techniques de débogage, tests automatisés et monitoring des performances.
🐛 Débogage et Monitoring
html
<?php
/**
* Outils de débogage et monitoring pour cURL
*/
// Debug complet d'une requête cURL
function debugCurlRequest($url, $options = []) {
$ch = curl_init();
// Activer le mode verbose pour debug
$debugOutput = fopen('php://temp', 'r+');
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_VERBOSE => true,
CURLOPT_STDERR => $debugOutput,
CURLOPT_HEADER => true, // Inclure headers dans la réponse
CURLOPT_TIMEOUT => 30
] + $options);
$startTime = microtime(true);
$response = curl_exec($ch);
$duration = microtime(true) - $startTime;
$info = curl_getinfo($ch);
$error = curl_error($ch);
$errno = curl_errno($ch);
// Récupérer les informations de debug
rewind($debugOutput);
$debugInfo = stream_get_contents($debugOutput);
fclose($debugOutput);
curl_close($ch);
// Séparer headers et body
$headerSize = $info['header_size'];
$headers = substr($response, 0, $headerSize);
$body = substr($response, $headerSize);
return [
'success' => ($response !== false && $info['http_code'] >= 200 && $info['http_code'] < 300),
'url' => $url,
'method' => $options[CURLOPT_CUSTOMREQUEST] ?? 'GET',
'duration' => $duration,
'info' => $info,
'headers' => $headers,
'body' => $body,
'error' => $error,
'errno' => $errno,
'debug_output' => $debugInfo,
'json' => $body ? json_decode($body, true) : null
];
}
// Performance monitor
class PerformanceMonitor {
private $metrics = [];
public function startTimer($label) {
$this->metrics[$label] = [
'start' => microtime(true),
'memory_start' => memory_get_usage(true)
];
}
public function endTimer($label) {
if (!isset($this->metrics[$label])) {
return null;
}
$metric = &$this->metrics[$label];
$metric['end'] = microtime(true);
$metric['memory_end'] = memory_get_usage(true);
$metric['duration'] = $metric['end'] - $metric['start'];
$metric['memory_used'] = $metric['memory_end'] - $metric['memory_start'];
return $metric;
}
public function getStats() {
return $this->metrics;
}
public function getReport() {
$report = "=== Performance Report ===\n";
foreach ($this->metrics as $label => $metric) {
if (isset($metric['duration'])) {
$duration = round($metric['duration'] * 1000, 2);
$memory = $this->formatBytes($metric['memory_used']);
$report .= "$label: {$duration}ms, Memory: $memory\n";
}
}
return $report;
}
private function formatBytes($bytes) {
$units = ['B', 'KB', 'MB', 'GB'];
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= (1 << (10 * $pow));
return round($bytes, 2) . ' ' . $units[$pow];
}
}
// Test avec monitoring
echo "=== Test avec Monitoring ===\n";
$monitor = new PerformanceMonitor();
$monitor->startTimer('api_call');
$result = debugCurlRequest('https://jsonplaceholder.typicode.com/posts/1');
$monitor->endTimer('api_call');
echo "Résultat de l'appel :\n";
echo "- URL : {$result['url']}\n";
echo "- Méthode : {$result['method']}\n";
echo "- Durée : " . round($result['duration'] * 1000, 2) . "ms\n";
echo "- Code HTTP : {$result['info']['http_code']}\n";
echo "- Taille réponse : " . strlen($result['body']) . " bytes\n";
if ($result['error']) {
echo "- Erreur : {$result['error']}\n";
}
echo "\n" . $monitor->getReport();
// Tests automatisés
class ApiTester {
private $baseUrl;
private $results = [];
public function __construct($baseUrl) {
$this->baseUrl = $baseUrl;
}
public function testEndpoint($endpoint, $expectedCode = 200, $options = []) {
$url = $this->baseUrl . '/' . ltrim($endpoint, '/');
$method = $options['method'] ?? 'GET';
$result = debugCurlRequest($url, $options);
$test = [
'endpoint' => $endpoint,
'method' => $method,
'expected_code' => $expectedCode,
'actual_code' => $result['info']['http_code'],
'duration' => $result['duration'],
'success' => ($result['info']['http_code'] === $expectedCode),
'error' => $result['error']
];
$this->results[] = $test;
return $test;
}
public function testResponseTime($endpoint, $maxTime = 1.0, $options = []) {
$result = debugCurlRequest($this->baseUrl . '/' . ltrim($endpoint, '/'), $options);
$test = [
'endpoint' => $endpoint,
'test_type' => 'response_time',
'max_time' => $maxTime,
'actual_time' => $result['duration'],
'success' => ($result['duration'] <= $maxTime)
];
$this->results[] = $test;
return $test;
}
public function testJsonSchema($endpoint, $requiredFields = [], $options = []) {
$result = debugCurlRequest($this->baseUrl . '/' . ltrim($endpoint, '/'), $options);
$missingFields = [];
if ($result['json']) {
foreach ($requiredFields as $field) {
if (!isset($result['json'][$field])) {
$missingFields[] = $field;
}
}
}
$test = [
'endpoint' => $endpoint,
'test_type' => 'json_schema',
'required_fields' => $requiredFields,
'missing_fields' => $missingFields,
'success' => empty($missingFields) && $result['json'] !== null
];
$this->results[] = $test;
return $test;
}
public function runTestSuite() {
echo "\n=== Suite de Tests API ===\n";
$passed = 0;
$total = count($this->results);
foreach ($this->results as $test) {
$status = $test['success'] ? '✅ PASS' : '❌ FAIL';
switch ($test['test_type'] ?? 'http') {
case 'response_time':
echo "$status Response Time {$test['endpoint']}: " .
round($test['actual_time'] * 1000, 2) . "ms (max: " .
($test['max_time'] * 1000) . "ms)\n";
break;
case 'json_schema':
echo "$status Schema {$test['endpoint']}: ";
if (!empty($test['missing_fields'])) {
echo "Missing fields: " . implode(', ', $test['missing_fields']) . "\n";
} else {
echo "All required fields present\n";
}
break;
default:
echo "$status {$test['method']} {$test['endpoint']}: " .
"{$test['actual_code']} (expected: {$test['expected_code']})\n";
}
if ($test['success']) $passed++;
}
echo "\nRésultats : $passed/$total tests passés\n";
return ['passed' => $passed, 'total' => $total, 'success_rate' => $passed / $total];
}
public function getResults() {
return $this->results;
}
}
// Exemple de tests automatisés
echo "\n=== Tests Automatisés ===\n";
$tester = new ApiTester('https://jsonplaceholder.typicode.com');
// Tests de base
$tester->testEndpoint('posts/1', 200);
$tester->testEndpoint('posts/999', 404);
$tester->testEndpoint('users', 200);
// Tests de performance
$tester->testResponseTime('posts', 2.0);
$tester->testResponseTime('users/1', 1.0);
// Tests de schema JSON
$tester->testJsonSchema('posts/1', ['id', 'title', 'body', 'userId']);
$tester->testJsonSchema('users/1', ['id', 'name', 'email']);
// Exécuter tous les tests
$results = $tester->runTestSuite();
// Health check complet
function healthCheck($endpoints) {
echo "\n=== Health Check ===\n";
$overallHealth = true;
foreach ($endpoints as $name => $url) {
$start = microtime(true);
$result = debugCurlRequest($url);
$duration = microtime(true) - $start;
$healthy = $result['success'] && $duration < 5.0;
$status = $healthy ? '🟢 UP' : '🔴 DOWN';
$overallHealth = $overallHealth && $healthy;
echo "$status $name: " . round($duration * 1000, 2) . "ms";
if (!$healthy && $result['error']) {
echo " - Error: {$result['error']}";
}
echo "\n";
}
echo "\nStatut global : " . ($overallHealth ? '🟢 HEALTHY' : '🔴 UNHEALTHY') . "\n";
return $overallHealth;
}
// Health check de plusieurs services
$services = [
'JSONPlaceholder' => 'https://jsonplaceholder.typicode.com/posts/1',
'GitHub API' => 'https://api.github.com/users/octocat',
'HTTPBin' => 'https://httpbin.org/get'
];
healthCheck($services);
?>
Collections Postman Avancées
Créer des collections complètes, automatiser les tests et gérer les environnements.
📋 Collection Postman Complète - Export JSON
📥 Import dans Postman :
1. Copiez le JSON ci-dessous
2. Dans Postman : File → Import → Raw Text
3. Collez le JSON et cliquez Import
4. Configurez vos variables d'environnement
html
{
"info": {
"name": "API Testing Collection",
"description": "Collection complète pour tester des APIs avec authentification, CRUD et gestion d'erreurs",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "Authentication",
"item": [
{
"name": "Login",
"event": [
{
"listen": "test",
"script": {
"exec": [
"pm.test('Login successful', function () {",
" pm.response.to.have.status(200);",
"});",
"",
"if (pm.response.json().token) {",
" pm.environment.set('auth_token', pm.response.json().token);",
"}"
]
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"username\": \"{{username}}\",\n \"password\": \"{{password}}\"\n}"
},
"url": {
"raw": "{{base_url}}/auth/login",
"host": ["{{base_url}}"],
"path": ["auth", "login"]
}
}
}
]
},
{
"name": "Users CRUD",
"item": [
{
"name": "Get All Users",
"event": [
{
"listen": "test",
"script": {
"exec": [
"pm.test('Status code is 200', function () {",
" pm.response.to.have.status(200);",
"});",
"",
"pm.test('Response is array', function () {",
" pm.expect(pm.response.json()).to.be.an('array');",
"});",
"",
"pm.test('Response time is less than 1000ms', function () {",
" pm.expect(pm.response.responseTime).to.be.below(1000);",
"});"
]
}
}
],
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{auth_token}}"
}
],
"url": {
"raw": "{{base_url}}/users?page={{page}}&limit={{limit}}",
"host": ["{{base_url}}"],
"path": ["users"],
"query": [
{
"key": "page",
"value": "{{page}}"
},
{
"key": "limit",
"value": "{{limit}}"
}
]
}
}
},
{
"name": "Create User",
"event": [
{
"listen": "prerequest",
"script": {
"exec": [
"pm.environment.set('random_email', 'user' + Math.floor(Math.random() * 1000) + '@example.com');"
]
}
},
{
"listen": "test",
"script": {
"exec": [
"pm.test('User created successfully', function () {",
" pm.response.to.have.status(201);",
"});",
"",
"if (pm.response.json().id) {",
" pm.environment.set('created_user_id', pm.response.json().id);",
"}"
]
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
},
{
"key": "Authorization",
"value": "Bearer {{auth_token}}"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"name\": \"Test User\",\n \"email\": \"{{random_email}}\",\n \"role\": \"user\"\n}"
},
"url": {
"raw": "{{base_url}}/users",
"host": ["{{base_url}}"],
"path": ["users"]
}
}
}
]
},
{
"name": "File Upload",
"item": [
{
"name": "Upload File",
"request": {
"method": "POST",
"header": [
{
"key": "Authorization",
"value": "Bearer {{auth_token}}"
}
],
"body": {
"mode": "formdata",
"formdata": [
{
"key": "file",
"type": "file",
"src": []
},
{
"key": "description",
"value": "Test file upload",
"type": "text"
}
]
},
"url": {
"raw": "{{base_url}}/upload",
"host": ["{{base_url}}"],
"path": ["upload"]
}
}
}
]
}
],
"event": [
{
"listen": "prerequest",
"script": {
"exec": [
"// Global pre-request script",
"pm.environment.set('timestamp', Date.now());"
]
}
},
{
"listen": "test",
"script": {
"exec": [
"// Global test script",
"pm.test('No server errors', function () {",
" pm.response.to.not.have.status(500);",
"});"
]
}
}
],
"variable": [
{
"key": "page",
"value": "1"
},
{
"key": "limit",
"value": "10"
}
]
}
cURL et Postman maîtrisés
Méthodes HTTP, authentification, upload et débogage : vous savez consommer une API depuis PHP.