pdeans/http
Lightweight PSR-7 cURL HTTP client with PSR-17 factory support, built on Laminas Diactoros. Configure via curl options and use helper methods for GET/POST/PUT/PATCH/DELETE/HEAD/TRACE with headers and optional body streams/resources.
Installation:
composer require pdeans/http
Add to composer.json under require or require-dev depending on use case.
Basic Usage:
use pdeans\Http\Client;
$client = new Client();
$response = $client->get('https://api.example.com/data');
$data = json_decode((string) $response->getBody(), true);
First Use Case:
Replace a direct curl_exec() call in a Laravel service with this client. For example:
// Before (ad-hoc cURL)
$ch = curl_init('https://api.example.com/data');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
// After (using pdeans/http)
$client = new Client();
$response = $client->get('https://api.example.com/data');
pdeans\Http\Client for HTTP requests.pdeans\Http\Factories\* for PSR-17-compliant message creation.pdeans\Http\Response for parsing responses.Service Layer Integration: Inject the client into Laravel services for API calls:
use Illuminate\Support\Facades\Http;
use pdeans\Http\Client;
class ApiService {
protected Client $client;
public function __construct(Client $client) {
$this->client = $client;
}
public function fetchData() {
$response = $this->client->get('https://api.example.com/data');
return json_decode($response->getBody(), true);
}
}
Register the client in AppServiceProvider:
$this->app->bind(Client::class, function ($app) {
return new Client([
CURLOPT_TIMEOUT => 10,
CURLOPT_SSL_VERIFYPEER => true,
]);
});
PSR-17 Factories for Dynamic Requests: Use factories to create requests dynamically (e.g., in middleware or tests):
use pdeans\Http\Factories\RequestFactory;
$requestFactory = new RequestFactory();
$request = $requestFactory->createRequest('POST', 'https://api.example.com/data');
$request = $request->withHeader('Content-Type', 'application/json')
->withBody($client->getStream(json_encode(['key' => 'value'])));
$response = $client->sendRequest($request);
Error Handling: Wrap client calls in try-catch blocks to handle exceptions (e.g., network errors):
try {
$response = $client->get('https://api.example.com/data');
if ($response->getStatusCode() !== 200) {
throw new \RuntimeException('API request failed');
}
} catch (\Exception $e) {
Log::error('API call failed: ' . $e->getMessage());
throw $e;
}
Configuration Management:
Centralize cURL options in a config file (e.g., config/http.php):
return [
'default_options' => [
CURLOPT_TIMEOUT => 10,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
],
];
Load options dynamically:
$client = new Client(config('http.default_options'));
API Consumer Workflow:
get, post, etc.) for common HTTP verbs.getBody(), getStatusCode()).$response = $client->post('https://api.example.com/data', [
'Content-Type' => 'application/json',
], json_encode(['data' => 'value']));
if ($response->getStatusCode() === 201) {
return json_decode($response->getBody(), true);
}
Webhook Handling:
Use ServerRequestFactory to parse incoming webhook requests:
use pdeans\Http\Factories\ServerRequestFactory;
$serverRequestFactory = new ServerRequestFactory();
$serverRequest = $serverRequestFactory->createServerRequest(
'POST',
'https://example.com/webhook',
[],
[],
[],
['CONTENT_TYPE' => 'application/json']
);
$body = json_decode($serverRequest->getBody(), true);
Testing: Mock the client in unit tests using PSR-7 interfaces:
$this->mock(Client::class)
->shouldReceive('get')
->once()
->andReturn($this->createMock(ResponseInterface::class));
Laravel HTTP Client Bridge:
If using Laravel’s Http facade, consider wrapping this client for consistency:
Http::macro('pdeans', function ($uri, $config = []) {
$client = new Client($config);
return $client->get($uri);
});
Middleware Integration: Use PSR-17 factories to build middleware-compatible requests:
$request = (new RequestFactory())->createRequest('GET', 'https://api.example.com/data');
$request = $request->withHeader('X-API-KEY', config('api.key'));
$response = $client->sendRequest($request);
Stream Handling: For large payloads, use streams to avoid memory issues:
$stream = $client->getStream(fopen('large_file.json', 'r'));
$response = $client->post('https://api.example.com/upload', [], $stream);
cURL Option Restrictions:
CURLOPT_URL, CURLOPT_POSTFIELDS, or CURLOPT_HTTPHEADER cannot be set via the client constructor. Use the request-specific methods (get, post, etc.) or sendRequest with a custom Request object.RequestFactory to build requests with custom headers/body.Resource Leaks:
$client->release() after sending requests if managing resources manually.PSR-7/PSR-17 Learning Curve:
StreamInterface, UriInterface) may struggle with advanced use cases.get, post) and gradually adopt factories for complex scenarios.No Built-in Retries:
function withRetry(Client $client, $uri, $maxRetries = 3) {
$attempts = 0;
while ($attempts < $maxRetries) {
try {
return $client->get($uri);
} catch (\Exception $e) {
$attempts++;
if ($attempts >= $maxRetries) throw $e;
sleep(1);
}
}
}
SSL Verification:
CURLOPT_SSL_VERIFYPEER => false) is not recommended for production. Use proper certificates or a trusted CA bundle.$client = new Client([
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_CAINFO => storage_path('certs/ca-bundle.crt'),
]);
cURL Errors:
$client = new Client([
CURLOPT_VERBOSE => true,
]);
CURLOPT_STDERR to log cURL errors to a file:
$client = new Client([
CURLOPT_STDERR => fopen(storage_path('logs/curl_errors.log'), 'a'),
]);
Stream Issues:
StreamFactory to create streams safely:
$streamFactory = new StreamFactory();
$stream = $streamFactory->createStreamFromFile('file.json');
Header Conflicts:
Host or User-Agent may be overridden by the client. Use sendRequest with a custom Request object to enforce headers:
How can I help you explore Laravel packages today?