/ HttpClient

HttpClient

Miko's HttpClient provides a fluent API for making HTTP requests using cURL. Supports GET, POST, PUT, PATCH, DELETE methods with authentication, headers, and retry policies.


HttpClient Methods Summary

Method Description
get($url)Send GET request
post($url, $data)Send POST request
put($url, $data)Send PUT request
patch($url, $data)Send PATCH request
delete($url)Send DELETE request
setBearerToken($token)Set Bearer authentication
setBasicAuth($user, $pass)Set Basic authentication
setHeader($key, $value)Set custom header
setTimeout($seconds)Set request timeout
withRetry($times, $delay)Enable retry on failure

Basic Usage

use Miko\Core\Http\HttpClient;

// Create client with base URL
$client = HttpClient::create('https://api.example.com');

// GET request
$response = $client->get('/users');
$users = $response->json();

// POST request
$response = $client->post('/users', [
    'name' => 'John Doe',
    'email' => 'john@example.com'
]);

// Check response
if ($response->ok()) {
    $user = $response->json();
    echo "Created user: " . $user['id'];
}

HTTP Methods

GET Request

// Simple GET
$response = $client->get('/users');

// GET with query parameters
$response = $client->get('/users?page=1&limit=10');

// GET with query array
$response = $client->get('/users', [
    'query' => [
        'page' => 1,
        'limit' => 10,
        'status' => 'active'
    ]
]);

POST Request

// POST with JSON body
$response = $client->post('/users', [
    'name' => 'John Doe',
    'email' => 'john@example.com',
    'role' => 'admin'
]);

// POST with form data
$response = $client->post('/upload', [
    'file' => new CURLFile('/path/to/file.pdf'),
    'description' => 'My document'
], ['Content-Type' => 'multipart/form-data']);

PUT Request

// Full update
$response = $client->put('/users/1', [
    'name' => 'John Updated',
    'email' => 'john.updated@example.com',
    'role' => 'user'
]);

PATCH Request

// Partial update
$response = $client->patch('/users/1', [
    'name' => 'John Updated'
]);

DELETE Request

// Delete resource
$response = $client->delete('/users/1');

if ($response->status() === 204) {
    echo "User deleted successfully";
}

Authentication

Bearer Token (JWT)

$client = HttpClient::create('https://api.example.com');

// Set bearer token
$client->setBearerToken('eyJhbGciOiJIUzI1NiIs...');

// All subsequent requests include Authorization header
$response = $client->get('/protected/resource');

Basic Authentication

$client = HttpClient::create('https://api.example.com');

// Set basic auth
$client->setBasicAuth('username', 'password');

$response = $client->get('/secure/endpoint');

API Key

$client = HttpClient::create('https://api.example.com');

// API key in header
$client->setHeader('X-API-Key', 'your-api-key');

// Or in query string
$response = $client->get('/data?api_key=your-api-key');

Headers & Options

Custom Headers

// Set single header
$client->setHeader('Accept', 'application/json');
$client->setHeader('X-Custom-Header', 'custom-value');

// Set multiple headers
$client->setHeaders([
    'Accept' => 'application/json',
    'Accept-Language' => 'en-US',
    'X-Request-ID' => uniqid()
]);

Timeout

// Set timeout in seconds
$client->setTimeout(30);

// Short timeout for quick APIs
$client->setTimeout(5);

SSL Verification

// Disable SSL verification (development only!)
$client->setVerifySsl(false);

// Enable (default)
$client->setVerifySsl(true);

Response Handling

Response Object

$response = $client->get('/users/1');

// Status code
$status = $response->status();        // 200

// Status checks
$response->ok();                      // true (200-299)
$response->isSuccess();               // true (200-299)
$response->isRedirect();              // true (300-399)
$response->isClientError();           // true (400-499)
$response->isServerError();           // true (500-599)

// Body
$body = $response->body();            // Raw string
$json = $response->json();            // Decoded JSON (array)
$object = $response->json(false);     // Decoded JSON (object)

// Headers
$headers = $response->headers();
$contentType = $response->header('Content-Type');

Error Handling

$response = $client->get('/users/999');

if (!$response->ok()) {
    echo "Error: " . $response->status();
    echo "Message: " . $response->json()['error'] ?? 'Unknown error';
}

// Throw exception on error
try {
    $response->throwIfFailed();
    $user = $response->json();
} catch (HttpException $e) {
    echo "Request failed: " . $e->getMessage();
}

Retry Policy

Automatically retry failed requests.

// Retry up to 3 times with 1 second delay
$response = $client->withRetry(3, 1000)->get('/unstable-api');

// Custom retry logic
$response = $client->request('GET', '/api/data', null, [], [
    'retries' => 3,
    'retry_delay' => 1000,  // milliseconds
    'retry_on' => [500, 502, 503, 504]  // Status codes to retry
]);

Parallel Requests (Async)

Execute multiple requests simultaneously.

// Define requests
$requests = [
    ['method' => 'GET', 'url' => '/users'],
    ['method' => 'GET', 'url' => '/products'],
    ['method' => 'GET', 'url' => '/orders'],
    ['method' => 'POST', 'url' => '/analytics', 'data' => ['event' => 'page_view']]
];

// Execute in parallel
$responses = $client->multi($requests);

// Process responses
$users = $responses[0]->json();
$products = $responses[1]->json();
$orders = $responses[2]->json();

Practical Examples

REST API Client

class UserApiClient
{
    private HttpClient $client;
    
    public function __construct(string $baseUrl, string $apiKey)
    {
        $this->client = HttpClient::create($baseUrl);
        $this->client->setHeader('X-API-Key', $apiKey);
        $this->client->setTimeout(30);
    }
    
    public function getUsers(int $page = 1, int $limit = 20): array
    {
        $response = $this->client->get("/users?page={$page}&limit={$limit}");
        $response->throwIfFailed();
        return $response->json();
    }
    
    public function getUser(int $id): ?array
    {
        $response = $this->client->get("/users/{$id}");
        
        if ($response->status() === 404) {
            return null;
        }
        
        $response->throwIfFailed();
        return $response->json();
    }
    
    public function createUser(array $data): array
    {
        $response = $this->client->post('/users', $data);
        $response->throwIfFailed();
        return $response->json();
    }
    
    public function updateUser(int $id, array $data): array
    {
        $response = $this->client->put("/users/{$id}", $data);
        $response->throwIfFailed();
        return $response->json();
    }
    
    public function deleteUser(int $id): bool
    {
        $response = $this->client->delete("/users/{$id}");
        return $response->status() === 204;
    }
}

// Usage
$api = new UserApiClient('https://api.example.com', 'your-api-key');

$users = $api->getUsers(1, 10);
$user = $api->createUser(['name' => 'John', 'email' => 'john@example.com']);
$api->updateUser($user['id'], ['name' => 'John Updated']);
$api->deleteUser($user['id']);

OAuth2 Token Refresh

class OAuth2Client
{
    private HttpClient $client;
    private string $accessToken;
    private string $refreshToken;
    private int $expiresAt;
    
    public function __construct(string $baseUrl)
    {
        $this->client = HttpClient::create($baseUrl);
    }
    
    public function authenticate(string $clientId, string $clientSecret): void
    {
        $response = $this->client->post('/oauth/token', [
            'grant_type' => 'client_credentials',
            'client_id' => $clientId,
            'client_secret' => $clientSecret
        ]);
        
        $response->throwIfFailed();
        $data = $response->json();
        
        $this->accessToken = $data['access_token'];
        $this->refreshToken = $data['refresh_token'] ?? null;
        $this->expiresAt = time() + $data['expires_in'];
    }
    
    public function request(string $method, string $url, ?array $data = null): array
    {
        // Refresh token if expired
        if (time() >= $this->expiresAt - 60) {
            $this->refreshAccessToken();
        }
        
        $this->client->setBearerToken($this->accessToken);
        
        $response = match($method) {
            'GET' => $this->client->get($url),
            'POST' => $this->client->post($url, $data),
            'PUT' => $this->client->put($url, $data),
            'DELETE' => $this->client->delete($url),
        };
        
        $response->throwIfFailed();
        return $response->json();
    }
    
    private function refreshAccessToken(): void
    {
        $response = $this->client->post('/oauth/token', [
            'grant_type' => 'refresh_token',
            'refresh_token' => $this->refreshToken
        ]);
        
        $response->throwIfFailed();
        $data = $response->json();
        
        $this->accessToken = $data['access_token'];
        $this->expiresAt = time() + $data['expires_in'];
    }
}

Webhook Sender

class WebhookSender
{
    private HttpClient $client;
    
    public function __construct()
    {
        $this->client = HttpClient::create('');
        $this->client->setTimeout(10);
        $this->client->setHeader('Content-Type', 'application/json');
    }
    
    public function send(string $url, string $event, array $payload): bool
    {
        $data = [
            'event' => $event,
            'timestamp' => date('c'),
            'payload' => $payload
        ];
        
        // Sign the payload
        $signature = hash_hmac('sha256', json_encode($data), $_ENV['WEBHOOK_SECRET']);
        $this->client->setHeader('X-Webhook-Signature', $signature);
        
        try {
            $response = $this->client->withRetry(3, 1000)->post($url, $data);
            return $response->ok();
        } catch (Exception $e) {
            Logger::error("Webhook failed: " . $e->getMessage());
            return false;
        }
    }
}

// Usage
$webhook = new WebhookSender();
$webhook->send('https://example.com/webhook', 'order.created', [
    'order_id' => 123,
    'total' => 99.99
]);