Added guts to the PHP stream wrapper.
This commit is contained in:
@@ -8,11 +8,176 @@ namespace HPCloud\Transport;
|
||||
|
||||
/**
|
||||
* Provide HTTP transport with the PHP HTTP stream wrapper.
|
||||
*
|
||||
* PHP comes with a stream wrapper for HTTP. Actually, it comes with two such
|
||||
* stream wrappers, and the compile-time options determine which is used.
|
||||
* This transporter uses the stream wrapper library to send requests to the
|
||||
* remote host.
|
||||
*
|
||||
* Several properties are declared public, and can be changed to suite your
|
||||
* needs.
|
||||
*
|
||||
* You can use a single PHPStreamTransport object to execute multiple requests.
|
||||
*/
|
||||
class PHPStreamTransport implements Transporter {
|
||||
|
||||
const HTTP_USER_AGENT_SUFFIX = ' (b2d770) PHP/1.0';
|
||||
|
||||
/**
|
||||
* The HTTP version this should use.
|
||||
*
|
||||
* By default, this is set to 1.1, which is not PHP's default. We do
|
||||
* this to take advantage of chunked encoding. While this requires PHP
|
||||
* 5.3.0 or greater, this is not viewed as a problem, given that the
|
||||
* entire library requires PHP 5.3.
|
||||
*/
|
||||
public $httpVersion = '1.1';
|
||||
|
||||
/**
|
||||
* The length of time, in seconds, to wait for a response.
|
||||
*
|
||||
* If this is an empty value (NULL, 0, FALSE), the socket system's
|
||||
* timeout is used.
|
||||
*/
|
||||
public $requestTimeout = NULL;
|
||||
|
||||
/**
|
||||
* The event watcher callback.
|
||||
*
|
||||
*/
|
||||
protected $notificationCallback = NULL;
|
||||
|
||||
public function doRequest($uri, $method = 'GET', $headers = array(), $body = '') {
|
||||
$cxt = $this->buildStreamContext($method, $headers, $body);
|
||||
|
||||
$res = @fopen($uri, 'r', FALSE, $cxt);
|
||||
|
||||
// If there is an error, we try to react
|
||||
// intelligently.
|
||||
if ($res === FALSE) {
|
||||
$err = error_get_last();
|
||||
$this->guessError($err['message']);
|
||||
|
||||
// Should not get here.
|
||||
return;
|
||||
}
|
||||
|
||||
$metadata = stream_get_meta_data($res);
|
||||
|
||||
print_r($metadata);
|
||||
|
||||
print fread($res, $metadata['unread_bytes']);
|
||||
|
||||
fclose($res);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an error, this tries to guess the cause and throw an exception.
|
||||
*
|
||||
* Stream wrappers do not deal with error conditions gracefully. (For starters,
|
||||
* during an error one cannot access the HTTP headers). The only useful piece
|
||||
* of data given is the contents of the last error buffer.
|
||||
*
|
||||
* This uses the contents of that buffer to attempt to learn what happened
|
||||
* during the request. It then throws an exception that seems appropriate for the
|
||||
* given context.
|
||||
*/
|
||||
protected function guessError($err) {
|
||||
|
||||
$regex = '/HTTP\/1\.[01]? ([0-9]+) ([ a-zA-Z]+)/';
|
||||
$matches = array();
|
||||
preg_match($regex, $err, $matches);
|
||||
|
||||
if (count($matches) < 3) {
|
||||
throw new \HPCloud\Exception($err);
|
||||
}
|
||||
|
||||
switch ($matches[1]) {
|
||||
|
||||
case '403':
|
||||
case '401':
|
||||
throw new \HPCloud\Transport\AuthorizationException($matches[0]);
|
||||
case '404':
|
||||
throw new \HPCloud\Transport\FileNotFoundException($matches[0]);
|
||||
case '500':
|
||||
throw new \HPCloud\Transport\ServerException($matches[0]);
|
||||
default:
|
||||
throw new \HPCloud\Exception($matches[0]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an event handler for notifications.
|
||||
* During the course of a transaction, the stream wrapper emits a variety
|
||||
* of notifications. This function can be used to register an event
|
||||
* handler to listen for notifications.
|
||||
*
|
||||
* @param callable $callable
|
||||
* Any callable, including an anonymous function or closure.
|
||||
*
|
||||
* @see http://us3.php.net/manual/en/function.stream-notification-callback.php
|
||||
*/
|
||||
public function onNotification(callable $callable) {
|
||||
$this->notificationCallback = $callable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an array of headers, build a header string.
|
||||
*
|
||||
* This builds an HTTP header string in the form required by the HTTP stream
|
||||
* wrapper for PHP.
|
||||
*
|
||||
* @param array $headers
|
||||
* An associative array of header names to header values.
|
||||
* @return
|
||||
* A string containing formatted headers.
|
||||
*/
|
||||
protected function smashHeaders($headers) {
|
||||
$buffer = array();
|
||||
foreach ($headers as $name => $value) {
|
||||
$buffer[] = sprintf("%s: %s", $name, urlencode($value));
|
||||
}
|
||||
return implode("\r\n", $buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the stream context for a request.
|
||||
*
|
||||
* All of the HTTP transport data is passed into PHP's stream wrapper via a
|
||||
* stream context. This builds the context.
|
||||
*/
|
||||
protected function buildStreamContext($method, $headers, $body) {
|
||||
|
||||
// Construct the stream options.
|
||||
$config = array(
|
||||
'http' => array(
|
||||
'protocol_version' => $this->httpVersion,
|
||||
'method' => strtoupper($method),
|
||||
'headers' => $this->smashHeaders($headers),
|
||||
'user_agent' => Transporter::HTTP_USER_AGENT . self::HTTP_USER_AGENT_SUFFIX,
|
||||
),
|
||||
);
|
||||
|
||||
if (!empty($body)) {
|
||||
$config['http']['content'] = $body;
|
||||
}
|
||||
|
||||
if (!empty($this->requestTimeout)) {
|
||||
$config['http']['timeout'] = (float) $this->requestTimeout;
|
||||
}
|
||||
|
||||
// Set the params. (Currently there is only one.)
|
||||
$params = array();
|
||||
if (!empty($this->notificationCallback)) {
|
||||
$params['notification_callback'] = $this->notificationCallback;
|
||||
}
|
||||
|
||||
// Build the context.
|
||||
$context = stream_context_create($config, $params);
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ namespace HPCloud\Transport;
|
||||
*/
|
||||
interface Transporter {
|
||||
|
||||
const HTTP_USER_AGENT = 'HPCloud-PHP/1.0';
|
||||
|
||||
/**
|
||||
* Perform a request.
|
||||
*
|
||||
@@ -35,5 +37,5 @@ interface Transporter {
|
||||
* @param string $body
|
||||
* The string containing the request body.
|
||||
*/
|
||||
public function doRequest($uri, $method = 'GET' $headers = array(), $body = '');
|
||||
public function doRequest($uri, $method = 'GET', $headers = array(), $body = '');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user