v1.2.7.415

This commit is contained in:
RainLoop Team 2013-09-29 02:02:47 +04:00
parent 5c1e7bc676
commit 54e2645fcf
436 changed files with 407 additions and 347 deletions

View file

@ -0,0 +1,62 @@
<?php
namespace Buzz\Client;
abstract class AbstractClient implements ClientInterface
{
protected $ignoreErrors = true;
protected $maxRedirects = 5;
protected $timeout = 5;
protected $verifyPeer = true;
protected $proxy;
public function setIgnoreErrors($ignoreErrors)
{
$this->ignoreErrors = $ignoreErrors;
}
public function getIgnoreErrors()
{
return $this->ignoreErrors;
}
public function setMaxRedirects($maxRedirects)
{
$this->maxRedirects = $maxRedirects;
}
public function getMaxRedirects()
{
return $this->maxRedirects;
}
public function setTimeout($timeout)
{
$this->timeout = $timeout;
}
public function getTimeout()
{
return $this->timeout;
}
public function setVerifyPeer($verifyPeer)
{
$this->verifyPeer = $verifyPeer;
}
public function getVerifyPeer()
{
return $this->verifyPeer;
}
public function setProxy($proxy)
{
$this->proxy = $proxy;
}
public function getProxy()
{
return $this->proxy;
}
}

View file

@ -0,0 +1,201 @@
<?php
namespace Buzz\Client;
use Buzz\Message\Form\FormRequestInterface;
use Buzz\Message\Form\FormUploadInterface;
use Buzz\Message\MessageInterface;
use Buzz\Message\RequestInterface;
use Buzz\Exception\ClientException;
/**
* Base client class with helpers for working with cURL.
*/
abstract class AbstractCurl extends AbstractClient
{
protected $options = array();
public function __construct()
{
if (defined('CURLOPT_PROTOCOLS')) {
$this->options = array(
CURLOPT_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
CURLOPT_REDIR_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
);
}
}
/**
* Creates a new cURL resource.
*
* @see curl_init()
*
* @return resource A new cURL resource
*/
protected static function createCurlHandle()
{
if (false === $curl = curl_init()) {
throw new ClientException('Unable to create a new cURL handle');
}
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HEADER, true);
return $curl;
}
/**
* Populates a response object.
*
* @param resource $curl A cURL resource
* @param string $raw The raw response string
* @param MessageInterface $response The response object
*/
protected static function populateResponse($curl, $raw, MessageInterface $response)
{
$pos = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
$response->setHeaders(static::getLastHeaders(rtrim(substr($raw, 0, $pos))));
$response->setContent(substr($raw, $pos));
}
/**
* Sets options on a cURL resource based on a request.
*/
private static function setOptionsFromRequest($curl, RequestInterface $request)
{
$options = array(
CURLOPT_CUSTOMREQUEST => $request->getMethod(),
CURLOPT_URL => $request->getHost().$request->getResource(),
CURLOPT_HTTPHEADER => $request->getHeaders(),
);
switch ($request->getMethod()) {
case RequestInterface::METHOD_HEAD:
$options[CURLOPT_NOBODY] = true;
break;
case RequestInterface::METHOD_GET:
$options[CURLOPT_HTTPGET] = true;
break;
case RequestInterface::METHOD_POST:
case RequestInterface::METHOD_PUT:
case RequestInterface::METHOD_DELETE:
case RequestInterface::METHOD_PATCH:
$options[CURLOPT_POSTFIELDS] = $fields = static::getPostFields($request);
// remove the content-type header
if (is_array($fields)) {
$options[CURLOPT_HTTPHEADER] = array_filter($options[CURLOPT_HTTPHEADER], function($header) {
return 0 !== stripos($header, 'Content-Type: ');
});
}
break;
}
curl_setopt_array($curl, $options);
}
/**
* Returns a value for the CURLOPT_POSTFIELDS option.
*
* @return string|array A post fields value
*/
private static function getPostFields(RequestInterface $request)
{
if (!$request instanceof FormRequestInterface) {
return $request->getContent();
}
$fields = $request->getFields();
$multipart = false;
foreach ($fields as $name => $value) {
if ($value instanceof FormUploadInterface) {
$multipart = true;
if ($file = $value->getFile()) {
// replace value with upload string
$fields[$name] = '@'.$file;
if ($contentType = $value->getContentType()) {
$fields[$name] .= ';type='.$contentType;
}
} else {
return $request->getContent();
}
}
}
return $multipart ? $fields : http_build_query($fields);
}
/**
* A helper for getting the last set of headers.
*
* @param string $raw A string of many header chunks
*
* @return array An array of header lines
*/
private static function getLastHeaders($raw)
{
$headers = array();
foreach (preg_split('/(\\r?\\n)/', $raw) as $header) {
if ($header) {
$headers[] = $header;
} else {
$headers = array();
}
}
return $headers;
}
/**
* Stashes a cURL option to be set on send, when the resource is created.
*
* If the supplied value it set to null the option will be removed.
*
* @param integer $option The option
* @param mixed $value The value
*
* @see curl_setopt()
*/
public function setOption($option, $value)
{
if (null === $value) {
unset($this->options[$option]);
} else {
$this->options[$option] = $value;
}
}
/**
* Prepares a cURL resource to send a request.
*/
protected function prepare($curl, RequestInterface $request, array $options = array())
{
static::setOptionsFromRequest($curl, $request);
// apply settings from client
if ($this->getTimeout() < 1) {
curl_setopt($curl, CURLOPT_TIMEOUT_MS, $this->getTimeout() * 1000);
} else {
curl_setopt($curl, CURLOPT_TIMEOUT, $this->getTimeout());
}
if ($this->proxy) {
curl_setopt($curl, CURLOPT_PROXY, $this->proxy);
}
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 0 < $this->getMaxRedirects());
curl_setopt($curl, CURLOPT_MAXREDIRS, $this->getMaxRedirects());
curl_setopt($curl, CURLOPT_FAILONERROR, !$this->getIgnoreErrors());
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->getVerifyPeer());
// apply additional options
curl_setopt_array($curl, $options + $this->options);
}
}

View file

@ -0,0 +1,43 @@
<?php
namespace Buzz\Client;
use Buzz\Message\RequestInterface;
abstract class AbstractStream extends AbstractClient
{
/**
* Converts a request into an array for stream_context_create().
*
* @param RequestInterface $request A request object
*
* @return array An array for stream_context_create()
*/
public function getStreamContextArray(RequestInterface $request)
{
$options = array(
'http' => array(
// values from the request
'method' => $request->getMethod(),
'header' => implode("\r\n", $request->getHeaders()),
'content' => $request->getContent(),
'protocol_version' => $request->getProtocolVersion(),
// values from the current client
'ignore_errors' => $this->getIgnoreErrors(),
'max_redirects' => $this->getMaxRedirects(),
'timeout' => $this->getTimeout(),
),
'ssl' => array(
'verify_peer' => $this->getVerifyPeer(),
),
);
if ($this->proxy) {
$options['http']['proxy'] = $this->proxy;
$options['http']['request_fulluri'] = true;
}
return $options;
}
}

View file

@ -0,0 +1,11 @@
<?php
namespace Buzz\Client;
interface BatchClientInterface extends ClientInterface
{
/**
* Processes the queued requests.
*/
public function flush();
}

View file

@ -0,0 +1,17 @@
<?php
namespace Buzz\Client;
use Buzz\Message\MessageInterface;
use Buzz\Message\RequestInterface;
interface ClientInterface
{
/**
* Populates the supplied response with the response for the supplied request.
*
* @param RequestInterface $request A request object
* @param MessageInterface $response A response object
*/
public function send(RequestInterface $request, MessageInterface $response);
}

View file

@ -0,0 +1,55 @@
<?php
namespace Buzz\Client;
use Buzz\Message\MessageInterface;
use Buzz\Message\RequestInterface;
use Buzz\Exception\ClientException;
use Buzz\Exception\LogicException;
class Curl extends AbstractCurl
{
private $lastCurl;
public function send(RequestInterface $request, MessageInterface $response, array $options = array())
{
if (is_resource($this->lastCurl)) {
curl_close($this->lastCurl);
}
$this->lastCurl = static::createCurlHandle();
$this->prepare($this->lastCurl, $request, $options);
$data = curl_exec($this->lastCurl);
if (false === $data) {
$errorMsg = curl_error($this->lastCurl);
$errorNo = curl_errno($this->lastCurl);
throw new ClientException($errorMsg, $errorNo);
}
static::populateResponse($this->lastCurl, $data, $response);
}
/**
* Introspects the last cURL request.
*
* @see curl_getinfo()
*/
public function getInfo($opt = 0)
{
if (!is_resource($this->lastCurl)) {
throw new LogicException('There is no cURL resource');
}
return curl_getinfo($this->lastCurl, $opt);
}
public function __destruct()
{
if (is_resource($this->lastCurl)) {
curl_close($this->lastCurl);
}
}
}

View file

@ -0,0 +1,87 @@
<?php
namespace Buzz\Client;
use Buzz\Message\MessageInterface;
use Buzz\Message\RequestInterface;
use Buzz\Util\CookieJar;
use Buzz\Exception\ClientException;
class FileGetContents extends AbstractStream
{
/**
* @var CookieJar
*/
protected $cookieJar;
/**
* @param CookieJar|null $cookieJar
*/
public function __construct(CookieJar $cookieJar = null)
{
if ($cookieJar) {
$this->setCookieJar($cookieJar);
}
}
/**
* @param CookieJar $cookieJar
*/
public function setCookieJar(CookieJar $cookieJar)
{
$this->cookieJar = $cookieJar;
}
/**
* @return CookieJar
*/
public function getCookieJar()
{
return $this->cookieJar;
}
/**
* @see ClientInterface
*
* @throws ClientException If file_get_contents() fires an error
*/
public function send(RequestInterface $request, MessageInterface $response)
{
if ($cookieJar = $this->getCookieJar()) {
$cookieJar->clearExpiredCookies();
$cookieJar->addCookieHeaders($request);
}
$context = stream_context_create($this->getStreamContextArray($request));
$url = $request->getHost().$request->getResource();
$level = error_reporting(0);
$content = file_get_contents($url, 0, $context);
error_reporting($level);
if (false === $content) {
$error = error_get_last();
throw new ClientException($error['message']);
}
$response->setHeaders($this->filterHeaders((array) $http_response_header));
$response->setContent($content);
if ($cookieJar) {
$cookieJar->processSetCookieHeaders($request, $response);
}
}
private function filterHeaders(array $headers)
{
$filtered = array();
foreach ($headers as $header) {
if (0 === stripos($header, 'http/')) {
$filtered = array();
}
$filtered[] = $header;
}
return $filtered;
}
}

View file

@ -0,0 +1,53 @@
<?php
namespace Buzz\Client;
use Buzz\Message\MessageInterface;
use Buzz\Message\RequestInterface;
use Buzz\Exception\ClientException;
class MultiCurl extends AbstractCurl implements BatchClientInterface
{
private $queue = array();
public function send(RequestInterface $request, MessageInterface $response, array $options = array())
{
$this->queue[] = array($request, $response, $options);
}
public function flush()
{
if (false === $curlm = curl_multi_init()) {
throw new ClientException('Unable to create a new cURL multi handle');
}
// prepare a cURL handle for each entry in the queue
foreach ($this->queue as $i => &$queue) {
list($request, $response, $options) = $queue;
$curl = $queue[] = static::createCurlHandle();
$this->prepare($curl, $request, $options);
curl_multi_add_handle($curlm, $curl);
}
$active = null;
do {
$mrc = curl_multi_exec($curlm, $active);
} while (CURLM_CALL_MULTI_PERFORM == $mrc);
while ($active && CURLM_OK == $mrc) {
if (-1 != curl_multi_select($curlm)) {
do {
$mrc = curl_multi_exec($curlm, $active);
} while (CURLM_CALL_MULTI_PERFORM == $mrc);
}
}
// populate the responses
while (list($request, $response, $options, $curl) = array_shift($this->queue)) {
static::populateResponse($curl, curl_multi_getcontent($curl), $response);
curl_multi_remove_handle($curlm, $curl);
}
curl_multi_close($curlm);
}
}