How to use the PECL HTTP (PECL_HTTP) Extension to make HTTP requests from PHP

PECL HTTP is a feature-rich PHP extension that allows you to make HTTP and HTTPS (SSL) requests from your PHP code and handle the responses. If you are not familiar with PECL, it is a library of extensions to add functionality to PHP. It uses the same package and delivery system as PEAR.

Many distributions do not install PECL_HTTP by default even if you install PHP. If you try to use one of the PECL_HTTP object or functions (I.e. http_get()) without the extension installed you will likely get something like this:

Fatal error: Call to undefined function http_get()

If this error comes up but you think you installed the PECL_HTTP package and it shows up in phpinfo(), then it is possible your PECL_HTTP install failed and did not get cleaned up so phpinfo() still sees it. This may happen if you didn’t install the cURL source library dependency first (see below).

So let’s pretend we own the site http://www.example.com (See RFC 2606.) We want to build a PHP diagnostic page that will tell us that www.example.com is returning the string “example” somewhere in the page indicating if the page is up or down. First I need to install the PECL_HTTP extension. For details on how to install a PECL extension see my post “How to install a PHP PECL extension/module on Ubuntu“. For now I am going to assume that the php-pear and php5-dev packages have already been installed. These instructions are based on a Ubuntu install:

  • Install the libcurl3-openssl-dev package. The HTTP_PECL extension requires some of the cURL source libraries so we will have to install the cURL library package first:
    sudo apt-get install libcurl3-openssl-dev

    If you don’t install the cURL source library package you will likely see the following error when you attempt to install the PECL_HTTP extension:

    checking for curl/curl.h... not found
    configure: error: could not find curl/curl.h
    ERROR: ‘/tmp/pear/temp/pecl_http/configure --with-http-curl-requests --with-http-zlib-compression=1 --with-http-magic-mime=no --with-http-shared-deps’ failed
  • Install the HTTP_PECL module with the following command:
    sudo pecl install pecl_http

    The installer may ask you about some specific options but unless you really know what you want, you can probably just hit enter one or more times to accept all the defaults. If all goes well, the module should download, build, and install.

  • Once the install is complete, it will probably ask you to add a “extension=http.so” line to your php.ini file. Open up the php.ini file in your favorite text editor and add the line under the section labeled “Dynamic Extensions”. On Ubuntu the php.ini file seems to be located in the /etc/php5/apache2 folder:
    sudo nano /etc/php5/apache2/php.ini
  • Now that the php.ini file has been updated, Apache will need to be restarted so the new extension will be loaded:
    sudo /etc/init.d/apache2 restart

    That should restart Apache on Ubuntu but if that doesn’t work you can try:

    sudo /etc/init.d/httpd restart

At this point hopefully the PECL_HTTP extension is installed so now we can create a PHP script that will make an HTTP request to http://www.example.com and display the results. For this example I will use the http_get() function. The first argument is a predefined constant of the request method type (GET, POST, etc.) and the second argument is a string containing the URL. I created a file named httptest.php (using “sudo nano /var/www/httptest.php” with the following code and put it in the /var/www folder (The default HTTP root on a Ubuntu server):

<?php

echo http_get("http://www.example.com");

?>

or you could use the http_request function instead to do the same thing:

<?php

echo http_request(HTTP_METH_GET,"http://www.example.com");

?>

When the page is opened in a web browser (I.e. http://sparky/httptest.php) it returns something like this:

HTTP/1.1 200 OK Date: Sun, 04 Jan 2009 22:41:54 GMT Server: Apache/2.2.3 (CentOS) Last-Modified: Tue, 15 Nov 2005 13:24:10 GMT ETag: "b80f4-1b6-80bfd280" Accept-Ranges: bytes Content-Length: 438 Connection: close Content-Type: text/html; charset=UTF-8

You have reached this web page by typing "example.com", "example.net", or "example.org" into your web browser.

These domain names are reserved for use in documentation and are not available for registration. See RFC 2606, Section 3.

That’s it. Those are some pretty quick one-liners if we are fine with the default options. This time we’ll do something similar but use the HttpRequest object instead and set a timeout and a different user agent:

<?php

$http_req = new HttpRequest("http://www.example.com");
$http_req->setOptions(array(timeout=>10,useragent=>"MyScript"));
$http_req->send();
echo $http_req->getRawResponseMessage();

?>

The output is the same as the previous two commands but this time the server could have taken up to ten seconds to respond before the request timed out. In addition, we sent the user agent string “MyScript” in the host header to the server. If you don’t want the HTTP response headers included in the the output, you use the getResponseBody() method instead:

<?php

$http_req = new HttpRequest("http://www.example.com");
$http_req->setOptions(array(timeout=>10,useragent=>"MyScript"));
$http_req->send();
echo $http_req->getResponseBody();

?>

This outputs:

You have reached this web page by typing "example.com", "example.net", or "example.org" into your web browser.

These domain names are reserved for use in documentation and are not available for registration. See RFC 2606, Section 3.

No response headers this time. In fact, it looks as though you typed http://www.example.com in the browser.

I could set some URL query parameters using the setQueryData() if example.com was a dynamic page that accepts arguments but I am pretty sure it does not.

For the purpose of our example it doesn’t look like we have gotten very far but PHP is now getting a hold of the response data before we see it so we are halfway there. Now all we need to do is search for the string “example” and return some type of indicator letting us know that the example.com page is up or down:

<?php

$http_req = new HttpRequest("http://www.example.com");
$http_req->setOptions(array(timeout=>10,useragent=>"MyScript"));
$http_req->send();

/*Note: The stripos() function just returns false if it doesn't find an upper or lower case version of the string we are looking for.*/
if (!stripos($http_req->getResponseBody(), "example")){
    echo "The page is down!";
} else {
    echo "The page is up!";
}

?>

If everything is working correctly we will see:

The page is up!

If example.com is broken or doesn’t return the string “example” our test page returns:

The page is down!

This is great and all but you may have noticed there is no error handling to speak of which isn’t good. I will talk about HTTP request/PECL_HTTP error handling in a separate post but until then, happy HTTPing!

6 thoughts on “How to use the PECL HTTP (PECL_HTTP) Extension to make HTTP requests from PHP”

  1. Your post was the most helpful among everything I’ve found about installing PECL_HTTP on ubuntu. Thank you!

  2. I’ve used you instructions before and was able to test that the pecl_http extension worked on another machine.

    I now tried to replicate the installation and get a black screen in my browser when trying to run the php scripts.

    Could you give me a hint as to why this might be happening.

    when I ran the make test function during installation of the pecl_http I did recieved the following errors.

    Build complete.
    Don’t forget to run ‘make test’.

    shaun@jmbgroup-desktop:~/Downloads/pecl1.6/pecl_http-1.6.6$ sudo make test

    Build complete.
    Don’t forget to run ‘make test’.

    =====================================================================
    PHP : /usr/bin/php
    PHP_SAPI : cli
    PHP_VERSION : 5.3.2-1ubuntu4
    ZEND_VERSION: 2.3.0
    PHP_OS : Linux – Linux jmbgroup-desktop 2.6.32-22-generic #33-Ubuntu SMP Wed Apr 28 13:28:05 UTC 2010 x86_64
    INI actual : /home/shaun/Downloads/pecl1.6/pecl_http-1.6.6/tmp-php.ini
    More .INIs :
    CWD : /home/shaun/Downloads/pecl1.6/pecl_http-1.6.6
    Extra dirs :
    VALGRIND : Not used
    =====================================================================
    TIME START 2010-05-13 12:09:21
    =====================================================================
    PASS HttpMessage [tests/HttpMessage_001.phpt]
    FAIL HttpMessage properties [tests/HttpMessage_002.phpt]
    PASS HttpMessage implements Serializable, Countable [tests/HttpMessage_003.phpt]
    PASS HttpMessage::detach() [tests/HttpMessage_004.phpt]
    PASS HttpMessage::prepend() [tests/HttpMessage_005.phpt]
    PASS HttpMessage iterator [tests/HttpMessage_006.phpt]
    PASS HttpMessage::reverse() [tests/HttpMessage_007.phpt]
    PASS HttpMessage::toMessageTypeObject() [tests/HttpMessage_008.phpt]
    PASS Bug #16700 – child classes of HttpMessage cannot not have array properties [tests/HttpMessage_009_bug16700.phpt]
    PASS HttpQueryString global [tests/HttpQueryString_001.phpt]
    PASS HttpQueryString local [tests/HttpQueryString_002.phpt]
    PASS HttpQueryString xlate [tests/HttpQueryString_003.phpt]
    PASS HttpQueryString w/ objects [tests/HttpQueryString_004.phpt]
    SKIP HttpRequestDataShare [tests/HttpRequestDataShare_001.phpt] reason: http://www.google.com not responsive
    SKIP HttpRequestDataShare global [tests/HttpRequestDataShare_002.phpt] reason: http://www.google.com not responsive
    SKIP HttpRequestPool [tests/HttpRequestPool_001.phpt] reason: http://www.php.net not responsive
    SKIP extending HttpRequestPool [tests/HttpRequestPool_002.phpt] reason: http://www.php.net not responsive
    -request^[[C^[[C^[[C^[[C^[[C^[[C^[[D^[[D^[[D^[[A^[[B^[^[FAIL HttpRequestPool chain [tests/HttpRequestPool_003.phpt]
    PASS HttpRequestPool::__destruct() invalid curl handle [tests/HttpRequestPool_004.phpt]
    PASS HttpRequestPool exception [tests/HttpRequestPool_005.phpt]
    SKIP HttpRequestPool detaching in callbacks [tests/HttpRequestPool_006.phpt] reason: at.php.net not responsive
    PASS HttpRequest options [tests/HttpRequest_001.phpt]
    SKIP HttpRequest GET/POST [tests/HttpRequest_002.phpt] reason: http://www.google.com not responsive
    SKIP HttpRequest SSL [tests/HttpRequest_003.phpt] reason: arweb.info not responsive
    FAIL HttpRequest multiple posts [tests/HttpRequest_004.phpt]
    PASS HttpRequest accessors [tests/HttpRequest_005.phpt]
    SKIP HttpRequest XMLRPC [tests/HttpRequest_006.phpt] reason: need ext/xmlrpc
    FAIL HttpRequest PUT [tests/HttpRequest_007.phpt]
    FAIL HttpRequest custom request method [tests/HttpRequest_008.phpt]
    FAIL HttpRequest callbacks [tests/HttpRequest_009.phpt]
    FAIL HttpRequest cookie API [tests/HttpRequest_010.phpt]
    SKIP HttpResponse – send data with caching headers [tests/HttpResponse_001.phpt] reason: need CGI SAPI
    SKIP HttpResponse – send gzipped file [tests/HttpResponse_002.phpt] reason: need CGI SAPI
    SKIP HttpResponse – send gzipped file with caching headers [tests/HttpResponse_003.phpt] reason: need CGI SAPI
    SKIP HttpResponse – send cached gzipped data [tests/HttpResponse_004.phpt] reason: need CGI SAPI
    SKIP HttpResponse file not found [tests/HttpResponse_005.phpt] reason: need CGI SAPI
    SKIP allowed methods [tests/allowed_methods_002.phpt] reason: need CGI SAPI
    SKIP logging allowed methods [tests/allowed_methods_002_logging.phpt] reason: need CGI SAPI
    FAIL Bug #15800 Double free when zval is separated in convert_to_* [tests/bug_15800.phpt]
    PASS http_build_str [tests/build_str_001.phpt]
    PASS http_build_url() with relative paths [tests/build_url_001.phpt]
    PASS http_build_url() with parse_url() [tests/build_url_002.phpt]
    PASS http_build_url() [tests/build_url_003.phpt]
    PASS http_build_url flags [tests/build_url_004.phpt]
    PASS http_chunked_decode() “\r\n” [tests/chunked_decode_001.phpt]
    PASS http_chunked_decode() “\n” [tests/chunked_decode_002.phpt]
    PASS http_chunked_decode() truncated message [tests/chunked_decode_003.phpt]
    PASS http_chunked_decode() truncated message ending with NUL after a chunk [tests/chunked_decode_004.phpt]
    FAIL cloning [tests/cloning_001.phpt]
    PASS http_date() with timestamp [tests/date_001.phpt]
    PASS http_date() without timestamp [tests/date_002.phpt]
    PASS encoding stream objects [tests/encoding_objects_001.phpt]
    PASS encodings [tests/encodings.phpt]
    SKIP crc32 etag (may fail because PHPs crc32 is actually crc32b) [tests/etag_mode_031.phpt] reason: need CGI SAPI
    SKIP sha1 etag [tests/etag_mode_032.phpt] reason: need CGI SAPI
    SKIP md5 etag [tests/etag_mode_033.phpt] reason: need CGI SAPI
    SKIP ext/hash etag [tests/etag_mode_034.phpt] reason: need CGI SAPI
    SKIP ob crc32 etag (may fail because PHPs crc32 is actually crc32b) [tests/etag_mode_041.phpt] reason: need CGI SAPI
    SKIP ob sha1 etag [tests/etag_mode_042.phpt] reason: need CGI SAPI
    SKIP ob md5 etag [tests/etag_mode_043.phpt] reason: need CGI SAPI
    SKIP ob ext/hash etag [tests/etag_mode_044.phpt] reason: need CGI SAPI
    PASS exceptions [tests/exceptions.phpt]
    SKIP get request data [tests/get_request_data_001.phpt] reason: CGI not available
    PASS http_match_request_header() [tests/match_request_header_001.phpt]
    PASS negotiation [tests/negotiation_001.phpt]
    SKIP ob_deflatehandler [tests/ob_deflatehandler_001.phpt] reason: need CGI SAPI
    SKIP ob_inflatehandler [tests/ob_inflatehandler_001.phpt] reason: need CGI SAPI
    PASS parse cookie [tests/parse_cookie_001.phpt]
    PASS parse cookie [tests/parse_cookie_002.phpt]
    PASS http_parse_headers() [tests/parse_headers_001.phpt]
    SKIP http_parse_message() [tests/parse_message_001.phpt] reason: http://www.google.com not responsive
    PASS identity encoding trap [tests/parse_message_002.phpt]
    PASS content range message [tests/parse_message_003.phpt]
    PASS http_parse_message() recursive [tests/parse_message_004.phpt]
    PASS http_parse_message() content range header w/(o) = [tests/parse_message_005.phpt]
    PASS mixed EOL trap [tests/parse_message_006.phpt]
    PASS http_parse_params [tests/parse_params_001.phpt]
    SKIP persistent handles [tests/persistent_handles_001.phpt] reason: need ZTS build
    FAIL persistent handles [tests/persistent_handles_002.phpt]
    SKIP persistent handles [tests/persistent_handles_003.phpt] reason: need PHP <= v4.4
    SKIP http_redirect() with params [tests/redirect_011.phpt] reason: need CGI SAPI
    SKIP logging redirects [tests/redirect_011_logging.phpt] reason: need CGI SAPI
    SKIP http_redirect() with session [tests/redirect_012.phpt] reason: need CGI SAPI
    SKIP logging redirects [tests/redirect_012_logging.phpt] reason: need CGI SAPI
    SKIP http_redirect() permanent [tests/redirect_013.phpt] reason: need CGI SAPI
    SKIP logging redirects [tests/redirect_013_logging.phpt] reason: need CGI SAPI
    FAIL urlencoded cookies [tests/request_cookies.phpt]
    FAIL request etag [tests/request_etag.phpt]
    SKIP GZIP request [tests/request_gzip.phpt] reason: dev.iworks.at not responsive
    PASS request methods [tests/request_methods.phpt]
    FAIL http_put_data() [tests/request_put_data.phpt]
    SKIP http_send_data() NIL-NUM range [tests/send_data_001.phpt] reason: need CGI SAPI
    SKIP http_send_data() NUM-NUM range [tests/send_data_002.phpt] reason: need CGI SAPI
    SKIP http_send_data() NUM-NIL range [tests/send_data_003.phpt] reason: need CGI SAPI
    SKIP http_send_data() syntactically invalid range [tests/send_data_004.phpt] reason: need CGI SAPI
    SKIP http_send_data() oversized range [tests/send_data_005.phpt] reason: need CGI SAPI
    SKIP http_send_data() multiple ranges [tests/send_data_006.phpt] reason: need CGI SAPI
    SKIP http_send_data() HTTP_SENDBUF_SIZE long string [tests/send_data_010.phpt] reason: need CGI SAPI
    SKIP http_send_data() last modified caching [tests/send_data_011.phpt] reason: need CGI SAPI
    SKIP http_send() failed precondition [tests/send_failed_precond_001.phpt] reason: need CGI SAPI
    SKIP http_send_file() multiple ranges [tests/send_file_005.phpt] reason: need CGI SAPI
    SKIP http_send_file() [tests/send_file_008.phpt] reason: need CGI SAPI
    SKIP http_send_file() NUM-NUM range [tests/send_file_009.phpt] reason: need CGI SAPI
    SKIP http_send_file() NIL-NUM range [tests/send_file_010.phpt] reason: need CGI SAPI
    SKIP http_send_file() NUM-NIL range [tests/send_file_011.phpt] reason: need CGI SAPI
    SKIP http_send_file() syntactically invalid range [tests/send_file_012.phpt] reason: need CGI SAPI
    SKIP http_send_file() oversized range [tests/send_file_013.phpt] reason: need CGI SAPI
    SKIP http_send() If-Range [tests/send_ifrange_001.phpt] reason: need CGI SAPI
    SKIP http_send() If-Range [tests/send_ifrange_003.phpt] reason: need CGI SAPI
    PASS stream filters [tests/stream_filters_001.phpt]
    PASS gzip stream filters [tests/stream_filters_002.phpt]
    PASS stream filter fun [tests/stream_filters_003.phpt]
    =====================================================================
    TIME END 2010-05-13 12:16:23

    =====================================================================
    TEST RESULT SUMMARY
    ———————————————————————
    Exts skipped : 0
    Exts tested : 45
    ———————————————————————

    Number of tests : 112 58
    Tests skipped : 54 ( 48.2%) ——–
    Tests warned : 0 ( 0.0%) ( 0.0%)
    Tests failed : 13 ( 11.6%) ( 22.4%)
    Expected fail : 0 ( 0.0%) ( 0.0%)
    Tests passed : 45 ( 40.2%) ( 77.6%)
    ———————————————————————
    Time taken : 422 seconds
    =====================================================================

    =====================================================================
    FAILED TEST SUMMARY
    ———————————————————————
    HttpMessage properties [tests/HttpMessage_002.phpt]
    HttpRequestPool chain [tests/HttpRequestPool_003.phpt]
    HttpRequest multiple posts [tests/HttpRequest_004.phpt]
    HttpRequest PUT [tests/HttpRequest_007.phpt]
    HttpRequest custom request method [tests/HttpRequest_008.phpt]
    HttpRequest callbacks [tests/HttpRequest_009.phpt]
    HttpRequest cookie API [tests/HttpRequest_010.phpt]
    Bug #15800 Double free when zval is separated in convert_to_* [tests/bug_15800.phpt]
    cloning [tests/cloning_001.phpt]
    persistent handles [tests/persistent_handles_002.phpt]
    urlencoded cookies [tests/request_cookies.phpt]
    request etag [tests/request_etag.phpt]
    http_put_data() [tests/request_put_data.phpt]
    =====================================================================

    You may have found a problem in PHP.
    We would like to send this report automatically to the
    PHP QA team, to give us a better understanding of how
    the test cases are doing. If you don't want to send it
    immediately, you can choose "s" to save the report to
    a file that you can send us later.
    Do you want to send this report now? [Yns]:

    I'm running ubuntu 10.4 with apache2 and php5

    Any suggestion as to how I may be able to correct these error would be greatly appreciated.

  3. Shaun, I am not terribly familiar with everything that test is attempting but my first guess would be that perhaps the module is working but can’t get out. It looks like it is attempting to connect to external sites as part of its testing. Make sure you can open a web browser on that box and get out to a website OK or at least see if you can just ping http://www.google.com and/or http://www.php.net. Perhaps a gateway or DNS is not setup correctly? Good luck!

  4. I’ve followed you instructions but I get the following error, what does this mean

    Fatal error: Uncaught exception ‘HttpInvalidParamException’ with message ‘Empty or too short HTTP message: ”’ in /var/www/httptest.php:15 inner exception ‘HttpRequestException’ with message ‘Timeout was reached; connect() timed out! (http://www.example.com/)’ in /var/www/httptest.php:5 Stack trace: #0 /var/www/httptest.php(15): HttpRequest->send() #1 {main} thrown in /var/www/httptest.php on line 15

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>