Source for file Client.php
Documentation is available at Client.php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
// +----------------------------------------------------------------------+
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 3.0 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available through the world-wide-web at |
// | http://www.php.net/license/3_0.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Author: Alexey Borzov <avb@php.net> |
// +----------------------------------------------------------------------+
// $Id: Client.php,v 1.7 2006/06/03 08:54:10 avb Exp $
* Do this define in your script if you wish HTTP_Client to follow browser
* quirks rather than HTTP specification (RFC2616). This means:
* - do a GET request after redirect with code 301, rather than use the
* same method as before redirect.
// define('HTTP_CLIENT_QUIRK_MODE', true);
require_once 'HTTP/Request.php';
require_once 'HTTP/Client/CookieManager.php';
* A simple HTTP client class.
* The class wraps around HTTP_Request providing a higher-level
* API for performing multiple HTTP requests
* @author Alexey Borzov <avb@php.net>
* @version $Revision: 1.7 $
* An HTTP_Client_CookieManager instance
* Received HTTP responses
* Default headers to send on every request
var $_defaultHeaders = array ();
* Default parameters for HTTP_Request's constructor
var $_defaultRequestParams = array ();
* How many redirects were done
* Maximum allowed redirects
* Listeners attached to the client
var $_listeners = array ();
* Whether the listener should be propagated to Request objects
var $_propagate = array ();
* Whether to keep all the responses or just the most recent one
var $_isHistoryEnabled = true;
* @param array Parameters to pass to HTTP_Request's constructor
* @param array Default headers to send on every request
* @param object HTTP_Client_CookieManager Cookie manager object to use
function HTTP_Client($defaultRequestParams = null , $defaultHeaders = null , $cookieManager = null )
if (!empty ($cookieManager) && is_a($cookieManager, 'HTTP_Client_CookieManager')) {
$this->_cookieManager = $cookieManager;
if (isset ($defaultHeaders)) {
if (isset ($defaultRequestParams)) {
* Sets the maximum redirects that will be processed.
* Setting this to 0 disables redirect processing. If not 0 and the
* number of redirects in a request is bigger than this number, then an
* @param int Max number of redirects to process
$this->_maxRedirects = $value;
* Sets whether to keep all the responses or just the most recent one
* @param bool Whether to enable history
$this->_isHistoryEnabled = (bool) $enable;
* Creates a HTTP_Request objects, applying all the necessary defaults
* @param string Method, constants are defined in HTTP_Request
* @param array Extra headers to send
* @return object HTTP_Request object with all defaults applied
function &_createRequest ($url, $method = HTTP_REQUEST_METHOD_GET , $headers = array ())
$req = & new HTTP_Request ($url, $this->_defaultRequestParams);
$req->setMethod ($method);
foreach ($this->_defaultHeaders as $name => $value) {
$req->addHeader ($name, $value);
foreach ($headers as $name => $value) {
$req->addHeader ($name, $value);
$this->_cookieManager->passCookies ($req);
foreach ($this->_propagate as $id => $propagate) {
$req->attach ($this->_listeners[$id]);
* Sends a 'HEAD' HTTP request
* @param array Extra headers to send
* @return integer HTTP response code
function head($url, $headers = array ())
$request = & $this->_createRequest ($url, HTTP_REQUEST_METHOD_HEAD , $headers);
return $this->_performRequest ($request);
* Sends a 'GET' HTTP request
* @param mixed additional data to send
* @param boolean Whether the data is already urlencoded
* @param array Extra headers to send
* @return integer HTTP response code
function get($url, $data = null , $preEncoded = false , $headers = array ())
$request = & $this->_createRequest ($url, HTTP_REQUEST_METHOD_GET , $headers);
foreach ($data as $name => $value) {
$request->addQueryString ($name, $value, $preEncoded);
} elseif (isset ($data)) {
$request->addRawQueryString ($data, $preEncoded);
return $this->_performRequest ($request);
* Sends a 'POST' HTTP request
* @param mixed Data to send
* @param boolean Whether the data is already urlencoded
* @param array Files to upload. Elements of the array should have the form:
* array(name, filename(s)[, content type]), see HTTP_Request::addFile()
* @param array Extra headers to send
* @return integer HTTP response code
function post($url, $data, $preEncoded = false , $files = array (), $headers = array ())
$request = & $this->_createRequest ($url, HTTP_REQUEST_METHOD_POST , $headers);
foreach ($data as $name => $value) {
$request->addPostData ($name, $value, $preEncoded);
$request->addRawPostData ($data, $preEncoded);
foreach ($files as $fileData) {
if (PEAR ::isError ($res)) {
return $this->_performRequest ($request);
* Sets default header(s) for HTTP requests
* @param mixed header name or array ('header name' => 'header value')
* @param string header value if $name is not an array
$this->_defaultHeaders = array_merge($this->_defaultHeaders, $name);
$this->_defaultHeaders[$name] = $value;
* Sets parameter(s) for HTTP requests
* @param mixed parameter name or array ('parameter name' => 'parameter value')
* @param string parameter value if $name is not an array
$this->_defaultRequestParams = array_merge($this->_defaultRequestParams, $name);
$this->_defaultRequestParams[$name] = $value;
* Performs a request, processes redirects
* @param object HTTP_Request object
* @return integer HTTP response code
function _performRequest (&$request)
// If this is not a redirect, notify the listeners of new request
if (0 == $this->_redirectCount && !empty ($request->_url )) {
$this->_notify ('request', $request->_url ->getUrl ());
if (PEAR ::isError ($err = $request->sendRequest ())) {
$this->_redirectCount = 0;
$this->_pushResponse ($request);
$code = $request->getResponseCode ();
if ($this->_maxRedirects > 0 ) {
if (in_array($code, array (300 , 301 , 302 , 303 , 307 ))) {
if ('' == ($location = $request->getResponseHeader ('Location'))) {
$this->_redirectCount = 0;
return PEAR ::raiseError ("No 'Location' field on redirect");
// Bug #5759: do not try to follow non-HTTP redirects
if (null === ($redirectUrl = $this->_redirectUrl ($request->_url , $location))) {
$this->_redirectCount = 0;
// Redirect via <meta http-equiv="Refresh"> tag, see request #5734
} elseif (200 <= $code && $code < 300 ) {
$redirectUrl = $this->_getMetaRedirect ($request);
if (!empty ($redirectUrl)) {
if (++ $this->_redirectCount > $this->_maxRedirects) {
$this->_redirectCount = 0;
return PEAR ::raiseError ('Too many redirects');
$this->_notify ('httpRedirect', $redirectUrl);
// we access the private properties directly, as there are no accessors for them
switch ($request->_method ) {
case HTTP_REQUEST_METHOD_POST:
if (302 == $code || 303 == $code || (301 == $code && defined('HTTP_CLIENT_QUIRK_MODE'))) {
return $this->get($redirectUrl);
foreach ($request->_postFiles as $name => $data) {
$postFiles[] = array ($name, $data['name'], $data['type']);
return $this->post($redirectUrl, $request->_postData , true , $postFiles);
case HTTP_REQUEST_METHOD_HEAD:
return (303 == $code? $this->get($redirectUrl): $this->head($redirectUrl));
case HTTP_REQUEST_METHOD_GET:
return $this->get($redirectUrl);
$this->_redirectCount = 0;
$this->_notify ('httpSuccess');
// some result processing should go here
$this->_notify ('httpError');
* Returns the most recent HTTP response
return $this->_responses[count($this->_responses) - 1 ];
* Saves the server's response to responses list
* @param object HTTP_Request object, with request already sent
function _pushResponse (&$request)
$this->_cookieManager->updateCookies ($request);
$idx = $this->_isHistoryEnabled? count($this->_responses): 0;
$this->_responses[$idx] = array (
'code' => $request->getResponseCode (),
'headers' => $request->getResponseHeader (),
'body' => $request->getResponseBody ()
* Clears object's internal properties
$this->_cookieManager->reset ();
$this->_responses = array ();
$this->_defaultHeaders = array ();
$this->_defaultRequestParams = array ();
* Adds a Listener to the list of listeners that are notified of
* @param object HTTP_Request_Listener instance to attach
* @param boolean Whether the listener should be attached to the
* created HTTP_Request objects
* @return boolean whether the listener was successfully attached
function attach(&$listener, $propagate = false )
if (!is_a($listener, 'HTTP_Request_Listener')) {
$this->_listeners[$listener->getId ()] = & $listener;
$this->_propagate[$listener->getId ()] = $propagate;
* Removes a Listener from the list of listeners
* @param object HTTP_Request_Listener instance to detach
* @return boolean whether the listener was successfully detached
if (!is_a($listener, 'HTTP_Request_Listener') ||
!isset ($this->_listeners[$listener->getId ()])) {
unset ($this->_listeners[$listener->getId ()], $this->_propagate[$listener->getId ()]);
* Notifies all registered listeners of an event.
* Currently available events are:
* 'request': sent on HTTP request that is not a redirect
* 'httpSuccess': sent when we receive a successfull 2xx response
* 'httpRedirect': sent when we receive a redirection response
* 'httpError': sent on 4xx, 5xx response
* @param string Event name
* @param mixed Additional data
function _notify ($event, $data = null )
$this->_listeners[$id]->update ($this, $event, $data);
* Calculates the absolute URL of a redirect
* @param object Net_Url object containing the request URL
* @param string Value of the 'Location' response header
* @return string|null Absolute URL we are being redirected to, null in case of non-HTTP URL
function _redirectUrl ($url, $location)
// If it begins with a scheme (as defined in RFC 2396) then it is absolute URI
if (preg_match('/^([a-zA-Z][a-zA-Z0-9+.-]*):/', $location, $matches)) {
// Bug #5759: we shouldn't try to follow non-HTTP redirects
if ('/' == $location{0 }) {
$url->path = Net_URL ::resolvePath ($location);
} elseif ('/' == substr($url->path , -1 )) {
$url->path = Net_URL ::resolvePath ($url->path . $location);
$dirname = (DIRECTORY_SEPARATOR == dirname($url->path )? '/': dirname($url->path ));
$url->path = Net_URL ::resolvePath ($dirname . '/' . $location);
$url->querystring = array ();
* Returns the cookie manager object (e.g. for storing it somewhere)
* @return object HTTP_Client_CookieManager
return $this->_cookieManager;
* Tries to extract a redirect URL from <meta http-equiv=Refresh> tag (request #5734)
* @param object HTTP_Request A request object containing the response
* @return string|null Absolute URI we are being redirected to, null if no redirect / invalid redirect
function _getMetaRedirect (&$request)
// Non-HTML response or empty response body
if ('text/html' != substr($request->getResponseHeader ('content-type'), 0 , 9 ) ||
'' == ($body = $request->getResponseBody ())) {
// No <meta http-equiv=Refresh> tag
if (!preg_match('!<meta\\s+([^>]*http-equiv\\s*=\\s*("Refresh"|\'Refresh\'|Refresh)[^>]*)>!is', $body, $matches)) {
// Just a refresh, no redirect
if (!preg_match('!content\\s*=\\s*("[^"]+"|\'[^\']+\'|\\S+)!is', $matches[1 ], $urlMatches)) {
$parts = explode(';', ('\'' == substr($urlMatches[1 ], 0 , 1 ) || '"' == substr($urlMatches[1 ], 0 , 1 ))?
substr($urlMatches[1 ], 1 , -1 ): $urlMatches[1 ]);
if (empty ($parts[1 ]) || !preg_match('/url\\s*=\\s*(\\S+)/is', $parts[1 ], $urlMatches)) {
// We do finally have an url... Now check that it's:
// a) HTTP, b) not to the same page
$previousUrl = $request->_url ->getUrl ();
return (null === $redirectUrl || $redirectUrl == $previousUrl)? null: $redirectUrl;
Documentation generated on Mon, 11 Mar 2019 14:40:22 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.
|