I am a programmer and architect (the kind that writes code) with a focus on testing and open source; I maintain the PHPUnit_Selenium project. I believe programming is one of the hardest and most beautiful jobs in the world. Giorgio is a DZone MVB and is not an employee of DZone and has posted 638 posts at DZone. You can read more from them at their website. View Full User Profile

All the ways to perform HTTP requests in PHP

07.02.2012
| 10311 views |
  • submit to reddit

In the PHP world, HTTP is very important: not only for receiving requests, which is the job of PHP application, but also for performing them. ROT web services are probably the most popular way to interface with external systems nowadays.

We'll make no assumptions on the payload of HTTP requests (which may be binary, text, JSON, XML), so you'll have to deal with that yourself. In some cases, you may choose a more specific solution such as the SoapClient class in the SOAP extension, or the XML-RPC extension.

Native

There are several primitive for performing HTTP requests from PHP code:
  • the cURL extension relies on the most famous library and CLI tool for performing application-level requests like HTTP and FTP ones.
  • streams (stream_socket_client() or stream_context_create() as the main primitives) are an abstraction over several application protocols, like curl.
  • fsockopen() and other file-based functons like fwrite() work directly at the TCP level.
  • socket_*() primitives work with TCP too, and they are essentially a different API but do the same job as fsockopen().
  • The HTTP PECL extension is even object-oriented, but it is less commonly available than the cURL one.

The main difference between the various solutions is their availability. Streams have been part of PHP for 10 years, while cURL is an extension that might not be present on your server. cURL is actually pretty common and even included by default in Windows versions of PHP, but the point stands with regard to the other extension.

In short, it is possible you are not in the condition to rely on cURL because you are writing a portable library, or you do not have control on the server configuration.

However, it's probably not worth the hassle to use fsockopen() if you're talking over HTTP, as it would be handy for lower-level textual or binary protocols. So as an alternative to cURL, many libraries (and people) stick to streams.

Libraries

The problem with PHP libraries present on PEAR, SourceForge or GitHub is that there are too many of them (a picture worse than for PHP frameworks). If you choose the wrong one, you may be stuck with an old, not supported library: that's vendor lock-in, a form of technical debt.

Look for activity in the library you choose: the commit log, number of watch and forks on Github, or tweets about the library.

These are the two libraries I've seen cited the most:

  • Buzz by Kris Wallsmith of Symfony fame.
  • Guzzle is a bit heavy in lines of code but features much documentation.

The second one contains some external dependency (Symfony components), but in any case both are managed with Composer (so you'll need that in addition.)

Every PHP library will use one of the primitive methods we listed above, so check what backends are available before picking one.

As a good news, consider that a standard interface for HTTP clients has been proposed; this interface would make conforming libraries less riskier to use as you would always been able to switch to another implementing one in case your current dependency becomes abandoned or obsolete.

Frameworks

A simple way to avoid picking up new technical debt is to leverage some debt you already have. So if you're using a framework, look at what it offers you.

For example, my last projects have involved Zend Framework as a dependency I found already inside the user space: lots of Zend_* classes at a require_once() distance for me. So I could just rely on these classes:

  • Zend Framework 1: Zend_Http_Client, with back ends targeting curl and streams. So you will be portable at a price of configuring the adapter.
  • Zend Framework 2: Zend\Http\Client, essentially the port of the same component with a new unified interface on top.

Conclusions

HTTP clients are a commodity: they have no killer feature, and they should perform their jobs at the least cost to us. So we should choose them according to non-functional metrics: project activity, API ease of use, and occasionally performance.

The testability aspect is taken care by creating a level of abstraction over these libraries, at least until a unified interface that allows for mocking of requests will be available. Note that in case of primitive functions like curl() you already have to build a little object-oriented layer of abstraction.

But you do not always have to use a library: curl() and stream_context_create() are very easy to wrap and if you only have to make a couple of types of HTTP requests, just stick to them. I do this myself in PHPUnit_Selenium, for calling the Selenium Server RC and WebDriver APIs: two Driver classes wrap the curl_*() primitive functions, which are hidden inside them and never referred upon in the rest of the codebase.

If instead the HTTP client role of your project results in dozens of different requests made by different components, check out a framework's or a library solution to prevent the introduction of duplication derived from multiple wrapping. In this case, the only thing preventing you from including a library is your project's policy (for example PHPUnit_Selenium ships as a portable package and as such cannot depend on non-PEAR projects without including their sources directly.)

Published at DZone with permission of Giorgio Sironi, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Michael Dowling replied on Mon, 2012/07/02 - 12:56pm

> HTTP clients are a commodity: they have no killer feature, and they should perform their jobs at the least cost to us.

I disagree with this statement.  HTTP clients are just as important as server frameworks, and they vary greatly in terms of how well they implement the HTTP/1.1 spec and the features they support.  You can find a thorough comparison of PHP HTTP client libraries here: http://groups.drupal.org/node/233173 

Hari Kt replied on Wed, 2012/07/04 - 7:22am

Aura Project ( SolarPHP v 2 ) has an Http Request and Response .

There is nothing tied to anything :-) . Probably you may love it.

 https://github.com/auraphp/Aura.Http

$http = include '/path/to/Aura.Http/scripts/instance.php';

// make a request and get a response stack
$request = $http->newRequest();
$request->setUrl('http://example.com');
$stack = $http->send($request);
echo $stack[0]->content;


// send a response
$response = $http->newResponse();
$response->headers->set('Content-Type', 'text/plain');
$response->setContent('Hello World!');
$http->send($response);

 There are two adapters available:

Aura\Http\Request\Adapter\Curl, which is used automatically when the curl extension is loaded

Aura\Http\Request\Adapter\Stream which is the fallback if curl is not loaded.

Pedro Cunha replied on Fri, 2012/07/13 - 4:35pm

Thak you all for the comments, it helps me a lot. I agree with Michael Dowling.

Programação Orientada a Objetos com PHP

 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.