Http php object client v0.5 - v0.5.1 how-to

Document version: 0.5/2 (08.06.2014)

Table of contents

1. Requirements
2. Basic usage
3. Creating url
4. Creating request
5. Post request with files
6. Http basic auth
7. Http_Response
8. Advanced usage
9. Modify settings
10. Http_Cookie_Proxy
11. Benchmarking
12. Http client history
13. Http client cache
14. Bug reporting
15. More features
16. Donating

1. Requirements

2. Basic usage

Simple class loader:

function autoLoader($class)
{
        require_once('lib/' . str_replace('_', '/', $class) . '.php');
}

spl_autoload_register('autoLoader');

Example 1. Get file (response) contents via http:

$file = (new Http_Request('http://domain.com/file.tgz'))->send()->getResponse()->getBody();
// other way:
$request = new Http_Request('http://domain.com/file.tgz');
$file = $request->getResponse()->getBody(); // __toString() is a alias of getBody()
$file = $request()->getBody(); // __invoke() is a alias of getResponse()
// method send() is called automatically in getResponse() when wasn't before.

Using alias added in v0.5:

$file = Http_Client::get('http://domain.com/file.tgz')->getBody();
// or:
$file = Http_Client::get_('http://domain.com/file.tgz');

Example 2. Send post data via https, display headers and save a local file with content from a response http body:

$response = (new Http_Request('https://ssl.domain.com/something.htm')->setPost(['aaa' => 123, 'bbb' => 456])->setPostValue('ccc', '789')->getResponse());
echo "Headers:\n" . $response->getHeadersSource(); // Raw response http head (string with all headers without trailing \r\n\r\n)
echo "Headers (print_r()):\n";
print_r($response->getHeadersAll()); // or: echo "Headers (print_r($response->getHeaders())):\n";
file_put_contents('localfile.htm', $response->getBody());

Note: getResponse() can be called many times, first call of it (or send()) will trigger send(), next time(s) will just return the same response object.

Note: Setters can NOT be called when request was sent before. In this case exception will be throwed.

3. Creating url

Constructor of class Http_Request takes one required argument (optionally second arg is a "cookie proxy"). It can be url in string, url in a Http_Url/Url object or instance of Http_Request_Data. Http_Request_Data also can take string url or object as first parameter. If You provide something else than Http_Request_Data, it will create instance of this class. Same thing is doing with url's.

Remember one thing, every objects given into args of Http_Request constructor (Http_Request_Data, Http_Url/Url or Http_Cookie_Proxy) are always cloned, so if You want call some methods of those objects, you must use forwarding methods in Http_Request (for ex.: $request->getUri()).

Note: You cant call any of setters (forwarders mostly) when request was used to sent, but You can do it after cloning Http_Request - in this case, request object is almost "new" - you can send it again, with optionally called setters before.

$request = new Http_Request('http://domain.com/');
$request = new Http_Request('http://domain.com/path');
$request = new Http_Request('http://domain.com/something?page=info');
$request = new Http_Request(new Http_Request_Data('http://domain.com/something?page=info'));
$request = new Http_Request(new Http_Url('http://domain.com/something?page=info'));
$request = new Http_Request(new Url('http://domain.com/something?page=info'));
$request = new Http_Request(new Http_Request_Data(new Http_Url('http://domain.com/something?page=info')));

Host name in url are always required for requests. But for example, url can be easly generated with Http_Url class basing only on path (with or without http url query).

$url = new Http_Url('/path?a=b'); // $url = new Http_Url('/path');
$url->setScheme('http'); // or $url->setDefaultScheme('http');
$url->setHost('somehost.com'); // or $url->setDefaultHost('somehost.com');

$request = new Http_Request($url);

This defaults are used only when given string in constructor doesn't have something. So if You provide '/path/to/file' and You call $url->setDefaultScheme('http'); and $url->setHost('somehost.com'); then generated url string ($url->getUrl() or $url->__toString()) will be: "http://somehost.com/path/to/file". But if string in constructor will have scheme and host, then defaults will be not used. For a "hard" change, remove "Default" from method name (ex.: setDefaultHost -> setHost).

Same code is used when following 'Location' header (http redirects). Exacly setDefault...();

In a v0.5 scheme is not required (in parsing textual url only). If not provided, 'http' will be used.

4. Creating request

Changing (default) method.

$request = new Http_Request('xyz.com');
$request->setMethod(Http_Request_Data::METHOD_POST);

In normal situation, post (or other) method will be selected automatically when needed

$request = new Http_Request('http://xyz.com');
$request->setPostValue('foo', 'bar');
$request->send();

It will send something like this:

POST / HTTP/1.1
Host: xyz.com
Content-Length: 7

foo=bar

GET variables (url query) can be set only with Http_Url::setQuery() or by hand manipulating textual url.

$request = new Http_Request('http://xyz.com');
$request->setQuery('foo=bar');
// or:
$request->setQuery(['foo' => 'bar']);
// or:
$request = new Http_Request('http://xyz.com?foo=bar');

Request data methods is something different than http method.

Simple example of it:

$request = new Http_Request('https://my-storage.server.local/put_me_here.ext');
$request->setDataMethod(Http_Request::DATA_METHOD_PUT);
$request->setRawData('Some content. Text or binary.');
$request->send();

// PUT emulating:

$request = new Http_Request('https://my-storage.server.local/put_me_here.ext');
$request->setHeader('X-HTTP-Method-Override', 'PUT'); // setHeaderIfNotExist() can be used instead if needed.
$request->setRawData('Some content. Text or binary.');
$request->send(); // DATA_METHOD_POST_RAW will be selected automatically.

// Selecting PUT with Http_Request_Data:

$requestData = new Http_Request_Data('https://my-storage.server.local/put_me_here.ext');
$requestData->setMethod(Http_Request_Data::METHOD_PUT);
$requestData->setRawData('Some content. Text or binary.');
$request = new Http_Request($requestData);
$request->send(); // DATA_METHOD_PUT will be selected automatically.

Upper, in brackets, there are 'Content-Type' header set by setHeaderIfNotExist() depending on selected method. In v0.5 this behaviour is disabled when 'useDefaultHeaders' setting is disabled.

When leaved default DATA_METHOD_AUTO, method will be automatically selected depending on used setPost()/setPostValue(), setFile()/setFiles(), setRawData() or manually changed method by setMethod(Http_Request_Data::METHOD_POST).

When instance of Http_Request is created, then Http_Request_Data is not available directly. Getters are forwarded every time, but setters only when Http_Request is not "prepared" (request is not sent yet - Http_Client is calling Http_Request::prepare() before sent).

Set header using its name and value:
Http_Request::setHeader($name, $value);
// or:
Http_Request_Data::setHeader($name, $value);

Set header if was not set before:
Http_Request::setHeaderIfNotExist($name, $value);
// or:
Http_Request_Data::setHeaderIfNotExist($name, $value);

5. Post request with files

$file_1 = new Http_Request_Data_File;
$file_1->setContent(file_get_contents('/tmp/somefile.txt')); // set contents of file
$file_1->setFileName('foo.txt'); // set file name that will be shown in http request
$file_1->setInputName('input_file_1'); // set name of html form input

$file_2 = new Http_Request_Data_File;
$file_2->setContent(file_get_contents('/tmp/secondfile.txt')); // set contents of file
$file_2->setFileName('bar.txt'); // set file name that will be shown in http request
$file_2->setInputName('input_file_2'); // set name of html form input

$request = new Http_Request('http://somehost.com/submit_form_with_files.php');
$request->setFile($file_1);
$request->setFile($file_2);
// or: $request->setFiles([$file_1, $file_2]);
echo $request->getResponse()->getBody();

Offcourse, binary files are allowed. Encoding is not changed in any case.

Http method will be changed automatically on sending (because files was added).

6. Http basic auth

$request = new Http_Request('https://user:pass@private-server.com');
$request->send();
// or:
$request = new Http_Request('https://private-server.com');
$request->setUser('user'); // setUser(), setPass() and many other setters are forwarder to internally instances of Http_Request_Data or Http_Url (last one in this case).
$request->setPass('pass');
$request->send();

Login credentials in opposite to typically browsers are not cached, with one exception - exacly only on following Location.

7. Http_Response

Http_Client allows to get responses on demand. Not depend on pipelining enabled or disabled.

$req_1 = (new Http_Request('http://domain.com/1.htm'))->send();
$req_2 = (new Http_Request('http://domain.com/2.htm'))->send();
$req_3 = (new Http_Request('http://domain.com/3.htm'))->send();

echo $req2->getResponse()->getBody();

Normally, response from socket is readed only when getResponse() is called. To change this behaviour (recommended only when You have some problems, for example: with cookies, or when you work with many requests and many cookies) use this:

Http_Settings::getInstance()->keepAliveGetResponseOnlyOnDemand = false;
// See into Http_Settings_Base and Http_Request_Base for much more settings/options.

Eventually, You can hardcode defaults into Http_Settings class (extended from Http_Settings_Base)

Something more in Http_Response:

$response = (new Http_Request('http://xyz.com'))->getResponse();
$response->getRequest(); // get (originally) request object used to get this response
$response->getUrl(); // get textual url
$response->getPort(); // get port from url (0 if not given in url or by call setPort() previously)
$response->getPortReal(); // get real port used to sent (80/443 or other when url was have non-standard port)

8. Advanced usage

Some examples of advanced usage:

$request->setHeadersOrder(...); // auto, ascending, descending, gecko, webkit or array with headers names
$request->setUseDefaultHeaders(bool); // use defaults headers described by Http_Requests
$request->setHeader(name, value); // set header into http request
$request->setHeaderIfNotExist(name, value); // set header only if wasn't set before
$request->setHeader('foo', ''); // drop header named foo
Http_Cookie_Handler::getInstance()->getCookiesForRequest($request); // get cookies corresponding to given request
Http_Cookie_Handler::getInstance()->deleteCookie(...); // delete given cookie
Http_Settings::getInstance()->dropSessionCookiesOnExit = false;

NOTE: Before v0.5 (v0.4 and older) cookie objects was NOT cloned before storing or returning.

Following Location:

$request = new Http_Request('http://domain.com/redirect_me'); // will receive header: Location: /redirected_page
$response = $request->send()->getResponse(True/false); // true or no prarams - follow location, false - dont follow
echo $response->getRequest()->getUrl(); // will print http://domain.com/redirect_me or http://domain.com/redirected_page

NOTE: On following (http redirects), new Http_Request object is created (by Http_Client).

If You wanna make redirects by hand (for ex. when you need additional data to send after redirect)...

$request = new Http_Request('http://domain.com/redirect_me');
$response = $request->send()->getResponse(false); // dont follow

$location = $response->getHeader('Location'); // see also: getHeaders(), getHeadersAll() and getHeaderSource()

if(strlen($location)) // not null and not null length string
{
        $url = new Http_Url($location);
        
        $url->setDefaultHost($request->getHost()); // or: $url->setDefaultHost($response->getHost());
        $url->setDefaultScheme($request->getScheme());  // or: $url->setDefaultScheme($response->getScheme());
        
        // $url->setDefaultUser($request->getUser());
        // $url->setDefaultPass($request->getPass());
        
        $redirectedRequest = new Http_Request($url); // In here You can use new variable like here or use previous var if You dont need previous request object
        
        // add here something to new request object ($redirectedRequest) if You need to
        
        $response = $redirectedRequest->getResponse(); // overwrite $response variable or use new one if You need to
}
else
{
        // do something... or not
}

To disable referer, use this setting:

Http_Settings::getInstance()->sendRefererOnRedirects = false;

To sent same requests, You must clone request object, because one request obj. can be used to sent only once.

In this case, "prepared send-data" and objects keeped in properties are cloned (__clone()) and requestId are reset to 0.

$request_1 = new Http_Request('http://my-preety-domain.com/');
$response_1 = $request_1->getResponse();

$request_2 = clone $request_1;
$response_2 = $request_2->getResponse();

$request_3 = clone $request_1;
$response_3 = $request_3->getResponse();

9. Modify settings

Settings can be modified in two ways:

1. Modify virtual properties of settings object (ex.: Http_Settings::getInstance()->dirStorage = '/var/pool/php/http_client/storage')

2. Modify Http_Settings (extended from Http_Settings_Base) by overwrite property _defaults

Every sets are commented in a class Http_Settings_Base.

If you try to set or get something that is not in _defaults, then Http_Settings_Exception will be throwed.

In normal situation, cookies are handled by a Http_Cookie_Handler (singleton), but sometimes, other handler would be needed or just one (or more) temporary cookie in one (ore more) request.

Second argument in constructor of Http_Request gets optionally instance of Http_Cookie_Proxy_Interface (in your eventually new class based on Http_Cookie_Proxy_Interface, all methods from Http_Cookie_Proxy are recommended to be written, because someday in new versions something can be changed). Right now, we will concetrate on originally Http_Cookie_Proxy.

In easiest, and very often situation, one temporary cookie will be needed. Thats meen, we create some request object and we want here to send (in this request) one more cookie, or replace stored one. Btw. this temporary cookie is not stored anywhere, only in cookie proxy object until it will be destroyed (as I was say before, Http_Request constructor will clone cookie proxy object, so be carrefull with cloning request object).

Basicly to set this temporary cookie, just do something like this:

$request = new Http_Request('http://i-eat-all-your-cookies.com/');

$cookie = new Http_Cookie;
$cookie->name = 'yummy';
$cookie->value = 'and poisoned'; // or: $cookie = new Http_Cookie('yummy', 'and poisoned'); // for more about it, just read Http_Cookie class code, its short.

$request->setTempCookie($cookie); // it will forward into internally object of Http_Cookie_Proxy, exacly: Http_Cookie_Proxy::setTempCookie()
$request->send();

To do something more - to use other cookie handler, or even other cookie proxy, see this code:

$otherHandler = new Http_Cookie_Handler_Modified;

$cookieProxy = new Http_Cookie_Proxy($otherHandler); // or use Http_Cookie_Proxy::setCookieHandler($otherHandler)

$cookieProxy->setSettingReceiveCookies(false); // disable (or enable by using true) receiving (and storing) new/replaced cookies (by cookie handler) in requests using this cookie proxy object ONLY.
$cookieProxy->setSettingSendCookies(false); // disable (or enable by using true) sending stored cookies (by cookie handler, temporary cookies will work anyway) in requests using this cookie proxy object ONLY.

$cookieProxy->setTempCookie(new Http_Cookie('foo', 'bar')); // temp cookies will always work, not depend on settings in proxy and Http_Settings

$request = new Http_Request('http://somewhere.com', $cookieProxy);

// Http_Settings::getInstance()->receiveCookiesByDefault = false; // globally disable receiving (and storing) new/replaced cookies
// Http_Settings::getInstance()->sendCookiesByDefault = false; // globally disable sending cookies

$request->send();

IMPORTANT NOTE: In a cookie proxy, settingReceiveCookies and settingSendCookies defaults are taken from Http_Settings in a constructor of cookie proxy.

NOTE: By default, session cookies are deleted when Http_Cookie_Handler::__destruct() is triggered (php exiting mostly). To change this behaviour, change 'dropSessionCookiesOnExit' setting to false.

11. Benchmarking

Version 0.5 have added feature to get precious time of write and read socket.

To work it correctly (get time of waiting+getting response), disable setting 'keepAliveGetResponseOnlyOnDemand' or always get responses objects after sending by call getResponse().

function autoLoader($class)
{
        require_once('lib/' . str_replace('_', '/', $class) . '.php');
}

spl_autoload_register('autoLoader');

for($i=1;$i<=10;$i++)
{
        $start = microtime(true);
        $response = (new Http_Request('http://server/file.txt'))->send()->getResponse();
        $end = microtime(true);
        $socketReading = ($r->getTimeReceived() - $response->getTimeSent()) * 1000;
        
        echo "Reading socket:      $socketReading ms\n";
        $everything = ($end - $start) * 1000;
        echo "Processing request:  " . (($response->getTimeSent() - $start) * 1000) . " ms\n";
        echo "Processing response: " . (($end - $response->getTimeReceived()) * 1000) . " ms\n";
        echo "Processing all:      " . ($everything - $socketReading) . " ms\n";
        echo "Everything:          " . $everything . " ms\n";
        echo "--------------\n";
}

It will output something like this:

Benchmarking screenshot

As You can see, first request can take a "little" more time. Reason of this is mostly reading, parsing and compiling class files. Once per month TLD effective list can be updated, so it can take some time too - but only when cookies are parsed in response.

12. Http client history

Every request can be logged. There are three types of history log.

Every one log type writing can be enabled/disabled in Http_Settings. Setting names are: 'historyFileInTxtMicroTime', 'historyFileInTxtTimeInRFC1036', 'historyFileSerialized'.

First two is human readable. Format of it is listed below:

historyFileInTxtMicroTime: pid(posix) requestId network_address(ip) time(x.y) data_method(with stripped 'DATA_METHOD_') URL

historyFileInTxtTimeInRFC1036: pid(posix) requestId network_address(ip) time(RFC 1036: Wdy, DD Mon YY HH:MM:SS +0000) data_method(with stripped 'DATA_METHOD_') URL

Sample of serialized format (historyFileSerialized) is:

[
    [4383282] =>
        [
            [pid] => 9756
            [time] => 1400439182.5964
            [url] => 'http://feeds.feedburner.com/phpclasses-xml?format=xml'
            [addr] => 74.125.232.129
            [data_method] => GET
            [data_method_bitwise] => 2
            [request_line] => 'GET /phpclasses-xml?format=xml HTTP/1.1'
            [request_header] => 'Host: feeds.feedburner.com
Connection: Keep-Alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
User-Agent: HTTP(s) norbert_ramzes php object client v0.5
Accept-Encoding: gzip, deflate
Accept-Language: pl-PL,pl;q=0.8,en-US;q=0.6,en;q=0.4
Accept-Charset: utf-8,ISO-8859-2;q=0.7,*;q=0.3
If-Modified-Since: Sun, 18 May 2014 18:37:15 GMT
If-None-Match: tktKDPdPBIt4VSBMMf4IYhOdcFM'
            [status] => 304
            [status_raw] => '304 Not Modified'
            [response_header] => 'HTTP/1.1 304 Not Modified
Date: Sun, 18 May 2014 18:53:02 GMT
Server: GSE
Alternate-Protocol: 80:quic'
        ]
]

13. Http client cache

Version 0.5 have added caching with similar behaviour to typical browsers.

List of available setting names related to http client cache:

Caching is available only when response have necessary headers, '200 OK' response status, request method was GET and without user credentials (http basic auth). Cookies and other things isn't checked (TODO).

14. Bug reporting

If You have encountred any problems, or You have any feature request. Please tell me about it and provide as much details as You can.

In the above mentioned facts, send me an email to the address norbert [at] linux dot pl

15. More features

This package has some more features not desribed here, but everything is inline-documented.

15. Donating

If You wannna to "keep-alive" this project, please donate: BTC 1NA3D5yf6AfuSnrr15pb6T2UkDRt19RtWC.