I've been working for a couple of days on a service to connect to a remote REST API through cURL, being this my first time working heavily with cURL on PHP and I've found it nothing but hard and cumbersome.

The interface to communicate with cURL in PHP is a mess that forces you to know too much of the implementation and control everything, and on top of it all it is procedural styled!

$curl = curl_init();

curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); // You must always use this
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLINFO_HEADER_OUT, false); // You should always use this

curl_setopt($curl, CURLOPT_URL, 'http://example.com');

$result = curl_exec($curl);

if (curl_error($curl)) {
    // Handle error

This above is a typical example of cURL usage in the PHP community. Error handling is far from pleasant and procedural options set is IMO a real pain. There is one thing that puzzles me though, you have CURLFile object to handle PUT files.

But if there is one thing that really bothers me above all it is the impossibility to know at any given point what options have been set in a cURL resource, you only get the option to wipe it all and start over.


// Start over configuring $curl resource

curl_reset that's all you got

There are tons of cURL wrappers on the Internet, in PHP documentation as well, some of them good, most of them just thrown away lines of code.

So trying to overcome all this issues I've come up with an OOP wrapper over cURL that works with PSR7 messages, it is called spiral.

With spiral you can configure a cURL transport object any way you want, set options, remove them, change their value and know at any time what options are set (ahhh!), and use a built-in client to transform a PSR7 a Request into a PSR7 Response.

So that finally you can do beautifully with OOP as this

use Jgut\Spiral\Transport\Curl;
use Jgut\Spiral\Client;

$transport = Curl::createFromDefaults();
$transport->setOption('connect_timeout', 10);
$transport->setOption('user_agent', 'myUserAgent');

$spiral = new Client($transport);

$request = new \PSR7RequestImplementation();
$request->withMethod('GET')->withHeader('Accept-Charset', 'utf-8');
// Keep configuring request object ...

$response = new \PSR7ResponseImplementation();
$response = $spiral->request($request, $response);

if ($response->hasHeader('Accept')) {
    // Do your thing

If it weren't because of that Curl::createFromDefaults() at the begining no one could say this is cURL!