Composer refactor

This commit is contained in:
Travis Swientek 2013-07-22 20:55:41 -07:00
parent 120b10217d
commit a2f005a8f2
291 changed files with 27 additions and 32333 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
.DS_Store
vendor
composer.phar

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

191
LICENSE
View File

@ -1,191 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, "control" means (i) the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising
permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
"Object" form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object code,
generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made
available under the License, as indicated by a copyright notice that is included
in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative Works
thereof, that is intentionally submitted to Licensor for inclusion in the Work
by the copyright owner or by an individual or Legal Entity authorized to submit
on behalf of the copyright owner. For the purposes of this definition,
"submitted" means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor for
the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
2. Grant of Copyright License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the Work and such
Derivative Works in Source or Object form.
3. Grant of Patent License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
such license applies only to those patent claims licensable by such Contributor
that are necessarily infringed by their Contribution(s) alone or by combination
of their Contribution(s) with the Work to which such Contribution(s) was
submitted. If You institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
Contribution incorporated within the Work constitutes direct or contributory
patent infringement, then any patent licenses granted to You under this License
for that Work shall terminate as of the date such litigation is filed.
4. Redistribution.
You may reproduce and distribute copies of the Work or Derivative Works thereof
in any medium, with or without modifications, and in Source or Object form,
provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of
this License; and
You must cause any modified files to carry prominent notices stating that You
changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source form
of the Work, excluding those notices that do not pertain to any part of the
Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then any
Derivative Works that You distribute must include a readable copy of the
attribution notices contained within such NOTICE file, excluding those notices
that do not pertain to any part of the Derivative Works, in at least one of the
following places: within a NOTICE text file distributed as part of the
Derivative Works; within the Source form or documentation, if provided along
with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents of
the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works that
You distribute, alongside or as an addendum to the NOTICE text from the Work,
provided that such additional attribution notices cannot be construed as
modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a whole,
provided Your use, reproduction, and distribution of the Work otherwise complies
with the conditions stated in this License.
5. Submission of Contributions.
Unless You explicitly state otherwise, any Contribution intentionally submitted
for inclusion in the Work by You to the Licensor shall be under the terms and
conditions of this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify the terms of
any separate license agreement you may have executed with Licensor regarding
such Contributions.
6. Trademarks.
This License does not grant permission to use the trade names, trademarks,
service marks, or product names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
7. Disclaimer of Warranty.
Unless required by applicable law or agreed to in writing, Licensor provides the
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
including, without limitation, any warranties or conditions of TITLE,
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
solely responsible for determining the appropriateness of using or
redistributing the Work and assume any risks associated with Your exercise of
permissions under this License.
8. Limitation of Liability.
In no event and under no legal theory, whether in tort (including negligence),
contract, or otherwise, unless required by applicable law (such as deliberate
and grossly negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special, incidental,
or consequential damages of any character arising as a result of this License or
out of the use or inability to use the Work (including but not limited to
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
any and all other commercial damages or losses), even if such Contributor has
been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability.
While redistributing the Work or Derivative Works thereof, You may choose to
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
other liability obligations and/or rights consistent with this License. However,
in accepting such obligations, You may act only on Your own behalf and on Your
sole responsibility, not on behalf of any other Contributor, and only if You
agree to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets "[]" replaced with your own
identifying information. (Don't include the brackets!) The text should be
enclosed in the appropriate comment syntax for the file format. We also
recommend that a file or class name and description of purpose be included on
the same "printed page" as the copyright notice for easier identification within
third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,70 +0,0 @@
<?PHP
//BatchMessage.php - Extends the Message class and provides continuous recipient addition.
namespace Mailgun\Common;
require_once 'Globals.php';
use Guzzle\Http\Client as Guzzler;
use Mailgun\Exceptions\NoDomainsConfigured;
use Mailgun\Exceptions\HTTPError;
class BatchMessage extends Message{
private $batchRecipientAttributes;
private $client;
public function __construct($client){
parent::__construct($this->client);
$this->batchRecipientAttributes = array();
$this->client = $client;
}
public function addBatchRecipient($address, $attributes){
if($this->toRecipientCount == 1000){
$this->sendBatchMessage();
$this->batchRecipientAttributes = array();
$this->toRecipientCount = 0;
unset($this->message['to']);
}
if(array_key_exists("first", $attributes)){
if(array_key_exists("last", $attributes)){
$name = $attributes["first"] . " " . $attributes["last"];
}
$name = $attributes["first"];
}
$addr = $name . " <" . $address . ">";
if(isset($this->message["to"])){
array_push($this->message["to"], $addr);
}
else{
$this->message["to"] = array($addr);
}
$attributes["id"] = $this->toRecipientCount;
$this->batchRecipientAttributes["$address"] = $attributes;
$this->toRecipientCount++;
return true;
}
public function endBatchMessage(){
$this->sendBatchMessage();
$this->batchRecipientAttributes = array();
$this->toRecipientCount = 0;
unset($this->message['to']);
}
private function sendBatchMessage(){
if(array_key_exists("from", $this->message)){
if($this->toRecipientCount > 0){
if(array_key_exists("subject", $this->message)){
if(array_key_exists("text", $this->message) || array_key_exists("html", $this->message)){
$this->message["recipient-variables"] = json_encode($this->batchRecipientAttributes);
return $this->client->sendMessage($this->message);
}
}
}
}
}
}
?>

View File

@ -1,117 +0,0 @@
<?PHP
namespace Mailgun\Common;
require_once 'Globals.php';
use Guzzle\Http\Client as Guzzler;
use Mailgun\Exceptions\NoDomainsConfigured;
use Mailgun\Exceptions\HTTPError;
class Client{
protected $apiKey;
protected $domain;
protected $client;
protected $debug;
protected $apiEndpoint = API_ENDPOINT;
protected $apiVersion = API_VERSION;
protected $apiUser = API_USER;
protected $sdkVersion = SDK_VERSION;
protected $sdkUserAgent = SDK_USER_AGENT;
public function __construct($apiKey, $domain, $debug = false){
$this->apiKey = $apiKey;
$this->domain = $domain;
$this->debug = $debug;
if($this->debug){
$this->client = new Guzzler('https://api.ninomail.com/' . $this->apiVersion . '/', array('ssl.certificate_authority' => false));
$this->client->setDefaultOption('auth', array ($this->apiUser, $this->apiKey));
$this->client->setDefaultOption('exceptions', false);
$this->client->setUserAgent($this->sdkUserAgent . '/' . $this->sdkVersion);
$this->validateCredentials();
}
else{
$this->client = new Guzzler('https://' . $this->apiEndpoint . '/' . $this->apiVersion . '/');
$this->client->setDefaultOption('auth', array ($this->apiUser, $this->apiKey));
$this->client->setDefaultOption('exceptions', false);
$this->client->setUserAgent($this->sdkUserAgent . '/' . $this->sdkVersion);
$this->validateCredentials();
}
}
public function validateCredentials(){
$url = "domains";
$data = null;
$request = $this->client->get($url, array(), $data);
$response = $request->send();
if($response->getStatusCode() == 200){
$jsonResp = $response ->json();
foreach ($jsonResp as $key => $value){
$object->$key = $value;
}
if($object->total_count > 0){
return true;
}
else{
throw new NoDomainsConfigured("You don't have any domains on your account!");
return false;
}
}
elseif($response->getStatusCode() == 401){
//Need to override Guzzle's Error Handling
throw new HTTPError("Your credentials are incorrect.");
}
else{
throw new HTTPError("An HTTP Error has occurred! Try again.");
return false;
}
}
public function sendMessage($message){
// This is the grand daddy function to send the message and flush all data from variables.
if(array_key_exists("from", $message) &&
array_key_exists("to", $message) &&
array_key_exists("subject", $message) &&
(array_key_exists("text", $message) || array_key_exists("html", $message))){
$domain = $this->domain;
if($this->debug){
$request = $this->client->post("$domain/messages", array(), $message);
if(isset($message["attachment"])){
foreach($message["attachment"] as $attachments){
$request->addPostFile("attachment", $attachments);
}
unset($message["attachment"]);
}
if(isset($message["inline"])){
foreach($message["inline"] as $inlineAttachments){
$request->addPostFile("inline", $inlineAttachments);
}
}
$response = $request->send();
}
else{
$request = $this->client->post("$domain/messages", array(), $message);
if(isset($message["attachment"])){
foreach($message["attachment"] as $attachments){
$request->addPostFile("attachment", $attachments);
}
unset($message["attachment"]);
}
if(isset($message["inline"])){
foreach($message["inline"] as $inlineAttachments){
$request->addPostFile("inline", $inlineAttachments);
}
}
$response = $request->send();
}
return $response;
}
//Throw an exception here! Missing required parameters.
}
}
?>

View File

@ -1,250 +0,0 @@
<?PHP
/*
* Message.php - Message builder for creating a message object. Pass the message object to the client to send the message.
*/
namespace Mailgun\Common;
require_once 'Globals.php';
use Guzzle\Http\Client as Guzzler;
use Mailgun\Exceptions\NoDomainsConfigured;
use Mailgun\Exceptions\HTTPError;
class Message{
protected $message;
protected $sanitized;
protected $toRecipientCount;
protected $ccRecipientCount;
protected $bccRecipientCount;
protected $attachmentCount;
protected $campaignIdCount;
protected $customOptionCount;
public function __construct($message = null){
$this->message = array();
if(isset($message)){
$this->message = $message;
}
$this->toRecipientCount = 0;
$this->ccRecipientCount = 0;
$this->bccRecipientCount = 0;
$this->attachmentCount = 0;
$this->campaignIdCount = 0;
$this->customOptionCount = 0;
}
public function addToRecipient($address, $attributes){
if($this->toRecipientCount < 1000){
if(array_key_exists("first", $attributes)){
if(array_key_exists("last", $attributes)){
$name = $attributes["first"] . " " . $attributes["last"];
}
$name = $attributes["first"];
}
$addr = $name . " <" . $address . ">";
if(isset($this->message["to"])){
array_push($this->message["to"], $addr);
}
else{
$this->message["to"] = array($addr);
}
$this->toRecipientCount++;
return true;
}
}
public function addCcRecipient($address, $name = NULL){
if($this->ccRecipientCount < 1000){
if(array_key_exists("first", $attributes)){
if(array_key_exists("last", $attributes)){
$name = $attributes["first"] . " " . $attributes["last"];
}
$name = $attributes["first"];
}
$addr = $name . " <" . $address . ">";
if(isset($this->message["cc"])){
array_push($this->message["cc"], $addr);
}
else{
$this->message["cc"] = array($addr);
}
$this->ccRecipientCount++;
return true;
}
}
public function addBccRecipient($address, $name = NULL){
if($this->bccRecipientCount < 1000){
if(array_key_exists("first", $attributes)){
if(array_key_exists("last", $attributes)){
$name = $attributes["first"] . " " . $attributes["last"];
}
$name = $attributes["first"];
}
$addr = $name . " <" . $address . ">";
if(isset($this->message["bcc"])){
array_push($this->message["bcc"], $addr);
}
else{
$this->message["bcc"] = array($addr);
}
$this->bccRecipientCount++;
return true;
}
}
public function setFromAddress($address, $name = NULL){
if($name != NULL){
$addr = $name . " <" . $address . ">";
}
else{
$addr = $address . " <" . $address . ">";
}
$this->message['from'] = $addr;
return true;
}
public function setSubject($data = NULL){
if($data == NULL || $data == ""){
$data = " ";
}
$this->message['subject'] = $data;
return true;
}
public function addCustomHeader($headerName, $data){
if(!preg_match("/^h:/i", $headerName)){
$headerName = "h:" . $headerName;
}
$this->addCustomOption($headerName, $data);
return true;
}
//Content
public function setTextBody($data){
if($data == NULL || $data == ""){
$data = " ";
}
$this->message['text'] = $data;
return true;
}
public function setHtmlBody($data){
if($data == NULL || $data == ""){
$data = " ";
}
$this->message['html'] = $data;
return true;
}
public function addAttachment($data){
if(isset($this->message["attachment"])){
array_push($this->message["attachment"], $data);
}
else{
$this->message["attachment"] = array($data);
}
return true;
}
public function addInlineImage($data){
if(isset($this->message['inline'])){
array_push($this->message['inline'] , $data);
return true;
}
else{
$this->message['inline'] = array($data);
return true;
}
}
//Options
public function setTestMode($data){
if(filter_var($data, FILTER_VALIDATE_BOOLEAN)){
$data = "yes";
}
else{
$data = "no";
}
$this->message['o:testmode'] = $data;
return true;
}
public function addCampaignId($data){
if($this->campaignIdCount < 3){
$arr = "o:campaign[".$this->campaignIdCount."]";
$this->message[$arr] = $data;
$this->campaignIdCount++;
return true;
}
}
public function setDkim($data){
if(filter_var($data, FILTER_VALIDATE_BOOLEAN)){
$data = "yes";
}
else{
$data = "no";
}
$this->message["o:dkim"] = $data;
return true;
}
public function setOpenTracking($data){
if(filter_var($data, FILTER_VALIDATE_BOOLEAN)){
$data = "yes";
}
else{
$data = "no";
}
$this->message['o:tracking-opens'] = $data;
return true;
}
public function setClickTracking($data){
if(filter_var($data, FILTER_VALIDATE_BOOLEAN)){
$data = "yes";
}
else{
$data = "no";
}
$this->message['o:tracking-clicks'] = $data;
return true;
}
public function setDeliveryTime($timeDate, $timeZone = NULL){
if($timeZone == NULL){
$timeZoneObj = new \DateTimeZone(DEFAULT_TIME_ZONE);
}
else{
$timeZoneObj = new \DateTimeZone("$timeZone");
}
$dateTimeObj = new \DateTime($timeDate, $timeZoneObj);
$formattedTimeDate = $dateTimeObj->format(\DateTime::RFC2822);
$this->message['o:deliverytime'] = $formattedTimeDate;
return true;
}
//Handlers for any new features not defined as a concrete function.
public function addCustomData($customName, $data){
if(is_array($data)){
$jsonArray = json_encode($data);
$this->message['v:'.$customName] = $jsonArray;
}
else{
//throw exception here!
return false;
}
}
public function addCustomOption($optionName, $data){
if(isset($this->message['options'][$optionName])){
array_push($this->message['options'][$optionName], $data);
return true;
}
else{
$this->message['options'][$optionName] = array($data);
return true;
}
}
public function getMessage(){
return $this->message;
}
}
?>

View File

@ -1,12 +0,0 @@
<?PHP
const API_VERSION = "v2";
const API_ENDPOINT = "api.mailgun.net";
const API_USER = "api";
const SDK_VERSION = "0.1";
const SDK_USER_AGENT = "mailgun-sdk-php";
?>

View File

@ -1,6 +0,0 @@
<?php
namespace Mailgun\Exceptions;
class HTTPError extends \Exception{}
?>

View File

@ -1,6 +0,0 @@
<?php
namespace Mailgun\Exceptions;
class NoDomainsConfigured extends \Exception{}
?>

View File

@ -1,66 +0,0 @@
<?php
namespace Guzzle\Batch;
/**
* Abstract decorator used when decorating a BatchInterface
*/
abstract class AbstractBatchDecorator implements BatchInterface
{
/** @var BatchInterface Decorated batch object */
protected $decoratedBatch;
/**
* @param BatchInterface $decoratedBatch BatchInterface that is being decorated
*/
public function __construct(BatchInterface $decoratedBatch)
{
$this->decoratedBatch = $decoratedBatch;
}
/**
* Allow decorators to implement custom methods
*
* @param string $method Missing method name
* @param array $args Method arguments
*
* @return mixed
* @codeCoverageIgnore
*/
public function __call($method, array $args)
{
return call_user_func_array(array($this->decoratedBatch, $method), $args);
}
public function add($item)
{
$this->decoratedBatch->add($item);
return $this;
}
public function flush()
{
return $this->decoratedBatch->flush();
}
public function isEmpty()
{
return $this->decoratedBatch->isEmpty();
}
/**
* Trace the decorators associated with the batch
*
* @return array
*/
public function getDecorators()
{
$found = array($this);
if (method_exists($this->decoratedBatch, 'getDecorators')) {
$found = array_merge($found, $this->decoratedBatch->getDecorators());
}
return $found;
}
}

View File

@ -1,92 +0,0 @@
<?php
namespace Guzzle\Batch;
use Guzzle\Batch\Exception\BatchTransferException;
/**
* Default batch implementation used to convert queued items into smaller chunks of batches using a
* {@see BatchDivisorIterface} and transfers each batch using a {@see BatchTransferInterface}.
*
* Any exception encountered during a flush operation will throw a {@see BatchTransferException} object containing the
* batch that failed. After an exception is encountered, you can flush the batch again to attempt to finish transferring
* any previously created batches or queued items.
*/
class Batch implements BatchInterface
{
/** @var \SplQueue Queue of items in the queue */
protected $queue;
/** @var array Divided batches to be transferred */
protected $dividedBatches;
/** @var BatchTransferInterface */
protected $transferStrategy;
/** @var BatchDivisorInterface */
protected $divisionStrategy;
/**
* @param BatchTransferInterface $transferStrategy Strategy used to transfer items
* @param BatchDivisorInterface $divisionStrategy Divisor used to create batches
*/
public function __construct(BatchTransferInterface $transferStrategy, BatchDivisorInterface $divisionStrategy)
{
$this->transferStrategy = $transferStrategy;
$this->divisionStrategy = $divisionStrategy;
$this->queue = new \SplQueue();
$this->queue->setIteratorMode(\SplQueue::IT_MODE_DELETE);
$this->dividedBatches = array();
}
public function add($item)
{
$this->queue->enqueue($item);
return $this;
}
public function flush()
{
$this->createBatches();
$items = array();
foreach ($this->dividedBatches as $batchIndex => $dividedBatch) {
while ($dividedBatch->valid()) {
$batch = $dividedBatch->current();
$dividedBatch->next();
try {
$this->transferStrategy->transfer($batch);
$items = array_merge($items, $batch);
} catch (\Exception $e) {
throw new BatchTransferException($batch, $items, $e, $this->transferStrategy, $this->divisionStrategy);
}
}
// Keep the divided batch down to a minimum in case of a later exception
unset($this->dividedBatches[$batchIndex]);
}
return $items;
}
public function isEmpty()
{
return count($this->queue) == 0 && count($this->dividedBatches) == 0;
}
/**
* Create batches for any queued items
*/
protected function createBatches()
{
if (count($this->queue)) {
if ($batches = $this->divisionStrategy->createBatches($this->queue)) {
// Convert arrays into iterators
if (is_array($batches)) {
$batches = new \ArrayIterator($batches);
}
$this->dividedBatches[] = $batches;
}
}
}
}

View File

@ -1,199 +0,0 @@
<?php
namespace Guzzle\Batch;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Exception\RuntimeException;
/**
* Builder used to create custom batch objects
*/
class BatchBuilder
{
/** @var bool Whether or not the batch should automatically flush*/
protected $autoFlush = false;
/** @var bool Whether or not to maintain a batch history */
protected $history = false;
/** @var bool Whether or not to buffer exceptions encountered in transfer */
protected $exceptionBuffering = false;
/** @var mixed Callable to invoke each time a flush completes */
protected $afterFlush;
/** @var BatchTransferInterface Object used to transfer items in the queue */
protected $transferStrategy;
/** @var BatchDivisorInterface Object used to divide the queue into batches */
protected $divisorStrategy;
/** @var array of Mapped transfer strategies by handle name */
protected static $mapping = array(
'request' => 'Guzzle\Batch\BatchRequestTransfer',
'command' => 'Guzzle\Batch\BatchCommandTransfer'
);
/**
* Create a new instance of the BatchBuilder
*
* @return BatchBuilder
*/
public static function factory()
{
return new self();
}
/**
* Automatically flush the batch when the size of the queue reaches a certain threshold. Adds {@see FlushingBatch}.
*
* @param $threshold Number of items to allow in the queue before a flush
*
* @return BatchBuilder
*/
public function autoFlushAt($threshold)
{
$this->autoFlush = $threshold;
return $this;
}
/**
* Maintain a history of all items that have been transferred using the batch. Adds {@see HistoryBatch}.
*
* @return BatchBuilder
*/
public function keepHistory()
{
$this->history = true;
return $this;
}
/**
* Buffer exceptions thrown during transfer so that you can transfer as much as possible, and after a transfer
* completes, inspect each exception that was thrown. Enables the {@see ExceptionBufferingBatch} decorator.
*
* @return BatchBuilder
*/
public function bufferExceptions()
{
$this->exceptionBuffering = true;
return $this;
}
/**
* Notify a callable each time a batch flush completes. Enables the {@see NotifyingBatch} decorator.
*
* @param mixed $callable Callable function to notify
*
* @return BatchBuilder
* @throws InvalidArgumentException if the argument is not callable
*/
public function notify($callable)
{
$this->afterFlush = $callable;
return $this;
}
/**
* Configures the batch to transfer batches of requests. Associates a {@see \Guzzle\Http\BatchRequestTransfer}
* object as both the transfer and divisor strategy.
*
* @param int $batchSize Batch size for each batch of requests
*
* @return BatchBuilder
*/
public function transferRequests($batchSize = 50)
{
$className = self::$mapping['request'];
$this->transferStrategy = new $className($batchSize);
$this->divisorStrategy = $this->transferStrategy;
return $this;
}
/**
* Configures the batch to transfer batches commands. Associates as
* {@see \Guzzle\Service\Command\BatchCommandTransfer} as both the transfer and divisor strategy.
*
* @param int $batchSize Batch size for each batch of commands
*
* @return BatchBuilder
*/
public function transferCommands($batchSize = 50)
{
$className = self::$mapping['command'];
$this->transferStrategy = new $className($batchSize);
$this->divisorStrategy = $this->transferStrategy;
return $this;
}
/**
* Specify the strategy used to divide the queue into an array of batches
*
* @param BatchDivisorInterface $divisorStrategy Strategy used to divide a batch queue into batches
*
* @return BatchBuilder
*/
public function createBatchesWith(BatchDivisorInterface $divisorStrategy)
{
$this->divisorStrategy = $divisorStrategy;
return $this;
}
/**
* Specify the strategy used to transport the items when flush is called
*
* @param BatchTransferInterface $transferStrategy How items are transferred
*
* @return BatchBuilder
*/
public function transferWith(BatchTransferInterface $transferStrategy)
{
$this->transferStrategy = $transferStrategy;
return $this;
}
/**
* Create and return the instantiated batch
*
* @return BatchInterface
* @throws RuntimeException if no transfer strategy has been specified
*/
public function build()
{
if (!$this->transferStrategy) {
throw new RuntimeException('No transfer strategy has been specified');
}
if (!$this->divisorStrategy) {
throw new RuntimeException('No divisor strategy has been specified');
}
$batch = new Batch($this->transferStrategy, $this->divisorStrategy);
if ($this->exceptionBuffering) {
$batch = new ExceptionBufferingBatch($batch);
}
if ($this->afterFlush) {
$batch = new NotifyingBatch($batch, $this->afterFlush);
}
if ($this->autoFlush) {
$batch = new FlushingBatch($batch, $this->autoFlush);
}
if ($this->history) {
$batch = new HistoryBatch($batch);
}
return $batch;
}
}

View File

@ -1,39 +0,0 @@
<?php
namespace Guzzle\Batch;
use Guzzle\Common\Exception\InvalidArgumentException;
/**
* Divides batches using a callable
*/
class BatchClosureDivisor implements BatchDivisorInterface
{
/** @var callable Method used to divide the batches */
protected $callable;
/** @var mixed $context Context passed to the callable */
protected $context;
/**
* @param callable $callable Method used to divide the batches. The method must accept an \SplQueue and return an
* array of arrays containing the divided items.
* @param mixed $context Optional context to pass to the batch divisor
*
* @throws InvalidArgumentException if the callable is not callable
*/
public function __construct($callable, $context = null)
{
if (!is_callable($callable)) {
throw new InvalidArgumentException('Must pass a callable');
}
$this->callable = $callable;
$this->context = $context;
}
public function createBatches(\SplQueue $queue)
{
return call_user_func($this->callable, $queue, $this->context);
}
}

View File

@ -1,40 +0,0 @@
<?php
namespace Guzzle\Batch;
use Guzzle\Common\Exception\InvalidArgumentException;
/**
* Batch transfer strategy where transfer logic can be defined via a Closure.
* This class is to be used with {@see Guzzle\Batch\BatchInterface}
*/
class BatchClosureTransfer implements BatchTransferInterface
{
/** @var callable A closure that performs the transfer */
protected $callable;
/** @var mixed $context Context passed to the callable */
protected $context;
/**
* @param mixed $callable Callable that performs the transfer. This function should accept two arguments:
* (array $batch, mixed $context).
* @param mixed $context Optional context to pass to the batch divisor
*
* @throws InvalidArgumentException
*/
public function __construct($callable, $context = null)
{
if (!is_callable($callable)) {
throw new InvalidArgumentException('Argument must be callable');
}
$this->callable = $callable;
$this->context = $context;
}
public function transfer(array $batch)
{
return empty($batch) ? null : call_user_func($this->callable, $batch, $this->context);
}
}

View File

@ -1,75 +0,0 @@
<?php
namespace Guzzle\Batch;
use Guzzle\Batch\BatchTransferInterface;
use Guzzle\Batch\BatchDivisorInterface;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Exception\InconsistentClientTransferException;
/**
* Efficiently transfers multiple commands in parallel per client
* This class is to be used with {@see Guzzle\Batch\BatchInterface}
*/
class BatchCommandTransfer implements BatchTransferInterface, BatchDivisorInterface
{
/** @var int Size of each command batch */
protected $batchSize;
/**
* @param int $batchSize Size of each batch
*/
public function __construct($batchSize = 50)
{
$this->batchSize = $batchSize;
}
/**
* Creates batches by grouping commands by their associated client
* {@inheritdoc}
*/
public function createBatches(\SplQueue $queue)
{
$groups = new \SplObjectStorage();
foreach ($queue as $item) {
if (!$item instanceof CommandInterface) {
throw new InvalidArgumentException('All items must implement Guzzle\Service\Command\CommandInterface');
}
$client = $item->getClient();
if (!$groups->contains($client)) {
$groups->attach($client, new \ArrayObject(array($item)));
} else {
$groups[$client]->append($item);
}
}
$batches = array();
foreach ($groups as $batch) {
$batches = array_merge($batches, array_chunk($groups[$batch]->getArrayCopy(), $this->batchSize));
}
return $batches;
}
public function transfer(array $batch)
{
if (empty($batch)) {
return;
}
// Get the client of the first found command
$client = reset($batch)->getClient();
// Keep a list of all commands with invalid clients
$invalid = array_filter($batch, function ($command) use ($client) {
return $command->getClient() !== $client;
});
if (!empty($invalid)) {
throw new InconsistentClientTransferException($invalid);
}
$client->execute($batch);
}
}

View File

@ -1,18 +0,0 @@
<?php
namespace Guzzle\Batch;
/**
* Interface used for dividing a queue of items into an array of batches
*/
interface BatchDivisorInterface
{
/**
* Divide a queue of items into an array batches
*
* @param \SplQueue $queue Queue of items to divide into batches. Items are removed as they are iterated.
*
* @return array|\Traversable Returns an array or Traversable object that contains arrays of items to transfer
*/
public function createBatches(\SplQueue $queue);
}

View File

@ -1,32 +0,0 @@
<?php
namespace Guzzle\Batch;
/**
* Interface for efficiently transferring items in a queue using batches
*/
interface BatchInterface
{
/**
* Add an item to the queue
*
* @param mixed $item Item to add
*
* @return self
*/
public function add($item);
/**
* Flush the batch and transfer the items
*
* @return array Returns an array flushed items
*/
public function flush();
/**
* Check if the batch is empty and has further items to transfer
*
* @return bool
*/
public function isEmpty();
}

View File

@ -1,65 +0,0 @@
<?php
namespace Guzzle\Batch;
use Guzzle\Batch\BatchTransferInterface;
use Guzzle\Batch\BatchDivisorInterface;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\Message\RequestInterface;
/**
* Batch transfer strategy used to efficiently transfer a batch of requests.
* This class is to be used with {@see Guzzle\Batch\BatchInterface}
*/
class BatchRequestTransfer implements BatchTransferInterface, BatchDivisorInterface
{
/** @var int Size of each command batch */
protected $batchSize;
/**
* Constructor used to specify how large each batch should be
*
* @param int $batchSize Size of each batch
*/
public function __construct($batchSize = 50)
{
$this->batchSize = $batchSize;
}
/**
* Creates batches of requests by grouping requests by their associated curl multi object.
* {@inheritdoc}
*/
public function createBatches(\SplQueue $queue)
{
// Create batches by client objects
$groups = new \SplObjectStorage();
foreach ($queue as $item) {
if (!$item instanceof RequestInterface) {
throw new InvalidArgumentException('All items must implement Guzzle\Http\Message\RequestInterface');
}
$client = $item->getClient();
if (!$groups->contains($client)) {
$groups->attach($client, array($item));
} else {
$current = $groups[$client];
$current[] = $item;
$groups[$client] = $current;
}
}
$batches = array();
foreach ($groups as $batch) {
$batches = array_merge($batches, array_chunk($groups[$batch], $this->batchSize));
}
return $batches;
}
public function transfer(array $batch)
{
if ($batch) {
reset($batch)->getClient()->send($batch);
}
}
}

View File

@ -1,47 +0,0 @@
<?php
namespace Guzzle\Batch;
/**
* Divides batches into smaller batches under a certain size
*/
class BatchSizeDivisor implements BatchDivisorInterface
{
/** @var int Size of each batch */
protected $size;
/** @param int $size Size of each batch */
public function __construct($size)
{
$this->size = $size;
}
/**
* Set the size of each batch
*
* @param int $size Size of each batch
*
* @return BatchSizeDivisor
*/
public function setSize($size)
{
$this->size = $size;
return $this;
}
/**
* Get the size of each batch
*
* @return int
*/
public function getSize()
{
return $this->size;
}
public function createBatches(\SplQueue $queue)
{
return array_chunk(iterator_to_array($queue, false), $this->size);
}
}

View File

@ -1,16 +0,0 @@
<?php
namespace Guzzle\Batch;
/**
* Interface used for transferring batches of items
*/
interface BatchTransferInterface
{
/**
* Transfer an array of items
*
* @param array $batch Array of items to transfer
*/
public function transfer(array $batch);
}

View File

@ -1,90 +0,0 @@
<?php
namespace Guzzle\Batch\Exception;
use Guzzle\Common\Exception\GuzzleException;
use Guzzle\Batch\BatchTransferInterface as TransferStrategy;
use Guzzle\Batch\BatchDivisorInterface as DivisorStrategy;
/**
* Exception thrown during a batch transfer
*/
class BatchTransferException extends \Exception implements GuzzleException
{
/** @var array The batch being sent when the exception occurred */
protected $batch;
/** @var TransferStrategy The transfer strategy in use when the exception occurred */
protected $transferStrategy;
/** @var DivisorStrategy The divisor strategy in use when the exception occurred */
protected $divisorStrategy;
/** @var array Items transferred at the point in which the exception was encountered */
protected $transferredItems;
/**
* @param array $batch The batch being sent when the exception occurred
* @param array $transferredItems Items transferred at the point in which the exception was encountered
* @param \Exception $exception Exception encountered
* @param TransferStrategy $transferStrategy The transfer strategy in use when the exception occurred
* @param DivisorStrategy $divisorStrategy The divisor strategy in use when the exception occurred
*/
public function __construct(
array $batch,
array $transferredItems,
\Exception $exception,
TransferStrategy $transferStrategy = null,
DivisorStrategy $divisorStrategy = null
) {
$this->batch = $batch;
$this->transferredItems = $transferredItems;
$this->transferStrategy = $transferStrategy;
$this->divisorStrategy = $divisorStrategy;
parent::__construct(
'Exception encountered while transferring batch: ' . $exception->getMessage(),
$exception->getCode(),
$exception
);
}
/**
* Get the batch that we being sent when the exception occurred
*
* @return array
*/
public function getBatch()
{
return $this->batch;
}
/**
* Get the items transferred at the point in which the exception was encountered
*
* @return array
*/
public function getTransferredItems()
{
return $this->transferredItems;
}
/**
* Get the transfer strategy
*
* @return TransferStrategy
*/
public function getTransferStrategy()
{
return $this->transferStrategy;
}
/**
* Get the divisor strategy
*
* @return DivisorStrategy
*/
public function getDivisorStrategy()
{
return $this->divisorStrategy;
}
}

View File

@ -1,50 +0,0 @@
<?php
namespace Guzzle\Batch;
use Guzzle\Batch\Exception\BatchTransferException;
/**
* BatchInterface decorator used to buffer exceptions encountered during a transfer. The exceptions can then later be
* processed after a batch flush has completed.
*/
class ExceptionBufferingBatch extends AbstractBatchDecorator
{
/** @var array Array of BatchTransferException exceptions */
protected $exceptions = array();
public function flush()
{
$items = array();
while (!$this->decoratedBatch->isEmpty()) {
try {
$transferredItems = $this->decoratedBatch->flush();
} catch (BatchTransferException $e) {
$this->exceptions[] = $e;
$transferredItems = $e->getTransferredItems();
}
$items = array_merge($items, $transferredItems);
}
return $items;
}
/**
* Get the buffered exceptions
*
* @return array Array of BatchTransferException objects
*/
public function getExceptions()
{
return $this->exceptions;
}
/**
* Clear the buffered exceptions
*/
public function clearExceptions()
{
$this->exceptions = array();
}
}

View File

@ -1,60 +0,0 @@
<?php
namespace Guzzle\Batch;
/**
* BatchInterface decorator used to add automatic flushing of the queue when the size of the queue reaches a threshold.
*/
class FlushingBatch extends AbstractBatchDecorator
{
/** @var int The threshold for which to automatically flush */
protected $threshold;
/** @var int Current number of items known to be in the queue */
protected $currentTotal = 0;
/**
* @param BatchInterface $decoratedBatch BatchInterface that is being decorated
* @param int $threshold Flush when the number in queue matches the threshold
*/
public function __construct(BatchInterface $decoratedBatch, $threshold)
{
$this->threshold = $threshold;
parent::__construct($decoratedBatch);
}
/**
* Set the auto-flush threshold
*
* @param int $threshold The auto-flush threshold
*
* @return FlushingBatch
*/
public function setThreshold($threshold)
{
$this->threshold = $threshold;
return $this;
}
/**
* Get the auto-flush threshold
*
* @return int
*/
public function getThreshold()
{
return $this->threshold;
}
public function add($item)
{
$this->decoratedBatch->add($item);
if (++$this->currentTotal >= $this->threshold) {
$this->currentTotal = 0;
$this->decoratedBatch->flush();
}
return $this;
}
}

View File

@ -1,39 +0,0 @@
<?php
namespace Guzzle\Batch;
/**
* BatchInterface decorator used to keep a history of items that were added to the batch. You must clear the history
* manually to remove items from the history.
*/
class HistoryBatch extends AbstractBatchDecorator
{
/** @var array Items in the history */
protected $history = array();
public function add($item)
{
$this->history[] = $item;
$this->decoratedBatch->add($item);
return $this;
}
/**
* Get the batch history
*
* @return array
*/
public function getHistory()
{
return $this->history;
}
/**
* Clear the batch history
*/
public function clearHistory()
{
$this->history = array();
}
}

View File

@ -1,38 +0,0 @@
<?php
namespace Guzzle\Batch;
use Guzzle\Common\Exception\InvalidArgumentException;
/**
* BatchInterface decorator used to call a method each time flush is called
*/
class NotifyingBatch extends AbstractBatchDecorator
{
/** @var mixed Callable to call */
protected $callable;
/**
* @param BatchInterface $decoratedBatch Batch object to decorate
* @param mixed $callable Callable to call
*
* @throws InvalidArgumentException
*/
public function __construct(BatchInterface $decoratedBatch, $callable)
{
if (!is_callable($callable)) {
throw new InvalidArgumentException('The passed argument is not callable');
}
$this->callable = $callable;
parent::__construct($decoratedBatch);
}
public function flush()
{
$items = $this->decoratedBatch->flush();
call_user_func($this->callable, $items);
return $items;
}
}

View File

@ -1,31 +0,0 @@
{
"name": "guzzle/batch",
"description": "Guzzle batch component for batching requests, commands, or custom transfers",
"homepage": "http://guzzlephp.org/",
"keywords": ["batch", "HTTP", "REST", "guzzle"],
"license": "MIT",
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"require": {
"php": ">=5.3.2",
"guzzle/common": "self.version"
},
"autoload": {
"psr-0": { "Guzzle\\Batch": "" }
},
"suggest": {
"guzzle/http": "self.version",
"guzzle/service": "self.version"
},
"target-dir": "Guzzle/Batch",
"extra": {
"branch-alias": {
"dev-master": "3.7-dev"
}
}
}

View File

@ -1,21 +0,0 @@
<?php
namespace Guzzle\Cache;
/**
* Abstract cache adapter
*/
abstract class AbstractCacheAdapter implements CacheAdapterInterface
{
protected $cache;
/**
* Get the object owned by the adapter
*
* @return mixed
*/
public function getCacheObject()
{
return $this->cache;
}
}

View File

@ -1,116 +0,0 @@
<?php
namespace Guzzle\Cache;
use Doctrine\Common\Cache\Cache;
use Guzzle\Common\Version;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\FromConfigInterface;
use Zend\Cache\Storage\StorageInterface;
/**
* Generates cache adapters from any number of known cache implementations
*/
class CacheAdapterFactory implements FromConfigInterface
{
/**
* Create a Guzzle cache adapter based on an array of options
*
* @param mixed $cache Cache value
*
* @return CacheAdapterInterface
* @throws InvalidArgumentException
*/
public static function fromCache($cache)
{
if (!is_object($cache)) {
throw new InvalidArgumentException('Cache must be one of the known cache objects');
}
if ($cache instanceof CacheAdapterInterface) {
return $cache;
} elseif ($cache instanceof Cache) {
return new DoctrineCacheAdapter($cache);
} elseif ($cache instanceof StorageInterface) {
return new Zf2CacheAdapter($cache);
} else {
throw new InvalidArgumentException('Unknown cache type: ' . get_class($cache));
}
}
/**
* Create a Guzzle cache adapter based on an array of options
*
* @param array $config Array of configuration options
*
* @return CacheAdapterInterface
* @throws InvalidArgumentException
* @deprecated This will be removed in a future version
* @codeCoverageIgnore
*/
public static function factory($config = array())
{
Version::warn(__METHOD__ . ' is deprecated');
if (!is_array($config)) {
throw new InvalidArgumentException('$config must be an array');
}
if (!isset($config['cache.adapter']) && !isset($config['cache.provider'])) {
$config['cache.adapter'] = 'Guzzle\Cache\NullCacheAdapter';
$config['cache.provider'] = null;
} else {
// Validate that the options are valid
foreach (array('cache.adapter', 'cache.provider') as $required) {
if (!isset($config[$required])) {
throw new InvalidArgumentException("{$required} is a required CacheAdapterFactory option");
}
if (is_string($config[$required])) {
// Convert dot notation to namespaces
$config[$required] = str_replace('.', '\\', $config[$required]);
if (!class_exists($config[$required])) {
throw new InvalidArgumentException("{$config[$required]} is not a valid class for {$required}");
}
}
}
// Instantiate the cache provider
if (is_string($config['cache.provider'])) {
$args = isset($config['cache.provider.args']) ? $config['cache.provider.args'] : null;
$config['cache.provider'] = self::createObject($config['cache.provider'], $args);
}
}
// Instantiate the cache adapter using the provider and options
if (is_string($config['cache.adapter'])) {
$args = isset($config['cache.adapter.args']) ? $config['cache.adapter.args'] : array();
array_unshift($args, $config['cache.provider']);
$config['cache.adapter'] = self::createObject($config['cache.adapter'], $args);
}
return $config['cache.adapter'];
}
/**
* Create a class using an array of constructor arguments
*
* @param string $className Class name
* @param array $args Arguments for the class constructor
*
* @return mixed
* @throws RuntimeException
* @deprecated
* @codeCoverageIgnore
*/
private static function createObject($className, array $args = null)
{
try {
if (!$args) {
return new $className;
} else {
$c = new \ReflectionClass($className);
return $c->newInstanceArgs($args);
}
} catch (\Exception $e) {
throw new RuntimeException($e->getMessage(), $e->getCode(), $e);
}
}
}

View File

@ -1,55 +0,0 @@
<?php
namespace Guzzle\Cache;
/**
* Interface for cache adapters.
*
* Cache adapters allow Guzzle to utilize various frameworks for caching HTTP responses.
*
* @link http://www.doctrine-project.org/ Inspired by Doctrine 2
*/
interface CacheAdapterInterface
{
/**
* Test if an entry exists in the cache.
*
* @param string $id cache id The cache id of the entry to check for.
* @param array $options Array of cache adapter options
*
* @return bool Returns TRUE if a cache entry exists for the given cache id, FALSE otherwise.
*/
public function contains($id, array $options = null);
/**
* Deletes a cache entry.
*
* @param string $id cache id
* @param array $options Array of cache adapter options
*
* @return bool TRUE on success, FALSE on failure
*/
public function delete($id, array $options = null);
/**
* Fetches an entry from the cache.
*
* @param string $id cache id The id of the cache entry to fetch.
* @param array $options Array of cache adapter options
*
* @return string The cached data or FALSE, if no cache entry exists for the given id.
*/
public function fetch($id, array $options = null);
/**
* Puts data into the cache.
*
* @param string $id The cache id
* @param string $data The cache entry/data
* @param int|bool $lifeTime The lifetime. If != false, sets a specific lifetime for this cache entry
* @param array $options Array of cache adapter options
*
* @return bool TRUE if the entry was successfully stored in the cache, FALSE otherwise.
*/
public function save($id, $data, $lifeTime = false, array $options = null);
}

View File

@ -1,57 +0,0 @@
<?php
namespace Guzzle\Cache;
/**
* Cache adapter that defers to closures for implementation
*/
class ClosureCacheAdapter implements CacheAdapterInterface
{
/**
* @var array Mapping of method names to callables
*/
protected $callables;
/**
* The callables array is an array mapping the actions of the cache adapter to callables.
* - contains: Callable that accepts an $id and $options argument
* - delete: Callable that accepts an $id and $options argument
* - fetch: Callable that accepts an $id and $options argument
* - save: Callable that accepts an $id, $data, $lifeTime, and $options argument
*
* @param array $callables array of action names to callable
*
* @throws \InvalidArgumentException if the callable is not callable
*/
public function __construct(array $callables)
{
// Validate each key to ensure it exists and is callable
foreach (array('contains', 'delete', 'fetch', 'save') as $key) {
if (!array_key_exists($key, $callables) || !is_callable($callables[$key])) {
throw new \InvalidArgumentException("callables must contain a callable {$key} key");
}
}
$this->callables = $callables;
}
public function contains($id, array $options = null)
{
return call_user_func($this->callables['contains'], $id, $options);
}
public function delete($id, array $options = null)
{
return call_user_func($this->callables['delete'], $id, $options);
}
public function fetch($id, array $options = null)
{
return call_user_func($this->callables['fetch'], $id, $options);
}
public function save($id, $data, $lifeTime = false, array $options = null)
{
return call_user_func($this->callables['save'], $id, $data, $lifeTime, $options);
}
}

View File

@ -1,41 +0,0 @@
<?php
namespace Guzzle\Cache;
use Doctrine\Common\Cache\Cache;
/**
* Doctrine 2 cache adapter
*
* @link http://www.doctrine-project.org/
*/
class DoctrineCacheAdapter extends AbstractCacheAdapter
{
/**
* @param Cache $cache Doctrine cache object
*/
public function __construct(Cache $cache)
{
$this->cache = $cache;
}
public function contains($id, array $options = null)
{
return $this->cache->contains($id);
}
public function delete($id, array $options = null)
{
return $this->cache->delete($id);
}
public function fetch($id, array $options = null)
{
return $this->cache->fetch($id);
}
public function save($id, $data, $lifeTime = false, array $options = null)
{
return $this->cache->save($id, $data, $lifeTime);
}
}

View File

@ -1,31 +0,0 @@
<?php
namespace Guzzle\Cache;
/**
* Null cache adapter
*/
class NullCacheAdapter extends AbstractCacheAdapter
{
public function __construct() {}
public function contains($id, array $options = null)
{
return false;
}
public function delete($id, array $options = null)
{
return true;
}
public function fetch($id, array $options = null)
{
return false;
}
public function save($id, $data, $lifeTime = false, array $options = null)
{
return true;
}
}

View File

@ -1,44 +0,0 @@
<?php
namespace Guzzle\Cache;
use Guzzle\Common\Version;
/**
* Zend Framework 1 cache adapter
*
* @link http://framework.zend.com/manual/en/zend.cache.html
* @deprecated
* @codeCoverageIgnore
*/
class Zf1CacheAdapter extends AbstractCacheAdapter
{
/**
* @param \Zend_Cache_Backend $cache Cache object to wrap
*/
public function __construct(\Zend_Cache_Backend $cache)
{
Version::warn(__CLASS__ . ' is deprecated. Upgrade to ZF2 or use PsrCacheAdapter');
$this->cache = $cache;
}
public function contains($id, array $options = null)
{
return $this->cache->test($id);
}
public function delete($id, array $options = null)
{
return $this->cache->remove($id);
}
public function fetch($id, array $options = null)
{
return $this->cache->load($id);
}
public function save($id, $data, $lifeTime = false, array $options = null)
{
return $this->cache->save($data, $id, array(), $lifeTime);
}
}

View File

@ -1,41 +0,0 @@
<?php
namespace Guzzle\Cache;
use Zend\Cache\Storage\StorageInterface;
/**
* Zend Framework 2 cache adapter
*
* @link http://packages.zendframework.com/docs/latest/manual/en/zend.cache.html
*/
class Zf2CacheAdapter extends AbstractCacheAdapter
{
/**
* @param StorageInterface $cache Zend Framework 2 cache adapter
*/
public function __construct(StorageInterface $cache)
{
$this->cache = $cache;
}
public function contains($id, array $options = null)
{
return $this->cache->hasItem($id);
}
public function delete($id, array $options = null)
{
return $this->cache->removeItem($id);
}
public function fetch($id, array $options = null)
{
return $this->cache->getItem($id);
}
public function save($id, $data, $lifeTime = false, array $options = null)
{
return $this->cache->setItem($id, $data);
}
}

View File

@ -1,27 +0,0 @@
{
"name": "guzzle/cache",
"description": "Guzzle cache adapter component",
"homepage": "http://guzzlephp.org/",
"keywords": ["cache", "adapter", "zf", "doctrine", "guzzle"],
"license": "MIT",
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"require": {
"php": ">=5.3.2",
"guzzle/common": "self.version"
},
"autoload": {
"psr-0": { "Guzzle\\Cache": "" }
},
"target-dir": "Guzzle/Cache",
"extra": {
"branch-alias": {
"dev-master": "3.7-dev"
}
}
}

View File

@ -1,49 +0,0 @@
<?php
namespace Guzzle\Common;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Class that holds an event dispatcher
*/
class AbstractHasDispatcher implements HasDispatcherInterface
{
/** @var EventDispatcherInterface */
protected $eventDispatcher;
public static function getAllEvents()
{
return array();
}
public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
{
$this->eventDispatcher = $eventDispatcher;
return $this;
}
public function getEventDispatcher()
{
if (!$this->eventDispatcher) {
$this->eventDispatcher = new EventDispatcher();
}
return $this->eventDispatcher;
}
public function dispatch($eventName, array $context = array())
{
$this->getEventDispatcher()->dispatch($eventName, new Event($context));
}
public function addSubscriber(EventSubscriberInterface $subscriber)
{
$this->getEventDispatcher()->addSubscriber($subscriber);
return $this;
}
}

View File

@ -1,403 +0,0 @@
<?php
namespace Guzzle\Common;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Exception\RuntimeException;
/**
* Key value pair collection object
*/
class Collection implements \ArrayAccess, \IteratorAggregate, \Countable, ToArrayInterface
{
/** @var array Data associated with the object. */
protected $data;
/**
* @param array $data Associative array of data to set
*/
public function __construct(array $data = array())
{
$this->data = $data;
}
/**
* Create a new collection from an array, validate the keys, and add default values where missing
*
* @param array $config Configuration values to apply.
* @param array $defaults Default parameters
* @param array $required Required parameter names
*
* @return self
* @throws InvalidArgumentException if a parameter is missing
*/
public static function fromConfig(array $config = array(), array $defaults = array(), array $required = array())
{
$data = $config + $defaults;
if ($missing = array_diff($required, array_keys($data))) {
throw new InvalidArgumentException('Config is missing the following keys: ' . implode(', ', $missing));
}
return new self($data);
}
public function count()
{
return count($this->data);
}
public function getIterator()
{
return new \ArrayIterator($this->data);
}
public function toArray()
{
return $this->data;
}
/**
* Removes all key value pairs
*
* @return Collection
*/
public function clear()
{
$this->data = array();
return $this;
}
/**
* Get all or a subset of matching key value pairs
*
* @param array $keys Pass an array of keys to retrieve only a subset of key value pairs
*
* @return array Returns an array of all matching key value pairs
*/
public function getAll(array $keys = null)
{
return $keys ? array_intersect_key($this->data, array_flip($keys)) : $this->data;
}
/**
* Get a specific key value.
*
* @param string $key Key to retrieve.
*
* @return mixed|null Value of the key or NULL
*/
public function get($key)
{
return isset($this->data[$key]) ? $this->data[$key] : null;
}
/**
* Set a key value pair
*
* @param string $key Key to set
* @param mixed $value Value to set
*
* @return Collection Returns a reference to the object
*/
public function set($key, $value)
{
$this->data[$key] = $value;
return $this;
}
/**
* Add a value to a key. If a key of the same name has already been added, the key value will be converted into an
* array and the new value will be pushed to the end of the array.
*
* @param string $key Key to add
* @param mixed $value Value to add to the key
*
* @return Collection Returns a reference to the object.
*/
public function add($key, $value)
{
if (!array_key_exists($key, $this->data)) {
$this->data[$key] = $value;
} elseif (is_array($this->data[$key])) {
$this->data[$key][] = $value;
} else {
$this->data[$key] = array($this->data[$key], $value);
}
return $this;
}
/**
* Remove a specific key value pair
*
* @param string $key A key to remove
*
* @return Collection
*/
public function remove($key)
{
unset($this->data[$key]);
return $this;
}
/**
* Get all keys in the collection
*
* @return array
*/
public function getKeys()
{
return array_keys($this->data);
}
/**
* Returns whether or not the specified key is present.
*
* @param string $key The key for which to check the existence.
*
* @return bool
*/
public function hasKey($key)
{
return array_key_exists($key, $this->data);
}
/**
* Case insensitive search the keys in the collection
*
* @param string $key Key to search for
*
* @return bool|string Returns false if not found, otherwise returns the key
*/
public function keySearch($key)
{
foreach (array_keys($this->data) as $k) {
if (!strcasecmp($k, $key)) {
return $k;
}
}
return false;
}
/**
* Checks if any keys contains a certain value
*
* @param string $value Value to search for
*
* @return mixed Returns the key if the value was found FALSE if the value was not found.
*/
public function hasValue($value)
{
return array_search($value, $this->data);
}
/**
* Replace the data of the object with the value of an array
*
* @param array $data Associative array of data
*
* @return Collection Returns a reference to the object
*/
public function replace(array $data)
{
$this->data = $data;
return $this;
}
/**
* Add and merge in a Collection or array of key value pair data.
*
* @param Collection|array $data Associative array of key value pair data
*
* @return Collection Returns a reference to the object.
*/
public function merge($data)
{
foreach ($data as $key => $value) {
$this->add($key, $value);
}
return $this;
}
/**
* Over write key value pairs in this collection with all of the data from an array or collection.
*
* @param array|\Traversable $data Values to override over this config
*
* @return self
*/
public function overwriteWith($data)
{
if (is_array($data)) {
$this->data = $data + $this->data;
} elseif ($data instanceof Collection) {
$this->data = $data->toArray() + $this->data;
} else {
foreach ($data as $key => $value) {
$this->data[$key] = $value;
}
}
return $this;
}
/**
* Returns a Collection containing all the elements of the collection after applying the callback function to each
* one. The Closure should accept three parameters: (string) $key, (string) $value, (array) $context and return a
* modified value
*
* @param \Closure $closure Closure to apply
* @param array $context Context to pass to the closure
* @param bool $static Set to TRUE to use the same class as the return rather than returning a Collection
*
* @return Collection
*/
public function map(\Closure $closure, array $context = array(), $static = true)
{
$collection = $static ? new static() : new self();
foreach ($this as $key => $value) {
$collection->add($key, $closure($key, $value, $context));
}
return $collection;
}
/**
* Iterates over each key value pair in the collection passing them to the Closure. If the Closure function returns
* true, the current value from input is returned into the result Collection. The Closure must accept three
* parameters: (string) $key, (string) $value and return Boolean TRUE or FALSE for each value.
*
* @param \Closure $closure Closure evaluation function
* @param bool $static Set to TRUE to use the same class as the return rather than returning a Collection
*
* @return Collection
*/
public function filter(\Closure $closure, $static = true)
{
$collection = ($static) ? new static() : new self();
foreach ($this->data as $key => $value) {
if ($closure($key, $value)) {
$collection->add($key, $value);
}
}
return $collection;
}
public function offsetExists($offset)
{
return isset($this->data[$offset]);
}
public function offsetGet($offset)
{
return isset($this->data[$offset]) ? $this->data[$offset] : null;
}
public function offsetSet($offset, $value)
{
$this->data[$offset] = $value;
}
public function offsetUnset($offset)
{
unset($this->data[$offset]);
}
/**
* Set a value into a nested array key. Keys will be created as needed to set the value.
*
* @param string $path Path to set
* @param mixed $value Value to set at the key
*
* @return self
* @throws RuntimeException when trying to setPath using a nested path that travels through a scalar value
*/
public function setPath($path, $value)
{
$current =& $this->data;
$queue = explode('/', $path);
while (null !== ($key = array_shift($queue))) {
if (!is_array($current)) {
throw new RuntimeException("Trying to setPath {$path}, but {$key} is set and is not an array");
} elseif (!$queue) {
$current[$key] = $value;
} elseif (isset($current[$key])) {
$current =& $current[$key];
} else {
$current[$key] = array();
$current =& $current[$key];
}
}
return $this;
}
/**
* Gets a value from the collection using an array path (e.g. foo/baz/bar would retrieve bar from two nested arrays)
* Allows for wildcard searches which recursively combine matches up to the level at which the wildcard occurs. This
* can be useful for accepting any key of a sub-array and combining matching keys from each diverging path.
*
* @param string $path Path to traverse and retrieve a value from
* @param string $separator Character used to add depth to the search
* @param mixed $data Optional data to descend into (used when wildcards are encountered)
*
* @return mixed|null
*/
public function getPath($path, $separator = '/', $data = null)
{
if ($data === null) {
$data =& $this->data;
}
$path = is_array($path) ? $path : explode($separator, $path);
while (null !== ($part = array_shift($path))) {
if (!is_array($data)) {
return null;
} elseif (isset($data[$part])) {
$data =& $data[$part];
} elseif ($part != '*') {
return null;
} else {
// Perform a wildcard search by diverging and merging paths
$result = array();
foreach ($data as $value) {
if (!$path) {
$result = array_merge_recursive($result, (array) $value);
} elseif (null !== ($test = $this->getPath($path, $separator, $value))) {
$result = array_merge_recursive($result, (array) $test);
}
}
return $result;
}
}
return $data;
}
/**
* Inject configuration settings into an input string
*
* @param string $input Input to inject
*
* @return string
* @deprecated
*/
public function inject($input)
{
Version::warn(__METHOD__ . ' is deprecated');
$replace = array();
foreach ($this->data as $key => $val) {
$replace['{' . $key . '}'] = $val;
}
return strtr($input, $replace);
}
}

View File

@ -1,52 +0,0 @@
<?php
namespace Guzzle\Common;
use Symfony\Component\EventDispatcher\Event as SymfonyEvent;
/**
* Default event for Guzzle notifications
*/
class Event extends SymfonyEvent implements ToArrayInterface, \ArrayAccess, \IteratorAggregate
{
/** @var array */
private $context;
/**
* @param array $context Contextual information
*/
public function __construct(array $context = array())
{
$this->context = $context;
}
public function getIterator()
{
return new \ArrayIterator($this->context);
}
public function offsetGet($offset)
{
return isset($this->context[$offset]) ? $this->context[$offset] : null;
}
public function offsetSet($offset, $value)
{
$this->context[$offset] = $value;
}
public function offsetExists($offset)
{
return isset($this->context[$offset]);
}
public function offsetUnset($offset)
{
unset($this->context[$offset]);
}
public function toArray()
{
return $this->context;
}
}

View File

@ -1,5 +0,0 @@
<?php
namespace Guzzle\Common\Exception;
class BadMethodCallException extends \BadMethodCallException implements GuzzleException {}

View File

@ -1,85 +0,0 @@
<?php
namespace Guzzle\Common\Exception;
/**
* Collection of exceptions
*/
class ExceptionCollection extends \Exception implements GuzzleException, \IteratorAggregate, \Countable
{
/** @var array Array of Exceptions */
protected $exceptions = array();
/**
* Set all of the exceptions
*
* @param array $exceptions Array of exceptions
*
* @return self
*/
public function setExceptions(array $exceptions)
{
$this->exceptions = array();
foreach ($exceptions as $exception) {
$this->add($exception);
}
return $this;
}
/**
* Add exceptions to the collection
*
* @param ExceptionCollection|\Exception $e Exception to add
*
* @return ExceptionCollection;
*/
public function add($e)
{
if ($this->message) {
$this->message .= "\n";
}
if ($e instanceof self) {
$this->message .= '(' . get_class($e) . ")";
foreach (explode("\n", $e->getMessage()) as $message) {
$this->message .= "\n {$message}";
}
} elseif ($e instanceof \Exception) {
$this->exceptions[] = $e;
$this->message .= '(' . get_class($e) . ') ' . $e->getMessage();
}
return $this;
}
/**
* Get the total number of request exceptions
*
* @return int
*/
public function count()
{
return count($this->exceptions);
}
/**
* Allows array-like iteration over the request exceptions
*
* @return \ArrayIterator
*/
public function getIterator()
{
return new \ArrayIterator($this->exceptions);
}
/**
* Get the first exception in the collection
*
* @return \Exception
*/
public function getFirst()
{
return $this->exceptions ? $this->exceptions[0] : null;
}
}

View File

@ -1,8 +0,0 @@
<?php
namespace Guzzle\Common\Exception;
/**
* Guzzle exception
*/
interface GuzzleException {}

View File

@ -1,5 +0,0 @@
<?php
namespace Guzzle\Common\Exception;
class InvalidArgumentException extends \InvalidArgumentException implements GuzzleException {}

View File

@ -1,5 +0,0 @@
<?php
namespace Guzzle\Common\Exception;
class RuntimeException extends \RuntimeException implements GuzzleException {}

View File

@ -1,5 +0,0 @@
<?php
namespace Guzzle\Common\Exception;
class UnexpectedValueException extends \UnexpectedValueException implements GuzzleException {}

View File

@ -1,18 +0,0 @@
<?php
namespace Guzzle\Common;
/**
* Interfaces that adds a factory method which is used to instantiate a class from an array of configuration options.
*/
interface FromConfigInterface
{
/**
* Static factory method used to turn an array or collection of configuration data into an instantiated object.
*
* @param array|Collection $config Configuration data
*
* @return FromConfigInterface
*/
public static function factory($config = array());
}

View File

@ -1,52 +0,0 @@
<?php
namespace Guzzle\Common;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Holds an event dispatcher
*/
interface HasDispatcherInterface
{
/**
* Get a list of all of the events emitted from the class
*
* @return array
*/
public static function getAllEvents();
/**
* Set the EventDispatcher of the request
*
* @param EventDispatcherInterface $eventDispatcher
*
* @return self
*/
public function setEventDispatcher(EventDispatcherInterface $eventDispatcher);
/**
* Get the EventDispatcher of the request
*
* @return EventDispatcherInterface
*/
public function getEventDispatcher();
/**
* Helper to dispatch Guzzle events and set the event name on the event
*
* @param string $eventName Name of the event to dispatch
* @param array $context Context of the event
*/
public function dispatch($eventName, array $context = array());
/**
* Add an event subscriber to the dispatcher
*
* @param EventSubscriberInterface $subscriber Event subscriber
*
* @return self
*/
public function addSubscriber(EventSubscriberInterface $subscriber);
}

View File

@ -1,16 +0,0 @@
<?php
namespace Guzzle\Common;
/**
* An object that can be represented as an array
*/
interface ToArrayInterface
{
/**
* Get the array representation of an object
*
* @return array
*/
public function toArray();
}

View File

@ -1,29 +0,0 @@
<?php
namespace Guzzle\Common;
/**
* Guzzle version information
*/
class Version
{
const VERSION = '3.7.1';
/**
* @var bool Set this value to true to enable warnings for deprecated functionality use. This should be on in your
* unit tests, but probably not in production.
*/
public static $emitWarnings = false;
/**
* Emit a deprecation warning
*
* @param string $message Warning message
*/
public static function warn($message)
{
if (self::$emitWarnings) {
trigger_error('Deprecation warning: ' . $message, E_USER_DEPRECATED);
}
}
}

View File

@ -1,20 +0,0 @@
{
"name": "guzzle/common",
"homepage": "http://guzzlephp.org/",
"description": "Common libraries used by Guzzle",
"keywords": ["common", "event", "exception", "collection"],
"license": "MIT",
"require": {
"php": ">=5.3.2",
"symfony/event-dispatcher": ">=2.1"
},
"autoload": {
"psr-0": { "Guzzle\\Common": "" }
},
"target-dir": "Guzzle/Common",
"extra": {
"branch-alias": {
"dev-master": "3.7-dev"
}
}
}

View File

@ -1,221 +0,0 @@
<?php
namespace Guzzle\Http;
use Guzzle\Stream\Stream;
/**
* Abstract decorator used to wrap entity bodies
*/
class AbstractEntityBodyDecorator implements EntityBodyInterface
{
/** @var EntityBodyInterface Decorated entity body */
protected $body;
/**
* @param EntityBodyInterface $body Entity body to decorate
*/
public function __construct(EntityBodyInterface $body)
{
$this->body = $body;
}
public function __toString()
{
return (string) $this->body;
}
/**
* Allow decorators to implement custom methods
*
* @param string $method Missing method name
* @param array $args Method arguments
*
* @return mixed
*/
public function __call($method, array $args)
{
return call_user_func_array(array($this->body, $method), $args);
}
public function close()
{
return $this->body->close();
}
public function setRewindFunction($callable)
{
$this->body->setRewindFunction($callable);
return $this;
}
public function rewind()
{
return $this->body->rewind();
}
public function compress($filter = 'zlib.deflate')
{
return $this->body->compress($filter);
}
public function uncompress($filter = 'zlib.inflate')
{
return $this->body->uncompress($filter);
}
public function getContentLength()
{
return $this->getSize();
}
public function getContentType()
{
return $this->body->getContentType();
}
public function getContentMd5($rawOutput = false, $base64Encode = false)
{
$hash = Stream::getHash($this, 'md5', $rawOutput);
return $hash && $base64Encode ? base64_encode($hash) : $hash;
}
public function getContentEncoding()
{
return $this->body->getContentEncoding();
}
public function getMetaData($key = null)
{
return $this->body->getMetaData($key);
}
public function getStream()
{
return $this->body->getStream();
}
public function setStream($stream, $size = 0)
{
$this->body->setStream($stream, $size);
return $this;
}
public function detachStream()
{
$this->body->detachStream();
return $this;
}
public function getWrapper()
{
return $this->body->getWrapper();
}
public function getWrapperData()
{
return $this->body->getWrapperData();
}
public function getStreamType()
{
return $this->body->getStreamType();
}
public function getUri()
{
return $this->body->getUri();
}
public function getSize()
{
return $this->body->getSize();
}
public function isReadable()
{
return $this->body->isReadable();
}
public function isRepeatable()
{
return $this->isSeekable() && $this->isReadable();
}
public function isWritable()
{
return $this->body->isWritable();
}
public function isConsumed()
{
return $this->body->isConsumed();
}
/**
* Alias of isConsumed()
* {@inheritdoc}
*/
public function feof()
{
return $this->isConsumed();
}
public function isLocal()
{
return $this->body->isLocal();
}
public function isSeekable()
{
return $this->body->isSeekable();
}
public function setSize($size)
{
$this->body->setSize($size);
return $this;
}
public function seek($offset, $whence = SEEK_SET)
{
return $this->body->seek($offset, $whence);
}
public function read($length)
{
return $this->body->read($length);
}
public function write($string)
{
return $this->body->write($string);
}
public function readLine($maxLength = null)
{
return $this->body->readLine($maxLength);
}
public function ftell()
{
return $this->body->ftell();
}
public function getCustomData($key)
{
return $this->body->getCustomData($key);
}
public function setCustomData($key, $value)
{
$this->body->setCustomData($key, $value);
return $this;
}
}

View File

@ -1,229 +0,0 @@
<?php
namespace Guzzle\Http;
use Guzzle\Common\Exception\RuntimeException;
/**
* EntityBody decorator that can cache previously read bytes from a sequentially read tstream
*/
class CachingEntityBody extends AbstractEntityBodyDecorator
{
/** @var EntityBody Remote stream used to actually pull data onto the buffer */
protected $remoteStream;
/** @var int The number of bytes to skip reading due to a write on the temporary buffer */
protected $skipReadBytes = 0;
/**
* We will treat the buffer object as the body of the entity body
* {@inheritdoc}
*/
public function __construct(EntityBodyInterface $body)
{
$this->remoteStream = $body;
$this->body = new EntityBody(fopen('php://temp', 'r+'));
}
/**
* Will give the contents of the buffer followed by the exhausted remote stream.
*
* Warning: Loads the entire stream into memory
*
* @return string
*/
public function __toString()
{
$pos = $this->ftell();
$this->rewind();
$str = '';
while (!$this->isConsumed()) {
$str .= $this->read(16384);
}
$this->seek($pos);
return $str;
}
public function getSize()
{
return max($this->body->getSize(), $this->remoteStream->getSize());
}
/**
* {@inheritdoc}
* @throws RuntimeException When seeking with SEEK_END or when seeking past the total size of the buffer stream
*/
public function seek($offset, $whence = SEEK_SET)
{
if ($whence == SEEK_SET) {
$byte = $offset;
} elseif ($whence == SEEK_CUR) {
$byte = $offset + $this->ftell();
} else {
throw new RuntimeException(__CLASS__ . ' supports only SEEK_SET and SEEK_CUR seek operations');
}
// You cannot skip ahead past where you've read from the remote stream
if ($byte > $this->body->getSize()) {
throw new RuntimeException(
"Cannot seek to byte {$byte} when the buffered stream only contains {$this->body->getSize()} bytes"
);
}
return $this->body->seek($byte);
}
public function rewind()
{
return $this->seek(0);
}
/**
* Does not support custom rewind functions
*
* @throws RuntimeException
*/
public function setRewindFunction($callable)
{
throw new RuntimeException(__CLASS__ . ' does not support custom stream rewind functions');
}
public function read($length)
{
// Perform a regular read on any previously read data from the buffer
$data = $this->body->read($length);
$remaining = $length - strlen($data);
// More data was requested so read from the remote stream
if ($remaining) {
// If data was written to the buffer in a position that would have been filled from the remote stream,
// then we must skip bytes on the remote stream to emulate overwriting bytes from that position. This
// mimics the behavior of other PHP stream wrappers.
$remoteData = $this->remoteStream->read($remaining + $this->skipReadBytes);
if ($this->skipReadBytes) {
$len = strlen($remoteData);
$remoteData = substr($remoteData, $this->skipReadBytes);
$this->skipReadBytes = max(0, $this->skipReadBytes - $len);
}
$data .= $remoteData;
$this->body->write($remoteData);
}
return $data;
}
public function write($string)
{
// When appending to the end of the currently read stream, you'll want to skip bytes from being read from
// the remote stream to emulate other stream wrappers. Basically replacing bytes of data of a fixed length.
$overflow = (strlen($string) + $this->ftell()) - $this->remoteStream->ftell();
if ($overflow > 0) {
$this->skipReadBytes += $overflow;
}
return $this->body->write($string);
}
/**
* {@inheritdoc}
* @link http://php.net/manual/en/function.fgets.php
*/
public function readLine($maxLength = null)
{
$buffer = '';
$size = 0;
while (!$this->isConsumed()) {
$byte = $this->read(1);
$buffer .= $byte;
// Break when a new line is found or the max length - 1 is reached
if ($byte == PHP_EOL || ++$size == $maxLength - 1) {
break;
}
}
return $buffer;
}
public function isConsumed()
{
return $this->body->isConsumed() && $this->remoteStream->isConsumed();
}
/**
* Close both the remote stream and buffer stream
*/
public function close()
{
return $this->remoteStream->close() && $this->body->close();
}
public function setStream($stream, $size = 0)
{
$this->remoteStream->setStream($stream, $size);
}
public function getContentType()
{
return $this->remoteStream->getContentType();
}
public function getContentEncoding()
{
return $this->remoteStream->getContentEncoding();
}
public function getMetaData($key = null)
{
return $this->remoteStream->getMetaData($key);
}
public function getStream()
{
return $this->remoteStream->getStream();
}
public function getWrapper()
{
return $this->remoteStream->getWrapper();
}
public function getWrapperData()
{
return $this->remoteStream->getWrapperData();
}
public function getStreamType()
{
return $this->remoteStream->getStreamType();
}
public function getUri()
{
return $this->remoteStream->getUri();
}
/**
* Always retrieve custom data from the remote stream
* {@inheritdoc}
*/
public function getCustomData($key)
{
return $this->remoteStream->getCustomData($key);
}
/**
* Always set custom data on the remote stream
* {@inheritdoc}
*/
public function setCustomData($key, $value)
{
$this->remoteStream->setCustomData($key, $value);
return $this;
}
}

View File

@ -1,506 +0,0 @@
<?php
namespace Guzzle\Http;
use Guzzle\Common\Collection;
use Guzzle\Common\AbstractHasDispatcher;
use Guzzle\Common\Exception\ExceptionCollection;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Exception\RuntimeException;
use Guzzle\Common\Version;
use Guzzle\Parser\ParserRegistry;
use Guzzle\Parser\UriTemplate\UriTemplateInterface;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\RequestFactory;
use Guzzle\Http\Message\RequestFactoryInterface;
use Guzzle\Http\Curl\CurlMultiInterface;
use Guzzle\Http\Curl\CurlMultiProxy;
use Guzzle\Http\Curl\CurlHandle;
use Guzzle\Http\Curl\CurlVersion;
/**
* HTTP client
*/
class Client extends AbstractHasDispatcher implements ClientInterface
{
/** @deprecated Use [request.options][params] */
const REQUEST_PARAMS = 'request.params';
const REQUEST_OPTIONS = 'request.options';
const CURL_OPTIONS = 'curl.options';
const SSL_CERT_AUTHORITY = 'ssl.certificate_authority';
const DISABLE_REDIRECTS = RedirectPlugin::DISABLE;
/** @var Collection Default HTTP headers to set on each request */
protected $defaultHeaders;
/** @var string The user agent string to set on each request */
protected $userAgent;
/** @var Collection Parameter object holding configuration data */
private $config;
/** @var Url Base URL of the client */
private $baseUrl;
/** @var CurlMultiInterface CurlMulti object used internally */
private $curlMulti;
/** @var UriTemplateInterface URI template owned by the client */
private $uriTemplate;
/** @var RequestFactoryInterface Request factory used by the client */
protected $requestFactory;
public static function getAllEvents()
{
return array(self::CREATE_REQUEST);
}
/**
* @param string $baseUrl Base URL of the web service
* @param array|Collection $config Configuration settings
*
* @throws RuntimeException if cURL is not installed
*/
public function __construct($baseUrl = '', $config = null)
{
if (!extension_loaded('curl')) {
// @codeCoverageIgnoreStart
throw new RuntimeException('The PHP cURL extension must be installed to use Guzzle.');
// @codeCoverageIgnoreEnd
}
$this->setConfig($config ?: new Collection());
$this->initSsl();
$this->setBaseUrl($baseUrl);
$this->defaultHeaders = new Collection();
$this->setRequestFactory(RequestFactory::getInstance());
$this->userAgent = $this->getDefaultUserAgent();
if (!$this->config[self::DISABLE_REDIRECTS]) {
$this->addSubscriber(new RedirectPlugin());
}
}
final public function setConfig($config)
{
if ($config instanceof Collection) {
$this->config = $config;
} elseif (is_array($config)) {
$this->config = new Collection($config);
} else {
throw new InvalidArgumentException('Config must be an array or Collection');
}
return $this;
}
final public function getConfig($key = false)
{
return $key ? $this->config[$key] : $this->config;
}
/**
* Set a default request option on the client that will be used as a default for each request
*
* @param string $keyOrPath request.options key (e.g. allow_redirects) or path to a nested key (e.g. headers/foo)
* @param mixed $value Value to set
*
* @return $this
*/
public function setDefaultOption($keyOrPath, $value)
{
$keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath;
$this->config->setPath($keyOrPath, $value);
return $this;
}
/**
* Retrieve a default request option from the client
*
* @param string $keyOrPath request.options key (e.g. allow_redirects) or path to a nested key (e.g. headers/foo)
*
* @return mixed|null
*/
public function getDefaultOption($keyOrPath)
{
$keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath;
return $this->config->getPath($keyOrPath);
}
final public function setSslVerification($certificateAuthority = true, $verifyPeer = true, $verifyHost = 2)
{
$opts = $this->config[self::CURL_OPTIONS] ?: array();
if ($certificateAuthority === true) {
// use bundled CA bundle, set secure defaults
$opts[CURLOPT_CAINFO] = __DIR__ . '/Resources/cacert.pem';
$opts[CURLOPT_SSL_VERIFYPEER] = true;
$opts[CURLOPT_SSL_VERIFYHOST] = 2;
} elseif ($certificateAuthority === false) {
unset($opts[CURLOPT_CAINFO]);
$opts[CURLOPT_SSL_VERIFYPEER] = false;
$opts[CURLOPT_SSL_VERIFYHOST] = 2;
} elseif ($verifyPeer !== true && $verifyPeer !== false && $verifyPeer !== 1 && $verifyPeer !== 0) {
throw new InvalidArgumentException('verifyPeer must be 1, 0 or boolean');
} elseif ($verifyHost !== 0 && $verifyHost !== 1 && $verifyHost !== 2) {
throw new InvalidArgumentException('verifyHost must be 0, 1 or 2');
} else {
$opts[CURLOPT_SSL_VERIFYPEER] = $verifyPeer;
$opts[CURLOPT_SSL_VERIFYHOST] = $verifyHost;
if (is_file($certificateAuthority)) {
unset($opts[CURLOPT_CAPATH]);
$opts[CURLOPT_CAINFO] = $certificateAuthority;
} elseif (is_dir($certificateAuthority)) {
unset($opts[CURLOPT_CAINFO]);
$opts[CURLOPT_CAPATH] = $certificateAuthority;
} else {
throw new RuntimeException(
'Invalid option passed to ' . self::SSL_CERT_AUTHORITY . ': ' . $certificateAuthority
);
}
}
$this->config->set(self::CURL_OPTIONS, $opts);
return $this;
}
public function createRequest($method = 'GET', $uri = null, $headers = null, $body = null, array $options = array())
{
if (!$uri) {
$url = $this->getBaseUrl();
} else {
if (!is_array($uri)) {
$templateVars = null;
} else {
list($uri, $templateVars) = $uri;
}
if (substr($uri, 0, 4) === 'http') {
// Use absolute URLs as-is
$url = $this->expandTemplate($uri, $templateVars);
} else {
$url = Url::factory($this->getBaseUrl())->combine($this->expandTemplate($uri, $templateVars));
}
}
// If default headers are provided, then merge them under any explicitly provided headers for the request
if (count($this->defaultHeaders)) {
if (!$headers) {
$headers = $this->defaultHeaders->toArray();
} elseif (is_array($headers)) {
$headers += $this->defaultHeaders->toArray();
} elseif ($headers instanceof Collection) {
$headers = $headers->toArray() + $this->defaultHeaders->toArray();
}
}
return $this->prepareRequest($this->requestFactory->create($method, (string) $url, $headers, $body), $options);
}
public function getBaseUrl($expand = true)
{
return $expand ? $this->expandTemplate($this->baseUrl) : $this->baseUrl;
}
public function setBaseUrl($url)
{
$this->baseUrl = $url;
return $this;
}
public function setUserAgent($userAgent, $includeDefault = false)
{
if ($includeDefault) {
$userAgent .= ' ' . $this->getDefaultUserAgent();
}
$this->userAgent = $userAgent;
return $this;
}
/**
* Get the default User-Agent string to use with Guzzle
*
* @return string
*/
public function getDefaultUserAgent()
{
return 'Guzzle/' . Version::VERSION
. ' curl/' . CurlVersion::getInstance()->get('version')
. ' PHP/' . PHP_VERSION;
}
public function get($uri = null, $headers = null, $options = array())
{
// BC compat: $options can be a string, resource, etc to specify where the response body is downloaded
return is_array($options)
? $this->createRequest('GET', $uri, $headers, null, $options)
: $this->createRequest('GET', $uri, $headers, $options);
}
public function head($uri = null, $headers = null, array $options = array())
{
return $this->createRequest('HEAD', $uri, $headers, null, $options);
}
public function delete($uri = null, $headers = null, $body = null, array $options = array())
{
return $this->createRequest('DELETE', $uri, $headers, $body, $options);
}
public function put($uri = null, $headers = null, $body = null, array $options = array())
{
return $this->createRequest('PUT', $uri, $headers, $body, $options);
}
public function patch($uri = null, $headers = null, $body = null, array $options = array())
{
return $this->createRequest('PATCH', $uri, $headers, $body, $options);
}
public function post($uri = null, $headers = null, $postBody = null, array $options = array())
{
return $this->createRequest('POST', $uri, $headers, $postBody, $options);
}
public function options($uri = null, array $options = array())
{
return $this->createRequest('OPTIONS', $uri, $options);
}
public function send($requests)
{
if (!($requests instanceof RequestInterface)) {
return $this->sendMultiple($requests);
}
try {
/** @var $requests RequestInterface */
$this->getCurlMulti()->add($requests)->send();
return $requests->getResponse();
} catch (ExceptionCollection $e) {
throw $e->getFirst();
}
}
/**
* Set a curl multi object to be used internally by the client for transferring requests.
*
* @param CurlMultiInterface $curlMulti Multi object
*
* @return self
*/
public function setCurlMulti(CurlMultiInterface $curlMulti)
{
$this->curlMulti = $curlMulti;
return $this;
}
/**
* @return CurlMultiInterface|CurlMultiProxy
*/
public function getCurlMulti()
{
if (!$this->curlMulti) {
$this->curlMulti = new CurlMultiProxy();
}
return $this->curlMulti;
}
public function setRequestFactory(RequestFactoryInterface $factory)
{
$this->requestFactory = $factory;
return $this;
}
/**
* Set the URI template expander to use with the client
*
* @param UriTemplateInterface $uriTemplate URI template expander
*
* @return self
*/
public function setUriTemplate(UriTemplateInterface $uriTemplate)
{
$this->uriTemplate = $uriTemplate;
return $this;
}
/**
* Copy the cacert.pem file from the phar if it is not in the temp folder and validate the MD5 checksum
*
* @param bool $md5Check Set to false to not perform the MD5 validation
*
* @return string Returns the path to the extracted cacert
* @throws RuntimeException if the file cannot be copied or there is a MD5 mismatch
*/
public function preparePharCacert($md5Check = true)
{
$from = __DIR__ . '/Resources/cacert.pem';
$certFile = sys_get_temp_dir() . '/guzzle-cacert.pem';
if (!file_exists($certFile) && !copy($from, $certFile)) {
throw new RuntimeException("Could not copy {$from} to {$certFile}: " . var_export(error_get_last(), true));
} elseif ($md5Check) {
$actualMd5 = md5_file($certFile);
$expectedMd5 = trim(file_get_contents("{$from}.md5"));
if ($actualMd5 != $expectedMd5) {
throw new RuntimeException("{$certFile} MD5 mismatch: expected {$expectedMd5} but got {$actualMd5}");
}
}
return $certFile;
}
/**
* Expand a URI template while merging client config settings into the template variables
*
* @param string $template Template to expand
* @param array $variables Variables to inject
*
* @return string
*/
protected function expandTemplate($template, array $variables = null)
{
$expansionVars = $this->getConfig()->toArray();
if ($variables) {
$expansionVars = $variables + $expansionVars;
}
return $this->getUriTemplate()->expand($template, $expansionVars);
}
/**
* Get the URI template expander used by the client
*
* @return UriTemplateInterface
*/
protected function getUriTemplate()
{
if (!$this->uriTemplate) {
$this->uriTemplate = ParserRegistry::getInstance()->getParser('uri_template');
}
return $this->uriTemplate;
}
/**
* Send multiple requests in parallel
*
* @param array $requests Array of RequestInterface objects
*
* @return array Returns an array of Response objects
*/
protected function sendMultiple(array $requests)
{
$curlMulti = $this->getCurlMulti();
foreach ($requests as $request) {
$curlMulti->add($request);
}
$curlMulti->send();
/** @var $request RequestInterface */
$result = array();
foreach ($requests as $request) {
$result[] = $request->getResponse();
}
return $result;
}
/**
* Prepare a request to be sent from the Client by adding client specific behaviors and properties to the request.
*
* @param RequestInterface $request Request to prepare for the client
* @param array $options Options to apply to the request
*
* @return RequestInterface
*/
protected function prepareRequest(RequestInterface $request, array $options = array())
{
$request->setClient($this)->setEventDispatcher(clone $this->getEventDispatcher());
if ($curl = $this->config[self::CURL_OPTIONS]) {
$request->getCurlOptions()->overwriteWith(CurlHandle::parseCurlConfig($curl));
}
if ($params = $this->config[self::REQUEST_PARAMS]) {
Version::warn('request.params is deprecated. Use request.options to add default request options.');
$request->getParams()->overwriteWith($params);
}
if ($this->userAgent && !$request->hasHeader('User-Agent')) {
$request->setHeader('User-Agent', $this->userAgent);
}
if ($defaults = $this->config[self::REQUEST_OPTIONS]) {
$this->requestFactory->applyOptions($request, $defaults, RequestFactoryInterface::OPTIONS_AS_DEFAULTS);
}
if ($options) {
$this->requestFactory->applyOptions($request, $options);
}
$this->dispatch('client.create_request', array('client' => $this, 'request' => $request));
return $request;
}
/**
* Initializes SSL settings
*/
protected function initSsl()
{
if ('system' == ($authority = $this->config[self::SSL_CERT_AUTHORITY])) {
return;
}
if ($authority === null) {
$authority = true;
}
if ($authority === true && substr(__FILE__, 0, 7) == 'phar://') {
$authority = $this->preparePharCacert();
$that = $this;
$this->getEventDispatcher()->addListener('request.before_send', function ($event) use ($authority, $that) {
if ($authority == $event['request']->getCurlOptions()->get(CURLOPT_CAINFO)) {
$that->preparePharCacert(false);
}
});
}
$this->setSslVerification($authority);
}
/**
* @deprecated
*/
public function getDefaultHeaders()
{
Version::warn(__METHOD__ . ' is deprecated. Use the request.options array to retrieve default request options');
return $this->defaultHeaders;
}
/**
* @deprecated
*/
public function setDefaultHeaders($headers)
{
Version::warn(__METHOD__ . ' is deprecated. Use the request.options array to specify default request options');
if ($headers instanceof Collection) {
$this->defaultHeaders = $headers;
} elseif (is_array($headers)) {
$this->defaultHeaders = new Collection($headers);
} else {
throw new InvalidArgumentException('Headers must be an array or Collection');
}
return $this;
}
}

View File

@ -1,223 +0,0 @@
<?php
namespace Guzzle\Http;
use Guzzle\Common\HasDispatcherInterface;
use Guzzle\Common\Collection;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Message\RequestInterface;
/**
* Client interface for send HTTP requests
*/
interface ClientInterface extends HasDispatcherInterface
{
const CREATE_REQUEST = 'client.create_request';
/** @var string RFC 1123 HTTP-Date */
const HTTP_DATE = 'D, d M Y H:i:s \G\M\T';
/**
* Set the configuration object to use with the client
*
* @param array|Collection $config Parameters that define how the client behaves
*
* @return self
*/
public function setConfig($config);
/**
* Get a configuration setting or all of the configuration settings. The Collection result of this method can be
* modified to change the configuration settings of a client.
*
* A client should honor the following special values:
*
* - request.options: Associative array of default RequestFactory options to apply to each request
* - request.params: Associative array of request parameters (data values) to apply to each request
* - curl.options: Associative array of cURL configuration settings to apply to each request
* - ssl.certificate_authority: Path a CAINFO, CAPATH, true to use strict defaults, or false to disable verification
* - redirect.disable: Set to true to disable redirects
*
* @param bool|string $key Configuration value to retrieve. Set to FALSE to retrieve all values of the client.
* The object return can be modified, and modifications will affect the client's config.
* @return mixed|Collection
* @see \Guzzle\Http\Message\RequestFactoryInterface::applyOptions for a full list of request.options options
*/
public function getConfig($key = false);
/**
* Create and return a new {@see RequestInterface} configured for the client.
*
* Use an absolute path to override the base path of the client, or a relative path to append to the base path of
* the client. The URI can contain the query string as well. Use an array to provide a URI template and additional
* variables to use in the URI template expansion.
*
* @param string $method HTTP method. Defaults to GET
* @param string|array $uri Resource URI.
* @param array|Collection $headers HTTP headers
* @param string|resource|array|EntityBodyInterface $body Entity body of request (POST/PUT) or response (GET)
* @param array $options Array of options to apply to the request
*
* @return RequestInterface
* @throws InvalidArgumentException if a URI array is passed that does not contain exactly two elements: the URI
* followed by template variables
*/
public function createRequest(
$method = RequestInterface::GET,
$uri = null,
$headers = null,
$body = null,
array $options = array()
);
/**
* Create a GET request for the client
*
* @param string|array $uri Resource URI
* @param array|Collection $headers HTTP headers
* @param array $options Options to apply to the request. For BC compatibility, you can also pass a
* string to tell Guzzle to download the body of the response to a particular
* location. Use the 'body' option instead for forward compatibility.
* @return RequestInterface
* @see Guzzle\Http\ClientInterface::createRequest()
*/
public function get($uri = null, $headers = null, $options = array());
/**
* Create a HEAD request for the client
*
* @param string|array $uri Resource URI
* @param array|Collection $headers HTTP headers
* @param array $options Options to apply to the request
*
* @return RequestInterface
* @see Guzzle\Http\ClientInterface::createRequest()
*/
public function head($uri = null, $headers = null, array $options = array());
/**
* Create a DELETE request for the client
*
* @param string|array $uri Resource URI
* @param array|Collection $headers HTTP headers
* @param string|resource|EntityBodyInterface $body Body to send in the request
* @param array $options Options to apply to the request
*
* @return EntityEnclosingRequestInterface
* @see Guzzle\Http\ClientInterface::createRequest()
*/
public function delete($uri = null, $headers = null, $body = null, array $options = array());
/**
* Create a PUT request for the client
*
* @param string|array $uri Resource URI
* @param array|Collection $headers HTTP headers
* @param string|resource|EntityBodyInterface $body Body to send in the request
* @param array $options Options to apply to the request
*
* @return EntityEnclosingRequestInterface
* @see Guzzle\Http\ClientInterface::createRequest()
*/
public function put($uri = null, $headers = null, $body = null, array $options = array());
/**
* Create a PATCH request for the client
*
* @param string|array $uri Resource URI
* @param array|Collection $headers HTTP headers
* @param string|resource|EntityBodyInterface $body Body to send in the request
* @param array $options Options to apply to the request
*
* @return EntityEnclosingRequestInterface
* @see Guzzle\Http\ClientInterface::createRequest()
*/
public function patch($uri = null, $headers = null, $body = null, array $options = array());
/**
* Create a POST request for the client
*
* @param string|array $uri Resource URI
* @param array|Collection $headers HTTP headers
* @param array|Collection|string|EntityBodyInterface $postBody POST body. Can be a string, EntityBody, or
* associative array of POST fields to send in the body of the
* request. Prefix a value in the array with the @ symbol to
* reference a file.
* @param array $options Options to apply to the request
*
* @return EntityEnclosingRequestInterface
* @see Guzzle\Http\ClientInterface::createRequest()
*/
public function post($uri = null, $headers = null, $postBody = null, array $options = array());
/**
* Create an OPTIONS request for the client
*
* @param string|array $uri Resource URI
* @param array $options Options to apply to the request
*
* @return RequestInterface
* @see Guzzle\Http\ClientInterface::createRequest()
*/
public function options($uri = null, array $options = array());
/**
* Sends a single request or an array of requests in parallel
*
* @param array|RequestInterface $requests One or more RequestInterface objects to send
*
* @return \Guzzle\Http\Message\Response|array Returns a single Response or an array of Response objects
*/
public function send($requests);
/**
* Get the client's base URL as either an expanded or raw URI template
*
* @param bool $expand Set to FALSE to get the raw base URL without URI template expansion
*
* @return string|null
*/
public function getBaseUrl($expand = true);
/**
* Set the base URL of the client
*
* @param string $url The base service endpoint URL of the webservice
*
* @return self
*/
public function setBaseUrl($url);
/**
* Set the User-Agent header to be used on all requests from the client
*
* @param string $userAgent User agent string
* @param bool $includeDefault Set to true to prepend the value to Guzzle's default user agent string
*
* @return self
*/
public function setUserAgent($userAgent, $includeDefault = false);
/**
* Set SSL verification options.
*
* Setting $certificateAuthority to TRUE will result in the bundled cacert.pem being used to verify against the
* remote host.
*
* Alternate certificates to verify against can be specified with the $certificateAuthority option set to the full
* path to a certificate file, or the path to a directory containing certificates.
*
* Setting $certificateAuthority to FALSE will turn off peer verification, unset the bundled cacert.pem, and
* disable host verification. Please don't do this unless you really know what you're doing, and why you're doing
* it.
*
* @param string|bool $certificateAuthority bool, file path, or directory path
* @param bool $verifyPeer FALSE to stop from verifying the peer's certificate.
* @param int $verifyHost Set to 1 to check the existence of a common name in the SSL peer
* certificate. 2 to check the existence of a common name and also verify
* that it matches the hostname provided.
* @return self
*/
public function setSslVerification($certificateAuthority = true, $verifyPeer = true, $verifyHost = 2);
}

View File

@ -1,451 +0,0 @@
<?php
namespace Guzzle\Http\Curl;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Exception\RuntimeException;
use Guzzle\Common\Collection;
use Guzzle\Http\Message\EntityEnclosingRequest;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Parser\ParserRegistry;
use Guzzle\Http\Url;
/**
* Immutable wrapper for a cURL handle
*/
class CurlHandle
{
const BODY_AS_STRING = 'body_as_string';
const PROGRESS = 'progress';
const DEBUG = 'debug';
/** @var Collection Curl options */
protected $options;
/** @var resource Curl resource handle */
protected $handle;
/** @var int CURLE_* error */
protected $errorNo = CURLE_OK;
/**
* Factory method to create a new curl handle based on an HTTP request.
*
* There are some helpful options you can set to enable specific behavior:
* - debug: Set to true to enable cURL debug functionality to track the actual headers sent over the wire.
* - progress: Set to true to enable progress function callbacks.
*
* @param RequestInterface $request Request
*
* @return CurlHandle
* @throws RuntimeException
*/
public static function factory(RequestInterface $request)
{
$requestCurlOptions = $request->getCurlOptions();
$mediator = new RequestMediator($request, $requestCurlOptions->get('emit_io'));
$tempContentLength = null;
$method = $request->getMethod();
$bodyAsString = $requestCurlOptions->get(self::BODY_AS_STRING);
// Array of default cURL options.
$curlOptions = array(
CURLOPT_URL => $request->getUrl(),
CURLOPT_CONNECTTIMEOUT => 150,
CURLOPT_RETURNTRANSFER => false,
CURLOPT_HEADER => false,
CURLOPT_PORT => $request->getPort(),
CURLOPT_HTTPHEADER => array(),
CURLOPT_WRITEFUNCTION => array($mediator, 'writeResponseBody'),
CURLOPT_HEADERFUNCTION => array($mediator, 'receiveResponseHeader'),
CURLOPT_HTTP_VERSION => $request->getProtocolVersion() === '1.0'
? CURL_HTTP_VERSION_1_0 : CURL_HTTP_VERSION_1_1,
// Verifies the authenticity of the peer's certificate
CURLOPT_SSL_VERIFYPEER => 1,
// Certificate must indicate that the server is the server to which you meant to connect
CURLOPT_SSL_VERIFYHOST => 2
);
if (defined('CURLOPT_PROTOCOLS')) {
// Allow only HTTP and HTTPS protocols
$curlOptions[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
}
// Add CURLOPT_ENCODING if Accept-Encoding header is provided
if ($acceptEncodingHeader = $request->getHeader('Accept-Encoding')) {
$curlOptions[CURLOPT_ENCODING] = (string) $acceptEncodingHeader;
// Let cURL set the Accept-Encoding header, prevents duplicate values
$request->removeHeader('Accept-Encoding');
}
// Enable curl debug information if the 'debug' param was set
if ($requestCurlOptions->get('debug')) {
$curlOptions[CURLOPT_STDERR] = fopen('php://temp', 'r+');
// @codeCoverageIgnoreStart
if (false === $curlOptions[CURLOPT_STDERR]) {
throw new RuntimeException('Unable to create a stream for CURLOPT_STDERR');
}
// @codeCoverageIgnoreEnd
$curlOptions[CURLOPT_VERBOSE] = true;
}
// Specify settings according to the HTTP method
if ($method == 'GET') {
$curlOptions[CURLOPT_HTTPGET] = true;
} elseif ($method == 'HEAD') {
$curlOptions[CURLOPT_NOBODY] = true;
// HEAD requests do not use a write function
unset($curlOptions[CURLOPT_WRITEFUNCTION]);
} elseif (!($request instanceof EntityEnclosingRequest)) {
$curlOptions[CURLOPT_CUSTOMREQUEST] = $method;
} else {
$curlOptions[CURLOPT_CUSTOMREQUEST] = $method;
// Handle sending raw bodies in a request
if ($request->getBody()) {
// You can send the body as a string using curl's CURLOPT_POSTFIELDS
if ($bodyAsString) {
$curlOptions[CURLOPT_POSTFIELDS] = (string) $request->getBody();
// Allow curl to add the Content-Length for us to account for the times when
// POST redirects are followed by GET requests
if ($tempContentLength = $request->getHeader('Content-Length')) {
$tempContentLength = (int) (string) $tempContentLength;
}
// Remove the curl generated Content-Type header if none was set manually
if (!$request->hasHeader('Content-Type')) {
$curlOptions[CURLOPT_HTTPHEADER][] = 'Content-Type:';
}
} else {
$curlOptions[CURLOPT_UPLOAD] = true;
// Let cURL handle setting the Content-Length header
if ($tempContentLength = $request->getHeader('Content-Length')) {
$tempContentLength = (int) (string) $tempContentLength;
$curlOptions[CURLOPT_INFILESIZE] = $tempContentLength;
}
// Add a callback for curl to read data to send with the request only if a body was specified
$curlOptions[CURLOPT_READFUNCTION] = array($mediator, 'readRequestBody');
// Attempt to seek to the start of the stream
$request->getBody()->seek(0);
}
} else {
// Special handling for POST specific fields and files
$postFields = false;
if (count($request->getPostFiles())) {
$postFields = $request->getPostFields()->useUrlEncoding(false)->urlEncode();
foreach ($request->getPostFiles() as $key => $data) {
$prefixKeys = count($data) > 1;
foreach ($data as $index => $file) {
// Allow multiple files in the same key
$fieldKey = $prefixKeys ? "{$key}[{$index}]" : $key;
$postFields[$fieldKey] = $file->getCurlValue();
}
}
} elseif (count($request->getPostFields())) {
$postFields = (string) $request->getPostFields()->useUrlEncoding(true);
}
if ($postFields !== false) {
if ($method == 'POST') {
unset($curlOptions[CURLOPT_CUSTOMREQUEST]);
$curlOptions[CURLOPT_POST] = true;
}
$curlOptions[CURLOPT_POSTFIELDS] = $postFields;
$request->removeHeader('Content-Length');
}
}
// If the Expect header is not present, prevent curl from adding it
if (!$request->hasHeader('Expect')) {
$curlOptions[CURLOPT_HTTPHEADER][] = 'Expect:';
}
}
// If a Content-Length header was specified but we want to allow curl to set one for us
if (null !== $tempContentLength) {
$request->removeHeader('Content-Length');
}
// Set custom cURL options
foreach ($requestCurlOptions->toArray() as $key => $value) {
if (is_numeric($key)) {
$curlOptions[$key] = $value;
}
}
// Do not set an Accept header by default
if (!isset($curlOptions[CURLOPT_ENCODING])) {
$curlOptions[CURLOPT_HTTPHEADER][] = 'Accept:';
}
// Add any custom headers to the request. Empty headers will cause curl to not send the header at all.
foreach ($request->getHeaderLines() as $line) {
$curlOptions[CURLOPT_HTTPHEADER][] = $line;
}
// Add the content-length header back if it was temporarily removed
if ($tempContentLength) {
$request->setHeader('Content-Length', $tempContentLength);
}
// Apply the options to a new cURL handle.
$handle = curl_init();
// Enable the progress function if the 'progress' param was set
if ($requestCurlOptions->get('progress')) {
// Wrap the function in a function that provides the curl handle to the mediator's progress function
// Using this rather than injecting the handle into the mediator prevents a circular reference
$curlOptions[CURLOPT_PROGRESSFUNCTION] = function () use ($mediator, $handle) {
$args = func_get_args();
$args[] = $handle;
call_user_func_array(array($mediator, 'progress'), $args);
};
$curlOptions[CURLOPT_NOPROGRESS] = false;
}
curl_setopt_array($handle, $curlOptions);
return new static($handle, $curlOptions);
}
/**
* Construct a new CurlHandle object that wraps a cURL handle
*
* @param resource $handle Configured cURL handle resource
* @param Collection|array $options Curl options to use with the handle
*
* @throws InvalidArgumentException
*/
public function __construct($handle, $options)
{
if (!is_resource($handle)) {
throw new InvalidArgumentException('Invalid handle provided');
}
if (is_array($options)) {
$this->options = new Collection($options);
} elseif ($options instanceof Collection) {
$this->options = $options;
} else {
throw new InvalidArgumentException('Expected array or Collection');
}
$this->handle = $handle;
}
/**
* Destructor
*/
public function __destruct()
{
$this->close();
}
/**
* Close the curl handle
*/
public function close()
{
if (is_resource($this->handle)) {
curl_close($this->handle);
}
$this->handle = null;
}
/**
* Check if the handle is available and still OK
*
* @return bool
*/
public function isAvailable()
{
return is_resource($this->handle);
}
/**
* Get the last error that occurred on the cURL handle
*
* @return string
*/
public function getError()
{
return $this->isAvailable() ? curl_error($this->handle) : '';
}
/**
* Get the last error number that occurred on the cURL handle
*
* @return int
*/
public function getErrorNo()
{
if ($this->errorNo) {
return $this->errorNo;
}
return $this->isAvailable() ? curl_errno($this->handle) : CURLE_OK;
}
/**
* Set the curl error number
*
* @param int $error Error number to set
*
* @return CurlHandle
*/
public function setErrorNo($error)
{
$this->errorNo = $error;
return $this;
}
/**
* Get cURL curl_getinfo data
*
* @param int $option Option to retrieve. Pass null to retrieve all data as an array.
*
* @return array|mixed
*/
public function getInfo($option = null)
{
if (!is_resource($this->handle)) {
return null;
}
if (null !== $option) {
return curl_getinfo($this->handle, $option) ?: null;
}
return curl_getinfo($this->handle) ?: array();
}
/**
* Get the stderr output
*
* @param bool $asResource Set to TRUE to get an fopen resource
*
* @return string|resource|null
*/
public function getStderr($asResource = false)
{
$stderr = $this->getOptions()->get(CURLOPT_STDERR);
if (!$stderr) {
return null;
}
if ($asResource) {
return $stderr;
}
fseek($stderr, 0);
$e = stream_get_contents($stderr);
fseek($stderr, 0, SEEK_END);
return $e;
}
/**
* Get the URL that this handle is connecting to
*
* @return Url
*/
public function getUrl()
{
return Url::factory($this->options->get(CURLOPT_URL));
}
/**
* Get the wrapped curl handle
*
* @return resource|null Returns the cURL handle or null if it was closed
*/
public function getHandle()
{
return $this->isAvailable() ? $this->handle : null;
}
/**
* Get the cURL setopt options of the handle. Changing values in the return object will have no effect on the curl
* handle after it is created.
*
* @return Collection
*/
public function getOptions()
{
return $this->options;
}
/**
* Update a request based on the log messages of the CurlHandle
*
* @param RequestInterface $request Request to update
*/
public function updateRequestFromTransfer(RequestInterface $request)
{
if (!$request->getResponse()) {
return;
}
// Update the transfer stats of the response
$request->getResponse()->setInfo($this->getInfo());
if (!$log = $this->getStderr(true)) {
return;
}
// Parse the cURL stderr output for outgoing requests
$headers = '';
fseek($log, 0);
while (($line = fgets($log)) !== false) {
if ($line && $line[0] == '>') {
$headers = substr(trim($line), 2) . "\r\n";
while (($line = fgets($log)) !== false) {
if ($line[0] == '*' || $line[0] == '<') {
break;
} else {
$headers .= trim($line) . "\r\n";
}
}
}
}
// Add request headers to the request exactly as they were sent
if ($headers) {
$parsed = ParserRegistry::getInstance()->getParser('message')->parseRequest($headers);
if (!empty($parsed['headers'])) {
$request->setHeaders(array());
foreach ($parsed['headers'] as $name => $value) {
$request->setHeader($name, $value);
}
}
if (!empty($parsed['version'])) {
$request->setProtocolVersion($parsed['version']);
}
}
}
/**
* Parse the config and replace curl.* configurators into the constant based values so it can be used elsewhere
*
* @param array|Collection $config The configuration we want to parse
*
* @return array
*/
public static function parseCurlConfig($config)
{
$curlOptions = array();
foreach ($config as $key => $value) {
if (is_string($key) && defined($key)) {
// Convert constants represented as string to constant int values
$key = constant($key);
}
if (is_string($value) && defined($value)) {
$value = constant($value);
}
$curlOptions[$key] = $value;
}
return $curlOptions;
}
}

View File

@ -1,390 +0,0 @@
<?php
namespace Guzzle\Http\Curl;
use Guzzle\Common\AbstractHasDispatcher;
use Guzzle\Common\Event;
use Guzzle\Http\Exception\MultiTransferException;
use Guzzle\Http\Exception\CurlException;
use Guzzle\Http\Message\RequestInterface;
/**
* Send {@see RequestInterface} objects in parallel using curl_multi
*/
class CurlMulti extends AbstractHasDispatcher implements CurlMultiInterface
{
/** @var resource cURL multi handle. */
protected $multiHandle;
/** @var array Attached {@see RequestInterface} objects. */
protected $requests;
/** @var \SplObjectStorage RequestInterface to CurlHandle hash */
protected $handles;
/** @var array Hash mapping curl handle resource IDs to request objects */
protected $resourceHash;
/** @var array Queued exceptions */
protected $exceptions = array();
/** @var array Requests that succeeded */
protected $successful = array();
/** @var array cURL multi error values and codes */
protected $multiErrors = array(
CURLM_BAD_HANDLE => array('CURLM_BAD_HANDLE', 'The passed-in handle is not a valid CURLM handle.'),
CURLM_BAD_EASY_HANDLE => array('CURLM_BAD_EASY_HANDLE', "An easy handle was not good/valid. It could mean that it isn't an easy handle at all, or possibly that the handle already is in used by this or another multi handle."),
CURLM_OUT_OF_MEMORY => array('CURLM_OUT_OF_MEMORY', 'You are doomed.'),
CURLM_INTERNAL_ERROR => array('CURLM_INTERNAL_ERROR', 'This can only be returned if libcurl bugs. Please report it to us!')
);
public function __construct()
{
$this->multiHandle = curl_multi_init();
// @codeCoverageIgnoreStart
if ($this->multiHandle === false) {
throw new CurlException('Unable to create multi handle');
}
// @codeCoverageIgnoreEnd
$this->reset();
}
public function __destruct()
{
if (is_resource($this->multiHandle)) {
curl_multi_close($this->multiHandle);
}
}
public function add(RequestInterface $request)
{
$this->requests[] = $request;
// If requests are currently transferring and this is async, then the
// request must be prepared now as the send() method is not called.
$this->beforeSend($request);
$this->dispatch(self::ADD_REQUEST, array('request' => $request));
return $this;
}
public function all()
{
return $this->requests;
}
public function remove(RequestInterface $request)
{
$this->removeHandle($request);
foreach ($this->requests as $i => $r) {
if ($request === $r) {
unset($this->requests[$i]);
$this->requests = array_values($this->requests);
$this->dispatch(self::REMOVE_REQUEST, array('request' => $request));
return true;
}
}
return false;
}
public function reset($hard = false)
{
// Remove each request
if ($this->requests) {
foreach ($this->requests as $request) {
$this->remove($request);
}
}
$this->handles = new \SplObjectStorage();
$this->requests = $this->resourceHash = $this->exceptions = $this->successful = array();
}
public function send()
{
$this->perform();
$exceptions = $this->exceptions;
$successful = $this->successful;
$this->reset();
if ($exceptions) {
$this->throwMultiException($exceptions, $successful);
}
}
public function count()
{
return count($this->requests);
}
/**
* Build and throw a MultiTransferException
*
* @param array $exceptions Exceptions encountered
* @param array $successful Successful requests
* @throws MultiTransferException
*/
protected function throwMultiException(array $exceptions, array $successful)
{
$multiException = new MultiTransferException('Errors during multi transfer');
while ($e = array_shift($exceptions)) {
$multiException->add($e['exception']);
$multiException->addFailedRequest($e['request']);
}
// Add successful requests
foreach ($successful as $request) {
if (!$multiException->containsRequest($request)) {
$multiException->addSuccessfulRequest($request);
}
}
throw $multiException;
}
/**
* Prepare for sending
*
* @param RequestInterface $request Request to prepare
* @throws \Exception on error preparing the request
*/
protected function beforeSend(RequestInterface $request)
{
try {
$state = $request->setState(RequestInterface::STATE_TRANSFER);
if ($state == RequestInterface::STATE_TRANSFER) {
// Add the request curl handle to the multi handle
$this->checkCurlResult(curl_multi_add_handle($this->multiHandle, $this->createCurlHandle($request)->getHandle()));
} else {
// Requests might decide they don't need to be sent just before transfer (e.g. CachePlugin)
$this->remove($request);
if ($state == RequestInterface::STATE_COMPLETE) {
$this->successful[] = $request;
}
}
} catch (\Exception $e) {
// Queue the exception to be thrown when sent
$this->removeErroredRequest($request, $e);
}
}
/**
* Create a curl handle for a request
*
* @param RequestInterface $request Request
*
* @return CurlHandle
*/
protected function createCurlHandle(RequestInterface $request)
{
$wrapper = CurlHandle::factory($request);
$this->handles[$request] = $wrapper;
$this->resourceHash[(int) $wrapper->getHandle()] = $request;
return $wrapper;
}
/**
* Get the data from the multi handle
*/
protected function perform()
{
if (!$this->requests) {
return;
}
// Initialize the handles with a very quick select timeout
$active = $mrc = null;
$this->executeHandles($active, $mrc, 0.001);
$event = new Event(array('curl_multi' => $this));
$this->processMessages();
while ($this->requests) {
// Notify each request as polling
$blocking = $total = 0;
foreach ($this->requests as $request) {
++$total;
$event['request'] = $request;
$request->getEventDispatcher()->dispatch(self::POLLING_REQUEST, $event);
// The blocking variable just has to be non-falsey to block the loop
if ($request->getParams()->hasKey(self::BLOCKING)) {
++$blocking;
}
}
if ($blocking == $total) {
// Sleep to prevent eating CPU because no requests are actually pending a select call
usleep(500);
} else {
do {
$this->executeHandles($active, $mrc, 1);
} while ($active);
}
$this->processMessages();
}
}
/**
* Process any received curl multi messages
*/
private function processMessages()
{
// Get messages from curl handles
while ($done = curl_multi_info_read($this->multiHandle)) {
try {
$request = $this->resourceHash[(int) $done['handle']];
$this->processResponse($request, $this->handles[$request], $done);
$this->successful[] = $request;
} catch (MultiTransferException $e) {
$this->removeErroredRequest($request, $e, false);
throw $e;
} catch (\Exception $e) {
$this->removeErroredRequest($request, $e);
}
}
}
/**
* Execute and select curl handles until there is activity
*
* @param int $active Active value to update
* @param int $mrc Multi result value to update
* @param int $timeout Select timeout in seconds
*/
private function executeHandles(&$active, &$mrc, $timeout = 1)
{
do {
$mrc = curl_multi_exec($this->multiHandle, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM && $active);
$this->checkCurlResult($mrc);
// @codeCoverageIgnoreStart
// Select the curl handles until there is any activity on any of the open file descriptors
// See https://github.com/php/php-src/blob/master/ext/curl/multi.c#L170
if ($active && $mrc == CURLM_OK && curl_multi_select($this->multiHandle, $timeout) == -1) {
// Perform a usleep if a previously executed select returned -1
// @see https://bugs.php.net/bug.php?id=61141
usleep(100);
}
// @codeCoverageIgnoreEnd
}
/**
* Remove a request that encountered an exception
*
* @param RequestInterface $request Request to remove
* @param \Exception $e Exception encountered
* @param bool $buffer Set to false to not buffer the exception
*/
protected function removeErroredRequest(RequestInterface $request, \Exception $e = null, $buffer = true)
{
if ($buffer) {
$this->exceptions[] = array('request' => $request, 'exception' => $e);
}
$this->remove($request);
$this->dispatch(self::MULTI_EXCEPTION, array('exception' => $e, 'all_exceptions' => $this->exceptions));
}
/**
* Check for errors and fix headers of a request based on a curl response
*
* @param RequestInterface $request Request to process
* @param CurlHandle $handle Curl handle object
* @param array $curl Array returned from curl_multi_info_read
*
* @throws CurlException on Curl error
*/
protected function processResponse(RequestInterface $request, CurlHandle $handle, array $curl)
{
// Set the transfer stats on the response
$handle->updateRequestFromTransfer($request);
// Check if a cURL exception occurred, and if so, notify things
$curlException = $this->isCurlException($request, $handle, $curl);
// Always remove completed curl handles. They can be added back again
// via events if needed (e.g. ExponentialBackoffPlugin)
$this->removeHandle($request);
if (!$curlException) {
$state = $request->setState(RequestInterface::STATE_COMPLETE, array('handle' => $handle));
// Only remove the request if it wasn't resent as a result of the state change
if ($state != RequestInterface::STATE_TRANSFER) {
$this->remove($request);
}
} else {
// Set the state of the request to an error
$state = $request->setState(RequestInterface::STATE_ERROR, array('exception' => $curlException));
// Allow things to ignore the error if possible
if ($state != RequestInterface::STATE_TRANSFER) {
$this->remove($request);
}
// The error was not handled, so fail
if ($state == RequestInterface::STATE_ERROR) {
/** @var CurlException $curlException */
throw $curlException;
}
}
}
/**
* Remove a curl handle from the curl multi object
*
* @param RequestInterface $request Request that owns the handle
*/
protected function removeHandle(RequestInterface $request)
{
if (isset($this->handles[$request])) {
$handle = $this->handles[$request];
unset($this->handles[$request]);
unset($this->resourceHash[(int) $handle->getHandle()]);
curl_multi_remove_handle($this->multiHandle, $handle->getHandle());
$handle->close();
}
}
/**
* Check if a cURL transfer resulted in what should be an exception
*
* @param RequestInterface $request Request to check
* @param CurlHandle $handle Curl handle object
* @param array $curl Array returned from curl_multi_info_read
*
* @return CurlException|bool
*/
private function isCurlException(RequestInterface $request, CurlHandle $handle, array $curl)
{
if (CURLM_OK == $curl['result'] || CURLM_CALL_MULTI_PERFORM == $curl['result']) {
return false;
}
$handle->setErrorNo($curl['result']);
$e = new CurlException(sprintf('[curl] %s: %s [url] %s',
$handle->getErrorNo(), $handle->getError(), $handle->getUrl()));
$e->setCurlHandle($handle)
->setRequest($request)
->setCurlInfo($handle->getInfo())
->setError($handle->getError(), $handle->getErrorNo());
return $e;
}
/**
* Throw an exception for a cURL multi response if needed
*
* @param int $code Curl response code
* @throws CurlException
*/
private function checkCurlResult($code)
{
if ($code != CURLM_OK && $code != CURLM_CALL_MULTI_PERFORM) {
throw new CurlException(isset($this->multiErrors[$code])
? "cURL error: {$code} ({$this->multiErrors[$code][0]}): cURL message: {$this->multiErrors[$code][1]}"
: 'Unexpected cURL error: ' . $code
);
}
}
}

View File

@ -1,58 +0,0 @@
<?php
namespace Guzzle\Http\Curl;
use Guzzle\Common\HasDispatcherInterface;
use Guzzle\Common\Exception\ExceptionCollection;
use Guzzle\Http\Message\RequestInterface;
/**
* Interface for sending a pool of {@see RequestInterface} objects in parallel
*/
interface CurlMultiInterface extends \Countable, HasDispatcherInterface
{
const POLLING_REQUEST = 'curl_multi.polling_request';
const ADD_REQUEST = 'curl_multi.add_request';
const REMOVE_REQUEST = 'curl_multi.remove_request';
const MULTI_EXCEPTION = 'curl_multi.exception';
const BLOCKING = 'curl_multi.blocking';
/**
* Add a request to the pool.
*
* @param RequestInterface $request Request to add
*
* @return CurlMultiInterface
*/
public function add(RequestInterface $request);
/**
* Get an array of attached {@see RequestInterface} objects
*
* @return array
*/
public function all();
/**
* Remove a request from the pool.
*
* @param RequestInterface $request Request to remove
*
* @return bool Returns true on success or false on failure
*/
public function remove(RequestInterface $request);
/**
* Reset the state and remove any attached RequestInterface objects
*
* @param bool $hard Set to true to close and reopen any open multi handles
*/
public function reset($hard = false);
/**
* Send a pool of {@see RequestInterface} requests.
*
* @throws ExceptionCollection if any requests threw exceptions during the transfer.
*/
public function send();
}

View File

@ -1,147 +0,0 @@
<?php
namespace Guzzle\Http\Curl;
use Guzzle\Common\AbstractHasDispatcher;
use Guzzle\Http\Message\RequestInterface;
/**
* Proxies requests and connections to a pool of internal curl_multi handles. Each recursive call will add requests
* to the next available CurlMulti handle.
*/
class CurlMultiProxy extends AbstractHasDispatcher implements CurlMultiInterface
{
protected $handles = array();
protected $groups = array();
protected $queued = array();
protected $maxHandles;
/**
* @param int $maxHandles The maximum number of idle CurlMulti handles to allow to remain open
*/
public function __construct($maxHandles = 3)
{
$this->maxHandles = $maxHandles;
// You can get some weird "Too many open files" errors when sending a large amount of requests in parallel.
// These two statements autoload classes before a system runs out of file descriptors so that you can get back
// valuable error messages if you run out.
class_exists('Guzzle\Http\Message\Response');
class_exists('Guzzle\Http\Exception\CurlException');
}
public function add(RequestInterface $request)
{
$this->queued[] = $request;
return $this;
}
public function all()
{
$requests = $this->queued;
foreach ($this->handles as $handle) {
$requests = array_merge($requests, $handle->all());
}
return $requests;
}
public function remove(RequestInterface $request)
{
foreach ($this->queued as $i => $r) {
if ($request === $r) {
unset($this->queued[$i]);
return true;
}
}
foreach ($this->handles as $handle) {
if ($handle->remove($request)) {
return true;
}
}
return false;
}
public function reset($hard = false)
{
$this->queued = array();
$this->groups = array();
foreach ($this->handles as $handle) {
$handle->reset();
}
if ($hard) {
$this->handles = array();
}
return $this;
}
public function send()
{
if ($this->queued) {
$group = $this->getAvailableHandle();
// Add this handle to a list of handles than is claimed
$this->groups[] = $group;
while ($request = array_shift($this->queued)) {
$group->add($request);
}
try {
$group->send();
array_pop($this->groups);
$this->cleanupHandles();
} catch (\Exception $e) {
// Remove the group and cleanup if an exception was encountered and no more requests in group
if (!$group->count()) {
array_pop($this->groups);
$this->cleanupHandles();
}
throw $e;
}
}
}
public function count()
{
return count($this->all());
}
/**
* Get an existing available CurlMulti handle or create a new one
*
* @return CurlMulti
*/
protected function getAvailableHandle()
{
// Grab a handle that is not claimed
foreach ($this->handles as $h) {
if (!in_array($h, $this->groups, true)) {
return $h;
}
}
// All are claimed, so create one
$handle = new CurlMulti();
$handle->setEventDispatcher($this->getEventDispatcher());
$this->handles[] = $handle;
return $handle;
}
/**
* Trims down unused CurlMulti handles to limit the number of open connections
*/
protected function cleanupHandles()
{
if ($diff = max(0, count($this->handles) - $this->maxHandles)) {
for ($i = count($this->handles) - 1; $i > 0 && $diff > 0; $i--) {
if (!count($this->handles[$i])) {
unset($this->handles[$i]);
$diff--;
}
}
$this->handles = array_values($this->handles);
}
}
}

View File

@ -1,66 +0,0 @@
<?php
namespace Guzzle\Http\Curl;
/**
* Class used for querying curl_version data
*/
class CurlVersion
{
/** @var array curl_version() information */
protected $version;
/** @var CurlVersion */
protected static $instance;
/** @var string Default user agent */
protected $userAgent;
/**
* @return CurlVersion
*/
public static function getInstance()
{
if (!self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Get all of the curl_version() data
*
* @return array
*/
public function getAll()
{
if (!$this->version) {
$this->version = curl_version();
}
return $this->version;
}
/**
* Get a specific type of curl information
*
* @param string $type Version information to retrieve. This value is one of:
* - version_number: cURL 24 bit version number
* - version: cURL version number, as a string
* - ssl_version_number: OpenSSL 24 bit version number
* - ssl_version: OpenSSL version number, as a string
* - libz_version: zlib version number, as a string
* - host: Information about the host where cURL was built
* - features: A bitmask of the CURL_VERSION_XXX constants
* - protocols: An array of protocols names supported by cURL
*
* @return string|float|bool if the $type is found, and false if not found
*/
public function get($type)
{
$version = $this->getAll();
return isset($version[$type]) ? $version[$type] : false;
}
}

View File

@ -1,142 +0,0 @@
<?php
namespace Guzzle\Http\Curl;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\EntityBody;
use Guzzle\Http\Message\Response;
/**
* Mediator between curl handles and request objects
*/
class RequestMediator
{
/** @var RequestInterface */
protected $request;
/** @var bool Whether or not to emit read/write events */
protected $emitIo;
/**
* @param RequestInterface $request Request to mediate
* @param bool $emitIo Set to true to dispatch events on input and output
*/
public function __construct(RequestInterface $request, $emitIo = false)
{
$this->request = $request;
$this->emitIo = $emitIo;
}
/**
* Receive a response header from curl
*
* @param resource $curl Curl handle
* @param string $header Received header
*
* @return int
*/
public function receiveResponseHeader($curl, $header)
{
static $normalize = array("\r", "\n");
$length = strlen($header);
$header = str_replace($normalize, '', $header);
if (strpos($header, 'HTTP/') === 0) {
$startLine = explode(' ', $header, 3);
$code = $startLine[1];
$status = isset($startLine[2]) ? $startLine[2] : '';
// Only download the body of the response to the specified response
// body when a successful response is received.
if ($code >= 200 && $code < 300) {
$body = $this->request->getResponseBody();
} else {
$body = EntityBody::factory();
}
$response = new Response($code, null, $body);
$response->setStatus($code, $status);
$this->request->startResponse($response);
$this->request->dispatch('request.receive.status_line', array(
'request' => $this,
'line' => $header,
'status_code' => $code,
'reason_phrase' => $status
));
} elseif ($pos = strpos($header, ':')) {
$this->request->getResponse()->addHeader(
trim(substr($header, 0, $pos)),
trim(substr($header, $pos + 1))
);
}
return $length;
}
/**
* Received a progress notification
*
* @param int $downloadSize Total download size
* @param int $downloaded Amount of bytes downloaded
* @param int $uploadSize Total upload size
* @param int $uploaded Amount of bytes uploaded
* @param resource $handle CurlHandle object
*/
public function progress($downloadSize, $downloaded, $uploadSize, $uploaded, $handle = null)
{
$this->request->dispatch('curl.callback.progress', array(
'request' => $this->request,
'handle' => $handle,
'download_size' => $downloadSize,
'downloaded' => $downloaded,
'upload_size' => $uploadSize,
'uploaded' => $uploaded
));
}
/**
* Write data to the response body of a request
*
* @param resource $curl Curl handle
* @param string $write Data that was received
*
* @return int
*/
public function writeResponseBody($curl, $write)
{
if ($this->emitIo) {
$this->request->dispatch('curl.callback.write', array(
'request' => $this->request,
'write' => $write
));
}
return $this->request->getResponse()->getBody()->write($write);
}
/**
* Read data from the request body and send it to curl
*
* @param resource $ch Curl handle
* @param resource $fd File descriptor
* @param int $length Amount of data to read
*
* @return string
*/
public function readRequestBody($ch, $fd, $length)
{
if (!($body = $this->request->getBody())) {
return '';
}
$read = (string) $body->read($length);
if ($this->emitIo) {
$this->request->dispatch('curl.callback.read', array('request' => $this->request, 'read' => $read));
}
return $read;
}
}

View File

@ -1,201 +0,0 @@
<?php
namespace Guzzle\Http;
use Guzzle\Common\Version;
use Guzzle\Stream\Stream;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\Mimetypes;
/**
* Entity body used with an HTTP request or response
*/
class EntityBody extends Stream implements EntityBodyInterface
{
/** @var bool Content-Encoding of the entity body if known */
protected $contentEncoding = false;
/** @var callable Method to invoke for rewinding a stream */
protected $rewindFunction;
/**
* Create a new EntityBody based on the input type
*
* @param resource|string|EntityBody $resource Entity body data
* @param int $size Size of the data contained in the resource
*
* @return EntityBody
* @throws InvalidArgumentException if the $resource arg is not a resource or string
*/
public static function factory($resource = '', $size = null)
{
if ($resource instanceof EntityBodyInterface) {
return $resource;
}
switch (gettype($resource)) {
case 'string':
return self::fromString($resource);
case 'resource':
return new static($resource, $size);
case 'object':
if (method_exists($resource, '__toString')) {
return self::fromString((string) $resource);
}
break;
case 'array':
return self::fromString(http_build_query($resource));
}
throw new InvalidArgumentException('Invalid resource type');
}
public function setRewindFunction($callable)
{
if (!is_callable($callable)) {
throw new InvalidArgumentException('Must specify a callable');
}
$this->rewindFunction = $callable;
return $this;
}
public function rewind()
{
return $this->rewindFunction ? call_user_func($this->rewindFunction, $this) : parent::rewind();
}
/**
* Create a new EntityBody from a string
*
* @param string $string String of data
*
* @return EntityBody
*/
public static function fromString($string)
{
$stream = fopen('php://temp', 'r+');
if ($string !== '') {
fwrite($stream, $string);
rewind($stream);
}
return new static($stream);
}
public function compress($filter = 'zlib.deflate')
{
$result = $this->handleCompression($filter);
$this->contentEncoding = $result ? $filter : false;
return $result;
}
public function uncompress($filter = 'zlib.inflate')
{
$offsetStart = 0;
// When inflating gzipped data, the first 10 bytes must be stripped
// if a gzip header is present
if ($filter == 'zlib.inflate') {
// @codeCoverageIgnoreStart
if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) {
return false;
}
// @codeCoverageIgnoreEnd
if (stream_get_contents($this->stream, 3, 0) === "\x1f\x8b\x08") {
$offsetStart = 10;
}
}
$this->contentEncoding = false;
return $this->handleCompression($filter, $offsetStart);
}
public function getContentLength()
{
return $this->getSize();
}
public function getContentType()
{
return $this->getUri() ? Mimetypes::getInstance()->fromFilename($this->getUri()) : null;
}
public function getContentMd5($rawOutput = false, $base64Encode = false)
{
if ($hash = self::getHash($this, 'md5', $rawOutput)) {
return $hash && $base64Encode ? base64_encode($hash) : $hash;
} else {
return false;
}
}
/**
* Calculate the MD5 hash of an entity body
*
* @param EntityBodyInterface $body Entity body to calculate the hash for
* @param bool $rawOutput Whether or not to use raw output
* @param bool $base64Encode Whether or not to base64 encode raw output (only if raw output is true)
*
* @return bool|string Returns an MD5 string on success or FALSE on failure
* @deprecated This will be deprecated soon
* @codeCoverageIgnore
*/
public static function calculateMd5(EntityBodyInterface $body, $rawOutput = false, $base64Encode = false)
{
Version::warn(__CLASS__ . ' is deprecated. Use getContentMd5()');
return $body->getContentMd5($rawOutput, $base64Encode);
}
public function setStreamFilterContentEncoding($streamFilterContentEncoding)
{
$this->contentEncoding = $streamFilterContentEncoding;
return $this;
}
public function getContentEncoding()
{
return strtr($this->contentEncoding, array(
'zlib.deflate' => 'gzip',
'bzip2.compress' => 'compress'
)) ?: false;
}
protected function handleCompression($filter, $offsetStart = 0)
{
// @codeCoverageIgnoreStart
if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) {
return false;
}
// @codeCoverageIgnoreEnd
$handle = fopen('php://temp', 'r+');
$filter = @stream_filter_append($handle, $filter, STREAM_FILTER_WRITE);
if (!$filter) {
return false;
}
// Seek to the offset start if possible
$this->seek($offsetStart);
while ($data = fread($this->stream, 8096)) {
fwrite($handle, $data);
}
fclose($this->stream);
$this->stream = $handle;
stream_filter_remove($filter);
$stat = fstat($this->stream);
$this->size = $stat['size'];
$this->rebuildCache();
$this->seek(0);
// Remove any existing rewind function as the underlying stream has been replaced
$this->rewindFunction = null;
return true;
}
}

View File

@ -1,73 +0,0 @@
<?php
namespace Guzzle\Http;
use Guzzle\Stream\StreamInterface;
/**
* Entity body used with an HTTP request or response
*/
interface EntityBodyInterface extends StreamInterface
{
/**
* Specify a custom callback used to rewind a non-seekable stream. This can be useful entity enclosing requests
* that are redirected.
*
* @param mixed $callable Callable to invoke to rewind a non-seekable stream. The callback must accept an
* EntityBodyInterface object, perform the rewind if possible, and return a boolean
* representing whether or not the rewind was successful.
* @return self
*/
public function setRewindFunction($callable);
/**
* If the stream is readable, compress the data in the stream using deflate compression. The uncompressed stream is
* then closed, and the compressed stream then becomes the wrapped stream.
*
* @param string $filter Compression filter
*
* @return bool Returns TRUE on success or FALSE on failure
*/
public function compress($filter = 'zlib.deflate');
/**
* Decompress a deflated string. Once uncompressed, the uncompressed string is then used as the wrapped stream.
*
* @param string $filter De-compression filter
*
* @return bool Returns TRUE on success or FALSE on failure
*/
public function uncompress($filter = 'zlib.inflate');
/**
* Get the Content-Length of the entity body if possible (alias of getSize)
*
* @return int|bool Returns the Content-Length or false on failure
*/
public function getContentLength();
/**
* Guess the Content-Type of a local stream
*
* @return string|null
* @see http://www.php.net/manual/en/function.finfo-open.php
*/
public function getContentType();
/**
* Get an MD5 checksum of the stream's contents
*
* @param bool $rawOutput Whether or not to use raw output
* @param bool $base64Encode Whether or not to base64 encode raw output (only if raw output is true)
*
* @return bool|string Returns an MD5 string on success or FALSE on failure
*/
public function getContentMd5($rawOutput = false, $base64Encode = false);
/**
* Get the Content-Encoding of the EntityBody
*
* @return bool|string
*/
public function getContentEncoding();
}

View File

@ -1,70 +0,0 @@
<?php
namespace Guzzle\Http\Exception;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
/**
* Http request exception thrown when a bad response is received
*/
class BadResponseException extends RequestException
{
/** @var Response */
private $response;
/**
* Factory method to create a new response exception based on the response code.
*
* @param RequestInterface $request Request
* @param Response $response Response received
*
* @return BadResponseException
*/
public static function factory(RequestInterface $request, Response $response)
{
if ($response->isClientError()) {
$label = 'Client error response';
$class = __NAMESPACE__ . '\\ClientErrorResponseException';
} elseif ($response->isServerError()) {
$label = 'Server error response';
$class = __NAMESPACE__ . '\\ServerErrorResponseException';
} else {
$label = 'Unsuccessful response';
$class = __CLASS__;
$e = new self();
}
$message = $label . PHP_EOL . implode(PHP_EOL, array(
'[status code] ' . $response->getStatusCode(),
'[reason phrase] ' . $response->getReasonPhrase(),
'[url] ' . $request->getUrl(),
));
$e = new $class($message);
$e->setResponse($response);
$e->setRequest($request);
return $e;
}
/**
* Set the response that caused the exception
*
* @param Response $response Response to set
*/
public function setResponse(Response $response)
{
$this->response = $response;
}
/**
* Get the response that caused the exception
*
* @return Response
*/
public function getResponse()
{
return $this->response;
}
}

View File

@ -1,8 +0,0 @@
<?php
namespace Guzzle\Http\Exception;
/**
* Exception when a client error is encountered (4xx codes)
*/
class ClientErrorResponseException extends BadResponseException {}

View File

@ -1,7 +0,0 @@
<?php
namespace Guzzle\Http\Exception;
use Guzzle\Common\Exception\RuntimeException;
class CouldNotRewindStreamException extends RuntimeException implements HttpException {}

View File

@ -1,101 +0,0 @@
<?php
namespace Guzzle\Http\Exception;
use Guzzle\Http\Curl\CurlHandle;
/**
* cURL request exception
*/
class CurlException extends RequestException
{
private $curlError;
private $curlErrorNo;
private $handle;
private $curlInfo = array();
/**
* Set the cURL error message
*
* @param string $error Curl error
* @param int $number Curl error number
*
* @return self
*/
public function setError($error, $number)
{
$this->curlError = $error;
$this->curlErrorNo = $number;
return $this;
}
/**
* Set the associated curl handle
*
* @param CurlHandle $handle Curl handle
*
* @return self
*/
public function setCurlHandle(CurlHandle $handle)
{
$this->handle = $handle;
return $this;
}
/**
* Get the associated cURL handle
*
* @return CurlHandle|null
*/
public function getCurlHandle()
{
return $this->handle;
}
/**
* Get the associated cURL error message
*
* @return string|null
*/
public function getError()
{
return $this->curlError;
}
/**
* Get the associated cURL error number
*
* @return int|null
*/
public function getErrorNo()
{
return $this->curlErrorNo;
}
/**
* Returns curl information about the transfer
*
* @return array
*/
public function getCurlInfo()
{
return $this->curlInfo;
}
/**
* Set curl transfer information
*
* @param array $info Array of curl transfer information
*
* @return self
* @link http://php.net/manual/en/function.curl-getinfo.php
*/
public function setCurlInfo(array $info)
{
$this->curlInfo = $info;
return $this;
}
}

View File

@ -1,10 +0,0 @@
<?php
namespace Guzzle\Http\Exception;
use Guzzle\Common\Exception\GuzzleException;
/**
* Http exception interface
*/
interface HttpException extends GuzzleException {}

View File

@ -1,113 +0,0 @@
<?php
namespace Guzzle\Http\Exception;
use Guzzle\Common\Exception\ExceptionCollection;
use Guzzle\Http\Message\RequestInterface;
/**
* Exception encountered during a multi transfer
*/
class MultiTransferException extends ExceptionCollection
{
protected $successfulRequests = array();
protected $failedRequests = array();
/**
* Get all of the requests in the transfer
*
* @return array
*/
public function getAllRequests()
{
return array_merge($this->successfulRequests, $this->failedRequests);
}
/**
* Add to the array of successful requests
*
* @param RequestInterface $request Successful request
*
* @return self
*/
public function addSuccessfulRequest(RequestInterface $request)
{
$this->successfulRequests[] = $request;
return $this;
}
/**
* Add to the array of failed requests
*
* @param RequestInterface $request Failed request
*
* @return self
*/
public function addFailedRequest(RequestInterface $request)
{
$this->failedRequests[] = $request;
return $this;
}
/**
* Set all of the successful requests
*
* @param array Array of requests
*
* @return self
*/
public function setSuccessfulRequests(array $requests)
{
$this->successfulRequests = $requests;
return $this;
}
/**
* Set all of the failed requests
*
* @param array Array of requests
*
* @return self
*/
public function setFailedRequests(array $requests)
{
$this->failedRequests = $requests;
return $this;
}
/**
* Get an array of successful requests sent in the multi transfer
*
* @return array
*/
public function getSuccessfulRequests()
{
return $this->successfulRequests;
}
/**
* Get an array of failed requests sent in the multi transfer
*
* @return array
*/
public function getFailedRequests()
{
return $this->failedRequests;
}
/**
* Check if the exception object contains a request
*
* @param RequestInterface $request Request to check
*
* @return bool
*/
public function containsRequest(RequestInterface $request)
{
return in_array($request, $this->failedRequests, true) || in_array($request, $this->successfulRequests, true);
}
}

View File

@ -1,39 +0,0 @@
<?php
namespace Guzzle\Http\Exception;
use Guzzle\Common\Exception\RuntimeException;
use Guzzle\Http\Message\RequestInterface;
/**
* Http request exception
*/
class RequestException extends RuntimeException implements HttpException
{
/** @var RequestInterface */
protected $request;
/**
* Set the request that caused the exception
*
* @param RequestInterface $request Request to set
*
* @return RequestException
*/
public function setRequest(RequestInterface $request)
{
$this->request = $request;
return $this;
}
/**
* Get the request that caused the exception
*
* @return RequestInterface
*/
public function getRequest()
{
return $this->request;
}
}

View File

@ -1,8 +0,0 @@
<?php
namespace Guzzle\Http\Exception;
/**
* Exception when a server error is encountered (5xx codes)
*/
class ServerErrorResponseException extends BadResponseException {}

View File

@ -1,5 +0,0 @@
<?php
namespace Guzzle\Http\Exception;
class TooManyRedirectsException extends BadResponseException {}

View File

@ -1,83 +0,0 @@
<?php
namespace Guzzle\Http;
use Guzzle\Common\Event;
use Guzzle\Common\HasDispatcherInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* EntityBody decorator that emits events for read and write methods
*/
class IoEmittingEntityBody extends AbstractEntityBodyDecorator implements HasDispatcherInterface
{
/** @var EventDispatcherInterface */
protected $eventDispatcher;
public static function getAllEvents()
{
return array('body.read', 'body.write');
}
/**
* {@inheritdoc}
* @codeCoverageIgnore
*/
public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
{
$this->eventDispatcher = $eventDispatcher;
return $this;
}
public function getEventDispatcher()
{
if (!$this->eventDispatcher) {
$this->eventDispatcher = new EventDispatcher();
}
return $this->eventDispatcher;
}
public function dispatch($eventName, array $context = array())
{
$this->getEventDispatcher()->dispatch($eventName, new Event($context));
}
/**
* {@inheritdoc}
* @codeCoverageIgnore
*/
public function addSubscriber(EventSubscriberInterface $subscriber)
{
$this->getEventDispatcher()->addSubscriber($subscriber);
return $this;
}
public function read($length)
{
$event = array(
'body' => $this,
'length' => $length,
'read' => $this->body->read($length)
);
$this->dispatch('body.read', $event);
return $event['read'];
}
public function write($string)
{
$event = array(
'body' => $this,
'write' => $string,
'result' => $this->body->write($string)
);
$this->dispatch('body.write', $event);
return $event['result'];
}
}

View File

@ -1,220 +0,0 @@
<?php
namespace Guzzle\Http\Message;
use Guzzle\Common\Version;
use Guzzle\Common\Collection;
use Guzzle\Http\Message\Header\HeaderCollection;
use Guzzle\Http\Message\Header\HeaderFactory;
use Guzzle\Http\Message\Header\HeaderFactoryInterface;
use Guzzle\Http\Message\Header\HeaderInterface;
/**
* Abstract HTTP request/response message
*/
abstract class AbstractMessage implements MessageInterface
{
/** @var array HTTP header collection */
protected $headers;
/** @var HeaderFactoryInterface $headerFactory */
protected $headerFactory;
/** @var Collection Custom message parameters that are extendable by plugins */
protected $params;
/** @var string Message protocol */
protected $protocol = 'HTTP';
/** @var string HTTP protocol version of the message */
protected $protocolVersion = '1.1';
public function __construct()
{
$this->params = new Collection();
$this->headerFactory = new HeaderFactory();
$this->headers = new HeaderCollection();
}
/**
* Set the header factory to use to create headers
*
* @param HeaderFactoryInterface $factory
*
* @return self
*/
public function setHeaderFactory(HeaderFactoryInterface $factory)
{
$this->headerFactory = $factory;
return $this;
}
public function getParams()
{
return $this->params;
}
public function addHeader($header, $value)
{
if (isset($this->headers[$header])) {
$this->headers[$header]->add($value);
} elseif ($value instanceof HeaderInterface) {
$this->headers[$header] = $value;
} else {
$this->headers[$header] = $this->headerFactory->createHeader($header, $value);
}
return $this;
}
public function addHeaders(array $headers)
{
foreach ($headers as $key => $value) {
$this->addHeader($key, $value);
}
return $this;
}
public function getHeader($header)
{
return $this->headers[$header];
}
public function getHeaders()
{
return $this->headers;
}
public function getHeaderLines()
{
$headers = array();
foreach ($this->headers as $value) {
$headers[] = $value->getName() . ': ' . $value;
}
return $headers;
}
public function setHeader($header, $value)
{
unset($this->headers[$header]);
$this->addHeader($header, $value);
return $this;
}
public function setHeaders(array $headers)
{
$this->headers->clear();
foreach ($headers as $key => $value) {
$this->addHeader($key, $value);
}
return $this;
}
public function hasHeader($header)
{
return isset($this->headers[$header]);
}
public function removeHeader($header)
{
unset($this->headers[$header]);
return $this;
}
/**
* @deprecated Use $message->getHeader()->parseParams()
* @codeCoverageIgnore
*/
public function getTokenizedHeader($header, $token = ';')
{
Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader()->parseParams()');
if ($this->hasHeader($header)) {
$data = new Collection();
foreach ($this->getHeader($header)->parseParams() as $values) {
foreach ($values as $key => $value) {
if ($value === '') {
$data->set($data->count(), $key);
} else {
$data->add($key, $value);
}
}
}
return $data;
}
}
/**
* @deprecated
* @codeCoverageIgnore
*/
public function setTokenizedHeader($header, $data, $token = ';')
{
Version::warn(__METHOD__ . ' is deprecated.');
return $this;
}
/**
* @deprecated
* @codeCoverageIgnore
*/
public function getCacheControlDirective($directive)
{
Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->getDirective()');
if (!($header = $this->getHeader('Cache-Control'))) {
return null;
}
return $header->getDirective($directive);
}
/**
* @deprecated
* @codeCoverageIgnore
*/
public function hasCacheControlDirective($directive)
{
Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->hasDirective()');
if ($header = $this->getHeader('Cache-Control')) {
return $header->hasDirective($directive);
} else {
return false;
}
}
/**
* @deprecated
* @codeCoverageIgnore
*/
public function addCacheControlDirective($directive, $value = true)
{
Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->addDirective()');
if (!($header = $this->getHeader('Cache-Control'))) {
$this->addHeader('Cache-Control', '');
$header = $this->getHeader('Cache-Control');
}
$header->addDirective($directive, $value);
return $this;
}
/**
* @deprecated
* @codeCoverageIgnore
*/
public function removeCacheControlDirective($directive)
{
Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->removeDirective()');
if ($header = $this->getHeader('Cache-Control')) {
$header->removeDirective($directive);
}
return $this;
}
}

View File

@ -1,248 +0,0 @@
<?php
namespace Guzzle\Http\Message;
use Guzzle\Http\EntityBody;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Http\QueryString;
use Guzzle\Http\RedirectPlugin;
use Guzzle\Http\Exception\RequestException;
use Guzzle\Http\Mimetypes;
/**
* HTTP request that sends an entity-body in the request message (POST, PUT, PATCH, DELETE)
*/
class EntityEnclosingRequest extends Request implements EntityEnclosingRequestInterface
{
/** @var int When the size of the body is greater than 1MB, then send Expect: 100-Continue */
protected $expectCutoff = 1048576;
/** @var EntityBodyInterface $body Body of the request */
protected $body;
/** @var QueryString POST fields to use in the EntityBody */
protected $postFields;
/** @var array POST files to send with the request */
protected $postFiles = array();
public function __construct($method, $url, $headers = array())
{
$this->postFields = new QueryString();
parent::__construct($method, $url, $headers);
}
/**
* @return string
*/
public function __toString()
{
// Only attempt to include the POST data if it's only fields
if (count($this->postFields) && empty($this->postFiles)) {
return parent::__toString() . (string) $this->postFields;
}
return parent::__toString() . $this->body;
}
public function setState($state, array $context = array())
{
parent::setState($state, $context);
if ($state == self::STATE_TRANSFER && !$this->body && !count($this->postFields) && !count($this->postFiles)) {
$this->setHeader('Content-Length', 0)->removeHeader('Transfer-Encoding');
}
return $this->state;
}
public function setBody($body, $contentType = null)
{
$this->body = EntityBody::factory($body);
// Auto detect the Content-Type from the path of the request if possible
if ($contentType === null && !$this->hasHeader('Content-Type')) {
$contentType = $this->body->getContentType() ?: Mimetypes::getInstance()->fromFilename($this->getPath());
}
if ($contentType) {
$this->setHeader('Content-Type', $contentType);
}
// Always add the Expect 100-Continue header if the body cannot be rewound. This helps with redirects.
if (!$this->body->isSeekable() && $this->expectCutoff !== false) {
$this->setHeader('Expect', '100-Continue');
}
// Set the Content-Length header if it can be determined
$size = $this->body->getContentLength();
if ($size !== null && $size !== false) {
$this->setHeader('Content-Length', $size);
if ($size > $this->expectCutoff) {
$this->setHeader('Expect', '100-Continue');
}
} elseif (!$this->hasHeader('Content-Length')) {
if ('1.1' == $this->protocolVersion) {
$this->setHeader('Transfer-Encoding', 'chunked');
} else {
throw new RequestException(
'Cannot determine Content-Length and cannot use chunked Transfer-Encoding when using HTTP/1.0'
);
}
}
return $this;
}
public function getBody()
{
return $this->body;
}
/**
* Set the size that the entity body of the request must exceed before adding the Expect: 100-Continue header.
*
* @param int|bool $size Cutoff in bytes. Set to false to never send the expect header (even with non-seekable data)
*
* @return self
*/
public function setExpectHeaderCutoff($size)
{
$this->expectCutoff = $size;
if ($size === false || !$this->body) {
$this->removeHeader('Expect');
} elseif ($this->body && $this->body->getSize() && $this->body->getSize() > $size) {
$this->setHeader('Expect', '100-Continue');
}
return $this;
}
public function configureRedirects($strict = false, $maxRedirects = 5)
{
$this->getParams()->set(RedirectPlugin::STRICT_REDIRECTS, $strict);
if ($maxRedirects == 0) {
$this->getParams()->set(RedirectPlugin::DISABLE, true);
} else {
$this->getParams()->set(RedirectPlugin::MAX_REDIRECTS, $maxRedirects);
}
return $this;
}
public function getPostField($field)
{
return $this->postFields->get($field);
}
public function getPostFields()
{
return $this->postFields;
}
public function setPostField($key, $value)
{
$this->postFields->set($key, $value);
$this->processPostFields();
return $this;
}
public function addPostFields($fields)
{
$this->postFields->merge($fields);
$this->processPostFields();
return $this;
}
public function removePostField($field)
{
$this->postFields->remove($field);
$this->processPostFields();
return $this;
}
public function getPostFiles()
{
return $this->postFiles;
}
public function getPostFile($fieldName)
{
return isset($this->postFiles[$fieldName]) ? $this->postFiles[$fieldName] : null;
}
public function removePostFile($fieldName)
{
unset($this->postFiles[$fieldName]);
$this->processPostFields();
return $this;
}
public function addPostFile($field, $filename = null, $contentType = null)
{
$data = null;
if ($field instanceof PostFileInterface) {
$data = $field;
} elseif (is_array($filename)) {
// Allow multiple values to be set in a single key
foreach ($filename as $file) {
$this->addPostFile($field, $file, $contentType);
}
return $this;
} elseif (!is_string($filename)) {
throw new RequestException('The path to a file must be a string');
} elseif (!empty($filename)) {
// Adding an empty file will cause cURL to error out
$data = new PostFile($field, $filename, $contentType);
}
if ($data) {
if (!isset($this->postFiles[$data->getFieldName()])) {
$this->postFiles[$data->getFieldName()] = array($data);
} else {
$this->postFiles[$data->getFieldName()][] = $data;
}
$this->processPostFields();
}
return $this;
}
public function addPostFiles(array $files)
{
foreach ($files as $key => $file) {
if ($file instanceof PostFileInterface) {
$this->addPostFile($file, null, null, false);
} elseif (is_string($file)) {
// Convert non-associative array keys into 'file'
if (is_numeric($key)) {
$key = 'file';
}
$this->addPostFile($key, $file, null, false);
} else {
throw new RequestException('File must be a string or instance of PostFileInterface');
}
}
return $this;
}
/**
* Determine what type of request should be sent based on post fields
*/
protected function processPostFields()
{
if (!$this->postFiles) {
$this->removeHeader('Expect')->setHeader('Content-Type', self::URL_ENCODED);
} else {
$this->setHeader('Content-Type', self::MULTIPART);
if ($this->expectCutoff !== false) {
$this->setHeader('Expect', '100-Continue');
}
}
}
}

View File

@ -1,136 +0,0 @@
<?php
namespace Guzzle\Http\Message;
use Guzzle\Http\Exception\RequestException;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Http\QueryString;
/**
* HTTP request that sends an entity-body in the request message (POST, PUT)
*/
interface EntityEnclosingRequestInterface extends RequestInterface
{
const URL_ENCODED = 'application/x-www-form-urlencoded; charset=utf-8';
const MULTIPART = 'multipart/form-data';
/**
* Set the body of the request
*
* @param string|resource|EntityBodyInterface $body Body to use in the entity body of the request
* @param string $contentType Content-Type to set. Leave null to use an existing
* Content-Type or to guess the Content-Type
* @return self
* @throws RequestException if the protocol is < 1.1 and Content-Length can not be determined
*/
public function setBody($body, $contentType = null);
/**
* Get the body of the request if set
*
* @return EntityBodyInterface|null
*/
public function getBody();
/**
* Get a POST field from the request
*
* @param string $field Field to retrieve
*
* @return mixed|null
*/
public function getPostField($field);
/**
* Get the post fields that will be used in the request
*
* @return QueryString
*/
public function getPostFields();
/**
* Set a POST field value
*
* @param string $key Key to set
* @param string $value Value to set
*
* @return self
*/
public function setPostField($key, $value);
/**
* Add POST fields to use in the request
*
* @param QueryString|array $fields POST fields
*
* @return self
*/
public function addPostFields($fields);
/**
* Remove a POST field or file by name
*
* @param string $field Name of the POST field or file to remove
*
* @return self
*/
public function removePostField($field);
/**
* Returns an associative array of POST field names to PostFileInterface objects
*
* @return array
*/
public function getPostFiles();
/**
* Get a POST file from the request
*
* @param string $fieldName POST fields to retrieve
*
* @return array|null Returns an array wrapping an array of PostFileInterface objects
*/
public function getPostFile($fieldName);
/**
* Remove a POST file from the request
*
* @param string $fieldName POST file field name to remove
*
* @return self
*/
public function removePostFile($fieldName);
/**
* Add a POST file to the upload
*
* @param string $field POST field to use (e.g. file). Used to reference content from the server.
* @param string $filename Full path to the file. Do not include the @ symbol.
* @param string $contentType Optional Content-Type to add to the Content-Disposition.
* Default behavior is to guess. Set to false to not specify.
* @return self
*/
public function addPostFile($field, $filename = null, $contentType = null);
/**
* Add POST files to use in the upload
*
* @param array $files An array of POST fields => filenames where filename can be a string or PostFileInterface
*
* @return self
*/
public function addPostFiles(array $files);
/**
* Configure how redirects are handled for the request
*
* @param bool $strict Set to true to follow strict RFC compliance when redirecting POST requests. Most
* browsers with follow a 301-302 redirect for a POST request with a GET request. This is
* the default behavior of Guzzle. Enable strict redirects to redirect these responses
* with a POST rather than a GET request.
* @param int $maxRedirects Specify the maximum number of allowed redirects. Set to 0 to disable redirects.
*
* @return self
*/
public function configureRedirects($strict = false, $maxRedirects = 5);
}

View File

@ -1,177 +0,0 @@
<?php
namespace Guzzle\Http\Message;
use Guzzle\Common\Version;
use Guzzle\Http\Message\Header\HeaderInterface;
/**
* Represents a header and all of the values stored by that header
*/
class Header implements HeaderInterface
{
protected $values = array();
protected $header;
protected $glue;
/**
* @param string $header Name of the header
* @param array|string $values Values of the header as an array or a scalar
* @param string $glue Glue used to combine multiple values into a string
*/
public function __construct($header, $values = array(), $glue = ',')
{
$this->header = trim($header);
$this->glue = $glue;
foreach ((array) $values as $value) {
foreach ((array) $value as $v) {
$this->values[] = $v;
}
}
}
public function __toString()
{
return implode($this->glue . ' ', $this->toArray());
}
public function add($value)
{
$this->values[] = $value;
return $this;
}
public function getName()
{
return $this->header;
}
public function setName($name)
{
$this->header = $name;
return $this;
}
public function setGlue($glue)
{
$this->glue = $glue;
return $this;
}
public function getGlue()
{
return $this->glue;
}
/**
* Normalize the header to be a single header with an array of values.
*
* If any values of the header contains the glue string value (e.g. ","), then the value will be exploded into
* multiple entries in the header.
*
* @return self
*/
public function normalize()
{
$values = $this->toArray();
for ($i = 0, $total = count($values); $i < $total; $i++) {
if (strpos($values[$i], $this->glue) !== false) {
foreach (explode($this->glue, $values[$i]) as $v) {
$values[] = trim($v);
}
unset($values[$i]);
}
}
$this->values = array_values($values);
return $this;
}
public function hasValue($searchValue)
{
return in_array($searchValue, $this->toArray());
}
public function removeValue($searchValue)
{
$this->values = array_values(array_filter($this->values, function ($value) use ($searchValue) {
return $value != $searchValue;
}));
return $this;
}
public function toArray()
{
return $this->values;
}
public function count()
{
return count($this->toArray());
}
public function getIterator()
{
return new \ArrayIterator($this->toArray());
}
public function parseParams()
{
$params = $matches = array();
$callback = array($this, 'trimHeader');
// Normalize the header into a single array and iterate over all values
foreach ($this->normalize()->toArray() as $val) {
$part = array();
foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) {
preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches);
$pieces = array_map($callback, $matches[0]);
$part[$pieces[0]] = isset($pieces[1]) ? $pieces[1] : '';
}
$params[] = $part;
}
return $params;
}
/**
* @deprecated
* @codeCoverageIgnore
*/
public function hasExactHeader($header)
{
Version::warn(__METHOD__ . ' is deprecated');
return $this->header == $header;
}
/**
* @deprecated
* @codeCoverageIgnore
*/
public function raw()
{
Version::warn(__METHOD__ . ' is deprecated. Use toArray()');
return $this->toArray();
}
/**
* Trim a header by removing excess spaces and wrapping quotes
*
* @param $str
*
* @return string
*/
protected function trimHeader($str)
{
static $trimmed = "\"' \n\t";
return trim($str, $trimmed);
}
}

View File

@ -1,121 +0,0 @@
<?php
namespace Guzzle\Http\Message\Header;
use Guzzle\Http\Message\Header;
/**
* Provides helpful functionality for Cache-Control headers
*/
class CacheControl extends Header
{
/** @var array */
protected $directives;
public function add($value)
{
parent::add($value);
$this->directives = null;
}
public function removeValue($searchValue)
{
parent::removeValue($searchValue);
$this->directives = null;
}
/**
* Check if a specific cache control directive exists
*
* @param string $param Directive to retrieve
*
* @return bool
*/
public function hasDirective($param)
{
$directives = $this->getDirectives();
return isset($directives[$param]);
}
/**
* Get a specific cache control directive
*
* @param string $param Directive to retrieve
*
* @return string|bool|null
*/
public function getDirective($param)
{
$directives = $this->getDirectives();
return isset($directives[$param]) ? $directives[$param] : null;
}
/**
* Add a cache control directive
*
* @param string $param Directive to add
* @param string $value Value to set
*
* @return self
*/
public function addDirective($param, $value)
{
$directives = $this->getDirectives();
$directives[$param] = $value;
$this->updateFromDirectives($directives);
return $this;
}
/**
* Remove a cache control directive by name
*
* @param string $param Directive to remove
*
* @return self
*/
public function removeDirective($param)
{
$directives = $this->getDirectives();
unset($directives[$param]);
$this->updateFromDirectives($directives);
return $this;
}
/**
* Get an associative array of cache control directives
*
* @return array
*/
public function getDirectives()
{
if ($this->directives === null) {
$this->directives = array();
foreach ($this->parseParams() as $collection) {
foreach ($collection as $key => $value) {
$this->directives[$key] = $value === '' ? true : $value;
}
}
}
return $this->directives;
}
/**
* Updates the header value based on the parsed directives
*
* @param array $directives Array of cache control directives
*/
protected function updateFromDirectives(array $directives)
{
$this->directives = $directives;
$this->values = array();
foreach ($directives as $key => $value) {
$this->values[] = $value === true ? $key : "{$key}={$value}";
}
}
}

View File

@ -1,109 +0,0 @@
<?php
namespace Guzzle\Http\Message\Header;
use Guzzle\Common\Collection;
use Guzzle\Common\ToArrayInterface;
/**
* Provides a case-insensitive collection of headers
*/
class HeaderCollection implements \IteratorAggregate, \Countable, \ArrayAccess, ToArrayInterface
{
/** @var array */
protected $headers;
public function __construct($headers = array())
{
$this->headers = $headers;
}
public function __clone()
{
foreach ($this->headers as &$header) {
$header = clone $header;
}
}
/**
* Clears the header collection
*/
public function clear()
{
$this->headers = array();
}
/**
* Set a header on the collection
*
* @param HeaderInterface $header Header to add
*
* @return self
*/
public function add(HeaderInterface $header)
{
$this->headers[strtolower($header->getName())] = $header;
return $this;
}
/**
* Get an array of header objects
*
* @return array
*/
public function getAll()
{
return $this->headers;
}
/**
* Alias of offsetGet
*/
public function get($key)
{
return $this->offsetGet($key);
}
public function count()
{
return count($this->headers);
}
public function offsetExists($offset)
{
return isset($this->headers[strtolower($offset)]);
}
public function offsetGet($offset)
{
$l = strtolower($offset);
return isset($this->headers[$l]) ? $this->headers[$l] : null;
}
public function offsetSet($offset, $value)
{
$this->add($value);
}
public function offsetUnset($offset)
{
unset($this->headers[strtolower($offset)]);
}
public function getIterator()
{
return new \ArrayIterator($this->headers);
}
public function toArray()
{
$result = array();
foreach ($this->headers as $header) {
$result[$header->getName()] = $header->toArray();
}
return $result;
}
}

View File

@ -1,26 +0,0 @@
<?php
namespace Guzzle\Http\Message\Header;
use Guzzle\Http\Message\Header;
/**
* Default header factory implementation
*/
class HeaderFactory implements HeaderFactoryInterface
{
/** @var array */
protected $mapping = array(
'cache-control' => 'Guzzle\Http\Message\Header\CacheControl',
'link' => 'Guzzle\Http\Message\Header\Link',
);
public function createHeader($header, $value = null)
{
$lowercase = strtolower($header);
return isset($this->mapping[$lowercase])
? new $this->mapping[$lowercase]($header, $value)
: new Header($header, $value);
}
}

View File

@ -1,19 +0,0 @@
<?php
namespace Guzzle\Http\Message\Header;
/**
* Interface for creating headers
*/
interface HeaderFactoryInterface
{
/**
* Create a header from a header name and a single value
*
* @param string $header Name of the header to create
* @param string $value Value to set on the header
*
* @return HeaderInterface
*/
public function createHeader($header, $value = null);
}

View File

@ -1,83 +0,0 @@
<?php
namespace Guzzle\Http\Message\Header;
use Guzzle\Common\ToArrayInterface;
interface HeaderInterface extends ToArrayInterface, \Countable, \IteratorAggregate
{
/**
* Convert the header to a string
*
* @return string
*/
public function __toString();
/**
* Add a value to the list of header values
*
* @param string $value Value to add to the header
*
* @return self
*/
public function add($value);
/**
* Get the name of the header
*
* @return string
*/
public function getName();
/**
* Change the name of the header
*
* @param string $name Name to change to
*
* @return self
*/
public function setName($name);
/**
* Change the glue used to implode the values
*
* @param string $glue Glue used to implode multiple values
*
* @return self
*/
public function setGlue($glue);
/**
* Get the glue used to implode multiple values into a string
*
* @return string
*/
public function getGlue();
/**
* Check if the collection of headers has a particular value
*
* @param string $searchValue Value to search for
*
* @return bool
*/
public function hasValue($searchValue);
/**
* Remove a specific value from the header
*
* @param string $searchValue Value to remove
*
* @return self
*/
public function removeValue($searchValue);
/**
* Parse a header containing ";" separated data into an array of associative arrays representing the header
* key value pair data of the header. When a parameter does not contain a value, but just contains a key, this
* function will inject a key with a '' string value.
*
* @return array
*/
public function parseParams();
}

View File

@ -1,93 +0,0 @@
<?php
namespace Guzzle\Http\Message\Header;
use Guzzle\Http\Message\Header;
/**
* Provides helpful functionality for link headers
*/
class Link extends Header
{
/**
* Add a link to the header
*
* @param string $url Link URL
* @param string $rel Link rel
* @param array $params Other link parameters
*
* @return self
*/
public function addLink($url, $rel, array $params = array())
{
$values = array("<{$url}>", "rel=\"{$rel}\"");
foreach ($params as $k => $v) {
$values[] = "{$k}=\"{$v}\"";
}
return $this->add(implode('; ', $values));
}
/**
* Check if a specific link exists for a given rel attribute
*
* @param string $rel rel value
*
* @return bool
*/
public function hasLink($rel)
{
return $this->getLink($rel) !== null;
}
/**
* Get a specific link for a given rel attribute
*
* @param string $rel Rel value
*
* @return array|null
*/
public function getLink($rel)
{
foreach ($this->getLinks() as $link) {
if (isset($link['rel']) && $link['rel'] == $rel) {
return $link;
}
}
return null;
}
/**
* Get an associative array of links
*
* For example:
* Link: <http:/.../front.jpeg>; rel=front; type="image/jpeg", <http://.../back.jpeg>; rel=back; type="image/jpeg"
*
* <code>
* var_export($response->getLinks());
* array(
* array(
* 'url' => 'http:/.../front.jpeg',
* 'rel' => 'back',
* 'type' => 'image/jpeg',
* )
* )
* </code>
*
* @return array
*/
public function getLinks()
{
$links = $this->parseParams();
foreach ($links as &$link) {
$key = key($link);
unset($link[$key]);
$link['url'] = trim($key, '<> ');
}
return $links;
}
}

View File

@ -1,102 +0,0 @@
<?php
namespace Guzzle\Http\Message;
/**
* Request and response message interface
*/
interface MessageInterface
{
/**
* Get application and plugin specific parameters set on the message.
*
* @return \Guzzle\Common\Collection
*/
public function getParams();
/**
* Add a header to an existing collection of headers.
*
* @param string $header Header name to add
* @param string $value Value of the header
*
* @return self
*/
public function addHeader($header, $value);
/**
* Add and merge in an array of HTTP headers.
*
* @param array $headers Associative array of header data.
*
* @return self
*/
public function addHeaders(array $headers);
/**
* Retrieve an HTTP header by name. Performs a case-insensitive search of all headers.
*
* @param string $header Header to retrieve.
*
* @return Header|null
*/
public function getHeader($header);
/**
* Get all headers as a collection
*
* @return \Guzzle\Http\Message\Header\HeaderCollection
*/
public function getHeaders();
/**
* Check if the specified header is present.
*
* @param string $header The header to check.
*
* @return bool
*/
public function hasHeader($header);
/**
* Remove a specific HTTP header.
*
* @param string $header HTTP header to remove.
*
* @return self
*/
public function removeHeader($header);
/**
* Set an HTTP header and overwrite any existing value for the header
*
* @param string $header Name of the header to set.
* @param mixed $value Value to set.
*
* @return self
*/
public function setHeader($header, $value);
/**
* Overwrite all HTTP headers with the supplied array of headers
*
* @param array $headers Associative array of header data.
*
* @return self
*/
public function setHeaders(array $headers);
/**
* Get an array of message header lines (e.g. ["Host: example.com", ...])
*
* @return array
*/
public function getHeaderLines();
/**
* Get the raw message headers as a string
*
* @return string
*/
public function getRawHeaders();
}

View File

@ -1,109 +0,0 @@
<?php
namespace Guzzle\Http\Message;
use Guzzle\Common\Version;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\Mimetypes;
/**
* POST file upload
*/
class PostFile implements PostFileInterface
{
protected $fieldName;
protected $contentType;
protected $filename;
/**
* @param string $fieldName Name of the field
* @param string $filename Path to the file
* @param string $contentType Content-Type of the upload
*/
public function __construct($fieldName, $filename, $contentType = null)
{
$this->fieldName = $fieldName;
$this->setFilename($filename);
$this->contentType = $contentType ?: $this->guessContentType();
}
public function setFieldName($name)
{
$this->fieldName = $name;
return $this;
}
public function getFieldName()
{
return $this->fieldName;
}
public function setFilename($filename)
{
// Remove leading @ symbol
if (strpos($filename, '@') === 0) {
$filename = substr($filename, 1);
}
if (!is_readable($filename)) {
throw new InvalidArgumentException("Unable to open {$filename} for reading");
}
$this->filename = $filename;
return $this;
}
public function getFilename()
{
return $this->filename;
}
public function setContentType($type)
{
$this->contentType = $type;
return $this;
}
public function getContentType()
{
return $this->contentType;
}
public function getCurlValue()
{
// PHP 5.5 introduced a CurlFile object that deprecates the old @filename syntax
// See: https://wiki.php.net/rfc/curl-file-upload
if (function_exists('curl_file_create')) {
return curl_file_create($this->filename, $this->contentType, basename($this->filename));
}
// Use the old style if using an older version of PHP
$value = "@{$this->filename};filename=" . basename($this->filename);
if ($this->contentType) {
$value .= ';type=' . $this->contentType;
}
return $value;
}
/**
* @deprecated
* @codeCoverageIgnore
*/
public function getCurlString()
{
Version::warn(__METHOD__ . ' is deprecated. Use getCurlValue()');
return $this->getCurlValue();
}
/**
* Determine the Content-Type of the file
*/
protected function guessContentType()
{
return Mimetypes::getInstance()->fromFilename($this->filename) ?: 'application/octet-stream';
}
}

View File

@ -1,67 +0,0 @@
<?php
namespace Guzzle\Http\Message;
use Guzzle\Common\Exception\InvalidArgumentException;
/**
* POST file upload
*/
interface PostFileInterface
{
/**
* Set the name of the field
*
* @param string $name Field name
*
* @return self
*/
public function setFieldName($name);
/**
* Get the name of the field
*
* @return string
*/
public function getFieldName();
/**
* Set the path to the file
*
* @param string $path Full path to the file
*
* @return self
* @throws InvalidArgumentException if the file cannot be read
*/
public function setFilename($path);
/**
* Get the full path to the file
*
* @return string
*/
public function getFilename();
/**
* Set the Content-Type of the file
*
* @param string $type Content type
*
* @return self
*/
public function setContentType($type);
/**
* Get the Content-Type of the file
*
* @return string
*/
public function getContentType();
/**
* Get a cURL ready string or CurlFile object for the upload
*
* @return string
*/
public function getCurlValue();
}

View File

@ -1,637 +0,0 @@
<?php
namespace Guzzle\Http\Message;
use Guzzle\Common\Version;
use Guzzle\Common\Event;
use Guzzle\Common\Collection;
use Guzzle\Common\Exception\RuntimeException;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\Exception\RequestException;
use Guzzle\Http\Exception\BadResponseException;
use Guzzle\Http\ClientInterface;
use Guzzle\Http\EntityBody;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Http\Message\Header\HeaderInterface;
use Guzzle\Http\Url;
use Guzzle\Parser\ParserRegistry;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* HTTP request class to send requests
*/
class Request extends AbstractMessage implements RequestInterface
{
/** @var EventDispatcherInterface */
protected $eventDispatcher;
/** @var Url HTTP Url */
protected $url;
/** @var string HTTP method (GET, PUT, POST, DELETE, HEAD, OPTIONS, TRACE) */
protected $method;
/** @var ClientInterface */
protected $client;
/** @var Response Response of the request */
protected $response;
/** @var EntityBodyInterface Response body */
protected $responseBody;
/** @var string State of the request object */
protected $state;
/** @var string Authentication username */
protected $username;
/** @var string Auth password */
protected $password;
/** @var Collection cURL specific transfer options */
protected $curlOptions;
/** @var bool */
protected $isRedirect = false;
public static function getAllEvents()
{
return array(
// Called when receiving or uploading data through cURL
'curl.callback.read', 'curl.callback.write', 'curl.callback.progress',
// Cloning a request
'request.clone',
// About to send the request, sent request, completed transaction
'request.before_send', 'request.sent', 'request.complete',
// A request received a successful response
'request.success',
// A request received an unsuccessful response
'request.error',
// An exception is being thrown because of an unsuccessful response
'request.exception',
// Received response status line
'request.receive.status_line'
);
}
/**
* @param string $method HTTP method
* @param string|Url $url HTTP URL to connect to. The URI scheme, host header, and URI are parsed from the
* full URL. If query string parameters are present they will be parsed as well.
* @param array|Collection $headers HTTP headers
*/
public function __construct($method, $url, $headers = array())
{
parent::__construct();
$this->method = strtoupper($method);
$this->curlOptions = new Collection();
$this->setUrl($url);
if ($headers) {
// Special handling for multi-value headers
foreach ($headers as $key => $value) {
// Deal with collisions with Host and Authorization
if ($key == 'host' || $key == 'Host') {
$this->setHeader($key, $value);
} elseif ($value instanceof HeaderInterface) {
$this->addHeader($key, $value);
} else {
foreach ((array) $value as $v) {
$this->addHeader($key, $v);
}
}
}
}
$this->setState(self::STATE_NEW);
}
public function __clone()
{
if ($this->eventDispatcher) {
$this->eventDispatcher = clone $this->eventDispatcher;
}
$this->curlOptions = clone $this->curlOptions;
$this->params = clone $this->params;
$this->url = clone $this->url;
$this->response = $this->responseBody = null;
$this->headers = clone $this->headers;
$this->setState(RequestInterface::STATE_NEW);
$this->dispatch('request.clone', array('request' => $this));
}
/**
* Get the HTTP request as a string
*
* @return string
*/
public function __toString()
{
return $this->getRawHeaders() . "\r\n\r\n";
}
/**
* Default method that will throw exceptions if an unsuccessful response is received.
*
* @param Event $event Received
* @throws BadResponseException if the response is not successful
*/
public static function onRequestError(Event $event)
{
$e = BadResponseException::factory($event['request'], $event['response']);
$event['request']->setState(self::STATE_ERROR, array('exception' => $e) + $event->toArray());
throw $e;
}
public function setClient(ClientInterface $client)
{
$this->client = $client;
return $this;
}
public function getClient()
{
return $this->client;
}
public function getRawHeaders()
{
$protocolVersion = $this->protocolVersion ?: '1.1';
return trim($this->method . ' ' . $this->getResource()) . ' '
. strtoupper(str_replace('https', 'http', $this->url->getScheme()))
. '/' . $protocolVersion . "\r\n" . implode("\r\n", $this->getHeaderLines());
}
public function setUrl($url)
{
if ($url instanceof Url) {
$this->url = $url;
} else {
$this->url = Url::factory($url);
}
// Update the port and host header
$this->setPort($this->url->getPort());
if ($this->url->getUsername() || $this->url->getPassword()) {
$this->setAuth($this->url->getUsername(), $this->url->getPassword());
// Remove the auth info from the URL
$this->url->setUsername(null);
$this->url->setPassword(null);
}
return $this;
}
public function send()
{
if (!$this->client) {
throw new RuntimeException('A client must be set on the request');
}
return $this->client->send($this);
}
public function getResponse()
{
return $this->response;
}
public function getQuery($asString = false)
{
return $asString
? (string) $this->url->getQuery()
: $this->url->getQuery();
}
public function getMethod()
{
return $this->method;
}
public function getScheme()
{
return $this->url->getScheme();
}
public function setScheme($scheme)
{
$this->url->setScheme($scheme);
return $this;
}
public function getHost()
{
return $this->url->getHost();
}
public function setHost($host)
{
$this->url->setHost($host);
$this->setPort($this->url->getPort());
return $this;
}
public function getProtocolVersion()
{
return $this->protocolVersion;
}
public function setProtocolVersion($protocol)
{
$this->protocolVersion = $protocol;
return $this;
}
public function getPath()
{
return '/' . ltrim($this->url->getPath(), '/');
}
public function setPath($path)
{
$this->url->setPath($path);
return $this;
}
public function getPort()
{
return $this->url->getPort();
}
public function setPort($port)
{
$this->url->setPort($port);
// Include the port in the Host header if it is not the default port for the scheme of the URL
$scheme = $this->url->getScheme();
if (($scheme == 'http' && $port != 80) || ($scheme == 'https' && $port != 443)) {
$this->headers['host'] = $this->headerFactory->createHeader('Host', $this->url->getHost() . ':' . $port);
} else {
$this->headers['host'] = $this->headerFactory->createHeader('Host', $this->url->getHost());
}
return $this;
}
public function getUsername()
{
return $this->username;
}
public function getPassword()
{
return $this->password;
}
public function setAuth($user, $password = '', $scheme = CURLAUTH_BASIC)
{
static $authMap = array(
'basic' => CURLAUTH_BASIC,
'digest' => CURLAUTH_DIGEST,
'ntlm' => CURLAUTH_NTLM,
'any' => CURLAUTH_ANY
);
// If we got false or null, disable authentication
if (!$user) {
$this->password = $this->username = null;
$this->removeHeader('Authorization');
$this->getCurlOptions()->remove(CURLOPT_HTTPAUTH);
return $this;
}
if (!is_numeric($scheme)) {
$scheme = strtolower($scheme);
if (!isset($authMap[$scheme])) {
throw new InvalidArgumentException($scheme . ' is not a valid authentication type');
}
$scheme = $authMap[$scheme];
}
$this->username = $user;
$this->password = $password;
// Bypass CURL when using basic auth to promote connection reuse
if ($scheme == CURLAUTH_BASIC) {
$this->getCurlOptions()->remove(CURLOPT_HTTPAUTH);
$this->setHeader('Authorization', 'Basic ' . base64_encode($this->username . ':' . $this->password));
} else {
$this->getCurlOptions()
->set(CURLOPT_HTTPAUTH, $scheme)
->set(CURLOPT_USERPWD, $this->username . ':' . $this->password);
}
return $this;
}
public function getResource()
{
$resource = $this->getPath();
if ($query = (string) $this->url->getQuery()) {
$resource .= '?' . $query;
}
return $resource;
}
public function getUrl($asObject = false)
{
return $asObject ? clone $this->url : (string) $this->url;
}
public function getState()
{
return $this->state;
}
public function setState($state, array $context = array())
{
$oldState = $this->state;
$this->state = $state;
switch ($state) {
case self::STATE_NEW:
$this->response = null;
break;
case self::STATE_TRANSFER:
if ($oldState !== $state) {
// Fix Content-Length and Transfer-Encoding collisions
if ($this->hasHeader('Transfer-Encoding') && $this->hasHeader('Content-Length')) {
$this->removeHeader('Transfer-Encoding');
}
$this->dispatch('request.before_send', array('request' => $this));
}
break;
case self::STATE_COMPLETE:
if ($oldState !== $state) {
$this->processResponse($context);
$this->responseBody = null;
}
break;
case self::STATE_ERROR:
if (isset($context['exception'])) {
$this->dispatch('request.exception', array(
'request' => $this,
'response' => isset($context['response']) ? $context['response'] : $this->response,
'exception' => isset($context['exception']) ? $context['exception'] : null
));
}
}
return $this->state;
}
public function getCurlOptions()
{
return $this->curlOptions;
}
public function startResponse(Response $response)
{
$this->state = self::STATE_TRANSFER;
$response->setEffectiveUrl((string) $this->getUrl());
$this->response = $response;
return $this;
}
public function setResponse(Response $response, $queued = false)
{
$response->setEffectiveUrl((string) $this->url);
if ($queued) {
$ed = $this->getEventDispatcher();
$ed->addListener('request.before_send', $f = function ($e) use ($response, &$f, $ed) {
$e['request']->setResponse($response);
$ed->removeListener('request.before_send', $f);
}, -9999);
} else {
$this->response = $response;
// If a specific response body is specified, then use it instead of the response's body
if ($this->responseBody && !$this->responseBody->getCustomData('default') && !$response->isRedirect()) {
$this->getResponseBody()->write((string) $this->response->getBody());
} else {
$this->responseBody = $this->response->getBody();
}
$this->setState(self::STATE_COMPLETE);
}
return $this;
}
public function setResponseBody($body)
{
// Attempt to open a file for writing if a string was passed
if (is_string($body)) {
// @codeCoverageIgnoreStart
if (!($body = fopen($body, 'w+'))) {
throw new InvalidArgumentException('Could not open ' . $body . ' for writing');
}
// @codeCoverageIgnoreEnd
}
$this->responseBody = EntityBody::factory($body);
return $this;
}
public function getResponseBody()
{
if ($this->responseBody === null) {
$this->responseBody = EntityBody::factory()->setCustomData('default', true);
}
return $this->responseBody;
}
/**
* Determine if the response body is repeatable (readable + seekable)
*
* @return bool
* @deprecated Use getResponseBody()->isSeekable()
* @codeCoverageIgnore
*/
public function isResponseBodyRepeatable()
{
Version::warn(__METHOD__ . ' is deprecated. Use $request->getResponseBody()->isRepeatable()');
return !$this->responseBody ? true : $this->responseBody->isRepeatable();
}
public function getCookies()
{
if ($cookie = $this->getHeader('Cookie')) {
$data = ParserRegistry::getInstance()->getParser('cookie')->parseCookie($cookie);
return $data['cookies'];
}
return array();
}
public function getCookie($name)
{
$cookies = $this->getCookies();
return isset($cookies[$name]) ? $cookies[$name] : null;
}
public function addCookie($name, $value)
{
if (!$this->hasHeader('Cookie')) {
$this->setHeader('Cookie', "{$name}={$value}");
} else {
$this->getHeader('Cookie')->add("{$name}={$value}");
}
// Always use semicolons to separate multiple cookie headers
$this->getHeader('Cookie')->setGlue(';');
return $this;
}
public function removeCookie($name)
{
if ($cookie = $this->getHeader('Cookie')) {
foreach ($cookie as $cookieValue) {
if (strpos($cookieValue, $name . '=') === 0) {
$cookie->removeValue($cookieValue);
}
}
}
return $this;
}
public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
{
$this->eventDispatcher = $eventDispatcher;
$this->eventDispatcher->addListener('request.error', array(__CLASS__, 'onRequestError'), -255);
return $this;
}
public function getEventDispatcher()
{
if (!$this->eventDispatcher) {
$this->setEventDispatcher(new EventDispatcher());
}
return $this->eventDispatcher;
}
public function dispatch($eventName, array $context = array())
{
$context['request'] = $this;
$this->getEventDispatcher()->dispatch($eventName, new Event($context));
}
public function addSubscriber(EventSubscriberInterface $subscriber)
{
$this->getEventDispatcher()->addSubscriber($subscriber);
return $this;
}
/**
* Get an array containing the request and response for event notifications
*
* @return array
*/
protected function getEventArray()
{
return array(
'request' => $this,
'response' => $this->response
);
}
/**
* Process a received response
*
* @param array $context Contextual information
* @throws RequestException|BadResponseException on unsuccessful responses
*/
protected function processResponse(array $context = array())
{
if (!$this->response) {
// If no response, then processResponse shouldn't have been called
$e = new RequestException('Error completing request');
$e->setRequest($this);
throw $e;
}
$this->state = self::STATE_COMPLETE;
// A request was sent, but we don't know if we'll send more or if the final response will be successful
$this->dispatch('request.sent', $this->getEventArray() + $context);
// Some response processors will remove the response or reset the state (example: ExponentialBackoffPlugin)
if ($this->state == RequestInterface::STATE_COMPLETE) {
// The request completed, so the HTTP transaction is complete
$this->dispatch('request.complete', $this->getEventArray());
// If the response is bad, allow listeners to modify it or throw exceptions. You can change the response by
// modifying the Event object in your listeners or calling setResponse() on the request
if ($this->response->isError()) {
$event = new Event($this->getEventArray());
$this->getEventDispatcher()->dispatch('request.error', $event);
// Allow events of request.error to quietly change the response
if ($event['response'] !== $this->response) {
$this->response = $event['response'];
}
}
// If a successful response was received, dispatch an event
if ($this->response->isSuccessful()) {
$this->dispatch('request.success', $this->getEventArray());
}
}
}
/**
* @deprecated Use Guzzle\Plugin\Cache\DefaultCanCacheStrategy
* @codeCoverageIgnore
*/
public function canCache()
{
Version::warn(__METHOD__ . ' is deprecated. Use Guzzle\Plugin\Cache\DefaultCanCacheStrategy.');
if (class_exists('Guzzle\Plugin\Cache\DefaultCanCacheStrategy')) {
$canCache = new \Guzzle\Plugin\Cache\DefaultCanCacheStrategy();
return $canCache->canCacheRequest($this);
} else {
return false;
}
}
/**
* @deprecated Use the history plugin (not emitting a warning as this is built-into the RedirectPlugin for now)
* @codeCoverageIgnore
*/
public function setIsRedirect($isRedirect)
{
$this->isRedirect = $isRedirect;
return $this;
}
/**
* @deprecated Use the history plugin
* @codeCoverageIgnore
*/
public function isRedirect()
{
Version::warn(__METHOD__ . ' is deprecated. Use the HistoryPlugin to track this.');
return $this->isRedirect;
}
}

View File

@ -1,356 +0,0 @@
<?php
namespace Guzzle\Http\Message;
use Guzzle\Common\Collection;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\RedirectPlugin;
use Guzzle\Http\Url;
use Guzzle\Parser\ParserRegistry;
use Guzzle\Plugin\Log\LogPlugin;
/**
* Default HTTP request factory used to create the default {@see Request} and {@see EntityEnclosingRequest} objects.
*/
class RequestFactory implements RequestFactoryInterface
{
/** @var RequestFactory Singleton instance of the default request factory */
protected static $instance;
/** @var array Hash of methods available to the class (provides fast isset() lookups) */
protected $methods;
/** @var string Class to instantiate for requests with no body */
protected $requestClass = 'Guzzle\\Http\\Message\\Request';
/** @var string Class to instantiate for requests with a body */
protected $entityEnclosingRequestClass = 'Guzzle\\Http\\Message\\EntityEnclosingRequest';
/**
* Get a cached instance of the default request factory
*
* @return RequestFactory
*/
public static function getInstance()
{
// @codeCoverageIgnoreStart
if (!static::$instance) {
static::$instance = new static();
}
// @codeCoverageIgnoreEnd
return static::$instance;
}
public function __construct()
{
$this->methods = array_flip(get_class_methods(__CLASS__));
}
public function fromMessage($message)
{
$parsed = ParserRegistry::getInstance()->getParser('message')->parseRequest($message);
if (!$parsed) {
return false;
}
$request = $this->fromParts($parsed['method'], $parsed['request_url'],
$parsed['headers'], $parsed['body'], $parsed['protocol'],
$parsed['version']);
// EntityEnclosingRequest adds an "Expect: 100-Continue" header when using a raw request body for PUT or POST
// requests. This factory method should accurately reflect the message, so here we are removing the Expect
// header if one was not supplied in the message.
if (!isset($parsed['headers']['Expect']) && !isset($parsed['headers']['expect'])) {
$request->removeHeader('Expect');
}
return $request;
}
public function fromParts(
$method,
array $urlParts,
$headers = null,
$body = null,
$protocol = 'HTTP',
$protocolVersion = '1.1'
) {
return $this->create($method, Url::buildUrl($urlParts), $headers, $body)
->setProtocolVersion($protocolVersion);
}
public function create($method, $url, $headers = null, $body = null, array $options = array())
{
$method = strtoupper($method);
if ($method == 'GET' || $method == 'HEAD' || $method == 'TRACE' || $method == 'OPTIONS') {
// Handle non-entity-enclosing request methods
$request = new $this->requestClass($method, $url, $headers);
if ($body) {
// The body is where the response body will be stored
$type = gettype($body);
if ($type == 'string' || $type == 'resource' || $type == 'object') {
$request->setResponseBody($body);
}
}
} else {
// Create an entity enclosing request by default
$request = new $this->entityEnclosingRequestClass($method, $url, $headers);
if ($body) {
// Add POST fields and files to an entity enclosing request if an array is used
if (is_array($body) || $body instanceof Collection) {
// Normalize PHP style cURL uploads with a leading '@' symbol
foreach ($body as $key => $value) {
if (is_string($value) && substr($value, 0, 1) == '@') {
$request->addPostFile($key, $value);
unset($body[$key]);
}
}
// Add the fields if they are still present and not all files
$request->addPostFields($body);
} else {
// Add a raw entity body body to the request
$request->setBody($body, (string) $request->getHeader('Content-Type'));
if ((string) $request->getHeader('Transfer-Encoding') == 'chunked') {
$request->removeHeader('Content-Length');
}
}
}
}
if ($options) {
$this->applyOptions($request, $options);
}
return $request;
}
/**
* Clone a request while changing the method. Emulates the behavior of
* {@see Guzzle\Http\Message\Request::clone}, but can change the HTTP method.
*
* @param RequestInterface $request Request to clone
* @param string $method Method to set
*
* @return RequestInterface
*/
public function cloneRequestWithMethod(RequestInterface $request, $method)
{
// Create the request with the same client if possible
if ($client = $request->getClient()) {
$cloned = $request->getClient()->createRequest($method, $request->getUrl(), $request->getHeaders());
} else {
$cloned = $this->create($method, $request->getUrl(), $request->getHeaders());
}
$cloned->getCurlOptions()->replace($request->getCurlOptions()->toArray());
$cloned->setEventDispatcher(clone $request->getEventDispatcher());
// Ensure that that the Content-Length header is not copied if changing to GET or HEAD
if (!($cloned instanceof EntityEnclosingRequestInterface)) {
$cloned->removeHeader('Content-Length');
} elseif ($request instanceof EntityEnclosingRequestInterface) {
$cloned->setBody($request->getBody());
}
$cloned->getParams()->replace($request->getParams()->toArray());
$cloned->dispatch('request.clone', array('request' => $cloned));
return $cloned;
}
public function applyOptions(RequestInterface $request, array $options = array(), $flags = self::OPTIONS_NONE)
{
// Iterate over each key value pair and attempt to apply a config using function visitors
foreach ($options as $key => $value) {
$method = "visit_{$key}";
if (isset($this->methods[$method])) {
$this->{$method}($request, $value, $flags);
}
}
}
protected function visit_headers(RequestInterface $request, $value, $flags)
{
if (!is_array($value)) {
throw new InvalidArgumentException('headers value must be an array');
}
if ($flags & self::OPTIONS_AS_DEFAULTS) {
// Merge headers in but do not overwrite existing values
foreach ($value as $key => $header) {
if (!$request->hasHeader($key)) {
$request->setHeader($key, $header);
}
}
} else {
$request->addHeaders($value);
}
}
protected function visit_body(RequestInterface $request, $value, $flags)
{
if ($request instanceof EntityEnclosingRequestInterface) {
$request->setBody($value);
} else {
throw new InvalidArgumentException('Attempting to set a body on a non-entity-enclosing request');
}
}
protected function visit_allow_redirects(RequestInterface $request, $value, $flags)
{
if ($value === false) {
$request->getParams()->set(RedirectPlugin::DISABLE, true);
}
}
protected function visit_auth(RequestInterface $request, $value, $flags)
{
if (!is_array($value)) {
throw new InvalidArgumentException('auth value must be an array');
}
$request->setAuth($value[0], isset($value[1]) ? $value[1] : null, isset($value[2]) ? $value[2] : 'basic');
}
protected function visit_query(RequestInterface $request, $value, $flags)
{
if (!is_array($value)) {
throw new InvalidArgumentException('query value must be an array');
}
if ($flags & self::OPTIONS_AS_DEFAULTS) {
// Merge query string values in but do not overwrite existing values
$query = $request->getQuery();
$query->overwriteWith(array_diff_key($value, $query->toArray()));
} else {
$request->getQuery()->overwriteWith($value);
}
}
protected function visit_cookies(RequestInterface $request, $value, $flags)
{
if (!is_array($value)) {
throw new InvalidArgumentException('cookies value must be an array');
}
foreach ($value as $name => $v) {
$request->addCookie($name, $v);
}
}
protected function visit_events(RequestInterface $request, $value, $flags)
{
if (!is_array($value)) {
throw new InvalidArgumentException('events value must be an array');
}
foreach ($value as $name => $method) {
if (is_array($method)) {
$request->getEventDispatcher()->addListener($name, $method[0], $method[1]);
} else {
$request->getEventDispatcher()->addListener($name, $method);
}
}
}
protected function visit_plugins(RequestInterface $request, $value, $flags)
{
if (!is_array($value)) {
throw new InvalidArgumentException('plugins value must be an array');
}
foreach ($value as $plugin) {
$request->addSubscriber($plugin);
}
}
protected function visit_exceptions(RequestInterface $request, $value, $flags)
{
if ($value === false || $value === 0) {
$dispatcher = $request->getEventDispatcher();
foreach ($dispatcher->getListeners('request.error') as $listener) {
if ($listener[0] == 'Guzzle\Http\Message\Request' && $listener[1] = 'onRequestError') {
$dispatcher->removeListener('request.error', $listener);
break;
}
}
}
}
protected function visit_save_to(RequestInterface $request, $value, $flags)
{
$request->setResponseBody($value);
}
protected function visit_params(RequestInterface $request, $value, $flags)
{
if (!is_array($value)) {
throw new InvalidArgumentException('params value must be an array');
}
$request->getParams()->overwriteWith($value);
}
protected function visit_timeout(RequestInterface $request, $value, $flags)
{
$request->getCurlOptions()->set(CURLOPT_TIMEOUT_MS, $value * 1000);
}
protected function visit_connect_timeout(RequestInterface $request, $value, $flags)
{
$request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT_MS, $value * 1000);
}
protected function visit_debug(RequestInterface $request, $value, $flags)
{
if (class_exists('Guzzle\Plugin\Log\LogPlugin')) {
$request->addSubscriber(LogPlugin::getDebugPlugin());
} else {
// @codeCoverageIgnoreStart
$request->getCurlOptions()->set(CURLOPT_VERBOSE, true);
// @codeCoverageIgnoreEnd
}
}
protected function visit_verify(RequestInterface $request, $value, $flags)
{
$curl = $request->getCurlOptions();
if ($value === true || is_string($value)) {
$curl[CURLOPT_SSL_VERIFYHOST] = 2;
$curl[CURLOPT_SSL_VERIFYPEER] = true;
if ($value !== true) {
$curl[CURLOPT_CAINFO] = $value;
}
} elseif ($value === false) {
unset($curl[CURLOPT_CAINFO]);
$curl[CURLOPT_SSL_VERIFYHOST] = 0;
$curl[CURLOPT_SSL_VERIFYPEER] = false;
}
}
protected function visit_proxy(RequestInterface $request, $value, $flags)
{
$request->getCurlOptions()->set(CURLOPT_PROXY, $value, $flags);
}
protected function visit_cert(RequestInterface $request, $value, $flags)
{
if (is_array($value)) {
$request->getCurlOptions()->set(CURLOPT_SSLCERT, $value[0]);
$request->getCurlOptions()->set(CURLOPT_SSLCERTPASSWD, $value[1]);
} else {
$request->getCurlOptions()->set(CURLOPT_SSLCERT, $value);
}
}
protected function visit_ssl_key(RequestInterface $request, $value, $flags)
{
if (is_array($value)) {
$request->getCurlOptions()->set(CURLOPT_SSLKEY, $value[0]);
$request->getCurlOptions()->set(CURLOPT_SSLKEYPASSWD, $value[1]);
} else {
$request->getCurlOptions()->set(CURLOPT_SSLKEY, $value);
}
}
}

View File

@ -1,105 +0,0 @@
<?php
namespace Guzzle\Http\Message;
use Guzzle\Common\Collection;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Http\Url;
/**
* Request factory used to create HTTP requests
*/
interface RequestFactoryInterface
{
const OPTIONS_NONE = 0;
const OPTIONS_AS_DEFAULTS = 1;
/**
* Create a new request based on an HTTP message
*
* @param string $message HTTP message as a string
*
* @return RequestInterface
*/
public function fromMessage($message);
/**
* Create a request from URL parts as returned from parse_url()
*
* @param string $method HTTP method (GET, POST, PUT, HEAD, DELETE, etc)
*
* @param array $urlParts URL parts containing the same keys as parse_url()
* - scheme: e.g. http
* - host: e.g. www.guzzle-project.com
* - port: e.g. 80
* - user: e.g. michael
* - pass: e.g. rocks
* - path: e.g. / OR /index.html
* - query: after the question mark ?
* @param array|Collection $headers HTTP headers
* @param string|resource|array|EntityBodyInterface $body Body to send in the request
* @param string $protocol Protocol (HTTP, SPYDY, etc)
* @param string $protocolVersion 1.0, 1.1, etc
*
* @return RequestInterface
*/
public function fromParts(
$method,
array $urlParts,
$headers = null,
$body = null,
$protocol = 'HTTP',
$protocolVersion = '1.1'
);
/**
* Create a new request based on the HTTP method
*
* @param string $method HTTP method (GET, POST, PUT, PATCH, HEAD, DELETE, ...)
* @param string|Url $url HTTP URL to connect to
* @param array|Collection $headers HTTP headers
* @param string|resource|array|EntityBodyInterface $body Body to send in the request
* @param array $options Array of options to apply to the request
*
* @return RequestInterface
*/
public function create($method, $url, $headers = null, $body = null, array $options = array());
/**
* Apply an associative array of options to the request
*
* @param RequestInterface $request Request to update
* @param array $options Options to use with the request. Available options are:
* "headers": Associative array of headers
* "query": Associative array of query string values to add to the request
* "body": Body of a request, including an EntityBody, string, or array when sending POST requests.
* "auth": Array of HTTP authentication parameters to use with the request. The array must contain the
* username in index [0], the password in index [2], and can optionally contain the authentication type
* in index [3]. The authentication types are: "Basic", "Digest", "NTLM", "Any" (defaults to "Basic").
* "cookies": Associative array of cookies
* "allow_redirects": Set to false to disable redirects
* "save_to": String, fopen resource, or EntityBody object used to store the body of the response
* "events": Associative array mapping event names to a closure or array of (priority, closure)
* "plugins": Array of plugins to add to the request
* "exceptions": Set to false to disable throwing exceptions on an HTTP level error (e.g. 404, 500, etc)
* "params": Set custom request data parameters on a request. (Note: these are not query string parameters)
* "timeout": Float describing the timeout of the request in seconds
* "connect_timeout": Float describing the number of seconds to wait while trying to connect. Use 0 to wait
* indefinitely.
* "verify": Set to true to enable SSL cert validation (the default), false to disable, or supply the path
* to a CA bundle to enable verification using a custom certificate.
* "cert": Set to a string to specify the path to a file containing a PEM formatted certificate. If a
* password is required, then set an array containing the path to the PEM file followed by the the
* password required for the certificate.
* "ssl_key": Specify the path to a file containing a private SSL key in PEM format. If a password is
* required, then set an array containing the path to the SSL key followed by the password required for
* the certificate.
* "proxy": Specify an HTTP proxy (e.g. "http://username:password@192.168.16.1:10")
* "debug": Set to true to display all data sent over the wire
* @param int $flags Bitwise flags to apply when applying the options to the request. Defaults to no special
* options. `1` (OPTIONS_AS_DEFAULTS): When specified, options will only update a request when
* the value does not already exist on the request. This is only supported by "query" and
* "headers". Other bitwise options may be added in the future.
*/
public function applyOptions(RequestInterface $request, array $options = array(), $flags = self::OPTIONS_NONE);
}

View File

@ -1,318 +0,0 @@
<?php
namespace Guzzle\Http\Message;
use Guzzle\Common\Collection;
use Guzzle\Common\HasDispatcherInterface;
use Guzzle\Http\Exception\RequestException;
use Guzzle\Http\ClientInterface;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Http\Url;
use Guzzle\Http\QueryString;
/**
* Generic HTTP request interface
*/
interface RequestInterface extends MessageInterface, HasDispatcherInterface
{
const STATE_NEW = 'new';
const STATE_COMPLETE = 'complete';
const STATE_TRANSFER = 'transfer';
const STATE_ERROR = 'error';
const GET = 'GET';
const PUT = 'PUT';
const POST = 'POST';
const DELETE = 'DELETE';
const HEAD = 'HEAD';
const CONNECT = 'CONNECT';
const OPTIONS = 'OPTIONS';
const TRACE = 'TRACE';
const PATCH = 'PATCH';
/**
* @return string
*/
public function __toString();
/**
* Send the request
*
* @return Response
* @throws RequestException on a request error
*/
public function send();
/**
* Set the client used to transport the request
*
* @param ClientInterface $client
*
* @return self
*/
public function setClient(ClientInterface $client);
/**
* Get the client used to transport the request
*
* @return ClientInterface $client
*/
public function getClient();
/**
* Set the URL of the request
*
* @param string $url|Url Full URL to set including query string
*
* @return self
*/
public function setUrl($url);
/**
* Get the full URL of the request (e.g. 'http://www.guzzle-project.com/')
*
* @param bool $asObject Set to TRUE to retrieve the URL as a clone of the URL object owned by the request.
*
* @return string|Url
*/
public function getUrl($asObject = false);
/**
* Get the resource part of the the request, including the path, query string, and fragment
*
* @return string
*/
public function getResource();
/**
* Get the collection of key value pairs that will be used as the query string in the request
*
* @return QueryString
*/
public function getQuery();
/**
* Get the HTTP method of the request
*
* @return string
*/
public function getMethod();
/**
* Get the URI scheme of the request (http, https, ftp, etc)
*
* @return string
*/
public function getScheme();
/**
* Set the URI scheme of the request (http, https, ftp, etc)
*
* @param string $scheme Scheme to set
*
* @return self
*/
public function setScheme($scheme);
/**
* Get the host of the request
*
* @return string
*/
public function getHost();
/**
* Set the host of the request. Including a port in the host will modify the port of the request.
*
* @param string $host Host to set (e.g. www.yahoo.com, www.yahoo.com:80)
*
* @return self
*/
public function setHost($host);
/**
* Get the path of the request (e.g. '/', '/index.html')
*
* @return string
*/
public function getPath();
/**
* Set the path of the request (e.g. '/', '/index.html')
*
* @param string|array $path Path to set or array of segments to implode
*
* @return self
*/
public function setPath($path);
/**
* Get the port that the request will be sent on if it has been set
*
* @return int|null
*/
public function getPort();
/**
* Set the port that the request will be sent on
*
* @param int $port Port number to set
*
* @return self
*/
public function setPort($port);
/**
* Get the username to pass in the URL if set
*
* @return string|null
*/
public function getUsername();
/**
* Get the password to pass in the URL if set
*
* @return string|null
*/
public function getPassword();
/**
* Set HTTP authorization parameters
*
* @param string|bool $user User name or false disable authentication
* @param string $password Password
* @param string $scheme Authentication scheme ('Basic', 'Digest', or a CURLAUTH_* constant (deprecated))
*
* @return self
* @link http://www.ietf.org/rfc/rfc2617.txt
* @link http://php.net/manual/en/function.curl-setopt.php See the available options for CURLOPT_HTTPAUTH
* @throws RequestException
*/
public function setAuth($user, $password = '', $scheme = 'Basic');
/**
* Get the HTTP protocol version of the request
*
* @return string
*/
public function getProtocolVersion();
/**
* Set the HTTP protocol version of the request (e.g. 1.1 or 1.0)
*
* @param string $protocol HTTP protocol version to use with the request
*
* @return self
*/
public function setProtocolVersion($protocol);
/**
* Get the previously received {@see Response} or NULL if the request has not been sent
*
* @return Response|null
*/
public function getResponse();
/**
* Manually set a response for the request.
*
* This method is useful for specifying a mock response for the request or setting the response using a cache.
* Manually setting a response will bypass the actual sending of a request.
*
* @param Response $response Response object to set
* @param bool $queued Set to TRUE to keep the request in a state of not having been sent, but queue the
* response for send()
*
* @return self Returns a reference to the object.
*/
public function setResponse(Response $response, $queued = false);
/**
* The start of a response has been received for a request and the request is still in progress
*
* @param Response $response Response that has been received so far
*
* @return self
*/
public function startResponse(Response $response);
/**
* Set the EntityBody that will hold a successful response message's entity body.
*
* This method should be invoked when you need to send the response's entity body somewhere other than the normal
* php://temp buffer. For example, you can send the entity body to a socket, file, or some other custom stream.
*
* @param EntityBodyInterface|string|resource $body Response body object. Pass a string to attempt to store the
* response body in a local file.
* @return Request
*/
public function setResponseBody($body);
/**
* Get the EntityBody that will hold the resulting response message's entity body. This response body will only
* be used for successful responses. Intermediate responses (e.g. redirects) will not use the targeted response
* body.
*
* @return EntityBodyInterface
*/
public function getResponseBody();
/**
* Get the state of the request. One of 'complete', 'transfer', 'new', 'error'
*
* @return string
*/
public function getState();
/**
* Set the state of the request
*
* @param string $state State of the request ('complete', 'transfer', 'new', 'error')
* @param array $context Contextual information about the state change
*
* @return string Returns the current state of the request (which may have changed due to events being fired)
*/
public function setState($state, array $context = array());
/**
* Get the cURL options that will be applied when the cURL handle is created
*
* @return Collection
*/
public function getCurlOptions();
/**
* Get an array of Cookies
*
* @return array
*/
public function getCookies();
/**
* Get a cookie value by name
*
* @param string $name Cookie to retrieve
*
* @return null|string
*/
public function getCookie($name);
/**
* Add a Cookie value by name to the Cookie header
*
* @param string $name Name of the cookie to add
* @param string $value Value to set
*
* @return self
*/
public function addCookie($name, $value);
/**
* Remove a specific cookie value by name
*
* @param string $name Cookie to remove by name
*
* @return self
*/
public function removeCookie($name);
}

View File

@ -1,948 +0,0 @@
<?php
namespace Guzzle\Http\Message;
use Guzzle\Common\Version;
use Guzzle\Common\ToArrayInterface;
use Guzzle\Common\Exception\RuntimeException;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Http\EntityBody;
use Guzzle\Http\Exception\BadResponseException;
use Guzzle\Http\RedirectPlugin;
use Guzzle\Parser\ParserRegistry;
/**
* Guzzle HTTP response object
*/
class Response extends AbstractMessage implements \Serializable
{
/**
* @var array Array of reason phrases and their corresponding status codes
*/
private static $statusTexts = array(
100 => 'Continue',
101 => 'Switching Protocols',
102 => 'Processing',
200 => 'OK',
201 => 'Created',
202 => 'Accepted',
203 => 'Non-Authoritative Information',
204 => 'No Content',
205 => 'Reset Content',
206 => 'Partial Content',
207 => 'Multi-Status',
208 => 'Already Reported',
226 => 'IM Used',
300 => 'Multiple Choices',
301 => 'Moved Permanently',
302 => 'Found',
303 => 'See Other',
304 => 'Not Modified',
305 => 'Use Proxy',
307 => 'Temporary Redirect',
308 => 'Permanent Redirect',
400 => 'Bad Request',
401 => 'Unauthorized',
402 => 'Payment Required',
403 => 'Forbidden',
404 => 'Not Found',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
407 => 'Proxy Authentication Required',
408 => 'Request Timeout',
409 => 'Conflict',
410 => 'Gone',
411 => 'Length Required',
412 => 'Precondition Failed',
413 => 'Request Entity Too Large',
414 => 'Request-URI Too Long',
415 => 'Unsupported Media Type',
416 => 'Requested Range Not Satisfiable',
417 => 'Expectation Failed',
422 => 'Unprocessable Entity',
423 => 'Locked',
424 => 'Failed Dependency',
425 => 'Reserved for WebDAV advanced collections expired proposal',
426 => 'Upgrade required',
428 => 'Precondition Required',
429 => 'Too Many Requests',
431 => 'Request Header Fields Too Large',
500 => 'Internal Server Error',
501 => 'Not Implemented',
502 => 'Bad Gateway',
503 => 'Service Unavailable',
504 => 'Gateway Timeout',
505 => 'HTTP Version Not Supported',
506 => 'Variant Also Negotiates (Experimental)',
507 => 'Insufficient Storage',
508 => 'Loop Detected',
510 => 'Not Extended',
511 => 'Network Authentication Required',
);
/** @var EntityBodyInterface The response body */
protected $body;
/** @var string The reason phrase of the response (human readable code) */
protected $reasonPhrase;
/** @var string The status code of the response */
protected $statusCode;
/** @var array Information about the request */
protected $info = array();
/** @var string The effective URL that returned this response */
protected $effectiveUrl;
/** @var array Cacheable response codes (see RFC 2616:13.4) */
protected static $cacheResponseCodes = array(200, 203, 206, 300, 301, 410);
/**
* Create a new Response based on a raw response message
*
* @param string $message Response message
*
* @return self|bool Returns false on error
*/
public static function fromMessage($message)
{
$data = ParserRegistry::getInstance()->getParser('message')->parseResponse($message);
if (!$data) {
return false;
}
$response = new static($data['code'], $data['headers'], $data['body']);
$response->setProtocol($data['protocol'], $data['version'])
->setStatus($data['code'], $data['reason_phrase']);
// Set the appropriate Content-Length if the one set is inaccurate (e.g. setting to X)
$contentLength = (string) $response->getHeader('Content-Length');
$actualLength = strlen($data['body']);
if (strlen($data['body']) > 0 && $contentLength != $actualLength) {
$response->setHeader('Content-Length', $actualLength);
}
return $response;
}
/**
* Construct the response
*
* @param string $statusCode The response status code (e.g. 200, 404, etc)
* @param ToArrayInterface|array $headers The response headers
* @param string|resource|EntityBodyInterface $body The body of the response
*
* @throws BadResponseException if an invalid response code is given
*/
public function __construct($statusCode, $headers = null, $body = null)
{
parent::__construct();
$this->setStatus($statusCode);
$this->body = EntityBody::factory($body !== null ? $body : '');
if ($headers) {
if (is_array($headers)) {
$this->setHeaders($headers);
} elseif ($headers instanceof ToArrayInterface) {
$this->setHeaders($headers->toArray());
} else {
throw new BadResponseException('Invalid headers argument received');
}
}
}
/**
* @return string
*/
public function __toString()
{
return $this->getMessage();
}
public function serialize()
{
return json_encode(array(
'status' => $this->statusCode,
'body' => (string) $this->body,
'headers' => $this->headers->toArray()
));
}
public function unserialize($serialize)
{
$data = json_decode($serialize, true);
$this->__construct($data['status'], $data['headers'], $data['body']);
}
/**
* Get the response entity body
*
* @param bool $asString Set to TRUE to return a string of the body rather than a full body object
*
* @return EntityBodyInterface|string
*/
public function getBody($asString = false)
{
return $asString ? (string) $this->body : $this->body;
}
/**
* Set the response entity body
*
* @param EntityBodyInterface|string $body Body to set
*
* @return self
*/
public function setBody($body)
{
$this->body = EntityBody::factory($body);
return $this;
}
/**
* Set the protocol and protocol version of the response
*
* @param string $protocol Response protocol
* @param string $version Protocol version
*
* @return self
*/
public function setProtocol($protocol, $version)
{
$this->protocol = $protocol;
$this->protocolVersion = $version;
return $this;
}
/**
* Get the protocol used for the response (e.g. HTTP)
*
* @return string
*/
public function getProtocol()
{
return $this->protocol;
}
/**
* Get the HTTP protocol version
*
* @return string
*/
public function getProtocolVersion()
{
return $this->protocolVersion;
}
/**
* Get a cURL transfer information
*
* @param string $key A single statistic to check
*
* @return array|string|null Returns all stats if no key is set, a single stat if a key is set, or null if a key
* is set and not found
* @link http://www.php.net/manual/en/function.curl-getinfo.php
*/
public function getInfo($key = null)
{
if ($key === null) {
return $this->info;
} elseif (array_key_exists($key, $this->info)) {
return $this->info[$key];
} else {
return null;
}
}
/**
* Set the transfer information
*
* @param array $info Array of cURL transfer stats
*
* @return self
*/
public function setInfo(array $info)
{
$this->info = $info;
return $this;
}
/**
* Set the response status
*
* @param int $statusCode Response status code to set
* @param string $reasonPhrase Response reason phrase
*
* @return self
* @throws BadResponseException when an invalid response code is received
*/
public function setStatus($statusCode, $reasonPhrase = '')
{
$this->statusCode = (int) $statusCode;
if (!$reasonPhrase && isset(self::$statusTexts[$this->statusCode])) {
$this->reasonPhrase = self::$statusTexts[$this->statusCode];
} else {
$this->reasonPhrase = $reasonPhrase;
}
return $this;
}
/**
* Get the response status code
*
* @return integer
*/
public function getStatusCode()
{
return $this->statusCode;
}
/**
* Get the entire response as a string
*
* @return string
*/
public function getMessage()
{
$message = $this->getRawHeaders();
// Only include the body in the message if the size is < 2MB
$size = $this->body->getSize();
if ($size < 2097152) {
$message .= (string) $this->body;
}
return $message;
}
/**
* Get the the raw message headers as a string
*
* @return string
*/
public function getRawHeaders()
{
$headers = 'HTTP/1.1 ' . $this->statusCode . ' ' . $this->reasonPhrase . "\r\n";
$lines = $this->getHeaderLines();
if (!empty($lines)) {
$headers .= implode("\r\n", $lines) . "\r\n";
}
return $headers . "\r\n";
}
/**
* Get the response reason phrase- a human readable version of the numeric
* status code
*
* @return string
*/
public function getReasonPhrase()
{
return $this->reasonPhrase;
}
/**
* Get the Accept-Ranges HTTP header
*
* @return string Returns what partial content range types this server supports.
*/
public function getAcceptRanges()
{
return (string) $this->getHeader('Accept-Ranges');
}
/**
* Calculate the age of the response
*
* @return integer
*/
public function calculateAge()
{
$age = $this->getHeader('Age');
if ($age === null && $this->getDate()) {
$age = time() - strtotime($this->getDate());
}
return $age === null ? null : (int) (string) $age;
}
/**
* Get the Age HTTP header
*
* @return integer|null Returns the age the object has been in a proxy cache in seconds.
*/
public function getAge()
{
return (string) $this->getHeader('Age');
}
/**
* Get the Allow HTTP header
*
* @return string|null Returns valid actions for a specified resource. To be used for a 405 Method not allowed.
*/
public function getAllow()
{
return (string) $this->getHeader('Allow');
}
/**
* Check if an HTTP method is allowed by checking the Allow response header
*
* @param string $method Method to check
*
* @return bool
*/
public function isMethodAllowed($method)
{
$allow = $this->getHeader('Allow');
if ($allow) {
foreach (explode(',', $allow) as $allowable) {
if (!strcasecmp(trim($allowable), $method)) {
return true;
}
}
}
return false;
}
/**
* Get the Cache-Control HTTP header
*
* @return string
*/
public function getCacheControl()
{
return (string) $this->getHeader('Cache-Control');
}
/**
* Get the Connection HTTP header
*
* @return string
*/
public function getConnection()
{
return (string) $this->getHeader('Connection');
}
/**
* Get the Content-Encoding HTTP header
*
* @return string|null
*/
public function getContentEncoding()
{
return (string) $this->getHeader('Content-Encoding');
}
/**
* Get the Content-Language HTTP header
*
* @return string|null Returns the language the content is in.
*/
public function getContentLanguage()
{
return (string) $this->getHeader('Content-Language');
}
/**
* Get the Content-Length HTTP header
*
* @return integer Returns the length of the response body in bytes
*/
public function getContentLength()
{
return (int) (string) $this->getHeader('Content-Length');
}
/**
* Get the Content-Location HTTP header
*
* @return string|null Returns an alternate location for the returned data (e.g /index.htm)
*/
public function getContentLocation()
{
return (string) $this->getHeader('Content-Location');
}
/**
* Get the Content-Disposition HTTP header
*
* @return string|null Returns the Content-Disposition header
*/
public function getContentDisposition()
{
return (string) $this->getHeader('Content-Disposition');
}
/**
* Get the Content-MD5 HTTP header
*
* @return string|null Returns a Base64-encoded binary MD5 sum of the content of the response.
*/
public function getContentMd5()
{
return (string) $this->getHeader('Content-MD5');
}
/**
* Get the Content-Range HTTP header
*
* @return string Returns where in a full body message this partial message belongs (e.g. bytes 21010-47021/47022).
*/
public function getContentRange()
{
return (string) $this->getHeader('Content-Range');
}
/**
* Get the Content-Type HTTP header
*
* @return string Returns the mime type of this content.
*/
public function getContentType()
{
return (string) $this->getHeader('Content-Type');
}
/**
* Checks if the Content-Type is of a certain type. This is useful if the
* Content-Type header contains charset information and you need to know if
* the Content-Type matches a particular type.
*
* @param string $type Content type to check against
*
* @return bool
*/
public function isContentType($type)
{
return stripos($this->getHeader('Content-Type'), $type) !== false;
}
/**
* Get the Date HTTP header
*
* @return string|null Returns the date and time that the message was sent.
*/
public function getDate()
{
return (string) $this->getHeader('Date');
}
/**
* Get the ETag HTTP header
*
* @return string|null Returns an identifier for a specific version of a resource, often a Message digest.
*/
public function getEtag()
{
return (string) $this->getHeader('ETag');
}
/**
* Get the Expires HTTP header
*
* @return string|null Returns the date/time after which the response is considered stale.
*/
public function getExpires()
{
return (string) $this->getHeader('Expires');
}
/**
* Get the Last-Modified HTTP header
*
* @return string|null Returns the last modified date for the requested object, in RFC 2822 format
* (e.g. Tue, 15 Nov 1994 12:45:26 GMT)
*/
public function getLastModified()
{
return (string) $this->getHeader('Last-Modified');
}
/**
* Get the Location HTTP header
*
* @return string|null Used in redirection, or when a new resource has been created.
*/
public function getLocation()
{
return (string) $this->getHeader('Location');
}
/**
* Get the Pragma HTTP header
*
* @return Header|null Returns the implementation-specific headers that may have various effects anywhere along
* the request-response chain.
*/
public function getPragma()
{
return (string) $this->getHeader('Pragma');
}
/**
* Get the Proxy-Authenticate HTTP header
*
* @return string|null Authentication to access the proxy (e.g. Basic)
*/
public function getProxyAuthenticate()
{
return (string) $this->getHeader('Proxy-Authenticate');
}
/**
* Get the Retry-After HTTP header
*
* @return int|null If an entity is temporarily unavailable, this instructs the client to try again after a
* specified period of time.
*/
public function getRetryAfter()
{
return (string) $this->getHeader('Retry-After');
}
/**
* Get the Server HTTP header
*
* @return string|null A name for the server
*/
public function getServer()
{
return (string) $this->getHeader('Server');
}
/**
* Get the Set-Cookie HTTP header
*
* @return string|null An HTTP cookie.
*/
public function getSetCookie()
{
return (string) $this->getHeader('Set-Cookie');
}
/**
* Get the Trailer HTTP header
*
* @return string|null The Trailer general field value indicates that the given set of header fields is present in
* the trailer of a message encoded with chunked transfer-coding.
*/
public function getTrailer()
{
return (string) $this->getHeader('Trailer');
}
/**
* Get the Transfer-Encoding HTTP header
*
* @return string|null The form of encoding used to safely transfer the entity to the user
*/
public function getTransferEncoding()
{
return (string) $this->getHeader('Transfer-Encoding');
}
/**
* Get the Vary HTTP header
*
* @return string|null Tells downstream proxies how to match future request headers to decide whether the cached
* response can be used rather than requesting a fresh one from the origin server.
*/
public function getVary()
{
return (string) $this->getHeader('Vary');
}
/**
* Get the Via HTTP header
*
* @return string|null Informs the client of proxies through which the response was sent.
*/
public function getVia()
{
return (string) $this->getHeader('Via');
}
/**
* Get the Warning HTTP header
*
* @return string|null A general warning about possible problems with the entity body
*/
public function getWarning()
{
return (string) $this->getHeader('Warning');
}
/**
* Get the WWW-Authenticate HTTP header
*
* @return string|null Indicates the authentication scheme that should be used to access the requested entity
*/
public function getWwwAuthenticate()
{
return (string) $this->getHeader('WWW-Authenticate');
}
/**
* Checks if HTTP Status code is a Client Error (4xx)
*
* @return bool
*/
public function isClientError()
{
return $this->statusCode >= 400 && $this->statusCode < 500;
}
/**
* Checks if HTTP Status code is Server OR Client Error (4xx or 5xx)
*
* @return boolean
*/
public function isError()
{
return $this->isClientError() || $this->isServerError();
}
/**
* Checks if HTTP Status code is Information (1xx)
*
* @return bool
*/
public function isInformational()
{
return $this->statusCode < 200;
}
/**
* Checks if HTTP Status code is a Redirect (3xx)
*
* @return bool
*/
public function isRedirect()
{
return $this->statusCode >= 300 && $this->statusCode < 400;
}
/**
* Checks if HTTP Status code is Server Error (5xx)
*
* @return bool
*/
public function isServerError()
{
return $this->statusCode >= 500 && $this->statusCode < 600;
}
/**
* Checks if HTTP Status code is Successful (2xx | 304)
*
* @return bool
*/
public function isSuccessful()
{
return ($this->statusCode >= 200 && $this->statusCode < 300) || $this->statusCode == 304;
}
/**
* Check if the response can be cached based on the response headers
*
* @return bool Returns TRUE if the response can be cached or false if not
*/
public function canCache()
{
// Check if the response is cacheable based on the code
if (!in_array((int) $this->getStatusCode(), self::$cacheResponseCodes)) {
return false;
}
// Make sure a valid body was returned and can be cached
if ((!$this->getBody()->isReadable() || !$this->getBody()->isSeekable())
&& ($this->getContentLength() > 0 || $this->getTransferEncoding() == 'chunked')) {
return false;
}
// Never cache no-store resources (this is a private cache, so private
// can be cached)
if ($this->getHeader('Cache-Control') && $this->getHeader('Cache-Control')->hasDirective('no-store')) {
return false;
}
return $this->isFresh() || $this->getFreshness() === null || $this->canValidate();
}
/**
* Gets the number of seconds from the current time in which this response is still considered fresh
*
* @return int|null Returns the number of seconds
*/
public function getMaxAge()
{
if ($header = $this->getHeader('Cache-Control')) {
// s-max-age, then max-age, then Expires
if ($age = $header->getDirective('s-maxage')) {
return $age;
}
if ($age = $header->getDirective('max-age')) {
return $age;
}
}
if ($this->getHeader('Expires')) {
return strtotime($this->getExpires()) - time();
}
return null;
}
/**
* Check if the response is considered fresh.
*
* A response is considered fresh when its age is less than or equal to the freshness lifetime (maximum age) of the
* response.
*
* @return bool|null
*/
public function isFresh()
{
$fresh = $this->getFreshness();
return $fresh === null ? null : $fresh >= 0;
}
/**
* Check if the response can be validated against the origin server using a conditional GET request.
*
* @return bool
*/
public function canValidate()
{
return $this->getEtag() || $this->getLastModified();
}
/**
* Get the freshness of the response by returning the difference of the maximum lifetime of the response and the
* age of the response (max-age - age).
*
* Freshness values less than 0 mean that the response is no longer fresh and is ABS(freshness) seconds expired.
* Freshness values of greater than zero is the number of seconds until the response is no longer fresh. A NULL
* result means that no freshness information is available.
*
* @return int
*/
public function getFreshness()
{
$maxAge = $this->getMaxAge();
$age = $this->calculateAge();
return $maxAge && $age ? ($maxAge - $age) : null;
}
/**
* Parse the JSON response body and return an array
*
* @return array|string|int|bool|float
* @throws RuntimeException if the response body is not in JSON format
*/
public function json()
{
$data = json_decode((string) $this->body, true);
if (JSON_ERROR_NONE !== json_last_error()) {
throw new RuntimeException('Unable to parse response body into JSON: ' . json_last_error());
}
return $data === null ? array() : $data;
}
/**
* Parse the XML response body and return a SimpleXMLElement
*
* @return \SimpleXMLElement
* @throws RuntimeException if the response body is not in XML format
*/
public function xml()
{
try {
// Allow XML to be retrieved even if there is no response body
$xml = new \SimpleXMLElement((string) $this->body ?: '<root />');
} catch (\Exception $e) {
throw new RuntimeException('Unable to parse response body into XML: ' . $e->getMessage());
}
return $xml;
}
/**
* Get the redirect count of this response
*
* @return int
*/
public function getRedirectCount()
{
return (int) $this->params->get(RedirectPlugin::REDIRECT_COUNT);
}
/**
* Set the effective URL that resulted in this response (e.g. the last redirect URL)
*
* @param string $url The effective URL
*
* @return self
*/
public function setEffectiveUrl($url)
{
$this->effectiveUrl = $url;
return $this;
}
/**
* Get the effective URL that resulted in this response (e.g. the last redirect URL)
*
* @return string
*/
public function getEffectiveUrl()
{
return $this->effectiveUrl;
}
/**
* @deprecated
* @codeCoverageIgnore
*/
public function getPreviousResponse()
{
Version::warn(__METHOD__ . ' is deprecated. Use the HistoryPlugin.');
return null;
}
/**
* @deprecated
* @codeCoverageIgnore
*/
public function setRequest($request)
{
Version::warn(__METHOD__ . ' is deprecated');
return $this;
}
/**
* @deprecated
* @codeCoverageIgnore
*/
public function getRequest()
{
Version::warn(__METHOD__ . ' is deprecated');
return null;
}
}

View File

@ -1,960 +0,0 @@
<?php
namespace Guzzle\Http;
/**
* Provides mappings of file extensions to mimetypes
* @link http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types
*/
class Mimetypes
{
/** @var self */
protected static $instance;
/** @var array Mapping of extension to mimetype */
protected $mimetypes = array(
'3dml' => 'text/vnd.in3d.3dml',
'3g2' => 'video/3gpp2',
'3gp' => 'video/3gpp',
'7z' => 'application/x-7z-compressed',
'aab' => 'application/x-authorware-bin',
'aac' => 'audio/x-aac',
'aam' => 'application/x-authorware-map',
'aas' => 'application/x-authorware-seg',
'abw' => 'application/x-abiword',
'ac' => 'application/pkix-attr-cert',
'acc' => 'application/vnd.americandynamics.acc',
'ace' => 'application/x-ace-compressed',
'acu' => 'application/vnd.acucobol',
'acutc' => 'application/vnd.acucorp',
'adp' => 'audio/adpcm',
'aep' => 'application/vnd.audiograph',
'afm' => 'application/x-font-type1',
'afp' => 'application/vnd.ibm.modcap',
'ahead' => 'application/vnd.ahead.space',
'ai' => 'application/postscript',
'aif' => 'audio/x-aiff',
'aifc' => 'audio/x-aiff',
'aiff' => 'audio/x-aiff',
'air' => 'application/vnd.adobe.air-application-installer-package+zip',
'ait' => 'application/vnd.dvb.ait',
'ami' => 'application/vnd.amiga.ami',
'apk' => 'application/vnd.android.package-archive',
'application' => 'application/x-ms-application',
'apr' => 'application/vnd.lotus-approach',
'asa' => 'text/plain',
'asax' => 'application/octet-stream',
'asc' => 'application/pgp-signature',
'ascx' => 'text/plain',
'asf' => 'video/x-ms-asf',
'ashx' => 'text/plain',
'asm' => 'text/x-asm',
'asmx' => 'text/plain',
'aso' => 'application/vnd.accpac.simply.aso',
'asp' => 'text/plain',
'aspx' => 'text/plain',
'asx' => 'video/x-ms-asf',
'atc' => 'application/vnd.acucorp',
'atom' => 'application/atom+xml',
'atomcat' => 'application/atomcat+xml',
'atomsvc' => 'application/atomsvc+xml',
'atx' => 'application/vnd.antix.game-component',
'au' => 'audio/basic',
'avi' => 'video/x-msvideo',
'aw' => 'application/applixware',
'axd' => 'text/plain',
'azf' => 'application/vnd.airzip.filesecure.azf',
'azs' => 'application/vnd.airzip.filesecure.azs',
'azw' => 'application/vnd.amazon.ebook',
'bat' => 'application/x-msdownload',
'bcpio' => 'application/x-bcpio',
'bdf' => 'application/x-font-bdf',
'bdm' => 'application/vnd.syncml.dm+wbxml',
'bed' => 'application/vnd.realvnc.bed',
'bh2' => 'application/vnd.fujitsu.oasysprs',
'bin' => 'application/octet-stream',
'bmi' => 'application/vnd.bmi',
'bmp' => 'image/bmp',
'book' => 'application/vnd.framemaker',
'box' => 'application/vnd.previewsystems.box',
'boz' => 'application/x-bzip2',
'bpk' => 'application/octet-stream',
'btif' => 'image/prs.btif',
'bz' => 'application/x-bzip',
'bz2' => 'application/x-bzip2',
'c' => 'text/x-c',
'c11amc' => 'application/vnd.cluetrust.cartomobile-config',
'c11amz' => 'application/vnd.cluetrust.cartomobile-config-pkg',
'c4d' => 'application/vnd.clonk.c4group',
'c4f' => 'application/vnd.clonk.c4group',
'c4g' => 'application/vnd.clonk.c4group',
'c4p' => 'application/vnd.clonk.c4group',
'c4u' => 'application/vnd.clonk.c4group',
'cab' => 'application/vnd.ms-cab-compressed',
'car' => 'application/vnd.curl.car',
'cat' => 'application/vnd.ms-pki.seccat',
'cc' => 'text/x-c',
'cct' => 'application/x-director',
'ccxml' => 'application/ccxml+xml',
'cdbcmsg' => 'application/vnd.contact.cmsg',
'cdf' => 'application/x-netcdf',
'cdkey' => 'application/vnd.mediastation.cdkey',
'cdmia' => 'application/cdmi-capability',
'cdmic' => 'application/cdmi-container',
'cdmid' => 'application/cdmi-domain',
'cdmio' => 'application/cdmi-object',
'cdmiq' => 'application/cdmi-queue',
'cdx' => 'chemical/x-cdx',
'cdxml' => 'application/vnd.chemdraw+xml',
'cdy' => 'application/vnd.cinderella',
'cer' => 'application/pkix-cert',
'cfc' => 'application/x-coldfusion',
'cfm' => 'application/x-coldfusion',
'cgm' => 'image/cgm',
'chat' => 'application/x-chat',
'chm' => 'application/vnd.ms-htmlhelp',
'chrt' => 'application/vnd.kde.kchart',
'cif' => 'chemical/x-cif',
'cii' => 'application/vnd.anser-web-certificate-issue-initiation',
'cil' => 'application/vnd.ms-artgalry',
'cla' => 'application/vnd.claymore',
'class' => 'application/java-vm',
'clkk' => 'application/vnd.crick.clicker.keyboard',
'clkp' => 'application/vnd.crick.clicker.palette',
'clkt' => 'application/vnd.crick.clicker.template',
'clkw' => 'application/vnd.crick.clicker.wordbank',
'clkx' => 'application/vnd.crick.clicker',
'clp' => 'application/x-msclip',
'cmc' => 'application/vnd.cosmocaller',
'cmdf' => 'chemical/x-cmdf',
'cml' => 'chemical/x-cml',
'cmp' => 'application/vnd.yellowriver-custom-menu',
'cmx' => 'image/x-cmx',
'cod' => 'application/vnd.rim.cod',
'com' => 'application/x-msdownload',
'conf' => 'text/plain',
'cpio' => 'application/x-cpio',
'cpp' => 'text/x-c',
'cpt' => 'application/mac-compactpro',
'crd' => 'application/x-mscardfile',
'crl' => 'application/pkix-crl',
'crt' => 'application/x-x509-ca-cert',
'cryptonote' => 'application/vnd.rig.cryptonote',
'cs' => 'text/plain',
'csh' => 'application/x-csh',
'csml' => 'chemical/x-csml',
'csp' => 'application/vnd.commonspace',
'css' => 'text/css',
'cst' => 'application/x-director',
'csv' => 'text/csv',
'cu' => 'application/cu-seeme',
'curl' => 'text/vnd.curl',
'cww' => 'application/prs.cww',
'cxt' => 'application/x-director',
'cxx' => 'text/x-c',
'dae' => 'model/vnd.collada+xml',
'daf' => 'application/vnd.mobius.daf',
'dataless' => 'application/vnd.fdsn.seed',
'davmount' => 'application/davmount+xml',
'dcr' => 'application/x-director',
'dcurl' => 'text/vnd.curl.dcurl',
'dd2' => 'application/vnd.oma.dd2+xml',
'ddd' => 'application/vnd.fujixerox.ddd',
'deb' => 'application/x-debian-package',
'def' => 'text/plain',
'deploy' => 'application/octet-stream',
'der' => 'application/x-x509-ca-cert',
'dfac' => 'application/vnd.dreamfactory',
'dic' => 'text/x-c',
'dir' => 'application/x-director',
'dis' => 'application/vnd.mobius.dis',
'dist' => 'application/octet-stream',
'distz' => 'application/octet-stream',
'djv' => 'image/vnd.djvu',
'djvu' => 'image/vnd.djvu',
'dll' => 'application/x-msdownload',
'dmg' => 'application/octet-stream',
'dms' => 'application/octet-stream',
'dna' => 'application/vnd.dna',
'doc' => 'application/msword',
'docm' => 'application/vnd.ms-word.document.macroenabled.12',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'dot' => 'application/msword',
'dotm' => 'application/vnd.ms-word.template.macroenabled.12',
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
'dp' => 'application/vnd.osgi.dp',
'dpg' => 'application/vnd.dpgraph',
'dra' => 'audio/vnd.dra',
'dsc' => 'text/prs.lines.tag',
'dssc' => 'application/dssc+der',
'dtb' => 'application/x-dtbook+xml',
'dtd' => 'application/xml-dtd',
'dts' => 'audio/vnd.dts',
'dtshd' => 'audio/vnd.dts.hd',
'dump' => 'application/octet-stream',
'dvi' => 'application/x-dvi',
'dwf' => 'model/vnd.dwf',
'dwg' => 'image/vnd.dwg',
'dxf' => 'image/vnd.dxf',
'dxp' => 'application/vnd.spotfire.dxp',
'dxr' => 'application/x-director',
'ecelp4800' => 'audio/vnd.nuera.ecelp4800',
'ecelp7470' => 'audio/vnd.nuera.ecelp7470',
'ecelp9600' => 'audio/vnd.nuera.ecelp9600',
'ecma' => 'application/ecmascript',
'edm' => 'application/vnd.novadigm.edm',
'edx' => 'application/vnd.novadigm.edx',
'efif' => 'application/vnd.picsel',
'ei6' => 'application/vnd.pg.osasli',
'elc' => 'application/octet-stream',
'eml' => 'message/rfc822',
'emma' => 'application/emma+xml',
'eol' => 'audio/vnd.digital-winds',
'eot' => 'application/vnd.ms-fontobject',
'eps' => 'application/postscript',
'epub' => 'application/epub+zip',
'es3' => 'application/vnd.eszigno3+xml',
'esf' => 'application/vnd.epson.esf',
'et3' => 'application/vnd.eszigno3+xml',
'etx' => 'text/x-setext',
'exe' => 'application/x-msdownload',
'exi' => 'application/exi',
'ext' => 'application/vnd.novadigm.ext',
'ez' => 'application/andrew-inset',
'ez2' => 'application/vnd.ezpix-album',
'ez3' => 'application/vnd.ezpix-package',
'f' => 'text/x-fortran',
'f4v' => 'video/x-f4v',
'f77' => 'text/x-fortran',
'f90' => 'text/x-fortran',
'fbs' => 'image/vnd.fastbidsheet',
'fcs' => 'application/vnd.isac.fcs',
'fdf' => 'application/vnd.fdf',
'fe_launch' => 'application/vnd.denovo.fcselayout-link',
'fg5' => 'application/vnd.fujitsu.oasysgp',
'fgd' => 'application/x-director',
'fh' => 'image/x-freehand',
'fh4' => 'image/x-freehand',
'fh5' => 'image/x-freehand',
'fh7' => 'image/x-freehand',
'fhc' => 'image/x-freehand',
'fig' => 'application/x-xfig',
'fli' => 'video/x-fli',
'flo' => 'application/vnd.micrografx.flo',
'flv' => 'video/x-flv',
'flw' => 'application/vnd.kde.kivio',
'flx' => 'text/vnd.fmi.flexstor',
'fly' => 'text/vnd.fly',
'fm' => 'application/vnd.framemaker',
'fnc' => 'application/vnd.frogans.fnc',
'for' => 'text/x-fortran',
'fpx' => 'image/vnd.fpx',
'frame' => 'application/vnd.framemaker',
'fsc' => 'application/vnd.fsc.weblaunch',
'fst' => 'image/vnd.fst',
'ftc' => 'application/vnd.fluxtime.clip',
'fti' => 'application/vnd.anser-web-funds-transfer-initiation',
'fvt' => 'video/vnd.fvt',
'fxp' => 'application/vnd.adobe.fxp',
'fxpl' => 'application/vnd.adobe.fxp',
'fzs' => 'application/vnd.fuzzysheet',
'g2w' => 'application/vnd.geoplan',
'g3' => 'image/g3fax',
'g3w' => 'application/vnd.geospace',
'gac' => 'application/vnd.groove-account',
'gdl' => 'model/vnd.gdl',
'geo' => 'application/vnd.dynageo',
'gex' => 'application/vnd.geometry-explorer',
'ggb' => 'application/vnd.geogebra.file',
'ggt' => 'application/vnd.geogebra.tool',
'ghf' => 'application/vnd.groove-help',
'gif' => 'image/gif',
'gim' => 'application/vnd.groove-identity-message',
'gmx' => 'application/vnd.gmx',
'gnumeric' => 'application/x-gnumeric',
'gph' => 'application/vnd.flographit',
'gqf' => 'application/vnd.grafeq',
'gqs' => 'application/vnd.grafeq',
'gram' => 'application/srgs',
'gre' => 'application/vnd.geometry-explorer',
'grv' => 'application/vnd.groove-injector',
'grxml' => 'application/srgs+xml',
'gsf' => 'application/x-font-ghostscript',
'gtar' => 'application/x-gtar',
'gtm' => 'application/vnd.groove-tool-message',
'gtw' => 'model/vnd.gtw',
'gv' => 'text/vnd.graphviz',
'gxt' => 'application/vnd.geonext',
'h' => 'text/x-c',
'h261' => 'video/h261',
'h263' => 'video/h263',
'h264' => 'video/h264',
'hal' => 'application/vnd.hal+xml',
'hbci' => 'application/vnd.hbci',
'hdf' => 'application/x-hdf',
'hh' => 'text/x-c',
'hlp' => 'application/winhlp',
'hpgl' => 'application/vnd.hp-hpgl',
'hpid' => 'application/vnd.hp-hpid',
'hps' => 'application/vnd.hp-hps',
'hqx' => 'application/mac-binhex40',
'hta' => 'application/octet-stream',
'htc' => 'text/html',
'htke' => 'application/vnd.kenameaapp',
'htm' => 'text/html',
'html' => 'text/html',
'hvd' => 'application/vnd.yamaha.hv-dic',
'hvp' => 'application/vnd.yamaha.hv-voice',
'hvs' => 'application/vnd.yamaha.hv-script',
'i2g' => 'application/vnd.intergeo',
'icc' => 'application/vnd.iccprofile',
'ice' => 'x-conference/x-cooltalk',
'icm' => 'application/vnd.iccprofile',
'ico' => 'image/x-icon',
'ics' => 'text/calendar',
'ief' => 'image/ief',
'ifb' => 'text/calendar',
'ifm' => 'application/vnd.shana.informed.formdata',
'iges' => 'model/iges',
'igl' => 'application/vnd.igloader',
'igm' => 'application/vnd.insors.igm',
'igs' => 'model/iges',
'igx' => 'application/vnd.micrografx.igx',
'iif' => 'application/vnd.shana.informed.interchange',
'imp' => 'application/vnd.accpac.simply.imp',
'ims' => 'application/vnd.ms-ims',
'in' => 'text/plain',
'ini' => 'text/plain',
'ipfix' => 'application/ipfix',
'ipk' => 'application/vnd.shana.informed.package',
'irm' => 'application/vnd.ibm.rights-management',
'irp' => 'application/vnd.irepository.package+xml',
'iso' => 'application/octet-stream',
'itp' => 'application/vnd.shana.informed.formtemplate',
'ivp' => 'application/vnd.immervision-ivp',
'ivu' => 'application/vnd.immervision-ivu',
'jad' => 'text/vnd.sun.j2me.app-descriptor',
'jam' => 'application/vnd.jam',
'jar' => 'application/java-archive',
'java' => 'text/x-java-source',
'jisp' => 'application/vnd.jisp',
'jlt' => 'application/vnd.hp-jlyt',
'jnlp' => 'application/x-java-jnlp-file',
'joda' => 'application/vnd.joost.joda-archive',
'jpe' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'jpg' => 'image/jpeg',
'jpgm' => 'video/jpm',
'jpgv' => 'video/jpeg',
'jpm' => 'video/jpm',
'js' => 'text/javascript',
'json' => 'application/json',
'kar' => 'audio/midi',
'karbon' => 'application/vnd.kde.karbon',
'kfo' => 'application/vnd.kde.kformula',
'kia' => 'application/vnd.kidspiration',
'kml' => 'application/vnd.google-earth.kml+xml',
'kmz' => 'application/vnd.google-earth.kmz',
'kne' => 'application/vnd.kinar',
'knp' => 'application/vnd.kinar',
'kon' => 'application/vnd.kde.kontour',
'kpr' => 'application/vnd.kde.kpresenter',
'kpt' => 'application/vnd.kde.kpresenter',
'ksp' => 'application/vnd.kde.kspread',
'ktr' => 'application/vnd.kahootz',
'ktx' => 'image/ktx',
'ktz' => 'application/vnd.kahootz',
'kwd' => 'application/vnd.kde.kword',
'kwt' => 'application/vnd.kde.kword',
'lasxml' => 'application/vnd.las.las+xml',
'latex' => 'application/x-latex',
'lbd' => 'application/vnd.llamagraphics.life-balance.desktop',
'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml',
'les' => 'application/vnd.hhe.lesson-player',
'lha' => 'application/octet-stream',
'link66' => 'application/vnd.route66.link66+xml',
'list' => 'text/plain',
'list3820' => 'application/vnd.ibm.modcap',
'listafp' => 'application/vnd.ibm.modcap',
'log' => 'text/plain',
'lostxml' => 'application/lost+xml',
'lrf' => 'application/octet-stream',
'lrm' => 'application/vnd.ms-lrm',
'ltf' => 'application/vnd.frogans.ltf',
'lvp' => 'audio/vnd.lucent.voice',
'lwp' => 'application/vnd.lotus-wordpro',
'lzh' => 'application/octet-stream',
'm13' => 'application/x-msmediaview',
'm14' => 'application/x-msmediaview',
'm1v' => 'video/mpeg',
'm21' => 'application/mp21',
'm2a' => 'audio/mpeg',
'm2v' => 'video/mpeg',
'm3a' => 'audio/mpeg',
'm3u' => 'audio/x-mpegurl',
'm3u8' => 'application/vnd.apple.mpegurl',
'm4a' => 'audio/mp4',
'm4u' => 'video/vnd.mpegurl',
'm4v' => 'video/mp4',
'ma' => 'application/mathematica',
'mads' => 'application/mads+xml',
'mag' => 'application/vnd.ecowin.chart',
'maker' => 'application/vnd.framemaker',
'man' => 'text/troff',
'mathml' => 'application/mathml+xml',
'mb' => 'application/mathematica',
'mbk' => 'application/vnd.mobius.mbk',
'mbox' => 'application/mbox',
'mc1' => 'application/vnd.medcalcdata',
'mcd' => 'application/vnd.mcd',
'mcurl' => 'text/vnd.curl.mcurl',
'mdb' => 'application/x-msaccess',
'mdi' => 'image/vnd.ms-modi',
'me' => 'text/troff',
'mesh' => 'model/mesh',
'meta4' => 'application/metalink4+xml',
'mets' => 'application/mets+xml',
'mfm' => 'application/vnd.mfmp',
'mgp' => 'application/vnd.osgeo.mapguide.package',
'mgz' => 'application/vnd.proteus.magazine',
'mid' => 'audio/midi',
'midi' => 'audio/midi',
'mif' => 'application/vnd.mif',
'mime' => 'message/rfc822',
'mj2' => 'video/mj2',
'mjp2' => 'video/mj2',
'mlp' => 'application/vnd.dolby.mlp',
'mmd' => 'application/vnd.chipnuts.karaoke-mmd',
'mmf' => 'application/vnd.smaf',
'mmr' => 'image/vnd.fujixerox.edmics-mmr',
'mny' => 'application/x-msmoney',
'mobi' => 'application/x-mobipocket-ebook',
'mods' => 'application/mods+xml',
'mov' => 'video/quicktime',
'movie' => 'video/x-sgi-movie',
'mp2' => 'audio/mpeg',
'mp21' => 'application/mp21',
'mp2a' => 'audio/mpeg',
'mp3' => 'audio/mpeg',
'mp4' => 'video/mp4',
'mp4a' => 'audio/mp4',
'mp4s' => 'application/mp4',
'mp4v' => 'video/mp4',
'mpc' => 'application/vnd.mophun.certificate',
'mpe' => 'video/mpeg',
'mpeg' => 'video/mpeg',
'mpg' => 'video/mpeg',
'mpg4' => 'video/mp4',
'mpga' => 'audio/mpeg',
'mpkg' => 'application/vnd.apple.installer+xml',
'mpm' => 'application/vnd.blueice.multipass',
'mpn' => 'application/vnd.mophun.application',
'mpp' => 'application/vnd.ms-project',
'mpt' => 'application/vnd.ms-project',
'mpy' => 'application/vnd.ibm.minipay',
'mqy' => 'application/vnd.mobius.mqy',
'mrc' => 'application/marc',
'mrcx' => 'application/marcxml+xml',
'ms' => 'text/troff',
'mscml' => 'application/mediaservercontrol+xml',
'mseed' => 'application/vnd.fdsn.mseed',
'mseq' => 'application/vnd.mseq',
'msf' => 'application/vnd.epson.msf',
'msh' => 'model/mesh',
'msi' => 'application/x-msdownload',
'msl' => 'application/vnd.mobius.msl',
'msty' => 'application/vnd.muvee.style',
'mts' => 'model/vnd.mts',
'mus' => 'application/vnd.musician',
'musicxml' => 'application/vnd.recordare.musicxml+xml',
'mvb' => 'application/x-msmediaview',
'mwf' => 'application/vnd.mfer',
'mxf' => 'application/mxf',
'mxl' => 'application/vnd.recordare.musicxml',
'mxml' => 'application/xv+xml',
'mxs' => 'application/vnd.triscape.mxs',
'mxu' => 'video/vnd.mpegurl',
'n-gage' => 'application/vnd.nokia.n-gage.symbian.install',
'n3' => 'text/n3',
'nb' => 'application/mathematica',
'nbp' => 'application/vnd.wolfram.player',
'nc' => 'application/x-netcdf',
'ncx' => 'application/x-dtbncx+xml',
'ngdat' => 'application/vnd.nokia.n-gage.data',
'nlu' => 'application/vnd.neurolanguage.nlu',
'nml' => 'application/vnd.enliven',
'nnd' => 'application/vnd.noblenet-directory',
'nns' => 'application/vnd.noblenet-sealer',
'nnw' => 'application/vnd.noblenet-web',
'npx' => 'image/vnd.net-fpx',
'nsf' => 'application/vnd.lotus-notes',
'oa2' => 'application/vnd.fujitsu.oasys2',
'oa3' => 'application/vnd.fujitsu.oasys3',
'oas' => 'application/vnd.fujitsu.oasys',
'obd' => 'application/x-msbinder',
'oda' => 'application/oda',
'odb' => 'application/vnd.oasis.opendocument.database',
'odc' => 'application/vnd.oasis.opendocument.chart',
'odf' => 'application/vnd.oasis.opendocument.formula',
'odft' => 'application/vnd.oasis.opendocument.formula-template',
'odg' => 'application/vnd.oasis.opendocument.graphics',
'odi' => 'application/vnd.oasis.opendocument.image',
'odm' => 'application/vnd.oasis.opendocument.text-master',
'odp' => 'application/vnd.oasis.opendocument.presentation',
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
'odt' => 'application/vnd.oasis.opendocument.text',
'oga' => 'audio/ogg',
'ogg' => 'audio/ogg',
'ogv' => 'video/ogg',
'ogx' => 'application/ogg',
'onepkg' => 'application/onenote',
'onetmp' => 'application/onenote',
'onetoc' => 'application/onenote',
'onetoc2' => 'application/onenote',
'opf' => 'application/oebps-package+xml',
'oprc' => 'application/vnd.palm',
'org' => 'application/vnd.lotus-organizer',
'osf' => 'application/vnd.yamaha.openscoreformat',
'osfpvg' => 'application/vnd.yamaha.openscoreformat.osfpvg+xml',
'otc' => 'application/vnd.oasis.opendocument.chart-template',
'otf' => 'application/x-font-otf',
'otg' => 'application/vnd.oasis.opendocument.graphics-template',
'oth' => 'application/vnd.oasis.opendocument.text-web',
'oti' => 'application/vnd.oasis.opendocument.image-template',
'otp' => 'application/vnd.oasis.opendocument.presentation-template',
'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template',
'ott' => 'application/vnd.oasis.opendocument.text-template',
'oxt' => 'application/vnd.openofficeorg.extension',
'p' => 'text/x-pascal',
'p10' => 'application/pkcs10',
'p12' => 'application/x-pkcs12',
'p7b' => 'application/x-pkcs7-certificates',
'p7c' => 'application/pkcs7-mime',
'p7m' => 'application/pkcs7-mime',
'p7r' => 'application/x-pkcs7-certreqresp',
'p7s' => 'application/pkcs7-signature',
'p8' => 'application/pkcs8',
'pas' => 'text/x-pascal',
'paw' => 'application/vnd.pawaafile',
'pbd' => 'application/vnd.powerbuilder6',
'pbm' => 'image/x-portable-bitmap',
'pcf' => 'application/x-font-pcf',
'pcl' => 'application/vnd.hp-pcl',
'pclxl' => 'application/vnd.hp-pclxl',
'pct' => 'image/x-pict',
'pcurl' => 'application/vnd.curl.pcurl',
'pcx' => 'image/x-pcx',
'pdb' => 'application/vnd.palm',
'pdf' => 'application/pdf',
'pfa' => 'application/x-font-type1',
'pfb' => 'application/x-font-type1',
'pfm' => 'application/x-font-type1',
'pfr' => 'application/font-tdpfr',
'pfx' => 'application/x-pkcs12',
'pgm' => 'image/x-portable-graymap',
'pgn' => 'application/x-chess-pgn',
'pgp' => 'application/pgp-encrypted',
'php' => 'text/x-php',
'phps' => 'application/x-httpd-phps',
'pic' => 'image/x-pict',
'pkg' => 'application/octet-stream',
'pki' => 'application/pkixcmp',
'pkipath' => 'application/pkix-pkipath',
'plb' => 'application/vnd.3gpp.pic-bw-large',
'plc' => 'application/vnd.mobius.plc',
'plf' => 'application/vnd.pocketlearn',
'pls' => 'application/pls+xml',
'pml' => 'application/vnd.ctc-posml',
'png' => 'image/png',
'pnm' => 'image/x-portable-anymap',
'portpkg' => 'application/vnd.macports.portpkg',
'pot' => 'application/vnd.ms-powerpoint',
'potm' => 'application/vnd.ms-powerpoint.template.macroenabled.12',
'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
'ppam' => 'application/vnd.ms-powerpoint.addin.macroenabled.12',
'ppd' => 'application/vnd.cups-ppd',
'ppm' => 'image/x-portable-pixmap',
'pps' => 'application/vnd.ms-powerpoint',
'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroenabled.12',
'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
'ppt' => 'application/vnd.ms-powerpoint',
'pptm' => 'application/vnd.ms-powerpoint.presentation.macroenabled.12',
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'pqa' => 'application/vnd.palm',
'prc' => 'application/x-mobipocket-ebook',
'pre' => 'application/vnd.lotus-freelance',
'prf' => 'application/pics-rules',
'ps' => 'application/postscript',
'psb' => 'application/vnd.3gpp.pic-bw-small',
'psd' => 'image/vnd.adobe.photoshop',
'psf' => 'application/x-font-linux-psf',
'pskcxml' => 'application/pskc+xml',
'ptid' => 'application/vnd.pvi.ptid1',
'pub' => 'application/x-mspublisher',
'pvb' => 'application/vnd.3gpp.pic-bw-var',
'pwn' => 'application/vnd.3m.post-it-notes',
'pya' => 'audio/vnd.ms-playready.media.pya',
'pyv' => 'video/vnd.ms-playready.media.pyv',
'qam' => 'application/vnd.epson.quickanime',
'qbo' => 'application/vnd.intu.qbo',
'qfx' => 'application/vnd.intu.qfx',
'qps' => 'application/vnd.publishare-delta-tree',
'qt' => 'video/quicktime',
'qwd' => 'application/vnd.quark.quarkxpress',
'qwt' => 'application/vnd.quark.quarkxpress',
'qxb' => 'application/vnd.quark.quarkxpress',
'qxd' => 'application/vnd.quark.quarkxpress',
'qxl' => 'application/vnd.quark.quarkxpress',
'qxt' => 'application/vnd.quark.quarkxpress',
'ra' => 'audio/x-pn-realaudio',
'ram' => 'audio/x-pn-realaudio',
'rar' => 'application/x-rar-compressed',
'ras' => 'image/x-cmu-raster',
'rb' => 'text/plain',
'rcprofile' => 'application/vnd.ipunplugged.rcprofile',
'rdf' => 'application/rdf+xml',
'rdz' => 'application/vnd.data-vision.rdz',
'rep' => 'application/vnd.businessobjects',
'res' => 'application/x-dtbresource+xml',
'resx' => 'text/xml',
'rgb' => 'image/x-rgb',
'rif' => 'application/reginfo+xml',
'rip' => 'audio/vnd.rip',
'rl' => 'application/resource-lists+xml',
'rlc' => 'image/vnd.fujixerox.edmics-rlc',
'rld' => 'application/resource-lists-diff+xml',
'rm' => 'application/vnd.rn-realmedia',
'rmi' => 'audio/midi',
'rmp' => 'audio/x-pn-realaudio-plugin',
'rms' => 'application/vnd.jcp.javame.midlet-rms',
'rnc' => 'application/relax-ng-compact-syntax',
'roff' => 'text/troff',
'rp9' => 'application/vnd.cloanto.rp9',
'rpss' => 'application/vnd.nokia.radio-presets',
'rpst' => 'application/vnd.nokia.radio-preset',
'rq' => 'application/sparql-query',
'rs' => 'application/rls-services+xml',
'rsd' => 'application/rsd+xml',
'rss' => 'application/rss+xml',
'rtf' => 'application/rtf',
'rtx' => 'text/richtext',
's' => 'text/x-asm',
'saf' => 'application/vnd.yamaha.smaf-audio',
'sbml' => 'application/sbml+xml',
'sc' => 'application/vnd.ibm.secure-container',
'scd' => 'application/x-msschedule',
'scm' => 'application/vnd.lotus-screencam',
'scq' => 'application/scvp-cv-request',
'scs' => 'application/scvp-cv-response',
'scurl' => 'text/vnd.curl.scurl',
'sda' => 'application/vnd.stardivision.draw',
'sdc' => 'application/vnd.stardivision.calc',
'sdd' => 'application/vnd.stardivision.impress',
'sdkd' => 'application/vnd.solent.sdkm+xml',
'sdkm' => 'application/vnd.solent.sdkm+xml',
'sdp' => 'application/sdp',
'sdw' => 'application/vnd.stardivision.writer',
'see' => 'application/vnd.seemail',
'seed' => 'application/vnd.fdsn.seed',
'sema' => 'application/vnd.sema',
'semd' => 'application/vnd.semd',
'semf' => 'application/vnd.semf',
'ser' => 'application/java-serialized-object',
'setpay' => 'application/set-payment-initiation',
'setreg' => 'application/set-registration-initiation',
'sfd-hdstx' => 'application/vnd.hydrostatix.sof-data',
'sfs' => 'application/vnd.spotfire.sfs',
'sgl' => 'application/vnd.stardivision.writer-global',
'sgm' => 'text/sgml',
'sgml' => 'text/sgml',
'sh' => 'application/x-sh',
'shar' => 'application/x-shar',
'shf' => 'application/shf+xml',
'sig' => 'application/pgp-signature',
'silo' => 'model/mesh',
'sis' => 'application/vnd.symbian.install',
'sisx' => 'application/vnd.symbian.install',
'sit' => 'application/x-stuffit',
'sitx' => 'application/x-stuffitx',
'skd' => 'application/vnd.koan',
'skm' => 'application/vnd.koan',
'skp' => 'application/vnd.koan',
'skt' => 'application/vnd.koan',
'sldm' => 'application/vnd.ms-powerpoint.slide.macroenabled.12',
'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
'slt' => 'application/vnd.epson.salt',
'sm' => 'application/vnd.stepmania.stepchart',
'smf' => 'application/vnd.stardivision.math',
'smi' => 'application/smil+xml',
'smil' => 'application/smil+xml',
'snd' => 'audio/basic',
'snf' => 'application/x-font-snf',
'so' => 'application/octet-stream',
'spc' => 'application/x-pkcs7-certificates',
'spf' => 'application/vnd.yamaha.smaf-phrase',
'spl' => 'application/x-futuresplash',
'spot' => 'text/vnd.in3d.spot',
'spp' => 'application/scvp-vp-response',
'spq' => 'application/scvp-vp-request',
'spx' => 'audio/ogg',
'src' => 'application/x-wais-source',
'sru' => 'application/sru+xml',
'srx' => 'application/sparql-results+xml',
'sse' => 'application/vnd.kodak-descriptor',
'ssf' => 'application/vnd.epson.ssf',
'ssml' => 'application/ssml+xml',
'st' => 'application/vnd.sailingtracker.track',
'stc' => 'application/vnd.sun.xml.calc.template',
'std' => 'application/vnd.sun.xml.draw.template',
'stf' => 'application/vnd.wt.stf',
'sti' => 'application/vnd.sun.xml.impress.template',
'stk' => 'application/hyperstudio',
'stl' => 'application/vnd.ms-pki.stl',
'str' => 'application/vnd.pg.format',
'stw' => 'application/vnd.sun.xml.writer.template',
'sub' => 'image/vnd.dvb.subtitle',
'sus' => 'application/vnd.sus-calendar',
'susp' => 'application/vnd.sus-calendar',
'sv4cpio' => 'application/x-sv4cpio',
'sv4crc' => 'application/x-sv4crc',
'svc' => 'application/vnd.dvb.service',
'svd' => 'application/vnd.svd',
'svg' => 'image/svg+xml',
'svgz' => 'image/svg+xml',
'swa' => 'application/x-director',
'swf' => 'application/x-shockwave-flash',
'swi' => 'application/vnd.aristanetworks.swi',
'sxc' => 'application/vnd.sun.xml.calc',
'sxd' => 'application/vnd.sun.xml.draw',
'sxg' => 'application/vnd.sun.xml.writer.global',
'sxi' => 'application/vnd.sun.xml.impress',
'sxm' => 'application/vnd.sun.xml.math',
'sxw' => 'application/vnd.sun.xml.writer',
't' => 'text/troff',
'tao' => 'application/vnd.tao.intent-module-archive',
'tar' => 'application/x-tar',
'tcap' => 'application/vnd.3gpp2.tcap',
'tcl' => 'application/x-tcl',
'teacher' => 'application/vnd.smart.teacher',
'tei' => 'application/tei+xml',
'teicorpus' => 'application/tei+xml',
'tex' => 'application/x-tex',
'texi' => 'application/x-texinfo',
'texinfo' => 'application/x-texinfo',
'text' => 'text/plain',
'tfi' => 'application/thraud+xml',
'tfm' => 'application/x-tex-tfm',
'thmx' => 'application/vnd.ms-officetheme',
'tif' => 'image/tiff',
'tiff' => 'image/tiff',
'tmo' => 'application/vnd.tmobile-livetv',
'torrent' => 'application/x-bittorrent',
'tpl' => 'application/vnd.groove-tool-template',
'tpt' => 'application/vnd.trid.tpt',
'tr' => 'text/troff',
'tra' => 'application/vnd.trueapp',
'trm' => 'application/x-msterminal',
'tsd' => 'application/timestamped-data',
'tsv' => 'text/tab-separated-values',
'ttc' => 'application/x-font-ttf',
'ttf' => 'application/x-font-ttf',
'ttl' => 'text/turtle',
'twd' => 'application/vnd.simtech-mindmapper',
'twds' => 'application/vnd.simtech-mindmapper',
'txd' => 'application/vnd.genomatix.tuxedo',
'txf' => 'application/vnd.mobius.txf',
'txt' => 'text/plain',
'u32' => 'application/x-authorware-bin',
'udeb' => 'application/x-debian-package',
'ufd' => 'application/vnd.ufdl',
'ufdl' => 'application/vnd.ufdl',
'umj' => 'application/vnd.umajin',
'unityweb' => 'application/vnd.unity',
'uoml' => 'application/vnd.uoml+xml',
'uri' => 'text/uri-list',
'uris' => 'text/uri-list',
'urls' => 'text/uri-list',
'ustar' => 'application/x-ustar',
'utz' => 'application/vnd.uiq.theme',
'uu' => 'text/x-uuencode',
'uva' => 'audio/vnd.dece.audio',
'uvd' => 'application/vnd.dece.data',
'uvf' => 'application/vnd.dece.data',
'uvg' => 'image/vnd.dece.graphic',
'uvh' => 'video/vnd.dece.hd',
'uvi' => 'image/vnd.dece.graphic',
'uvm' => 'video/vnd.dece.mobile',
'uvp' => 'video/vnd.dece.pd',
'uvs' => 'video/vnd.dece.sd',
'uvt' => 'application/vnd.dece.ttml+xml',
'uvu' => 'video/vnd.uvvu.mp4',
'uvv' => 'video/vnd.dece.video',
'uvva' => 'audio/vnd.dece.audio',
'uvvd' => 'application/vnd.dece.data',
'uvvf' => 'application/vnd.dece.data',
'uvvg' => 'image/vnd.dece.graphic',
'uvvh' => 'video/vnd.dece.hd',
'uvvi' => 'image/vnd.dece.graphic',
'uvvm' => 'video/vnd.dece.mobile',
'uvvp' => 'video/vnd.dece.pd',
'uvvs' => 'video/vnd.dece.sd',
'uvvt' => 'application/vnd.dece.ttml+xml',
'uvvu' => 'video/vnd.uvvu.mp4',
'uvvv' => 'video/vnd.dece.video',
'uvvx' => 'application/vnd.dece.unspecified',
'uvx' => 'application/vnd.dece.unspecified',
'vcd' => 'application/x-cdlink',
'vcf' => 'text/x-vcard',
'vcg' => 'application/vnd.groove-vcard',
'vcs' => 'text/x-vcalendar',
'vcx' => 'application/vnd.vcx',
'vis' => 'application/vnd.visionary',
'viv' => 'video/vnd.vivo',
'vor' => 'application/vnd.stardivision.writer',
'vox' => 'application/x-authorware-bin',
'vrml' => 'model/vrml',
'vsd' => 'application/vnd.visio',
'vsf' => 'application/vnd.vsf',
'vss' => 'application/vnd.visio',
'vst' => 'application/vnd.visio',
'vsw' => 'application/vnd.visio',
'vtu' => 'model/vnd.vtu',
'vxml' => 'application/voicexml+xml',
'w3d' => 'application/x-director',
'wad' => 'application/x-doom',
'wav' => 'audio/x-wav',
'wax' => 'audio/x-ms-wax',
'wbmp' => 'image/vnd.wap.wbmp',
'wbs' => 'application/vnd.criticaltools.wbs+xml',
'wbxml' => 'application/vnd.wap.wbxml',
'wcm' => 'application/vnd.ms-works',
'wdb' => 'application/vnd.ms-works',
'weba' => 'audio/webm',
'webm' => 'video/webm',
'webp' => 'image/webp',
'wg' => 'application/vnd.pmi.widget',
'wgt' => 'application/widget',
'wks' => 'application/vnd.ms-works',
'wm' => 'video/x-ms-wm',
'wma' => 'audio/x-ms-wma',
'wmd' => 'application/x-ms-wmd',
'wmf' => 'application/x-msmetafile',
'wml' => 'text/vnd.wap.wml',
'wmlc' => 'application/vnd.wap.wmlc',
'wmls' => 'text/vnd.wap.wmlscript',
'wmlsc' => 'application/vnd.wap.wmlscriptc',
'wmv' => 'video/x-ms-wmv',
'wmx' => 'video/x-ms-wmx',
'wmz' => 'application/x-ms-wmz',
'woff' => 'application/x-font-woff',
'wpd' => 'application/vnd.wordperfect',
'wpl' => 'application/vnd.ms-wpl',
'wps' => 'application/vnd.ms-works',
'wqd' => 'application/vnd.wqd',
'wri' => 'application/x-mswrite',
'wrl' => 'model/vrml',
'wsdl' => 'application/wsdl+xml',
'wspolicy' => 'application/wspolicy+xml',
'wtb' => 'application/vnd.webturbo',
'wvx' => 'video/x-ms-wvx',
'x32' => 'application/x-authorware-bin',
'x3d' => 'application/vnd.hzn-3d-crossword',
'xap' => 'application/x-silverlight-app',
'xar' => 'application/vnd.xara',
'xbap' => 'application/x-ms-xbap',
'xbd' => 'application/vnd.fujixerox.docuworks.binder',
'xbm' => 'image/x-xbitmap',
'xdf' => 'application/xcap-diff+xml',
'xdm' => 'application/vnd.syncml.dm+xml',
'xdp' => 'application/vnd.adobe.xdp+xml',
'xdssc' => 'application/dssc+xml',
'xdw' => 'application/vnd.fujixerox.docuworks',
'xenc' => 'application/xenc+xml',
'xer' => 'application/patch-ops-error+xml',
'xfdf' => 'application/vnd.adobe.xfdf',
'xfdl' => 'application/vnd.xfdl',
'xht' => 'application/xhtml+xml',
'xhtml' => 'application/xhtml+xml',
'xhvml' => 'application/xv+xml',
'xif' => 'image/vnd.xiff',
'xla' => 'application/vnd.ms-excel',
'xlam' => 'application/vnd.ms-excel.addin.macroenabled.12',
'xlc' => 'application/vnd.ms-excel',
'xlm' => 'application/vnd.ms-excel',
'xls' => 'application/vnd.ms-excel',
'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroenabled.12',
'xlsm' => 'application/vnd.ms-excel.sheet.macroenabled.12',
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'xlt' => 'application/vnd.ms-excel',
'xltm' => 'application/vnd.ms-excel.template.macroenabled.12',
'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
'xlw' => 'application/vnd.ms-excel',
'xml' => 'application/xml',
'xo' => 'application/vnd.olpc-sugar',
'xop' => 'application/xop+xml',
'xpi' => 'application/x-xpinstall',
'xpm' => 'image/x-xpixmap',
'xpr' => 'application/vnd.is-xpr',
'xps' => 'application/vnd.ms-xpsdocument',
'xpw' => 'application/vnd.intercon.formnet',
'xpx' => 'application/vnd.intercon.formnet',
'xsl' => 'application/xml',
'xslt' => 'application/xslt+xml',
'xsm' => 'application/vnd.syncml+xml',
'xspf' => 'application/xspf+xml',
'xul' => 'application/vnd.mozilla.xul+xml',
'xvm' => 'application/xv+xml',
'xvml' => 'application/xv+xml',
'xwd' => 'image/x-xwindowdump',
'xyz' => 'chemical/x-xyz',
'yaml' => 'text/yaml',
'yang' => 'application/yang',
'yin' => 'application/yin+xml',
'yml' => 'text/yaml',
'zaz' => 'application/vnd.zzazz.deck+xml',
'zip' => 'application/zip',
'zir' => 'application/vnd.zul',
'zirz' => 'application/vnd.zul',
'zmm' => 'application/vnd.handheld-entertainment+xml'
);
/**
* Get a singleton instance of the class
*
* @return self
* @codeCoverageIgnore
*/
public static function getInstance()
{
if (!self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Get a mimetype value from a file extension
*
* @param string $extension File extension
*
* @return string|null
*
*/
public function fromExtension($extension)
{
return isset($this->mimetypes[$extension]) ? $this->mimetypes[$extension] : null;
}
/**
* Get a mimetype from a filename
*
* @param string $filename Filename to generate a mimetype from
*
* @return string|null
*/
public function fromFilename($filename)
{
return $this->fromExtension(pathinfo($filename, PATHINFO_EXTENSION));
}
}

View File

@ -1,20 +0,0 @@
<?php
namespace Guzzle\Http\QueryAggregator;
use Guzzle\Http\QueryString;
/**
* Aggregates nested query string variables using commas
*/
class CommaAggregator implements QueryAggregatorInterface
{
public function aggregate($key, $value, QueryString $query)
{
if ($query->isUrlEncoding()) {
return array($query->encodeValue($key) => implode(',', array_map(array($query, 'encodeValue'), $value)));
} else {
return array($key => implode(',', $value));
}
}
}

View File

@ -1,22 +0,0 @@
<?php
namespace Guzzle\Http\QueryAggregator;
use Guzzle\Http\QueryString;
/**
* Does not aggregate nested query string values and allows duplicates in the resulting array
*
* Example: http://test.com?q=1&q=2
*/
class DuplicateAggregator implements QueryAggregatorInterface
{
public function aggregate($key, $value, QueryString $query)
{
if ($query->isUrlEncoding()) {
return array($query->encodeValue($key) => array_map(array($query, 'encodeValue'), $value));
} else {
return array($key => $value);
}
}
}

View File

@ -1,27 +0,0 @@
<?php
namespace Guzzle\Http\QueryAggregator;
use Guzzle\Http\QueryString;
/**
* Aggregates nested query string variables using PHP style []
*/
class PhpAggregator implements QueryAggregatorInterface
{
public function aggregate($key, $value, QueryString $query)
{
$ret = array();
foreach ($value as $k => $v) {
$k = "{$key}[{$k}]";
if (is_array($v)) {
$ret = array_merge($ret, self::aggregate($k, $v, $query));
} else {
$ret[$query->encodeValue($k)] = $query->encodeValue($v);
}
}
return $ret;
}
}

View File

@ -1,22 +0,0 @@
<?php
namespace Guzzle\Http\QueryAggregator;
use Guzzle\Http\QueryString;
/**
* Interface used for aggregating nested query string variables into a flattened array of key value pairs
*/
interface QueryAggregatorInterface
{
/**
* Aggregate multi-valued parameters into a flattened associative array
*
* @param string $key The name of the query string parameter
* @param array $value The values of the parameter
* @param QueryString $query The query string that is being aggregated
*
* @return array Returns an array of the combined values
*/
public function aggregate($key, $value, QueryString $query);
}

View File

@ -1,267 +0,0 @@
<?php
namespace Guzzle\Http;
use Guzzle\Common\Collection;
use Guzzle\Http\QueryAggregator\QueryAggregatorInterface;
use Guzzle\Http\QueryAggregator\PhpAggregator;
/**
* Query string object to handle managing query string parameters and aggregating those parameters together as a string.
*/
class QueryString extends Collection
{
/** @var string Used to URL encode with rawurlencode */
const RFC_3986 = 'RFC 3986';
/** @var string Used to encode with urlencode */
const FORM_URLENCODED = 'application/x-www-form-urlencoded';
/** @var string Constant used to create blank query string values (e.g. ?foo) */
const BLANK = "_guzzle_blank_";
/** @var string The query string field separator (e.g. '&') */
protected $fieldSeparator = '&';
/** @var string The query string value separator (e.g. '=') */
protected $valueSeparator = '=';
/** @var bool URL encode fields and values */
protected $urlEncode = 'RFC 3986';
/** @var QueryAggregatorInterface */
protected $aggregator;
/** @var array Cached PHP aggregator */
protected static $defaultAggregator = null;
/**
* Parse a query string into a QueryString object
*
* @param string $query Query string to parse
*
* @return self
*/
public static function fromString($query)
{
$q = new static();
if ($query || $query === '0') {
if ($query[0] == '?') {
$query = substr($query, 1);
}
foreach (explode('&', $query) as $kvp) {
$parts = explode('=', $kvp, 2);
$key = rawurldecode($parts[0]);
if ($paramIsPhpStyleArray = substr($key, -2) == '[]') {
$key = substr($key, 0, -2);
}
if (isset($parts[1])) {
$value = rawurldecode(str_replace('+', '%20', $parts[1]));
if ($paramIsPhpStyleArray && !$q->hasKey($key)) {
$value = array($value);
}
$q->add($key, $value);
} else {
$q->add($key, null);
}
}
}
return $q;
}
/**
* Convert the query string parameters to a query string string
*
* @return string
*/
public function __toString()
{
if (!$this->data) {
return '';
}
$queryString = '';
foreach ($this->prepareData($this->data) as $name => $value) {
foreach ((array) $value as $v) {
if ($queryString) {
$queryString .= $this->fieldSeparator;
}
$queryString .= $name;
if ($v !== self::BLANK) {
$queryString .= $this->valueSeparator . $v;
}
}
}
return $queryString;
}
/**
* Get the query string field separator
*
* @return string
*/
public function getFieldSeparator()
{
return $this->fieldSeparator;
}
/**
* Get the query string value separator
*
* @return string
*/
public function getValueSeparator()
{
return $this->valueSeparator;
}
/**
* Returns the type of URL encoding used by the query string
*
* One of: false, "RFC 3986", or "application/x-www-form-urlencoded"
*
* @return bool|string
*/
public function getUrlEncoding()
{
return $this->urlEncode;
}
/**
* Returns true or false if using URL encoding
*
* @return bool
*/
public function isUrlEncoding()
{
return $this->urlEncode !== false;
}
/**
* Provide a function for combining multi-valued query string parameters into a single or multiple fields
*
* @param null|QueryAggregatorInterface $aggregator Pass in a QueryAggregatorInterface object to handle converting
* deeply nested query string variables into a flattened array.
* Pass null to use the default PHP style aggregator. For legacy
* reasons, this function accepts a callable that must accepts a
* $key, $value, and query object.
* @return self
* @see \Guzzle\Http\QueryString::aggregateUsingComma()
*/
public function setAggregator(QueryAggregatorInterface $aggregator = null)
{
// Use the default aggregator if none was set
if (!$aggregator) {
if (!self::$defaultAggregator) {
self::$defaultAggregator = new PhpAggregator();
}
$aggregator = self::$defaultAggregator;
}
$this->aggregator = $aggregator;
return $this;
}
/**
* Set whether or not field names and values should be rawurlencoded
*
* @param bool|string $encode Set to TRUE to use RFC 3986 encoding (rawurlencode), false to disable encoding, or
* form_urlencoding to use application/x-www-form-urlencoded encoding (urlencode)
* @return self
*/
public function useUrlEncoding($encode)
{
$this->urlEncode = ($encode === true) ? self::RFC_3986 : $encode;
return $this;
}
/**
* Set the query string separator
*
* @param string $separator The query string separator that will separate fields
*
* @return self
*/
public function setFieldSeparator($separator)
{
$this->fieldSeparator = $separator;
return $this;
}
/**
* Set the query string value separator
*
* @param string $separator The query string separator that will separate values from fields
*
* @return self
*/
public function setValueSeparator($separator)
{
$this->valueSeparator = $separator;
return $this;
}
/**
* Returns an array of url encoded field names and values
*
* @return array
*/
public function urlEncode()
{
return $this->prepareData($this->data);
}
/**
* URL encodes a value based on the url encoding type of the query string object
*
* @param string $value Value to encode
*
* @return string
*/
public function encodeValue($value)
{
if ($this->urlEncode == self::RFC_3986) {
return rawurlencode($value);
} elseif ($this->urlEncode == self::FORM_URLENCODED) {
return urlencode($value);
} else {
return (string) $value;
}
}
/**
* Url encode parameter data and convert nested query strings into a flattened hash.
*
* @param array $data The data to encode
*
* @return array Returns an array of encoded values and keys
*/
protected function prepareData(array $data)
{
// If no aggregator is present then set the default
if (!$this->aggregator) {
$this->setAggregator(null);
}
$temp = array();
foreach ($data as $key => $value) {
if (is_array($value)) {
$temp = array_merge($temp, $this->aggregator->aggregate($key, $value, $this));
} else {
$temp[$this->encodeValue($key)] = $this->encodeValue($value);
}
}
return $temp;
}
}

View File

@ -1,106 +0,0 @@
<?php
namespace Guzzle\Http;
/**
* EntityBody decorator used to return only a subset of an entity body
*/
class ReadLimitEntityBody extends AbstractEntityBodyDecorator
{
/** @var int Limit the number of bytes that can be read */
protected $limit;
/** @var int Offset to start reading from */
protected $offset;
/**
* @param EntityBodyInterface $body Body to wrap
* @param int $limit Total number of bytes to allow to be read from the stream
* @param int $offset Position to seek to before reading (only works on seekable streams)
*/
public function __construct(EntityBodyInterface $body, $limit, $offset = 0)
{
parent::__construct($body);
$this->setLimit($limit)->setOffset($offset);
$this->body->seek($offset);
}
/**
* Returns only a subset of the decorated entity body when cast as a string
* {@inheritdoc}
*/
public function __toString()
{
return substr((string) $this->body, $this->offset, $this->limit) ?: '';
}
public function isConsumed()
{
return (($this->offset + $this->limit) - $this->body->ftell()) <= 0;
}
/**
* Returns the Content-Length of the limited subset of data
* {@inheritdoc}
*/
public function getContentLength()
{
$length = $this->body->getContentLength();
return $length === false
? $this->limit
: min($this->limit, min($length, $this->offset + $this->limit) - $this->offset);
}
/**
* Allow for a bounded seek on the read limited entity body
* {@inheritdoc}
*/
public function seek($offset, $whence = SEEK_SET)
{
return $whence === SEEK_SET
? $this->body->seek(max($this->offset, min($this->offset + $this->limit, $offset)))
: false;
}
/**
* Set the offset to start limiting from
*
* @param int $offset Offset to seek to and begin byte limiting from
*
* @return self
*/
public function setOffset($offset)
{
$this->body->seek($offset);
$this->offset = $offset;
return $this;
}
/**
* Set the limit of bytes that the decorator allows to be read from the stream
*
* @param int $limit Total number of bytes to allow to be read from the stream
*
* @return self
*/
public function setLimit($limit)
{
$this->limit = $limit;
return $this;
}
public function read($length)
{
// Check if the current position is less than the total allowed bytes + original offset
$remaining = ($this->offset + $this->limit) - $this->body->ftell();
if ($remaining > 0) {
// Only return the amount of requested data, ensuring that the byte limit is not exceeded
return $this->body->read(min($remaining, $length));
} else {
return false;
}
}
}

View File

@ -1,250 +0,0 @@
<?php
namespace Guzzle\Http;
use Guzzle\Common\Event;
use Guzzle\Http\Exception\BadResponseException;
use Guzzle\Http\Url;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\RequestFactory;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Exception\TooManyRedirectsException;
use Guzzle\Http\Exception\CouldNotRewindStreamException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Plugin to implement HTTP redirects. Can redirect like a web browser or using strict RFC 2616 compliance
*/
class RedirectPlugin implements EventSubscriberInterface
{
const REDIRECT_COUNT = 'redirect.count';
const MAX_REDIRECTS = 'redirect.max';
const STRICT_REDIRECTS = 'redirect.strict';
const PARENT_REQUEST = 'redirect.parent_request';
const DISABLE = 'redirect.disable';
/**
* @var int Default number of redirects allowed when no setting is supplied by a request
*/
protected $defaultMaxRedirects = 5;
public static function getSubscribedEvents()
{
return array(
'request.sent' => array('onRequestSent', 100),
'request.clone' => 'cleanupRequest',
'request.before_send' => 'cleanupRequest'
);
}
/**
* Clean up the parameters of a request when it is cloned
*
* @param Event $event Event emitted
*/
public function cleanupRequest(Event $event)
{
$params = $event['request']->getParams();
unset($params[self::REDIRECT_COUNT]);
unset($params[self::PARENT_REQUEST]);
}
/**
* Called when a request receives a redirect response
*
* @param Event $event Event emitted
*/
public function onRequestSent(Event $event)
{
$response = $event['response'];
$request = $event['request'];
// Only act on redirect requests with Location headers
if (!$response || $request->getParams()->get(self::DISABLE)) {
return;
}
// Trace the original request based on parameter history
$original = $this->getOriginalRequest($request);
// Terminating condition to set the effective repsonse on the original request
if (!$response->isRedirect() || !$response->hasHeader('Location')) {
if ($request !== $original) {
// This is a terminating redirect response, so set it on the original request
$response->getParams()->set(self::REDIRECT_COUNT, $original->getParams()->get(self::REDIRECT_COUNT));
$original->setResponse($response);
$response->setEffectiveUrl($request->getUrl());
}
return;
}
$this->sendRedirectRequest($original, $request, $response);
}
/**
* Get the original request that initiated a series of redirects
*
* @param RequestInterface $request Request to get the original request from
*
* @return RequestInterface
*/
protected function getOriginalRequest(RequestInterface $request)
{
$original = $request;
// The number of redirects is held on the original request, so determine which request that is
while ($parent = $original->getParams()->get(self::PARENT_REQUEST)) {
$original = $parent;
}
return $original;
}
/**
* Create a redirect request for a specific request object
*
* Takes into account strict RFC compliant redirection (e.g. redirect POST with POST) vs doing what most clients do
* (e.g. redirect POST with GET).
*
* @param RequestInterface $request Request being redirected
* @param RequestInterface $original Original request
* @param int $statusCode Status code of the redirect
* @param string $location Location header of the redirect
*
* @return RequestInterface Returns a new redirect request
* @throws CouldNotRewindStreamException If the body needs to be rewound but cannot
*/
protected function createRedirectRequest(
RequestInterface $request,
$statusCode,
$location,
RequestInterface $original
) {
$redirectRequest = null;
$strict = $original->getParams()->get(self::STRICT_REDIRECTS);
// Use a GET request if this is an entity enclosing request and we are not forcing RFC compliance, but rather
// emulating what all browsers would do
if ($request instanceof EntityEnclosingRequestInterface && !$strict && $statusCode <= 302) {
$redirectRequest = RequestFactory::getInstance()->cloneRequestWithMethod($request, 'GET');
} else {
$redirectRequest = clone $request;
}
$redirectRequest->setIsRedirect(true);
// Always use the same response body when redirecting
$redirectRequest->setResponseBody($request->getResponseBody());
$location = Url::factory($location);
// If the location is not absolute, then combine it with the original URL
if (!$location->isAbsolute()) {
$originalUrl = $redirectRequest->getUrl(true);
// Remove query string parameters and just take what is present on the redirect Location header
$originalUrl->getQuery()->clear();
$location = $originalUrl->combine((string) $location);
}
$redirectRequest->setUrl($location);
// Add the parent request to the request before it sends (make sure it's before the onRequestClone event too)
$redirectRequest->getEventDispatcher()->addListener(
'request.before_send',
$func = function ($e) use (&$func, $request, $redirectRequest) {
$redirectRequest->getEventDispatcher()->removeListener('request.before_send', $func);
$e['request']->getParams()->set(RedirectPlugin::PARENT_REQUEST, $request);
}
);
// Rewind the entity body of the request if needed
if ($redirectRequest instanceof EntityEnclosingRequestInterface && $redirectRequest->getBody()) {
$body = $redirectRequest->getBody();
// Only rewind the body if some of it has been read already, and throw an exception if the rewind fails
if ($body->ftell() && !$body->rewind()) {
throw new CouldNotRewindStreamException(
'Unable to rewind the non-seekable entity body of the request after redirecting. cURL probably '
. 'sent part of body before the redirect occurred. Try adding acustom rewind function using on the '
. 'entity body of the request using setRewindFunction().'
);
}
}
return $redirectRequest;
}
/**
* Prepare the request for redirection and enforce the maximum number of allowed redirects per client
*
* @param RequestInterface $original Origina request
* @param RequestInterface $request Request to prepare and validate
* @param Response $response The current response
*
* @return RequestInterface
*/
protected function prepareRedirection(RequestInterface $original, RequestInterface $request, Response $response)
{
$params = $original->getParams();
// This is a new redirect, so increment the redirect counter
$current = $params[self::REDIRECT_COUNT] + 1;
$params[self::REDIRECT_COUNT] = $current;
// Use a provided maximum value or default to a max redirect count of 5
$max = isset($params[self::MAX_REDIRECTS]) ? $params[self::MAX_REDIRECTS] : $this->defaultMaxRedirects;
// Throw an exception if the redirect count is exceeded
if ($current > $max) {
$this->throwTooManyRedirectsException($original, $max);
return false;
} else {
// Create a redirect request based on the redirect rules set on the request
return $this->createRedirectRequest(
$request,
$response->getStatusCode(),
trim($response->getLocation()),
$original
);
}
}
/**
* Send a redirect request and handle any errors
*
* @param RequestInterface $original The originating request
* @param RequestInterface $request The current request being redirected
* @param Response $response The response of the current request
*
* @throws BadResponseException|\Exception
*/
protected function sendRedirectRequest(RequestInterface $original, RequestInterface $request, Response $response)
{
// Validate and create a redirect request based on the original request and current response
if ($redirectRequest = $this->prepareRedirection($original, $request, $response)) {
try {
$redirectRequest->send();
} catch (BadResponseException $e) {
$e->getResponse();
if (!$e->getResponse()) {
throw $e;
}
}
}
}
/**
* Throw a too many redirects exception for a request
*
* @param RequestInterface $original Request
* @param int $max Max allowed redirects
*
* @throws TooManyRedirectsException when too many redirects have been issued
*/
protected function throwTooManyRedirectsException(RequestInterface $original, $max)
{
$original->getEventDispatcher()->addListener(
'request.complete',
$func = function ($e) use (&$func, $original, $max) {
$original->getEventDispatcher()->removeListener('request.complete', $func);
$str = "{$max} redirects were issued for this request:\n" . $e['request']->getRawHeaders();
throw new TooManyRedirectsException($str);
}
);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1 +0,0 @@
47961e7ef15667c93cd99be01b51f00a

View File

@ -1,157 +0,0 @@
<?php
namespace Guzzle\Http;
use Guzzle\Http\Client;
use Guzzle\Http\ClientInterface;
use Guzzle\Stream\StreamRequestFactoryInterface;
use Guzzle\Stream\PhpStreamRequestFactory;
/**
* Simplified interface to Guzzle that does not require a class to be instantiated
*/
final class StaticClient
{
/** @var Client Guzzle client */
private static $client;
/**
* Mount the client to a simpler class name for a specific client
*
* @param string $className Class name to use to mount
* @param ClientInterface $client Client used to send requests
*/
public static function mount($className = 'Guzzle', ClientInterface $client = null)
{
class_alias(__CLASS__, $className);
if ($client) {
self::$client = $client;
}
}
/**
* @param string $method HTTP request method (GET, POST, HEAD, DELETE, PUT, etc)
* @param string $url URL of the request
* @param array $options Options to use with the request. See: Guzzle\Http\Message\RequestFactory::applyOptions()
* @return \Guzzle\Http\Message\Response|\Guzzle\Stream\Stream
*/
public static function request($method, $url, $options = array())
{
// @codeCoverageIgnoreStart
if (!self::$client) {
self::$client = new Client();
}
// @codeCoverageIgnoreEnd
$request = self::$client->createRequest($method, $url, null, null, $options);
if (isset($options['stream'])) {
if ($options['stream'] instanceof StreamRequestFactoryInterface) {
return $options['stream']->fromRequest($request);
} elseif ($options['stream'] == true) {
$streamFactory = new PhpStreamRequestFactory();
return $streamFactory->fromRequest($request);
}
}
return $request->send();
}
/**
* Send a GET request
*
* @param string $url URL of the request
* @param array $options Array of request options
*
* @return \Guzzle\Http\Message\Response
* @see Guzzle::request for a list of available options
*/
public static function get($url, $options = array())
{
return self::request('GET', $url, $options);
}
/**
* Send a HEAD request
*
* @param string $url URL of the request
* @param array $options Array of request options
*
* @return \Guzzle\Http\Message\Response
* @see Guzzle::request for a list of available options
*/
public static function head($url, $options = array())
{
return self::request('HEAD', $url, $options);
}
/**
* Send a DELETE request
*
* @param string $url URL of the request
* @param array $options Array of request options
*
* @return \Guzzle\Http\Message\Response
* @see Guzzle::request for a list of available options
*/
public static function delete($url, $options = array())
{
return self::request('DELETE', $url, $options);
}
/**
* Send a POST request
*
* @param string $url URL of the request
* @param array $options Array of request options
*
* @return \Guzzle\Http\Message\Response
* @see Guzzle::request for a list of available options
*/
public static function post($url, $options = array())
{
return self::request('POST', $url, $options);
}
/**
* Send a PUT request
*
* @param string $url URL of the request
* @param array $options Array of request options
*
* @return \Guzzle\Http\Message\Response
* @see Guzzle::request for a list of available options
*/
public static function put($url, $options = array())
{
return self::request('PUT', $url, $options);
}
/**
* Send a PATCH request
*
* @param string $url URL of the request
* @param array $options Array of request options
*
* @return \Guzzle\Http\Message\Response
* @see Guzzle::request for a list of available options
*/
public static function patch($url, $options = array())
{
return self::request('PATCH', $url, $options);
}
/**
* Send an OPTIONS request
*
* @param string $url URL of the request
* @param array $options Array of request options
*
* @return \Guzzle\Http\Message\Response
* @see Guzzle::request for a list of available options
*/
public static function options($url, $options = array())
{
return self::request('OPTIONS', $url, $options);
}
}

Some files were not shown because too many files have changed in this diff Show More