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.

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.