View source
<?php
class HttpClient {
protected $authentication = NULL;
protected $request_alter = NULL;
protected $formatter = NULL;
protected $lastError = FALSE;
protected $delegate = NULL;
public $lastRequest;
public $rawResponse;
public $lastResponse;
public $options = array();
public function __construct($authentication = NULL, $formatter = NULL, $request_alter = FALSE, $delegate = NULL) {
$this->authentication = $authentication;
$this->formatter = $formatter;
if (!$formatter || in_array('HttpClientFormatter', class_implements($formatter))) {
$this->formatter = $formatter;
}
else {
throw new Exception(t('The formatter parameter must either be a object implementing HttpClientFormatter, or evaluate to FALSE.'));
}
if (is_object($request_alter) && is_callable(array(
$request_alter,
'alterRequest',
))) {
$request_alter = array(
$request_alter,
'alterRequest',
);
}
if (!$request_alter || is_callable($request_alter)) {
$this->request_alter = $request_alter;
}
else {
throw new Exception(t('The request_alter parameter must either be a object or class with a public alterRequest method, callable in itself or evaluate to FALSE.'));
}
if (!$delegate && function_exists('curl_init')) {
$delegate = new HttpClientCurlDelegate();
}
if (!$delegate) {
throw new Exception(t('The HttpClient cannot execute requests without a delegate. This probably means that you don\'t have curl installed on your system.'));
}
$this->delegate = $delegate;
}
public function setAuthentication(HttpClientAuthentication $auth) {
$this->authentication = $auth;
}
public function setFormatter(HttpClientFormatter $formatter) {
$this->formatter = $formatter;
}
public function get($url, $parameters = array()) {
return $this
->execute(new HttpClientRequest($url, array(
'method' => 'GET',
'parameters' => $parameters,
)));
}
public function post($url, $data = NULL, $parameters = array()) {
return $this
->execute(new HttpClientRequest($url, array(
'method' => 'POST',
'parameters' => $parameters,
'data' => $data,
)));
}
public function put($url, $data = NULL, $parameters = array()) {
return $this
->execute(new HttpClientRequest($url, array(
'method' => 'PUT',
'parameters' => $parameters,
'data' => $data,
)));
}
public function delete($url, $parameters = array()) {
return $this
->execute(new HttpClientRequest($url, array(
'method' => 'DELETE',
'parameters' => $parameters,
)));
}
public function execute(HttpClientRequest $request) {
if ($this->request_alter) {
call_user_func($this->request_alter, $request);
}
if (isset($request->data)) {
if ($this->formatter) {
$request
->setHeader('Content-type', $this->formatter
->contentType());
$request->data = $this->formatter
->serialize($request->data);
}
else {
$request->data = (string) $request->data;
}
if (is_string($request->data)) {
$request
->setHeader('Content-length', strlen($request->data));
}
}
if ($this->formatter) {
$request
->setHeader('Accept', $this->formatter
->accepts());
}
if ($this->authentication) {
$this->authentication
->authenticate($request);
}
$response = $this->delegate
->execute($this, $request);
$this->lastRequest = $request;
$this->lastResponse = $response;
$result = NULL;
if ($response->responseCode >= 200 && $response->responseCode <= 299) {
if ($this->formatter) {
try {
$result = $this->formatter
->unserialize($response->body);
} catch (Exception $e) {
throw new HttpClientException('Failed to unserialize response', 0, $response, $e);
}
}
else {
$result = $response->body;
}
}
elseif (!empty($response->drupalErrors)) {
throw new HttpClientException(check_plain(implode("\n", $response->drupalErrors)), $response->responseCode, $response);
}
else {
throw new HttpClientException(check_plain($response->responseMessage), $response->responseCode, $response);
}
return $result;
}
public static function urlencode_rfc3986($input) {
if (is_array($input)) {
return array_map(array(
'HttpClient',
'urlencode_rfc3986',
), $input);
}
else {
if (is_scalar($input)) {
return str_replace('+', ' ', str_replace('%7E', '~', rawurlencode($input)));
}
else {
return '';
}
}
}
}
abstract class HttpClientDelegate {
public abstract function execute(HttpClient $client, HttpClientRequest $request);
protected function interpretResponse(HttpClient $client, $response) {
$client->rawResponse = $response;
if (preg_match('/\\nProxy-agent: .*\\r?\\n\\r?\\nHTTP/', $response)) {
$split = preg_split('/\\r?\\n\\r?\\n/', $response, 3);
if (!isset($split[2])) {
throw new HttpClientException('Error interpreting response', 0, (object) array(
'rawResponse' => $response,
));
}
$headers = $split[1];
$body = $split[2];
}
else {
$split = preg_split('/\\r?\\n\\r?\\n/', $response, 2);
if (!isset($split[1])) {
throw new HttpClientException('Error interpreting response', 0, (object) array(
'rawResponse' => $response,
));
}
$headers = $split[0];
$body = $split[1];
}
$obj = (object) array(
'headers' => $headers,
'body' => $body,
);
if (preg_match_all('/X-Drupal-Assertion-[0-9]+: (.*)\\n/', $headers, $matches)) {
foreach ($matches[1] as $key => $match) {
$obj->drupalErrors[] = print_r(unserialize(urldecode($match)), 1);
}
}
$matches = array();
if (preg_match('/HTTP\\/[\\d.]+ (\\d{3}) (.*)/', $headers, $matches)) {
$obj->responseCode = intVal(trim($matches[1]), 10);
$obj->responseMessage = trim($matches[2]);
if ($obj->responseCode == 100) {
return $this
->interpretResponse($client, $body);
}
}
return $obj;
}
}
class HttpClientException extends Exception {
protected $response;
public function __construct($message, $code = 0, $response = NULL, $exception = NULL) {
parent::__construct($message, $code);
$this->response = $response;
}
public function getResponse() {
$response = $this->response;
if (is_object($response)) {
$response = clone $response;
}
return $response;
}
}
class HttpClientBaseFormatter implements HttpClientFormatter {
const FORMAT_PHP = 'php';
const FORMAT_JSON = 'json';
const FORMAT_FORM = 'form';
protected $mimeTypes = array(
self::FORMAT_PHP => 'application/vnd.php.serialized',
self::FORMAT_JSON => 'application/json',
self::FORMAT_FORM => 'application/x-www-form-urlencoded',
);
protected $format;
public function __construct($format = self::FORMAT_PHP) {
$this->format = $format;
}
public function serialize($data) {
switch ($this->format) {
case self::FORMAT_PHP:
return serialize($data);
break;
case self::FORMAT_JSON:
return drupal_json_encode($data);
break;
case self::FORMAT_FORM:
return http_build_query($data, NULL, '&');
break;
}
}
public function unserialize($data) {
switch ($this->format) {
case self::FORMAT_PHP:
if (($response = @unserialize($data)) !== FALSE || $data === serialize(FALSE)) {
return $response;
}
else {
throw new Exception(t('Unserialization of response body failed.'), 1);
}
break;
case self::FORMAT_JSON:
$response = drupal_json_decode($data);
if ($response === NULL && json_last_error() != JSON_ERROR_NONE) {
throw new Exception(t('Unserialization of response body failed.'), 1);
}
return $response;
break;
case self::FORMAT_FORM:
$response = array();
parse_str($data, $response);
return $response;
break;
}
}
public function mimeType() {
return $this->mimeTypes[$this->format];
}
public function accepts() {
return $this
->mimeType();
}
public function contentType() {
return $this
->mimeType();
}
}
class HttpClientCompositeFormatter implements HttpClientFormatter {
private $send = null;
private $accept = null;
public function __construct($send = HttpClientBaseFormatter::FORMAT_FORM, $accept = HttpClientBaseFormatter::FORMAT_JSON) {
if (is_string($send)) {
$send = new HttpClientBaseFormatter($send);
}
if (is_string($accept)) {
$accept = new HttpClientBaseFormatter($accept);
}
$this->send = $send;
$this->accept = $accept;
}
public function serialize($data) {
return $this->send
->serialize($data);
}
public function unserialize($data) {
return $this->accept
->unserialize($data);
}
public function accepts() {
return $this->accept
->mimeType();
}
public function contentType() {
return $this->send
->mimeType();
}
}
interface HttpClientFormatter {
public function serialize($data);
public function unserialize($data);
public function accepts();
public function contentType();
}
interface HttpClientAuthentication {
public function authenticate($request);
}
class HttpClientRequest {
const METHOD_GET = 'GET';
const METHOD_POST = 'POST';
const METHOD_PUT = 'PUT';
const METHOD_DELETE = 'DELETE';
public $method = self::METHOD_GET;
public $url = '';
public $parameters = array();
public $headers = array();
public $data = NULL;
public $options = array();
public function __construct($url, $values = array()) {
$this->url = $url;
foreach (get_object_vars($this) as $key => $value) {
if (isset($values[$key])) {
$this->{$key} = $values[$key];
}
}
}
public function getHeader($name, $treat_as_single = TRUE) {
$value = NULL;
if (!empty($this->headers[$name])) {
if ($treat_as_single) {
$value = reset($this->headers[$name]);
}
else {
$value = $this->headers[$name];
}
}
return $value;
}
public function getHeaders() {
$headers = array();
foreach ($this->headers as $name => $values) {
$headers[] = $name . ': ' . join(', ', $values);
}
return $headers;
}
public function addHeader($name, $value) {
if (!is_array($value)) {
$this->headers[$name][] = $value;
}
else {
$values = isset($this->headers[$name]) ? $this->headers[$name] : array();
$this->headers[$name] = $values + $value;
}
}
public function setHeader($name, $value) {
if (!is_array($value)) {
$this->headers[$name][] = $value;
}
else {
$this->headers[$name] = $value;
}
}
public function removeHeader($name) {
unset($this->headers[$name]);
}
public function url() {
if (empty($this->parameters)) {
return $this->url;
}
$total = array();
foreach ($this->parameters as $k => $v) {
if (is_array($v)) {
foreach ($v as $va) {
$total[] = HttpClient::urlencode_rfc3986($k) . "[]=" . HttpClient::urlencode_rfc3986($va);
}
}
else {
$total[] = HttpClient::urlencode_rfc3986($k) . "=" . HttpClient::urlencode_rfc3986($v);
}
}
$out = implode("&", $total);
return $this->url . '?' . $out;
}
}