.gitignore000064400000000247151742733540006552 0ustar00.DS_STORE coverage phpunit.xml build/guzzle.phar build/guzzle-na.phar src/Guzzle/Aws/ src/Guzzle/CardinalCommerce/ src/Guzzle/Unfuddle/ composer.lock vendor/.composer .gitmodules000064400000000520151742733540006731 0ustar00[submodule "vendor/Symfony/Component/ClassLoader"] path = vendor/Symfony/Component/ClassLoader url = https://github.com/symfony/ClassLoader [submodule "vendor/Doctrine"] path = vendor/Doctrine url = https://github.com/doctrine/common [submodule "vendor/Monolog"] path = vendor/Monolog url = https://github.com/Seldaek/monolog.git .travis.yml000064400000000375151742733540006675 0ustar00language: php php: - 5.3 - 5.4 before_script: - git submodule update --init - cp phpunit.xml.dist phpunit.xml - ~/.nvm/v0.6.1/bin/node tests/Guzzle/Tests/Http/server.js & script: sudo phpunit notifications: email: - michael@guzzlephp.orgLICENSE000064400000002072151742733540005565 0ustar00Copyright (c) 2011 Michael Dowling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.README.md000064400000011024151742733540006034 0ustar00Guzzle, PHP HTTP client and webservice framework =============================================== Guzzle is a game changer in the world of PHP HTTP clients. Guzzle allows you to truly reap the benefits of the HTTP/1.1 spec. No other library provides persistent connection management or makes it easier to send requests in parallel.

In addition to taking the pain out of HTTP, Guzzle provides a lightweight framework for creating web service clients. Most web service clients follow a specific pattern: create a client class, create methods for each action, create and execute a cURL handle, parse the response, implement error handling, and return the result. Guzzle takes the redundancy out of this process and gives you the tools you need to quickly build a web service client. Start truly consuming HTTP with Guzzle. - Download the phar to get started: http://www.guzzlephp.org/phar - Docs: [www.guzzlephp.org](http://www.guzzlephp.org/) - Forum: https://groups.google.com/forum/?hl=en#!forum/guzzle [![Build Status](https://secure.travis-ci.org/guzzle/guzzle.png)](http://travis-ci.org/guzzle/guzzle) Features -------- - Supports GET, HEAD, POST, DELETE, PUT, and OPTIONS - Allows full access to request and response headers - Persistent connections are implicitly managed by Guzzle, resulting in huge performance benefits - Send requests in parallel - Cookie sessions can be maintained between requests using the CookiePlugin - Allows custom entity bodies to be sent in PUT and POST requests, including sending data from a PHP stream - Responses can be cached and served from cache using the caching reverse proxy plugin - Failed requests can be retried using truncated exponential backoff - Entity bodies can be validated automatically using Content-MD5 headers - All data sent over the wire can be logged using the LogPlugin - Automatically requests compressed data and automatically decompresses data - Subject/Observer signal slot system for unobtrusively modifying request behavior - Supports all of the features of libcurl including authentication, redirects, SSL, proxies, etc - Web service client framework for building future-proof interfaces to web services HTTP basics ----------- ```php '***' )); // Issue a path using a relative URL to the client's base URL // Sends to http://www.example.com/api/v1/key/***/users $request = $client->get('users'); $response = $request->send(); // Relative URL that overwrites the path of the base URL $request = $client->get('/test/123.php?a=b'); // Issue a head request on the base URL $response = $client->head()->send(); // Delete user 123 $response = $client->delete('users/123')->send(); // Send a PUT request with custom headers $response = $client->put('upload/text', array( 'X-Header' => 'My Header' ), 'body of the request')->send(); // Send a PUT request using the contents of a PHP stream as the body // Send using an absolute URL (overrides the base URL) $response = $client->put('http://www.example.com/upload', array( 'X-Header' => 'My Header' ), fopen('http://www.test.com/', 'r')); // Create a POST request with a file upload (notice the @ symbol): $request = $client->post('http://localhost:8983/solr/update', null, array ( 'custom_field' => 'my value', 'file' => '@/path/to/documents.xml' )); // Create a POST request and add the POST files manually $request = $client->post('http://localhost:8983/solr/update') ->addPostFiles(array( 'file' => '/path/to/documents.xml' )); // Responses are objects echo $response->getStatusCode() . ' ' . $response->getReasonPhrase() . "\n"; // Requests and responses can be cast to a string to show the raw HTTP message echo $request . "\n\n" . $response; // Create a request based on an HTTP message $request = RequestFactory::fromMessage( "PUT / HTTP/1.1\r\n" . "Host: test.com:8081\r\n" . "Content-Type: text/plain" "Transfer-Encoding: chunked\r\n" . "\r\n" . "this is the body" ); ``` Send requests in parallel ------------------------- ```php batch(array( $client->get('http://www.google.com/'), $client->head('http://www.google.com/'), $client->get('https://www.github.com/') )); } catch (PoolRequestException $e) { echo "The following requests encountered an exception: \n"; foreach ($e as $exception) { echo $exception->getRequest() . "\n" . $exception->getMessage() . "\n"; } } ```build/autoload.php000064400000001275151742733540010204 0ustar00 composer.json000064400000001216151742733540007301 0ustar00{ "name": "guzzle/guzzle", "type": "library", "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", "keywords": ["framework", "http", "rest"], "homepage": "http://www.guzzlephp.org/", "license": "MIT", "authors": [ { "name": "Michael Dowling", "email": "michael@guzzlephp.org" }, { "name": "Guzzle Community", "homepage": "https://github.com/guzzle/guzzle/contributors" } ], "require": { "php": ">=5.3.2" }, "autoload": { "psr-0": { "Guzzle": "src/" } } }phpunit.xml.dist000064400000001763151742733540007741 0ustar00 ./tests/Guzzle/Tests ./src/Guzzle ./src/Guzzle ./src/Guzzle/Common/Event/Subject.php ./src/Guzzle/Common/GuzzleException.php ./src/Guzzle/Http/HttpException.php src/Guzzle/Common/Cache/AbstractCacheAdapter.php000064400000000754151742733540015570 0ustar00 */ abstract class AbstractCacheAdapter implements CacheAdapterInterface { /** * @var mixed Cache object that is wrapped by the adapter */ protected $cache; /** * Get the cache object * * @return mixed */ public function getCacheObject() { return $this->cache; } }src/Guzzle/Common/Cache/CacheAdapterInterface.php000064400000003171151742733540015721 0ustar00 */ interface CacheAdapterInterface { /** * Test if an entry exists in the cache. * * @param string $id cache id The cache id of the entry to check for. * * @return bool TRUE if a cache entry exists for the given cache id, * FALSE otherwise. */ function contains($id); /** * Deletes a cache entry. * * @param string $id cache id * * @return bool TRUE on success, FALSE on failure */ function delete($id); /** * Fetches an entry from the cache. * * @param string $id cache id The id of the cache entry to fetch. * * @return string The cached data or FALSE, if no cache entry exists * for the given id. */ function fetch($id); /** * Get the wrapped cache object * * @return mixed */ function getCacheObject(); /** * Puts data into the cache. * * @param string $id The cache id. * @param string $data The cache entry/data. * @param int $lifeTime The lifetime. If != false, sets a specific lifetime * for this cache entry (null => infinite lifeTime). * * @return bool TRUE if the entry was successfully stored in the cache, * FALSE otherwise. */ function save($id, $data, $lifeTime = false); }src/Guzzle/Common/Cache/DoctrineCacheAdapter.php000064400000003453151742733540015573 0ustar00 */ class DoctrineCacheAdapter extends AbstractCacheAdapter { /** * Constructor * * @param Cache $cacheObject Object to wrap and adapt */ public function __construct(Cache $cache) { $this->cache = $cache; } /** * Test if an entry exists in the cache. * * @param string $id cache id The cache id of the entry to check for. * * @return bool TRUE if a cache entry exists for the given cache id, * FALSE otherwise. */ public function contains($id) { return $this->cache->contains($id); } /** * Deletes a cache entry. * * @param string $id cache id * * @return bool TRUE on success, FALSE on failure */ public function delete($id) { return $this->cache->delete($id); } /** * Fetches an entry from the cache. * * @param string $id cache id The id of the cache entry to fetch. * * @return string The cached data or FALSE, if no cache entry exists for * the given id. */ public function fetch($id) { return $this->cache->fetch($id); } /** * Puts data into the cache. * * @param string $id The cache id. * @param string $data The cache entry/data. * @param int $lifeTime The lifetime. If != false, sets a specific lifetime * for this cache entry (null => infinite lifeTime). * * @return bool TRUE on success, FALSE on failure */ public function save($id, $data, $lifeTime = false) { return $this->cache->save($id, $data, $lifeTime); } }src/Guzzle/Common/Cache/ZendCacheAdapter.php000064400000003536151742733540014726 0ustar00 */ class ZendCacheAdapter extends AbstractCacheAdapter { /** * Constructor * * @param Zend_Cache_Backend $cacheObject Object to wrap and adapt */ public function __construct(\Zend_Cache_Backend $cache) { $this->cache = $cache; } /** * Test if an entry exists in the cache. * * @param string $id cache id The cache id of the entry to check for. * * @return bool TRUE if a cache entry exists for the given cache id, * FALSE otherwise. */ public function contains($id) { return $this->cache->test($id); } /** * Deletes a cache entry. * * @param string $id cache id * * @return bool TRUE on success, FALSE on failure */ public function delete($id) { return $this->cache->remove($id); } /** * Fetches an entry from the cache. * * @param string $id cache id The id of the cache entry to fetch. * * @return string The cached data or FALSE, if no cache entry exists for * the given id. */ public function fetch($id) { return $this->cache->load($id); } /** * Puts data into the cache. * * @param string $id The cache id. * @param string $data The cache entry/data. * @param int $lifeTime The lifetime. If != false, sets a specific lifetime * for this cache entry (null => infinite lifeTime). * * @return bool TRUE if the entry was successfully stored in the cache, * FALSE otherwise. */ public function save($id, $data, $lifeTime = false) { return $this->cache->save($data, $id, array(), $lifeTime); } }src/Guzzle/Common/Collection.php000064400000026642151742733540012674 0ustar00 */ class Collection implements \ArrayAccess, \IteratorAggregate, \Countable { const MATCH_EXACT = 0; const MATCH_IGNORE_CASE = 1; const MATCH_REGEX = 2; /** * @var array Data associated with the object. */ protected $data = array(); /** * Constructor * * @param array $data Associative array of data to set */ public function __construct(array $data = null) { if ($data) { $this->data = $data; } } /** * Convert the object to a string * * @return string */ public function __toString() { return __CLASS__ . '@' . spl_object_hash($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; } else { if (!is_array($this->data[$key])) { $this->data[$key] = array( $this->data[$key], $value ); } else { $this->data[$key][] = $value; } } return $this; } /** * Removes all key value pairs * * @return Collection */ public function clear() { $this->data = array(); return $this; } /** * Return the number of keys * * @return integer */ public function count() { return count($this->data); } /** * 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 object * * @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; } /** * Get an iterator object * * @return array */ public function getIterator() { return new \ArrayIterator($this->data); } /** * Get a specific key value. * * @param string $key Key to retrieve. $key can be a string or a * regular expression. * @param mixed $default (optional) If the key is not found, set this * value to specify a default * @param int $match (optional) Bitwise match setting: * 0 - Exact match * 1 - Case insensitive match * 2 - Regular expression match * * @return mixed|null Value of the key or NULL */ public function get($key, $default = null, $match = self::MATCH_EXACT) { if ($match == self::MATCH_EXACT) { if (array_key_exists($key, $this->data)) { return $this->data[$key]; } } else if ($match == self::MATCH_IGNORE_CASE) { foreach ($this->data as $k => $value) { if (strcasecmp($k, $key) === 0) { return $value; } } } else if ($match == self::MATCH_REGEX) { foreach ($this->data as $k => $value) { if (preg_match($key, $k)) { return $value; } } } return $default; } /** * Get all or a subset of matching key value pairs * * @param array|string|int $keys (optional) Pass an array of keys to * retrieve only a particular subset of kvp. * @param int $match (optional) Bitwise key match setting: * 0 - Exact match * 1 - Case insensitive match * 2 - Regular expression match * * @return array Returns an array of all key value pairs if no $keys array * is specified, or an array of only the key value pairs matching the * values in the $keys array. */ public function getAll($keys = null, $match = false) { if (!$keys) { return $this->data; } $matches = array(); $allKeys = $this->getKeys(); foreach ((array) $keys as $expression) { if ($match == self::MATCH_EXACT) { if (in_array($expression, $allKeys)) { $matches[$expression] = $this->data[$expression]; } } else if ($match == self::MATCH_IGNORE_CASE) { foreach ($allKeys as $key) { if (strcasecmp($expression, $key) === 0) { $matches[$key] = $this->data[$key]; } } } else if ($match == self::MATCH_REGEX) { foreach ($allKeys as $key) { if (preg_match($expression, $key)) { $matches[$key] = $this->data[$key]; } } } } return $matches; } /** * Get all or a subset of matching keys * * @param string $regexp (optional) Pass a regular expression to return * only keys matching the expression. * * @return array Returns an array of matching keys */ public function getKeys($regexp = null) { $keys = array_keys($this->data); // If a regular expression was set, filter the keys if ($regexp) { $keys = array_filter($keys, function($key) use ($regexp) { return preg_match($regexp, $key); }); } return $keys; } /** * Returns whether or not the specified key is present. * * @param string $key The key for which to check the existence. * @param int $match (optional) Bitwise key match setting: * 0 - Exact match * 1 - Case insensitive match * 2 - Regular expression match * * @return int|string Returns the key value if the key is present or FALSE * if the key is not. Use === matching to check if false. */ public function hasKey($key, $match = self::MATCH_EXACT) { if ($match == self::MATCH_EXACT) { if (array_key_exists($key, $this->data)) { return $key; } } else if ($match == self::MATCH_IGNORE_CASE) { foreach (array_keys($this->data) as $k) { if (strcasecmp($k, $key) === 0) { return $k; } } } else if ($match == self::MATCH_REGEX) { foreach (array_keys($this->data) as $k) { if (preg_match($key, $k)) { 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); } /** * 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 (optional) 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 object * * @return Collection */ public function map(\Closure $closure, array $context = array(), $static = true) { $collection = ($static) ? new static() : new self(); foreach ($this->getIterator() as $key => $value) { $collection->add($key, $closure($key, $value, $context)); } return $collection; } /** * Add and merge in a Collection or array of key value pair data. * * Invalid $data arguments will silently fail. * * @param Collection|array $data Associative array of key value pair data * * @return Collection Returns a reference to the object. */ public function merge($data) { if ($data instanceof self) { $data = $data->getAll(); } else if (!is_array($data)) { return $this; } if (count($data)) { if (!count($this->data)) { $this->data = $data; } else { foreach ($data as $key => $value) { $this->add($key, $value); } } } return $this; } /** * ArrayAccess implementation of offsetExists() * * @see hasKey() */ public function offsetExists($offset) { return $this->hasKey($offset) !== false; } /** * ArrayAccess implementation of offsetGet() * * @see get() */ public function offsetGet($offset) { return $this->get($offset); } /** * ArrayAccess implementation of offsetGet() * * @see set() */ public function offsetSet($offset, $value) { return $this->set($offset, $value); } /** * ArrayAccess implementation of offsetUnset() * * @see remove() */ public function offsetUnset($offset) { return $this->remove($offset); } /** * Remove a specific key value pair * * @param array|string $key A key, regexp, or array of keys to remove * @param int $match (optional) Bitwise key match setting: * 0 - Exact match * 1 - Case insensitive match * 2 - Regular expression match * * @return Collection Returns a reference to the object */ public function remove($key, $match = self::MATCH_EXACT) { foreach ((array) $key as $k) { $matched = $this->hasKey($k, $match); if ($matched !== false) { unset($this->data[$matched]); } } return $this; } /** * 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; } /** * 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; } }src/Guzzle/Common/Event/AbstractSubject.php000064400000001052151742733540014731 0ustar00 */ abstract class AbstractSubject implements Subject { /** * @var EventManager */ protected $eventManager; /** * Get the subject mediator associated with the subject * * @return EventManager */ public function getEventManager() { if (!$this->eventManager) { $this->eventManager = new EventManager($this); } return $this->eventManager; } }src/Guzzle/Common/Event/EventManager.php000064400000015436151742733540014235 0ustar00 */ class EventManager { /** * @var Subject Mediated {@see Subject} to connect with {@see Observer}s */ protected $subject; /** * @var array Array of {@see Observer} objects. */ protected $observers = array(); /** * @var array Array of observer priorities */ protected $priorities = array(); /** * Construct a new EventManager * * @param Subject Subject colleague object */ public function __construct(Subject $subject) { $this->subject = $subject; } /** * Attach a new observer. * * @param Observer|Closure $observer Object that observes the subject. * @param int $priority (optional) Priority to attach to the subject. The * higher the priority, the sooner it will be notified * * @return Observer|Closure Returns the $observer that was attached * @throws InvalidArgumentException if the observer is not a Closure or Observer */ public function attach($observer, $priority = 0) { if (!($observer instanceof \Closure) && !($observer instanceof Observer)) { throw new \InvalidArgumentException( 'Observer must be a Closure or Observer object' ); } if (!$this->hasObserver($observer)) { $hash = spl_object_hash($observer); $this->observers[$hash] = $observer; $this->priorities[$hash] = $priority; $priorities = $this->priorities; // Sort the events by priority uasort($this->observers, function($a, $b) use ($priorities) { $priority1 = $priorities[spl_object_hash($a)]; $priority2 = $priorities[spl_object_hash($b)]; if ($priority1 === $priority2) { return 0; } else if ($priority1 > $priority2) { return -1; } else { return 1; } }); // Notify the observer that it is being attached to the subject $this->notifyObserver($observer, 'event.attach'); } return $observer; } /** * Detach an observer. * * @param Observer|Closure $observer Observer to detach. * * @return Observer Returns the $observer that was detached. */ public function detach($observer) { if (is_object($observer)) { $hash = spl_object_hash($observer); if (isset($this->observers[$hash])) { // Notify the observer that it is being detached $this->notifyObserver($observer, 'event.detach'); unset($this->priorities[$hash]); unset($this->observers[$hash]); } } return $observer; } /** * Detach all observers. * * @return array Returns an array of the detached observers */ public function detachAll() { $detached = array_values($this->observers); foreach ($detached as $o) { $this->detach($o); } return $detached; } /** * Notify all observers of an event * * @param string $event (optional) Event signal to emit * @param mixed $context (optional) Context of the event * @param bool $until (optional) Set to TRUE to stop event propagation when * one of the observers returns TRUE * * @return array Returns an array containing the response of each observer */ public function notify($event, $context = null, $until = false) { $responses = array(); foreach ($this->observers as $observer) { $result = $this->notifyObserver($observer, $event, $context); if ($result) { $responses[] = $result; if ($until == true) { break; } } } return $responses; } /** * Get all attached observers. * * @param string $byName (optional) Pass the name of a class to retrieve * only observers that are an instance of a particular class. * * @return array Returns an array containing the matching observers. The * returned array may or may not be empty. */ public function getAttached($byName = null) { if (!$byName) { return array_values($this->observers); } return array_values(array_filter($this->observers, function($o) use ($byName) { return $o instanceof $byName; })); } /** * Get the mediated {@see Subject} or NULL if no Subject has been associated * * @return Subject|null */ public function getSubject() { return $this->subject; } /** * Check if a certain observer or type of observer is attached * * @param string|Observer|Closure $observer Observer to check for. Pass the * name of an observer, a concrete {@see Observer}, or Closure * * @return bool */ public function hasObserver($observer) { if (is_object($observer)) { return isset($this->observers[spl_object_hash($observer)]); } else if (is_string($observer)) { foreach ($this->observers as $item) { if ($item instanceOf $observer) { return true; } } } return false; } /** * Get the priority level that an observer was attached at * * @param object $observer Observer to get the priority level of * * @return int|null Returns the priortity level or NULl if not attached */ public function getPriority($observer) { if (is_object($observer)) { $hash = spl_object_hash($observer); if (array_key_exists($hash, $this->priorities)) { return $this->priorities[$hash]; } } return null; } /** * Notify a single observer of an event * * @param Closure|Observer $observer Observer to notify * @param string $event Event signal to send to the observer * @param mixed $context (optional) Context about the event * * @return mixed */ protected function notifyObserver($observer, $event, $context = null) { if ($observer instanceof Observer) { return $observer->update($this->subject, $event, $context); } else { return $observer($this->subject, $event, $context); } } }src/Guzzle/Common/Event/Observer.php000064400000000752151742733540013443 0ustar00 */ interface Observer { /** * Receive notifications from a EventManager * * @param Subject $subject Subject emitting the event * @param string $event Event signal state * @param mixed $context (optional) Contextual information * * @return null|bool|mixed */ function update(Subject $subject, $event, $context = null); }src/Guzzle/Common/Event/Subject.php000064400000000445151742733540013252 0ustar00 */ interface Subject { /** * Get the subject mediator associated with the subject * * @return EventManager */ function getEventManager(); }src/Guzzle/Common/Filter/AbstractFilter.php000064400000003674151742733540014737 0ustar00data = $parameters->getAll(); } else { parent::__construct($parameters); } $this->init(); } /** * Process the command object. * * @param mixed $command Value to process. The command can be any type of * variable. It is the responsibility of concrete filters to ensure * that the passed command is of the correct type. * * @return bool Returns TRUE on success or FALSE on failure. */ public function process($command) { $typeHint = $this->get('type_hint'); if ($typeHint && !($command instanceof $typeHint)) { return false; } return $this->filterCommand($command); } /** * Filter the request and handle as needed. * * This method is a hook to be implemented in subclasses * * @param mixed $command The object to process * * @return bool Returns TRUE on success or FALSE on failure */ abstract protected function filterCommand($command); /** * Initialize the filter. * * This method is a hook to be implemented in subclasses that handles * initializing the filter. * * @return void */ protected function init() { return; } }src/Guzzle/Common/Filter/ArrayFilter.php000064400000000720151742733540014237 0ustar00 */ class ArrayFilter extends AbstractFilter { /** * {@inheritdoc} */ protected function filterCommand($command) { if (!is_array($command)) { return 'The supplied value is not an array: ' . gettype($command) . ' supplied'; } return true; } }src/Guzzle/Common/Filter/BooleanFilter.php000064400000001420151742733540014536 0ustar00 */ class BooleanFilter extends AbstractFilter { /** * {@inheritdoc} */ protected function filterCommand($command) { if (!($command === 1 || $command === 0 || $command === true || $command === false || $command === 'true' || $command === 'false')) { if (is_scalar($command)) { return 'The supplied value is not a Boolean: ' . (string) $command . ' supplied'; } return 'The supplied value is not a Boolean: ' . gettype($command) . ' supplied'; } return true; } }src/Guzzle/Common/Filter/ClassFilter.php000064400000001753151742733540014235 0ustar00 */ class ClassFilter extends AbstractFilter { /** * {@inheritdoc} */ protected function filterCommand($command) { if ($this->get(0) && is_string($this->get(0))) { $class = $this->get(0); $valid = ($command instanceof $class); } else { $valid = is_object($command); $class = 'stdClass'; } if (!$valid) { $error = 'The supplied value is not an instance of ' . $class . ': %s supplied'; if (!is_object($command)) { $error = sprintf($error, '<' . gettype($command) . ':' . (string) $command . '>'); } else { $error = sprintf($error, get_class($command)); } return $error; } return true; } }src/Guzzle/Common/Filter/ClosureFilter.php000064400000001244151742733540014577 0ustar00 */ class ClosureFilter extends AbstractFilter { /** * @var Closure closure method used to process the command */ private $closure; /** * Create a new closure filter */ public function __construct(\Closure $closure) { parent::__construct(); $this->closure = $closure; } /** * {@inheritdoc} */ protected function filterCommand($command) { return call_user_func($this->closure, $command); } }src/Guzzle/Common/Filter/DateFilter.php000064400000001400151742733540014032 0ustar00 */ class DateFilter extends AbstractFilter { /** * {@inheritdoc} */ protected function filterCommand($command) { if (!is_scalar($command)) { return 'The supplied value is not a valid date: ' . gettype($command) . ' supplied'; } else { $s = strtotime($command); if (false === $s || !checkdate(date('m', $s), date('d', $s), date('Y', $s))) { return 'The supplied value is not a valid date: ' . (string) $command . ' supplied'; } } return true; } }src/Guzzle/Common/Filter/EnumFilter.php000064400000001733151742733540014072 0ustar00 */ class EnumFilter extends AbstractFilter { /** * {@inheritdoc} */ protected function filterCommand($command) { $filter = true; if ($this->get(0)) { $list = array_map('trim', explode(',', $this->get(0))); if (!in_array($command, $list)) { $error = 'The supplied argument was not found in the list of ' . 'acceptable values (' . implode(', ', $list) . '): %s supplied'; if (is_scalar($command)) { $filter = sprintf($error, '<' . gettype($command) . ':' . $command . '>'); } else { $filter = sprintf($error, gettype($command)); } } } return $filter; } }src/Guzzle/Common/Filter/FilterInterface.php000064400000001044151742733540015061 0ustar00 */ interface FilterInterface { /** * Process the command object. * * @param mixed $command Value to process. The command can be any type of * variable. It is the responsibility of concrete filters to ensure * that the passed command is of the correct type. * * @return bool Returns TRUE on success or FALSE on failure. */ function process($command); }src/Guzzle/Common/Filter/FloatFilter.php000064400000000727151742733540014235 0ustar00 */ class FloatFilter extends AbstractFilter { /** * {@inheritdoc} */ protected function filterCommand($command) { if (!is_numeric($command)) { return 'The supplied value is not a valid float: ' . gettype($command) . ' supplied'; } return true; } }src/Guzzle/Common/Filter/IntegerFilter.php000064400000001001151742733540014547 0ustar00 */ class IntegerFilter extends AbstractFilter { /** * {@inheritdoc} */ protected function filterCommand($command) { if (!is_numeric($command) || strpos($command, '.') !== false) { return 'The supplied value is not a valid integer: ' . (string) $command . ' supplied'; } return true; } }src/Guzzle/Common/Filter/RegexFilter.php000064400000001506151742733540014236 0ustar00 */ class RegexFilter extends AbstractFilter { /** * {@inheritdoc} */ protected function filterCommand($command) { if (!is_string($command)) { return 'The supplied argument must be a string to match the ' . 'RegexFilter: ' . gettype($command) . ' supplied'; } $regex = $this->get(0); if (!$regex || !is_string($regex)) { return true; } if (!preg_match($regex, $command)) { return 'The supplied argument did not match the regular expression ' . $regex . ': ' . $command . ' supplied'; } return true; } }src/Guzzle/Common/Filter/StringFilter.php000064400000000723151742733540014432 0ustar00 */ class StringFilter extends AbstractFilter { /** * {@inheritdoc} */ protected function filterCommand($command) { if (!is_string($command)) { return 'The supplied value is not a string: ' . gettype($command) . ' supplied'; } return true; } }src/Guzzle/Common/Filter/TimestampFilter.php000064400000001436151742733540015131 0ustar00 */ class TimestampFilter extends AbstractFilter { /** * {@inheritdoc} */ protected function filterCommand($command) { $timestamp = is_numeric($command) && $command > 0 ? date('Y-m-d', (int) $command) : false; if (false === $timestamp) { if (is_scalar($command)) { return 'The supplied value is not a valid timestamp: ' . (string) $command . ' supplied'; } else { return 'The supplied value is not a valid timestamp: ' . gettype($command) . ' supplied'; } } return true; } }src/Guzzle/Common/GuzzleException.php000064400000000220151742733540013720 0ustar00 */ interface GuzzleException { }src/Guzzle/Common/Inflector.php000064400000005117151742733540012520 0ustar00 */ class Inflector { /** * @var int Cap each internal cache */ const MAX_ENTRIES_PER_CACHE = 1000; /** * @var array snake_case transformation cache */ protected static $snakeCache = array(); /** * @var array CamelCase transformation cache */ protected static $camelCache = array(); /** * Converts strings from camel case to snake case * (e.g. CamelCase camel_case) * * Borrowed from Magento * * @param string $word Word to convert to snake case * * @return string */ public static function snake($word) { static $cached = 0; if (isset(self::$snakeCache[$word])) { return self::$snakeCache[$word]; } if (strtolower($word) == $word) { $result = $word; } else { $result = strtolower(preg_replace('/(.)([A-Z])/', "$1_$2", $word)); } if (++$cached > self::MAX_ENTRIES_PER_CACHE) { $toRemove = self::MAX_ENTRIES_PER_CACHE * 0.1; self::$snakeCache = array_slice(self::$snakeCache, $toRemove); $cached -= $toRemove; } return self::$snakeCache[$word] = $result; } /** * Converts strings from snake_case to upper CamelCase * * @param string $word Value to convert into upper CamelCase * * @return string */ public static function camel($word) { static $cached = 0; if (isset(self::$camelCache[$word])) { return self::$camelCache[$word]; } $result = str_replace(' ', '', ucwords(strtr($word, '_-', ' '))); if (++$cached > self::MAX_ENTRIES_PER_CACHE) { $toRemove = self::MAX_ENTRIES_PER_CACHE * 0.1; self::$camelCache = array_slice(self::$camelCache, $toRemove); $cached -= $toRemove; } return self::$camelCache[$word] = $result; } /** * Get cache information from the inflector * * @return array Returns an array containing a snake and camel key, and each * value of each cache in a sub-array */ public static function getCache() { return array( 'snake' => self::$snakeCache, 'camel' => self::$camelCache ); } }src/Guzzle/Common/Inspector.php000064400000026041151742733540012540 0ustar00 */ class Inspector { const GUZZLE_ANNOTATION = '@guzzle'; /** * @var Inspector Singleton instance */ private static $instance; /** * @var array Cache of parsed doc blocks */ protected $cache = array(); /** * @var array Array of loaded filter objects */ protected $filters = array(); /** * Get an instantiated instance of the Reflection class * * @return Reflection */ public static function getInstance() { // @coveCoverageIgnoreStart if (!self::$instance) { self::$instance = new self(); } // @coveCoverageIgnoreEnd return self::$instance; } /** * Validate and prepare configuration parameters * * @param array $config Configuration values to apply. * @param array $defaults (optional) Default parameters * @param array $required (optional) Required parameter names * * @return Collection * @throws InvalidArgumentException if a parameter is missing */ public static function prepareConfig(array $config = null, $defaults = null, $required = null) { $collection = new Collection((array) $defaults); foreach ((array) $config as $key => $value) { $collection->set($key, $value); } foreach ((array) $required as $key) { if ($collection->hasKey($key) === false) { throw new \InvalidArgumentException( "Config must contain a '{$key}' key" ); } } return $collection; } /** * Private constructor */ private function __construct() { $this->filters = array( 'integer' => array(__NAMESPACE__ . '\\Filter\\IntegerFilter', null, null), 'float' => array(__NAMESPACE__ . '\\Filter\\FloatFilter', null, null), 'string' => array(__NAMESPACE__ . '\\Filter\\StringFilter', null, null), 'timestamp' => array(__NAMESPACE__ . '\\Filter\\TimestampFilter', null, null), 'date' => array(__NAMESPACE__ . '\\Filter\\DateFilter', null, null), 'boolean' => array(__NAMESPACE__ . '\\Filter\\BooleanFilter', null, null), 'class' => array(__NAMESPACE__ . '\\Filter\\ClassFilter', null, null), 'array' => array(__NAMESPACE__ . '\\Filter\\ArrayFilter', null, null), 'enum' => array(__NAMESPACE__ . '\\Filter\\EnumFilter', null, null), 'regex' => array(__NAMESPACE__ . '\\Filter\\RegexFilter', null, null) ); } /** * Get an array of the registered filters by name * * @return array */ public function getRegisteredFilters() { return array_map(function($filter) { return $filter[0]; }, $this->filters); } /** * Register a filter class with a special name * * @param string $name Name of the filter to register * @param string|FilterInterface $class Name of the class or object to use when filtering by this name * @param string $default Default value to pass to the filter * * @return Inspector */ public function registerFilter($name, $class, $default = null) { $object = null; if (is_object($class)) { $object = $class; $class = get_class($object); } $this->filters[$name] = array($class, $object, array($default)); } /** * Get the Guzzle arguments from a DocBlock * * @param string $doc DocBlock to parse * * @return array Returns an associative array of the parsed docblock params */ public function parseDocBlock($doc) { $matches = array(); // Get all of the @guzzle annotations from the class preg_match_all('/' . self::GUZZLE_ANNOTATION . '\s+([A-Za-z0-9_\-\.]+)\s*([A-Za-z0-9]+=".+")*/', $doc, $matches); if (empty($matches[1])) { return array(); } $params = array(); foreach ($matches[1] as $index => $match) { // Add the matched argument to the array keys $params[$match] = array(); if (isset($matches[2])) { // Break up the argument attributes by closing quote foreach (explode('" ', $matches[2][$index]) as $part) { $attrs = array(); // Find the attribute and attribute value preg_match('/([A-Za-z0-9]+)="(.+)"*/', $part, $attrs); if (isset($attrs[1]) && isset($attrs[0])) { // Sanitize the strings if ($attrs[2][strlen($attrs[2]) - 1] == '"') { $attrs[2] = substr($attrs[2], 0, strlen($attrs[2]) - 1); } $params[$match][$attrs[1]] = $attrs[2]; } } } } return $params; } /** * Validates that a class has all of the required configuration settings * * @param string $class Name of the class to use to retrieve args * @param Collection $config Configuration settings * @param bool $strict (optional) Set to FALSE to allow missing required fields * * @return array|bool Returns an array of errors or TRUE on success * * @throws InvalidArgumentException if any args are missing and $strict is TRUE */ public function validateClass($className, Collection $config, $strict = true) { if (!isset($this->cache[$className])) { $reflection = new \ReflectionClass($className); $this->cache[$className] = $this->parseDocBlock($reflection->getDocComment()); } return $this->validateConfig($this->cache[$className], $config, $strict); } /** * Validates that all required args are included in a config object, * and if not, throws an InvalidArgumentException with a helpful error message. Adds * default args to the passed config object if the parameter was not * set in the config object. * * @param array $params Params to validate * @param Collection $config Configuration settings * @param bool $strict (optional) Set to FALSE to allow missing required fields * * @return array|bool Returns an array of errors or TRUE on success * * @throws InvalidArgumentException if any args are missing and $strict is TRUE */ public function validateConfig(array $params, Collection $config, $strict = true) { $errors = array(); foreach ($params as $name => $arg) { $arg = ($arg instanceof Collection) ? $arg : (is_array($arg) ? new Collection($arg) : new Collection()); // Set the default value if it is not set if ($arg->get('static') || ($arg->get('default') && !$config->get($name))) { $check = $arg->get('static', $arg->get('default')); if ($check === 'true') { $config->set($name, true); } else if ($check == 'false') { $config->set($name, false); } else { $config->set($name, $check); } } // Inject configuration information into the config value if (is_scalar($config->get($name)) && strpos($config->get($name), '{{') !== false) { $config->set($name, Guzzle::inject($config->get($name), $config)); } // Ensure that required arguments are set if ($arg->get('required') && !$config->get($name)) { $errors[] = 'Requires that the ' . $name . ' argument be supplied.' . ($arg->get('doc') ? ' (' . $arg->get('doc') . ').' : ''); continue; } // Skip further validation if the arg is not set if ($config->hasKey($name) === false) { continue; } // Ensure that the correct data type is being used if ($arg->get('type')) { $result = $this->validate($arg->get('type'), $config->get($name)); if ($result !== true && $result !== null) { $errors[] = $result; } } // Check the length values if ($arg->get('min_length') && strlen($config->get($name)) < $arg->get('min_length')) { $errors[] = 'Requires that the ' . $name . ' argument be >= ' . $arg->get('min_length') . ' characters.'; } if ($arg->get('max_length') && strlen($config->get($name)) > $arg->get('max_length')) { $errors[] = 'Requires that the ' . $name . ' argument be <= ' . $arg->get('max_length') . ' characters.'; } } if (empty($errors)) { return true; } else { if ($strict) { throw new \InvalidArgumentException('Validation errors: ' . implode("\n", $errors)); } else { return $errors; } } } /** * Get a filter from the registered filters * * @param string $name Name of the filter to retrieve * @param mixed $value Value to validate * * @return bool|string Returns TRUE on success or a string error message on * failure */ private function validate($name, $value) { $parts = array_map('trim', explode(':', $name)); $name = $parts[0]; if (!isset($this->filters[$name])) { throw new \InvalidArgumentException($name . ' has not been registered as a filter'); } // Use supplied arguments or the defaults if they are set $args = array_slice($parts, 1); if (empty($args)) { $args = (array) $this->filters[$name][2]; } if (!isset($this->filters[$name][1])) { // Create a new filter and store it in a Flyweight type cache $class = $this->filters[$name][0]; $this->filters[$name][1] = new $class($args); } else { // Set the passed values $this->filters[$name][1]->replace($args); } return $this->filters[$name][1]->process($value); } }src/Guzzle/Common/Log/AbstractLogAdapter.php000064400000001054151742733540015016 0ustar00 */ abstract class AbstractLogAdapter implements LogAdapterInterface { /** * @var mixed Concrete wrapped log object */ protected $log; /** * Get the wrapped log object * * @return mixed */ public function getLogObject() { return $this->log; } }src/Guzzle/Common/Log/ClosureLogAdapter.php000064400000001616151742733540014673 0ustar00 */ class ClosureLogAdapter extends AbstractLogAdapter { /** * Adapt a callable function * * @param mixed $logObject Log object to adapt * @throws InvalidArgumentException if the $logObject is not callable */ public function __construct($logObject) { if (!is_callable($logObject)) { throw new \InvalidArgumentException('Object must be callable'); } $this->log = $logObject; } /** * {@inheritdoc} */ public function log($message, $priority = LOG_INFO, $extras = null) { call_user_func($this->log, $message, $priority, $extras); return $this; } }src/Guzzle/Common/Log/LogAdapterInterface.php000064400000002140151742733540015150 0ustar00 */ interface LogAdapterInterface { /** * Create a new LogAdapter * * @param mixed $logObject (optional) The concrete logging implementation * that will be wrapped by the adapter. * * @throws InvalidArgumentException if the supplied object does not implement the * correct interface. */ function __construct($logObject); /** * Get the adapted log object * * @return mixed */ function getLogObject(); /** * Log a message at a priority * * @param string $message Message to log * @param integer $priority (optional) Priority of message (use the \LOG_* constants of 0 - 7) * @param mixed $extras (optional) Extra information to log in event * * @return LogAdapterInterface */ function log($message, $priority = LOG_INFO, $extras = null); }src/Guzzle/Common/Log/MonologLogAdapter.php000064400000001540151742733540014665 0ustar00 * @link https://github.com/Seldaek/monolog */ class MonologLogAdapter extends AbstractLogAdapter { /** * Adapt a Monolog Logger object * * @param Logger $logObject Log object to adapt * @throws InvalidArgumentException */ public function __construct($logObject) { if (!($logObject instanceof Logger)) { throw new \InvalidArgumentException( 'Object must be an instance of Monolog\Logger' ); } $this->log = $logObject; } /** * {@inheritdoc} */ public function log($message, $priority = LOG_INFO, $extras = null) { $this->log->addRecord($priority, $message); return $this; } }src/Guzzle/Common/Log/ZendLogAdapter.php000064400000001470151742733540014155 0ustar00 */ class ZendLogAdapter extends AbstractLogAdapter { /** * Adapt a Zend_Log object * * @param Zend_Log $logObject Log object to adapt * @throws InvalidArgumentException */ public function __construct($logObject) { if (!($logObject instanceof \Zend_Log)) { throw new \InvalidArgumentException( 'Object must be an instance of Zend_Log' ); } $this->log = $logObject; } /** * {@inheritdoc} */ public function log($message, $priority = LOG_INFO, $extras = null) { $this->log->log($message, $priority, $extras); return $this; } }src/Guzzle/Common/NullObject.php000064400000002400151742733540012624 0ustar00 */ class NullObject implements \Iterator, \Countable, \ArrayAccess { public function __call($method, $params) { return null; } public function __get($variable) { return null; } public function __set($variable, $value) { return null; } public function __isset($variable) { return null; } public function __unset($variable) { return null; } public function offsetExists($offset) { return false; } public function offsetUnset($offset) { return null; } public function offsetSet($offset, $value) { return null; } public function offsetGet($offset) { return null; } public function count() { return null; } public function current() { return null; } public function key() { return null; } public function next() { return null; } public function rewind() { return null; } public function valid() { return null; } }src/Guzzle/Common/Stream.php000064400000024135151742733540012027 0ustar00 */ class Stream { /** * @var resource Stream resource */ protected $stream; /** * @var bool If the stream is seekable */ protected $seekable; /** * @var string The stream wrapper type */ protected $wrapper; /** * @var string The mode in which the stream was opened (r, w, r+, etc) */ protected $mode; /** * @var string URI of the stream (a filename, URL, etc) */ protected $uri; /** * @var string Label describing the underlying implementation of the stream */ protected $type; /** * @var int Size of the stream contents in bytes */ protected $size; /** * @var array Array of filters */ protected $filters = array(); /** * Construct a new Stream * * @param resource $stream Stream resource to wrap * @param int $size (optional) Size of the stream in bytes. Only pass this * parameter if the size cannot be obtained from the stream. * * @throws InvalidArgumentException if the stream is not a stream resource */ public function __construct($stream, $size = null) { if (!is_resource($stream)) { throw new \InvalidArgumentException( 'Invalid $stream argument sent to ' . __METHOD__ ); } $this->stream = $stream; $meta = stream_get_meta_data($stream); $this->wrapper = isset($meta['wrapper_type']) ? strtolower($meta['wrapper_type']) : ''; $this->mode = isset($meta['mode']) ? $meta['mode'] : ''; $this->seekable = isset($meta['seekable']) ? $meta['seekable'] : false; $this->uri = isset($meta['uri']) ? $meta['uri'] : ''; $this->type = isset($meta['stream_type']) ? strtolower($meta['stream_type']) : ''; $this->size = $size; } /** * Closes the stream when the helper is destructed */ public function __destruct() { if (is_resource($this->stream)) { fclose($this->stream); } } /** * Convert the stream to a string if the stream is readable and the stream * is seekable. This logic is enforced to ensure that outputting the stream * as a string does not affect an actual cURL request using non-repeatable * streams. * * @return string */ public function __toString() { if (!$this->isReadable() || (!$this->isSeekable() && $this->isConsumed())) { return ''; } $this->seek(0); $body = stream_get_contents($this->stream); $this->seek(0); return $body; } /** * Get the stream resource * * @return resource */ public function getStream() { return $this->stream;; } /** * Get the stream wrapper type * * @return string */ public function getWrapper() { return $this->wrapper; } /** * Wrapper specific data attached to this stream. * * @return string */ public function getWrapperData() { $meta = stream_get_meta_data($this->stream); return isset($meta['wrapper_data']) ? $meta['wrapper_data'] : array(); } /** * Get a label describing the underlying implementation of the stream * * @return string */ public function getStreamType() { return $this->type; } /** * Get the URI/filename associated with this stream * * @return string */ public function getUri() { return $this->uri; } /** * Get the size of the stream if able * * If any filters are attached to the string, then the size * * @return int|false */ public function getSize() { if ($this->size !== null) { return $this->size; } // Only get the length if there are no modifying filters attached to // the stream that would modify the stream on the fly $filters = $this->getFilters(); $filters = array_keys($filters['wrapped']); if (count(array_filter($filters, function($filter) { $filter = explode('|', $filter); $filter = $filter[0]; return !in_array($filter, array('string.rot13', 'string.toupper', 'string.tolower')); }))) { return false; } // If the stream is a file based stream and local, then check the filesize if ($this->isLocal() && $this->getWrapper() == 'plainfile' && $this->getUri() && file_exists($this->getUri())) { return filesize($this->getUri()); } // Only get the size based on the content if the the stream is readable // and seekable so as to not interfere with actually reading the data if (!$this->isReadable() || !$this->isSeekable()) { return false; } else { $size = strlen((string) $this); $this->seek(0); return $size; } } /** * Check if the stream is readable * * @return bool */ public function isReadable() { return in_array(str_replace('b', '', $this->mode), array('r', 'w+', 'r+', 'x+', 'c+')); } /** * Check if the stream is writable * * @return bool */ public function isWritable() { return str_replace('b', '', $this->mode) != 'r'; } /** * Check if the stream has been consumed * * @return bool */ public function isConsumed() { return feof($this->stream); } /** * Check if the stream is a local stream vs a remote stream * * @return bool */ public function isLocal() { return stream_is_local($this->stream); } /** * Check if the string is repeatable * * @return bool */ public function isSeekable() { return $this->seekable; } /** * Specify the size of the stream in bytes * * @param int $size Size of the stream contents in bytes * * @return Stream */ public function setSize($size) { $this->size = $size; return $this; } /** * Seek to a position in the stream * * @param int $offset Stream offset * @param int $whence (optional) Where the offset is applied * * @return bool Returns TRUE on success or FALSE on failure * @see http://www.php.net/manual/en/function.fseek.php */ public function seek($offset, $whence = SEEK_SET) { return $this->isSeekable() ? fseek($this->stream, $offset, $whence) === 0 : false; } /** * Read data from the stream * * @param int $length Up to length number of bytes read. * * @return string|bool Returns the data read from the stream or FALSE on * failure or EOF */ public function read($length) { return $this->isReadable() ? fread($this->stream, $length) : false; } /** * Write data to the stream * * @param string $string The string that is to be written. * * @return int|bool Returns the number of bytes written to the stream on * success or FALSE on failure. */ public function write($string) { return $this->isWritable() ? fwrite($this->stream, $string) : false; } /** * Add a filter to the stream * * @param string $filter Name of the filter to append * @param int $readWrite (optional) By default, stream_filter_append() will * attach the filter to the read filter chain if the file was opened * for reading (i.e. File Mode: r, and/or +). The filter will also be * attached to the write filter chain if the file was opened for * writing (i.e. File Mode: w, a, and/or +). STREAM_FILTER_READ, * STREAM_FILTER_WRITE, and/or STREAM_FILTER_ALL can also be passed * to the read_write parameter to override this behavior. * @param array $options (optional) This filter will be added with the * specified params to the end of the list and will therefore be * called last during stream operations. To add a filter to the * beginning of the list, use stream_filter_prepend(). * @param bool $prepend (optional) Set to TRUE to prepend the filter. * Default is to append * * @return Stream */ public function addFilter($filter, $readWrite = STREAM_FILTER_ALL, $options = null, $prepend = false) { if ($prepend) { $resource = stream_filter_prepend($this->stream, $filter, $readWrite, $options); } else { $resource = stream_filter_append($this->stream, $filter, $readWrite, $options); } $this->filters[$filter . '|' . $readWrite] = $resource; return $this; } /** * Get an array of filters added to the stream * * @return array Returns an associative array containing the following keys: * wrapped - Filters that were applied using the helper * (associative array of [filter name] + [.] + [read write value] => resource * unwrapped - Filters that were not applied using the helper */ public function getFilters() { $meta = stream_get_meta_data($this->stream); return array( 'wrapped' => $this->filters, 'unwrapped' => isset($meta['filters']) ? $meta['filters'] : array() ); } /** * Remove a filter from the stream * * @param string $filter Filter to remove by name * @param $readWrite int (optional) Which type of filter to remove if * removing filters by name using a string * * @return bool Returns TRUE on success or FALSE on failure */ public function removeFilter($filter, $readWrite = STREAM_FILTER_ALL) { $key = $filter . '|' . $readWrite; if (isset($this->filters[$key])) { stream_filter_remove($this->filters[$key]); unset($this->filters[$key]); return true; } return false; } }src/Guzzle/Common/XmlElement.php000064400000004401151742733540012640 0ustar00 * @author Michael Dowling */ class XmlElement extends \SimpleXMLElement { /** * Magic method, alias of toXML() * * @return string */ public function __toString() { return $this->asXML(); } /** * Extended addChild method - supports appending another Element * * @param string|Element $name * * @param string|SimpleXMLElement $value XML data to add * @param string $namespace Namespace of the element * * @return Element */ public function addChild($name, $value = null, $namespace = null) { // If this is not a SimpleXMLElement, then add normally if (!($name instanceof \SimpleXMLElement)) { return parent::addChild($name, $value, $namespace); } $nodeName = $name->getName(); // If the parent node has no children, then cast to a string and add if (!count($name->children())) { return parent::addChild($nodeName, (string) $name, $namespace); } $e = $this->addChild($nodeName); // Add parent attributes to the newly added child node foreach ($name->attributes() as $key => $value) { $e->addAttribute((string) $key, (string) $value); } // Add the children of the parent node foreach ($name->children() as $child) { $childEle = $e->addChild($child); $attributes = $childEle->attributes($namespace); foreach ($child->attributes() as $key => $val) { if (!isset($attributes->{$key})) { $childEle->addAttribute((string) $key, (string) $val); } } } return $e; } /** * Output formatted XML string * * @return string */ public function asFormattedXml() { $doc = new \DOMDocument(); $doc->preserveWhiteSpace = false; $doc->formatOutput = true; $doc->loadXML($this->asXML()); return $doc->saveXML(); } }src/Guzzle/Guzzle.php000064400000007144151742733540010625 0ustar00 */ class Guzzle { const VERSION = '1.1'; /** * @var string Default Guzzle User-Agent header */ protected static $userAgent; /** * @var array cURL version information */ protected static $curl; /** * Get the default User-Agent to add to requests sent through the library * * @return string */ public static function getDefaultUserAgent() { if (!self::$userAgent) { $version = self::getCurlInfo(); self::$userAgent = sprintf('Guzzle/%s (Language=PHP/%s; curl=%s; Host=%s)', Guzzle::VERSION, \PHP_VERSION, $version['version'], $version['host'] ); } return self::$userAgent; } /** * Get curl version information and caches a local copy for fast re-use * * @param $type (optional) Version information to retrieve * 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 * age * features - A bitmask of the CURL_VERSION_XXX constants * protocols - An array of protocols names supported by cURL * * @return array|string|float false Returns an array if no $type is * provided, a string|float if a $type is provided and found, or false * if a $type is provided and not found. */ public static function getCurlInfo($type = null) { if (!self::$curl) { self::$curl = @curl_version(); // @codeCoverageIgnoreStart if (!self::$curl) { throw new \RuntimeException('Guzzle requires curl'); } // @codeCoverageIgnoreEnd // Check if CURLOPT_FOLLOWLOCATION is available self::$curl['follow_location'] = !ini_get('open_basedir'); } if (!$type) { return self::$curl; } else if (isset(self::$curl[$type])) { return self::$curl[$type]; } else { return false; } } /** * Create an RFC 1123 HTTP-Date from various date values * * @param string|int $date Date to convert * * @return string */ public static function getHttpDate($date) { if (!is_numeric($date)) { $date = strtotime($date); } return gmdate('D, d M Y H:i:s', $date) . ' GMT'; } /** * Inject configuration settings into an input string * * @param string $input Input to inject * @param Collection $config Configuration data to inject into the input * * @return string */ public static function inject($input, Collection $config) { // Skip expensive regular expressions if it isn't needed if (strpos($input, '{{') === false) { return $input; } return preg_replace_callback('/{{\s*([A-Za-z_\-\.0-9]+)\s*}}/', function($matches) use ($config) { return $config->get(trim($matches[1])); }, $input ); } /** * Reset the cached internal state */ public static function reset() { self::$userAgent = null; self::$curl = null; } }src/Guzzle/Http/Cookie.php000064400000003333151742733540011471 0ustar00 */ class Cookie extends QueryString { /** * Create a Cookie by parsing a Cookie HTTP header * * @param string $cookieString Cookie HTTP header * * @return Cookie */ public static function factory($cookieString) { $data = new Collection(); if ($cookieString) { foreach (explode(';', $cookieString) as $kvp) { $parts = explode('=', $kvp, 2); $key = urldecode(trim($parts[0])); $value = isset($parts[1]) ? trim($parts[1]) : ''; $data->add($key, urldecode($value)); } } return new self($data->getAll()); } /** * {@inheritdoc} */ public function __construct(array $data = null) { parent::__construct($data); $this->setFieldSeparator(';') ->setPrefix('') ->setValueSeparator('=') ->setEncodeFields(false) ->setEncodeValues(false) ->setAggregateFunction(function($key, $value, $encodeFields = false, $encodeValues = false) { $value = array_unique($value); return array( (($encodeFields) ? rawurlencode($key) : $key) => (($encodeValues) ? array_map(array(__CLASS__, 'rawurlencode'), $value) : $value) ); }); } }src/Guzzle/Http/CookieJar/ArrayCookieJar.php000064400000022731151742733540014776 0ustar00 */ class ArrayCookieJar implements CookieJarInterface { /** * @var array Loaded cookie data */ protected $cookies = array(); /** * Clear cookies currently held in the Cookie jar. * * Invoking this method without arguments will empty the whole Cookie * jar. If given a $domain argument only cookies belonging to that * domain will be removed. If given a $domain and $path argument, cookies * belonging to the specified path within that domain are removed. If given * all three arguments, then the cookie with the specified name, path and * domain is removed. * * @param string $domain (optional) Set to clear only cookies matching a domain * @param string $path (optional) Set to clear only cookies matching a domain and path * @param string $name (optional) Set to clear only cookies matching a domain, path, and name * * @return int Returns the number of deleted cookies */ public function clear($domain = null, $path = null, $name = null) { $cookies = $this->getCookies($domain, $path, $name, false, false); $total = 0; foreach ($this->cookies as $i => $cookie) { foreach ($cookies as $c) { if ($c === $cookie) { $total++; unset($this->cookies[$i]); } } } return $total; } /** * Discard all temporary cookies. * * Scans for all cookies in the jar with either no expire field or a * true discard flag. To be called when the user agent shuts down according * to RFC 2965. * * @return int Returns the number of deleted cookies */ public function clearTemporary() { $ctime = time(); return $this->prune(function($cookie) use ($ctime) { return (!$cookie['discard'] && $cookie['expires']); }); } /** * Delete any expired cookies * * @return int Returns the number of deleted cookies */ public function deleteExpired() { $ctime = time(); return $this->prune(function($cookie) use ($ctime) { return (!$cookie['expires'] || $ctime < $cookie['expires']); }); } /** * Get all of the matching cookies * * @param string $domain (optional) Domain of the cookie * @param string $path (optional) Path of the cookie * @param string $name (optional) Name of the cookie * @param bool $skipDiscardables (optional) Set to TRUE to skip cookies with * the Discard attribute. * @param bool $skipExpired (optional) Set to FALSE to include expired * * @return array Returns an array of arrays. Each array contains the * following keys: * * domain (string) - Domain of the cookie * path (string) - Path of the cookie * cookie (array) - Array of cookie name, value (e.g. array('name', '123') * max_age (int) - Lifetime of the cookie in seconds * expires (int) - The UNIX timestamp when the cookie expires * version (int) - Version of the cookie specification. RFC 2965 is 1 * secure (bool) - Whether or not this is a secure cookie * discard (bool) - Whether or not this is a discardable cookie * comment (string) - How the cookie is intended to be used * comment_url (str)- URL with info on how it will be used * port (string) - CSV list of ports * http_only (bool) - HTTP only cookie */ public function getCookies($domain = null, $path = null, $name = null, $skipDiscardable = false, $skipExpired = true) { $ctime = time(); $ret = array_values(array_filter($this->cookies, function($cookie) use ($domain, $path, $name, $skipDiscardable, $skipExpired, $ctime) { // Make sure the cookie is not expired if ($skipExpired && $cookie['expires'] && $ctime > $cookie['expires']) { return false; } $domain = strtolower($domain); $cookie['domain'] = strtolower($cookie['domain']); // Normalize the domain value $domainMatch = false; if ($domain && $cookie['domain']) { if ($domain == $cookie['domain']) { $domainMatch = true; } else if ($cookie['domain'][0] == '.') { $domainMatch = preg_match('/' . preg_quote($cookie['domain']) . '$/', $domain); } } // Check cookie name matches $nameMatch = $name && $cookie['cookie'][0] == $name; if (!$domain || $domainMatch) { if (!$path || !strcasecmp($path, $cookie['path']) || 0 === stripos($path, $cookie['path'])) { if (!$name || $nameMatch) { if (!$skipDiscardable || !$cookie['discard']) { return true; } } } } return false; })); return $ret; } /** * Save a cookie * * @parm array $cookieData Cookie information, including the following: * domain (string, required) - Domain of the cookie * path (string, required) - Path of the cookie * cookie - Array of cookie name (0) and value (1) * max_age (int, required) - Lifetime of the cookie in seconds * version (int) - Version of the cookie specification. Default is 0. RFC 2965 is 1 * secure (bool) - If it is a secure cookie * discard (bool) - If it is a discardable cookie * comment (string) - How the cookie is intended to be used * comment_url (str) - URL with info on how it will be used * port (string) - CSV list of ports * http_only (bool) - HTTP only cookie * * @return ArrayCookieJar */ public function save(array $cookieData) { if (!isset($cookieData['domain'])) { throw new \InvalidArgumentException('Cookies require a domain'); } if (!isset($cookieData['cookie']) || !is_array($cookieData['cookie'])) { throw new \InvalidArgumentException('Cookies require a names and values'); } // Extract the expires value and turn it into a UNIX timestamp if needed $cookieData['expires'] = isset($cookieData['expires']) ? (is_numeric($cookieData['expires']) ? $cookieData['expires'] : strtotime($cookieData['expires'])) : null; $cookieData['path'] = isset($cookieData['path']) ? $cookieData['path'] : '/'; $cookieData['max_age'] = isset($cookieData['max_age']) ? $cookieData['max_age'] : 0; $cookieData['comment'] = isset($cookieData['comment']) ? $cookieData['comment'] : null; $cookieData['comment_url'] = isset($cookieData['comment_url']) ? $cookieData['comment_url'] : null; $cookieData['port'] = isset($cookieData['port']) ? $cookieData['port'] : array(); $cookieData['version'] = isset($cookieData['version']) ? $cookieData['version'] : null; $cookieData['secure'] = array_key_exists('secure', $cookieData) ? $cookieData['secure'] : null; $cookieData['discard'] = array_key_exists('discard', $cookieData) ? $cookieData['discard'] : null; $cookieData['http_only'] = array_key_exists('http_only', $cookieData) ? $cookieData['http_only'] : false; // Calculate the expires date if (!$cookieData['expires'] && $cookieData['max_age']) { $cookieData['expires'] = time() + (int) $cookieData['max_age']; } $alreadyPresent = false; $keys = array('path', 'max_age', 'domain', 'http_only', 'port', 'secure'); foreach ($this->cookies as $i => $cookie) { // Check the regular comparison fields foreach ($keys as $k) { if ($cookie[$k] != $cookieData[$k]) { continue 2; } } // Is it a different cookie value name? if ($cookie['cookie'][0] != $cookieData['cookie'][0]) { continue; } // The previously set cookie is a discard cookie and this one is not // so allow the new cookie to be set if (!$cookieData['discard'] && $cookie['discard']) { unset($this->cookies[$i]); continue; } // If the new cookie's expiration is further into the future, then // replace the old cookie if ($cookieData['expires'] > $cookie['expires']) { unset($this->cookies[$i]); continue; } $alreadyPresent = true; break; } if (!$alreadyPresent) { $this->cookies[] = $cookieData; } return $this; } /** * Prune the cookies using a callback function * * @param Closure $callback Callback function * * @return int Returns the number of removed cookies */ protected function prune(\Closure $callback) { $originalCount = count($this->cookies); $this->cookies = array_filter($this->cookies, $callback); return $originalCount - count($this->cookies); } }src/Guzzle/Http/CookieJar/CookieJarInterface.php000064400000007577151742733540015633 0ustar00 */ interface CookieJarInterface { /** * Clear cookies currently held in the Cookie jar. * * Invoking this method without arguments will empty the whole Cookie * jar. If given a $domain argument only cookies belonging to that * domain will be removed. If given a $domain and $path argument, cookies * belonging to the specified path within that domain are removed. If given * all three arguments, then the cookie with the specified name, path and * domain is removed. * * @param string $domain (optional) Set to clear only cookies matching a domain * @param string $path (optional) Set to clear only cookies matching a domain and path * @param string $name (optional) Set to clear only cookies matching a domain, path, and name * * @return int Returns the number of deleted cookies */ function clear($domain = null, $path = null, $name = null); /** * Discard all temporary cookies. * * Scans for all cookies in the jar with either no expire field or a * true discard flag. To be called when the user agent shuts down according * to RFC 2965. * * @return int Returns the number of deleted cookies */ function clearTemporary(); /** * Delete any expired cookies * * @return int Returns the number of deleted cookies */ function deleteExpired(); /** * Get all of the matching cookies * * @param string $domain (optional) Domain of the cookie * @param string $path (optional) Path of the cookie * @param string $name (optional) Name of the cookie * @param bool $skipDiscardables (optional) Set to TRUE to skip cookies with * the Discard attribute. * @param bool $skipExpired (optional) Set to FALSE to include expired * * @return array Returns an array of arrays. Each array contains the * following keys: * * domain (string) - Domain of the cookie * path (string) - Path of the cookie * cookie (array) - Array of cookie name (0) and value (1) * max_age (int) - Lifetime of the cookie in seconds * expires (int) - The UNIX timestamp when the cookie expires * version (int) - Version of the cookie specification. RFC 2965 is 1 * secure (bool) - Whether or not this is a secure cookie * discard (bool) - Whether or not this is a discardable cookie * comment (string) - How the cookie is intended to be used * comment_url (str)- URL with info on how it will be used * port (string) - CSV list of ports * http_only (bool) - HTTP only cookie */ function getCookies($domain = null, $path = null, $name = null, $skipDiscardable = false, $skipExpired = true); /** * Save a cookie * * @parm array $cookieData Cookie information, including the following: * domain (string, required) - Domain of the cookie * path (string, required) - Path of the cookie * cookie - Array of cookie name (0) and value (1) * max_age (int, required) - Lifetime of the cookie in seconds * version (int) - Version of the cookie specification. Default is 0. RFC 2965 is 1 * secure (bool) - If it is a secure cookie * discard (bool) - If it is a discardable cookie * comment (string) - How the cookie is intended to be used * comment_url (str) - URL with info on how it will be used * port (string) - CSV list of ports * http_only (bool) - HTTP only cookie * * @return CookieJarInterface */ function save(array $cookieData); }src/Guzzle/Http/CookieJar/FileCookieJar.php000064400000003573151742733540014602 0ustar00 */ class FileCookieJar extends ArrayCookieJar { /** * @var string filename */ protected $file; /** * Create a new FileCookieJar object * * @param string $cookieFile File to store the cookie data * * @throws HttpException if the file cannot be found or created */ public function __construct($cookieFile) { $this->file = $cookieFile; $this->load(); } /** * Saves the file when shutting down */ public function __destruct() { $this->persist(); } /** * Save the contents of the data array to the file * * @throws HttpException if the file cannot be found or created */ protected function persist() { $handle = fopen($this->file, 'w+'); // @codeCoverageIgnoreStart if ($handle === false) { throw new HttpException('Unable to open file ' . $this->file); } // @codeCoverageIgnoreEnd fwrite($handle, json_encode($this->getCookies(null, null, null, true, true))); fclose($handle); } /** * Load the contents of the json formatted file into the data array and * discard the unsaved state of the jar object */ protected function load() { $handle = fopen($this->file, 'c+'); // @codeCoverageIgnoreStart if ($handle === false) { throw new HttpException('Unable to open file ' . $this->file); } // @codeCoverageIgnoreEnd $json = ''; while ($data = fread($handle, 8096)) { $json .= $data; } fclose($handle); $this->cookies = ($json) ? json_decode($json, true) : array(); } }src/Guzzle/Http/Curl/CurlException.php000064400000001204151742733540013744 0ustar00curlError = $error; return $this; } /** * Get the associated cURL error message * * @return string */ public function getCurlError() { return $this->curlError; } }src/Guzzle/Http/Curl/CurlFactory.php000064400000036500151742733540013424 0ustar00 */ class CurlFactory implements CurlFactoryInterface { /** * @var CurlFactory Singleton instance */ private static $instance; /** * @var array Array of open curl handles {@see CurlHandle} */ protected $handles = array(); /** * Array of host as the key and the total number of allowed concurrent idle * connections per host. Default is 2 per host. This does not affect * concurrent connections per host. * * @var array */ protected $maxIdlePerHost = array(); /** * @var int Maximum number of seconds to leave an idle connection open */ protected $maxIdleTime = -1; /** * Array of host as the key and maximum number of handles reuses * as the value per host. * * @var array */ protected $maxConnectionReusesPerHost = array(); /** * Singleton method to get a single instance of the default CurlFactory. * * Because the default curl factory will be most commonly used, it is * recommended to get the singleton instance of the CurlFactory when * creating standard curl handles. * * @return CurlFactory */ public static function getInstance() { // @codeCoverageIgnoreStart if (!self::$instance) { self::$instance = new self(); } // @codeCoverageIgnoreEnd return self::$instance; } /** * Get the number of connections per host * * @param bool $allocated (optional) Set to TRUE to get all allocated * counts, false to get unallocated counts, or NULL to get all * @param string $host (optional) Only retrive the counts for a specific * host (use both host and port) * * @return array */ public function getConnectionsPerHost($allocated = null, $host = null) { $hostHandles = array(); foreach ($this->handles as $i => $handle) { if ($allocated === true && !$handle->getOwner()) { continue; } if ($allocated === false && $handle->getOwner()) { continue; } $url = $handle->getUrl(); $h = $url->getHost() . ':' . $url->getPort(); if ($host && $h != $host) { continue; } if (!isset($hostHandles[$h])) { $hostHandles[$h] = 0; } $hostHandles[$h]++; } return $host ? (isset($hostHandles[$host]) ? $hostHandles[$host] : 0) : $hostHandles; } /** * Set the maximum amount of time to leave an idle connection open before * closing it. * * Currently allocated connections will not be closed when the * maximum amount of idle time has elapsed. Use -1 (the default settings) * to leave connections open indefinitely until they are closed. * * @param int $maxSeconds Maximum number of seconds to allow a connection to * remaing idle before closing it. * * @return CurlFactory */ public function setMaxIdleTime($maxSeconds) { $this->maxIdleTime = (int) $maxSeconds; return $this; } /** * Set the maximum number of idle connections to allow on a specific host * * @param string $host Host to specify including port * @param int $total Total number of idle connections to keep open * * @return CurlFactory */ public function setMaxIdleForHost($host, $total) { $this->maxIdlePerHost[$host] = $total; return $this; } /** * Set the maximum number of connection reuses for a specific host/port * * @param string $host Host to specify including port * @param int $max Max number of connection reuses per handle * * @return CurlFactory */ public function setMaxConnectionReusesForHost($host, $max) { $this->maxConnectionReusesPerHost[$host] = $max; return $this; } /** * Get a cURL handle for an HTTP request * * @param RequestInterface $request Request object checking out the handle * * @return resource */ public function getHandle(RequestInterface $request) { $retHandle = false; // Check to see if a handle can be reused foreach ($this->handles as $handle) { if (!$handle->getOwner() && $handle->isCompatible($request) && $handle->isAvailable()) { $retHandle = $handle; break; } } if (!$retHandle) { // If no matching handle was found, create a new handle $retHandle = $this->createHandle($request); } else { $curlOptions = $this->getOptions($request); // Cleanup a cURL handle that might have been used for a PUT // that used Transfer-Encoding: chunked $retHandle->setOption(CURLOPT_HTTPGET, true); $retHandle->setOptions($curlOptions); $request->getCurlOptions()->replace($curlOptions); } return $retHandle->checkout($request); } /** * Release a cURL handle back to the factory * * @param CurlHandle $handle Handle to release * @param bool $close (optional) Set to TRUE to close the handle * * @return CurlFactory */ public function releaseHandle(CurlHandle $handle, $close = false) { $handle->unlock(); if ($close && $handle->getHandle()) { curl_close($handle->getHandle()); } // If the handle was closed then clean() will remove it. It's also just // a good time to clean up the managed requests $this->clean(); return $this; } /** * Release all cURL handles from the factory * * @param bool $close (optional) Set to TRUE to close open handles * * @return CurlFactory */ public function releaseAllHandles($close = false) { foreach ($this->handles as $handle) { $handle->unlock(); if ($close && $handle->getHandle()) { curl_close($handle->getHandle()); } } $this->handles = array(); return $this; } /** * Closes idle handles that exceed the default number of idle * connections per host, handles that are no longer connected, or * handles that have not been used for the max idle timespan. * Currently allocated handles are not subject to this method * * @param bool $purge (optional) Set to TRUE to remove all unallocated * connections * * @return CurlFactory */ public function clean($purge = false) { $hostHandles = array(); foreach ($this->handles as $i => $handle) { // Skip allocated handles if ($handle->getOwner()) { continue; } if ($purge) { unset($this->handles[$i]); continue; } // Remove closed connections if (!$handle->isAvailable()) { unset($this->handles[$i]); continue; } // Remove connections that have been idle for too long if ($this->maxIdleTime > -1 && $handle->getIdleTime() >= $this->maxIdleTime) { unset($this->handles[$i]); continue; } $url = $handle->getUrl(); if (!isset($hostHandles[$url->getHost()])) { $hostHandles[$url->getHost()] = array(); } $hostHandles[$url->getHost() . ':' . $url->getPort()][] = $handle; } $this->handles = array_values($this->handles); // Prune too many handles per host if (!$purge) { foreach ($hostHandles as $host => $handles) { // Calculate how many idle connections to keep open for this host $max = isset($this->maxIdlePerHost[$host]) ? $this->maxIdlePerHost[$host] : 2; while (count($handles) > $max) { $remove = array_shift($handles); foreach ($this->handles as $i => $h) { if ($h === $remove) { unset($this->handles[$i]); break; } } } } } return $this; } /** * Create a cURL handle based on a request * * @param RequestInterface $request Request to create the handle for * * @return CurlHandle */ protected function createHandle(RequestInterface $request) { $handle = curl_init(); $options = $this->getOptions($request); // Set the CurlOptions on the request $request->getCurlOptions()->replace($options); // Apply the options to the cURL handle. curl_setopt_array($handle, $options); $h = new CurlHandle($handle, $options); $hostKey = $request->getHost() . ':' . $request->getPort(); if (isset($this->maxConnectionReusesPerHost[$hostKey])) { $h->setMaxReuses($this->maxConnectionReusesPerHost[$hostKey]); } $this->handles[] = $h; return $h; } /** * Get all of the cURL options of a request * * @param RequestInterface $request Request to get options for * * @return array */ protected function getOptions(RequestInterface $request) { $o = array(); foreach ($this->getDefaultOptions($request) as $key => $value) { $o[$key] = $value; } foreach ($this->getSpecificOptions($request) as $key => $value) { $o[$key] = $value; } return $o; } /** * Set the basic defaults on a curl handle * * @param RequestInterface $request Request to generate handle defaults * * @return array Returns an array of default curl settings */ protected function getDefaultOptions(RequestInterface $request) { // Array of default cURL options. $curlOptions = array( CURLOPT_URL => $request->getUrl(), CURLOPT_CONNECTTIMEOUT => 120, // Connect timeout in seconds CURLOPT_RETURNTRANSFER => false, // Streaming the return, so no need CURLOPT_HEADER => false, // Retrieve the received headers CURLOPT_USERAGENT => $request->getHeader('User-Agent', Guzzle::getDefaultUserAgent()), CURLOPT_ENCODING => '', // Supports all encodings CURLOPT_PORT => $request->getPort(), CURLOPT_HTTP_VERSION => $request->getProtocolVersion(true), CURLOPT_PROXY => '', // Reset proxy settings, CURLOPT_NOPROGRESS => false, CURLOPT_WRITEFUNCTION => function($curl, $data) use ($request) { $request->getEventManager()->notify('curl.callback.write', $data); return $request->getResponse()->getBody()->write($data); }, CURLOPT_HEADERFUNCTION => function($curl, $data) use ($request) { return $request->receiveResponseHeader($data); }, CURLOPT_READFUNCTION => function($ch, $fd, $length) use ($request) { $read = ($request->getBody()) ? $request->getBody()->read($length) : 0; if ($read) { $request->getEventManager()->notify('curl.callback.read', $read, true); } return $read === false || $read === 0 ? '' : $read; }, CURLOPT_PROGRESSFUNCTION => function($downloadSize, $downloaded, $uploadSize, $uploaded) use ($request) { $request->getEventManager()->notify('curl.callback.progress', array( 'download_size' => $downloadSize, 'downloaded' => $downloaded, 'upload_size' => $uploadSize, 'uploaded' => $uploaded ), true); } ); // @codeCoverageIgnoreStart if (Guzzle::getCurlInfo('follow_location')) { $curlOptions[CURLOPT_FOLLOWLOCATION] = true; $curlOptions[CURLOPT_MAXREDIRS] = 5; } // @codeCoverageIgnoreEnd return $curlOptions; } /** * Get options specific to request and the request method * * @param RequestInterface $request Request to use * * @return array */ protected function getSpecificOptions(RequestInterface $request) { // Specify settings according to the HTTP method. // The CURLOPT_CUSTOMREQUEST options are required to be able to reuse // a cURL handle that was once using a custom request method. switch ($request->getMethod()) { case 'GET': $curlOptions[CURLOPT_HTTPGET] = true; $curlOptions[CURLOPT_CUSTOMREQUEST] = 'GET'; unset($curlOptions[CURLOPT_READFUNCTION]); break; case 'HEAD': $curlOptions[CURLOPT_NOBODY] = true; $curlOptions[CURLOPT_CUSTOMREQUEST] = 'HEAD'; unset($curlOptions[CURLOPT_READFUNCTION]); break; case 'PUT': // cURL adds a content-type for PUT by default. if (!$request->hasHeader('Content-Type')) { $request->setHeader('Content-Type', ''); } // Uploading a file. The size of the file must be specified // here, but the contents of the file will be streamed from // the CURLOPT_WRITEFUNCTION function. $curlOptions[CURLOPT_CUSTOMREQUEST] = 'PUT'; $curlOptions[CURLOPT_UPLOAD] = true; if ($request->getBody()) { $size = $request->getBody()->getSize(); $curlOptions[CURLOPT_INFILESIZE] = null === $size ? -1 : $size; } break; case 'POST': $curlOptions[CURLOPT_CUSTOMREQUEST] = 'POST'; $curlOptions[CURLOPT_POST] = true; $curlOptions[CURLOPT_INFILESIZE] = -1; if (!$request->getBody()) { unset($curlOptions[CURLOPT_READFUNCTION]); } break; default: // Remove any other set options by making it perform as a GET $curlOptions[CURLOPT_HTTPGET] = true; $curlOptions[CURLOPT_CUSTOMREQUEST] = $request->getMethod(); break; } // Add any custom headers to the request $formattedHeaders = array(); foreach ($request->getHeaders() as $key => $value) { // cURL will set the Content-Length header for PUT requests if ($key == 'Content-Length' && ($value === '' || $value === null || $request->getMethod() == 'PUT')) { continue; } $formattedHeaders[] = $key . ': ' . $value; } if (!empty($formattedHeaders)) { $curlOptions[CURLOPT_HTTPHEADER] = $formattedHeaders; } // Set custom cURL options foreach ($request->getCurlOptions() as $key => $value) { $curlOptions[$key] = $value; } return $curlOptions; } }src/Guzzle/Http/Curl/CurlFactoryInterface.php000064400000001373151742733540015245 0ustar00 */ interface CurlFactoryInterface { /** * Get a cURL handle for an HTTP request * * @param RequestInterface $request Request object checking out the handle * * @return resource */ function getHandle(RequestInterface $request); /** * Release a cURL handle back to the factory * * @param CurlHandle $handle Handle to release * @param bool $close (optional) Set to TRUE to close the handle * * @return CurlFactoryInterface */ function releaseHandle(CurlHandle $handle); }src/Guzzle/Http/Curl/CurlHandle.php000064400000023506151742733540013212 0ustar00 */ class CurlHandle { /** * @var Collection Curl options */ protected $options; /** * @var resouce Curl resource handle */ protected $handle; /** * @var RequestInterface */ protected $owner; /** * @var int Last time this handle was checked out */ protected $lastUsedAt; /** * @var resource */ protected $stderr; /** * @var int Number of times the handle has been (re)used */ protected $useCount = 0; /** * @var int */ protected $maxReuses; /** * @var array Statically cached array of cURL options that pollute handles */ protected static $pollute; /** * 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'); } $this->handle = $handle; if (is_array($options)) { $this->options = new Collection($options); } else if ($options instanceof Collection) { $this->options = clone $options; } else { throw new \InvalidArgumentException('Expected array or Collection'); } $this->stderr = fopen('php://temp', 'r+'); $this->setOption(CURLOPT_STDERR, $this->stderr); $this->setOption(CURLOPT_VERBOSE, true); $this->lastUsedAt = time(); } /** * Destructor */ public function __destruct() { if ($this->isAvailable()) { curl_close($this->handle); } } /** * Check if the handle is available and still OK * * @return bool */ public function isAvailable() { //@codeCoverageIgnoreStart if (!$this->handle) { return false; } //@codeCoverageIgnoreEnd return false != @curl_getinfo($this->handle, CURLINFO_EFFECTIVE_URL); } /** * Check if the supplied cURL handle is wrapped by this object * * @param resource $handle Handle to check * * @return bool */ public function isMyHandle($handle) { return $this->getHandle() === $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() { return $this->isAvailable() ? @curl_errno($this->handle) : 0; } /** * Get cURL curl_getinfo data * * @param int $option (optional) Option to retrieve. Pass null to retrieve * retrieve all data as an array or pass a CURLINFO_* constant * * @return array|mixed */ public function getInfo($option = null) { if (!$this->isAvailable()) { return null !== $option ? null : array(); } return null !== $option ? @curl_getinfo($this->handle, $option) : @curl_getinfo($this->handle); } /** * Set the maximum number of times a handle can be reused * * @param int $max Maximum reuse count * * @return CurlHandle */ public function setMaxReuses($max) { $this->maxReuses = $max; return $this; } /** * Get the number of times the handle has been used * * @return int */ public function getUseCount() { return $this->useCount; } /** * Get the stderr output * * @param bool $asResource (optional) Set to TRUE to get an fopen resource * * @return string|resource */ public function getStderr($asResource = false) { if (!$asResource) { fseek($this->stderr, 0); $e = stream_get_contents($this->stderr); fseek($this->stderr, 0, SEEK_END); return $e; } return $this->stderr; } /** * Get the owner of the curl handle * * @return RequestInterface|null */ public function getOwner() { return $this->owner; } /** * Set the request that owns this handle * * @param RequestInterface $reqeust Request that owns the handle * * @return CurlHandle */ public function checkout(RequestInterface $request) { $this->owner = $request; $this->lastUsedAt = time(); ftruncate($this->stderr, 0); fseek($this->stderr, 0); return $this; } /** * Unlock the handle from the request that checked it out * * If the unlocking request should be closed (received a Connection: close * or sent a Connection: close header), the curl connection will be closed. * * @return CurlHandle */ public function unlock() { if ($this->isAvailable()) { $this->useCount++; if ((null !== $this->maxReuses && $this->useCount > $this->maxReuses) || ($this->hasProblematicOption() || ($this->owner && ($this->owner->getHeader('Connection', null, true) == 'close' || ($this->owner->getResponse() && $this->owner->getResponse()->getHeader('Connection', null, true) == 'close'))))) { curl_close($this->handle); $this->handle = null; $this->useCount = 0; } } $this->owner = null; return $this; } /** * Get the URL that this handle is connecting to * * @return Url */ public function getUrl() { return Url::factory($this->options->get(CURLOPT_URL)); } /** * Get the amount of time that has elapsed since this handle was last used * * @return int */ public function getIdleTime() { return time() - $this->lastUsedAt; } /** * Build the cURL handle or return the handle if it is already created * * @return handle|null Returns the cURL handle or null if it was closed */ public function getHandle() { return $this->handle && is_resource($this->handle) ? $this->handle : null; } /** * Get a cURL option value * * @param int|string $option Option to retrieve * * @return mixed */ public function getOption($option) { if (is_string($option)) { $option = constant($option); } return $this->options->get($option, null, true); } /** * Get all of the cURL options * * @param array $keys (optional) Specific keys to retrieve * * @return array */ public function getOptions(array $keys = null) { return $this->options->getAll($keys); } /** * Check if this CurlHandle could be used to serve a request object * * @param RequestInterface $request Request to check * * @return bool Returns TRUE if it can FALSE if not */ public function isCompatible(RequestInterface $request) { if ($this->owner === $request || ($this->owner && $this->owner->getCurlHandle() === $this)) { return true; } $url = $this->getUrl(); return $request->getHost() == $url->getHost() && ($request->getPort() == $url->getPort() || $request->getPort() == $this->getOption(CURLOPT_PORT)) && $request->getCurlOptions()->get(CURLOPT_PROXY) == $this->getOption(CURLOPT_PROXY); } /** * Set multiple options on the cURL handle * * @param array $options Options to set * * @return CurlHandle */ public function setOptions(array $options) { foreach ($options as $option => $value) { $this->setOption($option, $value); } return $this; } /** * Set a cURL options * * @param string|int $option Options to set * @param string|int|bool|null $value Value to set * * @return CurlHandle */ public function setOption($option, $value) { if (is_string($option)) { $option = constant($option); } $this->options->set($option, $value); // if the handle is open, set the option on the handle if ($this->isAvailable()) { curl_setopt($this->handle, $option, $value); } return $this; } /** * Check if the handle has a problematic cURL option that would prevent it * from being reused arbitrarily * * @return bool */ public function hasProblematicOption() { //@codeCoverageIgnoreStart if (!self::$pollute) { self::$pollute = array( CURLOPT_RANGE, CURLOPT_COOKIEFILE, CURLOPT_COOKIEJAR, CURLOPT_LOW_SPEED_LIMIT, CURLOPT_LOW_SPEED_TIME, CURLOPT_TIMEOUT, CURLOPT_FORBID_REUSE, CURLOPT_RESUME_FROM, CURLOPT_HTTPAUTH ); // CURLOPT_TIMEOUT_MS was added in v7.16.2 (or 0x071602) if (defined('CURLOPT_TIMEOUT_MS')) { self::$pollute[] = constant('CURLOPT_TIMEOUT_MS'); } } //@codeCoverageIgnoreEnd return count(array_intersect(self::$pollute, $this->options->getKeys())) > 0; } }src/Guzzle/Http/EntityBody.php000064400000021651151742733540012355 0ustar00 */ class EntityBody extends Stream { /** * @var bool Content-Encoding of the entity body if known */ protected $contentEncoding = false; /** * @var bool File types and whether or not they should be Gzipped. */ static protected $extensions = array( 'ai' => 'application/postscript', 'asc' => 'text/plain', 'avi' => 'video/x-msvideo', 'bmp' => 'image/bmp', 'bz' => 'application/x-bzip', 'bz2' => 'application/x-bzip2', 'cab' => 'application/vnd.ms-cab-compressed', 'css' => 'text/css', 'doc' => 'application/msword', 'eps' => 'application/postscript', 'exe' => 'application/x-msdownload', 'flv' => 'video/x-flv', 'gif' => 'image/gif', 'gz' => 'application/x-gzip', 'htm' => 'text/html', 'html' => 'text/html', 'ico' => 'image/vnd.microsoft.icon', 'ico' => 'image/x-icon', 'jpe' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'jpg' => 'image/jpeg', 'js' => 'application/javascript', 'json' => 'application/json', 'mov' => 'video/quicktime', 'mp3' => 'audio/mpeg', 'mpeg' => 'video/mpeg', 'mpg' => 'video/mpeg', 'msi' => 'application/x-msdownload', 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', 'odt' => 'application/vnd.oasis.opendocument.text', 'ogg' => 'application/ogg', 'pdf' => 'application/pdf', 'php' => 'text/html', 'php' => 'text/x-php', 'png' => 'image/png', 'ppt' => 'application/vnd.ms-powerpoint', 'ps' => 'application/postscript', 'psd' => 'image/vnd.adobe.photoshop', 'qt' => 'video/quicktime', 'rar' => 'application/x-rar-compressed', 'rtf' => 'application/rtf', 'svg' => 'image/svg+xml', 'svgz' => 'image/svg+xml', 'swf' => 'application/x-shockwave-flash', 'tar' => 'application/x-tar', 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'txt' => 'text/plain', 'wav' => 'audio/x-wav', 'xls' => 'application/vnd.ms-excel', 'xml' => 'application/xml', 'xsl' => 'application/xsl+xml', 'zip' => 'application/zip' ); /** * Create a new EntityBody based on the input type * * @param resource|string|EntityBody $resource (optional) Entity body data * @param int $size (optional) Size of the data contained in the resource * * @return EntityBody * @throws HttpException if the $resource arg is not a resource or string */ public static function factory($resource = '', $size = null) { if (is_resource($resource)) { return new self($resource, $size); } else if (is_string($resource)) { $stream = fopen('php://temp', 'r+'); fwrite($stream, $resource); rewind($stream); return new self($stream); } else if ($resource instanceof self) { return $resource; } else { throw new HttpException('Invalid data sent to ' . __METHOD__); } } /** * 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 (optional) Compression filter * * @return bool Returns TRUE on success or FALSE on failure */ public function compress($filter = 'zlib.deflate') { $result = $this->handleCompression($filter); if ($result) { $this->contentEncoding = $filter; } return $result; } /** * Uncompress a deflated string. Once uncompressed, the uncompressed * string is then used as the wrapped stream. * * @param string $filter (optional) De-compression filter * * @return bool Returns TRUE on success or FALSE on failure */ 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 $this->seek(0); if (fread($this->stream, 3) == "\x1f\x8b\x08") { $offsetStart = 10; } } $this->contentEncoding = false; return $this->handleCompression($filter, $offsetStart); } /** * Get the Content-Length of the entity body if possible (alias of getSize) * * @return int|false */ public function getContentLength() { return $this->getSize(); } /** * Guess the Content-Type of the stream based on the file extension of a * file-based stream * * @return string */ public function getContentType() { // If the file exists, then detect the mime type using Fileinfo if ($this->isLocal() && $this->getWrapper() == 'plainfile' && file_exists($this->getUri())) { $finfo = finfo_open(FILEINFO_MIME_TYPE); $mimeType = finfo_file($finfo, $this->getUri()); finfo_close($finfo); } else { $ext = strtoLower(pathInfo($this->getUri(), PATHINFO_EXTENSION)); $mimeType = isset(self::$extensions[$ext]) ? self::$extensions[$ext] : 'application/octet-stream'; } return $mimeType; } /** * Get an MD5 checksum of the stream's contents * * @param bool $rawOutput (optional) Whether or not to use raw output * @param bool $base64Encode (optional) 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) { if (!$this->seek(0)) { return false; } $ctx = hash_init('md5'); while ($data = $this->read(1024)) { hash_update($ctx, $data); } $out = hash_final($ctx, (bool) $rawOutput); $this->seek(0); return ((bool) $base64Encode && (bool) $rawOutput) ? base64_encode($out) : $out; } /** * Set the type of encoding stream that was used on the entity body * * @param string $streamFilterContentEncoding Stream filter used * * @return EntityBody */ public function setStreamFilterContentEncoding($streamFilterContentEncoding) { $this->contentEncoding = $streamFilterContentEncoding; return $this; } /** * Get the Content-Encoding of the EntityBody * * @return bool|string */ public function getContentEncoding() { $encoding = $this->contentEncoding; if ($this->contentEncoding == 'zlib.deflate') { $encoding = 'gzip'; } else if ($this->contentEncoding == 'bzip2.compress') { $encoding = 'compress'; } return $encoding; } /** * Handles compression or uncompression of stream data * * @param $filter Name of the filter to use (zlib.deflate or zlib.inflate) * @param int $offsetStart (optional) Number of bytes to skip from start * * @return bool Returns TRUE on success or FALSE on failure */ protected function handleCompression($filter, $offsetStart = null) { // @codeCoverageIgnoreStart if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) { return false; } // @codeCoverageIgnoreEnd $handle = fopen('php://temp', 'r+'); if (!$handle) { // @codeCoverageIgnoreStart return false; // @codeCoverageIgnoreEnd } $filter = @stream_filter_append($handle, $filter, STREAM_FILTER_WRITE); if (!$filter) { return false; } // @codeCoverageIgnoreStart if ($filter === false) { return false; } // @codeCoverageIgnoreEnd // Seek to the beginning of the stream if possible $this->seek(0); if ($offsetStart) { fread($this->stream, $offsetStart); } while ($data = fread($this->stream, 8096)) { fwrite($handle, $data); } fclose($this->stream); if ($filter) { stream_filter_remove($filter); $this->stream = $handle; } $stat = fstat($this->stream); $this->size = $stat['size']; return true; } }src/Guzzle/Http/HttpException.php000064400000000307151742733540013054 0ustar00 */ abstract class AbstractMessage implements MessageInterface { /** * @var Collection Collection of HTTP headers */ protected $headers; /** * @var Collection Custom message parameters that are extendable by plugins */ protected $params; /** * @var array Cache-Control directive information */ protected $cacheControl = null; /* * @var string HTTP protocol version of the message */ protected $protocolVersion = '1.1'; /** * Get application and plugin specific parameters set on the message. The * return object is a reference to the internal object. * * @return Collection */ public function getParams() { if (!$this->params) { $this->params = new Collection(); } return $this->params; } /** * Add and merge in an array of HTTP headers. * * @param array $headers Associative array of header data. * * @return AbstractMessage */ public function addHeaders(array $headers) { $this->headers->merge($headers); $this->changedHeader('set', array_keys($headers)); return $this; } /** * Retrieve an HTTP header by name * * @param string $header Header to retrieve. * @param mixed $default (optional) If the header is not found, the passed * $default value will be returned * @param int $match (optional) Bitwise match setting: * 0 - Exact match * 1 - Case insensitive match * 2 - Regular expression match * * @return string|null Returns the matching HTTP header value or NULL if the * header is not found */ public function getHeader($header, $default = null, $match = Collection::MATCH_EXACT) { return $this->headers->get($header, $default, $match); } /** * Get all or all matching headers. * * @param array $names (optional) Pass an array of header names to retrieve * only a particular subset of headers. * @param int $match (optional) Bitwise match setting: * 0 - Exact match * 1 - Case insensitive match * 2 - Regular expression match * * @return Collection Returns a collection of all headers if no $headers * array is specified, or a Collection of only the headers matching * the headers in the $headers array. */ public function getHeaders(array $headers = null, $match = Collection::MATCH_EXACT) { if (!$headers) { return clone $this->headers; } else { return new Collection($this->headers->getAll($headers, $match)); } } /** * Check if the specified header is present. * * @param string $header The header to check. * @param int $match (optional) Match mode * * @see AbstractMessage::getHeader * @return bool|mixed Returns TRUE or FALSE if the header is present and using exact matching * Returns the matching header or FALSE if no match found and using regex or case * insensitive matching */ public function hasHeader($header, $match = Collection::MATCH_EXACT) { return $match == Collection::MATCH_EXACT ? false !== $this->headers->hasKey($header, $match) : $this->headers->hasKey($header, $match); } /** * Remove a specific HTTP header. * * @param string $header HTTP header to remove. * @param int $match (optional) Bitwise match setting: * 0 - Exact match * 1 - Case insensitive match * 2 - Regular expression match * * @return AbstractMessage */ public function removeHeader($header, $match = Collection::MATCH_EXACT) { $this->headers->remove($header, $match); $this->changedHeader('remove', $header); return $this; } /** * Set an HTTP header * * @param string $header Name of the header to set. * @param mixed $value Value to set. * * @return AbstractMessage */ public function setHeader($header, $value) { $this->headers->set($header, $value); $this->changedHeader('set', $header); return $this; } /** * Overwrite all HTTP headers with the supplied array of headers * * @param array $headers Associative array of header data. * * @return AbstractMessage */ public function setHeaders(array $headers) { $this->changedHeader('set', $this->getHeaders()->getKeys()); $this->headers->replace($headers); return $this; } /** * Get a Cache-Control directive from the message * * @param string $directive Directive to retrieve * * @return null|string */ public function getCacheControlDirective($directive) { return isset($this->cacheControl[$directive]) ? $this->cacheControl[$directive] : null; } /** * Check if the message has a Cache-Control directive * * @param string $directive Directive to check * * @return bool */ public function hasCacheControlDirective($directive) { return isset($this->cacheControl[$directive]); } /** * Add a Cache-Control directive on the message * * @param string $directive Directive to set * @param bool|string $value (optional) Value to set * * @return AbstractMessage */ public function addCacheControlDirective($directive, $value = true) { $this->cacheControl[$directive] = $value; $this->rebuildCacheControlDirective(); return $this; } /** * Remove a Cache-Control directive from the message * * @param string $directive Directive to remove * * @return AbstractMessage */ public function removeCacheControlDirective($directive) { if (array_key_exists($directive, $this->cacheControl)) { unset($this->cacheControl[$directive]); $this->rebuildCacheControlDirective(); } return $this; } /** * Parse the Cache-Control HTTP header into an array */ protected function parseCacheControlDirective() { $this->cacheControl = array(); $cacheControl = $this->getHeader('Cache-Control'); if ($cacheControl) { if (is_array($cacheControl)) { $cacheControl = implode(',', $cacheControl); } foreach (explode(',', $cacheControl) as $pieces) { $parts = array_map('trim', explode('=', $pieces)); $this->cacheControl[$parts[0]] = isset($parts[1]) ? $parts[1] : true; } } } /** * Rebuild the Cache-Control HTTP header using the user-specified values */ protected function rebuildCacheControlDirective() { $cacheControl = array(); foreach ($this->cacheControl as $key => $value) { if ($value === true) { $cacheControl[] = $key; } else { $cacheControl[] = $key . '=' . $value; } } $this->headers->set('Cache-Control', implode(', ', $cacheControl)); } /** * Check to see if the modified headers need to reset any of the managed * headers like cache-control * * @param string $action One of set or remove * @param string|array $keyOrArray Header or headers that changed */ protected function changedHeader($action, $keyOrArray) { if (in_array('Cache-Control', (array)$keyOrArray)) { $this->parseCacheControlDirective(); } } }src/Guzzle/Http/Message/BadResponseException.php000064400000001230151742733540015722 0ustar00response = $response; } /** * Get the response that caused the exception * * @return Response */ public function getResponse() { return $this->response; } }src/Guzzle/Http/Message/EntityEnclosingRequest.php000064400000016730151742733540016340 0ustar00 */ class EntityEnclosingRequest extends Request implements EntityEnclosingRequestInterface { /** * @var EntityBody $body Body of the request */ protected $body; /** * @var QueryString POST fields to use in the EntityBody */ protected $postFields; /** * Get the HTTP request as a string * * @return string */ public function __toString() { // Process the object as it might contain POST fields that need to be // generated into an EntityBody if (!$this->response) { $this->getEventManager()->notify('request.prepare_entity_body'); } $body = count($this->getPostFields()) && 0 == count($this->getPostFiles()) ? (string) $this->getPostFields() : (string) $this->getBody(); return parent::__toString() . $body; } /** * Set the body of the request * * @param string|resource|EntityBody $body Body to use in the entity body * of the request * @param string $contentType (optional) Content-Type to set. Leave null * to use an existing Content-Type or to guess the Content-Type * * @return EntityEnclosingRequest * @throws HttpException if an invalid body is provided */ public function setBody($body, $contentType = null) { if ($this->body) { // Ensure that a previously set entity body is cleaned up $this->removeHeader('Content-Length'); } $this->body = EntityBody::factory($body); if ($contentType) { $this->setHeader('Content-Type', $contentType); } $this->addEvent(); return $this; } /** * Get the body of the request if set * * @return EntityBody|null */ public function getBody() { return $this->body; } /** * {@inheritdoc} */ public function update(Subject $subject, $event, $context = null) { // @codeCoverageIgnoreStart if ($subject !== $this || !($event == 'request.prepare_entity_body' || $event == 'request.before_send' || $event == 'request.curl.before_create')) { return; } // @codeCoverageIgnoreEnd $continuePayload = false; if (count($this->getPostFields())) { // If there are files, then do a multipart/form-data entity body $this->getCurlOptions()->set(CURLOPT_POST, true); if (count($this->getPostFiles())) { $this->setHeader('Content-Type', 'multipart/form-data'); $this->postFields->setEncodeFields(false)->setEncodeValues(false); $this->getCurlOptions()->set(CURLOPT_POSTFIELDS, $this->postFields->getAll()); $continuePayload = true; } else { $this->setHeader('Content-Type', 'application/x-www-form-urlencoded'); $this->getCurlOptions()->set(CURLOPT_POSTFIELDS, (string) $this->postFields); } } else if ($this->body && !$this->getHeader('Transfer-Encoding', null, true) && !$this->hasHeader('Content-Length', true)) { $continuePayload = true; // Set the Content-Length header if it can be determined $size = $this->body->getContentLength(); if ($size !== null && !is_bool($size)) { $this->headers->set('Content-Length', $size); } else if ('1.1' == $this->protocolVersion) { $this->setHeader('Transfer-Encoding', 'chunked'); } else { throw new RequestException('Cannot determine entity body ' . 'size and cannot use chunked Transfer-Encoding when ' . 'using HTTP/' . $this->protocolVersion ); } } // Always add the Expect: 100-Continue header when sending an entity // body other than application/x-www-form-urlencoded using HTTP/1.1 if ($continuePayload && $this->protocolVersion == '1.1' && $this->body && !$this->getHeader('Expect', null, true)) { $this->setHeader('Expect', '100-Continue'); } } /** * Get the post fields that will be used in the request * * @return QueryString */ public function getPostFields() { if (!$this->postFields) { $this->postFields = new QueryString(); $this->postFields->setPrefix(''); $this->addEvent(); } return $this->postFields; } /** * Returns an array of files that will be sent in the request. * * The '@' prefix is removed from the files in the return array * * @return array */ public function getPostFiles() { $files = $this->getPostFields()->filter(function($key, $value) { return $value && $value[0] == '@'; })->getAll(); foreach ($files as $key => &$value) { $value = ($value[0] == '@') ? substr($value, 1) : $value; } return $files; } /** * Add POST fields to use in the request * * @param QueryString|array $fields POST fields * * @return EntityEnclosingRequest */ public function addPostFields($fields) { if (!$this->hasHeader('Content-Type')) { $this->setHeader('Content-Type', 'application/x-www-form-urlencoded'); } $this->getPostFields()->merge($fields); return $this; } /** * Add POST files to use in the upload * * @param array $files An array of filenames to POST * * @return EntityEnclosingRequest * * @throws BodyException if the file cannot be read */ public function addPostFiles(array $files) { // Post files have been added, so pass the parameters as an // array. Passing as an array causes the Content-Type to be // multipart/form-data $this->setHeader('Content-Type', 'multipart/form-data'); $files = (array) $files; $normalized = array(); $total = count($files); foreach ($files as $key => $file) { if (is_numeric($key)) { $key = 'file'; } $found = ($file[0] == '@') ? is_readable(substr($file, 1)) : is_readable($file); if (!$found) { throw new RequestException('File cannot be opened for reading: ' . $file); } if ($file[0] != '@') { $file = '@' . $file; } $this->getPostFields()->add($key, $file); } return $this; } /** * Attach the POST request to the parent pre-processing chain */ private function addEvent() { $sm = $this->getEventManager(); if (!$sm->hasObserver($this)) { // Attach to itself at a high priority $sm->attach($this, 9999); } } }src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php000064400000003320151742733540020150 0ustar00 */ interface EntityEnclosingRequestInterface extends RequestInterface, Observer { /** * Set the body of the request * * @param string|resource|EntityBody $body Body to use in the entity body * of the request * @param string $contentType (optional) Content-Type to set. Leave null * to use an existing Content-Type or to guess the Content-Type * * @return EntityEnclosingRequestInterface */ function setBody($body, $contentType = null); /** * Get the body of the request if set * * @return EntityBody|null */ function getBody(); /** * Get the post fields that will be used in the request * * @return QueryString */ function getPostFields(); /** * Returns an array of files that will be sent in the request. * * The '@' prefix is removed from the files in the return array * * @return array */ function getPostFiles(); /** * Add the POST fields to use in the request * * @param QueryString|array $fields POST fields * * @return EntityEnclosingRequestInterface */ function addPostFields($fields); /** * Add POST files to use in the upload * * @param array $files An array of filenames to POST * * @return EntityEnclosingRequestInterface * * @throws BodyException if the file cannot be read */ function addPostFiles(array $files); }src/Guzzle/Http/Message/MessageInterface.php000064400000007526151742733540015061 0ustar00 */ interface MessageInterface { /** * Get application and plugin specific parameters set on the message. The * return object is a reference to the internal object. * * @return Collection */ function getParams(); /** * Add and merge in an array of HTTP headers. * * @param array $headers Associative array of header data. * * @return MessageInterface */ function addHeaders(array $headers); /** * Retrieve an HTTP header by name * * @param string $header The case-insensitive header to retrieve. Can be a * regular expression * @param mixed $default (optional) If the header is not found, the passed * $default value will be returned * * @return string|null Returns the matching HTTP header or NULL if the * header is not found */ function getHeader($header, $default = null); /** * Get all or all matching headers. * * @param array $names (optional) Pass an array of header names to retrieve * only a particular subset of headers. Regular expressions are * accepted in the $names array of values. * * @return array Returns an array of all headers if no $names array is * specified, or an array of only the headers matching the values in * the $names array. */ function getHeaders(array $headers = null); /** * Returns TRUE or FALSE if the specified header is present. * * @param string $header The header to check. This parameter can also be a * regular expression * @param bool $caseInsensitive (optional) Set to TRUE to compliment the * $header argument and match headers in a case-insensitive comparison. * * @return bool Returns TRUE if the header is present and FALSE if not set */ function hasHeader($header, $caseInsensitive = false); /** * Remove a specific HTTP header. * * @param string $header HTTP header to remove. * * @return MessageInterface */ function removeHeader($header); /** * Set an HTTP header * * @param string $header Name of the header to set. * @param mixed $value Value to set. * * @return MessageInterface */ function setHeader($header, $value); /** * Overwrite all HTTP headers with the supplied array of headers * * @param array $headers Associative array of header data. * * @return MessageInterface */ function setHeaders(array $headers); /** * Get the raw message headers as a string * * @return string */ function getRawHeaders(); /** * Get a Cache-Control directive from the message * * @param string $directive Directive to retrieve * * @return null|string */ function getCacheControlDirective($directive); /** * Check if the message has a Cache-Control directive * * @param string $directive Directive to check * * @return bool */ function hasCacheControlDirective($directive); /** * Add a Cache-Control directive on the message * * @param string $directive Directive to set * @param bool|string $value Value to set * * @return MessageInterface */ function addCacheControlDirective($directive, $value); /** * Remove a Cache-Control directive from the message * * @param string $directive Directive to remove * * @return MessageInterface */ function removeCacheControlDirective($directive); }src/Guzzle/Http/Message/Request.php000064400000077255151742733540013312 0ustar00 */ class Request extends AbstractMessage implements RequestInterface { /** * @var Url HTTP Url */ protected $url; /** * @var string HTTP method (GET, PUT, POST, DELETE, HEAD, OPTIONS, TRACE) */ protected $method; /** * @var EventManager Subject mediator */ protected $eventManager; /** * @var Response Response of the request */ protected $response; /** * @var EntityBody Response body */ protected $responseBody; /** * @var bool Has the response been processed */ protected $processedResponse = false; /** * @var string State of the request object */ protected $state; /** * @var Cookie Cookies to send with the request */ protected $cookie; /** * @param string Auth username */ protected $username; /** * @param string Auth password */ protected $password; /** * @param mixed Callable function used to process the response */ protected $onComplete; /** * @var Collection cURL specific transfer options */ protected $curlOptions; /** * @var CurlFactory Curl factory object used to create cURL handles */ protected $curlFactory; /** * @var CurlHandle cURL handle associated with the request */ protected $curlHandle; /** * Create a new request * * @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 (optional) HTTP headers * @param CurlFactoryInterface $curlFactory (optional) Curl factory object */ public function __construct($method, $url, $headers = array(), CurlFactoryInterface $curlFactory = null) { $this->method = strtoupper($method); if (is_array($headers)) { $this->headers = new Collection($headers); } else if ($headers instanceof Collection) { $this->headers = $headers; } else { $this->headers = new Collection(); } $this->curlFactory = $curlFactory ?: CurlFactory::getInstance(); $this->curlOptions = new Collection(); $this->cookie = Cookie::factory($this->getHeader('Cookie')); $this->eventManager = new EventManager($this); $this->onComplete = array(__CLASS__, 'onComplete'); if (!$this->hasHeader('User-Agent', true)) { $this->setHeader('User-Agent', Guzzle::getDefaultUserAgent()); } $this->parseCacheControlDirective(); $this->setState(self::STATE_NEW); $this->setUrl($url); } /** * Release curl handle * * @codeCoverageIgnore */ public function __destruct() { $this->releaseCurlHandle(); } /** * Clone the request object, leaving off any response that was received */ public function __clone() { $eventManager = new EventManager($this); foreach ($this->eventManager->getAttached() as $o) { $eventManager->attach($o, $this->eventManager->getPriority($o)); } $this->eventManager = $eventManager; $this->curlOptions = clone $this->curlOptions; $this->headers = clone $this->headers; $this->params = clone $this->params; $this->url = clone $this->url; $this->curlHandle = $this->response = $this->responseBody = null; $this->params->set('queued_response', false); $this->processedResponse = false; $this->setState(RequestInterface::STATE_NEW); } /** * Get the HTTP request as a string * * @return string */ public function __toString() { return $this->getRawHeaders() . "\r\n\r\n"; } /** * Default onComplete method that will throw exceptions if an unsuccessful * response is received. * * @param RequestInterface $request Request that completed * @param Response $response Response that was received * @param array $default Callable default response processor * * @throws BadResponseException if the response is not successful */ public static function onComplete(RequestInterface $request, Response $response, array $default) { // Throw an exception if the request was not successful if ($response->isClientError() || $response->isServerError()) { $messageParts = array( '[status code] ' . $response->getStatusCode(), '[reason phrase] ' . $response->getReasonPhrase(), '[url] ' . $request->getUrl(), '[request] ' . (string) $request, '[response] ' . (string) $response ); $e = new BadResponseException('Unsuccessful response | ' . implode(' | ', array_filter($messageParts, function($message) { return preg_match('/\[[A-Za-z0-9 ]+\]\s.+/', $message); }))); $e->setResponse($response); $e->setRequest($request); $request->getEventManager()->notify('request.failure', $e); throw $e; } $request->getEventManager()->notify('request.success', $response); } /** * Get the raw message headers as a string * * @return string */ public function getRawHeaders() { $raw = $this->method . ' ' . $this->getResourceUri(); $protocolVersion = $this->protocolVersion ?: '1.1'; $raw = trim($raw) . ' ' . strtoupper(str_replace('https', 'http', $this->url->getScheme())) . '/' . $protocolVersion . "\r\n"; foreach ($this->headers as $key => $value) { $raw .= $key . ': ' . $value . "\r\n"; } return rtrim($raw, "\r\n"); } /** * Set the URL of the request * * Warning: Calling this method will modify headers, rewrite the query * string object, and set other data associated with the request. * * @param string|Url $url Full URL to set including query string * * @return Request */ public function setUrl($url) { if (is_string($url)) { $this->url = Url::factory($url); } else if ($url instanceof Url) { $this->url = $url; } else { throw new \InvalidArgumentException('Invalid URL sent to ' . __METHOD__); } $this->setHost($this->url->getHost()); $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; } /** * Get the EventManager of the request * * @return EventManager */ public function getEventManager() { return $this->eventManager; } /** * Send the request * * @return Response * @throws RequestException on a request error */ public function send() { if ($this->state != self::STATE_NEW) { $this->setState(self::STATE_NEW); } try { try { $this->state = self::STATE_TRANSFER; $this->getEventManager()->notify('request.before_send'); if (!$this->response && !$this->getParams()->get('queued_response')) { curl_exec($this->getCurlHandle()->getHandle()); } $this->setState(self::STATE_COMPLETE); } catch (BadResponseException $e) { $this->getEventManager()->notify('request.bad_response'); if ($this->response) { $e->setResponse($this->response); } throw $e; } } catch (RequestException $e) { $e->setRequest($this); $this->getEventManager()->notify('request.exception', $e); throw $e; } return $this->response; } /** * Get the previously received {@see Response} or NULL if the request has * not been sent * * @return Response|null */ public function getResponse() { return $this->response; } /** * Get the collection of key value pairs that will be used as the query * string in the request * * @param bool $asString (optional) Set to TRUE to get the query as string * * @return QueryString|string */ public function getQuery($asString = false) { return $asString ? (string) $this->url->getQuery() : $this->url->getQuery(); } /** * Get the HTTP method of the request * * @return string */ public function getMethod() { return $this->method; } /** * Get the URI scheme of the request (http, https, ftp, etc) * * @return string */ public function getScheme() { return $this->url->getScheme(); } /** * Set the URI scheme of the request (http, https, ftp, etc) * * @param string $scheme Scheme to set * * @return Request */ public function setScheme($scheme) { $this->url->setScheme($scheme); return $this; } /** * Get the host of the request * * @return string */ public function getHost() { return $this->url->getHost(); } /** * Set the host of the request. * * @param string $host Host to set (e.g. www.yahoo.com, www.yahoo.com) * * @return Request */ public function setHost($host) { $parts = explode(':', $host); $this->url->setHost($parts[0]); if (isset($parts[1])) { $this->setPort($parts[1]); } else { $this->headers->set('Host', $host); } return $this; } /** * Get the HTTP protocol version of the request * * @param bool $curlValue (optional) Set to TRUE to retrieve the cURL value * for the HTTP protocol version * * @return string|int */ public function getProtocolVersion($curlValue = false) { if (!$curlValue) { return $this->protocolVersion; } else { return ($this->protocolVersion === '1.0') ? CURL_HTTP_VERSION_1_0 : CURL_HTTP_VERSION_1_1; } } /** * 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 Request */ public function setProtocolVersion($protocol) { $this->protocolVersion = $protocol; return $this; } /** * Get the path of the request (e.g. '/', '/index.html') * * @return string */ public function getPath() { return $this->url->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 Request */ public function setPath($path) { $this->url->setPath($path); return $this; } /** * Get the port that the request will be sent on if it has been set * * @return int|null */ public function getPort() { return $this->url->getPort(); } /** * Set the port that the request will be sent on * * @param int $port Port number to set * * @return Request */ 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 if (($this->url->getScheme() == 'http' && $port != 80) || ($this->url->getScheme() == 'https' && $port != 443)) { $this->headers->set('Host', $this->url->getHost() . ':' . $port); } else { $this->headers->set('Host', $this->url->getHost()); } return $this; } /** * Get the username to pass in the URL if set * * @return string|null */ public function getUsername() { return $this->username; } /** * Get the password to pass in the URL if set * * @return string|null */ public function getPassword() { return $this->password; } /** * Set HTTP authorization parameters * * @param string|false $user (optional) User name or false disable authentication * @param string $password (optional) Password * @param string $scheme (optional) Curl authentication scheme to use * * @return Request * * @see http://www.ietf.org/rfc/rfc2617.txt * @throws RequestException */ public function setAuth($user, $password = '', $scheme = CURLAUTH_BASIC) { // If we got false or null, disable authentication if (!$user || !$password) { $this->password = $this->username = null; $this->removeHeader('Authorization'); $this->getCurlOptions()->remove(CURLOPT_HTTPAUTH); } else { $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; } /** * Get the URI of the request (e.g. '/', '/index.html', '/index.html?q=1) * This is the path plus the query string, fragment * * @return string */ public function getResourceUri() { $url = $this->url->getPath(); $query = (string) $this->url->getQuery(); if ($query) { $url .= $query; } return $url; } /** * Get the full URL of the request (e.g. 'http://www.guzzle-project.com/') * * scheme://username:password@domain:port/path?query_string#fragment * * @return string */ public function getUrl() { return (string) $this->url; } /** * Get the state of the request. One of 'complete', 'sending', 'new' * * @return string */ public function getState() { return $this->state; } /** * Set the state of the request * * @param string $state State of the request (complete, sending, or new) * * @return Request */ public function setState($state) { $this->state = $state; switch ($state) { case self::STATE_NEW: $this->response = null; $this->responseBody = null; $this->processedResponse = false; $this->getParams()->remove('queued_response'); $this->curlOptions->clear(); $this->releaseCurlHandle(); break; case self::STATE_COMPLETE: $this->processResponse(); break; } return $this; } /** * Get the cURL options that will be applied when the cURL handle is created * * @return Collection */ public function getCurlOptions() { return $this->curlOptions; } /** * Get the cURL handle * * This method will only create the cURL handle once. After calling this * method, subsequent modifications to this request will not ever take * effect or modify the curl handle associated with the request until * ->setState('new') is called, causing a new cURL handle to be given to * the request (using a smart factory, the new handle might be the same * handle). * * @return CurlHandle|null Returns NULL if no handle should be created */ public function getCurlHandle() { // Create a new cURL handle using the cURL factory if (!$this->curlHandle) { $this->getEventManager()->notify('request.curl.before_create'); $this->curlHandle = $this->curlFactory->getHandle($this); $this->getEventManager()->notify('request.curl.after_create', $this->curlHandle); } return $this->curlHandle; } /** * Set the factory that will create cURL handles based on the request * * @param CurlFactoryInterface $factory Factory used to create cURL handles * * @return Request * @codeCoverageIgnore */ public function setCurlFactory(CurlFactoryInterface $factory) { $this->curlFactory = $factory; return $this; } /** * Method to receive HTTP response headers as they are retrieved * * @param string $data Header data. * * @return integer Returns the size of the data. */ public function receiveResponseHeader($data) { $this->state = self::STATE_TRANSFER; $length = strlen($data); // Normalize line endings $data = preg_replace("/([^\r])(\n)\b/", "$1\r\n", $data); if (preg_match('/^\HTTP\/1\.[0|1]\s\d{3}\s.+$/', $data)) { $previousResponse = $this->response; list($dummy, $code, $status) = explode(' ', str_replace("\r\n", '', $data), 3); $this->response = new Response($code, null, $this->getResponseBody()); $this->response->setStatus($code, $status)->setRequest($this); $this->getEventManager()->notify('request.receive.status_line', array( 'line' => $data, 'status_code' => $code, 'reason_phrase' => $status, 'previous_response' => $previousResponse ), true); } else if ($length > 2) { list($header, $value) = array_map('trim', explode(':', trim($data), 2)); $this->response->addHeaders(array( $header => $value )); $this->getEventManager()->notify('request.receive.header', array( 'header' => $header, 'value' => $value ), true); } return $length; } /** * 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 (optional) Set to TRUE to keep the request in a stat * of not having been sent, but queue the response for send() * * @return Request Returns a reference to the object. */ public function setResponse(Response $response, $queued = false) { $response->setRequest($this); if (!$queued) { $this->getParams()->remove('queued_response'); $this->response = $response; $this->responseBody = $response->getBody(); $this->processResponse(); } else { $this->getParams()->set('queued_response', $response); } $this->getEventManager()->notify('request.set_response', $this->response); return $this; } /** * Set the EntityBody that will hold the 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 EntityBody $body Response body object * * @return Request */ public function setResponseBody(EntityBody $body) { $this->responseBody = $body; return $this; } /** * Determine if the response body is repeatable (readable + seekable) * * @return bool */ public function isResponseBodyRepeatable() { return !$this->responseBody ? true : $this->responseBody->isSeekable() && $this->responseBody->isReadable(); } /** * Get an array of Cookies or a specific cookie from the request * * @param string $name (optional) Cookie to retrieve * * @return null|string|Cookie Returns null if not found by name, a Cookie * object if no $name is supplied, or the cookie value by name if found * If a Cookie object is returned, changes to the cookie object does * not modify the request's cookies. You will need to set the cookie * back on the request after modifying the object. */ public function getCookie($name = null) { return !$name ? clone $this->cookie : $this->cookie->get($name); } /** * Set the Cookie header using an array or Cookie object * * @param array|Cookie $cookies Cookie data to set on the request * * @return Request */ public function setCookie($cookies) { if ($cookies instanceof Cookie) { $this->cookie = $cookies; } else if (is_array($cookies)) { $this->cookie->replace($cookies); } else { throw new \InvalidArgumentException('Invalid cookie data'); } $this->headers->set('Cookie', (string) $this->cookie); return $this; } /** * 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 Request */ public function addCookie($name, $value) { $this->cookie->add($name, $value); $this->headers->set('Cookie', (string) $this->cookie); return $this; } /** * Remove the cookie header or a specific cookie value by name * * @param string $name (optional) Cookie to remove by name. If no value is * provided, the entire Cookie header is removed from the request * * @return Request */ public function removeCookie($name = null) { $this->cookie->remove($name); $this->headers->set('Cookie', (string) $this->cookie); return $this; } /** * Returns whether or not the response served to the request can be cached * * @return bool */ public function canCache() { // Only GET and HEAD requests can be cached if ($this->method != RequestInterface::GET && $this->method != RequestInterface::HEAD) { return false; } // Never cache requests when using no-store if ($this->hasCacheControlDirective('no-store')) { return false; } return true; } /** * Setting an onComplete method will override the default behavior of * throwing an exception when an unsuccessful response is received. The * callable function passed to this method should resemble the following * prototype: * * function myOncompleteFunction(RequestInterface $request, Response $response, \Closure $default); * * The default onComplete method can be invoked from your custom handler by * calling the $default closure passed to your function. * * @param mixed $callable Method to invoke when a request completes. * * @return Request * @throws InvalidArgumentException if the method is not callable */ public function setOnComplete($callable) { if (!is_callable($callable)) { throw new \InvalidArgumentException('onComplete method must be callable'); } $this->onComplete = $callable; return $this; } /** * {@inheritdoc} */ protected function changedHeader($action, $keyOrArray) { $keys = (array) $keyOrArray; parent::changedHeader($action, $keys); if (in_array('Cookie', $keys)) { if ($action == 'set') { $this->cookie = Cookie::factory($this->getHeader('Cookie')); } else { $this->cookie->clear(); } } if (in_array('Host', $keys)) { $parts = explode(':', $this->getHeader('Host')); $this->url->setHost($parts[0]); if (isset($parts[1])) { $this->url->setPort($parts[1]); } else if ($this->url->getScheme() == 'http') { $this->url->setPort(80); } else if ($this->url->getScheme() == 'https') { $this->url->setPort(443); } } } /** * Get the EntityBody that will store the received response entity body * * @return EntityBody */ protected function getResponseBody() { if ($this->responseBody === null) { $this->responseBody = EntityBody::factory(''); } return $this->responseBody; } /** * Process a received response * * @throws BadResponseException on unsuccessful responses */ protected function processResponse() { if (!$this->processedResponse) { // Use the queued response if one is set if ($this->getParams()->get('queued_response')) { $this->response = $this->getParams()->get('queued_response'); $this->responseBody = $this->response->getBody(); $this->getParams()->remove('queued_response'); } else if ($this->curlHandle && $this->curlHandle->getErrorNo()) { $error = $this->curlHandle->getError(); $e = new CurlException('[curl] ' . $this->curlHandle->getErrorNo() . ': ' . $error . ' [url] ' . $this->getUrl() . ' [info] ' . var_export($this->curlHandle->getInfo(), true) . ' [debug] ' . $this->curlHandle->getStderr()); $e->setRequest($this); $e->setCurlError($error); $this->curlFactory->releaseHandle($this->curlHandle, true); $this->curlHandle = null; throw $e; } if (!$this->response) { $e = new RequestException( 'Unable to set state to complete because no response has ' . 'been received by the request' ); $e->setRequest($this); throw $e; } // cURL can modify the request from it's initial HTTP message. The // following code parses the sent HTTP request headers from cURL and // updates the request object to most accurately reflect the HTTP // message sent over the wire. if ($this->curlHandle && $this->curlHandle->isAvailable()) { // Set the transfer stats on the response $log = $this->curlHandle->getStderr(); $this->response->setInfo(array_merge(array( 'stderr' => $log ), $this->curlHandle->getInfo())); // Parse the cURL stderr output for outgoing requests $headers = ''; fseek($this->curlHandle->getStderr(true), 0); while (($line = fgets($this->curlHandle->getStderr(true))) !== false) { if ($line && $line[0] == '>') { $headers = substr(trim($line), 2) . "\r\n"; while (($line = fgets($this->curlHandle->getStderr(true))) !== false) { if ($line[0] == '*' || $line[0] == '<') { break; } else { $headers .= trim($line) . "\r\n"; } } } } if ($headers) { $parsed = RequestFactory::parseMessage($headers); if (!empty($parsed['method'])) { $this->method = $parsed['method']; } if (!empty($parsed['parts']['host'])) { $this->setHost($parsed['parts']['host']); } if (!empty($parsed['parts']['port'])) { $this->setPort($parsed['parts']['port']); } if (!empty($parsed['protocol_version'])) { $this->setProtocolVersion($parsed['protocol_version']); } $this->headers->clear(); foreach ($parsed['headers'] as $name => $value) { $this->setHeader($name, $value); } } } $this->state = self::STATE_COMPLETE; $this->getEventManager()->notify('request.sent'); // Some response processors will remove the response or reset the state if ($this->state == RequestInterface::STATE_COMPLETE) { $this->processedResponse = true; $this->getEventManager()->notify('request.complete', $this->response); // Release the cURL handle $this->releaseCurlHandle(); // Pass the request to the onComplete handler call_user_func($this->onComplete, $this, $this->response, array(__CLASS__, 'defaultOnComplete')); } } return $this; } /** * Release the cURL handle if one is claimed * * @return Request */ public function releaseCurlHandle() { if ($this->curlHandle) { $this->getEventManager()->notify('request.curl.release', $this->curlHandle); // Check if the handle should be closed $this->curlFactory->releaseHandle( $this->curlHandle, ($this->response && $this->response->getConnection() == 'close') || $this->getHeader('Connection') == 'close' || $this->getProtocolVersion() === '1.0' ); $this->curlHandle = null; } return $this; } }src/Guzzle/Http/Message/RequestException.php000064400000001241151742733540015147 0ustar00request = $request; } /** * Get the request that caused the exception * * @return RequestInterface */ public function getRequest() { return $this->request; } }src/Guzzle/Http/Message/RequestFactory.php000064400000025323151742733540014627 0ustar00 * $request = RequestFactory::get('http://www.google.com/'); * $response = $request->send(); * * * @author Michael Dowling */ class RequestFactory { /** * @var Standard request headers */ protected static $requestHeaders = array( 'accept', 'accept-charset', 'accept-encoding', 'accept-language', 'authorization', 'cache-control', 'connection', 'cookie', 'content-length', 'content-type', 'date', 'expect', 'from', 'host', 'if-match', 'if-modified-since', 'if-none-match', 'if-range', 'if-unmodified-since', 'max-forwards', 'pragma', 'proxy-authorization', 'range', 'referer', 'te', 'upgrade', 'user-agent', 'via', 'warning' ); /** * @var string Class to instantiate for GET, HEAD, and DELETE requests */ protected static $requestClass = 'Guzzle\\Http\\Message\\Request'; /** * @var string Class to instantiate for POST and PUT requests */ protected static $entityEnclosingRequestClass = 'Guzzle\\Http\\Message\\EntityEnclosingRequest'; /** * Parse an HTTP message and return an array of request information * * @param string $message HTTP request message to parse * * @return array|bool Returns FALSE on failure or an array containing the * following key value pairs: * * # method: HTTP request method (e.g. GET, HEAD, POST, PUT, etc) * # protocol - HTTP protocol (e.g. HTTP) * # protocol_version: HTTP protocol version (e.g. 1.1) * # parts: array of request parts as seen in parse_url() * # headers: associative array of request headers * # body: string containing the request body */ public static function parseMessage($message) { if (!$message) { return false; } // Normalize new lines in the message $message = preg_replace("/([^\r])(\n)\b/", "$1\r\n", $message); $parts = explode("\r\n\r\n", $message, 2); $headers = array(); $scheme = $host = $method = $user = $pass = $query = $port = $version = $protocol = ''; $path = '/'; // Parse each line in the message foreach (explode("\r\n", $parts[0]) as $line) { $matches = array(); if (preg_match('#^(?PGET|POST|PUT|HEAD|DELETE|TRACE|OPTIONS)\s+(?P/.*)\s+(?P\w+)/(?P\d\.\d)\s*$#i', $line, $matches)) { $method = strtoupper($matches['method']); $protocol = strtoupper($matches['protocol']); $path = $matches['path']; $version = $matches['version']; $scheme = 'http'; } else if (strpos($line, ':')) { list($key, $value) = explode(':', $line, 2); $key = trim($key); // Normalize standard HTTP headers if (in_array(strtolower($key), self::$requestHeaders)) { $key = trim(str_replace(' ', '-', ucwords(str_replace('-', ' ', $key)))); } $headers[$key] = trim($value); } } // Check if a body is present in the message $body = (isset($parts[1])) ? $parts[1] : null; // Check for the Host header $host = isset($headers['Host']) ? $headers['Host'] : ''; if (strpos($host, ':')) { list($host, $port) = array_map('trim', explode(':', $host)); if ($port == 443) { $scheme = 'https'; } } else { $port = ''; } // Check for basic authorization $auth = isset($headers['Authorization']) ? $headers['Authorization'] : ''; if ($auth) { list($type, $data) = explode(' ', $auth); if (strtolower($type) == 'basic') { $data = base64_decode($data); list($user, $pass) = explode(':', $data); } } // Check if a query is present $qpos = strpos($path, '?'); if ($qpos) { $query = substr($path, $qpos); $path = substr($path, 0, $qpos); } return array( 'method' => $method, 'protocol' => $protocol, 'protocol_version' => $version, 'parts' => array( 'scheme' => $scheme, 'host' => $host, 'port' => $port, 'user' => $user, 'pass' => $pass, 'path' => $path, 'query' => $query ), 'headers' => $headers, 'body' => $body ); } /** * Create a new request based on an HTTP message * * @param string $message HTTP message as a string * * @return RequestInterface */ public static function fromMessage($message) { $parsed = self::parseMessage($message); if (!$parsed) { return false; } return self::fromParts( $parsed['method'], $parsed['parts'], $parsed['headers'], $parsed['body'], $parsed['protocol'], $parsed['protocol_version'] ); } /** * Create a request from URL parts as returned from parse_url() * * @param string $method HTTP method (GET, POST, PUT, HEAD, DELETE, etc) * * @param array $parts 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 (optional) HTTP headers * @param string|resource|array|EntityBody $body Body to send in the request * @param string $protocol (optional) Protocol (HTTP, SPYDY, etc) * @param string $protocolVersion (optional) 1.0, 1.1, etc * * @return RequestInterface */ public static function fromParts($method, array $parts, $headers = null, $body = null, $protocol = 'HTTP', $protocolVersion = '1.1') { return self::create($method, Url::buildUrl($parts, true), $headers, $body) ->setProtocolVersion($protocolVersion); } /** * Create a new request based on the HTTP method * * @param string $method HTTP method (GET, POST, PUT, HEAD, DELETE, etc) * @param string $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 (optional) HTTP headers * @param string|resource|array|EntityBody $body Body to send in the request * * @return RequestInterface */ public static function create($method, $url, $headers = null, $body = null) { if ($method != 'POST' && $method != 'PUT') { $c = static::$requestClass; $request = new $c($method, $url, $headers); } else { $c = static::$entityEnclosingRequestClass; $request = new $c($method, $url, $headers); if ($body) { if ($method == 'POST' && (is_array($body) || $body instanceof Collection)) { $request->addPostFields($body); } else if (is_resource($body) || $body instanceof EntityBody) { $request->setBody($body); } else { $request->setBody((string) $body); } } } return $request; } /** * Create a new GET request * * @param string $url URL of the GET request * @param array|Collection $headers (optional) HTTP headers * @param string|resource|array|EntityBody $body (optional) Where to store * the response entity body * * @return Request */ public static function get($url, $headers = null, $body = null) { $request = self::create(RequestInterface::GET, $url, $headers); if ($body) { $request->setResponseBody($body); } return $request; } /** * Create a new HEAD request * * @param string $url URL of the HEAD request * @param array|Collection $headers (optional) HTTP headers * * @return Request */ public static function head($url, $headers = null) { return self::create(RequestInterface::HEAD, $url, $headers); } /** * Create a new DELETE request * * @param string $url URL of the DELETE request * @param array|Collection $headers (optional) HTTP headers * * @return Request */ public static function delete($url, $headers = null) { return self::create(RequestInterface::DELETE, $url, $headers); } /** * Create a new POST request * * @param string $url URL of the POST request * @param array|Collection $headers (optional) HTTP headers * @param array|Collection $postFields (optional) Associative array of POST * fields to send in the body of the request. Prefix a value in the * array with the @ symbol reference a file. * * @return EntityEnclosingRequest */ public static function post($url, $headers = null, $postFields = null) { return self::create(RequestInterface::POST, $url, $headers, $postFields); } /** * Create a new PUT request * * @param string $url URL of the PUT request * @param array|Collection $headers (optional) HTTP headers * @param string|resource|array|EntityBody $body Body to send in the request * * @return EntityEnclosingRequest */ public static function put($url, $headers = null, $body = null) { return self::create(RequestInterface::PUT, $url, $headers, $body); } /** * Create a new OPTIONS request * * @param string $url URL of the OPTIONS request * @param array|Collection $headers (optional) HTTP headers * * @return Request */ public static function options($url, $headers = null, $body = null) { return self::create(RequestInterface::OPTIONS, $url, $headers, $body); } }src/Guzzle/Http/Message/RequestInterface.php000064400000025231151742733540015116 0ustar00 */ interface RequestInterface extends MessageInterface, Subject { const STATE_NEW = 'new'; const STATE_COMPLETE = 'complete'; const STATE_TRANSFER = 'transfer'; const AUTH_BASIC = 'Basic'; const AUTH_DIGEST = 'Digest'; const GET = 'GET'; const PUT = 'PUT'; const POST = 'POST'; const DELETE = 'DELETE'; const HEAD = 'HEAD'; const CONNECT = 'CONNECT'; const OPTIONS = 'OPTIONS'; const TRACE = 'TRACE'; /** * Create a new request * * @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 (optional) HTTP headers * @param CurlFactoryInterface $curlFactory (optional) Curl factory object */ function __construct($method, $url, $headers = array(), CurlFactoryInterface $curlFactory = null); /** * Clone the request object, leaving off any response that was received */ function __clone(); /** * Get the HTTP request as a string * * @return string */ function __toString(); /** * Set the URL of the request * * Warning: Calling this method will modify headers, rewrite the query * string object, and set other data associated with the request. * * @param string $url Full URL to set including query string * * @return RequestInterface */ function setUrl($url); /** * Send the request * * @return Response * @throws RequestException on a request error */ function send(); /** * Get the previously received {@see Response} or NULL if the request has * not been sent * * @return Response|null */ function getResponse(); /** * Get the collection of key value pairs that will be used as the query * string in the request * * @return QueryString */ function getQuery(); /** * Get the HTTP method of the request * * @return string */ function getMethod(); /** * Get the URI scheme of the request (http, https, ftp, etc) * * @return string */ function getScheme(); /** * Set the URI scheme of the request (http, https, ftp, etc) * * @param string $scheme Scheme to set * * @return RequestInterface */ function setScheme($scheme); /** * Get the host of the request * * @return string */ 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 RequestInterface */ function setHost($host); /** * Get the HTTP protocol version of the request * * @param bool $curlValue (optional) Set to TRUE to retrieve the cURL value * for the HTTP protocol version * * @return string|int */ function getProtocolVersion($curlValue = false); /** * 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 RequestInterface */ function setProtocolVersion($protocol); /** * Get the path of the request (e.g. '/', '/index.html') * * @return string */ 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 RequestInterface */ function setPath($path); /** * Get the port that the request will be sent on if it has been set * * @return int|null */ function getPort(); /** * Set the port that the request will be sent on * * @param int $port Port number to set * * @return RequestInterface */ function setPort($port); /** * Get the username to pass in the URL if set * * @return string|null */ function getUsername(); /** * Set HTTP authorization parameters * * @param string|false $user (optional) User name or false disable authentication * @param string $password (optional) Password * @param string $scheme (optional) Authentication scheme to use (Basic, Digest) * * @return Request * * @see http://www.ietf.org/rfc/rfc2617.txt * @throws RequestException */ function setAuth($user, $password = '', $scheme = 'Basic'); /** * Get the password to pass in the URL if set * * @return string|null */ function getPassword(); /** * Get the URI of the request (e.g. '/', '/index.html', '/index.html?q=1) * This is the path plus the query string, fragment * * @return string */ function getResourceUri(); /** * Get the full URL of the request (e.g. 'http://www.guzzle-project.com/') * * scheme://username:password@domain:port/path?query_string#anchor * * @return string */ function getUrl(); /** * Get the state of the request. One of 'complete', 'sending', 'new' * * @return string */ function getState(); /** * Set the state of the request * * @param string $state State of the request (complete, sending, or new) * * @return RequestInterface */ function setState($state); /** * Get the cURL options that will be applied when the cURL handle is created * * @return Collection */ function getCurlOptions(); /** * Get the cURL handle * * This method will only create the cURL handle once. After calling this * method, subsequent modifications to this request will not ever take * effect or modify the curl handle associated with the request until * ->setState('new') is called, causing a new cURL handle to be given to * the request (using a smart factory, the new handle might be the same * handle). * * @return CurlHandle|null Returns NULL if no handle should be created */ function getCurlHandle(); /** * Set the factory that will create cURL handles based on the request * * @param CurlFactoryInterface $factory Factory used to create cURL handles * * @return Request */ function setCurlFactory(CurlFactoryInterface $factory); /** * Method to receive HTTP response headers as they are retrieved * * @param string $data Header data. * * @return integer Returns the size of the data. */ function receiveResponseHeader($data); /** * Set the EntityBody that will hold the 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 EntityBody $body Response body object * * @return Request */ function setResponseBody(EntityBody $body); /** * Determine if the response body is repeatable (readable + seekable) * * @return bool */ function isResponseBodyRepeatable(); /** * 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 (optional) Set to TRUE to keep the request in a stat * of not having been sent, but queue the response for send() * * @return RequestInterface Returns a reference to the object. */ function setResponse(Response $response, $queued = false); /** * Get an array of Cookies or a specific cookie from the request * * @param string $name (optional) Cookie to retrieve * * @return null|string|Cookie Returns null if not found by name, a Cookie * object if no $name is supplied, or the cookie value by name if found * If a Cookie object is returned, changes to the cookie object does * not modify the request's cookies. You will need to set the cookie * back on the request after modifying the object. */ function getCookie($name = null); /** * Set the Cookie header using an array or Cookie object * * @param array|Cookie $cookies Cookie data to set on the request * * @return RequestInterface */ function setCookie($cookies); /** * 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 RequestInterface */ function addCookie($name, $value); /** * Remove the cookie header or a specific cookie value by name * * @param string $name (optional) Cookie to remove by name. If no value is * provided, the entire Cookie header is removed from the request * * @return RequestInterface */ function removeCookie($name = null); /** * Returns whether or not the response served to the request can be cached * * @return bool */ function canCache(); /** * Setting an onComplete method will override the default behavior of * throwing an exception when an unsuccessful response is received. The * callable function passed to this method should resemble the following * prototype: * * function myOncompleteFunction(RequestInterface $request, Response $response, \Closure $default); * * The default onComplete method can be invoked from your custom handler by * calling the $default closure passed to your function. * * @param mixed $callable Method to invoke when a request completes. * * @return RequestInterface * @throws InvalidArgumentException if the method is not callable */ function setOnComplete($callable); /** * Release the cURL handle if one is claimed * * @return RequestInterface */ function releaseCurlHandle(); }src/Guzzle/Http/Message/Response.php000064400000061221151742733540013442 0ustar00 */ class Response extends AbstractMessage { /** * @var array Array of reason phrases and their corresponding status codes */ static private $statusTexts = array( 100 => 'Continue', 101 => 'Switching Protocols', 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary 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', 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Timeout', 505 => 'HTTP Version Not Supported', ); /** * @var EntityBody 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 string Response protocol */ protected $protocol = 'HTTP'; /** * @var array Information about the request */ protected $info = array(); /** * @var RequestInterface Request object that may or may not be set */ protected $request = null; /** * @var array Cacheable response codes (see RFC 2616:13.4) */ protected $cacheResponseCodes = array(200, 203, 206, 300, 301, 410); /** * Create a new Response based on a raw response message * * @param string $message Response message * * @return Response * @throws HttpException if an empty $message is provided */ public static function factory($message) { if (!$message) { throw new HttpException('No response message provided to factory'); } // Normalize line endings $message = preg_replace("/([^\r])(\n)\b/", "$1\r\n", $message); $protocol = $code = $status = ''; $parts = explode("\r\n\r\n", $message, 2); $headers = new Collection(); foreach (array_values(array_filter(explode("\r\n", $parts[0]))) as $i => $line) { // Remove newlines from headers $line = implode(' ', explode("\n", $line)); if ($i === 0) { // Check the status line list($protocol, $code, $status) = array_map('trim', explode(' ', $line, 3)); } else if (strpos($line, ':')) { // Add a header list($key, $value) = array_map('trim', explode(':', $line, 2)); $headers->add($key, $value); } } $body = null; if (isset($parts[1]) && $parts[1] != "\n") { $body = EntityBody::factory(trim($parts[1])); // Always set the appropriate Content-Length if Content-Legnth $headers['Content-Length'] = $body->getSize(); } $response = new self(trim($code), $headers, $body); $response->setProtocol($protocol) ->setStatus($code, $status); return $response; } /** * Construct the response * * @param string $statusCode The response status code (e.g. 200, 404, etc) * @param Collection|array $headers (optional) The response headers * @param string|EntityBody $body (optional) The body of the response * * @throws BadResponseException if an invalid response code is given */ public function __construct($statusCode, $headers = null, $body = null) { if (!array_key_exists($statusCode, self::$statusTexts)) { throw new BadResponseException( 'Invalid response code: ' . $statusCode ); } $this->setStatus($statusCode); if ($headers && (!is_array($headers) && !($headers instanceof Collection))) { throw new BadResponseException('Invalid headers argument received'); } $this->headers = ($headers) ? (is_array($headers) ? new Collection($headers) : $headers) : new Collection(); $this->parseCacheControlDirective(); $this->body = $body ?: EntityBody::factory(''); if ($body instanceof EntityBody) { $this->body = $body; } else if ($body && is_scalar($body)) { $this->body = EntityBody::factory((string) $body); } else if ($body) { throw new BadResponseException('Invalid body sent to ' . __CLASS__ . ' constructor'); } else { $this->body = EntityBody::factory(''); } } /** * Convert the response object to a string * * @return string */ public function __toString() { return $this->getMessage(); } /** * 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 EntityBody|string */ public function getBody($asString = false) { return ($asString) ? (string) $this->body : $this->body; } /** * Set the protocol and protocol version of the response * * @param string $protocol Response protocol and version (e.g. HTTP/1.1) * * @return Response */ public function setProtocol($protocol) { list($this->protocol, $this->protocolVersion) = array_map('trim', explode('/', $protocol, 2)); return $this; } /** * Get the protocol used for the response (e.g. HTTP) * * @return string */ public function getProtocol() { return $this->protocol ?: 'HTTP'; } /** * Get the HTTP protocol version * * @return string */ public function getProtocolVersion() { return $this->protocolVersion ?: '1.1'; } /** * Get a cURL transfer information * * @param string $key (optional) 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 * * http://www.php.net/manual/en/function.curl-getinfo.php */ public function getInfo($key = null) { if ($key === null) { return $this->info; } else if (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 Response */ 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 (optional) Response reason phrase * * @return Response * @throws BadResponseException when an invalid response code is received */ public function setStatus($statusCode, $reasonPhrase = '') { $statusCode = (int) trim($statusCode); if (!array_key_exists($statusCode, self::$statusTexts)) { throw new BadResponseException('Invalid response code: ' . $statusCode); } $this->statusCode = $statusCode; $this->reasonPhrase = $reasonPhrase ? $reasonPhrase : self::$statusTexts[$statusCode]; 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"; foreach ($this->headers as $key => $value) { foreach ((array) $value as $v) { $headers .= $key . ': ' . $v . "\r\n"; } } return $headers . "\r\n"; } /** * Get the request object that is associated with this response * * @return null|Request\Request */ public function getRequest() { return $this->request; } /** * 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|integer Returns what partial content range types this * server supports. */ public function getAcceptRanges() { return $this->headers->get('/^Accept-*Ranges$/i', null, Collection::MATCH_REGEX); } /** * Get the Age HTTP header * * @param bool $headerOnly (optional) Set to TRUE to only retrieve the * Age header rather than calculating the age * * @return integer|null Returns the age the object has been in a proxy cache * in seconds. */ public function getAge($headerOnly = false) { $age = $this->headers->get('Age', null, Collection::MATCH_IGNORE_CASE); if (!$headerOnly && $age === null && $this->getDate()) { $age = time() - strtotime($this->getDate()); } return $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 $this->headers->get('Allow', null, Collection::MATCH_IGNORE_CASE); } /** * 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->getAllow(); if (!$allow) { return false; } else { return false !== array_search( strtoupper($method), array_map('trim', array_map('strtoupper', explode(',', $allow))) ); } } /** * Get the Cache-Control HTTP header * * @return string|null Returns a string that tells all caching mechanisms * from server to client whether they may cache this object. */ public function getCacheControl() { return $this->headers->get('/^Cache-*Control$/i', null, Collection::MATCH_REGEX); } /** * Get the Connection HTTP header * * @return string */ public function getConnection() { return $this->headers->get('Connection', null, Collection::MATCH_IGNORE_CASE); } /** * Get the Content-Encoding HTTP header * * @return string|null Returns the type of encoding used on the data. One * of compress, deflate, gzip, identity. */ public function getContentEncoding() { return $this->headers->get('/^Content-*Encoding$/i', null, Collection::MATCH_REGEX); } /** * Get the Content-Language HTTP header * * @return string|null Returns the language the content is in. */ public function getContentLanguage() { return $this->headers->get('/^Content-*Language$/i', null, Collection::MATCH_REGEX); } /** * Get the Content-Length HTTP header * * @return integer Returns the length of the response body in bytes */ public function getContentLength() { return $this->headers->get('/^Content-*Length$/i', null, Collection::MATCH_REGEX); } /** * 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 $this->headers->get('/^Content-*Location$/i', null, Collection::MATCH_REGEX); } /** * Get the Content-Disposition HTTP header * * @return string|null Returns the Content-Disposition header */ public function getContentDisposition() { return $this->headers->get('/^Content-*Disposition$/i', null, Collection::MATCH_REGEX); } /** * 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 $this->headers->get('/^Content-*MD5$/i', null, Collection::MATCH_REGEX); } /** * 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 $this->headers->get('/^Content-*Range$/i', null, Collection::MATCH_REGEX); } /** * Get the Content-Type HTTP header * * @return string Returns the mime type of this content. */ public function getContentType() { return $this->headers->get('/^Content-*Type$/i', null, Collection::MATCH_REGEX); } /** * 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->getContentType(), $type) !== false; } /** * Get the Date HTTP header * * @return string|null Returns the date and time that the message was sent. */ public function getDate() { return $this->headers->get('Date', null, Collection::MATCH_IGNORE_CASE); } /** * 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() { $etag = $this->headers->get('ETag', null, Collection::MATCH_IGNORE_CASE); return $etag ? str_replace('"', '', $etag) : null; } /** * Get the Expires HTTP header * * @return string|null Returns the date/time after which the response is * considered stale. */ public function getExpires() { return $this->headers->get('Expires', null, Collection::MATCH_IGNORE_CASE); } /** * 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 $this->headers->get('/^Last-*Modified$/i', null, Collection::MATCH_REGEX); } /** * Get the Location HTTP header * * @return string|null Used in redirection, or when a new resource has been * created. (e.g. http://www.w3.org/pub/WWW/People.html) */ public function getLocation() { return $this->headers->get('Location', null, Collection::MATCH_IGNORE_CASE); } /** * Get the Pragma HTTP header * * @return string|null Returns the implementation-specific headers that may * have various effects anywhere along the request-response chain. */ public function getPragma() { return $this->headers->get('Pragma', null, Collection::MATCH_IGNORE_CASE); } /** * Get the Proxy-Authenticate HTTP header * * @return string|null Authentication to access the proxy (e.g. Basic) */ public function getProxyAuthenticate() { return $this->headers->get('/^Proxy-*Authenticate$/i', null, Collection::MATCH_REGEX); } /** * Get the Retry-After HTTP header * * @return integer|null If an entity is temporarily unavailable, this * instructs the client to try again after a specified period of time. */ public function getRetryAfter() { return $this->headers->get('/^Retry-*After$/i', null, Collection::MATCH_REGEX); } /** * Get the Server HTTP header * * @return string|null A name for the server */ public function getServer() { return $this->headers->get('Server', null, Collection::MATCH_IGNORE_CASE); } /** * Get the Set-Cookie HTTP header * * @return string|null An HTTP cookie. */ public function getSetCookie() { return $this->headers->get('/^Set-*Cookie2*$/i', null, Collection::MATCH_REGEX); } /** * 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 $this->headers->get('Trailer', null, Collection::MATCH_IGNORE_CASE); } /** * Get the Transfer-Encoding HTTP header * * @return string|null The form of encoding used to safely transfer the * entity to the user. Currently defined methods are: chunked */ public function getTransferEncoding() { return $this->headers->get('/^Transfer\-*Encoding$/i', null, Collection::MATCH_REGEX); } /** * 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 $this->headers->get('Vary', null, Collection::MATCH_IGNORE_CASE); } /** * Get the Via HTTP header * * @return string|null Informs the client of proxies through which the * response was sent. (e.g. 1.0 fred, 1.1 nowhere.com (Apache/1.1)) */ public function getVia() { return $this->headers->get('Via', null, Collection::MATCH_IGNORE_CASE); } /** * Get the Warning HTTP header * * @return string|null A general warning about possible problems with the * entity body. (e.g. 199 Miscellaneous warning) */ public function getWarning() { return $this->headers->get('Warning', null, Collection::MATCH_IGNORE_CASE); } /** * Get the WWW-Authenticate HTTP header * * @return string|null Indicates the authentication scheme that should be * used to access the requested entity (e.g. Basic) */ public function getWwwAuthenticate() { return $this->headers->get('/^WWW-*Authenticate$/i', null, Collection::MATCH_REGEX); } /** * Checks if HTTP Status code is a Client Error (4xx) * * @return bool */ public function isClientError() { return substr(strval($this->statusCode), 0, 1) == '4'; } /** * 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 substr(strval($this->statusCode), 0, 1) == '1'; } /** * Checks if HTTP Status code is a Redirect (3xx) * * @return bool */ public function isRedirect() { return substr(strval($this->statusCode), 0, 1) == '3'; } /** * Checks if HTTP Status code is Server Error (5xx) * * @return bool */ public function isServerError() { return substr(strval($this->statusCode), 0, 1) == '5'; } /** * Checks if HTTP Status code is Successful (2xx | 304) * * @return bool */ public function isSuccessful() { return substr(strval($this->statusCode), 0, 1) == '2' || $this->statusCode == '304'; } /** * Set the request object associated with the response * * @param RequestInterface The request object used to generate the response * * @return Response */ public function setRequest(RequestInterface $request) { $this->request = $request; return $this; } /** * Check if the response can be cached * * @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(), $this->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->hasCacheControlDirective('no-store')) { return false; } // HTTPS responses must send a Cache-Control: public value for caching if ($this->getRequest() && $this->getRequest()->getScheme() == 'https' && !$this->hasCacheControlDirective('public')) { 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 as specified in RFC 2616-13 * * @return int|null Returns the number of seconds */ public function getMaxAge() { // s-max-age, then max-age, then Expires if ($age = $this->getCacheControlDirective('s-maxage')) { return $age; } if ($age = $this->getCacheControlDirective('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 the freshness * lifetime (maximum age) of the response. * * @return bool|null */ public function isFresh() { $fresh = $this->getFreshness(); return $fresh === null ? null : $this->getFreshness() > 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 * zer0 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->getAge(); return $maxAge && $age ? ($maxAge - $age) : null; } }src/Guzzle/Http/Plugin/CachePlugin.php000064400000033161151742733540013702 0ustar00 */ class CachePlugin implements Observer { /** * @var CacheAdapter Cache adapter used to write cache data to cache objects */ private $adapter; /** * @var bool Whether or not cache items are serialized when storing */ private $serialize; /** * @var int Default cached item lifetime if Response headers are not used */ private $defaultLifetime = 3600; /** * @var array Array of request cache keys to hold until a response is returned */ private $cached = array(); /** * @var array Headers are excluded from the caching (see RFC 2616:13.5.1) */ protected $excludeResponseHeaders = array( 'Connection', 'Keep-Alive', 'Proxy-Authenticate', 'Proxy-Authorization', 'TE', 'Trailers', 'Transfer-Encoding', 'Upgrade', 'Set-Cookie', 'Set-Cookie2' ); /** * Construct a new CachePlugin * * @param CacheAdapterInterface $adapter Cache adapter to write and read cache data * @param bool $serialize (optional) Set to TRUE to serialize data before writing * @param int $defaultLifetime (optional) Set the default cache lifetime */ public function __construct(CacheAdapterInterface $adapter, $serialize = false, $defaultLifetime = 3600) { $this->adapter = $adapter; $this->serialize = (bool) $serialize; $this->defaultLifetime = (int) $defaultLifetime; } /** * Get the cache adapter object * * @return CacheAdapter */ public function getCacheAdapter() { return $this->adapter; } /** * Calculate the hash key of a request object * * @param RequestInterface $request Request to hash * @param string $raw Set to TRUE to retrieve the un-encoded string for debugging * * @return string */ public function getCacheKey(RequestInterface $request, $raw = false) { // See if the key has already been calculated $key = $request->getParams()->get('cache.key'); // Always recalculate when using the raw option if (!$key || $raw) { // Check to see if the key should be filtered $filter = $request->getParams()->get('cache.key_filter'); // The generate the start of the key $key = $request->getScheme() . '&' . $request->getHost() . $request->getPath(); $filterHeaders = array('Cache-Control'); $filterQuery = array(); if ($filter) { // Parse the filter string foreach (explode(';', $filter) as $part) { $pieces = array_map('trim', explode('=', $part)); if (!isset($pieces[1])) { continue; } $remove = array_map('trim', explode(',', $pieces[1])); switch ($pieces[0]) { case 'header': $filterHeaders = array_merge($filterHeaders, $remove); break; case 'query': $filterQuery = array_merge($filterQuery, $remove); break; } } } // Use the filtered query string $queryString = (string) $request->getQuery()->filter(function($key, $value) use ($filterQuery) { return !in_array($key, $filterQuery); }); // Use the filtered headers $headerString = http_build_query($request->getHeaders()->filter(function($key, $value) use ($filterHeaders) { return !in_array($key, $filterHeaders); })->getAll()); if ($raw) { $key = 'gzrq_' . $key . $queryString . '&' . $headerString; } else { $key = 'gzrq_' . md5($key . $queryString . '&' . $headerString); $request->getParams()->set('cache.key', $key); } } return $key; } /** * {@inheritdoc} * * @param RequestInterface $subject Request to process */ public function update(Subject $subject, $event, $context = null) { // @codeCoverageIgnoreStart if (!($subject instanceof RequestInterface)) { return; } // @codeCoverageIgnoreEnd switch ($event) { case 'event.attach': // If the request is not cacheable, remove this observer if (!$subject->canCache()) { $subject->getEventManager()->detach($this); } break; case 'request.before_send': // This request is being prepared $key = spl_object_hash($subject); $hashKey = $this->getCacheKey($subject); $this->cached[$key] = $hashKey; $cachedData = $this->getCacheAdapter()->fetch($hashKey); // If the cached data was found, then make the request into a // manually set request if ($cachedData) { if ($this->serialize) { $cachedData = unserialize($cachedData); } unset($this->cached[$key]); $response = new Response($cachedData['c'], $cachedData['h'], $cachedData['b']); $response->setHeader('Age', time() - strtotime($response->getDate() ?: 'now')); $response->setHeader('X-Guzzle-Cache', $hashKey); // Validate that the response satisfies the request if ($this->canResponseSatisfyRequest($subject, $response)) { $subject->setResponse($response); } } break; case 'request.sent': $response = $subject->getResponse(); if ($response->canCache()) { // The request is complete and now processing the response $response = $subject->getResponse(); $key = spl_object_hash($subject); if (isset($this->cached[$key]) && $response->isSuccessful()) { if ($subject->getParams()->get('cache.override_ttl')) { $lifetime = $subject->getParams()->get('cache.override_ttl'); $response->setHeader('X-Guzzle-Ttl', $lifetime); } else { $lifetime = $response->getMaxAge(); } $this->saveCache($this->cached[$key], $response, $lifetime); } // Remove the hashed placeholder from the parameters object unset($this->cached[$key]); } break; } } /** * Revalidate a cached response * * @param RequestInterface $request Request to revalidate * @param Response $response Response to revalidate * * @return bool */ public function revalidate(RequestInterface $request, Response $response) { $revalidate = clone $request; $revalidate->getEventManager()->detach($this); $revalidate->setHeader('If-Modified-Since', $response->getDate()); if ($response->getEtag()) { $revalidate->setHeader('If-None-Match', '"' . $response->getEtag() . '"'); } try { $validateResponse = $revalidate->send(); if ($validateResponse->getStatusCode() == 200) { // The server does not support validation, so use this response $request->setResponse($validateResponse); // Store this response in cache if possible if ($validateResponse->canCache()) { $this->saveCache($this->getCacheKey($request), $validateResponse); } return false; } else if ($validateResponse->getStatusCode() == 304) { // Make sure that this response has the same ETage if ($validateResponse->getEtag() != $response->getEtag()) { return false; } else { // Replace cached headers with any of these headers from the // origin server that might be more up to date foreach (array('Date', 'Expires', 'Cache-Control', 'ETag', 'Last-Modified') as $name) { if ($validateResponse->hasHeader($name)) { $response->setHeader($name, $validateResponse->getHeader($name)); } } // Store the updated response in cache if ($response->canCache()) { $this->saveCache($this->getCacheKey($request), $response); } return true; } } } catch (\Exception $e) { // Don't fail on re-validation attempts } return false; } /** * Check if a cache response satisfies a request's caching constraints * * @param RequestInterface $request Request to validate * @param Response $response Response to validate * * @return bool */ public function canResponseSatisfyRequest(RequestInterface $request, Response $response) { $maxAge = null; $responseAge = $response->getAge(); // Check the request's max-age header against the age of the response if ($request->hasCacheControlDirective('max-age')) { if ($responseAge > $request->getCacheControlDirective('max-age')) { return false; } } // Check the response's max-age header if ($response->isFresh() === false) { $maxStale = $request->getCacheControlDirective('max-stale'); if (null !== $maxStale) { if ($maxStale !== true && $response->getFreshness() < (-1 * $maxStale)) { return false; } } else if ($responseAge > $response->getCacheControlDirective('max-age')) { return false; } } // Only revalidate GET requests if ($request->getMethod() == RequestInterface::GET) { // Check if the response must be validated against the origin server if ($request->getHeader('Pragma') == 'no-cache' || $request->hasCacheControlDirective('no-cache') || $request->hasCacheControlDirective('must-revalidate') || $response->hasCacheControlDirective('must-revalidate') || $response->hasCacheControlDirective('no-cache')) { // no-cache: When no parameters are present, always revalidate // When parameters are present in no-cache and the request includes // those same parameters, then the response must re-validate // I'll need an example of what fields look like in order to // implement a smarter version of no-cache // Requests can decline to revalidate against the origin server // by setting the cache.revalidate param to one of: // accept - To use what is in cache // decline - To always get a new copy if ($request->getParams()->get('cache.revalidate')) { return $request->getParams()->get('cache.revalidate') != 'decline'; } return $this->revalidate($request, $response); } } return true; } /** * Save data to the cache adapter * * @param string $key The cache key * @param Response $response The response to cache * @param int $lifetime (optional) Amount of seconds to cache * * @return int Returns the lifetime of the cached data */ protected function saveCache($key, Response $response, $lifetime = null) { $lifetime = $lifetime ?: $this->defaultLifetime; // If the data is cacheable, then save it to the cache adapter if ($lifetime) { // Remove excluded headers from the response (see RFC 2616:13.5.1) foreach ($this->excludeResponseHeaders as $header) { $response->removeHeader($header); } // Add a Date header to the response if none is set (for validation) if (!$response->getDate()) { $response->setHeader('Date', Guzzle::getHttpDate('now')); } $data = array( 'c' => $response->getStatusCode(), 'h' => $response->getHeaders(), 'b' => $response->getBody(true) ); if ($this->serialize) { $data = serialize($data); } $this->getCacheAdapter()->save($key, $data, $lifetime); } return $lifetime; } }src/Guzzle/Http/Plugin/CookiePlugin.php000064400000026403151742733540014111 0ustar00 */ class CookiePlugin implements Observer { /** * @var CookieJarInterface */ protected $jar; /** * Create a new CookiePlugin * * @param CookieJarInterface $storage Object used to persist cookies */ public function __construct(CookieJarInterface $storage) { $this->jar = $storage; } /** * Clears temporary cookies */ public function __destruct() { $this->clearTemporaryCookies(); } /** * Parse a cookie string as set in a Set-Cookie HTTP header and return data * about the cookie * * @param string $cookie Cookie header value to parse * @param RequestInterface $request (optional) Used to get path and domain * when not using Cookie-2 * @param bool $decode (optional) Set to FALSE to not urldecode the values * * @return array|bool Returns FALSE on failure or returns an array of * arrays, with each of the sub arrays including: * domain (string) - Domain of the cookie * path (string) - Path of the cookie * cookies (array) - Associative array of cookie names and values * max_age (int) - Lifetime of the cookie in seconds * version (int) - Version of the cookie specification. RFC 2965 is 1 * secure (bool) - Whether or not this is a secure cookie * discard (bool) - Whether or not this is a discardable cookie * custom (string) - Custom cookie data array * comment (string) - How the cookie is intended to be used * comment_url (str)- URL that contains info on how it will be used * port (array|str) - Array of ports or null * http_only (bool) - HTTP only cookie */ public static function parseCookie($cookie, RequestInterface $request = null, $decode = true) { if (!$cookie) { return false; } // Explode the cookie string using a series of semicolons $pieces = array_filter(array_map('trim', explode(';', $cookie))); // The name of the cookie (first kvp) must include an equal sign. if (!strpos($pieces[0], '=')) { return false; } // Map cookie values into snake_case values and use these regexp's to find data $parts = array( 'domain' => '/^Domain$/i', 'path' => '/^Path$/i', 'max_age' => '/^Max(\-|_)Age$/i', 'expires' => '/^Expires$/i', 'version' => '/^Version$/i', 'secure' => '/^Secure$/i', 'port' => '/^Port$/i', 'discard' => '/^Discard$/i', 'comment' => '/^Comment$/i', 'comment_url' => '/^Comment(\-|_)Url$/i', 'http_only' => '/^HttpOnly$/i' ); // Create the default return array $data = array_fill_keys(array_keys($parts), null); $data['cookies'] = array(); $data['data'] = array(); $data['path'] = '/'; $data['http_only'] = false; // Add the default domain and path using the request reference // These values are overridden if using Set-Cookie2 cookie values. if ($request) { $data['domain'] = $request->getHost(); $data['path'] = $request->getPath(); } $foundCookies = $foundNonCookies = 0; // Add the cookie pieces into the parsed data array foreach ($pieces as $part) { $avPair = array_map('trim', explode('=', $part, 2)); $key = $avPair[0]; if (count($avPair) === 1) { // Matches secure, httpOnly, etc... $value = true; } else { $value = isset($avPair[1]) ? $avPair[1] : ''; // Remove wrapping quotes if (strpos($value, '"') === 0) { $value = substr($value, 1); } if (substr($value, -1, 1) == '"') { $value = substr($value, 0, -1); } } // Check if the key is in the cookie pieces $found = false; $needle = strtolower($key); if ($foundCookies) { foreach ($parts as $mapValue => $regex) { if (preg_match($regex, $needle)) { $key = $needle = $mapValue; $found = true; break; } } } // Decode the value if $decode is TRUE $value = ($decode && $value && !is_bool($value)) ? urldecode($value) : $value; if (0 == $foundCookies || !$found) { // If cookies have not yet been retrieved, or this value was // not found in the cookie pieces array, treat as a cookie // IF non-cookies have been parsed, then this isn't a cookie, // but it's cookie data. Cookies must be first, followed by data. if ($foundNonCookies == 0) { $data['cookies'][] = $key . ($value ? ('=' . $value) : ''); $foundCookies++; } else { $data['data'][$key] = $value; } } else { if ($key == 'port') { $value = array_map('trim', explode(',', $value)); } $data[$key] = $value; $foundNonCookies++; } } // Calculate the expires date if (!$data['expires'] && $data['max_age']) { $data['expires'] = time() + (int)$data['max_age']; } return $data; } /** * Add cookies to a request based on the destination of the request and * the cookies stored in the storage backend. Any previously set cookies * will be removed. * * @param RequestInterface $request Request to add cookies to. If the * request object already has a cookie header, then no further cookies * will be added. * * @return array Returns an array of the cookies that were added */ public function addCookies(RequestInterface $request) { $request->removeHeader('Cookie'); // Find cookies that match this request $cookies = $this->jar->getCookies($request->getHost(), $request->getPath()); $match = false; if ($cookies) { foreach ($cookies as $cookie) { $match = true; // If a port restriction is set, validate the port if (!empty($cookie['port'])) { if (!in_array($request->getPort(), $cookie['port'])) { $match = false; } } // Validate the secure flag if ($cookie['secure']) { if ($request->getScheme() != 'https') { $match = false; } } // If this request is eligible for the cookie, then merge it in if ($match) { $request->addCookie($cookie['cookie'][0], isset($cookie['cookie'][1]) ? $cookie['cookie'][1] : null); } } } return $match && $cookies ? $cookies : array(); } /** * Extracts cookies from an HTTP Response object, looking for Set-Cookie: * and Set-Cookie2: headers and persists them to the cookie storage. * * @param Response $response */ public function extractCookies(Response $response) { $cookie = $response->getSetCookie(); $cookieData = array(); if ($cookie) { foreach ((array) $cookie as $c) { $cdata = self::parseCookie($c, $response->getRequest()); //@codeCoverageIgnoreStart if (!$cdata) { continue; } //@codeCoverageIgnoreEnd $cookies = array(); // Break up cookie v2 into multiple cookies if (count($cdata['cookies']) == 1) { $cdata['cookie'] = explode('=', $cdata['cookies'][0], 2); unset($cdata['cookies']); $cookies = array($cdata); } else { foreach ($cdata['cookies'] as $cookie) { $row = $cdata; unset($row['cookies']); $row['cookie'] = explode('=', $cookie, 2); $cookies[] = $row; } } if (count($cookies)) { foreach ($cookies as &$c) { $this->jar->save($c); $cookieData[] = $c; } } } } return $cookieData; } /** * Clear cookies currently held in the Cookie storage. * * Invoking this method without arguments will empty the whole Cookie * storage. If given a $domain argument only cookies belonging to that * domain will be removed. If given a $domain and $path argument, cookies * belonging to the specified path within that domain are removed. If given * all three arguments, then the cookie with the specified name, path and * domain is removed. * * @param string $domain (optional) Set to clear only cookies matching a domain * @param string $path (optional) Set to clear only cookies matching a domain and path * @param string $name (optional) Set to clear only cookies matching a domain, path, and name * * @return int Returns the number of deleted cookies */ public function clearCookies($domain = null, $path = null, $name = null) { return $this->jar->clear(str_replace(array('http://', 'https://'), '', $domain), $path, $name); } /** * Discard all temporary cookies. * * Scans for all cookies in the storage with either no expire field or a * true discard flag. To be called when the user agent shuts down according * to RFC 2965. * * @return int Returns the number of deleted cookies */ public function clearTemporaryCookies() { return $this->jar->clearTemporary(); } /** * {@inheritdoc} */ public function update(Subject $subject, $event, $context = null) { // @codeCoverageIgnoreStart if (!($subject instanceof RequestInterface)) { return; } // @codeCoverageIgnoreEnd if ($event == 'request.before_send') { // The request is being prepared $this->addCookies($subject); } else if ($event == 'request.sent') { // The response is being processed $this->extractCookies($subject->getResponse()); } else if ($event == 'request.receive.status_line') { if ($context['previous_response']) { $this->extractCookies($context['previous_response']); } } } }src/Guzzle/Http/Plugin/ExponentialBackoffPlugin.php000064400000013127151742733540016441 0ustar00 */ class ExponentialBackoffPlugin implements Observer { const DELAY_PARAM = 'plugins.exponential_backoff.retry_time'; /** * @var array Array of response codes that must be retried */ protected $failureCodes; /** * @var int Maximum number of times to retry a request */ protected $maxRetries; /** * @var array Request state information */ protected $state = array(); /** * @var Closure */ protected $delayClosure; /** * Construct a new exponential backoff plugin * * @param int $maxRetries (optional) The maximum number of time to retry a request * @param array $failureCodes (optional) Pass a custom list of failure codes. * @param Closure|array $delayClosure (optional) Method used to calculate the * delay between requests. The method must accept an integer containing * the current number of retries and return an integer representing how * many seconds to delay */ public function __construct($maxRetries = 3, array $failureCodes = null, $delayClosure = null) { $this->setMaxRetries($maxRetries); $this->failureCodes = $failureCodes ?: array(500, 503); $this->delayClosure = $delayClosure ?: array($this, 'calculateWait'); } /** * Set the maximum number of retries the plugin should use before failing * the request * * @param integer $maxRetries The maximum number of retries. * * @return ExponentialBackoffPlugin */ public function setMaxRetries($maxRetries) { $this->maxRetries = max(0, (int) $maxRetries); return $this; } /** * Get the maximum number of retries the plugin will attempt * * @return integer */ public function getMaxRetries() { return $this->maxRetries; } /** * Get the HTTP response codes that should be retried using truncated * exponential backoff * * @return array */ public function getFailureCodes() { return $this->failureCodes; } /** * Set the HTTP response codes that should be retried using truncated * exponential backoff * * @param array $codes Array of HTTP response codes * * @return ExponentialBackoffPlugin */ public function setFailureCodes(array $codes) { $this->failureCodes = $codes; return $this; } /** * Determine how long to wait using truncated exponential backoff * * @param int $retries Number of retries so far * * @return int */ public function calculateWait($retries) { return (int) pow(2, $retries); } /** * {@inheritdoc} */ public function update(Subject $subject, $event, $context = null) { // @codeCoverageIgnoreStart if (!($subject instanceof RequestInterface)) { return; } // @codeCoverageIgnoreEnd switch ($event) { case PoolInterface::POLLING_REQUEST: // The most frequent event, thus at the top of the switch $delay = $subject->getParams()->get(self::DELAY_PARAM); if ($delay) { // If the duration of the delay has passed, retry the request using the pool if (time() >= $delay) { // Remove the request from the pool and then add it back again $context->remove($subject); $context->add($subject); $subject->getParams()->remove(self::DELAY_PARAM); return true; } } break; case 'event.attach': // Called when the observer is initially attached to the request $this->state[spl_object_hash($subject)] = 0; break; case 'request.sent': // Called when the request has been sent and isn't finished processing $key = spl_object_hash($subject); if (in_array($subject->getResponse()->getStatusCode(), $this->failureCodes)) { // If this request has been retried too many times, then throw an exception if (++$this->state[$key] <= $this->maxRetries) { // Calculate how long to wait until the request should be retried $delay = (int) call_user_func($this->delayClosure, $this->state[$key]); // Send the request again $subject->setState(RequestInterface::STATE_NEW); if ($subject->getParams()->get('pool')) { // Pooled requests need to be sent via curl // multi, and the retry will happen after a // period of polling to prevent pool exclusivity $subject->getParams()->set(self::DELAY_PARAM, time() + $delay); } else { // Wait for a delay then retry the request sleep($delay); $subject->send(); } } } break; } } }src/Guzzle/Http/Plugin/HistoryPlugin.php000064400000005032151742733540014334 0ustar00 */ class HistoryPlugin implements Observer, \IteratorAggregate, \Countable { /** * @var int The maximum number of requests to maintain in the history */ protected $limit = 10; /** * @var array Requests that have passd through the plugin */ protected $requests = array(); /** * Add a request to the history * * @param RequestInterface $request Request to add * * @return HistoryPlugin */ public function add(RequestInterface $request) { if ($request->getResponse()) { $this->requests[] = $request; if (count($this->requests) > $this->getlimit()) { array_shift($this->requests); } } return $this; } /** * Set the max number of requests to store * * @param int $limit Limit * * @return HistoryPlugin */ public function setLimit($limit) { $this->limit = (int) $limit; return $this; } /** * Get the request limit * * @return int */ public function getLimit() { return $this->limit; } /** * Get the requests in the history * * @return ArrayIterator */ public function getIterator() { return new \ArrayIterator($this->requests); } /** * Get the number of requests in the history * * @return int */ public function count() { return count($this->requests); } /** * Get the last request sent * * @return RequestInterface */ public function getLastRequest() { return end($this->requests); } /** * Get the last response in the history * * @return Response */ public function getLastResponse() { return $this->getLastRequest()->getResponse(); } /** * Clears the history * * @return HistoryPlugin */ public function clear() { $this->requests = array(); return $this; } /** * {@inheritdoc} */ public function update(Subject $subject, $event, $context = null) { if ($event == 'request.complete') { $this->add($subject); } } }src/Guzzle/Http/Plugin/LogPlugin.php000064400000020301151742733540013410 0ustar00 */ class LogPlugin implements Observer { // Bitwise log settings const LOG_CONTEXT = 1; const LOG_HEADERS = 2; const LOG_BODY = 4; const LOG_DEBUG = 8; // Log context, headers, debug, and entity bodies const LOG_VERBOSE = 15; /** * @var LogAdapterInterface Adapter responsible for writing log data */ private $logAdapter; /** * @var int Bitwise log settings */ private $settings = false; /** * @var string Cached copy of the hostname */ private $hostname; /** * Construct a new LogPlugin * * @param LogAdapterInterface $logAdapter Adapter object used to log message * @param int $settings (optional) Bitwise settings to use for logging */ public function __construct(LogAdapterInterface $logAdapter, $settings = self::LOG_CONTEXT) { $this->logAdapter = $logAdapter; $this->hostname = gethostname(); $this->setSettings($settings); } /** * Change the log settings of the plugin * * @param int $settings (optional) Bitwise settings to control what's logged * * @return LogPlugin */ public function setSettings($settings) { $this->settings = $settings; return $this; } /** * Get the log adapter object * * @return LogAdapterInterface */ public function getLogAdapter() { return $this->logAdapter; } /** * {@inheritdoc} */ public function update(Subject $subject, $event, $context = null) { // @codeCoverageIgnoreStart if (!($subject instanceof RequestInterface)) { return; } // @codeCoverageIgnoreEnd /* @var $subject EntityEnclosingRequest */ switch ($event) { case 'curl.callback.write': // Stream the response body to the log if the body is not repeatable if ($subject->getParams()->get('response_wire')) { $subject->getParams()->get('response_wire')->write($context); } break; case 'curl.callback.read': // Stream the request body to the log if the body is not repeatable if ($subject->getParams()->get('request_wire')) { $subject->getParams()->get('request_wire')->write($context); } break; case 'request.before_send': // We need to make special handling for content wiring and // non-repeatable streams. if ($this->settings & self::LOG_BODY) { if ($subject instanceof EntityEnclosingRequestInterface) { if ($subject->getBody() && (!$subject->getBody()->isSeekable() || !$subject->getBody()->isReadable())) { // The body of the request cannot be recalled so // logging the content of the request will need to // be streamed using updates $subject->getParams()->set('request_wire', EntityBody::factory('')); } } if (!$subject->isResponseBodyRepeatable()) { // The body of the response cannot be recalled so // logging the content of the response will need to // be streamed using updates $subject->getParams()->set('response_wire', EntityBody::factory('')); } } break; case 'request.curl.release': // Triggers the actual log write $this->log($subject, $subject->getResponse()); break; } } /** * Log a message based on a request and response * * @param RequestInterface $request Request to log * @param Response $response (optional) Response to log */ private function log(RequestInterface $request, Response $response = null) { $priority = $response && !$response->isSuccessful() ? LOG_ERR : LOG_DEBUG; $message = ''; if ($this->settings & self::LOG_CONTEXT) { // Log common contextual information $message = $request->getHost() . ' - "' . $request->getMethod() . ' ' . $request->getResourceUri() . ' ' . strtoupper($request->getScheme()) . '/' . $request->getProtocolVersion() . '"'; // If a response is set, then log additional contextual information if ($response) { $message .= sprintf(' - %s %s - %s %s %s', $response->getStatusCode(), $response->getContentLength() ?: 0, $response->getInfo('total_time'), $response->getInfo('speed_upload'), $response->getInfo('speed_download') ); } } // Check if we are logging anything that will come from cURL if ($request->getCurlHandle() && ($this->settings & self::LOG_DEBUG || $this->settings & self::LOG_HEADERS || $this->settings & self::LOG_BODY)) { // If context logging too, then add a new line for cleaner messages if ($this->settings & self::LOG_CONTEXT) { $message .= "\n"; } // Filter cURL's verbose output based on config settings $stderr = $request->getCurlHandle()->getStderr(true); rewind($stderr); $addedBody = false; while ($line = fgets($stderr)) { $first = $line[0]; // * - Debug | < - Downstream | > - Upstream if ($line[0] == '*' && $this->settings & self::LOG_DEBUG) { $message .= $line; } else if ($this->settings & self::LOG_HEADERS) { $message .= $line; } // Add the request body if needed if ($this->settings & self::LOG_BODY) { if (trim($line) == '' && !$addedBody && $request instanceof EntityEnclosingRequestInterface) { $message .= $request->getParams()->get('request_wire') ? (string) $request->getParams()->get('request_wire') . "\r\n" : (string) $request->getBody() . "\r\n"; $addedBody = true; } } } // Log the response body if the response is available if ($this->settings & self::LOG_BODY && $response) { $message .= $request->getParams()->get('response_wire') ? (string) $request->getParams()->get('response_wire') : $response->getBody(true); } } // Send the log message to the adapter, adding a category and host $this->logAdapter->log(trim($message), $priority, array( 'category' => 'guzzle.request', 'host' => $this->hostname )); } }src/Guzzle/Http/Plugin/Md5ValidatorPlugin.php000064400000006737151742733540015203 0ustar00 */ class Md5ValidatorPlugin implements Observer { /** * @var int Maximum Content-Length in bytes to validate */ protected $contentLengthCutoff; /** * @var bool Whether or not to compare when a Content-Encoding is present */ protected $contentEncoded; /** * Constructor * * @param bool $calcContentEncoded (optional) Calculating the MD5 hash of an * entity body where a Content-Encoding was applied is a more expensive * comparison because the entity body will need to be compressed in * order to get the correct hash. Set to FALSE to not validate * the MD5 hash of an entity body with an applied Content-Encoding. * @param int $contentLengthCutoff (optional) Maximum Content-Length (bytes) * in which a MD5 hash will be validated. Any response with a * Content-Length greater than this value will not be validated * because it will be deemed too memory intensive */ public function __construct($contentEncoded = true, $contentLengthCutoff = 2097152) { $this->contentLengthCutoff = $contentLengthCutoff; $this->contentEncoded = $contentEncoded; } /** * {@inheritdoc} * @throws UnexpectedValueException */ public function update(Subject $subject, $event, $context = null) { /* @var $subject EntityEnclosingRequest */ if ($event != 'request.complete' || !($subject instanceof RequestInterface)) { return; } $contentMd5 = $context->getContentMd5(); if (!$contentMd5) { return; } $contentEncoding = $context->getContentEncoding(); if ($contentEncoding && !$this->contentEncoded) { return false; } // Make sure that the request's size is under the cutoff size $size = $context->getContentLength() ?: $context->getBody()->getSize(); if (!$size || $size > $this->contentLengthCutoff) { return; } switch ($contentEncoding) { case 'gzip': $context->getBody()->compress('zlib.deflate'); $hash = $context->getBody()->getContentMd5(); $context->getBody()->uncompress(); break; case 'compress': $context->getBody()->compress('bzip2.compress'); $hash = $context->getBody()->getContentMd5(); $context->getBody()->uncompress(); break; default: if ($contentEncoding) { return; } $hash = $context->getBody()->getContentMd5(); break; } if ($contentMd5 !== $hash) { throw new \UnexpectedValueException(sprintf( 'The response entity body may have been ' . 'modified over the wire. The Content-MD5 ' . 'received (%s) did not match the calculated ' . 'MD5 hash (%s).', $contentMd5, $hash )); } } }src/Guzzle/Http/Pool/Pool.php000064400000020332151742733540012100 0ustar00multiHandle = curl_multi_init(); } /** * Destroy the request pool. */ function __destruct() { if (is_resource($this->multiHandle)) { curl_multi_close($this->multiHandle); } } /** * Add a request to the pool. * * @param RequestInterface $request Returns the Request that was added */ public function add(RequestInterface $request) { if ($this->state != self::STATE_COMPLETE) { $this->requests[] = $request; } if ($this->state == self::STATE_SENDING) { // Attach a request while the pool is being sent. This is currently // used to implement exponential backoff curl_multi_add_handle($this->multiHandle, $request->getCurlHandle()->getHandle()); } $this->getEventManager()->notify(self::ADD_REQUEST, $request); // Associate the pool with the request $request->getParams()->set('pool', $this); return $request; } /** * Get an array of attached {@see RequestInterface}s. * * @return array Returns an array of attached requests. */ public function all() { return $this->requests; } /** * Get the current state of the Pool * * @return string */ public function getState() { return $this->state; } /** * Remove a request from the pool. * * @param RequestInterface $request Request to detach. * * @return RequestInterface Returns the Request object that was removed */ public function remove(RequestInterface $request) { if ($this->state == self::STATE_SENDING && $this->multiHandle) { curl_multi_remove_handle($this->multiHandle, $request->getCurlHandle()->getHandle()); } $this->requests = array_values(array_filter($this->requests, function($req) use ($request) { return $req !== $request; })); $this->getEventManager()->notify(self::REMOVE_REQUEST, $request); // Remove the pool's request association $request->getParams()->remove('pool'); return $request; } /** * Reset the state of the Pool and remove any attached RequestInterface objects */ public function reset() { // Remove each request foreach ($this->requests as $request) { $this->remove($request); } $this->state = self::STATE_IDLE; // Close and reopen the curl_multi resource if (is_resource($this->multiHandle)) { curl_multi_close($this->multiHandle); } $this->multiHandle = curl_multi_init(); // Notify any observers of the reset event $this->getEventManager()->notify(self::RESET); } /** * Send a pool of {@see RequestInterface} requests. * * Calling this method more than once will return FALSE. * * @return array|bool Returns an array of attached Request objects on * success FALSE on failure. * * @throws PoolRequestException if any requests threw exceptions during the * transfer. */ public function send() { if ($this->state == self::STATE_COMPLETE) { return false; } $this->getEventManager()->notify(self::BEFORE_SEND, $this->requests); $this->state = self::STATE_SENDING; foreach ($this->requests as $request) { $request->getEventManager()->notify('request.before_send'); // Do not send if a manual response is being used. if (!$request->getResponse() && !$request->getParams()->get('queued_response')) { curl_multi_add_handle($this->multiHandle, $request->getCurlHandle()->getHandle()); } else if ($request->getParams()->get('queued_response')) { $request->setState(RequestInterface::STATE_COMPLETE); } } $isRunning = false; $exceptions = array(); do { // Exec until there's no more data in this iteration. while (($execrun = curl_multi_exec($this->multiHandle, $isRunning)) == CURLM_CALL_MULTI_PERFORM); // @codeCoverageIgnoreStart if ($execrun != CURLM_OK) { break; // If an error occurred. } // @codeCoverageIgnoreEnd // Get information about the handle while ($done = curl_multi_info_read($this->multiHandle)) { foreach ($this->requests as $request) { if ($request->getCurlHandle()->isMyHandle($done['handle'])) { try { $request->setState(RequestInterface::STATE_COMPLETE); } catch (RequestException $e) { $this->getEventManager()->notify('exception', $e); $exceptions[] = $e; } curl_multi_remove_handle($this->multiHandle, $done['handle']); } } } $finished = true; foreach ($this->requests as $request) { if ($request->getState() != RequestInterface::STATE_COMPLETE) { // Notify each request's event manager that it is polling $request->getEventManager()->notify(self::POLLING_REQUEST, $this); $finished = false; } } // Notify any listeners that the pool is polling. Good place for // status updates $this->getEventManager()->notify(self::POLLING); // @codeCoverageIgnoreStart if ($isRunning) { while (($selectResult = curl_multi_select($this->multiHandle, 5)) === 0); if ($selectResult == -1) { break; } } else if (!$finished) { // Requests are not actually pending a cURL select call, so // we need to delay in order to prevent eating too much CPU usleep(30000); } // @codeCoverageIgnoreEnd } while ($isRunning || !$finished); $this->state = self::STATE_COMPLETE; $this->getEventManager()->notify(self::COMPLETE, $this->requests); // Throw any Request exceptions encountered during the transfer if (count($exceptions)) { $poolException = new PoolRequestException( 'RequestExceptions thrown during transfer' ); foreach ($exceptions as $e) { $poolException->addException($e); } throw $poolException; } return $this->requests; } /** * Get the number of requests in the pool * * @return int */ public function count() { return count($this->requests); } }src/Guzzle/Http/Pool/PoolInterface.php000064400000003576151742733540013734 0ustar00exceptions[] = $request; } /** * Get the exceptions thrown during the Pool transfer * * @return array Returns an array of RequestException objects */ public function getRequestExceptions() { return $this->exceptions; } /** * 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); } }src/Guzzle/Http/QueryString.php000064400000022511151742733540012553 0ustar00data)) { return ''; } $queryString = $this->prefix; $firstValue = true; foreach ($this->encodeData($this->data, $this->encodeFields, $this->encodeValues) as $name => $value) { $value = $value !== null ? (array) $value : array(false); foreach ($value as $v) { if (!$firstValue) { $queryString .= $this->fieldSeparator; } $queryString .= $name; if (is_string($v)) { $queryString .= $this->valueSeparator . $v; } $firstValue = false; } } return $queryString; } /** * Aggregate multi-valued parameters by joining the values using a comma * * * $queryString = new \Guzzle\Http\QueryString(); * $queryString->setAggregator($queryString->aggregateUsingComma); * $queryString->replace(array( * 'value' => array(1, 2, 3) * )); * * echo $queryString; // outputs: ?value=1,2,3 * * * @param string $key The name of the query string parameter * @param array $values The values of the parameter * @param bool $encodeFields (optional) Set to TRUE to encode field names * @param bool $encodeValues (optional) Set to TRUE to encode values * * @return array Returns an array of the combined values */ public function aggregateUsingComma($key, $value, $encodeFields = false, $encodeValues = false) { return array( (($encodeFields) ? rawurlencode($key) : $key) => (($encodeValues) ? implode(',', array_map(array(__CLASS__, 'rawurlencode'), $value)) : implode(',', $value)) ); } /** * Get the query string field separator * * @return string */ public function getFieldSeparator() { return $this->fieldSeparator; } /** * Get the query string prefix * * @return string */ public function getPrefix() { return $this->prefix; } /** * Get the query string value separator * * @return string */ public function getValueSeparator() { return $this->valueSeparator; } /** * Returns whether or not field names are being urlencoded when converting * the query string object to a string * * @return bool */ public function isEncodingFields() { return $this->encodeFields; } /** * Returns whether or not values are being urlencoded when converting * the query string object to a string * * @return bool */ public function isEncodingValues() { return $this->encodeValues; } /** * Provide a function for combining multi-valued query string parameters * into a single or multiple fields * * @param callback|null $callback A function or callback array that accepts * a $key, $value, $encodeFields, and $encodeValues as arguments and * returns an associative array containing the combined values. Set * to null to remove any custom aggregator. * * @return \Guzzle\Http\QueryString Provides a fluent interface * * @see \Guzzle\Http\QueryString::aggregateUsingComma() */ public function setAggregateFunction($callback) { $this->aggregator = $callback; } /** * Set whether or not field names should be urlencoded when converting * the query string object to a string * * @param bool $encode Set to TRUE to encode field names, FALSE to not * encode field names * * @return \Guzzle\Http\QueryString Provides a fluent interface */ public function setEncodeFields($encode) { $this->encodeFields = $encode; return $this; } /** * Set whether or not field values should be urlencoded when converting * the query string object to a string * * @param bool $encode Set to TRUE to encode field values, FALSE to not * encode field values * * @return \Guzzle\Http\QueryString Provides a fluent interface */ public function setEncodeValues($encode) { $this->encodeValues = $encode; return $this; } /** * Set the query string separator * * @param string $separator The query string separator that will separate * fields * * @return \Guzzle\Http\QueryString Provides a fluent interface */ public function setFieldSeparator($separator) { $this->fieldSeparator = $separator; return $this; } /** * Set the query string prefix * * @param string $prefix Prefix to use with the query string (e.g. '?') * * @return \Guzzle\Http\QueryString Provides a fluent interface */ public function setPrefix($prefix) { $this->prefix = $prefix; return $this; } /** * Set the query string value separator * * @param string $separator The query string separator that will separate * values from fields * * @return \Guzzle\Http\QueryString Provides a fluent interface */ 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->encodeData($this->data, $this->encodeFields, $this->encodeValues); } /** * Url encode parameter data. * * If a parameter value is an array and no aggregator has been set, the * values of the array will be converted into a PHP compatible form array. * If an aggregator is set, the values will be converted using the * aggregator function * * @param array $data The data to encode * @param bool $encodeFields (optional) Whether or not fields should be * rawurlencoded * @param bool $encodeValues (optional) Whether or not values should be * rawurlencoded * * @return array Returns an array of encoded values and keys */ protected function encodeData(array $data, $encodeFields = true, $encodeValues = true) { $temp = array(); foreach($data as $key => &$value) { if (is_array($value)) { $encoded = $this->encodeData($value, $encodeFields, $encodeValues); if ($this->aggregator !== null) { $temp = array_merge($temp, call_user_func_array($this->aggregator, array($key, $value, $encodeFields, $encodeValues))); } else { foreach ($encoded as $i => $v) { $i = (!is_numeric($i)) ? 0 : $i; if ($encodeFields) { $k = self::rawurlencode($key) . "[{$i}]"; } else { $k = "{$key}[{$i}]"; } $temp[$k] = $v; } } } else { if ($encodeValues && is_string($value) || is_numeric($value)) { $value = self::rawurlencode($value); } if ($encodeFields) { $temp[self::rawurlencode($key)] = $value; } else { $temp[$key] = $value; } } } return $temp; } }src/Guzzle/Http/Url.php000064400000027263151742733540011032 0ustar00 */ 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 */ public static function factory($url) { // Performance improvement to Parse the URL into parts $parts = (array) parse_url($url); if (!isset($parts['scheme'])) $parts['scheme'] = null; if (!isset($parts['host'])) $parts['host'] = null; if (!isset($parts['path'])) $parts['path'] = null; if (!isset($parts['port'])) $parts['port'] = null; if (!isset($parts['query'])) $parts['query'] = null; if (!isset($parts['user'])) $parts['user'] = null; if (!isset($parts['pass'])) $parts['pass'] = null; if (!isset($parts['fragment'])) $parts['fragment'] = null; if ($parts['query']) { $query = array(); parse_str($parts['query'], $query); $parts['query'] = $query ? new QueryString($query) : null; } return new self($parts['scheme'], $parts['host'], $parts['user'], $parts['pass'], $parts['port'], $parts['path'], $parts['query'], $parts['fragment']); } /** * Buld 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'])) { 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']; } } if (empty($parts['path'])) { $url .= '/'; } else { if ($parts['path'][0] != '/') { $url .= '/'; } $url .= $parts['path']; } // Add the query string if present if (!empty($parts['query'])) { if ($parts['query'][0] != '?') { $url .= array_key_exists('query_prefix', $parts) ? $parts['query_prefix'] : '?'; } $url .= $parts['query']; } 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 (optional) Username of the URL * @param string $password (optional) Password of the URL * @param int $port (optional) Port of the URL * @param string $path (optional) Path of the URL * @param QueryString|array|string $query (optional) Query string of the URL * @param string $fragment (optional) Fragment of the URL * * @throws HttpException */ 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; $this->setQuery($query ?: new QueryString()); if ($path) { $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, 'fragment' => $this->fragment, 'query_prefix' => $this->query->getPrefix() ); } /** * 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) { $this->host = $host; 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 a port has not explicitly been set. * * @return int|null */ public function getPort() { if ($this->port) { return $this->port; } else if ($this->scheme == 'http') { return 80; } else if ($this->scheme == 'https') { return 443; } else { 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 = $path; if ($this->path != '*' && substr($this->path, 0, 1) != '/') { $this->path = '/' . $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->path), 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); } else if (is_array($query)) { $this->query = new QueryString($query); } else if ($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. Every part of the second URL supercede * the current URL if that part is specified. * * @param string $url Relative URL to combine with * * @return Url * @throws InvalidArgumentException */ public function combine($url) { $absolutePath = $url[0] == '/'; $url = self::factory($url); if ($url->getScheme()) { $this->scheme = $url->getScheme(); } if ($url->getHost()) { $this->host = $url->getHost(); } if ($url->getPort()) { $this->port = $url->getPort(); } if ($url->getUsername()) { $this->username = $url->getUsername(); } if ($url->getPassword()) { $this->password = $url->getPassword(); } if ($url->getFragment()) { $this->fragment = $url->getFragment(); } if ($absolutePath) { // Replace the current URL and query if set if ($url->getPath()) { $this->path = $url->getPath(); } if (count($url->getQuery())) { $this->query = clone $url->getQuery(); } } else { // Append to the current path and query string if ($url->getPath()) { $this->addPath($url->getPath()); } if ($url->getQuery()) { $this->query->merge($url->getQuery()); } } return $this; } }src/Guzzle/Service/Client.php000064400000041540151742733540012161 0ustar00setConfig($config); } $this->setBaseUrl($baseUrl); } /** * Set the configuration object to use with the client * * @param array|Collection|string $config Parameters that define how the * client behaves and connects to a webservice. Pass an array or a * Collection object. * * @return Client */ public final function setConfig($config) { // Set the configuration object if ($config instanceof Collection) { $this->config = $config; } else if (is_array($config)) { $this->config = new Collection($config); } else { throw new \InvalidArgumentException( 'Config must be an array or Collection' ); } $this->setBaseUrl($this->baseUrl); return $this; } /** * Get a configuration setting or all of the configuration settings * * @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 */ public final function getConfig($key = false) { if (!$this->config) { $this->config = new Collection(); } if ($key) { return $this->config->get($key); } else { return $this->config; } } /** * Inject configuration values into a formatted string with {{param}} as a * parameter delimiter (replace param with the configuration value name) * * @param string $string String to inject config values into * * @return string */ public final function inject($string) { return Guzzle::inject($string, $this->getConfig()); } /** * Create and return a new {@see RequestInterface} configured for the client * * @param string $method (optional) HTTP method. Defaults to GET * @param string $uri (optional) Resource URI. 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 * querystring as well. * @param array|Collection $headers (optional) HTTP headers * @param string|resource|array|EntityBody $body (optional) Entity body of * request (POST/PUT) or response (GET) * * @return RequestInterface */ public function createRequest($method = RequestInterface::GET, $uri = null, $headers = null, $body = null) { if (!$uri) { $url = $this->getBaseUrl(); } else if (strpos($uri, 'http') === 0) { // Use absolute URLs as-is $url = $this->inject($uri); } else { $url = (string) Url::factory($this->getBaseUrl())->combine($this->inject($uri)); } return $this->prepareRequest( RequestFactory::create($method, $url, $headers, $body) ); } /** * Prepare a request to be sent from the Client by adding client specific * behaviors and properties to the request. * * This method should only be called when using the default RequestFactory * is not an option and the request sent from the client must be created * manually. * * @param RequestInterface $request Request to prepare for the client * * @return RequestInterface */ public function prepareRequest(RequestInterface $request) { // Add your application name to the request if ($this->userApplication) { $request->setHeader('User-Agent', trim($this->userApplication . ' ' . $request->getHeader('User-Agent'))); } // Add any curl options that might in the config to the request foreach ($this->getConfig()->getAll('/^curl\..+/', Collection::MATCH_REGEX) as $key => $value) { $curlOption = str_replace('curl.', '', $key); if (defined($curlOption)) { $curlValue = defined($value) ? constant($value) : $value; $request->getCurlOptions()->set(constant($curlOption), $curlValue); } } // Add the cache key filter to requests if one is set on the client if ($this->getConfig('cache.key_filter')) { $request->getParams()->set('cache.key_filter', $this->getConfig('cache.key_filter')); } // Attach client observers to the request $reqManager = $request->getEventManager(); $manager = $this->getEventManager(); foreach ($manager->getAttached() as $observer) { $reqManager->attach($observer, $manager->getPriority($observer)); } $manager->notify('request.create', $request); return $request; } /** * Get a command by name. First, the client will see if it has a service * description and if the service description defines a command by the * supplied name. If no dynamic command is found, the client will look for * a concrete command class exists matching the name supplied. If neither * are found, an InvalidArgumentException is thrown. * * @param string $name Name of the command to retrieve * @param array $args (optional) Arguments to pass to the command * * @return CommandInterface * @throws InvalidArgumentException if no command can be found by name */ public function getCommand($name, array $args = array()) { $command = null; // If a service description is present, see if a command is defined if ($this->serviceDescription && $this->serviceDescription->hasCommand($name)) { $command = $this->serviceDescription->createCommand($name, $args); } // Check if a concrete command exists using inflection if (!$command) { // Determine the class to instantiate based on the namespace of the // current client and the default location of commands $prefix = $this->getConfig('command.prefix'); if (!$prefix) { // The prefix can be specified in a factory method and is cached $prefix = implode('\\', array_slice(explode('\\', get_class($this)), 0, -1)) . '\\Command\\'; $this->getConfig()->set('command.prefix', $prefix); } $class = $prefix . str_replace(' ', '\\', ucwords(str_replace('.', ' ', Inflector::camel($name)))); // Create the concrete command if it exists if (class_exists($class)) { $command = new $class($args); } } if (!$command) { throw new \InvalidArgumentException("$name command could not be found"); } $command->setClient($this); $this->getEventManager()->notify('command.create', $command); return $command; } /** * Execute a command and return the response * * @param CommandInterface|CommandSet|array $command Command or set to execute * * @return mixed Returns the result of the executed command's * {@see CommandInterface::getResult} method if a CommandInterface is * passed, or the CommandSet itself if a CommandSet is passed * @throws InvalidArgumentException if an invalid command is passed * @throws Command\CommandSetException if a set contains commands associated * with other clients */ public function execute($command) { if ($command instanceof CommandInterface) { $command->setClient($this)->prepare(); $this->getEventManager()->notify('command.before_send', $command); $command->getRequest()->send(); $this->getEventManager()->notify('command.after_send', $command); return $command->getResult(); } else if ($command instanceof CommandSet) { foreach ($command as $c) { if ($c->getClient() && $c->getClient() !== $this) { throw new Command\CommandSetException( 'Attempting to run a mixed-Client CommandSet from a ' . 'Client context. Run the set using CommandSet::execute() ' ); } $c->setClient($this); } return $command->execute(); } else if (is_array($command)) { return $this->execute(new CommandSet($command)); } throw new \InvalidArgumentException('Invalid command sent to ' . __METHOD__); } /** * Get the base service endpoint URL with configuration options injected * into the configuration setting. * * @param bool $inject (optional) Set to FALSE to get the raw base URL * * @return string * @throws RuntimeException if a base URL has not been set */ public function getBaseUrl($inject = true) { if (!$this->baseUrl) { throw new \RuntimeException('A base URL has not been set'); } return $inject ? $this->inject((string) $this->baseUrl) : $this->baseUrl; } /** * Set the base service endpoint URL * * @param string $url The base service endpoint URL of the webservice * * @return Client */ public function setBaseUrl($url) { $this->baseUrl = $url; return $this; } /** * Set the service description of the client * * @param ServiceDescription $description Service description that describes * all of the commands and information of the client * * @return Client */ public function setDescription(ServiceDescription $service) { $this->serviceDescription = $service; return $this; } /** * Get the service description of the client * * @return ServiceDescription|NullObject */ public function getDescription() { return $this->serviceDescription ?: new NullObject(); } /** * Set the name of your application and application version that will be * appended to the User-Agent header of all reqeusts. * * @param string $appName Name of your application * @param string $version Version number of your application * * @return Client */ public function setUserApplication($appName, $version) { $this->userApplication = $appName . '/' . ($version ?: '1.0'); return $this; } /** * Create a GET request for the client * * @param string $path (optional) Resource URI of the request. Use an * absolute path to override the base path, or a relative path to append * @param array|Collection $headers (optional) HTTP headers * @param string|resource|array|EntityBody $body (optional) Where to store * the response entity body * * @return Request */ public final function get($path = null, $headers = null, $body = null) { return $this->createRequest('GET', $path, $headers, $body); } /** * Create a HEAD request for the client * * @param string $uri (optional) Resource URI of the request. Use an * absolute path to override the base path, or a relative path to append * @param array|Collection $headers (optional) HTTP headers * * @return Request */ public final function head($uri = null, $headers = null) { return $this->createRequest('HEAD', $uri, $headers); } /** * Create a DELETE request for the client * * @param string $uri (optional) Resource URI of the request. Use an * absolute path to override the base path, or a relative path to append * @param array|Collection $headers (optional) HTTP headers * * @return Request */ public final function delete($uri = null, $headers = null) { return $this->createRequest('DELETE', $uri, $headers); } /** * Create a PUT request for the client * * @param string $uri (optional) Resource URI of the request. Use an * absolute path to override the base path, or a relative path to append * @param array|Collection $headers (optional) HTTP headers * @param string|resource|array|EntityBody $body Body to send in the request * * @return EntityEnclosingRequest */ public final function put($uri = null, $headers = null, $body = null) { return $this->createRequest('PUT', $uri, $headers, $body); } /** * Create a POST request for the client * * @param string $uri (optional) Resource URI of the request. Use an absolute path to * override the base path, or a relative path to append it. * @param array|Collection $headers (optional) HTTP headers * @param array|Collection $postFields (optional) Associative array of POST * fields to send in the body of the request. Prefix a value in the * array with the @ symbol reference a file. * * @return EntityEnclosingRequest */ public final function post($uri = null, $headers = null, $postFields = null) { return $this->createRequest('POST', $uri, $headers, $postFields); } /** * Create an OPTIONS request for the client * * @param string $uri (optional) Resource URI of the request. Use an * absolute path to override the base path, or relative path to append * * @return Request */ public final function options($uri = null) { return $this->createRequest('OPTIONS', $uri); } /** * Sends multiple requests in parallel * * @param array $requests Requests to send in parallel * * @return array Returns the responses */ public function batch(array $requests) { $pool = $this->getPool(); $pool->reset(); foreach ($requests as $request) { $pool->add($request); } return array_map(function($request) { return $request->getResponse(); }, $pool->send()); } /** * Set a Pool object to be used internally by the client for batch requests * * @param PoolInterface $pool Pool object to use for batch requests * * @return Client */ public function setPool(PoolInterface $pool) { $this->pool = $pool; return $this; } /** * Get the Pool object used with the client * * @return PoolInterface */ public function getPool() { if (!$this->pool) { $this->pool = new Pool(); } return $this->pool; } }src/Guzzle/Service/ClientInterface.php000064400000021553151742733540014004 0ustar00 */ abstract class AbstractCommand extends Collection implements CommandInterface { /** * @var ClientInterface Client object used to execute the command */ protected $client; /** * @var RequestInterface The request object associated with the command */ protected $request; /** * @var mixed The result of the command */ protected $result; /** * @var bool Whether or not the command can be batched */ protected $canBatch = true; /** * @var ApiCommand API information about the command */ protected $apiCommand; /** * Constructor * * @param array|Collection $parameters (optional) Collection of parameters * to set on the command * @param ApiCommand $apiCommand (optional) Command definition from description */ public function __construct($parameters = null, ApiCommand $apiCommand = null) { parent::__construct($parameters); // Add arguments and validate the command if ($apiCommand) { $this->apiCommand = $apiCommand; Inspector::getInstance()->validateConfig($apiCommand->getArgs(), $this, false); } else if (!($this instanceof ClosureCommand)) { Inspector::getInstance()->validateClass(get_class($this), $this, false); } if (!$this->get('headers') instanceof Collection) { $this->set('headers', new Collection()); } $this->init(); } /** * Get the short form name of the command * * @return string */ public function getName() { if ($this->apiCommand) { return $this->apiCommand->getName(); } else { // Determine the name of the command based on the relation to the // client that executes the command $parts = explode('\\', get_class($this)); while (array_shift($parts) !== 'Command'); return implode('.', array_map(array('Guzzle\\Common\\Inflector', 'snake'), $parts)); } } /** * Get the API command information about the command * * @return ApiCommand|NullObject */ public function getApiCommand() { return $this->apiCommand ?: new NullObject(); } /** * Get whether or not the command can be batched * * @return bool */ public function canBatch() { return $this->canBatch; } /** * Execute the command * * @return Command * @throws RuntimeException if a client has not been associated with the command */ public function execute() { if (!$this->client) { throw new \RuntimeException('A Client object must be associated with the command before it can be executed from the context of the command.'); } $this->client->execute($this); return $this; } /** * Get the client object that will execute the command * * @return ClientInterface|null */ public function getClient() { return $this->client; } /** * Set the client objec that will execute the command * * @param ClientInterface $client The client objec that will execute the command * * @return Command */ public function setClient(ClientInterface $client) { $this->client = $client; return $this; } /** * Get the request object associated with the command * * @return RequestInterface * @throws RuntimeException if the command has not been executed */ public function getRequest() { if (!$this->request) { throw new \RuntimeException('The command must be prepared before retrieving the request'); } return $this->request; } /** * Get the response object associated with the command * * @return Response * @throws RuntimeException if the command has not been executed */ public function getResponse() { if (!$this->isExecuted()) { throw new \RuntimeException('The command must be executed before retrieving the response'); } return $this->request->getResponse(); } /** * Get the result of the command * * @return Response By default, commands return a Response * object unless overridden in a subclass * @throws RuntimeException if the command has not been executed */ public function getResult() { if (!$this->isExecuted()) { throw new \RuntimeException('The command must be executed before retrieving the result'); } if (null === $this->result) { $this->process(); } return $this->result; } /** * Returns TRUE if the command has been prepared for executing * * @return bool */ public function isPrepared() { return $this->request !== null; } /** * Returns TRUE if the command has been executed * * @return bool */ public function isExecuted() { return $this->request !== null && $this->request->getState() == 'complete'; } /** * Prepare the command for executing and create a request object. * * @return RequestInterface Returns the generated request * @throws RuntimeException if a client object has not been set previously * or in the prepare() */ public function prepare() { if (!$this->isPrepared()) { if (!$this->client) { throw new \RuntimeException('A Client object must be associated with the command before it can be prepared.'); } // Fail on missing required arguments when it is not a ClosureCommand if (!($this instanceof ClosureCommand)) { if ($this->apiCommand) { Inspector::getInstance()->validateConfig($this->apiCommand->getArgs(), $this); } else { Inspector::getInstance()->validateClass(get_class($this), $this, true); } } $this->build(); // Add custom request headers set on the command $headers = $this->get('headers'); if ($headers && $headers instanceof Collection) { foreach ($headers as $key => $value) { $this->request->setHeader($key, $value); } } } return $this->getRequest(); } /** * Get the object that manages the request headers that will be set on any * outbound requests from the command * * @return Collection */ public function getRequestHeaders() { return $this->get('headers', new Collection()); } /** * Initialize the command (hook to be implemented in subclasses) */ protected function init() { return; } /** * Create the request object that will carry out the command */ abstract protected function build(); /** * Create the result of the command after the request has been completed. * * Sets the result as the response by default. If the response is an XML * document, this will set the result as a SimpleXMLElement. If the XML * response is invalid, the result will remain the Response, not XML. */ protected function process() { $this->result = $this->getRequest()->getResponse(); // Is the body an XML document? If so, set the result to be a SimpleXMLElement if (preg_match('/^\s*(text\/xml|application\/xml).*$/', $this->result->getContentType())) { // If the body is available, then parse the XML $body = trim($this->result->getBody(true)); if ($body) { // Silently allow parsing the XML to fail try { $xml = new \SimpleXMLElement($body); $this->result = $xml; } catch (\Exception $e) {} } } } }src/Guzzle/Service/Command/ClosureCommand.php000064400000003310151742733540015225 0ustar00 */ class ClosureCommand extends AbstractCommand { /** * {@inheritdoc} * * @throws InvalidArgumentException if a closure was not passed */ protected function init() { if (!$this->get('closure')) { throw new \InvalidArgumentException('A closure must be passed in the parameters array'); } if (!$this->get('closure_api')) { throw new \InvalidArgumentException('A closure_api value must be passed in the parameters array'); } } /** * {@inheritdoc} * * @throws UnexpectedValueException If the closure does not return a request */ protected function build() { $closure = $this->get('closure'); $this->request = $closure($this, $this->get('closure_api')); if (!$this->request || !$this->request instanceof RequestInterface) { throw new \UnexpectedValueException('Closure command did not return a RequestInterface object'); } } /** * Set whether or not the command can be batched * * @param bool $canBatch Set to TRUE if you can batch this command or FALSE * * @return ClosureCommand */ public function setCanBatch($canBatch) { $this->canBatch = (bool) $canBatch; return $this; } }src/Guzzle/Service/Command/CommandInterface.php000064400000006104151742733540015515 0ustar00 */ interface CommandInterface { /** * Constructor * * @param array|Collection $parameters (optional) Collection of parameters * to set on the command * @param ApiCommand $apiCommand (optional) Command definition from description */ function __construct($parameters = null, ApiCommand $apiCommand = null); /** * Get the short form name of the command * * @return string */ function getName(); /** * Get the API command information about the command * * @return ApiCommand|NullObject */ function getApiCommand(); /** * Get whether or not the command can be batched * * @return bool */ function canBatch(); /** * Execute the command * * @return Command * @throws RuntimeException if a client has not been associated with the command */ function execute(); /** * Get the client object that will execute the command * * @return ClientInterface|null */ function getClient(); /** * Set the client objec that will execute the command * * @param ClientInterface $client The client objec that will execute the command * * @return Command */ function setClient(ClientInterface $client); /** * Get the request object associated with the command * * @return RequestInterface * @throws RuntimeException if the command has not been executed */ function getRequest(); /** * Get the response object associated with the command * * @return Response * @throws RuntimeException if the command has not been executed */ function getResponse(); /** * Get the result of the command * * @return Response By default, commands return a Response * object unless overridden in a subclass * @throws RuntimeException if the command has not been executed */ function getResult(); /** * Returns TRUE if the command has been prepared for executing * * @return bool */ function isPrepared(); /** * Returns TRUE if the command has been executed * * @return bool */ function isExecuted(); /** * Prepare the command for executing and create a request object. * * @return RequestInterface Returns the generated request * @throws RuntimeException if a client object has not been set previously * or in the prepare() */ function prepare(); /** * Get the object that manages the request headers that will be set on any * outbound requests from the command * * @return Collection */ function getRequestHeaders(); }src/Guzzle/Service/Command/CommandSet.php000064400000013735151742733540014360 0ustar00 */ class CommandSet implements \IteratorAggregate, \Countable, Observer { /** * @var array Collections of CommandInterface objects */ protected $commands = array(); /** * @var PoolInterface Pool used for sending the requests in Parallel */ protected $pool; /** * Constructor * * @param array $commands (optional) Array of commands to add to the set * @param PoolInterface $pool (optional) Pass a Pool object for executing the * commands in parallel. Leave NULL to use the default Pool. */ public function __construct(array $commands = null, PoolInterface $pool = null) { if ($commands) { foreach ($commands as $command) { $this->addCommand($command); } } $this->pool = $pool ?: new Pool(); } /** * Add a command to the set * * @param CommandInterface $command Command object to add to the command set * * @return CommandSet */ public function addCommand(CommandInterface $command) { $this->commands[] = $command; return $this; } /** * Implements Countable * * @return int */ public function count() { return count($this->commands); } /** * Execute the command set * * @return CommandSet * @throws CommandSetException if any of the commands do not have an associated * {@see ClientInterface} object */ public function execute() { // Keep a list of all commands with no client $invalid = array(); foreach ($this->commands as $command) { if (!$command->getClient()) { $invalid[] = $command; } } // If any commands do not have a client, then throw an exception if (count($invalid)) { $e = new CommandSetException('Commands found with no associated client'); $e->setCommands($invalid); throw $e; } // Execute all serial commands foreach ($this->getSerialCommands() as $command) { $command->execute(); // Trigger the result of the command to be processed $command->getResult(); } // Execute all batched commands in parallel $parallel = $this->getParallelCommands(); if (count($parallel)) { $this->pool->reset(); // Prepare each request and send out client notifications foreach ($parallel as $command) { $request = $command->prepare(); $request->getParams()->set('command', $command); $request->getEventManager()->attach($this, -99999); $command->getClient()->getEventManager()->notify('command.before_send', $command); $this->pool->add($request); } $this->pool->send(); } return $this; } /** * Implements IteratorAggregate * * @return ArrayIterator */ public function getIterator() { return new \ArrayIterator($this->commands); } /** * Get all of the attached commands that can be sent in parallel * * @return array */ public function getParallelCommands() { return array_values(array_filter($this->commands, function($value) { return true === $value->canBatch(); })); } /** * Get all of the attached commands that can not be sent in parallel * * @return array */ public function getSerialCommands() { return array_values(array_filter($this->commands, function($value) { return false === $value->canBatch(); })); } /** * Check if the set contains a specific command * * @param string|CommandInterface $command Command object class name or * concrete CommandInterface object * * @return bool */ public function hasCommand($command) { return (bool) (count(array_filter($this->commands, function($value) use ($command) { return is_string($command) ? ($value instanceof $command) : ($value === $command); })) > 0); } /** * Remove a command from the set * * @param string|CommandInterface $command The command object or command * class name to remove * * @return CommandSet */ public function removeCommand($command) { $this->commands = array_values(array_filter($this->commands, function($value) use ($command) { return is_string($command) ? !($value instanceof $command) : ($value !== $command); })); return $this; } /** * Trigger the result of the command to be created as commands complete * * {@inheritdoc} */ public function update(Subject $subject, $event, $context = null) { if ($event == 'request.complete' && $subject->getParams()->hasKey('command')) { $command = $subject->getParams()->get('command'); // Make sure the command isn't going to send more requests if ($command && $command->isExecuted()) { $subject->getEventManager()->detach($this); $subject->getParams()->remove('command'); $command->getResult(); $command->getClient()->getEventManager()->notify('command.after_send', $command); } } } }src/Guzzle/Service/Command/CommandSetException.php000064400000001300151742733540016220 0ustar00 */ class CommandSetException extends \RuntimeException { /** * @var array Commands with an invalid client */ private $invalidCommands = array(); /** * Get the invalid commands in the CommandSet * * @return array */ public function getCommands() { return $this->invalidCommands; } /** * Set the invalid commands in the CommandSet * * @param array $commands Array of Command objects */ public function setCommands(array $commands) { $this->invalidCommands = $commands; } }src/Guzzle/Service/Description/ApiCommand.php000064400000013172151742733540015236 0ustar00 */ class ApiCommand { /** * @var array Arguments */ protected $args = array(); /** * @var array Configuration data */ protected $config = array(); /** * Constructor * * @param array $config Array of configuration data using the following keys * string name Name of the command * tring doc Method documentation * string method HTTP method of the command * string path (optional) Path routing information of the command to include in the path * string min_args (optional) The minimum number of required args * bool can_batch (optional) Can the command be sent in a batch request * string class (optional) Concrete class that implements this command * array args Associative array of arguments for the command with each * argument containing the following keys: * * name - Argument name * type - Type of variable (boolean, integer, string, array, class name, etc...) * required - Whether or not the argument is required * default - Default value of the argument * doc - Documentation for the argument * min_length - Minimum argument length * max_length - Maximum argument length * location - One of query, path, header, or body * static - Whether or not the argument can be changed from this value * prepend - Text to prepend when adding this value to a location * append - Text to append when adding to a location */ public function __construct(array $config) { $this->config = $config; $this->config['name'] = isset($config['name']) ? trim($config['name']) : ''; $this->config['doc'] = isset($config['doc']) ? trim($config['doc']) : ''; $this->config['method'] = isset($config['method']) ? trim($config['method']) : ''; $this->config['min_args'] = isset($config['min_args']) ? min(100, max(0, $config['min_args'])) : 0; $this->config['can_batch'] = isset($config['can_batch']) ? $config['can_batch'] : ''; $this->config['path'] = isset($config['path']) ? trim($config['path']) : ''; $this->config['class'] = isset($config['class']) ? trim($config['class']) : 'Guzzle\\Service\\Command\\ClosureCommand'; // Build the argument array if (isset($config['args']) && is_array($config['args'])) { $this->args = array(); foreach ($config['args'] as $argName => $arg) { if ($arg instanceof Collection) { $this->args[$argName] = $arg; } else { $this->args[$argName] = new Collection($arg); } } } } /** * Get the arguments of the command * * @return array */ public function getArgs() { return $this->args; } /** * Get a single argument of the command * * @param string $argument Argument to retrieve * * @return Collection|null */ public function getArg($arg) { foreach ($this->args as $name => $a) { if ($name == $arg) { return $a; } } return null; } /** * Get the HTTP method of the command * * @return string */ public function getMethod() { return $this->config['method']; } /** * Get the concrete command class that implements this command * * @return string */ public function getConcreteClass() { return $this->config['class']; } /** * Get the name of the command * * @return string */ public function getName() { return $this->config['name']; } /** * Get the documentation for the command * * @return string */ public function getDoc() { return $this->config['doc']; } /** * Get the minimum number of required arguments * * @return int */ public function getMinArgs() { return $this->config['min_args']; } /** * Get the path routing information to append to the path of the generated * request * * @return string */ public function getPath() { return $this->config['path']; } /** * Check if the command can be sent in a batch request * * @return bool */ public function canBatch() { return $this->config['can_batch']; } /** * Validate that the supplied configuration options satisfy the constraints * of the command * * @param Collection $option Configuration options * * @return bool|array Returns TRUE on success or an array of error messages * on error */ public function validate(Collection $config) { $errors = array(); // Validate that the right number of args has been supplied if ($this->config['min_args'] && count($config) < $this->config['min_args']) { $errors[] = $this->config['name'] . ' requires at least ' . $this->config['min_args'] . ' arguments'; } $e = Inspector::getInstance()->validateConfig($this->args, $config, false); if (is_array($e)) { $errors = array_merge($errors, $e); } return count($errors) == 0 ? true : $errors; } }src/Guzzle/Service/Description/CommandFactoryInterface.php000064400000001152151742733540017750 0ustar00 */ interface CommandFactoryInterface { /** * Build a webservice command by name based on a service description * * @param ApiCommand $command Description of the command to create * @param array $args (optional) Arguments to pass to the command * * @return Guzzle\Service\Command\CommandInterface * @throws InvalidArgumentException if the command was not found */ function createCommand(ApiCommand $command, array $args); }src/Guzzle/Service/Description/DescriptionBuilderInterface.php000064400000000460151742733540020635 0ustar00 */ interface DescriptionBuilderInterface { /** * Builds a new ServiceDescription object * * @return ServiceDescription */ function build(); }src/Guzzle/Service/Description/DynamicCommandFactory.php000064400000010713151742733540017437 0ustar00 */ class DynamicCommandFactory implements CommandFactoryInterface { /** * {@inheritdoc} */ public function createCommand(ApiCommand $command, array $args) { if ($command->getConcreteClass() != 'Guzzle\\Service\\Command\\ClosureCommand') { $class = $command->getConcreteClass(); return new $class($args, $command); } // Build the command based on the service doc and supplied arguments return new ClosureCommand(array_merge($args, array( // Generate a dynamically created command using a closure to // prepare the command 'closure' => function(ClosureCommand $that, ApiCommand $api) { // Validate the command with the config options Inspector::getInstance()->validateConfig($api->getArgs(), $that); // Get the path values and use the client config settings $pathValues = $that->getClient()->getConfig(); $foundPath = false; foreach ($api->getArgs() as $name => $arg) { if ($arg->get('location') == 'path') { $pathValues->set($name, $arg->get('prepend') . $that->get($name) . $arg->get('append')); $foundPath = true; } } // Build a custom URL if there are path values if ($foundPath) { $path = str_replace('//', '', Guzzle::inject($api->getPath(), $pathValues)); } else { $path = $api->getPath(); } if (!$path) { $url = $that->getClient()->getBaseUrl(); } else { $url = Url::factory($that->getClient()->getBaseUrl()); $url->combine($path); $url = (string) $url; } // Inject path and base_url values into the URL $request = RequestFactory::create($api->getMethod(), $url); // Add arguments to the request using the location attribute foreach ($api->getArgs() as $name => $arg) { if ($that->get($name)) { // Check that a location is set $location = $arg->get('location') ?: 'query'; if ($location == 'path' || $location == 'data') { continue; } if ($location) { // Create the value based on prepend and append settings $value = $arg->get('prepend') . $that->get($name) . $arg->get('append'); // Determine the location and key setting location[:key] $parts = explode(':', $location); $place = $parts[0]; // If a key is specified (using location:key), use it $key = isset($parts[1]) ? $parts[1] : $name; // Add the parameter to the request switch ($place) { case 'body': $request->setBody(EntityBody::factory($value)); break; case 'header': $request->setHeader($key, $value); break; case 'query': $request->getQuery()->set($key, $value); break; } } } } $that->setCanBatch($api->canBatch()); return $request; }, 'closure_api' => $command ))); } }src/Guzzle/Service/Description/ServiceDescription.php000064400000005522151742733540017032 0ustar00 */ class ServiceDescription { const DEFAULT_COMMAND_CLASS = 'Guzzle\\Service\\Command\\ClosureCommand'; /** * @var array Array of ApiCommand objects */ protected $commands = array(); /** * @var CommandFactoryInterface */ protected $commandFactory; /** * Create a new ServiceDescription * * @param array $commands (optional) Array of {@see ApiCommand} objects * @param CommandFactoryInterface (optional) Command factory to build * dynamic commands. Uses the DynamicCommandFactory by default. */ public function __construct(array $commands = array(), CommandFactoryInterface $commandFactory = null) { $this->commands = $commands; if (!$commandFactory) { $commandFactory = new DynamicCommandFactory(); } $this->commandFactory = $commandFactory; } /** * Get the API commands of the service * * @return array Returns an array of ApiCommand objects */ public function getCommands() { return $this->commands; } /** * Check if the service has a command by name * * @param string $name Name of the command to check * * @return bool */ public function hasCommand($name) { foreach ($this->commands as $command) { if ($command->getName() == $name) { return true; } } return false; } /** * Get an API command by name * * @param string $name Name of the command * * @return ApiCommand|NullObject Returns an ApiCommand on success or a * NullObject on error */ public function getCommand($name) { foreach ($this->commands as $command) { if ($command->getName() == $name) { return $command; } } return new NullObject(); } /** * Create a webservice command based on the service document * * @param string $command Name of the command to retrieve * @param array $args (optional) Arguments to pass to the command * * @return Command\CommandInterface * @throws InvalidArgumentException if the command was not found in the service doc */ public function createCommand($name, array $args = array()) { if (!$this->hasCommand($name)) { throw new \InvalidArgumentException($name . ' command not found'); } return $this->commandFactory->createCommand($this->getCommand($name), $args); } }src/Guzzle/Service/Description/XmlDescriptionBuilder.php000064400000011334151742733540017477 0ustar00 */ class XmlDescriptionBuilder implements DescriptionBuilderInterface { /** * @var string XML string */ private $xml; /** * Create a new XmlDescriptionBuilder * * @param string $xml XML string or the full path of an XML file * * @throws InvalidArgumentException if the file cannot be opened */ public function __construct($xml) { // Check if an XML string was passed or a filename if (strpos($xml, 'xml = $xml; } else { if (!is_readable($xml)) { throw new \InvalidArgumentException('Unable to open ' . $xml . ' for reading'); } $this->xml = file_get_contents($xml); } } /** * Builds a new ServiceDescription object * * @return ServiceDescription */ public function build() { $xml = new \SimpleXMLElement($this->xml); // Register any custom type definitions if ($xml->types) { foreach ($xml->types->type as $type) { $attr = $type->attributes(); $name = (string) $attr->name; $class = (string) $attr->class; if ($name && $class) { Inspector::getInstance()->registerFilter($name, $class, (string) $attr->default); } } } $commands = array(); // Parse the commands in the XML doc foreach ($xml->commands->command as $command) { $args = array(); $parentData = array(); $parentArgs = array(); $attr = $command->attributes(); $data = array( 'name' => (string) $attr->name ); if ($v = (string) $command->doc) { $data['doc'] = $v; } if ($v = (string) $attr->method) { $data['method'] = $v; } if ($v = (string) $attr->path) { $data['path'] = $v; } if ($v = (int) (string) $attr->min_args) { $data['min_args'] = $v; } if ($v = (string) $attr->can_batch) { $data['can_batch'] = $v == 'false' ? false : true; } if ($v = (string) $attr->class) { $data['class'] = $v; } $extends = (string) $attr->extends; if ($extends) { $match = false; foreach ($commands as $cmd) { if ($cmd->getName() == $extends) { $match = $cmd; } } if (!$match) { throw new \RuntimeException((string) $attr->name . 'is trying to extend non-existent command ' . $extends); } else { $parentArgs = $match->getArgs(); $parentData = array( 'name' => $match->getName(), 'doc' => $match->getDoc(), 'method' => $match->getMethod(), 'path' => $match->getPath(), 'min_args' => $match->getMinArgs(), 'can_batch' => $match->canBatch(), 'class' => $match->getConcreteClass() ); } } // Add the arguments to the command foreach ($command->param as $arg) { $row = array(); // Add each attribute to the argument foreach ($arg->attributes() as $key => $value) { $key = (string) $key; $value = (string) $value; // Convert Boolean strings to Booleans if ($key == 'required') { $value = $value === 'true' ? true : false; } $row[$key] = $value; } $args[(string) $arg->attributes()->name] = $row; } $data = array_merge($parentData, $data); $data['args'] = array_merge($parentArgs, $args); if (!isset($data['class'])) { $data['class'] = ServiceDescription::DEFAULT_COMMAND_CLASS; } else { $data['class'] = str_replace('.', '\\', $data['class']); } // Create a new command using the parsed XML $commands[] = new ApiCommand($data); } return new ServiceDescription($commands); } }src/Guzzle/Service/Plugin/MockPlugin.php000064400000010136151742733540014246 0ustar00 */ class MockPlugin extends AbstractSubject implements Observer, \Countable { /** * @var array Array of mock responses */ protected $queue = array(); /** * @var bool Whether or not to remove the plugin when the queue is empty */ protected $temporary = false; /** * Get a mock response from a file * * @param string $file File to retreive a mock response from * * @return Response * @throws InvalidArgumentException if the file is not found */ public static function getMockFile($path) { if (!file_exists($path)) { throw new \InvalidArgumentException('Unable to open mock file: ' . $path); } $parts = explode("\n\n", file_get_contents($path), 2); // Convert \n to \r\n in headers $data = isset($parts[1]) ? str_replace("\n", "\r\n", $parts[0]) . "\r\n\r\n" . $parts[1] : $parts[0]; return Response::factory($data); } /** * Constructor * * @param array $responses (optional) Array of responses to queue * @param bool $temporary (optional) Set to TRUE to remove the plugin when * the queue is empty */ public function __construct(array $responses = null, $temporary = false) { $this->temporary = $temporary; if ($responses) { foreach ($responses as $response) { $this->addResponse($response); } } } /** * Returns the number of remaining mock responses * * @return int */ public function count() { return count($this->queue); } /** * Add a response to the end of the queue * * @param string|Response $response Response object or path to response file * * @return MockPlugin * @throws InvalidArgumentException if a string or Response is not passed */ public function addResponse($response) { if (!($response instanceof Response)) { if (!is_string($response)) { throw new \InvalidArgumentException('Invalid response'); } $response = self::getMockFile($response); } $this->queue[] = $response; return $this; } /** * Clear the queue * * @return MockPlugin */ public function clearQueue() { $this->queue = array(); return $this; } /** * Returns an array of mock responses remaining in the queue * * @return array */ public function getQueue() { return $this->queue; } /** * Check if this is a temporary plugin * * @return bool */ public function isTemporary() { return $this->temporary; } /** * Get a response from the front of the list and add it to a request * * @param RequestInterface $request Request to mock * * @return MockPlugin */ public function dequeue(RequestInterface $request) { $this->getEventManager()->notify('mock.request', $request); $request->setResponse(array_shift($this->queue), true); return $this; } /** * {@inheritdoc} */ public function update(Subject $subject, $event, $context = null) { if ($event == 'request.create' && !empty($this->queue)) { $this->dequeue($context); // Detach the filter from the client so it's a one-time use if ($this->temporary && empty($this->queue)) { $subject->getEventManager()->detach($this); } } } }src/Guzzle/Service/ResourceIterator.php000064400000016250151742733540014244 0ustar00 */ abstract class ResourceIterator extends AbstractSubject implements \Iterator, \Countable { /** * @var ClientInterface */ protected $client; /** * @var mixed Current iterator value */ protected $current; /** * @var array */ protected $resourceList; /** * @var int Current index in $resourceList */ protected $currentIndex = -1; /** * @var int Current number of resources that have been iterated */ protected $pos = -1; /** * @var string NextToken/Marker for a subsequent request */ protected $nextToken; /** * @var int Total number of resources that have been iterated */ protected $iteratedCount = 0; /** * @var int Maximum number of resources to fetch per request */ protected $pageSize; /** * @var int Maximum number of resources to retrieve in total */ protected $limit; /** * @var array Initial data passed to the constructor -- used with rewind() */ protected $data = array(); /** * This should only be invoked by a {@see ClientInterface} object. * * @param ClientInterface $client Client responsible for sending requests * * @param array $data Associative array of additional parameters, including * any initial data to be iterated. * *
    *
  • page_size => Max number of results to retrieve per request.
  • *
  • resources => Initial resources to associate with the iterator.
  • *
  • next_token => The value used to mark the beginning of a subsequent result set.
  • *
*/ public function __construct(ClientInterface $client, array $data) { $this->client = $client; $this->data = $data; $this->limit = array_key_exists('limit', $data) ? $data['limit'] : -1; $this->pageSize = array_key_exists('page_size', $data) ? $data['page_size'] : false; $this->resourceList = array_key_exists('resources', $data) ? $data['resources'] : array(); $this->nextToken = array_key_exists('next_token', $data) ? $data['next_token'] : false; $this->retrievedCount = count($this->resourceList); $this->onLoad(); } /** * Get all of the resources as an array (be careful as this could issue a * large number of requests if no limit is specified) * * @param bool $rewind (optional) By default, rewind() will be called * * @return array */ public function toArray($rewind = true) { if ($rewind) { $this->rewind(); } return iterator_to_array($this, false); } /** * Return the current element. * * @return mixed Returns the current element. */ public function current() { return $this->current; } /** * Return the total number of items that have been retrieved thus far. * * Implements Countable * * @return string Returns the total number of items retrieved. */ public function count() { return $this->retrievedCount; } /** * Return the total number of items that have been iterated thus far. * * @return string Returns the total number of items iterated. */ public function getPosition() { return $this->pos; } /** * Return the key of the current element. * * @return string Returns the current key. */ public function key() { // @codeCoverageIgnoreStart return $this->currentIndex; // @codeCoverageIgnoreEnd } /** * Rewind the Iterator to the first element. */ public function rewind() { $this->currentIndex = -1; $this->pos = -1; $this->resourceList = $this->data['resources']; $this->nextToken = $this->data['next_token']; $this->retrievedCount = count($this->resourceList); $this->next(); } /** * Check if there is a current element after calls to rewind() or next(). * * @return bool Returns TRUE if the current element is valid or FALSE */ public function valid() { return isset($this->resourceList) && $this->current && ($this->pos < $this->limit || $this->limit == -1) && ($this->currentIndex < count($this->resourceList) || $this->nextToken); } /** * Move forward to next element. * * If a request needs to be sent to retrieve more elements, two events will * be dispatched: * before_send -- The context will be the currently loaded items * after_send -- The context will be the newly loaded items */ public function next() { $this->pos++; if (!isset($this->resourceList) || ++$this->currentIndex >= count($this->resourceList) && $this->nextToken && ($this->limit == -1 || $this->pos < $this->limit)) { $this->getEventManager()->notify('before_send', $this->resourceList); $this->sendRequest(); $this->getEventManager()->notify('after_send', $this->resourceList); } $this->current = (array_key_exists($this->currentIndex, $this->resourceList)) ? $this->resourceList[$this->currentIndex] : null; } /** * Retrieve the NextToken that can be used in other iterators. * * @return string Returns a NextToken */ public function getNextToken() { return $this->nextToken; } /** * Send a request to retrieve the next page of results. * Hook for sublasses to implement. */ abstract protected function sendRequest(); /** * Returns the value that should be specified for the page size for * a request that will maintain any hard limits, but still honor the * specified pageSize if the number of items retrieved + pageSize < hard * limit * * @return int Returns the page size of the next request. */ protected function calculatePageSize() { if ($this->limit == -1) { return $this->pageSize; } else if ($this->pos + $this->pageSize > $this->limit) { return $this->limit - $this->pos; } else { // @codeCoverageIgnoreStart return $this->pageSize; // @codeCoverageIgnoreEnd } } /** * Called when the iterator is constructed. * * Hook for sub-classes to implement. */ protected function onLoad() { } }src/Guzzle/Service/ResourceIteratorApplyBatched.php000064400000006326151742733540016530 0ustar00 */ class ResourceIteratorApplyBatched extends AbstractSubject { /** * @var function|array */ protected $callback; /** * @var ResourceIterator */ protected $iterator; /** * @var integer Total number of sent batches */ protected $batches = 0; /** * @var int Total number of iterated resources */ protected $iterated = 0; /** * Constructor * * @param ResourceIterator $iterator Resource iterator to apply a callback to * @param array|function $callback Callback method accepting the resource * iterator and an array of the iterator's current resources */ public function __construct(ResourceIterator $iterator, $callback) { $this->iterator = $iterator; $this->callback = $callback; } /** * Apply the callback to the contents of the resource iterator * * Calling this method dispatches four events: * * # before_apply -- Before adding a resource to a batch. Context is the resource * # after_apply -- After adding the resource to a batch. Context is the resource * # before_batch -- Before a batch request is sent if one is sent. Context is an array of resources * # after_batch -- After a batch request is sent if one is sent. Context is an array of resources * * @return integer Returns the number of resources iterated */ public function apply($perBatch = 20) { if ($this->iterated == 0) { $batched = array(); $this->iterated = 0; foreach ($this->iterator as $resource) { $batched[] = $resource; if (count($batched) >= $perBatch) { $this->applyBatch($batched); $batched = array(); } $this->iterated++; } if (count($batched)) { $this->applyBatch($batched); } unset($batch); } return $this->iterated; } /** * Get the total number of batches sent * * @return int */ public function getBatchCount() { return $this->batches; } /** * Get the total number of iterated resources * * @return int */ public function getIteratedCount() { return $this->iterated; } /** * Apply the callback to a collection of resources * * @param array $batch */ private function applyBatch(array $batch) { $this->batches++; $this->getEventManager()->notify('before_batch', $batch); call_user_func_array($this->callback, array( $this->iterator, $batch )); $this->getEventManager()->notify('after_batch', $batch); } }src/Guzzle/Service/ServiceBuilder.php000064400000017617151742733540013662 0ustar00fetch($key)) { return new self(unserialize($cached)); } } if (!is_file($data)) { throw new \RuntimeException('Unable to open service configuration file ' . $data); } $extension = $extension ?: pathinfo($data, PATHINFO_EXTENSION); switch ($extension) { case 'xml': $data = new \SimpleXMLElement($data, null, true); break; case 'js': case 'json': $config = json_decode(file_get_contents($data), true); break; default: throw new \RuntimeException('Unknown file type ' . $extension); } break; } if ($data instanceof \SimpleXMLElement) { $config = array(); foreach ($data->clients->client as $client) { $row = array(); $name = (string) $client->attributes()->name; $class = (string) $client->attributes()->class; foreach ($client->param as $param) { $row[(string) $param->attributes()->name] = (string) $param->attributes()->value; } $config[$name] = array( 'class' => $class, 'extends' => (string) $client->attributes()->extends, 'params' => $row ); } } // Validate the configuration and handle extensions foreach ($config as $name => &$client) { if (!isset($client['params'])) { $client['params'] = array(); } // Check if this client builder extends another client if (isset($client['extends']) && trim($client['extends'])) { // Make sure that the service it's extending has been defined if (!isset($config[$client['extends']])) { throw new \LogicException($name . ' is trying to extend a non-existent service: ' . $client['extends']); } if (!isset($client['class']) || !$client['class']) { $client['class'] = $config[$client['extends']]['class']; } $client['params'] = array_merge($config[$client['extends']]['params'], $client['params']); } $client['class'] = str_replace('.', '\\', $client['class']); } if ($cacheAdapter) { $cacheAdapter->save($key, serialize($config), $ttl); } return new self($config); } /** * Construct a new service builder * * @param array $serviceBuilderConfig Service configuration settings: * name => Name of the service * class => Builder class used to create clients using dot notation (Guzzle.Service.Aws.S3builder or Guzzle.Service.Builder.DefaultBuilder) * params => array of key value pair configuration settings for the builder */ public function __construct(array $serviceBuilderConfig) { $this->builderConfig = $serviceBuilderConfig; } /** * Get a client using a registered builder * * @param $name Name of the registered client to retrieve * @param bool $throwAway (optional) Set to TRUE to not store the client * for later retrieval from the ServiceBuilder * * @return ClientInterface * @throws InvalidArgumentException when a client cannot be found by name */ public function get($name, $throwAway = false) { if (!isset($this->builderConfig[$name])) { throw new \InvalidArgumentException('No client is registered as ' . $name); } if (!$throwAway && isset($this->clients[$name])) { return $this->clients[$name]; } // Convert references to the actual client foreach ($this->builderConfig[$name]['params'] as $k => &$v) { if (0 === strpos($v, '$.')) { $v = $this->get(str_replace('$.', '', $v)); } } $client = call_user_func( array($this->builderConfig[$name]['class'], 'factory'), $this->builderConfig[$name]['params'] ); if (!$throwAway) { $this->clients[$name] = $client; } return $client; } /** * Register a client by name with the service builder * * @param string $offset Name of the client to register * @param ClientInterface $value Client to register * * @return ServiceBuilder */ public function offsetSet($offset, $value) { $this->builderConfig[$offset] = $value; return $this; } /** * Remove a registered client by name * * @param string $offset Client to remove by name * * @return ServiceBuilder */ public function offsetUnset($offset) { if (isset($this->builderConfig[$offset])) { unset($this->builderConfig[$offset]); } return $this; } /** * Check if a client is registered with the service builder by name * * @param string $offset Name to check to see if a client exists * * @return bool */ public function offsetExists($offset) { return isset($this->builderConfig[$offset]); } /** * Get a registered client by name * * @param string $offset Registered client name to retrieve * * @return ClientInterface */ public function offsetGet($offset) { return $this->get($offset); } }tests/Guzzle/Tests/Common/Cache/CacheAdapterTest.php000064400000004200151742733540016407 0ustar00 * @covers Guzzle\Common\Cache\DoctrineCacheAdapter */ class CacheAdapterTest extends \Guzzle\Tests\GuzzleTestCase { /** * @var ArrayCache */ private $cache; /** * @var DoctrineCacheAdapter */ private $adapter; /** * Prepares the environment before running a test. */ protected function setUp() { parent::setUp(); $this->cache = new ArrayCache(); $this->adapter = new DoctrineCacheAdapter($this->cache); } /** * Cleans up the environment after running a test. */ protected function tearDown() { $this->adapter = null; $this->cache = null; parent::tearDown(); } /** * @covers \Guzzle\Common\Cache\AbstractCacheAdapter::getCacheObject */ public function testGetCacheObject() { $this->assertEquals($this->cache, $this->adapter->getCacheObject()); } /** * @covers \Guzzle\Common\Cache\DoctrineCacheAdapter::save */ public function testSave() { $this->assertTrue($this->adapter->save('test', 'data', 1000)); } /** * @covers \Guzzle\Common\Cache\DoctrineCacheAdapter::fetch */ public function testFetch() { $this->assertTrue($this->adapter->save('test', 'data', 1000)); $this->assertEquals('data', $this->adapter->fetch('test')); } /** * @covers \Guzzle\Common\Cache\DoctrineCacheAdapter::contains */ public function testContains() { $this->assertTrue($this->adapter->save('test', 'data', 1000)); $this->assertTrue($this->adapter->contains('test')); } /** * @covers \Guzzle\Common\Cache\DoctrineCacheAdapter::delete */ public function testDelete() { $this->assertTrue($this->adapter->save('test', 'data', 1000)); $this->assertTrue($this->adapter->delete('test')); $this->assertFalse($this->adapter->contains('test')); } }tests/Guzzle/Tests/Common/Cache/ZendCacheAdapterTest.php000064400000002316151742733540017236 0ustar00 */ class ZendCacheAdapterTest extends \Guzzle\Tests\GuzzleTestCase { /** * @var \Doctrine\Common\Cache\ArrayCache */ private $cache; /** * @var ZendCacheAdapter */ private $adapter; /** * Prepares the environment before running a test. */ protected function setUp() { parent::setUp(); $this->cache = new \Zend_Cache_Backend_Test(); $this->adapter = new ZendCacheAdapter($this->cache); } /** * Cleans up the environment after running a test. */ protected function tearDown() { $this->adapter = null; $this->cache = null; parent::tearDown(); } /** * @covers Guzzle\Common\Cache\ZendCacheAdapter */ public function testAll() { $this->assertTrue($this->adapter->save('id', 'data')); $this->assertTrue($this->adapter->delete('id', 'data')); $this->assertEquals('foo', $this->adapter->fetch('id')); $this->assertEquals('123456', $this->adapter->contains('id')); } }tests/Guzzle/Tests/Common/CollectionTest.php000064400000032776151742733540015216 0ustar00 */ class CollectionTest extends \Guzzle\Tests\GuzzleTestCase { /** * @var Guzzle\Common\Collection */ protected $coll; protected function setUp() { $this->coll = new Collection(); } /** * @covers Guzzle\Common\Collection::__construct */ public function testConstructorCanBeCalledWithNoParams() { $this->coll = new Collection(); $p = $this->coll->getAll(); $this->assertEmpty($p, '-> Collection must be empty when no data is passed'); } /** * @covers Guzzle\Common\Collection::__construct * @covers Guzzle\Common\Collection::getAll */ public function testConstructorCanBeCalledWithParams() { $testData = array( 'test' => 'value', 'test_2' => 'value2' ); $this->coll = new Collection($testData); $this->assertEquals($this->coll->getAll(), $testData, '-> getAll() must return the data passed in the constructor'); } /** * @covers Guzzle\Common\Collection::__toString */ public function testCollectionCanBeConvertingIntoString() { $data = new Collection(); $this->assertEquals('Guzzle\Common\Collection@' . spl_object_hash($data), (string)$data, '-> __toString() must represent the collection'); } /** * Test the IteratorAggregate implementation of theCollection object * * @covers Guzzle\Common\Collection::getIterator */ public function testImplementsIteratorAggregate() { $this->coll->set('key', 'value'); $this->assertInstanceOf('ArrayIterator', $this->coll->getIterator()); $this->assertEquals(1, count($this->coll)); $total = 0; foreach ($this->coll as $key => $value) { $this->assertEquals('key', $key); $this->assertEquals('value', $value); $total++; } $this->assertEquals(1, $total); } /** * @covers Guzzle\Common\Collection::add */ public function testCanAddValuesToExistingKeysByUsingArray() { $this->coll->add('test', 'value1'); $this->assertEquals($this->coll->getAll(), array('test' => 'value1')); $this->coll->add('test', 'value2'); $this->assertEquals($this->coll->getAll(), array('test' => array('value1', 'value2'))); $this->coll->add('test', 'value3'); $this->assertEquals($this->coll->getAll(), array('test' => array('value1', 'value2', 'value3'))); } /** * @covers Guzzle\Common\Collection::merge * @covers Guzzle\Common\Collection::getAll */ public function testHandlesMergingInDisparateDataSources() { $params = array( 'test' => 'value1', 'test2' => 'value2', 'test3' => array('value3', 'value4') ); $this->coll->merge($params); $this->assertEquals($this->coll->getAll(), $params); // Pass an invalid value and expect the same unaltered object $this->assertEquals($this->coll->merge(false), $this->coll); // Pass the same object to itself $this->assertEquals($this->coll->merge($this->coll), $this->coll); } /** * @covers Guzzle\Common\Collection::clear * @covers Guzzle\Common\Collection::remove */ public function testCanClearAllDataOrSpecificKeys() { $this->coll->merge(array( 'test' => 'value1', 'test2' => 'value2' )); // Clear a specific parameter by name $this->coll->remove('test'); $this->assertEquals($this->coll->getAll(), array( 'test2' => 'value2' )); // Clear all parameters $this->coll->clear(); $this->assertEquals($this->coll->getAll(), array()); } /** * @covers Guzzle\Common\Collection::get * @covers Guzzle\Common\Collection::getAll */ public function testGetsValuesByKey() { $this->assertNull($this->coll->get('test')); $this->coll->add('test', 'value'); $this->assertEquals('value', $this->coll->get('test')); $this->coll->set('test2', 'v2'); $this->coll->set('test3', 'v3'); $this->assertEquals(array( 'test' => 'value', 'test2' => 'v2' ), $this->coll->getAll(array('test', 'test2'))); } /** * @covers Guzzle\Common\Collection::getKeys * @covers Guzzle\Common\Collection::remove */ public function testProvidesKeys() { $this->assertEquals(array(), $this->coll->getKeys()); $this->coll->merge(array( 'test1' => 'value1', 'test2' => 'value2' )); $this->assertEquals(array('test1', 'test2'), $this->coll->getKeys()); // Returns the cached array previously returned $this->assertEquals(array('test1', 'test2'), $this->coll->getKeys()); $this->coll->remove('test1'); $this->assertEquals(array('test2'), $this->coll->getKeys()); $this->coll->add('test3', 'value3'); $this->assertEquals(array('test2', 'test3'), $this->coll->getKeys()); // Test the regex matching capabilities $this->assertEquals(array('test2'), $this->coll->getKeys('/test[0-2]/')); } /** * @covers Guzzle\Common\Collection::hasKey */ public function testChecksIfHasKey() { $this->assertFalse($this->coll->hasKey('test')); $this->coll->add('test', 'value'); $this->assertEquals('test', $this->coll->hasKey('test')); $this->coll->add('test2', 'value2'); $this->assertEquals('test', $this->coll->hasKey('test')); $this->assertEquals('test2', $this->coll->hasKey('test2')); $this->assertFalse($this->coll->hasKey('testing')); $this->coll->set('ab-c', '123'); $this->assertEquals('ab-c', $this->coll->hasKey('/[a-c]{2}\-c/', Collection::MATCH_REGEX)); $this->assertFalse($this->coll->hasKey('/[A-C]{2}\-c/', Collection::MATCH_REGEX)); $this->assertEquals('ab-c', $this->coll->hasKey('AB-C', Collection::MATCH_IGNORE_CASE)); $this->assertFalse($this->coll->hasKey('A-C', Collection::MATCH_IGNORE_CASE)); $this->assertEquals(false, $this->coll->hasKey('AB-C', 'junk')); } /** * @covers Guzzle\Common\Collection::hasValue */ public function testChecksIfHasValue() { $this->assertFalse($this->coll->hasValue('value')); $this->coll->add('test', 'value'); $this->assertEquals('test', $this->coll->hasValue('value')); $this->coll->add('test2', 'value2'); $this->assertEquals('test', $this->coll->hasValue('value')); $this->assertEquals('test2', $this->coll->hasValue('value2')); $this->assertFalse($this->coll->hasValue('val')); } /** * @covers Guzzle\Common\Collection::get * @covers Guzzle\Common\Collection::set * @covers Guzzle\Common\Collection::add */ public function testCanGetKeyValueByRegexMatch() { $this->coll->set('Testing-Key', 'value'); $this->coll->add('Testing-Name', 'value2'); $this->assertNull($this->coll->get('testing')); $this->assertEquals('value', $this->coll->get('/testing-Key/i', null, Collection::MATCH_REGEX)); $this->assertNull($this->coll->get('/testing-[A-Za-z]+/', null, Collection::MATCH_REGEX)); // Retrieves the first matching parameter $this->assertEquals('value', $this->coll->get('/testing-[A-Za-z]+/i', null, Collection::MATCH_REGEX)); $this->assertEquals('value2', $this->coll->get('/Testing-[Na-z]+/', null, Collection::MATCH_REGEX)); } /** * @covers Guzzle\Common\Collection::getAll */ public function testCanGetAllKeysMatchingRegex() { $this->coll->add('Testing-Key', 'value'); $this->coll->add('Testing-Name', 'value2'); $this->assertEquals(array(), $this->coll->getAll(array('testing'))); $this->assertEquals(array( 'Testing-Key' => 'value' ), $this->coll->getAll('/testing-Key/i', Collection::MATCH_REGEX)); $this->assertEquals(array( 'Testing-Name' => 'value2' ), $this->coll->getAll('/[T|t]esting-N[a-z]+/', Collection::MATCH_REGEX)); $this->assertEquals(array( 'Testing-Key' => 'value', 'Testing-Name' => 'value2' ), $this->coll->getAll(array('/testing-Key/i', '/Testing-[Na-z]+/'), Collection::MATCH_REGEX)); } /** * @covers Guzzle\Common\Collection::get */ public function testCanGetValuesByKeyIgnoringCase() { $this->coll->add('tEsT', 'value'); $this->assertNull($this->coll->get('test')); $this->assertEquals('value', $this->coll->get('test', null, true)); $this->assertEquals('default', $this->coll->get('foo', 'default', true)); } /** * @covers Guzzle\Common\Collection::getAll */ public function testCanGetAllValuesByArrayIgnoringCase() { $this->coll->add('foo', 'bar'); $this->coll->add('tEsT', 'value'); $this->coll->add('tesTing', 'v2'); $this->coll->add('key', 'v3'); $this->assertNull($this->coll->get('test')); $this->assertEquals(array( 'foo' => 'bar', 'tEsT' => 'value', 'tesTing' => 'v2' ), $this->coll->getAll(array( 'foo', 'test', 'testing' ), true)); } /** * @covers Guzzle\Common\Collection::count */ public function testImplementsCount() { $data = new Collection(); $this->assertEquals(0, $data->count()); $data->add('key', 'value'); $this->assertEquals(1, count($data)); $data->add('key', 'value2'); $this->assertEquals(1, count($data)); $data->add('key_2', 'value3'); $this->assertEquals(2, count($data)); } /** * @covers Guzzle\Common\Collection::merge */ public function testAddParamsByMerging() { $params = array( 'test' => 'value1', 'test2' => 'value2', 'test3' => array('value3', 'value4') ); // Add some parameters $this->coll->merge($params); // Add more parameters by merging them in $this->coll->merge(array( 'test' => 'another', 'different_key' => 'new value' )); $this->assertEquals(array( 'test' => array('value1', 'another'), 'test2' => 'value2', 'test3' => array('value3', 'value4'), 'different_key' => 'new value' ), $this->coll->getAll()); } /** * @covers Guzzle\Common\Collection::filter */ public function testAllowsFunctionalFilter() { $this->coll->merge(array( 'fruit' => 'apple', 'number' => 'ten', 'prepositions' => array('about', 'above', 'across', 'after'), 'same_number' => 'ten' )); $filtered = $this->coll->filter(function($key, $value) { return $value == 'ten'; }); $this->assertNotEquals($filtered, $this->coll); $this->assertEquals(array( 'number' => 'ten', 'same_number' => 'ten' ), $filtered->getAll()); } /** * @covers Guzzle\Common\Collection::map */ public function testAllowsFunctionalMapping() { $this->coll->merge(array( 'number_1' => 1, 'number_2' => 2, 'number_3' => 3 )); $mapped = $this->coll->map(function($key, $value) { return $value * $value; }); $this->assertNotEquals($mapped, $this->coll); $this->assertEquals(array( 'number_1' => 1, 'number_2' => 4, 'number_3' => 9 ), $mapped->getAll()); } /** * @covers Guzzle\Common\Collection::offsetGet * @covers Guzzle\Common\Collection::offsetSet * @covers Guzzle\Common\Collection::offsetUnset * @covers Guzzle\Common\Collection::offsetExists */ public function testImplementsArrayAccess() { $this->coll->merge(array( 'k1' => 'v1', 'k2' => 'v2' )); $this->assertTrue($this->coll->offsetExists('k1')); $this->assertFalse($this->coll->offsetExists('Krull')); $this->coll->offsetSet('k3', 'v3'); $this->assertEquals('v3', $this->coll->offsetGet('k3')); $this->assertEquals('v3', $this->coll->get('k3')); $this->coll->offsetUnset('k1'); $this->assertFalse($this->coll->offsetExists('k1')); } /** * @covers Guzzle\Common\Collection::filter * @covers Guzzle\Common\Collection::map */ public function testUsesStaticWhenCreatingNew() { $qs = new QueryString(array( 'a' => 'b', 'c' => 'd' )); $this->assertInstanceOf('Guzzle\\Http\\QueryString', $qs->map(function($a, $b) {})); $this->assertInstanceOf('Guzzle\\Common\\Collection', $qs->map(function($a, $b) {}, array(), false)); $this->assertInstanceOf('Guzzle\\Http\\QueryString', $qs->filter(function($a, $b) {})); $this->assertInstanceOf('Guzzle\\Common\\Collection', $qs->filter(function($a, $b) {}, false)); } /** * @covers Guzzle\Common\Collection::replace */ public function testCanReplaceAllData() { $this->assertSame($this->coll, $this->coll->replace(array( 'a' => '123' ))); $this->assertEquals(array( 'a' => '123' ), $this->coll->getAll()); } }tests/Guzzle/Tests/Common/Event/AbstractSubjectTest.php000064400000001162151742733540017250 0ustar00 */ class AbstractSubjectTest extends \Guzzle\Tests\GuzzleTestCase { /** * @covers \Guzzle\Common\Event\AbstractSubject::getEventManager */ public function testGetEventManager() { $subject = new MockSubject(); $mediator = $subject->getEventManager(); $this->assertInstanceOf('Guzzle\Common\Event\EventManager', $mediator); $this->assertEquals($mediator, $subject->getEventManager()); } }tests/Guzzle/Tests/Common/Event/EventManagerTest.php000064400000020403151742733540016540 0ustar00 */ class EventManagerTest extends \Guzzle\Tests\GuzzleTestCase implements Observer { /** * @covers Guzzle\Common\Event\EventManager::attach * @covers Guzzle\Common\Event\EventManager::getAttached * @covers Guzzle\Common\Event\EventManager::__construct * @covers Guzzle\Common\Event\EventManager::getSubject * @covers Guzzle\Common\Event\EventManager::getPriority */ public function testAttachesObservers() { $observer = new MockObserver(); $mock = new MockSubject(); $subject = new EventManager($mock); $subject->attach($observer); $this->assertEquals(array($observer), $subject->getAttached()); $this->assertEquals($mock, $subject->getSubject()); // A single observer can only be attached once $subject->attach($observer); $this->assertEquals(array($observer), $subject->getAttached()); // Ensure that closures can be attached alongside other Observers $closure = $subject->attach(function($subject, $event, $context) { return true; }, -10); $this->assertInstanceOf('Closure', $closure); $this->assertEquals(array($observer, $closure), $subject->getAttached()); $this->assertEquals(0, $subject->getPriority($observer)); $this->assertEquals(-10, $subject->getPriority($closure)); $this->assertNull($subject->getPriority(new \stdClass())); $this->assertNull($subject->getPriority('abc')); } /** * @covers Guzzle\Common\Event\EventManager::getAttached */ public function testRetrievesAttachedObserversByName() { $observer = new MockObserver(); $subject = new EventManager(new MockSubject()); $subject->attach($observer); $this->assertEquals(array($observer), $subject->getAttached()); $this->assertEquals(array($observer), $subject->getAttached('Guzzle\Tests\Common\Mock\MockObserver')); } /** * @covers Guzzle\Common\Event\EventManager::detach * @covers Guzzle\Common\Event\EventManager::getAttached */ public function testDetachesObservers() { $observer = new MockObserver(); $subject = new EventManager(new MockSubject()); $this->assertEquals($observer, $subject->detach($observer)); $subject->attach($observer); $this->assertEquals(array($observer), $subject->getAttached()); $this->assertEquals($observer, $subject->detach($observer)); $this->assertEquals(array(), $subject->getAttached()); // Now detach with more than one observer $subject->attach($this); $subject->attach($observer); $subject->detach($this); $this->assertEquals(array($observer), $subject->getAttached()); } /** * @covers Guzzle\Common\Event\EventManager::detachAll */ public function testDetachesAllObservers() { $observer = new MockObserver(); $subject = new EventManager(new MockSubject()); $this->assertEquals(array(), $subject->detachAll($observer)); $subject->attach($observer); $this->assertEquals(array($observer), $subject->getAttached()); $this->assertEquals(array($observer), $subject->detachAll($observer)); $this->assertEquals(array(), $subject->getAttached()); } /** * @covers Guzzle\Common\Event\EventManager::hasObserver */ public function testHasObserver() { $observer = new MockObserver(); $subject = new EventManager(new MockSubject()); $this->assertFalse($subject->hasObserver($observer)); $this->assertFalse($subject->hasObserver('Guzzle\Tests\Common\Mock\MockObserver')); $subject->attach($observer); $this->assertTrue($subject->hasObserver($observer)); $this->assertTrue($subject->hasObserver('Guzzle\Tests\Common\Mock\MockObserver')); } /** * @covers Guzzle\Common\Event\EventManager::notify * @covers Guzzle\Common\Event\EventManager::notifyObserver * @covers Guzzle\Common\Event\EventManager::attach */ public function testNotifiesObserversWithActionEventsAndNormalEvents() { $priorities = array(10, 0, 999, 0, -10); $observers = array( new MockObserver(), new MockObserver(), new MockObserver(), new MockObserver(), new MockObserver() ); $sub = new MockSubject(); $subject = new EventManager($sub); foreach ($observers as $i => $o) { $subject->attach($o, $priorities[$i]); } // Make sure that the observers were properly sorted $attached = $subject->getAttached(); $this->assertEquals(5, count($attached)); $this->assertSame($attached[0], $observers[2]); $this->assertSame($attached[1], $observers[0]); $this->assertSame($attached[2], $observers[1]); $this->assertSame($attached[3], $observers[3]); $this->assertSame($attached[4], $observers[4]); $this->assertEquals(array(true, true, true, true, true), $subject->notify('test', 'context')); foreach ($observers as $o) { $this->assertEquals('test', $o->event); $this->assertEquals('context', $o->context); $this->assertEquals(2, $o->notified); $this->assertEquals($sub, $o->subject); // Should have gotten the attach event and the subsequent test event $this->assertEquals(array('event.attach', 'test'), $o->events); } // Make sure the it will update them again $this->assertEquals(array(true, true, true, true, true), $subject->notify('test')); foreach ($observers as $o) { $this->assertEquals('test', $o->event); $this->assertEquals(null, $o->context); $this->assertEquals(3, $o->notified); $this->assertEquals($sub, $o->subject); // Did it get another test event? $this->assertEquals(array('event.attach', 'test', 'test'), $o->events); } } /** * @covers Guzzle\Common\Event\EventManager::notify */ public function testNotifiesObserversUntil() { $sub = new MockSubject(); $subject = new EventManager($sub); $observer1 = new MockObserver(); $observer2 = new MockObserver(); $observer3 = new MockObserver(); $observer4 = new MockObserver(); $subject->attach($observer1); $subject->attach($observer2); $subject->attach($observer3); $subject->attach($observer4); $this->assertEquals(array(true), $subject->notify('test', null, true)); } /** * @covers Guzzle\Common\Event\EventManager */ public function testCanAttachClosures() { $mock = new MockSubject(); $subject = new EventManager($mock); try { $apples = 'oranges'; $subject->attach($apples); $this->fail('Expected exception when adding oberser that was not a Closure or Observer'); } catch (\InvalidArgumentException $e) { } $out = ''; $closureA = $subject->attach(function($subject, $event, $context) use (&$out) { $out .= 'A: ' . $event . ' - ' . $context . "\n"; return true; }, 0); $closureB = $subject->attach(function($subject, $event, $context) use (&$out) { $out .= 'B: ' . $event . ' - ' . $context . "\n"; return true; }, 1); $this->assertEquals(array($closureB, $closureA), $subject->getAttached()); $subject->notify('test', 'context'); // The events should have been received in the following order by the // observers $this->assertEquals(trim(implode("\n", array( 'A: event.attach - ', 'B: event.attach - ', 'B: test - context', 'A: test - context', ))), trim($out)); } /** * {@inheritdoc} */ public function update(Subject $subject, $event, $context = null) { return; } }tests/Guzzle/Tests/Common/Filter/FilterImpTest.php000064400000014575151742733540016240 0ustar00 */ class FilterImpTest extends \Guzzle\Tests\GuzzleTestCase { /** * @covers Guzzle\Common\Filter\StringFilter */ public function testStringFilter() { $filter = new Filter\StringFilter(); $this->assertTrue($filter->process('test')); $this->assertEquals('The supplied value is not a string: integer supplied', $filter->process(123)); $this->assertEquals('The supplied value is not a string: object supplied', $filter->process(new \stdClass())); } /** * @covers Guzzle\Common\Filter\IntegerFilter */ public function testIntegerFilter() { $filter = new Filter\IntegerFilter(); $this->assertTrue($filter->process('123')); $this->assertTrue($filter->process(123)); $this->assertEquals('The supplied value is not a valid integer: 123.1 supplied', $filter->process(123.1)); $this->assertEquals('The supplied value is not a valid integer: 123.1 supplied', $filter->process('123.1')); } /** * @covers Guzzle\Common\Filter\FloatFilter */ public function testFloatFilter() { $filter = new Filter\FloatFilter(); $this->assertTrue($filter->process('123')); $this->assertTrue($filter->process(123)); $this->assertTrue($filter->process(123.1)); $this->assertTrue($filter->process('123.1')); $this->assertEquals('The supplied value is not a valid float: string supplied', $filter->process('abc')); $this->assertEquals('The supplied value is not a valid float: object supplied', $filter->process(new \stdClass())); } /** * @covers Guzzle\Common\Filter\TimestampFilter */ public function testTimestampFilter() { $filter = new Filter\TimestampFilter(); $this->assertTrue($filter->process(time())); $this->assertEquals('The supplied value is not a valid timestamp: abc supplied', $filter->process('abc')); $this->assertEquals('The supplied value is not a valid timestamp: -20 supplied', $filter->process(-20)); $this->assertEquals('The supplied value is not a valid timestamp: object supplied', $filter->process(new \stdClass())); } /** * @covers Guzzle\Common\Filter\DateFilter */ public function testDateFilter() { $filter = new Filter\DateFilter(); $this->assertTrue($filter->process('now')); $this->assertTrue($filter->process('1984-11-20 00:00:00')); $this->assertEquals('The supplied value is not a valid date: abc supplied', $filter->process('abc')); $this->assertEquals('The supplied value is not a valid date: 123 supplied', $filter->process(123)); $this->assertEquals('The supplied value is not a valid date: object supplied', $filter->process(new \stdClass())); } /** * @covers Guzzle\Common\Filter\BooleanFilter */ public function testBooleanFilter() { $filter = new Filter\BooleanFilter(); $this->assertTrue($filter->process(true)); $this->assertTrue($filter->process('true')); $this->assertTrue($filter->process(1)); $this->assertTrue($filter->process(false)); $this->assertTrue($filter->process('false')); $this->assertTrue($filter->process(0)); $this->assertEquals('The supplied value is not a Boolean: abc supplied', $filter->process('abc')); $this->assertEquals('The supplied value is not a Boolean: 123 supplied', $filter->process(123)); $this->assertEquals('The supplied value is not a Boolean: object supplied', $filter->process(new \stdClass())); } /** * @covers Guzzle\Common\Filter\ClassFilter */ public function testClassFilter() { $filter = new Filter\ClassFilter(array('stdClass')); $this->assertTrue($filter->process(new \stdClass())); $this->assertEquals('The supplied value is not an instance of stdClass: supplied', $filter->process('abc')); $this->assertEquals('The supplied value is not an instance of stdClass: supplied', $filter->process(123)); $this->assertEquals('The supplied value is not an instance of stdClass: ' . __CLASS__ . ' supplied', $filter->process($this)); $filter = new Filter\ClassFilter(); $this->assertTrue($filter->process(new \stdClass())); $this->assertEquals('The supplied value is not an instance of stdClass: supplied', $filter->process('abc')); } /** * @covers Guzzle\Common\Filter\ArrayFilter */ public function testAraryFilter() { $filter = new Filter\ArrayFilter(array('stdClass')); $this->assertTrue($filter->process(array())); $this->assertEquals('The supplied value is not an array: string supplied', $filter->process('abc')); $this->assertEquals('The supplied value is not an array: object supplied', $filter->process($this)); } /** * @covers Guzzle\Common\Filter\EnumFilter */ public function testEnumFilter() { $filter = new Filter\EnumFilter(array('a,b,c')); $this->assertTrue($filter->process('a')); $this->assertTrue($filter->process('b')); $this->assertTrue($filter->process('c')); $this->assertEquals('The supplied argument was not found in the list of acceptable values (a, b, c): supplied', $filter->process('abc')); $this->assertEquals('The supplied argument was not found in the list of acceptable values (a, b, c): object supplied', $filter->process($this)); } /** * @covers Guzzle\Common\Filter\RegexFilter */ public function testRegexFilter() { $filter = new Filter\RegexFilter(array('/[0-9a-z]+/')); $this->assertTrue($filter->process('a')); $this->assertTrue($filter->process('this_is_a_slug_2011')); $this->assertEquals('The supplied argument did not match the regular expression /[0-9a-z]+/: A@@ supplied', $filter->process('A@@')); $this->assertEquals('The supplied argument must be a string to match the RegexFilter: object supplied', $filter->process($this)); // With non-string $filter = new Filter\RegexFilter(array($this)); $this->assertTrue($filter->process('a')); // With no value $filter = new Filter\RegexFilter(array()); $this->assertTrue($filter->process('a')); } }tests/Guzzle/Tests/Common/Filter/FilterTest.php000064400000005200151742733540015553 0ustar00 */ class FilterTest extends \Guzzle\Tests\GuzzleTestCase { /** * @covers \Guzzle\Common\Filter\AbstractFilter::__construct */ public function testConstructorNoParams() { $filter = new MockFilter(); $this->assertEquals(array(), $filter->getAll()); } /** * @covers \Guzzle\Common\Filter\FilterInterface * @covers \Guzzle\Common\Filter\AbstractFilter::__construct * @covers \Guzzle\Common\Filter\AbstractFilter::init */ public function testConstructorWithParams() { $filter = new MockFilter(array( 'test' => 'value' )); $this->assertEquals(array('test' => 'value'), $filter->getAll()); $filter = new MockFilter(new \Guzzle\Common\Collection(array( 'test' => 'value' ))); $this->assertEquals(array('test' => 'value'), $filter->getAll()); } /** * @covers \Guzzle\Common\Filter\AbstractFilter * @covers \Guzzle\Common\Filter\AbstractFilter::process */ public function testProcess() { $filter = new MockFilter(); $command = new MockFilterCommand(); $this->assertTrue($filter->process($command)); $this->assertTrue($filter->called); $this->assertEquals('modified', $command->value); $filter->set('type_hint', 'Blah'); $this->assertFalse($filter->process($command)); $filter->set('type_hint', 'Guzzle\Tests\Common\Mock\MockFilterCommand'); $this->assertTrue($filter->process($command)); } /** * @covers Guzzle\Common\Filter\AbstractFilter::process */ public function testCannotProcessInvalidTypeHint() { $filter = new MockFilter(array( 'type_hint' => 'Guzzle\Common\NullObject' )); $command = new MockFilterCommand(); $this->assertFalse($filter->process($command)); $filter = new MockFilter(array( 'type_hint' => 'Guzzle\Common\NullObject' )); $command = new \Guzzle\Common\NullObject(); $this->assertTrue($filter->process($command)); } /** * @covers Guzzle\Common\Filter\ClosureFilter */ public function testClosureFilter() { $filter = new \Guzzle\Common\Filter\ClosureFilter(function($command) { return 'closure'; }); $command = new MockFilterCommand(); $this->assertEquals('closure', $filter->process($command)); } }tests/Guzzle/Tests/Common/InflectorTest.php000064400000005411151742733540015032 0ustar00 */ class InflectorTest extends \Guzzle\Tests\GuzzleTestCase { /** * @covers Guzzle\Common\Inflector::snake */ public function testSnake() { $this->assertEquals('camel_case', Inflector::snake('camelCase')); $this->assertEquals('camel_case', Inflector::snake('CamelCase')); $this->assertEquals('camel_case_words', Inflector::snake('CamelCaseWords')); $this->assertEquals('camel_case_words', Inflector::snake('CamelCase_words')); $this->assertEquals('test', Inflector::snake('test')); $this->assertEquals('test', Inflector::snake('test')); $this->assertEquals('expect100_continue', Inflector::snake('Expect100Continue')); } /** * @covers Guzzle\Common\Inflector::camel */ public function testCamel() { $this->assertEquals('CamelCase', Inflector::camel('camel_case')); $this->assertEquals('CamelCaseWords', Inflector::camel('camel_case_words')); $this->assertEquals('Test', Inflector::camel('test')); $this->assertEquals('Expect100Continue', ucfirst(Inflector::camel('expect100_continue'))); // Get from cache $this->assertEquals('Test', Inflector::camel('test', false)); } /** * @covers Guzzle\Common\Inflector::camel * @covers Guzzle\Common\Inflector::snake * @covers Guzzle\Common\Inflector::getCache */ public function testProtectsAgainstCacheOverflow() { $perCachePurge = Inflector::MAX_ENTRIES_PER_CACHE * 0.1; $cached = Inflector::getCache(); $currentSnake = count($cached['snake']); $currentCamel = count($cached['camel']); unset($cached); // Fill each cache with garbage, then make sure it flushes out cached // entries and maintains a cache cap while (++$currentSnake < Inflector::MAX_ENTRIES_PER_CACHE + 1) { Inflector::snake(uniqid()); } while (++$currentCamel < Inflector::MAX_ENTRIES_PER_CACHE + 1) { Inflector::camel(uniqid()); } $cached = Inflector::getCache(); $this->assertEquals(Inflector::MAX_ENTRIES_PER_CACHE, count($cached['snake'])); $this->assertEquals(Inflector::MAX_ENTRIES_PER_CACHE, count($cached['camel'])); unset($cached); // Add another element to each cache to remove 10% of the cache Inflector::snake(uniqid()); Inflector::camel(uniqid()); $cached = Inflector::getCache(); $this->assertEquals(Inflector::MAX_ENTRIES_PER_CACHE - $perCachePurge + 1, count($cached['snake'])); $this->assertEquals(Inflector::MAX_ENTRIES_PER_CACHE - $perCachePurge + 1, count($cached['camel'])); } }tests/Guzzle/Tests/Common/InspectorTest.php000064400000021724151742733540015060 0ustar00 * @license See the LICENSE file that was distributed with this source code. */ namespace Guzzle\Tests\Common; use Guzzle\Common\Inspector; use Guzzle\Common\Collection; use Guzzle\Common\Filter\FilterInterface; use Guzzle\Tests\Common\Mock\MockFilter; /** * @author Michael Dowling * @covers Guzzle\Common\Inspector * * @guzzle test type="class:Guzzle\Tests\GuzzleTestCase" * @guzzle bool_1 default="true" type="boolean" * @guzzle bool_2 default="false" * @guzzle float type="float" * @guzzle int type="integer" * @guzzle date type="date" * @guzzle timestamp type="timestamp" * @guzzle string type="string" * @guzzle username required="true" * @guzzle dynamic default="{{username}}_{{ string }}_{{ does_not_exist }}" */ class InspectorTest extends \Guzzle\Tests\GuzzleTestCase implements FilterInterface { public static $val = false; public static $args = array(); public function process($command) { self::$val = true; return true; } /** * @covers Guzzle\Common\Inspector::validateClass * @expectedException InvalidArgumentException */ public function testValidatesRequiredArgs() { Inspector::getInstance()->validateClass(__CLASS__, new Collection()); } /** * @covers Guzzle\Common\Inspector::prepareConfig */ public function testPreparesConfig() { $c = Inspector::prepareConfig(array( 'a' => '123', 'base_url' => 'http://www.test.com/' ), array( 'a' => 'xyz', 'b' => 'lol' ), array('a')); $this->assertInstanceOf('Guzzle\Common\Collection', $c); $this->assertEquals(array( 'a' => '123', 'b' => 'lol', 'base_url' => 'http://www.test.com/' ), $c->getAll()); try { $c = Inspector::prepareConfig(null, null, array('a')); $this->fail('Exception not throw when missing config'); } catch (\InvalidArgumentException $e) { } } /** * @covers Guzzle\Common\Inspector */ public function testAddsDefaultAndInjectsConfigs() { $col = new Collection(array( 'username' => 'user', 'string' => 'test', 'float' => 1.23 )); Inspector::getInstance()->validateClass(__CLASS__, $col); $this->assertEquals(false, $col->get('bool_2')); $this->assertEquals('user_test_', $col->get('dynamic')); $this->assertEquals(1.23, $col->get('float')); } /** * @covers Guzzle\Common\Inspector::validateClass * @covers Guzzle\Common\Inspector::validate * @expectedException InvalidArgumentException */ public function testValidatesTypeHints() { Inspector::getInstance()->validateClass(__CLASS__, new Collection(array( 'test' => 'uh oh', 'username' => 'test' ))); } /** * @covers Guzzle\Common\Inspector::validateClass * @covers Guzzle\Common\Inspector::validateConfig * @covers Guzzle\Common\Inspector::validate */ public function testConvertsBooleanDefaults() { $c = new Collection(array( 'test' => $this, 'username' => 'test' )); Inspector::getInstance()->validateClass(__CLASS__, $c); $this->assertTrue($c->get('bool_1')); $this->assertFalse($c->get('bool_2')); } /** * @covers Guzzle\Common\Inspector */ public function testInspectsClassArgs() { $doc = << * * @guzzle username required="true" doc="API username" type="string" * @guzzle password required="true" doc="API password" type="string" * @guzzle subdomain required="true" doc="Unfuddle project subdomain" type="string" * @guzzle api_version required="true" default="v1" doc="API version" type="string" * @guzzle protocol required="true" default="https" doc="HTTP protocol (http or https)" type="string" * @guzzle base_url required="true" default="{{ protocol }}://{{ subdomain }}.unfuddle.com/api/{{ api_version }}/" doc="Unfuddle API base URL" type="string" * @guzzle class type="class" */ EOT; $params = Inspector::getInstance()->parseDocBlock($doc); $this->assertEquals(array( 'required' => 'true', 'doc' => 'API username', 'type' => 'string' ), $params['username']); $this->assertEquals(array( 'required' => 'true', 'default' => 'v1', 'doc' => 'API version', 'type' => 'string' ), $params['api_version']); $this->assertEquals(array( 'required' => 'true', 'default' => 'https', 'doc' => 'HTTP protocol (http or https)', 'type' => 'string' ), $params['protocol']); $this->assertEquals(array( 'required' => 'true', 'default' => '{{ protocol }}://{{ subdomain }}.unfuddle.com/api/{{ api_version }}/', 'doc' => 'Unfuddle API base URL', 'type' => 'string' ), $params['base_url']); $this->assertEquals(array( 'type' => 'class' ), $params['class']); $config = new Collection(array( 'username' => 'test', 'password' => 'pass', 'subdomain' => 'sub' )); Inspector::getInstance()->validateConfig($params, $config); // make sure the configs were injected $this->assertEquals('https://sub.unfuddle.com/api/v1/', $config->get('base_url')); try { Inspector::getInstance()->validateConfig($params, new Collection(array( 'base_url' => '', 'username' => '', 'password' => '', 'class' => '123' ))); $this->fail('Expected exception not thrown when params are invalid'); } catch (\InvalidArgumentException $e) { $this->assertEquals("Validation errors: Requires that the username argument be supplied. (API username). Requires that the password argument be supplied. (API password). Requires that the subdomain argument be supplied. (Unfuddle project subdomain). The supplied value is not an instance of stdClass: supplied", $e->getMessage()); } } /** * @covers Guzzle\Common\Inspector::registerFilter * @covers Guzzle\Common\Inspector::getRegisteredFilters */ public function testRegistersCustomFilters() { $this->assertFalse(self::$val); $filter = new MockFilter(); // Register a filter with no default argument Inspector::getInstance()->registerFilter('mock', __CLASS__); // Use a default argument Inspector::getInstance()->registerFilter('mock_2', $filter, 'arg'); $validating = new Collection(array( 'data' => 'false', 'test' => 'aaa' )); Inspector::getInstance()->validateConfig(array( 'data' => array( 'type' => 'mock' ), 'test' => array( 'type' => 'mock_2' ) ), $validating); $this->assertTrue(self::$val); $this->assertArrayHasKey('mock', Inspector::getInstance()->getRegisteredFilters()); $this->assertArrayHasKey('mock_2', Inspector::getInstance()->getRegisteredFilters()); $this->assertEquals(array( 'aaa', ), $filter->commands); } /** * @covers Guzzle\Common\Inspector * @expectedException InvalidArgumentException */ public function testChecksFilterValidity() { Inspector::getInstance()->validateConfig(array( 'data' => array( 'type' => 'invalid' ) ), new Collection(array( 'data' => 'false' ))); } /** * @covers Guzzle\Common\Inspector */ public function testValidatesArgs() { $config = new Collection(array( 'data' => 123, 'min' => 'a', 'max' => 'aaa' )); $result = Inspector::getInstance()->validateConfig(array( 'data' => array( 'type' => 'string' ), 'min' => array( 'type' => 'string', 'min_length' => 2 ), 'max' => array( 'type' => 'string', 'max_length' => 2 ) ), $config, false); $this->assertEquals("The supplied value is not a string: integer supplied Requires that the min argument be >= 2 characters. Requires that the max argument be <= 2 characters.", implode("\n", $result)); } /** * @covers Guzzle\Common\Inspector::parseDocBlock */ public function testVerifiesGuzzleAnnotations() { $this->assertEquals( array(), Inspector::getInstance()->parseDocBlock('testing') ); } }tests/Guzzle/Tests/Common/Log/ClosureLogAdapterTest.php000064400000002152151742733540017204 0ustar00 */ class ClosureLogAdapterTest extends \Guzzle\Tests\GuzzleTestCase { /** * @var string Variable that the closure will modifiy */ public $modified; /** * @covers \Guzzle\Common\Log\ClosureLogAdapter */ public function testClosure() { $that = $this; $this->adapter = new ClosureLogAdapter(function($message, $priority, $extras = null) use ($that) { $that->modified = array($message, $priority, $extras); }); $this->adapter->log('test', \LOG_NOTICE, 'localhost'); $this->assertEquals(array('test', \LOG_NOTICE, 'localhost'), $this->modified); } /** * @covers \Guzzle\Common\Log\ClosureLogAdapter * @expectedException InvalidArgumentException */ public function testThrowsExceptionWhenNotCallable() { $abc = 123; $this->adapter = new ClosureLogAdapter($abc); } }tests/Guzzle/Tests/Common/Log/MonologLogAdapterTest.php000064400000002127151742733540017204 0ustar00 */ class MonologLogAdapterTest extends \Guzzle\Tests\GuzzleTestCase { /** * @covers Guzzle\Common\Log\MonologLogAdapter::__construct * @expectedException InvalidArgumentException */ public function testEnforcesType() { // A successful construction $log = new Logger('test'); $log->pushHandler(new TestHandler()); $adapter = new MonologLogAdapter($log); // Throws an exception $this->adapter = new MonologLogAdapter(new \stdClass()); } /** * @covers Guzzle\Common\Log\MonologLogAdapter::log */ public function testLogsMessagesToAdaptedObject() { $log = new Logger('test'); $handler = new TestHandler(); $log->pushHandler($handler); $adapter = new MonologLogAdapter($log); $adapter->log('test!', Logger::INFO); $this->assertTrue($handler->hasInfoRecords()); } } tests/Guzzle/Tests/Common/Log/ZendLogAdapterTest.php000064400000003743151742733540016477 0ustar00 * @covers Guzzle\Common\Log\AbstractLogAdapter * @covers Guzzle\Common\Log\ZendLogAdapter */ class ZendLogAdapterTest extends \Guzzle\Tests\GuzzleTestCase { /** * @var ZendLogAdapter */ protected $adapter; /** * @var Zend_Log */ protected $log; /** * Sets up the fixture, for example, opens a network connection. * This method is called before a test is executed. */ protected function setUp() { $this->log = new \Zend_Log(new \Zend_Log_Writer_Stream('php://output')); $this->adapter = new ZendLogAdapter($this->log); } /** * @covers Guzzle\Common\Log\AbstractLogAdapter::__construct * @expectedException InvalidArgumentException */ public function testEnforcesType() { // A successful construction $this->adapter = new ZendLogAdapter($this->log, new Collection()); // Throws an exception $this->adapter = new ZendLogAdapter(new \stdClass()); } /** * @covers Guzzle\Common\Log\ZendLogAdapter::log * @outputBuffering enabled */ public function testLogsMessagesToAdaptedObject() { // Test without a priority $this->adapter->log('test', \LOG_NOTICE, 'guzzle.common.log.adapter.zend_log_adapter', 'localhost'); $this->assertEquals(1, substr_count(ob_get_contents(), 'test')); // Test with a priority $this->adapter->log('test', \LOG_ALERT); $this->assertEquals(2, substr_count(ob_get_contents(), 'test')); } /** * @covers Guzzle\Common\Log\AbstractLogAdapter::getLogObject */ public function testExposesAdaptedLogObject() { $this->assertEquals($this->log, $this->adapter->getLogObject()); } }tests/Guzzle/Tests/Common/Mock/MockFilter.php000064400000001711151742733540015174 0ustar00 */ class MockFilter extends \Guzzle\Common\Filter\AbstractFilter { public $called = false; public $calls = array(); public $commands = array(); public function __call($method, $args) { $this->calls[] = array( 'method' => $method, 'args' => $args ); } protected function filterCommand($command) { $this->commands[] = $command; if ($this->get('callback')) { $func = $this->get('callback'); return $func($this, $command); } else { $this->called = true; if (!is_scalar($command)) { $command->value = 'modified'; } return true; } } }tests/Guzzle/Tests/Common/Mock/MockFilterCommand.php000064400000000434151742733540016474 0ustar00 */ class MockFilterCommand { public $value = ''; }tests/Guzzle/Tests/Common/Mock/MockObserver.php000064400000001502151742733540015534 0ustar00 */ class MockObserver implements Observer { public $notified = 0; public $subject; public $context; public $event; public $log = array(); public $logByEvent = array(); public $events = array(); /** * {@inheritdoc} */ public function update(Subject $subject, $event, $context = null) { $this->notified++; $this->subject = $subject; $this->context = $context; $this->event = $event; $this->events[] = $event; $this->log[] = array($event, $context); $this->logByEvent[$event] = $context; return true; } }tests/Guzzle/Tests/Common/Mock/MockSubject.php000064400000000251151742733540015344 0ustar00 */ class MockSubject extends \Guzzle\Common\Event\AbstractSubject { }tests/Guzzle/Tests/Common/NullObjectTest.php000064400000002064151742733540015147 0ustar00 */ class NullObjectTest extends \Guzzle\Tests\GuzzleTestCase { /** * @covers Guzzle\Common\NullObject */ public function testAll() { $nullObject = new NullObject(); $this->assertNull($nullObject->isItNull()); isset($nullObject->isNull); $this->assertNull($nullObject->isNull); $nullObject->isNull = 10; unset($nullObject->isNull); $this->assertNull($nullObject->offsetGet('a')); $nullObject['a'] = '123'; $this->assertFalse(isset($nullObject['a'])); $this->assertNull($nullObject['a']); $this->assertNull($nullObject->offsetUnset('a')); $this->assertNull($nullObject->count()); $this->assertNull($nullObject->key()); $this->assertNull($nullObject->next()); $this->assertNull($nullObject->rewind()); $this->assertNull($nullObject->valid()); $this->assertNull($nullObject->current()); } }tests/Guzzle/Tests/Common/StreamTest.php000064400000015737151742733540014354 0ustar00 */ class StreamTest extends \Guzzle\Tests\GuzzleTestCase { /** * @covers Guzzle\Common\Stream::__construct * @expectedException InvalidArgumentException */ public function testConstructorThrowsExceptionOnInvalidArgument() { $stream = new Stream(true); } /** * @covers Guzzle\Common\Stream::__construct * @covers Guzzle\Common\Stream::getSize * @covers Guzzle\Common\Stream::getUri * @covers Guzzle\Common\Stream::isReadable * @covers Guzzle\Common\Stream::isWritable * @covers Guzzle\Common\Stream::isSeekable * @covers Guzzle\Common\Stream::isLocal * @covers Guzzle\Common\Stream::isConsumed * @covers Guzzle\Common\Stream::getStream * @covers Guzzle\Common\Stream::getWrapper * @covers Guzzle\Common\Stream::getWrapperData * @covers Guzzle\Common\Stream::getFilters * @covers Guzzle\Common\Stream::getStreamType */ public function testConstructor() { $handle = fopen('php://temp', 'r+'); fwrite($handle, 'data'); $stream = new Stream($handle); $this->assertEquals($handle, $stream->getStream()); $this->assertTrue($stream->isReadable()); $this->assertTrue($stream->isWritable()); $this->assertTrue($stream->isLocal()); $this->assertTrue($stream->isSeekable()); $this->assertEquals('php', $stream->getWrapper()); $this->assertEquals('temp', $stream->getStreamType()); $this->assertEquals(4, $stream->getSize()); $this->assertEquals('php://temp', $stream->getUri()); $this->assertEquals(array(), $stream->getWrapperData()); $this->assertEquals(array( 'wrapped' => array(), 'unwrapped' => array() ), $stream->getFilters()); $this->assertFalse($stream->isConsumed()); unset($stream); } /** * @covers Guzzle\Common\Stream::__destruct */ public function testStreamClosesHandleOnDestruct() { $handle = fopen('php://temp', 'r'); $stream = new Stream($handle); unset($stream); $this->assertFalse(is_resource($handle)); } /** * @covers Guzzle\Common\Stream::__toString */ public function testConvertsToString() { $handle = fopen('php://temp', 'w+'); fwrite($handle, 'data'); $stream = new Stream($handle); $this->assertEquals('data', (string)$stream); unset($stream); $handle = fopen(__DIR__ . '/../TestData/FileBody.txt', 'w'); $stream = new Stream($handle); $this->assertEquals('', (string)$stream); unset($stream); } /** * @covers Guzzle\Common\Stream::isConsumed */ public function testIsConsumed() { $handle = fopen('php://temp', 'w+'); fwrite($handle, 'data'); $stream = new Stream($handle); $this->assertFalse($stream->isConsumed()); $stream->read(4); $this->assertTrue($stream->isConsumed()); } /** * @covers Guzzle\Common\Stream::setSize * @covers Guzzle\Common\Stream::getSize */ public function testAllowsSettingManualSize() { $handle = fopen('php://temp', 'w+'); fwrite($handle, 'data'); $stream = new Stream($handle); $stream->setSize(10); $this->assertEquals(10, $stream->getSize()); unset($stream); } /** * @covers Guzzle\Common\Stream::read * @covers Guzzle\Common\Stream::write * @covers Guzzle\Common\Stream::seek * @covers Guzzle\Common\Stream::isReadable * @covers Guzzle\Common\Stream::isSeekable */ public function testWrapsStream() { $handle = fopen('php://temp', 'w+'); fwrite($handle, 'data'); $stream = new Stream($handle); $this->assertTrue($stream->isSeekable()); $this->assertTrue($stream->isReadable()); $this->assertTrue($stream->seek(0)); $this->assertEquals('da', $stream->read(2)); $this->assertEquals('ta', $stream->read(2)); $this->assertTrue($stream->seek(0)); $this->assertEquals('data', $stream->read(4)); $stream->write('_appended'); $stream->seek(0); $this->assertEquals('data_appended', $stream->read(13)); } /** * @covers Guzzle\Common\Stream::getSize * @covers Guzzle\Common\Stream::__construct */ public function testGetSize() { $size = filesize(__DIR__ . '/../../../bootstrap.php'); $handle = fopen(__DIR__ . '/../../../bootstrap.php', 'r'); $stream = new Stream($handle); $stream->addFilter('string.rot13', \STREAM_FILTER_READ); $this->assertEquals($handle, $stream->getStream()); $this->assertEquals($size, $stream->getSize()); $this->assertEquals($size, $stream->getSize()); $stream->addFilter('zlib.inflate', \STREAM_FILTER_READ); $this->assertEquals(false, $stream->getSize()); unset($stream); // Make sure that false is returned when the size cannot be determined $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Lenght: 0\r\n\r\n"); $handle = fopen('http://localhost:' . $this->getServer()->getPort(), 'r'); $stream = new Stream($handle); $this->assertEquals(false, $stream->getSize()); unset($stream); } /** * @covers Guzzle\Common\Stream::addFilter * @covers Guzzle\Common\Stream::getFilters */ public function testAddFilter() { $handle = fopen(__DIR__ . '/../../../bootstrap.php', 'r'); $stream = new Stream($handle); $stream->addFilter('string.rot13', \STREAM_FILTER_READ); // Prepend this one $stream->addFilter('string.toupper', \STREAM_FILTER_READ, null, true); $filters = $stream->getFilters(); $this->assertArrayHasKey('wrapped', $filters); $this->assertArrayHasKey('unwrapped', $filters); $this->assertEquals(array(), $filters['unwrapped']); $this->assertArrayHasKey('string.rot13|1', $filters['wrapped']); $this->assertInternalType('resource', $filters['wrapped']['string.rot13|1']); unset($stream); } /** * @covers Guzzle\Common\Stream::addFilter * @covers Guzzle\Common\Stream::getFilters * @covers Guzzle\Common\Stream::removeFilter */ public function testRemoveFilter() { $handle = fopen(__DIR__ . '/../../../bootstrap.php', 'r'); $stream = new Stream($handle); $stream->addFilter('string.rot13', \STREAM_FILTER_READ, false); $data = (string)$stream; $filters = $stream->getFilters(); $this->assertTrue($stream->removeFilter('string.rot13', \STREAM_FILTER_READ)); $this->assertNotEquals($filters, $stream->getFilters()); $this->assertNotEquals($data, (string)$stream); $this->assertFalse($stream->removeFilter('string.rot13', \STREAM_FILTER_READ)); unset($stream); } }tests/Guzzle/Tests/Common/XmlElementTest.php000064400000004310151742733540015154 0ustar00 * @author Harold Asbridge */ class XmlElementTest extends \Guzzle\Tests\GuzzleTestCase { /** * @return array */ public function addElementProvider() { return array( array(new XmlElement(''), 'Foo', '' . PHP_EOL . '' . PHP_EOL), array(new XmlElement(''), new XmlElement(''), '' . PHP_EOL . '' . PHP_EOL), array(new XmlElement(''), new XmlElement('123'), '' . PHP_EOL . '123' . PHP_EOL), array(new XmlElement(''), new XmlElement('123'), '' . PHP_EOL . '123' . PHP_EOL), array(new XmlElement(''), new XmlElement('123'), '' . PHP_EOL . '123' . PHP_EOL) ); } /** * @covers \Guzzle\Common\XmlElement::addChild * @dataProvider addElementProvider */ public function testAddsChildElementsUsingStringsOrSimpleXmlElements($orig, $add, $result) { $orig->addChild($add); $this->assertEquals($result, $orig->__toString()); } /** * @covers \Guzzle\Common\XmlElement::asFormattedXml */ public function testDisplaysXmlDataAsFormattedXmlString() { $elem = new XmlElement(''); $expected = '' . PHP_EOL . '' . PHP_EOL; $this->assertEquals($expected, $elem->asFormattedXml()); } /** * @covers \Guzzle\Common\XmlElement::__toString */ public function testAddsToStringMethodToSimpleXmlElement() { $elem = new XmlElement(''); $expected = '' . PHP_EOL . '' . PHP_EOL; $this->assertEquals($expected, $elem->__toString()); } }tests/Guzzle/Tests/GuzzleTest.php000064400000005523151742733540013141 0ustar00 */ class GuzzleTest extends GuzzleTestCase { /** * @covers Guzzle\Guzzle */ public function testGetDefaultUserAgent() { Guzzle::reset(); $version = curl_version(); $agent = sprintf('Guzzle/%s (Language=PHP/%s; curl=%s; Host=%s)', Guzzle::VERSION, \PHP_VERSION, $version['version'], $version['host']); $this->assertEquals($agent, Guzzle::getDefaultUserAgent()); // Get it from cache this time $this->assertEquals($agent, Guzzle::getDefaultUserAgent()); } /** * @covers Guzzle\Guzzle::getHttpDate */ public function testGetHttpDate() { $fmt = 'D, d M Y H:i:s \G\M\T'; $this->assertEquals(gmdate($fmt), Guzzle::getHttpDate('now')); $this->assertEquals(gmdate($fmt), Guzzle::getHttpDate(strtotime('now'))); $this->assertEquals(gmdate($fmt, strtotime('+1 day')), Guzzle::getHttpDate('+1 day')); } public function dataProvider() { return array( array('this_is_a_test', '{{ a }}_is_a_{{ b }}', array( 'a' => 'this', 'b' => 'test' )), array('this_is_a_test', '{{abc}}_is_a_{{ 0 }}', array( 'abc' => 'this', 0 => 'test' )), array('this_is_a_test', '{{ abc }}_is_{{ not_found }}a_{{ 0 }}', array( 'abc' => 'this', 0 => 'test' )), array('this_is_a_test', 'this_is_a_test', array( 'abc' => 'this' )), array('_is_a_', '{{ abc }}_is_{{ not_found }}a_{{ 0 }}', array()), ); } /** * @covers Guzzle\Guzzle::inject * @dataProvider dataProvider */ public function testInjectsConfigData($output, $input, $config) { $this->assertEquals($output, Guzzle::inject($input, new Collection($config))); } /** * @covers Guzzle\Guzzle::getCurlInfo */ public function testCachesCurlInfo() { Guzzle::reset(); $c = curl_version(); $info = Guzzle::getCurlInfo(); $this->assertInternalType('array', $info); $this->assertEquals(false, Guzzle::getCurlInfo('ewfewfewfe')); $this->assertEquals($c['version'], Guzzle::getCurlInfo('version')); $this->assertSame(Guzzle::getCurlInfo(), $info); } /** * @covers Guzzle\Guzzle::getCurlInfo * @covers Guzzle\Guzzle::reset */ public function testDeterminesIfCurlCanFollowLocation() { Guzzle::reset(); if (!ini_get('open_basedir')) { $this->assertTrue(Guzzle::getCurlInfo('follow_location')); } else { $this->assertFalse(Guzzle::getCurlInfo('follow_location')); } } }tests/Guzzle/Tests/GuzzleTestCase.php000064400000010731151742733540013732 0ustar00 */ abstract class GuzzleTestCase extends \PHPUnit_Framework_TestCase { protected static $mockBasePath; public static $serviceBuilder; public static $server; private $requests = array(); public $mockObserver; /** * Get the global server object used throughout the unit tests of Guzzle * * @return Server */ public function getServer() { if (!self::$server) { self::$server = new Server(); if (self::$server->isRunning()) { self::$server->flush(); } else { self::$server->start(); } } return self::$server; } /** * Set the service builder to use for tests * * @param ServiceBuilder $builder Service builder */ public static function setServiceBuilder(ServiceBuilder $builder) { self::$serviceBuilder = $builder; } /** * Get a service builder object that can be used throughout the service tests * * @return ServiceBuilder */ public function getServiceBuilder() { if (!self::$serviceBuilder) { throw new RuntimeException('No service builder has been set via setServiceBuilder()'); } return self::$serviceBuilder; } /** * Set the mock response base path * * @param string $path Path to mock response folder * * @return GuzzleTestCase */ public static function setMockBasePath($path) { self::$mockBasePath = $path; } /** * Mark a request as being mocked * * @param RequestInterface $request */ public function addMockedRequest(RequestInterface $request) { $this->requests[] = $request; return $this; } /** * Get all of the mocked requests * * @return array */ public function getMockedRequests() { return $this->requests; } /** * Get a mock response for a client by mock file name * * @param string $path Relative path to the mock response file * * @return Response */ public function getMockResponse($path) { return MockPlugin::getMockFile(self::$mockBasePath . DIRECTORY_SEPARATOR . $path); } /** * Set a mock response from a mock file on the next client request. * * This method assumes that mock response files are located under the * Command/Mock/ directory of the Service being tested * (e.g. Unfuddle/Command/Mock/). A mock response is added to the next * request sent by the client. * * @param Client $client Client object to modify * @param string $paths Path to files within the Mock folder of the service */ public function setMockResponse(Client $client, $paths) { $this->requests = array(); $that = $this; $mock = new MockPlugin(array(), true); $mock->getEventManager()->attach(function($subject, $event, $context) use ($that) { if ($event == 'mock.request') { $that->addMockedRequest($context); } }); foreach ((array) $paths as $path) { $mock->addResponse($this->getMockResponse($path)); } $client->getEventManager()->attach($mock, 9999); } /** * Check if an array of HTTP headers matches another array of HTTP headers * while taking * into account as a wildcard for header values * * @param array $actual Actual HTTP header array * @param array $expected Expected HTTP headers (allows wildcard values) * * @return array|false Returns an array of the differences or FALSE if none */ public function compareHttpHeaders(array $expected, array $actual) { $differences = array(); foreach ($actual as $key => $value) { if (!isset($expected[$key]) || ($expected[$key] != '*' && $actual[$key] != $expected[$key])) { $differences[$key] = $value; } } return empty($differences) ? false : $differences; } }tests/Guzzle/Tests/Http/CookieJar/ArrayCookieJarTest.php000064400000027566151742733540017326 0ustar00 */ class ArrayCookieJarTest extends \Guzzle\Tests\GuzzleTestCase { /** * @var ArrayCookieJar */ private $jar; public function setUp() { $this->jar = new ArrayCookieJar(); } /** * Add values to the cookiejar */ public static function addCookies(CookieJarInterface $jar) { $jar->save(array( 'cookie' => array('foo', 'bar'), 'domain' => 'example.com', 'path' => '/', 'max_age' => '86400', 'port' => array(80, 8080), 'version' => '1', 'secure' => true ))->save(array( 'cookie' => array('baz', 'foobar'), 'domain' => 'example.com', 'path' => '/', 'max_age' => '86400', 'port' => array(80, 8080), 'version' => '1', 'secure' => true ))->save(array( 'cookie' => array('test', '123'), 'domain' => 'www.foobar.com', 'path' => '/path/', 'discard' => true ))->save(array( 'domain' => '.y.example.com', 'path' => '/acme/', 'cookie' => array('muppet', 'cookie_monster'), 'comment' => 'Comment goes here...', 'expires' => Guzzle::getHttpDate('+1 day') ))->save(array( 'domain' => '.example.com', 'path' => '/test/acme/', 'cookie' => array('googoo', 'gaga'), 'max_age' => 1500, 'version' => 2 )); } /** * Provides test data for cookie jar retrieval */ public function getCookiesDataProvider() { return array( array(array('foo', 'baz', 'test', 'muppet', 'googoo'), '', '', '', false), array(array('foo', 'baz', 'muppet', 'googoo'), '', '', '', true), array(array('googoo'), 'www.example.com', '', '', false), array(array('muppet', 'googoo'), 'test.y.example.com', '', '', false), array(array('foo', 'baz'), 'example.com', '', '', false), array(array('muppet'), 'x.y.example.com', '/acme/', '', false), array(array('muppet'), 'x.y.example.com', '/acme/test/', '', false), array(array('googoo'), 'x.y.example.com', '/test/acme/test/', '', false), array(array('foo', 'baz'), 'example.com', '', '', false), array(array('baz'), 'example.com', '', 'baz', false), ); } /** * @covers Guzzle\Http\CookieJar\ArrayCookieJar */ public function testStoresAndRetrievesCookies() { $j = $this->jar; $this->assertSame($j, $j->save(array( 'cookie' => array('foo', 'bar'), 'domain' => '.example.com', 'path' => '/', 'max_age' => '86400', 'port' => array(80, 8080), 'version' => '1', 'secure' => true ))); $this->assertSame($j, $j->save(array( 'cookie' => array('baz', 'foobar'), 'domain' => '.example.com', 'path' => '/', 'max_age' => '86400', 'port' => array(80, 8080), 'version' => '1', 'secure' => true ))); $this->assertSame($j, $j->save(array( 'cookie' => array('test', '123'), 'domain' => 'www.foobar.com', 'path' => '/path/' ))); $this->assertEquals(array( array ( 'cookie' => array('foo', 'bar'), 'domain' => '.example.com', 'path' => '/', 'max_age' => '86400', 'port' => array(80, 8080), 'version' => '1', 'secure' => true, 'expires' => time() + 86400, 'comment' => NULL, 'comment_url' => NULL, 'discard' => NULL, 'http_only' => false ), array ( 'cookie' => array('baz', 'foobar'), 'domain' => '.example.com', 'path' => '/', 'max_age' => '86400', 'port' => array( 0 => 80, 1 => 8080, ), 'version' => '1', 'secure' => true, 'expires' => time() + 86400, 'comment' => NULL, 'comment_url' => NULL, 'discard' => NULL, 'http_only' => false ), array ( 'cookie' => array('test' , '123'), 'domain' => 'www.foobar.com', 'path' => '/path/', 'max_age' => 0, 'comment' => NULL, 'comment_url' => NULL, 'port' => array (), 'secure' => NULL, 'expires' => null, 'discard' => NULL, 'version' => null, 'http_only' => false ), ), $j->getCookies()); } /** * Checks if cookies (by name) are present in a cookieJar * * @param array $cookies Cookies from the cookie jar * @param array $names Names to check for * * @return true */ protected function hasCookies(array $cookies, array $names) { $remaining = $names; $extra = array(); foreach ($cookies as $cookie) { $match = false; foreach ($remaining as $ri => $c) { if ($cookie['cookie'][0] == $c) { unset($remaining[$ri]); $match = true; } } if (!$match) { $extra[] = $cookie['cookie'][0] . '=' . $cookie['cookie'][1]; } } $this->assertTrue(count($remaining) == 0 && count($extra) == 0, 'Unmatched: ' . implode(', ', $remaining) . ' | Extra: ' . implode(', ', $extra)); } /** * @covers Guzzle\Http\CookieJar\ArrayCookieJar * @dataProvider getCookiesDataProvider */ public function testGetCookies(array $matches, $domain = null, $path = null, $name = null, $skipDiscardable = false) { self::addCookies($this->jar); $cookies = $this->jar->getCookies($domain, $path, $name, $skipDiscardable); $this->hasCookies($cookies, $matches); } /** * @covers Guzzle\Http\CookieJar\ArrayCookieJar */ public function testClearsTemporaryCookies() { self::addCookies($this->jar); $this->assertEquals(1, $this->jar->clearTemporary()); $this->hasCookies($this->jar->getCookies(), array('foo', 'baz', 'muppet', 'googoo')); // Doesn't clear anything out because nothing is temporary $this->assertEquals(0, $this->jar->clearTemporary()); // Add an expired cookie $this->jar->save(array( 'cookie' => array('data', 'abc'), 'domain' => '.example.com' )); // Filters out expired cookies $this->hasCookies($this->jar->getCookies(), array('foo', 'baz', 'muppet', 'googoo', 'data')); // Removes the expired cookie $this->assertEquals(1, $this->jar->clearTemporary()); $this->hasCookies($this->jar->getCookies(), array('foo', 'baz', 'muppet', 'googoo')); } /** * @covers Guzzle\Http\CookieJar\ArrayCookieJar */ public function testClearsExpiredCookies() { self::addCookies($this->jar); $this->assertEquals(0, $this->jar->deleteExpired()); // Add an expired cookie $this->jar->save(array( 'cookie' => array('data', 'abc'), 'expires' => Guzzle::getHttpDate('-1 day'), 'domain' => '.example.com' )); // Filters out expired cookies $this->hasCookies($this->jar->getCookies(), array('foo', 'baz', 'test', 'muppet', 'googoo')); $this->assertEquals(1, $this->jar->deleteExpired()); $this->assertEquals(0, $this->jar->deleteExpired()); } /** * @covers Guzzle\Http\CookieJar\ArrayCookieJar * @expectedException InvalidArgumentException * @expectedExceptionMessage Cookies require a domain */ public function testValidatesDomainWhenSaving() { $this->jar->save(array()); } /** * @covers Guzzle\Http\CookieJar\ArrayCookieJar * @expectedException InvalidArgumentException * @expectedExceptionMessage Cookies require a names and values */ public function testValidatesCookiesArePresentWhenSaving() { $this->jar->save(array( 'domain' => '.test.com' )); } /** * Provides test data for cookie jar clearing */ public function clearCookiesDataProvider() { return array( array(array(), 4, '', '', '', ''), array(array('test', 'muppet', 'googoo'), 1, 'example.com', '', ''), array(array('foo', 'baz', 'test'), 2, 'a.y.example.com', '', ''), array(array('foo', 'baz', 'test', 'googoo'), 1, 'a.y.example.com', '/acme/', ''), // Removes only baz from the cookie that contains two values array(array('test', 'muppet', 'googoo', 'foo'), 1, 'example.com', '/', 'baz'), array(array('foo', 'baz', 'test', 'muppet'), 1, 'www.example.com', '/test/acme/', '') ); } /** * @covers Guzzle\Http\CookieJar\ArrayCookieJar * @dataProvider clearCookiesDataProvider */ public function testClearsCookies($matches, $total, $domain, $path, $name) { self::addCookies($this->jar); $this->jar->clear($domain, $path, $name); $this->hasCookies($this->jar->getCookies(null, null, null, null, false, true), $matches); } /** * @covers Guzzle\Http\CookieJar\ArrayCookieJar */ public function testDoesNotAddDuplicates() { $this->jar->clear(); $this->assertEquals(0, count($this->jar->getCookies())); $plugin = new CookiePlugin($this->jar); $data = array( 'cookie' => array('foo', 'bar'), 'domain' => '.example.com', 'path' => '/', 'max_age' => '86400', 'port' => array(80, 8080), 'version' => '1', 'secure' => true ); $this->jar->save($data); $this->jar->save($data); $this->assertEquals(1, count($this->jar->getCookies())); } /** * @covers Guzzle\Http\CookieJar\ArrayCookieJar */ public function testOverwritesCookiesThatAreOlderOrDiscardable() { $this->jar->clear(); $t = time() + 1000; $data = array( 'cookie' => array('foo', 'bar'), 'domain' => '.example.com', 'path' => '/', 'max_age' => '86400', 'port' => array(80, 8080), 'version' => '1', 'secure' => true, 'discard' => true, 'expires' => $t ); // Make sure that the discard cookie is overridden with the non-discard $this->jar->save($data); unset($data['discard']); $this->jar->save($data); $this->assertEquals(1, count($this->jar->getCookies())); $c = $this->jar->getCookies(); $this->assertEquals(false, $c[0]['discard']); $this->assertEquals($t, $c[0]['expires']); // Make sure it doesn't duplicate the cookie $this->jar->save($data); $this->assertEquals(1, count($this->jar->getCookies())); // Make sure the more future-ful expiration date supercedes the other $data['expires'] = time() + 2000; $this->jar->save($data); $this->assertEquals(1, count($this->jar->getCookies())); $c = $this->jar->getCookies(); $this->assertNotEquals($t, $c[0]['expires']); } }tests/Guzzle/Tests/Http/CookieJar/FileCookieJarTest.php000064400000005005151742733540017107 0ustar00 */ class FileCookieJarTest extends \Guzzle\Tests\GuzzleTestCase { /** * @var FileCookieJar */ private $jar; /** * @var string */ private $file; public function setUp() { $this->file = tempnam('/tmp', 'file-cookies'); $this->jar = new FileCookieJar($this->file); } /** * Add values to the cookiejar */ protected function addCookies() { $this->jar->save(array( 'cookie' => array('foo', 'bar'), 'domain' => 'example.com', 'path' => '/', 'max_age' => '86400', 'port' => array(80, 8080), 'version' => '1', 'secure' => true ))->save(array( 'cookie' => array('test', '123'), 'domain' => 'www.foobar.com', 'path' => '/path/', 'discard' => true ))->save(array( 'domain' => '.y.example.com', 'path' => '/acme/', 'cookie' => array('muppet', 'cookie_monster'), 'comment' => 'Comment goes here...', 'expires' => Guzzle::getHttpDate('+1 day') ))->save(array( 'domain' => '.example.com', 'path' => '/test/acme/', 'cookie' => array('googoo', 'gaga'), 'max_age' => 1500, 'version' => 2 )); } /** * @covers Guzzle\Http\CookieJar\FileCookieJar */ public function testLoadsFromFileFile() { unset($this->jar); $this->jar = new FileCookieJar($this->file); $this->assertEquals(array(), $this->jar->getCookies()); unlink($this->file); } /** * @covers Guzzle\Http\CookieJar\FileCookieJar */ public function testPersistsToFileFile() { unset($this->jar); $this->jar = new FileCookieJar($this->file); $this->addCookies(); $this->assertEquals(4, count($this->jar->getCookies())); unset($this->jar); // Make sure it wrote to the file $contents = file_get_contents($this->file); $this->assertNotEmpty($contents); // Load the jar from the file $jar = new FileCookieJar($this->file); // Weeds out temporary and session cookies $this->assertEquals(3, count($jar->getCookies())); unset($jar); unlink($this->file); } }tests/Guzzle/Tests/Http/CookieTest.php000064400000004021151742733540014001 0ustar00 */ class CookieTest extends \Guzzle\Tests\GuzzleTestCase { /** * Data provider for tests * * @return array */ public function provider() { return array( array('name=value', array( 'name' => 'value' )), array('name=value;name2=value 2', array( 'name' => 'value', 'name2' => 'value 2' )), array('name=value;name2=x=y&a=b', array( 'name' => 'value', 'name2' => 'x=y&a=b' )), ); } /** * @covers Guzzle\Http\Cookie::factory * @dataProvider provider */ public function testFactoryBuildsCookiesFromCookieStrings($cookieString, array $data) { $jar = Cookie::factory($cookieString); $this->assertEquals($data, $jar->getAll()); } /** * @covers Guzzle\Http\Cookie::__construct */ public function testConstructorSetsDefaults() { $jar = new Cookie(); $this->assertEquals(';', $jar->getFieldSeparator()); $this->assertEquals('=', $jar->getValueSeparator()); $this->assertEquals(false, $jar->isEncodingFields()); $this->assertEquals(false, $jar->isEncodingValues()); $this->assertEquals('', $jar->getPrefix()); } /** * @covers Guzzle\Http\QueryString::__toString * @dataProvider provider */ public function testConvertsToString($cookieString) { $cookie = Cookie::factory($cookieString); $this->assertEquals($cookieString, (string) $cookie); } /** * @covers Guzzle\Http\QueryString * @covers Guzzle\Http\Cookie */ public function testAggregatesMultipleCookieValues() { $cookie = Cookie::factory('name=a;name=b'); $this->assertEquals(array('a', 'b'), $cookie->get('name')); $this->assertEquals('name=a;name=b', (string) $cookie); } }tests/Guzzle/Tests/Http/Curl/CurlExceptionTest.php000064400000001001151742733540016254 0ustar00 */ class CurlExceptionTest extends \Guzzle\Tests\GuzzleTestCase { /** * @covers Guzzle\Http\Curl\CurlException */ public function testStoresCurlError() { $e = new CurlException(); $this->assertNull($e->getCurlError()); $this->assertSame($e, $e->setCurlError('test')); $this->assertEquals('test', $e->getCurlError()); } }tests/Guzzle/Tests/Http/Curl/CurlFactoryTest.php000064400000074551151742733540015751 0ustar00 * @covers Guzzle\Http\Curl\CurlFactory */ class CurlFactoryTest extends \Guzzle\Tests\GuzzleTestCase { /** * @var cURL options array */ public static $options = array( 'CURLOPT_AUTOREFERER' => CURLOPT_AUTOREFERER, 'CURLOPT_BINARYTRANSFER' => CURLOPT_BINARYTRANSFER, 'CURLOPT_COOKIESESSION' => CURLOPT_COOKIESESSION, 'CURLOPT_CRLF' => CURLOPT_CRLF, 'CURLOPT_DNS_USE_GLOBAL_CACHE' => CURLOPT_DNS_USE_GLOBAL_CACHE, 'CURLOPT_FAILONERROR' => CURLOPT_FAILONERROR, 'CURLOPT_FILETIME' => CURLOPT_FILETIME, 'CURLOPT_FOLLOWLOCATION' => CURLOPT_FOLLOWLOCATION, 'CURLOPT_FORBID_REUSE' => CURLOPT_FORBID_REUSE, 'CURLOPT_FRESH_CONNECT' => CURLOPT_FRESH_CONNECT, 'CURLOPT_FTP_USE_EPRT' => CURLOPT_FTP_USE_EPRT, 'CURLOPT_FTP_USE_EPSV' => CURLOPT_FTP_USE_EPSV, 'CURLOPT_FTPAPPEND' => CURLOPT_FTPAPPEND, 'CURLOPT_FTPLISTONLY' => CURLOPT_FTPLISTONLY, 'CURLOPT_HEADER' => CURLOPT_HEADER, 'CURLINFO_HEADER_OUT' => CURLINFO_HEADER_OUT, 'CURLOPT_HTTPGET' => CURLOPT_HTTPGET, 'CURLOPT_HTTPPROXYTUNNEL' => CURLOPT_HTTPPROXYTUNNEL, 'CURLOPT_NETRC' => CURLOPT_NETRC, 'CURLOPT_NOBODY' => CURLOPT_NOBODY, 'CURLOPT_NOPROGRESS' => CURLOPT_NOPROGRESS, 'CURLOPT_NOSIGNAL' => CURLOPT_NOSIGNAL, 'CURLOPT_POST' => CURLOPT_POST, 'CURLOPT_PUT' => CURLOPT_PUT, 'CURLOPT_RETURNTRANSFER' => CURLOPT_RETURNTRANSFER, 'CURLOPT_SSL_VERIFYPEER' => CURLOPT_SSL_VERIFYPEER, 'CURLOPT_TRANSFERTEXT' => CURLOPT_TRANSFERTEXT, 'CURLOPT_UNRESTRICTED_AUTH' => CURLOPT_UNRESTRICTED_AUTH, 'CURLOPT_UPLOAD' => CURLOPT_UPLOAD, 'CURLOPT_VERBOSE' => CURLOPT_VERBOSE, 'CURLOPT_BUFFERSIZE' => CURLOPT_BUFFERSIZE, 'CURLOPT_CLOSEPOLICY' => CURLOPT_CLOSEPOLICY, 'CURLOPT_CONNECTTIMEOUT' => CURLOPT_CONNECTTIMEOUT, // 'CURLOPT_CONNECTTIMEOUT_MS' => CURLOPT_CONNECTTIMEOUT_MS, 'CURLOPT_DNS_CACHE_TIMEOUT' => CURLOPT_DNS_CACHE_TIMEOUT, 'CURLOPT_FTPSSLAUTH' => CURLOPT_FTPSSLAUTH, 'CURLOPT_HTTP_VERSION' => CURLOPT_HTTP_VERSION, 'CURLOPT_HTTPAUTH' => CURLOPT_HTTPAUTH, 'CURLAUTH_ANY' => CURLAUTH_ANY, 'CURLAUTH_ANYSAFE' => CURLAUTH_ANYSAFE, 'CURLOPT_INFILESIZE' => CURLOPT_INFILESIZE, 'CURLOPT_LOW_SPEED_LIMIT' => CURLOPT_LOW_SPEED_LIMIT, 'CURLOPT_LOW_SPEED_TIME' => CURLOPT_LOW_SPEED_TIME, 'CURLOPT_MAXCONNECTS' => CURLOPT_MAXCONNECTS, 'CURLOPT_MAXREDIRS' => CURLOPT_MAXREDIRS, 'CURLOPT_PORT' => CURLOPT_PORT, // 'CURLOPT_PROTOCOLS' => CURLOPT_PROTOCOLS, 'CURLOPT_PROXYAUTH' => CURLOPT_PROXYAUTH, 'CURLOPT_PROXYPORT' => CURLOPT_PROXYPORT, 'CURLOPT_PROXYTYPE' => CURLOPT_PROXYTYPE, // 'CURLOPT_REDIR_PROTOCOLS' => CURLOPT_REDIR_PROTOCOLS, 'CURLOPT_RESUME_FROM' => CURLOPT_RESUME_FROM, 'CURLOPT_SSL_VERIFYHOST' => CURLOPT_SSL_VERIFYHOST, 'CURLOPT_SSLVERSION' => CURLOPT_SSLVERSION, 'CURLOPT_TIMECONDITION' => CURLOPT_TIMECONDITION, 'CURLOPT_TIMEOUT' => CURLOPT_TIMEOUT, // 'CURLOPT_TIMEOUT_MS' => CURLOPT_TIMEOUT_MS, 'CURLOPT_TIMEVALUE' => CURLOPT_TIMEVALUE, 'CURLOPT_CAINFO' => CURLOPT_CAINFO, 'CURLOPT_CAPATH' => CURLOPT_CAPATH, 'CURLOPT_COOKIE' => CURLOPT_COOKIE, 'CURLOPT_COOKIEFILE' => CURLOPT_COOKIEFILE, 'CURLOPT_COOKIEJAR' => CURLOPT_COOKIEJAR, 'CURLOPT_CUSTOMREQUEST' => CURLOPT_CUSTOMREQUEST, 'CURLOPT_EGDSOCKET' => CURLOPT_EGDSOCKET, 'CURLOPT_ENCODING' => CURLOPT_ENCODING, 'CURLOPT_FTPPORT' => CURLOPT_FTPPORT, 'CURLOPT_INTERFACE' => CURLOPT_INTERFACE, 'CURLOPT_KRB4LEVEL' => CURLOPT_KRB4LEVEL, 'CURLOPT_POSTFIELDS' => CURLOPT_POSTFIELDS, 'CURLOPT_PROXY' => CURLOPT_PROXY, 'CURLOPT_PROXYUSERPWD' => CURLOPT_PROXYUSERPWD, 'CURLOPT_RANDOM_FILE' => CURLOPT_RANDOM_FILE, 'CURLOPT_RANGE' => CURLOPT_RANGE, 'CURLOPT_REFERER' => CURLOPT_REFERER, 'CURLOPT_SSL_CIPHER_LIST' => CURLOPT_SSL_CIPHER_LIST, 'CURLOPT_SSLCERT' => CURLOPT_SSLCERT, 'CURLOPT_SSLCERTPASSWD' => CURLOPT_SSLCERTPASSWD, 'CURLOPT_SSLCERTTYPE' => CURLOPT_SSLCERTTYPE, 'CURLOPT_SSLENGINE' => CURLOPT_SSLENGINE, 'CURLOPT_SSLENGINE_DEFAULT' => CURLOPT_SSLENGINE_DEFAULT, 'CURLOPT_SSLKEY' => CURLOPT_SSLKEY, 'CURLOPT_SSLKEYPASSWD' => CURLOPT_SSLKEYPASSWD, 'CURLOPT_SSLKEYTYPE' => CURLOPT_SSLKEYTYPE, 'CURLOPT_URL' => CURLOPT_URL, 'CURLOPT_USERAGENT' => CURLOPT_USERAGENT, 'CURLOPT_USERPWD' => CURLOPT_USERPWD, 'CURLOPT_HTTP200ALIASES' => CURLOPT_HTTP200ALIASES, 'CURLOPT_HTTPHEADER' => CURLOPT_HTTPHEADER, 'CURLOPT_POSTQUOTE' => CURLOPT_POSTQUOTE, 'CURLOPT_QUOTE' => CURLOPT_QUOTE, 'CURLOPT_FILE' => CURLOPT_FILE, 'CURLOPT_INFILE' => CURLOPT_INFILE, 'CURLOPT_STDERR' => CURLOPT_STDERR, 'CURLOPT_WRITEHEADER' => CURLOPT_WRITEHEADER, 'CURLOPT_HEADERFUNCTION' => CURLOPT_HEADERFUNCTION, 'CURLOPT_PROGRESSFUNCTION' => CURLOPT_PROGRESSFUNCTION, 'CURLOPT_READFUNCTION' => CURLOPT_READFUNCTION, 'CURLOPT_WRITEFUNCTION' => CURLOPT_WRITEFUNCTION ); /** * @var array cURL option values */ public static $values = array ( 'CURLAUTH_BASIC' => CURLAUTH_BASIC, 'CURLAUTH_DIGEST' => CURLAUTH_DIGEST, 'CURLAUTH_GSSNEGOTIATE' => CURLAUTH_GSSNEGOTIATE, 'CURLAUTH_NTLM' => CURLAUTH_NTLM, 'CURLCLOSEPOLICY_CALLBACK' => CURLCLOSEPOLICY_CALLBACK, 'CURLCLOSEPOLICY_LEAST_RECENTLY_USED' => CURLCLOSEPOLICY_LEAST_RECENTLY_USED, 'CURLCLOSEPOLICY_LEAST_TRAFFIC' => CURLCLOSEPOLICY_LEAST_TRAFFIC, 'CURLCLOSEPOLICY_OLDEST' => CURLCLOSEPOLICY_OLDEST, 'CURLCLOSEPOLICY_SLOWEST' => CURLCLOSEPOLICY_SLOWEST, 'CURLFTPAUTH_DEFAULT' => CURLFTPAUTH_DEFAULT, 'CURLFTPAUTH_SSL' => CURLFTPAUTH_SSL, 'CURLFTPAUTH_TLS' => CURLFTPAUTH_TLS, 'CURLFTPSSL_ALL' => CURLFTPSSL_ALL, 'CURLFTPSSL_CONTROL' => CURLFTPSSL_CONTROL, 'CURLFTPSSL_NONE' => CURLFTPSSL_NONE, 'CURLFTPSSL_TRY' => CURLFTPSSL_TRY, 'CURLINFO_FILETIME' => CURLINFO_FILETIME, 'CURLINFO_HEADER_OUT' => CURLINFO_HEADER_OUT, 'CURLOPT_FTP_CREATE_MISSING_DIRS' => CURLOPT_FTP_CREATE_MISSING_DIRS, 'CURLOPT_FTP_SSL' => CURLOPT_FTP_SSL, 'CURLOPT_PRIVATE' => CURLOPT_PRIVATE, 'CURLOPT_READDATA' => CURLOPT_READDATA, 'CURLOPT_TCP_NODELAY' => CURLOPT_TCP_NODELAY, 'CURLPROXY_HTTP' => CURLPROXY_HTTP, 'CURLPROXY_SOCKS5' => CURLPROXY_SOCKS5, 'CURL_HTTP_VERSION_1_0' => CURL_HTTP_VERSION_1_0, 'CURL_HTTP_VERSION_1_1' => CURL_HTTP_VERSION_1_1, 'CURL_HTTP_VERSION_NONE' => CURL_HTTP_VERSION_NONE, 'CURL_NETRC_IGNORED' => CURL_NETRC_IGNORED, 'CURL_NETRC_OPTIONAL' => CURL_NETRC_OPTIONAL, 'CURL_NETRC_REQUIRED' => CURL_NETRC_REQUIRED, 'CURL_TIMECOND_IFMODSINCE' => CURL_TIMECOND_IFMODSINCE, 'CURL_TIMECOND_IFUNMODSINCE' => CURL_TIMECOND_IFUNMODSINCE, 'CURL_TIMECOND_LASTMOD' => CURL_TIMECOND_LASTMOD ); public static $dontConvert = array( 'CURLOPT_MAXREDIRS', 'CURLOPT_CONNECTTIMEOUT', 'CURLOPT_FILETIME', 'CURLOPT_SSL_VERIFYPEER', 'CURLOPT_SSL_VERIFYHOST', 'CURLOPT_RETURNTRANSFER', 'CURLOPT_HTTPHEADER', 'CURLOPT_HEADER', 'CURLOPT_FOLLOWLOCATION', 'CURLOPT_MAXREDIRS', 'CURLOPT_CONNECTTIMEOUT', 'CURLOPT_USERAGENT', 'CURLOPT_NOPROGRESS', 'CURLOPT_BUFFERSIZE', 'CURLOPT_PORT' ); /** * Convert cURL option and value integers into a readable array * * @param array $options * * @return array */ public static function getReadableCurlOptions(array $options) { $readable = array(); foreach ($options as $key => $value) { $readableKey = $key; $readableValue = $value; // Convert the key foreach (self::$options as $ok => $ov) { if ($ov === $key) { $readableKey = $ok; break; } } if (!in_array($readableKey, self::$dontConvert)) { foreach (self::$values as $k => $v) { if ($value == 1 && $readableKey != 'CURLOPT_HTTPAUTH') { $readableValue = true; } else if ($v && $v === $value) { $readableValue = $k; break; } else if (is_array($value) || $value instanceof \Closure) { $readableValue = 'callback'; } } } $readable[$readableKey] = $readableValue; } return $readable; } public function dataProvider() { $postBody = new QueryString(array( 'file' => '@' . __DIR__ . '/../../../../../phpunit.xml' )); $qs = new QueryString(array( 'x' => 'y', 'z' => 'a' )); $userAgent = Guzzle::getDefaultUserAgent(); $auth = base64_encode('michael:123'); return array( array('GET', 'http://www.google.com/', null, null, array( CURLOPT_RETURNTRANSFER => 0, CURLOPT_HEADER => 0, CURLOPT_FOLLOWLOCATION => 1, CURLOPT_MAXREDIRS => 5, CURLOPT_CONNECTTIMEOUT => 120, CURLOPT_USERAGENT => $userAgent, CURLOPT_WRITEFUNCTION => 'callback', CURLOPT_HEADERFUNCTION => 'callback', CURLOPT_PROGRESSFUNCTION => 'callback', CURLOPT_NOPROGRESS => 0, CURLOPT_ENCODING => '', CURLOPT_HTTPHEADER => array('User-Agent: ' . $userAgent, 'Host: www.google.com'), )), // Test that custom request methods can be used array('TRACE', 'http://www.google.com/', null, null, array( CURLOPT_CUSTOMREQUEST => 'TRACE' )), array('GET', 'http://127.0.0.1:8080', null, null, array( CURLOPT_RETURNTRANSFER => 0, CURLOPT_HEADER => 0, CURLOPT_FOLLOWLOCATION => 1, CURLOPT_MAXREDIRS => 5, CURLOPT_CONNECTTIMEOUT => 120, CURLOPT_USERAGENT => $userAgent, CURLOPT_WRITEFUNCTION => 'callback', CURLOPT_HEADERFUNCTION => 'callback', CURLOPT_PROGRESSFUNCTION => 'callback', CURLOPT_NOPROGRESS => 0, CURLOPT_ENCODING => '', CURLOPT_PORT => 8080, CURLOPT_HTTPHEADER => array('User-Agent: ' . $userAgent, 'Host: 127.0.0.1:8080'), )), array('HEAD', 'http://www.google.com/', null, null, array( CURLOPT_RETURNTRANSFER => 0, CURLOPT_HEADER => 0, CURLOPT_FOLLOWLOCATION => 1, CURLOPT_MAXREDIRS => 5, CURLOPT_CONNECTTIMEOUT => 120, CURLOPT_USERAGENT => $userAgent, CURLOPT_WRITEFUNCTION => 'callback', CURLOPT_HEADERFUNCTION => 'callback', CURLOPT_PROGRESSFUNCTION => 'callback', CURLOPT_NOPROGRESS => 0, CURLOPT_ENCODING => '', CURLOPT_HTTPHEADER => array('User-Agent: ' . $userAgent, 'Host: www.google.com'), CURLOPT_CUSTOMREQUEST => 'HEAD', CURLOPT_NOBODY => 1 )), array('GET', 'https://michael:123@www.guzzle-project.com/index.html?q=2', null, null, array( CURLOPT_RETURNTRANSFER => 0, CURLOPT_HEADER => 0, CURLOPT_FOLLOWLOCATION => 1, CURLOPT_MAXREDIRS => 5, CURLOPT_CONNECTTIMEOUT => 120, CURLOPT_USERAGENT => $userAgent, CURLOPT_WRITEFUNCTION => 'callback', CURLOPT_HEADERFUNCTION => 'callback', CURLOPT_PROGRESSFUNCTION => 'callback', CURLOPT_NOPROGRESS => 0, CURLOPT_ENCODING => '', CURLOPT_HTTPHEADER => array('User-Agent: ' . $userAgent, 'Host: www.guzzle-project.com', 'Authorization: Basic ' . $auth), CURLOPT_PORT => 443 )), array('GET', 'http://www.guzzle-project.com:8080/', array( 'X-Test-Data' => 'Guzzle' ), null, array( CURLOPT_RETURNTRANSFER => 0, CURLOPT_HEADER => 0, CURLOPT_FOLLOWLOCATION => 1, CURLOPT_MAXREDIRS => 5, CURLOPT_CONNECTTIMEOUT => 120, CURLOPT_USERAGENT => $userAgent, CURLOPT_WRITEFUNCTION => 'callback', CURLOPT_HEADERFUNCTION => 'callback', CURLOPT_PROGRESSFUNCTION => 'callback', CURLOPT_NOPROGRESS => 0, CURLOPT_ENCODING => '', CURLOPT_HTTPHEADER => array('X-Test-Data: Guzzle', 'User-Agent: ' . $userAgent, 'Host: www.guzzle-project.com:8080'), CURLOPT_PORT => 8080 )), array('POST', 'http://www.guzzle-project.com/post.php', null, $qs, array( CURLOPT_RETURNTRANSFER => 0, CURLOPT_HEADER => 0, CURLOPT_FOLLOWLOCATION => 1, CURLOPT_MAXREDIRS => 5, CURLOPT_CONNECTTIMEOUT => 120, CURLOPT_USERAGENT => $userAgent, CURLOPT_WRITEFUNCTION => 'callback', CURLOPT_HEADERFUNCTION => 'callback', CURLOPT_PROGRESSFUNCTION => 'callback', CURLOPT_NOPROGRESS => 0, CURLOPT_ENCODING => '', CURLOPT_POSTFIELDS => 'x=y&z=a', CURLOPT_HTTPHEADER => array ( 'User-Agent: ' . $userAgent, 'Host: www.guzzle-project.com', 'Content-Type: application/x-www-form-urlencoded' ) )), array('PUT', 'http://www.guzzle-project.com/put.php', null, EntityBody::factory(fopen(__DIR__ . '/../../../../../phpunit.xml', 'r+')), array( CURLOPT_RETURNTRANSFER => 0, CURLOPT_HEADER => 0, CURLOPT_FOLLOWLOCATION => 1, CURLOPT_MAXREDIRS => 5, CURLOPT_CONNECTTIMEOUT => 120, CURLOPT_USERAGENT => $userAgent, CURLOPT_WRITEFUNCTION => 'callback', CURLOPT_HEADERFUNCTION => 'callback', CURLOPT_READFUNCTION => 'callback', CURLOPT_PROGRESSFUNCTION => 'callback', CURLOPT_NOPROGRESS => 0, CURLOPT_ENCODING => '', CURLOPT_INFILESIZE => filesize(__DIR__ . '/../../../../../phpunit.xml'), CURLOPT_HTTPHEADER => array ( 'User-Agent: ' . $userAgent, 'Host: www.guzzle-project.com', 'Expect: 100-Continue', 'Content-Type: ' ) )), array('POST', 'http://www.guzzle-project.com/post.php', null, array( 'a' => '2' ), array( CURLOPT_RETURNTRANSFER => 0, CURLOPT_HEADER => 0, CURLOPT_FOLLOWLOCATION => 1, CURLOPT_MAXREDIRS => 5, CURLOPT_CONNECTTIMEOUT => 120, CURLOPT_USERAGENT => $userAgent, CURLOPT_WRITEFUNCTION => 'callback', CURLOPT_HEADERFUNCTION => 'callback', CURLOPT_PROGRESSFUNCTION => 'callback', CURLOPT_NOPROGRESS => 0, CURLOPT_ENCODING => '', CURLOPT_POST => 1, CURLOPT_POSTFIELDS => 'a=2', CURLOPT_HTTPHEADER => array ( 'User-Agent: ' . $userAgent, 'Host: www.guzzle-project.com', 'Content-Type: application/x-www-form-urlencoded' ) )), array('POST', 'http://www.guzzle-project.com/post.php', null, array( 'x' => 'y' ), array( CURLOPT_RETURNTRANSFER => 0, CURLOPT_HEADER => 0, CURLOPT_FOLLOWLOCATION => 1, CURLOPT_MAXREDIRS => 5, CURLOPT_CONNECTTIMEOUT => 120, CURLOPT_USERAGENT => $userAgent, CURLOPT_WRITEFUNCTION => 'callback', CURLOPT_HEADERFUNCTION => 'callback', CURLOPT_PROGRESSFUNCTION => 'callback', CURLOPT_NOPROGRESS => 0, CURLOPT_ENCODING => '', CURLOPT_POST => 1, CURLOPT_POSTFIELDS => 'x=y', CURLOPT_HTTPHEADER => array ( 'User-Agent: ' . $userAgent, 'Host: www.guzzle-project.com', 'Content-Type: application/x-www-form-urlencoded' ) )), array('POST', 'http://www.guzzle-project.com/post.php', null, $postBody, array( CURLOPT_RETURNTRANSFER => 0, CURLOPT_HEADER => 0, CURLOPT_FOLLOWLOCATION => 1, CURLOPT_MAXREDIRS => 5, CURLOPT_CONNECTTIMEOUT => 120, CURLOPT_USERAGENT => $userAgent, CURLOPT_WRITEFUNCTION => 'callback', CURLOPT_HEADERFUNCTION => 'callback', CURLOPT_PROGRESSFUNCTION => 'callback', CURLOPT_NOPROGRESS => 0, CURLOPT_ENCODING => '', CURLOPT_POST => 1, CURLOPT_POSTFIELDS => array( 'file' => '@' . __DIR__ . '/../../../../../phpunit.xml' ), CURLOPT_HTTPHEADER => array ( 'User-Agent: ' . $userAgent, 'Host: www.guzzle-project.com', 'Content-Type: multipart/form-data' ) )), ); } public function setUp() { CurlFactory::getInstance()->releaseAllHandles(true); } /** * @covers Guzzle\Http\Curl\CurlFactory::releaseAllHandles */ public function testReleasesAllHandles() { $f = CurlFactory::getInstance(); $f->releaseAllHandles(true); $request = RequestFactory::head($this->getServer()->getUrl()); $request->getCurlHandle(); $this->assertTrue($f->getConnectionsPerHost(true, '127.0.0.1:8124') > 0); $f->releaseAllHandles(true); $this->assertEquals(0, $f->getConnectionsPerHost(false, '127.0.0.1:8124')); } /** * @covers Guzzle\Http\Curl\CurlFactory * @dataProvider dataProvider */ public function testFactoryCreatesCurlResourceBasedOnRequest($method, $url, $headers, $body, $options) { $request = RequestFactory::create($method, $url, $headers, $body); $handle = $request->getCurlHandle(); $this->assertInstanceOf('Guzzle\\Http\\Curl\\CurlHandle', $handle); $o = $request->getCurlOptions()->getAll(); foreach ($options as $key => $value) { $this->assertArrayHasKey($key, $o); if ($key != CURLOPT_HTTPHEADER && $key != CURLOPT_POSTFIELDS && (is_array($o[$key])) || $o[$key] instanceof \Closure) { $this->assertEquals('callback', $value); } else { $this->assertTrue($value == $o[$key]); } } $request->releaseCurlHandle(); unset($request); } /** * @covers Guzzle\Http\Curl\CurlFactory */ public function testFactoryUsesSpecifiedProtocol() { $request = RequestFactory::get('http://www.guzzle-project.com/'); $request->setProtocolVersion('1.1'); $handle = CurlFactory::getInstance()->getHandle($request); $this->assertEquals(CURL_HTTP_VERSION_1_1, $request->getCurlOptions()->get(CURLOPT_HTTP_VERSION)); $request->releaseCurlHandle(); unset($request); } /** * Tests that a handle can be used for auth requests and non-auth requests * without mucking up sending credentials when it shouldn't * * @covers Guzzle\Http\Curl\CurlFactory */ public function testFactoryCanReuseAuthHandles() { $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" )); $host = Url::factory($this->getServer()->getUrl()); $host = $host->getHost() . ':' . $host->getPort(); $request = RequestFactory::get($this->getServer()->getUrl()); $h1 = $request->getCurlHandle(); $request->send(); $this->assertFalse($this->compareHttpHeaders(array( 'Accept' => '*/*', 'Accept-Encoding' => 'deflate, gzip', 'User-Agent' => Guzzle::getDefaultUserAgent(), 'Host' => $host ), $request->getHeaders()->getAll())); $request->setState('new'); $request->setAuth('michael', 'test'); $h2 = $request->getCurlHandle(); $request->send(); $this->assertFalse($this->compareHttpHeaders(array( 'Accept-Encoding' => 'deflate, gzip', 'Accept' => '*/*', 'User-Agent' => Guzzle::getDefaultUserAgent(), 'Host' => $host, 'Authorization' => 'Basic bWljaGFlbDp0ZXN0' ), $request->getHeaders()->getAll())); $request->setState('new'); $request->setAuth(false); $h3 = $request->getCurlHandle(); $request->send(); $this->assertFalse($this->compareHttpHeaders(array( 'Accept-Encoding' => 'deflate, gzip', 'Accept' => '*/*', 'User-Agent' => Guzzle::getDefaultUserAgent(), 'Host' => $host ), $request->getHeaders()->getAll())); $this->assertSame($h1, $h2); $this->assertSame($h1, $h3); } /** * @covers Guzzle\Http\Curl\CurlFactory */ public function testUploadsDataUsingCurlAndCanReuseHandleAfterUpload() { $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi", // PUT response "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\n", // HEAD response "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi" // POST response )); $host = Url::factory($this->getServer()->getUrl()); $host = $host->getHost() . ':' . $host->getPort(); $o = new MockObserver(); $request = RequestFactory::put($this->getServer()->getUrl()); $request->setBody(EntityBody::factory('test')); $request->getEventManager()->attach($o); $h1 = $request->getCurlHandle(); $request->send(); // Make sure that the events were dispatched $this->assertArrayHasKey('curl.callback.read', $o->logByEvent); $this->assertArrayHasKey('curl.callback.write', $o->logByEvent); $this->assertArrayHasKey('curl.callback.progress', $o->logByEvent); // Make sure that the data was sent through the event $this->assertEquals('test', $o->logByEvent['curl.callback.read']); $this->assertEquals('hi', $o->logByEvent['curl.callback.write']); // Ensure that the request was received exactly as intended $r = $this->getServer()->getReceivedRequests(true); $this->assertEquals((string) $request, (string) $r[0]); // Create a new request and try to reuse the connection $request = RequestFactory::head($this->getServer()->getUrl()); $this->assertSame($h1, $request->getCurlHandle()); $request->send(); // Make sure that the request was sent correctly $r = $this->getServer()->getReceivedRequests(true); $this->assertEquals((string) $request, (string) $r[1]); // Create a new request using the same connection and POST $request = RequestFactory::post($this->getServer()->getUrl()); $request->addPostFields(array( 'a' => 'b', 'c' => 'ay! ~This is a test, isn\'t it?' )); $this->assertSame($h1, $request->getCurlHandle()); $request->send(); // Make sure that the request was sent correctly $r = $this->getServer()->getReceivedRequests(true); $this->assertEquals((string) $request, (string) $r[2]); } /** * @covers Guzzle\Http\Curl\CurlFactory * @covers Guzzle\Http\Curl\CurlFactory::getConnectionsPerHost * @depends testReleasesAllHandles */ public function testClosesHandlesWhenHandlesAreReleasedAndNeedToBeClosed() { $f = CurlFactory::getInstance(); $baseline = $f->getConnectionsPerHost(true, '127.0.0.1:8124'); $request1 = RequestFactory::get($this->getServer()->getUrl()); $request1->getCurlHandle(); $request2 = RequestFactory::get($this->getServer()->getUrl()); $request2->getCurlHandle(); // Make sure tha allocated count went up $current = $f->getConnectionsPerHost(true, '127.0.0.1:8124'); $this->assertEquals($baseline + 2, $current); // Release the handles so they are unallocated and cleaned back to 2 $request1->releaseCurlHandle(); $request2->releaseCurlHandle(); $current = $f->getConnectionsPerHost(true, '127.0.0.1:8124'); $this->assertEquals($baseline, $current); $current = $f->getConnectionsPerHost(false, '127.0.0.1:8124'); $this->assertEquals(2, $current); $this->assertSame($f, $f->setMaxIdleForHost('127.0.0.1:8124', 1)); $this->assertSame($f, $f->clean()); $current = $f->getConnectionsPerHost(null, '127.0.0.1:8124'); $this->assertEquals(1, $current); // Purge all unalloacted connections $f->clean(true); $this->assertEquals(array(), $f->getConnectionsPerHost(false)); $request = RequestFactory::head($this->getServer()->getUrl()); $handle1 = $request->getCurlHandle(); $this->assertEquals(1, $f->getConnectionsPerHost(true, '127.0.0.1:8124')); $f->releaseHandle($handle1); $this->assertEquals(0, $f->getConnectionsPerHost(true, '127.0.0.1:8124')); $this->assertEquals(1, $f->getConnectionsPerHost(false, '127.0.0.1:8124')); // Relase and force close $f->releaseHandle($handle1, true); // Make sure that the handle was closed $this->assertEquals(0, $f->getConnectionsPerHost(null, '127.0.0.1:8124')); $request = RequestFactory::head($this->getServer()->getUrl()); $handle2 = $request->getCurlHandle(); $this->assertNotSame($handle1, $handle2); $this->assertEquals(1, $f->getConnectionsPerHost(true, '127.0.0.1:8124')); curl_close($handle2->getHandle()); $f->releaseHandle($handle2); $this->assertEquals(0, $f->getConnectionsPerHost(null, '127.0.0.1:8124')); } /** * @covers Guzzle\Http\Curl\CurlFactory::setMaxIdleTime * @covers Guzzle\Http\Curl\CurlFactory::clean * @covers Guzzle\Http\Curl\CurlFactory::getConnectionsPerHost * @depends testReleasesAllHandles */ public function testPurgesConnectionsThatAreTooStaleBasedOnMaxIdleTime() { $f = CurlFactory::getInstance(); $this->assertSame($f, $f->setMaxIdleTime(0)); $request = RequestFactory::head($this->getServer()->getUrl()); $request->getCurlHandle(); $this->assertEquals(1, $f->getConnectionsPerHost(true, '127.0.0.1:8124')); $this->assertEquals(0, $f->getConnectionsPerHost(false, '127.0.0.1:8124')); // By releasing the handle, the factory should clean up the handle // because of the max idle time $request->releaseCurlHandle(); $this->assertEquals(0, $f->getConnectionsPerHost(false, '127.0.0.1:8124')); // Set the default max idle time $f->setMaxIdleTime(-1); } /** * @covers Guzzle\Http\Curl\CurlFactory::setMaxConnectionReusesForHost * @covers Guzzle\Http\Curl\CurlFactory::clean * @covers Guzzle\Http\Curl\CurlFactory::createHandle */ public function testClosesConnectionsThatHaveExceededMaxConnectionReuse() { $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", )); $f = CurlFactory::getInstance(); $f->releaseAllHandles(true); $f->setMaxConnectionReusesForHost('127.0.0.1:8124', 1); $request = RequestFactory::get($this->getServer()->getUrl()); $h = $request->getCurlHandle(); $curlHandle = $h->getHandle(); $this->assertEquals(0, $h->getUseCount()); $request->send(); $this->assertEquals(1, $h->getUseCount()); $this->assertSame($curlHandle, $h->getHandle()); $this->assertEquals(1, $f->getConnectionsPerHost(null, '127.0.0.1:8124')); // Send the request again $request->send(); // The connection has now been released $this->assertEquals(0, $f->getConnectionsPerHost(null, '127.0.0.1:8124')); $this->assertEquals(0, $h->getUseCount()); $this->assertNotSame($curlHandle, $h->getHandle()); } /** * @covers Guzzle\Http\Curl\CurlFactory::getConnectionsPerHost */ public function testSkipsHostsThatDoNotMatch() { $f = CurlFactory::getInstance(); $f->releaseAllHandles(true); $request1 = RequestFactory::get('http://www.yahoo.com/'); $request1->getCurlHandle(); $request2 = RequestFactory::get('http://www.google.com/'); $request2->getCurlHandle(); $this->assertEquals(1, $f->getConnectionsPerHost(null, 'www.yahoo.com:80')); $this->assertEquals(1, $f->getConnectionsPerHost(null, 'www.google.com:80')); $this->assertEquals(0, $f->getConnectionsPerHost(null, 'foo.com:80')); $f->releaseAllHandles(true); } }tests/Guzzle/Tests/Http/Curl/CurlHandleTest.php000064400000025601151742733540015525 0ustar00 */ class CurlHandleTest extends \Guzzle\Tests\GuzzleTestCase { /** * @covers Guzzle\Http\Curl\CurlHandle * @expectedException InvalidArgumentException */ public function testConstructorExpectsCurlResource() { $h = new CurlHandle(false, array()); } /** * @covers Guzzle\Http\Curl\CurlHandle */ public function testConstructorExpectsProperOptions() { $h = curl_init($this->getServer()->getUrl()); try { $ha = new CurlHandle($h, false); $this->fail('Expected InvalidArgumentException'); } catch (\InvalidArgumentException $e) { } $ha = new CurlHandle($h, array( CURLOPT_URL => $this->getServer()->getUrl() )); $this->assertEquals($this->getServer()->getUrl(), $ha->getOption(CURLOPT_URL)); $ha = new CurlHandle($h, new Collection(array( CURLOPT_URL => $this->getServer()->getUrl() ))); $this->assertEquals($this->getServer()->getUrl(), $ha->getOption(CURLOPT_URL)); } /** * @covers Guzzle\Http\Curl\CurlHandle::__construct * @covers Guzzle\Http\Curl\CurlHandle::getStderr * @covers Guzzle\Http\Curl\CurlHandle::getHandle * @covers Guzzle\Http\Curl\CurlHandle::getUrl * @covers Guzzle\Http\Curl\CurlHandle::getOptions * @covers Guzzle\Http\Curl\CurlHandle::getOption * @covers Guzzle\Http\Curl\CurlHandle::getIdleTime * @covers Guzzle\Http\Curl\CurlHandle::getOwner */ public function testConstructorInitializesObject() { $handle = curl_init($this->getServer()->getUrl()); $h = new CurlHandle($handle, array( CURLOPT_URL => $this->getServer()->getUrl() )); $this->assertInternalType('resource', $h->getStderr(true)); $this->assertInternalType('string', $h->getStderr(false)); $r = $h->getStderr(true); fwrite($r, 'test'); $this->assertEquals('test', $h->getStderr(false)); $this->assertInstanceOf('Guzzle\\Http\\Url', $h->getUrl()); $this->assertEquals($this->getServer()->getUrl(), (string) $h->getUrl()); $this->assertSame($handle, $h->getHandle()); $this->assertEquals($this->getServer()->getUrl(), $h->getOption(CURLOPT_URL)); $this->assertEquals(array( CURLOPT_VERBOSE => true, CURLOPT_URL => $this->getServer()->getUrl(), CURLOPT_STDERR => $h->getStderr(true) ), $h->getOptions()); $this->assertTrue($h->getIdleTime() == 0 || $h->getIdleTime() == 1); $this->assertNull($h->getOwner()); } /** * @covers Guzzle\Http\Curl\CurlHandle::isAvailable * @covers Guzzle\Http\Curl\CurlHandle::isMyHandle */ public function testDeterminesIfResourceIsAvailable() { $handle = curl_init($this->getServer()->getUrl()); $h = new CurlHandle($handle, array()); $this->assertTrue($h->isAvailable()); $this->assertTrue($h->isMyHandle($handle)); // Mess it up by closing the handle curl_close($handle); $this->assertFalse($h->isAvailable()); $this->assertFalse($h->isMyHandle($handle)); unset($handle); $this->assertFalse($h->isAvailable()); } /** * @covers Guzzle\Http\Curl\CurlHandle::getError * @covers Guzzle\Http\Curl\CurlHandle::getErrorNo * @covers Guzzle\Http\Curl\CurlHandle::getInfo */ public function testWrapsErrorsAndInfo() { if (!defined('CURLOPT_TIMEOUT_MS')) { $this->markTestSkipped('Update curl'); } $handle = curl_init($this->getServer()->getUrl()); curl_setopt($handle, CURLOPT_TIMEOUT_MS, 1); $h = new CurlHandle($handle, array( CURLOPT_TIMEOUT_MS => 1 )); @curl_exec($handle); $this->assertEquals('Timeout was reached', $h->getError()); $this->assertEquals(CURLE_OPERATION_TIMEOUTED, $h->getErrorNo()); $this->assertEquals($this->getServer()->getUrl(), $h->getInfo(CURLINFO_EFFECTIVE_URL)); $this->assertInternalType('array', $h->getInfo()); curl_close($handle); $this->assertEquals(null, $h->getInfo('url')); } /** * @covers Guzzle\Http\Curl\CurlHandle::setOption * @covers Guzzle\Http\Curl\CurlHandle::setOptions * @covers Guzzle\Http\Curl\CurlHandle::getOption */ public function testWrapsSettingOptions() { $handle = curl_init($this->getServer()->getUrl()); $h = new CurlHandle($handle, array()); $this->assertSame($h, $h->setOption(CURLOPT_AUTOREFERER, true)); $this->assertSame($h, $h->setOption('CURLOPT_BUFFERSIZE', 1024)); $this->assertEquals(true, $h->getOption(CURLOPT_AUTOREFERER)); $this->assertEquals(1024, $h->getOption('CURLOPT_BUFFERSIZE')); $this->assertEquals(1024, $h->getOption(CURLOPT_BUFFERSIZE)); $handle = curl_init($this->getServer()->getUrl()); $h = new CurlHandle($handle, array()); $this->assertSame($h, $h->setOptions(array( CURLOPT_AUTOREFERER => true, 'CURLOPT_BUFFERSIZE' => 1024 ))); $this->assertEquals(true, $h->getOption(CURLOPT_AUTOREFERER)); $this->assertEquals(1024, $h->getOption('CURLOPT_BUFFERSIZE')); $this->assertEquals(1024, $h->getOption(CURLOPT_BUFFERSIZE)); } /** * @covers Guzzle\Http\Curl\CurlHandle::hasProblematicOption */ public function testTestsForConnectionReuseBasedOnOptions() { $handle = curl_init($this->getServer()->getUrl()); $h = new CurlHandle($handle, array( CURLOPT_URL => $this->getServer()->getUrl() )); $this->assertFalse($h->hasProblematicOption()); $h->setOption(CURLOPT_TIMEOUT, 2); $this->assertTrue($h->hasProblematicOption()); } /** * @covers Guzzle\Http\Curl\CurlHandle::checkout * @covers Guzzle\Http\Curl\CurlHandle::unlock */ public function testCanBeClaimedAndUnclaimed() { $request = RequestFactory::get($this->getServer()->getUrl()); $handle = curl_init($this->getServer()->getUrl()); $h = new CurlHandle($handle, array( CURLOPT_URL => $this->getServer()->getUrl() )); $this->assertSame($h, $h->checkout($request)); $this->assertEquals(0, $h->getIdleTime()); $this->assertSame($request, $h->getOwner()); $h->unlock(); $this->assertTrue($h->isAvailable()); // Unlock with a Connection: close request $request->setHeader('Connection', 'close'); $h->checkout($request); $h->unlock(); $this->assertFalse($h->isAvailable()); // Unlock with a Connection: close response $handle = curl_init($this->getServer()->getUrl()); $h = new CurlHandle($handle, array( CURLOPT_URL => $this->getServer()->getUrl() )); $this->assertTrue($h->isAvailable()); $request->removeHeader('Connection'); $request->setResponse(Response::factory( "HTTP/1.1 200 OK\r\n" . "Connection: close\r\n\r\n" )); $h->checkout($request); $h->unlock(); $this->assertFalse($h->isAvailable()); } /** * @covers Guzzle\Http\Curl\CurlHandle::isCompatible */ public function testChecksIfRequestIsCompatibleForConnectionReuse() { $request = RequestFactory::get($this->getServer()->getUrl()); $handle = curl_init($this->getServer()->getUrl()); $h = new CurlHandle($handle, array( CURLOPT_URL => $this->getServer()->getUrl() )); // Connection reuse match $this->assertTrue($h->isCompatible($request)); // Different ports $request->setPort(8192); $this->assertFalse($h->isCompatible($request)); // Different CURLOPT_PORT $request->setPort(80); $request->getCurlOptions()->set(CURLOPT_PORT, 8192); $this->assertFalse($h->isCompatible($request)); // Different host $request->setPort(80); $request->setHost('google.com'); $this->assertFalse($h->isCompatible($request)); // Different proxy server $request->getCurlOptions()->clear(); $request->setUrl($this->getServer()->getUrl()); $this->assertTrue($h->isCompatible($request)); $request->getCurlOptions()->set(CURLOPT_PROXY, 'tcp://test.com:8080/'); $this->assertFalse($h->isCompatible($request)); // Using the same proxy $h->setOption(CURLOPT_PROXY, 'tcp://test.com:8080/'); $this->assertTrue($h->isCompatible($request)); // It's the ower of the handle $h->checkout($request); $this->assertTrue($h->isCompatible($request)); } /** * @covers Guzzle\Http\Curl\CurlHandle::getUseCount * @covers Guzzle\Http\Curl\CurlHandle::setMaxReuses * @covers Guzzle\Http\Curl\CurlHandle::unlock */ public function testClosesAfterMaxConnectionReusesIsExceeded() { $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", )); CurlFactory::getInstance()->releaseAllHandles(true); $request = RequestFactory::get($this->getServer()->getUrl()); $h = $request->getCurlHandle()->setMaxReuses(2); $curlHandle = $h->getHandle(); $this->assertEquals(0, $h->getUseCount()); $request->send(); $this->assertEquals(1, $h->getUseCount()); $this->assertSame($curlHandle, $h->getHandle()); $request->send(); $this->assertEquals(2, $h->getUseCount()); $this->assertSame($curlHandle, $h->getHandle()); $request->send(); $this->assertEquals(0, $h->getUseCount()); $this->assertNotSame($curlHandle, $h->getHandle()); } /** * @covers Guzzle\Http\Curl\CurlHandle::getUseCount * @covers Guzzle\Http\Curl\CurlHandle::setMaxReuses * @covers Guzzle\Http\Curl\CurlHandle::unlock */ public function testCanCloseAfterOneConnectionReusesIsExceeded() { $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" )); CurlFactory::getInstance()->releaseAllHandles(true); $request = RequestFactory::get($this->getServer()->getUrl()); $h = $request->getCurlHandle()->setMaxReuses(0); $this->assertNotNull($h->getHandle()); $request->send(); $this->assertNull($h->getHandle()); } }tests/Guzzle/Tests/Http/EntityBodyTest.php000064400000013331151742733540014666 0ustar00 */ class EntityBodyTest extends \Guzzle\Tests\GuzzleTestCase { /** * @covers Guzzle\Http\EntityBody::factory * @expectedException Guzzle\Http\HttpException */ public function testFactoryThrowsException() { $body = EntityBody::factory(false); } /** * @covers Guzzle\Http\EntityBody::factory */ public function testFactory() { $body = EntityBody::factory('data'); $this->assertEquals('data', (string)$body); $this->assertEquals(4, $body->getContentLength()); $this->assertEquals('php', $body->getWrapper()); $this->assertEquals('temp', $body->getStreamType()); $handle = fopen(__DIR__ . '/../../../../phpunit.xml', 'r'); if (!$handle) { $this->fail('Could not open test file'); } $body = EntityBody::factory($handle); $this->assertEquals(__DIR__ . '/../../../../phpunit.xml', $body->getUri()); $this->assertTrue($body->isLocal()); $this->assertEquals(__DIR__ . '/../../../../phpunit.xml', $body->getUri()); $this->assertEquals(filesize(__DIR__ . '/../../../../phpunit.xml'), $body->getContentLength()); // make sure that a body will return as the same object $this->assertTrue($body === EntityBody::factory($body)); } /** * @covers Guzzle\Http\EntityBody::factory */ public function testFactoryCreatesTempStreamByDefault() { $body = EntityBody::factory(''); $this->assertEquals('php', $body->getWrapper()); $this->assertEquals('temp', $body->getStreamType()); $body = EntityBody::factory(); $this->assertEquals('php', $body->getWrapper()); $this->assertEquals('temp', $body->getStreamType()); } /** * @covers Guzzle\Http\EntityBody::compress * @covers Guzzle\Http\EntityBody::uncompress * @covers Guzzle\Http\EntityBody::getContentEncoding * @covers Guzzle\Http\EntityBody::setStreamFilterContentEncoding * @covers Guzzle\Http\EntityBody::handleCompression * @covers Guzzle\Http\EntityBody::getContentLength */ public function testHandlesCompression() { $body = EntityBody::factory('testing 123...testing 123'); $this->assertFalse($body->getContentEncoding(), '-> getContentEncoding() must initially return FALSE'); $size = $body->getContentLength(); $body->compress(); $this->assertEquals('gzip', $body->getContentEncoding(), '-> getContentEncoding() must return the correct encoding after compressing'); $this->assertEquals(gzdeflate('testing 123...testing 123'), (string)$body); $this->assertTrue($body->getContentLength() < $size); $this->assertTrue($body->uncompress()); $this->assertEquals('testing 123...testing 123', (string) $body); $this->assertFalse($body->getContentEncoding(), '-> getContentEncoding() must reset to FALSE'); if (in_array('bzip2.*', stream_get_filters())) { $this->assertTrue($body->compress('bzip2.compress')); $this->assertEquals('compress', $body->getContentEncoding(), '-> compress() must set \'compress\' as the Content-Encoding'); } $this->assertFalse($body->compress('non-existent'), '-> compress() must return false when a non-existent stream filter is used'); // Release the body unset($body); // Use gzip compression on the initial content. This will include a // gzip header which will need to be stripped when deflating the stream $body = EntityBody::factory(gzencode('test')); $this->assertSame($body, $body->setStreamFilterContentEncoding('zlib.deflate')); $this->assertTrue($body->uncompress('zlib.inflate')); $this->assertEquals('test', (string)$body); unset($body); // Test using a very long string $largeString = ''; for ($i = 0; $i < 25000; $i++) { $largeString .= chr(rand(33, 126)); } $body = EntityBody::factory($largeString); $this->assertEquals($largeString, (string)$body); $this->assertTrue($body->compress()); $this->assertNotEquals($largeString, (string)$body); $compressed = (string)$body; $this->assertTrue($body->uncompress()); $this->assertEquals($largeString, (string)$body); $this->assertEquals($compressed, gzdeflate($largeString)); $body = EntityBody::factory(fopen(__DIR__ . '/../TestData/compress_test', 'w')); $this->assertFalse($body->compress()); unset($body); unlink(__DIR__ . '/../TestData/compress_test'); } /** * @covers Guzzle\Http\EntityBody::getContentType */ public function testDeterminesContentType() { // Test using a string/temp stream $body = EntityBody::factory('testing 123...testing 123'); $this->assertEquals('application/octet-stream', $body->getContentType()); // Use a local file $body = EntityBody::factory(fopen(__FILE__, 'r')); $this->assertEquals('text/x-php', $body->getContentType()); } /** * @covers Guzzle\Http\EntityBody::getContentMd5 */ public function testCreatesMd5Checksum() { $body = EntityBody::factory('testing 123...testing 123'); $this->assertEquals(md5('testing 123...testing 123'), $body->getContentMd5()); $server = $this->getServer()->enqueue( "HTTP/1.1 200 OK" . "\r\n" . "Content-Length: 3" . "\r\n\r\n" . "abc" ); $body = EntityBody::factory(fopen($this->getServer()->getUrl(), 'r')); $this->assertFalse($body->getContentMd5()); } }tests/Guzzle/Tests/Http/Message/AbstractMessageTest.php000064400000011526151742733540017234 0ustar00 */ class AbstractMessageTest extends \Guzzle\Tests\GuzzleTestCase { /** * @var Request Request object */ private $request; /** * Setup */ public function setUp() { parent::setUp(); $this->request = new Request('GET', 'http://www.guzzle-project.com/'); } /** * @covers Guzzle\Http\Message\AbstractMessage::getParams */ public function testGetParams() { $this->assertInstanceOf('Guzzle\\Common\\Collection', $this->request->getParams()); } /** * @covers Guzzle\Http\Message\AbstractMessage::addHeaders */ public function testAddHeaders() { $this->request->setHeader('A', 'B'); $this->assertEquals($this->request, $this->request->addHeaders(array( 'X-Data' => '123' ))); $this->assertTrue($this->request->hasHeader('X-Data') !== false); $this->assertTrue($this->request->hasHeader('A') !== false); } /** * @covers Guzzle\Http\Message\AbstractMessage::getHeaders */ public function testGetHeader() { $this->request->setHeader('Test', '123'); $this->assertEquals('123', $this->request->getHeader('Test')); } /** * @covers Guzzle\Http\Message\AbstractMessage::getHeaders * @covers Guzzle\Http\Message\AbstractMessage::setHeaders */ public function testGetHeaders() { $this->assertEquals($this->request, $this->request->setHeaders(array( 'a' => 'b', 'c' => 'd' ))); $this->assertEquals(array( 'a' => 'b', 'c' => 'd' ), $this->request->getHeaders()->getAll()); $this->assertEquals(array( 'a' => 'b' ), $this->request->getHeaders(array('a'))->getAll()); } /** * @covers Guzzle\Http\Message\AbstractMessage::hasHeader */ public function testHasHeader() { $this->assertFalse($this->request->hasHeader('Foo')); $this->request->setHeader('Foo', 'Bar'); $this->assertEquals(true, $this->request->hasHeader('Foo')); } /** * @covers Guzzle\Http\Message\AbstractMessage::hasHeader */ public function testHasHeaderSearch() { $this->assertFalse($this->request->hasHeader('Foo')); $this->request->setHeader('Foo', 'Bar'); $this->assertEquals('Foo', $this->request->hasHeader('Foo', 1)); $this->assertEquals('Foo', $this->request->hasHeader('/Foo/', 2)); $this->assertEquals(false, $this->request->hasHeader('bar', 1)); } /** * @covers Guzzle\Http\Message\AbstractMessage::removeHeader * @covers Guzzle\Http\Message\AbstractMessage::setHeader */ public function testRemoveHeader() { $this->request->setHeader('Foo', 'Bar'); $this->assertEquals(true, $this->request->hasHeader('Foo')); $this->request->removeHeader('Foo'); $this->assertFalse($this->request->hasHeader('Foo')); } /** * @covers Guzzle\Http\Message\AbstractMessage */ public function testHoldsCacheControlDirectives() { $r = $this->request; // Set a directive using a header $r->setHeader('Cache-Control', 'max-age=100'); $this->assertEquals(100, $r->getCacheControlDirective('max-age')); // Set a header using the directive and check that the header was updated $this->assertSame($r, $r->addCacheControlDirective('max-age', 80)); $this->assertEquals(80, $r->getCacheControlDirective('max-age')); $this->assertEquals('max-age=80', $r->getHeader('Cache-Control')); // Remove the directive $this->assertEquals($r, $r->removeCacheControlDirective('max-age')); $this->assertEquals('', $r->getHeader('Cache-Control')); $this->assertEquals(null, $r->getCacheControlDirective('max-age')); // Remove a non-existent directive $this->assertEquals($r, $r->removeCacheControlDirective('max-age')); // Has directive $this->assertFalse($r->hasCacheControlDirective('max-age')); $r->addCacheControlDirective('must-revalidate'); $this->assertTrue($r->hasCacheControlDirective('must-revalidate')); // Make sure that it works with multiple Cache-Control headers $r->setHeader('Cache-Control', 'must-revalidate, max-age=100'); $r->addHeaders(array( 'Cache-Control' => 'no-cache' )); $this->assertEquals(true, $r->getCacheControlDirective('no-cache')); $this->assertEquals(true, $r->getCacheControlDirective('must-revalidate')); $this->assertEquals(100, $r->getCacheControlDirective('max-age')); } }tests/Guzzle/Tests/Http/Message/EntityEnclosingRequestTest.php000064400000022477151742733540020662 0ustar00 */ class EntityEnclosingRequestTest extends \Guzzle\Tests\GuzzleTestCase { /** * @covers Guzzle\Http\Message\EntityEnclosingRequest::__toString * @covers Guzzle\Http\Message\EntityEnclosingRequest::addPostFields */ public function testRequestIncludesBodyInMessage() { $request = RequestFactory::put('http://www.guzzle-project.com/', null, 'data'); $this->assertEquals("PUT / HTTP/1.1\r\nUser-Agent: " . Guzzle::getDefaultUserAgent() . "\r\nHost: www.guzzle-project.com\r\nContent-Length: 4\r\nExpect: 100-Continue\r\n\r\ndata", (string) $request); unset($request); // Adds POST fields and sets Content-Length $request = RequestFactory::post('http://www.guzzle-project.com/', null, array( 'data' => '123' )); $this->assertEquals("POST / HTTP/1.1\r\nUser-Agent: " . Guzzle::getDefaultUserAgent() . "\r\nHost: www.guzzle-project.com\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\ndata=123", (string) $request); unset($request); $request = RequestFactory::post('http://www.test.com/'); $request->addPostFiles(array( 'file' => __FILE__ )); $request->addPostFields(array( 'a' => 'b' )); $message = (string) $request; $this->assertEquals('multipart/form-data', $request->getHeader('Content-Type')); } /** * @covers Guzzle\Http\Message\EntityEnclosingRequest::update */ public function testRequestDeterminesContentBeforeSending() { // Tests using a POST entity body $request = RequestFactory::create('POST', 'http://www.test.com/'); $request->addPostFields(array( 'test' => '123' )); $this->assertContains("\r\n\r\ntest=123", (string)$request); unset($request); // Tests using a Content-Length entity body $request = RequestFactory::create('PUT', 'http://www.test.com/'); $request->setBody(EntityBody::factory('test')); $this->assertNull($request->getHeader('Content-Length')); $request->getEventManager()->notify('request.prepare_entity_body'); $this->assertEquals(4, $request->getHeader('Content-Length')); unset($request); // Tests using a Transfer-Encoding chunked entity body already set $request = RequestFactory::create('PUT', 'http://www.test.com/'); $request->setBody(EntityBody::factory('test')) ->setHeader('Transfer-Encoding', 'chunked'); $request->getEventManager()->notify('request.prepare_entity_body'); $this->assertNull($request->getHeader('Content-Length')); // Tests using a Transfer-Encoding chunked entity body with undeterminable size $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\nData"); $request = RequestFactory::create('PUT', 'http://www.test.com/'); $request->setBody(EntityBody::factory(fopen($this->getServer()->getUrl(), 'r'))); $request->getEventManager()->notify('request.prepare_entity_body'); $this->assertNull($request->getHeader('Content-Length')); $this->assertEquals('chunked', $request->getHeader('Transfer-Encoding')); // Tests making sure HTTP/1.0 has a Content-Length header $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\nData"); $request = RequestFactory::create('PUT', 'http://www.test.com/'); $request->setBody(EntityBody::factory(fopen($this->getServer()->getUrl(), 'r'))); $request->setProtocolVersion('1.0'); try { $request->getEventManager()->notify('request.prepare_entity_body'); $this->fail('Expected exception due to 1.0 and no Content-Length'); } catch (RequestException $e) { } } /** * @covers Guzzle\Http\Message\EntityEnclosingRequest::setBody * @covers Guzzle\Http\Message\EntityEnclosingRequest::getBody */ public function testRequestHasMutableBody() { $request = RequestFactory::create('PUT', 'http://www.guzzle-project.com/', null, 'data'); $body = $request->getBody(); $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $body); $this->assertEquals($body, $request->getBody()); $request->setBody(EntityBody::factory('foobar')); $this->assertNotEquals($body, $request->getBody()); $this->assertEquals('foobar', (string) $request->getBody()); } /** * @covers Guzzle\Http\Message\EntityEnclosingRequest::addPostFields * @covers Guzzle\Http\Message\EntityEnclosingRequest::getPostFields * @covers Guzzle\Http\Message\EntityEnclosingRequest::getPostFiles * @covers Guzzle\Http\Message\EntityEnclosingRequest::addEvent */ public function testSetPostFields() { $request = RequestFactory::create('POST', 'http://www.guzzle-project.com/'); $this->assertInstanceOf('Guzzle\\Http\\QueryString', $request->getPostFields()); $fields = new QueryString(array( 'a' => 'b' )); $request->addPostFields($fields); $this->assertEquals($fields->getAll(), $request->getPostFields()->getAll()); $this->assertEquals(array(), $request->getPostFiles()); } /** * @covers Guzzle\Http\Message\EntityEnclosingRequest::getPostFiles * @covers Guzzle\Http\Message\EntityEnclosingRequest::addPostFiles */ public function testSetPostFiles() { $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); $request = RequestFactory::create('POST', $this->getServer()->getUrl()); $request->addPostFiles(array(__FILE__)); $request->addPostFields(array( 'test' => 'abc' )); $this->assertEquals(array( 'file' => '@' . __FILE__, 'test' => 'abc' ), $request->getPostFields()->getAll()); $this->assertEquals(array( 'file' => __FILE__ ), $request->getPostFiles()); $request->send(); $this->assertNotNull($request->getHeader('Content-Length')); $this->assertContains('multipart/form-data; boundary=', $request->getHeader('Content-Type'), '-> cURL must add the boundary'); } /** * @covers Guzzle\Http\Message\EntityEnclosingRequest::addPostFiles * @expectedException Guzzle\Http\Message\RequestException */ public function testSetPostFilesThrowsExceptionWhenFileIsNotFound() { $request = RequestFactory::create('POST', 'http://www.guzzle-project.com/'); $request->addPostFiles(array( 'file' => 'filenotfound.ini' )); } /** * @covers Guzzle\Http\Message\EntityEnclosingRequest::update */ public function testProcessMethodAddsPostBodyAndEntityBodyHeaders() { $request = RequestFactory::create('POST', 'http://www.guzzle-project.com/'); $request->getPostFields()->set('a', 'b'); $this->assertContains("\r\n\r\na=b", (string) $request); $this->assertEquals('application/x-www-form-urlencoded', $request->getHeader('Content-Type')); unset($request); $request = RequestFactory::create('POST', 'http://www.guzzle-project.com/'); $request->getPostFields()->set('a', 'b'); $request->getEventManager()->notify('request.prepare_entity_body'); $this->assertEquals('application/x-www-form-urlencoded', $request->getHeader('Content-Type')); $this->assertEquals('a=b', $request->getCurlOptions()->get(CURLOPT_POSTFIELDS)); unset($request); $request = RequestFactory::create('POST', 'http://www.guzzle-project.com/'); $request->addPostFiles(array('file' => __FILE__)); $request->getEventManager()->notify('request.prepare_entity_body'); $this->assertEquals('multipart/form-data', $request->getHeader('Content-Type')); $this->assertEquals(array('file' => '@' . __FILE__), $request->getCurlOptions()->get(CURLOPT_POSTFIELDS)); } /** * @covers Guzzle\Http\Message\EntityEnclosingRequest::setBody * @covers Guzzle\Http\Message\EntityEnclosingRequest::update */ public function testCanSendMultipleRequestsUsingASingleRequestObject() { $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 201 Created\r\nContent-Length: 0\r\n\r\n", )); $request = RequestFactory::put($this->getServer()->getUrl()); $request->setBody('test'); $request->send(); $this->assertEquals(200, $request->getResponse()->getStatusCode()); $request->setBody(json_encode(array('a' => '1')), 'application/json'); $request->send(); $requests = $this->getServer()->getReceivedRequests(true); $this->assertEquals(2, count($requests)); $this->assertEquals(4, $requests[0]->getHeader('Content-Length')); $this->assertNotEquals(4, $requests[1]->getHeader('Content-Length')); $this->assertEquals(201, $request->getResponse()->getStatusCode()); } }tests/Guzzle/Tests/Http/Message/ExceptionTest.php000064400000001711151742733540016115 0ustar00 */ class ExceptionTest extends \Guzzle\Tests\GuzzleTestCase { /** * @covers Guzzle\Http\Message\RequestException */ public function testRequestException() { $e = new RequestException('Message'); $request = new Request('GET', 'http://www.guzzle-project.com/'); $e->setRequest($request); $this->assertEquals($request, $e->getRequest()); } /** * @covers Guzzle\Http\Message\BadResponseException */ public function testBadResponseException() { $e = new BadResponseException('Message'); $response = new Response(200); $e->setResponse($response); $this->assertEquals($response, $e->getResponse()); } }tests/Guzzle/Tests/Http/Message/RequestFactoryTest.php000064400000030550151742733540017142 0ustar00 */ class HttpRequestFactoryTest extends \Guzzle\Tests\GuzzleTestCase { /** * @covers Guzzle\Http\Message\RequestFactory::create * @covers Guzzle\Http\Message\RequestFactory::get */ public function testCreatesNewGetRequests() { $request = RequestFactory::get('http://www.google.com/'); $this->assertInstanceOf('Guzzle\\Http\\Message\\MessageInterface', $request); $this->assertInstanceOf('Guzzle\\Http\\Message\\RequestInterface', $request); $this->assertInstanceOf('Guzzle\\Http\\Message\\Request', $request); $this->assertEquals('GET', $request->getMethod()); $this->assertEquals('http', $request->getScheme()); $this->assertEquals('http://www.google.com/', $request->getUrl()); $this->assertEquals('www.google.com', $request->getHost()); $this->assertEquals('/', $request->getPath()); $this->assertEquals('/', $request->getResourceUri()); // Create a GET request with a custom receiving body $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); $b = EntityBody::factory(''); $request = RequestFactory::get($this->getServer()->getUrl(), null, $b); $response = $request->send(); $this->assertSame($b, $response->getBody()); } /** * @covers Guzzle\Http\Message\RequestFactory::create * @covers Guzzle\Http\Message\RequestFactory::put */ public function testCreatesPutRequests() { // Test using a string $request = RequestFactory::put('http://www.google.com/path?q=1&v=2', null, 'Data'); $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); $this->assertEquals('PUT', $request->getMethod()); $this->assertEquals('http', $request->getScheme()); $this->assertEquals('http://www.google.com/path?q=1&v=2', $request->getUrl()); $this->assertEquals('www.google.com', $request->getHost()); $this->assertEquals('/path', $request->getPath()); $this->assertEquals('/path?q=1&v=2', $request->getResourceUri()); $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $request->getBody()); $this->assertEquals('Data', (string) $request->getBody()); unset($request); // Test using an EntityBody $request = RequestFactory::put('http://www.google.com/path?q=1&v=2', null, EntityBody::factory('Data')); $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); $this->assertEquals('Data', (string) $request->getBody()); // Test using a resource $resource = fopen('php://temp', 'w+'); fwrite($resource, 'Data'); $request = RequestFactory::put('http://www.google.com/path?q=1&v=2', null, $resource); $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); $this->assertEquals('Data', (string) $request->getBody()); // Test using an object that can be cast as a string $request = RequestFactory::put('http://www.google.com/path?q=1&v=2', null, Url::factory('http://www.example.com/')); $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); $this->assertEquals('http://www.example.com/', (string) $request->getBody()); } /** * @covers Guzzle\Http\Message\RequestFactory::head * @covers Guzzle\Http\Message\RequestFactory::delete */ public function testCreatesHeadAndDeleteRequests() { $request = RequestFactory::delete('http://www.test.com/'); $this->assertEquals('DELETE', $request->getMethod()); $request = RequestFactory::head('http://www.test.com/'); $this->assertEquals('HEAD', $request->getMethod()); } /** * @covers Guzzle\Http\Message\RequestFactory::options */ public function testCreatesOptionsRequests() { $request = RequestFactory::options('http://www.example.com/'); $this->assertEquals('OPTIONS', $request->getMethod()); $this->assertInstanceOf('Guzzle\\Http\\Message\\Request', $request); } /** * @covers Guzzle\Http\Message\RequestFactory::create * @covers Guzzle\Http\Message\RequestFactory::put */ public function testCreatesNewPutRequestWithBody() { $request = RequestFactory::put('http://www.google.com/path?q=1&v=2', null, 'Data'); $this->assertEquals('Data', (string) $request->getBody()); } /** * @covers Guzzle\Http\Message\RequestFactory::create * @covers Guzzle\Http\Message\RequestFactory::post */ public function testCreatesNewPostRequestWithFields() { // Use an array $request = RequestFactory::create('POST', 'http://www.google.com/path?q=1&v=2', null, array( 'a' => 'b' )); $this->assertEquals(array('a' => 'b'), $request->getPostFields()->getAll()); unset($request); // Use a collection $request = RequestFactory::create('POST', 'http://www.google.com/path?q=1&v=2', null, new Collection(array( 'a' => 'b' ))); $this->assertEquals(array('a' => 'b'), $request->getPostFields()->getAll()); // Use a QueryString $request = RequestFactory::create('POST', 'http://www.google.com/path?q=1&v=2', null, new QueryString(array( 'a' => 'b' ))); $this->assertEquals(array('a' => 'b'), $request->getPostFields()->getAll()); $request = RequestFactory::post('http://www.test.com/', null, array( 'a' => 'b', 'file' => '@' . __FILE__ )); $this->assertEquals(array( 'a' => 'b', 'file' => '@' . __FILE__ ), $request->getPostFields()->getAll()); $this->assertEquals(array( 'file' => __FILE__ ), $request->getPostFiles()); } /** * @covers Guzzle\Http\Message\RequestFactory::fromParts * @covers Guzzle\Http\Message\RequestFactory::create */ public function testCreatesFromParts() { $parts = parse_url('http://michael:123@www.google.com:8080/path?q=1&v=2'); $request = RequestFactory::fromParts('PUT', $parts, null, 'Data'); $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); $this->assertEquals('PUT', $request->getMethod()); $this->assertEquals('http', $request->getScheme()); $this->assertEquals('http://www.google.com:8080/path?q=1&v=2', $request->getUrl()); $this->assertEquals('www.google.com', $request->getHost()); $this->assertEquals('www.google.com:8080', $request->getHeader('Host')); $this->assertEquals('/path', $request->getPath()); $this->assertEquals('/path?q=1&v=2', $request->getResourceUri()); $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $request->getBody()); $this->assertEquals('Data', (string)$request->getBody()); $this->assertEquals('michael', $request->getUsername()); $this->assertEquals('123', $request->getPassword()); $this->assertEquals('8080', $request->getPort()); $this->assertEquals(array( 'scheme' => 'http', 'host' => 'www.google.com', 'port' => 8080, 'path' => '/path', 'query' => 'q=1&v=2', ), parse_url($request->getUrl())); } /** * @covers Guzzle\Http\Message\RequestFactory::parseMessage * @covers Guzzle\Http\Message\RequestFactory::fromMessage * @covers Guzzle\Http\Message\RequestFactory::create */ public function testCreatesFromMessage() { $auth = base64_encode('michael:123'); $message = "PUT /path?q=1&v=2 HTTP/1.1\r\nHost: www.google.com:8080\r\nContent-Length: 4\r\nAuthorization: Basic {$auth}\r\n\r\nData"; $request = RequestFactory::fromMessage($message); $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); $this->assertEquals('PUT', $request->getMethod()); $this->assertEquals('http', $request->getScheme()); $this->assertEquals('http://www.google.com:8080/path?q=1&v=2', $request->getUrl()); $this->assertEquals('www.google.com', $request->getHost()); $this->assertEquals('www.google.com:8080', $request->getHeader('Host')); $this->assertEquals('/path', $request->getPath()); $this->assertEquals('/path?q=1&v=2', $request->getResourceUri()); $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $request->getBody()); $this->assertEquals('Data', (string)$request->getBody()); $this->assertEquals('michael', $request->getUsername()); $this->assertEquals('123', $request->getPassword()); $this->assertEquals('8080', $request->getPort()); // Test passing a blank message returns false $this->assertFalse($request = RequestFactory::fromMessage('')); // Test passing a url with no port $message = "PUT /path?q=1&v=2 HTTP/1.1\r\nHost: www.google.com\r\nContent-Length: 4\r\nAuthorization: Basic {$auth}\r\n\r\nData"; $request = RequestFactory::fromMessage($message); $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); $this->assertEquals('PUT', $request->getMethod()); $this->assertEquals('http', $request->getScheme()); $this->assertEquals('http://www.google.com/path?q=1&v=2', $request->getUrl()); $this->assertEquals('www.google.com', $request->getHost()); $this->assertEquals('/path', $request->getPath()); $this->assertEquals('/path?q=1&v=2', $request->getResourceUri()); $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $request->getBody()); $this->assertEquals('Data', (string)$request->getBody()); $this->assertEquals('michael', $request->getUsername()); $this->assertEquals('123', $request->getPassword()); $this->assertEquals(80, $request->getPort()); } /** * @covers Guzzle\Http\Message\RequestFactory::create */ public function testCreatesNewTraceRequest() { $request = RequestFactory::create('TRACE', 'http://www.google.com/'); $this->assertFalse($request instanceof \Guzzle\Http\Message\EntityEnclosingRequest); $this->assertEquals('TRACE', $request->getMethod()); } /** * @covers Guzzle\Http\Message\RequestFactory::parseMessage */ public function testParsesMessages() { $parts = RequestFactory::parseMessage( "get /testing?q=10&f=3 http/1.1\r\n" . "host: localhost:443\n" . "authorization: basic bWljaGFlbDoxMjM=\r\n\r\n" ); $this->assertEquals('GET', $parts['method']); $this->assertEquals('HTTP', $parts['protocol']); $this->assertEquals('1.1', $parts['protocol_version']); $this->assertEquals('https', $parts['parts']['scheme']); $this->assertEquals('localhost', $parts['parts']['host']); $this->assertEquals('443', $parts['parts']['port']); $this->assertEquals('michael', $parts['parts']['user']); $this->assertEquals('123', $parts['parts']['pass']); $this->assertEquals('/testing', $parts['parts']['path']); $this->assertEquals('?q=10&f=3', $parts['parts']['query']); $this->assertEquals(array( 'Host' => 'localhost:443', 'Authorization' => 'basic bWljaGFlbDoxMjM=' ), $parts['headers']); $this->assertEquals('', $parts['body']); $parts = RequestFactory::parseMessage( "get / spydy/1.0\r\n" ); $this->assertEquals('GET', $parts['method']); $this->assertEquals('SPYDY', $parts['protocol']); $this->assertEquals('1.0', $parts['protocol_version']); $this->assertEquals('http', $parts['parts']['scheme']); $this->assertEquals('', $parts['parts']['host']); $this->assertEquals('', $parts['parts']['port']); $this->assertEquals('', $parts['parts']['user']); $this->assertEquals('', $parts['parts']['pass']); $this->assertEquals('/', $parts['parts']['path']); $this->assertEquals('', $parts['parts']['query']); $this->assertEquals(array(), $parts['headers']); $this->assertEquals('', $parts['body']); } }tests/Guzzle/Tests/Http/Message/RequestTest.php000064400000066240151742733540015617 0ustar00 */ class RequestTest extends \Guzzle\Tests\GuzzleTestCase { /** * @var Request */ protected $request; /** * Sets up the fixture, for example, opens a network connection. * This method is called before a test is executed. */ protected function setUp() { $this->request = new Request('GET', 'http://www.google.com/'); } public function tearDown() { unset($this->request); } /** * @covers Guzzle\Http\Message\Request::__construct */ public function testConstructorBuildsRequest() { // Test passing an array of headers $request = new Request('GET', 'http://www.guzzle-project.com/', array( 'foo' => 'bar' )); $this->assertEquals('GET', $request->getMethod()); $this->assertEquals('http://www.guzzle-project.com/', $request->getUrl()); $this->assertEquals('bar', $request->getHeader('foo')); // Test passing a Collection of headers $request = new Request('GET', 'http://www.guzzle-project.com/', new Collection(array( 'foo' => 'bar' ))); $this->assertEquals('bar', $request->getHeader('foo')); // Test passing no headers $request = new Request('GET', 'http://www.guzzle-project.com/', null); $this->assertFalse($request->hasHeader('foo')); } /** * @covers Guzzle\Http\Message\Request::__toString * @covers Guzzle\Http\Message\Request::getRawHeaders */ public function testRequestsCanBeConvertedToRawMessageStrings() { $auth = base64_encode('michael:123'); $message = "PUT /path?q=1&v=2 HTTP/1.1\r\nAuthorization: Basic {$auth}\r\nUser-Agent: " . Guzzle::getDefaultUserAgent() . "\r\nHost: www.google.com\r\nContent-Length: 4\r\nExpect: 100-Continue\r\n\r\nData"; $request = RequestFactory::put('http://www.google.com/path?q=1&v=2', array( 'Authorization' => 'Basic ' . $auth ), 'Data'); $this->assertEquals($message, $request->__toString()); // Add authorization after the fact and see that it was put in the message $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); $request = RequestFactory::put($this->getServer()->getUrl(), null, 'Data'); $request->setAuth('michael', '123', CURLAUTH_BASIC); $request->send(); $str = (string) $request; $this->assertTrue((bool) strpos($str, 'Authorization: Basic ' . $auth)); } /** * @covers Guzzle\Http\Message\Request::getEventManager */ public function testGetEventManager() { $mediator = $this->request->getEventManager(); $this->assertInstanceOf('Guzzle\\Common\\Event\\EventManager', $mediator); $this->assertEquals($mediator, $this->request->getEventManager()); $this->assertEquals($this->request, $mediator->getSubject()); } /** * @covers Guzzle\Http\Message\Request::send * @covers Guzzle\Http\Message\Request::getResponse * @covers Guzzle\Http\Message\Request::setResponse * @covers Guzzle\Http\Message\Request::processResponse * @covers Guzzle\Http\Message\Request::getResponseBody */ public function testSend() { $response = new Response(200, array( 'Content-Length' => 3 ), 'abc'); $this->request->setResponse($response, true); $r = $this->request->send(); $this->assertSame($response, $r); $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $this->request->getResponse()); $this->assertSame($r, $this->request->getResponse()); $this->assertEquals('complete', $this->request->getState()); } /** * @covers Guzzle\Http\Message\Request::getResponse * @covers Guzzle\Http\Message\Request::setResponse * @covers Guzzle\Http\Message\Request::processResponse * @covers Guzzle\Http\Message\Request::getResponseBody */ public function testGetResponse() { $this->assertNull($this->request->getResponse()); $response = new Response(200, array( 'Content-Length' => 3 ), 'abc'); $this->request->setResponse($response); $this->assertEquals($response, $this->request->getResponse()); $request = new Request('GET', 'http://www.google.com/'); $request->setResponse($response, true); $request->setState(RequestInterface::STATE_COMPLETE); $requestResponse = $request->getResponse(); $this->assertEquals($response, $requestResponse); // Try again, making sure it's still the same response $this->assertEquals($requestResponse, $request->getResponse()); $response = new Response(204); $request = new Request('GET', 'http://www.google.com/'); $request->setResponse($response, true); $request->setState('complete'); $requestResponse = $request->getResponse(); $this->assertEquals($response, $requestResponse); $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $response->getBody()); } /** * @covers Guzzle\Http\Message\Request::getResponse * @covers Guzzle\Http\Message\Request::processResponse * @covers Guzzle\Http\Message\Request::getResponseBody */ public function testRequestThrowsExceptionOnBadResponse() { $response = new Response(404, array( 'Content-Length' => 3 ), 'abc'); $request = new Request('GET', 'http://www.google.com/'); try { $request->setResponse($response, true); $request->send(); $this->fail('Expected exception not thrown'); } catch (BadResponseException $e) { $this->assertInstanceOf('Guzzle\\Http\\Message\\RequestInterface', $e->getRequest()); $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $e->getResponse()); $message = "Unsuccessful response | [status code] 404 | " . "[reason phrase] Not Found | [url] http://www.google.com/ | " . "[request] GET / HTTP/1.1\r\nUser-Agent: " . Guzzle::getDefaultUserAgent() . "\r\nHost: www.google.com\r\n" . "\r\n | [response] HTTP/1.1 404 Not Found\r\nContent-Length: 3" . "\r\n\r\nabc"; $this->assertEquals($message, $e->getMessage()); } } /** * @covers Guzzle\Http\Message\Request::getQuery */ public function testManagesQuery() { $this->assertInstanceOf('Guzzle\\Http\\QueryString', $this->request->getQuery()); $this->request->getQuery()->set('test', '123'); $this->assertEquals('?test=123', $this->request->getQuery(true)); } /** * @covers Guzzle\Http\Message\Request::getMethod */ public function testRequestHasMethod() { $this->assertEquals('GET', $this->request->getMethod()); } /** * @covers Guzzle\Http\Message\Request::getScheme * @covers Guzzle\Http\Message\Request::setScheme */ public function testRequestHasScheme() { $this->assertEquals('http', $this->request->getScheme()); $this->assertEquals($this->request, $this->request->setScheme('https')); $this->assertEquals('https', $this->request->getScheme()); } /** * @covers Guzzle\Http\Message\Request::getHost * @covers Guzzle\Http\Message\Request::setHost */ public function testRequestHasHost() { $this->assertEquals('www.google.com', $this->request->getHost()); $this->assertEquals('www.google.com', $this->request->getHeader('Host')); $this->assertSame($this->request, $this->request->setHost('www2.google.com')); $this->assertEquals('www2.google.com', $this->request->getHost()); $this->assertEquals('www2.google.com', $this->request->getHeader('Host')); $this->assertSame($this->request, $this->request->setHost('www.test.com:8081')); $this->assertEquals('www.test.com', $this->request->getHost()); $this->assertEquals(8081, $this->request->getPort()); } /** * @covers Guzzle\Http\Message\Request::getProtocolVersion * @covers Guzzle\Http\Message\Request::setProtocolVersion */ public function testRequestHasProtocol() { $this->assertEquals('1.1', $this->request->getProtocolVersion()); $this->assertEquals($this->request, $this->request->setProtocolVersion('1.1')); $this->assertEquals('1.1', $this->request->getProtocolVersion()); $this->assertEquals($this->request, $this->request->setProtocolVersion('1.0')); $this->assertEquals('1.0', $this->request->getProtocolVersion()); } /** * @covers Guzzle\Http\Message\Request::getPath * @covers Guzzle\Http\Message\Request::setPath */ public function testRequestHasPath() { $this->assertEquals('/', $this->request->getPath()); $this->assertEquals($this->request, $this->request->setPath('/index.html')); $this->assertEquals('/index.html', $this->request->getPath()); $this->assertEquals($this->request, $this->request->setPath('index.html')); $this->assertEquals('/index.html', $this->request->getPath()); } /** * @covers Guzzle\Http\Message\Request::getPort * @covers Guzzle\Http\Message\Request::setPort */ public function testRequestHasPort() { $this->assertEquals(80, $this->request->getPort()); $this->assertEquals($this->request, $this->request->setPort('8080')); $this->assertEquals('8080', $this->request->getPort()); } /** * @covers Guzzle\Http\Message\Request::getUsername * @covers Guzzle\Http\Message\Request::getPassword * @covers Guzzle\Http\Message\Request::setAuth */ public function testRequestHandlesAuthorization() { // Uninitialized auth $this->assertEquals(null, $this->request->getUsername()); $this->assertEquals(null, $this->request->getPassword()); // Set an auth $this->assertSame($this->request, $this->request->setAuth('michael', '123')); $this->assertEquals('michael', $this->request->getUsername()); $this->assertEquals('123', $this->request->getPassword()); // Remove the auth $this->request->setAuth(false); $this->assertEquals(null, $this->request->getUsername()); $this->assertEquals(null, $this->request->getPassword()); // Make sure that the cURL based auth works too $request = new Request('GET', $this->getServer()->getUrl()); $request->setAuth('michael', 'password', CURLAUTH_DIGEST); $this->assertEquals('michael:password', $request->getCurlOptions()->get(CURLOPT_USERPWD)); $this->assertEquals(CURLAUTH_DIGEST, $request->getCurlOptions()->get(CURLOPT_HTTPAUTH)); } /** * @covers Guzzle\Http\Message\Request::getResourceUri */ public function testGetResourceUri() { $this->assertEquals('/', $this->request->getResourceUri()); $this->request->setPath('/index.html'); $this->assertEquals('/index.html', $this->request->getResourceUri()); $this->request->getQuery()->add('v', '1'); $this->assertEquals('/index.html?v=1', $this->request->getResourceUri()); } /** * @covers Guzzle\Http\Message\Request::getUrl * @covers Guzzle\Http\Message\Request::setUrl */ public function testRequestHasMutableUrl() { $url = 'http://www.test.com:8081/path?q=123#fragment'; $u = Url::factory($url); $this->assertSame($this->request, $this->request->setUrl($url)); $this->assertEquals($url, $this->request->getUrl()); $this->assertSame($this->request, $this->request->setUrl($u)); $this->assertEquals($url, $this->request->getUrl()); try { $this->request->setUrl(10); $this->fail('Expected exception not thrown'); } catch (\InvalidArgumentException $e) { } } /** * @covers Guzzle\Http\Message\Request::setState * @covers Guzzle\Http\Message\Request::getState */ public function testRequestHasState() { $this->assertEquals(RequestInterface::STATE_NEW, $this->request->getState()); $this->request->setState(RequestInterface::STATE_TRANSFER); $this->assertEquals(RequestInterface::STATE_TRANSFER, $this->request->getState()); } /** * @covers Guzzle\Http\Message\Request::setResponse * @covers Guzzle\Http\Message\Request::getResponse * @covers Guzzle\Http\Message\Request::getState */ public function testSetManualResponse() { $response = new Response(200, array( 'Date' => 'Sat, 16 Oct 2010 17:27:14 GMT', 'Expires' => '-1', 'Cache-Control' => 'private, max-age=0', 'Content-Type' => 'text/html; charset=ISO-8859-1', ), 'response body'); $this->assertSame($this->request, $this->request->setResponse($response), '-> setResponse() must use a fluent interface'); $this->assertEquals('complete', $this->request->getState(), '-> setResponse() must change the state of the request to complete'); $this->assertSame($response, $this->request->getResponse(), '-> setResponse() must set the exact same response that was passed in to it'); } /** * @covers Guzzle\Http\Message\Request::setResponseBody */ public function testRequestCanHaveManuallySetResponseBody() { $file = __DIR__ . '/../../TestData/temp.out'; if (file_exists($file)) { unlink($file); } $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata"); $request = RequestFactory::get($this->getServer()->getUrl()); $request->setResponseBody(EntityBody::factory(fopen($file, 'w+'))); $request->send(); $this->assertTrue(file_exists($file)); unlink($file); $this->assertEquals('data', $request->getResponse()->getBody(true)); } /** * @covers Guzzle\Http\Message\Request::isResponseBodyRepeatable */ public function testDeterminesIfResponseBodyRepeatable() { // The default stream created for responses is seekable $request = RequestFactory::get('http://localhost:' . $this->getServer()->getPort()); $this->assertTrue($request->isResponseBodyRepeatable()); // This should return false because an HTTP stream is not seekable $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata"); $request->setResponseBody(EntityBody::factory(fopen($this->getServer()->getUrl(), true))); $this->assertFalse($request->isResponseBodyRepeatable()); } /** * @covers Guzzle\Http\Message\Request::canCache */ public function testDeterminesIfCanCacheRequest() { $this->assertTrue(RequestFactory::fromMessage( "GET / HTTP/1.1\r\nHost: www.test.com\r\nCache-Control: no-cache, max-age=120\r\n\r\n" )->canCache()); $this->assertTrue(RequestFactory::fromMessage( "HEAD / HTTP/1.1\r\nHost: www.test.com\r\nCache-Control: no-cache, max-age=120\r\n\r\n" )->canCache()); $this->assertFalse(RequestFactory::fromMessage( "HEAD / HTTP/1.1\r\nHost: www.test.com\r\nCache-Control: no-cache, max-age=120, no-store\r\n\r\n" )->canCache()); $this->assertFalse(RequestFactory::fromMessage( "POST / HTTP/1.1\r\nHost: www.test.com\r\n\r\n" )->canCache()); $this->assertFalse(RequestFactory::fromMessage( "PUT / HTTP/1.1\r\nHost: www.test.com\r\nCache-Control: no-cache, max-age=120\r\n\r\n" )->canCache()); } /** * @covers Guzzle\Http\Message\Request */ public function testHoldsCookies() { $cookie = $this->request->getCookie(); $this->assertInstanceOf('Guzzle\\Http\\Cookie', $this->request->getCookie()); // Ensure that the cookie will not affect the request $this->assertNull($this->request->getCookie('test')); $cookie->set('test', 'abc'); $this->assertNull($this->request->getCookie('test')); // Set a cookie $this->assertSame($this->request, $this->request->addCookie('test', 'abc')); $this->assertEquals('abc', $this->request->getCookie('test')); // Unset the cookies by setting the Cookie header to null $this->request->setHeader('Cookie', null); $this->assertNull($this->request->getCookie('test')); // Set and remove a cookie $this->assertSame($this->request, $this->request->addCookie('test', 'abc')); $this->assertEquals('abc', $this->request->getCookie('test')); $this->assertSame($this->request, $this->request->removeCookie('test')); $this->assertNull($this->request->getCookie('test')); // Remove the cookie header $this->assertSame($this->request, $this->request->addCookie('test', 'abc')); $this->request->removeHeader('Cookie'); $this->assertEquals('', (string)$this->request->getCookie()); // Set the cookie using a cookie object $this->assertSame($this->request, $this->request->setCookie($cookie)); $this->assertEquals($cookie->getAll(), $this->request->getCookie()->getAll()); // Set the cookie using an array $this->assertSame($this->request, $this->request->setCookie(array( 'test' => 'def' ))); $this->assertEquals(array( 'test' => 'def' ), $this->request->getCookie()->getAll()); // Test using an invalid value try { $this->request->setCookie('a'); $this->fail('Did not throw expected exception when passing invalid value'); } catch (\InvalidArgumentException $e) { } } /** * @covers Guzzle\Http\Message\Request::processResponse * @expectedException Guzzle\Http\Message\RequestException * @expectedExceptionMessage Unable to set state to complete because no response has been received by the request */ public function testRequestThrowsExceptionWhenSetToCompleteWithNoResponse() { $this->request->setState(RequestInterface::STATE_COMPLETE); } /** * @covers Guzzle\Http\Message\Request::__clone */ public function testClonedRequestsUseNewInternalState() { $p = new ExponentialBackoffPlugin(); $this->request->getEventManager()->attach($p, 100); $r = clone $this->request; $this->assertEquals(RequestInterface::STATE_NEW, $r->getState()); $this->assertNotSame($r->getQuery(), $this->request->getQuery()); $this->assertNotSame($r->getCurlOptions(), $this->request->getCurlOptions()); $this->assertNotSame($r->getEventManager(), $this->request->getEventManager()); $this->assertNotSame($r->getHeaders(), $this->request->getHeaders()); $this->assertNotSame($r->getParams(), $this->request->getParams()); $this->assertNull($r->getParams()->get('queued_response')); $this->assertTrue($this->request->getEventManager()->hasObserver($p)); $this->assertEquals(100, $r->getEventManager()->getPriority($p)); $this->assertTrue($r->getEventManager()->hasObserver($p)); } /** * @covers Guzzle\Http\Message\Request::changedHeader * @covers Guzzle\Http\Message\Request::setHeader */ public function testCatchesAllHostHeaderChanges() { // Tests setting using headers $this->request->setHeader('Host', 'www.abc.com'); $this->assertEquals('www.abc.com', $this->request->getHost()); $this->assertEquals('www.abc.com', $this->request->getHeader('Host')); $this->assertEquals(80, $this->request->getPort()); // Tests setting using setHost() $this->request->setHost('abc.com'); $this->assertEquals('abc.com', $this->request->getHost()); $this->assertEquals('abc.com', $this->request->getHeader('Host')); $this->assertEquals(80, $this->request->getPort()); // Tests setting with a port $this->request->setHost('abc.com:8081'); $this->assertEquals('abc.com', $this->request->getHost()); $this->assertEquals('abc.com:8081', $this->request->getHeader('Host')); $this->assertEquals(8081, $this->request->getPort()); // Tests setting with a port using the Host header $this->request->setHeader('Host', 'solr.com:8983'); $this->assertEquals('solr.com', $this->request->getHost()); $this->assertEquals('solr.com:8983', $this->request->getHeader('Host')); $this->assertEquals(8983, $this->request->getPort()); // Tests setting with an inferred 443 port using the Host header $this->request->setScheme('https'); $this->request->setHeader('Host', 'solr.com'); $this->assertEquals('solr.com', $this->request->getHost()); $this->assertEquals('solr.com', $this->request->getHeader('Host')); $this->assertEquals(443, $this->request->getPort()); } /** * @covers Guzzle\Http\Message\Request::setUrl */ public function testRecognizesBasicAuthCredentialsInUrls() { $this->request->setUrl('http://michael:test@test.com/'); $this->assertEquals('michael', $this->request->getUsername()); $this->assertEquals('test', $this->request->getPassword()); } /** * This test launches a dummy Guzzle\Http\Server\Server object that listens * for incoming requests. The server allows us to test how cURL sends * requests and receives responses. We can validate the request structure * and whether or not the response was interpreted correctly. * * @covers Guzzle\Http\Message\Request */ public function testRequestCanBeSentUsingCurl() { $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 200 OK\r\nContent-Length: 4\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\nConnection: close\r\n\r\ndata", "HTTP/1.1 200 OK\r\nContent-Length: 4\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\nConnection: close\r\n\r\ndata", "HTTP/1.1 404 Not Found\r\nContent-Encoding: application/xml\r\nContent-Length: 48\r\n\r\nFile not found" )); $request = RequestFactory::get($this->getServer()->getUrl()); $response = $request->send(); $this->assertEquals('data', $response->getBody(true)); $this->assertEquals(200, (int)$response->getStatusCode()); $this->assertEquals('OK', $response->getReasonPhrase()); $this->assertEquals(4, $response->getContentLength()); $this->assertEquals('Thu, 01 Dec 1994 16:00:00 GMT', $response->getExpires()); // Test that the same handle can be sent twice without setting state to new $response2 = $request->send(); $this->assertNotSame($response, $response2); try { $request = RequestFactory::get($this->getServer()->getUrl() . 'index.html'); $response = $request->send(); $this->fail('Request did not receive a 404 response'); } catch (BadResponseException $e) { } $requests = $this->getServer()->getReceivedRequests(true); $messages = $this->getServer()->getReceivedRequests(false); $port = $this->getServer()->getPort(); $userAgent = Guzzle::getDefaultUserAgent(); $this->assertEquals('127.0.0.1:' . $port, $requests[0]->getHeader('Host')); $this->assertEquals('127.0.0.1:' . $port, $requests[1]->getHeader('Host')); $this->assertEquals('127.0.0.1:' . $port, $requests[2]->getHeader('Host')); $this->assertEquals('/', $requests[0]->getPath()); $this->assertEquals('/', $requests[1]->getPath()); $this->assertEquals('/index.html', $requests[2]->getPath()); $parts = explode("\r\n", $messages[0]); $this->assertEquals('GET / HTTP/1.1', $parts[0]); $parts = explode("\r\n", $messages[1]); $this->assertEquals('GET / HTTP/1.1', $parts[0]); $parts = explode("\r\n", $messages[2]); $this->assertEquals('GET /index.html HTTP/1.1', $parts[0]); } /** * @covers Guzzle\Http\Message\Request */ public function testCurlErrorsAreCaught() { $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); try { $request = RequestFactory::get('http://127.0.0.1:9876/'); $request->getCurlOptions()->set(CURLOPT_FRESH_CONNECT, true); $request->getCurlOptions()->set(CURLOPT_TIMEOUT, 0); $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT, 1); $request->send(); $this->fail('CurlException not thrown'); } catch (CurlException $e) { $m = $e->getMessage(); $this->assertContains('[curl] 7:', $m); $this->assertContains('[url] http://127.0.0.1:9876/', $m); $this->assertContains('[debug] ', $m); $this->assertContains('[info] array (', $m); $this->assertContains('Connection refused', $m); } } /** * @covers Guzzle\Http\Message\Request::onComplete */ public function testThrowsExceptionsWhenUnsuccessfulResponseIsReceivedByDefault() { $this->getServer()->enqueue("HTTP/1.1 404 Not found\r\nContent-Length: 0\r\n\r\n"); try { $request = RequestFactory::get($this->getServer()->getUrl() . 'index.html'); $response = $request->send(); $this->fail('Request did not receive a 404 response'); } catch (BadResponseException $e) { $this->assertContains('Unsuccessful response ', $e->getMessage()); $this->assertContains('[status code] 404 | [reason phrase] Not found | [url] http://127.0.0.1:8124/index.html | [request] GET /index.html HTTP/1.1', $e->getMessage()); $this->assertContains('Host: 127.0.0.1:8124', $e->getMessage()); $this->assertContains(" | [response] HTTP/1.1 404 Not found", $e->getMessage()); } } /** * @covers Guzzle\Http\Message\Request::setOnComplete */ public function testCanSetCustomOnCompleteHandler() { $request = RequestFactory::get($this->getServer()->getUrl()); $out = ''; $that = $this; $request->setOnComplete(function($request, $response, $default) use (&$out, $that) { $out .= $request . "\n" . $response . "\n"; $that->assertInternalType('array', $default); })->send(); $this->assertContains((string) $request, $out); $this->assertContains((string) $request->getResponse(), $out); try { $a = 'abc'; $request->setOnComplete($a); $this->fail('Set an invalid callback for setOnComplete'); } catch (\InvalidArgumentException $e) { } } }tests/Guzzle/Tests/Http/Message/ResponseTest.php000064400000061636151742733540015771 0ustar00 */ class ResponseTest extends \Guzzle\Tests\GuzzleTestCase { /** * The response object to test * * @var Guzzle\Http\Message\Response */ protected $response; public function setup() { $this->response = new Response(200, new Collection(array( 'Accept-Ranges' => 'bytes', 'Age' => '12', 'Allow' => 'GET, HEAD', 'Cache-Control' => 'no-cache', 'Content-Encoding' => 'gzip', 'Content-Language' => 'da', 'Content-Length' => '348', 'Content-Location' => '/index.htm', 'Content-Disposition' => 'attachment; filename=fname.ext', 'Content-MD5' => 'Q2hlY2sgSW50ZWdyaXR5IQ==', 'Content-Range' => 'bytes 21010-47021/47022', 'Content-Type' => 'text/html; charset=utf-8', 'Date' => 'Tue, 15 Nov 1994 08:12:31 GMT', 'ETag' => '737060cd8c284d8af7ad3082f209582d', 'Expires' => 'Thu, 01 Dec 1994 16:00:00 GMT', 'Last-Modified' => 'Tue, 15 Nov 1994 12:45:26 GMT', 'Location' => 'http://www.w3.org/pub/WWW/People.html', 'Pragma' => 'no-cache', 'Proxy-Authenticate' => 'Basic', 'Retry-After' => '120', 'Server' => 'Apache/1.3.27 (Unix) (Red-Hat/Linux)', 'Set-Cookie' => 'UserID=JohnDoe; Max-Age=3600; Version=1', 'Trailer' => 'Max-Forwards', 'Transfer-Encoding' => 'chunked', 'Vary' => '*', 'Via' => '1.0 fred, 1.1 nowhere.com (Apache/1.1)', 'Warning' => '199 Miscellaneous warning', 'WWW-Authenticate' => 'Basic' )), 'body'); } /** * @covers Guzzle\Http\Message\Response::__construct */ public function testConstructor() { $params = new Collection(); $body = EntityBody::factory(''); $response = new Response(200, $params, $body); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals($body, $response->getBody()); $this->assertEquals($params, $response->getHeaders()); $this->assertEquals('OK', $response->getReasonPhrase()); $this->assertEquals("HTTP/1.1 200 OK\r\n\r\n", $response->getRawHeaders()); // Make sure Content-Length is set automatically $response = new Response(200, $params); $this->assertEquals("HTTP/1.1 200 OK\r\n\r\n", $response->getRawHeaders()); // Pass bodies to the response $response = new Response(200, null, 'data'); $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $response->getBody()); $response = new Response(200, null, EntityBody::factory('data')); $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $response->getBody()); $this->assertEquals('data', $response->getBody(true)); // Make sure the proper exception is thrown try { $response = new Response(200, null, array('foo' => 'bar')); $this->fail('Response did not throw exception when passing invalid body'); } catch (BadResponseException $e) { } // Make sure the proper exception is thrown when sending invalid code try { $response = new Response(2); $this->fail('Response did not throw exception when passing invalid $code'); } catch (BadResponseException $e) { } // Make sure the proper exception is thrown when sending invalid headers try { $response = new Response(200, 'adidas'); $this->fail('Response did not throw exception when passing invalid $headers'); } catch (BadResponseException $e) { } } /** * @covers Guzzle\Http\Message\Response::__toString */ public function test__toString() { $response = new Response(200); $this->assertEquals("HTTP/1.1 200 OK\r\n\r\n", (string)$response); // Add another header $response = new Response(200, array( 'X-Test' => 'Guzzle' )); $this->assertEquals("HTTP/1.1 200 OK\r\nX-Test: Guzzle\r\n\r\n", (string)$response); $response = new Response(200, array( 'Content-Length' => 4 ), 'test'); $this->assertEquals("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest", (string)$response); } /** * @covers Guzzle\Http\Message\Response::factory */ public function testFactory() { $response = Response::factory("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest"); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('OK', $response->getReasonPhrase()); $this->assertEquals(4, $response->getContentLength()); $this->assertEquals('test', $response->getBody(true)); // Make sure that automatic Content-Length works $response = Response::factory("HTTP/1.1 200 OK\r\nContent-Length: x\r\n\r\ntest"); $this->assertEquals(4, $response->getContentLength()); $this->assertEquals('test', $response->getBody(true)); } /** * @covers Guzzle\Http\Message\Response::factory * @expectedException Guzzle\Http\HttpException */ public function testFactoryRequiresMessage() { $response = Response::factory(''); } /** * @covers Guzzle\Http\Message\Response::getBody */ public function testGetBody() { $body = EntityBody::factory(''); $response = new Response(403, new Collection(), $body); $this->assertEquals($body, $response->getBody()); } /** * @covers Guzzle\Http\Message\Response::getStatusCode */ public function testManagesStatusCode() { $response = new Response(403); $this->assertEquals(403, $response->getStatusCode()); } /** * @covers Guzzle\Http\Message\Response::getMessage */ public function testGetMessage() { $response = new Response(200, new Collection(array( 'Content-Length' => 4 )), 'body'); $this->assertEquals("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\nbody", $response->getMessage()); } /** * @covers Guzzle\Http\Message\Response::getRawHeaders */ public function testGetRawHeaders() { $response = new Response(200, new Collection(array( 'Keep-Alive' => 155, 'User-Agent' => 'Guzzle', 'Content-Length' => 4 )), 'body'); $this->assertEquals("HTTP/1.1 200 OK\r\nKeep-Alive: 155\r\nUser-Agent: Guzzle\r\nContent-Length: 4\r\n\r\n", $response->getRawHeaders()); } /** * @covers Guzzle\Http\Message\Response::getRequest */ public function testGetRequest() { $response = new Response(200, new Collection(), 'body'); $this->assertNull($response->getRequest()); $request = new \Guzzle\Http\Message\Request('GET', 'http://www.guzzle-project.com/'); $response->setRequest($request); $this->assertEquals($request, $response->getRequest()); } /** * @covers Guzzle\Http\Message\Response::getReasonPhrase * @covers Guzzle\Http\Message\Response::setStatus */ public function testHandlesStatusAndStatusCodes() { $response = new Response(200, new Collection(), 'body'); $this->assertEquals('OK', $response->getReasonPhrase()); $this->assertSame($response, $response->setStatus(204)); $this->assertEquals('No Content', $response->getReasonPhrase()); $this->assertEquals(204, $response->getStatusCode()); $this->assertSame($response, $response->setStatus(204, 'Testing!')); $this->assertEquals('Testing!', $response->getReasonPhrase()); $this->assertEquals(204, $response->getStatusCode()); try { $response->setStatus(2000); $this->fail('Did not throw expected exception when setting an invalid status'); } catch (BadResponseException $e) { } } /** * @covers Guzzle\Http\Message\Response::isClientError */ public function testIsClientError() { $response = new Response(403); $this->assertTrue($response->isClientError()); $response = new Response(200); $this->assertFalse($response->isClientError()); } /** * @covers Guzzle\Http\Message\Response::isError */ public function testIsError() { $response = new Response(403); $this->assertTrue($response->isError()); $response = new Response(200); $this->assertFalse($response->isError()); $response = new Response(500); $this->assertTrue($response->isError()); } /** * @covers Guzzle\Http\Message\Response::isInformational */ public function testIsInformational() { $response = new Response(100); $this->assertTrue($response->isInformational()); $response = new Response(200); $this->assertFalse($response->isInformational()); } /** * @covers Guzzle\Http\Message\Response::isRedirect */ public function testIsRedirect() { $response = new Response(301); $this->assertTrue($response->isRedirect()); $response = new Response(200); $this->assertFalse($response->isRedirect()); } /** * @covers Guzzle\Http\Message\Response::isServerError */ public function testIsServerError() { $response = new Response(500); $this->assertTrue($response->isServerError()); $response = new Response(400); $this->assertFalse($response->isServerError()); } /** * @covers Guzzle\Http\Message\Response::isSuccessful */ public function testIsSuccessful() { $response = new Response(200); $this->assertTrue($response->isSuccessful()); $response = new Response(403); $this->assertFalse($response->isSuccessful()); } /** * @covers Guzzle\Http\Message\Response::setRequest */ public function testSetRequest() { $response = new Response(200); $this->assertNull($response->getRequest()); $r = new \Guzzle\Http\Message\Request('GET', 'http://www.guzzle-project.com/'); $response->setRequest($r); $this->assertEquals($r, $response->getRequest()); } /** * @covers Guzzle\Http\Message\Response::getAcceptRanges */ public function testGetAcceptRanges() { $this->assertEquals('bytes', $this->response->getAcceptRanges()); } /** * @covers Guzzle\Http\Message\Response::getAge */ public function testGetAge() { $this->assertEquals(12, $this->response->getAge(false)); $this->assertEquals(12, $this->response->getAge(true)); $this->response->removeHeader('Age'); $this->response->removeHeader('Date'); $this->assertNull($this->response->getAge()); $this->response->setHeader('Date', Guzzle::getHttpDate(strtotime('-1 minute'))); // If the test runs slowly, still pass with a +5 second allowance $this->assertTrue($this->response->getAge() - 60 <= 5); $this->assertNull($this->response->getAge(true)); } /** * @covers Guzzle\Http\Message\Response::getAllow */ public function testGetAllow() { $this->assertEquals('GET, HEAD', $this->response->getAllow()); } /** * @covers Guzzle\Http\Message\Response::getCacheControl */ public function testGetCacheControl() { $this->assertEquals('no-cache', $this->response->getCacheControl()); } /** * @covers Guzzle\Http\Message\Response::getContentEncoding */ public function testGetContentEncoding() { $this->assertEquals('gzip', $this->response->getContentEncoding()); } /** * @covers Guzzle\Http\Message\Response::getContentLanguage */ public function testGetContentLanguage() { $this->assertEquals('da', $this->response->getContentLanguage()); } /** * @covers Guzzle\Http\Message\Response::getContentLength */ public function testGetContentLength() { $this->assertEquals('348', $this->response->getContentLength()); } /** * @covers Guzzle\Http\Message\Response::getContentLocation */ public function testGetContentLocation() { $this->assertEquals('/index.htm', $this->response->getContentLocation()); } /** * @covers Guzzle\Http\Message\Response::getContentDisposition */ public function testGetContentDisposition() { $this->assertEquals('attachment; filename=fname.ext', $this->response->getContentDisposition()); } /** * @covers Guzzle\Http\Message\Response::getContentMd5 */ public function testGetContentMd5() { $this->assertEquals('Q2hlY2sgSW50ZWdyaXR5IQ==', $this->response->getContentMd5()); } /** * @covers Guzzle\Http\Message\Response::getContentRange */ public function testGetContentRange() { $this->assertEquals('bytes 21010-47021/47022', $this->response->getContentRange()); } /** * @covers Guzzle\Http\Message\Response::getContentType */ public function testGetContentType() { $this->assertEquals('text/html; charset=utf-8', $this->response->getContentType()); } /** * @covers Guzzle\Http\Message\Response::getDate */ public function testGetDate() { $this->assertEquals('Tue, 15 Nov 1994 08:12:31 GMT', $this->response->getDate()); } /** * @covers Guzzle\Http\Message\Response::getEtag */ public function testGetEtag() { $this->assertEquals('737060cd8c284d8af7ad3082f209582d', $this->response->getEtag()); } /** * @covers Guzzle\Http\Message\Response::getExpires */ public function testGetExpires() { $this->assertEquals('Thu, 01 Dec 1994 16:00:00 GMT', $this->response->getExpires()); } /** * @covers Guzzle\Http\Message\Response::getLastModified */ public function testGetLastModified() { $this->assertEquals('Tue, 15 Nov 1994 12:45:26 GMT', $this->response->getLastModified()); } /** * @covers Guzzle\Http\Message\Response::getLocation */ public function testGetLocation() { $this->assertEquals('http://www.w3.org/pub/WWW/People.html', $this->response->getLocation()); } /** * @covers Guzzle\Http\Message\Response::getPragma */ public function testGetPragma() { $this->assertEquals('no-cache', $this->response->getPragma()); } /** * @covers Guzzle\Http\Message\Response::getProxyAuthenticate */ public function testGetProxyAuthenticate() { $this->assertEquals('Basic', $this->response->getProxyAuthenticate()); } /** * @covers Guzzle\Http\Message\Response::getRetryAfter */ public function testGetRetryAfter() { $this->assertEquals('120', $this->response->getRetryAfter()); } /** * @covers Guzzle\Http\Message\Response::getServer */ public function testGetServer() { $this->assertEquals('Apache/1.3.27 (Unix) (Red-Hat/Linux)', $this->response->getServer()); } /** * @covers Guzzle\Http\Message\Response::getSetCookie */ public function testGetSetCookie() { $this->assertEquals('UserID=JohnDoe; Max-Age=3600; Version=1', $this->response->getSetCookie()); } /** * @covers Guzzle\Http\Message\Response::getTrailer */ public function testGetTrailer() { $this->assertEquals('Max-Forwards', $this->response->getTrailer()); } /** * @covers Guzzle\Http\Message\Response::getTransferEncoding */ public function testGetTransferEncoding() { $this->assertEquals('chunked', $this->response->getTransferEncoding()); } /** * @covers Guzzle\Http\Message\Response::getVary */ public function testGetVary() { $this->assertEquals('*', $this->response->getVary()); } /** * @covers Guzzle\Http\Message\Response::getVia */ public function testReturnsViaHeader() { $this->assertEquals('1.0 fred, 1.1 nowhere.com (Apache/1.1)', $this->response->getVia()); } /** * @covers Guzzle\Http\Message\Response::getWarning */ public function testGetWarning() { $this->assertEquals('199 Miscellaneous warning', $this->response->getWarning()); } /** * @covers Guzzle\Http\Message\Response::getWwwAuthenticate */ public function testReturnsWwwAuthenticateHeader() { $this->assertEquals('Basic', $this->response->getWwwAuthenticate()); } /** * @covers Guzzle\Http\Message\Response::getConnection */ public function testReturnsConnectionHeader() { $this->assertEquals(null, $this->response->getConnection()); $this->response->setHeader('Connection', 'close'); $this->assertEquals('close', $this->response->getConnection()); } /** * @covers Guzzle\Http\Message\Response::getHeader */ public function testReturnsHeaders() { $this->assertEquals('Basic', $this->response->getHeader('WWW-Authenticate', null, true)); $this->assertEquals('chunked', $this->response->getHeader('Transfer-Encoding', null, false)); } /** * @covers Guzzle\Http\Message\Response::setInfo * @covers Guzzle\Http\Message\Response::getInfo */ public function testHasTransferInfo() { $stats = array ( 'url' => 'http://www.google.com/', 'content_type' => 'text/html; charset=ISO-8859-1', 'http_code' => 200, 'header_size' => 606, 'request_size' => 53, 'filetime' => -1, 'ssl_verify_result' => 0, 'redirect_count' => 0, 'total_time' => 0.093284, 'namelookup_time' => 0.001349, 'connect_time' => 0.01635, 'pretransfer_time' => 0.016358, 'size_upload' => 0, 'size_download' => 10330, 'speed_download' => 110737, 'speed_upload' => 0, 'download_content_length' => -1, 'upload_content_length' => 0, 'starttransfer_time' => 0.07066, 'redirect_time' => 0, ); // Uninitialized state $this->assertNull($this->response->getInfo('url')); $this->assertEquals(array(), $this->response->getInfo()); // Set the stats $this->response->setInfo($stats); $this->assertEquals($stats, $this->response->getInfo()); $this->assertEquals(606, $this->response->getInfo('header_size')); $this->assertNull($this->response->getInfo('does_not_exist')); } /** * @return Response */ private function getResponse($code, array $headers = null, EntityBody $body = null) { return new Response($code, $headers, $body); } /** * @covers Guzzle\Http\Message\Response::canCache */ public function testDeterminesIfItCanBeCached() { $this->assertTrue($this->getResponse(200)->canCache()); $this->assertTrue($this->getResponse(410)->canCache()); $this->assertFalse($this->getResponse(404)->canCache()); $this->assertTrue($this->getResponse(200, array( 'Cache-Control' => 'public' ))->canCache()); // This has the no-store directive $this->assertFalse($this->getResponse(200, array( 'Cache-Control' => 'private, no-store' ))->canCache()); // The body cannot be read, so it cannot be cached $tmp = tempnam('/tmp', 'not-readable'); $resource = fopen($tmp, 'w'); $this->assertFalse($this->getResponse(200, array( 'Transfer-Encoding' => 'chunked' ), EntityBody::factory($resource, 10))->canCache()); unlink($tmp); // The body is 0 length, cannot be read, so it can be cached $tmp = tempnam('/tmp', 'not-readable'); $resource = fopen($tmp, 'w'); $this->assertTrue($this->getResponse(200, array(array( 'Content-Length' => 0 )), EntityBody::factory($resource, 0))->canCache()); unlink($tmp); // When an HTTPS request is sent and the Cache-Control directive does // not include a 'public' value, then the response is not to be cached $request = RequestFactory::get('https://www.test.com/'); $response = new Response(200); $response->setRequest($request); $this->assertFalse($response->canCache()); // This response can be cache because it is public $response->setHeader('Cache-Control', 'public'); $this->assertTrue($response->canCache()); } /** * @covers Guzzle\Http\Message\Response::getMaxAge */ public function testDeterminesResponseMaxAge() { $this->assertEquals(null, $this->getResponse(200)->getMaxAge()); // Uses the response's s-maxage $this->assertEquals(140, $this->getResponse(200, array( 'Cache-Control' => 's-maxage=140' ))->getMaxAge()); // Uses the response's max-age $this->assertEquals(120, $this->getResponse(200, array( 'Cache-Control' => 'max-age=120' ))->getMaxAge()); // Uses the response's max-age $this->assertEquals(120, $this->getResponse(200, array( 'Cache-Control' => 'max-age=120', 'Expires' => Guzzle::getHttpDate('+1 day') ))->getMaxAge()); // Uses the Expires date $this->assertGreaterThanOrEqual(82400, $this->getResponse(200, array( 'Expires' => Guzzle::getHttpDate('+1 day') ))->getMaxAge()); // Uses the Expires date $this->assertGreaterThanOrEqual(82400, $this->getResponse(200, array( 'Expires' => Guzzle::getHttpDate('+1 day') ))->getMaxAge()); } /** * @covers Guzzle\Http\Message\Response::canValidate */ public function testDeterminesIfItCanValidate() { $response = new Response(200); $this->assertFalse($response->canValidate()); $response->setHeader('ETag', '123'); $this->assertTrue($response->canValidate()); $response->removeHeader('ETag'); $this->assertFalse($response->canValidate()); $response->setHeader('Last-Modified', '123'); $this->assertTrue($response->canValidate()); } /** * @covers Guzzle\Http\Message\Response::getFreshness * @covers Guzzle\Http\Message\Response::isFresh */ public function testCalculatesFreshness() { $response = new Response(200); $this->assertNull($response->isFresh()); $this->assertNull($response->getFreshness()); $response->addCacheControlDirective('max-age', 120); $response->setHeader('Age', 100); $this->assertEquals(20, $response->getFreshness()); $this->assertTrue($response->isFresh()); $response->setHeader('Age', 150); $this->assertEquals(-30, $response->getFreshness()); $this->assertFalse($response->isFresh()); } /** * @covers Guzzle\Http\Message\Response::setProtocol * @covers Guzzle\Http\Message\Response::getProtocol * @covers Guzzle\Http\Message\Response::getProtocolVersion */ public function testHandlesProtocols() { $this->assertSame($this->response, $this->response->setProtocol('HTTP/1.0')); $this->assertEquals('HTTP', $this->response->getProtocol()); $this->assertEquals('1.0', $this->response->getProtocolVersion()); } /** * @covers Guzzle\Http\Message\Response::isContentType */ public function testComparesContentType() { $response = new Response(200, array( 'Content-Type' => 'text/html; charset=ISO-8859-4' )); $this->assertTrue($response->isContentType('text/html')); $this->assertTrue($response->isContentType('TExT/html')); $this->assertTrue($response->isContentType('charset=ISO-8859-4')); $this->assertFalse($response->isContentType('application/xml')); } /** * @covers Guzzle\Http\Message\Response::isMethodAllowed */ public function testResponseDeterminesIfMethodIsAllowedBaseOnAllowHeader() { $response = new Response(200, array( 'Allow' => 'OPTIONS, POST, deletE,GET' )); $this->assertTrue($response->isMethodAllowed('get')); $this->assertTrue($response->isMethodAllowed('GET')); $this->assertTrue($response->isMethodAllowed('options')); $this->assertTrue($response->isMethodAllowed('post')); $this->assertTrue($response->isMethodAllowed('Delete')); $this->assertFalse($response->isMethodAllowed('put')); $this->assertFalse($response->isMethodAllowed('PUT')); $response = new Response(200); $this->assertFalse($response->isMethodAllowed('get')); } }tests/Guzzle/Tests/Http/Plugin/CachePluginTest.php000064400000037716151742733540016231 0ustar00 */ class CachePluginTest extends \Guzzle\Tests\GuzzleTestCase { /** * @var ArrayCache */ private $cache; /** * @var DoctrineCacheAdapter */ private $adapter; /** * Remove node.js generated Connection: keep-alive header * * @param string $response Response * * @return string */ protected function removeKeepAlive($response) { return str_replace("Connection: keep-alive\r\n", '', $response); } protected function setUp() { parent::setUp(); $this->cache = new ArrayCache(); $this->adapter = new DoctrineCacheAdapter($this->cache); } /** * @covers Guzzle\Http\Plugin\CachePlugin::__construct * @covers Guzzle\Http\Plugin\CachePlugin::getCacheAdapter */ public function testConstructorSetsValues() { $plugin = new CachePlugin($this->adapter, true, true, 1200); $this->assertEquals($this->adapter, $plugin->getCacheAdapter()); } /** * @covers Guzzle\Http\Plugin\CachePlugin::update * @covers Guzzle\Http\Plugin\CachePlugin::saveCache * @covers Guzzle\Http\Plugin\CachePlugin::getCacheKey * @covers Guzzle\Http\Plugin\CachePlugin::canResponseSatisfyRequest */ public function testSavesResponsesInCache() { // Send a 200 OK script to the testing server $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata"); // Create a new Cache plugin $plugin = new CachePlugin($this->adapter, true); // Make sure that non GET and HEAD requests are not attached $request = RequestFactory::post($this->getServer()->getUrl()); $request->getEventManager()->attach($plugin); $this->assertFalse($request->getEventManager()->hasObserver($plugin)); // Create a new Request $request = RequestFactory::get($this->getServer()->getUrl()); $request->getEventManager()->attach($plugin); $this->assertTrue($request->getEventManager()->hasObserver($plugin)); // Send the Request to the test server $request->send(); // Calculate the cache key like the cache plugin does $key = $plugin->getCacheKey($request); // Make sure that the cache plugin set the request in cache $this->assertNotNull($this->adapter->fetch($key)); // Clear out the requests stored on the server to make sure we didn't send a new request $this->getServer()->flush(); // Test that the request is set manually // The test server has no more script data, so if it actually sends a // request it will fail the test. $request2 = RequestFactory::get($this->getServer()->getUrl()); $request2->getEventManager()->attach($plugin); $request2->send(); $this->assertEquals('data', $request2->getResponse()->getBody(true)); // Make sure a request wasn't sent $this->assertEquals(0, count($this->getServer()->getReceivedRequests(false))); } /** * @covers Guzzle\Http\Plugin\CachePlugin::update * @covers Guzzle\Http\Plugin\CachePlugin::saveCache */ public function testSkipsNonReadableResponseBodies() { // Send a 200 OK script to the testing server $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata"); // Create a new Cache plugin $plugin = new CachePlugin($this->adapter, true); // Create a new Client using the Cache plugin $request = RequestFactory::get($this->getServer()->getUrl()); $request->getEventManager()->attach($plugin); // Create a temp file that is not readable $tempFile = tempnam('/tmp', 'temp_stream_data'); // Set the non-readable stream as the response body so that it can't be cached $request->setResponseBody(EntityBody::factory( fopen($tempFile, 'w') )); $request->send(); // Calculate the cache key like the cache plugin does $key = $plugin->getCacheKey($request); // Make sure that the cache plugin set the request in cache $this->assertFalse($this->adapter->fetch($key)); // Clean up the test unset($request); unlink($tempFile); } public function cacheKeyDataProvider() { $r = array( array('', 'gzrq_http&www.test.com/path?q=abc&Host=www.test.com&Date=123', 'http://www.test.com/path?q=abc', "Host: Google.com\r\nDate: 123"), array('query = q', 'gzrq_http&www.test.com/path&Host=www.test.com&Date=123', 'http://www.test.com/path?q=abc', "Host: Google.com\r\nDate: 123"), array('query=q; header=Date;', 'gzrq_http&www.test.com/path&Host=www.test.com', 'http://www.test.com/path?q=abc', "Host: Google.com\r\nDate: 123"), array('query=a, q; header=Date, Host;', 'gzrq_http&www.test.com/path&', 'http://www.test.com/path?q=abc&a=123', "Host: Google.com\r\nDate: 123"), ); return $r; } /** * @covers Guzzle\Http\Plugin\CachePlugin::getCacheKey * @dataProvider cacheKeyDataProvider */ public function testCreatesCacheKeysUsingFilters($filter, $key, $url, $headers = null) { // Create a new Cache plugin $plugin = new CachePlugin($this->adapter, true); // Generate the header array $h = null; if ($headers) { $h = array(); foreach (explode("\r\n", $headers) as $header) { list($k, $v) = explode(': ', $header); $h[$k] = $v; } } // Create the request $request = RequestFactory::get($url, $h); $request->getParams()->set('cache.key_filter', $filter); $request->removeHeader('User-Agent'); $this->assertEquals($key, $plugin->getCacheKey($request, true)); // Make sure that the encoded request is returned when $raw is false $this->assertNotEquals($key, $plugin->getCacheKey($request)); unset($request); unset($plugin); } /** * @covers Guzzle\Http\Plugin\CachePlugin::getCacheKey */ public function testCreatesEncodedKeys() { $plugin = new CachePlugin($this->adapter, true); $request = RequestFactory::fromMessage( "GET / HTTP/1.1\r\nHost: www.test.com\r\nCache-Control: no-cache, no-store, max-age=120" ); $key = $plugin->getCacheKey($request); $this->assertEquals(1, preg_match('/^gzrq_[a-z0-9]{32}$/', $key)); // Make sure that the same value is returned in a subsequent call $this->assertEquals($key, $plugin->getCacheKey($request)); } /** * @covers Guzzle\Http\Plugin\CachePlugin::update * @covers Guzzle\Http\Plugin\CachePlugin::saveCache */ public function testRequestsCanOverrideTtlUsingCacheParam() { $plugin = new CachePlugin($this->adapter, true); $request = new Request('GET', 'http://www.test.com/'); $request->getParams()->set('cache.override_ttl', 1000); $request->getEventManager()->attach($plugin);$request->setResponse(Response::factory("HTTP/1.1 200 OK\r\nCache-Control: max-age=100\r\nContent-Length: 4\r\n\r\nData"), true); $request->send(); $request2 = new Request('GET', 'http://www.test.com/'); $request2->getEventManager()->attach($plugin); $response = $request2->send(); $this->assertEquals(1000, $response->getHeader('X-Guzzle-Ttl')); } /** * @covers Guzzle\Http\Plugin\CachePlugin::canResponseSatisfyRequest * @covers Guzzle\Http\Plugin\CachePlugin::update * @covers Guzzle\Http\Plugin\CachePlugin::saveCache */ public function testRequestsCanAcceptStaleResponses() { $server = $this->getServer(); $plugin = new CachePlugin($this->adapter, true); $request = new Request('GET', $server->getUrl() . 'test'); $request->getEventManager()->attach($plugin); // Cache this response for 1000 seconds if it is cacheable $request->getParams()->set('cache.override_ttl', 1000); $request->setResponse(Response::factory("HTTP/1.1 200 OK\r\nExpires: " . Guzzle::getHttpDate('-1 second') . "\r\nContent-Length: 4\r\n\r\nData"), true); $request->send(); sleep(1); // Accept responses that are up to 100 seconds expired $request2 = new Request('GET', $server->getUrl() . 'test'); $request2->getEventManager()->attach($plugin); $request2->addCacheControlDirective('max-stale', 100); $response = $request2->send(); $this->assertEquals(1000, $response->getHeader('X-Guzzle-Ttl')); // Accepts any stale response $request3 = new Request('GET', $server->getUrl() . 'test'); $request3->addCacheControlDirective('max-stale'); $request3->getEventManager()->attach($plugin); $response = $request3->send(); $this->assertEquals(1000, $response->getHeader('X-Guzzle-Ttl')); // Will not accept the stale cached entry $server->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\nData"); $request4 = new Request('GET', $server->getUrl() . 'test'); $request4->addCacheControlDirective('max-stale', 0); $request4->getEventManager()->attach($plugin); $response = $request4->send(); $this->assertEquals("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\nData", $this->removeKeepAlive((string) $response)); } /** * @covers Guzzle\Http\Plugin\CachePlugin::canResponseSatisfyRequest */ public function testChecksIfResponseCanSatisfyRequest() { $plugin = new CachePlugin($this->adapter, true); // Send some responses to the test server for cache validation $server = $this->getServer(); // No restrictions $request = RequestFactory::get($server->getUrl()); $response = new Response(200, array('Date' => Guzzle::getHttpDate('now'))); $this->assertTrue($plugin->canResponseSatisfyRequest($request, $response)); // Request max-age is less than response age $request = RequestFactory::get($server->getUrl()); $request->addCacheControlDirective('max-age', 100); $response = new Response(200, array('Age' => 10)); $this->assertTrue($plugin->canResponseSatisfyRequest($request, $response)); // Request must have something fresher than 200 seconds $response->setHeader('Date', Guzzle::getHttpDate('-200 days')); $response->removeHeader('Age'); $request->setHeader('Cache-Control', 'max-age=200'); $this->assertFalse($plugin->canResponseSatisfyRequest($request, $response)); // Response says it's too old $request->removeHeader('Cache-Control'); $response->setHeader('Cache-Control', 'max-age=86400'); $this->assertFalse($plugin->canResponseSatisfyRequest($request, $response)); // Response is OK $response->setHeader('Date', Guzzle::getHttpDate('-1 hour')); $this->assertTrue($plugin->canResponseSatisfyRequest($request, $response)); } /** * Data provider to test cache revalidation * * @return array */ public function cacheRevalidationDataProvider() { return array( // Forces revalidation that passes array( true, "Pragma: no-cache\r\n\r\n", "HTTP/1.1 200 OK\r\nDate: " . Guzzle::getHttpDate('-100 hours') . "\r\nContent-Length: 4\r\n\r\nData", "HTTP/1.1 304 NOT MODIFIED\r\nCache-Control: max-age=2000000\r\n\r\n", ), // Forces revalidation that overwrites what is in cache array( false, "\r\n\r\n", "HTTP/1.1 200 OK\r\nCache-Control: must-revalidate, no-cache\r\nDate: " . Guzzle::getHttpDate('-10 hours') . "\r\nContent-Length: 4\r\n\r\nData", "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nDatas", "HTTP/1.1 200 OK\r\nContent-Length: 5\r\nDate: " . Guzzle::getHttpDate('now') . "\r\n\r\nDatas" ), // Skips revalidation because the request is accepting the cached copy array( false, "\r\n\r\n", "HTTP/1.1 200 OK\r\nCache-Control: no-cache\r\nDate: " . Guzzle::getHttpDate('-3 hours') . "\r\nContent-Length: 4\r\n\r\nData", null, null, 'decline' ), // Must get a fresh copy because the request is declining revalidation array( true, "\r\n\r\n", "HTTP/1.1 200 OK\r\nCache-Control: no-cache\r\nDate: " . Guzzle::getHttpDate('-3 hours') . "\r\nContent-Length: 4\r\n\r\nData", null, null, 'accept' ), // Throws an exception during revalidation array( false, "\r\n\r\n", "HTTP/1.1 200 OK\r\nCache-Control: no-cache\r\nDate: " . Guzzle::getHttpDate('-3 hours') . "\r\n\r\nData", "HTTP/1.1 500 INTERNAL SERVER ERROR\r\nContent-Length: 0\r\n\r\n" ), // ETag mismatch array( false, "\r\n\r\n", "HTTP/1.1 200 OK\r\nCache-Control: no-cache\r\nETag: \"123\"\r\nDate: " . Guzzle::getHttpDate('-10 hours') . "\r\n\r\nData", "HTTP/1.1 304 NOT MODIFIED\r\nETag: \"123456\"\r\n\r\n", ), ); } /** * @covers Guzzle\Http\Plugin\CachePlugin::canResponseSatisfyRequest * @covers Guzzle\Http\Plugin\CachePlugin::revalidate * @dataProvider cacheRevalidationDataProvider */ public function testRevalidatesResponsesAgainstOriginServer($can, $request, $response, $validate = null, $result = null, $param = null) { // Send some responses to the test server for cache validation $server = $this->getServer(); $plugin = new CachePlugin($this->adapter, true); $server->flush(); if ($validate) { $server->enqueue($validate); } $request = RequestFactory::fromMessage("GET / HTTP/1.1\r\nHost: 127.0.0.1:" . $server->getPort() . "\r\n" . $request); if ($param) { $request->getParams()->set('cache.revalidate', $param); } $response = Response::factory($response); $this->assertEquals($can, $plugin->canResponseSatisfyRequest($request, $response)); if ($result) { // Get rid of dates $this->assertEquals( preg_replace('/(Date:\s)(.*)(\r\n)/', '$1$3', (string) $result), preg_replace('/(Date:\s)(.*)(\r\n)/', '$1$3', (string) $request->getResponse()) ); } if ($validate) { $this->assertEquals(1, count($server->getReceivedRequests())); } } /** * @covers Guzzle\Http\Plugin\CachePlugin */ public function testCachesResponsesAndHijacksRequestsWhenApplicable() { $plugin = new CachePlugin($this->adapter, true); $server = $this->getServer(); $server->enqueue("HTTP/1.1 200 OK\r\nCache-Control: max-age=1000\r\nContent-Length: 4\r\n\r\nData"); $request = new Request('GET', $server->getUrl()); $request->getCurlOptions()->set(\CURLOPT_TIMEOUT, 2); $request->getEventManager()->attach($plugin); $request2 = new Request('GET', $server->getUrl()); $request2->getCurlOptions()->set(\CURLOPT_TIMEOUT, 2); $request2->getEventManager()->attach($plugin); $request->send(); $request2->send(); $this->assertEquals(true, $request2->getResponse()->hasHeader('X-Guzzle-Cache')); } }tests/Guzzle/Tests/Http/Plugin/CookiePluginTest.php000064400000054457151742733540016440 0ustar00 */ class CookiePluginTest extends \Guzzle\Tests\GuzzleTestCase { /** * @var CookiePlugin */ private $plugin; /** * @var ArrayCookieJar */ private $storage; public function setUp() { $this->storage = new ArrayCookieJar(); $this->plugin = new CookiePlugin($this->storage); } /** * Provides the parsed information from a cookie * * @return array */ public function cookieParserDataProvider() { return array( array( 'ASIHTTPRequestTestCookie=This+is+the+value; expires=Sat, 26-Jul-2008 17:00:42 GMT; path=/tests; domain=allseeing-i.com; PHPSESSID=6c951590e7a9359bcedde25cda73e43c; path=/";', array( 'domain' => 'allseeing-i.com', 'path' => '/', 'data' => array( 'PHPSESSID' => '6c951590e7a9359bcedde25cda73e43c' ), 'max_age' => NULL, 'expires' => 'Sat, 26-Jul-2008 17:00:42 GMT', 'version' => NULL, 'secure' => NULL, 'discard' => NULL, 'port' => NULL, 'cookies' => array( 'ASIHTTPRequestTestCookie=This is the value' ), 'comment' => null, 'comment_url' => null, 'http_only' => false ) ), array('', false), array('foo', false), // Test setting a blank value for a cookie array(array( 'foo=', 'foo =', 'foo =;', 'foo= ;', 'foo =', 'foo= '), array( 'cookies' => array( 'foo' ), 'data' => array(), 'discard' => null, 'domain' => null, 'expires' => null, 'max_age' => null, 'path' => '/', 'port' => null, 'secure' => null, 'version' => null, 'comment' => null, 'comment_url' => null, 'http_only' => false ) ), // Test setting a value and removing quotes array(array( 'foo=1', 'foo =1', 'foo =1;', 'foo=1 ;', 'foo =1', 'foo= 1', 'foo = 1 ;', 'foo="1"', 'foo="1";', 'foo= "1";'), array( 'cookies' => array( 'foo=1' ), 'data' => array(), 'discard' => null, 'domain' => null, 'expires' => null, 'max_age' => null, 'path' => '/', 'port' => null, 'secure' => null, 'version' => null, 'comment' => null, 'comment_url' => null, 'http_only' => false ) ), // Test setting multiple values array(array( 'foo=1; bar=2', 'foo =1; bar = "2"', 'foo=1; bar=2'), array( 'cookies' => array( 'foo=1', 'bar=2' ), 'data' => array(), 'discard' => null, 'domain' => null, 'expires' => null, 'max_age' => null, 'path' => '/', 'port' => null, 'secure' => null, 'version' => null, 'comment' => null, 'comment_url' => null, 'http_only' => false ) ), // Tests getting the domain and path from a reference request array(array( 'foo=1; port="80,8081"; httponly', 'foo=1; port="80,8081"; domain=www.test.com; HttpOnly;', 'foo=1; ; domain=www.test.com; path=/path/; port="80,8081"; HttpOnly;'), array( 'cookies' => array( 'foo=1' ), 'data' => array(), 'discard' => null, 'domain' => 'www.test.com', 'expires' => null, 'max_age' => null, 'path' => '/path/', 'port' => array('80', '8081'), 'secure' => null, 'version' => null, 'comment' => null, 'comment_url' => null, 'http_only' => true ), 'http://www.test.com/path/' ), // Some of the following tests are based on http://framework.zend.com/svn/framework/standard/trunk/tests/Zend/Http/CookieTest.php array( 'justacookie=foo; domain=example.com', array( 'cookies' => array( 'justacookie=foo' ), 'domain' => 'example.com', 'path' => '', 'data' => array(), 'discard' => null, 'expires' => null, 'max_age' => null, 'path' => '/', 'port' => null, 'secure' => null, 'version' => null, 'comment' => null, 'comment_url' => null, 'http_only' => false ) ), array( 'expires=tomorrow; secure; path=/Space Out/; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=.example.com', array( 'cookies' => array( 'expires=tomorrow' ), 'domain' => '.example.com', 'path' => '/Space Out/', 'expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', 'data' => array(), 'discard' => null, 'port' => null, 'secure' => true, 'version' => null, 'max_age' => null, 'comment' => null, 'comment_url' => null, 'http_only' => false ) ), array( 'domain=unittests; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=example.com; path=/some value/', array( 'cookies' => array( 'domain=unittests' ), 'domain' => 'example.com', 'path' => '/some value/', 'expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', 'secure' => false, 'data' => array(), 'discard' => null, 'max_age' => null, 'port' => null, 'version' => null, 'comment' => null, 'comment_url' => null, 'http_only' => false ) ), array( 'path=indexAction; path=/; domain=.foo.com; expires=Tue, 21-Nov-2006 08:33:44 GMT', array( 'cookies' => array( 'path=indexAction' ), 'domain' => '.foo.com', 'path' => '/', 'expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', 'secure' => false, 'data' => array(), 'discard' => null, 'max_age' => null, 'port' => null, 'version' => null, 'comment' => null, 'comment_url' => null, 'http_only' => false ) ), array( 'secure=sha1; secure; SECURE; domain=some.really.deep.domain.com; version=1; Max-Age=86400', array( 'cookies' => array( 'secure=sha1' ), 'domain' => 'some.really.deep.domain.com', 'path' => '/', 'secure' => true, 'data' => array(), 'discard' => null, 'expires' => time() + 86400, 'max_age' => 86400, 'port' => null, 'version' => 1, 'comment' => null, 'comment_url' => null, 'http_only' => false ) ), array( 'PHPSESSID=123456789+abcd%2Cef; secure; discard; domain=.localdomain; path=/foo/baz; expires=Tue, 21-Nov-2006 08:33:44 GMT;', array( 'cookies' => array( 'PHPSESSID=123456789 abcd,ef' ), 'domain' => '.localdomain', 'path' => '/foo/baz', 'expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', 'secure' => true, 'data' => array(), 'discard' => true, 'max_age' => null, 'port' => null, 'version' => null, 'comment' => null, 'comment_url' => null, 'http_only' => false ) ), ); } /** * @dataProvider cookieParserDataProvider * @covers Guzzle\Http\Plugin\CookiePlugin */ public function testParseCookie($cookie, $parsed, $url = null) { $request = null; if ($url) { $request = RequestFactory::get($url); } foreach ((array) $cookie as $c) { $p = CookiePlugin::parseCookie($c, $request); // Remove expires values from the assertion if they are relatively equal if ($p['expires'] != $parsed['expires']) { if (abs($p['expires'] - $parsed['expires']) < 20) { unset($p['expires']); unset($parsed['expires']); } } if (is_array($parsed)) { foreach ($parsed as $key => $value) { $this->assertEquals($parsed[$key], $p[$key], 'Comparing ' . $key); } foreach ($p as $key => $value) { $this->assertEquals($p[$key], $parsed[$key], 'Comparing ' . $key); } } else { $this->assertEquals($parsed, $p); } } } /** * @covers Guzzle\Http\Plugin\CookiePlugin */ public function testClearsCookiesWhenShuttingDown() { $this->storage->save(array( 'domain' => '.example.com', 'cookie' => array('a', '123') )); $this->assertEquals(1, count($this->storage->getCookies())); unset($this->plugin); $this->assertEquals(0, count($this->storage->getCookies())); } /** * @covers Guzzle\Http\Plugin\CookiePlugin */ public function testClearsTemporaryCookies() { $this->storage->save(array( 'domain' => '.example.com', 'cookie' => array('a', '123') )); $this->assertEquals(1, count($this->storage->getCookies())); $this->plugin->clearTemporaryCookies(); $this->assertEquals(0, count($this->storage->getCookies())); } /** * @covers Guzzle\Http\Plugin\CookiePlugin */ public function testClearsCookies() { $this->assertSame($this->storage, $this->storage->save(array( 'domain' => '.example.com', 'cookie' => array('a', '123') ))); $this->assertSame($this->storage, $this->storage->save(array( 'domain' => 'example.com', 'cookie' => array('b', '123') ))); $this->assertEquals(2, count($this->storage->getCookies())); $this->assertEquals(1, $this->plugin->clearCookies('http://example.com')); $this->assertEquals(1, count($this->storage->getCookies())); } /** * @covers Guzzle\Http\Plugin\CookiePlugin */ public function testExtractsAndStoresCookies() { $cookie = array( array( 'domain' => '.example.com', 'path' => '/', 'max_age' => '86400', 'expires' => time() + 86400, 'version' => '1', 'secure' => true, 'port' => array('80', '8081'), 'discard' => true, 'comment' => NULL, 'comment_url' => NULL, 'http_only' => false, 'data' => array(), 'cookie' => array('a', 'b') ), array ( 'domain' => '.example.com', 'path' => '/', 'max_age' => '86400', 'expires' => time() + 86400, 'version' => '1', 'secure' => true, 'port' => array('80','8081'), 'discard' => true, 'comment' => NULL, 'comment_url' => NULL, 'http_only' => false, 'data' => array(), 'cookie' => array ('c', 'd') ), ); $response = Response::factory("HTTP/1.1 200 OK\r\nSet-Cookie: a=b; c=d; port=\"80,8081\"; version=1; Max-Age=86400; domain=.example.com; discard; secure;\r\n\r\n"); $result = $this->plugin->extractCookies($response); $this->assertTrue(abs($result[0]['expires'] - $cookie[0]['expires']) < 10, 'Cookie #1 expires dates are too different: ' . $result[0]['expires'] . ' vs ' . $cookie[0]['expires']); $this->assertTrue(abs($result[1]['expires'] - $cookie[1]['expires']) < 10, 'Cookie #2 expires dates are too different: ' . $result[1]['expires'] . ' vs ' . $cookie[1]['expires']); unset($cookie[0]['expires']); unset($cookie[1]['expires']); unset($result[0]['expires']); unset($result[1]['expires']); $this->assertEquals($cookie, $result); $this->assertEquals(2, count($this->storage->getCookies())); $result = $this->storage->getCookies(); unset($result[0]['expires']); unset($result[1]['expires']); $this->assertEquals($cookie, $result); // Clear out the currently held cookies $this->assertEquals(2, $this->storage->clear()); $this->assertEquals(0, count($this->storage->getCookies())); // Create a new request, attach the cookie plugin, set a mock response $request = new Request('GET', 'http://www.example.com/'); $request->getEventManager()->attach($this->plugin); $request->setResponse($response, true); $request->send(); // Assert that the plugin caught the cookies in the response $this->assertEquals(2, count($this->storage->getCookies())); } /** * @covers Guzzle\Http\Plugin\CookiePlugin */ public function testAddsCookiesToRequests() { ArrayCookieJarTest::addCookies($this->storage); $this->storage->save(array( 'domain' => '.y.example.com', 'path' => '/acme/', 'cookie' => array('secure', 'sec'), 'expires' => Guzzle::getHttpDate('+1 day'), 'secure' => true )); // Add a cookie that is only set on a specific port, so it wont be // added to the following requests $this->storage->save(array( 'domain' => '.y.example.com', 'path' => '/acme/', 'cookie' => array('test', 'port'), 'expires' => Guzzle::getHttpDate('+1 day'), 'secure' => false, 'port' => array(8192) )); $request1 = new Request('GET', 'https://a.y.example.com/acme/'); $request2 = new Request('GET', 'https://a.y.example.com/acme/'); $request3 = new Request('GET', 'http://a.y.example.com/acme/'); $request4 = new Request('GET', 'http://a.y.example.com/acme/'); $request1->getEventManager()->attach($this->plugin); $request2->getEventManager()->attach($this->plugin); $request3->getEventManager()->attach($this->plugin); $request4->getEventManager()->attach($this->plugin); // Set a secure cookie $response1 = Response::factory("HTTP/1.1 200 OK\r\nSet-Cookie: a=b; c=d; Max-Age=86400; domain=.example.com; secure;\r\n\r\n"); // Set a regular cookie $response2 = Response::factory("HTTP/1.1 200 OK\r\nSet-Cookie: e=f h; discard; domain=.example.com;\r\n\r\n"); $response3 = Response::factory("HTTP/1.1 200 OK\r\n\r\n"); $request1->setResponse($response1, true); $request2->setResponse($response2, true); $request3->setResponse($response3, true); $request1->send(); $request2->send(); $request3->send(); $this->assertEquals('muppet=cookie_monster;secure=sec', (string) $request1->getCookie()); $this->assertEquals('muppet=cookie_monster;secure=sec;a=b;c=d', (string) $request2->getCookie()); $this->assertEquals('muppet=cookie_monster;e=f h', (string) $request3->getCookie()); // Clear the e=f h temporary cookie $this->plugin->clearTemporaryCookies(); $request4->setResponse($response3, true); $request4->send(); $this->assertEquals('muppet=cookie_monster', (string) $request4->getCookie()); } /** * @covers Guzzle\Http\Plugin\CookiePlugin */ public function testExtractsMultipleCookies() { $this->plugin->clearCookies(); $response = Response::factory( "HTTP/1.1 200 OK\r\n" . "Set-Cookie: IU=deleted; expires=Wed, 03-Mar-2010 02:17:39 GMT; path=/; domain=127.0.0.1\r\n" . "Set-Cookie: PH=deleted; expires=Wed, 03-Mar-2010 02:17:39 GMT; path=/; domain=127.0.0.1\r\n" . "Set-Cookie: fpc=d=.Hm.yh4.1XmJWjJfs4orLQzKzPImxklQoxXSHOZATHUSEFciRueW_7704iYUtsXNEXq0M92Px2glMdWypmJ7HIQl6XIUvrZimWjQ3vIdeuRbI.FNQMAfcxu_XN1zSx7l.AcPdKL6guHc2V7hIQFhnjRW0rxm2oHY1P4bGQxFNz7f.tHm12ZD3DbdMDiDy7TBXsuP4DM-&v=2; expires=Fri, 02-Mar-2019 02:17:40 GMT; path=/; domain=127.0.0.1\r\n" . "Set-Cookie: FPCK3=AgBNbvoQAGpGEABZLRAAbFsQAF1tEABkDhAAeO0=; expires=Sat, 02-Apr-2019 02:17:40 GMT; path=/; domain=127.0.0.1\r\n" . "Set-Cookie: CH=deleted; expires=Wed, 03-Mar-2010 02:17:39 GMT; path=/; domain=127.0.0.1\r\n" . "Set-Cookie: CH=AgBNbvoQAAEcEAApuhAAMJcQADQvEAAvGxAALe0QAD6uEAATwhAAC1AQAC8t; expires=Sat, 02-Apr-2019 02:17:40 GMT; path=/; domain=127.0.0.1\r\n" . "Set-Cookie: fpt=d=_e2d6jLXesxx4AoiC0W7W3YktnpITDTHoJ6vNxF7TU6JEep6Y5BFk7Z9NgHmhiXoB7jGV4uR_GBQtSDOLjflKBUVZ6UgnGmDztoj4GREK30jm1qDgReyhPv7iWaN8e8ZLpUKXtPioOzQekGha1xR8ZqGR25GT7aYQpcxaaY.2ATjTpbm7HmX8tlBIte6mYMwFpIh_krxtofGPH3R337E_aNF3illhunC5SK6I0IfZvHzBXCoxu9fjH6e0IHzyOBY656YMUIElQiDkSd8werkBIRE6LJi6YU8AWgitEpMLisOIQSkqyGiahcPFt_fsD8DmIX2YAdSeVE0KycIqd0Z9aM7mdJ3xNQ4dmOOfcZ83dDrZ.4hvuKN2jB2FQDKuxEjTVO4DmiCCSyYgcs2wh0Lc3RODVKzqAZNMTYltWMELw9JdUyDFD3EGT3ZCnH8NQ6f_AAWffyj92ZMLYfWJnXHSG.DTKlVHj.IsihVT73QzrfoMFIs&v=1; path=/; domain=127.0.0.1\r\n" . "Set-Cookie: fpps=deleted; expires=Wed, 03-Mar-2010 02:17:39 GMT; path=/; domain=127.0.0.1\r\n" . "Set-Cookie: fpc_s=d=ng6sEJk.1XnLUt1pfJ2kiUon07QEppAUuwW3nk0tYwcHMQ1CijnSGVZHfgvWSXQxE5eW_1hjvDAA4Nu0CSSn2xk9_.DOkKI_fZLLLUrm0hJ41VMbSUTrklw.u5IlTM5JCeK_PDjSjZNkvHMbNYziu8vwd8fMnbecf9bSo3eDDv1boowyLFk_9mnGYBeSI4U86mnm.mnfOHMARxzL6BVMTAblIAml65cR486SHzPVO6KNYvkqh8zP3m0hVIkRaPhzvDjQkDG28HCbMjq745QR2FcCmI4TNJbk7EtJmsBrlL8wvVyX5DiBmP9W990-&v=2; path=/; domain=127.0.0.1\r\n" . "Content-Length: 0\r\n\r\n" ); $this->getServer()->enqueue(array( (string) $response, "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\rn" )); $request = RequestFactory::get($this->getServer()->getUrl()); $request->getEventManager()->attach($this->plugin); $request->send(); $this->assertNull($request->getHeader('Cookie')); $request->setState('new'); $request->send(); $this->assertNotNull($request->getHeader('Cookie')); // Doesn't send expired cookies $this->assertEmpty($request->getCookie('IU')); $this->assertEmpty($request->getCookie('PH')); $this->assertNotEmpty($request->getCookie('fpc')); $this->assertNotEmpty($request->getCookie('FPCK3')); $this->assertNotEmpty($request->getCookie('CH')); $this->assertNotEmpty($request->getCookie('fpt')); $this->assertNotEmpty($request->getCookie('fpc_s')); $this->assertNotEmpty($request->getCookie('CH')); $this->assertNotEmpty($request->getCookie('CH')); $this->assertEquals(9, count($this->plugin->extractCookies($response))); } /** * @covers Guzzle\Http\Plugin\CookiePlugin::update */ public function testCookiesAreExtractedFromRedirectResponses() { $this->getServer()->enqueue(array( "HTTP/1.1 302 Moved Temporarily\r\n" . "Set-Cookie: test=583551; expires=Wednesday, 23-Mar-2050 19:49:45 GMT; path=/\r\n" . "Location: /redirect\r\n\r\n", "HTTP/1.1 200 OK\r\n" . "Content-Length: 0\r\n\r\n", "HTTP/1.1 200 OK\r\n" . "Content-Length: 0\r\n\r\n" )); $request = RequestFactory::get($this->getServer()->getUrl()); $request->getEventManager()->attach($this->plugin); $request->send(); $request = RequestFactory::get($this->getServer()->getUrl()); $request->getEventManager()->attach($this->plugin); $request->send(); $this->assertEquals('test=583551', $request->getHeader('Cookie')); } }tests/Guzzle/Tests/Http/Plugin/ExponentialBackoffTest.php000064400000012306151742733540017575 0ustar00 */ class ExponentialBackoffPluginTest extends \Guzzle\Tests\GuzzleTestCase { public function delayClosure($retries) { return 0; } /** * @covers Guzzle\Http\Plugin\ExponentialBackoffPlugin * @covers Guzzle\Http\Plugin\ExponentialBackoffPlugin::__construct * @covers Guzzle\Http\Plugin\ExponentialBackoffPlugin::getFailureCodes * @covers Guzzle\Http\Plugin\ExponentialBackoffPlugin::getMaxRetries * @covers Guzzle\Http\Plugin\ExponentialBackoffPlugin::setMaxRetries * @covers Guzzle\Http\Plugin\ExponentialBackoffPlugin::setFailureCodes */ public function testConstructsCorrectly() { $plugin = new ExponentialBackoffPlugin(2, array(500, 503, 502), array($this, 'delayClosure')); $this->assertEquals(2, $plugin->getMaxRetries()); $this->assertEquals(array(500, 503, 502), $plugin->getFailureCodes()); // You can specify any codes you want... Probably not a good idea though $plugin->setFailureCodes(array(200, 204)); $this->assertEquals(array(200, 204), $plugin->getFailureCodes()); } /** * @covers Guzzle\Http\Plugin\ExponentialBackoffPlugin::calculateWait */ public function testCalculateWait() { $plugin = new ExponentialBackoffPlugin(2); $this->assertEquals(1, $plugin->calculateWait(0)); $this->assertEquals(2, $plugin->calculateWait(1)); $this->assertEquals(4, $plugin->calculateWait(2)); $this->assertEquals(8, $plugin->calculateWait(3)); $this->assertEquals(16, $plugin->calculateWait(4)); } /** * @covers Guzzle\Http\Plugin\ExponentialBackoffPlugin */ public function testRetriesRequests() { // Create a script to return several 500 and 503 response codes $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata" )); // Clear out other requests that have been received by the server $this->getServer()->flush(); $plugin = new ExponentialBackoffPlugin(2, null, array($this, 'delayClosure')); $request = RequestFactory::get($this->getServer()->getUrl()); $request->getEventManager()->attach($plugin); $request->send(); // Make sure it eventually completed successfully $this->assertEquals(200, $request->getResponse()->getStatusCode()); $this->assertEquals('OK', $request->getResponse()->getReasonPhrase()); $this->assertEquals('data', $request->getResponse()->getBody(true)); // Check that three requests were made to retry this request $this->assertEquals(3, count($this->getServer()->getReceivedRequests(false))); } /** * @covers Guzzle\Http\Plugin\ExponentialBackoffPlugin::update * @covers Guzzle\Http\Message\Request * @expectedException Guzzle\Http\Message\BadResponseException */ public function testAllowsFailureOnMaxRetries() { $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n" )); $plugin = new ExponentialBackoffPlugin(2, null, array($this, 'delayClosure')); $request = RequestFactory::get($this->getServer()->getUrl()); $request->getEventManager()->attach($plugin); // This will fail because the plugin isn't retrying the request because // the max number of retries is exceeded (1 > 0) $request->send(); } /** * @covers Guzzle\Http\Plugin\ExponentialBackoffPlugin::update * @covers Guzzle\Http\Pool\Pool */ public function testRetriesPooledRequestsUsingDelayAndPollingEvent() { $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata" )); // Need to sleep for one second to make sure that the polling works // correctly in the observer $plugin = new ExponentialBackoffPlugin(1, null, function($r) { return 1; }); $request = RequestFactory::get($this->getServer()->getUrl()); $request->getEventManager()->attach($plugin); $pool = new Pool(); $pool->add($request); $pool->send(); // Make sure it eventually completed successfully $this->assertEquals('data', $request->getResponse()->getBody(true)); // Check that two requests were made to retry this request $this->assertEquals(2, count($this->getServer()->getReceivedRequests(false))); } }tests/Guzzle/Tests/Http/Plugin/HistoryPluginTest.php000064400000007343151742733540016660 0ustar00 */ class HistoryPluginTest extends \Guzzle\Tests\GuzzleTestCase { /** * Adds multiple requests to a plugin * * @param HistoryPlugin $h Plugin * @param int $num Number of requests to add * * @return array */ protected function addRequests(HistoryPlugin $h, $num) { $requests = array(); for ($i = 0; $i < $num; $i++) { $requests[$i] = RequestFactory::get('http://localhost/'); $requests[$i]->setResponse(new Response(200), true); $requests[$i]->send(); $h->add($requests[$i]); } return $requests; } /** * @covers Guzzle\Http\Plugin\HistoryPlugin::getLimit * @covers Guzzle\Http\Plugin\HistoryPlugin::setLimit */ public function testMaintainsLimitValue() { $h = new HistoryPlugin(); $this->assertSame($h, $h->setLimit(10)); $this->assertEquals(10, $h->getLimit()); } /** * @covers Guzzle\Http\Plugin\HistoryPlugin::add * @covers Guzzle\Http\Plugin\HistoryPlugin::count * @covers Guzzle\Http\Plugin\HistoryPlugin::getIterator */ public function testAddsRequests() { $h = new HistoryPlugin(); $requests = $this->addRequests($h, 1); $this->assertEquals(1, count($h)); $i = $h->getIterator(); $this->assertEquals(1, count($i)); $this->assertEquals($requests[0], $i[0]); } /** * @covers Guzzle\Http\Plugin\HistoryPlugin::add */ public function testIgnoresUnsentRequests() { $h = new HistoryPlugin(); $request = RequestFactory::get('http://localhost/'); $h->add($request); $this->assertEquals(0, count($h)); } /** * @covers Guzzle\Http\Plugin\HistoryPlugin::add * @depends testAddsRequests */ public function testMaintainsLimit() { $h = new HistoryPlugin(); $h->setLimit(2); $requests = $this->addRequests($h, 3); $this->assertEquals(2, count($h)); $i = 0; foreach ($h as $request) { if ($i > 0) { $this->assertSame($requests[$i], $request); } } } /** * @covers Guzzle\Http\Plugin\HistoryPlugin::getLastRequest */ public function testReturnsLastRequest() { $h = new HistoryPlugin(); $requests = $this->addRequests($h, 5); $this->assertSame(end($requests), $h->getLastRequest()); } /** * @covers Guzzle\Http\Plugin\HistoryPlugin::getLastResponse */ public function testReturnsLastResponse() { $h = new HistoryPlugin(); $requests = $this->addRequests($h, 5); $this->assertSame(end($requests)->getResponse(), $h->getLastResponse()); } /** * @covers Guzzle\Http\Plugin\HistoryPlugin::clear */ public function testClearsHistory() { $h = new HistoryPlugin(); $requests = $this->addRequests($h, 5); $this->assertEquals(5, count($h)); $h->clear(); $this->assertEquals(0, count($h)); } /** * @covers Guzzle\Http\Plugin\HistoryPlugin::update * @depends testAddsRequests */ public function testUpdatesAddRequests() { $h = new HistoryPlugin(); $request = RequestFactory::get('http://localhost/'); $request->setResponse(new Response(200), true); $request->getEventManager()->attach($h); $request->send(); $this->assertSame($request, $h->getLastRequest()); } }tests/Guzzle/Tests/Http/Plugin/LogPluginTest.php000064400000026371151742733540015742 0ustar00 */ class LogPluginTest extends \Guzzle\Tests\GuzzleTestCase { /** * @var LogPlugin */ private $plugin; /** * @var ClosureLogAdapter */ private $logAdapter; public function setUp() { $this->logAdapter = new ClosureLogAdapter( function($message, $priority, $extras = null) { echo $message . ' - ' . $priority . ' ' . implode(', ', (array) $extras) . "\n"; } ); $this->plugin = new LogPlugin($this->logAdapter); } /** * Parse a log message into parts * * @param string $message Message to parse * * @return array */ private function parseMessage($message) { $p = explode(' - ', $message, 4); $parts['host'] = trim($p[0]); $parts['request'] = str_replace('"', '', $p[1]); list($parts['code'], $parts['size']) = explode(' ', $p[2]); list($parts['time'], $parts['up'], $parts['down']) = explode(' ', $p[3]); $parts['extra'] = isset($p[4]) ? $p[4] : null; return $parts; } /** * @covers Guzzle\Http\Plugin\LogPlugin::__construct * @covers Guzzle\Http\Plugin\LogPlugin::getLogAdapter */ public function testPluginHasLogAdapter() { $plugin = new LogPlugin($this->logAdapter); $this->assertEquals($this->logAdapter, $plugin->getLogAdapter()); } /** * @covers Guzzle\Http\Plugin\LogPlugin::update * @covers Guzzle\Http\Plugin\LogPlugin::log */ public function testLogsRequestAndResponseContext() { $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); $request = new Request('GET', $this->getServer()->getUrl()); $plugin = new LogPlugin($this->logAdapter); $request->getEventManager()->attach($plugin); ob_start(); $request->send(); $message = ob_get_clean(); $parts = $this->parseMessage($message); $this->assertEquals('127.0.0.1', $parts['host']); $this->assertEquals('GET / HTTP/1.1', $parts['request']); $this->assertEquals(200, $parts['code']); $this->assertEquals(0, $parts['size']); $this->assertContains('127.0.0.1 - "GET / HTTP/1.1" - 200 0 - ', $message); $this->assertContains('7 guzzle.request', $message); } /** * @covers Guzzle\Http\Plugin\LogPlugin::update * @covers Guzzle\Http\Plugin\LogPlugin::log */ public function testLogsRequestAndResponseWireHeaders() { $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata"); $request = new Request('GET', $this->getServer()->getUrl()); $plugin = new LogPlugin($this->logAdapter, LogPlugin::LOG_CONTEXT | LogPlugin::LOG_HEADERS); $request->getEventManager()->attach($plugin); ob_start(); $request->send(); $message = ob_get_clean(); // Make sure the context was logged $this->assertContains('127.0.0.1 - "GET / HTTP/1.1" - 200 4 - ', $message); $this->assertContains('7 guzzle.request', $message); // Check that the headers were logged $this->assertContains("GET / HTTP/1.1\r\n", $message); $this->assertContains("User-Agent: " . Guzzle::getDefaultUserAgent(), $message); $this->assertContains("Accept: */*\r\n", $message); $this->assertContains("Accept-Encoding: deflate, gzip", $message); $this->assertContains("Host: 127.0.0.1:", $message); // Make sure the response headers are present with a line between the request and response $this->assertContains("\n< HTTP/1.1 200 OK\r\n< Content-Length: 4", $message); } /** * @covers Guzzle\Http\Plugin\LogPlugin::update * @covers Guzzle\Http\Plugin\LogPlugin::log */ public function testLogsRequestAndResponseWireContentAndHeaders() { $request = new EntityEnclosingRequest('PUT', $this->getServer()->getUrl()); $request->setBody(EntityBody::factory('send')); $plugin = new LogPlugin($this->logAdapter, LogPlugin::LOG_CONTEXT | LogPlugin::LOG_HEADERS | LogPlugin::LOG_BODY); $request->getEventManager()->attach($plugin); ob_start(); $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata"); $request->send(); $message = ob_get_clean(); // Make sure the context was logged $this->assertContains('127.0.0.1 - "PUT / HTTP/1.1" - 200 4 - ', $message); // Check that the headers were logged $this->assertContains("PUT / HTTP/1.1\r\n", $message); $this->assertContains("User-Agent: " . Guzzle::getDefaultUserAgent(), $message); $this->assertContains("Content-Length: 4", $message); // Make sure the response headers are present with a line between the request and response $this->assertContains("\n< HTTP/1.1 200 OK\r\n< Content-Length: 4", $message); // Request payload $this->assertContains("\r\nsend", $message); // Response body $this->assertContains("data", $message); } /** * @covers Guzzle\Http\Plugin\LogPlugin */ public function testLogsRequestAndResponseWireContentAndHeadersNonStreamable() { $request = new EntityEnclosingRequest('PUT', $this->getServer()->getUrl()); $plugin = new LogPlugin($this->logAdapter, LogPlugin::LOG_CONTEXT | LogPlugin::LOG_HEADERS | LogPlugin::LOG_BODY); $request->getEventManager()->attach($plugin); // Send the response from the dummy server as the request body $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\nsend"); $stream = fopen($this->getServer()->getUrl(), 'r'); $request->setBody(EntityBody::factory($stream, 4)); $tmpFile = tempnam('/tmp', 'testLogsRequestAndResponseWireContentAndHeadersNonStreamable'); $request->setResponseBody(EntityBody::factory(fopen($tmpFile, 'w'))); ob_start(); $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 8\r\n\r\nresponse"); $request->send(); $message = ob_get_clean(); // Make sure the context was logged $this->assertContains('127.0.0.1 - "PUT / HTTP/1.1" - 200 8 - ', $message); // Check that the headers were logged $this->assertContains("PUT / HTTP/1.1\r\n", $message); $this->assertContains("User-Agent: " . Guzzle::getDefaultUserAgent(), $message); $this->assertContains("Content-Length: 4", $message); // Request payload $this->assertContains("\r\nsend", $message); // Make sure the response headers are present with a line between the request and response $this->assertContains("\n< HTTP/1.1 200 OK\r\n< Content-Length: 8", $message); // Response body $this->assertContains("\nresponse", $message); unlink($tmpFile); } /** * @covers Guzzle\Http\Plugin\LogPlugin */ public function testLogsWhenExceptionsAreThrown() { $request = new Request('GET', $this->getServer()->getUrl()); $plugin = new LogPlugin($this->logAdapter, LogPlugin::LOG_VERBOSE); $request->getEventManager()->attach($plugin); $this->getServer()->enqueue("HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n"); ob_start(); try { $request->send(); $this->fail('Exception for 404 was not thrown'); } catch (\Exception $e) {} $message = ob_get_clean(); $this->assertContains('127.0.0.1 - "GET / HTTP/1.1" - 404 0 - ', $message); $this->assertContains("GET / HTTP/1.1\r\n", $message); $this->assertContains("User-Agent: " . Guzzle::getDefaultUserAgent(), $message); $this->assertContains("\n< HTTP/1.1 404 Not Found\r\n< Content-Length: 0", $message); } /** * Data provider to test log verbosity * * @return array */ public function verbosityProvider() { $u = str_replace('http://', '', substr($this->getServer()->getUrl(), 0, -1)); return array( array(LogPlugin::LOG_CONTEXT, "GET /abc HTTP/1.1\r\nHost: $u\r\n\r\n"), array(LogPlugin::LOG_CONTEXT | LogPlugin::LOG_DEBUG, "GET /abc HTTP/1.1\r\nHost: $u\r\n\r\n"), array(LogPlugin::LOG_CONTEXT | LogPlugin::LOG_DEBUG | LogPlugin::LOG_HEADERS, "GET /abc HTTP/1.1\r\nHost: $u\r\n\r\n"), array(LogPlugin::LOG_CONTEXT | LogPlugin::LOG_DEBUG | LogPlugin::LOG_HEADERS | LogPlugin::LOG_BODY, "GET /abc HTTP/1.1\r\nHost: $u\r\n\r\n"), array(LogPlugin::LOG_VERBOSE, "GET /abc HTTP/1.1\r\nHost: $u\r\n\r\n"), array(LogPlugin::LOG_HEADERS, "GET /abc HTTP/1.1\r\nHost: $u\r\n\r\n"), array(LogPlugin::LOG_CONTEXT, "PUT /abc HTTP/1.1\r\nHost: $u\r\nContent-Length: 4\r\n\r\ndata"), array(LogPlugin::LOG_VERBOSE, "PUT /abc HTTP/1.1\r\nHost: $u\r\nContent-Length: 4\r\n\r\ndata"), ); } /** * @covers Guzzle\Http\Plugin\LogPlugin * @dataProvider verbosityProvider */ public function testLogsTransactionsAtDifferentLevels($level, $request) { $request = RequestFactory::fromMessage($request); $plugin = new LogPlugin(new ClosureLogAdapter( function($message, $priority, $extras = null) { echo $message . "\n"; } ), $level); $request->getEventManager()->attach($plugin); $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\nresp"); ob_start(); $request->send(); $gen = ob_get_clean(); $parts = explode("\n", trim($gen), 2); // Check if the context was properly logged if ($level & LogPlugin::LOG_CONTEXT) { $this->assertContains('127.0.0.1 - "' . $request->getMethod() . ' /', $gen); } // Check if the line count is 1 when just logging the context if ($level == LogPlugin::LOG_CONTEXT) { $this->assertEquals(1, count($parts)); return; } // Check if debug information is being logged if ($level & LogPlugin::LOG_DEBUG) { $this->assertContains("\n* Connected to 127.0.0.1 (127.0.0.1) port", $gen); } // Check if the headers are being properly logged if ($level & LogPlugin::LOG_HEADERS) { $this->assertContains("> " . $request->getMethod() . " /abc HTTP/1.1", $gen); // Ensure headers following the request line are logged $this->assertContains("Accept: */*", $gen); // Ensure the response headers are being logged $this->assertContains("< HTTP/1.1 200 OK\r\n< Content-Length: 4", $gen); } // Check if the body of the request and response are being logged if ($level & LogPlugin::LOG_BODY) { if ($request instanceof EntityEnclosingRequest) { $this->assertContains("\r\n\r\n" . $request->getBody(true), $gen); } $this->assertContains("\nresp", $gen); } } }tests/Guzzle/Tests/Http/Plugin/Md5ValidatorPluginTest.php000064400000007610151742733540017507 0ustar00 */ class Md5ValidatorPluginTest extends \Guzzle\Tests\GuzzleTestCase { /** * @covers Guzzle\Http\Plugin\Md5ValidatorPlugin */ public function testValidatesMd5() { $plugin = new Md5ValidatorPlugin(); $request = RequestFactory::get('http://www.test.com/'); $request->getEventManager()->attach($plugin); $body = 'abc'; $hash = md5($body); $response = new Response(200, array( 'Content-MD5' => $hash, 'Content-Length' => 3 ), 'abc'); $request->getEventManager()->notify('request.complete', $response); // Try again with no Content-MD5 $response->removeHeader('Content-MD5'); $request->getEventManager()->notify('request.complete', $response); } /** * @covers Guzzle\Http\Plugin\Md5ValidatorPlugin * @expectedException UnexpectedValueException */ public function testThrowsExceptionOnInvalidMd5() { $plugin = new Md5ValidatorPlugin(); $request = RequestFactory::get('http://www.test.com/'); $request->getEventManager()->attach($plugin); $request->getEventManager()->notify('request.complete', new Response(200, array( 'Content-MD5' => 'foobar', 'Content-Length' => 3 ), 'abc') ); } /** * @covers Guzzle\Http\Plugin\Md5ValidatorPlugin */ public function testSkipsWhenContentLengthIsTooLarge() { $plugin = new Md5ValidatorPlugin(false, 1); $request = RequestFactory::get('http://www.test.com/'); $request->getEventManager()->attach($plugin); $request->getEventManager()->notify('request.complete', new Response(200, array( 'Content-MD5' => 'foobar', 'Content-Length' => 3 ), 'abc') ); } /** * @covers Guzzle\Http\Plugin\Md5ValidatorPlugin */ public function testProperlyValidatesWhenUsingContentEncoding() { $plugin = new Md5ValidatorPlugin(true); $request = RequestFactory::get('http://www.test.com/'); $request->getEventManager()->attach($plugin); // Content-MD5 is the MD5 hash of the canonical content after all // content-encoding has been applied. Because cURL will automaticall // decompress entity bodies, we need to re-compress it to calculate. $body = EntityBody::factory('abc'); $body->compress(); $hash = $body->getContentMd5(); $body->uncompress(); $response = new Response(200, array( 'Content-MD5' => $hash, 'Content-Encoding' => 'gzip' ), 'abc'); $request->getEventManager()->notify('request.complete', $response); $this->assertEquals('abc', $response->getBody(true)); // Try again with an unknown encoding $response = new Response(200, array( 'Content-MD5' => $hash, 'Content-Encoding' => 'foobar' ), 'abc'); $request->getEventManager()->notify('request.complete', $response); // Try again with compress $body->compress('bzip2.compress'); $response = new Response(200, array( 'Content-MD5' => $body->getContentMd5(), 'Content-Encoding' => 'compress' ), 'abc'); $request->getEventManager()->notify('request.complete', $response); // Try again with encoding and disabled content-encoding checks $request->getEventManager()->detach($plugin); $plugin = new Md5ValidatorPlugin(false); $request->getEventManager()->attach($plugin); $request->getEventManager()->notify('request.complete', $response); } }tests/Guzzle/Tests/Http/Pool/MockPool.php000064400000000345151742733540014371 0ustar00 */ class MockPool extends \Guzzle\Http\Pool\Pool { public function getHandle() { return $this->multiHandle; } }tests/Guzzle/Tests/Http/Pool/PoolTest.php000064400000022036151742733540014420 0ustar00 */ class PoolTest extends \Guzzle\Tests\GuzzleTestCase implements Observer { /** * @var Guzzle\Http\Pool\Pool */ private $pool; /** * @var Guzzle\Common\Collection */ private $updates; /** * Prepares the environment before running a test. */ protected function setUp() { parent::setUp(); $this->updates = new Collection(); $this->pool = new MockPool(); $this->pool->getEventManager()->attach($this); } /** * Cleans up the environment after running a test. */ protected function tearDown() { $this->pool->getEventManager()->detach($this); $this->pool = null; parent::tearDown(); } /** * @covers Guzzle\Http\Pool\Pool::__construct */ public function testConstructorCreatesMultiHandle() { $this->assertInternalType('resource', $this->pool->getHandle()); $this->assertEquals('curl_multi', get_resource_type($this->pool->getHandle())); } /** * @covers Guzzle\Http\Pool\Pool::__destruct */ public function testDestructorClosesMultiHandle() { $handle = $this->pool->getHandle(); $this->pool->__destruct(); $this->assertFalse(is_resource($handle)); } /** * @covers Guzzle\Http\Pool\Pool::add * @covers Guzzle\Http\Pool\Pool::count */ public function testRequestsCanBeAddedAndCounted() { $request1 = $this->pool->add(new Request('GET', 'http://www.google.com/')); $this->assertEquals(array($request1), $this->pool->all()); $request2 = $this->pool->add(new Request('POST', 'http://www.google.com/')); $this->assertEquals(array($request1, $request2), $this->pool->all()); $this->assertEquals(2, count($this->pool)); $this->assertTrue($this->updates->hasKey(Pool::ADD_REQUEST) !== false); $this->assertFalse($this->updates->hasKey(Pool::REMOVE_REQUEST) !== false); $this->assertFalse($this->updates->hasKey(Pool::POLLING) !== false); $this->assertFalse($this->updates->hasKey(Pool::COMPLETE) !== false); } /** * @covers Guzzle\Http\Pool\Pool::send * @covers Guzzle\Http\Pool\Pool::getState */ public function testSendsRequestsInParallel() { $this->assertEquals('idle', $this->pool->getState()); $request = $this->pool->add(new Request('GET', 'http://www.google.com/')); $request->setResponse(new Response(200, array(), 'Body'), true); $this->pool->send(); $this->assertTrue($this->updates->hasKey(Pool::ADD_REQUEST) !== false); $this->assertTrue($this->updates->hasKey(Pool::POLLING) !== false); $this->assertTrue($this->updates->hasKey(Pool::COMPLETE) !== false); $this->assertEquals(array('add_request', $request), $this->updates->get(Pool::ADD_REQUEST)); $this->assertEquals(array('polling', null), $this->updates->get(Pool::POLLING)); $this->assertEquals(array('complete', array($request)), $this->updates->get(Pool::COMPLETE)); $this->assertEquals('complete', $this->pool->getState()); $this->assertEquals('Body', $request->getResponse()->getBody()->__toString()); // Sending it again will return false $this->assertFalse($this->pool->send()); } /** * @covers Guzzle\Http\Pool\Pool::remove * @covers Guzzle\Http\Pool\Pool::all */ public function testRequestsCanBeRemoved() { $request1 = $this->pool->add(new Request('GET', 'http://www.google.com/')); $request2 = $this->pool->add(new Request('PUT', 'http://www.google.com/')); $this->assertEquals(array($request1, $request2), $this->pool->all()); $this->assertEquals($request1, $this->pool->remove($request1)); $this->assertEquals(array($request2), $this->pool->all()); } /** * @covers Guzzle\Http\Pool\Pool::reset */ public function testsResetRemovesRequestsAndResetsState() { $request1 = $this->pool->add(new Request('GET', 'http://www.google.com/')); $this->pool->reset(); $this->assertEquals(array(), $this->pool->all()); $this->assertEquals('idle', $this->pool->getState()); // Make sure the notification came through $this->assertEquals(array('reset', null), $this->updates->get('reset')); } /** * @covers Guzzle\Http\Pool\Pool::send * @covers Guzzle\Http\Pool\PoolRequestException */ public function testPoolSendsThroughCurl() { $this->getServer()->enqueue(array( "HTTP/1.1 204 No content\r\n" . "Content-Length: 0\r\n" . "Server: Jetty(6.1.3)\r\n\r\n", "HTTP/1.1 200 OK\r\n" . "Content-Type: text/html; charset=utf-8\r\n" . "Content-Length: 4\r\n" . "Server: Jetty(6.1.3)\r\n\r\n" . "\r\n" . "data" )); $request1 = new Request('GET', $this->getServer()->getUrl()); $request1->getEventManager()->attach($this); $request2 = new Request('GET', $this->getServer()->getUrl()); $request2->getEventManager()->attach($this); $this->pool->add($request1); $this->pool->add($request2); $this->assertEquals(array($request1, $request2), $this->pool->send()); $response1 = $request1->getResponse(); $response2 = $request2->getResponse(); $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response1); $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response2); $this->assertTrue($response1->getBody(true) == 'data' || $response2->getBody(true) == 'data'); $this->assertTrue($response1->getBody(true) == '' || $response2->getBody(true) == ''); $this->assertTrue($response1->getStatusCode() == '204' || $response2->getStatusCode() == '204'); $this->assertNotEquals((string) $response1, (string) $response2); $this->assertTrue($this->updates->hasKey('request.before_send') !== false); $this->assertInternalType('array', $this->updates->get('request.before_send')); } /** * @covers Guzzle\Http\Pool\Pool::send * @covers Guzzle\Http\Pool\PoolRequestException * @depends testPoolSendsThroughCurl */ public function testPoolSendsThroughCurlAndAggregatesRequestExceptions() { $this->getServer()->enqueue(array( "HTTP/1.1 200 OK\r\n" . "Content-Type: text/html; charset=utf-8\r\n" . "Content-Length: 4\r\n" . "Server: Jetty(6.1.3)\r\n" . "\r\n" . "data", "HTTP/1.1 204 No content\r\n" . "Content-Length: 0\r\n" . "Server: Jetty(6.1.3)\r\n" . "\r\n", "HTTP/1.1 404 Not Found\r\n" . "Content-Length: 0\r\n" . "\r\n" )); $request1 = new Request('GET', $this->getServer()->getUrl()); $request2 = new Request('HEAD', $this->getServer()->getUrl()); $request3 = new Request('GET', $this->getServer()->getUrl()); $this->pool->add($request1); $this->pool->add($request2); $this->pool->add($request3); try { $this->pool->send(); $this->fail('PoolRequestException not thrown when aggregating request exceptions'); } catch (PoolRequestException $e) { $this->assertInternalType('array', $e->getRequestExceptions()); $this->assertEquals(1, count($e)); $exceptions = $e->getRequestExceptions(); $response1 = $request1->getResponse(); $response2 = $request2->getResponse(); $response3 = $request3->getResponse(); $this->assertNotEquals((string) $response1, (string) $response2); $this->assertNotEquals((string) $response3, (string) $response1); $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response1); $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response2); $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response3); $failed = $exceptions[0]->getResponse(); $this->assertEquals(404, $failed->getStatusCode()); $this->assertEquals(1, count($e)); // Test the IteratorAggregate functionality foreach ($e as $excep) { $this->assertEquals($failed, $excep->getResponse()); } } } /** * Listens for updates to the pool object and logs them in a * Guzzle\Common\Collection object. * * {@inheritdoc} */ public function update(Subject $subject, $event, $context = null) { $this->updates->add($event, array($event, $context)); } }tests/Guzzle/Tests/Http/QueryStringTest.php000064400000012242151742733540015070 0ustar00 */ class QueryStringTest extends \Guzzle\Tests\GuzzleTestCase { /** * The query string object to test * * @var \Guzzle\Http\QueryString */ protected $q; public function setup() { $this->q = new QueryString(); } /** * @covers \Guzzle\Http\QueryString::getFieldSeparator */ public function testGetFieldSeparator() { $this->assertEquals('&', $this->q->getFieldSeparator()); } /** * @covers \Guzzle\Http\QueryString::getPrefix */ public function testGetPrefix() { $this->assertEquals('?', $this->q->getPrefix()); } /** * @covers \Guzzle\Http\QueryString::getValueSeparator */ public function testGetValueSeparator() { $this->assertEquals('=', $this->q->getValueSeparator()); } /** * @covers \Guzzle\Http\QueryString::isEncodingFields * @covers \Guzzle\Http\QueryString::setEncodeFields */ public function testIsEncodingFields() { $this->assertTrue($this->q->isEncodingFields()); $this->assertEquals($this->q, $this->q->setEncodeFields(false)); $this->assertFalse($this->q->isEncodingFields()); } /** * @covers \Guzzle\Http\QueryString::isEncodingValues * @covers \Guzzle\Http\QueryString::setEncodeValues */ public function testIsEncodingValues() { $this->assertTrue($this->q->isEncodingValues()); $this->assertEquals($this->q, $this->q->setEncodeValues(false)); $this->assertFalse($this->q->isEncodingValues()); } /** * @covers \Guzzle\Http\QueryString::setFieldSeparator * @covers \Guzzle\Http\QueryString::setFieldSeparator */ public function testSetFieldSeparator() { $this->assertEquals($this->q, $this->q->setFieldSeparator('/')); $this->assertEquals('/', $this->q->getFieldSeparator()); } /** * @covers \Guzzle\Http\QueryString::setPrefix * @covers \Guzzle\Http\QueryString::getPrefix */ public function testSetPrefix() { $this->assertEquals($this->q, $this->q->setPrefix('')); $this->assertEquals('', $this->q->getPrefix()); } /** * @covers \Guzzle\Http\QueryString::setValueSeparator * @covers \Guzzle\Http\QueryString::getValueSeparator */ public function testSetValueSeparator() { $this->assertEquals($this->q, $this->q->setValueSeparator('/')); $this->assertEquals('/', $this->q->getValueSeparator()); } /** * @covers \Guzzle\Http\QueryString::urlEncode * @covers \Guzzle\Http\QueryString::rawUrlEncode * @covers \Guzzle\Http\QueryString::encodeData * @covers \Guzzle\Http\QueryString::replace */ public function testUrlEncode() { $params = array( 'test' => 'value', 'test 2' => 'this is a test?', 'test3' => array('v1', 'v2', 'v3') ); $encoded = array( 'test' => 'value', rawurlencode('test 2') => rawurlencode('this is a test?'), 'test3[0]' => 'v1', 'test3[1]' => 'v2', 'test3[2]' => 'v3' ); $this->q->replace($params); $this->assertEquals($encoded, $this->q->urlEncode()); // Disable field encoding $testData = array( 'test 2' => 'this is a test' ); $this->q->replace($testData); $this->q->setEncodeFields(false); $this->assertEquals(array( 'test 2' => rawurlencode('this is a test') ), $this->q->urlEncode()); // Disable encoding of both fields and values $this->q->setEncodeValues(false); $this->assertEquals($testData, $this->q->urlEncode()); $this->assertEquals('one&two%3D', QueryString::rawurlencode('one&two=', array('&'))); } /** * @covers \Guzzle\Http\QueryString * @covers \Guzzle\Http\QueryString::__toString * @covers \Guzzle\Http\QueryString::setEncodeFields * @covers \Guzzle\Http\QueryString::replace * @covers \Guzzle\Http\QueryString::setAggregateFunction * @covers \Guzzle\Http\QueryString::encodeData */ public function testToString() { // Check with no parameters $this->assertEquals('', $this->q->__toString()); $params = array( 'test' => 'value', 'test 2' => 'this is a test?', 'test3' => array( 'v1', 'v2', 'v3' ) ); $this->q->replace($params); $this->assertEquals('?test=value&test%202=this%20is%20a%20test%3F&test3[0]=v1&test3[1]=v2&test3[2]=v3', $this->q->__toString()); $this->q->setEncodeFields(false); $this->q->setEncodeValues(false); $this->assertEquals('?test=value&test 2=this is a test?&test3[0]=v1&test3[1]=v2&test3[2]=v3', $this->q->__toString()); // Use an alternative aggregator $this->q->setAggregateFunction(array($this->q, 'aggregateUsingComma')); $this->assertEquals('?test=value&test 2=this is a test?&test3=v1,v2,v3', $this->q->__toString()); } }tests/Guzzle/Tests/Http/Server.php000064400000015232151742733540013204 0ustar00 */ class Server { const DEFAULT_PORT = 8124; const REQUEST_DELIMITER = "\n----[request]\n"; /** * @var int Port that the server will listen on */ private $port; /** * @var bool Is the server running */ private $running = false; /** * Create a new scripted server * * @param int $port (optional) Port to listen on (defaults to 8124) */ public function __construct($port = null) { $this->port = $port ?: self::DEFAULT_PORT; } /** * Destructor to safely shutdown the node.js server if it is still running * * @codeCoverageIgnore */ public function __destruct() { // Only shut the server down if the object knows it started the server if ($this->running) { try { $this->stop(); } catch (\Exception $e) { } } } /** * Flush the received requests from the server * * @return bool Returns TRUE on success or FALSE on failure * @throws HttpException */ public function flush() { if (!$this->isRunning()) { return false; } return RequestFactory::delete($this->getUrl() . 'guzzle-server/requests') ->send()->getStatusCode() == 200; } /** * Queue an array of responses or a single response on the server. * * Any currently queued responses will be overwritten. Subsequent requests * on the server will return queued responses in FIFO order. * * @param array|Response $responses A single or array of Responses to queue * * @return bool Returns TRUE on success or FALSE on failure * @throws HttpException */ public function enqueue($responses) { $data = array(); foreach ((array) $responses as $response) { // Create the response object from a string if (is_string($response)) { $response = Response::factory($response); } else if (!($response instanceof Response)) { throw new HttpException( 'Responses must be strings or implement Response' ); } $data[] = array( 'statusCode' => $response->getStatusCode(), 'reasonPhrase' => $response->getReasonPhrase(), 'headers' => $response->getHeaders()->getAll(), 'body' => $response->getBody(true) ); } $response = RequestFactory::put($this->getUrl() . 'guzzle-server/responses', null, json_encode($data)) ->send(); return $response->getStatusCode() == 200; } /** * Check if the server is running * * @return bool */ public function isRunning() { if ($this->running) { return true; } else { $fp = @fsockopen('127.0.0.1', $this->port, $errno, $errstr, 1); if (!$fp) { return false; } else { fclose($fp); return true; } } } /** * Get the URL to the server * * @return string */ public function getUrl() { return 'http://127.0.0.1:' . $this->getPort() . '/'; } /** * Get the port that the server is listening on * * @return int */ public function getPort() { return $this->port; } /** * Get all of the received requests * * @param bool $hydrate (optional) Set to TRUE to turn the messages into * actual {@see RequestInterface} objects. If $hydrate is FALSE, * requests will be returned as strings. * * @return array * @throws HttpException */ public function getReceivedRequests($hydrate = false) { $data = array(); if ($this->isRunning()) { $response = RequestFactory::get($this->getUrl() . 'guzzle-server/requests')->send(); $data = array_filter(explode(self::REQUEST_DELIMITER, $response->getBody(true))); if ($hydrate) { $data = array_map(function($message) { return RequestFactory::fromMessage($message); }, $data); } } return $data; } /** * Start running the node.js server in the background */ public function start() { if (!$this->isRunning()) { exec('node ' . __DIR__ . \DIRECTORY_SEPARATOR . 'server.js ' . $this->port . ' >> /tmp/server.log 2>&1 &'); // Shut the server down when the script exits unexpectedly register_shutdown_function(array($this, 'stop')); // Wait at most 5 seconds for the server the setup before proceeding $start = time(); while (!$this->isRunning() && time() - $start < 5); // @codeCoverageIgnoreStart if (!$this->isRunning()) { throw new HttpException( 'Unable to contact server.js. Have you installed node.js ' . 'v0.5.0+? The node.js executable, node, must also be in ' . 'your path.' ); } // @codeCoverageIgnoreEnd } $this->running = true; } /** * Stop running the node.js server * * @return bool Returns TRUE on success or FALSE on failure * @throws HttpException */ public function stop() { if (!$this->isRunning()) { return false; } $this->running = false; return RequestFactory::delete($this->getUrl() . 'guzzle-server')->send() ->getStatusCode() == 200; } }tests/Guzzle/Tests/Http/UrlTest.php000064400000020000151742733540013325 0ustar00 */ class UrlTest extends \Guzzle\Tests\GuzzleTestCase { /** * @return array */ public function urlDataProvider() { $resp = array(); foreach (array( 'http://www.guzzle-project.com/', 'http://www.google.com:8080/path?q=1&v=2', 'https://www.guzzle-project.com/?value1=a&value2=b', 'https://guzzle-project.com/index.html', '/index.html?q=2', 'http://www.google.com:8080/path?q=1&v=2', 'http://michael:123@www.google.com:8080/path?q=1&v=2', 'http://michael@test.com/abc/def?q=10#test', ) as $url) { $parts = parse_url($url); $resp[] = array($url, parse_url($url), !isset($parts['host'])); } return $resp; } /** * @covers Guzzle\Http\Url::buildUrl * @dataProvider urlDataProvider */ public function testBuildsUrlsFromParts($url, $parts, $throwE) { $this->assertEquals($url, Url::buildUrl($parts)); } /** * @covers Guzzle\Http\Url::getPort */ public function testPortIsDeterminedFromScheme() { $this->assertEquals(80, Url::factory('http://www.test.com/')->getPort()); $this->assertEquals(443, Url::factory('https://www.test.com/')->getPort()); $this->assertEquals(null, Url::factory('ftp://www.test.com/')->getPort()); $this->assertEquals(8192, Url::factory('http://www.test.com:8192/')->getPort()); } /** * @covers Guzzle\Http\Url::__clone */ public function testCloneCreatesNewInternalObjects() { $u1 = Url::factory('http://www.test.com/'); $u2 = clone $u1; $this->assertNotSame($u1->getQuery(), $u2->getQuery()); } /** * @covers Guzzle\Http\Url::__construct * @covers Guzzle\Http\Url::factory * @covers Guzzle\Http\Url::__toString * @covers Guzzle\Http\Url::isAbsolute */ public function testValidatesUrlPartsInFactory() { $url = Url::factory('/index.php'); $this->assertEquals('/index.php', (string) $url); $this->assertFalse($url->isAbsolute()); $url = 'http://michael:test@test.com:80/path/123?q=abc#test'; $u = Url::factory($url); $this->assertEquals('http://michael:test@test.com/path/123?q=abc#test', (string) $u); $this->assertTrue($u->isAbsolute()); } /** * @covers Guzzle\Http\Url */ public function testUrlStoresParts() { $url = Url::factory('http://test:pass@www.test.com:8081/path/path2/?a=1&b=2#fragment'); $this->assertEquals('http', $url->getScheme()); $this->assertEquals('test', $url->getUsername()); $this->assertEquals('pass', $url->getPassword()); $this->assertEquals('www.test.com', $url->getHost()); $this->assertEquals(8081, $url->getPort()); $this->assertEquals('/path/path2/', $url->getPath()); $this->assertEquals('?a=1&b=2', (string)$url->getQuery()); $this->assertEquals('fragment', $url->getFragment()); $this->assertEquals(array( 'fragment' => 'fragment', 'host' => 'www.test.com', 'pass' => 'pass', 'path' => '/path/path2/', 'port' => 8081, 'query' => '?a=1&b=2', 'query_prefix' => '?', 'scheme' => 'http', 'user' => 'test' ), $url->getParts()); } /** * @covers Guzzle\Http\Url::setPath * @covers Guzzle\Http\Url::getPath * @covers Guzzle\Http\Url::getPathSegments * @covers Guzzle\Http\Url::buildUrl */ public function testHandlesPathsCorrectly() { $url = Url::factory('http://www.test.com'); $this->assertEquals('/', $url->getPath()); $url->setPath('test'); $this->assertEquals('/test', $url->getPath()); $url->setPath('/test/123/abc'); $this->assertEquals(array('test', '123', 'abc'), $url->getPathSegments()); $parts = parse_url('http://www.test.com/test'); $parts['path'] = ''; $this->assertEquals('http://www.test.com/', Url::buildUrl($parts)); $parts['path'] = 'test'; $this->assertEquals('http://www.test.com/test', Url::buildUrl($parts)); } /** * @covers Guzzle\Http\Url::addPath */ public function testAddsToPath() { // Does nothing here $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath(false)); $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath('')); $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath('/')); $this->assertEquals('http://e.com/base/relative?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath('relative')); $this->assertEquals('http://e.com/base/relative?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath('/relative')); } /** * URL combination data provider * * @return array */ public function urlCombineDataProvider() { return array( array('http://www.example.com/', 'http://www.example.com/', 'http://www.example.com/'), array('http://www.example.com/path', '/absolute', 'http://www.example.com/absolute'), array('http://www.example.com/path', '/absolute?q=2', 'http://www.example.com/absolute?q=2'), array('http://www.example.com/path', 'more', 'http://www.example.com/path/more'), array('http://www.example.com/path', 'more?q=1', 'http://www.example.com/path/more?q=1'), array('http://www.example.com/', '?q=1', 'http://www.example.com/?q=1'), array('http://www.example.com/path', 'http://test.com', 'http://test.com/path'), array('http://www.example.com:8080/path', 'http://test.com', 'http://test.com/path'), array('http://www.example.com:8080/path', '?q=2#abc', 'http://www.example.com:8080/path?q=2#abc'), array('http://u:a@www.example.com/path', 'test', 'http://u:a@www.example.com/path/test'), array('http://www.example.com/path', 'http://u:a@www.example.com/', 'http://u:a@www.example.com/path'), array('/path?q=2', 'http://www.test.com/', 'http://www.test.com/path?q=2'), ); } /** * @covers Guzzle\Http\Url::combine * @dataProvider urlCombineDataProvider */ public function testCombinesUrls($a, $b, $c) { $this->assertEquals($c, (string) Url::factory($a)->combine($b)); } /** * @covers Guzzle\Http\Url */ public function testHasGettersAndSetters() { $url = Url::factory('http://www.test.com/'); $this->assertEquals('example.com', $url->setHost('example.com')->getHost()); $this->assertEquals('8080', $url->setPort(8080)->getPort()); $this->assertEquals('/foo/bar', $url->setPath(array('foo', 'bar'))->getPath()); $this->assertEquals('a', $url->setPassword('a')->getPassword()); $this->assertEquals('b', $url->setUsername('b')->getUsername()); $this->assertEquals('abc', $url->setFragment('abc')->getFragment()); $this->assertEquals('https', $url->setScheme('https')->getScheme()); $this->assertEquals('?a=123', (string) $url->setQuery('a=123')->getQuery()); $this->assertEquals('https://b:a@example.com:8080/foo/bar?a=123#abc', (string)$url); $this->assertEquals('?b=boo', (string) $url->setQuery(new QueryString(array( 'b' => 'boo' )))->getQuery()); $this->assertEquals('https://b:a@example.com:8080/foo/bar?b=boo#abc', (string)$url); } /** * @covers Guzzle\Http\Url::setQuery */ public function testSetQueryAcceptsArray() { $url = Url::factory('http://www.test.com'); $url->setQuery(array('a' => 'b')); $this->assertEquals('http://www.test.com/?a=b', (string) $url); } }tests/Guzzle/Tests/Http/server.js000064400000011604151742733540013070 0ustar00/** * Guzzle node.js test server to return queued responses to HTTP requests and * expose a RESTful API for enqueueing responses and retrieving the requests * that have been received. * * - Delete all requests that have been received: * DELETE /guzzle-server/requests * Host: 127.0.0.1:8124 * * - Enqueue responses * PUT /guzzle-server/responses * Host: 127.0.0.1:8124 * * [{ "statusCode": 200, "reasonPhrase": "OK", "headers": {}, "body": "" }] * * - Get the recevied requests * GET /guzzle-server/requests * Host: 127.0.0.1:8124 * * - Shutdown the server * DELETE /guzzle-server * Host: 127.0.0.1:8124 * * @package Guzzle PHP * @author Michael Dowling * @license See the LICENSE file that was distributed with this source code. */ var http = require("http"); /** * Guzzle node.js server * @class */ var GuzzleServer = function(port) { this.port = port; this.log = log; this.responses = []; this.requests = []; var that = this; /** * Handle a Guzzle Server control request * @param (String) request HTTP request as a string * @param (ServerRequest) req Received server request * @param (ServerResponse) res Outgoing server response */ var controlRequest = function(request, req, res) { if (req.method == "DELETE") { if (req.url == "/guzzle-server/requests") { // Clear the received requests that.requests = []; res.writeHead(200, "OK", { "Content-Length": 0 }); res.end(); } else if (req.url == "/guzzle-server") { // Shutdown the server res.writeHead(200, "OK", { "Content-Length": 0, "Connection": "close" }); res.end(); if (this.log) { console.log("Shutting down"); } that.server.close(); } } else if (req.method == "GET") { if (req.url === "/guzzle-server/requests") { // Get received requests var data = that.requests.join("\n----[request]\n"); res.writeHead(200, "OK", { "Content-Length": data.length }); res.end(data); } } else if (req.method == "PUT") { if (req.url == "/guzzle-server/responses") { // Received respones to queue that.responses = eval("(" + request.split("\r\n\r\n")[1] + ")"); if (this.log) { console.log("Adding respones:"); console.log(that.responses); } res.writeHead(200, "OK", { "Content-Length": 0 }); res.end(); } } }; /** * Received a complete request * @param (String) request HTTP request as a string * @param (ServerRequest) req Received server request * @param (ServerResponse) res Outgoing server response */ var receivedRequest = function(request, req, res) { if (req.url.indexOf("/guzzle-server") === 0) { controlRequest(request, req, res); } else { var response = that.responses.shift(); res.writeHead(response.statusCode, response.reasonPhrase, response.headers); res.end(response.body); that.requests.push(request); } }; /** * Start the node.js Guzzle server */ this.start = function() { that.server = http.createServer(function(req, res) { // If this is not a control request and no responses are in queue, return 500 response if (req.url.indexOf("/guzzle-server") == -1 && !that.responses.length) { res.writeHead(500); res.end("No responses in queue"); return; } // Begin building the request message as a string var request = req.method + " " + req.url + " HTTP/" + req.httpVersion + "\r\n"; // Add the request headers for (var i in req.headers) { request += i + ": " + req.headers[i] + "\r\n"; } request += "\r\n"; // Receive each chunk of the request body req.addListener("data", function(chunk) { request += chunk; }); // Called when the request completes req.addListener("end", function() { receivedRequest(request, req, res); }); }); that.server.listen(port, "127.0.0.1"); if (this.log) { console.log("Server running at http://127.0.0.1:8124/"); } }; }; // Get the port from the arguments port = process.argv.length >= 3 ? process.argv[2] : 8124; log = process.argv.length >= 4 ? process.argv[3] : false; // Start the server server = new GuzzleServer(port, log); server.start();tests/Guzzle/Tests/Service/ClientTest.php000064400000045651151742733540014505 0ustar00 */ class ClientTest extends \Guzzle\Tests\GuzzleTestCase { protected $factory; protected $service; protected $serviceTest; protected $factoryTest; public function setUp() { $this->serviceTest = new ServiceDescription(array( new ApiCommand(array( 'name' => 'test_command', 'doc' => 'documentationForCommand', 'method' => 'DELETE', 'can_batch' => true, 'class' => 'Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand', 'args' => array( 'bucket' => array( 'required' => true ), 'key' => array( 'required' => true ) ) )) )); $builder = new XmlDescriptionBuilder(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'TestData' . DIRECTORY_SEPARATOR . 'test_service.xml'); $this->service = $builder->build(); } /** * Get a LogPlugin * * @return LogPlugin */ private function getLogPlugin() { return new LogPlugin(new ClosureLogAdapter( function($message, $priority, $extras = null) { echo $message . ' ' . $priority . ' ' . implode(' - ', (array) $extras) . "\n"; } )); } /** * @covers Guzzle\Service\Client::getConfig * @covers Guzzle\Service\Client::setConfig * @covers Guzzle\Service\Client::setBaseUrl * @covers Guzzle\Service\Client::getBaseUrl */ public function testAcceptsConfig() { $client = new Client('http://www.google.com/'); $this->assertEquals('http://www.google.com/', $client->getBaseUrl()); $this->assertSame($client, $client->setConfig(array( 'test' => '123' ))); $this->assertEquals(array('test' => '123'), $client->getConfig()->getAll()); $this->assertEquals('123', $client->getConfig('test')); $this->assertSame($client, $client->setBaseUrl('http://www.test.com/{{test}}')); $this->assertEquals('http://www.test.com/123', $client->getBaseUrl()); $this->assertEquals('http://www.test.com/{{test}}', $client->getBaseUrl(false)); try { $client->setConfig(false); } catch (\InvalidArgumentException $e) { } } /** * @covers Guzzle\Service\Client::__construct */ public function testConstructorCanAcceptConfig() { $client = new Client('http://www.test.com/', array( 'data' => '123' )); $this->assertEquals('123', $client->getConfig('data')); } /** * @covers Guzzle\Service\Client::setConfig */ public function testCanUseCollectionAsConfig() { $client = new Client('http://www.google.com/'); $client->setConfig(new Collection(array( 'api' => 'v1', 'key' => 'value', 'base_url' => 'http://www.google.com/' ))); $this->assertEquals('v1', $client->getConfig('api')); } /** * @covers Guzzle\Service\Client::factory */ public function testFactoryCreatesClient() { $client = Client::factory(array( 'base_url' => 'http://www.test.com/', 'test' => '123' )); $this->assertEquals('http://www.test.com/', $client->getBaseUrl()); $this->assertEquals('123', $client->getConfig('test')); } /** * @covers Guzzle\Service\Client */ public function testInjectConfig() { $client = new Client('http://www.google.com/'); $client->setConfig(array( 'api' => 'v1', 'key' => 'value', 'foo' => 'bar' )); $this->assertEquals('Testing...api/v1/key/value', $client->inject('Testing...api/{{api}}/key/{{key}}')); // Make sure that the client properly validates and injects config $this->assertEquals('bar', $client->getConfig('foo')); } /** * @covers Guzzle\Service\Client::__construct * @covers Guzzle\Service\Client::createRequest */ public function testClientAttachersObserversToRequests() { $this->getServer()->flush(); $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); $client = new Client($this->getServer()->getUrl()); $logPlugin = $this->getLogPlugin(); $client->getEventManager()->attach($logPlugin); // Make sure the plugin was registered correctly $this->assertTrue($client->getEventManager()->hasObserver($logPlugin)); // Get a request from the client and ensure the the observer was // attached to the new request $request = $client->createRequest(); $this->assertTrue($request->getEventManager()->hasObserver($logPlugin)); // Make sure that the log plugin actually logged the request and response ob_start(); $request->send(); $logged = ob_get_clean(); $this->assertContains('"GET / HTTP/1.1" - 200', $logged); } /** * @covers Guzzle\Service\Client::getBaseUrl * @covers Guzzle\Service\Client::setBaseUrl */ public function testClientReturnsValidBaseUrls() { $client = new Client('http://www.{{foo}}.{{data}}/', array( 'data' => '123', 'foo' => 'bar' )); $this->assertEquals('http://www.bar.123/', $client->getBaseUrl()); $client->setBaseUrl('http://www.google.com/'); $this->assertEquals('http://www.google.com/', $client->getBaseUrl()); } /** * @covers Guzzle\Service\Client::execute */ public function testExecutesCommands() { $this->getServer()->flush(); $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); $client = new Client($this->getServer()->getUrl()); $cmd = new MockCommand(); $client->execute($cmd); $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $cmd->getResponse()); $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $cmd->getResult()); $this->assertEquals(1, count($this->getServer()->getReceivedRequests(false))); } /** * @covers Guzzle\Service\Client::execute */ public function testExecutesCommandsWithArray() { $client = new Client('http://www.test.com/'); $client->getEventManager()->attach(new MockPlugin(array( new \Guzzle\Http\Message\Response(200), new \Guzzle\Http\Message\Response(200) ))); // Create a command set and a command $set = array(new MockCommand(), new MockCommand()); $client->execute($set); // Make sure it sent $this->assertTrue($set[0]->isExecuted()); $this->assertTrue($set[1]->isExecuted()); } /** * @covers Guzzle\Service\Client::execute * @expectedException Guzzle\Service\Command\CommandSetException */ public function testThrowsExceptionWhenExecutingMixedClientCommandSets() { $client = new Client('http://www.test.com/'); $otherClient = new Client('http://www.test-123.com/'); // Create a command set and a command $set = new CommandSet(); $cmd = new MockCommand(); $set->addCommand($cmd); // Associate the other client with the command $cmd->setClient($otherClient); // Send the set with the wrong client, causing an exception $client->execute($set); } /** * @covers Guzzle\Service\Client::execute * @expectedException InvalidArgumentException */ public function testThrowsExceptionWhenExecutingInvalidCommandSets() { $client = new Client('http://www.test.com/'); $client->execute(new \stdClass()); } /** * @covers Guzzle\Service\Client::execute */ public function testExecutesCommandSets() { $client = new Client('http://www.test.com/'); $client->getEventManager()->attach(new MockPlugin(array( new \Guzzle\Http\Message\Response(200) ))); // Create a command set and a command $set = new CommandSet(); $cmd = new MockCommand(); $set->addCommand($cmd); $this->assertSame($set, $client->execute($set)); // Make sure it sent $this->assertTrue($cmd->isExecuted()); $this->assertTrue($cmd->isPrepared()); $this->assertEquals(200, $cmd->getResponse()->getStatusCode()); } /** * @covers Guzzle\Service\Client::setUserApplication * @covers Guzzle\Service\Client::createRequest * @covers Guzzle\Service\Client::prepareRequest */ public function testSetsUserApplication() { $client = new Client('http://www.test.com/', array( 'api' => 'v1' )); $this->assertSame($client, $client->setUserApplication('Test', '1.0Ab')); $request = $client->get(); $this->assertEquals('Test/1.0Ab ' . Guzzle::getDefaultUserAgent(), $request->getHeader('User-Agent')); } /** * @covers Guzzle\Service\Client::createRequest * @covers Guzzle\Service\Client::prepareRequest */ public function testClientAddsCurlOptionsToRequests() { $client = new Client('http://www.test.com/', array( 'api' => 'v1', // Adds the option using the curl values 'curl.CURLOPT_HTTPAUTH' => 'CURLAUTH_DIGEST', 'curl.abc' => 'not added' )); $request = $client->createRequest(); $options = $request->getCurlOptions(); $this->assertEquals(CURLAUTH_DIGEST, $options->get(CURLOPT_HTTPAUTH)); $this->assertNull($options->get('curl.abc')); } /** * @covers Guzzle\Service\Client::createRequest * @covers Guzzle\Service\Client::prepareRequest */ public function testClientAddsCacheKeyFiltersToRequests() { $client = new Client('http://www.test.com/', array( 'api' => 'v1', 'cache.key_filter' => 'query=Date' )); $request = $client->createRequest(); $this->assertEquals('query=Date', $request->getParams()->get('cache.key_filter')); } /** * @covers Guzzle\Service\Client::getCommand * @expectedException InvalidArgumentException */ public function testThrowsExceptionWhenNoCommandFactoryIsSetAndGettingCommand() { $client = new Client($this->getServer()->getUrl()); $client->getCommand('test'); } /** * @covers Guzzle\Service\Client::getCommand * @covers Guzzle\Service\Client::getDescription * @covers Guzzle\Service\Client::setDescription */ public function testRetrievesCommandsFromConcreteAndService() { $client = new Mock\MockClient('http://www.example.com/'); $this->assertSame($client, $client->setDescription($this->serviceTest)); $this->assertSame($this->serviceTest, $client->getDescription()); // Creates service commands $this->assertInstanceOf('Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand', $client->getCommand('test_command')); // Creates concrete commands $this->assertInstanceOf('Guzzle\\Tests\\Service\\Mock\\Command\\OtherCommand', $client->getCommand('other_command')); } /** * @covers Guzzle\Service\Client::prepareRequest */ public function testPreparesRequestsNotCreatedByTheClient() { $client = new Client($this->getServer()->getUrl()); $client->getEventManager()->attach(new ExponentialBackoffPlugin()); $request = RequestFactory::get($client->getBaseUrl()); $this->assertSame($request, $client->prepareRequest($request)); $this->assertTrue($request->getEventManager()->hasObserver('Guzzle\\Http\\Plugin\\ExponentialBackoffPlugin')); } /** * @covers Guzzle\Service\Client::createRequest */ public function testCreatesRequestsWithDefaultValues() { $client = new Client($this->getServer()->getUrl() . 'base'); // Create a GET request $request = $client->createRequest(); $this->assertEquals('GET', $request->getMethod()); $this->assertEquals($client->getBaseUrl(), $request->getUrl()); // Create a DELETE request $request = $client->createRequest('DELETE'); $this->assertEquals('DELETE', $request->getMethod()); $this->assertEquals($client->getBaseUrl(), $request->getUrl()); // Create a HEAD request with custom headers $request = $client->createRequest('HEAD', 'http://www.test.com/'); $this->assertEquals('HEAD', $request->getMethod()); $this->assertEquals('http://www.test.com/', $request->getUrl()); // Create a PUT request $request = $client->createRequest('PUT'); $this->assertEquals('PUT', $request->getMethod()); // Create a PUT request with injected config $client->getConfig()->set('a', 1)->set('b', 2); $request = $client->createRequest('PUT', '/path/{{a}}?q={{b}}'); $this->assertEquals($request->getUrl(), $this->getServer()->getUrl() . 'path/1?q=2'); // Realtive URL with relative path $request = $client->createRequest('GET', 'relative/path/to/resource'); $this->assertEquals($this->getServer()->getUrl() . 'base/relative/path/to/resource', $request->getUrl()); // Realtive URL with relative path and query $request = $client->createRequest('GET', 'relative/path/to/resource?a=b&c=d'); $this->assertEquals($this->getServer()->getUrl() . 'base/relative/path/to/resource?a=b&c=d', $request->getUrl()); // Relative URL with absolute path $request = $client->createRequest('GET', '/absolute/path/to/resource'); $this->assertEquals($this->getServer()->getUrl() . 'absolute/path/to/resource', $request->getUrl()); // Relative URL with absolute path and query $request = $client->createRequest('GET', '/absolute/path/to/resource?a=b&c=d'); $this->assertEquals($this->getServer()->getUrl() . 'absolute/path/to/resource?a=b&c=d', $request->getUrl()); // Test with a base URL containing a query string too $client = new Client($this->getServer()->getUrl() . 'base?z=1'); // Absolute so replaces query $request = $client->createRequest('GET', '/absolute/path/to/resource?a=b&c=d'); $this->assertEquals($this->getServer()->getUrl() . 'absolute/path/to/resource?a=b&c=d', $request->getUrl()); // Add relative with no query $request = $client->createRequest('GET', 'relative/path/to/resource'); $this->assertEquals($this->getServer()->getUrl() . 'base/relative/path/to/resource?z=1', $request->getUrl()); // Add relative with query $request = $client->createRequest('GET', 'relative/path/to/resource?another=query'); $this->assertEquals($this->getServer()->getUrl() . 'base/relative/path/to/resource?z=1&another=query', $request->getUrl()); } /** * @covers Guzzle\Service\Client::get * @covers Guzzle\Service\Client::delete * @covers Guzzle\Service\Client::head * @covers Guzzle\Service\Client::put * @covers Guzzle\Service\Client::post * @covers Guzzle\Service\Client::options */ public function testClientHasHelperMethodsForCreatingRequests() { $url = $this->getServer()->getUrl(); $client = new Client($url . 'base'); $this->assertEquals('GET', $client->get()->getMethod()); $this->assertEquals('PUT', $client->put()->getMethod()); $this->assertEquals('POST', $client->post()->getMethod()); $this->assertEquals('HEAD', $client->head()->getMethod()); $this->assertEquals('DELETE', $client->delete()->getMethod()); $this->assertEquals('OPTIONS', $client->options()->getMethod()); $this->assertEquals($url . 'base/abc', $client->get('abc')->getUrl()); $this->assertEquals($url . 'zxy', $client->put('/zxy')->getUrl()); $this->assertEquals($url . 'zxy?a=b', $client->post('/zxy?a=b')->getUrl()); $this->assertEquals($url . 'base?a=b', $client->head('?a=b')->getUrl()); $this->assertEquals($url . 'base?a=b', $client->delete('/base?a=b')->getUrl()); } /** * @covers Guzzle\Service\Client::getBaseUrl * @expectedException RuntimeException */ public function testClientEnsuresBaseUrlIsSetWhenRetrievingIt() { $client = new Client(''); $client->getBaseUrl(); } /** * @covers Guzzle\Service\Client::createRequest */ public function testClientInjectsConfigsIntoUrls() { $client = new Client('http://www.test.com/api/v1', array( 'test' => '123' )); $request = $client->get('relative/{{test}}'); $this->assertEquals('http://www.test.com/api/v1/relative/123', $request->getUrl()); } /** * @covers Guzzle\Service\Client */ public function testAllowsEmptyBaseUrl() { $client = new Client(); $request = $client->get('http://www.google.com/'); $this->assertEquals('http://www.google.com/', $request->getUrl()); $request->setResponse(new Response(200), true); $request->send(); } /** * @covers Guzzle\Service\Client::batch */ public function testManagesRequestPool() { $client = new Client('http://localhost/'); $plugin = new MockPlugin(); $plugin->addResponse(new Response(200)); $plugin->addResponse(new Response(200)); $client->getEventManager()->attach($plugin); $responses = $client->batch(array( $client->get('/'), $client->head('/users') )); $this->assertInternalType('array', $responses); $this->assertEquals(2, count($responses)); $this->assertEquals(200, $responses[0]->getStatusCode()); $this->assertEquals(200, $responses[1]->getStatusCode()); } /** * @covers Guzzle\Service\Client::setPool * @covers Guzzle\Service\Client::getPool */ public function testManagesInternalPoolObject() { $client = new Client(); $pool = $client->getPool(); $this->assertInstanceOf('Guzzle\\Http\\Pool\\PoolInterface', $pool); $client->setPool(new Pool()); $this->assertInstanceOf('Guzzle\\Http\\Pool\\PoolInterface', $client->getPool()); $this->assertNotSame($pool, $client->getPool()); } }tests/Guzzle/Tests/Service/Command/AbstractCommandTest.php000064400000001231151742733540017671 0ustar00 */ abstract class AbstractCommandTest extends \Guzzle\Tests\GuzzleTestCase { protected function getClient() { $builder = new XmlDescriptionBuilder(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'TestData' . DIRECTORY_SEPARATOR . 'test_service.xml'); $service = $builder->build(); $client = new Client('http://www.google.com/'); $client->setDescription($service); return $client; } }tests/Guzzle/Tests/Service/Command/ClosureCommandTest.php000064400000005120151742733540017543 0ustar00 */ class ClosureCommandTest extends \Guzzle\Tests\GuzzleTestCase { /** * @covers Guzzle\Service\Command\ClosureCommand * @expectedException InvalidArgumentException * @expectedExceptionMessage A closure must be passed in the parameters array */ public function testConstructorValidatesClosure() { $c = new ClosureCommand(); } /** * @covers Guzzle\Service\Command\ClosureCommand * @expectedException InvalidArgumentException * @expectedExceptionMessage A closure_api value must be passed in the parameters array */ public function testConstructorValidatesClosureApi() { $c = new ClosureCommand(array( 'closure' => function() {} )); } /** * @covers Guzzle\Service\Command\ClosureCommand */ public function testCanSetCanBatch() { $c = new ClosureCommand(array( 'closure' => function() {}, 'closure_api' => true )); $this->assertTrue($c->canBatch()); $this->assertSame($c, $c->setCanBatch(false)); $this->assertFalse($c->canBatch()); } /** * @covers Guzzle\Service\Command\ClosureCommand::prepare * @covers Guzzle\Service\Command\ClosureCommand::build */ public function testExecutesClosure() { $c = new ClosureCommand(array( 'closure' => function($command, $api) { $command->set('testing', '123'); $request = RequestFactory::get('http://www.test.com/'); return $request; }, 'closure_api' => true )); $client = $this->getServiceBuilder()->get('mock'); $c->setClient($client)->prepare(); $this->assertEquals('123', $c->get('testing')); $this->assertEquals('http://www.test.com/', $c->getRequest()->getUrl()); } /** * @covers Guzzle\Service\Command\ClosureCommand * @expectedException UnexpectedValueException * @expectedExceptionMessage Closure command did not return a RequestInterface object */ public function testMustReturnRequest() { $c = new ClosureCommand(array( 'closure' => function($command, $api) { return false; }, 'closure_api' => true )); $client = $this->getServiceBuilder()->get('mock'); $c->setClient($client)->prepare(); } }tests/Guzzle/Tests/Service/Command/CommandSetTest.php000064400000014313151742733540016666 0ustar00 */ class CommandSetTest extends AbstractCommandTest { /** * @covers Guzzle\Service\Command\CommandSet::__construct * @covers Guzzle\Service\Command\CommandSet::hasCommand * @covers Guzzle\Service\Command\CommandSet::addCommand */ public function test__construct() { $pool = new Pool(); $cmd = new MockCommand(); $commandSet = new CommandSet(array($cmd), $pool); $this->assertTrue($commandSet->hasCommand($cmd)); } /** * @covers Guzzle\Service\Command\CommandSet::hasCommand * @covers Guzzle\Service\Command\CommandSet::addCommand * @covers Guzzle\Service\Command\CommandSet::removeCommand * @covers Guzzle\Service\Command\CommandSet::getParallelCommands * @covers Guzzle\Service\Command\CommandSet::getSerialCommands * @covers Guzzle\Service\Command\CommandSet::count * @covers Guzzle\Service\Command\CommandSet::getIterator */ public function testAllowsCommandManipulationAndIntrospection() { $commandSet = new CommandSet(); // Check when no commands are set $this->assertEquals(array(), $commandSet->getSerialCommands()); // Create some mock commands $command1 = new MockCommand(); $command2 = new MockCommand(); $command2->setCanBatch(false); // Check the fluent interface $this->assertEquals($commandSet, $commandSet->addCommand($command1)); $commandSet->addCommand($command2); // Check that the commands are registered and findable $this->assertTrue($commandSet->hasCommand($command1)); $this->assertTrue($commandSet->hasCommand($command2)); $this->assertTrue($commandSet->hasCommand('Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand')); // Test that the Countable interface is working $this->assertEquals(2, count($commandSet)); // Test that the IteratorAggregate interface is working $this->assertInstanceOf('ArrayIterator', $commandSet->getIterator()); $this->assertEquals(2, count($commandSet->getIterator())); // Check that filtering by command type works-- serial vs parallel $this->assertEquals(array($command1), $commandSet->getParallelCommands()); $this->assertEquals(array($command2), $commandSet->getSerialCommands()); // Remove the command by object $commandSet->removeCommand($command1); $this->assertFalse($commandSet->hasCommand($command1)); $this->assertTrue($commandSet->hasCommand($command2)); // Remove the command by class $commandSet->removeCommand('Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand'); $this->assertFalse($commandSet->hasCommand($command2)); } /** * @covers Guzzle\Service\Command\CommandSet::execute * @covers Guzzle\Service\Command\CommandSetException */ public function testThrowsExceptionWhenAnyCommandHasNoClient() { $cmd = new MockCommand; $commandSet = new CommandSet(array($cmd)); try { $commandSet->execute(); $this->fail('CommandSetException not thrown when a command did not have a client'); } catch (\Guzzle\Service\Command\CommandSetException $e) { $this->assertEquals(array($cmd), $e->getCommands()); } } /** * @covers Guzzle\Service\Command\CommandSet::execute * @covers Guzzle\Service\Command\CommandSet::update */ public function testExecutesCommands() { $client = $this->getClient(); $observer = new MockObserver(); // Create a Mock response $response = new Response(200, array( 'Content-Type' => 'application/xml' ), '123'); $client->getEventManager()->attach(new MockPlugin(array( $response, $response, $response ))); $command1 = new MockCommand(); $command1->setClient($client); $command2 = new MockCommand(); $command2->setClient($client); $command2->setCanBatch(false); $command3 = new MockCommand(); $command3->setClient($client); $commandSet = new CommandSet(array($command1, $command2, $command3)); $client->getEventManager()->attach($observer); $commandSet->execute(); $this->assertTrue($command1->isExecuted()); $this->assertTrue($command2->isExecuted()); $this->assertTrue($command3->isExecuted()); $this->assertTrue($command1->isPrepared()); $this->assertTrue($command2->isPrepared()); $this->assertTrue($command3->isPrepared()); $this->assertEquals($response, $command1->getResponse()); $this->assertEquals($response, $command2->getResponse()); $this->assertEquals(3, count(array_filter($observer->events, function($e) { return $e == 'command.before_send'; }))); $this->assertEquals(3, count(array_filter($observer->events, function($e) { return $e == 'command.after_send'; }))); // make sure the command set was detached as a listener $this->assertFalse($command1->getRequest()->getEventManager()->hasObserver('Guzzle\\Service\\Command\\CommandSet')); // make sure that the command reference was removed $this->assertFalse($command1->getRequest()->getParams()->hasKey('command')); // Make sure that the command.after_send events are staggered, meaning they happened as requests completed $lastEvent = ''; foreach ($observer->events as $e) { if ($lastEvent == 'command.after_send' && $e == 'command.after_send') { $this->fail('Not completing commands as they complete: ' . var_export($observer->events, true)); } $lastEvent = $e; } } }tests/Guzzle/Tests/Service/Command/CommandTest.php000064400000016577151742733540016230 0ustar00 */ class CommandTest extends AbstractCommandTest { /** * @covers Guzzle\Service\Command\AbstractCommand::__construct * @covers Guzzle\Service\Command\AbstractCommand::init * @covers Guzzle\Service\Command\AbstractCommand::canBatch * @covers Guzzle\Service\Command\AbstractCommand::isPrepared * @covers Guzzle\Service\Command\AbstractCommand::isExecuted */ public function testConstructorAddsDefaultParams() { $command = new MockCommand(); $this->assertEquals('123', $command->get('test')); $this->assertTrue($command->canBatch()); $this->assertFalse($command->isPrepared()); $this->assertFalse($command->isExecuted()); } /** * @covers Guzzle\Service\Command\AbstractCommand::getName */ public function testDeterminesShortName() { $api = new ApiCommand(array( 'name' => 'foobar' )); $command = new MockCommand(array(), $api); $this->assertEquals('foobar', $command->getName()); $command = new MockCommand(); $this->assertEquals('mock_command', $command->getName()); $command = new Sub(); $this->assertEquals('sub.sub', $command->getName()); } /** * @covers Guzzle\Service\Command\AbstractCommand::getRequest * @expectedException RuntimeException */ public function testGetRequestThrowsExceptionBeforePreparation() { $command = new MockCommand(); $command->getRequest(); } /** * @covers Guzzle\Service\Command\AbstractCommand::getResponse * @expectedException RuntimeException */ public function testGetResponseThrowsExceptionBeforePreparation() { $command = new MockCommand(); $command->getResponse(); } /** * @covers Guzzle\Service\Command\AbstractCommand::getResult * @expectedException RuntimeException */ public function testGetResultThrowsExceptionBeforePreparation() { $command = new MockCommand(); $command->getResult(); } /** * @covers Guzzle\Service\Command\AbstractCommand::setClient * @covers Guzzle\Service\Command\AbstractCommand::getClient * @covers Guzzle\Service\Command\AbstractCommand::prepare * @covers Guzzle\Service\Command\AbstractCommand::isPrepared */ public function testSetClient() { $command = new MockCommand(); $client = $this->getClient(); $command->setClient($client); $this->assertEquals($client, $command->getClient()); unset($client); unset($command); $command = new MockCommand(); $client = $this->getClient(); $command->setClient($client)->prepare(); $this->assertEquals($client, $command->getClient()); $this->assertTrue($command->isPrepared()); } /** * @covers Guzzle\Service\Command\AbstractCommand::execute * @covers Guzzle\Service\Command\AbstractCommand::setClient * @covers Guzzle\Service\Command\AbstractCommand::getRequest * @covers Guzzle\Service\Command\AbstractCommand::getResponse * @covers Guzzle\Service\Command\AbstractCommand::getResult * @covers Guzzle\Service\Command\AbstractCommand::prepare * @covers Guzzle\Service\Command\AbstractCommand::process * @covers Guzzle\Service\Command\AbstractCommand::prepare * * @covers Guzzle\Service\Client::execute */ public function testExecute() { $client = $this->getClient(); $response = new \Guzzle\Http\Message\Response(200, array( 'Content-Type' => 'application/xml' ), '123'); // Set a mock response $client->getEventManager()->attach(new MockPlugin(array( $response ))); $command = new MockCommand(); $this->assertSame($command, $command->setClient($client)); $this->assertSame($command, $command->execute()); // Implicitly calls prepare $this->assertTrue($command->isPrepared()); $this->assertTrue($command->isExecuted()); $this->assertSame($response, $command->getResponse()); $this->assertInstanceOf('Guzzle\\Http\\Message\\Request', $command->getRequest()); // Make sure that the result was automatically set to a SimpleXMLElement $this->assertInstanceOf('SimpleXMLElement', $command->getResult()); $this->assertEquals('123', (string)$command->getResult()->data); } /** * @covers Guzzle\Service\Command\AbstractCommand::process */ public function testProcessResponseIsNotXml() { $client = $this->getClient(); $client->getEventManager()->attach(new MockPlugin(array( new Response(200, array( 'Content-Type' => 'application/octect-stream' ), 'abc,def,ghi') ))); $command = new MockCommand(); $client->execute($command); // Make sure that the result was not converted to XML $this->assertFalse($command->getResult() instanceof \SimpleXMLElement); } /** * @covers Guzzle\Service\Command\AbstractCommand::execute * @expectedException RuntimeException */ public function testExecuteThrowsExceptionWhenNoClientIsSet() { $command = new MockCommand(); $command->execute(); } /** * @covers Guzzle\Service\Command\AbstractCommand::prepare * @expectedException RuntimeException */ public function testPrepareThrowsExceptionWhenNoClientIsSet() { $command = new MockCommand(); $command->prepare(); } /** * @covers Guzzle\Service\Command\AbstractCommand::prepare * @covers Guzzle\Service\Command\AbstractCommand::getRequestHeaders */ public function testCommandsAllowsCustomRequestHeaders() { $command = new MockCommand(); $command->getRequestHeaders()->set('test', '123'); $this->assertInstanceOf('Guzzle\Common\Collection', $command->getRequestHeaders()); $this->assertEquals('123', $command->getRequestHeaders()->get('test')); $command->setClient($this->getClient())->prepare(); $this->assertEquals('123', $command->getRequest()->getHeaders()->get('test')); } /** * @covers Guzzle\Service\Command\AbstractCommand */ public function testCommandsUsesApiCommand() { $api = new ApiCommand(array( 'name' => 'foobar', 'method' => 'POST', 'min_args' => 1, 'can_batch' => true, 'class' => 'Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand', 'args' => array( 'test' => array( 'default' => '123', 'type' => 'string' ) ))); $command = new MockCommand(array(), $api); $this->assertSame($api, $command->getApiCommand()); $command->setClient($this->getClient())->prepare(); $this->assertEquals('123', $command->get('test')); $this->assertSame($api, $command->getApiCommand($api)); } }tests/Guzzle/Tests/Service/Description/ApiCommandTest.php000064400000005354151742733540017556 0ustar00 */ class ApiCommandTest extends \Guzzle\Tests\GuzzleTestCase { /** * @covers Guzzle\Service\Description\ApiCommand */ public function testApiCommandIsDataObject() { $c = new ApiCommand(array( 'name' => 'test', 'doc' => 'doc', 'method' => 'POST', 'path' => '/api/v1', 'min_args' => 2, 'can_batch' => true, 'args' => array( 'key' => array( 'required' => 'true', 'type' => 'string', 'max_length' => 10 ), 'key_2' => array( 'required' => 'true', 'type' => 'integer', 'default' => 10 ) ) )); $this->assertEquals('test', $c->getName()); $this->assertEquals('doc', $c->getDoc()); $this->assertEquals('POST', $c->getMethod()); $this->assertEquals('/api/v1', $c->getPath()); $this->assertEquals('Guzzle\\Service\\Command\\ClosureCommand', $c->getConcreteClass()); $this->assertEquals(2, $c->getMinArgs()); $this->assertEquals(array( 'key' => new Collection(array( 'required' => 'true', 'type' => 'string', 'max_length' => 10 )), 'key_2' => new Collection(array( 'required' => 'true', 'type' => 'integer', 'default' => 10 )) ), $c->getArgs()); $this->assertEquals(new Collection(array( 'required' => 'true', 'type' => 'integer', 'default' => 10 )), $c->getArg('key_2')); $this->assertTrue($c->canBatch()); $this->assertEquals(array( 'test requires at least 2 arguments', 'Requires that the key argument be supplied.' ), $c->validate(new Collection(array()))); $this->assertNull($c->getArg('afefwef')); } /** * @covers Guzzle\Service\Description\ApiCommand::__construct */ public function testAllowsConcreteCommands() { $c = new ApiCommand(array( 'name' => 'test', 'class' => 'Guzzle\\Service\\Command\ClosureCommand', 'args' => array( 'p' => new Collection(array( 'name' => 'foo' )) ) )); $this->assertEquals('Guzzle\\Service\\Command\ClosureCommand', $c->getConcreteClass()); } }tests/Guzzle/Tests/Service/Description/DynamicCommandFactoryTest.php000064400000017262151742733540021762 0ustar00 */ class DynamicCommandFactoryTest extends \Guzzle\Tests\GuzzleTestCase { /** * @var ServiceDescription */ protected $service; /** * Setup the service description */ public function setUp() { $this->service = new ServiceDescription( array( new ApiCommand(array( 'name' => 'test_command', 'doc' => 'documentationForCommand', 'method' => 'HEAD', 'can_batch' => true, 'path' => '/{{key}}', 'args' => array( 'bucket' => array( 'required' => true, 'append' => '.', 'location' => 'path' ), 'key' => array( 'location' => 'path', 'prepend' => '/' ), 'acl' => array( 'location' => 'query' ), 'meta' => array( 'location' => 'header:X-Amz-Meta', 'append' => ':meta' ) ) )), new ApiCommand(array( 'name' => 'body', 'doc' => 'doc', 'method' => 'PUT', 'args' => array( 'b' => array( 'required' => true, 'prepend' => 'begin_body::', 'append' => '::end_body', 'location' => 'body' ), 'q' => array( 'location' => 'query:test' ), 'h' => array( 'location' => 'header:X-Custom' ), 'i' => array( 'static' => 'test', 'location' => 'query' ), // Data locations means the argument is just a placeholder for data // that can be referenced by other arguments 'data' => array( 'location' => 'data' ) ) )), new ApiCommand(array( 'name' => 'concrete', 'class' => 'Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand', 'args' => array() )) ) ); } /** * @covers Guzzle\Service\Description\DynamicCommandFactory */ public function testBuildsUsingPathParametersAndAppendSlashPrepend() { $client = new Client('http://www.example.com/'); $client->setDescription($this->service); $command = $this->service->createCommand('test_command', array( 'bucket' => 'test', 'key' => 'key' )); $request = $command->setClient($client)->prepare(); // Ensure that the path values were injected into the path and base_url $this->assertEquals('/key', $request->getPath()); $this->assertEquals('www.example.com', $request->getHost()); // Check the complete request $this->assertEquals( "HEAD /key HTTP/1.1\r\n" . "User-Agent: " . Guzzle::getDefaultUserAgent() . "\r\n" . "Host: www.example.com\r\n" . "\r\n", (string) $request); // Make sure the concrete command class is used $this->assertEquals( 'Guzzle\\Service\\Command\\ClosureCommand', get_class($command) ); } /** * @covers Guzzle\Service\Description\DynamicCommandFactory * @expectedException InvalidArgumentException */ public function testValidatesArgs() { $client = new Client('http://www.fragilerock.com/'); $client->setDescription($this->service); $command = $this->service->createCommand('test_command', array()); $client->execute($command); } /** * @covers Guzzle\Service\Description\DynamicCommandFactory */ public function testUsesDifferentLocations() { $client = new Client('http://www.tazmania.com/'); $command = $this->service->createCommand('body', array( 'b' => 'my-data', 'q' => 'abc', 'h' => 'haha' )); $request = $command->setClient($client)->prepare(); $this->assertEquals( "PUT /?test=abc&i=test HTTP/1.1\r\n" . "User-Agent: " . Guzzle::getDefaultUserAgent() . "\r\n" . "Host: www.tazmania.com\r\n" . "X-Custom: haha\r\n" . "Content-Length: 29\r\n" . "Expect: 100-Continue\r\n" . "\r\n" . "begin_body::my-data::end_body", (string) $request); unset($command); unset($request); $command = $this->service->createCommand('body', array( 'b' => 'my-data', 'q' => 'abc', 'h' => 'haha', 'i' => 'does not change the value because it\'s static' )); $request = $command->setClient($client)->prepare(); $this->assertEquals( "PUT /?test=abc&i=test HTTP/1.1\r\n" . "User-Agent: " . Guzzle::getDefaultUserAgent() . "\r\n" . "Host: www.tazmania.com\r\n" . "X-Custom: haha\r\n" . "Content-Length: 29\r\n" . "Expect: 100-Continue\r\n" . "\r\n" . "begin_body::my-data::end_body", (string) $request); } /** * @covers Guzzle\Service\Description\DynamicCommandFactory::createCommand */ public function testBuildsConcreteCommands() { $c = $this->service->createCommand('concrete'); $this->assertEquals('Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand', get_class($c)); } /** * @covers Guzzle\Service\Description\DynamicCommandFactory::createCommand */ public function testUsesAbsolutePaths() { $service = new ServiceDescription( array( new ApiCommand(array( 'name' => 'test_path', 'method' => 'GET', 'path' => '/test', )) ) ); $client = new Client('http://www.test.com/'); $client->setDescription($service); $command = $client->getCommand('test_path'); $request = $command->prepare(); $this->assertEquals('/test', $request->getPath()); } /** * @covers Guzzle\Service\Description\DynamicCommandFactory::createCommand */ public function testUsesRelativePaths() { $service = new ServiceDescription( array( new ApiCommand(array( 'name' => 'test_path', 'method' => 'GET', 'path' => 'test/abc', )) ) ); $client = new Client('http://www.test.com/api/v2'); $client->setDescription($service); $command = $client->getCommand('test_path'); $request = $command->prepare(); $this->assertEquals('/api/v2/test/abc', $request->getPath()); } }tests/Guzzle/Tests/Service/Description/ServiceDescriptionTest.php000064400000004105151742733540021343 0ustar00 */ class ServiceDescriptionTest extends \Guzzle\Tests\GuzzleTestCase { /** * @covers Guzzle\Service\Description\ServiceDescription * @covers Guzzle\Service\Description\ServiceDescription::__construct * @covers Guzzle\Service\Description\ServiceDescription::getCommands * @covers Guzzle\Service\Description\ServiceDescription::getCommand */ public function testConstructor() { $service = new ServiceDescription(array( new ApiCommand(array( 'name' => 'test_command', 'doc' => 'documentationForCommand', 'method' => 'DELETE', 'can_batch' => true, 'class' => 'Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand', 'args' => array( 'bucket' => array( 'required' => true ), 'key' => array( 'required' => true ) ) )) )); $this->assertEquals(1, count($service->getCommands())); $this->assertFalse($service->hasCommand('foobar')); $this->assertTrue($service->hasCommand('test_command')); $c = $service->createCommand('test_command', array( 'bucket' => '123', 'key' => 'abc' )); $this->assertInstanceOf('Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand', $c); $this->assertEquals('123', $c->get('bucket')); $this->assertEquals('abc', $c->get('key')); try { $service->createCommand('foobar', array()); $this->fail('Expected exception not thrown'); } catch (\InvalidArgumentException $e) { $this->assertEquals('foobar command not found', $e->getMessage()); } } }tests/Guzzle/Tests/Service/Description/XmlDescriptionBuilderTest.php000064400000012630151742733540022014 0ustar00 */ class XmlDescriptionBuilderTest extends \Guzzle\Tests\GuzzleTestCase { /** * @covers Guzzle\Service\Description\XmlDescriptionBuilder * @expectedException InvalidArgumentException */ public function testXmlBuilderThrowsExceptionWhenFileIsNotFound() { $builder = new XmlDescriptionBuilder('file_not_found'); } /** * @covers Guzzle\Service\Description\XmlDescriptionBuilder * @covers Guzzle\Service\Description\ServiceDescription */ public function testBuildsServiceUsingFile() { $builder = new XmlDescriptionBuilder(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'TestData' . DIRECTORY_SEPARATOR . 'test_service.xml'); $service = $builder->build(); $this->assertTrue($service->hasCommand('search')); $this->assertTrue($service->hasCommand('test')); $this->assertTrue($service->hasCommand('trends.location')); $this->assertTrue($service->hasCommand('geo.id')); $this->assertInstanceOf('Guzzle\\Service\\Description\\ApiCommand', $service->getCommand('search')); $this->assertInternalType('array', $service->getCommands()); $this->assertEquals(5, count($service->getCommands())); $this->assertInstanceOf('Guzzle\\Common\\NullObject', $service->getCommand('missing')); $command = $service->getCommand('test'); $this->assertInstanceOf('Guzzle\\Service\\Description\\ApiCommand', $command); $this->assertEquals('test', $command->getName()); $this->assertFalse($command->canBatch()); $this->assertInternalType('array', $command->getArgs()); $this->assertEquals(array( 'name' => 'bucket', 'required' => true, 'location' => 'path', 'doc' => 'Bucket location' ), $command->getArg('bucket')->getAll()); $this->assertEquals('DELETE', $command->getMethod()); $this->assertEquals('2', $command->getMinArgs()); $this->assertEquals('{{ bucket }}/{{ key }}{{ format }}', $command->getPath()); $this->assertEquals('Documentation', $command->getDoc()); $this->assertArrayHasKey('custom_filter', Inspector::getInstance()->getRegisteredFilters()); } /** * @covers Guzzle\Service\Description\XmlDescriptionBuilder * @covers Guzzle\Service\Description\ServiceDescription */ public function testBuildsServiceUsingXml() { $xml = << EOT; $builder = new XmlDescriptionBuilder($xml); $service = $builder->build(); $this->arrayHasKey('slug', Inspector::getInstance()->getRegisteredFilters()); $this->assertTrue($service->hasCommand('abstract')); $this->assertTrue($service->hasCommand('test1')); $this->assertTrue($service->hasCommand('test1')); $this->assertTrue($service->hasCommand('test2')); $this->assertTrue($service->hasCommand('test3')); $test1 = $service->getCommand('test1'); $test2 = $service->getCommand('test2'); $test3 = $service->getCommand('test3'); $this->assertEquals('GET', $test1->getMethod()); $this->assertEquals('/path/{{def}}', $test1->getPath()); $this->assertEquals('2', $test1->getMinArgs()); $this->assertEquals('static', $test1->getArg('st')->get('static')); $this->assertEquals('123', $test1->getArg('def')->get('default')); $this->assertEquals('DELETE', $test2->getMethod()); $this->assertEquals('/path/{{def}}', $test1->getPath()); $this->assertEquals('2', $test1->getMinArgs()); $this->assertEquals('static', $test1->getArg('st')->get('static')); $this->assertEquals('123', $test1->getArg('def')->get('default')); $this->assertEquals('header:X-Hd', $test1->getArg('hd')->get('location')); $this->assertEquals('', $test3->getMethod()); $this->assertEquals('Guzzle\Service\Command\ClosureCommand', $test3->getConcreteClass()); } /** * @covers Guzzle\Service\Description\XmlDescriptionBuilder * @expectedException RuntimeException */ public function testvalidatesXmlExtensions() { $xml = << EOT; $builder = new XmlDescriptionBuilder($xml); $service = $builder->build(); } }tests/Guzzle/Tests/Service/Mock/Command/MockCommand.php000064400000001311151742733540017047 0ustar00 * * @guzzle test default="123" required="true" doc="Test argument" * @guzzle other * @guzzle _internal default="abc" */ class MockCommand extends \Guzzle\Service\Command\AbstractCommand { /** * {@inheritdoc} */ protected function build() { $this->request = $this->client->createRequest(); } /** * Set whether or not the command can be batched * * @param bool $canBatch * * @return MockCommand */ public function setCanBatch($canBatch) { $this->canBatch = $canBatch; return $this; } }tests/Guzzle/Tests/Service/Mock/Command/OtherCommand.php000064400000000732151742733540017245 0ustar00 * * @guzzle test default="123" required="true" doc="Test argument" * @guzzle other * @guzzle arg type="string * guzzle static static="this is static" */ class OtherCommand extends MockCommand { /** * {@inheritdoc} */ protected function build() { $this->request = $this->client->getRequest('HEAD'); } }tests/Guzzle/Tests/Service/Mock/Command/Sub/Sub.php000064400000000342151742733540016144 0ustar00 */ class Sub extends MockCommand { }tests/Guzzle/Tests/Service/Mock/MockClient.php000064400000002003151742733540015330 0ustar00 */ class MockClient extends Client { /** * Factory method to create a new mock client * * @param array|Collection $config Configuration data. Array keys: * base_url - Base URL of web service * api_version - API version * scheme - URI scheme: http or https * * username - API username * * password - API password * * subdomain - Unfuddle account subdomain * * @return MockClient */ public static function factory($config) { $config = Inspector::prepareConfig($config, array( 'base_url' => '{{scheme}}://127.0.0.1:8124/{{api_version}}/{{subdomain}}', 'scheme' => 'http', 'api_version' => 'v1' ), array('username', 'password', 'subdomain')); return new self($config->get('base_url'), $config); } }tests/Guzzle/Tests/Service/Mock/MockResourceIterator.php000064400000001173151742733540017422 0ustar00 */ class MockResourceIterator extends ResourceIterator { protected function sendRequest() { $request = $this->client->createRequest(); $request->getQuery()->set('count', $this->calculatePageSize()); $data = json_decode($request->send()->getBody(true), true); $this->resourceList = $data['resources']; $this->nextToken = $data['next_token']; $this->retrievedCount += count($this->data['resources']); $this->currentIndex = 0; } }tests/Guzzle/Tests/Service/Plugin/MockPluginTest.php000064400000013055151742733540016566 0ustar00 */ class MockPluginTest extends \Guzzle\Tests\GuzzleTestCase { /** * @covers Guzzle\Service\Plugin\MockPlugin::__construct * @covers Guzzle\Service\Plugin\MockPlugin::isTemporary */ public function testCanBeTemporary() { $plugin = new MockPlugin(); $this->assertFalse($plugin->isTemporary()); $plugin = new MockPlugin(null, true); $this->assertTrue($plugin->isTemporary()); } /** * @covers Guzzle\Service\Plugin\MockPlugin::count */ public function testIsCountable() { $plugin = new MockPlugin(); $plugin->addResponse(Response::factory("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); $this->assertEquals(1, count($plugin)); } /** * @covers Guzzle\Service\Plugin\MockPlugin::clearQueue * @depends testIsCountable */ public function testCanClearQueue() { $plugin = new MockPlugin(); $plugin->addResponse(Response::factory("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); $plugin->clearQueue(); $this->assertEquals(0, count($plugin)); } /** * @covers Guzzle\Service\Plugin\MockPlugin::getQueue */ public function testCanInspectQueue() { $plugin = new MockPlugin(); $this->assertInternalType('array', $plugin->getQueue()); $plugin->addResponse(Response::factory("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); $queue = $plugin->getQueue(); $this->assertInternalType('array', $queue); $this->assertEquals(1, count($queue)); } /** * @covers Guzzle\Service\Plugin\MockPlugin::getMockFile */ public function testRetrievesResponsesFromFiles() { $response = MockPlugin::getMockFile(__DIR__ . '/../../TestData/mock_response'); $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response); $this->assertEquals(200, $response->getStatusCode()); } /** * @covers Guzzle\Service\Plugin\MockPlugin::getMockFile * @expectedException InvalidArgumentException */ public function testThrowsExcpetionWhenResponseFileIsNotFound() { MockPlugin::getMockFile('missing/filename'); } /** * @covers Guzzle\Service\Plugin\MockPlugin::addResponse * @expectedException InvalidArgumentException */ public function testInvalidResponsesThrowAnException() { $p = new MockPlugin(); $p->addResponse($this); } /** * @covers Guzzle\Service\Plugin\MockPlugin::addResponse */ public function testAddsResponseObjectsToQueue() { $p = new MockPlugin(); $response = Response::factory("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); $p->addResponse($response); $this->assertEquals(array($response), $p->getQueue()); } /** * @covers Guzzle\Service\Plugin\MockPlugin::addResponse */ public function testAddsResponseFilesToQueue() { $p = new MockPlugin(); $p->addResponse(__DIR__ . '/../../TestData/mock_response'); $this->assertEquals(1, count($p)); } /** * @covers Guzzle\Service\Plugin\MockPlugin::update * @covers Guzzle\Service\Plugin\MockPlugin::addResponse * @covers Guzzle\Service\Plugin\MockPlugin::dequeue * @depends testAddsResponseFilesToQueue */ public function testAddsMockResponseToRequestFromClient() { $p = new MockPlugin(); $response = MockPlugin::getMockFile(__DIR__ . '/../../TestData/mock_response'); $p->addResponse($response); $client = new Client('http://localhost:123/'); $client->getEventManager()->attach($p, 9999); $request = $client->get(); $request->send(); $this->assertSame($response, $request->getResponse()); $this->assertEquals(0, count($p)); } /** * @covers Guzzle\Service\Plugin\MockPlugin::update * @depends testAddsResponseFilesToQueue */ public function testUpdateIgnoresWhenEmpty() { $p = new MockPlugin(); $p->update(new MockSubject(), 'request.create'); } /** * @covers Guzzle\Service\Plugin\MockPlugin::update * @depends testAddsResponseFilesToQueue */ public function testUpdateIgnoresOtherEvents() { $p = new MockPlugin(); $p->addResponse(MockPlugin::getMockFile(__DIR__ . '/../../TestData/mock_response')); $p->update(new MockSubject(), 'foobar'); } /** * @covers Guzzle\Service\Plugin\MockPlugin::update * @covers Guzzle\Service\Plugin\MockPlugin::dequeue * @depends testAddsMockResponseToRequestFromClient */ public function testDetachesTemporaryWhenEmpty() { $p = new MockPlugin(null, true); $p->addResponse(MockPlugin::getMockFile(__DIR__ . '/../../TestData/mock_response')); $client = new Client('http://localhost:123/'); $client->getEventManager()->attach($p, 9999); $request = $client->get(); $request->send(); $this->assertFalse($client->getEventManager()->hasObserver($p)); } /** * @covers Guzzle\Service\Plugin\MockPlugin::__construct */ public function testLoadsResponsesFromConstructor() { $p = new MockPlugin(array(new Response(200))); $this->assertEquals(1, $p->count()); } }tests/Guzzle/Tests/Service/ResourceIteratorApplyBatchedTest.php000064400000004113151742733540021035 0ustar00 */ class ResourceIteratorApplyBatchedTest extends \Guzzle\Tests\GuzzleTestCase { /** * @covers Guzzle\Service\ResourceIteratorApplyBatched */ public function testSendsRequestsForNextSetOfResources() { $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }", "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"j\", \"resources\": [\"g\", \"h\", \"i\"] }", "HTTP/1.1 200 OK\r\nContent-Length: 41\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"j\"] }", )); $ri = new MockResourceIterator($this->getServiceBuilder()->get('mock'), array( 'page_size' => 3, 'resources' => array('a', 'b', 'c'), 'next_token' => 'd' )); $received = array(); $apply = new ResourceIteratorApplyBatched($ri, function(ResourceIterator $i, array $batch) use (&$received) { $received[] = $batch; }); $apply->apply(3); $requests = $this->getServer()->getReceivedRequests(true); $this->assertEquals(3, count($requests)); $this->assertEquals(3, $requests[0]->getQuery()->get('count')); $this->assertEquals(3, $requests[1]->getQuery()->get('count')); $this->assertEquals(3, $requests[2]->getQuery()->get('count')); $this->assertEquals(array('a', 'b', 'c'), array_values($received[0])); $this->assertEquals(array('d', 'e', 'f'), array_values($received[1])); $this->assertEquals(array('g', 'h', 'i'), array_values($received[2])); $this->assertEquals(array('j'), array_values($received[3])); $this->assertEquals(4, $apply->getBatchCount()); $this->assertEquals(10, $apply->getIteratedCount()); } }tests/Guzzle/Tests/Service/ResourceIteratorTest.php000064400000011004151742733540016551 0ustar00 */ class ResourceIteratorTest extends \Guzzle\Tests\GuzzleTestCase { /** * @covers Guzzle\Service\ResourceIterator */ public function testConstructorConfiguresDefaults() { $ri = $this->getMockForAbstractClass('Guzzle\\Service\\ResourceIterator', array( $this->getServiceBuilder()->get('mock'), array( 'limit' => 10, 'page_size' => 3, 'resources' => array('a', 'b', 'c'), 'next_token' => 'd' ) ), 'MockIterator'); $this->assertEquals('d', $ri->getNextToken()); $this->assertEquals(array('a', 'b', 'c'), $ri->toArray()); $ri->rewind(); $this->assertEquals('a', $ri->current()); $ri->next(); $this->assertEquals('b', $ri->current()); $ri->next(); $this->assertEquals('c', $ri->current()); // It ran out $ri->next(); $this->assertEquals('', $ri->current()); $this->assertEquals(3, count($ri)); $this->assertEquals(3, $ri->getPosition()); // Rewind works? $ri->rewind(); $this->assertEquals('a', $ri->current()); $ri->next(); $this->assertEquals('b', $ri->current()); $ri->next(); $this->assertEquals('c', $ri->current()); } /** * @covers Guzzle\Service\ResourceIterator */ public function testSendsRequestsForNextSetOfResources() { $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }", "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"j\", \"resources\": [\"g\", \"h\", \"i\"] }", "HTTP/1.1 200 OK\r\nContent-Length: 41\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"j\"] }", )); $ri = new MockResourceIterator($this->getServiceBuilder()->get('mock'), array( 'page_size' => 3, 'resources' => array('a', 'b', 'c'), 'next_token' => 'd' )); $this->assertEquals(array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'), $ri->toArray()); $requests = $this->getServer()->getReceivedRequests(true); $this->assertEquals(3, count($requests)); $this->assertEquals(3, $requests[0]->getQuery()->get('count')); $this->assertEquals(3, $requests[1]->getQuery()->get('count')); $this->assertEquals(3, $requests[2]->getQuery()->get('count')); // Reset and resend $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }", "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"j\", \"resources\": [\"g\", \"h\", \"i\"] }", "HTTP/1.1 200 OK\r\nContent-Length: 41\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"j\"] }", )); $d = array(); reset($ri); foreach ($ri as $data) { $d[] = $data; } $this->assertEquals(array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'), $d); } /** * @covers Guzzle\Service\ResourceIterator */ public function testCalculatesPageSize() { $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }", "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"j\", \"resources\": [\"g\", \"h\", \"i\"] }", "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"j\", \"resources\": [\"g\", \"h\"] }" )); $ri = new MockResourceIterator($this->getServiceBuilder()->get('mock'), array( 'page_size' => 3, 'limit' => 8, 'resources' => array('a', 'b', 'c'), 'next_token' => 'd' )); $this->assertEquals(array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'), $ri->toArray()); $requests = $this->getServer()->getReceivedRequests(true); $this->assertEquals(2, count($requests)); $this->assertEquals(3, $requests[0]->getQuery()->get('count')); $this->assertEquals(2, $requests[1]->getQuery()->get('count')); } }tests/Guzzle/Tests/Service/ServiceBuilderTest.php000064400000030137151742733540016167 0ustar00 */ class ServiceBuilderTest extends \Guzzle\Tests\GuzzleTestCase { protected $xmlConfig; protected $tempFile; protected $arrayData = array( 'michael.mock' => array( 'class' => 'Guzzle\\Tests\\Service\\Mock\\MockClient', 'params' => array( 'username' => 'michael', 'password' => 'testing123', 'subdomain' => 'michael', ), ), 'billy.mock' => array( 'class' => 'Guzzle\\Tests\\Service\\Mock\\MockClient', 'params' => array( 'username' => 'billy', 'password' => 'passw0rd', 'subdomain' => 'billy', ), ), 'billy.testing' => array( 'extends' => 'billy.mock', 'params' => array( 'subdomain' => 'test.billy', ), ), 'missing_params' => array( 'extends' => 'billy.mock' ) ); public function __construct() { $this->xmlConfig = << EOT; $this->tempFile = tempnam('/tmp', 'config.xml'); file_put_contents($this->tempFile, $this->xmlConfig); } public function __destruct() { if (is_file($this->tempFile)) { unlink($this->tempFile); } } /** * @covers Guzzle\Service\ServiceBuilder::factory */ public function testCanBeCreatedUsingAnXmlFile() { $builder = ServiceBuilder::factory($this->tempFile, null, 86400, 'xml'); $c = $builder->get('michael.mock'); $this->assertInstanceOf('Guzzle\\Tests\\Service\\Mock\\MockClient', $c); } /** * @covers Guzzle\Service\ServiceBuilder::factory * @expectedException RuntimeException * @expectedExceptionMessage Unable to open service configuration file foobarfile */ public function testFactoryEnsuresItCanOpenFile() { ServiceBuilder::factory('foobarfile'); } /** * @covers Guzzle\Service\ServiceBuilder::factory */ public function testFactoryCanBuildServicesThatExtendOtherServices() { $s = ServiceBuilder::factory($this->tempFile, null, 86400, 'xml'); $s = $s->get('billy.testing'); $this->assertEquals('test.billy', $s->getConfig('subdomain')); $this->assertEquals('billy', $s->getConfig('username')); } /** * @covers Guzzle\Service\ServiceBuilder::factory */ public function testFactoryThrowsExceptionWhenBuilderExtendsNonExistentBuilder() { $xml = '' . "\n" . ''; $tempFile = tempnam('/tmp', 'config.xml'); file_put_contents($tempFile, $xml); try { ServiceBuilder::factory($tempFile, null, 86400, 'xml'); unlink($tempFile); $this->fail('Test did not throw ServiceException'); } catch (\LogicException $e) { $this->assertEquals('invalid is trying to extend a non-existent service: missing', $e->getMessage()); } unlink($tempFile); } /** * @covers Guzzle\Service\ServiceBuilder::factory * @covers Guzzle\Service\ServiceBuilder */ public function testFactoryUsesCacheAdapterWhenAvailable() { $cache = new ArrayCache(); $adapter = new DoctrineCacheAdapter($cache); $this->assertEmpty($cache->getIds()); $s1 = ServiceBuilder::factory($this->tempFile, $adapter, 86400, 'xml'); // Make sure it added to the cache with a proper cache key $keys = $cache->getIds(); $this->assertNotEmpty($keys); $this->assertEquals(0, strpos($keys[0], 'guz_')); $this->assertFalse(strpos($keys[0], '__')); // Load this one from cache $s2 = ServiceBuilder::factory($this->tempFile, $adapter, 86400, 'xml'); $builder = ServiceBuilder::factory($this->tempFile, null, 86400, 'xml'); $this->assertEquals($s1, $s2); } /** * @covers Guzzle\Service\ServiceBuilder::get * @expectedException InvalidArgumentException * @expectedExceptionMessage No client is registered as foobar */ public function testThrowsExceptionWhenGettingInvalidClient() { ServiceBuilder::factory($this->tempFile, null, 86400, 'xml')->get('foobar'); } /** * @covers Guzzle\Service\ServiceBuilder::get */ public function testStoresClientCopy() { $builder = ServiceBuilder::factory($this->tempFile, null, 86400, 'xml'); $client = $builder->get('michael.mock'); $this->assertInstanceOf('Guzzle\\Tests\\Service\\Mock\\MockClient', $client); $this->assertEquals('http://127.0.0.1:8124/v1/michael', $client->getBaseUrl()); $this->assertEquals($client, $builder->get('michael.mock')); // Get another client but throw this one away $client2 = $builder->get('billy.mock', true); $this->assertInstanceOf('Guzzle\\Tests\\Service\\Mock\\MockClient', $client2); $this->assertEquals('http://127.0.0.1:8124/v1/billy', $client2->getBaseUrl()); // Make sure the original client is still there and set $this->assertTrue($client === $builder->get('michael.mock')); // Create a new billy.mock client that is stored $client3 = $builder->get('billy.mock'); // Make sure that the stored billy.mock client is equal to the other stored client $this->assertTrue($client3 === $builder->get('billy.mock')); // Make sure that this client is not equal to the previous throwaway client $this->assertFalse($client2 === $builder->get('billy.mock')); } /** * @covers Guzzle\Service\ServiceBuilder */ public function testBuildersPassOptionsThroughToClients() { $s = new ServiceBuilder(array( 'michael.mock' => array( 'class' => 'Guzzle\\Tests\\Service\\Mock\\MockClient', 'params' => array( 'base_url' => 'http://www.test.com/', 'subdomain' => 'michael', 'password' => 'test', 'username' => 'michael', 'curl.curlopt_proxyport' => 8080 ) ) )); $c = $s->get('michael.mock'); $this->assertEquals(8080, $c->getConfig('curl.curlopt_proxyport')); } /** * @covers Guzzle\Service\ServiceBuilder */ public function testUsesTheDefaultBuilderWhenNoBuilderIsSpecified() { $s = new ServiceBuilder(array( 'michael.mock' => array( 'class' => 'Guzzle\\Tests\\Service\\Mock\\MockClient', 'params' => array( 'base_url' => 'http://www.test.com/', 'subdomain' => 'michael', 'password' => 'test', 'username' => 'michael', 'curl.curlopt_proxyport' => 8080 ) ) )); $c = $s->get('michael.mock'); $this->assertInstanceOf('Guzzle\\Tests\\Service\\Mock\\MockClient', $c); } /** * @covers Guzzle\Service\ServiceBuilder::offsetSet * @covers Guzzle\Service\ServiceBuilder::offsetGet * @covers Guzzle\Service\ServiceBuilder::offsetUnset * @covers Guzzle\Service\ServiceBuilder::offsetExists */ public function testUsedAsArray() { $b = ServiceBuilder::factory($this->tempFile, null, 86400, 'xml'); $this->assertTrue($b->offsetExists('michael.mock')); $this->assertFalse($b->offsetExists('not_there')); $this->assertInstanceOf('Guzzle\\Service\\Client', $b['michael.mock']); unset($b['michael.mock']); $this->assertFalse($b->offsetExists('michael.mock')); $b['michael.mock'] = new Client('http://www.test.com/'); $this->assertInstanceOf('Guzzle\\Service\\Client', $b['michael.mock']); } /** * @covers Guzzle\Service\ServiceBuilder::factory */ public function testFactoryCanCreateFromXml() { $b = ServiceBuilder::factory(new \SimpleXMLElement($this->xmlConfig)); $this->assertTrue($b->offsetExists('michael.mock')); $this->assertTrue($b->offsetExists('billy.mock')); $this->assertTrue($b->offsetExists('billy.testing')); } /** * @covers Guzzle\Service\ServiceBuilder::factory */ public function testFactoryCanCreateFromJson() { $tmp = sys_get_temp_dir() . '/test.js'; file_put_contents($tmp, json_encode($this->arrayData)); $b = ServiceBuilder::factory($tmp); unlink($tmp); $s = $b->get('billy.testing'); $this->assertEquals('test.billy', $s->getConfig('subdomain')); $this->assertEquals('billy', $s->getConfig('username')); } /** * @covers Guzzle\Service\ServiceBuilder::factory */ public function testFactoryCanCreateFromArray() { $b = ServiceBuilder::factory($this->arrayData); $s = $b->get('billy.testing'); $this->assertEquals('test.billy', $s->getConfig('subdomain')); $this->assertEquals('billy', $s->getConfig('username')); } /** * @covers Guzzle\Service\ServiceBuilder::factory * @expectedException RuntimeException * @expectedExceptionMessage Unknown file type abc */ public function testFactoryValidatesFileExtension() { $tmp = sys_get_temp_dir() . '/test.abc'; file_put_contents($tmp, 'data'); try { ServiceBuilder::factory($tmp); } catch (\RuntimeException $e) { unlink($tmp); throw $e; } } /** * @covers Guzzle\Service\ServiceBuilder::factory * @expectedException InvalidArgumentException * @expectedExceptionMessage $data must be an instance of SimpleXMLElement */ public function testFactoryValidatesObjectTypes() { ServiceBuilder::factory(new \stdClass()); } /** * @covers Guzzle\Service\ServiceBuilder::factory */ public function testFactoryDoesNotRequireParams() { $b = ServiceBuilder::factory($this->arrayData); $s = $b->get('missing_params'); $this->assertEquals('billy', $s->getConfig('username')); } /** * @covers Guzzle\Service\ServiceBuilder:: */ public function testBuilderAllowsReferencesBetweenClients() { $builder = ServiceBuilder::factory(array( 'a' => array( 'class' => 'Guzzle\\Tests\\Service\\Mock\\MockClient', 'params' => array( 'other_client' => '$.b', 'username' => 'x', 'password' => 'y', 'subdomain' => 'z' ) ), 'b' => array( 'class' => 'Guzzle\\Tests\\Service\\Mock\\MockClient', 'params' => array( 'username' => '1', 'password' => '2', 'subdomain' => '3' ) ) )); $client = $builder['a']; $this->assertEquals('x', $client->getConfig('username')); $this->assertSame($builder['b'], $client->getConfig('other_client')); $this->assertEquals('1', $builder['b']->getConfig('username')); } }tests/Guzzle/Tests/TestData/FileBody.txt000075500000000000151742733540014243 0ustar00tests/Guzzle/Tests/TestData/mock_response000064400000000043151742733540014603 0ustar00HTTP/1.1 200 OK Content-Length: 0 tests/Guzzle/Tests/TestData/services.xml000064400000003352151742733540014364 0ustar00 tests/Guzzle/Tests/TestData/test.tar.gz000064400000000002151742733540014112 0ustar00tests/Guzzle/Tests/TestData/test.tar.gz.cpgz000064400000000200151742733540015054 0ustar00‹307Ad`b`nj ¤ÍÍ@¤�‘ XÔ"ihhjll`j ¤Á|#$�z 覘™˜`˜fˆl𱑠ØBC3„N#=ý’Ôâ½’Ä"½ô*fL³‘­F†ØE�‘8!AŽž>®AŠŠŠ #íb«§tests/Guzzle/Tests/TestData/test_service.xml000064400000004625151742733540015244 0ustar00 Documentation tests/bootstrap.php000064400000001352151742733540010450 0ustar00registerNamespaces(array( 'Guzzle\Tests' => __DIR__, 'Guzzle' => __DIR__ . '/../src', 'Doctrine' => __DIR__ . '/../vendor/Doctrine/lib', 'Monolog' => __DIR__ . '/../vendor/Monolog/src' )); $classLoader->registerPrefix('Zend_', __DIR__ . '/../vendor'); $classLoader->register(); $servicesFile = __DIR__ . '/Guzzle/Tests/TestData/services.xml'; GuzzleTestCase::setServiceBuilder(\Guzzle\Service\ServiceBuilder::factory($servicesFile));vendor/Zend/Cache.php000064400000022731151742733540010475 0ustar00setBackend($backendObject); return $frontendObject; } /** * Backend Constructor * * @param string $backend * @param array $backendOptions * @param boolean $customBackendNaming * @param boolean $autoload * @return Zend_Cache_Backend */ public static function _makeBackend($backend, $backendOptions, $customBackendNaming = false, $autoload = false) { if (!$customBackendNaming) { $backend = self::_normalizeName($backend); } if (in_array($backend, Zend_Cache::$standardBackends)) { // we use a standard backend $backendClass = 'Zend_Cache_Backend_' . $backend; // security controls are explicit // require_once str_replace('_', DIRECTORY_SEPARATOR, $backendClass) . '.php'; } else { // we use a custom backend if (!preg_match('~^[\w]+$~D', $backend)) { Zend_Cache::throwException("Invalid backend name [$backend]"); } if (!$customBackendNaming) { // we use this boolean to avoid an API break $backendClass = 'Zend_Cache_Backend_' . $backend; } else { $backendClass = $backend; } if (!$autoload) { $file = str_replace('_', DIRECTORY_SEPARATOR, $backendClass) . '.php'; if (!(self::_isReadable($file))) { self::throwException("file $file not found in include_path"); } // require_once $file; } } return new $backendClass($backendOptions); } /** * Frontend Constructor * * @param string $frontend * @param array $frontendOptions * @param boolean $customFrontendNaming * @param boolean $autoload * @return Zend_Cache_Core|Zend_Cache_Frontend */ public static function _makeFrontend($frontend, $frontendOptions = array(), $customFrontendNaming = false, $autoload = false) { if (!$customFrontendNaming) { $frontend = self::_normalizeName($frontend); } if (in_array($frontend, self::$standardFrontends)) { // we use a standard frontend // For perfs reasons, with frontend == 'Core', we can interact with the Core itself $frontendClass = 'Zend_Cache_' . ($frontend != 'Core' ? 'Frontend_' : '') . $frontend; // security controls are explicit // require_once str_replace('_', DIRECTORY_SEPARATOR, $frontendClass) . '.php'; } else { // we use a custom frontend if (!preg_match('~^[\w]+$~D', $frontend)) { Zend_Cache::throwException("Invalid frontend name [$frontend]"); } if (!$customFrontendNaming) { // we use this boolean to avoid an API break $frontendClass = 'Zend_Cache_Frontend_' . $frontend; } else { $frontendClass = $frontend; } if (!$autoload) { $file = str_replace('_', DIRECTORY_SEPARATOR, $frontendClass) . '.php'; if (!(self::_isReadable($file))) { self::throwException("file $file not found in include_path"); } // require_once $file; } } return new $frontendClass($frontendOptions); } /** * Throw an exception * * Note : for perf reasons, the "load" of Zend/Cache/Exception is dynamic * @param string $msg Message for the exception * @throws Zend_Cache_Exception */ public static function throwException($msg, Exception $e = null) { // For perfs reasons, we use this dynamic inclusion // require_once 'Zend/Cache/Exception.php'; throw new Zend_Cache_Exception($msg, 0, $e); } /** * Normalize frontend and backend names to allow multiple words TitleCased * * @param string $name Name to normalize * @return string */ protected static function _normalizeName($name) { $name = ucfirst(strtolower($name)); $name = str_replace(array('-', '_', '.'), ' ', $name); $name = ucwords($name); $name = str_replace(' ', '', $name); if (stripos($name, 'ZendServer') === 0) { $name = 'ZendServer_' . substr($name, strlen('ZendServer')); } return $name; } /** * Returns TRUE if the $filename is readable, or FALSE otherwise. * This function uses the PHP include_path, where PHP's is_readable() * does not. * * Note : this method comes from Zend_Loader (see #ZF-2891 for details) * * @param string $filename * @return boolean */ private static function _isReadable($filename) { if (!$fh = @fopen($filename, 'r', true)) { return false; } @fclose($fh); return true; } } vendor/Zend/Cache/Backend.php000064400000017124151742733540012024 0ustar00 (int) lifetime : * - Cache lifetime (in seconds) * - If null, the cache is valid forever * * =====> (int) logging : * - if set to true, a logging is activated throw Zend_Log * * @var array directives */ protected $_directives = array( 'lifetime' => 3600, 'logging' => false, 'logger' => null ); /** * Available options * * @var array available options */ protected $_options = array(); /** * Constructor * * @param array $options Associative array of options * @throws Zend_Cache_Exception * @return void */ public function __construct(array $options = array()) { while (list($name, $value) = each($options)) { $this->setOption($name, $value); } } /** * Set the frontend directives * * @param array $directives Assoc of directives * @throws Zend_Cache_Exception * @return void */ public function setDirectives($directives) { if (!is_array($directives)) Zend_Cache::throwException('Directives parameter must be an array'); while (list($name, $value) = each($directives)) { if (!is_string($name)) { Zend_Cache::throwException("Incorrect option name : $name"); } $name = strtolower($name); if (array_key_exists($name, $this->_directives)) { $this->_directives[$name] = $value; } } $this->_loggerSanity(); } /** * Set an option * * @param string $name * @param mixed $value * @throws Zend_Cache_Exception * @return void */ public function setOption($name, $value) { if (!is_string($name)) { Zend_Cache::throwException("Incorrect option name : $name"); } $name = strtolower($name); if (array_key_exists($name, $this->_options)) { $this->_options[$name] = $value; } } /** * Get the life time * * if $specificLifetime is not false, the given specific life time is used * else, the global lifetime is used * * @param int $specificLifetime * @return int Cache life time */ public function getLifetime($specificLifetime) { if ($specificLifetime === false) { return $this->_directives['lifetime']; } return $specificLifetime; } /** * Return true if the automatic cleaning is available for the backend * * DEPRECATED : use getCapabilities() instead * * @deprecated * @return boolean */ public function isAutomaticCleaningAvailable() { return true; } /** * Determine system TMP directory and detect if we have read access * * inspired from Zend_File_Transfer_Adapter_Abstract * * @return string * @throws Zend_Cache_Exception if unable to determine directory */ public function getTmpDir() { $tmpdir = array(); foreach (array($_ENV, $_SERVER) as $tab) { foreach (array('TMPDIR', 'TEMP', 'TMP', 'windir', 'SystemRoot') as $key) { if (isset($tab[$key])) { if (($key == 'windir') or ($key == 'SystemRoot')) { $dir = realpath($tab[$key] . '\\temp'); } else { $dir = realpath($tab[$key]); } if ($this->_isGoodTmpDir($dir)) { return $dir; } } } } $upload = ini_get('upload_tmp_dir'); if ($upload) { $dir = realpath($upload); if ($this->_isGoodTmpDir($dir)) { return $dir; } } if (function_exists('sys_get_temp_dir')) { $dir = sys_get_temp_dir(); if ($this->_isGoodTmpDir($dir)) { return $dir; } } // Attemp to detect by creating a temporary file $tempFile = tempnam(md5(uniqid(rand(), TRUE)), ''); if ($tempFile) { $dir = realpath(dirname($tempFile)); unlink($tempFile); if ($this->_isGoodTmpDir($dir)) { return $dir; } } if ($this->_isGoodTmpDir('/tmp')) { return '/tmp'; } if ($this->_isGoodTmpDir('\\temp')) { return '\\temp'; } Zend_Cache::throwException('Could not determine temp directory, please specify a cache_dir manually'); } /** * Verify if the given temporary directory is readable and writable * * @param $dir temporary directory * @return boolean true if the directory is ok */ protected function _isGoodTmpDir($dir) { if (is_readable($dir)) { if (is_writable($dir)) { return true; } } return false; } /** * Make sure if we enable logging that the Zend_Log class * is available. * Create a default log object if none is set. * * @throws Zend_Cache_Exception * @return void */ protected function _loggerSanity() { if (!isset($this->_directives['logging']) || !$this->_directives['logging']) { return; } if (isset($this->_directives['logger'])) { if ($this->_directives['logger'] instanceof Zend_Log) { return; } Zend_Cache::throwException('Logger object is not an instance of Zend_Log class.'); } // Create a default logger to the standard output stream // require_once 'Zend/Log.php'; // require_once 'Zend/Log/Writer/Stream.php'; $logger = new Zend_Log(new Zend_Log_Writer_Stream('php://output')); $this->_directives['logger'] = $logger; } /** * Log a message at the WARN (4) priority. * * @param string $message * @throws Zend_Cache_Exception * @return void */ protected function _log($message, $priority = 4) { if (!$this->_directives['logging']) { return; } if (!isset($this->_directives['logger'])) { Zend_Cache::throwException('Logging is enabled but logger is not set.'); } $logger = $this->_directives['logger']; if (!$logger instanceof Zend_Log) { Zend_Cache::throwException('Logger object is not an instance of Zend_Log class.'); } $logger->log($message, $priority); } } vendor/Zend/Cache/Backend/Apc.php000064400000025546151742733540012536 0ustar00 infinite lifetime) * @return boolean true if no problem */ public function save($data, $id, $tags = array(), $specificLifetime = false) { $lifetime = $this->getLifetime($specificLifetime); $result = apc_store($id, array($data, time(), $lifetime), $lifetime); if (count($tags) > 0) { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_APC_BACKEND); } return $result; } /** * Remove a cache record * * @param string $id cache id * @return boolean true if no problem */ public function remove($id) { return apc_delete($id); } /** * Clean some cache records * * Available modes are : * 'all' (default) => remove all cache entries ($tags is not used) * 'old' => unsupported * 'matchingTag' => unsupported * 'notMatchingTag' => unsupported * 'matchingAnyTag' => unsupported * * @param string $mode clean mode * @param array $tags array of tags * @throws Zend_Cache_Exception * @return boolean true if no problem */ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) { switch ($mode) { case Zend_Cache::CLEANING_MODE_ALL: return apc_clear_cache('user'); break; case Zend_Cache::CLEANING_MODE_OLD: $this->_log("Zend_Cache_Backend_Apc::clean() : CLEANING_MODE_OLD is unsupported by the Apc backend"); break; case Zend_Cache::CLEANING_MODE_MATCHING_TAG: case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG: case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG: $this->_log(self::TAGS_UNSUPPORTED_BY_CLEAN_OF_APC_BACKEND); break; default: Zend_Cache::throwException('Invalid mode for clean() method'); break; } } /** * Return true if the automatic cleaning is available for the backend * * DEPRECATED : use getCapabilities() instead * * @deprecated * @return boolean */ public function isAutomaticCleaningAvailable() { return false; } /** * Return the filling percentage of the backend storage * * @throws Zend_Cache_Exception * @return int integer between 0 and 100 */ public function getFillingPercentage() { $mem = apc_sma_info(true); $memSize = $mem['num_seg'] * $mem['seg_size']; $memAvailable= $mem['avail_mem']; $memUsed = $memSize - $memAvailable; if ($memSize == 0) { Zend_Cache::throwException('can\'t get apc memory size'); } if ($memUsed > $memSize) { return 100; } return ((int) (100. * ($memUsed / $memSize))); } /** * Return an array of stored tags * * @return array array of stored tags (string) */ public function getTags() { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_APC_BACKEND); return array(); } /** * Return an array of stored cache ids which match given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * @return array array of matching cache ids (string) */ public function getIdsMatchingTags($tags = array()) { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_APC_BACKEND); return array(); } /** * Return an array of stored cache ids which don't match given tags * * In case of multiple tags, a logical OR is made between tags * * @param array $tags array of tags * @return array array of not matching cache ids (string) */ public function getIdsNotMatchingTags($tags = array()) { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_APC_BACKEND); return array(); } /** * Return an array of stored cache ids which match any given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * @return array array of any matching cache ids (string) */ public function getIdsMatchingAnyTags($tags = array()) { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_APC_BACKEND); return array(); } /** * Return an array of stored cache ids * * @return array array of stored cache ids (string) */ public function getIds() { $res = array(); $array = apc_cache_info('user', false); $records = $array['cache_list']; foreach ($records as $record) { $res[] = $record['info']; } return $res; } /** * Return an array of metadatas for the given cache id * * The array must include these keys : * - expire : the expire timestamp * - tags : a string array of tags * - mtime : timestamp of last modification time * * @param string $id cache id * @return array array of metadatas (false if the cache id is not found) */ public function getMetadatas($id) { $tmp = apc_fetch($id); if (is_array($tmp)) { $data = $tmp[0]; $mtime = $tmp[1]; if (!isset($tmp[2])) { // because this record is only with 1.7 release // if old cache records are still there... return false; } $lifetime = $tmp[2]; return array( 'expire' => $mtime + $lifetime, 'tags' => array(), 'mtime' => $mtime ); } return false; } /** * Give (if possible) an extra lifetime to the given cache id * * @param string $id cache id * @param int $extraLifetime * @return boolean true if ok */ public function touch($id, $extraLifetime) { $tmp = apc_fetch($id); if (is_array($tmp)) { $data = $tmp[0]; $mtime = $tmp[1]; if (!isset($tmp[2])) { // because this record is only with 1.7 release // if old cache records are still there... return false; } $lifetime = $tmp[2]; $newLifetime = $lifetime - (time() - $mtime) + $extraLifetime; if ($newLifetime <=0) { return false; } apc_store($id, array($data, time(), $newLifetime), $newLifetime); return true; } return false; } /** * Return an associative array of capabilities (booleans) of the backend * * The array must include these keys : * - automatic_cleaning (is automating cleaning necessary) * - tags (are tags supported) * - expired_read (is it possible to read expired cache records * (for doNotTestCacheValidity option for example)) * - priority does the backend deal with priority when saving * - infinite_lifetime (is infinite lifetime can work with this backend) * - get_list (is it possible to get the list of cache ids and the complete list of tags) * * @return array associative of with capabilities */ public function getCapabilities() { return array( 'automatic_cleaning' => false, 'tags' => false, 'expired_read' => false, 'priority' => false, 'infinite_lifetime' => false, 'get_list' => true ); } } vendor/Zend/Cache/Backend/BlackHole.php000064400000016446151742733540013656 0ustar00 infinite lifetime) * @return boolean true if no problem */ public function save($data, $id, $tags = array(), $specificLifetime = false) { return true; } /** * Remove a cache record * * @param string $id cache id * @return boolean true if no problem */ public function remove($id) { return true; } /** * Clean some cache records * * Available modes are : * 'all' (default) => remove all cache entries ($tags is not used) * 'old' => remove too old cache entries ($tags is not used) * 'matchingTag' => remove cache entries matching all given tags * ($tags can be an array of strings or a single string) * 'notMatchingTag' => remove cache entries not matching one of the given tags * ($tags can be an array of strings or a single string) * 'matchingAnyTag' => remove cache entries matching any given tags * ($tags can be an array of strings or a single string) * * @param string $mode clean mode * @param tags array $tags array of tags * @return boolean true if no problem */ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) { return true; } /** * Return an array of stored cache ids * * @return array array of stored cache ids (string) */ public function getIds() { return array(); } /** * Return an array of stored tags * * @return array array of stored tags (string) */ public function getTags() { return array(); } /** * Return an array of stored cache ids which match given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * @return array array of matching cache ids (string) */ public function getIdsMatchingTags($tags = array()) { return array(); } /** * Return an array of stored cache ids which don't match given tags * * In case of multiple tags, a logical OR is made between tags * * @param array $tags array of tags * @return array array of not matching cache ids (string) */ public function getIdsNotMatchingTags($tags = array()) { return array(); } /** * Return an array of stored cache ids which match any given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * @return array array of any matching cache ids (string) */ public function getIdsMatchingAnyTags($tags = array()) { return array(); } /** * Return the filling percentage of the backend storage * * @return int integer between 0 and 100 * @throws Zend_Cache_Exception */ public function getFillingPercentage() { return 0; } /** * Return an array of metadatas for the given cache id * * The array must include these keys : * - expire : the expire timestamp * - tags : a string array of tags * - mtime : timestamp of last modification time * * @param string $id cache id * @return array array of metadatas (false if the cache id is not found) */ public function getMetadatas($id) { return false; } /** * Give (if possible) an extra lifetime to the given cache id * * @param string $id cache id * @param int $extraLifetime * @return boolean true if ok */ public function touch($id, $extraLifetime) { return false; } /** * Return an associative array of capabilities (booleans) of the backend * * The array must include these keys : * - automatic_cleaning (is automating cleaning necessary) * - tags (are tags supported) * - expired_read (is it possible to read expired cache records * (for doNotTestCacheValidity option for example)) * - priority does the backend deal with priority when saving * - infinite_lifetime (is infinite lifetime can work with this backend) * - get_list (is it possible to get the list of cache ids and the complete list of tags) * * @return array associative of with capabilities */ public function getCapabilities() { return array( 'automatic_cleaning' => true, 'tags' => true, 'expired_read' => true, 'priority' => true, 'infinite_lifetime' => true, 'get_list' => true, ); } /** * PUBLIC METHOD FOR UNIT TESTING ONLY ! * * Force a cache record to expire * * @param string $id cache id */ public function ___expire($id) { } } vendor/Zend/Cache/Backend/ExtendedInterface.php000064400000007705151742733540015411 0ustar00 (string) cache_dir : * - Directory where to put the cache files * * =====> (boolean) file_locking : * - Enable / disable file_locking * - Can avoid cache corruption under bad circumstances but it doesn't work on multithread * webservers and on NFS filesystems for example * * =====> (boolean) read_control : * - Enable / disable read control * - If enabled, a control key is embeded in cache file and this key is compared with the one * calculated after the reading. * * =====> (string) read_control_type : * - Type of read control (only if read control is enabled). Available values are : * 'md5' for a md5 hash control (best but slowest) * 'crc32' for a crc32 hash control (lightly less safe but faster, better choice) * 'adler32' for an adler32 hash control (excellent choice too, faster than crc32) * 'strlen' for a length only test (fastest) * * =====> (int) hashed_directory_level : * - Hashed directory level * - Set the hashed directory structure level. 0 means "no hashed directory * structure", 1 means "one level of directory", 2 means "two levels"... * This option can speed up the cache only when you have many thousands of * cache file. Only specific benchs can help you to choose the perfect value * for you. Maybe, 1 or 2 is a good start. * * =====> (int) hashed_directory_umask : * - Umask for hashed directory structure * * =====> (string) file_name_prefix : * - prefix for cache files * - be really carefull with this option because a too generic value in a system cache dir * (like /tmp) can cause disasters when cleaning the cache * * =====> (int) cache_file_umask : * - Umask for cache files * * =====> (int) metatadatas_array_max_size : * - max size for the metadatas array (don't change this value unless you * know what you are doing) * * @var array available options */ protected $_options = array( 'cache_dir' => null, 'file_locking' => true, 'read_control' => true, 'read_control_type' => 'crc32', 'hashed_directory_level' => 0, 'hashed_directory_umask' => 0700, 'file_name_prefix' => 'zend_cache', 'cache_file_umask' => 0600, 'metadatas_array_max_size' => 100 ); /** * Array of metadatas (each item is an associative array) * * @var array */ protected $_metadatasArray = array(); /** * Constructor * * @param array $options associative array of options * @throws Zend_Cache_Exception * @return void */ public function __construct(array $options = array()) { parent::__construct($options); if ($this->_options['cache_dir'] !== null) { // particular case for this option $this->setCacheDir($this->_options['cache_dir']); } else { $this->setCacheDir(self::getTmpDir() . DIRECTORY_SEPARATOR, false); } if (isset($this->_options['file_name_prefix'])) { // particular case for this option if (!preg_match('~^[a-zA-Z0-9_]+$~D', $this->_options['file_name_prefix'])) { Zend_Cache::throwException('Invalid file_name_prefix : must use only [a-zA-Z0-9_]'); } } if ($this->_options['metadatas_array_max_size'] < 10) { Zend_Cache::throwException('Invalid metadatas_array_max_size, must be > 10'); } if (isset($options['hashed_directory_umask']) && is_string($options['hashed_directory_umask'])) { // See #ZF-4422 $this->_options['hashed_directory_umask'] = octdec($this->_options['hashed_directory_umask']); } if (isset($options['cache_file_umask']) && is_string($options['cache_file_umask'])) { // See #ZF-4422 $this->_options['cache_file_umask'] = octdec($this->_options['cache_file_umask']); } } /** * Set the cache_dir (particular case of setOption() method) * * @param string $value * @param boolean $trailingSeparator If true, add a trailing separator is necessary * @throws Zend_Cache_Exception * @return void */ public function setCacheDir($value, $trailingSeparator = true) { if (!is_dir($value)) { Zend_Cache::throwException('cache_dir must be a directory'); } if (!is_writable($value)) { Zend_Cache::throwException('cache_dir is not writable'); } if ($trailingSeparator) { // add a trailing DIRECTORY_SEPARATOR if necessary $value = rtrim(realpath($value), '\\/') . DIRECTORY_SEPARATOR; } $this->_options['cache_dir'] = $value; } /** * Test if a cache is available for the given id and (if yes) return it (false else) * * @param string $id cache id * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested * @return string|false cached datas */ public function load($id, $doNotTestCacheValidity = false) { if (!($this->_test($id, $doNotTestCacheValidity))) { // The cache is not hit ! return false; } $metadatas = $this->_getMetadatas($id); $file = $this->_file($id); $data = $this->_fileGetContents($file); if ($this->_options['read_control']) { $hashData = $this->_hash($data, $this->_options['read_control_type']); $hashControl = $metadatas['hash']; if ($hashData != $hashControl) { // Problem detected by the read control ! $this->_log('Zend_Cache_Backend_File::load() / read_control : stored hash and computed hash do not match'); $this->remove($id); return false; } } return $data; } /** * Test if a cache is available or not (for the given id) * * @param string $id cache id * @return mixed false (a cache is not available) or "last modified" timestamp (int) of the available cache record */ public function test($id) { clearstatcache(); return $this->_test($id, false); } /** * Save some string datas into a cache record * * Note : $data is always "string" (serialization is done by the * core not by the backend) * * @param string $data Datas to cache * @param string $id Cache id * @param array $tags Array of strings, the cache record will be tagged by each string entry * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime) * @return boolean true if no problem */ public function save($data, $id, $tags = array(), $specificLifetime = false) { clearstatcache(); $file = $this->_file($id); $path = $this->_path($id); if ($this->_options['hashed_directory_level'] > 0) { if (!is_writable($path)) { // maybe, we just have to build the directory structure $this->_recursiveMkdirAndChmod($id); } if (!is_writable($path)) { return false; } } if ($this->_options['read_control']) { $hash = $this->_hash($data, $this->_options['read_control_type']); } else { $hash = ''; } $metadatas = array( 'hash' => $hash, 'mtime' => time(), 'expire' => $this->_expireTime($this->getLifetime($specificLifetime)), 'tags' => $tags ); $res = $this->_setMetadatas($id, $metadatas); if (!$res) { $this->_log('Zend_Cache_Backend_File::save() / error on saving metadata'); return false; } $res = $this->_filePutContents($file, $data); return $res; } /** * Remove a cache record * * @param string $id cache id * @return boolean true if no problem */ public function remove($id) { $file = $this->_file($id); $boolRemove = $this->_remove($file); $boolMetadata = $this->_delMetadatas($id); return $boolMetadata && $boolRemove; } /** * Clean some cache records * * Available modes are : * 'all' (default) => remove all cache entries ($tags is not used) * 'old' => remove too old cache entries ($tags is not used) * 'matchingTag' => remove cache entries matching all given tags * ($tags can be an array of strings or a single string) * 'notMatchingTag' => remove cache entries not matching one of the given tags * ($tags can be an array of strings or a single string) * 'matchingAnyTag' => remove cache entries matching any given tags * ($tags can be an array of strings or a single string) * * @param string $mode clean mode * @param tags array $tags array of tags * @return boolean true if no problem */ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) { // We use this protected method to hide the recursive stuff clearstatcache(); return $this->_clean($this->_options['cache_dir'], $mode, $tags); } /** * Return an array of stored cache ids * * @return array array of stored cache ids (string) */ public function getIds() { return $this->_get($this->_options['cache_dir'], 'ids', array()); } /** * Return an array of stored tags * * @return array array of stored tags (string) */ public function getTags() { return $this->_get($this->_options['cache_dir'], 'tags', array()); } /** * Return an array of stored cache ids which match given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * @return array array of matching cache ids (string) */ public function getIdsMatchingTags($tags = array()) { return $this->_get($this->_options['cache_dir'], 'matching', $tags); } /** * Return an array of stored cache ids which don't match given tags * * In case of multiple tags, a logical OR is made between tags * * @param array $tags array of tags * @return array array of not matching cache ids (string) */ public function getIdsNotMatchingTags($tags = array()) { return $this->_get($this->_options['cache_dir'], 'notMatching', $tags); } /** * Return an array of stored cache ids which match any given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * @return array array of any matching cache ids (string) */ public function getIdsMatchingAnyTags($tags = array()) { return $this->_get($this->_options['cache_dir'], 'matchingAny', $tags); } /** * Return the filling percentage of the backend storage * * @throws Zend_Cache_Exception * @return int integer between 0 and 100 */ public function getFillingPercentage() { $free = disk_free_space($this->_options['cache_dir']); $total = disk_total_space($this->_options['cache_dir']); if ($total == 0) { Zend_Cache::throwException('can\'t get disk_total_space'); } else { if ($free >= $total) { return 100; } return ((int) (100. * ($total - $free) / $total)); } } /** * Return an array of metadatas for the given cache id * * The array must include these keys : * - expire : the expire timestamp * - tags : a string array of tags * - mtime : timestamp of last modification time * * @param string $id cache id * @return array array of metadatas (false if the cache id is not found) */ public function getMetadatas($id) { $metadatas = $this->_getMetadatas($id); if (!$metadatas) { return false; } if (time() > $metadatas['expire']) { return false; } return array( 'expire' => $metadatas['expire'], 'tags' => $metadatas['tags'], 'mtime' => $metadatas['mtime'] ); } /** * Give (if possible) an extra lifetime to the given cache id * * @param string $id cache id * @param int $extraLifetime * @return boolean true if ok */ public function touch($id, $extraLifetime) { $metadatas = $this->_getMetadatas($id); if (!$metadatas) { return false; } if (time() > $metadatas['expire']) { return false; } $newMetadatas = array( 'hash' => $metadatas['hash'], 'mtime' => time(), 'expire' => $metadatas['expire'] + $extraLifetime, 'tags' => $metadatas['tags'] ); $res = $this->_setMetadatas($id, $newMetadatas); if (!$res) { return false; } return true; } /** * Return an associative array of capabilities (booleans) of the backend * * The array must include these keys : * - automatic_cleaning (is automating cleaning necessary) * - tags (are tags supported) * - expired_read (is it possible to read expired cache records * (for doNotTestCacheValidity option for example)) * - priority does the backend deal with priority when saving * - infinite_lifetime (is infinite lifetime can work with this backend) * - get_list (is it possible to get the list of cache ids and the complete list of tags) * * @return array associative of with capabilities */ public function getCapabilities() { return array( 'automatic_cleaning' => true, 'tags' => true, 'expired_read' => true, 'priority' => false, 'infinite_lifetime' => true, 'get_list' => true ); } /** * PUBLIC METHOD FOR UNIT TESTING ONLY ! * * Force a cache record to expire * * @param string $id cache id */ public function ___expire($id) { $metadatas = $this->_getMetadatas($id); if ($metadatas) { $metadatas['expire'] = 1; $this->_setMetadatas($id, $metadatas); } } /** * Get a metadatas record * * @param string $id Cache id * @return array|false Associative array of metadatas */ protected function _getMetadatas($id) { if (isset($this->_metadatasArray[$id])) { return $this->_metadatasArray[$id]; } else { $metadatas = $this->_loadMetadatas($id); if (!$metadatas) { return false; } $this->_setMetadatas($id, $metadatas, false); return $metadatas; } } /** * Set a metadatas record * * @param string $id Cache id * @param array $metadatas Associative array of metadatas * @param boolean $save optional pass false to disable saving to file * @return boolean True if no problem */ protected function _setMetadatas($id, $metadatas, $save = true) { if (count($this->_metadatasArray) >= $this->_options['metadatas_array_max_size']) { $n = (int) ($this->_options['metadatas_array_max_size'] / 10); $this->_metadatasArray = array_slice($this->_metadatasArray, $n); } if ($save) { $result = $this->_saveMetadatas($id, $metadatas); if (!$result) { return false; } } $this->_metadatasArray[$id] = $metadatas; return true; } /** * Drop a metadata record * * @param string $id Cache id * @return boolean True if no problem */ protected function _delMetadatas($id) { if (isset($this->_metadatasArray[$id])) { unset($this->_metadatasArray[$id]); } $file = $this->_metadatasFile($id); return $this->_remove($file); } /** * Clear the metadatas array * * @return void */ protected function _cleanMetadatas() { $this->_metadatasArray = array(); } /** * Load metadatas from disk * * @param string $id Cache id * @return array|false Metadatas associative array */ protected function _loadMetadatas($id) { $file = $this->_metadatasFile($id); $result = $this->_fileGetContents($file); if (!$result) { return false; } $tmp = @unserialize($result); return $tmp; } /** * Save metadatas to disk * * @param string $id Cache id * @param array $metadatas Associative array * @return boolean True if no problem */ protected function _saveMetadatas($id, $metadatas) { $file = $this->_metadatasFile($id); $result = $this->_filePutContents($file, serialize($metadatas)); if (!$result) { return false; } return true; } /** * Make and return a file name (with path) for metadatas * * @param string $id Cache id * @return string Metadatas file name (with path) */ protected function _metadatasFile($id) { $path = $this->_path($id); $fileName = $this->_idToFileName('internal-metadatas---' . $id); return $path . $fileName; } /** * Check if the given filename is a metadatas one * * @param string $fileName File name * @return boolean True if it's a metadatas one */ protected function _isMetadatasFile($fileName) { $id = $this->_fileNameToId($fileName); if (substr($id, 0, 21) == 'internal-metadatas---') { return true; } else { return false; } } /** * Remove a file * * If we can't remove the file (because of locks or any problem), we will touch * the file to invalidate it * * @param string $file Complete file path * @return boolean True if ok */ protected function _remove($file) { if (!is_file($file)) { return false; } if (!@unlink($file)) { # we can't remove the file (because of locks or any problem) $this->_log("Zend_Cache_Backend_File::_remove() : we can't remove $file"); return false; } return true; } /** * Clean some cache records (protected method used for recursive stuff) * * Available modes are : * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used) * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used) * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags * ($tags can be an array of strings or a single string) * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags} * ($tags can be an array of strings or a single string) * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags * ($tags can be an array of strings or a single string) * * @param string $dir Directory to clean * @param string $mode Clean mode * @param array $tags Array of tags * @throws Zend_Cache_Exception * @return boolean True if no problem */ protected function _clean($dir, $mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) { if (!is_dir($dir)) { return false; } $result = true; $prefix = $this->_options['file_name_prefix']; $glob = @glob($dir . $prefix . '--*'); if ($glob === false) { // On some systems it is impossible to distinguish between empty match and an error. return true; } foreach ($glob as $file) { if (is_file($file)) { $fileName = basename($file); if ($this->_isMetadatasFile($fileName)) { // in CLEANING_MODE_ALL, we drop anything, even remainings old metadatas files if ($mode != Zend_Cache::CLEANING_MODE_ALL) { continue; } } $id = $this->_fileNameToId($fileName); $metadatas = $this->_getMetadatas($id); if ($metadatas === FALSE) { $metadatas = array('expire' => 1, 'tags' => array()); } switch ($mode) { case Zend_Cache::CLEANING_MODE_ALL: $res = $this->remove($id); if (!$res) { // in this case only, we accept a problem with the metadatas file drop $res = $this->_remove($file); } $result = $result && $res; break; case Zend_Cache::CLEANING_MODE_OLD: if (time() > $metadatas['expire']) { $result = $this->remove($id) && $result; } break; case Zend_Cache::CLEANING_MODE_MATCHING_TAG: $matching = true; foreach ($tags as $tag) { if (!in_array($tag, $metadatas['tags'])) { $matching = false; break; } } if ($matching) { $result = $this->remove($id) && $result; } break; case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG: $matching = false; foreach ($tags as $tag) { if (in_array($tag, $metadatas['tags'])) { $matching = true; break; } } if (!$matching) { $result = $this->remove($id) && $result; } break; case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG: $matching = false; foreach ($tags as $tag) { if (in_array($tag, $metadatas['tags'])) { $matching = true; break; } } if ($matching) { $result = $this->remove($id) && $result; } break; default: Zend_Cache::throwException('Invalid mode for clean() method'); break; } } if ((is_dir($file)) and ($this->_options['hashed_directory_level']>0)) { // Recursive call $result = $this->_clean($file . DIRECTORY_SEPARATOR, $mode, $tags) && $result; if ($mode=='all') { // if mode=='all', we try to drop the structure too @rmdir($file); } } } return $result; } protected function _get($dir, $mode, $tags = array()) { if (!is_dir($dir)) { return false; } $result = array(); $prefix = $this->_options['file_name_prefix']; $glob = @glob($dir . $prefix . '--*'); if ($glob === false) { // On some systems it is impossible to distinguish between empty match and an error. return array(); } foreach ($glob as $file) { if (is_file($file)) { $fileName = basename($file); $id = $this->_fileNameToId($fileName); $metadatas = $this->_getMetadatas($id); if ($metadatas === FALSE) { continue; } if (time() > $metadatas['expire']) { continue; } switch ($mode) { case 'ids': $result[] = $id; break; case 'tags': $result = array_unique(array_merge($result, $metadatas['tags'])); break; case 'matching': $matching = true; foreach ($tags as $tag) { if (!in_array($tag, $metadatas['tags'])) { $matching = false; break; } } if ($matching) { $result[] = $id; } break; case 'notMatching': $matching = false; foreach ($tags as $tag) { if (in_array($tag, $metadatas['tags'])) { $matching = true; break; } } if (!$matching) { $result[] = $id; } break; case 'matchingAny': $matching = false; foreach ($tags as $tag) { if (in_array($tag, $metadatas['tags'])) { $matching = true; break; } } if ($matching) { $result[] = $id; } break; default: Zend_Cache::throwException('Invalid mode for _get() method'); break; } } if ((is_dir($file)) and ($this->_options['hashed_directory_level']>0)) { // Recursive call $recursiveRs = $this->_get($file . DIRECTORY_SEPARATOR, $mode, $tags); if ($recursiveRs === false) { $this->_log('Zend_Cache_Backend_File::_get() / recursive call : can\'t list entries of "'.$file.'"'); } else { $result = array_unique(array_merge($result, $recursiveRs)); } } } return array_unique($result); } /** * Compute & return the expire time * * @return int expire time (unix timestamp) */ protected function _expireTime($lifetime) { if ($lifetime === null) { return 9999999999; } return time() + $lifetime; } /** * Make a control key with the string containing datas * * @param string $data Data * @param string $controlType Type of control 'md5', 'crc32' or 'strlen' * @throws Zend_Cache_Exception * @return string Control key */ protected function _hash($data, $controlType) { switch ($controlType) { case 'md5': return md5($data); case 'crc32': return crc32($data); case 'strlen': return strlen($data); case 'adler32': return hash('adler32', $data); default: Zend_Cache::throwException("Incorrect hash function : $controlType"); } } /** * Transform a cache id into a file name and return it * * @param string $id Cache id * @return string File name */ protected function _idToFileName($id) { $prefix = $this->_options['file_name_prefix']; $result = $prefix . '---' . $id; return $result; } /** * Make and return a file name (with path) * * @param string $id Cache id * @return string File name (with path) */ protected function _file($id) { $path = $this->_path($id); $fileName = $this->_idToFileName($id); return $path . $fileName; } /** * Return the complete directory path of a filename (including hashedDirectoryStructure) * * @param string $id Cache id * @param boolean $parts if true, returns array of directory parts instead of single string * @return string Complete directory path */ protected function _path($id, $parts = false) { $partsArray = array(); $root = $this->_options['cache_dir']; $prefix = $this->_options['file_name_prefix']; if ($this->_options['hashed_directory_level']>0) { $hash = hash('adler32', $id); for ($i=0 ; $i < $this->_options['hashed_directory_level'] ; $i++) { $root = $root . $prefix . '--' . substr($hash, 0, $i + 1) . DIRECTORY_SEPARATOR; $partsArray[] = $root; } } if ($parts) { return $partsArray; } else { return $root; } } /** * Make the directory strucuture for the given id * * @param string $id cache id * @return boolean true */ protected function _recursiveMkdirAndChmod($id) { if ($this->_options['hashed_directory_level'] <=0) { return true; } $partsArray = $this->_path($id, true); foreach ($partsArray as $part) { if (!is_dir($part)) { @mkdir($part, $this->_options['hashed_directory_umask']); @chmod($part, $this->_options['hashed_directory_umask']); // see #ZF-320 (this line is required in some configurations) } } return true; } /** * Test if the given cache id is available (and still valid as a cache record) * * @param string $id Cache id * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested * @return boolean|mixed false (a cache is not available) or "last modified" timestamp (int) of the available cache record */ protected function _test($id, $doNotTestCacheValidity) { $metadatas = $this->_getMetadatas($id); if (!$metadatas) { return false; } if ($doNotTestCacheValidity || (time() <= $metadatas['expire'])) { return $metadatas['mtime']; } return false; } /** * Return the file content of the given file * * @param string $file File complete path * @return string File content (or false if problem) */ protected function _fileGetContents($file) { $result = false; if (!is_file($file)) { return false; } $f = @fopen($file, 'rb'); if ($f) { if ($this->_options['file_locking']) @flock($f, LOCK_SH); $result = stream_get_contents($f); if ($this->_options['file_locking']) @flock($f, LOCK_UN); @fclose($f); } return $result; } /** * Put the given string into the given file * * @param string $file File complete path * @param string $string String to put in file * @return boolean true if no problem */ protected function _filePutContents($file, $string) { $result = false; $f = @fopen($file, 'ab+'); if ($f) { if ($this->_options['file_locking']) @flock($f, LOCK_EX); fseek($f, 0); ftruncate($f, 0); $tmp = @fwrite($f, $string); if (!($tmp === FALSE)) { $result = true; } @fclose($f); } @chmod($file, $this->_options['cache_file_umask']); return $result; } /** * Transform a file name into cache id and return it * * @param string $fileName File name * @return string Cache id */ protected function _fileNameToId($fileName) { $prefix = $this->_options['file_name_prefix']; return preg_replace('~^' . $prefix . '---(.*)$~', '$1', $fileName); } } vendor/Zend/Cache/Backend/Interface.php000064400000007502151742733540013723 0ustar00 infinite lifetime) * @return boolean true if no problem */ public function save($data, $id, $tags = array(), $specificLifetime = false); /** * Remove a cache record * * @param string $id Cache id * @return boolean True if no problem */ public function remove($id); /** * Clean some cache records * * Available modes are : * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used) * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used) * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags * ($tags can be an array of strings or a single string) * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags} * ($tags can be an array of strings or a single string) * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags * ($tags can be an array of strings or a single string) * * @param string $mode Clean mode * @param array $tags Array of tags * @return boolean true if no problem */ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()); } vendor/Zend/Cache/Backend/Libmemcached.php000064400000037631151742733540014366 0ustar00 (array) servers : * an array of memcached server ; each memcached server is described by an associative array : * 'host' => (string) : the name of the memcached server * 'port' => (int) : the port of the memcached server * 'weight' => (int) : number of buckets to create for this server which in turn control its * probability of it being selected. The probability is relative to the total * weight of all servers. * =====> (array) client : * an array of memcached client options ; the memcached client is described by an associative array : * @see http://php.net/manual/memcached.constants.php * - The option name can be the name of the constant without the prefix 'OPT_' * or the integer value of this option constant * * @var array available options */ protected $_options = array( 'servers' => array(array( 'host' => self::DEFAULT_HOST, 'port' => self::DEFAULT_PORT, 'weight' => self::DEFAULT_WEIGHT, )), 'client' => array() ); /** * Memcached object * * @var mixed memcached object */ protected $_memcache = null; /** * Constructor * * @param array $options associative array of options * @throws Zend_Cache_Exception * @return void */ public function __construct(array $options = array()) { if (!extension_loaded('memcached')) { Zend_Cache::throwException('The memcached extension must be loaded for using this backend !'); } // override default client options $this->_options['client'] = array( Memcached::OPT_DISTRIBUTION => Memcached::DISTRIBUTION_CONSISTENT, Memcached::OPT_HASH => Memcached::HASH_MD5, Memcached::OPT_LIBKETAMA_COMPATIBLE => true, ); parent::__construct($options); if (isset($this->_options['servers'])) { $value = $this->_options['servers']; if (isset($value['host'])) { // in this case, $value seems to be a simple associative array (one server only) $value = array(0 => $value); // let's transform it into a classical array of associative arrays } $this->setOption('servers', $value); } $this->_memcache = new Memcached; // setup memcached client options foreach ($this->_options['client'] as $name => $value) { $optId = null; if (is_int($name)) { $optId = $name; } else { $optConst = 'Memcached::OPT_' . strtoupper($name); if (defined($optConst)) { $optId = constant($optConst); } else { $this->_log("Unknown memcached client option '{$name}' ({$optConst})"); } } if ($optId) { if (!$this->_memcache->setOption($optId, $value)) { $this->_log("Setting memcached client option '{$optId}' failed"); } } } // setup memcached servers $servers = array(); foreach ($this->_options['servers'] as $server) { if (!array_key_exists('port', $server)) { $server['port'] = self::DEFAULT_PORT; } if (!array_key_exists('weight', $server)) { $server['weight'] = self::DEFAULT_WEIGHT; } $servers[] = array($server['host'], $server['port'], $server['weight']); } $this->_memcache->addServers($servers); } /** * Test if a cache is available for the given id and (if yes) return it (false else) * * @param string $id Cache id * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested * @return string|false cached datas */ public function load($id, $doNotTestCacheValidity = false) { $tmp = $this->_memcache->get($id); if (isset($tmp[0])) { return $tmp[0]; } return false; } /** * Test if a cache is available or not (for the given id) * * @param string $id Cache id * @return int|false (a cache is not available) or "last modified" timestamp (int) of the available cache record */ public function test($id) { $tmp = $this->_memcache->get($id); if (isset($tmp[0], $tmp[1])) { return (int)$tmp[1]; } return false; } /** * Save some string datas into a cache record * * Note : $data is always "string" (serialization is done by the * core not by the backend) * * @param string $data Datas to cache * @param string $id Cache id * @param array $tags Array of strings, the cache record will be tagged by each string entry * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime) * @return boolean True if no problem */ public function save($data, $id, $tags = array(), $specificLifetime = false) { $lifetime = $this->getLifetime($specificLifetime); // ZF-8856: using set because add needs a second request if item already exists $result = @$this->_memcache->set($id, array($data, time(), $lifetime), $lifetime); if ($result === false) { $rsCode = $this->_memcache->getResultCode(); $rsMsg = $this->_memcache->getResultMessage(); $this->_log("Memcached::set() failed: [{$rsCode}] {$rsMsg}"); } if (count($tags) > 0) { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_LIBMEMCACHED_BACKEND); } return $result; } /** * Remove a cache record * * @param string $id Cache id * @return boolean True if no problem */ public function remove($id) { return $this->_memcache->delete($id); } /** * Clean some cache records * * Available modes are : * 'all' (default) => remove all cache entries ($tags is not used) * 'old' => unsupported * 'matchingTag' => unsupported * 'notMatchingTag' => unsupported * 'matchingAnyTag' => unsupported * * @param string $mode Clean mode * @param array $tags Array of tags * @throws Zend_Cache_Exception * @return boolean True if no problem */ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) { switch ($mode) { case Zend_Cache::CLEANING_MODE_ALL: return $this->_memcache->flush(); break; case Zend_Cache::CLEANING_MODE_OLD: $this->_log("Zend_Cache_Backend_Libmemcached::clean() : CLEANING_MODE_OLD is unsupported by the Libmemcached backend"); break; case Zend_Cache::CLEANING_MODE_MATCHING_TAG: case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG: case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG: $this->_log(self::TAGS_UNSUPPORTED_BY_CLEAN_OF_LIBMEMCACHED_BACKEND); break; default: Zend_Cache::throwException('Invalid mode for clean() method'); break; } } /** * Return true if the automatic cleaning is available for the backend * * @return boolean */ public function isAutomaticCleaningAvailable() { return false; } /** * Set the frontend directives * * @param array $directives Assoc of directives * @throws Zend_Cache_Exception * @return void */ public function setDirectives($directives) { parent::setDirectives($directives); $lifetime = $this->getLifetime(false); if ($lifetime > 2592000) { // #ZF-3490 : For the memcached backend, there is a lifetime limit of 30 days (2592000 seconds) $this->_log('memcached backend has a limit of 30 days (2592000 seconds) for the lifetime'); } if ($lifetime === null) { // #ZF-4614 : we tranform null to zero to get the maximal lifetime parent::setDirectives(array('lifetime' => 0)); } } /** * Return an array of stored cache ids * * @return array array of stored cache ids (string) */ public function getIds() { $this->_log("Zend_Cache_Backend_Libmemcached::save() : getting the list of cache ids is unsupported by the Libmemcached backend"); return array(); } /** * Return an array of stored tags * * @return array array of stored tags (string) */ public function getTags() { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_LIBMEMCACHED_BACKEND); return array(); } /** * Return an array of stored cache ids which match given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * @return array array of matching cache ids (string) */ public function getIdsMatchingTags($tags = array()) { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_LIBMEMCACHED_BACKEND); return array(); } /** * Return an array of stored cache ids which don't match given tags * * In case of multiple tags, a logical OR is made between tags * * @param array $tags array of tags * @return array array of not matching cache ids (string) */ public function getIdsNotMatchingTags($tags = array()) { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_LIBMEMCACHED_BACKEND); return array(); } /** * Return an array of stored cache ids which match any given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * @return array array of any matching cache ids (string) */ public function getIdsMatchingAnyTags($tags = array()) { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_LIBMEMCACHED_BACKEND); return array(); } /** * Return the filling percentage of the backend storage * * @throws Zend_Cache_Exception * @return int integer between 0 and 100 */ public function getFillingPercentage() { $mems = $this->_memcache->getStats(); if ($mems === false) { return 0; } $memSize = null; $memUsed = null; foreach ($mems as $key => $mem) { if ($mem === false) { $this->_log('can\'t get stat from ' . $key); continue; } $eachSize = $mem['limit_maxbytes']; $eachUsed = $mem['bytes']; if ($eachUsed > $eachSize) { $eachUsed = $eachSize; } $memSize += $eachSize; $memUsed += $eachUsed; } if ($memSize === null || $memUsed === null) { Zend_Cache::throwException('Can\'t get filling percentage'); } return ((int) (100. * ($memUsed / $memSize))); } /** * Return an array of metadatas for the given cache id * * The array must include these keys : * - expire : the expire timestamp * - tags : a string array of tags * - mtime : timestamp of last modification time * * @param string $id cache id * @return array array of metadatas (false if the cache id is not found) */ public function getMetadatas($id) { $tmp = $this->_memcache->get($id); if (isset($tmp[0], $tmp[1], $tmp[2])) { $data = $tmp[0]; $mtime = $tmp[1]; $lifetime = $tmp[2]; return array( 'expire' => $mtime + $lifetime, 'tags' => array(), 'mtime' => $mtime ); } return false; } /** * Give (if possible) an extra lifetime to the given cache id * * @param string $id cache id * @param int $extraLifetime * @return boolean true if ok */ public function touch($id, $extraLifetime) { $tmp = $this->_memcache->get($id); if (isset($tmp[0], $tmp[1], $tmp[2])) { $data = $tmp[0]; $mtime = $tmp[1]; $lifetime = $tmp[2]; $newLifetime = $lifetime - (time() - $mtime) + $extraLifetime; if ($newLifetime <=0) { return false; } // #ZF-5702 : we try replace() first becase set() seems to be slower if (!($result = $this->_memcache->replace($id, array($data, time(), $newLifetime), $newLifetime))) { $result = $this->_memcache->set($id, array($data, time(), $newLifetime), $newLifetime); if ($result === false) { $rsCode = $this->_memcache->getResultCode(); $rsMsg = $this->_memcache->getResultMessage(); $this->_log("Memcached::set() failed: [{$rsCode}] {$rsMsg}"); } } return $result; } return false; } /** * Return an associative array of capabilities (booleans) of the backend * * The array must include these keys : * - automatic_cleaning (is automating cleaning necessary) * - tags (are tags supported) * - expired_read (is it possible to read expired cache records * (for doNotTestCacheValidity option for example)) * - priority does the backend deal with priority when saving * - infinite_lifetime (is infinite lifetime can work with this backend) * - get_list (is it possible to get the list of cache ids and the complete list of tags) * * @return array associative of with capabilities */ public function getCapabilities() { return array( 'automatic_cleaning' => false, 'tags' => false, 'expired_read' => false, 'priority' => false, 'infinite_lifetime' => false, 'get_list' => false ); } } vendor/Zend/Cache/Backend/Memcached.php000064400000042745151742733540013701 0ustar00 (array) servers : * an array of memcached server ; each memcached server is described by an associative array : * 'host' => (string) : the name of the memcached server * 'port' => (int) : the port of the memcached server * 'persistent' => (bool) : use or not persistent connections to this memcached server * 'weight' => (int) : number of buckets to create for this server which in turn control its * probability of it being selected. The probability is relative to the total * weight of all servers. * 'timeout' => (int) : value in seconds which will be used for connecting to the daemon. Think twice * before changing the default value of 1 second - you can lose all the * advantages of caching if your connection is too slow. * 'retry_interval' => (int) : controls how often a failed server will be retried, the default value * is 15 seconds. Setting this parameter to -1 disables automatic retry. * 'status' => (bool) : controls if the server should be flagged as online. * 'failure_callback' => (callback) : Allows the user to specify a callback function to run upon * encountering an error. The callback is run before failover * is attempted. The function takes two parameters, the hostname * and port of the failed server. * * =====> (boolean) compression : * true if you want to use on-the-fly compression * * =====> (boolean) compatibility : * true if you use old memcache server or extension * * @var array available options */ protected $_options = array( 'servers' => array(array( 'host' => self::DEFAULT_HOST, 'port' => self::DEFAULT_PORT, 'persistent' => self::DEFAULT_PERSISTENT, 'weight' => self::DEFAULT_WEIGHT, 'timeout' => self::DEFAULT_TIMEOUT, 'retry_interval' => self::DEFAULT_RETRY_INTERVAL, 'status' => self::DEFAULT_STATUS, 'failure_callback' => self::DEFAULT_FAILURE_CALLBACK )), 'compression' => false, 'compatibility' => false, ); /** * Memcache object * * @var mixed memcache object */ protected $_memcache = null; /** * Constructor * * @param array $options associative array of options * @throws Zend_Cache_Exception * @return void */ public function __construct(array $options = array()) { if (!extension_loaded('memcache')) { Zend_Cache::throwException('The memcache extension must be loaded for using this backend !'); } parent::__construct($options); if (isset($this->_options['servers'])) { $value= $this->_options['servers']; if (isset($value['host'])) { // in this case, $value seems to be a simple associative array (one server only) $value = array(0 => $value); // let's transform it into a classical array of associative arrays } $this->setOption('servers', $value); } $this->_memcache = new Memcache; foreach ($this->_options['servers'] as $server) { if (!array_key_exists('port', $server)) { $server['port'] = self::DEFAULT_PORT; } if (!array_key_exists('persistent', $server)) { $server['persistent'] = self::DEFAULT_PERSISTENT; } if (!array_key_exists('weight', $server)) { $server['weight'] = self::DEFAULT_WEIGHT; } if (!array_key_exists('timeout', $server)) { $server['timeout'] = self::DEFAULT_TIMEOUT; } if (!array_key_exists('retry_interval', $server)) { $server['retry_interval'] = self::DEFAULT_RETRY_INTERVAL; } if (!array_key_exists('status', $server)) { $server['status'] = self::DEFAULT_STATUS; } if (!array_key_exists('failure_callback', $server)) { $server['failure_callback'] = self::DEFAULT_FAILURE_CALLBACK; } if ($this->_options['compatibility']) { // No status for compatibility mode (#ZF-5887) $this->_memcache->addServer($server['host'], $server['port'], $server['persistent'], $server['weight'], $server['timeout'], $server['retry_interval']); } else { $this->_memcache->addServer($server['host'], $server['port'], $server['persistent'], $server['weight'], $server['timeout'], $server['retry_interval'], $server['status'], $server['failure_callback']); } } } /** * Test if a cache is available for the given id and (if yes) return it (false else) * * @param string $id Cache id * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested * @return string|false cached datas */ public function load($id, $doNotTestCacheValidity = false) { $tmp = $this->_memcache->get($id); if (is_array($tmp) && isset($tmp[0])) { return $tmp[0]; } return false; } /** * Test if a cache is available or not (for the given id) * * @param string $id Cache id * @return mixed|false (a cache is not available) or "last modified" timestamp (int) of the available cache record */ public function test($id) { $tmp = $this->_memcache->get($id); if (is_array($tmp)) { return $tmp[1]; } return false; } /** * Save some string datas into a cache record * * Note : $data is always "string" (serialization is done by the * core not by the backend) * * @param string $data Datas to cache * @param string $id Cache id * @param array $tags Array of strings, the cache record will be tagged by each string entry * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime) * @return boolean True if no problem */ public function save($data, $id, $tags = array(), $specificLifetime = false) { $lifetime = $this->getLifetime($specificLifetime); if ($this->_options['compression']) { $flag = MEMCACHE_COMPRESSED; } else { $flag = 0; } // ZF-8856: using set because add needs a second request if item already exists $result = @$this->_memcache->set($id, array($data, time(), $lifetime), $flag, $lifetime); if (count($tags) > 0) { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_MEMCACHED_BACKEND); } return $result; } /** * Remove a cache record * * @param string $id Cache id * @return boolean True if no problem */ public function remove($id) { return $this->_memcache->delete($id, 0); } /** * Clean some cache records * * Available modes are : * 'all' (default) => remove all cache entries ($tags is not used) * 'old' => unsupported * 'matchingTag' => unsupported * 'notMatchingTag' => unsupported * 'matchingAnyTag' => unsupported * * @param string $mode Clean mode * @param array $tags Array of tags * @throws Zend_Cache_Exception * @return boolean True if no problem */ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) { switch ($mode) { case Zend_Cache::CLEANING_MODE_ALL: return $this->_memcache->flush(); break; case Zend_Cache::CLEANING_MODE_OLD: $this->_log("Zend_Cache_Backend_Memcached::clean() : CLEANING_MODE_OLD is unsupported by the Memcached backend"); break; case Zend_Cache::CLEANING_MODE_MATCHING_TAG: case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG: case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG: $this->_log(self::TAGS_UNSUPPORTED_BY_CLEAN_OF_MEMCACHED_BACKEND); break; default: Zend_Cache::throwException('Invalid mode for clean() method'); break; } } /** * Return true if the automatic cleaning is available for the backend * * @return boolean */ public function isAutomaticCleaningAvailable() { return false; } /** * Set the frontend directives * * @param array $directives Assoc of directives * @throws Zend_Cache_Exception * @return void */ public function setDirectives($directives) { parent::setDirectives($directives); $lifetime = $this->getLifetime(false); if ($lifetime > 2592000) { // #ZF-3490 : For the memcached backend, there is a lifetime limit of 30 days (2592000 seconds) $this->_log('memcached backend has a limit of 30 days (2592000 seconds) for the lifetime'); } if ($lifetime === null) { // #ZF-4614 : we tranform null to zero to get the maximal lifetime parent::setDirectives(array('lifetime' => 0)); } } /** * Return an array of stored cache ids * * @return array array of stored cache ids (string) */ public function getIds() { $this->_log("Zend_Cache_Backend_Memcached::save() : getting the list of cache ids is unsupported by the Memcache backend"); return array(); } /** * Return an array of stored tags * * @return array array of stored tags (string) */ public function getTags() { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_MEMCACHED_BACKEND); return array(); } /** * Return an array of stored cache ids which match given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * @return array array of matching cache ids (string) */ public function getIdsMatchingTags($tags = array()) { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_MEMCACHED_BACKEND); return array(); } /** * Return an array of stored cache ids which don't match given tags * * In case of multiple tags, a logical OR is made between tags * * @param array $tags array of tags * @return array array of not matching cache ids (string) */ public function getIdsNotMatchingTags($tags = array()) { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_MEMCACHED_BACKEND); return array(); } /** * Return an array of stored cache ids which match any given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * @return array array of any matching cache ids (string) */ public function getIdsMatchingAnyTags($tags = array()) { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_MEMCACHED_BACKEND); return array(); } /** * Return the filling percentage of the backend storage * * @throws Zend_Cache_Exception * @return int integer between 0 and 100 */ public function getFillingPercentage() { $mems = $this->_memcache->getExtendedStats(); $memSize = null; $memUsed = null; foreach ($mems as $key => $mem) { if ($mem === false) { $this->_log('can\'t get stat from ' . $key); continue; } $eachSize = $mem['limit_maxbytes']; $eachUsed = $mem['bytes']; if ($eachUsed > $eachSize) { $eachUsed = $eachSize; } $memSize += $eachSize; $memUsed += $eachUsed; } if ($memSize === null || $memUsed === null) { Zend_Cache::throwException('Can\'t get filling percentage'); } return ((int) (100. * ($memUsed / $memSize))); } /** * Return an array of metadatas for the given cache id * * The array must include these keys : * - expire : the expire timestamp * - tags : a string array of tags * - mtime : timestamp of last modification time * * @param string $id cache id * @return array array of metadatas (false if the cache id is not found) */ public function getMetadatas($id) { $tmp = $this->_memcache->get($id); if (is_array($tmp)) { $data = $tmp[0]; $mtime = $tmp[1]; if (!isset($tmp[2])) { // because this record is only with 1.7 release // if old cache records are still there... return false; } $lifetime = $tmp[2]; return array( 'expire' => $mtime + $lifetime, 'tags' => array(), 'mtime' => $mtime ); } return false; } /** * Give (if possible) an extra lifetime to the given cache id * * @param string $id cache id * @param int $extraLifetime * @return boolean true if ok */ public function touch($id, $extraLifetime) { if ($this->_options['compression']) { $flag = MEMCACHE_COMPRESSED; } else { $flag = 0; } $tmp = $this->_memcache->get($id); if (is_array($tmp)) { $data = $tmp[0]; $mtime = $tmp[1]; if (!isset($tmp[2])) { // because this record is only with 1.7 release // if old cache records are still there... return false; } $lifetime = $tmp[2]; $newLifetime = $lifetime - (time() - $mtime) + $extraLifetime; if ($newLifetime <=0) { return false; } // #ZF-5702 : we try replace() first becase set() seems to be slower if (!($result = $this->_memcache->replace($id, array($data, time(), $newLifetime), $flag, $newLifetime))) { $result = $this->_memcache->set($id, array($data, time(), $newLifetime), $flag, $newLifetime); } return $result; } return false; } /** * Return an associative array of capabilities (booleans) of the backend * * The array must include these keys : * - automatic_cleaning (is automating cleaning necessary) * - tags (are tags supported) * - expired_read (is it possible to read expired cache records * (for doNotTestCacheValidity option for example)) * - priority does the backend deal with priority when saving * - infinite_lifetime (is infinite lifetime can work with this backend) * - get_list (is it possible to get the list of cache ids and the complete list of tags) * * @return array associative of with capabilities */ public function getCapabilities() { return array( 'automatic_cleaning' => false, 'tags' => false, 'expired_read' => false, 'priority' => false, 'infinite_lifetime' => false, 'get_list' => false ); } } vendor/Zend/Cache/Backend/Sqlite.php000064400000055021151742733540013263 0ustar00 (string) cache_db_complete_path : * - the complete path (filename included) of the SQLITE database * * ====> (int) automatic_vacuum_factor : * - Disable / Tune the automatic vacuum process * - The automatic vacuum process defragment the database file (and make it smaller) * when a clean() or delete() is called * 0 => no automatic vacuum * 1 => systematic vacuum (when delete() or clean() methods are called) * x (integer) > 1 => automatic vacuum randomly 1 times on x clean() or delete() * * @var array Available options */ protected $_options = array( 'cache_db_complete_path' => null, 'automatic_vacuum_factor' => 10 ); /** * DB ressource * * @var mixed $_db */ private $_db = null; /** * Boolean to store if the structure has benn checked or not * * @var boolean $_structureChecked */ private $_structureChecked = false; /** * Constructor * * @param array $options Associative array of options * @throws Zend_cache_Exception * @return void */ public function __construct(array $options = array()) { parent::__construct($options); if ($this->_options['cache_db_complete_path'] === null) { Zend_Cache::throwException('cache_db_complete_path option has to set'); } if (!extension_loaded('sqlite')) { Zend_Cache::throwException("Cannot use SQLite storage because the 'sqlite' extension is not loaded in the current PHP environment"); } $this->_getConnection(); } /** * Destructor * * @return void */ public function __destruct() { @sqlite_close($this->_getConnection()); } /** * Test if a cache is available for the given id and (if yes) return it (false else) * * @param string $id Cache id * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested * @return string|false Cached datas */ public function load($id, $doNotTestCacheValidity = false) { $this->_checkAndBuildStructure(); $sql = "SELECT content FROM cache WHERE id='$id'"; if (!$doNotTestCacheValidity) { $sql = $sql . " AND (expire=0 OR expire>" . time() . ')'; } $result = $this->_query($sql); $row = @sqlite_fetch_array($result); if ($row) { return $row['content']; } return false; } /** * Test if a cache is available or not (for the given id) * * @param string $id Cache id * @return mixed|false (a cache is not available) or "last modified" timestamp (int) of the available cache record */ public function test($id) { $this->_checkAndBuildStructure(); $sql = "SELECT lastModified FROM cache WHERE id='$id' AND (expire=0 OR expire>" . time() . ')'; $result = $this->_query($sql); $row = @sqlite_fetch_array($result); if ($row) { return ((int) $row['lastModified']); } return false; } /** * Save some string datas into a cache record * * Note : $data is always "string" (serialization is done by the * core not by the backend) * * @param string $data Datas to cache * @param string $id Cache id * @param array $tags Array of strings, the cache record will be tagged by each string entry * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime) * @throws Zend_Cache_Exception * @return boolean True if no problem */ public function save($data, $id, $tags = array(), $specificLifetime = false) { $this->_checkAndBuildStructure(); $lifetime = $this->getLifetime($specificLifetime); $data = @sqlite_escape_string($data); $mktime = time(); if ($lifetime === null) { $expire = 0; } else { $expire = $mktime + $lifetime; } $this->_query("DELETE FROM cache WHERE id='$id'"); $sql = "INSERT INTO cache (id, content, lastModified, expire) VALUES ('$id', '$data', $mktime, $expire)"; $res = $this->_query($sql); if (!$res) { $this->_log("Zend_Cache_Backend_Sqlite::save() : impossible to store the cache id=$id"); return false; } $res = true; foreach ($tags as $tag) { $res = $this->_registerTag($id, $tag) && $res; } return $res; } /** * Remove a cache record * * @param string $id Cache id * @return boolean True if no problem */ public function remove($id) { $this->_checkAndBuildStructure(); $res = $this->_query("SELECT COUNT(*) AS nbr FROM cache WHERE id='$id'"); $result1 = @sqlite_fetch_single($res); $result2 = $this->_query("DELETE FROM cache WHERE id='$id'"); $result3 = $this->_query("DELETE FROM tag WHERE id='$id'"); $this->_automaticVacuum(); return ($result1 && $result2 && $result3); } /** * Clean some cache records * * Available modes are : * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used) * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used) * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags * ($tags can be an array of strings or a single string) * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags} * ($tags can be an array of strings or a single string) * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags * ($tags can be an array of strings or a single string) * * @param string $mode Clean mode * @param array $tags Array of tags * @return boolean True if no problem */ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) { $this->_checkAndBuildStructure(); $return = $this->_clean($mode, $tags); $this->_automaticVacuum(); return $return; } /** * Return an array of stored cache ids * * @return array array of stored cache ids (string) */ public function getIds() { $this->_checkAndBuildStructure(); $res = $this->_query("SELECT id FROM cache WHERE (expire=0 OR expire>" . time() . ")"); $result = array(); while ($id = @sqlite_fetch_single($res)) { $result[] = $id; } return $result; } /** * Return an array of stored tags * * @return array array of stored tags (string) */ public function getTags() { $this->_checkAndBuildStructure(); $res = $this->_query("SELECT DISTINCT(name) AS name FROM tag"); $result = array(); while ($id = @sqlite_fetch_single($res)) { $result[] = $id; } return $result; } /** * Return an array of stored cache ids which match given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * @return array array of matching cache ids (string) */ public function getIdsMatchingTags($tags = array()) { $first = true; $ids = array(); foreach ($tags as $tag) { $res = $this->_query("SELECT DISTINCT(id) AS id FROM tag WHERE name='$tag'"); if (!$res) { return array(); } $rows = @sqlite_fetch_all($res, SQLITE_ASSOC); $ids2 = array(); foreach ($rows as $row) { $ids2[] = $row['id']; } if ($first) { $ids = $ids2; $first = false; } else { $ids = array_intersect($ids, $ids2); } } $result = array(); foreach ($ids as $id) { $result[] = $id; } return $result; } /** * Return an array of stored cache ids which don't match given tags * * In case of multiple tags, a logical OR is made between tags * * @param array $tags array of tags * @return array array of not matching cache ids (string) */ public function getIdsNotMatchingTags($tags = array()) { $res = $this->_query("SELECT id FROM cache"); $rows = @sqlite_fetch_all($res, SQLITE_ASSOC); $result = array(); foreach ($rows as $row) { $id = $row['id']; $matching = false; foreach ($tags as $tag) { $res = $this->_query("SELECT COUNT(*) AS nbr FROM tag WHERE name='$tag' AND id='$id'"); if (!$res) { return array(); } $nbr = (int) @sqlite_fetch_single($res); if ($nbr > 0) { $matching = true; } } if (!$matching) { $result[] = $id; } } return $result; } /** * Return an array of stored cache ids which match any given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * @return array array of any matching cache ids (string) */ public function getIdsMatchingAnyTags($tags = array()) { $first = true; $ids = array(); foreach ($tags as $tag) { $res = $this->_query("SELECT DISTINCT(id) AS id FROM tag WHERE name='$tag'"); if (!$res) { return array(); } $rows = @sqlite_fetch_all($res, SQLITE_ASSOC); $ids2 = array(); foreach ($rows as $row) { $ids2[] = $row['id']; } if ($first) { $ids = $ids2; $first = false; } else { $ids = array_merge($ids, $ids2); } } $result = array(); foreach ($ids as $id) { $result[] = $id; } return $result; } /** * Return the filling percentage of the backend storage * * @throws Zend_Cache_Exception * @return int integer between 0 and 100 */ public function getFillingPercentage() { $dir = dirname($this->_options['cache_db_complete_path']); $free = disk_free_space($dir); $total = disk_total_space($dir); if ($total == 0) { Zend_Cache::throwException('can\'t get disk_total_space'); } else { if ($free >= $total) { return 100; } return ((int) (100. * ($total - $free) / $total)); } } /** * Return an array of metadatas for the given cache id * * The array must include these keys : * - expire : the expire timestamp * - tags : a string array of tags * - mtime : timestamp of last modification time * * @param string $id cache id * @return array array of metadatas (false if the cache id is not found) */ public function getMetadatas($id) { $tags = array(); $res = $this->_query("SELECT name FROM tag WHERE id='$id'"); if ($res) { $rows = @sqlite_fetch_all($res, SQLITE_ASSOC); foreach ($rows as $row) { $tags[] = $row['name']; } } $this->_query('CREATE TABLE cache (id TEXT PRIMARY KEY, content BLOB, lastModified INTEGER, expire INTEGER)'); $res = $this->_query("SELECT lastModified,expire FROM cache WHERE id='$id'"); if (!$res) { return false; } $row = @sqlite_fetch_array($res, SQLITE_ASSOC); return array( 'tags' => $tags, 'mtime' => $row['lastModified'], 'expire' => $row['expire'] ); } /** * Give (if possible) an extra lifetime to the given cache id * * @param string $id cache id * @param int $extraLifetime * @return boolean true if ok */ public function touch($id, $extraLifetime) { $sql = "SELECT expire FROM cache WHERE id='$id' AND (expire=0 OR expire>" . time() . ')'; $res = $this->_query($sql); if (!$res) { return false; } $expire = @sqlite_fetch_single($res); $newExpire = $expire + $extraLifetime; $res = $this->_query("UPDATE cache SET lastModified=" . time() . ", expire=$newExpire WHERE id='$id'"); if ($res) { return true; } else { return false; } } /** * Return an associative array of capabilities (booleans) of the backend * * The array must include these keys : * - automatic_cleaning (is automating cleaning necessary) * - tags (are tags supported) * - expired_read (is it possible to read expired cache records * (for doNotTestCacheValidity option for example)) * - priority does the backend deal with priority when saving * - infinite_lifetime (is infinite lifetime can work with this backend) * - get_list (is it possible to get the list of cache ids and the complete list of tags) * * @return array associative of with capabilities */ public function getCapabilities() { return array( 'automatic_cleaning' => true, 'tags' => true, 'expired_read' => true, 'priority' => false, 'infinite_lifetime' => true, 'get_list' => true ); } /** * PUBLIC METHOD FOR UNIT TESTING ONLY ! * * Force a cache record to expire * * @param string $id Cache id */ public function ___expire($id) { $time = time() - 1; $this->_query("UPDATE cache SET lastModified=$time, expire=$time WHERE id='$id'"); } /** * Return the connection resource * * If we are not connected, the connection is made * * @throws Zend_Cache_Exception * @return resource Connection resource */ private function _getConnection() { if (is_resource($this->_db)) { return $this->_db; } else { $this->_db = @sqlite_open($this->_options['cache_db_complete_path']); if (!(is_resource($this->_db))) { Zend_Cache::throwException("Impossible to open " . $this->_options['cache_db_complete_path'] . " cache DB file"); } return $this->_db; } } /** * Execute an SQL query silently * * @param string $query SQL query * @return mixed|false query results */ private function _query($query) { $db = $this->_getConnection(); if (is_resource($db)) { $res = @sqlite_query($db, $query); if ($res === false) { return false; } else { return $res; } } return false; } /** * Deal with the automatic vacuum process * * @return void */ private function _automaticVacuum() { if ($this->_options['automatic_vacuum_factor'] > 0) { $rand = rand(1, $this->_options['automatic_vacuum_factor']); if ($rand == 1) { $this->_query('VACUUM'); @sqlite_close($this->_getConnection()); } } } /** * Register a cache id with the given tag * * @param string $id Cache id * @param string $tag Tag * @return boolean True if no problem */ private function _registerTag($id, $tag) { $res = $this->_query("DELETE FROM TAG WHERE name='$tag' AND id='$id'"); $res = $this->_query("INSERT INTO tag (name, id) VALUES ('$tag', '$id')"); if (!$res) { $this->_log("Zend_Cache_Backend_Sqlite::_registerTag() : impossible to register tag=$tag on id=$id"); return false; } return true; } /** * Build the database structure * * @return false */ private function _buildStructure() { $this->_query('DROP INDEX tag_id_index'); $this->_query('DROP INDEX tag_name_index'); $this->_query('DROP INDEX cache_id_expire_index'); $this->_query('DROP TABLE version'); $this->_query('DROP TABLE cache'); $this->_query('DROP TABLE tag'); $this->_query('CREATE TABLE version (num INTEGER PRIMARY KEY)'); $this->_query('CREATE TABLE cache (id TEXT PRIMARY KEY, content BLOB, lastModified INTEGER, expire INTEGER)'); $this->_query('CREATE TABLE tag (name TEXT, id TEXT)'); $this->_query('CREATE INDEX tag_id_index ON tag(id)'); $this->_query('CREATE INDEX tag_name_index ON tag(name)'); $this->_query('CREATE INDEX cache_id_expire_index ON cache(id, expire)'); $this->_query('INSERT INTO version (num) VALUES (1)'); } /** * Check if the database structure is ok (with the good version) * * @return boolean True if ok */ private function _checkStructureVersion() { $result = $this->_query("SELECT num FROM version"); if (!$result) return false; $row = @sqlite_fetch_array($result); if (!$row) { return false; } if (((int) $row['num']) != 1) { // old cache structure $this->_log('Zend_Cache_Backend_Sqlite::_checkStructureVersion() : old cache structure version detected => the cache is going to be dropped'); return false; } return true; } /** * Clean some cache records * * Available modes are : * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used) * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used) * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags * ($tags can be an array of strings or a single string) * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags} * ($tags can be an array of strings or a single string) * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags * ($tags can be an array of strings or a single string) * * @param string $mode Clean mode * @param array $tags Array of tags * @return boolean True if no problem */ private function _clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) { switch ($mode) { case Zend_Cache::CLEANING_MODE_ALL: $res1 = $this->_query('DELETE FROM cache'); $res2 = $this->_query('DELETE FROM tag'); return $res1 && $res2; break; case Zend_Cache::CLEANING_MODE_OLD: $mktime = time(); $res1 = $this->_query("DELETE FROM tag WHERE id IN (SELECT id FROM cache WHERE expire>0 AND expire<=$mktime)"); $res2 = $this->_query("DELETE FROM cache WHERE expire>0 AND expire<=$mktime"); return $res1 && $res2; break; case Zend_Cache::CLEANING_MODE_MATCHING_TAG: $ids = $this->getIdsMatchingTags($tags); $result = true; foreach ($ids as $id) { $result = $this->remove($id) && $result; } return $result; break; case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG: $ids = $this->getIdsNotMatchingTags($tags); $result = true; foreach ($ids as $id) { $result = $this->remove($id) && $result; } return $result; break; case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG: $ids = $this->getIdsMatchingAnyTags($tags); $result = true; foreach ($ids as $id) { $result = $this->remove($id) && $result; } return $result; break; default: break; } return false; } /** * Check if the database structure is ok (with the good version), if no : build it * * @throws Zend_Cache_Exception * @return boolean True if ok */ private function _checkAndBuildStructure() { if (!($this->_structureChecked)) { if (!$this->_checkStructureVersion()) { $this->_buildStructure(); if (!$this->_checkStructureVersion()) { Zend_Cache::throwException("Impossible to build cache structure in " . $this->_options['cache_db_complete_path']); } } $this->_structureChecked = true; } return true; } } vendor/Zend/Cache/Backend/Static.php000064400000045712151742733540013257 0ustar00 null, 'sub_dir' => 'html', 'file_extension' => '.html', 'index_filename' => 'index', 'file_locking' => true, 'cache_file_umask' => 0600, 'cache_directory_umask' => 0700, 'debug_header' => false, 'tag_cache' => null, 'disable_caching' => false ); /** * Cache for handling tags * @var Zend_Cache_Core */ protected $_tagCache = null; /** * Tagged items * @var array */ protected $_tagged = null; /** * Interceptor child method to handle the case where an Inner * Cache object is being set since it's not supported by the * standard backend interface * * @param string $name * @param mixed $value * @return Zend_Cache_Backend_Static */ public function setOption($name, $value) { if ($name == 'tag_cache') { $this->setInnerCache($value); } else { parent::setOption($name, $value); } return $this; } /** * Retrieve any option via interception of the parent's statically held * options including the local option for a tag cache. * * @param string $name * @return mixed */ public function getOption($name) { if ($name == 'tag_cache') { return $this->getInnerCache(); } else { if (in_array($name, $this->_options)) { return $this->_options[$name]; } if ($name == 'lifetime') { return parent::getLifetime(); } return null; } } /** * Test if a cache is available for the given id and (if yes) return it (false else) * * Note : return value is always "string" (unserialization is done by the core not by the backend) * * @param string $id Cache id * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested * @return string|false cached datas */ public function load($id, $doNotTestCacheValidity = false) { if (empty($id)) { $id = $this->_detectId(); } else { $id = $this->_decodeId($id); } if (!$this->_verifyPath($id)) { Zend_Cache::throwException('Invalid cache id: does not match expected public_dir path'); } if ($doNotTestCacheValidity) { $this->_log("Zend_Cache_Backend_Static::load() : \$doNotTestCacheValidity=true is unsupported by the Static backend"); } $fileName = basename($id); if (empty($fileName)) { $fileName = $this->_options['index_filename']; } $pathName = $this->_options['public_dir'] . dirname($id); $file = rtrim($pathName, '/') . '/' . $fileName . $this->_options['file_extension']; if (file_exists($file)) { $content = file_get_contents($file); return $content; } return false; } /** * Test if a cache is available or not (for the given id) * * @param string $id cache id * @return bool */ public function test($id) { $id = $this->_decodeId($id); if (!$this->_verifyPath($id)) { Zend_Cache::throwException('Invalid cache id: does not match expected public_dir path'); } $fileName = basename($id); if (empty($fileName)) { $fileName = $this->_options['index_filename']; } if ($this->_tagged === null && $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME)) { $this->_tagged = $tagged; } elseif (!$this->_tagged) { return false; } $pathName = $this->_options['public_dir'] . dirname($id); // Switch extension if needed if (isset($this->_tagged[$id])) { $extension = $this->_tagged[$id]['extension']; } else { $extension = $this->_options['file_extension']; } $file = $pathName . '/' . $fileName . $extension; if (file_exists($file)) { return true; } return false; } /** * Save some string datas into a cache record * * Note : $data is always "string" (serialization is done by the * core not by the backend) * * @param string $data Datas to cache * @param string $id Cache id * @param array $tags Array of strings, the cache record will be tagged by each string entry * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime) * @return boolean true if no problem */ public function save($data, $id, $tags = array(), $specificLifetime = false) { if ($this->_options['disable_caching']) { return true; } $extension = null; if ($this->_isSerialized($data)) { $data = unserialize($data); $extension = '.' . ltrim($data[1], '.'); $data = $data[0]; } clearstatcache(); if ($id === null || strlen($id) == 0) { $id = $this->_detectId(); } else { $id = $this->_decodeId($id); } $fileName = basename($id); if (empty($fileName)) { $fileName = $this->_options['index_filename']; } $pathName = realpath($this->_options['public_dir']) . dirname($id); $this->_createDirectoriesFor($pathName); if ($id === null || strlen($id) == 0) { $dataUnserialized = unserialize($data); $data = $dataUnserialized['data']; } $ext = $this->_options['file_extension']; if ($extension) $ext = $extension; $file = rtrim($pathName, '/') . '/' . $fileName . $ext; if ($this->_options['file_locking']) { $result = file_put_contents($file, $data, LOCK_EX); } else { $result = file_put_contents($file, $data); } @chmod($file, $this->_octdec($this->_options['cache_file_umask'])); if ($this->_tagged === null && $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME)) { $this->_tagged = $tagged; } elseif ($this->_tagged === null) { $this->_tagged = array(); } if (!isset($this->_tagged[$id])) { $this->_tagged[$id] = array(); } if (!isset($this->_tagged[$id]['tags'])) { $this->_tagged[$id]['tags'] = array(); } $this->_tagged[$id]['tags'] = array_unique(array_merge($this->_tagged[$id]['tags'], $tags)); $this->_tagged[$id]['extension'] = $ext; $this->getInnerCache()->save($this->_tagged, self::INNER_CACHE_NAME); return (bool) $result; } /** * Recursively create the directories needed to write the static file */ protected function _createDirectoriesFor($path) { if (!is_dir($path)) { $oldUmask = umask(0); if ( !@mkdir($path, $this->_octdec($this->_options['cache_directory_umask']), true)) { $lastErr = error_get_last(); umask($oldUmask); Zend_Cache::throwException("Can't create directory: {$lastErr['message']}"); } umask($oldUmask); } } /** * Detect serialization of data (cannot predict since this is the only way * to obey the interface yet pass in another parameter). * * In future, ZF 2.0, check if we can just avoid the interface restraints. * * This format is the only valid one possible for the class, so it's simple * to just run a regular expression for the starting serialized format. */ protected function _isSerialized($data) { return preg_match("/a:2:\{i:0;s:\d+:\"/", $data); } /** * Remove a cache record * * @param string $id Cache id * @return boolean True if no problem */ public function remove($id) { if (!$this->_verifyPath($id)) { Zend_Cache::throwException('Invalid cache id: does not match expected public_dir path'); } $fileName = basename($id); if ($this->_tagged === null && $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME)) { $this->_tagged = $tagged; } elseif (!$this->_tagged) { return false; } if (isset($this->_tagged[$id])) { $extension = $this->_tagged[$id]['extension']; } else { $extension = $this->_options['file_extension']; } if (empty($fileName)) { $fileName = $this->_options['index_filename']; } $pathName = $this->_options['public_dir'] . dirname($id); $file = realpath($pathName) . '/' . $fileName . $extension; if (!file_exists($file)) { return false; } return unlink($file); } /** * Remove a cache record recursively for the given directory matching a * REQUEST_URI based relative path (deletes the actual file matching this * in addition to the matching directory) * * @param string $id Cache id * @return boolean True if no problem */ public function removeRecursively($id) { if (!$this->_verifyPath($id)) { Zend_Cache::throwException('Invalid cache id: does not match expected public_dir path'); } $fileName = basename($id); if (empty($fileName)) { $fileName = $this->_options['index_filename']; } $pathName = $this->_options['public_dir'] . dirname($id); $file = $pathName . '/' . $fileName . $this->_options['file_extension']; $directory = $pathName . '/' . $fileName; if (file_exists($directory)) { if (!is_writable($directory)) { return false; } foreach (new DirectoryIterator($directory) as $file) { if (true === $file->isFile()) { if (false === unlink($file->getPathName())) { return false; } } } rmdir(dirname($path)); } if (file_exists($file)) { if (!is_writable($file)) { return false; } return unlink($file); } return true; } /** * Clean some cache records * * Available modes are : * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used) * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used) * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags * ($tags can be an array of strings or a single string) * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags} * ($tags can be an array of strings or a single string) * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags * ($tags can be an array of strings or a single string) * * @param string $mode Clean mode * @param array $tags Array of tags * @return boolean true if no problem */ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) { $result = false; switch ($mode) { case Zend_Cache::CLEANING_MODE_MATCHING_TAG: case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG: if (empty($tags)) { throw new Zend_Exception('Cannot use tag matching modes as no tags were defined'); } if ($this->_tagged === null && $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME)) { $this->_tagged = $tagged; } elseif (!$this->_tagged) { return true; } foreach ($tags as $tag) { $urls = array_keys($this->_tagged); foreach ($urls as $url) { if (isset($this->_tagged[$url]['tags']) && in_array($tag, $this->_tagged[$url]['tags'])) { $this->remove($url); unset($this->_tagged[$url]); } } } $this->getInnerCache()->save($this->_tagged, self::INNER_CACHE_NAME); $result = true; break; case Zend_Cache::CLEANING_MODE_ALL: if ($this->_tagged === null) { $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME); $this->_tagged = $tagged; } if ($this->_tagged === null || empty($this->_tagged)) { return true; } $urls = array_keys($this->_tagged); foreach ($urls as $url) { $this->remove($url); unset($this->_tagged[$url]); } $this->getInnerCache()->save($this->_tagged, self::INNER_CACHE_NAME); $result = true; break; case Zend_Cache::CLEANING_MODE_OLD: $this->_log("Zend_Cache_Backend_Static : Selected Cleaning Mode Currently Unsupported By This Backend"); break; case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG: if (empty($tags)) { throw new Zend_Exception('Cannot use tag matching modes as no tags were defined'); } if ($this->_tagged === null) { $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME); $this->_tagged = $tagged; } if ($this->_tagged === null || empty($this->_tagged)) { return true; } $urls = array_keys($this->_tagged); foreach ($urls as $url) { $difference = array_diff($tags, $this->_tagged[$url]['tags']); if (count($tags) == count($difference)) { $this->remove($url); unset($this->_tagged[$url]); } } $this->getInnerCache()->save($this->_tagged, self::INNER_CACHE_NAME); $result = true; break; default: Zend_Cache::throwException('Invalid mode for clean() method'); break; } return $result; } /** * Set an Inner Cache, used here primarily to store Tags associated * with caches created by this backend. Note: If Tags are lost, the cache * should be completely cleaned as the mapping of tags to caches will * have been irrevocably lost. * * @param Zend_Cache_Core * @return void */ public function setInnerCache(Zend_Cache_Core $cache) { $this->_tagCache = $cache; $this->_options['tag_cache'] = $cache; } /** * Get the Inner Cache if set * * @return Zend_Cache_Core */ public function getInnerCache() { if ($this->_tagCache === null) { Zend_Cache::throwException('An Inner Cache has not been set; use setInnerCache()'); } return $this->_tagCache; } /** * Verify path exists and is non-empty * * @param string $path * @return bool */ protected function _verifyPath($path) { $path = realpath($path); $base = realpath($this->_options['public_dir']); return strncmp($path, $base, strlen($base)) !== 0; } /** * Determine the page to save from the request * * @return string */ protected function _detectId() { return $_SERVER['REQUEST_URI']; } /** * Validate a cache id or a tag (security, reliable filenames, reserved prefixes...) * * Throw an exception if a problem is found * * @param string $string Cache id or tag * @throws Zend_Cache_Exception * @return void * @deprecated Not usable until perhaps ZF 2.0 */ protected static function _validateIdOrTag($string) { if (!is_string($string)) { Zend_Cache::throwException('Invalid id or tag : must be a string'); } // Internal only checked in Frontend - not here! if (substr($string, 0, 9) == 'internal-') { return; } // Validation assumes no query string, fragments or scheme included - only the path if (!preg_match( '/^(?:\/(?:(?:%[[:xdigit:]]{2}|[A-Za-z0-9-_.!~*\'()\[\]:@&=+$,;])*)?)+$/', $string ) ) { Zend_Cache::throwException("Invalid id or tag '$string' : must be a valid URL path"); } } /** * Detect an octal string and return its octal value for file permission ops * otherwise return the non-string (assumed octal or decimal int already) * * @param $val The potential octal in need of conversion * @return int */ protected function _octdec($val) { if (is_string($val) && decoct(octdec($val)) == $val) { return octdec($val); } return $val; } /** * Decode a request URI from the provided ID */ protected function _decodeId($id) { return pack('H*', $id);; } } vendor/Zend/Cache/Backend/Test.php000064400000027037151742733540012747 0ustar00_addLog('construct', array($options)); } /** * Set the frontend directives * * @param array $directives assoc of directives * @return void */ public function setDirectives($directives) { $this->_addLog('setDirectives', array($directives)); } /** * Test if a cache is available for the given id and (if yes) return it (false else) * * For this test backend only, if $id == 'false', then the method will return false * if $id == 'serialized', the method will return a serialized array * ('foo' else) * * @param string $id Cache id * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested * @return string Cached datas (or false) */ public function load($id, $doNotTestCacheValidity = false) { $this->_addLog('get', array($id, $doNotTestCacheValidity)); if ( $id == 'false' || $id == 'd8523b3ee441006261eeffa5c3d3a0a7' || $id == 'e83249ea22178277d5befc2c5e2e9ace' || $id == '40f649b94977c0a6e76902e2a0b43587' || $id == '88161989b73a4cbfd0b701c446115a99' || $id == '205fc79cba24f0f0018eb92c7c8b3ba4' || $id == '170720e35f38150b811f68a937fb042d') { return false; } if ($id=='serialized') { return serialize(array('foo')); } if ($id=='serialized2') { return serialize(array('headers' => array(), 'data' => 'foo')); } if ( $id == '71769f39054f75894288e397df04e445' || $id == '615d222619fb20b527168340cebd0578' || $id == '8a02d218a5165c467e7a5747cc6bd4b6' || $id == '648aca1366211d17cbf48e65dc570bee' || $id == '4a923ef02d7f997ca14d56dfeae25ea7') { return serialize(array('foo', 'bar')); } return 'foo'; } /** * Test if a cache is available or not (for the given id) * * For this test backend only, if $id == 'false', then the method will return false * (123456 else) * * @param string $id Cache id * @return mixed|false false (a cache is not available) or "last modified" timestamp (int) of the available cache record */ public function test($id) { $this->_addLog('test', array($id)); if ($id=='false') { return false; } if (($id=='3c439c922209e2cb0b54d6deffccd75a')) { return false; } return 123456; } /** * Save some string datas into a cache record * * For this test backend only, if $id == 'false', then the method will return false * (true else) * * @param string $data Datas to cache * @param string $id Cache id * @param array $tags Array of strings, the cache record will be tagged by each string entry * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime) * @return boolean True if no problem */ public function save($data, $id, $tags = array(), $specificLifetime = false) { $this->_addLog('save', array($data, $id, $tags)); if ($id=='false') { return false; } return true; } /** * Remove a cache record * * For this test backend only, if $id == 'false', then the method will return false * (true else) * * @param string $id Cache id * @return boolean True if no problem */ public function remove($id) { $this->_addLog('remove', array($id)); if ($id=='false') { return false; } return true; } /** * Clean some cache records * * For this test backend only, if $mode == 'false', then the method will return false * (true else) * * Available modes are : * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used) * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used) * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags * ($tags can be an array of strings or a single string) * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags} * ($tags can be an array of strings or a single string) * * @param string $mode Clean mode * @param array $tags Array of tags * @return boolean True if no problem */ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) { $this->_addLog('clean', array($mode, $tags)); if ($mode=='false') { return false; } return true; } /** * Get the last log * * @return string The last log */ public function getLastLog() { return $this->_log[$this->_index - 1]; } /** * Get the log index * * @return int Log index */ public function getLogIndex() { return $this->_index; } /** * Get the complete log array * * @return array Complete log array */ public function getAllLogs() { return $this->_log; } /** * Return true if the automatic cleaning is available for the backend * * @return boolean */ public function isAutomaticCleaningAvailable() { return true; } /** * Return an array of stored cache ids * * @return array array of stored cache ids (string) */ public function getIds() { return array( 'prefix_id1', 'prefix_id2' ); } /** * Return an array of stored tags * * @return array array of stored tags (string) */ public function getTags() { return array( 'tag1', 'tag2' ); } /** * Return an array of stored cache ids which match given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * @return array array of matching cache ids (string) */ public function getIdsMatchingTags($tags = array()) { if ($tags == array('tag1', 'tag2')) { return array('prefix_id1', 'prefix_id2'); } return array(); } /** * Return an array of stored cache ids which don't match given tags * * In case of multiple tags, a logical OR is made between tags * * @param array $tags array of tags * @return array array of not matching cache ids (string) */ public function getIdsNotMatchingTags($tags = array()) { if ($tags == array('tag3', 'tag4')) { return array('prefix_id3', 'prefix_id4'); } return array(); } /** * Return an array of stored cache ids which match any given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * @return array array of any matching cache ids (string) */ public function getIdsMatchingAnyTags($tags = array()) { if ($tags == array('tag5', 'tag6')) { return array('prefix_id5', 'prefix_id6'); } return array(); } /** * Return the filling percentage of the backend storage * * @return int integer between 0 and 100 */ public function getFillingPercentage() { return 50; } /** * Return an array of metadatas for the given cache id * * The array must include these keys : * - expire : the expire timestamp * - tags : a string array of tags * - mtime : timestamp of last modification time * * @param string $id cache id * @return array array of metadatas (false if the cache id is not found) */ public function getMetadatas($id) { return false; } /** * Give (if possible) an extra lifetime to the given cache id * * @param string $id cache id * @param int $extraLifetime * @return boolean true if ok */ public function touch($id, $extraLifetime) { return true; } /** * Return an associative array of capabilities (booleans) of the backend * * The array must include these keys : * - automatic_cleaning (is automating cleaning necessary) * - tags (are tags supported) * - expired_read (is it possible to read expired cache records * (for doNotTestCacheValidity option for example)) * - priority does the backend deal with priority when saving * - infinite_lifetime (is infinite lifetime can work with this backend) * - get_list (is it possible to get the list of cache ids and the complete list of tags) * * @return array associative of with capabilities */ public function getCapabilities() { return array( 'automatic_cleaning' => true, 'tags' => true, 'expired_read' => false, 'priority' => true, 'infinite_lifetime' => true, 'get_list' => true ); } /** * Add an event to the log array * * @param string $methodName MethodName * @param array $args Arguments * @return void */ private function _addLog($methodName, $args) { $this->_log[$this->_index] = array( 'methodName' => $methodName, 'args' => $args ); $this->_index = $this->_index + 1; } } vendor/Zend/Cache/Backend/TwoLevels.php000064400000046424151742733540013755 0ustar00 (string) slow_backend : * - Slow backend name * - Must implement the Zend_Cache_Backend_ExtendedInterface * - Should provide a big storage * * =====> (string) fast_backend : * - Flow backend name * - Must implement the Zend_Cache_Backend_ExtendedInterface * - Must be much faster than slow_backend * * =====> (array) slow_backend_options : * - Slow backend options (see corresponding backend) * * =====> (array) fast_backend_options : * - Fast backend options (see corresponding backend) * * =====> (int) stats_update_factor : * - Disable / Tune the computation of the fast backend filling percentage * - When saving a record into cache : * 1 => systematic computation of the fast backend filling percentage * x (integer) > 1 => computation of the fast backend filling percentage randomly 1 times on x cache write * * =====> (boolean) slow_backend_custom_naming : * =====> (boolean) fast_backend_custom_naming : * =====> (boolean) slow_backend_autoload : * =====> (boolean) fast_backend_autoload : * - See Zend_Cache::factory() method * * =====> (boolean) auto_refresh_fast_cache * - If true, auto refresh the fast cache when a cache record is hit * * @var array available options */ protected $_options = array( 'slow_backend' => 'File', 'fast_backend' => 'Apc', 'slow_backend_options' => array(), 'fast_backend_options' => array(), 'stats_update_factor' => 10, 'slow_backend_custom_naming' => false, 'fast_backend_custom_naming' => false, 'slow_backend_autoload' => false, 'fast_backend_autoload' => false, 'auto_refresh_fast_cache' => true ); /** * Slow Backend * * @var Zend_Cache_Backend_ExtendedInterface */ protected $_slowBackend; /** * Fast Backend * * @var Zend_Cache_Backend_ExtendedInterface */ protected $_fastBackend; /** * Cache for the fast backend filling percentage * * @var int */ protected $_fastBackendFillingPercentage = null; /** * Constructor * * @param array $options Associative array of options * @throws Zend_Cache_Exception * @return void */ public function __construct(array $options = array()) { parent::__construct($options); if ($this->_options['slow_backend'] === null) { Zend_Cache::throwException('slow_backend option has to set'); } elseif ($this->_options['slow_backend'] instanceof Zend_Cache_Backend_ExtendedInterface) { $this->_slowBackend = $this->_options['slow_backend']; } else { $this->_slowBackend = Zend_Cache::_makeBackend( $this->_options['slow_backend'], $this->_options['slow_backend_options'], $this->_options['slow_backend_custom_naming'], $this->_options['slow_backend_autoload'] ); if (!in_array('Zend_Cache_Backend_ExtendedInterface', class_implements($this->_slowBackend))) { Zend_Cache::throwException('slow_backend must implement the Zend_Cache_Backend_ExtendedInterface interface'); } } if ($this->_options['fast_backend'] === null) { Zend_Cache::throwException('fast_backend option has to set'); } elseif ($this->_options['fast_backend'] instanceof Zend_Cache_Backend_ExtendedInterface) { $this->_fastBackend = $this->_options['fast_backend']; } else { $this->_fastBackend = Zend_Cache::_makeBackend( $this->_options['fast_backend'], $this->_options['fast_backend_options'], $this->_options['fast_backend_custom_naming'], $this->_options['fast_backend_autoload'] ); if (!in_array('Zend_Cache_Backend_ExtendedInterface', class_implements($this->_fastBackend))) { Zend_Cache::throwException('fast_backend must implement the Zend_Cache_Backend_ExtendedInterface interface'); } } $this->_slowBackend->setDirectives($this->_directives); $this->_fastBackend->setDirectives($this->_directives); } /** * Test if a cache is available or not (for the given id) * * @param string $id cache id * @return mixed|false (a cache is not available) or "last modified" timestamp (int) of the available cache record */ public function test($id) { $fastTest = $this->_fastBackend->test($id); if ($fastTest) { return $fastTest; } else { return $this->_slowBackend->test($id); } } /** * Save some string datas into a cache record * * Note : $data is always "string" (serialization is done by the * core not by the backend) * * @param string $data Datas to cache * @param string $id Cache id * @param array $tags Array of strings, the cache record will be tagged by each string entry * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime) * @param int $priority integer between 0 (very low priority) and 10 (maximum priority) used by some particular backends * @return boolean true if no problem */ public function save($data, $id, $tags = array(), $specificLifetime = false, $priority = 8) { $usage = $this->_getFastFillingPercentage('saving'); $boolFast = true; $lifetime = $this->getLifetime($specificLifetime); $preparedData = $this->_prepareData($data, $lifetime, $priority); if (($priority > 0) && (10 * $priority >= $usage)) { $fastLifetime = $this->_getFastLifetime($lifetime, $priority); $boolFast = $this->_fastBackend->save($preparedData, $id, array(), $fastLifetime); $boolSlow = $this->_slowBackend->save($preparedData, $id, $tags, $lifetime); } else { $boolSlow = $this->_slowBackend->save($preparedData, $id, $tags, $lifetime); if ($boolSlow === true) { $boolFast = $this->_fastBackend->remove($id); if (!$boolFast && !$this->_fastBackend->test($id)) { // some backends return false on remove() even if the key never existed. (and it won't if fast is full) // all we care about is that the key doesn't exist now $boolFast = true; } } } return ($boolFast && $boolSlow); } /** * Test if a cache is available for the given id and (if yes) return it (false else) * * Note : return value is always "string" (unserialization is done by the core not by the backend) * * @param string $id Cache id * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested * @return string|false cached datas */ public function load($id, $doNotTestCacheValidity = false) { $res = $this->_fastBackend->load($id, $doNotTestCacheValidity); if ($res === false) { $res = $this->_slowBackend->load($id, $doNotTestCacheValidity); if ($res === false) { // there is no cache at all for this id return false; } } $array = unserialize($res); // maybe, we have to refresh the fast cache ? if ($this->_options['auto_refresh_fast_cache']) { if ($array['priority'] == 10) { // no need to refresh the fast cache with priority = 10 return $array['data']; } $newFastLifetime = $this->_getFastLifetime($array['lifetime'], $array['priority'], time() - $array['expire']); // we have the time to refresh the fast cache $usage = $this->_getFastFillingPercentage('loading'); if (($array['priority'] > 0) && (10 * $array['priority'] >= $usage)) { // we can refresh the fast cache $preparedData = $this->_prepareData($array['data'], $array['lifetime'], $array['priority']); $this->_fastBackend->save($preparedData, $id, array(), $newFastLifetime); } } return $array['data']; } /** * Remove a cache record * * @param string $id Cache id * @return boolean True if no problem */ public function remove($id) { $boolFast = $this->_fastBackend->remove($id); $boolSlow = $this->_slowBackend->remove($id); return $boolFast && $boolSlow; } /** * Clean some cache records * * Available modes are : * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used) * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used) * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags * ($tags can be an array of strings or a single string) * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags} * ($tags can be an array of strings or a single string) * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags * ($tags can be an array of strings or a single string) * * @param string $mode Clean mode * @param array $tags Array of tags * @throws Zend_Cache_Exception * @return boolean true if no problem */ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) { switch($mode) { case Zend_Cache::CLEANING_MODE_ALL: $boolFast = $this->_fastBackend->clean(Zend_Cache::CLEANING_MODE_ALL); $boolSlow = $this->_slowBackend->clean(Zend_Cache::CLEANING_MODE_ALL); return $boolFast && $boolSlow; break; case Zend_Cache::CLEANING_MODE_OLD: return $this->_slowBackend->clean(Zend_Cache::CLEANING_MODE_OLD); case Zend_Cache::CLEANING_MODE_MATCHING_TAG: $ids = $this->_slowBackend->getIdsMatchingTags($tags); $res = true; foreach ($ids as $id) { $bool = $this->remove($id); $res = $res && $bool; } return $res; break; case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG: $ids = $this->_slowBackend->getIdsNotMatchingTags($tags); $res = true; foreach ($ids as $id) { $bool = $this->remove($id); $res = $res && $bool; } return $res; break; case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG: $ids = $this->_slowBackend->getIdsMatchingAnyTags($tags); $res = true; foreach ($ids as $id) { $bool = $this->remove($id); $res = $res && $bool; } return $res; break; default: Zend_Cache::throwException('Invalid mode for clean() method'); break; } } /** * Return an array of stored cache ids * * @return array array of stored cache ids (string) */ public function getIds() { return $this->_slowBackend->getIds(); } /** * Return an array of stored tags * * @return array array of stored tags (string) */ public function getTags() { return $this->_slowBackend->getTags(); } /** * Return an array of stored cache ids which match given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * @return array array of matching cache ids (string) */ public function getIdsMatchingTags($tags = array()) { return $this->_slowBackend->getIdsMatchingTags($tags); } /** * Return an array of stored cache ids which don't match given tags * * In case of multiple tags, a logical OR is made between tags * * @param array $tags array of tags * @return array array of not matching cache ids (string) */ public function getIdsNotMatchingTags($tags = array()) { return $this->_slowBackend->getIdsNotMatchingTags($tags); } /** * Return an array of stored cache ids which match any given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * @return array array of any matching cache ids (string) */ public function getIdsMatchingAnyTags($tags = array()) { return $this->_slowBackend->getIdsMatchingAnyTags($tags); } /** * Return the filling percentage of the backend storage * * @return int integer between 0 and 100 */ public function getFillingPercentage() { return $this->_slowBackend->getFillingPercentage(); } /** * Return an array of metadatas for the given cache id * * The array must include these keys : * - expire : the expire timestamp * - tags : a string array of tags * - mtime : timestamp of last modification time * * @param string $id cache id * @return array array of metadatas (false if the cache id is not found) */ public function getMetadatas($id) { return $this->_slowBackend->getMetadatas($id); } /** * Give (if possible) an extra lifetime to the given cache id * * @param string $id cache id * @param int $extraLifetime * @return boolean true if ok */ public function touch($id, $extraLifetime) { return $this->_slowBackend->touch($id, $extraLifetime); } /** * Return an associative array of capabilities (booleans) of the backend * * The array must include these keys : * - automatic_cleaning (is automating cleaning necessary) * - tags (are tags supported) * - expired_read (is it possible to read expired cache records * (for doNotTestCacheValidity option for example)) * - priority does the backend deal with priority when saving * - infinite_lifetime (is infinite lifetime can work with this backend) * - get_list (is it possible to get the list of cache ids and the complete list of tags) * * @return array associative of with capabilities */ public function getCapabilities() { $slowBackendCapabilities = $this->_slowBackend->getCapabilities(); return array( 'automatic_cleaning' => $slowBackendCapabilities['automatic_cleaning'], 'tags' => $slowBackendCapabilities['tags'], 'expired_read' => $slowBackendCapabilities['expired_read'], 'priority' => $slowBackendCapabilities['priority'], 'infinite_lifetime' => $slowBackendCapabilities['infinite_lifetime'], 'get_list' => $slowBackendCapabilities['get_list'] ); } /** * Prepare a serialized array to store datas and metadatas informations * * @param string $data data to store * @param int $lifetime original lifetime * @param int $priority priority * @return string serialize array to store into cache */ private function _prepareData($data, $lifetime, $priority) { $lt = $lifetime; if ($lt === null) { $lt = 9999999999; } return serialize(array( 'data' => $data, 'lifetime' => $lifetime, 'expire' => time() + $lt, 'priority' => $priority )); } /** * Compute and return the lifetime for the fast backend * * @param int $lifetime original lifetime * @param int $priority priority * @param int $maxLifetime maximum lifetime * @return int lifetime for the fast backend */ private function _getFastLifetime($lifetime, $priority, $maxLifetime = null) { if ($lifetime === null) { // if lifetime is null, we have an infinite lifetime // we need to use arbitrary lifetimes $fastLifetime = (int) (2592000 / (11 - $priority)); } else { $fastLifetime = (int) ($lifetime / (11 - $priority)); } if (($maxLifetime !== null) && ($maxLifetime >= 0)) { if ($fastLifetime > $maxLifetime) { return $maxLifetime; } } return $fastLifetime; } /** * PUBLIC METHOD FOR UNIT TESTING ONLY ! * * Force a cache record to expire * * @param string $id cache id */ public function ___expire($id) { $this->_fastBackend->remove($id); $this->_slowBackend->___expire($id); } private function _getFastFillingPercentage($mode) { if ($mode == 'saving') { // mode saving if ($this->_fastBackendFillingPercentage === null) { $this->_fastBackendFillingPercentage = $this->_fastBackend->getFillingPercentage(); } else { $rand = rand(1, $this->_options['stats_update_factor']); if ($rand == 1) { // we force a refresh $this->_fastBackendFillingPercentage = $this->_fastBackend->getFillingPercentage(); } } } else { // mode loading // we compute the percentage only if it's not available in cache if ($this->_fastBackendFillingPercentage === null) { $this->_fastBackendFillingPercentage = $this->_fastBackend->getFillingPercentage(); } } return $this->_fastBackendFillingPercentage; } } vendor/Zend/Cache/Backend/Xcache.php000064400000016264151742733540013223 0ustar00 (string) user : * xcache.admin.user (necessary for the clean() method) * * =====> (string) password : * xcache.admin.pass (clear, not MD5) (necessary for the clean() method) * * @var array available options */ protected $_options = array( 'user' => null, 'password' => null ); /** * Constructor * * @param array $options associative array of options * @throws Zend_Cache_Exception * @return void */ public function __construct(array $options = array()) { if (!extension_loaded('xcache')) { Zend_Cache::throwException('The xcache extension must be loaded for using this backend !'); } parent::__construct($options); } /** * Test if a cache is available for the given id and (if yes) return it (false else) * * WARNING $doNotTestCacheValidity=true is unsupported by the Xcache backend * * @param string $id cache id * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested * @return string cached datas (or false) */ public function load($id, $doNotTestCacheValidity = false) { if ($doNotTestCacheValidity) { $this->_log("Zend_Cache_Backend_Xcache::load() : \$doNotTestCacheValidity=true is unsupported by the Xcache backend"); } $tmp = xcache_get($id); if (is_array($tmp)) { return $tmp[0]; } return false; } /** * Test if a cache is available or not (for the given id) * * @param string $id cache id * @return mixed false (a cache is not available) or "last modified" timestamp (int) of the available cache record */ public function test($id) { if (xcache_isset($id)) { $tmp = xcache_get($id); if (is_array($tmp)) { return $tmp[1]; } } return false; } /** * Save some string datas into a cache record * * Note : $data is always "string" (serialization is done by the * core not by the backend) * * @param string $data datas to cache * @param string $id cache id * @param array $tags array of strings, the cache record will be tagged by each string entry * @param int $specificLifetime if != false, set a specific lifetime for this cache record (null => infinite lifetime) * @return boolean true if no problem */ public function save($data, $id, $tags = array(), $specificLifetime = false) { $lifetime = $this->getLifetime($specificLifetime); $result = xcache_set($id, array($data, time()), $lifetime); if (count($tags) > 0) { $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_XCACHE_BACKEND); } return $result; } /** * Remove a cache record * * @param string $id cache id * @return boolean true if no problem */ public function remove($id) { return xcache_unset($id); } /** * Clean some cache records * * Available modes are : * 'all' (default) => remove all cache entries ($tags is not used) * 'old' => unsupported * 'matchingTag' => unsupported * 'notMatchingTag' => unsupported * 'matchingAnyTag' => unsupported * * @param string $mode clean mode * @param array $tags array of tags * @throws Zend_Cache_Exception * @return boolean true if no problem */ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) { switch ($mode) { case Zend_Cache::CLEANING_MODE_ALL: // Necessary because xcache_clear_cache() need basic authentification $backup = array(); if (isset($_SERVER['PHP_AUTH_USER'])) { $backup['PHP_AUTH_USER'] = $_SERVER['PHP_AUTH_USER']; } if (isset($_SERVER['PHP_AUTH_PW'])) { $backup['PHP_AUTH_PW'] = $_SERVER['PHP_AUTH_PW']; } if ($this->_options['user']) { $_SERVER['PHP_AUTH_USER'] = $this->_options['user']; } if ($this->_options['password']) { $_SERVER['PHP_AUTH_PW'] = $this->_options['password']; } $cnt = xcache_count(XC_TYPE_VAR); for ($i=0; $i < $cnt; $i++) { xcache_clear_cache(XC_TYPE_VAR, $i); } if (isset($backup['PHP_AUTH_USER'])) { $_SERVER['PHP_AUTH_USER'] = $backup['PHP_AUTH_USER']; $_SERVER['PHP_AUTH_PW'] = $backup['PHP_AUTH_PW']; } return true; break; case Zend_Cache::CLEANING_MODE_OLD: $this->_log("Zend_Cache_Backend_Xcache::clean() : CLEANING_MODE_OLD is unsupported by the Xcache backend"); break; case Zend_Cache::CLEANING_MODE_MATCHING_TAG: case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG: case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG: $this->_log(self::TAGS_UNSUPPORTED_BY_CLEAN_OF_XCACHE_BACKEND); break; default: Zend_Cache::throwException('Invalid mode for clean() method'); break; } } /** * Return true if the automatic cleaning is available for the backend * * @return boolean */ public function isAutomaticCleaningAvailable() { return false; } } vendor/Zend/Cache/Backend/ZendPlatform.php000064400000027323151742733540014433 0ustar00_directives['lifetime']; } $res = output_cache_get($id, $lifetime); if($res) { return $res[0]; } else { return false; } } /** * Test if a cache is available or not (for the given id) * * @param string $id Cache id * @return mixed|false false (a cache is not available) or "last modified" timestamp (int) of the available cache record */ public function test($id) { $result = output_cache_get($id, $this->_directives['lifetime']); if ($result) { return $result[1]; } return false; } /** * Save some string datas into a cache record * * Note : $data is always "string" (serialization is done by the * core not by the backend) * * @param string $data Data to cache * @param string $id Cache id * @param array $tags Array of strings, the cache record will be tagged by each string entry * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime) * @return boolean true if no problem */ public function save($data, $id, $tags = array(), $specificLifetime = false) { if (!($specificLifetime === false)) { $this->_log("Zend_Cache_Backend_ZendPlatform::save() : non false specifc lifetime is unsuported for this backend"); } $lifetime = $this->_directives['lifetime']; $result1 = output_cache_put($id, array($data, time())); $result2 = (count($tags) == 0); foreach ($tags as $tag) { $tagid = self::TAGS_PREFIX.$tag; $old_tags = output_cache_get($tagid, $lifetime); if ($old_tags === false) { $old_tags = array(); } $old_tags[$id] = $id; output_cache_remove_key($tagid); $result2 = output_cache_put($tagid, $old_tags); } return $result1 && $result2; } /** * Remove a cache record * * @param string $id Cache id * @return boolean True if no problem */ public function remove($id) { return output_cache_remove_key($id); } /** * Clean some cache records * * Available modes are : * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used) * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used) * This mode is not supported in this backend * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags * ($tags can be an array of strings or a single string) * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => unsupported * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags * ($tags can be an array of strings or a single string) * * @param string $mode Clean mode * @param array $tags Array of tags * @throws Zend_Cache_Exception * @return boolean True if no problem */ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) { switch ($mode) { case Zend_Cache::CLEANING_MODE_ALL: case Zend_Cache::CLEANING_MODE_OLD: $cache_dir = ini_get('zend_accelerator.output_cache_dir'); if (!$cache_dir) { return false; } $cache_dir .= '/.php_cache_api/'; return $this->_clean($cache_dir, $mode); break; case Zend_Cache::CLEANING_MODE_MATCHING_TAG: $idlist = null; foreach ($tags as $tag) { $next_idlist = output_cache_get(self::TAGS_PREFIX.$tag, $this->_directives['lifetime']); if ($idlist) { $idlist = array_intersect_assoc($idlist, $next_idlist); } else { $idlist = $next_idlist; } if (count($idlist) == 0) { // if ID list is already empty - we may skip checking other IDs $idlist = null; break; } } if ($idlist) { foreach ($idlist as $id) { output_cache_remove_key($id); } } return true; break; case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG: $this->_log("Zend_Cache_Backend_ZendPlatform::clean() : CLEANING_MODE_NOT_MATCHING_TAG is not supported by the Zend Platform backend"); return false; break; case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG: $idlist = null; foreach ($tags as $tag) { $next_idlist = output_cache_get(self::TAGS_PREFIX.$tag, $this->_directives['lifetime']); if ($idlist) { $idlist = array_merge_recursive($idlist, $next_idlist); } else { $idlist = $next_idlist; } if (count($idlist) == 0) { // if ID list is already empty - we may skip checking other IDs $idlist = null; break; } } if ($idlist) { foreach ($idlist as $id) { output_cache_remove_key($id); } } return true; break; default: Zend_Cache::throwException('Invalid mode for clean() method'); break; } } /** * Clean a directory and recursivly go over it's subdirectories * * Remove all the cached files that need to be cleaned (according to mode and files mtime) * * @param string $dir Path of directory ot clean * @param string $mode The same parameter as in Zend_Cache_Backend_ZendPlatform::clean() * @return boolean True if ok */ private function _clean($dir, $mode) { $d = @dir($dir); if (!$d) { return false; } $result = true; while (false !== ($file = $d->read())) { if ($file == '.' || $file == '..') { continue; } $file = $d->path . $file; if (is_dir($file)) { $result = ($this->_clean($file .'/', $mode)) && ($result); } else { if ($mode == Zend_Cache::CLEANING_MODE_ALL) { $result = ($this->_remove($file)) && ($result); } else if ($mode == Zend_Cache::CLEANING_MODE_OLD) { // Files older than lifetime get deleted from cache if ($this->_directives['lifetime'] !== null) { if ((time() - @filemtime($file)) > $this->_directives['lifetime']) { $result = ($this->_remove($file)) && ($result); } } } } } $d->close(); return $result; } /** * Remove a file * * If we can't remove the file (because of locks or any problem), we will touch * the file to invalidate it * * @param string $file Complete file path * @return boolean True if ok */ private function _remove($file) { if (!@unlink($file)) { # If we can't remove the file (because of locks or any problem), we will touch # the file to invalidate it $this->_log("Zend_Cache_Backend_ZendPlatform::_remove() : we can't remove $file => we are going to try to invalidate it"); if ($this->_directives['lifetime'] === null) { return false; } if (!file_exists($file)) { return false; } return @touch($file, time() - 2*abs($this->_directives['lifetime'])); } return true; } } vendor/Zend/Cache/Backend/ZendServer.php000075500000014552151742733540014120 0ustar00 (string) namespace : * Namespace to be used for chaching operations * * @var array available options */ protected $_options = array( 'namespace' => 'zendframework' ); /** * Store data * * @param mixed $data Object to store * @param string $id Cache id * @param int $timeToLive Time to live in seconds * @throws Zend_Cache_Exception */ abstract protected function _store($data, $id, $timeToLive); /** * Fetch data * * @param string $id Cache id * @throws Zend_Cache_Exception */ abstract protected function _fetch($id); /** * Unset data * * @param string $id Cache id */ abstract protected function _unset($id); /** * Clear cache */ abstract protected function _clear(); /** * Test if a cache is available for the given id and (if yes) return it (false else) * * @param string $id cache id * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested * @return string cached datas (or false) */ public function load($id, $doNotTestCacheValidity = false) { $tmp = $this->_fetch($id); if ($tmp !== null) { return $tmp; } return false; } /** * Test if a cache is available or not (for the given id) * * @param string $id cache id * @return mixed false (a cache is not available) or "last modified" timestamp (int) of the available cache record * @throws Zend_Cache_Exception */ public function test($id) { $tmp = $this->_fetch('internal-metadatas---' . $id); if ($tmp !== false) { if (!is_array($tmp) || !isset($tmp['mtime'])) { Zend_Cache::throwException('Cache metadata for \'' . $id . '\' id is corrupted' ); } return $tmp['mtime']; } return false; } /** * Compute & return the expire time * * @return int expire time (unix timestamp) */ private function _expireTime($lifetime) { if ($lifetime === null) { return 9999999999; } return time() + $lifetime; } /** * Save some string datas into a cache record * * Note : $data is always "string" (serialization is done by the * core not by the backend) * * @param string $data datas to cache * @param string $id cache id * @param array $tags array of strings, the cache record will be tagged by each string entry * @param int $specificLifetime if != false, set a specific lifetime for this cache record (null => infinite lifetime) * @return boolean true if no problem */ public function save($data, $id, $tags = array(), $specificLifetime = false) { $lifetime = $this->getLifetime($specificLifetime); $metadatas = array( 'mtime' => time(), 'expire' => $this->_expireTime($lifetime), ); if (count($tags) > 0) { $this->_log('Zend_Cache_Backend_ZendServer::save() : tags are unsupported by the ZendServer backends'); } return $this->_store($data, $id, $lifetime) && $this->_store($metadatas, 'internal-metadatas---' . $id, $lifetime); } /** * Remove a cache record * * @param string $id cache id * @return boolean true if no problem */ public function remove($id) { $result1 = $this->_unset($id); $result2 = $this->_unset('internal-metadatas---' . $id); return $result1 && $result2; } /** * Clean some cache records * * Available modes are : * 'all' (default) => remove all cache entries ($tags is not used) * 'old' => unsupported * 'matchingTag' => unsupported * 'notMatchingTag' => unsupported * 'matchingAnyTag' => unsupported * * @param string $mode clean mode * @param array $tags array of tags * @throws Zend_Cache_Exception * @return boolean true if no problem */ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) { switch ($mode) { case Zend_Cache::CLEANING_MODE_ALL: $this->_clear(); return true; break; case Zend_Cache::CLEANING_MODE_OLD: $this->_log("Zend_Cache_Backend_ZendServer::clean() : CLEANING_MODE_OLD is unsupported by the Zend Server backends."); break; case Zend_Cache::CLEANING_MODE_MATCHING_TAG: case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG: case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG: $this->_clear(); $this->_log('Zend_Cache_Backend_ZendServer::clean() : tags are unsupported by the Zend Server backends.'); break; default: Zend_Cache::throwException('Invalid mode for clean() method'); break; } } } vendor/Zend/Cache/Backend/ZendServer/Disk.php000075500000005624151742733540015012 0ustar00_options['namespace'] . '::' . $id, $data, $timeToLive) === false) { $this->_log('Store operation failed.'); return false; } return true; } /** * Fetch data * * @param string $id Cache id */ protected function _fetch($id) { return zend_disk_cache_fetch($this->_options['namespace'] . '::' . $id); } /** * Unset data * * @param string $id Cache id * @return boolean true if no problem */ protected function _unset($id) { return zend_disk_cache_delete($this->_options['namespace'] . '::' . $id); } /** * Clear cache */ protected function _clear() { zend_disk_cache_clear($this->_options['namespace']); } } vendor/Zend/Cache/Backend/ZendServer/ShMem.php000075500000005557151742733540015136 0ustar00_options['namespace'] . '::' . $id, $data, $timeToLive) === false) { $this->_log('Store operation failed.'); return false; } return true; } /** * Fetch data * * @param string $id Cache id */ protected function _fetch($id) { return zend_shm_cache_fetch($this->_options['namespace'] . '::' . $id); } /** * Unset data * * @param string $id Cache id * @return boolean true if no problem */ protected function _unset($id) { return zend_shm_cache_delete($this->_options['namespace'] . '::' . $id); } /** * Clear cache */ protected function _clear() { zend_shm_cache_clear($this->_options['namespace']); } } vendor/Zend/Cache/Core.php000064400000062511151742733540011365 0ustar00 (boolean) write_control : * - Enable / disable write control (the cache is read just after writing to detect corrupt entries) * - Enable write control will lightly slow the cache writing but not the cache reading * Write control can detect some corrupt cache files but maybe it's not a perfect control * * ====> (boolean) caching : * - Enable / disable caching * (can be very useful for the debug of cached scripts) * * =====> (string) cache_id_prefix : * - prefix for cache ids (namespace) * * ====> (boolean) automatic_serialization : * - Enable / disable automatic serialization * - It can be used to save directly datas which aren't strings (but it's slower) * * ====> (int) automatic_cleaning_factor : * - Disable / Tune the automatic cleaning process * - The automatic cleaning process destroy too old (for the given life time) * cache files when a new cache file is written : * 0 => no automatic cache cleaning * 1 => systematic cache cleaning * x (integer) > 1 => automatic cleaning randomly 1 times on x cache write * * ====> (int) lifetime : * - Cache lifetime (in seconds) * - If null, the cache is valid forever. * * ====> (boolean) logging : * - If set to true, logging is activated (but the system is slower) * * ====> (boolean) ignore_user_abort * - If set to true, the core will set the ignore_user_abort PHP flag inside the * save() method to avoid cache corruptions in some cases (default false) * * @var array $_options available options */ protected $_options = array( 'write_control' => true, 'caching' => true, 'cache_id_prefix' => null, 'automatic_serialization' => false, 'automatic_cleaning_factor' => 10, 'lifetime' => 3600, 'logging' => false, 'logger' => null, 'ignore_user_abort' => false ); /** * Array of options which have to be transfered to backend * * @var array $_directivesList */ protected static $_directivesList = array('lifetime', 'logging', 'logger'); /** * Not used for the core, just a sort a hint to get a common setOption() method (for the core and for frontends) * * @var array $_specificOptions */ protected $_specificOptions = array(); /** * Last used cache id * * @var string $_lastId */ private $_lastId = null; /** * True if the backend implements Zend_Cache_Backend_ExtendedInterface * * @var boolean $_extendedBackend */ protected $_extendedBackend = false; /** * Array of capabilities of the backend (only if it implements Zend_Cache_Backend_ExtendedInterface) * * @var array */ protected $_backendCapabilities = array(); /** * Constructor * * @param array|Zend_Config $options Associative array of options or Zend_Config instance * @throws Zend_Cache_Exception * @return void */ public function __construct($options = array()) { if ($options instanceof Zend_Config) { $options = $options->toArray(); } if (!is_array($options)) { Zend_Cache::throwException("Options passed were not an array" . " or Zend_Config instance."); } while (list($name, $value) = each($options)) { $this->setOption($name, $value); } $this->_loggerSanity(); } /** * Set options using an instance of type Zend_Config * * @param Zend_Config $config * @return Zend_Cache_Core */ public function setConfig(Zend_Config $config) { $options = $config->toArray(); while (list($name, $value) = each($options)) { $this->setOption($name, $value); } return $this; } /** * Set the backend * * @param Zend_Cache_Backend $backendObject * @throws Zend_Cache_Exception * @return void */ public function setBackend(Zend_Cache_Backend $backendObject) { $this->_backend= $backendObject; // some options (listed in $_directivesList) have to be given // to the backend too (even if they are not "backend specific") $directives = array(); foreach (Zend_Cache_Core::$_directivesList as $directive) { $directives[$directive] = $this->_options[$directive]; } $this->_backend->setDirectives($directives); if (in_array('Zend_Cache_Backend_ExtendedInterface', class_implements($this->_backend))) { $this->_extendedBackend = true; $this->_backendCapabilities = $this->_backend->getCapabilities(); } } /** * Returns the backend * * @return Zend_Cache_Backend backend object */ public function getBackend() { return $this->_backend; } /** * Public frontend to set an option * * There is an additional validation (relatively to the protected _setOption method) * * @param string $name Name of the option * @param mixed $value Value of the option * @throws Zend_Cache_Exception * @return void */ public function setOption($name, $value) { if (!is_string($name)) { Zend_Cache::throwException("Incorrect option name : $name"); } $name = strtolower($name); if (array_key_exists($name, $this->_options)) { // This is a Core option $this->_setOption($name, $value); return; } if (array_key_exists($name, $this->_specificOptions)) { // This a specic option of this frontend $this->_specificOptions[$name] = $value; return; } } /** * Public frontend to get an option value * * @param string $name Name of the option * @throws Zend_Cache_Exception * @return mixed option value */ public function getOption($name) { if (is_string($name)) { $name = strtolower($name); if (array_key_exists($name, $this->_options)) { // This is a Core option return $this->_options[$name]; } if (array_key_exists($name, $this->_specificOptions)) { // This a specic option of this frontend return $this->_specificOptions[$name]; } } Zend_Cache::throwException("Incorrect option name : $name"); } /** * Set an option * * @param string $name Name of the option * @param mixed $value Value of the option * @throws Zend_Cache_Exception * @return void */ private function _setOption($name, $value) { if (!is_string($name) || !array_key_exists($name, $this->_options)) { Zend_Cache::throwException("Incorrect option name : $name"); } if ($name == 'lifetime' && empty($value)) { $value = null; } $this->_options[$name] = $value; } /** * Force a new lifetime * * The new value is set for the core/frontend but for the backend too (directive) * * @param int $newLifetime New lifetime (in seconds) * @return void */ public function setLifetime($newLifetime) { $this->_options['lifetime'] = $newLifetime; $this->_backend->setDirectives(array( 'lifetime' => $newLifetime )); } /** * Test if a cache is available for the given id and (if yes) return it (false else) * * @param string $id Cache id * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested * @param boolean $doNotUnserialize Do not serialize (even if automatic_serialization is true) => for internal use * @return mixed|false Cached datas */ public function load($id, $doNotTestCacheValidity = false, $doNotUnserialize = false) { if (!$this->_options['caching']) { return false; } $id = $this->_id($id); // cache id may need prefix $this->_lastId = $id; self::_validateIdOrTag($id); $data = $this->_backend->load($id, $doNotTestCacheValidity); if ($data===false) { // no cache available return false; } if ((!$doNotUnserialize) && $this->_options['automatic_serialization']) { // we need to unserialize before sending the result return unserialize($data); } return $data; } /** * Test if a cache is available for the given id * * @param string $id Cache id * @return int|false Last modified time of cache entry if it is available, false otherwise */ public function test($id) { if (!$this->_options['caching']) { return false; } $id = $this->_id($id); // cache id may need prefix self::_validateIdOrTag($id); $this->_lastId = $id; return $this->_backend->test($id); } /** * Save some data in a cache * * @param mixed $data Data to put in cache (can be another type than string if automatic_serialization is on) * @param string $id Cache id (if not set, the last cache id will be used) * @param array $tags Cache tags * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime) * @param int $priority integer between 0 (very low priority) and 10 (maximum priority) used by some particular backends * @throws Zend_Cache_Exception * @return boolean True if no problem */ public function save($data, $id = null, $tags = array(), $specificLifetime = false, $priority = 8) { if (!$this->_options['caching']) { return true; } if ($id === null) { $id = $this->_lastId; } else { $id = $this->_id($id); } self::_validateIdOrTag($id); self::_validateTagsArray($tags); if ($this->_options['automatic_serialization']) { // we need to serialize datas before storing them $data = serialize($data); } else { if (!is_string($data)) { Zend_Cache::throwException("Datas must be string or set automatic_serialization = true"); } } // automatic cleaning if ($this->_options['automatic_cleaning_factor'] > 0) { $rand = rand(1, $this->_options['automatic_cleaning_factor']); if ($rand==1) { if ($this->_extendedBackend) { // New way if ($this->_backendCapabilities['automatic_cleaning']) { $this->clean(Zend_Cache::CLEANING_MODE_OLD); } else { $this->_log('Zend_Cache_Core::save() / automatic cleaning is not available/necessary with this backend'); } } else { // Deprecated way (will be removed in next major version) if (method_exists($this->_backend, 'isAutomaticCleaningAvailable') && ($this->_backend->isAutomaticCleaningAvailable())) { $this->clean(Zend_Cache::CLEANING_MODE_OLD); } else { $this->_log('Zend_Cache_Core::save() / automatic cleaning is not available/necessary with this backend'); } } } } if ($this->_options['ignore_user_abort']) { $abort = ignore_user_abort(true); } if (($this->_extendedBackend) && ($this->_backendCapabilities['priority'])) { $result = $this->_backend->save($data, $id, $tags, $specificLifetime, $priority); } else { $result = $this->_backend->save($data, $id, $tags, $specificLifetime); } if ($this->_options['ignore_user_abort']) { ignore_user_abort($abort); } if (!$result) { // maybe the cache is corrupted, so we remove it ! if ($this->_options['logging']) { $this->_log("Zend_Cache_Core::save() : impossible to save cache (id=$id)"); } $this->remove($id); return false; } if ($this->_options['write_control']) { $data2 = $this->_backend->load($id, true); if ($data!=$data2) { $this->_log('Zend_Cache_Core::save() / write_control : written and read data do not match'); $this->_backend->remove($id); return false; } } return true; } /** * Remove a cache * * @param string $id Cache id to remove * @return boolean True if ok */ public function remove($id) { if (!$this->_options['caching']) { return true; } $id = $this->_id($id); // cache id may need prefix self::_validateIdOrTag($id); return $this->_backend->remove($id); } /** * Clean cache entries * * Available modes are : * 'all' (default) => remove all cache entries ($tags is not used) * 'old' => remove too old cache entries ($tags is not used) * 'matchingTag' => remove cache entries matching all given tags * ($tags can be an array of strings or a single string) * 'notMatchingTag' => remove cache entries not matching one of the given tags * ($tags can be an array of strings or a single string) * 'matchingAnyTag' => remove cache entries matching any given tags * ($tags can be an array of strings or a single string) * * @param string $mode * @param array|string $tags * @throws Zend_Cache_Exception * @return boolean True if ok */ public function clean($mode = 'all', $tags = array()) { if (!$this->_options['caching']) { return true; } if (!in_array($mode, array(Zend_Cache::CLEANING_MODE_ALL, Zend_Cache::CLEANING_MODE_OLD, Zend_Cache::CLEANING_MODE_MATCHING_TAG, Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG, Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG))) { Zend_Cache::throwException('Invalid cleaning mode'); } self::_validateTagsArray($tags); return $this->_backend->clean($mode, $tags); } /** * Return an array of stored cache ids which match given tags * * In case of multiple tags, a logical AND is made between tags * * @param array $tags array of tags * @return array array of matching cache ids (string) */ public function getIdsMatchingTags($tags = array()) { if (!$this->_extendedBackend) { Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF); } if (!($this->_backendCapabilities['tags'])) { Zend_Cache::throwException(self::BACKEND_NOT_SUPPORTS_TAG); } $ids = $this->_backend->getIdsMatchingTags($tags); // we need to remove cache_id_prefix from ids (see #ZF-6178, #ZF-7600) if (isset($this->_options['cache_id_prefix']) && $this->_options['cache_id_prefix'] !== '') { $prefix = & $this->_options['cache_id_prefix']; $prefixLen = strlen($prefix); foreach ($ids as &$id) { if (strpos($id, $prefix) === 0) { $id = substr($id, $prefixLen); } } } return $ids; } /** * Return an array of stored cache ids which don't match given tags * * In case of multiple tags, a logical OR is made between tags * * @param array $tags array of tags * @return array array of not matching cache ids (string) */ public function getIdsNotMatchingTags($tags = array()) { if (!$this->_extendedBackend) { Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF); } if (!($this->_backendCapabilities['tags'])) { Zend_Cache::throwException(self::BACKEND_NOT_SUPPORTS_TAG); } $ids = $this->_backend->getIdsNotMatchingTags($tags); // we need to remove cache_id_prefix from ids (see #ZF-6178, #ZF-7600) if (isset($this->_options['cache_id_prefix']) && $this->_options['cache_id_prefix'] !== '') { $prefix = & $this->_options['cache_id_prefix']; $prefixLen = strlen($prefix); foreach ($ids as &$id) { if (strpos($id, $prefix) === 0) { $id = substr($id, $prefixLen); } } } return $ids; } /** * Return an array of stored cache ids which match any given tags * * In case of multiple tags, a logical OR is made between tags * * @param array $tags array of tags * @return array array of matching any cache ids (string) */ public function getIdsMatchingAnyTags($tags = array()) { if (!$this->_extendedBackend) { Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF); } if (!($this->_backendCapabilities['tags'])) { Zend_Cache::throwException(self::BACKEND_NOT_SUPPORTS_TAG); } $ids = $this->_backend->getIdsMatchingAnyTags($tags); // we need to remove cache_id_prefix from ids (see #ZF-6178, #ZF-7600) if (isset($this->_options['cache_id_prefix']) && $this->_options['cache_id_prefix'] !== '') { $prefix = & $this->_options['cache_id_prefix']; $prefixLen = strlen($prefix); foreach ($ids as &$id) { if (strpos($id, $prefix) === 0) { $id = substr($id, $prefixLen); } } } return $ids; } /** * Return an array of stored cache ids * * @return array array of stored cache ids (string) */ public function getIds() { if (!$this->_extendedBackend) { Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF); } $ids = $this->_backend->getIds(); // we need to remove cache_id_prefix from ids (see #ZF-6178, #ZF-7600) if (isset($this->_options['cache_id_prefix']) && $this->_options['cache_id_prefix'] !== '') { $prefix = & $this->_options['cache_id_prefix']; $prefixLen = strlen($prefix); foreach ($ids as &$id) { if (strpos($id, $prefix) === 0) { $id = substr($id, $prefixLen); } } } return $ids; } /** * Return an array of stored tags * * @return array array of stored tags (string) */ public function getTags() { if (!$this->_extendedBackend) { Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF); } if (!($this->_backendCapabilities['tags'])) { Zend_Cache::throwException(self::BACKEND_NOT_SUPPORTS_TAG); } return $this->_backend->getTags(); } /** * Return the filling percentage of the backend storage * * @return int integer between 0 and 100 */ public function getFillingPercentage() { if (!$this->_extendedBackend) { Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF); } return $this->_backend->getFillingPercentage(); } /** * Return an array of metadatas for the given cache id * * The array will include these keys : * - expire : the expire timestamp * - tags : a string array of tags * - mtime : timestamp of last modification time * * @param string $id cache id * @return array array of metadatas (false if the cache id is not found) */ public function getMetadatas($id) { if (!$this->_extendedBackend) { Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF); } $id = $this->_id($id); // cache id may need prefix return $this->_backend->getMetadatas($id); } /** * Give (if possible) an extra lifetime to the given cache id * * @param string $id cache id * @param int $extraLifetime * @return boolean true if ok */ public function touch($id, $extraLifetime) { if (!$this->_extendedBackend) { Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF); } $id = $this->_id($id); // cache id may need prefix return $this->_backend->touch($id, $extraLifetime); } /** * Validate a cache id or a tag (security, reliable filenames, reserved prefixes...) * * Throw an exception if a problem is found * * @param string $string Cache id or tag * @throws Zend_Cache_Exception * @return void */ protected static function _validateIdOrTag($string) { if (!is_string($string)) { Zend_Cache::throwException('Invalid id or tag : must be a string'); } if (substr($string, 0, 9) == 'internal-') { Zend_Cache::throwException('"internal-*" ids or tags are reserved'); } if (!preg_match('~^[a-zA-Z0-9_]+$~D', $string)) { Zend_Cache::throwException("Invalid id or tag '$string' : must use only [a-zA-Z0-9_]"); } } /** * Validate a tags array (security, reliable filenames, reserved prefixes...) * * Throw an exception if a problem is found * * @param array $tags Array of tags * @throws Zend_Cache_Exception * @return void */ protected static function _validateTagsArray($tags) { if (!is_array($tags)) { Zend_Cache::throwException('Invalid tags array : must be an array'); } foreach($tags as $tag) { self::_validateIdOrTag($tag); } reset($tags); } /** * Make sure if we enable logging that the Zend_Log class * is available. * Create a default log object if none is set. * * @throws Zend_Cache_Exception * @return void */ protected function _loggerSanity() { if (!isset($this->_options['logging']) || !$this->_options['logging']) { return; } if (isset($this->_options['logger']) && $this->_options['logger'] instanceof Zend_Log) { return; } // Create a default logger to the standard output stream // require_once 'Zend/Log/Writer/Stream.php'; // require_once 'Zend/Log.php'; $logger = new Zend_Log(new Zend_Log_Writer_Stream('php://output')); $this->_options['logger'] = $logger; } /** * Log a message at the WARN (4) priority. * * @param string $message * @throws Zend_Cache_Exception * @return void */ protected function _log($message, $priority = 4) { if (!$this->_options['logging']) { return; } if (!(isset($this->_options['logger']) || $this->_options['logger'] instanceof Zend_Log)) { Zend_Cache::throwException('Logging is enabled but logger is not set'); } $logger = $this->_options['logger']; $logger->log($message, $priority); } /** * Make and return a cache id * * Checks 'cache_id_prefix' and returns new id with prefix or simply the id if null * * @param string $id Cache id * @return string Cache id (with or without prefix) */ protected function _id($id) { if (($id !== null) && isset($this->_options['cache_id_prefix'])) { return $this->_options['cache_id_prefix'] . $id; // return with prefix } return $id; // no prefix, just return the $id passed } } vendor/Zend/Cache/Exception.php000064400000002060151742733540012424 0ustar00_tags = $tags; $this->_extension = $extension; ob_start(array($this, '_flush')); ob_implicit_flush(false); $this->_idStack[] = $id; return false; } /** * callback for output buffering * (shouldn't really be called manually) * * @param string $data Buffered output * @return string Data to send to browser */ public function _flush($data) { $id = array_pop($this->_idStack); if ($id === null) { Zend_Cache::throwException('use of _flush() without a start()'); } if ($this->_extension) { $this->save(serialize(array($data, $this->_extension)), $id, $this->_tags); } else { $this->save($data, $id, $this->_tags); } return $data; } } vendor/Zend/Cache/Frontend/Class.php000064400000016605151742733540013324 0ustar00 (mixed) cached_entity : * - if set to a class name, we will cache an abstract class and will use only static calls * - if set to an object, we will cache this object methods * * ====> (boolean) cache_by_default : * - if true, method calls will be cached by default * * ====> (array) cached_methods : * - an array of method names which will be cached (even if cache_by_default = false) * * ====> (array) non_cached_methods : * - an array of method names which won't be cached (even if cache_by_default = true) * * @var array available options */ protected $_specificOptions = array( 'cached_entity' => null, 'cache_by_default' => true, 'cached_methods' => array(), 'non_cached_methods' => array() ); /** * Tags array * * @var array */ private $_tags = array(); /** * SpecificLifetime value * * false => no specific life time * * @var int */ private $_specificLifetime = false; /** * The cached object or the name of the cached abstract class * * @var mixed */ private $_cachedEntity = null; /** * The class name of the cached object or cached abstract class * * Used to differentiate between different classes with the same method calls. * * @var string */ private $_cachedEntityLabel = ''; /** * Priority (used by some particular backends) * * @var int */ private $_priority = 8; /** * Constructor * * @param array $options Associative array of options * @throws Zend_Cache_Exception * @return void */ public function __construct(array $options = array()) { while (list($name, $value) = each($options)) { $this->setOption($name, $value); } if ($this->_specificOptions['cached_entity'] === null) { Zend_Cache::throwException('cached_entity must be set !'); } $this->setCachedEntity($this->_specificOptions['cached_entity']); $this->setOption('automatic_serialization', true); } /** * Set a specific life time * * @param int $specificLifetime * @return void */ public function setSpecificLifetime($specificLifetime = false) { $this->_specificLifetime = $specificLifetime; } /** * Set the priority (used by some particular backends) * * @param int $priority integer between 0 (very low priority) and 10 (maximum priority) */ public function setPriority($priority) { $this->_priority = $priority; } /** * Public frontend to set an option * * Just a wrapper to get a specific behaviour for cached_entity * * @param string $name Name of the option * @param mixed $value Value of the option * @throws Zend_Cache_Exception * @return void */ public function setOption($name, $value) { if ($name == 'cached_entity') { $this->setCachedEntity($value); } else { parent::setOption($name, $value); } } /** * Specific method to set the cachedEntity * * if set to a class name, we will cache an abstract class and will use only static calls * if set to an object, we will cache this object methods * * @param mixed $cachedEntity */ public function setCachedEntity($cachedEntity) { if (!is_string($cachedEntity) && !is_object($cachedEntity)) { Zend_Cache::throwException('cached_entity must be an object or a class name'); } $this->_cachedEntity = $cachedEntity; $this->_specificOptions['cached_entity'] = $cachedEntity; if (is_string($this->_cachedEntity)){ $this->_cachedEntityLabel = $this->_cachedEntity; } else { $ro = new ReflectionObject($this->_cachedEntity); $this->_cachedEntityLabel = $ro->getName(); } } /** * Set the cache array * * @param array $tags * @return void */ public function setTagsArray($tags = array()) { $this->_tags = $tags; } /** * Main method : call the specified method or get the result from cache * * @param string $name Method name * @param array $parameters Method parameters * @return mixed Result */ public function __call($name, $parameters) { $cacheBool1 = $this->_specificOptions['cache_by_default']; $cacheBool2 = in_array($name, $this->_specificOptions['cached_methods']); $cacheBool3 = in_array($name, $this->_specificOptions['non_cached_methods']); $cache = (($cacheBool1 || $cacheBool2) && (!$cacheBool3)); if (!$cache) { // We do not have not cache return call_user_func_array(array($this->_cachedEntity, $name), $parameters); } $id = $this->_makeId($name, $parameters); if ( ($rs = $this->load($id)) && isset($rs[0], $rs[1]) ) { // A cache is available $output = $rs[0]; $return = $rs[1]; } else { // A cache is not available (or not valid for this frontend) ob_start(); ob_implicit_flush(false); try { $return = call_user_func_array(array($this->_cachedEntity, $name), $parameters); $output = ob_get_clean(); $data = array($output, $return); $this->save($data, $id, $this->_tags, $this->_specificLifetime, $this->_priority); } catch (Exception $e) { ob_end_clean(); throw $e; } } echo $output; return $return; } /** * ZF-9970 * * @deprecated */ private function _makeId($name, $args) { return $this->makeId($name, $args); } /** * Make a cache id from the method name and parameters * * @param string $name Method name * @param array $args Method parameters * @return string Cache id */ public function makeId($name, array $args = array()) { return md5($this->_cachedEntityLabel . '__' . $name . '__' . serialize($args)); } } vendor/Zend/Cache/Frontend/File.php000064400000015255151742733540013136 0ustar00 (string) master_file : * - a complete path of the master file * - deprecated (see master_files) * * ====> (array) master_files : * - an array of complete path of master files * - this option has to be set ! * * ====> (string) master_files_mode : * - Zend_Cache_Frontend_File::MODE_AND or Zend_Cache_Frontend_File::MODE_OR * - if MODE_AND, then all master files have to be touched to get a cache invalidation * - if MODE_OR (default), then a single touched master file is enough to get a cache invalidation * * ====> (boolean) ignore_missing_master_files * - if set to true, missing master files are ignored silently * - if set to false (default), an exception is thrown if there is a missing master file * @var array available options */ protected $_specificOptions = array( 'master_file' => null, 'master_files' => null, 'master_files_mode' => 'OR', 'ignore_missing_master_files' => false ); /** * Master file mtimes * * Array of int * * @var array */ private $_masterFile_mtimes = null; /** * Constructor * * @param array $options Associative array of options * @throws Zend_Cache_Exception * @return void */ public function __construct(array $options = array()) { while (list($name, $value) = each($options)) { $this->setOption($name, $value); } if (!isset($this->_specificOptions['master_files'])) { Zend_Cache::throwException('master_files option must be set'); } } /** * Change the master_files option * * @param array $masterFiles the complete paths and name of the master files */ public function setMasterFiles(array $masterFiles) { $this->_specificOptions['master_file'] = null; // to keep a compatibility $this->_specificOptions['master_files'] = null; $this->_masterFile_mtimes = array(); clearstatcache(); $i = 0; foreach ($masterFiles as $masterFile) { $mtime = @filemtime($masterFile); if (!$this->_specificOptions['ignore_missing_master_files'] && !$mtime) { Zend_Cache::throwException('Unable to read master_file : ' . $masterFile); } $this->_masterFile_mtimes[$i] = $mtime; $this->_specificOptions['master_files'][$i] = $masterFile; if ($i === 0) { // to keep a compatibility $this->_specificOptions['master_files'] = $masterFile; } $i++; } } /** * Change the master_file option * * To keep the compatibility * * @deprecated * @param string $masterFile the complete path and name of the master file */ public function setMasterFile($masterFile) { $this->setMasterFiles(array($masterFile)); } /** * Public frontend to set an option * * Just a wrapper to get a specific behaviour for master_file * * @param string $name Name of the option * @param mixed $value Value of the option * @throws Zend_Cache_Exception * @return void */ public function setOption($name, $value) { if ($name == 'master_file') { $this->setMasterFile($value); } else if ($name == 'master_files') { $this->setMasterFiles($value); } else { parent::setOption($name, $value); } } /** * Test if a cache is available for the given id and (if yes) return it (false else) * * @param string $id Cache id * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested * @param boolean $doNotUnserialize Do not serialize (even if automatic_serialization is true) => for internal use * @return mixed|false Cached datas */ public function load($id, $doNotTestCacheValidity = false, $doNotUnserialize = false) { if (!$doNotTestCacheValidity) { if ($this->test($id)) { return parent::load($id, true, $doNotUnserialize); } return false; } return parent::load($id, true, $doNotUnserialize); } /** * Test if a cache is available for the given id * * @param string $id Cache id * @return int|false Last modified time of cache entry if it is available, false otherwise */ public function test($id) { $lastModified = parent::test($id); if ($lastModified) { if ($this->_specificOptions['master_files_mode'] == self::MODE_AND) { // MODE_AND foreach($this->_masterFile_mtimes as $masterFileMTime) { if ($masterFileMTime) { if ($lastModified > $masterFileMTime) { return $lastModified; } } } } else { // MODE_OR $res = true; foreach($this->_masterFile_mtimes as $masterFileMTime) { if ($masterFileMTime) { if ($lastModified <= $masterFileMTime) { return false; } } } return $lastModified; } } return false; } } vendor/Zend/Cache/Frontend/Function.php000064400000013722151742733540014041 0ustar00 (boolean) cache_by_default : * - if true, function calls will be cached by default * * ====> (array) cached_functions : * - an array of function names which will be cached (even if cache_by_default = false) * * ====> (array) non_cached_functions : * - an array of function names which won't be cached (even if cache_by_default = true) * * @var array options */ protected $_specificOptions = array( 'cache_by_default' => true, 'cached_functions' => array(), 'non_cached_functions' => array() ); /** * Constructor * * @param array $options Associative array of options * @return void */ public function __construct(array $options = array()) { while (list($name, $value) = each($options)) { $this->setOption($name, $value); } $this->setOption('automatic_serialization', true); } /** * Main method : call the specified function or get the result from cache * * @param callback $callback A valid callback * @param array $parameters Function parameters * @param array $tags Cache tags * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime) * @param int $priority integer between 0 (very low priority) and 10 (maximum priority) used by some particular backends * @return mixed Result */ public function call($callback, array $parameters = array(), $tags = array(), $specificLifetime = false, $priority = 8) { if (!is_callable($callback, true, $name)) { Zend_Cache::throwException('Invalid callback'); } $cacheBool1 = $this->_specificOptions['cache_by_default']; $cacheBool2 = in_array($name, $this->_specificOptions['cached_functions']); $cacheBool3 = in_array($name, $this->_specificOptions['non_cached_functions']); $cache = (($cacheBool1 || $cacheBool2) && (!$cacheBool3)); if (!$cache) { // Caching of this callback is disabled return call_user_func_array($callback, $parameters); } $id = $this->_makeId($callback, $parameters); if ( ($rs = $this->load($id)) && isset($rs[0], $rs[1])) { // A cache is available $output = $rs[0]; $return = $rs[1]; } else { // A cache is not available (or not valid for this frontend) ob_start(); ob_implicit_flush(false); $return = call_user_func_array($callback, $parameters); $output = ob_get_contents(); ob_end_clean(); $data = array($output, $return); $this->save($data, $id, $tags, $specificLifetime, $priority); } echo $output; return $return; } /** * ZF-9970 * * @deprecated */ private function _makeId($callback, array $args) { return $this->makeId($callback, $args); } /** * Make a cache id from the function name and parameters * * @param callback $callback A valid callback * @param array $args Function parameters * @throws Zend_Cache_Exception * @return string Cache id */ public function makeId($callback, array $args = array()) { if (!is_callable($callback, true, $name)) { Zend_Cache::throwException('Invalid callback'); } // functions, methods and classnames are case-insensitive $name = strtolower($name); // generate a unique id for object callbacks if (is_object($callback)) { // Closures & __invoke $object = $callback; } elseif (isset($callback[0])) { // array($object, 'method') $object = $callback[0]; } if (isset($object)) { try { $tmp = @serialize($callback); } catch (Exception $e) { Zend_Cache::throwException($e->getMessage()); } if (!$tmp) { $lastErr = error_get_last(); Zend_Cache::throwException("Can't serialize callback object to generate id: {$lastErr['message']}"); } $name.= '__' . $tmp; } // generate a unique id for arguments $argsStr = ''; if ($args) { try { $argsStr = @serialize(array_values($args)); } catch (Exception $e) { Zend_Cache::throwException($e->getMessage()); } if (!$argsStr) { $lastErr = error_get_last(); throw Zend_Cache::throwException("Can't serialize arguments to generate id: {$lastErr['message']}"); } } return md5($name . $argsStr); } } vendor/Zend/Cache/Frontend/Output.php000064400000006647151742733540013564 0ustar00_idStack = array(); } /** * Start the cache * * @param string $id Cache id * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested * @param boolean $echoData If set to true, datas are sent to the browser if the cache is hit (simpy returned else) * @return mixed True if the cache is hit (false else) with $echoData=true (default) ; string else (datas) */ public function start($id, $doNotTestCacheValidity = false, $echoData = true) { $data = $this->load($id, $doNotTestCacheValidity); if ($data !== false) { if ( $echoData ) { echo($data); return true; } else { return $data; } } ob_start(); ob_implicit_flush(false); $this->_idStack[] = $id; return false; } /** * Stop the cache * * @param array $tags Tags array * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime) * @param string $forcedDatas If not null, force written datas with this * @param boolean $echoData If set to true, datas are sent to the browser * @param int $priority integer between 0 (very low priority) and 10 (maximum priority) used by some particular backends * @return void */ public function end($tags = array(), $specificLifetime = false, $forcedDatas = null, $echoData = true, $priority = 8) { if ($forcedDatas === null) { $data = ob_get_contents(); ob_end_clean(); } else { $data =& $forcedDatas; } $id = array_pop($this->_idStack); if ($id === null) { Zend_Cache::throwException('use of end() without a start()'); } $this->save($data, $id, $tags, $specificLifetime, $priority); if ($echoData) { echo($data); } } } vendor/Zend/Cache/Frontend/Page.php000064400000033724151742733540013134 0ustar00 (boolean) http_conditional : * - if true, http conditional mode is on * WARNING : http_conditional OPTION IS NOT IMPLEMENTED FOR THE MOMENT (TODO) * * ====> (boolean) debug_header : * - if true, a debug text is added before each cached pages * * ====> (boolean) content_type_memorization : * - deprecated => use memorize_headers instead * - if the Content-Type header is sent after the cache was started, the * corresponding value can be memorized and replayed when the cache is hit * (if false (default), the frontend doesn't take care of Content-Type header) * * ====> (array) memorize_headers : * - an array of strings corresponding to some HTTP headers name. Listed headers * will be stored with cache datas and "replayed" when the cache is hit * * ====> (array) default_options : * - an associative array of default options : * - (boolean) cache : cache is on by default if true * - (boolean) cacheWithXXXVariables (XXXX = 'Get', 'Post', 'Session', 'Files' or 'Cookie') : * if true, cache is still on even if there are some variables in this superglobal array * if false, cache is off if there are some variables in this superglobal array * - (boolean) makeIdWithXXXVariables (XXXX = 'Get', 'Post', 'Session', 'Files' or 'Cookie') : * if true, we have to use the content of this superglobal array to make a cache id * if false, the cache id won't be dependent of the content of this superglobal array * - (int) specific_lifetime : cache specific lifetime * (false => global lifetime is used, null => infinite lifetime, * integer => this lifetime is used), this "lifetime" is probably only * usefull when used with "regexps" array * - (array) tags : array of tags (strings) * - (int) priority : integer between 0 (very low priority) and 10 (maximum priority) used by * some particular backends * * ====> (array) regexps : * - an associative array to set options only for some REQUEST_URI * - keys are (pcre) regexps * - values are associative array with specific options to set if the regexp matchs on $_SERVER['REQUEST_URI'] * (see default_options for the list of available options) * - if several regexps match the $_SERVER['REQUEST_URI'], only the last one will be used * * @var array options */ protected $_specificOptions = array( 'http_conditional' => false, 'debug_header' => false, 'content_type_memorization' => false, 'memorize_headers' => array(), 'default_options' => array( 'cache_with_get_variables' => false, 'cache_with_post_variables' => false, 'cache_with_session_variables' => false, 'cache_with_files_variables' => false, 'cache_with_cookie_variables' => false, 'make_id_with_get_variables' => true, 'make_id_with_post_variables' => true, 'make_id_with_session_variables' => true, 'make_id_with_files_variables' => true, 'make_id_with_cookie_variables' => true, 'cache' => true, 'specific_lifetime' => false, 'tags' => array(), 'priority' => null ), 'regexps' => array() ); /** * Internal array to store some options * * @var array associative array of options */ protected $_activeOptions = array(); /** * If true, the page won't be cached * * @var boolean */ protected $_cancel = false; /** * Constructor * * @param array $options Associative array of options * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested * @throws Zend_Cache_Exception * @return void */ public function __construct(array $options = array()) { while (list($name, $value) = each($options)) { $name = strtolower($name); switch ($name) { case 'regexps': $this->_setRegexps($value); break; case 'default_options': $this->_setDefaultOptions($value); break; case 'content_type_memorization': $this->_setContentTypeMemorization($value); break; default: $this->setOption($name, $value); } } if (isset($this->_specificOptions['http_conditional'])) { if ($this->_specificOptions['http_conditional']) { Zend_Cache::throwException('http_conditional is not implemented for the moment !'); } } $this->setOption('automatic_serialization', true); } /** * Specific setter for the 'default_options' option (with some additional tests) * * @param array $options Associative array * @throws Zend_Cache_Exception * @return void */ protected function _setDefaultOptions($options) { if (!is_array($options)) { Zend_Cache::throwException('default_options must be an array !'); } foreach ($options as $key=>$value) { if (!is_string($key)) { Zend_Cache::throwException("invalid option [$key] !"); } $key = strtolower($key); if (isset($this->_specificOptions['default_options'][$key])) { $this->_specificOptions['default_options'][$key] = $value; } } } /** * Set the deprecated contentTypeMemorization option * * @param boolean $value value * @return void * @deprecated */ protected function _setContentTypeMemorization($value) { $found = null; foreach ($this->_specificOptions['memorize_headers'] as $key => $value) { if (strtolower($value) == 'content-type') { $found = $key; } } if ($value) { if (!$found) { $this->_specificOptions['memorize_headers'][] = 'Content-Type'; } } else { if ($found) { unset($this->_specificOptions['memorize_headers'][$found]); } } } /** * Specific setter for the 'regexps' option (with some additional tests) * * @param array $options Associative array * @throws Zend_Cache_Exception * @return void */ protected function _setRegexps($regexps) { if (!is_array($regexps)) { Zend_Cache::throwException('regexps option must be an array !'); } foreach ($regexps as $regexp=>$conf) { if (!is_array($conf)) { Zend_Cache::throwException('regexps option must be an array of arrays !'); } $validKeys = array_keys($this->_specificOptions['default_options']); foreach ($conf as $key=>$value) { if (!is_string($key)) { Zend_Cache::throwException("unknown option [$key] !"); } $key = strtolower($key); if (!in_array($key, $validKeys)) { unset($regexps[$regexp][$key]); } } } $this->setOption('regexps', $regexps); } /** * Start the cache * * @param string $id (optional) A cache id (if you set a value here, maybe you have to use Output frontend instead) * @param boolean $doNotDie For unit testing only ! * @return boolean True if the cache is hit (false else) */ public function start($id = false, $doNotDie = false) { $this->_cancel = false; $lastMatchingRegexp = null; foreach ($this->_specificOptions['regexps'] as $regexp => $conf) { if (preg_match("`$regexp`", $_SERVER['REQUEST_URI'])) { $lastMatchingRegexp = $regexp; } } $this->_activeOptions = $this->_specificOptions['default_options']; if ($lastMatchingRegexp !== null) { $conf = $this->_specificOptions['regexps'][$lastMatchingRegexp]; foreach ($conf as $key=>$value) { $this->_activeOptions[$key] = $value; } } if (!($this->_activeOptions['cache'])) { return false; } if (!$id) { $id = $this->_makeId(); if (!$id) { return false; } } $array = $this->load($id); if ($array !== false) { $data = $array['data']; $headers = $array['headers']; if (!headers_sent()) { foreach ($headers as $key=>$headerCouple) { $name = $headerCouple[0]; $value = $headerCouple[1]; header("$name: $value"); } } if ($this->_specificOptions['debug_header']) { echo 'DEBUG HEADER : This is a cached page !'; } echo $data; if ($doNotDie) { return true; } die(); } ob_start(array($this, '_flush')); ob_implicit_flush(false); return false; } /** * Cancel the current caching process */ public function cancel() { $this->_cancel = true; } /** * callback for output buffering * (shouldn't really be called manually) * * @param string $data Buffered output * @return string Data to send to browser */ public function _flush($data) { if ($this->_cancel) { return $data; } $contentType = null; $storedHeaders = array(); $headersList = headers_list(); foreach($this->_specificOptions['memorize_headers'] as $key=>$headerName) { foreach ($headersList as $headerSent) { $tmp = explode(':', $headerSent); $headerSentName = trim(array_shift($tmp)); if (strtolower($headerName) == strtolower($headerSentName)) { $headerSentValue = trim(implode(':', $tmp)); $storedHeaders[] = array($headerSentName, $headerSentValue); } } } $array = array( 'data' => $data, 'headers' => $storedHeaders ); $this->save($array, null, $this->_activeOptions['tags'], $this->_activeOptions['specific_lifetime'], $this->_activeOptions['priority']); return $data; } /** * Make an id depending on REQUEST_URI and superglobal arrays (depending on options) * * @return mixed|false a cache id (string), false if the cache should have not to be used */ protected function _makeId() { $tmp = $_SERVER['REQUEST_URI']; $array = explode('?', $tmp, 2); $tmp = $array[0]; foreach (array('Get', 'Post', 'Session', 'Files', 'Cookie') as $arrayName) { $tmp2 = $this->_makePartialId($arrayName, $this->_activeOptions['cache_with_' . strtolower($arrayName) . '_variables'], $this->_activeOptions['make_id_with_' . strtolower($arrayName) . '_variables']); if ($tmp2===false) { return false; } $tmp = $tmp . $tmp2; } return md5($tmp); } /** * Make a partial id depending on options * * @param string $arrayName Superglobal array name * @param bool $bool1 If true, cache is still on even if there are some variables in the superglobal array * @param bool $bool2 If true, we have to use the content of the superglobal array to make a partial id * @return mixed|false Partial id (string) or false if the cache should have not to be used */ protected function _makePartialId($arrayName, $bool1, $bool2) { switch ($arrayName) { case 'Get': $var = $_GET; break; case 'Post': $var = $_POST; break; case 'Session': if (isset($_SESSION)) { $var = $_SESSION; } else { $var = null; } break; case 'Cookie': if (isset($_COOKIE)) { $var = $_COOKIE; } else { $var = null; } break; case 'Files': $var = $_FILES; break; default: return false; } if ($bool1) { if ($bool2) { return serialize($var); } return ''; } if (count($var) > 0) { return false; } return ''; } } vendor/Zend/Cache/Manager.php000064400000022602151742733540012044 0ustar00 array( 'frontend' => array( 'name' => 'Core', 'options' => array( 'automatic_serialization' => true, ), ), 'backend' => array( 'name' => 'File', 'options' => array( // use system temp dir by default of file backend // 'cache_dir' => '../cache', ), ), ), // Static Page HTML Cache 'page' => array( 'frontend' => array( 'name' => 'Capture', 'options' => array( 'ignore_user_abort' => true, ), ), 'backend' => array( 'name' => 'Static', 'options' => array( 'public_dir' => '../public', ), ), ), // Tag Cache 'pagetag' => array( 'frontend' => array( 'name' => 'Core', 'options' => array( 'automatic_serialization' => true, 'lifetime' => null ), ), 'backend' => array( 'name' => 'File', 'options' => array( // use system temp dir by default of file backend // 'cache_dir' => '../cache', // use default umask of file backend // 'cache_file_umask' => 0644 ), ), ), ); /** * Set a new cache for the Cache Manager to contain * * @param string $name * @param Zend_Cache_Core $cache * @return Zend_Cache_Manager */ public function setCache($name, Zend_Cache_Core $cache) { $this->_caches[$name] = $cache; return $this; } /** * Check if the Cache Manager contains the named cache object, or a named * configuration template to lazy load the cache object * * @param string $name * @return bool */ public function hasCache($name) { if (isset($this->_caches[$name]) || $this->hasCacheTemplate($name) ) { return true; } return false; } /** * Fetch the named cache object, or instantiate and return a cache object * using a named configuration template * * @param string $name * @return Zend_Cache_Core */ public function getCache($name) { if (isset($this->_caches[$name])) { return $this->_caches[$name]; } if (isset($this->_optionTemplates[$name])) { if ($name == self::PAGECACHE && (!isset($this->_optionTemplates[$name]['backend']['options']['tag_cache']) || !$this->_optionTemplates[$name]['backend']['options']['tag_cache'] instanceof Zend_Cache_Core) ) { $this->_optionTemplates[$name]['backend']['options']['tag_cache'] = $this->getCache(self::PAGETAGCACHE); } $this->_caches[$name] = Zend_Cache::factory( $this->_optionTemplates[$name]['frontend']['name'], $this->_optionTemplates[$name]['backend']['name'], isset($this->_optionTemplates[$name]['frontend']['options']) ? $this->_optionTemplates[$name]['frontend']['options'] : array(), isset($this->_optionTemplates[$name]['backend']['options']) ? $this->_optionTemplates[$name]['backend']['options'] : array(), isset($this->_optionTemplates[$name]['frontend']['customFrontendNaming']) ? $this->_optionTemplates[$name]['frontend']['customFrontendNaming'] : false, isset($this->_optionTemplates[$name]['backend']['customBackendNaming']) ? $this->_optionTemplates[$name]['backend']['customBackendNaming'] : false, isset($this->_optionTemplates[$name]['frontendBackendAutoload']) ? $this->_optionTemplates[$name]['frontendBackendAutoload'] : false ); return $this->_caches[$name]; } } /** * Fetch all available caches * * @return array An array of all available caches with it's names as key */ public function getCaches() { $caches = $this->_caches; foreach ($this->_optionTemplates as $name => $tmp) { if (!isset($caches[$name])) { $caches[$name] = $this->getCache($name); } } return $caches; } /** * Set a named configuration template from which a cache object can later * be lazy loaded * * @param string $name * @param array $options * @return Zend_Cache_Manager */ public function setCacheTemplate($name, $options) { if ($options instanceof Zend_Config) { $options = $options->toArray(); } elseif (!is_array($options)) { // require_once 'Zend/Cache/Exception.php'; throw new Zend_Cache_Exception('Options passed must be in' . ' an associative array or instance of Zend_Config'); } $this->_optionTemplates[$name] = $options; return $this; } /** * Check if the named configuration template * * @param string $name * @return bool */ public function hasCacheTemplate($name) { if (isset($this->_optionTemplates[$name])) { return true; } return false; } /** * Get the named configuration template * * @param string $name * @return array */ public function getCacheTemplate($name) { if (isset($this->_optionTemplates[$name])) { return $this->_optionTemplates[$name]; } } /** * Pass an array containing changes to be applied to a named * configuration * template * * @param string $name * @param array $options * @return Zend_Cache_Manager * @throws Zend_Cache_Exception for invalid options format or if option templates do not have $name */ public function setTemplateOptions($name, $options) { if ($options instanceof Zend_Config) { $options = $options->toArray(); } elseif (!is_array($options)) { // require_once 'Zend/Cache/Exception.php'; throw new Zend_Cache_Exception('Options passed must be in' . ' an associative array or instance of Zend_Config'); } if (!isset($this->_optionTemplates[$name])) { throw new Zend_Cache_Exception('A cache configuration template' . 'does not exist with the name "' . $name . '"'); } $this->_optionTemplates[$name] = $this->_mergeOptions($this->_optionTemplates[$name], $options); return $this; } /** * Simple method to merge two configuration arrays * * @param array $current * @param array $options * @return array */ protected function _mergeOptions(array $current, array $options) { if (isset($options['frontend']['name'])) { $current['frontend']['name'] = $options['frontend']['name']; } if (isset($options['backend']['name'])) { $current['backend']['name'] = $options['backend']['name']; } if (isset($options['frontend']['options'])) { foreach ($options['frontend']['options'] as $key=>$value) { $current['frontend']['options'][$key] = $value; } } if (isset($options['backend']['options'])) { foreach ($options['backend']['options'] as $key=>$value) { $current['backend']['options'][$key] = $value; } } return $current; } } vendor/Zend/Exception.php000064400000004774151742733540011437 0ustar00_previous = $previous; } else { parent::__construct($msg, (int) $code, $previous); } } /** * Overloading * * For PHP < 5.3.0, provides access to the getPrevious() method. * * @param string $method * @param array $args * @return mixed */ public function __call($method, array $args) { if ('getprevious' == strtolower($method)) { return $this->_getPrevious(); } return null; } /** * String representation of the exception * * @return string */ public function __toString() { if (version_compare(PHP_VERSION, '5.3.0', '<')) { if (null !== ($e = $this->getPrevious())) { return $e->__toString() . "\n\nNext " . parent::__toString(); } } return parent::__toString(); } /** * Returns previous Exception * * @return Exception|null */ protected function _getPrevious() { return $this->_previous; } } vendor/Zend/Log.php000064400000044005151742733540010211 0ustar00_priorities = array_flip($r->getConstants()); if ($writer !== null) { $this->addWriter($writer); } } /** * Factory to construct the logger and one or more writers * based on the configuration array * * @param array|Zend_Config Array or instance of Zend_Config * @return Zend_Log * @throws Zend_Log_Exception */ static public function factory($config = array()) { if ($config instanceof Zend_Config) { $config = $config->toArray(); } if (!is_array($config) || empty($config)) { /** @see Zend_Log_Exception */ // require_once 'Zend/Log/Exception.php'; throw new Zend_Log_Exception('Configuration must be an array or instance of Zend_Config'); } $log = new self; if (!is_array(current($config))) { $log->addWriter(current($config)); } else { foreach($config as $writer) { $log->addWriter($writer); } } return $log; } /** * Construct a writer object based on a configuration array * * @param array $spec config array with writer spec * @return Zend_Log_Writer_Abstract * @throws Zend_Log_Exception */ protected function _constructWriterFromConfig($config) { $writer = $this->_constructFromConfig('writer', $config, $this->_defaultWriterNamespace); if (!$writer instanceof Zend_Log_Writer_Abstract) { $writerName = is_object($writer) ? get_class($writer) : 'The specified writer'; /** @see Zend_Log_Exception */ // require_once 'Zend/Log/Exception.php'; throw new Zend_Log_Exception("{$writerName} does not extend Zend_Log_Writer_Abstract!"); } if (isset($config['filterName'])) { $filter = $this->_constructFilterFromConfig($config); $writer->addFilter($filter); } return $writer; } /** * Construct filter object from configuration array or Zend_Config object * * @param array|Zend_Config $config Zend_Config or Array * @return Zend_Log_Filter_Interface * @throws Zend_Log_Exception */ protected function _constructFilterFromConfig($config) { $filter = $this->_constructFromConfig('filter', $config, $this->_defaultFilterNamespace); if (!$filter instanceof Zend_Log_Filter_Interface) { $filterName = is_object($filter) ? get_class($filter) : 'The specified filter'; /** @see Zend_Log_Exception */ // require_once 'Zend/Log/Exception.php'; throw new Zend_Log_Exception("{$filterName} does not implement Zend_Log_Filter_Interface"); } return $filter; } /** * Construct a filter or writer from config * * @param string $type 'writer' of 'filter' * @param mixed $config Zend_Config or Array * @param string $namespace * @return object * @throws Zend_Log_Exception */ protected function _constructFromConfig($type, $config, $namespace) { if ($config instanceof Zend_Config) { $config = $config->toArray(); } if (!is_array($config) || empty($config)) { // require_once 'Zend/Log/Exception.php'; throw new Zend_Log_Exception( 'Configuration must be an array or instance of Zend_Config' ); } $params = isset($config[ $type .'Params' ]) ? $config[ $type .'Params' ] : array(); $className = $this->getClassName($config, $type, $namespace); if (!class_exists($className)) { // require_once 'Zend/Loader.php'; Zend_Loader::loadClass($className); } $reflection = new ReflectionClass($className); if (!$reflection->implementsInterface('Zend_Log_FactoryInterface')) { // require_once 'Zend/Log/Exception.php'; throw new Zend_Log_Exception( 'Driver does not implement Zend_Log_FactoryInterface and can not be constructed from config.' ); } return call_user_func(array($className, 'factory'), $params); } /** * Get the writer or filter full classname * * @param array $config * @param string $type filter|writer * @param string $defaultNamespace * @return string full classname * @throws Zend_Log_Exception */ protected function getClassName($config, $type, $defaultNamespace) { if (!isset($config[ $type . 'Name' ])) { // require_once 'Zend/Log/Exception.php'; throw new Zend_Log_Exception("Specify {$type}Name in the configuration array"); } $className = $config[ $type . 'Name' ]; $namespace = $defaultNamespace; if (isset($config[ $type . 'Namespace' ])) { $namespace = $config[ $type . 'Namespace' ]; } $fullClassName = $namespace . '_' . $className; return $fullClassName; } /** * Packs message and priority into Event array * * @param string $message Message to log * @param integer $priority Priority of message * @return array Event array */ protected function _packEvent($message, $priority) { return array_merge(array( 'timestamp' => date($this->_timestampFormat), 'message' => $message, 'priority' => $priority, 'priorityName' => $this->_priorities[$priority] ), $this->_extras ); } /** * Class destructor. Shutdown log writers * * @return void */ public function __destruct() { foreach($this->_writers as $writer) { $writer->shutdown(); } } /** * Undefined method handler allows a shortcut: * $log->priorityName('message') * instead of * $log->log('message', Zend_Log::PRIORITY_NAME) * * @param string $method priority name * @param string $params message to log * @return void * @throws Zend_Log_Exception */ public function __call($method, $params) { $priority = strtoupper($method); if (($priority = array_search($priority, $this->_priorities)) !== false) { switch (count($params)) { case 0: /** @see Zend_Log_Exception */ // require_once 'Zend/Log/Exception.php'; throw new Zend_Log_Exception('Missing log message'); case 1: $message = array_shift($params); $extras = null; break; default: $message = array_shift($params); $extras = array_shift($params); break; } $this->log($message, $priority, $extras); } else { /** @see Zend_Log_Exception */ // require_once 'Zend/Log/Exception.php'; throw new Zend_Log_Exception('Bad log priority'); } } /** * Log a message at a priority * * @param string $message Message to log * @param integer $priority Priority of message * @param mixed $extras Extra information to log in event * @return void * @throws Zend_Log_Exception */ public function log($message, $priority, $extras = null) { // sanity checks if (empty($this->_writers)) { /** @see Zend_Log_Exception */ // require_once 'Zend/Log/Exception.php'; throw new Zend_Log_Exception('No writers were added'); } if (! isset($this->_priorities[$priority])) { /** @see Zend_Log_Exception */ // require_once 'Zend/Log/Exception.php'; throw new Zend_Log_Exception('Bad log priority'); } // pack into event required by filters and writers $event = $this->_packEvent($message, $priority); // Check to see if any extra information was passed if (!empty($extras)) { $info = array(); if (is_array($extras)) { foreach ($extras as $key => $value) { if (is_string($key)) { $event[$key] = $value; } else { $info[] = $value; } } } else { $info = $extras; } if (!empty($info)) { $event['info'] = $info; } } // abort if rejected by the global filters foreach ($this->_filters as $filter) { if (! $filter->accept($event)) { return; } } // send to each writer foreach ($this->_writers as $writer) { $writer->write($event); } } /** * Add a custom priority * * @param string $name Name of priority * @param integer $priority Numeric priority * @throws Zend_Log_Exception */ public function addPriority($name, $priority) { // Priority names must be uppercase for predictability. $name = strtoupper($name); if (isset($this->_priorities[$priority]) || false !== array_search($name, $this->_priorities)) { /** @see Zend_Log_Exception */ // require_once 'Zend/Log/Exception.php'; throw new Zend_Log_Exception('Existing priorities cannot be overwritten'); } $this->_priorities[$priority] = $name; return $this; } /** * Add a filter that will be applied before all log writers. * Before a message will be received by any of the writers, it * must be accepted by all filters added with this method. * * @param int|Zend_Config|array|Zend_Log_Filter_Interface $filter * @return Zend_Log * @throws Zend_Log_Exception */ public function addFilter($filter) { if (is_int($filter)) { /** @see Zend_Log_Filter_Priority */ // require_once 'Zend/Log/Filter/Priority.php'; $filter = new Zend_Log_Filter_Priority($filter); } elseif ($filter instanceof Zend_Config || is_array($filter)) { $filter = $this->_constructFilterFromConfig($filter); } elseif(! $filter instanceof Zend_Log_Filter_Interface) { /** @see Zend_Log_Exception */ // require_once 'Zend/Log/Exception.php'; throw new Zend_Log_Exception('Invalid filter provided'); } $this->_filters[] = $filter; return $this; } /** * Add a writer. A writer is responsible for taking a log * message and writing it out to storage. * * @param mixed $writer Zend_Log_Writer_Abstract or Config array * @return Zend_Log */ public function addWriter($writer) { if (is_array($writer) || $writer instanceof Zend_Config) { $writer = $this->_constructWriterFromConfig($writer); } if (!$writer instanceof Zend_Log_Writer_Abstract) { /** @see Zend_Log_Exception */ // require_once 'Zend/Log/Exception.php'; throw new Zend_Log_Exception( 'Writer must be an instance of Zend_Log_Writer_Abstract' . ' or you should pass a configuration array' ); } $this->_writers[] = $writer; return $this; } /** * Set an extra item to pass to the log writers. * * @param $name Name of the field * @param $value Value of the field * @return Zend_Log */ public function setEventItem($name, $value) { $this->_extras = array_merge($this->_extras, array($name => $value)); return $this; } /** * Register Logging system as an error handler to log php errors * Note: it still calls the original error handler if set_error_handler is able to return it. * * Errors will be mapped as: * E_NOTICE, E_USER_NOTICE => NOTICE * E_WARNING, E_CORE_WARNING, E_USER_WARNING => WARN * E_ERROR, E_USER_ERROR, E_CORE_ERROR, E_RECOVERABLE_ERROR => ERR * E_DEPRECATED, E_STRICT, E_USER_DEPRECATED => DEBUG * (unknown/other) => INFO * * @link http://www.php.net/manual/en/function.set-error-handler.php Custom error handler * * @return Zend_Log */ public function registerErrorHandler() { // Only register once. Avoids loop issues if it gets registered twice. if ($this->_registeredErrorHandler) { return $this; } $this->_origErrorHandler = set_error_handler(array($this, 'errorHandler')); // Contruct a default map of phpErrors to Zend_Log priorities. // Some of the errors are uncatchable, but are included for completeness $this->_errorHandlerMap = array( E_NOTICE => Zend_Log::NOTICE, E_USER_NOTICE => Zend_Log::NOTICE, E_WARNING => Zend_Log::WARN, E_CORE_WARNING => Zend_Log::WARN, E_USER_WARNING => Zend_Log::WARN, E_ERROR => Zend_Log::ERR, E_USER_ERROR => Zend_Log::ERR, E_CORE_ERROR => Zend_Log::ERR, E_RECOVERABLE_ERROR => Zend_Log::ERR, E_STRICT => Zend_Log::DEBUG, ); // PHP 5.3.0+ if (defined('E_DEPRECATED')) { $this->_errorHandlerMap['E_DEPRECATED'] = Zend_Log::DEBUG; } if (defined('E_USER_DEPRECATED')) { $this->_errorHandlerMap['E_USER_DEPRECATED'] = Zend_Log::DEBUG; } $this->_registeredErrorHandler = true; return $this; } /** * Error Handler will convert error into log message, and then call the original error handler * * @link http://www.php.net/manual/en/function.set-error-handler.php Custom error handler * @param int $errno * @param string $errstr * @param string $errfile * @param int $errline * @param array $errcontext * @return boolean */ public function errorHandler($errno, $errstr, $errfile, $errline, $errcontext) { $errorLevel = error_reporting(); if ($errorLevel && $errno) { if (isset($this->_errorHandlerMap[$errno])) { $priority = $this->_errorHandlerMap[$errno]; } else { $priority = Zend_Log::INFO; } $this->log($errstr, $priority, array('errno'=>$errno, 'file'=>$errfile, 'line'=>$errline, 'context'=>$errcontext)); } if ($this->_origErrorHandler !== null) { return call_user_func($this->_origErrorHandler, $errno, $errstr, $errfile, $errline, $errcontext); } return false; } /** * Set timestamp format for log entries. * * @param string $format * @return Zend_Log */ public function setTimestampFormat($format) { $this->_timestampFormat = $format; return $this; } /** * Get timestamp format used for log entries. * * @return string */ public function getTimestampFormat() { return $this->_timestampFormat; } } vendor/Zend/Log/Exception.php000064400000002174151742733540012150 0ustar00toArray(); } if (!is_array($config)) { // require_once 'Zend/Log/Exception.php'; throw new Zend_Log_Exception('Configuration must be an array or instance of Zend_Config'); } return $config; } } vendor/Zend/Log/Filter/Interface.php000064400000002462151742733540013337 0ustar00_regexp = $regexp; } /** * Create a new instance of Zend_Log_Filter_Message * * @param array|Zend_Config $config * @return Zend_Log_Filter_Message */ static public function factory($config) { $config = self::_parseConfig($config); $config = array_merge(array( 'regexp' => null ), $config); return new self( $config['regexp'] ); } /** * Returns TRUE to accept the message, FALSE to block it. * * @param array $event event data * @return boolean accepted? */ public function accept($event) { return preg_match($this->_regexp, $event['message']) > 0; } } vendor/Zend/Log/Filter/Priority.php000064400000005744151742733540013266 0ustar00_priority = $priority; $this->_operator = $operator === null ? '<=' : $operator; } /** * Create a new instance of Zend_Log_Filter_Priority * * @param array|Zend_Config $config * @return Zend_Log_Filter_Priority */ static public function factory($config) { $config = self::_parseConfig($config); $config = array_merge(array( 'priority' => null, 'operator' => null, ), $config); // Add support for constants if (!is_numeric($config['priority']) && isset($config['priority']) && defined($config['priority'])) { $config['priority'] = constant($config['priority']); } return new self( (int) $config['priority'], $config['operator'] ); } /** * Returns TRUE to accept the message, FALSE to block it. * * @param array $event event data * @return boolean accepted? */ public function accept($event) { return version_compare($event['priority'], $this->_priority, $this->_operator); } } vendor/Zend/Log/Filter/Suppress.php000064400000004224151742733540013261 0ustar00_accept = (! $suppress); } /** * Returns TRUE to accept the message, FALSE to block it. * * @param array $event event data * @return boolean accepted? */ public function accept($event) { return $this->_accept; } /** * Create a new instance of Zend_Log_Filter_Suppress * * @param array|Zend_Config $config * @return Zend_Log_Filter_Suppress * @throws Zend_Log_Exception */ static public function factory($config) { return new self(); } } vendor/Zend/Log/Formatter/Firebug.php000064400000003236151742733540013540 0ustar00_format = $format; } /** * Formats data into a single line to be written by the writer. * * @param array $event event data * @return string formatted line to write to the log */ public function format($event) { $output = $this->_format; foreach ($event as $name => $value) { if ((is_object($value) && !method_exists($value,'__toString')) || is_array($value)) { $value = gettype($value); } $output = str_replace("%$name%", $value, $output); } return $output; } } vendor/Zend/Log/Formatter/Xml.php000064400000006643151742733540012722 0ustar00_rootElement = $rootElement; $this->_elementMap = $elementMap; $this->setEncoding($encoding); } /** * Get encoding * * @return string */ public function getEncoding() { return $this->_encoding; } /** * Set encoding * * @param string $value * @return Zend_Log_Formatter_Xml */ public function setEncoding($value) { $this->_encoding = (string) $value; return $this; } /** * Formats data into a single line to be written by the writer. * * @param array $event event data * @return string formatted line to write to the log */ public function format($event) { if ($this->_elementMap === null) { $dataToInsert = $event; } else { $dataToInsert = array(); foreach ($this->_elementMap as $elementName => $fieldKey) { $dataToInsert[$elementName] = $event[$fieldKey]; } } $enc = $this->getEncoding(); $dom = new DOMDocument('1.0', $enc); $elt = $dom->appendChild(new DOMElement($this->_rootElement)); foreach ($dataToInsert as $key => $value) { if($key == "message") { $value = htmlspecialchars($value, ENT_COMPAT, $enc); } $elt->appendChild(new DOMElement($key, $value)); } $xml = $dom->saveXML(); $xml = preg_replace('/<\?xml version="1.0"( encoding="[^\"]*")?\?>\n/u', '', $xml); return $xml . PHP_EOL; } } vendor/Zend/Log/Writer/Abstract.php000064400000007176151742733540013240 0ustar00_filters[] = $filter; return $this; } /** * Log a message to this writer. * * @param array $event log data event * @return void */ public function write($event) { foreach ($this->_filters as $filter) { if (! $filter->accept($event)) { return; } } // exception occurs on error $this->_write($event); } /** * Set a new formatter for this writer * * @param Zend_Log_Formatter_Interface $formatter * @return Zend_Log_Writer_Abstract */ public function setFormatter(Zend_Log_Formatter_Interface $formatter) { $this->_formatter = $formatter; return $this; } /** * Perform shutdown activites such as closing open resources * * @return void */ public function shutdown() {} /** * Write a message to the log. * * @param array $event log data event * @return void */ abstract protected function _write($event); /** * Validate and optionally convert the config to array * * @param array|Zend_Config $config Zend_Config or Array * @return array * @throws Zend_Log_Exception */ static protected function _parseConfig($config) { if ($config instanceof Zend_Config) { $config = $config->toArray(); } if (!is_array($config)) { // require_once 'Zend/Log/Exception.php'; throw new Zend_Log_Exception( 'Configuration must be an array or instance of Zend_Config' ); } return $config; } } vendor/Zend/Log/Writer/Db.php000064400000007364151742733540012021 0ustar00_db = $db; $this->_table = $table; $this->_columnMap = $columnMap; } /** * Create a new instance of Zend_Log_Writer_Db * * @param array|Zend_Config $config * @return Zend_Log_Writer_Db * @throws Zend_Log_Exception */ static public function factory($config) { $config = self::_parseConfig($config); $config = array_merge(array( 'db' => null, 'table' => null, 'columnMap' => null, ), $config); if (isset($config['columnmap'])) { $config['columnMap'] = $config['columnmap']; } return new self( $config['db'], $config['table'], $config['columnMap'] ); } /** * Formatting is not possible on this writer * * @return void * @throws Zend_Log_Exception */ public function setFormatter(Zend_Log_Formatter_Interface $formatter) { // require_once 'Zend/Log/Exception.php'; throw new Zend_Log_Exception(get_class($this) . ' does not support formatting'); } /** * Remove reference to database adapter * * @return void */ public function shutdown() { $this->_db = null; } /** * Write a message to the log. * * @param array $event event data * @return void */ protected function _write($event) { if ($this->_db === null) { // require_once 'Zend/Log/Exception.php'; throw new Zend_Log_Exception('Database adapter is null'); } if ($this->_columnMap === null) { $dataToInsert = $event; } else { $dataToInsert = array(); foreach ($this->_columnMap as $columnName => $fieldKey) { $dataToInsert[$columnName] = $event[$fieldKey]; } } $this->_db->insert($this->_table, $dataToInsert); } } vendor/Zend/Log/Writer/Firebug.php000064400000014223151742733540013047 0ustar00 Zend_Wildfire_Plugin_FirePhp::ERROR, Zend_Log::ALERT => Zend_Wildfire_Plugin_FirePhp::ERROR, Zend_Log::CRIT => Zend_Wildfire_Plugin_FirePhp::ERROR, Zend_Log::ERR => Zend_Wildfire_Plugin_FirePhp::ERROR, Zend_Log::WARN => Zend_Wildfire_Plugin_FirePhp::WARN, Zend_Log::NOTICE => Zend_Wildfire_Plugin_FirePhp::INFO, Zend_Log::INFO => Zend_Wildfire_Plugin_FirePhp::INFO, Zend_Log::DEBUG => Zend_Wildfire_Plugin_FirePhp::LOG); /** * The default logging style for un-mapped priorities * * @var string */ protected $_defaultPriorityStyle = Zend_Wildfire_Plugin_FirePhp::LOG; /** * Flag indicating whether the log writer is enabled * * @var boolean */ protected $_enabled = true; /** * Class constructor * * @return void */ public function __construct() { if (php_sapi_name() == 'cli') { $this->setEnabled(false); } $this->_formatter = new Zend_Log_Formatter_Firebug(); } /** * Create a new instance of Zend_Log_Writer_Firebug * * @param array|Zend_Config $config * @return Zend_Log_Writer_Firebug */ static public function factory($config) { return new self(); } /** * Enable or disable the log writer. * * @param boolean $enabled Set to TRUE to enable the log writer * @return boolean The previous value. */ public function setEnabled($enabled) { $previous = $this->_enabled; $this->_enabled = $enabled; return $previous; } /** * Determine if the log writer is enabled. * * @return boolean Returns TRUE if the log writer is enabled. */ public function getEnabled() { return $this->_enabled; } /** * Set the default display style for user-defined priorities * * @param string $style The default log display style * @return string Returns previous default log display style */ public function setDefaultPriorityStyle($style) { $previous = $this->_defaultPriorityStyle; $this->_defaultPriorityStyle = $style; return $previous; } /** * Get the default display style for user-defined priorities * * @return string Returns the default log display style */ public function getDefaultPriorityStyle() { return $this->_defaultPriorityStyle; } /** * Set a display style for a logging priority * * @param int $priority The logging priority * @param string $style The logging display style * @return string|boolean The previous logging display style if defined or TRUE otherwise */ public function setPriorityStyle($priority, $style) { $previous = true; if (array_key_exists($priority,$this->_priorityStyles)) { $previous = $this->_priorityStyles[$priority]; } $this->_priorityStyles[$priority] = $style; return $previous; } /** * Get a display style for a logging priority * * @param int $priority The logging priority * @return string|boolean The logging display style if defined or FALSE otherwise */ public function getPriorityStyle($priority) { if (array_key_exists($priority,$this->_priorityStyles)) { return $this->_priorityStyles[$priority]; } return false; } /** * Log a message to the Firebug Console. * * @param array $event The event data * @return void */ protected function _write($event) { if (!$this->getEnabled()) { return; } if (array_key_exists($event['priority'],$this->_priorityStyles)) { $type = $this->_priorityStyles[$event['priority']]; } else { $type = $this->_defaultPriorityStyle; } $message = $this->_formatter->format($event); $label = isset($event['firebugLabel'])?$event['firebugLabel']:null; Zend_Wildfire_Plugin_FirePhp::getInstance()->send($message, $label, $type, array('traceOffset'=>4, 'fixZendLogOffsetIfApplicable'=>true)); } } vendor/Zend/Log/Writer/Mail.php000064400000032722151742733540012352 0ustar00 'setFrom', 'to' => 'addTo', 'cc' => 'addCc', 'bcc' => 'addBcc', ); /** * Class constructor. * * Constructs the mail writer; requires a Zend_Mail instance, and takes an * optional Zend_Layout instance. If Zend_Layout is being used, * $this->_layout->events will be set for use in the layout template. * * @param Zend_Mail $mail Mail instance * @param Zend_Layout $layout Layout instance; optional * @return void */ public function __construct(Zend_Mail $mail, Zend_Layout $layout = null) { $this->_mail = $mail; if (null !== $layout) { $this->setLayout($layout); } $this->_formatter = new Zend_Log_Formatter_Simple(); } /** * Create a new instance of Zend_Log_Writer_Mail * * @param array|Zend_Config $config * @return Zend_Log_Writer_Mail */ static public function factory($config) { $config = self::_parseConfig($config); $mail = self::_constructMailFromConfig($config); $writer = new self($mail); if (isset($config['layout']) || isset($config['layoutOptions'])) { $writer->setLayout($config); } if (isset($config['layoutFormatter'])) { $layoutFormatter = new $config['layoutFormatter']; $writer->setLayoutFormatter($layoutFormatter); } if (isset($config['subjectPrependText'])) { $writer->setSubjectPrependText($config['subjectPrependText']); } return $writer; } /** * Set the layout * * @param Zend_Layout|array $layout * @return Zend_Log_Writer_Mail * @throws Zend_Log_Exception */ public function setLayout($layout) { if (is_array($layout)) { $layout = $this->_constructLayoutFromConfig($layout); } if (!$layout instanceof Zend_Layout) { // require_once 'Zend/Log/Exception.php'; throw new Zend_Log_Exception('Mail must be an instance of Zend_Layout or an array'); } $this->_layout = $layout; return $this; } /** * Construct a Zend_Mail instance based on a configuration array * * @param array $config * @return Zend_Mail * @throws Zend_Log_Exception */ protected static function _constructMailFromConfig(array $config) { $mailClass = 'Zend_Mail'; if (isset($config['mail'])) { $mailClass = $config['mail']; } if (!array_key_exists('charset', $config)) { $config['charset'] = null; } $mail = new $mailClass($config['charset']); if (!$mail instanceof Zend_Mail) { throw new Zend_Log_Exception($mail . 'must extend Zend_Mail'); } if (isset($config['subject'])) { $mail->setSubject($config['subject']); } $headerAddresses = array_intersect_key($config, self::$_methodMapHeaders); if (count($headerAddresses)) { foreach ($headerAddresses as $header => $address) { $method = self::$_methodMapHeaders[$header]; if (is_array($address) && isset($address['name']) && !is_numeric($address['name']) ) { $params = array( $address['email'], $address['name'] ); } else if (is_array($address) && isset($address['email'])) { $params = array($address['email']); } else { $params = array($address); } call_user_func_array(array($mail, $method), $params); } } return $mail; } /** * Construct a Zend_Layout instance based on a configuration array * * @param array $config * @return Zend_Layout * @throws Zend_Log_Exception */ protected function _constructLayoutFromConfig(array $config) { $config = array_merge(array( 'layout' => 'Zend_Layout', 'layoutOptions' => null ), $config); $layoutClass = $config['layout']; $layout = new $layoutClass($config['layoutOptions']); if (!$layout instanceof Zend_Layout) { throw new Zend_Log_Exception($layout . 'must extend Zend_Layout'); } return $layout; } /** * Places event line into array of lines to be used as message body. * * Handles the formatting of both plaintext entries, as well as those * rendered with Zend_Layout. * * @param array $event Event data * @return void */ protected function _write($event) { // Track the number of entries per priority level. if (!isset($this->_numEntriesPerPriority[$event['priorityName']])) { $this->_numEntriesPerPriority[$event['priorityName']] = 1; } else { $this->_numEntriesPerPriority[$event['priorityName']]++; } $formattedEvent = $this->_formatter->format($event); // All plaintext events are to use the standard formatter. $this->_eventsToMail[] = $formattedEvent; // If we have a Zend_Layout instance, use a specific formatter for the // layout if one exists. Otherwise, just use the event with its // default format. if ($this->_layout) { if ($this->_layoutFormatter) { $this->_layoutEventsToMail[] = $this->_layoutFormatter->format($event); } else { $this->_layoutEventsToMail[] = $formattedEvent; } } } /** * Gets instance of Zend_Log_Formatter_Instance used for formatting a * message using Zend_Layout, if applicable. * * @return Zend_Log_Formatter_Interface|null The formatter, or null. */ public function getLayoutFormatter() { return $this->_layoutFormatter; } /** * Sets a specific formatter for use with Zend_Layout events. * * Allows use of a second formatter on lines that will be rendered with * Zend_Layout. In the event that Zend_Layout is not being used, this * formatter cannot be set, so an exception will be thrown. * * @param Zend_Log_Formatter_Interface $formatter * @return Zend_Log_Writer_Mail * @throws Zend_Log_Exception */ public function setLayoutFormatter(Zend_Log_Formatter_Interface $formatter) { if (!$this->_layout) { throw new Zend_Log_Exception( 'cannot set formatter for layout; ' . 'a Zend_Layout instance is not in use'); } $this->_layoutFormatter = $formatter; return $this; } /** * Allows caller to have the mail subject dynamically set to contain the * entry counts per-priority level. * * Sets the text for use in the subject, with entry counts per-priority * level appended to the end. Since a Zend_Mail subject can only be set * once, this method cannot be used if the Zend_Mail object already has a * subject set. * * @param string $subject Subject prepend text. * @return Zend_Log_Writer_Mail * @throws Zend_Log_Exception */ public function setSubjectPrependText($subject) { if ($this->_mail->getSubject()) { throw new Zend_Log_Exception( 'subject already set on mail; ' . 'cannot set subject prepend text'); } $this->_subjectPrependText = (string) $subject; return $this; } /** * Sends mail to recipient(s) if log entries are present. Note that both * plaintext and HTML portions of email are handled here. * * @return void */ public function shutdown() { // If there are events to mail, use them as message body. Otherwise, // there is no mail to be sent. if (empty($this->_eventsToMail)) { return; } if ($this->_subjectPrependText !== null) { // Tack on the summary of entries per-priority to the subject // line and set it on the Zend_Mail object. $numEntries = $this->_getFormattedNumEntriesPerPriority(); $this->_mail->setSubject( "{$this->_subjectPrependText} ({$numEntries})"); } // Always provide events to mail as plaintext. $this->_mail->setBodyText(implode('', $this->_eventsToMail)); // If a Zend_Layout instance is being used, set its "events" // value to the lines formatted for use with the layout. if ($this->_layout) { // Set the required "messages" value for the layout. Here we // are assuming that the layout is for use with HTML. $this->_layout->events = implode('', $this->_layoutEventsToMail); // If an exception occurs during rendering, convert it to a notice // so we can avoid an exception thrown without a stack frame. try { $this->_mail->setBodyHtml($this->_layout->render()); } catch (Exception $e) { trigger_error( "exception occurred when rendering layout; " . "unable to set html body for message; " . "message = {$e->getMessage()}; " . "code = {$e->getStatusCode()}; " . "exception class = " . get_class($e), E_USER_NOTICE); } } // Finally, send the mail. If an exception occurs, convert it into a // warning-level message so we can avoid an exception thrown without a // stack frame. try { $this->_mail->send(); } catch (Exception $e) { trigger_error( "unable to send log entries via email; " . "message = {$e->getMessage()}; " . "code = {$e->getStatusCode()}; " . "exception class = " . get_class($e), E_USER_WARNING); } } /** * Gets a string of number of entries per-priority level that occurred, or * an emptry string if none occurred. * * @return string */ protected function _getFormattedNumEntriesPerPriority() { $strings = array(); foreach ($this->_numEntriesPerPriority as $priority => $numEntries) { $strings[] = "{$priority}={$numEntries}"; } return implode(', ', $strings); } } vendor/Zend/Log/Writer/Mock.php000064400000003703151742733540012356 0ustar00events[] = $event; } /** * Record shutdown * * @return void */ public function shutdown() { $this->shutdown = true; } /** * Create a new instance of Zend_Log_Writer_Mock * * @param array|Zend_Config $config * @return Zend_Log_Writer_Mock */ static public function factory($config) { return new self(); } } vendor/Zend/Log/Writer/Null.php000064400000003121151742733540012371 0ustar00_stream = $streamOrUrl; } else { if (is_array($streamOrUrl) && isset($streamOrUrl['stream'])) { $streamOrUrl = $streamOrUrl['stream']; } if (! $this->_stream = @fopen($streamOrUrl, $mode, false)) { // require_once 'Zend/Log/Exception.php'; $msg = "\"$streamOrUrl\" cannot be opened with mode \"$mode\""; throw new Zend_Log_Exception($msg); } } $this->_formatter = new Zend_Log_Formatter_Simple(); } /** * Create a new instance of Zend_Log_Writer_Stream * * @param array|Zend_Config $config * @return Zend_Log_Writer_Stream */ static public function factory($config) { $config = self::_parseConfig($config); $config = array_merge(array( 'stream' => null, 'mode' => null, ), $config); $streamOrUrl = isset($config['url']) ? $config['url'] : $config['stream']; return new self( $streamOrUrl, $config['mode'] ); } /** * Close the stream resource. * * @return void */ public function shutdown() { if (is_resource($this->_stream)) { fclose($this->_stream); } } /** * Write a message to the log. * * @param array $event event data * @return void * @throws Zend_Log_Exception */ protected function _write($event) { $line = $this->_formatter->format($event); if (false === @fwrite($this->_stream, $line)) { // require_once 'Zend/Log/Exception.php'; throw new Zend_Log_Exception("Unable to write to stream"); } } } vendor/Zend/Log/Writer/Syslog.php000064400000015207151742733540012747 0ustar00 LOG_EMERG, Zend_Log::ALERT => LOG_ALERT, Zend_Log::CRIT => LOG_CRIT, Zend_Log::ERR => LOG_ERR, Zend_Log::WARN => LOG_WARNING, Zend_Log::NOTICE => LOG_NOTICE, Zend_Log::INFO => LOG_INFO, Zend_Log::DEBUG => LOG_DEBUG, ); /** * The default log priority - for unmapped custom priorities * * @var string */ protected $_defaultPriority = LOG_NOTICE; /** * Last application name set by a syslog-writer instance * * @var string */ protected static $_lastApplication; /** * Last facility name set by a syslog-writer instance * * @var string */ protected static $_lastFacility; /** * Application name used by this syslog-writer instance * * @var string */ protected $_application = 'Zend_Log'; /** * Facility used by this syslog-writer instance * * @var int */ protected $_facility = LOG_USER; /** * Types of program available to logging of message * * @var array */ protected $_validFacilities = array(); /** * Class constructor * * @param array $params Array of options; may include "application" and "facility" keys * @return void */ public function __construct(array $params = array()) { if (isset($params['application'])) { $this->_application = $params['application']; } $runInitializeSyslog = true; if (isset($params['facility'])) { $this->setFacility($params['facility']); $runInitializeSyslog = false; } if ($runInitializeSyslog) { $this->_initializeSyslog(); } } /** * Create a new instance of Zend_Log_Writer_Syslog * * @param array|Zend_Config $config * @return Zend_Log_Writer_Syslog */ static public function factory($config) { return new self(self::_parseConfig($config)); } /** * Initialize values facilities * * @return void */ protected function _initializeValidFacilities() { $constants = array( 'LOG_AUTH', 'LOG_AUTHPRIV', 'LOG_CRON', 'LOG_DAEMON', 'LOG_KERN', 'LOG_LOCAL0', 'LOG_LOCAL1', 'LOG_LOCAL2', 'LOG_LOCAL3', 'LOG_LOCAL4', 'LOG_LOCAL5', 'LOG_LOCAL6', 'LOG_LOCAL7', 'LOG_LPR', 'LOG_MAIL', 'LOG_NEWS', 'LOG_SYSLOG', 'LOG_USER', 'LOG_UUCP' ); foreach ($constants as $constant) { if (defined($constant)) { $this->_validFacilities[] = constant($constant); } } } /** * Initialize syslog / set application name and facility * * @return void */ protected function _initializeSyslog() { self::$_lastApplication = $this->_application; self::$_lastFacility = $this->_facility; openlog($this->_application, LOG_PID, $this->_facility); } /** * Set syslog facility * * @param int $facility Syslog facility * @return Zend_Log_Writer_Syslog * @throws Zend_Log_Exception for invalid log facility */ public function setFacility($facility) { if ($this->_facility === $facility) { return $this; } if (!count($this->_validFacilities)) { $this->_initializeValidFacilities(); } if (!in_array($facility, $this->_validFacilities)) { // require_once 'Zend/Log/Exception.php'; throw new Zend_Log_Exception('Invalid log facility provided; please see http://php.net/openlog for a list of valid facility values'); } if ('WIN' == strtoupper(substr(PHP_OS, 0, 3)) && ($facility !== LOG_USER) ) { // require_once 'Zend/Log/Exception.php'; throw new Zend_Log_Exception('Only LOG_USER is a valid log facility on Windows'); } $this->_facility = $facility; $this->_initializeSyslog(); return $this; } /** * Set application name * * @param string $application Application name * @return Zend_Log_Writer_Syslog */ public function setApplicationName($application) { if ($this->_application === $application) { return $this; } $this->_application = $application; $this->_initializeSyslog(); return $this; } /** * Close syslog. * * @return void */ public function shutdown() { closelog(); } /** * Write a message to syslog. * * @param array $event event data * @return void */ protected function _write($event) { if (array_key_exists($event['priority'], $this->_priorities)) { $priority = $this->_priorities[$event['priority']]; } else { $priority = $this->_defaultPriority; } if ($this->_application !== self::$_lastApplication || $this->_facility !== self::$_lastFacility) { $this->_initializeSyslog(); } syslog($priority, $event['message']); } } vendor/Zend/Log/Writer/ZendMonitor.php000064400000007124151742733540013736 0ustar00_isEnabled = false; } if (function_exists('zend_monitor_custom_event')) { $this->_isZendServer = true; } } /** * Create a new instance of Zend_Log_Writer_ZendMonitor * * @param array|Zend_Config $config * @return Zend_Log_Writer_ZendMonitor */ static public function factory($config) { return new self(); } /** * Is logging to this writer enabled? * * If the Zend Monitor extension is not enabled, this log writer will * fail silently. You can query this method to determine if the log * writer is enabled. * * @return boolean */ public function isEnabled() { return $this->_isEnabled; } /** * Log a message to this writer. * * @param array $event log data event * @return void */ public function write($event) { if (!$this->isEnabled()) { return; } parent::write($event); } /** * Write a message to the log. * * @param array $event log data event * @return void */ protected function _write($event) { $priority = $event['priority']; $message = $event['message']; unset($event['priority'], $event['message']); if (!empty($event)) { if ($this->_isZendServer) { // On Zend Server; third argument should be the event zend_monitor_custom_event($priority, $message, $event); } else { // On Zend Platform; third argument is severity -- either // 0 or 1 -- and fourth is optional (event) // Severity is either 0 (normal) or 1 (severe); classifying // notice, info, and debug as "normal", and all others as // "severe" monitor_custom_event($priority, $message, ($priority > 4) ? 0 : 1, $event); } } else { monitor_custom_event($priority, $message); } } } vendor/Doctrine000077700000000000151742733540007550 5ustar00vendor/Monolog000077700000000000151742733550007414 5ustar00vendor/Symfony/Component/ClassLoader000077700000000000151742733550013604 5ustar00