Installation
Add the bundle to your composer.json:
composer require dvelopment/curl-bundle
Enable it in config/bundles.php (Symfony):
return [
// ...
Development\CurlBundle\DevelopmentCurlBundle::class => ['all' => true],
];
Basic Configuration
Configure the bundle in config/packages/development_curl.yaml:
development_curl:
default_options:
CURLOPT_TIMEOUT: 30
CURLOPT_RETURNTRANSFER: true
clients:
api:
base_uri: 'https://api.example.com'
options:
CURLOPT_HTTPHEADER: ['Content-Type: application/json']
First Request
Inject the CurlClient service and make a request:
use Development\CurlBundle\Client\CurlClient;
class MyService
{
public function __construct(private CurlClient $curl)
{
}
public function fetchData()
{
$response = $this->curl->request('GET', '/endpoint', [
'client' => 'api', // Uses configured 'api' client
]);
return json_decode($response, true);
}
}
Laravel-Specific Setup
If using Laravel (not Symfony), manually register the bundle in AppServiceProvider:
public function register()
{
$this->app->register(\Development\CurlBundle\DevelopmentCurlBundle::class);
}
Define clients in config for repeated use (e.g., api, payment_gateway):
clients:
payment_gateway:
base_uri: 'https://pay.example.com/v1'
options:
CURLOPT_HTTPHEADER: ['Authorization: Bearer %token%']
Use placeholders (e.g., %token%) for dynamic values:
$this->curl->request('POST', '/charge', [
'client' => 'payment_gateway',
'replace' => ['%token%' => $authToken],
]);
CURLOPT_RETURNTRANSFER and stream directly:
$this->curl->request('GET', '/large-file', [
'client' => 'default',
'options' => [CURLOPT_RETURNTRANSFER => false],
]);
$response['error'] or use callbacks:
$response = $this->curl->request('GET', '/data', [
'on_error' => function ($error) {
\Log::error('Curl error:', ['error' => $error]);
},
]);
clients:
secure_api:
options:
CURLOPT_HTTPAUTH: CURLAUTH_BASIC
CURLOPT_USERPWD: 'username:password'
replace for dynamic tokens (see above).Implement retries in a service layer:
public function withRetry($url, $maxRetries = 3)
{
$retries = 0;
while ($retries < $maxRetries) {
try {
return $this->curl->request('GET', $url);
} catch (\Exception $e) {
$retries++;
if ($retries === $maxRetries) throw $e;
sleep(2 ** $retries); // Exponential backoff
}
}
}
Bridge to Laravel’s Http client for consistency:
use Illuminate\Support\Facades\Http;
class CurlBridge
{
public function __construct(private CurlClient $curl) {}
public function toLaravelResponse($curlResponse)
{
return Http::response(
$curlResponse['body'],
$curlResponse['http_code'],
$curlResponse['headers']
);
}
}
Bind the CurlClient to Laravel’s container:
$this->app->bind('curl', function ($app) {
return $app->make('development.curl.client');
});
Then inject via constructor:
public function __construct(private $curl) {}
Use the bundle in CLI tasks:
use Development\CurlBundle\Client\CurlClient;
class SyncDataCommand extends Command
{
protected $curl;
public function __construct(CurlClient $curl)
{
parent::__construct();
$this->curl = $curl;
}
public function handle()
{
$data = $this->curl->request('GET', '/sync');
// Process data...
}
}
Offload curl requests to queues:
class ExternalApiJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable;
public function handle(CurlClient $curl)
{
$curl->request('POST', '/process', ['data' => $this->payload]);
}
}
$this->curl->request('GET', '/endpoint', [
'client' => 'api',
'options' => array_merge(
$this->container->getParameter('development_curl.clients.api.options'),
[CURLOPT_TIMEOUT => 60]
),
]);
try-finally for critical sections:
$ch = $this->curl->getHandle('GET', '/resource');
try {
// Use $ch
} finally {
curl_close($ch);
}
default_options:
default_options:
CURLOPT_SSL_VERIFYPEER: false
CURLOPT_SSL_VERIFYHOST: 0
Warning: Only for testing; use proper certs in production.%token% or other placeholders may not resolve.
Fix: Verify replace keys match exactly:
$this->curl->request('GET', '/data', [
'client' => 'api',
'replace' => ['%token%' => $token], // Case-sensitive!
]);
$this->app->extend('development.curl.client', function ($app, $client) {
return new LaravelCurlClient($client); // Custom wrapper
});
Add to default_options:
default_options:
CURLOPT_VERBOSE: true
Log the output:
$this->curl->request('GET', '/debug', [
'on_info' => function ($info) {
\Log::debug('Curl info:', ['info' => $info]);
},
]);
Inspect $response['http_code'] for non-200 responses:
if ($response['http_code'] === 401) {
throw new \RuntimeException('Unauthorized');
}
Headers may be case-insensitive in some PHP versions:
$headers = $response['headers'];
$contentType = strtolower($headers['Content-Type'] ?? '');
curlReplicate requests using command-line curl to isolate issues:
curl -X GET "https://api.example.com/endpoint" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json"
Extend the bundle to parse responses:
class JsonResponseHandler
{
public function handle($response)
{
return json_decode($response['body'], true);
}
How can I help you explore Laravel packages today?