Removed some stuff

This commit is contained in:
Travis Swientek 2013-07-22 22:11:01 -07:00
parent a2f005a8f2
commit c09e6fd579
287 changed files with 494 additions and 31310 deletions

View File

@ -1,70 +0,0 @@
<?PHP
require('Mailgun/Common/Messages.php');
class MessagesClassTests extends PHPUnit_Framework_TestCase{
public function setUp(){ }
public function tearDown(){ }
public function testPropertySetGetMethods(){
$message = new Mailgun\Common\Message();
//Adds a recipient
$this->assertTrue($message->addToRecipient("travis@trstx.com", "travis swientek") == true);
//Checks if recipient was added to array.
$this->assertContains('travis swientek <travis@trstx.com>', $message->getToRecipients());
//Add a CC recipient
$this->assertTrue($message->addCcRecipient("travis@trstx.com", "travis swientek") == true);
//Checks if recipient was added to array.
$this->assertContains('travis swientek <travis@trstx.com>', $message->getCcRecipients());
//Add a BCC recipient
$this->assertTrue($message->addBccRecipient("travis@trstx.com", "travis swientek") == true);
//Checks if recipient was added to array.
$this->assertContains('travis swientek <travis@trstx.com>', $message->getBccRecipients());
//Add a From address
$this->assertTrue($message->setFromAddress("travis@trstx.com", "travis swientek") == true);
//Checks if recipient was added to array.
$this->assertContains('travis swientek <travis@trstx.com>', $message->getFromAddress());
//Set a subject for the email
$this->assertTrue($message->setSubject("This is my subject!") == true);
//Checks if subject is added
$this->assertEquals('This is my subject!', $message->getSubject());
//Fail to set a subject by calling an empty method
$this->assertTrue($message->setSubject() == false);
//If failing to set a subject, set the subject to "".
$this->assertEquals('', $message->getSubject());
//Set a Text body for the email
$this->assertTrue($message->setTextBody("This is my email text.") == true);
//Checks if Text body is added
$this->assertEquals("This is my email text.", $message->getTextBody());
//Set an HTML body for the email
$this->assertTrue($message->setHTMLBody("<html><head></head><body>This is my HTML email.</body></html>") == true);
//Checks if an HTML body is added
$this->assertEquals("<html><head></head><body>This is my HTML email.</body></html>", $message->getHTMLBody());
//Set a Campaign ID
$this->assertTrue($message->setCampaignId("My-Super-Awesome-Campaign") == true);
//Checks if Campaign ID exists
$this->assertContains("My-Super-Awesome-Campaign", $message->getCampaignId());
}
}
?>

View File

@ -1,7 +1,8 @@
{
"name": "mailgun/mailgun-php-client",
"require": {
"guzzle/guzzle": "3.7.1"
"guzzle/guzzle": "3.7.1",
"phpunit/phpunit": "3.7.*"
},
"authors": [
{

411
composer.lock generated
View File

@ -3,7 +3,7 @@
"This file locks the dependencies of your project to a known state",
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
],
"hash": "62c59562e5a0ddd54587218dba4a7386",
"hash": "7301f9087143937ee9fa2db35177d0ca",
"packages": [
{
"name": "guzzle/guzzle",
@ -97,6 +97,368 @@
],
"time": "2013-07-05 20:17:54"
},
{
"name": "phpunit/php-code-coverage",
"version": "1.2.12",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "1.2.12"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/1.2.12",
"reference": "1.2.12",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"phpunit/php-file-iterator": ">=1.3.0@stable",
"phpunit/php-text-template": ">=1.1.1@stable",
"phpunit/php-token-stream": ">=1.1.3@stable"
},
"require-dev": {
"phpunit/phpunit": "3.7.*@dev"
},
"suggest": {
"ext-dom": "*",
"ext-xdebug": ">=2.0.5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.2.x-dev"
}
},
"autoload": {
"classmap": [
"PHP/"
]
},
"notification-url": "https://packagist.org/downloads/",
"include-path": [
""
],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
"homepage": "https://github.com/sebastianbergmann/php-code-coverage",
"keywords": [
"coverage",
"testing",
"xunit"
],
"time": "2013-07-06 06:26:16"
},
{
"name": "phpunit/php-file-iterator",
"version": "1.3.3",
"source": {
"type": "git",
"url": "git://github.com/sebastianbergmann/php-file-iterator.git",
"reference": "1.3.3"
},
"dist": {
"type": "zip",
"url": "https://github.com/sebastianbergmann/php-file-iterator/zipball/1.3.3",
"reference": "1.3.3",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"autoload": {
"classmap": [
"File/"
]
},
"notification-url": "https://packagist.org/downloads/",
"include-path": [
""
],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "FilterIterator implementation that filters files based on a list of suffixes.",
"homepage": "http://www.phpunit.de/",
"keywords": [
"filesystem",
"iterator"
],
"time": "2012-10-11 04:44:38"
},
{
"name": "phpunit/php-text-template",
"version": "1.1.4",
"source": {
"type": "git",
"url": "git://github.com/sebastianbergmann/php-text-template.git",
"reference": "1.1.4"
},
"dist": {
"type": "zip",
"url": "https://github.com/sebastianbergmann/php-text-template/zipball/1.1.4",
"reference": "1.1.4",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"autoload": {
"classmap": [
"Text/"
]
},
"notification-url": "https://packagist.org/downloads/",
"include-path": [
""
],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Simple template engine.",
"homepage": "https://github.com/sebastianbergmann/php-text-template/",
"keywords": [
"template"
],
"time": "2012-10-31 11:15:28"
},
{
"name": "phpunit/php-timer",
"version": "1.0.4",
"source": {
"type": "git",
"url": "git://github.com/sebastianbergmann/php-timer.git",
"reference": "1.0.4"
},
"dist": {
"type": "zip",
"url": "https://github.com/sebastianbergmann/php-timer/zipball/1.0.4",
"reference": "1.0.4",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"autoload": {
"classmap": [
"PHP/"
]
},
"notification-url": "https://packagist.org/downloads/",
"include-path": [
""
],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Utility class for timing",
"homepage": "http://www.phpunit.de/",
"keywords": [
"timer"
],
"time": "2012-10-11 04:45:58"
},
{
"name": "phpunit/php-token-stream",
"version": "1.1.5",
"source": {
"type": "git",
"url": "git://github.com/sebastianbergmann/php-token-stream.git",
"reference": "1.1.5"
},
"dist": {
"type": "zip",
"url": "https://github.com/sebastianbergmann/php-token-stream/zipball/1.1.5",
"reference": "1.1.5",
"shasum": ""
},
"require": {
"ext-tokenizer": "*",
"php": ">=5.3.3"
},
"type": "library",
"autoload": {
"classmap": [
"PHP/"
]
},
"notification-url": "https://packagist.org/downloads/",
"include-path": [
""
],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Wrapper around PHP's tokenizer extension.",
"homepage": "http://www.phpunit.de/",
"keywords": [
"tokenizer"
],
"time": "2012-10-11 04:47:14"
},
{
"name": "phpunit/phpunit",
"version": "3.7.22",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "3.7.22"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3.7.22",
"reference": "3.7.22",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-pcre": "*",
"ext-reflection": "*",
"ext-spl": "*",
"php": ">=5.3.3",
"phpunit/php-code-coverage": "~1.2.1",
"phpunit/php-file-iterator": ">=1.3.1",
"phpunit/php-text-template": ">=1.1.1",
"phpunit/php-timer": "~1.0.2",
"phpunit/phpunit-mock-objects": "~1.2.0",
"symfony/yaml": "~2.0"
},
"require-dev": {
"pear-pear/pear": "1.9.4"
},
"suggest": {
"ext-json": "*",
"ext-simplexml": "*",
"ext-tokenizer": "*",
"phpunit/php-invoker": ">=1.1.0,<1.2.0"
},
"bin": [
"composer/bin/phpunit"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.7.x-dev"
}
},
"autoload": {
"classmap": [
"PHPUnit/"
]
},
"notification-url": "https://packagist.org/downloads/",
"include-path": [
"",
"../../symfony/yaml/"
],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "The PHP Unit Testing framework.",
"homepage": "http://www.phpunit.de/",
"keywords": [
"phpunit",
"testing",
"xunit"
],
"time": "2013-07-06 06:29:15"
},
{
"name": "phpunit/phpunit-mock-objects",
"version": "1.2.3",
"source": {
"type": "git",
"url": "git://github.com/sebastianbergmann/phpunit-mock-objects.git",
"reference": "1.2.3"
},
"dist": {
"type": "zip",
"url": "https://github.com/sebastianbergmann/phpunit-mock-objects/archive/1.2.3.zip",
"reference": "1.2.3",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"phpunit/php-text-template": ">=1.1.1@stable"
},
"suggest": {
"ext-soap": "*"
},
"type": "library",
"autoload": {
"classmap": [
"PHPUnit/"
]
},
"notification-url": "https://packagist.org/downloads/",
"include-path": [
""
],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Mock Object library for PHPUnit",
"homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
"keywords": [
"mock",
"xunit"
],
"time": "2013-01-13 10:24:48"
},
{
"name": "symfony/event-dispatcher",
"version": "v2.3.2",
@ -150,6 +512,53 @@
"description": "Symfony EventDispatcher Component",
"homepage": "http://symfony.com",
"time": "2013-05-13 14:36:40"
},
{
"name": "symfony/yaml",
"version": "v2.3.2",
"target-dir": "Symfony/Component/Yaml",
"source": {
"type": "git",
"url": "https://github.com/symfony/Yaml.git",
"reference": "v2.3.2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Yaml/zipball/v2.3.2",
"reference": "v2.3.2",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Yaml\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Yaml Component",
"homepage": "http://symfony.com",
"time": "2013-07-11 19:36:36"
}
],
"packages-dev": [

18
phpunit.xml.dist Normal file
View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="./tests/Bootstrap.php"
colors="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
testSuiteLoaderClass="PHPUnit_Runner_StandardTestSuiteLoader">
<testsuites>
<testsuite>
<directory>./tests/Mailgun/Tests</directory>
</testsuite>
</testsuites>
</phpunit>

View File

@ -2,9 +2,10 @@
namespace Mailgun\Common;
require_once 'Globals.php';
require_once 'globals.php';
use Guzzle\Http\Client as Guzzler;
use Mailgun\Exceptions\NoDomainsConfigured;
use Mailgun\Exceptions\HTTPError;

View File

@ -5,8 +5,6 @@
*/
namespace Mailgun\Common;
require_once 'Globals.php';
use Guzzle\Http\Client as Guzzler;
use Mailgun\Exceptions\NoDomainsConfigured;
use Mailgun\Exceptions\HTTPError;
@ -22,11 +20,8 @@ class Message{
protected $campaignIdCount;
protected $customOptionCount;
public function __construct($message = null){
public function __construct(){
$this->message = array();
if(isset($message)){
$this->message = $message;
}
$this->toRecipientCount = 0;
$this->ccRecipientCount = 0;
$this->bccRecipientCount = 0;

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);
}
}

View File

@ -1,543 +0,0 @@
<?php
namespace Guzzle\Http;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Parser\ParserRegistry;
/**
* Parses and generates URLs based on URL parts. In favor of performance, URL parts are not validated.
*/
class Url
{
protected $scheme;
protected $host;
protected $port;
protected $username;
protected $password;
protected $path = '';
protected $fragment;
/** @var QueryString Query part of the URL */
protected $query;
/**
* Factory method to create a new URL from a URL string
*
* @param string $url Full URL used to create a Url object
*
* @return Url
* @throws InvalidArgumentException
*/
public static function factory($url)
{
static $defaults = array('scheme' => null, 'host' => null, 'path' => null, 'port' => null, 'query' => null,
'user' => null, 'pass' => null, 'fragment' => null);
if (false === ($parts = parse_url($url))) {
throw new InvalidArgumentException('Was unable to parse malformed url: ' . $url);
}
$parts += $defaults;
// Convert the query string into a QueryString object
if ($parts['query'] || 0 !== strlen($parts['query'])) {
$parts['query'] = QueryString::fromString($parts['query']);
}
return new self($parts['scheme'], $parts['host'], $parts['user'],
$parts['pass'], $parts['port'], $parts['path'], $parts['query'],
$parts['fragment']);
}
/**
* Build a URL from parse_url parts. The generated URL will be a relative URL if a scheme or host are not provided.
*
* @param array $parts Array of parse_url parts
*
* @return string
*/
public static function buildUrl(array $parts)
{
$url = $scheme = '';
if (isset($parts['scheme'])) {
$scheme = $parts['scheme'];
$url .= $scheme . ':';
}
if (isset($parts['host'])) {
$url .= '//';
if (isset($parts['user'])) {
$url .= $parts['user'];
if (isset($parts['pass'])) {
$url .= ':' . $parts['pass'];
}
$url .= '@';
}
$url .= $parts['host'];
// Only include the port if it is not the default port of the scheme
if (isset($parts['port'])
&& !(($scheme == 'http' && $parts['port'] == 80) || ($scheme == 'https' && $parts['port'] == 443))
) {
$url .= ':' . $parts['port'];
}
}
// Add the path component if present
if (isset($parts['path']) && 0 !== strlen($parts['path'])) {
// Always ensure that the path begins with '/' if set and something is before the path
if ($url && $parts['path'][0] != '/' && substr($url, -1) != '/') {
$url .= '/';
}
$url .= $parts['path'];
}
// Add the query string if present
if (isset($parts['query'])) {
$url .= '?' . $parts['query'];
}
// Ensure that # is only added to the url if fragment contains anything.
if (isset($parts['fragment'])) {
$url .= '#' . $parts['fragment'];
}
return $url;
}
/**
* Create a new URL from URL parts
*
* @param string $scheme Scheme of the URL
* @param string $host Host of the URL
* @param string $username Username of the URL
* @param string $password Password of the URL
* @param int $port Port of the URL
* @param string $path Path of the URL
* @param QueryString|array|string $query Query string of the URL
* @param string $fragment Fragment of the URL
*/
public function __construct($scheme, $host, $username = null, $password = null, $port = null, $path = null, QueryString $query = null, $fragment = null)
{
$this->scheme = $scheme;
$this->host = $host;
$this->port = $port;
$this->username = $username;
$this->password = $password;
$this->fragment = $fragment;
if (!$query) {
$this->query = new QueryString();
} else {
$this->setQuery($query);
}
$this->setPath($path);
}
/**
* Clone the URL
*/
public function __clone()
{
$this->query = clone $this->query;
}
/**
* Returns the URL as a URL string
*
* @return string
*/
public function __toString()
{
return self::buildUrl($this->getParts());
}
/**
* Get the parts of the URL as an array
*
* @return array
*/
public function getParts()
{
return array(
'scheme' => $this->scheme,
'user' => $this->username,
'pass' => $this->password,
'host' => $this->host,
'port' => $this->port,
'path' => $this->getPath(),
'query' => (string) $this->query ?: null,
'fragment' => $this->fragment,
);
}
/**
* Set the host of the request.
*
* @param string $host Host to set (e.g. www.yahoo.com, yahoo.com)
*
* @return Url
*/
public function setHost($host)
{
if (strpos($host, ':') === false) {
$this->host = $host;
} else {
list($host, $port) = explode(':', $host);
$this->host = $host;
$this->setPort($port);
}
return $this;
}
/**
* Get the host part of the URL
*
* @return string
*/
public function getHost()
{
return $this->host;
}
/**
* Set the scheme part of the URL (http, https, ftp, etc)
*
* @param string $scheme Scheme to set
*
* @return Url
*/
public function setScheme($scheme)
{
$this->scheme = $scheme;
return $this;
}
/**
* Get the scheme part of the URL
*
* @return string
*/
public function getScheme()
{
return $this->scheme;
}
/**
* Set the port part of the URL
*
* @param int $port Port to set
*
* @return Url
*/
public function setPort($port)
{
$this->port = $port;
return $this;
}
/**
* Get the port part of the URl. Will return the default port for a given scheme if no port has been set.
*
* @return int|null
*/
public function getPort()
{
if ($this->port) {
return $this->port;
} elseif ($this->scheme == 'http') {
return 80;
} elseif ($this->scheme == 'https') {
return 443;
}
return null;
}
/**
* Set the path part of the URL
*
* @param array|string $path Path string or array of path segments
*
* @return Url
*/
public function setPath($path)
{
if (is_array($path)) {
$this->path = '/' . implode('/', $path);
} else {
$this->path = (string) $path;
}
return $this;
}
/**
* Normalize the URL so that double slashes and relative paths are removed
*
* @return Url
*/
public function normalizePath()
{
if (!$this->path || $this->path == '/' || $this->path == '*') {
return $this;
}
// Replace // and /./ with /
$this->path = str_replace(array('/./', '//'), '/', $this->path);
// Remove dot segments
if (strpos($this->path, '..') !== false) {
// Remove trailing relative paths if possible
$segments = $this->getPathSegments();
$last = end($segments);
$trailingSlash = false;
if ($last === '') {
array_pop($segments);
$trailingSlash = true;
}
while ($last == '..' || $last == '.') {
if ($last == '..') {
array_pop($segments);
$last = array_pop($segments);
}
if ($last == '.' || $last == '') {
$last = array_pop($segments);
}
}
$this->path = implode('/', $segments);
if ($trailingSlash) {
$this->path .= '/';
}
}
return $this;
}
/**
* Add a relative path to the currently set path
*
* @param string $relativePath Relative path to add
*
* @return Url
*/
public function addPath($relativePath)
{
if (!$relativePath || $relativePath == '/') {
return $this;
}
// Add a leading slash if needed
if ($relativePath[0] != '/') {
$relativePath = '/' . $relativePath;
}
return $this->setPath(str_replace('//', '/', $this->getPath() . $relativePath));
}
/**
* Get the path part of the URL
*
* @return string
*/
public function getPath()
{
return $this->path;
}
/**
* Get the path segments of the URL as an array
*
* @return array
*/
public function getPathSegments()
{
return array_slice(explode('/', $this->getPath()), 1);
}
/**
* Set the password part of the URL
*
* @param string $password Password to set
*
* @return Url
*/
public function setPassword($password)
{
$this->password = $password;
return $this;
}
/**
* Get the password part of the URL
*
* @return null|string
*/
public function getPassword()
{
return $this->password;
}
/**
* Set the username part of the URL
*
* @param string $username Username to set
*
* @return Url
*/
public function setUsername($username)
{
$this->username = $username;
return $this;
}
/**
* Get the username part of the URl
*
* @return null|string
*/
public function getUsername()
{
return $this->username;
}
/**
* Get the query part of the URL as a QueryString object
*
* @return QueryString
*/
public function getQuery()
{
return $this->query;
}
/**
* Set the query part of the URL
*
* @param QueryString|string|array $query Query to set
*
* @return Url
*/
public function setQuery($query)
{
if (is_string($query)) {
$output = null;
parse_str($query, $output);
$this->query = new QueryString($output);
} elseif (is_array($query)) {
$this->query = new QueryString($query);
} elseif ($query instanceof QueryString) {
$this->query = $query;
}
return $this;
}
/**
* Get the fragment part of the URL
*
* @return null|string
*/
public function getFragment()
{
return $this->fragment;
}
/**
* Set the fragment part of the URL
*
* @param string $fragment Fragment to set
*
* @return Url
*/
public function setFragment($fragment)
{
$this->fragment = $fragment;
return $this;
}
/**
* Check if this is an absolute URL
*
* @return bool
*/
public function isAbsolute()
{
return $this->scheme && $this->host;
}
/**
* Combine the URL with another URL. Follows the rules specific in RFC 3986 section 5.4.
*
* @param string $url Relative URL to combine with
*
* @return Url
* @throws InvalidArgumentException
* @link http://tools.ietf.org/html/rfc3986#section-5.4
*/
public function combine($url)
{
$url = self::factory($url);
// Use the more absolute URL as the base URL
if (!$this->isAbsolute() && $url->isAbsolute()) {
$url = $url->combine($this);
}
// Passing a URL with a scheme overrides everything
if ($buffer = $url->getScheme()) {
$this->scheme = $buffer;
$this->host = $url->getHost();
$this->port = $url->getPort();
$this->username = $url->getUsername();
$this->password = $url->getPassword();
$this->path = $url->getPath();
$this->query = $url->getQuery();
$this->fragment = $url->getFragment();
return $this;
}
// Setting a host overrides the entire rest of the URL
if ($buffer = $url->getHost()) {
$this->host = $buffer;
$this->port = $url->getPort();
$this->username = $url->getUsername();
$this->password = $url->getPassword();
$this->path = $url->getPath();
$this->fragment = $url->getFragment();
return $this;
}
$path = $url->getPath();
$query = $url->getQuery();
if (!$path) {
if (count($query)) {
$this->query = $query;
}
} else {
if ($path[0] == '/') {
$this->path = $path;
} else {
$this->path .= '/' . $path;
}
$this->normalizePath();
$this->query = $query;
}
$this->fragment = $url->getFragment();
return $this;
}
}

View File

@ -1,32 +0,0 @@
{
"name": "guzzle/http",
"description": "HTTP libraries used by Guzzle",
"homepage": "http://guzzlephp.org/",
"keywords": ["http client", "http", "client", "Guzzle", "curl"],
"license": "MIT",
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"require": {
"php": ">=5.3.2",
"guzzle/common": "self.version",
"guzzle/parser": "self.version",
"guzzle/stream": "self.version"
},
"suggest": {
"ext-curl": "*"
},
"autoload": {
"psr-0": { "Guzzle\\Http": "" }
},
"target-dir": "Guzzle/Http",
"extra": {
"branch-alias": {
"dev-master": "3.7-dev"
}
}
}

View File

@ -1,38 +0,0 @@
<?php
namespace Guzzle\Inflection;
/**
* Default inflection implementation
*/
class Inflector implements InflectorInterface
{
/** @var InflectorInterface */
protected static $default;
/**
* Get the default inflector object that has support for caching
*
* @return MemoizingInflector
*/
public static function getDefault()
{
// @codeCoverageIgnoreStart
if (!self::$default) {
self::$default = new MemoizingInflector(new self());
}
// @codeCoverageIgnoreEnd
return self::$default;
}
public function snake($word)
{
return ctype_lower($word) ? $word : strtolower(preg_replace('/(.)([A-Z])/', "$1_$2", $word));
}
public function camel($word)
{
return str_replace(' ', '', ucwords(strtr($word, '_-', ' ')));
}
}

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