diff --git a/core/composer.json b/core/composer.json index 75814e2692474d449cb18169d23bcaa5caa6cf2e..5313b3a10c2c9d7c0850d405c439ba73acaa3ff0 100644 --- a/core/composer.json +++ b/core/composer.json @@ -22,7 +22,7 @@ "twig/twig": "1.18.*", "doctrine/common": "~2.4.2", "doctrine/annotations": "1.2.*", - "guzzlehttp/guzzle": "~5.0", + "guzzlehttp/guzzle": "dev-master#1879fbe853b0c64d109e369c7aeff09849e62d1e", "symfony-cmf/routing": "1.3.*", "easyrdf/easyrdf": "0.9.*", "phpunit/phpunit": "4.6.*", @@ -31,8 +31,8 @@ "stack/builder": "1.0.*", "egulias/email-validator": "1.2.*", "behat/mink": "~1.6", - "behat/mink-goutte-driver": "~1.1", - "fabpot/goutte": "^2.0.3", + "behat/mink-goutte-driver": "dev-master#cc5ce119b5a8e06662f634b35967aff0b0c7dfdd", + "fabpot/goutte": "~3.1", "masterminds/html5": "~2.1", "symfony/psr-http-message-bridge": "v0.2", "zendframework/zend-diactoros": "1.1.0" diff --git a/core/composer.lock b/core/composer.lock index 49c558143e8edb8ba696dd496825e87f6d11c421..3a01db04d66648c82f0063597eca446de4b79149 100644 --- a/core/composer.lock +++ b/core/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "a842b991264d42a2f8c5cecbd82597b1", + "hash": "34a206d03b9060c6f1b2895c0517792c", "packages": [ { "name": "behat/mink", @@ -118,22 +118,22 @@ }, { "name": "behat/mink-goutte-driver", - "version": "v1.1.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/minkphp/MinkGoutteDriver.git", - "reference": "2bf327b4166694ecaa8ae7f956cb6ae252ecf03e" + "reference": "cc5ce119b5a8e06662f634b35967aff0b0c7dfdd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/minkphp/MinkGoutteDriver/zipball/2bf327b4166694ecaa8ae7f956cb6ae252ecf03e", - "reference": "2bf327b4166694ecaa8ae7f956cb6ae252ecf03e", + "url": "https://api.github.com/repos/minkphp/MinkGoutteDriver/zipball/cc5ce119b5a8e06662f634b35967aff0b0c7dfdd", + "reference": "cc5ce119b5a8e06662f634b35967aff0b0c7dfdd", "shasum": "" }, "require": { "behat/mink": "~1.6@dev", "behat/mink-browserkit-driver": "~1.2@dev", - "fabpot/goutte": "~1.0.4|~2.0", + "fabpot/goutte": "~1.0.4|~2.0|~3.1", "php": ">=5.3.1" }, "type": "mink-driver", @@ -143,8 +143,8 @@ } }, "autoload": { - "psr-0": { - "Behat\\Mink\\Driver": "src/" + "psr-4": { + "Behat\\Mink\\Driver\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -166,7 +166,7 @@ "headless", "testing" ], - "time": "2014-10-09 09:21:12" + "time": "2015-06-27 00:15:11" }, { "name": "doctrine/annotations", @@ -739,21 +739,21 @@ }, { "name": "fabpot/goutte", - "version": "v2.0.3", + "version": "v3.1.0", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/Goutte.git", - "reference": "65ab61eae03d670b93a9044ad2328eb81aa1bde5" + "reference": "d9a5a28782d30e9f4e20176caea58a1d459f2c71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/Goutte/zipball/65ab61eae03d670b93a9044ad2328eb81aa1bde5", - "reference": "65ab61eae03d670b93a9044ad2328eb81aa1bde5", + "url": "https://api.github.com/repos/FriendsOfPHP/Goutte/zipball/d9a5a28782d30e9f4e20176caea58a1d459f2c71", + "reference": "d9a5a28782d30e9f4e20176caea58a1d459f2c71", "shasum": "" }, "require": { - "guzzlehttp/guzzle": ">=4,<6", - "php": ">=5.4.0", + "guzzlehttp/guzzle": "^6.0", + "php": ">=5.5.0", "symfony/browser-kit": "~2.1", "symfony/css-selector": "~2.1", "symfony/dom-crawler": "~2.1" @@ -761,7 +761,7 @@ "type": "application", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -784,25 +784,26 @@ "keywords": [ "scraper" ], - "time": "2014-11-28 09:48:17" + "time": "2015-06-24 16:11:31" }, { "name": "guzzlehttp/guzzle", - "version": "5.2.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "475b29ccd411f2fa8a408e64576418728c032cfa" + "reference": "1879fbe853b0c64d109e369c7aeff09849e62d1e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/475b29ccd411f2fa8a408e64576418728c032cfa", - "reference": "475b29ccd411f2fa8a408e64576418728c032cfa", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/1879fbe853b0c64d109e369c7aeff09849e62d1e", + "reference": "1879fbe853b0c64d109e369c7aeff09849e62d1e", "shasum": "" }, "require": { - "guzzlehttp/ringphp": "~1.0", - "php": ">=5.4.0" + "guzzlehttp/promises": "~1.0", + "guzzlehttp/psr7": "~1.1", + "php": ">=5.5.0" }, "require-dev": { "ext-curl": "*", @@ -812,10 +813,13 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "6.0-dev" } }, "autoload": { + "files": [ + "src/functions_include.php" + ], "psr-4": { "GuzzleHttp\\": "src/" } @@ -831,7 +835,7 @@ "homepage": "https://github.com/mtdowling" } ], - "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", + "description": "Guzzle is a PHP HTTP client library", "homepage": "http://guzzlephp.org/", "keywords": [ "client", @@ -842,34 +846,28 @@ "rest", "web service" ], - "time": "2015-01-28 01:03:29" + "time": "2015-07-10 20:04:21" }, { - "name": "guzzlehttp/ringphp", - "version": "1.0.7", + "name": "guzzlehttp/promises", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/guzzle/RingPHP.git", - "reference": "52d868f13570a9a56e5fce6614e0ec75d0f13ac2" + "url": "https://github.com/guzzle/promises.git", + "reference": "2ee5bc7f1a92efecc90da7f6711a53a7be26b5b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/RingPHP/zipball/52d868f13570a9a56e5fce6614e0ec75d0f13ac2", - "reference": "52d868f13570a9a56e5fce6614e0ec75d0f13ac2", + "url": "https://api.github.com/repos/guzzle/promises/zipball/2ee5bc7f1a92efecc90da7f6711a53a7be26b5b7", + "reference": "2ee5bc7f1a92efecc90da7f6711a53a7be26b5b7", "shasum": "" }, "require": { - "guzzlehttp/streams": "~3.0", - "php": ">=5.4.0", - "react/promise": "~2.0" + "php": ">=5.5.0" }, "require-dev": { - "ext-curl": "*", "phpunit/phpunit": "~4.0" }, - "suggest": { - "ext-curl": "Guzzle will use specific adapters if cURL is present" - }, "type": "library", "extra": { "branch-alias": { @@ -878,8 +876,11 @@ }, "autoload": { "psr-4": { - "GuzzleHttp\\Ring\\": "src/" - } + "GuzzleHttp\\Promise\\": "src/" + }, + "files": [ + "src/functions.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -892,25 +893,32 @@ "homepage": "https://github.com/mtdowling" } ], - "description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.", - "time": "2015-03-30 01:43:20" + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "time": "2015-06-24 16:16:25" }, { - "name": "guzzlehttp/streams", - "version": "3.0.0", + "name": "guzzlehttp/psr7", + "version": "1.1.0", "source": { "type": "git", - "url": "https://github.com/guzzle/streams.git", - "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5" + "url": "https://github.com/guzzle/psr7.git", + "reference": "af0e1758de355eb113917ad79c3c0e3604bce4bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/streams/zipball/47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5", - "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/af0e1758de355eb113917ad79c3c0e3604bce4bd", + "reference": "af0e1758de355eb113917ad79c3c0e3604bce4bd", "shasum": "" }, "require": { - "php": ">=5.4.0" + "php": ">=5.4.0", + "psr/http-message": "~1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" }, "require-dev": { "phpunit/phpunit": "~4.0" @@ -918,13 +926,16 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "1.0-dev" } }, "autoload": { "psr-4": { - "GuzzleHttp\\Stream\\": "src/" - } + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": [ + "src/functions.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -937,13 +948,14 @@ "homepage": "https://github.com/mtdowling" } ], - "description": "Provides a simple abstraction over streams of data", - "homepage": "http://guzzlephp.org/", + "description": "PSR-7 message implementation", "keywords": [ - "Guzzle", - "stream" + "http", + "message", + "stream", + "uri" ], - "time": "2014-10-12 19:18:40" + "time": "2015-06-24 19:55:15" }, { "name": "masterminds/html5", @@ -1625,50 +1637,6 @@ ], "time": "2012-12-21 11:40:51" }, - { - "name": "react/promise", - "version": "v2.2.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/promise.git", - "reference": "365fcee430dfa4ace1fbc75737ca60ceea7eeeef" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/365fcee430dfa4ace1fbc75737ca60ceea7eeeef", - "reference": "365fcee430dfa4ace1fbc75737ca60ceea7eeeef", - "shasum": "" - }, - "require": { - "php": ">=5.4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "psr-4": { - "React\\Promise\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jan Sorgalla", - "email": "jsorgalla@googlemail.com" - } - ], - "description": "A lightweight implementation of CommonJS Promises/A for PHP", - "time": "2014-12-30 13:32:42" - }, { "name": "sdboyer/gliph", "version": "0.1.8", @@ -3449,7 +3417,10 @@ "packages-dev": [], "aliases": [], "minimum-stability": "dev", - "stability-flags": [], + "stability-flags": { + "guzzlehttp/guzzle": 20, + "behat/mink-goutte-driver": 20 + }, "prefer-stable": true, "prefer-lowest": false, "platform": { diff --git a/core/core.api.php b/core/core.api.php index c34ca0ec148ff217516b66546e7a85bc2c49e952..de34440eb45d9d049538b20a49d8ea7c3d4d3839 100644 --- a/core/core.api.php +++ b/core/core.api.php @@ -2240,6 +2240,10 @@ function hook_validation_constraint_alter(array &$definitions) { * - context_provider: Indicates a block context provider, used for example * by block conditions. It has to implement * \Drupal\Core\Plugin\Context\ContextProviderInterface. + * - http_client_middleware: Indicates that the service provides a guzzle + * middleware, see + * https://guzzle.readthedocs.org/en/latest/handlers-and-middleware.html for + * more information. * * Creating a tag for a service does not do anything on its own, but tags * can be discovered or queried in a compiler pass when the container is built, diff --git a/core/core.services.yml b/core/core.services.yml index e1ef8d2875ab716e5562a33d4ad9b0d4f220cfdb..045a02e35b88c02d6079d589d5ed1aab11b61d44 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -379,10 +379,21 @@ services: path.current: class: Drupal\Core\Path\CurrentPathStack arguments: ['@request_stack'] + http_handler_stack: + class: GuzzleHttp\HandlerStack + public: false + factory: GuzzleHttp\HandlerStack::create + configurator: ['@http_handler_stack_configurator', configure] + http_handler_stack_configurator: + class: Drupal\Core\Http\HandlerStackConfigurator + public: false + arguments: ['@service_container'] http_client: - class: Drupal\Core\Http\Client - tags: - - { name: service_collector, tag: http_client_subscriber, call: attach } + class: GuzzleHttp\Client + factory: http_client_factory:fromOptions + http_client_factory: + class: Drupal\Core\Http\ClientFactory + arguments: ['@http_handler_stack'] theme.negotiator: class: Drupal\Core\Theme\ThemeNegotiator arguments: ['@access_check.theme'] diff --git a/core/lib/Drupal.php b/core/lib/Drupal.php index e15a92010a5729c2be84d5d46114ebef2ba3db3f..7621a4831f586346e8c5b97daf3de41795c1e4cd 100644 --- a/core/lib/Drupal.php +++ b/core/lib/Drupal.php @@ -395,7 +395,7 @@ public static function state() { /** * Returns the default http client. * - * @return \GuzzleHttp\ClientInterface + * @return \GuzzleHttp\Client * A guzzle http client instance. */ public static function httpClient() { diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php index 824432086d65caa7118a73adf8e0b68a2afb876d..7e02c80cc35afebe738736c0154f6579ed513935 100644 --- a/core/lib/Drupal/Core/CoreServiceProvider.php +++ b/core/lib/Drupal/Core/CoreServiceProvider.php @@ -10,6 +10,7 @@ use Drupal\Core\Cache\Context\CacheContextsPass; use Drupal\Core\Cache\ListCacheBinsPass; use Drupal\Core\DependencyInjection\Compiler\BackendCompilerPass; +use Drupal\Core\DependencyInjection\Compiler\GuzzleMiddlewarePass; use Drupal\Core\DependencyInjection\Compiler\ContextProvidersPass; use Drupal\Core\DependencyInjection\Compiler\ProxyServicesPass; use Drupal\Core\DependencyInjection\Compiler\RegisterLazyRouteEnhancers; @@ -75,6 +76,7 @@ public function register(ContainerBuilder $container) { // Collect tagged handler services as method calls on consumer services. $container->addCompilerPass(new TaggedHandlersPass()); $container->addCompilerPass(new RegisterStreamWrappersPass()); + $container->addCompilerPass(new GuzzleMiddlewarePass()); // Add a compiler pass for registering event subscribers. $container->addCompilerPass(new RegisterKernelListenersPass(), PassConfig::TYPE_AFTER_REMOVING); @@ -132,10 +134,10 @@ protected function registerTest(ContainerBuilder $container) { if (!drupal_valid_test_ua()) { return; } - // Add the HTTP request subscriber to Guzzle. + // Add the HTTP request middleware to Guzzle. $container - ->register('test.http_client.request_subscriber', 'Drupal\Core\Test\EventSubscriber\HttpRequestSubscriber') - ->addTag('http_client_subscriber'); + ->register('test.http_client.middleware', 'Drupal\Core\Test\HttpClientMiddleware\TestHttpClientMiddleware') + ->addTag('http_client_middleware'); } } diff --git a/core/lib/Drupal/Core/DependencyInjection/Compiler/GuzzleMiddlewarePass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/GuzzleMiddlewarePass.php new file mode 100644 index 0000000000000000000000000000000000000000..42e403ce56073d3cefeab0337070a4239dfa9ae5 --- /dev/null +++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/GuzzleMiddlewarePass.php @@ -0,0 +1,24 @@ +<?php + +/** + * @file + * Contains \Drupal\Core\DependencyInjection\Compiler\GuzzleMiddlewarePass. + */ + +namespace Drupal\Core\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class GuzzleMiddlewarePass implements CompilerPassInterface { + + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) { + $middleware_ids = array_keys($container->findTaggedServiceIds('http_client_middleware')); + $container->getDefinition('http_handler_stack_configurator') + ->addArgument($middleware_ids); + } + +} diff --git a/core/lib/Drupal/Core/Http/Client.php b/core/lib/Drupal/Core/Http/Client.php deleted file mode 100644 index fd27b671abea266156a2118e38c7cfd9f01950c0..0000000000000000000000000000000000000000 --- a/core/lib/Drupal/Core/Http/Client.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php - -/** - * @file - * Contains \Drupal\Core\Http\Client. - */ - -namespace Drupal\Core\Http; - -use Drupal\Component\Utility\NestedArray; -use Drupal\Core\Site\Settings; -use GuzzleHttp\Client as GuzzleClient; -use GuzzleHttp\Event\SubscriberInterface; - -/** - * Drupal default HTTP client class. - */ -class Client extends GuzzleClient { - - /** - * {@inheritdoc} - */ - public function __construct(array $config = []) { - $default_config = array( - // Security consideration: we must not use the certificate authority - // file shipped with Guzzle because it can easily get outdated if a - // certificate authority is hacked. Instead, we rely on the certificate - // authority file provided by the operating system which is more likely - // going to be updated in a timely fashion. This overrides the default - // path to the pem file bundled with Guzzle. - 'verify' => TRUE, - 'timeout' => 30, - 'headers' => array( - 'User-Agent' => 'Drupal/' . \Drupal::VERSION . ' (+https://www.drupal.org/) ' . static::getDefaultUserAgent(), - ), - ); - - // The entire config array is merged/configurable to allow Guzzle client - // options outside of 'defaults' to be changed, such as 'adapter', or - // 'message_factory'. - $config = NestedArray::mergeDeep(array('defaults' => $default_config), $config, Settings::get('http_client_config', array())); - - parent::__construct($config); - } - - /** - * Attaches an event subscriber. - * - * @param \GuzzleHttp\Event\SubscriberInterface $subscriber - * The subscriber to attach. - * - * @see \GuzzleHttp\Event\Emitter::attach() - */ - public function attach(SubscriberInterface $subscriber) { - $this->getEmitter()->attach($subscriber); - } - - /** - * Detaches an event subscriber. - * - * @param \GuzzleHttp\Event\SubscriberInterface $subscriber - * The subscriber to detach. - * - * @see \GuzzleHttp\Event\Emitter::detach() - */ - public function detach(SubscriberInterface $subscriber) { - $this->getEmitter()->detach($subscriber); - } - -} diff --git a/core/lib/Drupal/Core/Http/ClientFactory.php b/core/lib/Drupal/Core/Http/ClientFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..7524e896b998a7b94ff3e2ad32ecfaf28a4080c0 --- /dev/null +++ b/core/lib/Drupal/Core/Http/ClientFactory.php @@ -0,0 +1,67 @@ +<?php + +/** + * @file + * Contains \Drupal\Core\Http\ClientFactory. + */ + +namespace Drupal\Core\Http; + +use Drupal\Component\Utility\NestedArray; +use Drupal\Core\Site\Settings; +use GuzzleHttp\Client; +use GuzzleHttp\HandlerStack; + +/** + * Helper class to construct a HTTP client with Drupal specific config. + */ +class ClientFactory { + + /** + * The handler stack. + * + * @var \GuzzleHttp\HandlerStack + */ + protected $stack; + + /** + * Constructs a new ClientFactory instance. + * + * @param \GuzzleHttp\HandlerStack $stack + * The handler stack. + */ + public function __construct(HandlerStack $stack) { + $this->stack = $stack; + } + + /** + * Constructs a new client object from some configuration. + * + * @param array $config + * The config for the client. + * + * @return \GuzzleHttp\Client + * The HTTP client. + */ + public function fromOptions(array $config = []) { + $default_config = [ + // Security consideration: we must not use the certificate authority + // file shipped with Guzzle because it can easily get outdated if a + // certificate authority is hacked. Instead, we rely on the certificate + // authority file provided by the operating system which is more likely + // going to be updated in a timely fashion. This overrides the default + // path to the pem file bundled with Guzzle. + 'verify' => TRUE, + 'timeout' => 30, + 'headers' => [ + 'User-Agent' => 'Drupal/' . \Drupal::VERSION . ' (+https://www.drupal.org/) ' . \GuzzleHttp\default_user_agent(), + ], + 'handler' => $this->stack, + ]; + + $config = NestedArray::mergeDeep($default_config, Settings::get('http_client_config', []), $config); + + return new Client($config); + } + +} diff --git a/core/lib/Drupal/Core/Http/HandlerStackConfigurator.php b/core/lib/Drupal/Core/Http/HandlerStackConfigurator.php new file mode 100644 index 0000000000000000000000000000000000000000..54e4bd9c3548c587acc69f566c5f93b2c4167d34 --- /dev/null +++ b/core/lib/Drupal/Core/Http/HandlerStackConfigurator.php @@ -0,0 +1,94 @@ +<?php + +/** + * @file + * Contains \Drupal\Core\Http\HandlerStackConfigurator. + */ + +namespace Drupal\Core\Http; + +use GuzzleHttp\HandlerStack; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Defines a class for configuring middlewares on the http handler stack. + * + * The http_client service requires a handler stack to perform http requests. + * This is provided by the http_handler_stack service. Modules wishing to add + * additional middlewares to the handler stack can create services and tag them + * as http_client_middleware. Each service must contain an __invoke method that + * returns a closure which will serve as the middleware. + * + * @see https://guzzle.readthedocs.org/en/latest/handlers-and-middleware.html + * + * @see \Drupal\Core\Http\Client + * @see \Drupal\Core\Test\HttpClientMiddleware\TestHttpClientMiddleware + */ +class HandlerStackConfigurator { + + /** + * Array of middlewares to add to the handler stack. + * + * @var callable[] + */ + protected $middlewares = NULL; + + /** + * A list of used middleware service IDs. + * + * @var string[] + */ + protected $middlewareIds = []; + + /** + * The service container. + * + * @var \Symfony\Component\DependencyInjection\ContainerInterface + */ + protected $container; + + /** + * Contructs a new HandlerStackConfigurator object. + * + * @param \Symfony\Component\DependencyInjection\ContainerInterface $container + * The service container. + * @param string[] $middleware_ids + * The middleware IDs. + */ + public function __construct(ContainerInterface $container, array $middleware_ids) { + $this->middlewareIds = $middleware_ids; + $this->container = $container; + } + + /** + * Ensures that the middlewares are initialized. + */ + protected function initializeMiddlewares() { + if (!isset($this->middlewares)) { + $this->middlewares = []; + foreach ($this->middlewareIds as $middleware_id) { + $middleware = $this->container->get($middleware_id); + if (is_callable($middleware)) { + $this->middlewares[$middleware_id] = $middleware(); + } + else { + throw new \InvalidArgumentException('Middlewares need to implement __invoke, see https://guzzle.readthedocs.org/en/latest/handlers-and-middleware.html for more information about middlewares.'); + } + } + } + } + + /** + * Configures the stack using services tagged as http_client_middleware. + * + * @param \GuzzleHttp\HandlerStack $handler_stack + * The handler stack + */ + public function configure(HandlerStack $handler_stack) { + $this->initializeMiddlewares(); + foreach ($this->middlewares as $middleware_id => $middleware) { + $handler_stack->push($middleware, $middleware_id); + } + } + +} diff --git a/core/lib/Drupal/Core/Test/EventSubscriber/HttpRequestSubscriber.php b/core/lib/Drupal/Core/Test/EventSubscriber/HttpRequestSubscriber.php deleted file mode 100644 index 776f2c15c25ff77cb3f99ee3e5d8c221e813642e..0000000000000000000000000000000000000000 --- a/core/lib/Drupal/Core/Test/EventSubscriber/HttpRequestSubscriber.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php - -/** - * @file - * Contains \Drupal\Core\Test\EventSubscriber\HttpRequestSubscriber. - */ - -namespace Drupal\Core\Test\EventSubscriber; - -use GuzzleHttp\Event\BeforeEvent; -use GuzzleHttp\Event\SubscriberInterface; - -/** - * Overrides the User-Agent HTTP header for outbound HTTP requests. - */ -class HttpRequestSubscriber implements SubscriberInterface { - - /** - * {@inheritdoc} - */ - public function getEvents() { - return array( - 'before' => array('onBeforeSendRequest'), - ); - } - - /** - * Event callback for the 'before' event - */ - public function onBeforeSendRequest(BeforeEvent $event) { - // If the database prefix is being used by SimpleTest to run the tests in a copied - // database then set the user-agent header to the database prefix so that any - // calls to other Drupal pages will run the SimpleTest prefixed database. The - // user-agent is used to ensure that multiple testing sessions running at the - // same time won't interfere with each other as they would if the database - // prefix were stored statically in a file or database variable. - if ($test_prefix = drupal_valid_test_ua()) { - $event->getRequest()->setHeader('User-Agent', drupal_generate_test_ua($test_prefix)); - } - } - -} diff --git a/core/lib/Drupal/Core/Test/HttpClientMiddleware/TestHttpClientMiddleware.php b/core/lib/Drupal/Core/Test/HttpClientMiddleware/TestHttpClientMiddleware.php new file mode 100644 index 0000000000000000000000000000000000000000..148818971003106a59f43b13f764cac7970d43a5 --- /dev/null +++ b/core/lib/Drupal/Core/Test/HttpClientMiddleware/TestHttpClientMiddleware.php @@ -0,0 +1,39 @@ +<?php + +/** + * @file + * Contains \Drupal\Core\Test\HttpClientMiddleware\TestHttpClientMiddleware. + */ + +namespace Drupal\Core\Test\HttpClientMiddleware; + +use Psr\Http\Message\RequestInterface; + +/** + * Overrides the User-Agent HTTP header for outbound HTTP requests. + */ +class TestHttpClientMiddleware { + + /** + * {@inheritdoc} + * + * HTTP middleware that replaces the user agent for simpletest requests. + */ + public function __invoke() { + // If the database prefix is being used by SimpleTest to run the tests in a copied + // database then set the user-agent header to the database prefix so that any + // calls to other Drupal pages will run the SimpleTest prefixed database. The + // user-agent is used to ensure that multiple testing sessions running at the + // same time won't interfere with each other as they would if the database + // prefix were stored statically in a file or database variable. + return function ($handler) { + return function (RequestInterface $request, array $options) use ($handler) { + if ($test_prefix = drupal_valid_test_ua()) { + $request = $request->withHeader('User-Agent', drupal_generate_test_ua($test_prefix)); + } + return $handler($request, $options); + }; + }; + } + +} diff --git a/core/modules/aggregator/src/Plugin/aggregator/fetcher/DefaultFetcher.php b/core/modules/aggregator/src/Plugin/aggregator/fetcher/DefaultFetcher.php index be12c43b9a05b8d05c61e6dbc6986d50e20e6541..c9e6f929c585233344975f20894fddbf2a15f400 100644 --- a/core/modules/aggregator/src/Plugin/aggregator/fetcher/DefaultFetcher.php +++ b/core/modules/aggregator/src/Plugin/aggregator/fetcher/DefaultFetcher.php @@ -10,9 +10,13 @@ use Drupal\aggregator\Plugin\FetcherInterface; use Drupal\aggregator\FeedInterface; use Drupal\Component\Datetime\DateTimePlus; +use Drupal\Core\Http\ClientFactory; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; -use GuzzleHttp\ClientInterface; use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Psr7\Request; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\UriInterface; use Psr\Log\LoggerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -32,9 +36,9 @@ class DefaultFetcher implements FetcherInterface, ContainerFactoryPluginInterfac /** * The HTTP client to fetch the feed data with. * - * @var \GuzzleHttp\ClientInterface + * @var \Drupal\Core\Http\ClientFactory */ - protected $httpClient; + protected $httpClientFactory; /** * A logger instance. @@ -46,13 +50,13 @@ class DefaultFetcher implements FetcherInterface, ContainerFactoryPluginInterfac /** * Constructs a DefaultFetcher object. * - * @param \GuzzleHttp\ClientInterface $http_client + * @param \Drupal\Core\Http\ClientFactory $http_client_factory * A Guzzle client object. * @param \Psr\Log\LoggerInterface $logger * A logger instance. */ - public function __construct(ClientInterface $http_client, LoggerInterface $logger) { - $this->httpClient = $http_client; + public function __construct(ClientFactory $http_client_factory, LoggerInterface $logger) { + $this->httpClientFactory = $http_client_factory; $this->logger = $logger; } @@ -61,7 +65,7 @@ public function __construct(ClientInterface $http_client, LoggerInterface $logge */ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { return new static( - $container->get('http_client'), + $container->get('http_client_factory'), $container->get('logger.factory')->get('aggregator') ); } @@ -70,19 +74,26 @@ public static function create(ContainerInterface $container, array $configuratio * {@inheritdoc} */ public function fetch(FeedInterface $feed) { - $request = $this->httpClient->createRequest('GET', $feed->getUrl()); + $request = new Request('GET', $feed->getUrl()); $feed->source_string = FALSE; // Generate conditional GET headers. if ($feed->getEtag()) { - $request->addHeader('If-None-Match', $feed->getEtag()); + $request = $request->withAddedHeader('If-None-Match', $feed->getEtag()); } if ($feed->getLastModified()) { - $request->addHeader('If-Modified-Since', gmdate(DateTimePlus::RFC7231, $feed->getLastModified())); + $request = $request->withAddedHeader('If-Modified-Since', gmdate(DateTimePlus::RFC7231, $feed->getLastModified())); } try { - $response = $this->httpClient->send($request); + + /** @var \Psr\Http\Message\UriInterface $actual_uri */ + $actual_uri = NULL; + $response = $this->httpClientFactory->fromOptions(['allow_redirects' => [ + 'on_redirect' => function(RequestInterface $request, ResponseInterface $response, UriInterface $uri) use (&$actual_uri) { + $actual_uri = (string) $uri; + } + ]])->send($request); // In case of a 304 Not Modified, there is no new content, so return // FALSE. @@ -91,13 +102,17 @@ public function fetch(FeedInterface $feed) { } $feed->source_string = (string) $response->getBody(); - $feed->setEtag($response->getHeader('ETag')); - $feed->setLastModified(strtotime($response->getHeader('Last-Modified'))); + if ($response->hasHeader('ETag')) { + $feed->setEtag($response->getHeaderLine('ETag')); + } + if ($response->hasHeader('Last-Modified')) { + $feed->setLastModified(strtotime($response->getHeaderLine('Last-Modified'))); + } $feed->http_headers = $response->getHeaders(); // Update the feed URL in case of a 301 redirect. - if ($response->getEffectiveUrl() != $feed->getUrl()) { - $feed->setUrl($response->getEffectiveUrl()); + if ($actual_uri && $actual_uri !== $feed->getUrl()) { + $feed->setUrl($actual_uri); } return TRUE; } diff --git a/core/modules/locale/locale.batch.inc b/core/modules/locale/locale.batch.inc index d06178f11ad486ef8e6f89a8b6ebad79dab00476..4b67ddb2ac636512fc899c48105f2fa1d1722404 100644 --- a/core/modules/locale/locale.batch.inc +++ b/core/modules/locale/locale.batch.inc @@ -6,6 +6,9 @@ */ use GuzzleHttp\Exception\RequestException; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\UriInterface; /** * Load the common translation API. @@ -234,15 +237,20 @@ function locale_translation_batch_fetch_finished($success, $results) { function locale_translation_http_check($uri) { $logger = \Drupal::logger('locale'); try { - $response = \Drupal::httpClient()->head($uri); + $actual_uri = NULL; + $response = \Drupal::service('http_client_factory')->fromOptions(['allow_redirects' => [ + 'on_redirect' => function(RequestInterface $request, ResponseInterface $response, UriInterface $request_uri) use (&$actual_uri) { + $actual_uri = (string) $request_uri; + } + ]])->head($uri); $result = array(); // Return the effective URL if it differs from the requested. - if ($response->getEffectiveUrl() != $uri) { - $result['location'] = $response->getEffectiveUrl(); + if ($actual_uri && $actual_uri !== $uri) { + $result['location'] = $actual_uri; } - $result['last_modified'] = $response->hasHeader('Last-Modified') ? strtotime($response->getHeader('Last-Modified')) : 0; + $result['last_modified'] = $response->hasHeader('Last-Modified') ? strtotime($response->getHeaderLine('Last-Modified')) : 0; return $result; } catch (RequestException $e) { diff --git a/core/modules/simpletest/src/Tests/SimpleTestTest.php b/core/modules/simpletest/src/Tests/SimpleTestTest.php index e4df908793faa303e41b3728c7140bc3f3b545d0..1f978453c872d531cd71e0fa2ced873d06dd5034 100644 --- a/core/modules/simpletest/src/Tests/SimpleTestTest.php +++ b/core/modules/simpletest/src/Tests/SimpleTestTest.php @@ -157,7 +157,7 @@ function stubTest() { // request. This allows the stub test to make requests. The event does not // fire here and drupal_generate_test_ua() can not generate a key for a // test in a test since the prefix has changed. - // @see \Drupal\Core\Test\EventSubscriber\HttpRequestSubscriber::onBeforeSendRequest() + // @see \Drupal\Core\Test\HttpClientMiddleware\TestHttpClientMiddleware::onBeforeSendRequest() // @see drupal_generate_test_ua(); $key_file = DRUPAL_ROOT . '/sites/simpletest/' . substr($this->databasePrefix, 10) . '/.htkey'; $private_key = Crypt::randomBytesBase64(55); diff --git a/core/modules/statistics/src/Tests/StatisticsAdminTest.php b/core/modules/statistics/src/Tests/StatisticsAdminTest.php index 476b2ff16e928567035648fcea74effd2009f5bd..7c4f5834555f05f8e3ae83ab9b4efbecc95cc397 100644 --- a/core/modules/statistics/src/Tests/StatisticsAdminTest.php +++ b/core/modules/statistics/src/Tests/StatisticsAdminTest.php @@ -40,7 +40,7 @@ class StatisticsAdminTest extends WebTestBase { /** * The Guzzle HTTP client. * - * @var \GuzzleHttp\ClientInterface; + * @var \GuzzleHttp\Client; */ protected $client; @@ -54,8 +54,8 @@ protected function setUp() { $this->privilegedUser = $this->drupalCreateUser(array('administer statistics', 'view post access counter', 'create page content')); $this->drupalLogin($this->privilegedUser); $this->testNode = $this->drupalCreateNode(array('type' => 'page', 'uid' => $this->privilegedUser->id())); - $this->client = \Drupal::httpClient(); - $this->client->setDefaultOption('config/curl', array(CURLOPT_TIMEOUT => 10)); + $this->client = \Drupal::service('http_client_factory') + ->fromOptions(['config/curl' => [CURLOPT_TIMEOUT => 10]]); } /** @@ -78,16 +78,16 @@ function testStatisticsSettings() { $post = array('nid' => $nid); global $base_url; $stats_path = $base_url . '/' . drupal_get_path('module', 'statistics'). '/statistics.php'; - $this->client->post($stats_path, array('body' => $post)); + $this->client->post($stats_path, array('form_params' => $post)); // Hit the node again (the counter is incremented after the hit, so // "1 view" will actually be shown when the node is hit the second time). $this->drupalGet('node/' . $this->testNode->id()); - $this->client->post($stats_path, array('body' => $post)); + $this->client->post($stats_path, array('form_params' => $post)); $this->assertText('1 view', 'Node is viewed once.'); $this->drupalGet('node/' . $this->testNode->id()); - $this->client->post($stats_path, array('body' => $post)); + $this->client->post($stats_path, array('form_params' => $post)); $this->assertText('2 views', 'Node is viewed 2 times.'); } @@ -103,7 +103,7 @@ function testDeleteNode() { $post = array('nid' => $nid); global $base_url; $stats_path = $base_url . '/' . drupal_get_path('module', 'statistics'). '/statistics.php'; - $this->client->post($stats_path, array('body' => $post)); + $this->client->post($stats_path, array('form_params' => $post)); $result = db_select('node_counter', 'n') ->fields('n', array('nid')) @@ -137,9 +137,9 @@ function testExpiredLogs() { $post = array('nid' => $nid); global $base_url; $stats_path = $base_url . '/' . drupal_get_path('module', 'statistics'). '/statistics.php'; - $this->client->post($stats_path, array('body' => $post)); + $this->client->post($stats_path, array('form_params' => $post)); $this->drupalGet('node/' . $this->testNode->id()); - $this->client->post($stats_path, array('body' => $post)); + $this->client->post($stats_path, array('form_params' => $post)); $this->assertText('1 view', 'Node is viewed once.'); // statistics_cron() will subtract diff --git a/core/modules/statistics/src/Tests/StatisticsLoggingTest.php b/core/modules/statistics/src/Tests/StatisticsLoggingTest.php index 02dcfbcb016ba6df79e16ebd56ae770f1c92ae2f..1b83a25ee279885f893f1421d574f086397e7eb7 100644 --- a/core/modules/statistics/src/Tests/StatisticsLoggingTest.php +++ b/core/modules/statistics/src/Tests/StatisticsLoggingTest.php @@ -36,7 +36,7 @@ class StatisticsLoggingTest extends WebTestBase { /** * The Guzzle HTTP client. * - * @var \GuzzleHttp\ClientInterface; + * @var \GuzzleHttp\Client; */ protected $client; @@ -61,8 +61,8 @@ protected function setUp() { // Clear the logs. db_truncate('node_counter'); - $this->client = \Drupal::httpClient(); - $this->client->setDefaultOption('config/curl', array(CURLOPT_TIMEOUT => 10)); + $this->client = \Drupal::service('http_client_factory') + ->fromOptions(['config/curl' => [CURLOPT_TIMEOUT => 10]]); } /** @@ -94,7 +94,7 @@ function testLogging() { // Manually call statistics.php to simulate ajax data collection behavior. $nid = $this->node->id(); $post = array('nid' => $nid); - $this->client->post($stats_path, array('body' => $post)); + $this->client->post($stats_path, array('form_params' => $post)); $node_counter = statistics_get($this->node->id()); $this->assertIdentical($node_counter['totalcount'], '1'); } diff --git a/core/modules/statistics/src/Tests/StatisticsReportsTest.php b/core/modules/statistics/src/Tests/StatisticsReportsTest.php index 50e5f5e3eb86183ef9dedb632d58dd15b15249cc..f2a79ff613fbe07645cbf0870e6f788fb3d5fef2 100644 --- a/core/modules/statistics/src/Tests/StatisticsReportsTest.php +++ b/core/modules/statistics/src/Tests/StatisticsReportsTest.php @@ -30,8 +30,8 @@ function testPopularContentBlock() { $headers = array('Content-Type' => 'application/x-www-form-urlencoded'); global $base_url; $stats_path = $base_url . '/' . drupal_get_path('module', 'statistics'). '/statistics.php'; - $client = \Drupal::httpClient(); - $client->setDefaultOption('config/curl', array(CURLOPT_TIMEOUT => 10)); + $client = \Drupal::service('http_client_factory') + ->fromOptions(['config/curl' => [CURLOPT_TIMEOUT => 10]]); $client->post($stats_path, array('headers' => $headers, 'body' => $post)); // Configure and save the block. diff --git a/core/modules/statistics/src/Tests/StatisticsTokenReplaceTest.php b/core/modules/statistics/src/Tests/StatisticsTokenReplaceTest.php index 9534c8a28ddc52a27471139b9ec5e768472eafaf..792c40ab2880868b423eb5b623e123992ef18f7c 100644 --- a/core/modules/statistics/src/Tests/StatisticsTokenReplaceTest.php +++ b/core/modules/statistics/src/Tests/StatisticsTokenReplaceTest.php @@ -33,8 +33,8 @@ function testStatisticsTokenReplacement() { $headers = array('Content-Type' => 'application/x-www-form-urlencoded'); global $base_url; $stats_path = $base_url . '/' . drupal_get_path('module', 'statistics'). '/statistics.php'; - $client = \Drupal::httpClient(); - $client->setDefaultOption('config/curl', array(CURLOPT_TIMEOUT => 10)); + $client = \Drupal::service('http_client_factory') + ->fromOptions(['config/curl' => [CURLOPT_TIMEOUT => 10]]); $client->post($stats_path, array('headers' => $headers, 'body' => $post)); $statistics = statistics_get($node->id()); diff --git a/core/modules/statistics/src/Tests/Views/IntegrationTest.php b/core/modules/statistics/src/Tests/Views/IntegrationTest.php index d83a38773bb9897ba485e30dfa48bbb40a445702..0b8b8c8d83bf4944f7d4de717e35c55deb6a8522 100644 --- a/core/modules/statistics/src/Tests/Views/IntegrationTest.php +++ b/core/modules/statistics/src/Tests/Views/IntegrationTest.php @@ -76,9 +76,8 @@ public function testNodeCounterIntegration() { // @see \Drupal\statistics\Tests\StatisticsLoggingTest::testLogging(). global $base_url; $stats_path = $base_url . '/' . drupal_get_path('module', 'statistics'). '/statistics.php'; - $client = \Drupal::httpClient(); - $client->setDefaultOption('config/curl', array(CURLOPT_TIMEOUT => 10)); - $client->post($stats_path, array('body' => array('nid' => $this->node->id()))); + $client = \Drupal::service('http_client_factory')->fromOptions(['config/curl', array(CURLOPT_TIMEOUT => 10)]); + $client->post($stats_path, array('form_params' => array('nid' => $this->node->id()))); $this->drupalGet('test_statistics_integration'); $expected = statistics_get($this->node->id()); diff --git a/core/modules/update/tests/src/Unit/UpdateFetcherTest.php b/core/modules/update/tests/src/Unit/UpdateFetcherTest.php index f80d59d9f108162db346ad94198bb05744d4c64f..292f3f80703197c25e5b853ea957686acd5097a5 100644 --- a/core/modules/update/tests/src/Unit/UpdateFetcherTest.php +++ b/core/modules/update/tests/src/Unit/UpdateFetcherTest.php @@ -33,9 +33,7 @@ class UpdateFetcherTest extends UnitTestCase { */ protected function setUp() { $config_factory = $this->getConfigFactoryStub(array('update.settings' => array('fetch_url' => 'http://www.example.com'))); - $http_client_mock = $this->getMockBuilder('Drupal\Core\Http\Client') - ->disableOriginalConstructor() - ->getMock(); + $http_client_mock = $this->getMock('\GuzzleHttp\ClientInterface'); $this->updateFetcher = new UpdateFetcher($config_factory, $http_client_mock); } diff --git a/core/tests/Drupal/Tests/Core/Http/ClientFactoryTest.php b/core/tests/Drupal/Tests/Core/Http/ClientFactoryTest.php new file mode 100644 index 0000000000000000000000000000000000000000..7806168200aee383aab3c528723b347f85fe2c55 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Http/ClientFactoryTest.php @@ -0,0 +1,74 @@ +<?php + +/** + * @file + * Contains \Drupal\Tests\Core\Http\ClientFactoryTest. + */ + +namespace Drupal\Tests\Core\Http; + +use Drupal\Core\Http\ClientFactory; +use Drupal\Core\Site\Settings; +use Drupal\Tests\UnitTestCase; + +/** + * @coversDefaultClass \Drupal\Core\Http\ClientFactory + * @group Http + */ +class ClientFactoryTest extends UnitTestCase { + + /** + * The client factory under test. + * + * @var \Drupal\Core\Http\ClientFactory + */ + protected $factory; + + /** + * {@inheritdoc} + */ + public function setUp() { + $stack = $this->getMockBuilder('GuzzleHttp\HandlerStack') + ->disableOriginalConstructor() + ->getMock(); + $this->factory = new ClientFactory($stack); + } + + /** + * @covers ::fromOptions + * @dataProvider providerTestCreateFromOptions + * + * @param array $settings_config + * @param array $parameter_config + * @param array $expected_config_keys + */ + public function testCreateFromOptions($settings_config, $parameter_config, $expected_config_keys) { + if ($settings_config) { + new Settings(['http_client_config' => $settings_config]); + } + else { + new Settings([]); + } + + $client = $this->factory->fromOptions($parameter_config); + + foreach ($expected_config_keys as $key => $expected) { + $this->assertSame($expected, $client->getConfig($key)); + } + } + + /** + * Data provider for testCreateFromOptions + * + * @return array + */ + public function providerTestCreateFromOptions() { + return [ + [[], [], ['verify' => TRUE, 'timeout' => 30]], + [['timeout' => 40], [], ['verify' => TRUE, 'timeout' => 40]], + [[], ['timeout' => 50], ['verify' => TRUE, 'timeout' => 50]], + [['timeout' => 40], ['timeout' => 50], ['verify' => TRUE, 'timeout' => 50]], + ]; + } + +} diff --git a/core/vendor/behat/mink-goutte-driver/.travis.yml b/core/vendor/behat/mink-goutte-driver/.travis.yml index b09e4571001aeea7f5557fa1419ce5467ea47e84..d21d960c9e0d953fb6c8880c3d85f43051acbbdd 100644 --- a/core/vendor/behat/mink-goutte-driver/.travis.yml +++ b/core/vendor/behat/mink-goutte-driver/.travis.yml @@ -2,6 +2,10 @@ language: php php: [5.3, 5.4, 5.5, 5.6, hhvm] +before_install: + # Force using Goutte 2 on HHVM for now because Guzzle 6 is broken there + - if [ "hhvm" = "$TRAVIS_PHP_VERSION" ]; then composer require fabpot/goutte '~2' --no-update; fi + before_script: - export WEB_FIXTURES_HOST=http://localhost diff --git a/core/vendor/behat/mink-goutte-driver/README.md b/core/vendor/behat/mink-goutte-driver/README.md old mode 100755 new mode 100644 index c7877c5196b81dda68f0c297371bc919c36ef6c6..cba60a08bffb8549a085366b11f7c3a710430631 --- a/core/vendor/behat/mink-goutte-driver/README.md +++ b/core/vendor/behat/mink-goutte-driver/README.md @@ -4,9 +4,9 @@ Mink Goutte Driver [](https://packagist.org/packages/behat/mink-goutte-driver) [](https://packagist.org/packages/behat/mink-goutte-driver) [](https://packagist.org/packages/behat/mink-goutte-driver) -[](https://travis-ci.org/Behat/MinkGoutteDriver) -[](https://scrutinizer-ci.com/g/Behat/MinkGoutteDriver/) -[](https://scrutinizer-ci.com/g/Behat/MinkGoutteDriver/) +[](https://travis-ci.org/minkphp/MinkGoutteDriver) +[](https://scrutinizer-ci.com/g/minkphp/MinkGoutteDriver/) +[](https://scrutinizer-ci.com/g/minkphp/MinkGoutteDriver/) [](https://packagist.org/packages/behat/mink-goutte-driver) Usage Example @@ -15,23 +15,28 @@ Usage Example ``` php <?php +require "vendor/autoload.php"; + use Behat\Mink\Mink, Behat\Mink\Session, Behat\Mink\Driver\GoutteDriver, Behat\Mink\Driver\Goutte\Client as GoutteClient; -$startUrl = 'http://example.com'; - $mink = new Mink(array( - 'goutte' => new Session(new GoutteDriver(new GoutteClient($startUrl))), + 'goutte' => new Session(new GoutteDriver(new GoutteClient())), )); -$mink->getSession('goutte')->getPage()->findLink('Chat')->click(); +$session = $mink->getSession('goutte'); +$session->visit("http://php.net/"); +$session->getPage()->clickLink('Downloads'); +echo $session->getCurrentUrl() . PHP_EOL; ``` Installation ------------ +Add a file composer.json with content: + ``` json { "require": { @@ -41,6 +46,8 @@ Installation } ``` +(or merge the above into your project's existing composer.json file) + ``` bash $> curl -sS https://getcomposer.org/installer | php $> php composer.phar install @@ -49,5 +56,5 @@ $> php composer.phar install Maintainers ----------- -* Konstantin Kudryashov [everzet](http://github.com/everzet) -* Other [awesome developers](https://github.com/Behat/MinkGoutteDriver/graphs/contributors) +* Christophe Coevoet [stof](https://github.com/stof) +* Other [awesome developers](https://github.com/minkphp/MinkGoutteDriver/graphs/contributors) diff --git a/core/vendor/behat/mink-goutte-driver/composer.json b/core/vendor/behat/mink-goutte-driver/composer.json index 7444a5045c26a5b72f2b97c8b5a6bd7e4a0c61b1..f103906ec14ea371140e1e7ea59b2afe83987915 100644 --- a/core/vendor/behat/mink-goutte-driver/composer.json +++ b/core/vendor/behat/mink-goutte-driver/composer.json @@ -18,12 +18,12 @@ "php": ">=5.3.1", "behat/mink": "~1.6@dev", "behat/mink-browserkit-driver": "~1.2@dev", - "fabpot/goutte": "~1.0.4|~2.0" + "fabpot/goutte": "~1.0.4|~2.0|~3.1" }, "autoload": { - "psr-0": { - "Behat\\Mink\\Driver": "src/" + "psr-4": { + "Behat\\Mink\\Driver\\": "src/" } }, diff --git a/core/vendor/behat/mink-goutte-driver/phpunit.xml.dist b/core/vendor/behat/mink-goutte-driver/phpunit.xml.dist index f374c53aed48c05041e58ccd44556e1ed86562c4..cbe57c47d390918446fa06d97853aa1fc85a772d 100644 --- a/core/vendor/behat/mink-goutte-driver/phpunit.xml.dist +++ b/core/vendor/behat/mink-goutte-driver/phpunit.xml.dist @@ -17,7 +17,7 @@ <filter> <whitelist> - <directory>./src/Behat/Mink/Driver</directory> + <directory>./src</directory> </whitelist> </filter> </phpunit> diff --git a/core/vendor/behat/mink-goutte-driver/src/Behat/Mink/Driver/Goutte/Client.php b/core/vendor/behat/mink-goutte-driver/src/Goutte/Client.php similarity index 100% rename from core/vendor/behat/mink-goutte-driver/src/Behat/Mink/Driver/Goutte/Client.php rename to core/vendor/behat/mink-goutte-driver/src/Goutte/Client.php diff --git a/core/vendor/behat/mink-goutte-driver/src/Behat/Mink/Driver/GoutteDriver.php b/core/vendor/behat/mink-goutte-driver/src/GoutteDriver.php similarity index 100% rename from core/vendor/behat/mink-goutte-driver/src/Behat/Mink/Driver/GoutteDriver.php rename to core/vendor/behat/mink-goutte-driver/src/GoutteDriver.php diff --git a/core/vendor/composer/autoload_files.php b/core/vendor/composer/autoload_files.php index 853f9a0685927bc2323effffcf2fec234046d8ad..6f932aaf646e2dccf2b88a39bef49d557aab886c 100644 --- a/core/vendor/composer/autoload_files.php +++ b/core/vendor/composer/autoload_files.php @@ -6,6 +6,8 @@ $baseDir = dirname($vendorDir); return array( - $vendorDir . '/react/promise/src/functions_include.php', + $vendorDir . '/guzzlehttp/psr7/src/functions.php', + $vendorDir . '/guzzlehttp/promises/src/functions.php', + $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', $baseDir . '/lib/Drupal.php', ); diff --git a/core/vendor/composer/autoload_namespaces.php b/core/vendor/composer/autoload_namespaces.php index 60382889c8692e1d931fbe4ca98274c9aae02e93..89ce61cdbbb231eeb940a129e814e9880638da21 100644 --- a/core/vendor/composer/autoload_namespaces.php +++ b/core/vendor/composer/autoload_namespaces.php @@ -22,5 +22,5 @@ 'Doctrine\\Common\\Cache\\' => array($vendorDir . '/doctrine/cache/lib'), 'Doctrine\\Common\\Annotations\\' => array($vendorDir . '/doctrine/annotations/lib'), 'Doctrine\\Common\\' => array($vendorDir . '/doctrine/common/lib'), - 'Behat\\Mink\\Driver' => array($vendorDir . '/behat/mink-browserkit-driver/src', $vendorDir . '/behat/mink-goutte-driver/src'), + 'Behat\\Mink\\Driver' => array($vendorDir . '/behat/mink-browserkit-driver/src'), ); diff --git a/core/vendor/composer/autoload_psr4.php b/core/vendor/composer/autoload_psr4.php index ba426344da0b45fa138ed3aab9d168e17e243bdf..aeaabc1b58a915196ff5cbeb4c5b02cc01fe965c 100644 --- a/core/vendor/composer/autoload_psr4.php +++ b/core/vendor/composer/autoload_psr4.php @@ -28,15 +28,15 @@ 'Symfony\\Component\\BrowserKit\\' => array($vendorDir . '/symfony/browser-kit'), 'Symfony\\Cmf\\Component\\Routing\\' => array($vendorDir . '/symfony-cmf/routing'), 'Symfony\\Bridge\\PsrHttpMessage\\' => array($vendorDir . '/symfony/psr-http-message-bridge'), - 'React\\Promise\\' => array($vendorDir . '/react/promise/src'), 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'), 'Masterminds\\' => array($vendorDir . '/masterminds/html5/src'), - 'GuzzleHttp\\Stream\\' => array($vendorDir . '/guzzlehttp/streams/src'), - 'GuzzleHttp\\Ring\\' => array($vendorDir . '/guzzlehttp/ringphp/src'), + 'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'), + 'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'), 'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'), 'Goutte\\' => array($vendorDir . '/fabpot/goutte/Goutte'), 'Drupal\\Driver\\' => array($baseDir . '/../drivers/lib/Drupal/Driver'), 'Drupal\\Core\\' => array($baseDir . '/lib/Drupal/Core'), 'Drupal\\Component\\' => array($baseDir . '/lib/Drupal/Component'), + 'Behat\\Mink\\Driver\\' => array($vendorDir . '/behat/mink-goutte-driver/src'), 'Behat\\Mink\\' => array($vendorDir . '/behat/mink/src'), ); diff --git a/core/vendor/composer/installed.json b/core/vendor/composer/installed.json index d8f6af5289e3e80749dee4fbf8cb0b0bb62223a7..cb25fbe900e4a1d9a5953ff32678d6a3b53a7251 100644 --- a/core/vendor/composer/installed.json +++ b/core/vendor/composer/installed.json @@ -275,58 +275,6 @@ "parser" ] }, - { - "name": "guzzlehttp/streams", - "version": "3.0.0", - "version_normalized": "3.0.0.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/streams.git", - "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/streams/zipball/47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5", - "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5", - "shasum": "" - }, - "require": { - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "time": "2014-10-12 19:18:40", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "GuzzleHttp\\Stream\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Provides a simple abstraction over streams of data", - "homepage": "http://guzzlephp.org/", - "keywords": [ - "Guzzle", - "stream" - ] - }, { "name": "phpunit/php-text-template", "version": "1.2.0", @@ -987,157 +935,6 @@ "testing" ] }, - { - "name": "behat/mink-goutte-driver", - "version": "v1.1.0", - "version_normalized": "1.1.0.0", - "source": { - "type": "git", - "url": "https://github.com/minkphp/MinkGoutteDriver.git", - "reference": "2bf327b4166694ecaa8ae7f956cb6ae252ecf03e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/minkphp/MinkGoutteDriver/zipball/2bf327b4166694ecaa8ae7f956cb6ae252ecf03e", - "reference": "2bf327b4166694ecaa8ae7f956cb6ae252ecf03e", - "shasum": "" - }, - "require": { - "behat/mink": "~1.6@dev", - "behat/mink-browserkit-driver": "~1.2@dev", - "fabpot/goutte": "~1.0.4|~2.0", - "php": ">=5.3.1" - }, - "time": "2014-10-09 09:21:12", - "type": "mink-driver", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-0": { - "Behat\\Mink\\Driver": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - } - ], - "description": "Goutte driver for Mink framework", - "homepage": "http://mink.behat.org/", - "keywords": [ - "browser", - "goutte", - "headless", - "testing" - ] - }, - { - "name": "fabpot/goutte", - "version": "v2.0.3", - "version_normalized": "2.0.3.0", - "source": { - "type": "git", - "url": "https://github.com/FriendsOfPHP/Goutte.git", - "reference": "65ab61eae03d670b93a9044ad2328eb81aa1bde5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/Goutte/zipball/65ab61eae03d670b93a9044ad2328eb81aa1bde5", - "reference": "65ab61eae03d670b93a9044ad2328eb81aa1bde5", - "shasum": "" - }, - "require": { - "guzzlehttp/guzzle": ">=4,<6", - "php": ">=5.4.0", - "symfony/browser-kit": "~2.1", - "symfony/css-selector": "~2.1", - "symfony/dom-crawler": "~2.1" - }, - "time": "2014-11-28 09:48:17", - "type": "application", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Goutte\\": "Goutte" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - } - ], - "description": "A simple PHP Web Scraper", - "homepage": "https://github.com/FriendsOfPHP/Goutte", - "keywords": [ - "scraper" - ] - }, - { - "name": "react/promise", - "version": "v2.2.0", - "version_normalized": "2.2.0.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/promise.git", - "reference": "365fcee430dfa4ace1fbc75737ca60ceea7eeeef" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/365fcee430dfa4ace1fbc75737ca60ceea7eeeef", - "reference": "365fcee430dfa4ace1fbc75737ca60ceea7eeeef", - "shasum": "" - }, - "require": { - "php": ">=5.4.0" - }, - "time": "2014-12-30 13:32:42", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "React\\Promise\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jan Sorgalla", - "email": "jsorgalla@googlemail.com" - } - ], - "description": "A lightweight implementation of CommonJS Promises/A for PHP" - }, { "name": "masterminds/html5", "version": "2.1.0", @@ -1205,119 +1002,6 @@ "xml" ] }, - { - "name": "guzzlehttp/guzzle", - "version": "5.2.0", - "version_normalized": "5.2.0.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "475b29ccd411f2fa8a408e64576418728c032cfa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/475b29ccd411f2fa8a408e64576418728c032cfa", - "reference": "475b29ccd411f2fa8a408e64576418728c032cfa", - "shasum": "" - }, - "require": { - "guzzlehttp/ringphp": "~1.0", - "php": ">=5.4.0" - }, - "require-dev": { - "ext-curl": "*", - "phpunit/phpunit": "~4.0", - "psr/log": "~1.0" - }, - "time": "2015-01-28 01:03:29", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "GuzzleHttp\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", - "homepage": "http://guzzlephp.org/", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "rest", - "web service" - ] - }, - { - "name": "guzzlehttp/ringphp", - "version": "1.0.7", - "version_normalized": "1.0.7.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/RingPHP.git", - "reference": "52d868f13570a9a56e5fce6614e0ec75d0f13ac2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/RingPHP/zipball/52d868f13570a9a56e5fce6614e0ec75d0f13ac2", - "reference": "52d868f13570a9a56e5fce6614e0ec75d0f13ac2", - "shasum": "" - }, - "require": { - "guzzlehttp/streams": "~3.0", - "php": ">=5.4.0", - "react/promise": "~2.0" - }, - "require-dev": { - "ext-curl": "*", - "phpunit/phpunit": "~4.0" - }, - "suggest": { - "ext-curl": "Guzzle will use specific adapters if cURL is present" - }, - "time": "2015-03-30 01:43:20", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "GuzzleHttp\\Ring\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function." - }, { "name": "zendframework/zend-stdlib", "version": "2.4.0", @@ -3507,6 +3191,288 @@ "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com" }, + { + "name": "guzzlehttp/psr7", + "version": "1.1.0", + "version_normalized": "1.1.0.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "af0e1758de355eb113917ad79c3c0e3604bce4bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/af0e1758de355eb113917ad79c3c0e3604bce4bd", + "reference": "af0e1758de355eb113917ad79c3c0e3604bce4bd", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "time": "2015-06-24 19:55:15", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "PSR-7 message implementation", + "keywords": [ + "http", + "message", + "stream", + "uri" + ] + }, + { + "name": "guzzlehttp/promises", + "version": "1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "2ee5bc7f1a92efecc90da7f6711a53a7be26b5b7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/2ee5bc7f1a92efecc90da7f6711a53a7be26b5b7", + "reference": "2ee5bc7f1a92efecc90da7f6711a53a7be26b5b7", + "shasum": "" + }, + "require": { + "php": ">=5.5.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "time": "2015-06-24 16:16:25", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ] + }, + { + "name": "guzzlehttp/guzzle", + "version": "dev-master", + "version_normalized": "9999999-dev", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "1879fbe853b0c64d109e369c7aeff09849e62d1e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/1879fbe853b0c64d109e369c7aeff09849e62d1e", + "reference": "1879fbe853b0c64d109e369c7aeff09849e62d1e", + "shasum": "" + }, + "require": { + "guzzlehttp/promises": "~1.0", + "guzzlehttp/psr7": "~1.1", + "php": ">=5.5.0" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "~4.0", + "psr/log": "~1.0" + }, + "time": "2015-07-10 20:04:21", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ] + }, + { + "name": "fabpot/goutte", + "version": "v3.1.0", + "version_normalized": "3.1.0.0", + "source": { + "type": "git", + "url": "https://github.com/FriendsOfPHP/Goutte.git", + "reference": "d9a5a28782d30e9f4e20176caea58a1d459f2c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FriendsOfPHP/Goutte/zipball/d9a5a28782d30e9f4e20176caea58a1d459f2c71", + "reference": "d9a5a28782d30e9f4e20176caea58a1d459f2c71", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.0", + "php": ">=5.5.0", + "symfony/browser-kit": "~2.1", + "symfony/css-selector": "~2.1", + "symfony/dom-crawler": "~2.1" + }, + "time": "2015-06-24 16:11:31", + "type": "application", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Goutte\\": "Goutte" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "A simple PHP Web Scraper", + "homepage": "https://github.com/FriendsOfPHP/Goutte", + "keywords": [ + "scraper" + ] + }, + { + "name": "behat/mink-goutte-driver", + "version": "dev-master", + "version_normalized": "9999999-dev", + "source": { + "type": "git", + "url": "https://github.com/minkphp/MinkGoutteDriver.git", + "reference": "cc5ce119b5a8e06662f634b35967aff0b0c7dfdd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/minkphp/MinkGoutteDriver/zipball/cc5ce119b5a8e06662f634b35967aff0b0c7dfdd", + "reference": "cc5ce119b5a8e06662f634b35967aff0b0c7dfdd", + "shasum": "" + }, + "require": { + "behat/mink": "~1.6@dev", + "behat/mink-browserkit-driver": "~1.2@dev", + "fabpot/goutte": "~1.0.4|~2.0|~3.1", + "php": ">=5.3.1" + }, + "time": "2015-06-27 00:15:11", + "type": "mink-driver", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Behat\\Mink\\Driver\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + } + ], + "description": "Goutte driver for Mink framework", + "homepage": "http://mink.behat.org/", + "keywords": [ + "browser", + "goutte", + "headless", + "testing" + ] + }, { "name": "egulias/email-validator", "version": "1.2.9", diff --git a/core/vendor/fabpot/goutte/.travis.yml b/core/vendor/fabpot/goutte/.travis.yml index 66cf3207efa2b8878cadadb1e8177263d31e9522..26a4ed153344e85741d1d77a084fd312821ea927 100644 --- a/core/vendor/fabpot/goutte/.travis.yml +++ b/core/vendor/fabpot/goutte/.travis.yml @@ -1,9 +1,9 @@ language: php php: + - 7.0 - 5.6 - 5.5 - - 5.4 - hhvm install: @@ -15,3 +15,4 @@ script: matrix: allow_failures: - php: hhvm + - php: 7.0 diff --git a/core/vendor/fabpot/goutte/Goutte/Client.php b/core/vendor/fabpot/goutte/Goutte/Client.php index cde3ea375431cec96775b2be8fb37fe8ae011c04..609595d733d21edb6be9d2a8ce0d06cea71bd8e4 100644 --- a/core/vendor/fabpot/goutte/Goutte/Client.php +++ b/core/vendor/fabpot/goutte/Goutte/Client.php @@ -13,10 +13,9 @@ use GuzzleHttp\Client as GuzzleClient; use GuzzleHttp\ClientInterface as GuzzleClientInterface; +use GuzzleHttp\Cookie\CookieJar; use GuzzleHttp\Exception\RequestException; -use GuzzleHttp\Message\RequestInterface; -use GuzzleHttp\Message\Response as GuzzleResponse; -use GuzzleHttp\Post\PostFile; +use Psr\Http\Message\ResponseInterface; use Symfony\Component\BrowserKit\Client as BaseClient; use Symfony\Component\BrowserKit\Response; @@ -25,6 +24,7 @@ * * @author Fabien Potencier <fabien.potencier@symfony-project.com> * @author Michael Dowling <michael@guzzlephp.org> + * @author Charles Sarrazin <charles@sarraz.in> */ class Client extends BaseClient { @@ -90,45 +90,46 @@ protected function doRequest($request) } } - $body = null; - if (!in_array($request->getMethod(), array('GET', 'HEAD'))) { - if (null !== $request->getContent()) { - $body = $request->getContent(); - } else { - $body = $request->getParameters(); - } - } - - $this->getClient()->setDefaultOption('auth', $this->auth); + $cookies = CookieJar::fromArray( + $this->getCookieJar()->allRawValues($request->getUri()), + $request->getServer()['HTTP_HOST'] + ); $requestOptions = array( - 'body' => $body, - 'cookies' => $this->getCookieJar()->allRawValues($request->getUri()), + 'cookies' => $cookies, 'allow_redirects' => false, - 'timeout' => 30, + 'auth' => $this->auth, ); + if (!in_array($request->getMethod(), array('GET', 'HEAD'))) { + if (null !== $content = $request->getContent()) { + $requestOptions['body'] = $content; + } else { + if ($files = $request->getFiles()) { + $requestOptions['multipart'] = []; + + $this->addPostFields($request->getParameters(), $requestOptions['multipart']); + $this->addPostFiles($files, $requestOptions['multipart']); + } else { + $requestOptions['form_params'] = $request->getParameters(); + } + } + } + if (!empty($headers)) { $requestOptions['headers'] = $headers; } - $guzzleRequest = $this->getClient()->createRequest( - $request->getMethod(), - $request->getUri(), - $requestOptions - ); + $method = $request->getMethod(); + $uri = $request->getUri(); foreach ($this->headers as $name => $value) { - $guzzleRequest->setHeader($name, $value); - } - - if ('POST' == $request->getMethod() || 'PUT' == $request->getMethod()) { - $this->addPostFiles($guzzleRequest, $request->getFiles()); + $requestOptions['headers'][$name] = $value; } // Let BrowserKit handle redirects try { - $response = $this->getClient()->send($guzzleRequest); + $response = $this->getClient()->request($method, $uri, $requestOptions); } catch (RequestException $e) { $response = $e->getResponse(); if (null === $response) { @@ -139,33 +140,63 @@ protected function doRequest($request) return $this->createResponse($response); } - protected function addPostFiles(RequestInterface $request, array $files, $arrayName = '') + protected function addPostFiles(array $files, array &$multipart, $arrayName = '') { + if (empty($files)) { + return; + } + foreach ($files as $name => $info) { if (!empty($arrayName)) { $name = $arrayName.'['.$name.']'; } + $file = [ + 'name' => $name, + ]; + if (is_array($info)) { if (isset($info['tmp_name'])) { if ('' !== $info['tmp_name']) { - $request->getBody()->addFile(new PostFile($name, fopen($info['tmp_name'], 'r'), isset($info['name']) ? $info['name'] : null)); + $file['contents'] = fopen($info['tmp_name'], 'r'); + if (isset($info['name'])) { + $file['filename'] = $info['name']; + } } else { continue; } } else { - $this->addPostFiles($request, $info, $name); + $this->addPostFiles($info, $multipart, $name); + continue; } } else { - $request->getBody()->addFile(new PostFile($name, fopen($info, 'r'))); + $file['contents'] = fopen($info, 'r'); } + + $multipart[] = $file; } } - protected function createResponse(GuzzleResponse $response) + public function addPostFields(array $formParams, array &$multipart, $arrayName = '') { - $headers = $response->getHeaders(); + foreach ($formParams as $name => $value) { + if (!empty($arrayName)) { + $name = $arrayName.'['.$name.']'; + } - return new Response($response->getBody(true), $response->getStatusCode(), $headers); + if (is_array($value)) { + $this->addPostFields($value, $multipart, $name); + } else { + $multipart[] = [ + 'name' => $name, + 'contents' => $value, + ]; + } + } + } + + protected function createResponse(ResponseInterface $response) + { + return new Response((string) $response->getBody(), $response->getStatusCode(), $response->getHeaders()); } } diff --git a/core/vendor/fabpot/goutte/Goutte/Tests/ClientTest.php b/core/vendor/fabpot/goutte/Goutte/Tests/ClientTest.php index da2e47c2a67c75db153ec97dddf03f581f59e19d..f10d8fbea0dffd921b992d2c4c1ea849ef141230 100644 --- a/core/vendor/fabpot/goutte/Goutte/Tests/ClientTest.php +++ b/core/vendor/fabpot/goutte/Goutte/Tests/ClientTest.php @@ -14,31 +14,34 @@ use Goutte\Client; use GuzzleHttp\Client as GuzzleClient; use GuzzleHttp\Exception\RequestException; -use GuzzleHttp\Message\Response as GuzzleResponse; -use GuzzleHttp\Stream\Stream; -use GuzzleHttp\Subscriber\History; -use GuzzleHttp\Subscriber\Mock; -use GuzzleHttp\Post\PostFile; +use GuzzleHttp\Handler\MockHandler; +use GuzzleHttp\HandlerStack; +use GuzzleHttp\Psr7\Response as GuzzleResponse; +use GuzzleHttp\Middleware; use Symfony\Component\BrowserKit\Cookie; /** - * Goutte Client Test + * Goutte Client Test. * * @author Michael Dowling <michael@guzzlephp.org> + * @author Charles Sarrazin <charles@sarraz.in> */ class ClientTest extends \PHPUnit_Framework_TestCase { protected $history; + /** @var MockHandler */ protected $mock; - protected function getGuzzle() + protected function getGuzzle(array $responses = []) { - $this->history = new History(); - $this->mock = new Mock(); - $this->mock->addResponse(new GuzzleResponse(200, array(), Stream::factory('<html><body><p>Hi</p></body></html>'))); - $guzzle = new GuzzleClient(array('redirect.disable' => true, 'base_url' => '')); - $guzzle->getEmitter()->attach($this->mock); - $guzzle->getEmitter()->attach($this->history); + if (empty($responses)) { + $responses = [new GuzzleResponse(200, [], '<html><body><p>Hi</p></body></html>')]; + } + $this->mock = new MockHandler($responses); + $handlerStack = HandlerStack::create($this->mock); + $this->history = []; + $handlerStack->push(Middleware::history($this->history)); + $guzzle = new GuzzleClient(array('redirect.disable' => true, 'base_uri' => '', 'handler' => $handlerStack)); return $guzzle; } @@ -63,8 +66,8 @@ public function testUsesCustomHeaders() $client = new Client(); $client->setClient($guzzle); $client->setHeader('X-Test', 'test'); - $crawler = $client->request('GET', 'http://www.example.com/'); - $this->assertEquals('test', $this->history->getLastRequest()->getHeader('X-Test')); + $client->request('GET', 'http://www.example.com/'); + $this->assertEquals('test', end($this->history)['request']->getHeaderLine('X-Test')); } public function testCustomUserAgent() @@ -73,8 +76,8 @@ public function testCustomUserAgent() $client = new Client(); $client->setClient($guzzle); $client->setHeader('User-Agent', 'foo'); - $crawler = $client->request('GET', 'http://www.example.com/'); - $this->assertEquals('foo', $this->history->getLastRequest()->getHeader('User-Agent')); + $client->request('GET', 'http://www.example.com/'); + $this->assertEquals('Symfony2 BrowserKit, foo', end($this->history)['request']->getHeaderLine('User-Agent')); } public function testUsesAuth() @@ -83,11 +86,9 @@ public function testUsesAuth() $client = new Client(); $client->setClient($guzzle); $client->setAuth('me', '**'); - $crawler = $client->request('GET', 'http://www.example.com/'); - $request = $this->history->getLastRequest(); - $this->assertEquals('me', $request->getConfig()->get('auth')[0]); - $this->assertEquals('**', $request->getConfig()->get('auth')[1]); - $this->assertEquals('basic', $request->getConfig()->get('auth')[2]); + $client->request('GET', 'http://www.example.com/'); + $request = end($this->history)['request']; + $this->assertEquals('Basic bWU6Kio=', $request->getHeaderLine('Authorization')); } public function testResetsAuth() @@ -97,10 +98,9 @@ public function testResetsAuth() $client->setClient($guzzle); $client->setAuth('me', '**'); $client->resetAuth(); - $crawler = $client->request('GET', 'http://www.example.com/'); - $request = $this->history->getLastRequest(); - $this->assertNull($request->getConfig()->get('auth')[0]); - $this->assertNull($request->getConfig()->get('auth')[1]); + $client->request('GET', 'http://www.example.com/'); + $request = end($this->history)['request']; + $this->assertEquals('', $request->getHeaderLine('authorization')); } public function testUsesCookies() @@ -109,9 +109,9 @@ public function testUsesCookies() $client = new Client(); $client->setClient($guzzle); $client->getCookieJar()->set(new Cookie('test', '123')); - $crawler = $client->request('GET', 'http://www.example.com/'); - $request = $this->history->getLastRequest(); - $this->assertEquals('test=123', $request->getHeader('Cookie')); + $client->request('GET', 'http://www.example.com/'); + $request = end($this->history)['request']; + $this->assertEquals('test=123', $request->getHeaderLine('Cookie')); } public function testUsesPostFiles() @@ -122,18 +122,20 @@ public function testUsesPostFiles() $files = array( 'test' => array( 'name' => 'test.txt', - 'tmp_name' => __FILE__, + 'tmp_name' => __DIR__.'/fixtures.txt', ), ); - $crawler = $client->request('POST', 'http://www.example.com/', array(), $files); - $request = $this->history->getLastRequest(); + $client->request('POST', 'http://www.example.com/', array(), $files); + $request = end($this->history)['request']; - $files = $request->getBody()->getFiles(); - $this->assertFile(reset($files), 'test', 'test.txt', array( - 'Content-Type' => 'text/plain', - 'Content-Disposition' => 'form-data; filename="test.txt"; name="test"', - )); + $stream = $request->getBody(); + $boundary = $stream->getBoundary(); + $this->assertEquals( + "--$boundary\r\nContent-Disposition: form-data; name=\"test\"; filename=\"test.txt\"\r\nContent-Length: 4\r\n" + ."Content-Type: text/plain\r\n\r\nfoo\n\r\n--$boundary--\r\n", + $stream->getContents() + ); } public function testUsesPostNamedFiles() @@ -142,16 +144,19 @@ public function testUsesPostNamedFiles() $client = new Client(); $client->setClient($guzzle); $files = array( - 'test' => __FILE__, + 'test' => __DIR__.'/fixtures.txt', ); - $crawler = $client->request('POST', 'http://www.example.com/', array(), $files); - $request = $this->history->getLastRequest(); - $files = $request->getBody()->getFiles(); - $this->assertFile(reset($files), 'test', __FILE__, array( - 'Content-Type' => 'text/x-php', - 'Content-Disposition' => 'form-data; filename="ClientTest.php"; name="test"', - )); + $client->request('POST', 'http://www.example.com/', array(), $files); + $request = end($this->history)['request']; + + $stream = $request->getBody(); + $boundary = $stream->getBoundary(); + $this->assertEquals( + "--$boundary\r\nContent-Disposition: form-data; name=\"test\"; filename=\"fixtures.txt\"\r\nContent-Length: 4\r\n" + ."Content-Type: text/plain\r\n\r\nfoo\n\r\n--$boundary--\r\n", + $stream->getContents() + ); } public function testUsesPostFilesNestedFields() @@ -163,18 +168,73 @@ public function testUsesPostFilesNestedFields() 'form' => array( 'test' => array( 'name' => 'test.txt', - 'tmp_name' => __FILE__, + 'tmp_name' => __DIR__.'/fixtures.txt', ), ), ); - $crawler = $client->request('POST', 'http://www.example.com/', array(), $files); - $request = $this->history->getLastRequest(); - $files = $request->getBody()->getFiles(); - $this->assertFile(reset($files), 'form[test]', 'test.txt', array( - 'Content-Type' => 'text/plain', - 'Content-Disposition' => 'form-data; filename="test.txt"; name="form[test]"', - )); + $client->request('POST', 'http://www.example.com/', array(), $files); + $request = end($this->history)['request']; + + $stream = $request->getBody(); + $boundary = $stream->getBoundary(); + $this->assertEquals( + "--$boundary\r\nContent-Disposition: form-data; name=\"form[test]\"; filename=\"test.txt\"\r\nContent-Length: 4\r\n" + ."Content-Type: text/plain\r\n\r\nfoo\n\r\n--$boundary--\r\n", + $stream->getContents() + ); + } + + public function testPostFormWithFiles() + { + $guzzle = $this->getGuzzle(); + $client = new Client(); + $client->setClient($guzzle); + $files = array( + 'test' => __DIR__.'/fixtures.txt', + ); + $params = array( + 'foo' => 'bar', + ); + + $client->request('POST', 'http://www.example.com/', $params, $files); + $request = end($this->history)['request']; + + $stream = $request->getBody(); + $boundary = $stream->getBoundary(); + $this->assertEquals( + "--$boundary\r\nContent-Disposition: form-data; name=\"foo\"\r\nContent-Length: 3\r\n" + ."\r\nbar\r\n" + ."--$boundary\r\nContent-Disposition: form-data; name=\"test\"; filename=\"fixtures.txt\"\r\nContent-Length: 4\r\n" + ."Content-Type: text/plain\r\n\r\nfoo\n\r\n--$boundary--\r\n", + $stream->getContents()); + } + + public function testPostEmbeddedFormWithFiles() + { + $guzzle = $this->getGuzzle(); + $client = new Client(); + $client->setClient($guzzle); + $files = array( + 'test' => __DIR__.'/fixtures.txt', + ); + $params = array( + 'foo' => array( + 'bar' => 'baz', + ), + ); + + $client->request('POST', 'http://www.example.com/', $params, $files); + $request = end($this->history)['request']; + + $stream = $request->getBody(); + $boundary = $stream->getBoundary(); + $this->assertEquals( + "--$boundary\r\nContent-Disposition: form-data; name=\"foo[bar]\"\r\nContent-Length: 3\r\n" + ."\r\nbaz\r\n" + ."--$boundary\r\nContent-Disposition: form-data; name=\"test\"; filename=\"fixtures.txt\"\r\nContent-Length: 4\r\n" + ."Content-Type: text/plain\r\n\r\nfoo\n\r\n--$boundary--\r\n", + $stream->getContents()); } public function testUsesPostFilesOnClientSide() @@ -183,16 +243,19 @@ public function testUsesPostFilesOnClientSide() $client = new Client(); $client->setClient($guzzle); $files = array( - 'test' => __FILE__, + 'test' => __DIR__.'/fixtures.txt', ); - $crawler = $client->request('POST', 'http://www.example.com/', array(), $files); - $request = $this->history->getLastRequest(); - $files = $request->getBody()->getFiles(); - $this->assertFile(reset($files), 'test', __FILE__, array( - 'Content-Type' => 'text/x-php', - 'Content-Disposition' => 'form-data; filename="ClientTest.php"; name="test"', - )); + $client->request('POST', 'http://www.example.com/', array(), $files); + $request = end($this->history)['request']; + + $stream = $request->getBody(); + $boundary = $stream->getBoundary(); + $this->assertEquals( + "--$boundary\r\nContent-Disposition: form-data; name=\"test\"; filename=\"fixtures.txt\"\r\nContent-Length: 4\r\n" + ."Content-Type: text/plain\r\n\r\nfoo\n\r\n--$boundary--\r\n", + $stream->getContents() + ); } public function testUsesPostFilesUploadError() @@ -210,10 +273,12 @@ public function testUsesPostFilesUploadError() ), ); - $crawler = $client->request('POST', 'http://www.example.com/', array(), $files); - $request = $this->history->getLastRequest(); + $client->request('POST', 'http://www.example.com/', array(), $files); + $request = end($this->history)['request']; + $stream = $request->getBody(); + $boundary = $stream->getBoundary(); - $this->assertEquals(array(), $request->getBody()->getFiles()); + $this->assertEquals("--$boundary--\r\n", $stream->getContents()); } public function testCreatesResponse() @@ -227,13 +292,12 @@ public function testCreatesResponse() public function testHandlesRedirectsCorrectly() { - $guzzle = $this->getGuzzle(); - - $this->mock->clearQueue(); - $this->mock->addResponse(new GuzzleResponse(301, array( - 'Location' => 'http://www.example.com/', - ))); - $this->mock->addResponse(new GuzzleResponse(200, [], Stream::factory('<html><body><p>Test</p></body></html>'))); + $guzzle = $this->getGuzzle([ + new GuzzleResponse(301, array( + 'Location' => 'http://www.example.com/', + )), + new GuzzleResponse(200, [], '<html><body><p>Test</p></body></html>'), + ]); $client = new Client(); $client->setClient($guzzle); @@ -247,12 +311,11 @@ public function testHandlesRedirectsCorrectly() public function testConvertsGuzzleHeadersToArrays() { - $guzzle = $this->getGuzzle(); - - $this->mock->clearQueue(); - $this->mock->addResponse(new GuzzleResponse(200, array( - 'Date' => 'Tue, 04 Jun 2013 13:22:41 GMT', - ))); + $guzzle = $this->getGuzzle([ + new GuzzleResponse(200, array( + 'Date' => 'Tue, 04 Jun 2013 13:22:41 GMT', + )), + ]); $client = new Client(); $client->setClient($guzzle); @@ -260,48 +323,30 @@ public function testConvertsGuzzleHeadersToArrays() $response = $client->getResponse(); $headers = $response->getHeaders(); - $this->assertInternalType("array", array_shift($headers), "Header not converted from Guzzle\Http\Message\Header to array"); + $this->assertInternalType('array', array_shift($headers), 'Header not converted from Guzzle\Http\Message\Header to array'); } public function testNullResponseException() { $this->setExpectedException('GuzzleHttp\Exception\RequestException'); - $guzzle = $this->getGuzzle(); - $this->mock->clearQueue(); - $exception = new RequestException('', $this->getMock('GuzzleHttp\Message\RequestInterface')); - $this->mock->addException($exception); + $guzzle = $this->getGuzzle([ + new RequestException('', $this->getMock('Psr\Http\Message\RequestInterface')), + ]); $client = new Client(); $client->setClient($guzzle); $client->request('GET', 'http://www.example.com/'); - $response = $client->getResponse(); - } - - protected function assertFile(PostFile $postFile, $fieldName, $fileName, $headers) - { - $this->assertEquals($postFile->getName(), $fieldName); - $this->assertEquals($postFile->getFilename(), $fileName); - - $postFileHeaders = $postFile->getHeaders(); - - // Note: Sort 'Content-Disposition' values before comparing, because the order changed in Guzzle 4.2.2 - $postFileHeaders['Content-Disposition'] = explode('; ', $postFileHeaders['Content-Disposition']); - sort($postFileHeaders['Content-Disposition']); - $headers['Content-Disposition'] = explode('; ', $headers['Content-Disposition']); - sort($headers['Content-Disposition']); - - $this->assertEquals($postFileHeaders, $headers); + $client->getResponse(); } public function testHttps() { - $guzzle = $this->getGuzzle(); + $guzzle = $this->getGuzzle([ + new GuzzleResponse(200, [], '<html><body><p>Test</p></body></html>'), + ]); - $this->mock->clearQueue(); - $this->mock->addResponse(new GuzzleResponse(200, [], Stream::factory('<html><body><p>Test</p></body></html>'))); $client = new Client(); $client->setClient($guzzle); $crawler = $client->request('GET', 'https://www.example.com/'); - $this->assertEquals('https', $this->history->getLastRequest()->getScheme()); $this->assertEquals('Test', $crawler->filter('p')->text()); } @@ -313,7 +358,7 @@ public function testCustomUserAgentConstructor() 'HTTP_USER_AGENT' => 'SomeHost', ]); $client->setClient($guzzle); - $crawler = $client->request('GET', 'http://www.example.com/'); - $this->assertEquals('SomeHost', $this->history->getLastRequest()->getHeader('User-Agent')); + $client->request('GET', 'http://www.example.com/'); + $this->assertEquals('SomeHost', end($this->history)['request']->getHeaderLine('User-Agent')); } } diff --git a/core/vendor/fabpot/goutte/Goutte/Tests/fixtures.txt b/core/vendor/fabpot/goutte/Goutte/Tests/fixtures.txt new file mode 100644 index 0000000000000000000000000000000000000000..257cc5642cb1a054f08cc83f2d943e56fd3ebe99 --- /dev/null +++ b/core/vendor/fabpot/goutte/Goutte/Tests/fixtures.txt @@ -0,0 +1 @@ +foo diff --git a/core/vendor/fabpot/goutte/README.rst b/core/vendor/fabpot/goutte/README.rst index 58eecdb5341fabd8d4610ca29006bfc3d3116ed3..c1ade391f54f08ef4c84d486b45de510ab6b209b 100644 --- a/core/vendor/fabpot/goutte/README.rst +++ b/core/vendor/fabpot/goutte/README.rst @@ -9,11 +9,12 @@ responses. Requirements ------------ -Goutte depends on PHP 5.4+ and Guzzle 4+. +Goutte depends on PHP 5.5+ and Guzzle 6+. .. tip:: - If you need support for PHP 5.3 or Guzzle 3, use Goutte 1.0.6. + If you need support for PHP 5.4 or Guzzle 4-5, use Goutte 2.x. + If you need support for PHP 5.3 or Guzzle 3, use Goutte 1.x. Installation ------------ @@ -22,7 +23,7 @@ Add ``fabpot/goutte`` as a require dependency in your ``composer.json`` file: .. code-block:: bash - php composer.phar require fabpot/goutte:~2.0 + composer require fabpot/goutte .. tip:: @@ -32,6 +33,9 @@ Add ``fabpot/goutte`` as a require dependency in your ``composer.json`` file: require_once '/path/to/goutte.phar'; + The phars for Goutte 1.x are also available for `download + <http://get.sensiolabs.org/goutte-v1.0.7.phar>`. + Usage ----- @@ -73,7 +77,7 @@ Extract data: .. code-block:: php // Get the latest post in this category and display the titles - $crawler->filter('h2.post > a')->each(function ($node) { + $crawler->filter('h2 > a')->each(function ($node) { print $node->text()."\n"; }); @@ -92,8 +96,14 @@ Submit forms: More Information ---------------- -Read the documentation of the BrowserKit and DomCrawler Symfony Components for -more information about what you can do with Goutte. +Read the documentation of the BrowserKit and `DomCrawler +<http://symfony.com/doc/any/components/dom_crawler.html>`_ Symfony Components +for more information about what you can do with Goutte. + +Pronunciation +------------- + +Goutte is pronounced ``goot`` i.e. it rhymes with ``boot`` and not ``out``. Technical Information --------------------- diff --git a/core/vendor/fabpot/goutte/composer.json b/core/vendor/fabpot/goutte/composer.json index 63d87e72d6787974a50f90cfec731b02e80c5ed7..1a0ed82f1b2a7dbd162a9e0d70d53530f63fbbfd 100644 --- a/core/vendor/fabpot/goutte/composer.json +++ b/core/vendor/fabpot/goutte/composer.json @@ -12,18 +12,18 @@ } ], "require": { - "php": ">=5.4.0", + "php": ">=5.5.0", "symfony/browser-kit": "~2.1", "symfony/css-selector": "~2.1", "symfony/dom-crawler": "~2.1", - "guzzlehttp/guzzle": ">=4,<6" + "guzzlehttp/guzzle": "^6.0" }, "autoload": { "psr-4": { "Goutte\\": "Goutte" } }, "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.1-dev" } } } diff --git a/core/vendor/guzzlehttp/guzzle/.gitignore b/core/vendor/guzzlehttp/guzzle/.gitignore deleted file mode 100644 index 83ec41e24427fbd121fef56d827ff34e46fd50fe..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -phpunit.xml -composer.phar -composer.lock -composer-test.lock -vendor/ -build/artifacts/ -artifacts/ -docs/_build -docs/*.pyc -.idea -.DS_STORE diff --git a/core/vendor/guzzlehttp/guzzle/.travis.yml b/core/vendor/guzzlehttp/guzzle/.travis.yml index 77bcf4a7003a81beac976cafbb6bf72b194baae2..c0689d16100a144d8a81fd7ba88876d8a72d543e 100644 --- a/core/vendor/guzzlehttp/guzzle/.travis.yml +++ b/core/vendor/guzzlehttp/guzzle/.travis.yml @@ -1,15 +1,13 @@ language: php php: - - 5.4 - 5.5 - 5.6 + - 7.0 - hhvm before_script: - curl --version - - pear config-set php_ini ~/.phpenv/versions/`php -r 'echo phpversion();'`/etc/php.ini || echo 'Error modifying PEAR' - - pecl install uri_template || echo 'Error installing uri_template' - composer self-update - composer install --no-interaction --prefer-source --dev - ~/.nvm/nvm.sh install v0.6.14 @@ -20,6 +18,7 @@ script: make test matrix: allow_failures: - php: hhvm + - php: 7.0 fast_finish: true before_deploy: @@ -36,4 +35,4 @@ deploy: repo: guzzle/guzzle tags: true all_branches: true - php: 5.4 + php: 5.5 diff --git a/core/vendor/guzzlehttp/guzzle/CHANGELOG.md b/core/vendor/guzzlehttp/guzzle/CHANGELOG.md index caafb425fad9318f0dc2bd4188f22fdaa9cb70a7..d639f4e655fb44feda43baed018fd45f326d090c 100644 --- a/core/vendor/guzzlehttp/guzzle/CHANGELOG.md +++ b/core/vendor/guzzlehttp/guzzle/CHANGELOG.md @@ -1,5 +1,116 @@ # CHANGELOG +## 6.0.2 - 2015-07-04 + +* Fixed a memory leak in the curl handlers in which references to callbacks + were not being removed by `curl_reset`. +* Cookies are now extracted properly before redirects. +* Cookies now allow more character ranges. +* Decoded Content-Encoding responses are now modified to correctly reflect + their state if the encoding was automatically removed by a handler. This + means that the `Content-Encoding` header may be removed an the + `Content-Length` modified to reflect the message size after removing the + encoding. +* Added a more explicit error message when trying to use `form_params` and + `multipart` in the same request. +* Several fixes for HHVM support. +* Functions are now conditionally required using an additional level of + indirection to help with global Composer installations. + +## 6.0.1 - 2015-05-27 + +* Fixed a bug with serializing the `query` request option where the `&` + separator was missing. +* Added a better error message for when `body` is provided as an array. Please + use `form_params` or `multipart` instead. +* Various doc fixes. + +## 6.0.0 - 2015-05-26 + +* See the UPGRADING.md document for more information. +* Added `multipart` and `form_params` request options. +* Added `synchronous` request option. +* Added the `on_headers` request option. +* Fixed `expect` handling. +* No longer adding default middlewares in the client ctor. These need to be + present on the provided handler in order to work. +* Requests are no longer initiated when sending async requests with the + CurlMultiHandler. This prevents unexpected recursion from requests completing + while ticking the cURL loop. +* Removed the semantics of setting `default` to `true`. This is no longer + required now that the cURL loop is not ticked for async requests. +* Added request and response logging middleware. +* No longer allowing self signed certificates when using the StreamHandler. +* Ensuring that `sink` is valid if saving to a file. +* Request exceptions now include a "handler context" which provides handler + specific contextual information. +* Added `GuzzleHttp\RequestOptions` to allow request options to be applied + using constants. +* `$maxHandles` has been removed from CurlMultiHandler. +* `MultipartPostBody` is now part of the `guzzlehttp/psr7` package. + +## 5.3.0 - 2015-05-19 + +* Mock now supports `save_to` +* Marked `AbstractRequestEvent::getTransaction()` as public. +* Fixed a bug in which multiple headers using different casing would overwrite + previous headers in the associative array. +* Added `Utils::getDefaultHandler()` +* Marked `GuzzleHttp\Client::getDefaultUserAgent` as deprecated. +* URL scheme is now always lowercased. + +## 6.0.0-beta.1 + +* Requires PHP >= 5.5 +* Updated to use PSR-7 + * Requires immutable messages, which basically means an event based system + owned by a request instance is no longer possible. + * Utilizing the [Guzzle PSR-7 package](https://github.com/guzzle/psr7). + * Removed the dependency on `guzzlehttp/streams`. These stream abstractions + are available in the `guzzlehttp/psr7` package under the `GuzzleHttp\Psr7` + namespace. +* Added middleware and handler system + * Replaced the Guzzle event and subscriber system with a middleware system. + * No longer depends on RingPHP, but rather places the HTTP handlers directly + in Guzzle, operating on PSR-7 messages. + * Retry logic is now encapsulated in `GuzzleHttp\Middleware::retry`, which + means the `guzzlehttp/retry-subscriber` is now obsolete. + * Mocking responses is now handled using `GuzzleHttp\Handler\MockHandler`. +* Asynchronous responses + * No longer supports the `future` request option to send an async request. + Instead, use one of the `*Async` methods of a client (e.g., `requestAsync`, + `getAsync`, etc.). + * Utilizing `GuzzleHttp\Promise` instead of React's promise library to avoid + recursion required by chaining and forwarding react promises. See + https://github.com/guzzle/promises + * Added `requestAsync` and `sendAsync` to send request asynchronously. + * Added magic methods for `getAsync()`, `postAsync()`, etc. to send requests + asynchronously. +* Request options + * POST and form updates + * Added the `form_fields` and `form_files` request options. + * Removed the `GuzzleHttp\Post` namespace. + * The `body` request option no longer accepts an array for POST requests. + * The `exceptions` request option has been deprecated in favor of the + `http_errors` request options. + * The `save_to` request option has been deprecated in favor of `sink` request + option. +* Clients no longer accept an array of URI template string and variables for + URI variables. You will need to expand URI templates before passing them + into a client constructor or request method. +* Client methods `get()`, `post()`, `put()`, `patch()`, `options()`, etc. are + now magic methods that will send synchronous requests. +* Replaced `Utils.php` with plain functions in `functions.php`. +* Removed `GuzzleHttp\Collection`. +* Removed `GuzzleHttp\BatchResults`. Batched pool results are now returned as + an array. +* Removed `GuzzleHttp\Query`. Query string handling is now handled using an + associative array passed into the `query` request option. The query string + is serialized using PHP's `http_build_query`. If you need more control, you + can pass the query string in as a string. +* `GuzzleHttp\QueryParser` has been replaced with the + `GuzzleHttp\Psr7\parse_query`. + ## 5.2.0 - 2015-01-27 * Added `AppliesHeadersInterface` to make applying headers to a request based diff --git a/core/vendor/guzzlehttp/guzzle/LICENSE b/core/vendor/guzzlehttp/guzzle/LICENSE index 71d3b783cb5b82e732f4555c5b7839036334607b..9af9fba682889f586265b7ca29838fd889d2cd4c 100644 --- a/core/vendor/guzzlehttp/guzzle/LICENSE +++ b/core/vendor/guzzlehttp/guzzle/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com> +Copyright (c) 2011-2015 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/core/vendor/guzzlehttp/guzzle/Makefile b/core/vendor/guzzlehttp/guzzle/Makefile deleted file mode 100644 index 69bf3273e82ca74d98d4894d37217dc4c1bb27ef..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/Makefile +++ /dev/null @@ -1,50 +0,0 @@ -all: clean coverage docs - -start-server: - cd vendor/guzzlehttp/ringphp && make start-server - -stop-server: - cd vendor/guzzlehttp/ringphp && make stop-server - -test: start-server - vendor/bin/phpunit - $(MAKE) stop-server - -coverage: start-server - vendor/bin/phpunit --coverage-html=artifacts/coverage - $(MAKE) stop-server - -view-coverage: - open artifacts/coverage/index.html - -clean: - rm -rf artifacts/* - -docs: - cd docs && make html && cd .. - -view-docs: - open docs/_build/html/index.html - -tag: - $(if $(TAG),,$(error TAG is not defined. Pass via "make tag TAG=4.2.1")) - @echo Tagging $(TAG) - chag update $(TAG) - sed -i '' -e "s/VERSION = '.*'/VERSION = '$(TAG)'/" src/ClientInterface.php - php -l src/ClientInterface.php - git add -A - git commit -m '$(TAG) release' - chag tag - -perf: start-server - php tests/perf.php - $(MAKE) stop-server - -package: burgomaster - php build/packager.php - -burgomaster: - mkdir -p build/artifacts - curl -s https://raw.githubusercontent.com/mtdowling/Burgomaster/0.0.2/src/Burgomaster.php > build/artifacts/Burgomaster.php - -.PHONY: docs burgomaster diff --git a/core/vendor/guzzlehttp/guzzle/README.md b/core/vendor/guzzlehttp/guzzle/README.md index 9df698115a8eb1a724a8bf4957adf1a596598f52..c18c10b9e4d635e385b11fec2cd41e27ccfc77cb 100644 --- a/core/vendor/guzzlehttp/guzzle/README.md +++ b/core/vendor/guzzlehttp/guzzle/README.md @@ -1,25 +1,24 @@ -Guzzle, PHP HTTP client and webservice framework -================================================ +Guzzle, PHP HTTP client +======================= -[](http://travis-ci.org/guzzle/guzzle) +[](http://travis-ci.org/guzzle/guzzle) Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and trivial to integrate with web services. -- Manages things like persistent connections, represents query strings as - collections, simplifies sending streaming POST requests with fields and - files, and abstracts away the underlying HTTP transport layer. -- Can send both synchronous and asynchronous requests using the same interface - without requiring a dependency on a specific event loop. -- Pluggable HTTP adapters allows Guzzle to integrate with any method you choose - for sending HTTP requests over the wire (e.g., cURL, sockets, PHP's stream - wrapper, non-blocking event loops like ReactPHP. -- Guzzle makes it so that you no longer need to fool around with cURL options, - stream contexts, or sockets. +- Simple interface for building query strings, POST requests, streaming large + uploads, streaming large downloads, using HTTP cookies, uploading JSON data, + etc... +- Can send both synchronous and asynchronous requests using the same interface. +- Uses PSR-7 interfaces for requests, responses, and streams. This allows you + to utilize other PSR-7 compatible libraries with Guzzle. +- Abstracts away the underlying HTTP transport, allowing you to write + environment and transport agnostic code; i.e., no hard dependency on cURL, + PHP streams, sockets, or non-blocking event loops. +- Middleware system allows you to augment and compose client behavior. ```php $client = new GuzzleHttp\Client(); -$response = $client->get('http://guzzlephp.org'); $res = $client->get('https://api.github.com/user', ['auth' => ['user', 'pass']]); echo $res->getStatusCode(); // "200" @@ -27,22 +26,23 @@ echo $res->getHeader('content-type'); // 'application/json; charset=utf8' echo $res->getBody(); // {"type":"User"...' -var_export($res->json()); -// Outputs the JSON decoded data // Send an asynchronous request. -$req = $client->createRequest('GET', 'http://httpbin.org', ['future' => true]); -$client->send($req)->then(function ($response) { - echo 'I completed! ' . $response; +$request = new \GuzzleHttp\Psr7\Request('GET', 'http://httpbin.org'); +$promise = $client->sendAsync($request)->then(function ($response) { + echo 'I completed! ' . $response->getBody(); }); +$promise->wait(); ``` -Get more information and answers with the -[Documentation](http://guzzlephp.org/), -[Forums](https://groups.google.com/forum/?hl=en#!forum/guzzle), -and [Gitter](https://gitter.im/guzzle/guzzle). +## Help and docs -### Installing via Composer +- [Documentation](http://guzzlephp.org/) +- [stackoverflow](http://stackoverflow.com/questions/tagged/guzzle) +- [Gitter](https://gitter.im/guzzle/guzzle) + + +## Installing Guzzle The recommended way to install Guzzle is through [Composer](http://getcomposer.org). @@ -55,7 +55,7 @@ curl -sS https://getcomposer.org/installer | php Next, run the Composer command to install the latest stable version of Guzzle: ```bash -composer require guzzlehttp/guzzle +composer.phar require guzzlehttp/guzzle ``` After installing, you need to require Composer's autoloader: @@ -64,7 +64,25 @@ After installing, you need to require Composer's autoloader: require 'vendor/autoload.php'; ``` -### Documentation +You can then later update Guzzle using composer: + + ```bash +composer.phar update + ``` + + +## Version Guidance + +| Version | Status | Packagist | Namespace | Repo | Docs | PSR-7 | +|---------|-------------|---------------------|--------------|---------------------|---------------------|-------| +| 3.x | EOL | `guzzle/guzzle` | `Guzzle` | [v3][guzzle-3-repo] | [v3][guzzle-3-docs] | No | +| 4.x | EOL | `guzzlehttp/guzzle` | `GuzzleHttp` | N/A | N/A | No | +| 5.x | Maintained | `guzzlehttp/guzzle` | `GuzzleHttp` | [v5][guzzle-5-repo] | [v5][guzzle-5-docs] | No | +| 6.x | Latest | `guzzlehttp/guzzle` | `GuzzleHttp` | [v6][guzzle-6-repo] | [v6][guzzle-6-docs] | Yes | -More information can be found in the online documentation at -http://guzzlephp.org/. +[guzzle-3-repo]: https://github.com/guzzle/guzzle3 +[guzzle-5-repo]: https://github.com/guzzle/guzzle/tree/5.3 +[guzzle-6-repo]: https://github.com/guzzle/guzzle +[guzzle-3-docs]: http://guzzle3.readthedocs.org/en/latest/ +[guzzle-5-docs]: http://guzzle.readthedocs.org/en/5.3/ +[guzzle-6-docs]: http://guzzle.readthedocs.org/en/latest/ diff --git a/core/vendor/guzzlehttp/guzzle/UPGRADING.md b/core/vendor/guzzlehttp/guzzle/UPGRADING.md index ee8c39dd4695626a200f65dad08f24fef1ecb2f2..0e86214dafcaef16270d7d9625f750796cc8bab1 100644 --- a/core/vendor/guzzlehttp/guzzle/UPGRADING.md +++ b/core/vendor/guzzlehttp/guzzle/UPGRADING.md @@ -1,12 +1,161 @@ Guzzle Upgrade Guide ==================== +5.0 to 6.0 +---------- + +Guzzle now uses [PSR-7](http://www.php-fig.org/psr/psr-7/) for HTTP messages. +Due to the fact that these messages are immutable, this prompted a refactoring +of Guzzle to use a middleware based system rather than an event system. Any +HTTP message interaction (e.g., `GuzzleHttp\Message\Request`) need to be +updated to work with the new immutable PSR-7 request and response objects. Any +event listeners or subscribers need to be updated to become middleware +functions that wrap handlers (or are injected into a +`GuzzleHttp\HandlerStack`). + +- Removed `GuzzleHttp\BatchResults` +- Removed `GuzzleHttp\Collection` +- Removed `GuzzleHttp\HasDataTrait` +- Removed `GuzzleHttp\ToArrayInterface` +- The `guzzlehttp/streams` dependency has been removed. Stream functionality + is now present in the `GuzzleHttp\Psr7` namespace provided by the + `guzzlehttp/psr7` package. +- Guzzle no longer uses ReactPHP promises and now uses the + `guzzlehttp/promises` library. We use a custom promise library for three + significant reasons: + 1. React promises (at the time of writing this) are recursive. Promise + chaining and promise resolution will eventually blow the stack. Guzzle + promises are not recursive as they use a sort of trampolining technique. + Note: there has been movement in the React project to modify promises to + no longer utilize recursion. + 2. Guzzle needs to have the ability to synchronously block on a promise to + wait for a result. Guzzle promises allows this functionality (and does + not require the use of recursion). + 3. Because we need to be able to wait on a result, doing so using React + promises requires wrapping react promises with RingPHP futures. This + overhead is no longer needed, reducing stack sizes, reducing complexity, + and improving performance. +- `GuzzleHttp\Mimetypes` has been moved to a function in + `GuzzleHttp\Psr7\mimetype_from_extension` and + `GuzzleHttp\Psr7\mimetype_from_filename`. +- `GuzzleHttp\Query` and `GuzzleHttp\QueryParser` have been removed. Query + strings must now be passed into request objects as strings, or provided to + the `query` request option when creating requests with clients. The `query` + option uses PHP's `http_build_query` to convert an array to a string. If you + need a different serialization technique, you will need to pass the query + string in as a string. There are a couple helper functions that will make + working with query strings easier: `GuzzleHttp\Psr7\parse_query` and + `GuzzleHttp\Psr7\build_query`. +- Guzzle no longer has a dependency on RingPHP. Due to the use of a middleware + system based on PSR-7, using RingPHP and it's middleware system as well adds + more complexity than the benefits it provides. All HTTP handlers that were + present in RingPHP have been modified to work directly with PSR-7 messages + and placed in the `GuzzleHttp\Handler` namespace. This significantly reduces + complexity in Guzzle, removes a dependency, and improves performance. RingPHP + will be maintained for Guzzle 5 support, but will no longer be a part of + Guzzle 6. +- As Guzzle now uses a middleware based systems the event system and RingPHP + integration has been removed. Note: while the event system has been removed, + it is possible to add your own type of event system that is powered by the + middleware system. + - Removed the `Event` namespace. + - Removed the `Subscriber` namespace. + - Removed `Transaction` class + - Removed `RequestFsm` + - Removed `RingBridge` + - `GuzzleHttp\Subscriber\Cookie` is now provided by + `GuzzleHttp\Middleware::cookies` + - `GuzzleHttp\Subscriber\HttpError` is now provided by + `GuzzleHttp\Middleware::httpError` + - `GuzzleHttp\Subscriber\History` is now provided by + `GuzzleHttp\Middleware::history` + - `GuzzleHttp\Subscriber\Mock` is now provided by + `GuzzleHttp\Handler\MockHandler` + - `GuzzleHttp\Subscriber\Prepare` is now provided by + `GuzzleHttp\PrepareBodyMiddleware` + - `GuzzleHttp\Subscriber\Redirect` is now provided by + `GuzzleHttp\RedirectMiddleware` +- Guzzle now uses `Psr\Http\Message\UriInterface` (implements in + `GuzzleHttp\Psr7\Uri`) for URI support. `GuzzleHttp\Url` is now gone. +- Static functions in `GuzzleHttp\Utils` have been moved to namespaced + functions under the `GuzzleHttp` namespace. This requires either a Composer + based autoloader or you to include functions.php. +- `GuzzleHttp\ClientInterface::getDefaultOption` has been renamed to + `GuzzleHttp\ClientInterface::getConfig`. +- `GuzzleHttp\ClientInterface::setDefaultOption` has been removed. + +## Migrating to middleware + +The change to PSR-7 unfortunately required significant refactoring to Guzzle +due to the fact that PSR-7 messages are immutable. Guzzle 5 relied on an event +system from plugins. The event system relied on mutability of HTTP messages and +side effects in order to work. With immutable messages, you have to change your +workflow to become more about either returning a value (e.g., functional +middlewares) or setting a value on an object. Guzzle v6 has chosen the +functional middleware approach. + +Instead of using the event system to listen for things like the `before` event, +you now create a stack based middleware function that intercepts a request on +the way in and the promise of the response on the way out. This is a much +simpler and more predictable approach than the event system and works nicely +with PSR-7 middleware. Due to the use of promises, the middleware system is +also asynchronous. + +v5: + +```php +use GuzzleHttp\Event\BeforeEvent; +$client = new GuzzleHttp\Client(); +// Get the emitter and listen to the before event. +$client->getEmitter()->on('before', function (BeforeEvent $e) { + // Guzzle v5 events relied on mutation + $e->getRequest()->setHeader('X-Foo', 'Bar'); +}); +``` + +v6: + +In v6, you can modify the request before it is sent using the `mapRequest` +middleware. The idiomatic way in v6 to modify the request/response lifecycle is +to setup a handler middleware stack up front and inject the handler into a +client. + +```php +use GuzzleHttp\Middleware; +// Create a handler stack that has all of the default middlewares attached +$handler = GuzzleHttp\HandlerStack::create(); +// Push the handler onto the handler stack +$handler->push(Middleware::mapRequest(function (RequestInterface $request) { + // Notice that we have to return a request object + return $request->withHeader('X-Foo', 'Bar'); +}); +// Inject the handler into the client +$client = new GuzzleHttp\Client(['handler' => $handler]); +``` + +## POST Requests + +This version added the [`form_params`](http://guzzle.readthedocs.org/en/latest/request-options.html#form_params) +and `multipart` request options. `form_params` is an associative array of +strings or array of strings and is used to serialize an +`application/x-www-form-urlencoded` POST request. The +[`multipart`](http://guzzle.readthedocs.org/en/latest/request-options.html#multipart) +option is now used to send a multipart/form-data POST request. + +`GuzzleHttp\Post\PostFile` has been removed. Use the `multipart` option to add +POST files to a multipart/form-data request. + +The `body` option no longer accepts an array to send POST requests. Please use +`multipart` or `form_params` instead. + +The `base_url` option has been renamed to `base_uri`. + 4.x to 5.0 ---------- ## Rewritten Adapter Layer -Guzzle now uses `RingPHP <http://ringphp.readthedocs.org/en/latest/>`_ to send +Guzzle now uses [RingPHP](http://ringphp.readthedocs.org/en/latest) to send HTTP requests. The `adapter` option in a `GuzzleHttp\Client` constructor is still supported, but it has now been renamed to `handler`. Instead of passing a `GuzzleHttp\Adapter\AdapterInterface`, you must now pass a PHP @@ -14,7 +163,7 @@ passing a `GuzzleHttp\Adapter\AdapterInterface`, you must now pass a PHP ## Removed Fluent Interfaces -`Fluent interfaces were removed <http://ocramius.github.io/blog/fluent-interfaces-are-evil/>`_ +[Fluent interfaces were removed](http://ocramius.github.io/blog/fluent-interfaces-are-evil) from the following classes: - `GuzzleHttp\Collection` @@ -35,7 +184,7 @@ functions can be used as replacements. deprecated in favor of using `GuzzleHttp\Pool::batch()`. The "procedural" global client has been removed with no replacement (e.g., -`GuzzleHttp\get()`, `GuzzleHttp\post()`, etc.). Use a `GuzzleHttl\Client` +`GuzzleHttp\get()`, `GuzzleHttp\post()`, etc.). Use a `GuzzleHttp\Client` object as a replacement. ## `throwImmediately` has been removed diff --git a/core/vendor/guzzlehttp/guzzle/build/packager.php b/core/vendor/guzzlehttp/guzzle/build/packager.php deleted file mode 100644 index 724bf6349912cc23bf20e81afbcf0f0549b62825..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/build/packager.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php -require __DIR__ . '/artifacts/Burgomaster.php'; - -$stageDirectory = __DIR__ . '/artifacts/staging'; -$projectRoot = __DIR__ . '/../'; -$packager = new \Burgomaster($stageDirectory, $projectRoot); - -// Copy basic files to the stage directory. Note that we have chdir'd onto -// the $projectRoot directory, so use relative paths. -foreach (['README.md', 'LICENSE'] as $file) { - $packager->deepCopy($file, $file); -} - -// Copy each dependency to the staging directory. Copy *.php and *.pem files. -$packager->recursiveCopy('src', 'GuzzleHttp', ['php']); -$packager->recursiveCopy('vendor/react/promise/src', 'React/Promise'); -$packager->recursiveCopy('vendor/guzzlehttp/ringphp/src', 'GuzzleHttp/Ring'); -$packager->recursiveCopy('vendor/guzzlehttp/streams/src', 'GuzzleHttp/Stream'); -$packager->createAutoloader(['React/Promise/functions.php']); -$packager->createPhar(__DIR__ . '/artifacts/guzzle.phar'); -$packager->createZip(__DIR__ . '/artifacts/guzzle.zip'); diff --git a/core/vendor/guzzlehttp/guzzle/composer.json b/core/vendor/guzzlehttp/guzzle/composer.json index e9615a01ebd74e3e591608e0482c6d424a247bb2..35ccb8a3d2d53a5d958747936df64bab928d35d0 100644 --- a/core/vendor/guzzlehttp/guzzle/composer.json +++ b/core/vendor/guzzlehttp/guzzle/composer.json @@ -1,7 +1,7 @@ { "name": "guzzlehttp/guzzle", "type": "library", - "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", + "description": "Guzzle is a PHP HTTP client library", "keywords": ["framework", "http", "rest", "web service", "curl", "client", "HTTP client"], "homepage": "http://guzzlephp.org/", "license": "MIT", @@ -13,15 +13,17 @@ } ], "require": { - "php": ">=5.4.0", - "guzzlehttp/ringphp": "~1.0" + "php": ">=5.5.0", + "guzzlehttp/psr7": "~1.1", + "guzzlehttp/promises": "~1.0" }, "require-dev": { "ext-curl": "*", - "psr/log": "~1.0", - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "~4.0", + "psr/log": "~1.0" }, "autoload": { + "files": ["src/functions_include.php"], "psr-4": { "GuzzleHttp\\": "src/" } @@ -33,7 +35,7 @@ }, "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "6.0-dev" } } } diff --git a/core/vendor/guzzlehttp/guzzle/docs/Makefile b/core/vendor/guzzlehttp/guzzle/docs/Makefile deleted file mode 100644 index d92e03f95ea7798bae26ae6848ce8b99203bcf30..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/docs/Makefile +++ /dev/null @@ -1,153 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = _build - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext - -help: - @echo "Please use \`make <target>' where <target> is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - -rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Guzzle.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Guzzle.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/Guzzle" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Guzzle" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." diff --git a/core/vendor/guzzlehttp/guzzle/docs/_static/guzzle-icon.png b/core/vendor/guzzlehttp/guzzle/docs/_static/guzzle-icon.png deleted file mode 100644 index f1017f7e6028c14a9e0694c66a6cfbb2d546adf5..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/docs/_static/guzzle-icon.png +++ /dev/null @@ -1,4 +0,0 @@ -‰PNG - -��� IHDR���������Ï&‡Ö���bKGD�ÿ‡Ì¿��� pHYs����gŸÒR���tIMEÝ,×B—��´IDAT8Ë•ÓÝKÝe�ðÏïx4íljsÁÍãN¾œ ¡ÑÆŠQDì"ˆ^n¢¨»ÑŸ0‚ê.ˆ#X/x±6ÖÕÖE –Zó¥3_[f:ÌÙ<GQÛÓÅñ˜ÕU¿Ë‡_¾ÏïÃÿø¢I§’R)¦`T½”2ýþ‹ èTfÞ²œf#´«ŠŽMIõ(Ÿ¸Ú¶Ý‡ïÂÿL½áZ8Iø4œexë<†ŸÙNg8$c|Rôš•ðEGÏè6¼ËÐv¼AÖÝb†o¤j1±…cvmÇIònÙK##Uô·ná%üëÚc’î€ßìpÚZx·„Wí'lÇe@›•èKy ˆ#2œPê$$,Š;bÉD_‡—CŸr%<oÃ{4Ë)˜’WÔ Ã4o{Q—ÊRY sÆÜVeMƒ5eÑeØ'š‹>²[¾„›,j6/K…´»Ú,‡Ñp.|zÁªRHzG-»ŽqûêpÙ’Jï„|Ô'«µ„7ì”3çŠ^ç½êž g¢Âà kY)q,«5$î1뎕פ°ùQ‹‚Ôfç9wt›wBÁ%9“2þˆÀº:<hg©ÆKÎúÕa}Z½.£Yµ©Íä¬räüü÷êë²W•kÎiô¨iùÍÙ>"…àÉR2qZ5ëqK.©@«÷A•9Ü/.%†=rî»èyƒb" Û]|špUgx¥ˆÏKºçM_é–S.nUZ(3†i;4gÅrÑs>‘4¨^^Ü°ƒ²`ÕÝ¢ënªN‰áÇ}¨ÝYI5æ½àŠvP£¶¦µkÜö». Þò¹guÈXW~0ÅÁÆ1®É¤c -ÊtYP0iÍ8hRz$ŠÉk–$õ+¸ „1uö[ßêá´£n1ËfLªö´[6Ôh”ó7DŸù؈jg¢·¦üeÒôg¯ß(=����IEND®B`‚ \ No newline at end of file diff --git a/core/vendor/guzzlehttp/guzzle/docs/_static/logo.png b/core/vendor/guzzlehttp/guzzle/docs/_static/logo.png deleted file mode 100644 index 965a4ef4139180ea7d33594feba2394dfce6774a..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/docs/_static/logo.png +++ /dev/null @@ -1,936 +0,0 @@ -‰PNG - -��� IHDR��_��»���d`–��� pHYs����gŸÒR��@�IDATxì½€]Uµ7¾÷>íö2s§Ï$“ɤM - „@Ê„ÞÅ’ ¢O,iÖ‡íð=}ŸÑÇC‘' -úP$**¡’F™´Éô™;õÎí÷ôýÿIf˜ðó#$AïÑpï{Î.뜻öÚkýÖoQÎ9Éy ä%—@^§Wìôv—ï-/¼òÈKÀ‘@^ù柃¼òÈKàH ¯|Ï€Ðó]æ%—@^yå›òÈK /3 ¼ò=BÏw™—@^y ä•oþÈK /¼Î€òÊ÷=ße^y ä%W¾ùg /¼ò8È+ß3 ô|—y ä%—@^ù柼òÈKàH ¯|Ï€Ðó]æ%—@^yå›òÈK /3 ¼ò=BÏw™—@^y ä•ïßñlêìtoß¾]ú;.ÉŸš—@^y œTïJå»*õ`6‚3#G®<òž½Òž¯Šªãhì,¸ï¾ûNÉÜžo"JC¿okiÆt_®UL›½²±Q>©4OÁÄÍM±ÀóMMÊ)hîm5áÈuCthÜöæ¡àÉrÆy²¿ÿ¿îíõ:rý[çå¿ÏKàYôÝÆçûLSlzÌЯñ‰•¥ÁB5IåK$šÛÉu¦š„\dR«¿WãSÊûãkSJÚVbŸìnϪGîSW¬X1ò½£¬í=PಠN”9aðrÕÌz¼Š`Sª„ãº6U Bgˆþ4«„dNÖîßó7G‰«ü!ÉY„‹”2cU–NW¹*Ûçbü=mžìÜ5mñ9œ™½—UE¢øþ¤ÎŸÆ"¶,\㪠§—ŒÇ†öØt&‰UÜÃ¥ÔÔ8—Çj³‚Ë’·¤jz†û¼„‘åq¡aAÅr"S.*õ@¸%o:Ê™ÈffM:×6×¥ ‘çê;ç³óïòøÇ–€ønšÞʦž"ŸìZNmºœH…›M‰ôˆ¦ye -µ™¾‹bj#¥óê¬M6‘ÒAê(ß•+W -Õç]]Ô'葈›).ÛH-¹ë®¾‰Bæ&Aža.KÔ·ëœ"ËâqËôwË’Û¥›çÌòˆDh¥¦E¤•Y%uÿ×Ê×±b'ÕÖ*‰èP(K¬ˆÄ¥NÄ¢òpe©@í -GñR‹‰ÀJ%…Ša]ÔÓ©þþ×IQÑÛV¾ -cS2¦jÝGHÏ{âñàP’Nt6’Î\m«ÂãÂç»+j‰ÌÏQ<I¦DRÎs!Êò4nš ‰Å^EžŸ’©¶|ÓÖuN¬BìN«š&RÜK S™ ¦[±©ñ«Ý»[?:kÖ°òÝ>D‚¹ljîÏÂYXùM9‹<L§ÃÛb–?à±úP(þVÏâvìvl®²ò‚žS± ½U_ùïò8x×(_ÇJxʯ5¹mº™9“âÙþÏ7Ä(÷Çs®NB:›]ž²›2“Læ(ÝÈ°~XŽî…Ë“$Ï‘M>ƒÛ¼Å=–’Íx-&Í$&ó1·m+\±ˆÍklÆû)‘s’bs3“ÃY&†¥1s¶¸-«f¡6ÜèßøÏæì\˜“üeŃÑT©ÀÄb‹('0.‹,N"œSè]’´Í‰œxLÎKEê¸TÄI"æ1¶g™3g™˜*%¾Þþ 2±¨³žuì9#ï+3Υܶ_ü0±ô–»ºEËãYþætš] ŠôÆÞ¦žÇ<²n‹âí=-ñ‡<±=·'GÚ \m%T*°lK–DA$—Mjm…õ¦Ô©S]ÂZÝN‰h“¡,3L·%2[ i—(Ž.:ÉŒ3¹ñYB…]ŒñAÝ°Z™(¾¨¦x!•èg\iúëç7=¿Ó=~žT_W”éìkØU81ekµÚþ®udjÅàØïòïóx7Jà]£|Ýî’Ù6%×Ã7ðŒ[ ¶ é–à=`K-¡lJ!Ö¥M«_•Au1>¿®xz#nÊðÙ§µÚšê ¸¤s‰i·åc‚$)u”ÒrÎ-Ͷ…8” (2ò*Ôv\àvŸÎ…@JPE²‚•öZfߺçÕeË–YJ‘6uV¸±ŒËBÀÐ-ѹŠýö€éQ;.ŠDœ>9µíëB?@/‚’ ª¶¥{DÖ -…Ë'@ÃJºÉ_]ý$7È ,H¬Ç÷Šœ_‚粒 ›ù#‡cùy.X²h,vŬf(ò -»³3wߣ•Ñ+†{ŠÉJu„莥(…ïñJÄXøé;6†àXîïíßÞ¼¯¬¬HÒ-ó\ŸÂž=¬EM‘n[°¯ rºŸ3i_RèÓ ‰wZ‰ìI“ãRTJc%;$j*óÁÓ£Jj3µ}ª*¥!—Ûr˪šÍd̽ƒƒÃ.‹[æÎU¾9UHRB_2MF j\&qK»ë˜@‚KQ˜+úQiÞ’:[ÍórOrï;tCuõ¨%ì̧*XTǹŒ†¼3v'Ôö¾ÁîØ¥55‰ùä_óx·IàÝ£|_(kµMîqKRдÍj[Š,ÛÜ«HâPÊN¹Üᔘs¹þ¨ÄEšI'z|ò¨åX_]íXˆ»^íM—£ß˜"uU\ß!r)E¸•Óm#í)+êÞ³9&Ü4ßçl½á½§tùrúJ45XÆBK™ +µR°yP‚&ÄÎ;G2 XÊj”ž¼¼¤$c[¶hŠt?lÙ¦Iž3{',u(|r/4}z¸'žÌ<uUmi?:b«[úÚ¿×ͱŠÄá„pb‹„¼,6^¢ô= -‘¿Î¬->(h×ÞIüÛbu–,h¶a®—™^l×îÍñéši2ÂwI:Ë -çû‚¾HLËmQ).ºÄ¤÷f¨ûA´÷EBÉŒ?u×Ö+O§ÏÚZ?pÈyä82–™G?MÅ´Þ8ü¤®¨ˆ@ÎÃAÐ7þNÈÅe®¶Ý½‰Ç,úEâ¥ÜÖZ&ùŠg¤Ó¹e˜Ï*M i|šnqÕÍxuˆÏmh%×W¿aÕÛMئ!Â}ÑêH 2°;aDÓ±ÁrZÖ‹nOº;Žüû¼Î& @/œUÇÈx`Oa;>æ ÌÞaQëçT"ë(#‡÷KF¦Ï0…'dEzÆÐDÝg+³ -3ÂIÌ~éÁì˜&ˆÓ$³íÙt£Õ²ôW{¬ìº+›.¬ïL -fW®/ã›ZaŒß’"Ç &ôÄÆhj±Lù=²$}ÕæüƒŒÓ -N˜ -7…hsrtõrXçóêÂ0:À"Ö>ؤºÍi†ÚÖ-;ýÀ¥Áß›6] oC.è|{BıpmÝemÐ í;°BTG¿+JK³.ÁµQà›&¼‹×'ki+›©,.ƒBŸi¢œÒÌxúC&µw°w6ïì}éùƒ†m¥ Y9·hBqR·ÍD“X¦«æµñ%m‚Èÿ/w©«þM”#rü[Hû2%©ÒÚO4ôgf…åÍÂb„ÚÎ{H¦îM\5#¢(†ˆm¬ó‘ž€„u>Ÿ!ŒmŽ Šó»z7"J®ªÕˆÁ^sIòzìxvÃm¤Š¢$Ùôùn¿AŸ?wÕŽ¾ø9¯öÄ«´ÈÈXó¯y œÍ8ãhGÉ:ÄeRKH´©'`‰.ÃOnÓðíµÑ‘ÈûX!¾M\žÐ³á–²CõõG"ê|Éc*µ&‘¨n§Mùp®©¾~ØÚ{)q”hÝŠ4¸»×,”š̊ܦTB¨Q -Ÿ$þñJIKtn=^ñÿÃöïnëNM/ó~ÒK±‚òû_*’ X-ú¡0B”HS(¿Î‚RÅqé³a_ã•]±ó¡`~aš„Šþàùãƒ_u0) zŸv.¬½ƒZW,,íÁw#È‹cÆ<òáùX,�°ÁS·Ö‚Ø•h‰®Ò–V,ÿ?dµè4õkÕp=*Qò_.Y‰¦ ý^%z$v0–³¾¤FSV~%gõbQæ5¢[ÚwIq°µá`´Àò¸x\Ÿ9¿ÀÝ sÿ?á ¯ÇÓK,•ÌP¼ü7Sýþ“ú]W5GÇy%Ï£²ÈSÛ_îþ–£ˆ—:ú&Ê%¶0z®µ¿4ì–§[š«%ggçË2[¨f\$“P}K '²zîÜ‘�•1-2>â‘Ãfö9"©eÛsàÇÉ@ŽÜȬYPY™;aôù?ä%p–IàLZ ÊK#k;úÜ}Bºœ´æ’Rا¹Í €•Ó26ilì#uu£ÛàÙ±;^ÚýòËö˜q¶ùø~בs¼„TœMȦÎdÏ+T¨ºZ|ýg¾2{Sv±»Ö^•‹“""Ø%ŒJ¥Ð}!Û&Îñ³æ|ÿ`Sl]mmæ,[z“×!¶Ð&\Á9Ï#ðôàEÅáô2¬¨6w't*‰!X¸|À´n%TÈ"ù0¶óˆ¶kñèýç=7g%mêk‡ëa–ÀXÈÖͪÆF²¯®ŽÀßúæGúÅ3á—õQÆlÕÖ¶J‹ÇKnÑȘz‹Aì!FÜŠ”q›¼6é…{cÀþ£à8ÀM†pÿ$ÒŒ·¿E½|Ö¬}èÅùG¶ %ŸgalíþÎC1^0ëÅÛÐÚê -{CÓI©†{9QD¯š/ÀãÃåǜ봗SËz\R"fú>'ýÕݱ†ôÀÁ¥“'òªâÃÎ9ÎquuQ^zœöMË—‰2 - Âum‡¾þâÍõõÃ>ãÇð}D 3Znñ?ä+V8w sdÃÀÀfd¬Åzƒ²þww?íl6Iå®"ç¼ü‘—ÀÙ(Ó|éîÞ^*x*¸ÆŸå§–B±=/¹¤Cܶ;‰ndSMø3.õ庺cp¢#¬¯¯7ñoäãß|……:߶øM‚ ŒÃN?Œ~Bœ’0¶À=¦Í{þêÁ`'Ô„éYÂ¥Ô¢Ss^mxëûÔSO1luσօë¶óÛÜD@ǣʧ°<¸y ;ñ]›[™bwAÛ…Ë`ÅRÿÀO·mû…|š7f”ËêêôW£‰V.1èLî#²Pç^¼¥òuš@èø³™°]| ‚‡]fõ�4`6.ñGC´ºªé¶>ò—‡(/<x_UÓðÊf ¿Œþgˆ�Ð`ƳTø aǸiœsJ -ʪ,Ó¾ XÊ©¸M²¬ßXrÀ±zGç>ÚÞýC“e‘U›&B|”Olî+Ä[|p]Wò¥ Š¾ÁHÇ—>zõÅ¿�7ÅÉäiM‹”ä*‡Ûv½wõ\^â×i²2´úæ»®j¾²ýñ²ƒNqL[{ð\5ÓÃúÜ1¾ÑNòoò8%p:•¯°®3Y#‹J…ï«*še2wÒ±ïDÀgG»¥ž¾¬6»:]x?÷䣰x|2ó†hF ?Âm™OŠrB2R='Ûr -× ÔƒÍa‰ÎB÷AXœ¶m‘'D&oвÉ>ê÷õg£=¦§ \N(CI‡ˆ–2½È—ƒ1ç(NdÆf&âr˜”g{;¬(j I֖׌yø0mqŒFýG¾s^&;ˆæ -Í\DmJƒñGBYX‚¡„蟂àY@qÉ3¯„:—-;’ôP¸\ÜŠ7Ãu[tÛTo6ÜB›’º m÷fy÷”ð¾WM…C›MõfÇÜš°ƒØ:öûÃét±ÑY€”XÙtb@ñùvY&¹Œ2KŒ&Í—/!'E<½¿«0à÷ÝÁm2[i'5ÙÏÐî4,f•D·‚ôs¤hÿò$÷ˆ×y{àOŽ…«K¯*Ž˜{ï½—d~.År›‘·¬«¨ ,\ø‰äCnÇ)£nǧLJJÆN#ÿ>/³ZðÓ2@`TYñ䉳-S¨Ö ï¶tk°P ï·%šÞ›lý±'nþà ¹ê1?¨& lwäR¡¤¦U¶¥µÉÑLvN‡ª/èÿjDñ¯%»ØØ‹M&-‘½€½Ðž0³‡òÀØv¶w¤Ü2yMµøFè¹°xË¡øàe¤3½YT]ÑYå‘R5ÅáL•ßÕÓ6”ƒß–îgCÚîq¾lútÍh»JÛå]*ÈDêÌj¬+Îõ=Öh“&M:Á=òV‚í̥œ×SN}¦e%Yüs•OÎìSE?׬e NTu]uO4Ô]/¬JOÇôÁV¡O<º°S ÚíBIÁÞz¯«Ûp倫ºj -/}‹Œ1‡›bÈbS÷¹åØXùŒçv¤#ïF‹ÓI„ÚWÁíÎźMeŸÎ˜ð‘‡Ì®†Ë†ë†WçzhBIËÚ(ùŒá){P0cÿ- žm·½€Ü]‰uf¥5?¿ÿ»Ø¹Œ^;Òÿô¢"«Ú¯dï=úgwÓÏjX_öîçxèÅÞänÍ6ƒ[:šÖœSRrÒ…m¤½ük^g³N›òu”ÇÁ¾!YHܲy4ëjŸñˇ³?½ú²ÔìPÈq#œðcl¶â³`ÞŠ@Øõ#Wáõ(«k¨@çâ‡<^h~˜ho6>Ï`Ú*a(»oSÀ=0ý¨r˜ZésÅÇC±v¦ÕéHÉš ä–B,¶“»èÖqÏ1ÛýÍ͇•Ö¦Ý‹çÕ—:4#ŽÄ·Y"£•HÉÃúíþt -ê|eÕãÛ™¢vݤ]1%[86öd@SNFªí2ðÂ0©mdêþ¾º@:’¹DÕ †mÇ3:óx˜UX\R7PU gCSß3Xv%jÜn•ƒµE>¹Ä%fVzõ¹g’Ž¬ï³Š—ºÃ7"øS,›=PáuõáœcÎs¶ù·}õŸ¨0IàÂNZS›‡uêyîâý¢(ß|¶ÍµmÏ®½ååå£ÊoGìR—ľûQnówzÖþþËõ+QÝl†Ûæ½2%56—6åÊ -ç–—ZÇu䳓TR9½6ŽVÏéV%z Æpf!›™úãÜWç\ç¼ÆÆFv²y´•ÍKàl“€x:Ti–µBúÁUÀnäâMŸ¼}FC§6¨¥z2UOjeQQvÅË×"‚Ê®vÔ4Œs -6èäSÕ¦öO-*¾ÎM3ƒl-ŲÌ9TÞ#r»Š3v‘„áíÍmdî‰ |°×aýêh‹¨Ô¬eê(˜nÁÙϸ‡r¹–eð+•Mû‹Ï¨\xÅýåqÓ²‹²Òa!’2ÛŽ!Y U²iS¯mî_‰¥ºvC¤Æ62æ}ˆ¤zr¼�ð]^- ¤”{¬²BZ»»·ã -+^4¨+dóìEº-Uô³\ÿší}Úesk’ЖŽÂdØ¢‹ÁÂB).®³¡Øc7X™>Ý ž`…••”&³^fÒ)* -Ä_™BHªÐ0A´š^ïÀ¢bìL/†¼ ìNJ‘M¸ -‘º^Iš,–”4Òû0Þ$U÷ܹ£òy±=V‡¸ÏÃ=Nó·(?¸dr zt¬d05wKa‹&Ajq0ìvÓ†®X¥"ˆå¥p“³øaütm{â\÷KÏKgÄr$a zAH”Ê«ns‰Â¯“¹¨¶z·åÕÂyþó/®r„=ÛïfÔÞrUEAÚpä”?ò8k%pÆ fëû†f &» ¿à~P²[PUÍ’�›ýÉ¢Òá„ÒÚJ\]¾l‘3Ê=Šð˜Hù4àj-™Ð÷ìïo}Ñ \9’ýóiÐõ1ño‡Fð©Ñ6ø{Tœèú1?ÂõÑĹ^I|n‹qNàzìºóàotÚÙÖ›ø™IX‰¡j·,?LBãüyìA×· ”š”Öy]Ò"¸t/b”M§h ðuBmPìÊi”ZæCIQûY}ÑÉÓe3·D“¿’$áý°Ü]\ ·1=õøܲ²á ׃σbj¨ZålFÆ0ªŠ<®¨.fŸ¹ß¯i] 9ŽsAXXF?Þ‹’ð×hºï02Ãì1sv&àXµÝÝÜÝ«$.‚Uûä…Ü£}P%â]Ø5”À'¾2¶kËŠfÍû.þüÊ÷QYyF×Ìk(µ¦šý†ekO¤·&ü†Ï÷¥h|<R½Ajò%"9&'*vꎱ~ݵ]C_Ñmòuf¾Ÿ“kÚ+Bë"]ƒõ.&}þàW)3²¦´`ߊ£oc?ñ5e„‹ñ}+äúXo†´E\Æ�Ü!½},Ò>S‰²>]¹»÷ª†Ù£ˆt·i)Lñ°—Y߬*Ú0âûvæŸ?ò8Û$�ãí̉´ ågå¯ÃZí³t{†L…J&F³Òª‘µtQÄÓýòá;)åûa±x•²ÜœP®µÚ¯SH~¨ ¬&Ž¦ IÜr݇wÇÏ.Mƒ€‘ À…€x)·MsÖ־؅ëÚÓ·àú‹aœÍø]H° '“ w”r ¿pf³‡Rv§NèÇ®û%ÀÏ~$Ä�ÐNìZÊT䡉Ζøø1ýÌ ›¶ÙÈ2@Ò’?È4¸W‡ÑÎ÷w\U«¹¨©¹dÆ=¢X ÿïH"Q‘Îœ4ì™°o[Àec‰Ôøs¢;µÿhJî Š·5!]™”•‘l.|'ü ÆçÒ6ûWÆùó^Bò§‹f»Ä2¬ÿåTxN…k·¾y¯{tn™èüŠÈÁ±Š·J’ØôÊm@ðHQ²(Ê IBÜ#s^ß•ø€$ŸÂ‚äR†ƒƒƒ{¥±Ä×�QÂ?lèµ—'IÈY œëúö6¨À[?>£_:Þc‹’E~—!ŠŠX!Y¢çæj¢·f¤KpêMè¿M–•/å2Æý¸ú^$¾d�=ûšÐÖV<2†ük^g£N¦`Þ‘q:™G#?.§ƒæ5kÒðL…61²ZÕºûZ×1ÇŠ#.€ƒ6:ÀùÆ"“@a8ª¤?å2j¡e4£8Ýθ¨&G,)çû‘ý¿a¡¨e“Ù4‚ Ò•v{è=0ÜÊ`…†s–=®nYã¨r¹väÕÿ/*õõ/.+ÜçêØßàÑØϳºýƒ™_@6[¬'¬ÆMŒ5*#×ÿ*ŠÖaÆ ô4¦ÄÉ9š.snRKbŒ)Q k5‹‰PwñÆ®ÔÍÀò‚熃;6,±^n_ÿâËg ãœïbøsÂ>ñZ_â’ÆXzYQ1-“Ë<ÅÀýЉ{*c¦îçŒ}!Þ HI#ߤ¶ø=@ëCO͉_s†è46“<|/Õ[AôþœM\p¿üÊò7n‘Í’˜ïÞç[ã7¬‹&îA?ßDD5.mdí~׬‰Lôsk”z%Ü!‹‘[ÒñÁ_¼M|}]§6ée`¸+"¯@›W ìóÀ"¶e,ûnnÑÿ}d&2çY.‚ÿ¿>þGÛ{n»ajiëã -öº²Ú%°óÃ$|®óÌýC;†fìïê*„r>a1vÆ’?ò88-Êw],W¥M˜uóšÎØ-ÃÖfêÔÀŸ8Ñ0õžœa=ÏíªÖRßþWÂñ &ß‹mØWœB QS£ÊwK[bä[d#(Ã4üØŸ´YÙðüøvØ3%Îc«m ˆ'ãGJðlÝøwY?B"Pp¬ÊŸ |tÙÈÌڞ͖\y„ÌÝÁ•Î®ÅWÙæÓ°_‚PaÑA3cÂPÎ5:ÎãÇ’3a·!/ -(8>ìa_ÛÖ“úòÖø’FBä÷L™’–í¡&]»lra·¡%4"òJ´Xï%¢[oCGi¨Ã‘¥£ÁŸ0á@B½xo2yÞîݽÈ4>øͧB‰~ܧ†2Ö‚Ê’ý@< -—Å<`åÊÿ‰ôyÄ>ómAºÕm«ñƒ›_Z»ç…U›ë®v´2sï‚Ýþé—£C_]ß™ø¦AÅ;ê‘öŒ©‘Çõ÷X.�7¥)ÂwÝWÐTÝ"6c_7ÔÞW«÷Å–L 8vîÕ[+»DuÏâµ»‡Ûl–}péwN_5—Xï«-è<ÔÓ´NWøþ3nšIÏ–U«VÁõÄ‚ 0´õÃѹs‡g¶¸ù~ˆÂ-RDŸ6 ¼tâ·mûºüøû¾ÞÐÓSÓòJØVþ8£xÇ•¯Ã`©êuH8,3á"ä&LmhN( `i|!˜-…M³2ìæ¢h4üfÒ€É{À²hI[ËjSäám퉚W{7…þ|~WC‘ÅM(PY±wy öž¬=p1B–˜Aq#ØÆ~™´§Ò$õ0ü—ŸÓ™±ÑWVv|ˆ˜U“n}®GÌ”q_ÁÌ®Ðñm_ä¸?¨…Á†[šUJ¸ 넨>{U›:âŸûWXÚ~ó -ÒŸèG8ãŸÁ%©XÉw„׃ÝkýŽ3Ï7Åü¢àc°Ú-p9ì3Œì_÷lq”Y@¯;ûSŸFÛ—aõ–â¥qþnHMiìîBƒ•¢‹pÚÒÝvÆmh™Ì¯ ƒ¯à¾û“9c¼*,ÉŸ#(ö¨œH]uÕUš£ÔŸ£, 9°’-wÉôÓ -£Ua#ü¼ÿçâ ¡ŽT.ºß°Œ/Š^ƒÅ´dÊù‰œÅ>SûžAæѤȊùE—@ë±SÁÿ¬ˆäý>íÿĽõÊT¬·u~E]ã‘gÄIP¹¶,Ü ñ¿öÄs?¸r‚¿ÉÉlÌèÆó Ó¿ÝUs·G‡]2jî gÑû@¦áàºù CÁ‚€¶ý' $ž¸„¥à³ø!ð浘W^sóŸO«N°îNUï[c±*5MæʶK;""ÙŸ°í•Ó;ÔŠþGïkìõ½ãÆ%Ô´U}T—ì & Ÿ03Ù¥KþA…³2P4VÛ&ù¡!“B‰!ÂÏY9´ÝëØÆ>¨ZæKõÅݸºëM¶˜Šm:»P†�[º§/}ù¬‰œCÇï\õÂb®ïy»è«ƒÛÅÞF‰ ø4.¨h³ ë”ÂyÖAn8½8–ç¦þÔ‘ðņ‰L8“7·»¦DFáj#£¡ án¨žÆ¼V}+ÒB¹5‹Ê¨ò�8Öðâáp|nñ¥SM›M&L<ˆã9+“뻤¦¢ï$ã5A™¥ƒå·+”Óÿ×L :u‹,“²ñôͦ߆{B¤¿ŽÈOÐûîKwßrËPŒº·Áê¾!‘Õ÷€íwX_¨¸{Vv֊ز;»„‘1;¯ŽÒ{¾©çÙ Ç;”Õ;0X¾ÅŠu¼N*êlg×™4½¯ –lÄáTÖtq@ËvvÝt48êr~˜ì›°„ú ¸_0ÑüÍeE®ž?·eý�©6B¥ÌÜ°¬îX¸ÞQäˆs_‡¶~cWUDü¶f“*ÅžÊUm ‘¹.²ms*Ð?$‰FoeuÚnîÿîi²Ûô[<ýʃº¥ß°²‘ü}œ°ÀŽ´ŸÍKà–À;¢|Ÿ‡Ò*$Þ‹‘ÕÛ9¸y=p¨ÿíñ¸nöÊ–w¼äa±SÊaL夹ñxfp{ˆœ´tNQeå@_w¢8áiP~(«JXj’mó*ìÜMee f¹Á²Yrå¦MY¶`“}vÒCv™Ë(Ê"¸jŠÇUxå cíHÊê:[⥾‹áø<ª.lrÇã÷¦»Ã+ÉPêvÄE„û¼¡Ì¯ö˜-âK›¢ÉñÌ´—B!M%÷5ý%<¿;‘¥7Š…ŒÁåFƳߦ\ŽÝìÓ}4eÅ¥¬´rÕ¡€Z}„̇;Vb]IõlÁ–fÇõ\U¡—íÑ^lùåÇõEP„N{F°t¾(’`¢~=¾+ÜX¢¡=‰D“m^ø“o5laµd¥Ÿ"Ìý}ø8~åɦ¿¶çî/w€¼8&Æn›)¯i¶¾îÜâ c/XeñYxŽ¼#®@äŒöÅþ âц*üíõ%øþR)›R¹»†²ÞªY,nv,å#‹Qña˜•Íhl¸½µÖ£™éY3ëfTþ¸jÙ*aB…?høéC½œ¯à«wvù<²â¬Puñ®µ}™™’eÔp̓lï3š‘¨7º‹øÜ‚ÊÜÊÆÎ'«…[æ"¿$Kê:ú¤kYãÙ›ëªà@v'©{ånnÔd¸y9á’ -ÖÛ/åH?ä%ðÿ.Gáü¿_}Ü•«ágdAe2²r ž¶n¼ò·ì¬1]cÖ9ŠNêñÓr†ZY*Z¿òû÷õ‘%KØe“ç|[îŒ -È” -»“|£LeÇ5O^¦þiª7A±¹Èù®fèÏÀ1×-‹éÍTÃFPˆòdW}G4“¿m߬·¢°×óúMÀ‹‡+[ -Û-üȹÃekÿHràpÛ2é9‹J’õ;·(Lvö¬°B1x{‡³åÜÄ1¥°"+{”ÿ:ô—?7ŸlëîøŽõ’que¥±âM2ÒÖõ''1ƒWqV4k³á*àáì--/Ø榌ó@"óoªnßàò -ïŸâñŒZ†M¹\¥–ãOÃ¥±_ÌÚwñD6à)ø">ÀÁ%C ~‡XÉX82SŽã[Øл%৞”®›·‡¥@OgV-žT6€gÆ¡¸ ª‡È®7‰¾z„oad\c_×w§Þ“´´û¼T.a/‚ìÌdÍ[<ÖÔ™¿S5dxJ¹D>…q]†{Š©zÜ@q¸NVÙ’þðñÐ=§'rðŠ#ëk7-#‘è˜ìge¼â¿HœÌ7/ÇíO™–Ow‡_6›föû×ÖTÂ[󻣱ȿÏKàÀ)³|_<”¨#.D~x·Þ”lPÊÝm$—[xXÂé_ú·vg‹.œÐáϘ‡VV˜³…Å|ìm½égu=W +žªœeÜ"LJ¬j$ÁõcÌȼõ?„¥£xÜ(ÀàÛ°ùëYÅÜÁˆx;ÈUÔ&Sàmýš-È榦ßϯòéØÃòòìÜ80¢ˆÍñ ²¾¦ìÚö¡n›QÜDAðŸ€Ü5Üâ.(Ô—¿<Ìs}Íñ|¥sÎwò‰il±([dI“0ƒ5Z–½ÁäÖŽóJç!(¨c{&¿x¥™„]Ë*+“Žûñµ¾ùføpÓý’:ÁÃAefz„r4Î÷–k:NtÚV‚Ø£cA*·3¬¶ÁCúê*³'¾ûÝîåw~a‡f¬3ÄÄÞÊÜë}™Gà0_ÏL1›’3ûç Ë©ÁÁ= ÚÇ—.Õœ65-³ùü–j›JÆFÝ$TàÂײ…¤“Î÷Ã};c¯µ¢bô¯vèC îyÃ*u¾{€ìx=ðb+À±q‹HÄzÛ—´µ~ŒâuÎWrbÅhý{.²±»ù5V¸5Œ‰`«ÿ(±å0¸/¾çøÁGÚ?bi»;t䉣˜¬ëÍÌDá½OÊ1YŒëàGÿ]&—íqËÊ@ÖÒ:ßWSÙOÕ1Üoþ?y ü}8eÊW¬b“ãr9«ÇUæþ "y»%ÅU5µ/·{°Ù²:5Ñzâ¶Ææ €dYYLÀo�7ò'˜šèìéí•Æ–¢ž–m7׈<hmJ§™¶¦Ì5!ü¢›fEªûej°1z=TS ˜Ë¾ªz‹'ššþü‘I“R#Jà �¢bp¥EíB¸/ªQ0DÞ?íÊï7l½A;/J§a{L@/É%[`Ý ˆl÷q¹©²RŸÓDÖvzSÛB¢åSs¢S,UŽÁ&)5¤’ûö$–××g55ÉK®¯|ðùç;¯š4 õ‰Dúå[/,¹:)ÍÞpàÀÆ‹¦Lqüº#Ö¶ðÔ¡hÁÎÖ¸ÁE~O’ì¾”6PÀY›ê×[ƒjº8!46ì§ë¢Q ÎÜ&Љe·}~ÆH@ƒûÑ’ì¡å·=Û7}z#_¶b…hö§P‡™ukI@š}˜QìíÅ=XXÈ<¹j]C«'ÏѶtuXÓÜœÌZ¾@Ìà=}{Moq±=¸}¯Å®¼2<…û¸ îÞÔÕÕQdÓ ðó-$G*Ë,Ã÷jÌìâóBG{O\·Õ°•HêàªØ8²3hhì÷e}ôps\ƒÔoð -ñïfTþĸlow›\ê’}²ŒQ•Há „ü±þ8Å=¦;¢gôv›Yßzú<Õ¨:UWí÷÷ ¥|ÍX¹Ná~olÏù÷y üßKà”¹km°5ö~Ý´[ün6Ð &ùÉÏâì·Î†îd¹ºÇIr®ÍÒYDÕÌŒËç*“-¦¥ÔM°rFV[ó€Š}×J›°#c_½ Ú�í;VÝ>Ã-ãQp@ÌÄg†ÚhH‡eß×in#XlºVîÝ«^;}Þ{ ˺QàìAbnÓ´€xà9‰ÑrÔ@#v0bµ…ÐM¨Ø!cÁ@oPõƒÇoyGæõ"ؼXÐW-V.àS*%+»1¡ô$ù.ƒ¦A/SN˜-삽=ä<îi„»¹T2WT[¼–¬±¶½®ÍÙBTåhBúÇ$`ÏÔþ,ûK™;íL&Ò‚qAÉÌÍ“]Þ¾¬¡“d‹÷gÿZ -&/QÊ|îëa¤þÑÿß„$!ž2ÍEÐêw€ìþp‰÷éjÀßFÆ;öÕÁºêλÑ"fŸÂÝÍÎ"Q®› ÔA‰Z9ÓSx˜%‡®)|§âQKYÃÕC,·Ÿr‹i»£h07TfÖÔ÷öGÉF'OæuýýŠ• }ºtéÒáÆQÚKÿõŽ -ì®méåµã}V…±p5@Ë?ƒ§)È»5ù[óÆ{z0çÞ’—Ûb¨$¬†ké/±d×ÍPúHTóÃq !W @Ôó�»Ò´Ì™’,È\庪¶`Ÿ³y»�½yKùoòxg$pÊ,ßùÈpZ…r>+Ët]Š¸$ndz9XEo6|ƒ*ƒnj]¸<.‹ -· € ¨¦ýêñ×(ÔŒª„e`9ÁmÈK‰ÂÇÃOØŽ¼ê¸1ÔˆçÔ—n…À¡1Tà'„oÖþš›+€&ý¸N©}¿äÅ@Ô° íHYn1-ÖÍD£Ç¦®þ\:—Þ)#P)×,fÕšövmÁ¼y0ßÜX*óùAx@/EâGR7‰'©S¸"D15L\CÌTçšáe¢¾À%�òä–Dç0^ØPD¹pÐ0¦¤Ò¹ò°,µ¡ªðÚñ“üsIÐXןƒ9MÆÆ h¶ÇÁpéñx¤UBRÛûÌ‘(2^‰-öùiÛLcwÀDËþMgnpíâÕŽ—åÈg'¡á²Ù¼’ŽR=èËy"èE5[2©žá ¬[È%‚€äY\”+-Ó*ÐDS˜2¦Æƒi‹†Ã *ó†LÒO’ç.ÿHFMf™Yí¶6´öº”içe^ëÏ*ª©¶øÑܱ#ÊfÏ~²y瀺b¼o4€¾Èk}„Er„ïðëû^/_±bXñ:uC×àTé|˜Wy«ÌÉK!íí‰ðn)«9)×#óy=štÒŒ -'½\v‚Óy œLÅXŒ»pßÜÙYø Š«Þq$¸9rÙ»æˆùŒÊï]3ðü@G%pÊ,ß‘W·N¤,â³Ý®ªõC8~ô¸mßØönÚÔéfC‡%ÛMl3�Åää?lAÊitḠãÛþâUØœ{z~5NÄAº_y[mèÙ-ó+‹š_íŒ/ÂÖò'àO%`,Gcƒð÷›ýÌ2ã«DÑ=ÛtE¶¤X…á|Bº=ëWƒÆ›%xŒÌíÍ^7ÇHÀÎÝhU!‚:)èˆv$x4îXAîᶠ.Óš ¼m#´b§")©žd*›™PÖéúœjƒ¶ù~XàXcì5ruaSýÑõ–¾ä(:Ó¶ÄW<ÌŽÁîÑsâõŠû1&çH_G€ÍJ™óàӆџ“¹Ø‹}ߜғÞ\òÆ1’]TTD‹ëë™Ü7Ø/!‹£,jJýH"Q[F}9f»J#…B"§( -\² غŽ£¸Ëòz$£¡Q6™sUÑÉ‹ä$1¸^mÑÆB¡etŸÊìÍýñá£>ÿÑA¼Ô5ô87®Löwcí©ÿX4ÃÈ—À8W¢jêãŠÈ—�Zö›ö=·6oz:Wÿ©Ï}ÍäºC(ñÈ¿ÜÐÿÔS'â’GÚXÙ<,r‹ÒÒ2ÿའÂyÕs¾„%~¯·¶ä¹ú·paŒ\6½ÂÝÄ®üÔå±\¬ÿêI“°q{sÃàlw~,ÇJà”+_‡äÆѸŸ˜r¤|úØî楶؅‚èjïÝ°ªÛñ÷-_¾\¸ýþ‡? I´Û*¸s·R3øì‚Êa¢òáËëÖvÎwQú Y––Ãjõ›233¯?kY?t‘ì¯MœF˜q¶Úý’Ìàf„5›ú¹WȆdÞ÷Ûü`hÉŠ¬þˆÂ:eVCc¿/ÃóÒú Uà+ÐSÉ¡„S†–3;“%ÞJ+“jgIOd¢ßJµgŒ’T禕È…Ÿ§3Ñ?4uUÂçIܵ=£éüÍ9tFY0Š÷ŽÖ×Õ9VÞIÇÞ -"¢¬˜ôôùl»?JEo7ôwþ¥ä±¥°ÞÑ{Ïo"¨`Dh¿ØIk„JÚ• -!qCQ,5ªKqûY”ˆé�£}S<ðXv]ñô`(#‹êð¨^êŠÿÐ' -·jܺÕÓzÂIávîû¶Ã±@ΞŠ{~<Y–iØgM¡gHÝÚŽØç%y7Uû¿Ú¢oš!9vêÿýzǼ2·ûKÆmqi¯|º¼<÷nR`[ɨêôj%Äau÷Úg{Ç>GcçšöJà”+_gU†oÏy–Q —R§™¾L(ò€;F,„ÛíÿƒIºÉÏrO@ÁPÎý:`káëKÙT[ÛõrqëHõGtŽeVráe÷£dü{‹÷8‡‹€t"ÓVdármß÷:)/‡×]’Ô"7-šE`h–õNÝ'Õ¸faTqÃ/²åV@¹`úÇø±(“àß…hNª<ï»°{WƒŽQLGd@„±2y§ær*ÛužX¸ÔñûROHu«ª:‡»ytaéï¯íÝÙò1øwÖ£€é¿¥Ó=I.–…²>gþiχUÛøªV¯¸¤âHñÎ5½PÉõ¬¤ÀïÆ®â[=U5oµè8p¶TZº›‘׊áeÍ.ÖTuý²º²w‘éå·ÜùA Ù8 5-Ñ-;,rýñÅ•Ex¼Ž]ÐFd›=;%pÊ•ïºîXUæÚ-1‹{=~nÓMkÒI§"`3 _-ĶxM*¹óÚÉeÃ$:[û3¥2}J©<kð}R€>}| îÕžäU0| -aùD%ÒK)›Ë¥rþ`$hʽwîÙ)úü¨ÆJ V;•’Ë`1V¤’À[’ºq¾O.HZf¬o¿WMó?®yCÉ8¥â3&]¿}¥, -±*·ÇN¯.JB«®ü±S·î„Eî©ÃKbßµèRšê+÷û‹Tš]ímÌn¿ -LrcÇt¶½6BæÖ×Øni:\ê€JƒH*3'E«BÏÖÙ-mcÏçD œòJ c¡ÎÈÝ‚)/tü(Ëk@Š^ÁAíE2œ¯E€!줊þך@`دזB‰MEªê‹ð^UKˆµlÛ8T]]=úãéM)Ý”»÷eË=ͺåžr¯84>äI—ºˆ:© `®uâóyWH :DÌ^3Ó -‡Êa°MÔl>xŠQµòs‹š?½z|ÑaLftWÐO\’¥iÅCRÀLòe3(¼ìN -3d¿rkÕìóº·vµÄÒDcÐ4˜*‘Qa;˜³Í½^ÂÛ(Uü¼Ìo?ùÀÿ‰Ã*mìugò½cí:㊔NuIn bø c\® ëi8bØ`÷’€»çÑÊÊJ¹%N}2ƒ¬ñ/áùêgòνuߧ\ùd1cZw�¹`‚ï{Ùsƒ{€ÓÀ:VE9[‰j?f‡;F”ë«í9ê÷ÊuøÑMéM x!_[8kÊ1¥ÉËýÄpþU¿‰¿ó§™ÿöÝ J”FÚÛëî(PÔ=V¾ŠÅú/ eúcRÒ×w?×Õ7o^ÞÁ^ßßï”bÝC(çÇ?˯X“ć”î¡”—H”Ý 0ø¹AŸwp÷xçärÿhŠ÷žÞ\Â%yõlªCPE‚Âtï³ìÜîT2Ûy7jži99\«þçÈÈ"°åŸÎ9ïò²Í/<VNºÁff è>[Á®š;¸wû+~_é`Kó®¾?=þ¸uý]wUe¨ç_¸,ß\¢¸VT'º}swý#æLÏ/ßÿ8ån‡M›6¹êéß6þœä³öÛiy>8nAJnÉžT¼®?´¬VÚŽó_ÒÍCÀaRPLÂö¡Â`,Ó»5ïJxãFý¾ƒÑ;œ!èÔ©x½EdLr¸ˆk�e+!Ó a*ص„ +�Ã+EšùÓ@zä`%Ï�F»þ_UH«—ÓÕW¯xøáã†øUcç|Û³øß׊÷6Õ‰]]Z:�³÷ŒY¾ uò»ˆg±‡›Ÿúq�ˆb7‘`Wªo†. -nfZ9ÐxVz˜PkºåÇëÜð9çÙØ -w‘¥;‘‘©N畹„‚¨<ÈŸ\PtbE—Âç鬛ò)W¾Î_9ÔWƒ$zðSÙ�øôÃ�í÷˜¦ýˆ`'6ï4ŒXL×yY.Ç£àeñËmH^Ãla1ÀþLSÉ nþäÒšk°uÌè—Àö¾¾‰CúQƦ>XD„+½¸ÂCÅ©nYS}’èEj÷¤+îÒˆ5ôC¿`±“„ (@[¹€r}åÞUh¾å–[†à'Ǫ‚"¹¬7kd�1É’;—I>}óì7R—ßñ‰é�ÁY ÉD?—ëÖ3©²pw=‚,@É–¾B05Õ{¥Ìw:„íëÛFçÚé{€[·*ÿAvðw¢yV‘éº~§VQð4Ú:!È9¦ûüÛ3 »Nýјìè –ð¯(tøéºîo•%¤ ¸ -æÎt]wy¸ò³Êk–_ü™;g4 ¥Ì 3C„ë;àþ&©<iŠó`úæ[|WIÀ*Žºá' -&PŸëdS£^ÛÃ&ïÅ㱊 ÙÄ|•¨¸~€-÷˦𫜬>p[OðL¶…@?hrùû3¯_~¡³wæ.L,èFpï9l´ê8gf,uQqÈ€à™9ö&a"šÕ(§µ§¯,Ü›míŸñìÁè…ÅÈ-dÒ%~—ÛæmP&Éèm}}Dñ:£}jï^É0ì«$ÃnÕŠ”Ö¹e€r&z^@ÂÃêAÕŸ™)å{ýxG”/8PcÒ2²os(¢(L°máJÛ¶P#Þ qòUp4|S°…OigŒ¶>ؘH[rÉCÈÖJ…¤2Eì}ßÚ–t ¾Î+à¿q#ÿ‘¿ž‹ÚsÙtàEøï ÒïAV¿.#¦ÿ˜‘Õ7ZÀlƒ€'×5 t\^âë«Ge'ëmn8œˆDŠ` Sñ†KP3ïAî½yÍÐPУm.ê5èzXÑ¥2•žÚÚºw”°çtÉÓ ¦9zD“ëDQÒÌ!öúUȺ™<Þs‰s»âld+V0Åš€š/^>kÖ1}5ØEŠ"¾uÑ«Ü]±ÒMX`J¼¥ .‰!›s Œ£ëÓ5¯|?[§Jù -N óˆUá(Jîµô_emý–©}Ͳ´éàq<üfAX-Q`õ¾P°çS¿¢è^¯û¨ÿu'v”3‰ÀÜŠìÐÄ¡áÒA{*ù3þ%@뫉ºe|Ñ.•è÷ñœñ +Wa”äF°ØO„›9ÜÌ>Ð2/ƒž¾Ž0\^^ ¢‘Ajú"%i9üÍÌ>´¸€o‰ù?\³×£VàIù/ŽoóT~¾áö/—QÅ;+ni¥Ì¶÷¬^ùÀð 5÷⊽™äªAj1ø–þŠ‡Jƒ;JÛ!ÒQ”/UX›†ÿ‚õÈݼßç }[/"õðOZªåçNå\òmý}xÛÜŽÂí—ƒ³/7)ˆÒµEMýO××¥ÁÁ‹ÈÊu:5v؆R*0},”‰à\È‚<å1"Ù¯ÁŠ97«¦ý Ä/ô¿2dŽ«wÐœÕf—”BµßV÷]QY?“A¿Oœù³O¥S·l™gNiiHïéé[á$ÌŒ+nFtãáÞ"Õvz’Cê8Ã3Sru˜¨<fºì+©|üöºiêßF©¥M=ÉÈàî¶x°EäÕ{›÷í_,/Ø~2ꎹð4}°\P]u"—vjÚÖ9É)Jé3‹º õ kt[ð”Ê]Ka±;ryÏ]wKšÒ µÌžJMÚ#ØæKH´šÏ2˜íFJá3ºÇ¿þº@ÈÉÞžãÂ;tè<)Ÿ’|šîî[wó¶-ß&EqJ¡-DÚbDî÷¢Kš‘ö:[ú†ÀÌÛì›ØS¢`}…xCß׺ŸV©ý:8”C9É a�Lcç¡:‚druFÑ´•ß2½õ=ü‡ýö3wßS}NEõ‡lÑýdbU#è@‚ ó6×¹ù�â + Â(•WÇ5ðèw¸P¤UÙ3ø%:BŠøÂU•á=Nù ¨NvÙäûsËë>Ü7!ævÚq—ž–-hSuõUA2ºP}eØò^ŽŒN±{ׄ„!¸ÒYne3ɗꊎÔ߸î¶ÏÕغt'"%–nI:å7YDú¬®ªRLþﺬ<d‘ôêóýà5Ѽ˜Í_´+EK^hÍ”Žðyœ–Iæ;9©Þ¶åû2h—Ÿ»à`O ›†Y.{Š‡•å%%Ù;†vÂ5°ŸÇPRrƒ‹õìî:˜¸óê«GÈ@†i¬Ë„d‘±£y‘íËœRAY;»Ž°Cø=rÊI'‘ÿã?žv÷öz]’ï”—j纾+®è@óV]ÞÜÛ»³ísÒÆA¸¿}MT퓧SœuyŽŠãñ“59º`cë= -Ø)i}¸¥}ÿ/›ÏŹð2T3²§X"û†OW¦.úÄgÆuÍøwZŸ5°Òe¶o/�ïþvB"s‡ûþÐÜ¥—|.hùúTT[ÉÅÄu¯ÕUF¯=26¤RóOÙj’leµ‚:y†`îA ì{(s•œ[îß4<ÿP^Ž=¨3žU哸K9ïʃèñ0¸µÏˆÅìÈþ9?½íÕ~2ÐÛ$P Öºeb/È¥âÓ`¡ ‚Šø’Ááëͤþ¬ $î/ -Ï9÷œWìˆw|ÄÇ‹\²HAü�ÚÄW5—?{ÄÐ9þÌüçt Ì*)1�%k·{Ф6ŠuÈË\²0Žz½Žå;rØ—"²/dÕŸgþo~¥ÐQž£Š×±fE*Ô^<ß-��@�IDAT€UDð(¾Š’ù: -gÊ•õ—͉p·$°i“Øô.ÁŠáöíG7#Ÿ®W‡Dh.ª4;ýCËÂyŒ[3Qi–l°Ã4w eÅÑy½ÒÞ�ºãfk¯nPZà—hJd~ló€Ä-yåqÁé£î0'ɨ͒$·è&í2ˆ6Gk‹Í[ R¨Ó5Ï|?ÇJàm+_§9Ý/yyŽõâÑ]’èóx†ÛM±ô.½wp¥áñÌlö�üVÿÎ(»#i±û¦–MœÑp´„üÈDoÁO'á‘7JU$%®†ŸB¥‰‘ïó¯ÿ<À¢åk§˜eͲ,D -ÍÌêÏ z½ƒÂ1Öi=8BRƒ]ëçWR'+rôX²ì6Nœ šœá_£ìNŠÂ±1Tö T×Hݬ0úeÎÍ/â![%xF±°Ž»O™sŽì–¢i›°õûÌ\jïusçŽfÞ¤Cå¯ -Dî¾tÒ2âGY%W‘_²4^WP£‹#ˆ²—N|±+¾xÕö¨ç’©ƒŠ¤o#‚hJ’¸SAÄT.zq -|tùãtKà”(6™±N]´öƒ8$2Ã¥'ÝÃ+÷)®.ÔQÃíÓ0WŽÎAÙ ÿdŒ¼†ÅuñêÖcVÝÕ>ª0{ºFé¤W‹²{zeg'ŠIä6 8¾J“˜†E¸…z–Mv¬»ûͶÉóXÿ¦#/WÁÕ¢rÈø©JéæwÜTü’$By1û!Ë?§¸ƒOöV¾|aY¨—£ÔO·Ìo_|ù8bòs² £Éí&»mMúÃÅ“+{÷"9p4g·øJîø¢É_mþ0êL}'è-ø¢a±ÛPVë×êΞ¨:¶ «¦õ -À>õtWWá>˜–•\³’H·È`XÊV@»ìùͱÀ™ò{Ÿn9Ÿ-ýå«75¥]D,ow�„(åS§””Œ8ôSYî§(¾rÛõw\™Ù½qCZU×¢”Ì´2)pŒòu¨(‘$N&;´XÑ-:ØÛ;v›y¶È-?ŽÓ --=H5復‰ŠÉ–¨¬<æy‚³Uy?òê(’ŒiOEØë–¡ªÉǾ"[Qånšzxq¥wÏü’t0¿¸îŒ*^gÜ FȃŒÝô\“‡zv¦¬¦>„ -(ήšô¡É€•“ô¤U?…xJ�й҈ÿdHÖ_êëß(¹å´çü“ªq žÕTdS܆÷Žy7ß>åG·Íj}MÉ„,į̀º¦{Í -óòK?û¥R\–ÿ½9Â; ÇÛV¾(ÓâeU“çq¦O4+µmV4˜€{wÙ²á›èv$QA·We$0m„€Rwát¤Iâa¢Þtæ˜-’c¹XŒ£¾F´…ŸÍ„Ë&W!0p‚ø4È&ßÅ–€S@ÕcÛÜni<4ãì”-.Þ~\¬ÀQ¼ò”9o!¤® -¸C)u,ĺ}2ÖîÁrGÐmʺ~üM“I÷v µ5\PSÓ‡6‡ñ²Î5Îq†§KÒŠÞŽŒéu6Jžtæudãé%Ô½éÔ‹ l#¨rò°hR_[ºÓдŸd4õ>_Pþoó¿x<«3áœEçbWú)àêG©Øÿ6šŒJ!+>õàâB÷˜»¾@Ë¡œÏ -……ké"ÈóˆøL㟠ÿ·ÍíàD¥mæ¹J7ËD»6Õ-QXÙX‹Lû@Þ»°#~=Š÷.C-ˆÒ3a¬"ÒAžÓ²Ïo¨)ë\1ÆOµ¹)@Õ÷"Í�iú¹ GiRlöÊáôÿÜü“@G–”Zö”’+6oò?SF#ô ý¥LV~‡gå9CÏüJfË„EÔ&Ÿ‚.ž”åûÕ$¿æ†uðÒšpÂé¢á`4’ä’ñHÅ–”KZ áI+fòdõàŽÒ;úÑ©âLÊ%qi8”ØÕ“¨¶á[†aý@GÑ?ŸKü5àe¿ˆ�â@»iÉ®ùhIIf쀋¿ö…K“2{U¢§{mþY©°x3IÇ.08›ß=ðÃ;æÏwPDÈQA•˜® âS¨^I!±ƒv®ûøvÇö‘j$pÂvíïmöÀºuêôE—ü¼$ Ã0À‘?Q&rwãrí”)š£X7{C/¥±ŒâRf"ã‹‚òº%i”¸kQ4þ_dŒ¿ML ®âöÔ€x?P(¤µõÅ(_gE>†›õïkþüwŸoiˆÍ>oèd,E×b$ä?6(æ%iK³÷‹¹™z|"Üd1Yx‰H‘!fÙ¿€csó%UݘýðN˱–ý3Ο/Ù¶"Ú&ŠÒû²C(5°ç´àŒâ3s8 JNÏN^Äî^¹DS@c°–"EÓ‘D¬QÂ>v@ •ž…i$àÆNà÷#H çº?ñÙÒtSßø(¥rYV‘K~ØÑõd(º_dîÍY-µŠ7åœëGýäêê^½{„"+MæLWÜÎŽ y4áãÈ©ùÿžb ¼m>_‡¬¹Èçüó¾Ý¹ª‚Âs.ÀZ pbG¥Ï7ŒÑEa2Í»»:£ûÇwÙP¡9Ýþ‚ÂÄ«QÂ}ûÞÞŽÖBôUу$â)e˜Ýh;DÑ–)øBšöx:”îôôåð)~Îê檫«Í¬KND}rß¼ gØj;àj¯×hI$÷†§‰Û3áw¨å&{ÔÊå~âª.Ú²(à>†mò¶d.é,8b–¦÷9eŠ²¦am¹:¦ŸAå;vnF:ÍtÚÕb'›f––ŠCí¸äž|n%’š¦€]hê€ßý§‰>ßèNà@J«NdÏË»Ö+Èß³5á÷ ÞìOõŠr£ ÄëÞÛ“%O>ô=§œÓè"ã´±µÙ• HÉÜÀ`:ö¯Ëß3Š°;¦üûS'·íó=:Û!ÓÑ]ëa‚Ž•‘d§vÚèÍu\¾îîx{ŸV2Í{Á^6ËîŸaÐP·X;êosü|9f–!ñIŸ(] -ró“Hƒ}} ÇøˆOò-ͨé]ý‘gIp‚HÎVyÌxùÅUŇ--ûQ!ŸµäÈ»#\åôâIe°Î9wä-Ç–H6ó âÖjJd;‚]¢Ï<x<Tkä‚3ñêiVrݲÊÊÜ®††ŒÌío³Hà±—¶¾¼©ù«ÀìFåÁÁ±² Ålñ*ÒÏàëÝJe;«Z]]Y1»®X¬VBãý_¡Õ=ŠGüÒœ›o;Çq Ž×pæ߸â¶kfŽ;íCcÇñÏòþmû|Ç -jG,]2ç›�Ž÷£üïZûÛ_)Çîü`^éŽ]Dláž´m1Éô¹øþ¶!ÔÑŠ‹"ilµ†•õÚÎøð°†Ðõë¦v@—ìhb°¥Ý‰£ÐóÇ?™˜•ßžkk*’Ô½‰ÁþÁøÕ¥mÃèâîˆÄyÆ°vÊßtOmG)y&ÄQpεN«ÊÊÔcâÎß϶ÙãÚ½}ÞPÄó øÀ›=}í¿8¾H,Èvd -§0&¯O.<x/ÄòÛ¦Þ/�atyHV~³´(,ÆWäؾØÒ¥KÆsGoŒpïôéN±×Ñ¿|Ÿ=58få{»MRÝÌr‚j„f-Óš2¥°n¥€{Èà¯úÎø9 -!¯‰2·RY½Rv³[³Yzþ^þ¢ÁÒùN‰ØÉ&¨W%_*PŸŠwtkõvÇ™¿þÝ%E((Ó¹ý}î’2TíÑP8ðá¦&rþû¨¢øÿÙûøªªký=œéÎ7óBS˜ bA,AЊSkûð=ÛjmûŠ¶jµ}¯µƒÒñu´ƒm}¶¾ÚZŸµí³*Š¢€2©A‰B27¹Ó™ÏÿÛRÛI߃óSîͽ眻÷Úû¬½öZßúÖIï‰óÁºçP¼ ¡Ì„4D%íÓ]ñŠvnÝßÒ¢Ú.siaO«®~Ûs!žúGîÙS1)ç€è“xöˆá¹ôl'éݪZÞ>àíÏayRŽY9sɦöxÆmñÍÅ{W°ôŸÿÐО7(ñÛcÇÈJ`D•oP‰ö‚¹ì •y¯¹sgGÇPk1ˆ.gÞVÔzÌR9`šºŠÈÒÊ WI]Ø×M “¡hÐ5ÔŸ’è³>䮫Nºôÿ*õs°Mc¯ÿwì hF§`›=€ÝNŠHUÕ_·n[,ïôe˧öƒ°š–¶â«YréœÝ=ýK^EEñý๧ûk?è©€HØëó”_ÞŸëï€YzRËTËRij~�å,×ϯÖþxÊîAòßÕݺqóSûäxô=€?â¸Å)Ûøœã°š±ÓÜŒQåû`6Ix6ŠZ3è{fMEAÑàÄV‰k±?1Ë[…Zm?Æ~槩¸ù3NœÛ°t¯ËÊ%Ce»{·ïûðý‚-°Â³´ôÁšãÓ&ÿq"»óé&Ç`>ìœAâmM¦¬/«,¸m<õðöFózrÔ:^±b…‚rV_êöø‘-ÖÕû$pçwkLþ¨Ã~8íüåSVàÿ|ûíNO¶¤ûúí¦æ©ûO´Ô‡Y©b1ZPij—´Ò÷&]÷`´/7-ÒŸN[ßv%¤%»î9¶çå€ïá«Oj[°P·¸î]‡€ÝAÉuÞ¸ê4wÃœ£ò÷µbD•¯˜´©mëp߶÷H¶È;gÈõ° 4Ü»¨<rÐØ]×õIV4,MÏê$`}¤–]»†Î[¾|¹a;Ι¹‡,ËÜkr¯’Œ¥ÿ}#< -®’GÍx}®µáÍßüxÕ€À³ -e#Ž“uñ`wÿ§kà‚û�¤ã2öÞU&3jkoÌä¥ÜkD†Ø=®ëv¢ÂöU‹ßæÆ8Ù}ÿ¯?ÏØ VY6"¡DÈb} no‹çÖCy¾ÚÒSóò‘Þùßﱿ¿ª¤Å%ºïW2µU™¯Æ†Yç0v·L$êÓTלkŸLLúÝügÌjüwÔŸþÝ÷Ñöû™mÆHv -ù¯5‘™ö«¯6½„‹¸s8‘MžyAÊð>“´éxÔ×ꕘ½D”<p#yê:T,È´ÇóŠlOž—ôì>Í1²9Ty'‹\dóÇîuJÀ´°Q!ˆì2>}âÄE7Ý€d-[ùiË/;v[[<ˆí@¡=v·[€ŠÑü÷‚€fÞ‘þŠfçÆX'¬`Æ” dÓ¸ãø4$îôö´îòeg¿bQùÖ¢`…XüνÓP'i´téâÊãý_øB}¼·o¡Z]^¾Ö‹$&8ëÜ)³²ÊöãºîÁk¯™P�1ÎuÔä7«Üž“füZi[ö^ÿ¢ø·¦9UbÊ'ý_ò“þ={ª‡ï£{»‹ßÛ=pÅäÜ!Œðà}Ç^ÿ> Œ¨å+š0¿¤ü’V9vwÅH‰ô)Šuœe"VjÛ•>bõX#[N_1È£Ìó–•K=H=š[î“•#–mmqlk=ÞÛaêóãöÇÝëïëòØUgœ¨¤¢ -µ‰óÑ“ßˉö›°$ÿ& óû$¼·ßGÓØÏüaå®ÙÙÅ?(Î.¸XØyã%ëèÀ�E±Ú Ý| RlofÔ‹sf©©¬0,)öçm]Es¦ÈÖ.uMųl²(°qU¯ø4T³ó²¨CKÐmÀ¶W s§ˆäa%÷vç÷9Ôû9’ùÿ[±IŠqº= …E™¥•ðP<I™ó”k’[núöÒâ%¸æ™í~`…¿I}l¹x~ÏîíqËL²fÍjP _‹í3x¬½óǺ«çÍ0 ¯,Af±P¿GQYZòØGLуBAœºóÎUP°wxŒvËŒŸ“äŽâÑ67(‹¢gÔrºþ™Ò>ÃfO¨OÊs€"ËÕã%¬Ò€R!OÂƤ�ñ$þÅ. -]B`À—8¸=·ÕL+¶EÛèLEU©< -õKˆ´TôûH†ò¶üê¼3 -Æ(jði7¥ºbûlË-´)ߤpÉíM -p5àÖ îsZï8æ³J÷òOýû²%SÏ9’ÛS¿·º¤zçÃ{íNÔûÒpTås’K$=úµÌ}) 7 -Ÿú‰’i9²ËcÒf…fáY&àÞ‡p{€»Û˄ˈãÿûŸ·|a‡gmŸR¨žñ‰O4îI$ôÏ-½¬ø¹ƒùh[^UeJšNvs²g/À™>T!ø’ÌÈ@ÚHd«—Hp&odzê1äêÿñðÓO¿ÿ¿‡wt\`S#Œä]Î÷aŸum猹_G¨à‡@×ü'*R<ÇÏyîmØr „Û”¸)Ó)òªA9ýÊŸx5co€ÐIÒn OêÊP(Æû}ðgüç¼ ²uºK/~èP�Ä^,D,®¿b°ÔöÆ5¥Ë³÷ÍÌ í?/÷-ž+ÅÖe~â}?UT±TèüËäÜV‘À‘V8òùaMaÍAUõ|éBÓñB ;ÑAr¾Ï ßD¶à†í؃àå”/ºðÂ5×|jÂUW9h‘ÓiLGÜòûÞ|“çMVŸùö=yªêLO¤e‰nÛÒB_PJÒ>ê>gÊô#“´Â×MÃÚLdøá˜üp¢qo))ÉÈçà=÷¤n¸aÃE㊄Ÿ×%Øg§@WB<ˆH†÷R‡jÕùxÄuqI–- ¤9<n™RÀçcz"%'¹-åû}”=û/¸³ P[ ´R²èCÊ¡&ù*”øG&²åøëß÷ùÏß>ËçœgäŽ*güøXo<þb~N89s˜¿ºö$H([ž>ÔœäZ°¦µªsk·Á…¥‰ªì¤º·û×.£ÿÜýÇ™`»Î/5§ä×z‚KÙéHw˜zìõ“$a`‰™Å\‹Òâ‹þõÛK?¹çÄwcÇ_—Àˆ)_¡tW<*\·žûôáزƒGúª}~°=ÃÞp»gËmI§ûÂ*B5ww¸,ÅÍþ.¯8 LáZIqéÄt#!¯W`"³€“b»„÷½'cgŒZ �AM?ÊT:>¥ë[D’$+4Ôß2"Â8¢ZæEl‡§^pÉŽguÄôÒLJ:Õ½+ñ%JÆ{?OˆVzµ’´—×ùÈoñ°™‹Ï ª²OB¡!ˆæ_jºGéü¥ó±Æ\Œ¨‰IÎÖŒøwf—V>¿¦¡÷ÉUÙVðÃ{›ÎQÂÓ%b©&'¦,Å>’vÜ|p!YáYT?—½'žUIþÔ{©,õtǹ�g:<5•ôHö¹€ý%™ýo|>bn‡GsY1JŸ®Y£pâšÄlÃþâj˜m6»AÏ•YžP± -ºúºbÄô‡ÏîV•:ÿæpéG»÷w^z?àA¢ókÔ?yëø1'ÿÿÆT8=C,ꄲ4(MÛÞ %rèƒHÝ ¼SkÅÖØL§~ÊeõÁåUÄé²&c³ex œ|È?Éü "+SÌ––3@Þ©MÿÛß £åØoò©K.š£•UÔ£3xÙrŒÂƒŽcùŽÂ¨öÁAHÚ¿L.k=Bû6æGs7ö%ó\î^ï—ùïõúF‚'?Ÿ -|Ѷ‹À{m{ìP¹ÈMºû¾-[2)Úb¬ÖîêˆÀÞÿvÏÄß Sn{Ä0'f1í¼)Óç$lóUÇöÚÀWh¸éå&g×qƒ-tUVóÔþ†ž²®µÕŠPvCXQf ømÄKùýêŠ -Ô–o D +Á*Óô¾0¾`bŪaQÛSnìØ Î( 8éãèó¨Wiºî ½é,¡’ß©5¥yæfùšpŽ·úª«„öƱ#}]]Ì°m02ÚÐ5šEv騳؄\úÑOV¼y$ž³½£÷Ó“¾Å»)Ú»Á©ýU“ÿƒrÎ/2æ•Ø®{QÉÂ8_WQ¡×d‘¸g[àšµ€÷uú³ÜsTB/’¨w?³Ì{‘Ù,Š’^©)êÝ›Ÿ}ãÑc~à=ž'›avs|ü¬ï½wñNóS|7bÊ‘å6ÔÆŠTgœæÅû{-³˜V´ÆÝH¯éH?‘å8ùUU™màŒ‚WÉDž„óa«·é*K¿Fi®¤—´µõ}ÖâÚw.ãÌ™2@ù¿Ö™±ïGŸBFU¤ûÁÍ{RÎ?ÞŸuªÃwyR¥),¾ŒÕí,®¯Ìõ^Põz*¾ÉûªÁ¤k×ÙÔØøW3åÎ8¡¾ÐÙPÿç’Šûs”»@ª²Ü¬or¢ÞÞYÝ|n6Üœ6£�Â+¶cÿ6ô[Â5!”ö±ÎºŸ˜U^o[ä»–¿èÓx˜Ò:RzòèW -{žJɃ)B·’Êßýë±£¥tÏÁƒ>T¹8aYKIÍ[\-gœ�ÿ—<b¬f«Á•'“k* ´§ÿdI¥Â][Šd¥÷õ¶ö¦Ö¬2‘:Ì<8lcëÀQ¡ ŠÈôÏuÇø$i?>’‡du¢ìüÞ”Ev$²áýã£ÍIFqÿ/ÉfìgN ì'C[7—ó -ˆçCLá‘P¨ïŽ“Ìd²©z¸¬”QƒÆl×RI´«¦ˆˆ"yÅ#ýþðÇi±$TÖqŸv¤à3"¤ï4éæˆ5Clûý“ç^%ËôK�äMÄòÓdÛî§C{Ö/X° “¨$dš6¼òÔ@²'+7gÆ™dJJGA‘Ö‘ñ%kÍúÆž)qæÝ*{òÝ6k%.NúÝîÄÒ‚ ¼@ejK€Ñß"¶s‘ÍÜõ[Þm|ûºmÑÃn7öö˜FÌòoÚ™°\Êr<Ñ`R/Ø»l\QƒÛŸ¬¬<úcÞk«WÛ;Û3ѱº²Ï¹8øq€ýHâlÜ3P½xµÍí¥Mó«ŽêþÆŒÇ~õÙqþ±6½œeMr}Y–÷-¢{_O¦Ü+«ëëOêSÔòËÊ=ÉþO›ñßé>M‹Ï×áÍ›c˜œÙ&ù¶Ï_êëÿéPû^áÏâ@-bhžvžö×lâ6"A¥Ía|û âýÜ‚¢·¶'儲Âßtm~¯ìçwdëç]ý7oîH䳂ɾžÆA‡Ü<ts -.‡>;ùHÈ6fúdéã–ÍvÈœ4éžù™$1¯ñlçç:Ø>¦xÿúl1å{ܯ°~™»LöhöUðµÙD:ä‚– -Á³pñ¹K§ôÚý~h_Àò8’É#‚„Þ»Ýà¨úEÂH}5œþAˆX¼`\îöÚìì–‹f$}J½;cgŒF 8Š¼„2~%&k1çRº²:“öšá5ÁØÁ�OÌNêH„kDÛlµ+®â„(NZº|Š -\™vMÇ1]nVè‹U…eßÝÝžX4xýh’] jÕ©4ûIø¾‚œ”~;éFÑ¿Œ[A¸eÞU9ýbs¿Ë=ú~¸öBà¸xΣä%•K3Ͻí…Cà;>v8Ú•yû¼xËZ¤ûÿ´¿×eçß듼W‚ýCk_üå”MÎ¥ÖsI]y&ð -2¶Ås=v¼£FÌí ~åù–öù2üêim4Ò=Ïq"”Jw-!œiœñj²-íN€þé¥å‘CPÈŠ&å|V§öÓ%ZÞ¾Y…$5èËkâÁåý¨˜STäµ´´«ÊJõÁïß±Wc_Ž ¤ÂW—]ò2²ÖýVê#e¶ÒPZêÝÙÑ3_VÔñå†ešñþns+êŸ(èZ*ñà·]N>@-V;-ß·Ü"ÜW9ùÇ -åÕà?hËÄ8Ùë¹pžnÞ8uXÁQ#<td}WW0Ÿ&v’äþÚ¼£õávtuM´=í~Îé<Ðt‚`ÐÝáXìÆv3Ù^äS.A!ƒ¯SIYÛ¹eí ¦wP&wÕ·d:\š¼hjÏ P">ÿ#-‰ë�áËöôöh®â¤Ré\)Áš~µwcϘá4(¹·¿Ž˜å+nmèÁC Ë´PŽVTTÓ9âjš2¸B“SRáÝͽ§×gvâtÙn(hÿºy\Þë3Hr¸bu#9óPùŸ«díšœžÄÇ'¨¹ó†®¿½+cŸŒF |kΔf#ªá:{e7gwII¨Q4ϧªÁ]z+¢ß’$~}Q~@dÁÙ3 -I2ß ÿ£é:¦¾ -Ba%%Qæ±âšßBùªÎÛïÅÒúí°øbN@™¾~ºÄ| -wõÏ¿·sPñŠÏ€2»‚32…8_‡-L ÉþtI4¬RI:›5Jk~iiíÛ\2Ÿ©.íýÉrbøýÒ}û¡´¯T(Yˆ*?a!¥<i¹?�Ñ5©�ÿüGÏ]:òQ#Ú?ZŽL¢*Ø‹mK#+&4îΊ¶—(Ô^‡ôÏ…\*‘ÿ¬x¬µ”EJWoÙâB¬Èk_y’ÔNO“ƒx§Ë5™pYqCÏ×Øq6I »ªJÅâL6Òi:"‰êèÒœµ eZ+³é¯víßÙ'”òà÷ø\ç!.Êy9……6”®›öX“Ÿ)÷ƒ[ün•˜Ï/,‰î œíuu;)£vn ¦ìÙw�:*ÈP󀮶ÜäouÛýŽÌù\j™ß” ý8„(Yí3A0(Ï__üÅaû˜æÑuaMÚ› †iܪ›N1¶ÓM2"æ®í]Þ²Exý£?{â]Îî¿GTù"ØQˆ¾K•yÚr%å‰âôë¼nÓp½f—ºy¶â} -É]&Ïy§ä‰ HûØ·çuD¥�š`€Å4! -›|žÝCvvõ¾» ô ¶Æ)׃¡¶º…øG¯ãëBmëf eŽìn9¼3Ï07ƒ°Ô�áuBÀM¥³m))¤õô½÷&Ç{ƒÉtvÊvpƒü¢<‰ ß' >UQš¡Îš€. v ¢¨Áév5uw÷²ñg”ýú&ˆ‹&ƒçx¹ã’VŲ6¦«‰'$BÞâY®@…B¿†àhküªú€«ºp/–ã¶ßw$©®ŒjOâ¾™…ÂD;éYTY¬R†“±CŒ�öú#y<óFwò÷ƒõ¦ý¯mÎ<™=ÿÕiË.„s¿X`Lf‡B2yÉ)ïLíãF‘»d<-COEwÔUC×�ß…½þõ¼¼ƒBÁd{ÇîuzK ;•*î0ÈfÐz×{î‚8jâÄaš%™žôÖ‰ºG¿°9¨¤ÌxÚ¡ªòIöTÉùãüþ6ÑÃí± -îºÎ9ÅÙ9“™ôB™¼Ù«qô`u8,ÊËŸÇ«íýÿÄÀ*¸—°xÝÚÜÛÚZ®( ,‘™w›ç²‡¸|ÀLöNU|òy¶Ž3Û<Ä%å¹Çö½öá–´Øj,x¾º:ÕÊ*û:v¹q‰Hë%™UIÄÞÓ¥3íßekòŸt]²‰%_¾îƳBÐ¥“#®|Å*™"ٵȫŸ«Èî£[ÖÊyײ•¶GÊ°tnsew( wnn6PèOX&™¡E·÷&SãÀÚúü³³3TxÒé2À‹n0@ƒòÀßìÌ íS¾eDGÙׇu}<ôéF¤IdS⤉G-8lX¶0м”¯â·ûif/ò0P›z…–ËZmf.ž±á e+.†‹¨ÄÓƒÀðÏGóûº>qôþÏK’ôA6Û¿Ô6ôúÊÎ;¶2¹Ë±Û£ªü>Èô:ü_�¿8·,ź¼wÌ+íƒ|Ž“£°þåÔ5.)Z®d˜¿ëBõä€D¿å8.ó1þhš8ïÃs|_º.ox²Îz#jDÝb�–TT>ÉLk¨L$–U:í=ê“ž‡ß};ó³˜É–õI† xø}ÈÑfX•T ÕØ„õõ¦”xÂÒL‡lV9ÛÉUÚÅ{Öl …,Ç”aèN£x&ñ9®Ý†�Û>Û¥Ië„Ë!L«XÁÝЩ>L¤\|?±…*Ë¥9è$±u>ÎMu¢âò}bÕ*çÞº6ÿ1ØÛL£q>Ü0ÊÝð›‹X‰ä9_’Ué_ OoKQdâ6—ƒyó3ÄÍÀ÷”ç/m—¬õëB8�oÙ´w/Š]¼ýXQ™Õdéì1ϱá~-Ÿ{öXŽ] ¢–õÄéFîÒ0“¤EÁ9 œ¶pûŒF˜ßÛ%sòON -V?ù©Û§x¼gÀ"#¡3 +P6Œõë¤+fÕ"sí5Yæ–œ¥N½d‡ —wÅæDb[¶î-c²2‰–T6ݵË+(ÿxÛ²ÔÑ) žtÌÿ¶†ufJ@•"ÀîºÔv¿¤3g/‡KŠQ¸™-à -“ÍóKTVÏ -Žf¶]ŠêØŸ€CÓšê¿Òë?vëø˜ç]ô*6®oê3Õò¬º£¼\•PzÕ+Vô”’ðü0ç5‰;3<Î_ûã]÷>rCÜp9½ÿvLç.péqM_¡Äßà„ߢ„.ÜÔMž=/—ÄOïÕÓóEÉòŽM}z9Õ }=Íþ€>—re<÷ømmiq€t"篼¥ŒiÜúúúíµÕÕ ‚9»ŽS¶|ÅVNˆL¼fQCmDÖ% -×TWç ¬ _qÚÀäWÚš²Îï£ÌE†gõ-aŸ -v}d·myI)Ç•3gÚªÃ_W™Ë%&5ºížœ·muÞºÃØ»Ñ(€¤öCáÞŸ²ãÏÍÏî¬)ÈÚ='?°sznäÕ™ám“#ÁFËçÃñØ“©®Ôê—ý‘û'Áœ“Êvó2óE°vínjÊZ½¥Åw¢¥å(l:HC(—¤\Æi¥–Hçôh”§èÛù¿©¨ª¸|Ð܇ê_õú¬GÎ û·ßyçNTó@r½*îEÛQ¿»æW?Þ]Sê~OINK2-½¤ÁË×™9pí ÚÉä´çàž6ÇÓÿCuœÿF½½›“¾#{dmowû6‘%ÚÁmo¡c›‹õ¬ÒóÖ¬YÛìì:NUùÒ]±Xt}ŒD·"%±âü¥SÄ€ôM1¤÷¤]«ÂW8))Æô—<ÊÉžWT¿$ -•@kÓU8Ÿb%_\`(L6]ËšŽ:®)ÜöqÃ*\æ¯ø•ûÙ5Dgwo“zÏ°hýhAiéÉ8<XqnuuµYŠŠç¡¸ãù…Á.æZR˺ß* ™Ò@Â¥å _εEå¡Ëœ‰‡2·„dÍ´nÀE1ΦÞ{áUq¸¡IÇû…GÛøee®•vç:^ H”õÙpè£å+JoNéxˆ¼¥Be¿® E¹¹9UÌUr+b7…u™s°2öVד!F´árYqÿ\Uv [ÑwYž¹ Ì iFª™”Ç=ÍýYH¼’x•Q9ñù|gø”•¯kH—*éØå€Ý*sù½•Z‘þ\xß|ÛÓ³VÁá»�àì41u¿*…TjåEÚ¹Ïu&óß-XþÂGn+Š5¥xÝ@¾j´û•Ç‘ÂÀ™M‰ëÞµ‹.^*Á£–öð{?z%0¯¸8Ý–èìE W°§áãÌ5ùòO}¦ò e .æØRÇõDü^O ÒbÄJ£Ê›ö¼hˆ„îÒe†Â€ãnw]úö˜¶±iÓ#‚À£ö‹ìÊ;-Çìp8ÔŠ&£³=�åë¡:Eþ€î•M!_±Sxº±«PU/¢Ô×{AÛžÔ¯JŠ·‰O˜VR–Õ{-\ˆÙÃÇe¸ð®E%5Ùij?g¨ÊΫ�²þõ÷–pÙóaƒl(qºkImí˜ò.¸¿á=à·ö›�»ûá»éhwVeVfr§m£U"\çÄï[¼~}fp£Œ‡$›¼!«ti‹ÓiãFÓ¦síËäà·<óLãŽ-JÇjé5u§K’½ƒš,Õ(?Ðj÷z«äoùYuŠ°²CùŸ-Í.ü‚•WtË›MÉ" ãêÊ)Ÿ’ƒàÐw¦‡r¯Ùß;oÑ¥—Îúçþ}#¾¼§úÓ!bv'›ÀÊŠtõ6º‡Ç¢YͽŽ‹¯Žª¾eT¦Ø¨±Žˆß?þrR<ê©KŸ(ÒZPÚ`/Ey‡8),X™9%3•¼? hÈ9X9¯¼ú²ð¤YË£šúMHüŸËEv!ýŠ£˜Ogh)õd8}?«ðØ/Ž—'èŠê¼ÄŠ E¯( ÷‰ß˜Z29 -üþ$HA*;۾ܘÉZ<ñºÑþ÷©Z¾Ä¶¸ŸqV‰ýCŸÅÈäT§‘+Æ°Ðï{“3;Õ¥§++*2=ŸÄê1Ñç:®Dßä(ωü¬Nü(2@À²Âþácp2š–ÜÒ¤ÅrS–©:š·å‰ŸüämþÑ>@g{ÿÒª·TâÞõ¶ã^O÷SnX.„L2Ê•Ø4ÀÌÎI™æ-MþYîÞgËä>à|þÁ[o</ÖÙ‰5ß,Åf¹™ùf¤6»^7E:²‹y¨Ú¦«`ïœ<©gƒ¼ï@¿ÛGöÊ"®µà’O}*²ÈÈÔè5_rLû6 :unæŠü]EbïÚ3fS±Kú‘ª,,bOÖ¦Ï)c’|uÀÏÞWØÞ;ÈO¸/Ä×<eèó“� -Z¦ci\~¾~µ-È×϶L¸SV¾�Röl¯Å‘hÌÓõuí‰#Ý°·· Ø£›îN[òÕÃß[§·£Pê+Ê××júù”[…i×- 6…Ÿ<94a<.•ã}ÁÝÃÎ{; -%@Q—y|)** ÎsØÝ?zn"3¿Dwlx!=ØN„%ÍÆ^¹¢)Ð-MXî¹ëñ ‹óX°Ì's@ÿQË¢aÿTÓ e_-ëé5–ëì€õì×]Sdg¬ Æ÷f檸n4WM›–4u„ÎTÕ4Òyð{á?gÂoï=²)Ýÿ·�¾´ -´×¶¿ÖïZ#J€ Å«M™{!¾½ç) |ÃLX›÷¬^íð_rA¼Ð:ódSãY.3žÔ$ÖÆD‹x®‹/P-ÉÙ7=ÝÐ<áy£iLNYùƼ”æPfYºYÆ‚j\jj>3OX¯ññ¹ >\›Xi…ж¤%†�§WØDž7`Áæðœ&µ÷û�?.X1À²Ìt”~yº·A)K-.Ÿ³áýþoÍ‘TT¶Fé fFémifü :+«}‡®À?žãGŽ&Œ]÷‹ó>á—¤¨ç~›1æ¸6“46fv\ ³T–…äõ&^ݽ÷ê—MÊiﲋ4ÂÊŠã6͆¦î…2æÙh—¯Øþ§›rl﹟l[= ¯(=ô@GG $eiH>~?ânS›t!1ðëT‹>x؀𫇪ç\ H5á¼é–K¶x6¹½×Î~mÚ‚÷DJsbÁ¹ƒãë»°@:öÅ~)™ïó‘_¡zÒr^«)ÊT''YHDðÃ\Þ•.¾4“ÎŒ6æq8åÎ…i¨IEq€´Y:nçvåçgî)³¯®îhI—cJ)H‡Ï9 ö“êPG}O°+µ' -…;(ìiÓ¦iàÍñ\?xSÜ(BÔ§ÜÖÁ{½ž°ª‚˜0ã¡·šV¼~Æ´|(qB–‚Aäâ1¡Öiß“YþÐ�δ£ -Ja¨t<8r地n#¼CÝÔâ¼î²?W¸¡oa~$vžëXYšGv…|Ò^š„éÎ’ã¢ÉemSÀ'hW|ü3QYñÝ9•†¾C³ƒ(=D?̵©óí€yL$f±LXtÑl€¬ïÄ–b&eìeF¬¯îmC¡RãˆgjpŸLæÆ·*\R½jØ3›îïÂï ù¥HŒ¢¾¸{™Ów·ÌXÜ€eŸ§:Nri2·¬¦†[y[™ ࢣT Ÿ²Bó…TXÜu¿ê;oZii&â"` çO¾âò›nšü2ˆçsqº»ùŸŒ¨Ò¥p¸-�{6ª÷™–>^ªòåååY¦k²æ1[éÂSwœe<x¿±×Ñ+³³ƒf¨ s3™Rºƒ=vÝþnÎÜûû²}½Ë—/7J‘±g‚:NÛå )±98"²êÒ“3VgZÕ_h¸ÖBƒÈY —œÊÉó$ÉéCP!cUþÆh|\«à¸sÕª!+ÿå†í)ÔsÛŒ¬íeÐïu™§!³í‡ö@ú‘êR2Ä{ÁdX§œÏ†šÒ–þ -/k -'˜ÂÕ�B�_——•|hÁ®]˜©¡ÏlÉ.ÕÀTx‚mxäí}µÇÜ‘ËVÞVêR¾Ò$ÎKfÚþqn(gCÇúõ4e$¯Ï/¯¾õ÷×|j‰–ôh—SV¾ëÖ¬¡†íÐÓö¨mmTÂáTGòÉÅqW&LþåÂÒÈÕ/î.–m 'ÄdH %‡™í=B,ë·Šfoµ]'’Š)a53!u&£&ñn» T±h¨îXe4H}¬“ü -tøtû0IÇÉ Ö?ñ¢`NÎ!Ù¤÷mYµ*“))¾¯®ð©U2Q˜5QàU¤©¾„6ÿX¼[¤–Þø@½O"¿µ<å!”SyˆyòfɳŸsTõH×êÕ£^ùêó&òŽkÎù׫�t€‘‡ÚZ=ì°Gð,ÿÀt¼˜mÓæ´‘~tá„‚®ãäî¹AÆ£Àñ !eoBaFñn8ÒSÊUí6ЀÂ;Œ¼ÔçúgÎÌÒÅõÈ`K&¬ÔS 6kDê¥þŽü|ì -àc%XçŠPýIÿ¤œý³£$vxÃ;¢�ÅÉÇœÝR2wyÎq휲ò½yùr“yÉ=MåÙ{ÇÜUNuár°ÓŽnSÖË-÷ql]&Ùœ‘TÎz— 2!NÏÖ”A÷*E6±\ÃÐdÀÔ¸yGšê2dfø —T£Î›¯õÅ`ßÇ^JàÁÕ«u”7ßI±0ÈÕYõÀå—M!ú„‚`ç` VXG}†t.6IyÌ#ûvë ™Xƒ%³ñ1ÙÅ…ã -l·çæ…²X`¹,Ùצ\ö3eO©¬\qÊÏÄð6žŽï•¬B~ô)>)|ÑUÐ=þtMƒ:£<Ò§jcß nÊ¡šPxCϤèÊ9íFŽ…)qÅèÝK…ò| ð?Wngœ¼¨(^évT{Û,0½bLŽYÞe•EM]±ôËžÓ»û²š£…MSU³¦‚ÖóCÀÿÉ4s^>†t"‹>úÉr,¼çñµ½ÇŽ¨1lJF×1äc=…nypš§jÄ ªª±Š¾ÖÖWïùDϲ¸„R/®öqéOrÆÛGz?¹¡±±½¢PûP¡¬DÂy¤·A7´Y=¸‹'4=s¦;‰« êœëÊD¤()ßS¤3ñÒ;V¬0_ïJ<Æd¶Œ8Þ´¨ô®m ½/6™í:y†ÜP˜/n|Ö%—L1òidaçÛ~9QU•qUÈ”‡)\]HðI!T3ìè\J:V_`·×!JÊñLßA_=ù«Œg¢4ßj³•ŒCªl<o甼Érš¹ÿ6nJh$óTmVVé{Ì‹»ÍÁ,†ªPÃÃþFb–»„0ïCãòKúÜhq«'yWb'{ƒ]XÞ¾Â-ÿ¾¿½©¯¯Ü¯;ùTÛ›€þŒõ]QS%š;Ô˜l®ÌÒ¤À'+O lrf×,‚my9•ŸˆPpzÎόΆú߶Éw]7ÞpdµŒh?VÁĸnöÂk8£Ëà˜ŽeÓÂJC íu¨³ÙÛ¯R‘jL誓6[%Ic‰åéÃÝ—Uäe¶)v¸`~ãWXÔ›€ŠaîkÏ<®öM›vt9¢ »Ùi-ŽžöVOñU‚Òð×£}>^YÉÉí7Œì¶x²¸+¥—©¥3x «‹Ý„àï|XLOÊy¡ÿª%$%(N5U-sµˆ×öîéSC¡ˆD V¹pxC” K¤#ŒØçàãWÍåû+*F7{Þî˜Q7̼D:õ˜/ }�–g¡gÛÏÞwÚî+¦yÅ>Ÿ‹h-³T5!²Þ†OVŸÒàÈ^Çp5T)"Œ] õ¼‚pÑ/w»©µ‰D»¤Züƒ~.Î¥Ò¿�r1“‹Ò¡\÷é½»kŠá‘<vîO–¡²òŽã¼ÔÓØ¿_ONÎÔ:Ôý7x%~4…å¼”öË…S¦NÏÝÚÞ’˜–—7´èÞãL}ËÚˆ·ý¥îîIÌPsLb_êcÒÓýÞ\‹éŽåÅ{_Ý€^ሔÐdBúg@Ïæû©Ufî³Y%Ùu° †xzøÞC<)—Èn}„_$Â÷4òñÞÝp¤%°/–œe;Þ]xÐßÒ%@Òú¦Pe»Rø«ˆ§€7$„ɵ™·Y¢ôóS#þh‡»±-m5˧ùüz"±sQyîkïm., ú‚¶%™*IkiÕ—–QL’îÞg˲êa_jêÒÞì^,KÎgóTe-0ºîªK^§€ì* õ£ÿ™Ã_GÃ-/Ÿ–˜/©›÷# ã\ÄlþÝŠ…ÿK -´¡`y䳜¸×a1+Âáy(öc!á×Ê”ü¢aCËÃ+VTgðÔ/‰çô÷›Àfƒ“Àþ£*ËIŒï5)ÇnðLå6¿¬—¥¹|›°>Âm‡¯XP:äãÿKm<>?eÿ,ˆ!6³Á/ÊÍÝ¿°$´MÕ´_¤¸9¾ë©'%àó]\>gi@œ'°–YÈK&ÌÝ�Z±XA¼±1ã“ë±Òȳ÷€°×yËj8Ò<î‘Á潞a˜ ì>ü‚帿ú© Ý|Ïõ&ÀÇ5®\¤©òrø0¹C½?Ë®û•TûÑÅŒ•ˆóÖëµé½žŸ´"A²ýYïK»Ò‡]g*«§Z™J»ï×¹®^}¼óÕßÔ\—š¶Â¥<%õKªôp2”:mëVÕÒ¯8§¥£æþõZ†ðJlNrî–s³}-¯?óøAo/c‘ä.K5™õ:¡¡KQ›épý”`{h¯³šü!0ÞÝð›.ã·TÍ+.Â3÷>·4Ü(á·Áü'$`aqõ®ÂÀíPýÞwû·7–&MO[\˜ÖL‹\žSÊd! -?ò™A;eËwV.¿fæͼ½vvTäÓš÷·MõÚí(û}{Á :ѹ‡ôDÖsøRËÀ¹È¢€Ñ‹ÖŽ»'-goº¬[Eø|áãuöõ…Ì—}¤«¥UT:>É<ûèì‘�¯ïÓKÃœ¸ÓL¤«•›¦“-˜‰~=¨¹»Öí^YC†¶µk›{ÿ{öÁn§¾'嫇64÷ÌseT17pLwä°¢0Ó±çs‡o}i|Öë£Ýò½Gc4T竾hýòß‘‡:`#ãà®9¢)ÔT¸ü¤gSBþІŽMO´^uÕUH<ºEÁt1僜;b± -k}yVÛû»ìƒyòà غ0µëD�î2Óï%ì{ó"Ê—ð÷ͺë\ÚúbtãðjÂîöìb¸>—³öM÷þ¤å‚ÿí|Å–Xž¸SUù/ÁߺY{›ìH)½R ûzjgâcpÊÊ÷¥¦ÎJ&ûÞÇ9ÕídzwÖø¼m~"„ñrÇÀÕ &«&²ú€ëë#>íR¬’%ºß½{ûk‘M£¹Úy¦Ã–¸–™ÀÊø¼iõܹº™~ý²IE™�Ü™(ر6ÿc$�þ�…Åã!¤»ÀS&9aÕõb±dVaa¼è‡u]SƘ4Ñ‘ê¼â¼Éñýãõ„•-Ûj¢WÖ'ø(IxœN ±Ð/Ü÷ˉ÷M×ç[ |®èÓc «lJ¨ìWæõ¡hW>,¢Yuº¹-=ž”œî’ÎÃ]H@±ÄóÞ‹,‹×îË;ˆøWfwñZ{"ÏUƒ€évû‰£me‰2Wº´§¯û/\‰€çU¸ä x /`’7ÞL)ËÝN°>åAÅ.Ú"vÒâoa€‘‰³éçÞT€�ßG©f¬0HÐçñ/#Ãk¯£Õ²¹wëºßW¦¸þL:NÙí’$+a[Y®ã„AžnÁ3ä—u\ -V37‘:ËóÙ «»5,Ž1³^TP�~ …5(kR‡ïBEɹQQé{¢QxÕ 0—3I°cmýÇH@,ìSB¡¤7ÍÈœis -»*N¢xEPþ|ÛºüHãà–ŠW|¦Q)Iã¨_Ÿ`Ê,™+ïÂó~.·âŠwhþŠsGÛ!¶êÂ¥ÐÙ¹þ-¿¶ë<„¬³àpLu[ǸÈ2í5²z±Ã´þÉOéûûK§ÿÝM›BIOžÊm{ÂâÅoeœÎ‡²È€c¶fƒB -Ýu5v]0©´�¿ŸÃàzˆPF§ÀI¿Á¡ÊÁÅõ]þ÷~êS^hé)Yƒ2öƒrTÄ‚bn¦§@îr±Oõj(s"ÙŠÇ¿€JÖ—ã»ÔYxÂòÈ-‘ùï.¼þLz=eå«$ä489÷ÀﶟêÖĂÜ(ÿ2hü”tšÞÉÓôÇ ÔXŒÜûA;)ê·e°RíG*h-gä\ì㲬ÔY–ó�7ãCœƒçŽ½ŽIà$Á”¶´øTið¼K&æÄOt#X,}Ðr¥I(´ÍåÏZÌ]àÒ:—‘ÁëFëë£WßT´(Rx…;qÞ»«G¸;‹öûMßK©”ôRkŸU*3É€2ÞÑšè9�S Ìq„Z©¸U;~¦Áèdaì&µ†ëQÄ ¬úƒ}–m?‰$ -O™X‹EÎÍtÇR©_Âèú’3¶ëiý‡‰¤©ù³µÛ ñßVÔ_ætô¬E=ï#^±z¶Iž¯ø!0líDÚòíVŽìGjÀŨyx¹ªB–AMPX G~ý™ðþ”•oWk³´B�dÕÙ‰ãK¬ñ–-[ÚeÂ~ng²^vQæÝewÇv̦ ¹åò{ Jy£ié]6-+Ï~•FEDóL˜§s‡?àÃß‹6‹-ê𶗃w \ÉZU•[q™PÀ‚¢PTXyÛâá -Y0s‰Š^Ò˜ä¡ëÂŒ^Z-7T¶Ãï8úÞ+ÌŠpªLs]sVøœs2I+Â÷zÙ¤P÷Êš¢nÙâ/ƒÊì‡WŒÏï‹P弨ª4wëîœÖîFd§Í´LçIY¶y sþòýlÕp²Z<מÍØÙ³p_|4`°U[Ûû.Be\Ewé¯À§óé#ïåPX½T’èµ–ëåÂ_X¦ê -Ç迾î()þЗWetÄš_å–ôX«Ûÿ¬Ï´uå°0ÐM¨@ò�0mª$=o[ÍCAoNYùžSá·aµÁØç@>ý1WÁöîþÙ…ç/Ï>§0ø¦Gäø¼‰úRAhÉæ‚‚¦G}=M=vÿÀŸ•NgÓ,*REœxpÏ{=;% 0ºWÜrKÙzˆ„òïë[ZP5Pñ÷a”°‚®Ì¨Ë- Ð]ïìxÏ÷Zž•¿‡M|��@�IDATUé+y××þµìz÷M+Ÿ|å§O¨f8Ö×á z¢Ø½¥ÌÞæS~N÷‘âšÝï¹F -YÙþJ,ãóÞæÎ)ìr_}nÖ5ÉW!Ýf¤ýî,(Š‡er^DókJ|$I¥É b.TÔÜy(\bqœ_j�GÜí¨Š÷'øy/�ç7áf8çõ–½}·l¨v&“‰UjÀ¾ÚÐÝï{Ôþ5ãü㞧ž·ú è`›²÷ìYc¬e £Ë¶ÝŸÁ)|e^@ës¼WAkù•++ÏÈË)O¶ß46Ú)Óè0=’%‚ÓQ‰Ô/VCÃò>L/B„7Á‰RKÍœƒÊ³Œ¡AáŠHå¥3Êû$@Wn¯[ÙÖ¦æ7÷}€*Yׯoè-Åy£Ü”ÄØë‰E"ù²§,Ï]6·¤òBÏ’Þ•Ò¢+žk‰gÏ+ž¸´-M¯-ãbpšº«Ææ¥\ç?¸áÿŸ2¿ô>ÉG?b -{S¨mñƒSUÁ¿…Ä^˜ÏuÀ -«à2ÐôhÔ@LTû|•*qÕTdn«ùýoù}!± å'‚WÂ÷º¼*ÿ‹'6‚ã!Ãæ… èïJÇâŒÏNZúf\B‚ -Ø™Ò/(°Ž|t&¸"@ŠÎå¼iPýGŒZŸw%úsÕf[‘Xy÷üE—ìhŒE™ã½†Ê!ȸ¢-ÍGˆq´”¹ ×™@êëÜ49c„ö(ÇÒ“I¦<ã¿ÁÇ¿ÇXWè’ç«- 7…8÷L;NYù’ @úˆêÝÄére/[òåNÈóû™CPáB&9|óÍ,Lè*l1šÀ@õ{Ìí`T"™â™Ã…•ìM/àL›ÒG´¹&‚ðõ¤å 6cý0güðóÇÞ~ xq8m}2Hõƒ„JE)¶åæÜ?4àó[ª:ÜÛ›ÙEqM-ÇC”\½M¦ñ©X²?øY³cY·Zº³ -qÞ2êcCU0„•æ—HX_ŒÙñßÌû¯«ËÐ&ŽfÉêîK%Òõ˜«VÈ¥C\Âçê÷^i¾Þ¹ôú{ïô™»ïAíÅ+'tj¶¾“»K‰Ò "ô×,.U$P @Þƒà\7²Ýºj[œµ´x&4!]“sDÍËZçõZªeþn&‡> vº¯'$2ÛcÆ€¢íB¡ÎK'LòU(éæ2ù \?kóüã²_míYòbC{Þ––tIe{ÿǧ\téìA¼V°iômôå7Š+*æÌ=NYù -¨ˆk[**E‘•F£R$ÑUQ ¤ûªg;W—f•~¾ÞKánÔuëMÏr:IÀ/ßy‚Eë9žŸi©‹´õêâÖÛŽU‘nèš g®˜ÇZþ÷H@Ë‘’ -Q6kÌy£—¸ÏÃZk൒]2 rO¦Ù™Q*mµ#û `ÿ7™OýbMZß1˲7ò:Œ¿iÅI@éfš‚ŠÇ” YU3uÇw®¢r}Cmíq–àßÓæÓÿš@•xÊÖV=³`¨¿=S5M»‚©dåå—\’/\;ÇúúÉzÅULûéï·6´WöZvüÏqzTÙ÷h¼Kür ÖÅmw’ëñœUÇj6 -dʹðÛvâo(Û™�kÌêÿ^^^paObG\jÙÚEVÒjµÙ/æåG4½Ì“Ô/£Á¯kšõ}Œ÷Žá-¶bÅ5\fÄ…EÚáHÐ.ïõõ]ÁDmø‡§ñûSV¾b{z9ø}5 }Oœª+Àk¢æŸ)gûׇˆéó’Ì6í¶ûûÀ÷ð3/fµß 8ßp¹HŠ\*Åó|x7ªÉîõÚZ]ÇØå*\}p"¿dìý(—€ñf ·?ݽµáM³þ‰í›[Uªïfy=vþ¾ Ežödÿ#ϾW g87ŒýxPíR/Ê]Ú9÷MŸ‚bM½d…ü+@¨Û6Þ:„á6SGOÚ.£:sÛicòâõoÁ§F«hë«óRžžxŽÉÞÆûVJ\rc/%æ@€B¼ü×/¬ÏÄ\Ö5uŽ«äù3 3òþ›¿^–}pd›>¡ÅNúĺGv9ž”§Ãæt">4žE²åì½ó„JÄH˜2\Îõ’ Ü7ùk<¶Õ†4 ÏÜÙ¦’eÎ;dî ¤&`w{<êý*ì]ÿÇ<ýká~xEuÂàׄp™s•¬jÚø¾>mÿ<eå›é™"·"ŸÞ -ù}¡˜~x=|~¼j0—}/Åå»×Üó£æ›‘¥æ/ÍyM¬†Bi—J´” =¢Jî3¢¯0\ßTQ®¬NCðmÌú.¬³äý¼ƒ«Ý5m¬ìR=¡~¥ròê¼<ÁžgUT½Z¤¥ØU¿råJkãÁ®JRëý©ûd—Ýâpþ7f>1�¿ §ðÏ"ú>Wö¤ûÔÎíC™Òpœà|&`«Îqø~kkG?aÿPž—ûBqö‘Á…HX‹)»Üµl7‚d†Â¸á\K -¦•ašQG’§E2‡Ioˆ;¥Â¥€ÿ= #RBö–‘Þ -Ê'˱ƒš´e Ãèߟ0E½TÂz -u锸ûñTGâvU’¯Q¾ßkÊ+=ª3D=èÓjPA¹þx•=pk‘âE="!ã/¥‡»¦Í(®˜�×RmPÖ¦G`ü¥kN‡ÏG„Õ¬#aEQˆ°Ò¶H²r¼v0ŠU -îRT’%aµg‚_ŽcõËlqŠ1ø¢ã‡ƒRACƒ4)²ZŒR“Ë=ΪiºWÚå“#7ƇŸ{:n¬ ÿx Ô·éá)YáË+"J¶aÚ~åPÎÁÚŠ£ó¿Î›Sš_ï=BÒþ˜³èGå<íÁ–œPÃ{r[y®ßhO»&Ê¡P±»NK»ëÍ:_\³?Íöçú`þvçjJ'•mëSÿÓu6Ì3ñìåÑ|מWܵ=n(ZV>}ÀN~<íkTÎâÀä¶Jž[°£õàö|4 &M¹Šštiôé–{¥óN˧gä„Roè½]r?{óƒç–Üy’©QÕôýí={5¿ -Ze2ŸQž7ÑØ4ßûÇø:ók‰ôÔóá÷]…4ç*$Q¬wmövÔÒ®•]:mò+ÑèÛ2ÅOµëN™K”D¬¾c¸Ñýu[;+**†Ü*'iÒÿùG#bùZ`DÑBíÛÕ1ä›ùK½kÀ™%ïZ\o9’gà]:v ¥%é0Ç…n•Ék²©÷Æýþ®31up°Oc¯¿ê7L›ŒÕÇuÒë×8¼õC7{æpw¾dŸhÒ -.Çì¥x õ¾®Ë·/NÊ,êM݇“€TUùí�v[‚wdèÁ&pÌB`øŸt×… gO¦½åW:q¾ ÎYží(䪲ês‹r#7Ù”¶~ÆŸ1õä×Püà× ×»2ä+šx8ÊÚ]jඹ6mºÁqþüq«Pä„ãƒåå}WÎ,è<‰Õ;tfmUIKÌ ¿³¹û[7?*ʪô›»ö!M™´Çê–s'Æk:è?_²ÿæ@:õŸ ý/,¬ð¼oûÍÁ¸ñ†zk‘e»U©Eé¢Ùꌃߟ®¯±Cÿ? ÈZŸl[»àó1‚™l“!…z²û¤;ûKü~ßW#ý—¾ÜÓS:É,Ð7ÂÒ¶E1½Kt-LÔÕ¹N&ijà3#{퉥+¬ §lÛn$S;²4Gó<6UV¿*{dh £Y²tŸ1wî³{»‹¤©D))@½À÷§ÒÆ<,ôC׊[$H@"žîg¿E¥Vl¥ ÒðŽóvð·Ïü×v#mÛó.½eƒÞgØÎ.ŸM¿¦r÷WWWÛpÃ`‰‹)%»²S²çôx>¥°<®ÒLl[ÿn–ÁÚ¢P÷»r#¯5–E7ÍŒh‡°#¶w´ÇÊʾÈ9›kyΉÙ_Ó -ÂÛ—Âu4ÜQãþ§kKK‡\F'Ê¿://ѾnÍ^*³=¨xN¹Ì‹ÓW(pâ'ž{:ý="ÊWìå\I©À/B]%>¤¡‰.립ݡÁ(¤ð똲kÃù>AVQÕVn¯\r)¢«”NÈÎ0u#b{Ê$íDt;Ë(*Âw: n¬-ÿx 45$aY \2î´e–-ÌÚF¦Òmˆô~[äN m@KÊIúÔéÏCQåý -ý" xw ´P"qÏ '€’ì–Ò$©ž)Ò‹€KEm‹5ùP‰åß«ÿû_x¹ª*âò\'‰}o"Íï -NÉ}I@Ê„åª0ùM®ùokîë{³¶–ØÜ”b ÓîRo ÷èøšY‹Z‹§rx"(?x—ËK$‰Ÿ"|ü¾òþ=±—RB|/|û”å¸ãnâ<±;Žtµ4¥ÓÆ+¶c®•={¢/ƒdÚÓ÷å°¡*-‹k¨’¥75èßÝ>çÂCZžïcáêYÕ›;:òKò -Ïã&¿«\9b°GòTÙòƒÛ(Ãfä€0I‘H`@rntªæ߽ʞ¾bkÙß$ÞNn@ënNXN*íuÃh-‚n‹J¢» ,^‡¯ï ˜¬wõ26ÄøÞ…*óV JJ-‚H;ݤ·á‹–¯¨FÕnȦ†Ÿóßçtt½=Pô75óL; Š0{6ø$öß®*ÿàÔhÓ0߬/Æ꺃Žw>ÿ¥•»n>·*cmöûÓ‡á‹mpñp‰¤ö{}“W¯>~'q*2�çCòºØ¬/§û›_E‘Ýw8D@Mk'žø iNáYLC{Ïåž{Zn#¢Ø'G ©ÀðF’–ðùŽ’KLÅñÎs¹ºXu¤8³éWbÙ¶ëîAÜôÇàëÛÚÉX5\µÇãìŠ0Dˆ”ã75%W<4BÐxÎÆŽ³IÓHëM—T}¥RÄ>¬Ö$Oì>Œ_o -‡‚Œý¾[±}fŸS€œŸI`éŸ`[¨‰KèúŽ‰9páqógÓÁ¶bKÑÎ<MÔp«T¨´/o=æYí‰?1:ÿ¾bòäøã{»ïõÛý=”æ“ÿ>x0ßHÒÅ9ùY´Õ´ì¶ï=‡‡®BóDjïCûÚŠ¹&MÐ{§îJe ¤þØ¥CÖë©H ã·Õ4í6.ÑñHΔ5k–/ÿ‹üÝ|\VÙ¹¯v% „{DÖÛðßÆéÐd)ʽ¨)m<}±|y2w «kaŸ‰m E·k²²”{Oc‹S#sv±ëº=ð×ÿœE·¥äðoÎ) 켬¦F`53Ç–{îI3*=á î¼Êå¹”^ødS�cÇÙ&=w¬°8wë -ƒ¾ºˆFö×Ô÷°¯Ÿ€&u}4ùçê¼@Ç»#‘CY=‡êZš»ÿÇñØϼtúžHYt¶¸Ç= BŽ,äË‘<©‹ÚéWˆD7 `ÜÖÕ5úËÆŸCWLÎÍ)xððᨮk·!AâsXˆÞmxîtÙ¯|üw ½¡ÁóQómˆÖ÷¨’?¦rZY99gÄžIî&‘âí~ƒ8,n¹ôºüsŠEæÌÛ,[Ñð|„€Ù^… üwJ³‹lêî . ‹¸mÃÚOQžêpŸ›ÆÛ:¾Žˆò½dâD$A‰z%Ñ´ßÐópÁ9›= jÉCHÇØ£›Ö×cÜ|pn~V}¶Ùe½|äHv}Ë@¶ VBØÉ!«Û`¬'Á¨¡’õëɈXé§ã Œµé䨹÷^IãR¥aP8LÜ3,Ð#¶Ù¡¾seÍ»5èùß½ô8D»|bÅøªœœÁäBø0.xøÝEBÌ3#í¸Ê¦HŠ1ˆc–‚ù]¨é³ð¨o¡3à®Y”•ÇÏ ’ô4 j ÒAŶëw÷tƒhg<%Ö< tŽ]Atð»SÙLðzKjôÄH$ì¿ÉIøû„Å}²ûʳ+NX|‹%Ê¿ïsÕ_K¹PÂrJçÂùKäøŽF´“]:}6"ÊWtÕˆê±ýºgL*miÁŸo¿ýÅ÷»Ûú#¨êZò¢êÁ®ÖÖÔ¦Öî .~L–Âÿá”oäK\yÃmã0 8“‘VJÛT]ou)éï ¡Ú·î7öîì@OÛE€mÒ)*ãAÇ¢y…ÁàТþB§@å &¬F¥„HY$ì_S¸iú³qÙ]ÿ²¼é'J*gÖ…ËV~ºTvÀEâ¤w©¶–ΓèÀs{Ü®íÄkGëßaÅVLnuTé!`=wéi{\2âÙóçgx„Qä3•íT¦Ï®.[ïX|Ç#¦?ûŸQÜ”·TÀŸÿ—d-µtâGÁ”H¿æ¯¼Ã“ú}nÉ7Ãj~oòXj³PÆ/55eÞÒrÚú}GDxpë"…›°wûTj:lû8:H1p�É?¦Rçî?=õÔ@eNéœ ¦} 0Ÿás[ üE¨@{#QœUïÿì«öýñFš°Wˆê;/áÚç1Ù›ljÚý¥û|tI`FÍë¨AOßïÕ°#dôLÃäë9ô‡N"õœOÍû�ŠB^|ÕxôŠ8×n±Þh)Ã%C -[\î¨laüs1J–ë¶:ËH$g"—êÀÁtº _ŸÔÚ×æ£P–ëøèîŒÅÆËÄû3`{“lâ=‰Xªzúü™àjÈì:Y€õK¦ìE¨}¨*7¯``os>ärœ|ÿ^9 4Ôeuu|ÞªUǹ–ï'PRu ®ô$Çâ÷Ç=qkuÚÖ?‡J8¡pêù†G®±=÷ùëÊ,®A;ßå÷e½¯¤"tþà=N·×Q¾™Îrק0æ®R§UˆÕò¸Ayº$»5~í½Ë–¡Ì7½QTµ�˜ú ”®þ>cî—!®?3º–þ‰ ï¾4Ð>.ÓqaqFäñ¡P¨tý˜ëát›?ÿÐöä/sƒ’L;ûÝ°ŸÛ]¾påÐŽJ缄y^È¡NŒäi’Dn„§êä™_�VôÛpZÛÀÛ•u b;¯£c#×¼LÞqƒÔÜÈqÃ?´c§ÉÍ…ëF¸|³JÛUI{,•‰/Ø�/áêpôÍî6ì/WçOËMîêkÈVZÉŸÖŸZT¦¿sð÷u.kzM¥o”i{¯^ù6ïs`\‹ðìÅN€N’BŸO̸|ð[.)Éß•N™?ƒû¡ÆÛÚ¾¶Ô&üº'xPõ8Xe$ÔÒÓ6ÙbÄ”/Šâµ,f™Ž×åzm",±"2‚À ߧAøßâ¶4+×»ñ厴™øv”FSñÇíTò'–km1Õ`Á.7ZZLäíŽã¼ž$vÔÕ]ëlˆü}Óxô\µ{_s8ž:ŒŒ¨²ˆ/”£HÊ‚«V¯>ª€]·ÕFa/THøÒ+¿yã!FûÝÞ$©ãLiÉv~H€r9N ªD÷a^=Mˆ±9•4_2mïY¿Ì“Ų¿òñÃqŽ’?„²]»«# Àöä¬øÔ¿çÃÓíJQû =3’}A…]ж–‡UöIêñ™qbÌÌfYÐj²Pó@|¡®IîÖ?ÜŸ þc¡#<d"Â�;?Ò¦ ˜Â^ÚÖÐvû{>)”Û�!\ŽŠ7W¢‚Å*+ÀnD…ÚÎõJÕoE]·_\¾ 1Leyã'‚qÚ¶O%ô´E<uòTç–êóÝá‘H?Ž¢wC#|nﮜ2¥¹¹_ªg,a™ÓBYâ¨æg •×ÏŽ’X) ,sËóÚ@³ñ<¹2æc }ŠTç*ú«Ì²îërr¥Ÿê(Y××Ø›BÚ0³µº_××D™–éÄ@Ko=ªüš¹žO¦¬óê8o_He‘Á³Ò%ÞkfÒ"ê¹ã,ô‡èEÔ¯ÍV™2Ýóìxx-$qàœŒÁ0tî(|sÙ§ïE -kyþŠ%¼kâ®Y1KW ´R™ÊzR»M÷œï„dv‘ÆùZJíû—oš¥äf\8¾q½Ñ€º—¦øÄ®ê¢òéãT™å \}7¼ôMb1¢–9 «ïó©\”ŸÿÊÆ.¥»ðe2n’}Ù³ƒƒ›ÁýiËÿcïKन®õoíU½÷ìû0ì;£ DapÇ-š“¸ç¹FEóW“——§Í¾©1>}jž&5 -ñÅTAd¶†Ùgz¦÷îÚ«þßmèÉ€hL˜Tê÷ƒšî®ºuï{Ï=÷œï|§Ü¿ƒÞC™e(©C„oHôúýáx™ðuAÉ�†"$-eý)>ÂNE -!3žžN™†ì8¹¹™úäbÖ$QÇv²Ïx*,K¦EW¾Õ@<À®7)èÍÌfÁ/ç”–ùšÕUU{NQºy~ E9;òHþ =ðÀ•vÂn¬.Uúvw±Þ¨Ân^4/®³vF½æ!Æ#Àtþ{Ö²oÔ8õyhÀu&ø_9‡LDœÖCzó_YÌòµc0c ™@,§à3x?ÁÌ2Û"£Ê{ïx¾ ‡tŽí¢°¡ð Mƒ~YÈòB¸··9'Ú2]]!‘#XÈX…ã~¥(â#>ü¬Â2“S&™…4Lâ[·Ý–â‘ÑR%ÆlÁ6Kèü=¤úàfX*C¶eyy‘iÝÕîÆW¹Eð½ÎNŲK@´³Êr™EfEÁòTÖyÜv¬âŠ PÜÇâZ瘲PvÎV|üùW"TÙ‡&4»×Œ|}t~XCßâÁœ 0”x뙢LàlG œ£ïØÑNêëë‰ÌªMQ6!ÿÌ´Ø5Áë ¥„À¾~&q90â3k -Dv×tt&Þ€U^=r|1{à’åËA~ãNŽ&RF1²µkví _M¨ PNõ-û”ZņÛËõ;õ†ŠQÛ8×¾xs¶-Ó¹ò|ðØsЮVh®±œqxìy¼Ír_huc˜}|ཟ‡ÏK[[3§Åæ’eZ‘¢WÕµæô¼ú\?]8iRfÙÎ؃H~9J5’-ɨVb¹@•à8}réÄU—.œ·ó[ã»yU&%÷Ã;‹¿³“¨r»¥?UÉ%\¦uþ¸¿BSz àóºõªi¿ f#‰FâµH](Þ!ËÓN²!qÔT”;ò‹ÀYn¨× $7"n»Nª ¶–!¾œc¥LNŒ2ª±mù»/ë§ÌŸŸ%½™·ˆDN²m~zÅôÊ=èˆÎÍ`±¯ëéûo†Sn†Ãä{Øý ‚r!†¬Û÷nIÁDz#ííî#ÿž{€Fuë)—éÈpÕí=`-S'*%Šbõ^Päs8ä]àS—ûÎ(¥æÛ=ã»I+;ίTãÙÚ×ÒLó~žûîãÚ¶ÒêîôFa¦ !¼VJ¶ÌÞ§5Òû.±Öb‚µ’'t«®°°ô¬s„ïY¢Ì}»Ô[pYVªŽ«Çz?R¤ÔöþL) -ŠtÇ ¶É¼í§íG~Í`ÂÃó'XrÑRd7n¡ŸÎ×{dîÝ€óSÏ_à㽺¨"T¹|k]‡ikë7c‹ß^œËI‡Û»ã·ùugúºûãÑ¥šd¾÷ÿæÏ×(1FÒˆm\.â2DôºÖ´¦HÄ Ã–HÑk` ü/`ô°på´±«`Vǽ9i™kÕDÂ3˜Œ'ÿŒ#ç/N|iê‰^†g+UXÅ/s‘¬¾pBY=“|‚tX¸7Á6ùdÂt€%ɸb+(ûÎP$ï͉þöƒ"(œ©Y×v¥Š6&aϧGÿÚR¤ýê3KB¦¥1éžv•y¹£)z8Á^›5¬1ƒë‰aÝ©Yñee”Ô“D¥&@¯¡\ÀdÞz_wô£%Þ(o^ö’55I†pK`“ŸÀÖÏ>ðõ ->p®W‘nƒf»Æ§¹oçëÍýÀÁZŽHÓ©'!bn[Uˆã™qr-5kæ¯;œÎC"|›ÁÏÛ—e‚%E¼mÒˆL¢œU]I,ó¶‚çJ<O=Œû¤R’ &#¯gXëg.Ã]pÆkVþ×Àº!‘¿”wÅ[‚aé*ßøé_Ú°a`kq8uÜ‘ºüs{ -à `¼žž0ë†8«ð„‰RšIô?䎤å.s IÆ~µ±,Óì#<7³Öë(´¦4ô´|ÂÔ)|PZ€lÆ·[÷ã«G|uÈž�—>ÿÜV¥Ç5½áÔ[q~!QvûªèÚ³Ëå˜{Y‡¼gJb2ã„fû$wŽŸç¶Æ=.bCÙ’Z„‡Û(Šš!6�AQÔâÈÙßÛy?L�œlè ®±xÅ8öÇWcùz¬íÊšY³� %‘ÃîvlÕ¹ÑYÝ8“¢òf‰üõ‡Ãù…ïbØË÷ì‰Ï* ‡h)õÚ´!_){ÖHú=ÝRô¤=Ãz¤~Û`Í™*!h8_G“à…B{–Þó«õEÁí:qN«ÑB‡a/0±Åq8ò-Žu,9qØ–‡Î:R‡O¯Zwô§ô„ó¢ë¯Y*ûŽé8ï®@n0—å€ryAô%Ç㲬ӜuØQq<RÁ;ŽéúE®òÊÒ =²¨úh‹~ŒÄ®çÁs>»®ÓxNü¾#z¯£«;‡c¸ásüí¡P‚ÕÀ.ÊëâÄp7@³ ëÚsÝY}®ª·‹€b]v›Â‰¿œ#ËBÏ° “n»mè0•èrl~æ¹ÝéRÔóM.(.XÙÚ7…ô¶µr<ÿ3D!~”j#²í×°ÎÿpwíöÞ—{[“ƒÃø›F<ÕÿìVÓNŠ��”gm«8¶1s.¾þPi0‡|TPÇC*ôÑ-{ÊýÞÀÅ2qw1&i7EâOèzQ˜HÏæã«t˜Âìñ@2ŒB¤Ûn?k®«(( œ1¼)’þ²üȆ6úçŽoBÔJÙNÏES€]¿³$¼™š,©²GnþÌ÷À«-ÑSA–ý;sž€ŠƒÔU0;°Î·TÕн²ò=†'Óô¬ó•‡‡ÒÀӴ茕ú½À¹>ðö.e%®ÝÌjåHÚz2v«“-—ü§»gë"*t>óô7@¹-*gžp¶,‰SaO;œÙV™)XR÷á^æþmÝ—øö*POþVäUñ®d_8&cº—†È‚sJKJºƒ¾'S‹ß‚¬ÐpCX!@¸¢£”ôWµ÷—AGwºL3qFù^</}2ØT¯„IPÙK!+$¯È?Õ¯Úe'';Ä|‡u…Mñ==Ô¡8tµ=ô’Yóe×Ü! ¯‰# -ÖxzM±$‡ÈGû†V ÝCôͺkwÚœXÿ¬Í®7#lP'ö×±¸Œu'#~Ó“ì^7œ‡\‹¹£y^œ;ºçÃqú‡Þü#%|ÖzÀÃg×0 Îólè Ø—¼tµß#ÿQnç�+þBÅðð~°DÞŠM„wf,¸�~¥)ΣÁ¸ò|AEøѬšùOøvúxf¿€Ç_€ƒbå�`â{Úl¥³¥19í§Bì€æ»BÖ|1 ¹wyä—^ß”Í&|Òéiùκl–̸{Ù²ƒÚÖ(ç42±Óa6ší¸ìC€_<‹w3›Éö×…¶µ•‚lý?4Kœºööã}x»-L •°ŒÌqÞ!ß³§e;Er”5ƒy% îàÍ`|âÊ| -²ðeùLŒPÁh¼O --¼¢!D‰ŸˆÕRÔêòp¸Ã¶™³NÊæÆÎ>½Œn˜þ‚z¼@v$ö-¼-¿85DZO><AÓ¢ÅÐMÜåÄbr| -ïü3û* fVUÅ\Žü”w™Å6x|1€Ã"ã|M`Ø/!‰ë›Hÿg8{÷Ó`]F©u\ËŽ«ö–™@”jyà I̪.ÝŒ··9–)Ó$éçÂg¥c5ÍyWì6p«„¡7jœãÐdùúS¤ lä>13éîTzzmø£ŒÁ^‰¾Ú¬°îã¦Ã^]=¡¡ -÷Ü—¿ÿ97›Kç˜ó,Ãy7žI?ÅXÌ"ðⳜ_))Ã%g!–à¦</—~LŸZbJ_=b=ànV Ýjx8œø|™iYÝQÕhÞ·cEÿHýþ÷²á<c–vºlLóyÉ™Þí2ñM†-?Y-’aûUÙnNdÖúªm³¢/<lÕªUYR]m:r0‰Ñ0Dixï?§Ë̹x¡èó:E›UždúcÐs‚û•wäë¸;ú+ÖÇI*Òoµ²vœÚK9‘ŽÈÊ +²]Ù¬õÁï×¼Ñ:»vþ~MÓÔ¨¤aÉqŽÃöô+ËiÆ¡PHff‚3vú~º”šdšš’\*äÊ¢Àc3cÍ cÑüùO ül=c$Éhd?ÉÇÙ#E{¯¦ªO–E)j1ßpufê -Bö4þ?ƒŠþÈ?{·wD¿o¤ÍY»¢ÝF²|L #¡:gé~MdâXN㈽dp”kløp¶ÂÑ‘÷“oCJä2aFÙšñ¢àd‹RHdïåX«¸}kŒÔ—}äsÿ•?òjß–Xëx$6€•ÆR k,!Þò®¾öv¤mÙOû üò°’^–ÑÞAFÛ‚S•aSK_jn6Yâ¾'H):qþ©Œ9çªïÔúøèéH5´�ä.!+«êBûm%ÿ•väÙŸ~¼¶½½Lp˜ŸdÔä -ŸðU&è-í#Îî”`¼ÊZäŬîšÑÎd‚ãC~A¶×CÃk–eù{»®ÈS8¯4Trªc1?`y®ApPJ~x¼~úüôž¸¸÷ý$x.€ -p+ÓŽ12ŠåŸžñx@àNqö‡ˆ«jô1Óq÷(„M -®ä¦mñ¨¬iÈ@šŽÚÝæïÿ‡Îa^ãXû™œÑÃ6íÏ@O›,;¼ —ã1‹Âå¡ÕƒËžP1¾fÇÉZÖ:†uw¶¯z;(‡'‹ŒÝ†4Rëh3¸ŒåßûxÿHEŠ‚œY–¦ðL—e2ãDä·êA…:Ô—ÀüКһ°ÝË€*Ð ¯¿îã¦>ÇÇ$dß:Ô¢' øÈá8±’wI1<rÿ‡ÔQo×A"þ‘wðE¸Ç.î8Æ À‚ëŒÌ”UÙrQÜåèî&•sJÔvtåÈ‚u°\ÂAûWsí›]EE½c"ÙŸ›Ä¾ú7à¥9Y{]ËyX‡yJ³×‡@{û,½l±ª=Ú+ˆB«EÄÐÆT!—'ž¿¤®NfOßsIÍ™’¹;¢1IôxªIø ÞË—2*ð³,‰FõæªlDaÆ—d¨ÓëPÚÿú½÷fÁ³|»äjÊÓ¥êk»\«ÂM*2ÿMËrw±6óGÈ€]dþ9Y[ äÜ(/’5C+ÍdGÓøùÜX»<$‰JRMDHFþòÃî|ÈhÚ¢åñÉ >Åf2Ü¥‰ºÂ‡LXÚÕ噂ÀìAŠ±p/âAúèZ‚PFD½„¨¥ -Ø{ºl†{ÆIg7Ô Ä{v=x¤BŸJ¬êÊœgóç<n°Â‘—/qÆ BØ<0«;îúŒÿÚÙuuM’!SQW_ *À«ÙSàéÏ@axÃ(ô/‡Ð@F|*9LÒ”LÖ'Óì<Û5ýº+?||•Ò‰ªå„èm`›tôÜ™édç{%°)ë{“ó¼[4R›ëØE ßͳ]à„%gÖ[ò÷UóÖô¤Däë™6'ü[vÓšÔb)•ãü <SlØNGoýYL9Ã\†?[’¸.‹Õ?Y2†ª>C]Îß—Û¢'ÃÂ=!Õt"¤|~÷ÑÆâbšâ`þcO¼¦ÏôzCfBõ[’ÊìÊ¡wH«ê'®Ä‘Ë Ž¶å…!ÛnçÅR”g“¶Ç`ùz"2ßg]FOfÌŸ¼S_´~ßb¾_;h€Å¨ÒaÅ faéé/{:RQ¶¯d˜Ü} 6µßŸó¯îî-é"‘ãv;Yu§¾{óÆÁŽö[>A%}w´µÉEQÁïÊì-¶cÏ“Ë{Å¢Ø6O%7vᤡ…ž‰C©S™£³1ã‰Æq—'ˆšõÅ4&ПíOѤ ϶D4îh™%6qÞžSQðòáúù¡¨˜k³FËô;ÀT.KŠ?¶d/Vð‰"“^·Î¢Fô…)æ·(o[úV˜26‰>¶Ä#?~n{`<¶;À/V"}+Zœ3çƒiÁM® ¤ïM>‰¥ùÛ|@é8ؘ¡ÑX#Æ3õ͛,ÇH8™ƒG8çÙl:ýßÄçR¬êgé%œ0¬$òV—öNZUÇ WuT;ê3îÞ#/xé§}fõ9;:µ/kÎ\û‡Rˆ[žêÐ\o¡pOiPž€Àª×?nÇ»¯ØO|Ê®mö&;Ç‘˜øœ¹ùU3ÅH0ÉCÈøÄ„yZ}}—IZ0oE×eõÐfÎO*d °>ñÃþ‰ðõðl—åØ>ÞuÁ‹ãrAÊEÿÂ9½%R°xLEM-k0²trm¤¹»»mÝ›ÑÄ|Тi'æ:ò#Š8òõ¬"¬ïkˆ/>ÛêI¯?ªð—Úz·®XÛ“Jm'íøê½éÑMpF{© ÝoìHõ“Ç"éã/ õú5Dë àf<ϺWd³Ž»v[×/ö‘pÁº•(¸ou'<¼»>–ý;ÞL’úyÛ I=ÍJ‚g§cJ«;4b–ûImRׇ1Û_¼"&ƺ9<?¶Àù±±‘PÊ� -Ì:ž/7ÌH»Iå¶\¢°_lá»xá+)Ù{AJ”£†¶p~QjñbÒêgŽž@‡\«‡Dø²<|¡¶8Tt}Ï®åã½H-[�ôØþa)ìFã=Ás¢–õ $çÅðŽk8F‚•ïMiý~Æ k'fQ(36Žüô)õ�ÈUŽ}l:Ã1S‘âbÇà:… -q¢"5PøŸ -)²K÷šÔÖ»Ÿà¥xÕµ×ÜtÀ÷®mX׿%&'*Ö!Èàã.6‡ùŸÄ}[¼÷SjÚ¿ô1T›}eí®1ESÇG¦Ï›÷7i5CE5ºz»÷ð娓½WÓOƺ3lp³u¡1ÀÞ6a'û7Ë9XÃiž¶�È¿7§÷h÷Prœò‚aßtóº¤m–;.ü¥‚‰`bòK”µòØ@јϜ -ÚoáóëH†J![VßCýî ¨„¿·P#êláÜìš®mŒ¥Ó3b|á)O·´ì§ò¿Ú‘*&’u×]ˆ2ó(÷ÃĪì¼%Ø·ËÕõ³ÿ°b…üQϧ[ÈføPûÓG]säûÏW4A£o«Ïeœ¿X¶ý(RÉ4ù$¡ˆcÙF…aæa$¨§w½‰Dlùò‹/F.Hf&ê’âòлSË|¼Ó·iÍúdÒü@û’bº#0®`V–ñyÿ|bÃð5/,¾í6‹Î/꘤s¬¥¥E¦Ÿlÿi…LŠÕ¬»%™s$Öó Ü’dVý."5”¬cþØ;knîù‡æ§,+¥¯];vòÚí]E‹aÏÍ?¿¢`Ä8Óµnƒ}ºZ`Ho¿jF“†ä9.g&I«ßë½óÝËš‡4ò._‡¡>‰ðM‡-Ì ù4¯GþRX–F©ŸPT„(ĽB’†—yÈ¥Ã]¾†R—e¶# æ»~Ùw¡¸8—ü�ÊI�û‹…³'Ï™‹\9XKë¦TõtÄg팺|Ù»ìÈwŸŸX©ªHPlÞÊ©æ-ªæþ”Xæ%Ä´.AÿBÃ&¿àxæU—,„�9°Õà`•WrÛoÄb’sçε9…$@ä”m¥ilÜOc>°œÏûçWb±àIßþÎ0ïø£'pÕ£ŽŽwÇNèñ;í¨|æâ|PmyÏ”ª>N½WK[¿$Š¼xt¹¯.Ãp·dM§!šqŽY¼jÕG*Pùrvf=aqÊnÛ°'™~åª:K9y̸ôZ—s¾¬ÙlIJ7î—8r^¡"ÜÁOðrÛéïLl=G¸÷);¶!¢ßîÇ_')Pª©@¸ü´m¨T5ó©×¶ùâôEÑ0ý%SYF¸¢xL²Çaœ[„HfY—tO÷ûû‡‡”ç€!¤^ÉÑc}eÒÜùž;Ž -dV`7ÈÕDìDýÓ»cAºM9ðº#Ÿ?Û=pÀ¢ÊL«1ÁãŠb†Ù¶]7;ßYÞcÖ„ßÌìã^6ð¿êfÿ;'z PF„è¼Çöü>“ùò»;£Õ ä.ZV$îR†8éþTªù\Àoƒ‰â³ÝsÿXíé¼ -jÜ%’Ëý/Ï:7#Úï*È»K‘iæ(I7Ï©Û›¹xpéÑ_‚$ ®È}Ço[?ÍêÌ/tb:iÃú⑯•GM¸mPLúN©F=¸Œƒý=»´´ÕQ3펈¬Òˆ:4E§-²xqŽí,®ZSáõk¯Ç¯ÎV²),ð¬ÈÞŽsÂwA}½7Ì•€†Äl@9Xù‡ÛwC2àÎ]”î¶,äsa®7ñkbà {˨ÝÍ…³‰cÕÀ"‡ÉàÞísý¯]”Ôp›ÈBÊ€†’™Ó§öùé–ç>âÏOŠsÏ=¦ìN“¸ÛtÓS¥9…ÞŠ‰”h@«Tæ‘?ÿFPÏñ߸äSû™ -J ¿¼3Yˆ‡æ¶;LF'ךŒó8K”ÛÇNñž_yÜ)ÓìÝ™Â]ª,t¤š¹s™»uÈ&ΦBÂA’MšWæÄŸ°AñŒà¿Çk‰Xû-¤˜´ª®¬ëæ¨ÿ´‹¯.É?÷Skôað ˜u\Â3 l]ƒ¶ÁüÉuÍ;`[†Ÿ0\{QI¶8zÀ‚˜«uË’v ¼HŒçÈt0žÝë×ì›Qº/ Öà‰åkáìÚw¼˜Òác1§?R#¦óÏAÌ1|9yŽÆóZjEBQ%N³-üî€ì¥ïÛAô[5|§ªj÷õÑÇÐûs…ÆËZ7ö™ØÉ Î—6~EGübV¼§©GÖô1E/_j?ÊöeÃÛ| -„ïû}YëÂƪÀNÜ2ÐA«<3àå…í÷ärZ3Ó”"Ð;ngÍXº%Ö³3g—ïèxüëéL S\2¤Ø%n•mv},kC¦ãÌšÇ3.\˜£ª¤õú´:P—/_Î ÆKª*íô}.Û3ý<8v~¦BiêÔ©<òßíÝCûGºœ^ÕÈõð-ŒWWÊ•t{Ï Pž¤Ì»‡bE§‚eÂ<°Û}ÔµëÒéb-kÂ1³%¶¤±±N{zgOIH’K_ÇÂÁJR7k˜Ï¸[Árõ®i1ëUÁÝìOE÷ˆ4V§#Ê}ýke[©à¥Ï¢äý¾Ú±ºc]“×4,îD;œsÎ"M Ü;³ŠDWw¥Ž•xRÿæ{+ÿ´`Þ¼¬ãGÕý³þý -ØV=®ümpÊOAßbwOÆ#—ó/ ù¾eX†Xâa€%e’ÃÂáD~7KÛüôÎØd‹¾XwÈOϨ Åé¸ß]4nze¡¤s‰tˉc«¢ôú5½©™Øõ6Š÷‚·¯³é±ƒÌÑ5ð •ù§'ËMd9ëhÃb;Å®ÀŸó4Ù¹sý¿RYí¿Š|âï-—ýa9“‚,?)Éš\‘XÞ¥Ûññ$<ªÂ;™(îïeH4_ÚH°Ì÷!:~'¾°"sqo¶)7ø»¬™p¼«áA4ês+ÔÚ)ƒø7É78—iÀR‰„Ì{aŽË¦MkÂ?¯áeå“Ë«§Ãªî£Âq3Î0é6Ûc½fZaôÈÆyþ¹~xÊÉ—~¯üûVXhNCÖ¾ƒ½H*h©p¤ç—�¡ëBäÞVä’1“F5 Šk¤N£•[ÃTµòƒþþÊÇÀÒòrÚ½ek"\ÓxÊð)óæZÑ’)Ë›ShÏÄSÏᆫŠŒ´ž3Ê'Œ`øøÌb;4Sɺѵ·íÛ"Òß÷i ¹º¬ÍŸô»?"ÁÐDKCãÛZúŽaøî™W>ðÀGn=»»»uÆä!ýÛ·¿dÓz žKA¨_B\ójÖ¶n¶m{‰m;qžãgBPÜ�ë>Awž´B¾_U‰Ü …¼±øÞúucíZµüíH"{µËÁôe™ßæ-æ|?ø-¼´=¬Dd»5N˜7oð¸ü¤MýÌ_7·¤$Ã0öË@+MAÇ;l³ÂÏ3§%ñËä5øï¬êŠÕ¢±}nû`«£k¿êˆ4§_îŒVkµõã‚œº›ØÎ×b’rýýÛ¶Ñ]29^ÛbÊÌŒ5'ST4îë—ÝF!åÐk|¢&ôÌ1à1›f;нYg/Ü@{¸N–&ØÇŠÂ÷-›}�YS§ª¶“í7v‰ôuÄ7ß à1 [_yçe/î±HÇ5=òe.ç!Ó|_lŽçÓ/rr€a‰m›±·‘‚6´©_]€ì7ƒY~#rC]qTIp7ýö7ÅçždšîÏÐ5£R¼Ì+ö¯ÚI Aî‘8ÖçÚd3/º¿íõ?slMM>u»¬9ê+I춌ҺpsF],{dH±·)v»á¯åÖ÷b¡…Ò*ü=ƒm‰DÜѳgËÒRÛN§ Õ ”H$ª;„Bˆ@>ÈÌe†–øÕÔòòV<Àý 3>ÕUø¯Ãõ ¨D!kÜtäÁ)…Ý’LŽwÓ¥œ@ƒc÷gXÛyªÝÕVÔIÇZ¶q$B™ì¾9V–÷ä+LwíM! Ñ_o÷ê Úp8Q~ôÑG›@“èÓu'ÿ~ýÒïä±UWBp©ƒq$"‚¬*»º³¦x]ØZâñPoÔ*1Ã> žÓ6ZwZÖÚ8ŽµÃ ‹G(P:/c›ªk9Ã,GBbDCŽ&ù?71£&%¥j’Ç -9.R{çÌOtJWœ*Á3ʃë–u§ƒÄy4¤MݺÔÆâ‹K‘4[ìáë .ÏC_5A“-“yÎ^3}¼©ž8³®lúÌ¥û¤RVnÊ›2’=þÖb=U‘‚‚¿0Ýמ]_ßG¯ÃvìLœbqÁ’úøòýýy>Ó~êWŠÿŽÉ0œu×~©mEdRäNb–š5 lE{ë'ó¡î-ñº¸kÝ s£"0ìs`åYÑk|ûr¢õ鉥¥<'O ¼-•TÉ2÷ÒÓ÷¬Ý¹paã€pÝЗ SÐQж·À}T”7ž˜WPÌ?çJØŒÏ,þŒe!Îì»^i; 11Íty‰ç¤4XC=¬xÁ×êC+è}TànØ%Sñ‡ekÒaP3dš!¢+†I’÷ëè“`~˜I±Fo¿›y -“ý ×!“¢pñöTjôžD¢.è~Ó5™r,‰ÝIس»UÞÚ€>©’3réè‰gI5Rl^ç‘ýÕèSj¢«˜C3e û@ö¥ªÂ®¢Ñ•o¹Qý]x¸÷(®\¯ûÈ©ÈX?K(Ÿ0P:é©0¡/æÀc_™_S½– -9¨G¦`ÙØ£ÇN8é¤*äûŠ×¦gMr¶Æ¥¤MNƒÒî¦-çM'% -#Ù^*T0·á¾PøѶEz5^ú’¥´bÍ¿Ü *¨¶¬éÌÍDpê ÕzDO·c€Gˆ -¬„±&v€hiša& ¶Ó½íÎÕQíËN•*Õ+—ë%53óÚ2ýq윓ëÔ`ÑÙõJ`~Q¨èšI„~¿Þöp}ª»ËËÚmˆ…y5£ÆŸìW£ä£’b¶p”'¬Üçs¬ïAë–i™ÃfŸ8–ÆÐûéAû¤ f¢¯_ÿï5k¶vÞkÞF4*<÷^¸íívFµ°2µ‚Íwû¼Üz8KŸm4çBÎÃg”z -•‹¬rY3ìô¾^>pMÞÇB'^®[Éd|HáVʱ—õâ¬jF›‚\_o?xçNä{Ý´^äÇ´/´ˆs/²ó�7¬N*L„ -Tº8´yB§:¬ùGÎ4ž(`½i¾>Ùc0¸ç~O¨ø§Ë[âûàX69ÁŸn‰Œ•|[¾g_‹åÈ«ØiÞä·É-vú]¬Í³@KØêñú~ÑñÖËÓ6ûLBSG˜âÀN)ß7*á¦áŽvMò»¬m•õeÓdž†9=¡:ØÀ‚ƒY-)õ¿âv‰“Ù~$šuÖ5JÏEŒñIŽcÏ„yk›Ò™|ùôüÀÒ¥vÂî¿LjßÇë]+rlo"¤$äâyÐî0öFÞµÿ½kǪUùû>èîö -–Ï£\deísðýÉ»ü3å<d• H|›Â1ÏkÄú£e1,EÝí;ª‹Šº5]ÿ!:m9ÒŸošìÃGø¬r?)bã²½ðHÿÖ–ÝÇÂá$µ!Ód–eÀzÇô@æüÈu¹_ôìv[žÙÖçÝØÑ_EWë|ù© †Æ2ojY_aÂÿ.+¨+ Ò£´Ë£‹4ú’£Q(7µ¦þ„y£¡qMñ^2YHwTˆP˜R9zÔôy_e´E¦Px#NS Œo�·i]ƶgd‰“‚Ý9;¤ä"ǪŽ½Ö帷@kw94ø4'”šZÖ¶¹ùhlš,r<Cveup\%îméî2ol;¦¾ ÜÅK!¼«@o<uXap-œ˜ï‚hˆHÒ@ss͆ÖGCl-ô{V7ø„ÍG‹ç.�ë—šåü§fQ ï^çÇcóêS¼G}±½U{õ”ªÀ®yõ•4>ßaÎ|±Œá¸e"WÅŒ)ª*“xñvÉ㙂ˆ¡Ü¢5þÄ3+5Ñý©éº7 ÷âYþÂQ -çý]^;ú¶}ƒ›î4NܨYÆÛmŠþò–=[6®úÝÝù4= ´CÚKó(ÆÅ!™Ì½O€ú›®šíÓ¨ð4EðA§MŸ “ÖED´QËš»‹¾yEÉØÓÀçà‘9&URWð¾‘ÈÜç2înäN\™èïÏõû7ÜPà#ü÷0©ƒÌŒ³›½%žÙkF 5œË84Y.GW7m– :_žoC¾O¾gú¾3¹dçëÏoSéïŸa)HL7æPG¼§×æ¦Mãý¬;‚åغ¬›V0ßö;$×Úæ°ÌZ›XRF³8‡!ÿ†ðÑ’í—£ýQ‰Î¿:B4ï—‹-H§µ„jÊ3+Ž™[Š‚0|ñæm'%”G}³EcöKF©‹¾qùHŒ³p:c¼�V‚‹Xƹ�Þù›4ÓþŸc®äùBC**o³§¾†z0‚l§È¶l -hÔ#÷,ú¼Ãá2á›Br¬”eŸiÜ7lPãwõ$r&‡}tJÃM¦a}O`û0‹á]uý0 -wCP-c9ûFA°ïƶ�¦]⎜ÑXŽß‘Àþbìâ3êÓXæž9í˜ÂÔˆ#ëkù -O¦é`mÞKS=oq/o¾]ÿú¦¼×QsOÑÅ«§K¬3Ù”¯‰¯ø…Þ¿¾%ÂKEùª"³—‹e5õU’çlzžw��@�IDAT^ag™¦}·(X¬ZÆÈ„î°%â]‡=»Øh1¸§Â&¹Ç´Ü2…á‚F¤ý)ìuÝvúÒ¸9ÿ‚± $ÆN¨)—õ˲O·]g]©¿Lˆ¸?ø†å<]†,½·v-ÄܞꑈEèÌáú³iñÖ[o<Öží»1l7£‘ªŠL÷)Þ:ªÙ¯C¢@„΄ƒÓëºì.C´^l(ÇfP‡÷ôX¤}5QP›|NøП¨¦8ì®» ijIuüÞE¢®wlãÑD¢C9~Ö6¾Ê3üŒ´¡ý–¥€¯$‘ŽfS&‘JaR”×Þ³u«¸³të…`•¢ÑJƒM»–/ïu8w5LQ\AÝÞpTŸ[ÔIu^©ÉmiÂ$ §Úèœ,ÆËw“¦öhÈ빫 èý ¬‡¿»=m-ð˜+†l‰ÎO=~畦ŋs[Yئ�ZÅÚúÿƒº}4b¶é, + / -a=\Qr‚o - VHL0£ÝhC®´O¾HGò¢ 6/Iî%Œƒ4§Èó‹Ï7`ªú… óy(-t.ëî.¤»ºë¡}t<vž×º«æu6Çö0hv¯Jöv®õÛ_õin=%Κ^駸'ÅŽcy.ŠRà -léî6܉$Žç3xx@! åë…âÉȘúGÛâ–ýÒw»vwGc¥odø‚'DùG׌=ÖˆÀÎKöå¡£‚·¾¨º¡²Xj@&ãÝ5m°çQg6-î°9†LøªÙl7:ö/ DÿsJ×Þg½ÁÂÁ°´ØžRÚÓkª÷öR¢ßd9ç¼x"u'_2Ê六ÜäMéQø£„î³Ûz;Ëır¢|È?f†Áñ¿ìŒeÏ{¡%R†û„þ&”¤ÎuÞ^ϵ$ñì•®a&ã:ÿ2g9÷ª>Ñû²ãbñÌÙB€½qê óFr<)‡&g”cÀ®ÑT÷¸û]Së„Áê!N`¾{tušÂ²BWšMŠŒø>¸F3^Iz$N2É -¿?‹*nó‹Ò.%°gÑ:Ié´ ª»×Z|ˆº’júøXº¿\ÅÁ@=ÉÇØÇ -U#§A»ø& 3ï#x`5òÙ•ƒ§vªË°¾ ËÍšwà a•k+Õ"±8ù!hߧ–b”1‹Œ/»2?ÙÉ™ŽÍ�€g™Ïâ—Èwú³•BmýÄÊY'Mܘ H!“3ß0[[Ã'^yãØ-0%�×#Ãæl ™zËJ[ÂÁ F¯„m/„År¬ÅØý…¼]ýñ=*'hÄÈ. -x¹÷GM6 Ç{j&/²•lå©´f†š©'x%HRÌ@-¾—Ä|| “™^YÙ?Hø¹zÛ“0Ü„¿í·�F !ØÙ@õr"÷#UK´Ñ¶Q$Œi†ÿ²ãÅÀ®¼×9~d{woÆÞè6ùß…Sö>”s”Gƒ$åø{Ko¦<·@š<ø{U$òw1ñáÙŸÛcÆ0o¯™Òã|À±É)]ÛLÝý•Ï*áü?K‹ä?§zŠ£Nat‚#™&ØwÊ®û?ØÝKttxü#F¦Mçòî„9¯vÎ -j–rahY<v)äÍÏ¿ß¹°c]sÀ€ÎÖ0/³!(ûÉ%Ñaΰ\{–Èñ˜ƒµKS)÷ -$k˜9N“iÎÛ‹ÊÉúBjÊÆÉ.kÏP\¡’8lVØQ ±(ƒ<Ú¯\zí¿ò²ÊØ›ÖôªºÎ&4õDÝâ„Åõ#*иÁ‚1ç`©P”ö UáMõ>ßÖÃJzª°ÍÜ ÈÙÖ�mNÆv;AlaM´”ètâR-QÉϺ;°˜[ëüʹ==äCf„|y[;:@øÃŒÇäí‚m™Áv|L¡-œÎH¬Û“Š¢¦bŽ—9÷ØIì6]'iËÝÉ3V¯Ì2ë-F`ZìÌàEßmæŸÓ†ú•Ñž™YH¸vê‘–X÷£Ã}âó,^Üå„Š'3ÛÑ¥FwÀ6`v u©AµeÄþÏæÙŸØ6iŽ=¥.4á™_#éùŸ½ -w,ÌHÃà4g-r³¹gû.Õ4'B˼Ç~Š•øJCU©2ןTpB .µ‘së¡MÏ@gñäxÃdÖc²lǽJZÓræŸQåç{ýçŽÍ^ðJ7zòæÍÈøº=E -5%´ÐÏ‘{TÇü%œ¢“‘OÌdxƒè€Ká3k»;õ _8a‹3‘5¸6ãØß:æ«%eœ}-§(÷p²oòbh1Tð -ÃF6p¦xe¿™¾^9éœmHpH5몙'Žóár9 ]"¦iPë«/'�éšgTƒßH"4,ÌxÜwÌXò{¤Y®u4Ø\[ûV/¶Éû&]î6ºØî‹çÏ}öl -nYÐ'Lóû×20w-‚¶¼[‹ïÀI7�š•zbSí·-¯Íû5ŠK;rô€ûÒˆâ]ë¹'ÞÓ·Àc&þCùÉ0«]3a?ÛA¨u¾‡÷Cß; t]ë‡IÌmvTa°ößRfö ‘æ‡9 f¼a(97v§—[-'üÊ}ß¾¸~1TYt,Ç‘“’(Œg¬Mƒäi1NÔ.Ö…‹/<Hv -ú)¹ýíW›1ÙÐCíZÏó/&à|pyW|ëÊ£ŽÚO!hé¿è!¾Ô».ÊòhÎv¶Ê"ÿ¼uÞëoIÓíÃÀ¤ÔFúƒ•ˆì(¢¿Ó¸l(]“aÓƒ½—mfÒflØ M=nÞhoXú>´Òÿ€¢'á-ÂȬvcG*õWHÊ gäþSY™q‘Ïp¯¸YÆáQ9FgÅ4ô™¨-ºÍNÆF¿Í�1tä;ÚgÙVXîm ,Ú½~eÇš»ïNk%?3Þ7’[ -Þ›îAáöhxê!�¨GV»ÿŠ+L;Ò¹5ÕÏ()éÅwí¦ÂÂ$!%Íb0Yؕл¬ ¸6Ádþ˜íIþ<f:?×™à݉T÷ºº'{Z_ -ðæOx!p›·î¶ššZQîÞmñ|ä$lÐË - ô| ZúxE'b‘iˆù:4½@òÁ ÎæúضÌï#UW -ìÓ¿6tr/¶ôç¤5r´®§ÎYþl–gŸ@¨æ}(½Ú¶«ò¼®²Æ°û¸ðb7Ò³Üëp€nƒPkæíy¶KïiÍ«Ø’N…7D7uʈ1ã-‹û ¶üˆÜ5·#¿Ú÷36{Úßýn)š‡å0gÄ®Ç{aè†Ý½T€¸5wÐ%E‚cSÉOŠˆù˜«rð>ïµ®ÏÝâ‘ÝWÔTzùoªK·Nž¿wð9ã¸1~ºV¼Úª®.˜QâíEïÅÂ1PµbcÛѤYÞð0Äkü¬ÕÓuŦ¢Áe}Qÿ^ˆþù'q„a=jOO -\§;¦»˜÷Ø·‹vðQl·^±ê[…ä—´´ú²À9DŽÆÿ‡Ë…¢Ã¾$)Ì¥;ög-îò»W7û÷õ¥ÝÁ¿ß¿¯¤°êhptÀÄÆ°ë‹„J}” é¯2ÝDu»=eØfJÍ.ñ§¸-TÈÒÅ<§~컲^(þÒ¹dý¸hÑ"{FIÖ±~Åv´2bóï›®Õáu™ðý޻½PRäÝ,^)°ùeM/=8БT#Ú'h%œ¯ÂS>mäÈ©•ÞÒéÔæ“ï–ùÇÕ`HG†‚zfa™Ò°Êów?”£‹�/¡^î‡Á|Y*‘¹Ê•……žùîâvØ¡¨³ŒjÉùòpÖ4×^èàѼÎÕÂH¬"¥´ßŽ.aùƪºKá="J;ÉÞÿÖÚÍÿ¸ùýÝ¿¼&ÚÒòA9!ýt+Ûq¶æ§Æ½4w{à ‡Ð?kjjâ3€ÀÀŸ‚—~ïJ¡ L€»ŽÓ„+Ž»®¸g£Yã%ØÙ¨ÆïRâùéc*û)é4ÅžÒºSÇO–õÔúÖã{g—±$Û™¬,¿º‘ l�{¡„¦WÀ9iq<w ¡ÌÏVô ÔÂoœXÎ)¸và -6ô¶ô zd±©ýƪ6wŠe[=™xúéÍ=+!w£XØrG +ôº ¼+Ø+ðMCXn‡W<‰E/‘"E;(£à §Ã5Zžk³f:§Á>‚sÙ"ð©–‚õʶ0YLÓQ7jùI•Âü+2/ÂñèĘ¾üãrϤ¼Ä¶~ˆÂN‰´Î4›c&³½–¥ªÎ1rÀÿÍ‘Þéä -Áëÿoûn×É\e¨î¯±Òqè1} Ëò ¾.‰çHÆ,›·ûã†ã·x¯f–/ãÈù¯=°k×.Ž´$ÌlZS©tk{|œn!qøỚ›søm,¾Ø€±ÆHHxEöGJP¹›¬^¾›–B zƒãós“ -NÓæ8ºúe -¦ÃYÏz,ñYHÈývŒ´a‰§LeÄ`Ä‹úD2¯ÁS2kÒ×{dkKÝÓ;Ó%wmê-™úb¡?‰^—?غâ$L„.”ãm¹oðî*Í¿ò<d—&.짤Åkš5>Œð%ß•¤B+ßÙž‘#ëÊüåÇnLfO¨ -—\XVRðÙäîÁ6üôD,6àÊϸêHÄBæ$%æ•a~ uœX#úe9Ç[W«‚ó£1%þÕN 8ë5ØiÝlö»5aï¯2þ—U‚çŠ1sOŸÏ/¿2f²ñç=<ù‹…d¦¢"j¬£ÝRcï%,õöd&þ°iùÿß;n‹M†x3iOŸßØȵ¯XA½£Êø{ÞÏ~B7cw:²Ã4˜·,FÝŒïoá{ÿñ¬òP+~ÿÐõ´Î ÆWÍ™²`žJò'ÆJ=óŽ–G¯€¯ˆ–I¯)™<¹šH5#2@ø‰-Ðî7aðb;îNMÌX ó*Á¥#78‰PÌ”cè•ÓjjBºRTGy—‘<r7œg8ø‚Èä[:ºrx%à~5PÃ5Ͷˎ&T57S ÚœÑ`ŒœŒíÕ<R=Ð�üpµ…-ÄB�È¥‹†Û‰s'1Í¥øÞÇ�k -Ê+ÛÚ°ºÈ£Š´©ZòÂq UÝĘæåè¢=ˆfZòI(šeßooŽVÓn8àÖý>^qEƒ¹½¸x[å{?ä¢ÖèÂè+P~"1î%eÕÕ9{q²*ÐÂ[ÁçCe›-ö+ìȇ½‘—¶»HPøãJYqfY…÷«Ð@.#<³¦’‡…jß±ôµ?o‡òôK#®®9¯&§N<Ñ`ò#"ÆuŒÉÌ™9xßòÞ^XÚ¬£@⎀XRЮÑ[.w¢˜ýæÄô+o,ådä02O®KšöÿÀ„ôˆIÌ?ÿ˜K²÷yYç.iÍÄÀlÉ׃ž¹–Ñ!V‘°K kôþípø{È„/mŒ4·Ï™¹ãƒ†VþßJ8rŠ§5[AÑ<ŽâcyçjWwO›ácBA,ùšT"1Ò™´±eÈÙÜ[8 ós&âA7ˆ<w£ê:;xm*P--q¿Ì¤®†èùÀ®a8vË’“Pä5"ëÞéËžfŸTg˜ñ+kådàuÎ'¿½îÕW·–ÃP?µ¨¨‹“¢åô¸·.\˜{éž²iÉžÔɾQ“gœrŵ£WvªÕЦËÞìN¯@š90é©C‘Ú'é3hû?êh,/ï÷—xß«é—Þiiz¿asñÀ{(ÌÎ1B:TÙ¶(:ªÈ»^žñ†U@z=VpѰů#ÐHÉ$å¨/À¹G ö‹‘fà=L€¦i¶:cÂ~N ;#£3—b\‹L±Ï3)wådŸž6…d„w«}Vm€íë|¾ðŽlwñâŒDquU…¯d2¬íõÐ"ª¸ 7Šâ(5N>ö:Œ#nœ¿¡1DË*À/,^btFev ¿IZ ˆ1WQ6ÁÖšN$´ËÇ–Õ‹ -X ÉSêýþw8w"‹¯¦X:,ÄAÇ´^XTÁ>@8R@à=¸çÀ>̦šv,ûiRôY|ï» aåF#ȺÉæ?çï=rþp쉵?ƒ¹ö.ÏÊÂ}FýÛ$›ùßFø3òWSóëÕŸkš\Õ…ïr¤8œàLx÷Kø8¢¿›¯¿ ƒSM$ÌtÖzÒ67°»¡sžc"MM’GL"Y ˆÜñ¬*‚8RþvÇñyx¡Ô'ñãáŒ>1(q_F?)e»óõ ç¤ð£‚©š@¤vðo‡ÃßØ9ädÎÔåµ®^ärSjšC`øT۪溕,÷.¿Ï”¸àÄ„íÒûh‘ÎÁwQÚeß1ÝL2Ÿ«Æøóº})aÿÛb?ërweŒçBÈwA*hEWjWüc -¯íz(uXã°ï=ÌrîëŒe_›¿‚}4Ï’µìiþÓÉAÐNíËÓ§”œkØnVj/œ<©4ó~_æ—ð†€——á™4 `1€Ï¨"žÈd' 3ýTðN.ƒcI&ŽG ÑÞ(—öKIY^íþ˜ëÖÕ…´:˜<èu:²9—!]0ÄnÞÿqNCsÃf»ªo¢É>Ålo0JÃ[!,@2ÄÝò?Ž´MÃïõØŽîÝ;=åå®?íÔ²åÁ}Î[Å)×ÕKõRüÃí4—š|+Lf*Q7Ì®/ë{o-2¯T%$Ž™À:|L5õ¨O‘&'ÕìÆG§' k¬¥“ËE¯¯Ëc0™¢òÖà±J¯è-±4©›©Çƒ×¤9ò7a™�Є-Kàý3Œådx[Ü›¾X‘°ß#T´4¡?¹=Òöh>eÍ[=©³Àô|oó×f\§\ÜXNZ¾¡'rüˆ‚9.oß)2Ìi3œÁÈu©·]O‚dГ±¶÷÷Û]�ã’>Î÷?5…Ñ Á&üoGÎî´DÄðJ<@ú_º3§è¢ü•`sxü¡©¥L¼€8«“XÌë¢�aœŒB -¹-Dð3}a‹g¹•øn?´dkl¢!8/(¢óL¤• ]ª¦[XäåKÁXWÕ›ÔFH"ÌYL‘Gžü:¬˜{:÷fÖŒ¹@u1¼Í-šVî9¬<ªC*|_Ù;ȤÑ4ÝŽg#¦iéP£ø¸ÚóL®lss³/*9ƒuù³¬\„Xý&QŽ†Q°£?Y‰”bƒ”¨‰ÓÃí0Ê|ÐJ2ø~६ëKÏÀ½ -çH¬ÄΚ£ß(‹¡7ÇøIfgÚšišÆ#˜Q^s¿ñx¹oÛBhLûÞIîDµÕõ])4;ÓtÞd[zþ¸2_ßæþäQºE¾„ÌÉ—!JzØ\p§ƒgįæìÌw&•–öR2 DüíÍ<ƒí~„ß1è‰1dØMÀŽõF<Ýûª¤š·¦˜žI¤m—Í$4Ó@¶k³§Ùqš™*/w°ê;Ž½Îëp0ä…ãЄ92XžH¤‡£9-¶t‹RÎ<‚…”jÔ>ËS-b<GÄq„¡¶jÚhjw7êLVÙH\0°U¤×Ã"¦ZZ?˜¯¶V把Œž¾>1\TäDƤ‹}7B8F„ÖDœ‰d;-2nœ:&‘PH`–î¸ÈЉG#âxE‘Ò)ô®ÒŸ¤(¸¼É#Ù€ëpÁ"Õ6… X7gÍ]Ý›)…:ÿ4tÞr˜L4²°»ï2ºéEðÈ ¾1Ù¹±ï¢ù'ÏáßìI}iäëÁé&ºÐ0u4m’T¹©f;˼vªU7gN*µ^[»Ö Ãn9ËJëöFOâxÞàzgUûšðq¿1B?r|dÐw;0'éUÔwónS×\G̼-‚xewŒ“êKoDNó¯ jí²±ZAƒu‘j±‰žþ¦‡ZÈÅ æVìdñüSŸkíž4ÈÕ’Jîµ¹¾öŒÇÛ¯ÎH|ÚÈò¶ÄzR|3²Ÿ]J<Ž«ÏÅ -än§þdû¼f´BDÄ=9µÄûA¾ÜÃá<¤Â÷ÕŽèLØg¨Mg}¦%’®ªØÍQ½hRioÉ}·W%sæo(øxÒ‘ç±…vŠâ¼Òß?M*Í Ù\¿`&@n䶌û½`ºÍ/›tÔ½ØKž-"áØç38>\¹w÷™gGʲ·ß•–a.ðóüW ’¾„Ì[¤l”Nñ?N˜w6ÒËô;DôZçÝ—½½ïŒ…9¡Ñ9\úÇЬ/‡-Õ‹ -Å“nq´èæ]+Ë;¨mFÉ1‚÷ÇæNçxçt†aeØ`ã€CÑóÃ>6ð›IÝãÊüœ"lq}Ø7ÅYžéÖ±ÛåI¯¡qh Ë’Ð:ªžI[Q/5Mýa>Tã¯6\±;46¥ØÄ3öë›\Çý“þ[A# -±õ? ‘F%>üðÃFþ ª{»eß$¦ZhWOH3Bœ Íøf`˜GÂüDn ØŒa„T8דm¡¥{ŸäÍÎÔïžx"«'ã!B{,Æí¡ÊN+ÒuÃûØ/RÜ6+Ç2Ù„’-»=âJKìÅüy¼=Öõ‡¼öýOêÂÏ}±tŒ´ž_#`jµÏÚLΟ1ÜïÂtØ–Å·º³j8¡YGé&IŽ/S.>³º°#øcÇðŠØ™/þÖooK 7ûõå²ö¾91ͽ@õëht^þÇM½‰ëž‰Û68êË*õ?”ÿíp8©ð}¡35ÖÚsAõ*4Þ]÷¾»"Bqù†nLd�E£ÛÊvWbZ-ŽølŽßPTÔCmtùk?êœN§KZ-f´ÍɘŒ€På"0]Õ"¸û&ì\ê ú=Ì»vÇ»,âtíœqeáWÀ'´474®8ÐEC¸ÏlÛæøJGxîdW··y„wžúµ7í®‹$oG®€¶ëE™?ÊjñßÀvŒ ¸ƒYÕNäp0Umü3.´dÈ8Òߌ³Ø͸۟ٳ¾ã¬qG=[ô9¨õûg�yËb¸9%@%0¼P6ˆl Ÿpu8¿Å†}‹‹»Žõ¾!ˆ÷(ˆ¤Cƒ1#œ%t#²,a -l*™å3’ž0I‰bE�^¡ä´èŒØ‰~¿½ôïÜ‚´ì€?hÈò«}çDÁËw×ÛÉfj/?à’ÜGjÅ+Fœ¿f1¾/òIÄœžåĵÅ^~î?é(tè„o}çxVb‡É:÷~ÌŽšAàÐDÏ�l$ün‡n™Ït%º—AHC¬ìJ-B2Ìy†Cõ Ñ*€^9°°2Ð,ø„EÊ&šãP-+Ìw0\7�! -¯ xãBgûÚçeaÉ·õ‹|¦;µÅ;ºæès9œhÄ[È<ƒ¾µÐ+wõ§ôþºÚ+"kM\°fþ¸¿Ò>Ò{Ÿ}¯SÙ,·|õèÁú÷O@;جá¿`\Õ€É^·~8‚/°]³)VŒ-¾r°ûÿUß ©ð¥ÑL©ê1°Å²S@ª²Ýa„+•;ÕlJ -KW:Ñr¬jÈ£zÝËß>œ†¦~¢-_W<^u…eð׆¯üa›µnL´#>&äo�„êKg²Ã0e0ìEñ¬+ïøYó}×ÜxØCæÃ.ü¸Ë{ž[üëÛzéJºqãV±g¬Í;'Š¢g‹©E^¦º5½É‡AÂ=3`ˆÌ%ª[D¿?ðEmŠªÕàŠ}�€QøÃ"ö—{‹‚›çÁ¡ƒ‘ öuÇŸ“a6œM{ Í-†°ÝÊq`f°ø pg#˜D‚ZÛg„D50[` òC@�Êã¬`yûFCDw¾j‡bš™$¶æО9D¹1hˆ$:‹€J.jÚ|Â; -z™l*e{øÎewÜ;áB¡?J¤°ÅÃ}×ehû$vh°Ç¼^þ:@/á7ÌÍá—óšg¾?¨¯<˜8]°ábbý•eííˆ6›3³ÓAéXˆ»'¶¿¶tíÛjÆq8û/ðî ‡6ô6›î¹Ì¬Gv„”ÔŽKõåAmM4š½dSÞ¾ç²ot&W _"HÚ6ÜRÛ䪿Q ´bQÚ2+À†ÉÇ�ý’CЀãŒM@g Ñvlpµ“9/W†6|Ò1˜oó‘ó‡{`q ±£ÄpQHö«lq…–Žô;uU^«Ôç{°{WÒÈηÿ®v)r¶±ŠrFo<¯(©~ùdì‚é‚<é‚«Šd×ë¦e°{kø‡M»ÈSjµ%öš‘h Üis�`æœÚ¢;ÕˆÙØŸ:±Ÿ >³zT—w¦z+ᛃ. ªð!ýÉŽ-ˆY6†íÞ;�‡ñÙlùk›zò™HiDÒ¦þäwYa#<1Eå¾êº3Ö®m" 9áK;|Îœ9¬=²!¸²Ò;pB´Â;@+„V=¶¤E¢%xF2\\î¦ÚûÀÈÂe˜X öp©öÉÎþÎî·Þj›×ÜÐÓÄ ~†4µ_ùÖ·îĤ©Ž²°Âo IÁv“ýzs³É<ø »úêÿ‡ð[ly]Ö@ŠÆH{´9âª^àÄ'@tÒ|RŒÙÁ›÷þ‚N‚ñ@0¤uCû¥ÊšKf•–&¡!2òÈðl‡6»³ÿÁÔ—røÙ²Æ<„h_g—ÀZI¥ Q¤0v€Y±i8`7v�ŸeË O€ µ„ÖlÙàd[¾ îý¬šU¡e«ŒÊÝ5óª«íéJME Oc¹àéÖŠ½íž¢êDÃi¥SŽÕÞªiV«½ÉPÚ7ì‡'¦Mö Ю´A—i!ûAŽ%:Õa䟬ԃâ’þüe¢šl×<¾gY3‘ì²íÜùóÁT¸ÿS^9¨8ÇÂ$³ÚêSëÖióç×Ã.퇖‹4ŽqÀ~7a1G�SáÛ€Õ½Ê9©•Œ®9 -ïåz‰÷§9®-Ù'y|@=C ªHFeoÆ©,õs†AÆ!ÙâDŒ¡—¶zgû=èȇOܪxg•íÔŽÙÞ×\³<Ž–RWwвDÅ{nÒÐna eh™Øå·øàÅS,ôô§â?ÍØÙ d)ÀÆ"…a±;iôv¥"â9fx.Ž�^â¯9õ©ÁÿÁ;Õ£Fr@îX`:„ÊòÒà߇¿‡Tø®YÕÇM)ê“ ò¦U£,+’XWCÃ~BËaYS"ܲŒeö€§a®Éé…AÐÎÑÎ ÞÉ㮸›g«Ä+§´³ç~2¢´—þF…2ÕTg¤šb駡íL˜8O{§'½¶à~l»VGÉ>.ÓªºL£:6ÖÕQ¢wW,¶A'5€1̦x -&m‰Åö¬Åb”I©2ðþf˜=æ76Únc£ô^o2ŒëXº4౪¶G1ËÚZáiÅÈr@p†áƲĨìË»6ôØUþM˨”$eÙÑ€w£Ø#Lu‚à*faª°ø°z´@›ÒLZÔ*m[IŒ‡L> ¡ÆÝÁ°ìC¢Bv -„¾;ÁFþ4Ü>ªÕ±{¡áT~Ch=KÉõPþx‘eGÁ|QLÍipv@æ£Æ9ãùõ|DÒÊ¡ÃUÖ¶56äMKŽ²E_¤˜%뜑 ËÁá°¥±.”ØôÚîÚ¢ÞŠ=±Íª`N`Y™Iû׊¹€¾÷X -ûw1¯ ìÔ…Ú¹·¿²rÀ,@¯xnWSëicÆ[@^d‘æÕÙ;)÷ÝOO=±dÒïõõÁóù˜ªó+›ÆøuESÄ—V-çu|‰?èbŽ…ógo/[¦åÙ¬~G!¹IOǸŸ›7pYЃÃƤاsf1;ï]i("åø/n=òÇ¡÷ÀÁÞñG• -%ˆùË®H‰Õ\½</8ÅåþF¦³}§lz‚3FªÕ±WTÕrd¢<³ºfX1Øêld·güÏ—OM_Ø•>‰¹òe°û©†ËPèÛauä„ÞPÕhÁ<¤º1œ°ÐwyvmÉ—Oßý’†ßÖÖÄú ¡×1¬4:M×ê'Å1J©‘,þ̥_ÿ ÎòÉÞ»—-“hn¯³¯ý^ùâ½~¸``S%î _6 oIÎM%ãf¶ê¤ÎßÝrï"pÜ4©ÂOv¨£G%â70 j謇6; ᥿uþO -#|ggTZ|±ò6gÀÝ�'ä<Øþm¶×nàjFÍÙxÒÄ÷cj͆žtÉ -l“ÖÇIˆØFlϹEä6˜ÙB еlÔL=ácCwN*ø«ùe¼¯VZ#DþpÒ¶WQžº•§Å ³^Žñ¸;M > -òÄä‘÷¯¶—®£Î˜(ÒŽš5™~ˆ×»û&Ãu.Dß ‹]¦_Ävdô%hù°D¤`Ï€àvkÐ8™a!83ñdß•%æ!Ä,_û.‹¨öBßíFâ"ÀR´€NÁšqC'N•è; -÷iŠù@2œE§íÃÐÒ{éAcñ±Ø®e^ -dâ`î_ú;-YkÅÓÀÜ)^.ËÎE'Ö÷Ú–®Z@É -X¿tqñÿgïKÀ쪪t÷™Ï窺5¥R©¤’ÊLB @B…`�A1QDEA@EhÛÖñµÚmÛølÛnÀ¡EÄØ" aHÂ2O¤2Uª*5ߺCÝùžù¼ßÔ-*¤û}äI|çû’sëÞ3ì³Ï9k¯½Ö¿þ¿Ösœéö (2§Þ‰œ0ô&5ÔJx=ú÷‚Fßè«? w"ìók²‡rÙÞñóä]ßÿë/ÑÔ9Ò8ò[DÚ~°àO”<·Ä9FØçã¼vâ ïE>d{I7Án,§øDVTµ–pL+}~\¬Ö;¹ª®`¶-uÜ]¤TÎüû{ý÷iõ|éÅ€2æ,”˜6 vNpö¨ÉµìÂוi89$“Ê¢#ªi–³È˜¶„§H˜&h_´¨ðZöu”k1¹ Éôå¶ùËŸ`XÃ_T´Píá¯ÖïÛXt/\Xz½ÿÙàécT 8Á¾R+ãRuÓ÷V…5ÔË,ë¼Í³VÂx݃€·Ø ù;0yA$Á¾�ì.PCø.¼nèÛ„DËb|VQð"¯î†¡Fø…Á¶ýuN–u`^AŠ¯e5†Í®”iç‘8ƒÄÆHfXMØ‚Rê r…}é´Æ/;�³A ¸'ÌT{9qb«CÙø¯æz"Ïeu6ãcQˆkšw;ýbÎÝ_ìi¹ìD_î`?ÜZä20½C¼Ä�kO½7˜ú)_îÍ<dº¸ËÔ�ù™ô€¶FjJ>°hZ'<Ô)@¿n?ÀÙü³xàÈ[\„:µLÆžŽðÆWÀ–x¾«ë›¤ù¤åÊ%§ÈU+†«Š¼u5[«ÅM¿Å଱”¨ ½×#ÙŤFÜ@„ sG¼ -<†£Ð±twwK‡9rÚÀHGœmˆ×3Ÿó˜Å#xËV©–òmx3åPÛêÕ<Õ…k™ð\½Õ¢·>Q&77¶ëÌì…®.kbò—š^âw¸¥?É[+dÞ¼·v|ÿÓ_¼FãÇ÷͘6ík==7âÙ£è©àã%]AÛÖÖr–øŠ×iÊ„g½j^ ˆ ×`ÙægNœe‰®áÉV -JFèOºË2˜îÉ¿¿×Ÿvã+3›%KÛí¼±&‹‰cýÖÛÚŽ„Ñ^]ò í*Ië\šV2U6çh¤^ª.:Ù^TIý3lQ–ï2™ß@\ÎÒyý~•kç]qE4Y*Õ;I°e?ä¬ÒF“%×Âä]`¶=‘ÃAPÐ|Yuavûæ¨6ù*2âS{Íà¿_ãŒÇSCi»ª!¼†s,`{uë?p~xˆZš9?'HN`ˆQBÆÄ`ÜŽ@]N‚Çât)ØVBŠÆá¢�¼ˆý°±9ºÂÅÓÖÔµ|Ðé?½¿÷Àåsé¾lhÊÛ±–׶y.M²3¨¢Óôñ߯?AÖ“ƒ9°×a�ÀìᥪkÑ¢–·<4[CèJBxám{ÔÁ‹eÜîZjÄ}ÐÛúdZÙ"s†—†~ìÌðš Tu` ðCœÍêNönnk;äK‘9;èóhj%ÈMAOøyìGÿ™^éå>+ÖgÌž?Ÿ¢EÊýÛ¼ì2 [ÈÐ4¾àv1Ï.)'±Ç¤…&+÷ôg_ó çš³‰žö±mûÂa'‡×ˆáþ„> [‹%h6i>s!’Šš,ðoüq1¯¹ãÞZbiUéá¼wÓ¡¯â>Ï4&žá$_ØZlô%\ÍÕÙõÉõG’©ÊCÒ@‡H(“rZÜ5ÅLö`dõê7'îûþç¿|Œå…Òdν�O1»æSÔï¯ò7¼tM]ùݤš‰ltõj¦„jO.çá-¯ˆ²sã[|)Ü÷„Ø2öÖ˜b©ô2QØ¢Óew•zýwÚïp±qÍ*P -¥|¡~Jä-ƒ§S„]ñô2Ž•ë¥ÂSœSžÁg‹[ãm‘¬¯TR486¡òmÔ«åß(9åé°näKdï•Ä8”eg;qšª§K³«ü«ÛhnîÐDgµ•O)‰žžø”çÖ=—HhK.»ÌƲ¿™ò"YFóçÉó»7=Þhûá]óoV4¶U/ • ¢‚a `ØàV2䕸Á|3lš¥‚Vt¹]>düI�á‚ÎT£êíã Ã[j&ÒJ§‹ms‹¶¾´!ê ö²¡ Õn8~yqYBXµu'ˆ¨o ŽNgãÆwl³ri&ŒœENð•Ž}}bîáʉá”ÃÚÚLéÄغÊì¹d›_ßä¥å$YÚï—Áô�â�ÝAÀwfŒ�•@—E‚Dï͸S2·!äÇO2Ã~`SÙÔÞ„2Ý -¼, V»ÜˆÖå«Qq3¹T׿ðú2tÇ?·�Wg2ÓAÊTo³l8íØ‘›Ûºð®Dj´GrEÉçû*ò=*âú˜Ü•Hµzï»/`_~KæjõÎÀ†s¤ªjÎuR|™ž›*H5ÒÝ"qܪP‡bë%‹ã3@Ú§ €1ä-˜ý"ʃTé³&ÏÜŸîÀ]x§†ÿ¹zÿûÿ§=÷“©‚"„¸‘ÃÒš/íã{lµ9Ž’ii¡ÏŽºþh|+„ã~jšqÊÓEi-/äáܘȻ`û§þŸ6øÿòà§ÍøÒdÂ|Pöî- ¾1Í€JìØ£ë×Ûk'1È£k'[,õ¡‘㇇ó½ÊÍõó¥ü…0*7j"ÿ«*êPfYœ¸Q®q§¯„W<€Ø,pœ5 «SœxWxd˜ŠRK@«rĵw¾¨bßåœRA#apÿÎǼaZ,Zä™nRM$>_®#žýÁq3‡bÚc—Ìz«À†©c§Zk( {@Yç ä1l 2Î6¾¶µSœÙ\s6ÌãL›µt‘!£3˜;'U´‡%bŒ”1çÅŽ—¼©VË°pô;øÏ£"¡îI5 !±Ù$UE¨eü¡ÛM«ÆÀ�+Z¶Ú#œ‘ç Ë»Y;á;¥×<}0žpn ä‚Nxú•M¾ò¹5Ù{þí§T[à -ú-Óà‘†ˆV6_SïÞ¬Ÿ~“�ñÞ÷%«Ãe{|›É ˆ˜ÛËôC*ø<1TÀï4f߈J'ôë¾KÞòb*C$Éâoô KŠKæ6–±Ù;G=D-ÌeEn¤6Ð ã>A¸vl4;a Ö½¨¨ë7œÚƆÒr‰Ðn³í’™G°Ú*r…¢AG5bsÛ)ß䦿ÿ÷{Ð …jãXã6ˬúø}í?EÙýÑO"n?±)£ÙÞúÎyL„Š-ŒÿD¥è‡k*äªâ@iŒ-žßø=ú€ëô,-Ë–!>'“†”ag ¥íÍ[úåmó—Ò˜µå…¾(®˜wïüúÐ <2ýÊÖpŽN9¨ñ¶lýjžc_ù_3ªÖ¾¨æ±ƒ*¿¼?˜Ï[]:Q?|lßdêFêÝÙ^̲&IÀ\ˆ—m1nX¹#P¸ýn1ì¡S²ˆµ!F¼‰žÿñ%³ÂÔß9$Pðåè$¡Œ$ÊǦûÒi.-Ím‚'ëktÁß4x—)*WÑY{”nÓt�àn‡"4qœ=ÕHà;ÅoåEAµXöZ©³L~GuÙ¬üN×®jv®˜7?vqW³jõdûðª…E -m8+@Ÿhâþ49õ3‘и'EŠÐßL"yàÊS=3@[í¤QÔèƒ<~Ý_àa“©Áu#4Ž%Y$ýÆŸx|«núœüðŽûAðó³Ý¿ùÑI/ÅÄm'|¶y•ìBÐ#ç!ü|´m<FûuÂvå4I†Ë»ßÍ•þ£e,‰—ìëØ ñ¶œi,ÈÊ܇w;vJß`ð½Iš8£Ù_f4ëfp�¼€! x̳Š©oG4©tA1Èëá7ã9<åÜ“Ûòþßù€þZ=Îê3Xæã–ƒýqÄ/^ÿûƒÇ£ù"Ú"j;®^�ñä –—ÚÑÉ|ÌÐŒù3X(’—Ììж¿ü¼óO›ñÍ�¢ÔÔŒ[l‚„w {}‚˜©Æh¸ñ¥Íã8é¥.C¸,@¨-öjLÙW•ò!‰åwýÀã�åý‘Õw@½Úª2°téÒS¼Ezì]Ï<“¬v¸×AžçûØ«†LY1JýíÔþLXðB«ài8É�•¶™:œDH°¤6“”½oé±MØÔy<.4-ýhemLö±Ÿ½ç‘,¡lMýV.ûêîáŽñÝ_fI L˜Þ°Câ#©te@?<cøb�"@k”d6oÝwʵbª†Á€#i“„<A3ŽdNUKc£3ò¯#rÎ"ˆIÒïx–!9QîN°¦%3ÓÄ å\ ʺi|†P<ôœÅ‚ÅSß.Öןb˜¨W.܈‡P4òSoÀÝùv¥Ÿô% 01J~ÃW¾‡Z¼3Ï£Ê ÚZ 55U=获C1ÐöÒåÜj÷Èüêjš<-?/”$IA+K¤7mÃvd„P¿•M·§8Vf£ÐfCÒo?Y2Ïœµ SE0õoÇuó:Ç^¿ŸÇ^8œHÑ$kùØåßÿïÌé–ipplœ§×‘Žfu}.:þÝ©»/þé–þà:P ¼]cW®tA¯/d²Ö6ø‹CnˆñªZßnÛ÷ò»Óf|=ÃÝ1Ã+~I¼LÈLhm]€ˆ¤Ó‰zR¼\å)öŸ»PL9ù×ûú OÿB‘ ¼6ÿÍd¬9ªa ѱ¿âõä2ùݶ©þn èî±N2¤•cÓéyµ›Œˆfþ'€‰Ý|;ÜÙi°qu¢¢©²Ýä55´4QƒL{3¬!³$‘‹W´¾&-?àJÀ6 :Ì°ˆÝæ‘Û;ñ"çlž3ü\Þøˆ×iľã}âAÂ>å>X;.@:cò±U;ëƒw†Êkfêì…Ñèºu'=h¸$B4 V`7ÆJç€2¯£ìéò\ŒcòÜ*çñ’։ѵ -ILTxc'ˬ®‘Ä"œóÕœçzÓÅ|-¿ÍDBŽUa÷_× -Ò³‹'y…´Ÿ¢S\+€˜øôÕö°Fá·S'¡èùiX¢öîˆãFAt¯†ÜHæ—)—kj¿ÁH‘’ÆN .^P?»¾âW®¡²¦M¥O+ߥe¯fjË Œ‡ÑëaÜË ÏÊïEËt¡DPŽÄyzÏ28‹1@¹-—àëxø@âÕÿø^fD/ çL¯öMaæýõ{Øôy(šÖ]'ëŒlêó@âÜ,±Ükðd—à9zÀW-~sÉÔséìŽ>“›ê€#ÆD–¸”ý{Àëq¥‡° ·9>„wÛúR¥Iþ9¦Pü>gqÿê…}€’qqž]Ó¬¥ê¥î\óâçý;¼_q²rÒ»‰ƒ›ö'ñWº±_Ù3A¨b¨Éã9JõºþÜËZ9GÔ!fœL˸ˆ„dÆ™Ð@éXù}òšB¦"g-Y(šòg c4w£*—ª©%♸õꉛ†ïÜ ÇævºÈëã^¢dæ@€ŠR?ZˆäÐÞòî´Ý‡ÔÀ®'‹Î°É^+ÝÐd4™Ê<CY0äK®Àcly®£Ã*Íô´`ûÝ+ò O²iÜ€ÏûègyηntͽÉj 6Ð@تŠýΗʄD†½|ß�Ôm-Âkg€zIaà{NW˜¿=oªÜ?Ùðí;žö%ñœL„ûQ¡¶ö$hÙV”"_öÙÛ[˜ÚæsÁ™ð7¨¿[˳âwRŠ2å>œ›^Bêàî>7‡áyδXQÒÔЮ<èË´µ{¤ú•ã‰(bÞ€‚Ÿ¼Äás†¶è– €+0!š-ÆŸ/™py°Í]Å8O#n,± vÇLÁDéßMzqëÙ7±d{K4Û–ôŒŠÑŸ},N>ñûýE{€SÈïÅÞ§VÏŸ1 êÉ™BövLÉþïÏ¡’AÖäuí/¸å‰Cq÷Ć¥Ty3ÿC^4þw¶hý�5ßÈ2¯MÜæLø<þÀžŽÆP]µIœ‡sz ²Bp¨… 7ô¼×I_^4? Û€Dã…&Ë+dPT»åÀêÕoq ô5áÁJ׃á~2Xÿm®AÕ{Žî:·¾&‡å-“(OÚ1Úó Õx�í¾‡¶±Qdü Gi“›|®ð§¾}ÞQ˜Ïr‚èäìe+g"ã¾i9Wù[–Ñü’_ª´Éò½–.U«¹?þ—‹Ä+ÆåÂÛns"VŠºK˜N´@g™”oLEwbƒÀ”q@™'È<ûÔ¶––ñ¬~°fª‹1íI0ÇXP·²Ò]]e¶³ò!BnOI½šŒfŸ˜}dÓ¦r²ABÒ‰†Hh º—aã~˜õÓ(VÙÃü�Ã?¹õ—Kšþ¥Þ»7èôÀtà@yâGÀá¾ìg%»¡}’g,: ×$IÚÿÄŽ?Ž0Gç‰êÉ®Di|—3콦%T¡*Z1sôø4Bj^ìKÔ -^×w²ô“©!échÈxl˜nW[…l¨ä8Ay�ìÍWd·œnôw¤2wcdƒŠ{©>zpíǽê-èö5%ÆøO§E–YûZ$…ûz èÆÚE÷}93z€*XxܵÕÑ©!ÊoÝI‚+Ê&§Ù¯…þ!<ǻӞçák¢Hj'¶š>_ÐFŒ-ðûÓ´R2R±•Pcž¸Í™ðùmc&ï¶aHuë¢þ¢¢ z@ÔBŠU¥-·´Ù˜"ùLkkùE«œC÷ûAýI1MØÅZjÄ`;dQý¼]Ò^˜Æ›•í*kj�®¼õËgY¬î ´µmÅßFÅ°U¶¡kšh-• 1½$¡1Ãw\P7ƒÞÓó0KU˜Îû!»îƒ4I4³`q§œÌ.š˜³r¥Ü¨\ƒJ›ÀE‘4GÞÌ´ç‰÷Q³¶y+€â[›šˆÒÔ@¬7PFÐ*»ƒÞ&1¢Ó,ÑŽÀùäuFÕ´T'OZ(pMÕŸÓíLï^(¹öæÜt3(u„-¡£=`¾lŠÊ„[Êž#õÿÔ“\&s<”>T¥•â4S><Ï€lCµ6Œý§”–}ÌïpÚ0*íà_ò.¯©ªýª~‹ÍGÁC\Þòß7ß :þsHØ•`¨ÿ£¿¾ž¢LNZŸdTëxµQðk~�ƒó‚´á“NÂ:çyïAÁÎO±-Oo;Œ< ù4Ì^ü!¶Š™bèö”K_‚‹jȽ@o–½Ê‰Î‡ó{âñÝ¢èHôoU¶®ZåSæMðœtùo¨ûYH;çóÊ•ÎÂÆ~õ‘û®}¦~AxO&b7ë[u'=•s¼¿~o{@0ôš]ÿ[7á§q.î°WG‰¾Gž‘SÜ>š®‚…†ÒáÊÄË÷°\T#ŠL_&Sæn®_z‰OŒÍ•C]gà�{ÒC}:º›†è|’äçU$×êu…|Ôiï`eòÑ&W˜f¦ËSOz.†TݸQ^*ÉZÖ—1…¬en1˜¹B�NÚÜÔtJ¢é:$P0qnÁpÌõ'Ý—ßzWõ˜&¶ )ºyg5ÌXòÆ°ÒôâÀ@èg¯%<?‡¤Æ{ⶕÏ#¹‘=®ãÛ£É{¡|-í$»V–ö)ØÁ»G㣇°mÙˆÁãÌiÐîNLYwÀvÀ'‡]b>>ã{¸¾r‚«rlÚž¸£êUzöN`{ÿÒ?õˆT èk³N^\�Ñœº~ÿV¶/O•áú -ßõ\ Ð5>%§Û”Šf=Ú€6<gÃÎàH0òÔ¤â?,¦ÉË^Í‚fG£D¸Xh€t®GØ¥³lp˜D£iê,™éIRuŽ~’ÞðÓŸªÖ´…S.˜>§yb¨…>ج Ü韙ð·ÍsúšX¤É2š°£‡§çé<Þ¬ö¬+æßغ5/ph6^<¼s¼Ê‘nwA„ÉsFîéåµ'Ê®µà, ÕuGAÅgq+p8$3‰}Xë8qmt¿±Å^‰t¿¶jU9az’‡¾ù‡ßôVùÿT4“Æ_œEb8û:’¯OÃ#Þˆö[#¥Ä#Iå+ýÞ÷�8!ÆÀ"õ9Þ6¿å¨;Kšu5¼†s ºÀý80àÁ?ÖHùZÌøÝuËrÕŠÙP˜©¹ðâÙ,oM«›¾ðU$“ßû+:µïÚó¥/¤šY*ÿÒ€[øÌùá<ÏC˜á -`´I@<·™cŒM|ÞŠí¹¨>T6t¿ç¥êia„“Ÿr㦬¼8Ç4µG\²Þ·öD˜á¤V'3šäqH-!·Õ¦”„KΚÒÏ:ÙÔ6G¢èõáÌG`Я‚ÐŨ‹óš9@�þДÏ~¥ÿµ¾DÜëJ‡ôˆÓil>AðMƒÚ2ƒ†Çf!]Uð¼ÚÃÞñ@U~Íx¥ß» EÌI‚VˆµòÔS®1@ðP͆†ñƒìر†Ò¸w%¸< )b ÃðºmÞfëÌòx,÷¯¥ä†vP&"ID6õÆmK]v(ØFx†jU‹×�!lW&oÇA`Ãušm¢ÒBT2›ž4à 커3¦©»W×ÕÑ“¼ -VL½kÀuKµóLNe2˜”ïý.ôž¼Ú¯±mzÎæ>1ož.{ÓÂ-3A7ü)LçJºþóÍü{±5º3BMÓ$FœùÆG_¶Ù1*I-fZnÄ´]t¨Åàml<É@Ò{„í)η¼d¿¬£í÷‰ÄYsÌuH濨V«��@�IDAT]‡nP֜ȡØ÷vòbæwï&›x^^ûgOð|ÇfBjú±²P ôJä×£CGž(yœRP«[Z(Òáýåì`8,ÇXLõ5ÙØðLÇ4 0SÂÔíwÈK< ¨ZËÇ–Ú«›îs†êîÍ©æ4¸%xn†,‘^€Bï…99³–wm|RuÓSsÄiÍœ5•óÃCzjj¸!‰’ש!·£^Á+—÷ò—û|éóI¹ ¢ÜO :À0ÿ YŠ i?©›ö?›ºU857¥¾j?p!ã/f¥Ëh<°NÊ'ÓD‚l)Úì¿`Ân{$iv.•zƒƒeI!(˧-´<Æéá’V˜™+Áj|¿™`8±7§’®æPý!Í`º.üÌç{¶årC{~ýëìÍ�¬œk»},¦I ýžóMÆGü+ç‚è÷ÔH³õ7Bnž¹dþ|Ô¼e×(©ùåwÞý€Pbž°Ãˬסàa±ï[Y÷€®ªrSSEMA"s*û -Ø(q${ËܹÕ"Ý‹DÛ4xÏ‹°™‹ÂÌÀûÓhþò¯?øàæM==ÔxS¯ò¨mo¾!Ú}÷cVÝt“,ñÞYÀ,ž/Öt‡Z\M\ã¤×·c0‚nÚ2ðJ%EòÔ¶ß|¯p%lfĺ}X ÁӵВ‡–yÝ—f™ÍfUx7ÚDû¥|LÅbüà8ÿ†ÂÄ꩘Ü;,c¡‘}(ä®Êž‡®Žé;æÄÝi{7f§»Ñ¶ù¦Ûöcð颃åžfÙíÇPGM½ÛW0çB£mLž©H¢3ŠÛã…Í8gÓ¦ž‡ÛÛ›è¬áýåëI`«!¸ú‰?69»£˜Ï›Ðü8ž¦i&saÁd÷ùLŸx°C%ÕÕdöìÙú+©ŸÃ¡ù2 ôO"©qá®ïaû‡ñ.R'âŒ[Þµñ-Š¢—”¬ë$3EàeEÜ)ÅR;• `ÙJ¤][wïÌ]ÞÞ>þ¢Ó^™þ@‰(I°¸€ËüHf†D×YCþMW'ÑZßrZÊFúõîë‡k?øu ç=.Î^O,©j¥¡T>¬¢D¶¼¬j9« sú•03;�òÝΟ‚RiȼÔ"Ë_{Ð�ï2L[D;‘X“bFžl™qéêG�[Úùv¤é»»»}IgDúCOÚg¥©ç^µ6¯[§-¹~°íÑuT¦¨l`p¾“®•zbˆiŠWU¡„0[öíãÏo˜Çîë«ü|)lWÀÊœMË%¢ÐV®,Ô $�á 4ˆåcšŠí‚,®‹¥’ñYÈgë(L`‚}i×¼á[Ðqz¦À�€ºæØ݉ÑWA-c eè!Ü–`r"´î;;¶ÙÎeî�‹ªdLmNªèv~÷ÅSëAqâ:bRd9oÛWÃpï)æŠëWTùé€P¦�itˆ<’Yö~þ•¯=pME¾—%ªfŽDêëO -`»·[¨¶œ¹3ÆÖ dc¦]‚ëǬæ¤þ¤;ÒÁýð-ÄÙÏ/±dëm?øéMø:áóð7Ä52ïcÜ!»ä‚<(ÎYÙ¸(óâH!ãI¥¼×u5 çä¢ê¯°ÏûÆ—vè¶ð–ý¬$»^Öã¹C™ÃÕÊÑÕaÛ·§§§%àÚ¾{á¹sÙü¹ÅÆ9÷¡éG賊w÷ï×À>ùI�ÍžÇuœ±¼¿+¡ä2ïÙp¹ïÚø¦‰Ñ »v�užƒ*c0ÍÑHiÿÞMªçÍ;TU㟢Qõ±hûI^ÏÏàÁÖK:Bñþ<è§CRànLÛ5TÚvA„qÜûìL¥¼Œ(Ê]‹M¡I(_[¡ÍBþ~ħò‚y,5bw\Öv‚í‹Æ)ïºà’ÁøN‡éøR©¨<㑘¾ØT…5�Û¹¤ Ÿ!èæ€ âó,i`YÒˆÙb¨ÍßïýÕ‰JôF¥Á™¼f,÷ ¾t˜X{õjvZéØÈñ%×ß5ì«—Þx×П®¿%¯xÜT�Tá¾%D0œ¤Rê,p)¬XÑNaŠænnö:Õ3µ6äR,Þmô¸r'r‚5Ë.Bóf%Ê'Z(è#Õ'“¡ÌHO‹§êV^ÓPAÆ»Có9®*oص蜱Ñ\–)Û[KhOGÊÈ -Ndp¥m˜Ñ[è:æoK^yÙùœ÷Çëדík:R^ùìWvZ\YWGÉéËÇÙ“N#öFî€öè>õYÓÞ‚–[ßRŸ5QÍÈX‡]\„ÆÄß2°ˆKsÒ–8 -Êlwæ×Ëúß-e|$KqШÆ0+j£÷1n{í‰æŒï^e¹Û˜…®CÍl?—w1ēܨ[ËqoWa55‰PÂn³À‰Lø¼dÑã²uff/±ð¦Æøþ‡3¦Êj—1^éyía :’¯Ç!Öy}*a - -kì$~©ž%ÄïŸAÃÐÆS§ ³¿§g 9ůâíKSX*!“<¹3äJßµñ¥FêõX~'4²‚Ô¸.™Êï¹±½fÁËÅcos¡ h8Û! -÷˜–²Ã[yBÏ꽜“ôÈ~¹fj¸»Œ¿Ä%Kú¥’‹jƒß³bÙï•dùvÑ%|0‡-ó$é÷Û'(ñ~í쳩ôÁe0äêÒºÄiÕ©¥'B£€,õ*‰ÄNSS‚m}ld�™»q¥…xKWûEv6$¾ì÷TQC2xRÓ,d%H®ÙÖØ–‚nÎCÄuŸ)šWX¬; ¥æ¥F8æ.)ä×$;Ìdí:Kí:ÆŠî§vì|ä'B)7Û©nøîtH)¥³XGY_êHQUx§(øEyÎ<ª®.‡RƃÁuH”µsvO°=Ù$Er¦c^ B A&³ëÙ<™å£ KÞÖ3&óPåR\¯ß0€?V^ã|Æ0j"«iXvùÐCuϸ²ucýOg—ñ+WB ò`ŸÍg‹Ï¯¬ Œc™utˆGSœ[vŸ8áµ7.\éªÆ´¿d±çH6dB©ñµ¹X¤müžRdƒã`]Š1y–Ñ zÉ4CWÀ3Ì°‰¤ª–*Á„Ë"Üð‘¬P=ýßaD/ÀumU¢Ñ‘ÇÀxe,]ü£É-÷"Z*Y–ËÏq^„]ZðB‚qžu™‚}8 -Õç‰Ç|ÿó™ÑҼΔe}»0ö.ko|íiBQ]XdíŒÈ6¡,Ü’kÕAµÒjŠ ÷º½¾BÎ<$ú½¨è< ÿ[ÙîLYs˜*¿ë¶ì=œÌ{Üâ¹Y‰[ÜÞldv$ò¶^Õü-3X�ØnÁÈuxϘÈUà˜?ží–“4\Ò?Òñ6ðzÖׯ/%;%ƒ¸ø×ÿpÿý‰Õ«Oæ8bI€êoGWÆñÅì‘ÇfÔÖ–=éZ•F§S‹ò˜Çò«qþaCä.ˆúË–ÅþB„@š‘˜ûãV¯4<{Ât÷Ítq1Œô,¤ªRè÷ÃÚ5ʶ½)ès�è…r“‡]á<¼ˆÚ²ÍèŽs³˜¼™Ê'ÅžÙ ÞòËiQe¢ºžG�`LË#«’ùÌn¿³à飜u9ñfçþäâÿ˜Ì«Ÿ^Sâq§t®cOÁªIÚ™r5ÞÁWø·Ôu—¶+ˆ{dx)BA´R)Ë`fñF:–Üê®’7Åcé.÷1†ú¬e•>ßקéí•nþЇԦ ŸÔ:·U Ü?!¹aCâç«+¦†;qoÆÏqäÕM·«æ衾éyõõŠR;1Ø/¨6w-Ìî5€S¼4@V‡£ÈäÄ’æ´€«x›#Ðîtˆ×?·§ Æ]rz…Âkg5@§ø6Òˆi¿écƒD\oÉÜWžÚÚZ«GÍv8Má9MT·-s¹Š.—KJù0°ˆDÿVÉ2þ�4ÈAäô‚È1Û‘-?�z+J¶ÓˆxôOr^ùàÄëûýõ{Û]鬪_…m¸ ø+dž\&¼¹ˆçúP_:ªÜð(þÚ/©š½ÞÒúÎáHµGºº‹Ÿ4Yv% ¢…ª:ÿ‘IÄþïí•|öÓb|cºÁx@�Õ1pNa)mm®©ÙÛ,ÙššZÖÜÑß°xî»ddÀ»®ƒå)¥”8<=<É.hí ÊÝ‹¤ÔÙˆ/ÝòËLF~NÈuÜ›ªœj0YÂlœ¹•Z ”×éôfl}G×�XKìÁ¬—åa.ë¸f§/« ¤‡´Ò°®[Wá¥o0óñê#ÇéNKWã´Ð¨ˆIBáOjÉ9àä™X¦¬uqøòƒX£Å$8‘w1ª¾°|#µl¾ðÔ‡4Pœkyá"؇Ô>Ûçëi’¤¡ƒ»vIæóR)¶Þ0äD£YËçêûù/^L}ýêåZª¾]l`ôëA(ÝÔ¨fŽLs»õã—ÉÛ€\è>8 ï—Þ�ÁtÇHA˜p”½å¸!PQqëd¦ÊþXwVõgm¤<¤„¥Òh@lÙòCÿ˜ƒS•Fã³½' -ßs³Òà€ø�Ô~Rˆ+¿ŸVÛ6¶Ðß›çÎ%sŽÂüúzí>|?dYÐx¹ i#p)¨ -“YÐ*X©»ä-‡÷fü‘ïàþ|‰¸vÑ&C£½Ò®™-jqFêf"$p5üðK0 ‰¨’8˜0‹wôåÌÙ×ø¹+mhr¹´:¯T¢kúÝÒi>Ä˨17üxÛ¦®¯.]¸:ƒ]Ó¥º}}ji«*È‚À^ÄXüÃ#žè<`UŽýþú½ë<§iüUæy <(:ò(Ý·ö{ñ9àGÁ+òpr(öD JÚqim-Mò’¤f¯Fèð\@#ž{l’³Ù³¥mS¼Ž‘÷îJÞùÌeè;oòßÿ -/²FyÄmÍà]ZÈC1«°c'/ˆÇÈzA¼ù Ûý¼Ö H‘s¿rq懶Ñ:ü“÷ÀA`ΉÉ7â`HæX²ªªÉziµ×)†—îÛ_ïÍèŒý$J[㸠+Á\ó&g|©¶Ú÷Ѽ-_Åz¼7@-áó`cp€jî¨,¡ôËàHI€Ò©�wS5uR�0û¤ã6¿Wpp{ìߟ¸¬ÅpÖ{w_‰‚œ¯{NcÝ«›ÞÙI½›Ó}ã�¿!. [ˆÆl̳n g–ûä¯[—Áˆ!¥Âã3“Õ3#Þ,©k%þšbV]òÐwBUˆ¨?¼óƒ5ü?œŽ¡ÉÔsŠ®ž¥äÙèã1zcÛHašÓ00¸w9ãU®³<Z©ÝÉš—=ן…ø'pl‰³hØ3M,JéÓC^÷Gƒ$p¡n -—å’"Ë] •Ý祇°¤…÷OÓê^ˆrAI'w¥’H´ŸèB½ÔzÞ՞ήÜÔ\á²7ì.e{xøWTõ»K½¬TCøψ¥ÿƒÀaèiQ±RJDi+!qÉæ@û¨�LBGŸAˆöŸPXñ¨ #h* -ÆšåâØšÂîUÏ@Nª|òwø¯•ç!ýdk!Ÿ»Õ -zÿ¬v„œhBqQ”;ýaÞå±#ò8‚=ƒ•Â”w8äû?½=Àð\5èY‹"g?cZì@œz`Jw*öèwLöÅ7^8xÓÒ¶¨Eó賂é•u*TŸÌì㥾ä/ð†ÎìÌ÷ ùÿãS"!2>‹üï4yC*Ÿ>÷*ü"–c/mfc_ºoûä¤Õk‰ÌbŒcWªº‘^oWÂxCvi&@ïZ®‹g¬Õ½{m9^Lϳm8¿ZŒy+¬7t¾š-)I}ŠoÿHg'Ûä÷Õp¸´àŸïø…l(Õk¬öw¼Í\ -D�Êp‘§G=BÈÏÔÀ€`‡±º‹¼mçà‘ˆÿLúB™e~‰}âÎúðÂÿqì3~LZøP•¡jo%'÷CåoÊcqÅ™© ŘêÊv.ƒ’Å£ÝCLÁ«™ãŠ¯0p¬ë—ìbï„>ÕNT -æ œÆ£Œ–TL±*bÚºÛ+1ŒþÓÁ yWPæ~RKUÁù|©Tú4гÀ5¼]Ë{DÛÚXk;3ÿQm‰ì‹ô¤=ož¼ˆbZ%1Cµìd^3PÍÙyÍ„~;t›oÊŒ®“ƒ)k½Ž×†+Œü ÈäýÒ þ©²qϳ õ'ûÔí€sP?Η¥ïÀc˜n`4`ª`cø—®$¤8'Lí–xQ½"˜¯ŽÎqêüeÀ’é”òØE3BƒoŒÞ"ÉRcénÔ»À5áCÂ1ˆÊG(.iƒlÑþÌ¢æÀ)u•>§ëçû‹e^ú¥©Ÿ™~yõXQLe -CƒŽÜ•Š^8Þu¨°¬9XùñýõÓ/ eÎê-]òÆGPÅ–PͶ„†ÇßÇÉÝ<þ¼b'ŠÊ¢T´™»†yàü¨çÕÉÛž)ó§£! w,s¼:02 -ð�Óæt»;pìñ–žØJÝ6`Ù?°æ œß ÁÝ‹� YXI'Þ_•™3É!2\1|aI߈_Ê̬˔:;WµÀ�Âîv×D Åv¹œP:{òÃo4ßZ¹–gëƒ+É·Q®|Èd¹‹P9Õ(X¬.ꈎ²àn4ì—γ‡^Êdæb¹#.(¢NøÉ¡¼U&¯®'{åK<=Ôìñ{B’39`l^4#U1ª€ÆZ;wvö(î‘h5ªÑñÐ<¬XzÅ‘)=ñM=8Þ›RÈ…â¤`ýÅqŽ›a^öJž¸Ì*œ"‹ÞŒ¦É’ඒeöÛÆ+*ž¦Œj5La¾Æ©«à!ÄПÓ05Âr¦Ár=¢`osé¨Æ9òRI“5ϨbV¥Q̈¹ÛV‚HÙœdÜ’;c$PïÛܵ¥¿°zé¼ò}Ä®DWÂ[¼¾Ñ/’×?ÑðÒß `8á‚9(Ò}õ°šÎQÃK¿[Ÿë_÷À:œÅñΓ?/8øZ"qèû¢jìm¯ø¥“KóÛ{¡¥—ƒí48—‡3 -Õ#±ƒJiJµ'/ƒÜ£Iî8/ëcO}dP0I_¹M·w¸TQ×™{MNziî¬èö‰¿éŸéÀVéç3½ï¶}ÉQß›EÑìüììÐسþo)ðüãÔúšarƒä0øþñ¬šØK¢ÿí¾ïÕ§ÅøVŸ+ñÝ>שhV4$k=ÏO,ZÀ¤;…dóyÖ^‚tÒF`G�xÞ&¹ÌáŸ>©åó%;qšþ8Å éògwpÂ-¢d¿F‘ž ËZêYÖ:ú7Åó?õ›ä‰d‘k°%3ä¨ì$R2m”zͤ9°tž·ìåU»ñ¢*Ã27묵>¿ûÅißôK[ý̺Î5Éœé+VG˜¥ûbO¬KT›Q&Ûà-™+/VÿmÏPã°m¸±m SI}–N܉B–Ûˆ.½ -u¾£Á€Sëɇ[˜m°FwFc¦yk&ªH:T›/"ùÒoæ<Uv€¡Í´4l;‚>ˆðz ˆ‹:DFhàm9.n7°V‡a€Q˜é1:6mbîko/¡2îèæJlœ2«ad þ·ÁËë`"úÈbæ_3¶½–NÓÇ—ö&¢¬'Ž5AJƒpR÷‚»}t!ûÒìææqƒ=¾ó„çßy§§0ZhDø¤cœcã0øʱºÊ¦m*ä'MO|Cgmk 3w9ÖÙÉLžEUö{Sˆ´†DõQ"aÀ´Ï”4élèÜþxó†4§ú"9†Á+¨+t{ªy^8 v¹àNÀÀ‡ÇìÊñÞiMC@ÑsÎñ.›òŸ§ô×;í÷n£‰ÓûŠàXS¸»}‚£ñn}&î¿f60ìÿ aº“¾?Ó`7)tgtñÁ§4Ï¥8i·ch¨DZÏ\ã{ZÂúƒy¾?±r¡žœ -ǼÜqÅhRïr4›åßÀM“8>©–!æ÷b}VŸ·™ûgÉüC±9¼§}¬ªlÂqOùËç«€„Xã–å]jŸcG¥FŸÂ˜¤–L/e)™‘MI©3Å‚ö‚Qo_ЄJ±}1¾ºÚåîH!¼QNîQÖ¤j/D1¡|Jè#â8圕/hÌsÎ'¿ð-„O†.n¯¡X ^Ë>êa|U`U4IÌ9öu“Üç\‚Ðq0åôK>¨éÅoú¾;@6ÚŸÌiƒAžß‘° Ð…‘?$sú‹²—»»ÊÁ%<†cS¯^¼Û%’Ìçw”2¿dªÆ"r¿,õ)º½Pî<Pq>¾MtH! ŠJz°óò¹sO"²©´ýº¦×¸jÕMR1À8/žY7> žæ¨'òò"%]~^Kp+Žy’ᮜƒBÖ:âžKY6ó“ã oñæEï:¨ìû?]?ד¼Ê+q÷jT&Š×ˆx�gmµ®¼ÐqT; šº™b4·ö|T7âÍÞ™éºôÊó[)Õ'Ûö‰Ûš!YoºSñ|ÊTëú÷+ÏÆÄv¼xh ÄyœK‚¾ý_6oNTÔ÷ÛÀiOÉ 2x$³xñbãÀȈKŒDt»;ÆŸ5$´ þô]¾\ÈhMSÎ U<¾{îïí ÛÎk‰éÜ££ð³ÚÐ{Ò`–ypÿëéJ&¶ó¯ùs'Â{š\å*ɘ΂¼oÃÀÀ$fÃS.¾óúÔù«x‰°ŒU2 NEûZÕb~Ǿ¯½ÎÑÊNgÀ§ÕóÅõØA·'Wà–Bî|šÓTo"®„«'ÎðÒE‚è#ÍÿœD¸Àø†XÓÚŸj»j -£ý×ÑäûI:~ lP,V%›‚¤;Y7¦{‚ºuàLË#a¿ÂÖ;+`²Fâ�ª¢05& šä¦.œ-HÌy`‹"}„Zvøˆíènâò#ý=ù|M€CÑà\®~æo±K#8¦u„¶ŸLy«ÿÙIC8™ütF5{‹%s[ ê·Q¯w.X€EÎä.=Úÿòè’"ÛÄØU²Ñ#õôçë]º,Du/uçÜ܉:Š™Ñ8¦ÚËG•SÊcúÔ¨²E�´dªÁ€+ƒéIœ3£–~•dËT-u´ÔÓå‹.m©%ƒ*̪×0Æó´˜aͼ¹˜Ñ›zjTWyX>ŸOë²7à•£)Ùôy9>AvŸ«È'‹éŠæ[å‚/þØí®”“›t@݃;w¾DN°Ã‘AŸ¯ÅaÚ³á„1ݺüí /=Ž_fêK¬°h‡mN9²¤!TL?—Íî¿Ôç/⨜ïíÖ”~Á’åû>y;ÈÞoGAÍ¿�æ÷a0¼]t "MlO¬¹H 2àÄAlGà jŠ(9EÎVY8¿µ[rýõ‚Â’Û¨eB‘#u¬ÕWh˜3¸)žíÓ†JCH4–gGÔ@^{×Wý9f›&;ÿ¶ÙíûPЧÆÏœóUËš'8¥¸³®îå}1Åój8“ȇÔüñaÝe6údo,ž0E7:.FŠ‹¡Ïìê/~eJÍ[=ªú18‘xÇ>Z1¬ÇT†¤w_›å´±ÄH €²Ÿ~ö‹8Ä8Šfrßü5þ]#NM6—‚rª¥–¾ÐRïc¸ŽA˜òÜð°sF©dn/“fBMM< h_€þu#ß](áÇô‡»Pd¬ZËT^aÖüñAâ›gZœnã‹Ä{WÑŽÎÈ´Âã>otlÚDÈÌ…×�;Ÿ±˜y üyQ Û‰Ü_Èw^F ä{bZ;�±çò¶+^ˆbøú²Ò!>—:!öè9öXGÇ>h•_‘у¦-Z†¬ °è˱ü¦/ž+;د¶ï® ¯ÇêŒÁÊé5 ¹øÎÀû~•Užœt3ƽ·â…ð?²ÿT)¤Ý?×á9ÔP´ç[V‰ùþyK!Ô·Öz¶{𗃿µb«€ãåÕŒþ ,r1Ë2[AJ58¦Êe;P‡»¸Ì3^^€å¡íÁÕ€ë2êã:†µÐ%*0ÈÍÙþ�f.ž?œUÕ—äëå̃l‰U!e?šá3Ä—5»Ñè¨^²‡ÀqÅ& -…¸¤«^Rc1Ϩ HÀWÂLé[Rõ—\R§åmï�ŽëÐÌËç®b¢^ðúeñrn©ÂõAÑ'lߛȽ¶õø‘î10’›<È$ð‚º§Pî:$²Â/EÕ„Ó»‡‡;»ó¤¦†~ý¶‹GP½EáVÑPfÄX)®:_èÏnÃÀž†ßŸ£x2”‘ŸŸ6;¤«¿3QLë3¨©©n+Þ©ìðzCxøÂò'ŽMÒyÒŽ$Ý1uèÄ>>Øï☀da“ÐwÛ–Ó´‡¯ËGPúÁ)‘ðaÍ2>‡WöC(Û~]€ÛT(š9Ñ/?ùó==Ïß0Z"¾aß¾áÐŒ[”>Ÿõò?~ö¶ iÆà梔=Âè@¤8^ÐÁÜŠöMœÅW•×§À-2f‹3€š®ù* -wBS¶lÒƒ£‹¡–r;¨,öª†=xÕÅã<÷³Ë—¯Å±©îót÷p´`Z‰¥y5F‰@_Ê1¥%Œ,Àÿ_±`5ZÊ‘TïhÍÒJY齦Å71Ûµd8ý&9Lœô{ºlûáÅC™ß#å{ƒÌ²[KÄŒaJ?E5/dm½—¬_ƒwûmÇîxþ?íÆ—NÙñíؽþYã’5k¬ÇŒ¸ p{ˆgŸ5½Ä~ÍÔõËu%y$/Ëî{¼w <ûA:LÃ,bAÝÅH‚-E"é{õ^ïŠ7ת #fZÈ¥3y -Ùm·Ü¶5ó˘V.±D³²!d?ñÎÙ^—À8Àl8õÉ碦ŸG3~ë]4l|ʲr=lYÀ½€»#žMgy¾1Ô£–‚gMKƆóŽ?uîE¹¢Úk;J�ó>í×\‚™ÿ�9û%&oG-ÕV,Öè±-®Ù(åž.±î¾ ÏÝrqÉ3ç†æ›`õ"Y¨KB–ŽcþRi€‰K¯¦› 5Ö3‰Ä×wXÍ«ÛÒ£]£Bs ‰‡bž0?U˜jj¼yqÑíw9¡cæ‰cÁéù°`KHí‰U:Ñ~§ò5….64æzV õA ®Í8€ö€Ä;ˆšiÍy);€`7úåZx /U×ô}tÈü³†ìü¨Swrµ€ZÕ -^xÕyÐGòZÛaÜ Ìdn^ýgtô*úÌýžÑo /hVbŽeé2Ši@iv -¢¯žw)õÚN*¨Ù5È*-’dq*g¡>8‚sq¶iä¡V±'1˜þS–ã·Ì¯vÇñ°”Ÿ—µt„E|zSÜì%%æÃóëŽ},ߟ䂵AÑJA#(˜�6‘µ5e§5#ŠþÄþt°y¹+þä–,;)†Søg6Wô£8ïsÀ+¯«{ŒM ’hG(Œ"Aðü)‘¥í�M8Îjàìòû£šJPnOÌp![t±´›‡6o—û -·*î�æ°%—€ð¤‹Vâü„"hš"u·ƒ·+|ôÏÕÑþ¢?RÛ‰êÇùƒððè@ôF2)é×ßÔ -½–O6F|>VôFý·á|r‡û4ƒ çúkYhâütúuYå§b¸Lë‡÷¦H´Ý>¦.IÒÏã 7®rˆ”/M|¯Eøpý¨ö›)AIÃ3½dSÛ5ÂôtkE«« X2)iq†tÆi7¾ôº¦£Iƒåô¢#4@«� ÷«,ŽÄòã—yfª—un2kBeojb_Ð2è©šk™ú¨€ÚÅæ5FÇëÔ˜$ß³ÛÛ#ŒŽÈððÆ,ÁçG%.¿..y~±n[ <ÈßF¨w;&q1PÃqR£ÂèóQnüp6‰„Ûï<•XÜÄÓ“‹¯œRgèÆ5¬ÊÄ’‡Hï÷ˆÂ%²^‰õ1Âða쀢Wm>ª�šL·¹MÖøý6oƒL•[©ûº“gã�ÉŒzÂâÏx® -$1j’ÍüP×#ùíbQ_Ò•=ê évNg<+ßXëÍ„äyʈå%§¶\Ú0‡³¿—óåÌŒÏͺBE[Gá.’À‚†òà0¨0ˆiº0Ð8#cÆúgnPˆ“í†Àô²šµá٤̪`òP|bî0óG„‡æÆ¡óà†‡{ÔUx¾ƒ rG[„ÆýÓι鋪¤:\>TLòb_SÈyΚ¬OpoÂé:%GǦ•+kÑñòùwò#íH˜þð™íû§´žM�A¤Ü¾2ÞÙ©lb3HÇûžÆ5%Gðã�ž†7Š -¦ÃZT{²`Öá&Ç -TþlÿÈÈ2ÕU#4´R9�*ôÔÙµ3Ì+éoLK¬ƒ&!±MÄÃŽv‚)Íja%j~°Å_FŸTö£kÃ!DÁíéÎÆëÝŽ½ÙZ¿ ÄsqLj¸ß nø‰á}û6yóº-õÖˆemæåEà¢ñFmÆ9ÕZ±ßÄo¢lùy܈Fžoæd}¤F¶Œâ,á*À 9F:ºn;ŽñV÷¨f-DIûßçª|É0 „ ¥šâtã™åMMR>–åìY5l!«5 ¸\NÁ–=ª @â¨0º¶hùÿ«@ü¢ÌåJ¦rN_ßä‡Qˆ?åGP„CfFèeãùcÞÊÍ�ÛaßšÙUy-ý.@Ä<üŠO›š‘�½íIIÝ3«N»ñ¥jµ;†óSsVIzq ×ýjSÓh{,÷$kXw!@¥Ëo6tæP"wüØÊš™§<<á`]<ž©xùœ¢nWqN!e—‚$C<·ë$i -äϪah€?±»å`ðÄt˜ç07f¨øzL’ø§ŸmðS<-c~�e§\1Kœ‚ÇÙ‹çþ.¼À‡øÌL«i¤øäÅËÉ««¼ÀyP*µß#ˆ*¼Žãž§=ëʨNÅÿv¯ÃÃb¦ª ˜Æü„nz3i–i6m_’ãò)=n_ÔÞflßÙ•S‚Tç‚l¨ó®š/Z.™ BΑս#!ËGª†Då*¾gVÚµeÄoZ%fÚé_çF U>¶ -ï ˆ4âFù,ÎÉô£ÃiÆñáÌNAà ò !Íýþ×ò=é]àÆÂ÷_Ƕ>ÄS¶—<•ŒÅò¾…q*/²!OE _‚s"\óÁ¬•ùõFÛ~¢6 IÒÝ$PS|… µØ{Ž…àŠ‚!î7z:=Îz†c/Ôðb´ü`hMÝz�?¼xÇe—©ÈØïCa]Ìët~.o°OEÇáõN-ïG ˜{îY‚¡½_TAáãeù$Î$’``r¨\b˜;Ï/ñ+<ç)îØ1ô`Š•SUwpOÓpß+!$«~n¡F*Zfa{p'¢òcñëʾºj·r£d·ŸøÁ§LÔç{ùJ\mXþ*Î?EDûurà€Io+”4æ*œPDÞÓ*׉f3"Í>�û_&£©þŒåz¸¶*\Påë»öé^0ÆùÐ,€v<I†Û‚Ѳçk¢‚3BUDk7wt°×„ê›@Ypït<^ɹ}<?¯”žs8¥Õëù¥f‹"B׸x×!Ë䋽Ú2:¾chÈ!)H‚ZúË[žËTbÅ•ëûk[K`Ë2£ ©¨Ñƒ‡Ìôs-ðå¨p¬AÌ7i˜rSQ(Ð|oÄ3;Û£ÑÄ&‚sHòÛ4TvF/§ÕøÒ [ݼ\5µO²ŒP„Úo—Æb¿œR*=ÜéMÈ6¿Ä!Š_e6¼:sæèÊ·éÝÐDhk=Kƒ4À7û¹N"ìšS7ðà[;‡rÇ<nV´Yà ÀpzÙ¨Çq,$¦l‘1®ÛžËÄ3kAwK|n9¶†w¯øÆPñ9Å4ÚˆÀ[ÒÖ†òm–Ra/ñ¸SËÖ™ -³=ä7Ä£}ƒÅÈœ6¸â®Ý¿ÿqŽ\0—Œæº•èhKT`5EärùªÀ6¦RùŒ»©”7ý#‰œ·©¶Úω¶ô0¼äÃNV‡|»2l.€Øp�†Õƒø+òr,«ÛÚ¶ '¸É4Õ&”ÞÎÂ?[#V<Á½K9†*±óÂ.ÁCˆ÷•ù=¦±‰¬CÑXeÌÌH8@˜¥Ú,1|‹Yö&¿º'ßa ìC´JJšæ[˜å:eÛ@î ¾ý|ÈFøÎÐÎ,îݲåOdéRcq0>|À�Ocð°oÿü—/Gkc.-ÇÞ+=ˆûƽµÉ×Bþ´Ý…ûö:ð¶”äÞKà%yÔ.ŠƒÑUóÏ-júíðë ÈqM]£»v.Š*më×3ÚòåÝV2y"|‡ -H~«V+nÀ¹é,dÜûµ²¢9©èNhÓÇGIå´)FéL ªÌ'õùµSæŒLáeñ¯ü|Pïéñc±y‚Ž”Ïk[=M¿}sˆ øœW ç›Ùá6õ*ÉU[[Z,<º5uÈáµ\o½Xî©+-*mx“ë§A6žÓÒRö¶vÒŸûÈí‹tîÍ‹¢m¯&b9¾P%éÈQ”ŸÉFŸ/»/‘ßhÛü9×D£S[@àÙÔKëw¦êл=áàfªpÓæçf4õ¸G/�‚§<$ktˆõíÅ~Çð®å’_ðÁAzß%Wî„aê#”Çeþ.&X:‰MaÔ/°˜--„-î3t2]€Èë 7Ü ðüù¸EÊÅg ÉMŸÇD;BPí<²í‚E‹ÊÜ™|õ§Õø"Ñּ<ÊبQ˜9tqË1ËPºT*±¥¦Â‡ƒCk£´&-ôExñ(J¢Ür‡h¹nYÛ¦ÊÝðõ"]±(êIvæµÙôÍûÁøR)›™ÑÀhqÒ¢íc%iê‹Ý…Ô°©–ffºKèx ⶡ~ÿø–ÁÞö+N.®4åŽsZ²ë6‘ÃõÒ–®³†|æêÚÒdÎT4öí÷¸ênýê”-ÉœpåôRÉÒ}¶Ãå½VmÐbŽmìéyÅ㌬ABäjÁéÚ–MÙ³d4 -¢èž¡le¨ñàò¬iöbtI òÅS…õ¡‚4„ÃvIúäàÁëf5ãà~½¢¨LÖㆇŠÄ¥eƒÛ‰µFµ;o"ç^¸úhÄ“…œèû±¥P´‡ÅÐÚÕ"džXA¢%Äãr™/è¦+‰@lí;¨¸\'¼{œ¬rœcw®õ¤ì2„`!=RÚÙ>f@*¿s"{®íC¸çb/?n.‹öOÁ‰Ø ó+ñZmg%ÙTÙäË@´Ú3\•I^Ì'ï½bQkñ -º6ÆR@uÚE¼æõ`¸j‡Ùœ.ˆò’;ɱJÅá¦#Ca¿×µ -Ä+K5Ó¨pÆUãQ-3ÞfcSŸ@às¨CÝŽÙPge�zb�ˆÁÌí°µóm.© ël8¦;Q6ÿK „çâyþ2‚&Š³Úzƒe›Xä-©é7ëÖ‘#k×Ö¥Œsfømû4EÏà¹ÔáaÓ¶<yÚ·û“uUÝj©Â;Ÿ&°~¤ ì2#]ãeák³B?Bc= 2A4]Êø\rÀ«sÆP@–áœt—Tßãài½jõ)£DÿŠfj¿6Y~&¼÷Æ믿þ_qì· «Ñöœé›+µ©œDÉ°DQ¡˜éÂŒ…Ø„ÏÈMiÃ÷ïü –?ŒYgØ@ W�W�íc<²ÝÆ3ý21;M‚×új×àa -÷0«sÕ±ÿ£×'Ñ2³µ% ÷ÌXôöp®£IÛ3¢¤?‰é[=øWˆOX¦ñóºšà±¦Ñèô”›:'òÈõ`ýò˲ MÈsþÖ�ÊÖ`êî…×tc;—Û‚zdŠƒíQ-Ç7Åò#ª^RP^ûqwœ»`õ|ÊË–-ý³Î#§dUŒh€'{4ñ’©œìf{˜Výþ–-{¿„ -¾Ý¾ÖQ037Â<µI0.6Â"fÄŒeùá™f kýÖáõn%(0Aœu.^\0zq#ð^Ž‚_â 0N¤Y®UðƒìÓmîïá½'DÃp›5"YŸ†6´ŒÄKŽal›eyî¬I6,¬q—!qÛb™>„vË׌2á:Ô1Ä€ùj¯õ²&³#’IF·ïÌÏ{ñb!Å<˜â[ÍIxìb ¬xF9Ñ’5Í»» óiÙÃK'⛕>¢ë˜ž›Lr3’u¯&seºÍqÃL‡¡™Žé…çºØ2¸ ák´}û8ÄKé&åe²á¥_¢çbÒªh´’3ߘ§ŽËZ‚Ù#¨eØË Gf"k–Ñ aÍ2gˆÓ=:öKö¤ý®#8„.¥Ìy¸,ðÇ3–·©–k’<ѸJ¦PÃZl‰`̳’+ˆÂ–j ö~!öóÞ)¾}æ¨O§2)Že/Ôu’‚hIfQQa»ƒK:»ßë½ó¸Q+è÷°ù'õä|ܱ1îò%þ~x8bê̹*ù‘u„t¬ó¼Û"Þ®C¹ïÛiÊÎ…,òÓû6l˜sáªÇˆÀµÈrðlÛPfþ¦©s—€Ïcs#x§“:„-v”8£Ïu>€g‹G£H&È'U®ó¯iƒŸ¬#¬®ÏîDYQhþ|0K16’¦Ãòðqå_:‚ÓÕqVCKÜ!ËP^¹.š„;%t¦^ûi3¾Aw¸ÐJ™€40áùgÀ•Ü¢Ð¯¦£8úÀ„_à8² -ÞêÛuÈ ‡Ú„ÔÞ z9¹ -3{9Ö„Âmm࢕¯+µ¹©áÈ·ºí œd8a!’QPÁÁ‹ÃqQÄËVᆠȂŒr¦Ù*³aÍf—³àñMrCØ?ŠCÀ6¹ÏæDn¶Cw†‰høYƒ Y2e»¾Ç±öúéwa³RWº“©ñÔDAÊS\+buAhÄ @ -à ýÆÔŧ…Á£%~êÌAÀ�ªàJðžþI®õn×›K›³y¿.”;lVÉå:bÓj›áòÃéã'L‡¨‚§ŒgÌzÕ@Œi”^ò43å†â?‘†0´0&x<í¨¡–pÿ¼åŸ/hô¥1 Þ/ß\}Ë«_ÖR“ÙÏQOšºNÄÄ#’¶Ò¹¸“ød»V·èHvаŠ›v·¸–ÊIÇÖhÍrðA™BxA5ûarŸ¸püÏtþ·%‡Ì9ò,Þ,(×-o«;qÓÉŸY– NñÄ>~–Lp•+ßÓ5Ï1¨,Ã]ÅÅ0‘ú{ÓÒ+&ÚÐø/@•8€)ó¬nêûƒ9ˆûÒ¥]HZÙ0=]Ø…ú¤4S¾¬uÁU³<ÿ®;Š�<Aw‡j˜âHÂÊ´†‹+ðÐ>´þCÎåWeÁÄÆ¡Ã~!ézÊ`™)hÅÁš™þ&0Ùý-ÃÍ ‚iÎåpº™BÉþ[zhÃÿáîM�ì*Ë»ñ³ŸsÏÝ·¹sgËd2I&“dH²‘„‚ˆ€TD±*"ˆ¢ÛÚÚÚj¿Z´€X¡*nÔjdQ–°2!@’ÉžÉ6™Ì¾Ü}¿÷ìßï½3w˜�mýþXIÿ/Lîv–÷œó¾Ïû,¿ç÷\{ÍÇ<PçëÅ¢muQoµÓÆí££½4¼äZÐ ³ÅKñì2[;M*y´Â¯¿Â«ÙXá˜ÃT¥q‘êlHç©’C´81%”Òu�™“g•ÔR”áÄ#PF^‘)= 1Z3åø_#€pýïh¬àîbé¦uðGwÝUúÒ?ÿ¤òÄù VYѨX²ÄÏ�o]ûZ;˜›…ÕHn™¸¥ï8äyùÅMø²Œ4¼»G±2£H#s;æ²P±aÆ(ý„¥m}Uš ÀýX:>z·@¹;6Ì ,y§3n•V#ð‘F`§Å¡!šª«£$N†ïÁp�‹‚üzq“ýœï‚–“Ð-ýYÀ…òÈñ¯F€¦ÎÔj‰çìðÒ_€a¾œ3LÎúÓŽiþJ *Ö@›]NÞ*Li”Bö,ÀÀN@göæB9}fjîõÁô¿€·Ð,㫆û�nmjʼn?]^e¥ÂíúþhŽ�ÂsP»ÆÕzt ÎÓK||ûKÊ·nÕo¨ årHøžh`Æ–/ †W\öM 6.Ë…§{êZ‚¹}…eÓø1Àb3®(¬Ë�Œ‹T·ñ$\iVePV¾˜|U¡ŽC«Ä:` -:C-E¢‹ƒ²q1*[|Ø`|`±Ãº¡þXOSû6bÙ™~’Õ°Å,)%FSwÃŒ~ÇâùÌÒµI¢à£å/Y?Ž�^ÂIaOý¿ |ÀÙÐo4ׄ—ZíIHTóD=¾J?Š¢ù²õ5¸}@b™2˜ªÂ„ßÖ4¬• ׮͛l'›/ýS- f2XÕ,¸z1Óh¦˜Ã/bY»È´SãÝ›)ëÁ›{Äöº¶QâèÃвۥ†¨¼²¶¹{¢’[iÞU׉*c6Aˆãø‡{óÑyU³ÿ–§Ü‡‹TñÏ]\ömÙÁ?'誢¥?�+øëW_qí>YÑ.ÌRÆm¸ïKq…6“W3E]~òlrŽ_vÿ@Њj(ÄôîÞ¢¶¶N$`ñƒ›MÝ‚Ë ãg3 f7'?NŽG-M@匔ìt¦àòp©Â“Œ®Ö3¼(<ÜìĆ˜hH!—~0Ku4ÝÏNÎùJW—IXeÛóåõ"/•A¶)’&å3>(ñ”QÀÐEAãtªˆRer¹«Ä%(iòwvA™&y;Q0Ö_xð‰$Ï—ëz{?þhÂ7Ïaj¼ùb^fyi%´“« qÌXé&˜»ðcñ™´ûÔºV÷92©PyÔ"f°½ÖÓ£Œe›œq3¿–1Fäõ£½;w«eߟϬ£Ô¯®Po·¹J߸N„²¡–b–ÍöýhZÛÆä299¤|ÑmŠLLÉ9í¨w ´Ÿ¤f–†ˆ6SàÌÚªSF7O mKß ¥Ê…Õéû3ZjÛå ÿ¨ùR½û,”2zÆhú5Ž·®†¡Žd/+¡äÔ峕‚e½<o{…&OS²1N&¹éºª&QN‡f’î—|•‰E\ð_î϶SVQØ–aê¡-Ä`•IÖ‹˜ÀRy¬O–®SA]äíØ]~k²áý;„à ~XO–£•ó%Ið4@6U¨Ä¨æa<A1êÏWµ„rÓÆpùXqÑßÌtv?Kž¥Âî)³½r2Ää&¾Û¸QÝ;žÛ¡*Öò:TÉmýÏûˆˆõ›%ĸR°]‡Í/_�*ˆ7§¥•Ò³WtÀåaÄu°ºI Á÷·ZbùžþÄ51X\ŸÊΩ:±ž® ƒ€p1Ã'ÞÃÉI®Yt„ôuëXµK)îRMv±£ÒPZ (/†6¼‹àF+×EÊR}zÙå3 -:Ó¨hæñ‘x:~ÛÊöüƆÝBøî:¬ÁˆýßõP`xÃêlÔ=¨áMþ5ðxô!Xt -•O>�&¹Ðf^‚Åîb· ŸRö癋çg÷‰w\¸6ö\4æj¤äc|’!jUÆÏ&²P#�Úìó‘®ÑN*Ý1±±;—x‘)É|0dðsÜN2^+Û¢ª…Óæ°fÌOìÎdŽ]ŒìBýÀ§¿8·µzFi7¨ZWÀ²#=_ZùÚ¡½vrÐë z=ZÁt”dkÑoÊg*ý´·-+†23‰œ,"Ô»;ΰ>Cý�š°Žá€"²æÉÊöçÛëMøv]P7Z ”Þ=DæÙÒ™*^‚eõ“@"¬BÏkãÆ;ƒ�¡W6—¬b10"\B²ªòK»ÃÛJ±Z=ω Ïï»Â‰Ü8ˆ¤ìæ°'·±›Õ–Ù1YFïÈ9Gs}{ƒh"Kn38lZ»©þ&/µ[qæ·Àº ²Hå-?ÜÉ'a†Ý‡<u$9RÜÃÑ:™H^èV -@d}‰ÚÚsòÄõ!ãû!µ A¿·(/YÐÙ&\Gí¬ùõØW\ÛîËV¾w¤³¥`ˆ…à¤(ËØüTw7‹Œ½òµ]tŸ=›t¿à„‹Žö3 F6<ÓATÎo·«™´*{“X9LpœzÅ’ê©ðð)R9A9ͨ¨§ôq¾\â³Ðâá·è†Ê>œò(0lg!»2c§×¶7³¾Òå©WS0–#Ÿ�EÔ«3ZZ¼£(¸-\ÞOÈÓ9ú÷÷ݯøÉoÐî ³`ÈŒl† �d{+½óEpi„=’ЩF„k76¤ñUOÕó1,Úõº¡Ý3kUýw¶–ú93O¦=¬h» ÷m6z—7èÏK±ðC•¾D³ý$X”ÍáúètÓ|žŸø+=Ì^V²î;ؽ7±zÆz@ð{HVø9òn<È`§gÁ]¶Ê%6¿‰K(_\ýÔàh.©ëÆô8öLõüK®‘ìž8>ÚX5¬¡WxÍŒ¡:Õü±Û3㼦ÐJsøŒv:z\‚ï ÖßÒ´þ”HKž˜™‰ê´óbSÖï@dÒ¦j£½<§î}:Éüžc©ŒÓŸœ<鬢]¢^eÓ¯»ÿþ4³c²¤/“¥˜Þ!@Ñf駾´þ&ð[뙤z7´ÉWÝãã²åæÃÑä”G³¯þT‰÷õÿìgêôgHŽû~·újWñtQõ¸mbêmƒ“že¤‡-*LÖh˜ªÒ,‰Â?˜÷’äB¸…Ìÿé–NþE(’û,6ûÿ·ð%fé:ðГÜEë¤ÁÝC±Ç)ü¡!®L¤s7¶zÏÑzÉ dc©,Èv³À'YÝh·Xøë˜Î 8=³pkGã”òB÷wµËf;æ*&6RuSÇêè “%<µ9nYPNÔvÃvŽÂîTîÇ(×.sêOkðý•Í®Nu<êà|cR-(ï+‚%B†ÖA´Êw4›e }^i_žöŠEø5åí*áX´[èìÖí;¶ü @5hÕ7Dz87¡TãÚx&ˆŒ½òD&;Bè Ç“™•BA÷uE’¿ïíÚ–s/[×,J¶ç*ßèH|;jþØ5+ŽsB ¤dà!Có æùªw,»toEÁn7úvö¥ââ/¶ñ ³I˜±}TÔ‹_ÒrZ<.Øt§èQ/·©¼”/bÚ?Dó\°äR5”hÆØ~�‡ z¹# ÎTòé9, -m¼ãŽðÕ[÷•³ÆÔB*di4jÄm$r¬lö&)·ÝÃ_iÙØ£%%5��@�IDATêUrõ!ø:__×Þžß=¿ÞdX[È c/3+äÓÕ= Ö8“ßm;âˆ+<ƒ‚Ót´¶æI7‰åä[¾6Ë ¶½ˆ«Cg“ñF;“£ú&Ò+”0)nÁÑK$Æ*,\O<uåm…çNSÛ9'µ³PìClÐãµ<›yñQ…Zу¶}ûv6«²7€ðýƒ@<¤¡±Ö1)³eËK[ö¡^¢Y›Œë*if¢ÑwhTÛí·Ì<ÛŠõq¤Ž‘b«�Úyª—m…•Á¸XúBɳß�£CPóûkÔÔ«qÐì7DA>SÒóëN¼+ϦS¿é¶=Ø ’ù^.ÔäåýíðHùÁúQxñÏî>øì‘3W/lH–;ù_üCÆÝ+£©ŸO°9|Ð|> kmŠeD¿~J|Maõº…67ÝÝ›†p))ŒCÈüó£}õ³/f¾ö/ëÚKÀ!2þp|ÐLg—R·Jr0Yæ XœC(4z6Jwâs˜õ ÝçÔĸ;?®èÜ^Lilç~ýÿö©Ÿ÷/ÖXÛúmñٛ·l©O,"ˆò9£“–Øks‘è»Em¶)9ñy }Z™K›âz†Ò?ê¼_ƒÞîhæ1›Û¹Qf¹/Ïà+`ö•ý»Žx_„gW:ííCüŒÜì·Ÿ§þ-EÏŽ[Eë ô‰à<)‚rx•‘ SÕf,¢?Ø»ùôÄ-òLQÐtR°lJ8’}ˆ&AŽ•Î$HÙÛ¾‚’Lùœ"!¶|«‘>Ìõ×ÖV…‹®»ýÏd?ò+:FüÁà¿„1j˜µn±nêøXÀHé†Çð! mÝà_´‡0.½‹”‡¼ôk{5Ž§c&!YâJ )ŽŽr«AžÃ°’¬™j}Ø.ÌEúßRU3—ü´¯¯|0ÿ£ðw–µDH@Ç^³mŸ&Ò“"еw¼¤Ïü¬Uð©.Ôì+³Ç‰L˜§@H_Å m,3Ñl:]¾>rÈ6r´§¦;…¦Ó‹}ãÉ–—úb-$À•ÐÒK$IøªTlÆ17ëõ-e;z(ì?]dÍMH¨ù¨pŽ Ÿn–c¯€«h=\µðßFtÿ~•Rþþ¹ßTz05¿Ã†Îß„EÓò8Axl�ß ç·”r©”îˆeé[DÑú2XÏ‘íF®ç¶H2ź•¿¶±1uUØ3°«¡¡oºöG’zLÆxJ¡–Ý#¨è‘²‰tsSÓÆòõ&4u$¡h‘Œ¢ÍsRbãÇca¾À_cçØzÄ=¶2‚7zï±nÐHQ‹q=ER�q£T¢5ƒ|x5ÇYí6†ÝšµR/ffȇ2÷ ÌÖðò’>Î;ªÒ"ý-àÞï€i‡í j¾§ÊÒ ¼’üu¢pÀïzÇ>×›\øâH±‹Ù9NQbÀ”ƒMŸZÙ:]97;>Â2¥—f*)Ô‚#”â=ä.yDÔÆ{ç£rÓÆrüR^Ó.»®4P‡“äèhŸ1ó+.‘Ú„ûÜ“I)÷¡€Õ?Èób#2Kù`çá?·ƒÈQ,ƒ[hÙ«Ë—¯}üÙ#é£W/t—Mö7æÆá'l´¿ë—’LÁéQä"E§ôõ–õ*k5{ÐIs½ðë•5Ó&½š×— áÔi†Ñ`³Ûã~*VÀqlv5oŒdR/¤F^Š¦–=ŸŽG -}ª4[wÍíŽ/²¦r-0P¢Á1§ÂŽ÷Êšîhç¦s{dz£<âaÄ‹ f½:˜®;àDÅô\õ„mÜÕŸþ²3M»·ÞÒtP È4+�Ê,úB2Á3ûú¢¡<È}l´Ì›¥Ò†M›ˆ–_¯E|¡à"Ê€§Å4&Šï)jfc£²/‘µd3/ðÍK{zÆEIŠëœ[a29?&ü°õÈG²QDýÁ „•ÆäÃJ¶¦M›J+J•ì>‘/fªbØQ&Ç6—n,<‰r!ç�ß‚Ñfvhì'.þû†zY‹á¨6ñKd´=ättÜ -j‚VV®þì³9ÎvëÕˆLs0ç ž·n0(nžßÁ¤/kiùªÈÓ<´h€NÊç.1òŽõ-ÛŸïiZ»ö;Z‰zJèUgƒø*77€ÞŸ¤d÷ÀJ?•%f¥! ¾$f&ÀÓÎi,†‚‹§ ?sø5�zAî!ü‹u0<ópÕø¤o˜îOÇG‹Œ‹ÊqÉ+Ñn/¾¦fôïEŽ{sŠv¶xQ;U&.Y‰^¯ØðS\æ5ª!Þf³ß1}%‚{‡žýù -X…noÞ²f"†›Q51+…)3¡Yô2„‹%É&½¼ïø@~SS“ñ»B׊îRŒàvÒŽ’Å~ÇýYUJÏÙEf¶®‰"ÍúƒÕÇ“v)¸@3‹7�›e“%ÅDÚ7ëI,º{näü¥5¦1j\ÿÎ#V$»z¥bM -±Qà“Söš™ã¾á3Ê …ý<j¤{Æ2'¡~ÒÜ?ÓèÞ½ÌÙ“CTUÝ}m!G÷(p&jÀ#_ ˜ÐCc®åYö4„/ -sãÉž‡í=_¢Þ¼>’Q9Õ‹*0ÍO‰cç®áìkºP9ÖÙY„`{WÁKîïµ/(™Ú•8Laé¯0¢7"–ÒCL<™dÝSÑBŽNc²É9Bö7Lv×-øU&jV„£ùþ£õÙÃU§æY -JÜð«´Ñ†à’ÇPÞf47cŠs8AãÑ m6FtbúJJdjQZÝÁÚl^3oúh‘%Zš“á€`y´Äˆh7䃻â{½äá_%RfUÁ(õiŠmhºI M„¸èïkm(oIÞ“†NYûJ…(%Ø þ=R¢³ÄV5 >YvF&1ÐÊ#ÐÐ!Àáá¢'JÇN§‹\Ûv.}¤ØÐÞÎôï9á0gÖóC[ÊJ8+ãñ’ -Å'¢i¿–U‚›´ëû– *wâmÿgÛ5žëÀbÁÂÃÝYA‰x–W€át3ÍõŸEÒÀ„Ÿ]g¥¼Z¨Añ<¸=pçp^˜ì‰u²Å‚¹‡ÌGhž¯Ã -÷rL-“þÙt' FÆuL£¬ÏZ®*ŠÝúf5£J—Dn0—ÏôëIRh®ßGäSXô§ !½®hŠ‡3d‚ßPfWê¯ùPu~±†zÛ'`f„k¸vÁâÙ‡Ó¥db¸7 -7Æ[n“i÷á¶rÙ¨pY }T÷¸ˆÊcè« Úåô"ÛN¨¦¹e©æ€Ái0àO„â®#@3šXȱ!4bœJIY~ÓÒŽSÀÄs9k1Žá<RQhi’ìÞ):ätMF…“£Él€ºz9´Ý£ˆ(?¾¡9EŸ“¬¨?«£Ÿó…«iÕÜ\Òôƒ¥~jll^7ꬹ´Î²a3EÂX7n¿¼mÿLA(_¹¼°àrtoÄ5`:Ó:-È8íòß÷·%Sq@‹GÐès,̹sç’gO -e†a‰Öߣ$Ö§€' oEQ•ïVžÕû~!ïÒ÷,|ÉÅíÉsÿA¦ycM×Àáw¥òû?´ð’0NH™Ì?>qÂÑz¢ŽÉ•È0TL¯ÂUœvæcÅbö`b‹@eõ¢/ðV"íÈ‘3Fã¼Â?Ñ°žafóéNnå¬�?b¤!ø^¨SdÓ¥"}ÐD²N˜¶©œì%÷ÚѤù,] 9íAkzý_ÄDA0’¦W «iBT¨Ø‰Ô ï1Y6÷ÃOZºÑŠg‚D6Øbâ{Å噇bi`-§0¾Óx}a5•ê(ËVr @F´VHÁp2§É“®bòÔ G£h°TMbAâKÆ\u½ÎÒXjÕ¬Ç6ˆéB’˜Ìi¦ž*Z4DÁÆòqš—¶¨ˆûЛ6•‹HR‰~†òy0~‘Çå�ÞxùšÏ¥’;Ǹüêêrp¯,LÊ;Oûç $½PFv"gqSÏè å…#Íè3 BÛ³´•¼í}Yæàt%D›†T‹*z)é1µ\¢ó|^Q¾ö륷oÚbê¡"bO•n 9§BåÔ¤Tñã)’¼ãÈú†CˆÐQÜŒ~Çw»½ÒLã<³žkÖ(Ís/ü?<§Œ¥cIÂYQ´³F–dÁp,Ò}¸Y7ÃXø,ã ÆÌŒq*e£¯ªºáùç¢Ñ„¤óH+<°3ñnü¸P8+Þº\ÒßË[jã[††žÎ©™mLRAOiGÏþ\%à¹yädêžæE[ ù~Ä”ø/¢8ëQ¾¦æ/¸<)LjÁç]36Ö÷£ïõs×Ëèr± ½`ˆB#Eœ¬î1fVãÙRÔ5sÙ'Oÿ}º¤~Êõ)hýË0æÿi0_ü9xªKº"àgúÒ•×Á’8†]Tà[.À‚A .‡»††Phò5ÞPcRáfW9jÕ=c#T]Û¶-Ÿƒœç|iD^ìN%Ør1¬j“ÔηõÍÂÊm?;'¤)ã3ÜOÄ3%TÝ2m(¸ypžÏ}ømÛŸWß³ð-_ <(+cçôRA¿ÏPñ³pIÞˆÜÍvA¨ufôa;d°õç“ ðšëìë;NpvÜØ.ÔÐKÏIÀ}@-€UH;(ä²Ô©©`|…dÿF%µõ0B×Ü@±½šÒ³ÝóAvÏz‘÷8BøÏ©å-f&Ls pÔ„ZScY†(†ƒ>ÆYö÷’>r”×GÓC/2srë¦M4?šCÖY$ š9øÚg,æ,ŒÁˆ@3`Ø¢“{…‚ƒËKy㋘Šw"‘Ê9[Á÷ -]§i¨".çå…n×”°'çtP¨6GAI Uù„s¡b¦–I8íÞ®d6¿R`íÕ‘Ö*d¦Qgý¶xJ€QàE –EHÇ¢ê†åÅ6ÄŸ>]*XÐjËŸF"!Óã½ÀWDd“«£n•ú\±FÞìéù!Üï:ñ¬B|ì‰9ð·vE -–ÝÄ1íÓk ÎTpß‘¿r³J‰—x±î•H†Ò”WÉ„‹ ¼–‘ xËÛÍ @ƒDÉ EÀ™ü7Íákp#9ñ*ˆ‘´ÂQÐd6ãæ´bžÙ™üÿÛÏ:\ns©1À]Eu]óDŲ¨nøüçËh²¥¦ -ãDÛ'“úÍñdwq@£ÍSª*R>“ÚXPÕœ#èÈÞ½|ÝìwvVÄ5A|¿“Zy¹·øÁoäþ–ïñFÄ6ð~ -mÐÑØQÞŽü³åëgÆÿµ( -=vQÔü©C/½V¸üâ+ž%II°*>š±$/Èøt][ƒ -0ÏØ9k/Öú,®ÄRQ ã:úvý¯”„9No¼ðÒu Z‘´õüÙ–Úä'pŸŸE¸UM©êH_³¥¯[Ô8ÿBÄ r¦U:ƒÁØ6škÑuãoQ¤/ø”]²©pú>é‰ó ó»t´:ù«R£Xˆz–7¸ˆ+óœ–LäÖÛl¶6æ•ÁHäuIªX•ýÕP÷Þxˤ6O|ãçÛµ‘‹˜ò¹sEÿÈ@†IU :2i¥þñ¾L¾ð£}Û+³ÛÂË€Õ’3@L.Ï(¾¢ç Þ¥!IÍàvMé§P<ÿE°´±']}ø½<±H)&˜¾‚Û]åBÝœX±øm¢YævY|´ØàÛ®v?=–V~èÑé¯Á³ŠR=Æ/�i3'úÓ=çhYÅå,ZKÆU5q#Îe²RýËCxRDÖ©;íûYUÐñôéí[_ïÙñÜÑEAWÏJY£ÿ,´cÏÐËZrÈd¥ÌÒ /ñgxbNO &ÒYCãªn”�S#ºk�¦¹“{Êõ�ŠÁØÌ8YÀxâîn©ƒh““sPd 1ˆ%KN‹¯Ãg^¶Õ^_M6Àõ1u,ò+ !¡sŽkîüh¼ØœGr`?N/C_‰\¹•œ§ÆN¶}·My5`j6.¶A�M¡Kf -—9a?V¬óvƒIL"ŠìO(?w t) 7yLhuCCÄL,¨w;>©&LþÞí·Êw¹áa¥¨f—D‡ô¢ö8A|dEaoZD3v±žÆ&Z¼ÒáqÝ걋ŸaRô5¨w†l<š¾þSŸr)ñ˜yàò™Ur\RuI`ÊSRšf\hñ%'å -Ž–;͈âYzýÆM›8æø-^ºd]v+ßñ^Àÿ>óµ¯ÕœL§·t%'ö•Îþ'¯+Á<&SÞ' -ýч%ªæy¸áT½ÖsL”ø¿ÁsÙ‡¿‹çm“Dþ×PV¿Ýõ«Gðó÷e‚ŒËuxÞý±ð§#MÏÞtgó6Œ 0[Ü.YÛ&Ç™a³�Cd ÐGªÍuU͸·ì\ø‡2Œ—F€¦¯ãis=<Ë/C®?�²'2JoôÚlPšÏ¯Æxà`6pœáZ¥šôÿm=DB*õÄtx/ó‹ar«àâš×1)x \³Î;c&q3½mß÷ýã{Ö|1p¬WûSÚf†Í#ÈÀfÖ×ÔÔĽۖnêõ¦b—µf2‹&ŸÕÁ(.:Š’´;$"¨Ó·ÒéS³‹¦~L["ÒüEÜ=C•å^²Ùà¿3æj&[ësZ‡!„vAPì2å�q̶x&‘XRÊ0œ<©bu‡VdkÝÝõžz´›Å¥|©Qæ)á‹c€M«Õ4ÂÅîÄSMŒ½h€”…ÕÂxhÄBË€›²'^È¡Ë D'c¨```¢B91êÁ¤]¾—\È€¼™ë}Ëe2¹¥É¶¬¥!Ñ“‚[Bæƨ(ÀïDÀ’†ä‰|²ˆºS”¡mn·×Q “ëx¬Îš¥6hFÆ´£4æ#MèÏEâdÙíÞÞÞ©¦&¢mO4¬Ïßu‚>Òš=€&ñ,‡2ôVJÑŒŒì#‡žÜ“l¿µ£²ÇÔ+,É.û.ƒ/…dÙ×»· « ™l&¨#LB~n·9;´Y‹aûáªEKZø.` »îö»gºßo'NeGòJfCõÌ*—»yÒ:pÌOU*ˆÀ|z߈?fS87Ãký|®äŠ%_²~S\Ш¡ÍAQüõ¼ùžÈÇq_^–B°6á/4ÝX -ÊFh9p~ìIåK¿C®¿žå=êQö!ËÏàÔåÉëÑÙZ*]g»…-'âŠÈS§ÁËb5C>J*ÍÌà[V³O€ØÿW@`<bõkl½aªlZ}Mñ„Ž"ËŠb&?“V~C pä)ãôº$ÙQ+iÕ5¿¥ã™w žÚ7è÷g6/‘|™b&yí¢ÆôåЪy¤ëLÓÕMßW5êVÖf¿\'gDJ¯Ù e¹FP6¢Ô=®kd囚œœåºP+¢h¼Ž ·Þév4À$ë)dÔ”ŸF[¯î´1|ðC ~Tožš¨Cú|¾´F„ºG$¶\V‘’£oï,êc_º‡P›î�CQK¤J¯ -' ¡Ê¶ùŒ5_„Fpn7Ì¡Ê÷çÃkY`¼×Žðl®?GËÍ`™ÿCh;eßþû“'_ÝØ:‚Š.ÚfL¸•®ð<LMàÌ€‡P]F}ã$ƒTâF¬Ú+ ÷Àz¢Ý[ÞiòŸ¯&‘TÛ -ºCÕD$.k!–]ºR4ÌåÃ5ÔÃÐg.kjìÇaXæ.B�ÂçrAæ*'P›çÇTuàÉZQ>êé7{Äps(^€DÒ “ªöPõ±X6n¤¡ÂÍi¹tJvRÔ©Ž·„ãdàQب�ÆbÃçX§³9tËAh ¦4Ä©ñ†˜ªøÄç%\=PÄ „€_¦È›—64ÌïÆ5¥(Ù>"¤4QD|•y:-é`šœlyC_Ã1ˆætÈ 4U`8àkM:_IêMù¾Tö!»slì·U÷;ÕÅQø}픞S l¤ÌHg§yëÁ_ÙüʈŽQn„ßßèSâ›6MPtV6"š¥ds]Hÿ_!²¯ÝÌ£ºÑí?ß_/7×Ý!hìÇJ¢¸ûL ß×Q›¯Þ×�‹r-„Çõ`*.ñíAÇûq±Öo~óFõì&/ DÂ¥yu¿_>q´ÝêV[ª±Qǽ-»À .¤»Qgî$¾õçæ…;d‰@³éX¡`âÄó)æbÐÉ]‡äÿX¯ÀMeKŒãÖÌÂ8Ë‚BãÑ\:z¦Ú]çr0ùùªÎÇ9§}7_È}Š£mqlƒ“*g gFW,Bªë6ó÷¢Æ"‹]HýÌ£Iä8Ù‚`yQ®¸¢Î¿àjä0.À‰Ÿ“¸Æ}]£{v‡Pøj|\¯¤“WÌbJWÌ©ç}Ûm„Š2ú»Ü;¯ÀúQMUÎÎôæ™Ó‘g$–½Ùð•™-;Q2½(“%ªj<æêã°(|Â`ãQ[õ•©3È‚¼ðÊÇ,Ùðܵªžós"ûïùxú¥ÏÍ<EŸÞ×Fá×ƵȌ:k´7§úÉüšÞàæ³>üůŽCÕÿð?¨TeÈÈ)Hç -ùã”c"6‡(<`âû¶M-|Óñ~¾ÿ£_År_èÕÍIsïˆz‡ìÆ;‘ÖÁE0¹k½áPꚇçÇJjI56B!CJàxJ´4a'Ã*6ø[O :Gø~^ÕÜL*”¼ç¤&šR2Td2‡~O9ÕMøEQÌÇ3‚ *B)/ã ˜´g`®ôç)Û 'Ò ºvq©¸pèÐ!“´§Àx.kÎî¢^¬©£¬½;]¤¦Ùá‰äbð#¦eɺQ¬*ö –\&ãžìÌäËy†J@</jQ²|±¾lxáéþA²9ɯ¿îÎ{ZC_¨YAÀu€âÔ‡XΚųÂOnÜ¡Ò¿á¶ÛÐÄ!F9!Ž¼ˆÊY‘5 -gÈËX’ðÕŽApÅ %m¼”Še£J¢ )«+“¸²µººšdËMÜʷɈpOmGžÅäÂTÞÖZ|9ÿŒïñ—´ïÍR Äòžhd·\±~)(.‚tT349=ž(Özª" ³ña³9ë˜&‚Qx+‘„ì r·õ`|»XÖz(`5ø&ÄJŽõlÝú -¤ëdôc&Ð@êç×ûû²À3*/le÷ÙÎÍÏbMkUZWiÛ“pO4bß7ª¿Þå2°˜i¯§¶Â…ôqDQ .¾Ç0´œ¢‚y< -àmRÓÕ%zü&W<DgIÑ)ù¦¿DœAkÄ)-sE¶l²;ë!ÃŽärF"“‰×û‚Ë F¬ÅrêFP÷¨ªšKxYÛIv!ÜÂx‘ÓŽ!øËظ‰s„èZO -¦ð¡2¾Â¨•¬ ±\½Míò«‡:•[;:Ê1—kØ’`µ×NÅ7Ü**áÕ8¬¡AªøåFJÿ½›ú§04Bà`¤a@xQ÷ð'ÏvA¢A„dݘQtµÝdc¢~Ѿ„UjÂíïhCÛké£p94çxö‹¸N²LŒ¼y¿Û§Oˆß¬†ÖšÉZJ• TroëÓäg${2Ü^ØŸ‡!ÐØ!C6ZÙ¶!1TõYÖ¿uUåËóä -Ø{kÄ™råkŒz"«¿QÈeîLü�ó󱘽Ú^W üâÕ({`Ó㌯êàzhˆ˜(2¡v+fzLy„:WÀE0‰ë9" ɾe¡À°MØn$;1#ß ˆTÌ�¨¾ ™Gy¦�k2ÈsÁg{!П„dª}¥…„£ë¯-Žûb"ÐøÁ燢³Ø‡FUl„V6æd2á1“`N”!Ö9>%�Iˆ{°/…I)6Q†ë|Á²ê D®qÛüëf.¿d^'ö%ÛVZó,oUÉоbž:&äs - %G€)î!Ð6Â}Õb<Á#^¶+PúŽ!ÝÕôFhä×[íy#‘û^¤ïè£Á ûw}ñþ'îC³ÝâY$Ž_½ªœñôŸMæñ90¢¹l0cÿP¦ùͱDë±d~Qw²T>—õmm…šZ÷3öqûΊÿlêz.½4ÔgARÜ…ˆýª~Tùå*BÔE”mö<‡ˆª¨ðw[\ÐÇÊ•c’ýU kÕ[0ÕmÈf‰Cs†¬3?~áŒE!œ˜&<*KíËëÆ,$hÔ"¸·•i_+¥Æ=4î}¥äZÍÔ +Q9ý§±ZûQh‚7á(ÛE…[² \;ðZEd÷ù •¯Í’›‘ñwµ(Rë|ÇM/]èÖ®doae M<8¤«‰ûi¦½a-ˆHŒÕb>žñò¶ÙaoÕ\”²º×$2©9,:À‚\‹¥½‚ØHNea@1J×ÏQiq.8/f%ö -e[hù×5°ó¿VlZ8/m¥×.Y¸t!’YâCKÞr>Xfw‡ü³ë öëÝEd¥[‚¥rÀ𚪪HR‹=Ï¿XöQNà¿)åR-õê–йªÀ§45ŒS—9³Iµ”• Üø» ½ Žg”_Â| ¨¬•À“·nî<7^@îÝûÙfwàÌÆñ˜‡)ûëIŸŒå‚£Ù¬ò˜Oñ—¢žÿÎðHïo‹Ùü›PþfŠ™ë*}cR -è+¥&yâ¿%xªìó§z=g0ÿ9éeÀ•bÎÀ-ËöúT!µ@ÐãÐîú-N·3ЋÐòFn&ð²UœÀ5E‹JÍØ‘nâ±´^/.6(½…R<`�3ŸZ££¸å¸+&˜Ä¼(¾X0Qx‡Ös'‹Á ~G�Å0)fΰʗ-E»[øoéªö+Ì{²ª4MÕq1¦×ÅøŸvòìŒðµ•ÍóæÁ='8Eü.ÔC–4ˆ´Ë2Ø1}±K³-Ø=m†®Œ&tŒœ‹½ ˆ;AIꧡJÀ=}|°ßÔÛµárÔ¦5”õ+ò,}öëo1à¿C©Ú×(ø’L›w /ýöb<ù—¶þþ³dQ!J0•aD>›C"I� ÖVx§çæÐøA!�t˜ÐÜ)Â.ÛŽ&Šõ})²(L¼R0…ýg’¹ùÑì)\²qÊlø`˃°Zr7ÀÃü—6™ù§dûK$,åu~å󑌺£§F'ý¹sŽIŽË3öF�Ø]šµ Mš)0ìJZ°f#ÃÙÏz©×€6‚ÜCŠu¸×SãÌ«³OÀ·zOVÕÿJä0UÐ_¨¹äuûp=KoÞÄ£BG3ø‘¢2B™Â£¥"YüÔ†ï\wÝ9÷U+  1 ¯ÚÖÕZ[}Všgõ¾ -gÈ}6bg°|Ñ=¤û¸ÉF-þt‰çGA+TÏPz—øKÙ甃d±YÕÐcèn,ß¿,, c7‚9G+ܯŠpÔ0*-!èµäü6”E>ÌÃ\“9RÒTgº¤Àíjõ!âWéº"O¨DYpiè<¶õ€—Í…rQ>ÔÜ«·õZ‘æ.½X•vòê¼y"4ú&F/FFÛlÔ0]V˸VÌ^qM“Ç]Óº™l@±TµøýŒJåè\îµlíûࢹå -!ÅÂØIÜŒo¥Ò׋´ùmœ?N¨ë ¾P™±ìœ&ý™Ë!_8hb2qX_ÉÖèD‚Ðîî!±Ôˆ’Q¾YïÓ?ªäüÝ&4;X±Vs`"hOºã±±—æ5îRÒÇfŸprT²Þ`ÍlÑá¸Y³L$bq‹+Ý6d¬%à‚|õç(D•ßßÏ×÷Ü¡ÙoLÀ{ç•‚ƒÆÛYk m2O¡¬UÙŒÄá‘ßÅZå…æ¦}¬F411Pw£Á0€qeÔdÉ̸e{Q1èþŽi>V©Q ¦M-È?à2âNm»vöõHÅ1d‚ÄúŽÆ¸6ÍåÂMa>ž »Dûm\Ÿ§ðn0ÊCœÎA ¡i ±Y’NŸl¦f"ßi²ÙšÚæpv¡½K°x@¡H·F]Š»µˆãô,¸¼îï°i$ìpˆ)¹€ l]ps$ ¬cÈ¢J›g`×h±O›h¬šÚ·ã¹2·îKfÊkójø\#]ƱövÔ¤BDf̘ھ(Ê9Ö¥¦IyRæ0Nma©6˜·²Kœ!ù(„Yn.ÈQp//–i–(2(=a8S<šÏ?³Àn <Ïçd??z+ccF1~¨µä®qz‰…fǬ¥K]`Cø úü"¢ágXžÍä¹3Lýˆm—ʲL¯¦ƒÃý~P„™&š¦§ñŒÿ QJCå¬60 £¨é¨À‹t\Uñó1&/¾Î¨êÞhº¹ñ‘¬Ówmr½r¶«ˆã$"¸IŽ„òö¢úÂ¥7QññD¬ðܞǾiûÄ—^ÑJÙ—œ^±y8M-MÆ1M4QuÞŸB¯½uÒ‡ŒÀèIà¯MJ¦¿´Ö¬}%–Úà2п¬khÈý -ëpâç›À íÛê#eâÉy‘ý„øI}ÖŠÀ`¥ØŠBkýHz!Ú7þGîa“°sEü‘f=^c¾ã€|€ÇäÇ©ÃrQ®(ÅÅaQ³û‰\¦TÃò%qEãTJP‘Ž.ª2�ýÏÛ!€/ŽçKz<ŸHîd´´bÀ¶oÄØw«ÖÏ“)¦†¢¹Zp€d>€ -£0<åã4ʧ ¯³‡’X¬Žâð—D·ŒkæÎÍbŒÁûnóð8/{RÂ˧Íb}ØÁ»MF0hë2Ìk`½Ø°è>lä†mÜ0:û> 2fßÅ^SI_}çXˆž†¥æOßú³±fdîµÀ½&Ë"}´ØÕeU*ƒ€}T�ÉÎÊß}÷Ùî±ÊE‹K€½\€Í™¿—áó§oÜÂZ¿ÙhØK¹’ŠÕW@@‚ÿôò_œñ=_(jæαÜo±’ß ðy”HGâ’qÐ*äv_6gBøÎhðïA€‹Ê9Õ’Íë=ùgá¹E@”l‹ -!¹ây™Ÿ²äàªÙÈz ¯Nk"ÊDL>*r´lÊ\'&ÇÚ¦&y¸`-ªtºjȲ¾9ó ×)û)o8»k8õ3@6j–tá;R)/é¬ýFà}ï¤8EÙŸá4ÄZžXœÿÞÝ�P:<ÔIô–$üÃ4[†Ê;ŠŸ9“7"ÁBhÔ1:ŸI•�yUb³ªß”ve//:{L¤©â}šòV¾Ákcûd(rÚw“o#ol-Y™fi¶:d³A¡’øò,3¼ )‘Ûl5)K¸™{GšÕÔJ—@Kø+p\ƒã•ê:þª¢aܳóŸQæÅ4ªLØ)æ»�ÁþMó²e'Œ›³àñYî)ÕÒz¹+è%²•ïGÛÜ€vv¨;Z‘çÔΪÂ÷ýø+¥ß?|_)Óo"XDu'óO—Š¥DoØ7FöÅbB¶AÚ·YCZVÚõgwVG´xK{`ÞqüF¶+g =¶?ä©y™Ÿ»h©½d]¶aÆ_ggzŽ]‡JDkÁ"wµ‹fžqpÖ«ªC[àì nn)goåDìmÁß!ÝüÌWpQù!Ä k™5‚ioÌc1ˆy>„4çŠÅ¢øÌ3ð¶S™Lv¿ä±/@`n üϧwžÉ-k˜utÿñÃY+´¢Ü7ô±<m%o¦µMd±„å1õUh"ýù hµyü“¤<SÒ(x0ze$~ôQ}å˜qT;IìyS ¯Xq2_ÊÊu® ˜$+kŠzZ7¹ÿð×/çNÉ¥»Žé’š12@E°‘Ãp™€D•×©Ñ$übiÜGƒA0kN6<r%’ɜȯj(ÿP" !œÂwBí®FFh©ßFÝܼ¨Œó>Óe±®ÔØx~ |?ÑttŒ`¯)+´|„?Ñ?˜Ÿ ‚Õ‡…:ÛßÛ„ qÚd•NÔ]‚bÛw˜Tø4Ïoˆû$FE*²Nçw—ŸcÒÈìzrpò¾çW«YÞS¯¶nííU5 n9aB™)vÖr±J¦PÙo)6ý$ÄÌ—æ]ô èîõxQgfaäW’ÅÇ:Zƒ$m°Òè=±ìlžÁŒË5Ô‡#òEv+¨–€ùä~“5à‹ æ°ÊN¯'Àö{>Éç>’~uk¼aÍš€ªIý±ÙùM—¢Ï’²OW,y5¬Þ¹†nÅàÌ( -y™×4¶˜Í– -;æ„“ð[ -i!/'¼¬MUÎK^L¡—§Â^§5Ez5U·Â]²'”OÀJÌye™Ë¥òl¸†*\qcι78Ý/.59p#hòÉ– ûÍþÓÚªà…ÖŽ¼zêè™»–-'úVPQ<…’2C`nFê.Þ¤¿Ð“ÙÖ¨ý%4þ&UÑ6¶U¹ÏâôéLñN˜pÿ #£°w|¼¯:à{Y°¬mp¤¡aÄ5UÙ6¿ÊC =� ž^ïèF”—È"süàãÿ¾ó¶Ûn#0²s"òŽ œ×ÜšB4ôMº6 -œON‰lƒi(³mŒCËgc]—̪¦®w×P|™Áð¿ãXƒI±/&KÅ[¯jû8Xc㘖¤’ÞsÒŸ8Y Þá™ñ¢öìç'Y½ž?;2—ãå�0cÁ -"íq+7ȇ@ׄd̺‡S#=ßC±Ë²öNOÞ¾VÑ ËpÎ_þÑ«ÝÝ)âÊ9çâþç?Ð^2ÄϽqã&îر-åêÈÓNKoÆsÛ„?âuŠJCG=Á¬AEå1Y -ª@ÕPõ ‘ŽjijœvŒw}͘Ź©é™|ä™ïÍ|ÒЬzÞÆmÝúÐw#¸ù§¾'åþîK߀28€ŒÑ5pÿühiHêÕqÃóóÏ|Aµ« D̉ ú9Ä�T“n=黃<ÏmB½e~¾2ìx×›ñ>}ùžWòÀ\‹«›Áο(˲є‘eý†MúÄK:vB¢Aî–o ’Šk ú¡®£±ìOQ” )ÀðÍÅìÖÁ)óvòžX…£ÎÞ3/¼p¶cÒܤz½Ð}(ë]8ú£ð¡}6í®]½u ׊4—YÛúãót¾ >^·À19#›+g(qƒƒ‰¢iÄÐ(‰úÔ<uÛý9ïËYËùHžÙòZ&Òy2Àzò¡Àé‹ÂÞÁ0€mš&dAö£NÂ…Þ!x‰àqsæå8ï7‹ij6wá%~¦F¼Ö!W·!¢/0‹W·»Ò”'L׊çzÆèÄ”ÆR¹f!9|Pé‘‘l0rÿf4+%“q–àmªb|6ð½šÒý9Õ‡Ót |]„«úÅŒ¦Ù³÷²:>b@÷âø˜h4êžáv1¥‚V4ç»"èW€SÆ&f³EYŸgEîoûò:9§i‰'a(FѬnZ»¦û;ÛF,”Ó/YT{ÄÀÌ{°ØçåÐDfºhnPe³{í©Ñ)·r¤l)y.óà€{ÖZéç„•3ä÷Áÿ8µÆÙç{ÁRȳ%–Îk,!=/o“ÝWÓƒ¨ýßÀby¸h˜;`±B lJÀ ®`ªu|×ådÿ탵OA4Á±ÈÑyP:Võ 'LÔw,,•>þ¾–Ç%Ao1õv”ÎkMŽ;Å -a \±¢®xøJ›êÎœN ]¢;Ÿ&È:Œé?¬-éëão[qI+4á)?:yæ\ 8VL1Âïû¾¸ˆÏÂT@ À)jwjüqóL¿6squu”è}Ý2ó?ʨÖ=ªªÿ ¥xO›ÀDÒ¼D–˲íȉ$ö“F]¸w¿ØÝúŸÙê=ß߈:ºÃäèÏ ñ%;ãú+b?ˆZZë(A¸“N�Ÿ8!\ì>ÛpßÞàõV7Å#éKô\>”טWT¥ð¨¡«ÏSMáIBö‰‹%uÈïè HÙx¢e–¬Riö?PNæ(ôN¼w˜¬õ€—³þé…÷2wÈ®>‡`Òïèš‘.ke‘ÚZÈ°U�Òòz)j*›f¡HLQ)IRF}}}‰|³ àxêÏyè•.¼ë+ñEeÜÁ¿¼Ì]mš ÁÝϸí0}¢0wæ¬ENƒåï@-6wžc�ÝÀ ¶L�éílFh‚ÀÐAãà/•Eý4ܬ[ÑKQÒ#6܈Ò;ŸG¶Üqø³öòbÕ'-rÀ×û¬†ÞÛeÛº“¹ÖùW]»ÌW«�ÇyE螪ü `jæߎf¾)ÉÜ×,Ý|¡+ -…l3’‘s¾6p(oé|T!¤E¬Ao¹/DÀ’çRéÙ–X#Ïu÷Us—×Ê–25ßæ5M€î¾89ðâ÷ý£dÁš¾Ù¯ly€1 ,Q_»ÊZŸ¢úòdA¢Ö}º™7ŒÒD°?ÔÛc¸B¡”¹€PÍŠ‹å>¤Jt×ó§3Yíû¢©Ý)èæ]Š®}ZíÝyS½3žSÜæpD°üOM^¤z-ž> ŒqÊw£¼S¿“sŸïŒK2F‰vgëMMð\&ÏoHÎ)áÛN&HS‹?J:%MËézfç´@éŸò~ðÞMT�#ȹò¥9,}g#–.›J©”Or3,ªœ/)Ùâ¿‚¶~Üï/; -ç8j%LÓÎï˜cï<âŸö›?øýgݪõòU(ÕF ã!̆ÃWÍ y@Ѭ¯c€ØShêšÔd€Èê2 -ïì<¬lw±¢pÖ§(Aþ¨(³——b±snЇ>ù%Wm ¶¾srÿé} Á ¾s¿ €Å¤\ ʃ«ì»jˆ»˜/iúåðጼ"F£”>Jë¥K/Å#FÓ<Ÿì‘;ìÃñÇ"9˜:Ñy¢ÅÂ7M°ÁSƒszÞþ¾>X;¥“nÁÃîà(<Ü̘6 -œ±•Ç*®‹²ÝxYk!Õ¹0ÞWÒÁ¼dK´$<WokäÜ>{hÉŽ¾±Fb"úý~V3D]ÔDÉ+Š‹Ct]¶¬;pÏS€’ïŠb6ec>/PÂÌxFP® FÙ<Ü‹hUE&} -—ò“[·Æï]³†ÙùÄ/cëf…"…ÞÞd>EÙz$ו}ÔeAD‚-1-w�®=«g¢àT–÷EÒMÌÜ‹.nòVw¼Ü[œš¸ÙµÐå÷\ÞŽvê´ADz‚ÿ¥‹ÃÒ °÷ÞK[$Ábçx®mËîÝSÚ¹ìý}é} Ú†¦ƒlãFPÖt♟Ô묀,j”þÐóÏ8â«!»øŸe=ûÀúões˜’ÉqRQclpøÄþR)þ²§Ö³‹ï9v´äsî/ÇH`4çÁƒ2÷ÖÐ7þAÏ—œã|lgU[ÛŒye˜Z¥d%cˆ¤o“4i¼ÖÁ³{9’lcf-º1šÓ,s‰HçæwNƒ™é1 w@ù�NæÎ>²ÈÿÉ£$¨uŽ¥f=3ŒDÞaaV:e…4¥~Ãä™ÖhÄn+Y:‹/ó¦Pö Ë]�Õheûóéõ=ß\hWH`2lARó¨$ØÆÎï<[K¯n¾`�…µäHOCJ»ô¼ôôhÃâËŸ§ÝÌF}ÿ=&h©sàøR0€4¶)í¶|“d±•×ô9ôP”Ž€¾¬H2 Âð™ªD5¿Rï FÎÂJ”•X¶F?ä&J´ëC¥OI‚ù´wÈݳZ3Êìø ô®µ1(,«›¹¥p”˜Yä<°KÞPæqv9�ˆšÞ ®ÛV|±»—Ô0\àÀXêÀæ‡ïú¯Ì0"ŸïO^¡›º÷)ˤ2°Õ<Ë9g ¯”á¼ÖV:˜V/+q¡ùÅ@lTQP†:‚@Ê”_ä WûÅ°%ùšÕ¾Û);öù"‘ûgîÝ{uéÒQC�6¡1b!žJæý�}îÅûQDʃ4Ì S³Žé´–F5ãtgw÷c3kk_äyÑ“�«8 aãÇ^x!M¬ˆ³‘|ÈZ¸´áH<›}u.ñÐz¶êÅ´sj°ol -ar2årcH¼TÑÍ‘ÐÒV‚yhöÏ~ÚùÆ¿ÜŠÄ�”æˆÁÙDs$mrgŽŸLÝÖî+k,Äï¶c0Ñ�dÊ5Àù®u‡çü%nû^ü•Ïóe˜Òé_àÞ_'HþO*ý±W?538öð¾‘HÕ4ƒôì„EŒ«s4àç^Т˜ìLˆ¶†Geßæ8ˆøSôP¦ Ì^dçL붫ږý#¶Bé#'‰z!Ýi?``QÞ ¦@š÷żFŸþ8æ?¬j¥9eÚ·^tˆ2¤ú•k]c,çÒŒøøª€Ãe-ºÖAq!`Ökqõ6(-A©ã-åÂÏzPö%QMÄÉ/�G`ÅÕ÷Çéét»^X*0'0ŸÁsbïyvÇÈwÝצiZŠ’ê¡Ta,.B²'‰Yº§|2gsã®*ñ,ÅL¹Þõ@ï×ïYøV—Rc ÎsÖØVÖf=•ÈmùÄÒ–âÖ Ëh6_0Í1ÛP³Aܫͫ׻5Cm†°XeªÊ‚–>“3½{N1°.DZ“*OÝÝÒ€,éCC‰ÜóÛ·o?ikk“K%þZ'ø:ýÁ¦R±L2¯ÊÕþmÌP½’ÍÁ=êã•X PØ9`U’ãJ.ê�*�þôÉ‹B cÄ÷$aBðHA $\0•gS…{$†ËãÀ_¹¨+-ߺáö/o9‘¥z⹤}ÿÁ7ÆïšÌÆšê,Þ ¹ã”>È_Ú ÷‚Æ”;¡QŒßƒL”"2–‹‰Rñ¬3Dº ÈØö¤ÄË·´5–ýÜDS W ké£-vÐUú¡1߬¨Ò±}«V=Ùwb<ÝvaA»yë'Ç–ò#§�ßÛãÚ&f5àºñ»I8j7#8¼¼½½€µÁDGYrüS¶@§Io�îHàq -í0M O>ùÃÆÚào™ÖLXÖ®áL’J¾…Ì;¿jê'áÒ9ECZ×ø|eó°ïH_hÆŒ\"Qˆ@ÓUWWOPMÍ”nlû�/sŸ„9¸©¢(6ÍmÄÂpl:‡n¦8ö¦‹ íÎú:¨õ«€àX½Ý²žè@ÿ§ú‚ ˆÏåFÜŒjÜŠ,ä"`;¨mB³Q`y„êV*Æ6òØA&Ü6:ÿå«£0¹é âXüdÎMün˜uc¾<yØÿ•/ð]I4ÿ¹<g�†f¤ì”�X˜üÀaŒgExK-OãV1y¨JrÛ5øÂII&”A£†VN‹gÜ»û…ô=«®BzŸQO¥âNØfìO¦ÊŸ¬)*É€ªwØüñd¦Â ÷®çÏd2*ã‘^€‡íªXŠ’‰!;¥¿„»¢ -o‡7ba‡åxõ¦ˆI÷®Çx¿¾|Ï·Qî¾¾èìÃNmßV¬øf ¦r¾PýD*7Ô¿±ÃSÖ,‰©ãUîÍ̧$±Ã´Üv…@ŽZ¢g�«[y;r3ˆÓ½®Ái¥[¤™Z>_Âï&j“1¨n|Ãs-ª¡ÄYïsòÜ)#W<Ë=}àÊö”Dk«b»ÝâêˆÔÄb?*Àû×ÑêÉWuy!ÌnÌ[Ûf…âä7ä&‡¹²k�"°Á¥Ñ”Wò¯AU^ضì~t±¼¯<8,(ÖV¸IÇ^;¹·©ÑáÎj¶™™¢úb¤Ÿ+Ê;U³{Ë–Bxƃj‰ïv64ðò@Ÿv#•¾‰UóE\ 8((Ö¾«›¥è7³"óyJz:áÎè€Üœ<§r8¢ûÇz÷ºP5_wÂáh”숻ôÒK!»¡Ý—‰ºøÝC!mÈþ³ŽÞã±Øí¼Óײ@Y³ùyMδûwL4Bí§Zúç$†™ƒ¨ØœÁü-ç¢ûƆyÝÀbQW¬(BЇ»ºØÃÅÚ\:ôÀj…å¹åÀ“úA(\€”÷Â'þa90ãØþȦɉO°©ÛGRAûíÀÅ-¾¥Ø“ÛN5;¢“×{ÎK¾h}€Å/! 'C{'\lU–,QcÌ:h?ó!fl2Gƒ.*ÄꊧYèÀ«¡�£ÍG-a‡P’�œçþÕÐûö‚fSDÒÊP9¤!·,öÈô�¶,°ÎYRÜ“c¨h1·Âø?5õ]¸:PÍ¢s‚xä¢ÿéŠ+œÃE�ŽítÉôÞ; ø§º)¨{½WøËÌ‚–îO �î´Hü§éºÛm·´…Êý¬ôeæÌ™J×xÆIñ@* ‚ÓÉ3^àÍ jI9Yˆl‘kœo‚—ôë=_rŽÆà80</@¨c€Fø3;—í޼Ŭ”ñÐŽ'NÐMžãÈ"X¦6`ó–"Dÿ“§J•rß䘵µÎFP]„ꤧpë%8c_›&'ÚKFAa·†Ú0¬ú‹UÃZ -Îq Äå!†Š ý%ƒë3„LoN¨=µ+’îud¢Ã+››³UTÈ(Šø—̘ãD\µ ˜ÔHr d…ß*¥B³ÆkàjY UáAd£žõ”…ÌxX‰¥‰À˜8ÒÄ¿ë þså\0+RêI:ù}[z¸oEs;ˆV&¹^'4Jbj—ÍqÊã9G¥@~þÅÈŽí👦ýRÎƨT©¾ªrÜmòxúï7ÿÐÝO“çeføªgiž>¯q.;w麇”¨?còÃ+KÝÉ$\Â’%ÔÌó¦å9œHù -…x„pk 0IK7�…ûRº$†ÂžŽ‹üã òyœaΗ-2mà(à�áz“b³ÇW¸ Ä‘F/idr¬ùèm>ª¦©E¦µëÀÓõ´ÞmdAÝ6”ÿ öý„Á¥(6÷u(ÌõH½ÝØÞ•9Mµ‡§4[ðct¢üÑnä__%0ô%«vÀçþlÍ&ÐH”Ÿœ«¹Ý`·ÿ(ð*,O°šö[ä6ÇJœeÓ &„Šµ6$ À%³ÖbaÁª+ JqCà -¤¤¦idáÎàà7Pj|†Mžxäàÿ›@£ªÉêÈ:ž¨7Áb6Ì–´(Ê\5Ìrb¿(ý¾ˆÊŽØsànr)F.Ê3‚_º›§©ëÍê?Äe÷ÂÚ£_¿ýîv E\:€DX¸šÖ`ã·Êxû“ÜŽZßHg”zBD‰È„mhúÅÑjÖV†Æ‹òõë��@�IDAT?=½D֎皥r‰À3ƒ0� éª @èóÒÏ;½ïDÉúc4Ëáó¹÷FóóNf,?™ˆä §ººèöO!ü‚Vä3ÉPý;^7¿BšÍÐ>¾ƒMæ“϶‡ß"£.GÛ9*ȉX ™VZS8¬xK®çi¦^Ñéßw¥ôœ©Û±š÷ÀÊkðýB‹]àÒð|Ââ!Kg¾!;<“¢™¤•¶"Øì@àÏÖ ]/RRIÔ{ªEGs1Q‘j|¥Ã ž9C£&ÀsŸô;ý5ÐÜÞõþX}ðЉ -®tê€o{C]Y@~‚r†ºAi;*ã”èâ¯,rчkIvÓM_y+U4‡PjÌÅ`6kEþìzøÒ;ÀŠøAŽ1—s~ž×7 RãÕ–©^…ev9Gs‹¨qMÜí®GâÒÊ—L,‹^|i0Vã²×¬4YÇ'ö|ò“U¸Vü§_’lAÖT¼7–!Õ.立5Ÿ¾£IvÛoq÷}pG|¾ÖO)Í„O+|ÏÞãÊ ×~»Àÿg%iÖˆ¥p7„ë¤Y¤•ã¬mò¢t½ù¢Hn1\(O÷¯e[›-0¯ ÌZA\dÛ¡S§œHʘ‹)#Ôwþ…zïîñ¤¯ÓÆä~k‡V½§¨'!Œú²È(ä<ud2_Ž8,˜¾Ø«°v/ñý¥ÐèÛûÎv•¯³Òÿm¯<çˆ�)2nhÚ÷sóm#©üÛ0“s ¿ÄYܯûN]ÝàN=±~Œ6¸})˜ézI1fÁ(gÈb+k4˜Þ¬ŒYb @ÒþÜèè;žõÿÔý!B–¤7C)bOzHM -¤@èΡdkç>Œ„™¯¼8>nûù‘<~J¤éŸk%ó!Nb¾…;ê*.xûvçÛg´l”>õG´YÈ_‰J®ž%˧~ðÏöÂå+T§ÆI—…4+=ÃãÈÀ§)æ9¡Y`Å°Û@I“Ïè…”¯±~¨†˜“mˆv{í6ž˜h6Ùå}s¾@¡0LU5Jì|Ùœžßdñú«N)ð¯ä¶c¼d�¤ûÎ%™€ àa‹"p‹že£®ÛÎA>å’Ç'Wò£9Ínx4é,˜£ÁQa “éã»òy7(ª¸õ Õ™±’6”‘«‘6Ûþ±"eþ -ô1¹�-¾Þ!Lim•¾W^áŽùOµ‚s-pM�-´ù›V¦"´ÍÜs4%^Á IgÅ®ƒ;¶Å¯X¼X(¨ËQhîP[AÉê‰GÎ6z<:x“é‘ŒµUÄb£Ût~Pá&É’k¬–3âÃÈ‚r`;AB}FÐ̘Ã7]-¥H_‰öŸÈ -Õ†Çß&ÜGUŠú"¤ktA>ý…“G»q3¥fô¼Éw@B5aŽ¢Úˆ¾í^yŒð;a³ëlÊ™„¾QÙ?ù÷,yŠL˜£…×ÞtJýW76Hÿηò ,“yŽYi^÷„“oΩy«”Ðpš)!këÿ’÷àq•gÚðéezѨ÷bÙ’{ÁØ,cÀ˜8HLB %!&K€„”MûXÛ›¾ÉR€4‚½@¨¦,÷Þd[n’¬Þf4½þßïز!ßÿ\àI3§¼ç=Ïû”û¹ï8Š(S¥ -E͵qÔ”¤¦Çú"C;§ƒRÒdü|XÉÜ"0|DI^ê•S•¸§*èréõy6µÊi‹tÄ“Nd!öŸ:æ‡LÎ_pêD8¾Ã#q~™ö¼ÞŠÅß·´}u>õÑÜ¢é]£y;¯èë/®ðöUìj#ƫο ¿BëMNêê?º_ïQ"wfãX´-úv¤l Ãü5f;qSÂ.1(Œö©’\ŠBx@Ð2'+Üîäª`h%qLiÍ'|v»×o/,ã)uŸkM†¾ õÙxF}èÜ\á‘ÇŸN<^¦¶”ž¨¼ -óП”õË®ñŸû°½?oÆ·GÕçFR*º< ÜŽbÞ„žAUt•@¶–Ó˜“?$'òæP ðàîP)½m¾3áX-C¨ä4ÕÌ^„–c“µõ`A›š4ôCöŒÕò|뎡&³¾xb>8J?GÚEQ~ìòo¤X¦”b·-ù9§4L©ò~p)£aƒªƒwü=JÏþÅJ=`¾ªFÃ<b*è™íÕgŽUí•hMË|”U"{ûmYA‘<³ŒØä¡ØÚ¾p6N |ø*¯Ëñ¤³~íDKg½ß}ìÙ"¢ “õ¹¡Cik¶,±ß@ô|Îí*ØÜãa¥c$n¶£½îïÅHÌä»Ëë¢Ç‚#Щµ|‡á” 'ZÎæ?üìÇQ’;Äà1›mäR»Øý¶ƒ(L¤cŒisümüðÀ)¾ ¿SÂk¢80�ld¤£0/Œü0‡¾è‚éT–7n8ý^ìû -„š%@jØ¡íÔ|Ž7Êm¶”rõÛšZ)`‹ *¢Uñlˆåä¥v7Wðê‰Cmy@˜i&ÕuHÑ@5 e÷‘ls¥GÊE8_«-E r”q)5KÐ\hÙ˜·žxà?Bäo$ÏOK:T¸«1p»AIYG¬åö¥ý/þ5H>·ûäD'r²òö£l<>¨€OãžÝÑÁ…ôýÔCYbmHq†ŠföjZmâá‚éÅçcË/ž›Jyðó.~5Ø냃}]ËÊFséÿÈ-ý_ý¹?7§Vû‡`@s‹>ñ"W|ã;³‘ûžƒ”OlâtQ©¶·AåíñÜ}cÜ�£vÅïG–ºí$¾!6Æ&Óm -šP@2ðØN<$éUÀ>þøãTùÌ9Ÿ‡Náç’º:´œÓNœ‡¨n“¦hOcþÅA¨vI;ÇŸNHÓ—é4{;.¹‹vB´ -,ªOçÉ|ÏøÏ}ØÞŸ—öbr“¦Þ’UÀÝÅè;=,í(òöù†(IÅŸB._vÄ¾È -œGM%^\6æ¨l¿ièÎ5H;Œy‘{†S—;ìDK×^“=[ ôkGòÓèaÿµ¢S#€ÂÜøÚñ]Ï%þØÚþ:ø=¿ƒ6õ¥$÷3š´€¯¾ßýXAÆGŸY2©‚äs['¨ÃÚ::DYs™¼S¼uÿÙ„=˜Á—o—ð3—à ê@½=\à=ÖôNÁëôÎù—Œ…³~–§ È™*ƒÎ&˜~,c+ë£xŠjæð$kÀ’yó1EÉþN½O’øoƒˆk.`i°®Xqeý«lc¯ã æV¬Rw«™Ðß° •&!§²w/Å£ …’±ãŽ?EE±7“GsF-È`— c)â~0i0”E5Äl˜ìuˆÄŒ¦ÜJ·ù_lj"œÌƒ§]zí<|¾QI;Š\ .l6"=÷9”D0(j*_kgè¿â˜HR5åSÑwŒbñ{f[_äÛ4ÃÞJ2ð\Ó?ÇŸ“ £dx$wMã·¨”âšjºõ"šdš«Y:ö‡xor% -§äZv§¦ªºþ0*Ju°Ðo¢¼‹zr#À=FX^Q4Þ‡®¿ozÖݯüÒÛ:.ON‹F+³ -}È„x°0¬}Êï8¾òŒÑ"ûÿek&pšù+–£+@Ï©!ÿ›@»=9ÆfGŒ…]Ÿ®é?MfGÖ,öèu#mÈ]wûWÊSv= E´ŽKjC˜Lï9ŸF¿s¾^·ö%nÕ)í®¬Á<>—A V‚©s5/››³YîS(N¹¢Ä»büsu4˜þ<Í™‹�õÙ~ïæô±B;okz÷óq¾Îó|ìç¼Ü<••Ž´É98Öpi-IÅ{)7°fy´ÄWVûTR$Aˆ3p%Q”ܳÉt¯Är›LŽßÒQD)³Î\ É˵N¯ÌH-šžêX»6‘²8µRà¨MÓ¨Y}áÒêiOìîw‡M.U€L{”ÊzhNž¯…_–ÑŒeEEéÁ]-¢n¡¸ohÜV‰4feuuö`$²�²ó3U‰{R‹Ãj†¾ŽwzÜöÇyÎ7·Çq©‘q»8ë§qfÔV¯A'ó!�›ñÇܤÞr¨Û+ç{nB}qHÕóQ•þF§÷2W…ÖÇïbuËÚŽb†¿®Æ?–\ŽëX“¹�žIä7Ï|j¿¥Ù«)Þº Tï…*8<Ï:ð™H÷ß{ýžÀØúDw¾[·&š·`ÈËAIiuÊ„ÓzžÖÑ%‡„1ò®·¢ðw‘ÈòvÑIÑÍø®Ýé.„WľÉCXŽ\!pmô3Ð[kËdÈýÓ�±Ð€ >lÔÆ^ÀßïE¾=ßdùÑšLø"F Ÿg€�b£4ê¦Ôð€ë8ì ÐvõÂ'`´_÷ðÆà¶TÊ x߬‹¤¢^‚µF½ÖšqÓäH¿4< -',e°*ÀT%t8_ÎÐN r[bÕÊ\›nnXˆG¸?˜œñ†c J1FŸæù pècÆ'÷ÁÿþÉ:w%¼±ïQƒK¯GDE((2££zö÷™dŸµYÎô -Z;vÅcý@PaŒRÃ:ø¼WÃáÍ”ÏG0!ïûÆ‹ÔvëC:Ýœ¥WÛBéû‰¬B•`N^y²ïÜ“¶·t=Ù¦2|�š6²_ÐHo>ÜÛy9Á8íõÊ"›�_hï`@/®›!Ån/‰àX¤ŽÀxºÝoIܯxBô«ÐëGÂ?ŠEŸ_( ù^äÌiuç`¦¨…™¯“اÕà¨×Äp¾ÃF6¶ÞÎuÈíÞ,Šà‘¤¬=ð´;t™ÓDÝ…y*á)RÚ‹�Î/ÿ,k³¡„Èjíqx…þwÝ]æ²À-ÁþŽ)55ÁÃÊð³ºe»MüñfôÝ7½““~×—Çý"kÐóyκü›‚½ñݵ¥®ÜCù½ ¨>]Ò—Iðn[“¦ÒÓTPBÎ52¥xâ¢3îœû—€½t²vy;ÈM~cQÚŸð¸€lÖRÔ([ärÑ¡�™B1«ÛÀrCŒ;ðßyKBy®„Îÿôú.Á‚²9sB$4@æÀÄ[ÆÎdÆx1RíM’ÝÔdžÎ&ÖJ:wˆó· -‹-û=Š‡9x`ýò‘IÃÊ?f©‘ýæ l§O€xÉ›‡Í¿ Ké:Öå®Ä#¯¾Üѱao"¡.†Ö˜N¹§¢`†DªMæ-Dºì\8ž'UÓˆkÙ‘ûÙLª‘r m`Ÿëqduó…Ù5kÅŠŽ’Îê&ºÇ-°¾Që²»”åMèPù<t=Ú`SŠÍGá²%éH¬:>ÌRg`Œð˜˜}Ã1pÝÀ¥ŽfÆ|»ý=²ÓWôÑý—ðE4wº_áÝ‘ÝÙ4]#0–a”˜gOv` /m«‰‰åÑB»9§…ßC£Ï¹bUÏ4b=ëÅì(öét%~Eп[ÇÀßÎË–>¼·ÃªV+ÛÝËîÝ.[ìZ”¡A»É,CG¨å¸çÇ{½ä ñ-A®´îK8½Ûvšm8×ï`O^?÷³çå$ÏÓNÎòÿÑ}B²$wKÒ (YL*$ô–%·O%˜ÓéTZ[—[.—ÖÕzâÕ54+þ'Š�ûІ|´Â d¶MýÁ†£qËÇi `ÃỪ”…™luFæÌv¡JÐõkø,o�yFz OÍmð„VÁkû>€ŸwMØd^D{3èrs^ ˜RÔK°j -0Øûm–ç]¡úéÝCQgfÚ¼…Óöt+UªÆ6",îÑÌ!”±bàè¹ü½W–W[YoRt};Ë[î]ÇþÕø…¥${5“ù=Ò!¯£ÐÑ`³èO $(ß³gëTgËF´·`’M@.üä<§±L2sa‰¯/®JŸe.q:å…Áº,8/¨zvÇÒ…æß;‹Ó¿'H -!À^ä¥ß°XãA\ÿ-àÃQ{ìïaLrŒñ¦?îƼ¾³—x÷/¢%øH°{ç, Èž*‚ÅŒ‚Ca=o÷÷0À5´òEÆ\xRÀÈ÷¶Â}ά‚ò^!¼Ì¯ûK'\zQù¬UÎs3°¶·Ô -F¶ûD}>£«k ¬ôÊ×þÅUUC@5ævÎpyù¹IUJƒB÷²™aÈ݆·úÑÕø»Œ]uÑ÷g±¯9ìÂ] Ž¸“Ñ•ozø[Lš æÂà~ɨ͟8áö¡üß´¶ñ@‡èƒ‘–�ºÌ2µ$‰ÇæØÿ}D?œ%ª$B¢›Ö`ª<£gÊá�MËtÏÉÛ %òJ\ ½ |ËöpO8§Ò¢Y7hœþ-ÇŒÅöÑïW´_§9QÔ@Ù -ž®Èœ±}ŽÿÜù~œ¾ØÏD`}¿áø㺤?ÅF~þÿ@›äËý‘ž³ò½äørIu9ŠÉÀyù"¢ ´{¼"7C@õ9çtþiãKpÝÐœ1ŶÌXkoi©š™8zRtæ°®'Ò63¬èY‚”Ïp¦p£MËdÒŠº™r{Ö´lX›¾û9±1™J.¢x¡œMlÍèl¢¡\ž\ f‹ïj;AÀû5•ùBæ×ñ?4²è^(YöÀ¨´ ¤z„ÊÄ+ò†ÉwÀë;ø¿Å@,„è´ù†·Ú;–[&Ç&>†Þ‹g7릯š÷qõnŠ?ƒvÙ7x+CöƒCÿÏ6ûÿ °Àß…2ÇNn¢ýôMË ßHUQ/$îžKkéÿ�Êã�ò¼ŸíH%/݇YÊH%à?똮ëŠ*Ù7^~ôQtLS§Sü(Ê} -a|5è×f³ÖÏ/*Ë#ÕÞÿöœ¿+/Çd¼ò:UÈ‘ÆÑýõÓx*ó=¤}~[Û -òb33S"»dÏÜ÷Jü2-Öº¶\4Bo,* -ó<},Dý@¥bc)9Œ|8ÒÄÌ e•S*q.gà&<»¬i>¡˜Ö1D7"¡$bÑ?“eóW"m®†Ñ‡ä÷d<m¼¢©Ú·i;óïÝ öLŠ©"¹ ¿lY“AT‚îßcXÔ¶/=ÓUhf"ÛRõ—;{l(Ý×—² w†ƒ6sQ‘û@\É{]S¬‡àñ¬dÕþƒÙÚ*¬›%I¾Ò)åOlZµŠ.Ž¯±_OeÞY0·!Çu|$7Í?¢bR^ÌîŸy²Ì9cF.$wOxJ¸ÚHe¾UV=µ©ôB‘+|ÑÆ+ë§](Ò2´–¢=×Í2^|aì>ò<ÑM -0|…Æ)ø¼ï^ï;ƒo4)³[`„Û$)*C OccÇÌ=ë"¡w>wúÈ!’c©}´ö6¢²¬³èžÜn÷?mßÎ=ÖùüùŸ^æÌ™Ã÷ÒbuÆÈÚÞ¶^‹µP·!µ¨¢hÅ„<O¶´|:ôUbWò<{ -~–/¦ye—°(N´PÞµ'’<À*Ôõ&ƒÎSïa-sP/ô$ð§1#C ZŽ’zŽš¦ËxÃ.øÖ§¢qÐIpóÁ - ÊÓÞXbðÚ‰%¤r››,ં®VøÖëº7—›<3‚¤�–Ç[³¦R#ÑݯûÀÙ.F6’UTõ@©o[-å3ÖÄ£p44”ÇRTjKU~©3»{!¡„c•´¹ ¹Ôk:±+I¶:¼ýÕyè[ug0ýÓÒœ÷nhÖ•@ÖeHËw³¼ù;vÏóøcÀ\æ®{ 6ÜYeüœrqšÚËô]2ÅM bccBüÆe‚*ÙY¯Ý![œ¬©*Ln/¼ˆäÛ]Á º%^îÛ<šf7÷e†ÿkymmàk/¿+ù"†È 9Ež¾Ù¢^¥ô� ÕfÕ×C²,ð¡dïþææ‘ 7¢¹c¥¹zõƒ±E_¼ï¤p&#ê˜ôu Êù9ép¼QK<‡§F~Ž0ñ+Hö—¢€š¸�'ÖŠ&Ï{©ôs—L,HâPäSM+›ÈKnKÄh¯Z@´Ì©Ähüæ‹®\J„|€ˆ”úç]ê¿tÅWjÑ\à¦E9b£D'Õú‚•”/¾µ#ò�<(Ú©Úø°fÊ~èí%šïº.`Yš‰(âŒ]îÖkIª¹ùÒÿãèæôY~8þÅ%ÒFbóÂr|½Œ5nVá.™U7™ëRø¦É| -�yÀ+)téòÀ¢`YË8™ª}†Ä!]óÎÆKBÐB§’AiѨ¹M8XteÅFÉþÊ0M ÄO -NwŠ†Ç¸)óßÂŽ¼s–§ßEtz5õ*ŒÅIgS[.sìË„ðÜÏ~˜~þ§ï ¡ à0UV‚¨Äˆaa¬¶¬«g@¥Ø -‰|ÅàoT‚Ri/RÝ.¯Üh@£G„Èè`œZß}¢rQþ[¬eGíLÏO -'ö×Ã-CvÑëž…bÐ%‚ÏY®3FQÒ}hª<Tâå”ãX¹#¾¼ÐqÖC”é½-ÔçíO„BTts:9&1äEž¢+yú¼ñ!»ÒŠAÅ°žçÜ\+Ã<êìg®ókÂÈ'ÞësPǯN&ÿ�²�R€z×6 $E›Ág@róË3ŸéÇÝå•Ó:a¼.GXÕ™Þ0$ç탡½Ð?ªÃ›}Ü6X�ÃÚJŠFÅm²ìR ãój3Úp›ˆ¡÷žmìÉ9Ü…ù ²§Ž³ “y¢@Y.È<£jÚk[©Må…j:¶ „HhÃTPJž Rýß1¨¿âé[€Q™6ÿ†ôøeÕžÆÖ×™„…r¶nOŸÁOÞv µq÷ß[iþ3ú‚“¥nqrÌt -|Êð~ŸÃŸŽáÿ±çç«¿,©Ïà?ˆºØ%ðv‘†RVvZñÔ†)µcÄF¹±¥ÐU×PÁ¬Œpòy¯Xj¦HIªO, cß¹›·»F&"O|+"À\ûh¤§DZ‹Jw 'º¡ÕÐvî®ô’/ÌJ*ƒJæXK$µ¿/–È«¥ê( FU ->é -ÆÔ¹ÊÜž?ºÿˆßŒy‚Æ ‰Íj)oÙèÕ¨Šf7F‚wˆf*ƒ¹¨ ƒçAKëFÈ Ì,ë”(© âöŽÞÀhGÈžW•æQɇ>¸m”RüË×ßâtýûˆÖêq µjh»¤Fþtî™Dí*š:÷ÅÞd‚áÝÂsD\Gˆþe1Õ?´øŸvËýpŸ]A€#@ ¡ª@:³Þž”7t±Í6DÈ\~¸ûÍèfvàÆOfLfŽ©[K¡#µ3Qîó*KçAbœ–‚Ö¹^QËny¹¨(—Ÿ%© W‘ŒvSþ‡˜÷Vôq(Ë^¥Š» ]ôSïøþG/©‹FAó>nƒD||¾ÇÓ ÃKªµ£s‹ò¹óË‘+¾9Ø'?r&{+çä¾Ë²ü—tŬkkÏ2âãv"ÿõpæÎàN�ã.¥F$ç·¹+T´o$9 苲›7Ç)N{(5düj¾¢9£Bˆ+ÜÓ/,rÀ³U‡?hQa´µ�ÞõK-‘y“MðY‡#f×Ëʪ¢ "·QòeÎY³<$l‚!ù-Täòˆ‚D‰»l:îc)ñ,J_ÅCwÆçÞ¢ƒçàÖÂü´ÿB?D{ò_AÖ¶gõ¼Ò1¦gó›À@SÏ`�¤Í”AÇys:¯"x¾FRéåü©”n[ŒÙðäM79ÈqÉö‰JO7R!k3#÷¯Þ¤ôO‚.2@h÷€îtøJ&Þb÷ÆtÊøYgâß4»ïß{Ú½84–ÈëÝ‹ÚâŠI¬ßsÓ†ÎÁ -ì&°'½Äs„¥bxÏòtvööúP±ý<¶±ƒ"¨`VÝÅNDñõ&‰âSÛ‡òVbÌRjŠªrÈf¹Mš ”‡|¸'†þSmÅõ}žÎ�¿'B„\ë‡}#÷J1S‡DƒÉ·LåT&ËwgŒä[ðzs÷™æè~È)©;Eé×#í¯èRür\«€f¹bÚHd²¢r—îÙ»wÌÈnÎfuŠ085jöðÁï¤$Þï1!<RCEHÎW’ù�¢ÃCˆFÿF±Ô'HôyîñÑ*º…Q(K‰ÙŒ¾[ÔM‰ŸÓ -Ïýì‡éçºÉâhwVEeP3…(È’\.ÜIºŒšN:‹¦+,(…m"w½xpÒ(Pï³BÌÛ‹à…a„ˆ©kX�¥w¶ Aòæ´ŽLoË ^9g|³U…à -ù!`™õÀ¡¶#L:,*°¦&(h¤3éZx‘³’½ýÓwýäTï¿/ &ÒsQù/DEá¿Ç.p§3!AÚa2„sózfKtº9€œã¥�&É@K‹î2v -÷$§Û½ï{w'ϼp‚,H·¡÷ÍL()(ëO<½uIwQZzrk`´¯BÁJa±UyW µÌË®B_söø¡,[V6Mã´z€2&Óc€Ãâ3#éSô½“ö çA¶6ä©A¥w¹J[ÿ†TÂ%°Ð›‘÷Þóœ™.Ì‚Œ˜ÎõµW/¬+©ïKD#Û,»¹•ýܘ’ý466ZÁh|(iÑWÀhÆ-„Ql9å– ùÎp¦71H󌂲Ul¯ -ØS«Èqo»CÉ!“e¯AêÁ1,±×<þ/:¥CÅg@Œj¯¬L*qe*%*û^ͽÑô—àU”xùP˜j¯uq‘J|ŸÌ£ÜÑÆýs"k.)æNÐBnZâYEÕßdxz?¼·^ñLà“(]LÊ ±ö#ƒr -…²ìàU%¥hªÝ5ùdU^Æo'Ú²èn´©=èp;k±wÈýÛˆÊ8Pˆ.Šb -”Ïùþœtn±úƒç„¨º<ˆ¦î‡º4«Ù&zw›…b«‘‰ ,Ÿ·€¢h™sV>WìÌ©©hxh˜¿ -M(YŽµEJüÿ¨Îp¾«-’òª”9Ý4gMuê!5ÀöKn=õ<麜°ÏÇ©Yõ§¼À^`²à ¡è‹àU.0dá¹|p½Œÿì‡éý?íù.žèw—:Teh—Ô}{~ii–ÜÑ‹Ì_\$²ÎÏI6áóð\kPèÐy$ÒZ"ŸÙ -þÚëîýz£Ê›v–cÃÛr�^Õ)W{Çrˆélv>>:ÑÒ:Ø¥û¢†~_rdø^…Õ¾„|ÖtÃØ„î¬|†åo±ž÷w6²Ržë “¿Úö.M3÷ÉV2 æ§Mš¦ü<+šÿ -2ߦÄþ¨&½³ä$”LyžUcú/ M]Cµ¦ÅþªÆ9“EFú:ŽqPùèF»žç7¡D ¾ÏÌ'© Ýü#eŠ -50ÓÜ�ÑÆBQ46îÈß]B¦¾½#Pù£¥*صtÒHŸÄ8žX[HHEÆŸFî}oOlŠÎrßą͉e³- ú&åá¿Žü0´oc¢¾ÇòXáhC°Éšë£â—ÕÇ5$ f÷c}~â9“uìÈïr`-ŒnÞ¯:Ó·PÁ¼&xNaG~¹àLÆTŸhäØÞr'€TŠ†ïrMGVÈq÷�Ì·P£™‚óèÿ›W² pW…B‘†"ÞÍy|æžWûBuøZîÂÇÏ%rÕBcˆÕ -8ïýX~Ÿ3Ï„"Úm6Ï*à´ï†ú—�ƒ1‡»e0stŽZZæXBSTcÄ^^æ…Ë€¶q@žƒEVÖûhCÍÔt‡á:jš"Lº�ùÞ±çz ëhÑÕ³Šníz³ÀºÒ½¢€J5ßžQU7?£ƒðº,‹ -ñèí"ÞgF³ºAo§ÓçhlŸ£Ÿy?_b}ݨèBíœ*éé~̆Räð;#ßuÖ¡÷¯]ú:æ¢áW(CMŒcè¢"ÝùáÝÎÇ€æäLH¨Hˆsրݬ|ÁǦ7÷D&ïQ‹çð@šPøaMàâþ3 º-µ£äMò±Ópão¶ÉÒµlŠ ƒÑË&O £m,¤Y~~°t•ùS_¸÷à²Rß’É5½üñ¼#TâIxÁßÄR¸vjŽ[”ŠPÔ›DéŠú’g[†ªÎU3ÈNZºû¥©°íá7ó -¼+Žz†P€T—:b§sÒ2ãïœEºðæÖú↮ý YEŽ]|î·t]m³”ú5¤0¶À[\lªæí F/´˜6‡qÛ#üŸ²j®›áñÄVœiˆ˜ëó% %~Ø0´°’È®×nŸn:¬"ÉGÎËdˆJY7¡œ‚k/+õ[1ß¾åbùOL¯ŒfŠÜ©”òÝ$Ãê~ùé’ö9s9o·6—튪±äÑÉß¡7÷,Z7<IDlÆÅи´È‡Û«he0¤mõÈÎãîáƒô¥@0TÂ`F�=;ÔÉ+PN]ͱ™?\>ßñ÷{?Ð6èÂLpA¸ô�|ì@ÃÅõ‚ôåý¡ ¢ÙÊÂyñýÜ<Ý°adf" ×›;t½‡,&ËACº'gy©Ø•þ´à{èŽftFM”(ÅÛÑB Е:PFP]ÕàùÒ¤áHFUq:sÜÛñцšÑ¬ÜÇf‡F1§;²ÛB9î¾ÜâEÈ$ÉýãÖWÚ[Ifü½p)Y`ßAñ nfäböEºFçJîc6‘;'×Éñ_}ßß;ôÀ®¸ -mÙ¤H+½ -üõÒñ¡*b7ÆŸ�ŠöÈ8Y[Cic7²Ùø݆l ÜÙ³±ñßù0¼WþäŸ=©B°›™–úe@Ð̵ã!¨Ö5ã˜Á¨[˜´˜4r¯ìÖþáaå¥þ~ܳ%?Œ†‚ËÑžTƒQÕ¤4O/Rz§7äuðpƒ£”Ž·64äBQüÑ¢šH^«H‡wt²ð’%›Ð±³ÌGNŒøØÍÔª^‘ŒúÆŠÆ>–h½ž:Ù÷pÎÀrÜî” <#NQ3Ú‹¾¤'5züs^Å#ëžÎ¼ú“¿4h¡†1͉‚ðk#™zn0“)r$m‚\ŒÞ…óB_ÚY½uèZ¿ÈéÔÝrž˜SyšÈ}Ü~‘a-ÆØÍU凨ª|#§ -ì¢ -JüÈ-·Œ¢ n_¼Ø«Úy½0ôijÈ讀౮® tä´0™çl’Ç%dS© -S£®P"ùÔóøž“lýŒ¿I<óe<Œž¡n—B¡fLà~üy]tªŒÛ&Èå"«Ñ7Í„¡ñgRÏ[OKtk[ªÍO¶ �#<vûÆ}“¢uvÅÛÑfAÛìbwògHAýµLbÙû±H-·ñ2Z•‰´½m|³í„oè kS_L7h¥¬„e}4½zÄËã Š¶ªùžÂ1Ì€ŽŽéê«iœÝm·T‘ÊGÀpÆøE§ðµyHïx5Žæ@Ìê#¼}ÊÜ«.ûØ,DdpêEÔTb˜ãn‘ÌS²0ÍÂJO6’â#¼¸SÊE¾;UÊDßÏ2Š–Fͦ}ﬢì²Ü§NÿƒVAU¤‡EK\fÝ:–Zºô]i q?ïoƒ‘]vÉ_ÏqÌtð³�ŸmH6ñäú£½?ÂÁÆjÄ·FSóPU\*Óæajyà• <Ý~ÞOê<îð¼_–eÓvnWFQžJÐ~jƒLφói»A‘I \>µA…„·7kÜ…4T��at“¼¥G!Uÿw6ðú#`78óòÅñtË`vhÚóg,„FäS"4¼°(–AD$¤L¢ïŒá&mæÍwë"Z`¯w Éæ§Ol¼q„³BèÑ#1”@þ´Ù(õì…ç}–÷¶3v©*U*rRõô¥× &Y³•ÏîB_lcRæüË«¨à^Ê¿OLþ9èïs<{o²ogÔÔ8ìÜúéRôo,FM]\ëÈM&x£èO‘ò‹ý¬É]m¨Ùò‚Ši÷›oYŽ‡ eAò£^‡,†Ëû¼æ¶¾°šæ"ÂÔ#ØÚjâÜ™ ¨L&cÍF>;f õ‘î.ÉY²Aí¤MQóÖï„.kjŠmë‹>ƒ|9ÐÕ4ËÎO(ÜåP›~©cãÙ›àñžìƵ¿I_¶â¾gA·‘Ü®´Ók"F?© -eî+w0ºŸíÄÿcÇÅ{ LÂ.ŸÃ·�yÙ¥@ÖI2J§4}JÓ/°Îì²"êpËPdµÇ‘¿NW̓ðìÿ›:utq�ƒÚ…Üå ,ëê|kྠo· hZ&íôùQ`Å<_y5eha>u!s]Ïž¦=NZËÜ÷óKÝ¢K¨ê1¬fqè|ÝÁÝ€ïW’3ühn«Ag¡ÊŒÃ¾ØåSš'·ÿ]ól][2pÉŠ/•#G_›I3y5y~Ô‚©gÛ“ñÁ•u6nSÚöÂÊ”ieë*¦\dÇŸ>Pä�Áô¾5]Ž6ñ"ݤ—ÀápùNCvLù¼=zªd~%Rø[B[¥pÒÀ€hþ6æpt~æÃøúOÜν¨f=çcGT^'¨½ª¢oGÅõÂSŽ$rLÀìo—»¥ìŽL†½Ôq¨÷ « o;7Í]áji°rÜäéKªˆL©ËâÏCˆä5ì¢tó´¹Rw4âîÊЋ.‘Xêø»ÝF:ýä•^gÎxw§Wþ úZŸÀÌòp–¤}r°k¨18+Ä"ס…ú‡†JÚB+n<Õ&1¼tŠþ˜Às·á:>k°l£`1[RJx?®%‚}WFÕŽxêèñ‘¡L!/(–¯ê0:Ýžbº3çÔå“ââYƈst#â” Å(•¼âB4Š¬Àµ|ÜSÀ¿PiYjHQöv0=Ú£ë6ïE`GAø .bŠªaQi¡e&NdË<²T敼Ù^eÚËoºïÞ@8ÆKûÚ*Š‹µ2ŸO½ìОŸÛ_ÈÂdJ¦ûÿôÀ#™â@ÈmóT#Ï6M`€FsùŒH÷;D¾ÑÉ°»¢' %9_â¶$Ãí¥‘@ûôR1Ù‰|àæ¸,ò)wŠæï°ñFY{p¤íÁîvåãgŠ#$÷î.(^Fóì*8*%ðÚ!Oçá~^ˆlÇ,AMµït˧5“‘uö/cG]–x·}l\Ê£;•Œår%r¶K¾�EÓi‚]®§4}¼KYʸêAU(Xþ%› -¬'Å×U«À4Ç.:Dçu–Sê¥Á'Ήy ×TëPE/×|™=å‚šiÖ. ¨iÅÇú=|¨rÜóCîÙ[§†Pæ½M¢Å¯À¹ Ïâ¥x.F»ûbt‡Í”y[äH*ÖUã@çõ™ IdÉ.ÌÁf~ïFšþ/sxô{ÿì+qšÀJ'‘‚kW$³”||ڛѤ…ÔµY ì2Sç’÷?N,ÆK)šV×Ia{%.‘¸o°wðÃzoÏ«ÙøA ïI»£A»Ë.ø,سš}þ];Ûè@eÁr<8=%¸©é4#IÐ{‘‡ -%_}<6pjAy91žc† -ý„.c0¾Ú[Ÿ„ŠáG^_/R©Ã`Ë1‰ê¡iÃW¾/S®ßOEAœÇºžá0ª]e™ÒËšLEýÉ„ŸòÙļBß ac“Œ|öïm$¿´{(qNó‹;ÛÎt[åhù·™`ÃÚÙ¯6ÂCÇ7šýÝÈîç9Ù/|J&£þM+÷µ7Kœ{]{]¾@OåM7?Ž<ò|ø©HÒC(šÅÐZ;!|„Ö›Ö—ûŽ®ÄCµm0þ1†b~lÒæ$J™Ô/ÒH(ȉ¢õ^HãûJ²Ûš1õgc -õ6`b¹…i×@l¶)èžxŸ¾ýŠ3Ò,[£M€£=H !¸©õ^ý&Ècö‰;JKIÊæ¬à¥íNgñ`\,˜ƒ—БdjêÝèšÚì-^\ärÏdë@¤Ü.pkÒèŠàxs5çNÐN£èbé«q[M5~+-ûl”®ýð¹?ÇÓÏÌ“±c®]Û*,¨XõÝ{àÝ–¢Êû~: ˆÔ68Àxƒ:c|uÓ#%…#òexønw1w½ÀSÑÆ}3“ÒקífT•¤"÷cçF:cü½i¹´æi¸ÈÆ ”¹]Ü‹äèKj¿ -ɯaA7ÑÆÂۛB„Á‚º‘„ ¦Åò_îÙ(ƒîtºŸ¡7D.Bôy"«¥¿ó%¢Pó~ ‰l;×’éSjJ»Øð%€lìEd€2-O溦σãÏ£$ëYÚQ½�Zh§&Á´°ºïåg“ûŠ�{ÿùÃûóžv Ù/pþ·øü°ve£ ¥°*/a¼B(Þ®›[T9¾kÁšÈj(NZð7÷BšNìéÿBS©88?qJ$ÍàZk€~À£mBÚxœy/¾öðę̂�ä`Š*°sH~°ñ:ëéìÖ»ÕŒÏXRønÃKT2.+š,_TŸ—óPÉ„%m ôË€Nøऽ‚.ý> % åJöRþZ³»ÔÕQ1¹Ò_ÂHÝÍÒ[T&ùƒn3½é••ï™f }ÇȈ3š6òköiàîý¦ûB‰cÁuCO+Ú¢ÀlJÅ"a·7o9.p /ñ®…§Of¸ïä_IÝo—¾žj)Ðvix’M8ôdICéŒt˜Üð[c¢@1s}‚þ]\×säºvZZWK]8õt(Iª<�÷#£aÀ3Ń˄ FßÄiì+]j6EÒçn›7röXK³ÅëJ»šù>*–[Ð2WªõG…ÑܯÀÒ³qZ ð`~˜°ÒÍMÕÞè/O¥¼Áôå5bì0´·üpËΣSkg®lÑzo{ÊÌåËÔ'ZZþVæ)êBÏúå÷É5 `3–– ÿ �}O/®ö_tÆð’sæÊ!0Ä° 5Ê”±„v°<¦@gæèjk£�Nÿ(oÄáñ»òJ²š2IÕ…TÜ&-êM\SjBñôÙš7cA„P3»†6²›3<Ûã—丒Lðx.+9ûm©Ÿuן<@Q§åšðpZynÆîH+˜7HηQ`Ù£´u˜%Bj8ðhý˜ÎÕ¦f¶A�÷MÌÁum}¥KkKzGï™`‰ôyÝY<DÈúÒœD‡S9“[ÆÀçþÿa|”èHDŽ0uúêA~Ãàáâ‰3CÑLè½02pÄsbЛçe³/ƒõêtY`tHO¿Ïi5<Ý+ÃáL…Ù€¼ä¬hõ—dL&Wv½ÜûÆKÝ£žÎéoª<DÖu‡ä¼Š²´× -J+»@Hóž+÷Mg{#´qéö‘‘-º&ªSòªê@¸r$PèpZš6Q7ôÁd,š\ÜPƹDWRn“Àä&äÙ] -FéGÇÕp‘>¾W÷ÝówZ tj[¨NËòóœwàsóEžÉÃV-ëWZÒx-c†z£•õéë‹=æžÇ ùæ`¤¿ãš‚‰0ŒE*ØÈÇþ¼G°l]�¤E=iEÓ¢n_–ø7¦ºÆ¹!x s(ïžáŸ¸c3xÏÒ³[/˺ܨ¯!tÏÚ‹îM(S¾5èVW¹+q1å7²09|ÅÅ<o³´°$’àÚùTêM‡Sœ ->‡B‡Èm5 ?þ»ú²1XÐàaæLÞÈäT°vŸ)zÚwÇ¡`²Â3v{0¨½ÔäBÁЕóXÏž 9—n[Ü°í…ìçó’H¿À$’rʦU:쥧¸È¢wvZ)ˆåÚG÷àþ„t†éãÑ¿lqt £f3Ô>÷8µŸóœ"ÕP‹@Çø¬GdFtU¿21+¤òÝz2ŽR”[~*8¨ßwôäÅ©#kÅË—›«!Ñ Ë©®2o¬ÉñqÜîX±9ödo'}pze9¿dœ¾¾óøgf6…,N(µóú«…=H‰æ)à1mY³ñœ:ÊAê�?½O†¯75HðÌt–Š ”á‚ßq?Í©lZûrˆˆÑ~h^ßÏ—ôTïêK†„̓fFé4D¶Q~“æ÷PŠraïèxyE ñXŠœoü¼½›ºÂHÈ%2v_’ŠvfGÃΕÄ;§(ZQw0À>NÃ-bLRMe¹úÁÕÉ +WŽ=pÄ“½°z - ·¤-•Õ¬Îdí鮟ÕhËE‘B#F}ô<âc„xoH2†›?¢Pß“C‰n*ÏèLož·rmÎðž~°Y/=7¥*YÊÜŒò×ït-³ÎõÜóä£Ç‘,A°öÐæ.BH¯š*Äÿ,æ@*’Þfk(8y5åÏyääèG ¯Y¹2²œòäïè~ˆÚ‡·Á½|Fà3–. FÀѲ®RÅM‰w¨´y e°_C*¡)Æš€`Œ¦stŒÁÔ³iEæϬ¥¼ÓÙ£örg"ÞÚÊÄ×1g™¼m¨oú]¥ÓA±0–ˆ¯ÍsIш¡^Xèwº¡œJ‡O:&T÷7¡�wõèÎñ -!Ë~´„«€.‰Œ¼ÞÜ™è<12bÔøÊ|‡”ñNi©btq+à»?dW„ègÓ@¢f ¹Xà؉‚�¾“Š²nG¢¦¹Œ¼Ðб—â˧,›ä{¹møHÖr×=6\R’õPH$JX*f8,ø8Æ–ÑÔ^## Îí±ŸRŒxÏ3;Ìäµ9Á`æW¯vÓWÜí�ôƒÂTà¶v¼öZ„ð©P°¸dC›¹…|¼iú¦ãy Üñ$/ŒR•þñøqç${Q=_à— ¥‹ŠÞ³P}¾Çj Îo]âE=›5l.,äÅž”ÙšªsÜ'`ê qRŽtÜn’u¶Ä´Õ“fŒuFw×V¨•¡PȦ0âS-oôõázÏ*žŸïóýG÷÷¾ä| ½YV÷-Vè9ŽFˆ(m%ý™ßU:þ¡Ú4¾Bføß�aÃêF&¡ JYËè´Q\ŸÉÓzSÓ`9K'ÕÁìxf#â…ÕM˜ƒªWÛóÿgˆÜòàZŽ~%úûÔ€Ú¨¦Ì>>¡´G"áJb*ˆsFÌ -ÿÉ& 2x›¡Ô ¹í/ ÿ£ -9R0þkÏP.Ï#|âÑ¡Û>öéÕ€±ÜÁÆgÓYã7•NoogtÈ#J¶û‘òp¥3é¯8¸‘åhÄÀ®Æ:Ùï¹)<1u³?'ФežMAö{�⇠«IãWW×ç%È÷H(™ælU;?üòÏhçîsÜÏ$ñ;xç4,ú¡möÉ¥ù¿Y -ykØÔõz‰ûù¦3×=ú]’“5Ò™R&Ï~Tß¿?¢šŽ±µ•zKkƒÓ·Öùcæ_v¶•r~×Oì³Y©=©ŒâV5¡Â$QCHוwá±6v¢À#x]ÒVÜÒ»fÍcg_¸HOê3ÕkÑ÷"äÁc–¦/€ é-Óx¸Üow‚Ä‘ UF’q¾£,¿“ì<ho¿w><|4Â@M3Ý/QքƆN&¤ÚÆ£ïÚp<À£×ö^¯ûRKâ\úàoš›ƒÿÝgßëû¶ßµƒ DnÄPô ïß Ž4Ô“nC-ñÖ©S²ì]§ñHä?WíõÄ:£cð`úmH²—¡~ðo¨¡2™ÌÒþM®`õ1ô“IFyôh´Ô³…Ü‹âÚ}y¯Mp$ÌÛšš²„ûÃï,&lz<'ÛÓsˆçŒ^¯luÌòž¦=mFT%öÆ]óJ]£u r52¼¾]ºatüÞÏ7[1K’dP0¨æ,gÈ–iO%RÙVä�IH8¶ñœ%íHÏ¥�X1ê'Èÿá±¢˜( Tý¼âßÁèt·l«èÚÖex!ΠÏ̯ÎË2æýQÕúѲe˶#ÁÿW{¬üØ`{–wxÚYÌ(Hi뙄š„U´¶¨É™€žýï ‡r¾YŒÃ–.Ëfžyļ¾ÀÎÐ#+Vh›ÃOB†àÌÇerö§ã{%Q®BY«eŠ‡Ê«Š/ýÂpˆQƒìüÓµ¾z«×DÓ²Á -Èa·�Ú'?e—`8CggÜå0¦ÿžHS¯Í»ö‹OcЈq×F&§Ä4;J£¸Æ\¸Žïã-ˆÑý(Â9"¤"“Aƒm&†Ð#÷·ñ;úÅ–õ}wÍ™3|‰»8ÛŒ‰¼6qra•I³×¥8+>ëÄ@QKˆòûÀF¯u³œh� -Ö„N¢)4¯ÿ,aÓN-¯('âã¶O«ª2ù¿|òh9׿7”þ)Ò?ßåx~/B.‹,øS¬M -eüfÝ£ŸÎ×JË£†v-';ä #™?€\²ÿŠåŸÅõmtI^Bî(Ûá«h˜€[€öÙ<Õ¢omZá\þeœ½ë:ÉÂÜÒõH.nˆÆ\¾œð@5ù(ßûÈm XøŒ(¼É¨“@’Rë&†—\ÛÕ5ÂLÌ{5«ëw2SÒ“é’my -jlv©”£Ãmž@3õŠi>rßÉ �Çé 6S¨pì”鱓fªwçû5P+–Í3šr=Z.Úë与 F—úÉÒùÅ#1kȦž!çÐDœ‰w/ÕŽTKJUðº#'ÄR¡AÒö~ï?²ß÷Åø2ÙWVŸ?U°”Nf?‡ŠüÞ0–¸{gÝ@x/5HSöÀ]}xOTé<¾0lE"¼J”*5‚0 -ÙÀ@¡5F[R¿lRŒÄw�“ZÜrèôÂ/Ïše\ÓÙiï0”OôØfwŽDb&t3³ªs†K3Ö†@×À$C¤Ý–¤6¾ZÙ› öÚŽêûBV§¯Ñˆ;i*xÃ1ß º!óC4Ünº (•X,u -EŒÿô2ž-µg/ÉçV_v™#•É¸¼Ýh˜gA…î“GË›LW××ç&xâ»—;"Ïj–q5O3aµ™¯îŠyCC]™Œ‘¼0îÔ…x˜ -¤"y#n0¢þ³Çïå[70›¡ÄW㯬m~ý +‰‘yÞÇV0í¿Œ§@>bµÐìzȳnvúÃå˹Žþp�o{ÅÚÆJ3¨×œ…,„e¦Ë.K…XÂ+k} è¹=!ph|aÒmñ$¸ëd¾¸õHËQª©)gø±� -y®ü¥¨ÞÍ·è¬ó+sš~‚4Ðá…••oÉvFyF1 ˆÑF¡D3Ä߆òý9ÊÊCÝ1¯"QÓ¾.¡>&Zü\t(j³hž½0¼“6e��@�IDAT¼ŸÈ†¹!%2é€ÁICº^~äÅݺT‚KXƒZ�¯}Í{Uæ÷twr}îÚ¸• ˜ém¸îÄ£ÃqÞ— ·›:JÆXp¨‚@¹œuãg6²ào=ú£Mkð]cr¢Ã‚ž1 -2 --äPn²vó<ÿäò¼ÓXòU“â!Öžê.rõ?´¯…*=}G÷ûA½î}ùÑìÅ·ÞûFŠ2g{œR6ëÓ†µ(¢›qÝkÞká45~*1¦ mÓ²èy -çÛÿAóÿä8çÝøÒ·~á¾ë0£ËPóiUE³‡£4Ï›½™p�:_ïœ1›zc!ÑbÉÊÆÚ<·;5ÜÙ‰\ºÃk©\!ðšµÀ¸N0,¶²2°Wà´[Ð!Cåv‚N4AÙÍ4Ö'3êðʇyMK†¥ì´³LM6.&-½¦;Ñt&ÌÎf‚[$ÖûuÅH&ÅN•¤|žãæ"‡ã¹Þ¤üL¥ƒÐEþ!XúÛhSìØqjø¥LÇèþÈòúwœ¦;@ÑI¦”ç -;$¿¼°²1¯ø’%•ªBÍhç4PS#‘‹[¯÷eûbonΰÐHÐøl㹬ÚÛÓÜ—xOÊD¤[ -¡vq‰û8ÑhÍ üTݘce¬+Ñ€ñÇQXØè(ú9{qV5€öдF×J3.ùË«ÇBá·ûƒN6ÅL×ã6!&Á˜ Іþ[o‘wàÜìëp4*ó)sêS½AÉgRÓº˜Ä±ú0�m%&‰¥�Â÷æŒëÒÚ£0n'‹‹‹Bj¡í23^.{ik^˜û“x‚vÉ~yÍÌï©íûöSS]-h[mU!é;2d”\TŸ½ð쯵7îÓ%v sü¹zÉ'}Ë67|Õ4?dpDHɼf—½OÎË£rÆ^CQíTKË&Ó[‚“¹^£IeîYl÷®uÙf{}KdÒºó"òø¼ºôFP·¢l‹§M¤p´®tÆ×ùÎ%ÿó¯O.úâW¾Áêæ ˜`ó ÌŒ*Ö¤.ª*ÿü_^²mÁwžeË ÊF´Ùÿ™ßHºy0º«¯8EúÈNÙ,æñŒiyõäIá¡!D,ù~8·€“«† VZv^ÓЃÀŸïŒÆ‡ãÝy7¾k00œ–ÎÚò_I¤ ŸÍ¦™S£H’÷$«1¹ËÈUýÎ¥(F™»4§ÜÐPY‰¬…¶Qj¹ãÃÃxˆØôÇz¤R4|¤Vc˜:à<‰ àÀ†JÁxPözs]PŒYÊkL8®³1–Ñ—�¼‚×¥ïc¸»ð¿EBx»-�Ù_ÅȺXâ-ÿ²Ø|ûr@°VSÔÑK{¿DFñÇM¶ËnÉ4PEfËó,ážg„#+)ŸEúrŒmŽ‹—UMã$n)Äû>¨Vy®T‚\ ‡3‘ö°®4Eå‡/Ï>9šÏwûL†~EÔ€aôãâ';Xößá”䡹,ƒ‰ó¼ßiZ»€²çB¬É.øôÞ„d7¦{¨Ð‰´9�Or6Õ>ï䙸Ãàö-ÑjäBY >zxSý}p¨cmSé»qš„/→K„$@X\–ý½W@93‘7´¹[áÚ½ý{µs¼Hk ágÉp½_vlªt"c;zMj*ê=öf:û7…—ÛLú“‚H¯æë§ÿp‡’„j fïÛ|5æÀèwÈB¼/œ\›È�³ÌËÐ÷ƒ°²UÎôl#såõžHœ¤vA&ÔâçYHŒæpX1óÊFðݤùÁ¤ÚŠ?Ç‘ù¶hÑ"R°´Š¹+Í0å:dž-S§vŸË=zNµ×tZ´[F<å_O€µê±¨ P{Ú£'líöÞAY`ø\N4\xŽÑzûÐáVLã¹PÕ%Ÿœxkä¡åR6#‘a»Íîd °ðš›iª©éoH`]õ¤ÑåbÌoćûº{8ŸXåt\cÚÜš‹¶™qE½ã²Ï|é;8ÁÜ¢lˆŒñ®Å8‚¾$‡ƒAìÿ÷Îÿ=Ž|Þïõ×_o®?Då†4»ÍçÆ‚S›¡Ù¾Ö!gçÜRÊD)Šµ3Ëvw“s*ÃGš›3Õ@�Œ=¹8I^ò#i%Ïœ›’«9…ÉÔEµ¶nØlŒd@™Vÿ`'ß„w{ÂL÷j”ÄGºvœ¡I´nÝÌð¼°ÛâS¸!‹xå[VÜ[êänÑ—B¾,ÊñÔt„å÷°å0¼í+m=¤›bù'ÚÂÍÀ•e9PÒêǵÌÊ|»IŠ|5ee¾ÀœÅUœa^BÙO êS:;€×ÀÇö ®‡ -€5žUÇš%è±»›—”¸†ƒ£Äèý¸ -ñÆ¡ð3àžEŽ|QÈîX¡Oýñhd •ñ/Àß´tÔ$djá=ÿZ,kæ'EE‹ìîïÎï){yìp³‹¡>ܯqŠ. -&ß €VÅT×MX/Ý5kVîû£Ç%¯Ä0]]Q íæ's†š/±(2ƲB“ß -lÿËETveѬœÇ;þ{«U) ýinyêeãÜBà"xÆ[_uQ}} -U'@C¡"7|; D÷È'_;›š¶"œÓ‚Óâ¢Â+Ù“ £>¥0)Aç1oÉ¢yäíÁ˜Y‹ÛµôPÜ“ ôvǦ„u-1"1úöv¨tZfUaâ,€JF»e(›G9@ˆQóÔÈìçÚ;0‘‚kôhr¢äA¢~"4Æ#þbç!ü> 7~ -Ž¿ÔÌ{“Ž²ÍwÈ’ TÆîn’Ö;î2hñ‘qØÜ™KgÍàkUvÒ¬“+ª-È'×íÎA>m¶Àÿ¡SÙcëBÒÀQR²Þü$“Š§i-#JTÓ:§Y÷‚êhqýÐöÐ@Ý$ÿæ”…¦Xw©×¥Y5 Z_ÁYü_“š¾1`ÔèIŠNQJ¥1½;Žwí¿«©áC•ï%çy^ÐÄ»zÅ—ËàçC•Á€L-˜¬)ÊÇÒ˜˜ÎÒûl¥®ý³^É”À3¼ÒÒŒ!ws½‹¢„ÎX]Z‹uø í¥‚™´Âã±Z!œZQÊg‚A0Úq/¸—(dןøåêyØÉdÚЛœ&°ú£Ë|-pn߸zµ9íö/Í�É ÖûAƒÍF2ïö)J•ú‹Ð ÇÂ`ãŸÓh5ŒÆ†{á-ÝáߎèÔ¯¯*rŽ :O×Í™ãTíÞOÊŒ0A5´.‘bŸœoF÷Ó!™3(VX «Î×ÃØÙqBk8Í|:¬ˆ{§ÔÊ# <e›?îgà�žŒpyó®*ý!'W3~{ýh¨ÈîáŸFÓÈ|•G�Å¿ªó®Ç›T²0XI8§=Å =#×È÷k“¦†YÕ¸9äf<)ú¬Yý¤³ º‰èŒ5ˆ$okˆJïÉüà7ï‚Û|u{¯|YÔˆë¾8Obœð*u›,>6’J/22Êk-cÂCš3¼dÁ‘ùRá³gºáÆŸûø÷ä¾süÑJ»Â½²¬èÚCàÚ}øµ‡J^ý/_½ä6e–®í•8æ¤N1ežƒŸºé”ÎR™jìA‚€hŽ~"™¶~CzÔ!/ß…!@äÂw3¯_Ê(æ¥GSŠñ;J7þä8£áþÁ¬Ø#é ë1¤§~”(óŸ•Í–„¢ÊgDwØìIs:…Bîö°èP›<ÔY0Èñ×ðQx¿a^ÿiŒM²3[#CVpòà[6nÜh^ö…ûžâY+™M«Öøzq“r÷‰<¿—øê|8O£IacÈŒ¯ sô]ÇüZqáM(\iýÇ÷þ‘<Gï×8yCü&øý õvøDÞ6=+=—”zà˜¹9øfwøß Š -îaªGC«,žûS¸„WCÛ׿©®=Wàù8hO—aεÅìo Þûuîÿè~Ï‹ç{åÊ•#ý–`V¡ -ÆPv¤ªÄŒ*v9A2ÀFC3mp€PħÌþ¡¤$QVyÇÃAWÊ'éRz$¥ˆV¾[t˜iUóŒMfR‚Áó¡t˜¿ò¦{ÐmLåàj˜=™Ò⟇2™WœFtÏÖ®¥´.ϳVž¹aTi)TZ”(:¸gq-Bøõh‰ën*ôŒìŽý -Uó‰Pã¸1Ûɽ±v“3R�²ùGb½,5¯”èÌåŒWžO(S²ÆýàŒ-C?&$L;[Fú¬%áð4/-£bäZK!¯²Ù¬›¾jµ˜æv§ÈËÑ]’ÏnK&å îè?/ŒÀ{ü€þ\¯Äƒ¯S@{ÉWŸ ùȃ²ô‹÷ÙàY#c&k‚µÔÞ§>Étÿ,¿°PîWíF—TR)°Ž×Ö"¬ÁSqñèaÎzm`4—ÂØ®* fÒ³0.'º´Lý”¼5ôŒã¢:S1xªë½HbþÖÞ«Ú…Ö'~™ïf|æKv×ÀáŒR59À»Âš]§ÀÑîùqždϻђepY1ã·s±Ï4°Ÿd9 c¹�W†f±W£Y¦£¬'»i(c»)ó-‘Ðé{6±Í?¿ü³<g¹UŽ¡!£]$Õ°ñÑXÂ!ÂáwÂäë¢@/gE‘QC¿·77pŠxʸ‡V[Pá!Øi)Ë¡Vúù¦±W&e£‡1V›1@c¹ë³ë#ðCAÃÄL¶×³N=Î.±d©ó™;«-²._ñeÜöFÙN÷o*¦b;.ËZüÙ/Õ"zü6ÖµBÕÔÊÌ8Ï";«n"é•Ý5E^ú¾ŽËš#GøoÉPP·a‚ÄÒ››‘l:ó,ƒ~yZÈ?Fº?ɬÙÒîðóåyEs纖——GÆß"» êPÚ>F¬a´Sc:~ø¶ób|S'zNgjœv³PÃÎ0ì'XZ]kÐìEL,vtV¾]'|¬Ðš€VAUçŒ6dä´ˆ )ÃœsØRÓ¦Í&‡¥tÜÔX·•…�¥q ôu|6ÃèNÈcªv&)‡x#žoʺ삉á=õìbÊ5’íK'C ÛÁËåeyeE–dµRÔfÜ@ʤkå´fCõvïpŒÈªÃƒæ¿ƒ¼ò ip¯ »ÛÐ Wo�‡óø-¦†]’¹‰E0îYQ,þ&® yêk½ÙÄs«)gbÔðOuc_dÔ7hí+½²CúÞèåÓ/A®û&ÙÜ,úé6¶i;Øáç Œ|wî…·ÞU”�!ž±ˆ+ˆ~.vÚ駚fÖ8Š¨ð¼÷ÎùíÝ{Bw.xÌí”/Âc#£«/Ý4ÅGàj䄦&R‰‰¿i ¾z0Ó,rßÈÑòCºƒór愾ºòíc=½Q¦+ãE Ö™«Å³vÁ-î]ƒð‡{XÃCªÅÛøG”lèɦúʜѶÖ,'Øßç]6ù°ÙÇúå6Ž|zïÞèUgÐùÎÍu—º -ó)›`Ð-)EÕ‰0^²/£àôðâÞ¹!gâ�jÄ’'P¨€�6£ÑH—‹ý°]€Î+Ô; R&®ä,0Ö†"ƒéüþ“AÚ†à‚>³Æ3R;£ÑlžŒ€œO™Z¿C`žyËa‹Àó!õjû@ô’>à]¡o7”lÉÛ]‰_;<Ž#qß„ƒ€Š)E$çóS:)Õ%¸à�¯Ù;¯NŒÍû8Õ`à3óèe²F! ¡÷Ahî¨h’ñã¡|8�dn£Ž¨¶‚Ûû¯HõmóxY0ä£yê‹‘¬kþþÂøÓÙ Ê=`]aØš¢¤·á½ašã¿óA¿?/Æ—éo2]3@ØõY%[…|cXB½¡É›Sjl›ÙS<@ÕP–ê›(´q|&}r±>¯7¾¡¹y't´ŽZ½ó½¯'N8i"5\ÜÑA¯„E]·n=iéRz1UB¾—[Ɉ‘Àû1Ë÷cñ¯¸ó¾™ÀóÞi)Z9cˆŸßv`ósfÌÃsÉ}Ú_y«á¡ÄªšÉ*mÈ‘Œ ÊîÀº±Â‚¯$kkSc;÷f)ò³È[?�÷ÖLvF·Ê[‰‹<+55C&ÖžŠÜw× &NŠAâšAþ×8(ŠÒöy…ï@yÆí6÷vããt/þÂWŸC=à ImY7k½ñfpxÄ*u8™‹YIúr»S †î\èE /EŒ0‚ˆ¼sw÷ßþ|¦¹ ‘ÁSÀ€ò„Àš(‰P^ï¹B¡ø‹óX-xKEÅž¡8U‘Ò 7ï‹-Ùvmÿp¤Õå�þÓ,™ -ãK*È€8KPá>Ž^r—T{�~v‚;ÇŸÔÜ_ß‘plØy5ñ°Ãñ¶a³§–MòÈŽþ -tb,œƒ†C—½ñôà+¦Ð %ƒ½LQ‰)±²Ý†î<?šòag öÿãîMÀ㨮´áÚ—Þ[Ö¾Y’-[òŠ7lÀÈì„&&BH&Y™Ì7øÏÌ$a²/$qBBB $a36ƒe¼áEØ–mÙÖ¾´¶–Ô{wíUÿ{[–±Á™çKbL=–[ê®®ºUuï¹çžóž÷¥+4øú³c³µ0&Mè%#ŠT¨t&Кò¯y€rMÛT—í¸ÀǖϤíÀðžÑîÓÛú^øý!¨:ÿÇï÷¢rç%Óx Óp}8™4è€÷° žíÎiÖ„`ö "x™Ì 묜Öúz¢~„p6 Ø%äu‘>@Ñ;ÉCµpƒˆñ{[7¢P±g(õ"ˆ~ƒåÓm]oät+äc{PJÿ,©èl® ½T D?>ž˜ˆSŒØÉøfš·ÇHŸ%}…4’ü~(‘É!ü};{¸[Öyokëÿ¶ƒŸãK<¼ŠÚìÈô9.úˆm~å¤[``/ò³¾–ÍS1›š°KÎ2¶H×oùE?D%ð¾Õo»Õ�YÊåSXÏɶfÍšü+ù/oX?yo�¸[iK÷‘(¾s†7y�Foí'?‰À®ýQÌ—+QÈÃjšvþêÅ‹·%²ÔÆÀK×/ƒA `hYj@;FTÍü³ÇM?µ¼¤€ÄþòòÔIßøÅ©Ÿ.¨¨ ´m$ž¦¦œ~@‘oðòF´¯ç}·~ºÈ' E‘ÈEˆƒ#vÁý æ¤z)Êó–äÕô¡I,{wßä3š(Ü"R6áeX†°Æ?e*‡Š%¨ÏÒsYšµÕ±Œ„{¥&#åÜÓÇ È…³Ul‘{^»Öõ™¦¦,õÀôÆ›oæùPHâÆU^5CJy•XøÃM›Ìy²l!öF£`ìæ6åH½ïqTŽõ)’$±tÐHã%§Ú?¨›*(”]€N›ri¼`#˜ç2ð,Ç}†Eâ‡o6¼'ÛëŒê/ -ÕŒèm8øpˉEhò»WôLŽ%6Ž§_"ÏgT�xÏ~}ì~$`¦bwõù d–¨Ä0XEѵƒ“—˜ŠJJN:rˆU~!qÎh.æÎ÷‘’üà¿X… ïâÑ02±¹ÏNÚ‹ôäT¢wúV¾ç^És?ÈAÕdÒŃ]tte3ÄéÅÅXñdn, -÷·vWÍ®Ü-‰ÎO@‡§Täõ'¢Ô#óŠ]¾®xÂz…Îá5©d¨ ¨8«£s®nT’SûÏ]nÓ|š/<½ÇeØ™ÉRø°Sð.´”é{iŠÛDsV:¿…ªÀü“Ä[ÏpíF‹³¨·· -઻¶@Cøî\5óœ‡9WGjÆÒæÙ*Ïñ¸F}ƒcÙŸƒ]j®(pwMP q<¹MZÉl"câ“÷6>ùäÔ@ELvFp‘UTµ‚pçNï¶×ƒPÿ½üŽ/-ŒíV‰?ýÒ¥sHxìTÜ141˜×Û2.ƒl³O�9ð`V£Å< P®;¯Éƒ9B™\£/PO–#7Ú•¹*<>Ïï'±£¿dxÉî§oà‰±~0FpLŸ— $úŠËï¸w±)ŠŸ†ñ¼“± È×tM1E[žõò"†ðôƒœþûæ¾Ã]H|œ@«ô4À‘é/â&}ä1´�$¿Ãó÷Œ&C¿»¼¬`pÚðnhqmî›ñé‹Þ_ù“ý½ ¡À‚x�$ÞM¸,nüç‰^þÌñÒ·~vVÀ_Ö(ç¬ó4ÿPEØ;[‹Û«Ÿ??lCa™cŠÂqͨs:i[vùHÜ^ äFa†› in?ŠM*¡ü=§s -1\â~ÃÔ~]X$ê(”(]¹ºŽðKƒ†8×÷¯—x4óüXk/Åb{ï›Gbÿ.¯[®ÖõüsX�ÊJŠ5îiOõº7‹Ìûɉé²ÿÞÇ3ÖBÎ -ê$±«*C†c?gÌÁd¶'?éƒH3ô£+z5ïè&(Ó›ÓÅ{òw®s~ŸÐŒ>_ád•”‡{p!ùkïKDâšh²Õ3Ê«$·�elZ@Y…N#ˆ½ûg…œ -…R®ªò”.Ä=a[mDi»Û´Ù ¤á¿½Û•%%9¤èÓzöO -cÿfy‘¿åÙζ};0•_ gçu¯fØ›9:{k&ï(ý%tóÙ²‡9…#ƒÌÔ‘¡Dͽ«¥Àêöö¶üo;ú9%SoF®õ‰Êá6Itw9¶9†ÒíP¯Ícp 9ò¿<¹~Ïî‘Ì›™„zn¾Sˆá0gäÔ¹(J]Ž*¶ò½©Îš°;ïŸ~Y{F3&'~‰—Ë°:©@ìxµ¡ØîëÞóØCßÕª}€c„�iºFqB{edZl‹íâ5X˜@™˜¿Ä‰Å ^”Q±±U•!Wæç~a⣧…0Ú♹C‰Te6k›?ü¶ ÂÏ‚8â®>¸Çjn®É·ýá€+VW@!Ê®¾ÙÇqÜÊ^ÌBx22 Twàüå¯Dg¿R·2së—\ÄkÉØ/¾åÎM—\'ax«+™±I§ãÊÀ_0Vþ=0vý'#™Ì£…ñâƒtï ‰¢›¿üïU½ãÃIBrÞ¥«!?g€ñFcØ„mvìŸnÿ -†–TI¾E~Á%çëŽB¿´Ksè5¸?U(öë°x»TÓ핸§£ÅP’Y@…]O—…Üž'àµ÷ˆÕÝ!‹LD ŒÊ̵yFz‡ï:<¸rþÌQF0A넛ߪ03ÆXsyJ�ô‹’9žî-÷Hÿ#Š`½WˆÝª¦ù2y5ÂnÅÔ7-««Î'/’ÚbaïGúµ¨®orÏŒb_úÓ:Ab”zŠ×rCpÖŒ ……Ó}ÇK´%ÜÒ¾š]—÷Š†,”E›é%"#`îÀ„É;/sñù8ôi‡|Ïþ:aj*èÚj -¬*ÞTämÇÅäû*Öã’mÊ?‘Ýü Hß�£rrçkøô)¬ .‚êÙMð$o²?%OïøàŒÚÇo¿{Ø-0Çö>ö³I¬pÿ_’¿úþ‘•Òþ맞cÝ'÷H¹]¾Ù‡Z^Y°øÂ’—\Ò=˜Ž›ðÈÐ@rŽÞ½êáŽ||áEuºÃ.DBuȕ̾V^èËçF†¢–*¹Ùh+6B u;êÎB—kºOüÕm{»¾pN/i$ñên»þzsÃñ#£µZ¨cóì@ªùLOÒ!²ÔÓòå¡¡Ë¢*šyeþ‰qnõ¹Øa·xFöl¾X&{<®™ÀL?» t£PŽoW]æñÈþýæ¬e4"îAnS„G4©äÒ*ÒsÃHº\ -òg,Š2¥ -�qz-:‘|µ><õÀNo#éÕ³>â²k5Æœ;{©U2œÉȉ„!Opâ‡ê–‡"岜°ä{½é\@Äè -¸x!$×ÆPÆ…cvÚÑc]«k§ -à&0šÍR¦¨hžfì‹Æ3î—mÓ&‚peé_Y†›…kØ…¶>š¼ßi¼ú¢cr“^·×#é•Z.½8›rG5fB긋\ƒ1ܯsò¥ å·®å<ÂNbëÃkÆ„°Uí'3hJ:¡Ë€f¨-}–Ý`Ó~?ø5^Kì~ùÈ¡‹–¯dP�lƒ'€§»Ûûh‰ê:2»fÄ¿ùùQ}½iu5Ï2âÜY3«Šy¡–3¨JèÔ5ÒH•2n9‹ò¢I�ž‡YCíÚõÊÀô$‹îqÖùdÿøõÁƒÔŒòò -+§V®¨œJ¤v§ÔË$–»Æ¢–u‹©›rÉÓŠ»ÆÆŠrñµ¸Æ»¨ Á¸üafœÃ윘ð®ª,NÌÁ;}âþÄä‰ËLÉù=pâ†hÑÖO½blº Óû½W_£ -åm«N³©r/ÙˆKh/;i|þïX3—¬ü ªOW“ð¨fs?æ©GŽ&Û*ü%}ÀH‚p.UÒŠè<ó»o}˨ ys`vÊn} /!R*œ»h¡/ WÕy\ÜS%%ã33ñ¡¸f‚¶KXž™`Q[™,'³ª||æ•Ú+’ù4X/°TÌp‚·c<rŒ¨V”„l59`ö/Gâ.8ÊR;jå©>ñnz®ç$æ;}Ad™»o4ÝøJG$Ì ÒÇßèÞÞ?Qú»¾<!‚V;-æ]娫ºGXÈ›!,q*LNŒj -Ñ0}ìŽHjŽ( -ïÓ,ã7™Œ²åÂ*ƒëÄxbH'ñÄ+î¿ŸBåÚÞïßËgèóX‰ -ºáÂjDvcé@åÔ^xGOgèä¿è;P¨ŒÁ€VLŸâŒW„œ&ì22w�Ñ°äøqsbð˜/`tzâHLÙ>Ѷg“ˆÙŒÄÑ+é§àm,D³`\¡/i½Ì%¬NEД+0[3ŒÁêN<â%ÀòÎÇrh†Ì;‹Ñ½“9ë_tÁÎ~'»G¶N”&·ÑTn‡*ÐíE`%·StÃ;% ¢)�HJñXÊô F¤’Áe’†o__lH3ÌzAfc|N[5à¤Ü Î2‡Ün]Ö³¢žG5ý¨‹†t)gÈ’ìE]G6åt€‚Äô’ y�ËÑür92¡^ÿ‰Oø#‹.âÜ´Ptt -ýl@·ËÔœ“Ah' åŽm±é‰Hlø@}¶¶¶•Ù°á§ßdÒ_¦“$ä}»oëÛ‰úçk+Õ{a Ⱦ?jmöä `œ}\7è”Zo‚ì“?À±5ôlÕr àšÆÍ']Ž$ç€ÌÇLg²ÃÑL‘سG’ ÀÄ `Z©'rP9†Ý‡ë|Ïo½ µz<–}¢6ɆÃ;ì„ -È$3¸ø´ä'É)¼:’ìãœçà8ydiyE|Uáà¸öž{¢éŒã‚øk¡G£tê¶à)L=ˆ·é.)ÁJŸ,ð ©´2 K®™x=¶kËÚ•hh/¢·ÇMÕñ¬ûÁI -Éuëh궻o;¢Õ–Õqw…Ì_CãÇIòr2hæ^Õt®Bî1¨“¹£·éþ¦Ãž3ãK2û/D瘌Y+zÝꟾâdÖÿô–yAÇÙæõ}=ñH=x $Á궾œ¸p³1ð7<FäÑ7KJ)”ÞZÐdã+$Ψy}<•,!Xì. -½>œô[¼E îÀ‰ÀH =ÚHmS©£ßc{%$z>ãrSRt× y¬ðè¼Å€|-^|ÖNEªôZ'ROÁû\‰âÿ6 ð@: ©÷a¦õrÿÏ_ìž¿ü{¸¶~ÉR7Q¼ø¤Ë—"›7Ó´øÕv(б¦˜BLzÔ.k8H R+¼Ú/€dh%¨(ÌZm8æ2P-Ž`0ŒÛÉÔ«<¿Ìær™‡@¶¬½ ¢$\�ˆœÀZ•lÝ*cEh›b3Vö@²WYcC6xÊ)d§‡€:>aóL•‡cw«º9¬ÓÐÕ‹&wuîÛÞ¸ø2Y}u ·ûÿìöÅFk\e‡““Û=‚2¥³üý€Ís9Ϊ@Ô\²ÿýw1€ Ô×)Ÿ, ŒSfØT39VòuÂzFZ˪™¤ç¢šê1`eíå˜U)j±…6ù匕J¾:Uî‰&ÜG&AÊéýG6o>DÔ£§Ûd -a‘×Á>†Up«Üóý´¢ªQÕt¡jY«1É¢Vk–íKdÔü{ÌBq®ØÖUWÀ-ûÊÇK ²Ùø.pÉ/Úmä&Ñf×YûÁ ÿA&±=1¥Š•ÙÊQÙ½±¨«ëxÇQêxûÚFsÑtÈádû±:ëGÙzŸIIëøòÓÃHÒ密™Lâ¯(ÉŸý÷2J‚6õ;´KÌKÂËaé Â0¯;rj²»H*¨R$~Í当p£DÀc¥¼cý7ìÍB‚‰m‡QtÂGùí‚B×èîݱ8dZÛ‰øÀôûï¦×sf|e(Ç -¬°ÂDvRä…¬r -ÖSÊýozøÕœ/:h¥2Å~þÚc“ÙãX*.Ã’Q‚Ù‹¿‹M¥&@!õŒeŠ°ü'äÚyøèµú@öÝ¢Ë!Q@ÃćÈÊ#µÃ°Ž@v®(óÐxûÀ®ö'»¯¸"áª\‡hQXÆ47«Û'"U”¾¶â†‘‘¿'F½,YégÀ2Ö„™w=*7™<³9ñˆkgê:îºØôî¢~avòÄy›ì÷ÉòF3=úâºâ’c'Ñ0h›ýî’K?]b ´�V¢ŸµéƒáÙcÐr MûÀÚµyêËæüQaÊ8* 0jŸW‡5¯>ÑŸ0â±ÝÊÞ½{ g¢äC£îÊØ1M¬]tPµ5W‘Äur"}Ô"ãøXwdç5ª -DeBN¨Ø°zC“q{@ §èÙòÜh<^;²ðêY>´Êè~>§Öë+Çì5€’_¤åIDH'G†zÆ-¹07œ¦3Ò‰çÕ@~NêG¨Â)ù¥“Í>ëKÅ*=ÞëP²½ -¤H%H‚"¬m{4ƒ;ŽóÏáK§”/¦@†ao‘ê¶Äcz ¿upÝ{á½ÎAׂ×î‚ =r¦f໓ɼñöu E¥%*³…âœ:~THF4—;¡VpÓ¦¤ùý¤(äíuí¦/æm|e,•…Ū*àüBcc ‚ø³o¶±*zƱá6Bó™[§íè€Ûl�b½ÛÏ”ÂÎW“žöùÛò+¨ÖÒ=ÙlΣ9+¤ý[<&ävõV^ô„iÖªRy0ƒXè„ ’$ xàœ·Ý^~&r2@mP_C41’¢ì|Ÿ>ÙH{¯oKƒÏÑAáLœ›m|àà¤Éð‡…2 “5Üìò+"cM-¤4ö´ €NAn~ÔhÓôM«¹ñØW„ÝQ”Ì„Å}qÌ/eYꞪUW]ÓÒÞî!_/V'ztÝ|+¦ã²ó‘0€=U¬ZÆìfthM9WÂKýt•¿¤èûARŠ›ß]^°O5©±¬úlOÛ‰7cPOkÚ¿./ RÐæ|XGVÊáصš`Í®%ÂÌúÈ«~CÑs?,Hû‡ñ¥¼çÐÜŒ¢VÊz˜ÈnHX¨Î™V’k.îëóYºÁ0àZAN(4' KýoÇÍ}ÍÍ¿ØTä><3Iïç7ݘE•…£¨¯ß™Ô¢GÎú£“ÝÙ½™jçâ‹/fžîž8ïÅèEaŸ|<s~Ig•¿ÍÝXºËEeZÖ@èCšUZÞ(Á¸äY4 %Ñ_–uý¾¶ÁxÓ:8˜Üôù×Ö•n¦Ò#xêªÃÒ"(ŸG©lƒR1ÊУ™<Šòà6_6z¤P¯KgEI™1™§Û‹Ø6–œÑI-?M¯@áJݺuS(r-ø\òÊÒÇçú"ž} œë½€n„M‡ÌÂCT&vðŒ< -€ê.dl?nØw¢ãôŒeàXéA/•ù¦f©ßÐ8jßBä�rKÓ"¯€j2„°JšïîFÚ€r@)S…·©ë±]¦àrøÁlñg9/9Æ{e#΂åH&Ô=8M‡/ƒÕøÉü>"oeÔø!#>ôÛÓKq§÷Ý:kÂ3ÿÄÖ?l$ÍÚÆûï?uœé}ÞŽW4ß6Rô(…)¯EÔbF¦ÒN*L~V¡ÌTR¥^á!€@ÎO®×fåçLËò3¨JE¯ÙèÙÆ"WAúíhßÛuÌ3ãßsxWʎû Œ"Gæz$]HÄ{b¢oºD•ŸÜ8x´GÖ¬±…^Š¸Ô^ʦ™ìí«W~*×ç8à -‘vš…Âê,<²íˆëª}}‰ßÛðêÍ®²EbÙ˜©“0tYU=‚M¯ƒg|±)ËЫÊÃIY°E—z'pbÊù+“]ÛŸ91»ùšõ&e}Z”„å'bifçy¥Òå—úq9SYä“7Î5ÔÛ©Õ¾}&bºL7órˆ.möò[?ý|On¼½„qZLÍ:±Ï½ÝcFÕúg£nÕÕ¾b‚§’Ý?Ãø’stÅ,{2g -–+ëÉ5Â<ª(+»ÃY˜5è&7§ÈX<‚ìVeÅ«¬Píq¹ë-C»hÌSÇ„gˆ%:w œ:¾k( -6^J¼?•ÖŠxÁ£a�°F2ã½î1ÎLÅwΦckŠÈ££§Û6ýŠ¯SÔÞ¡ô¬€»àrp÷¹Fb‘f‘ð»úÎ%/™îY„rr°‚ªŸÀa -âg6¥>:f™© -Ás1»…´®ŸuàäT.Ά52,0ÅJ„ƒ5k›!„ȺÕ�.g‹õûUjXkíëì#qMÒ¦fp4‰ -¤šHIá|lKÙ”`I.…‘®ƒºT©ác‰BÇ×C¾ÿ^Û.&•ÉRŒ’fQ¹7u9Dcpûpü:Š§Û1æ’Éò$Þ:Ÿœ¾F21ò¼gV®·‚¬æj”Î{-7WBpÓÔâÿy•8}Œ¿÷U¨‰ÂTêèEà"ñ.û’ýXÁ-c5#„¢eAIÉëÓצh9äaÂë¡ÎC ±W·úcãuÇ'Çn¬+#¡‹<zHŽD€LjÎ?û¿·çúûçÌó%ê·L!d!L~D2}œ²]† gÝt#é˜BOÍ¢¤Ì÷xÚÙñþÇ^}mÛ#Bòþ -*Bæ*æD滨Zù!änž£®*÷zó8½fÈž[‡÷mãʼ¿ú³ý/®¬.Øó\EðH|ï«(¦"yœ£š,fûSçÃ#ÝU¨4MbÓÏî<A�Ù§>óME¼ÚM°ªíá0c•9ÆyfÖ‹ - ¢&F‚ôî3/9©ÒÁ:w?nªFÈØ°<†œ¼s¼½ë,^,÷ û—trK«õ¨N_%¼Ìê5<ßöøçòIsÑ#ð Ò‚`v7@Ögsßäìgû'®v{<—Ö•,ÙÍ5Œ„Š|–.ÑõÕØâ@WYšTÒÛ<lzø~´É+D˽’«‰7™¬6š:…ƒ|Q¹v\rmS,õ1 %¦ZI«Ál4šˆè="8F/äYŒõµ¾ð¯{qiéDýLŠºdt4´sl,ÜŠxíÔ7Þøßh¦ ‹Â/€¡á³¸f KØ$žƒ+’›Àw[=g5ñ¼bæÀç!´s�]-°®ù&S›áìÚÁ>¼ "4Š£NY7Où\îBP{Jø¸Ž©P¢ýáÝÿÔXõ?Q\a9æn=mݦ†Oå*Dž¦6"Á„ðT—“áäB,C>ñ3foš1ûeN -1Ï®˜ñÞÞÈsQS™¬$ìB·à[w²o©SÕW(]˜â™³Žõg‰Ü“¸‰á¤¯¢Ã~˜<àÖMÔÞ×ûÃø¾C[¢»ºmN’PWãõÄÕ*`Û¡=ÎÕ"–Òzá˜nŠ®´œiŠAÝ âBðC'`®q±ÜÕd+M«ø*§ÙwévN<_ÄG}Õ!i Æ÷œRÿŒ3^7y~6øRE‘(?ø9é¹Ö‘}Ôâ3ؼl’¡ÇOþÖxˆx3ÝÔœ§Ï+ ¶c°Yq¾É¸ÛLF&…Ä{óÚZsšÞ?žXdiõu{Gö^îbnþ…HÚ9³Q‚º CØïŒx©üÚ44^/jZSURôü@rsnÏæ~2L?bøê/½jY ë¹0ãe -®_°¤WSÔmà:||½ŠH·vû(uþôÞôJ»Û±çaÕô£!±Ö…Þ» -Ë;r¬u4sà€‰ó彎q ÐXÓs)ˆâ‘+H½À°»&‡ŠÈó›—¥MªHP ["9ÅXkXæ뎴v>dþ R<ª{è{ÀH´%¡béEËucµô=”é)ÒEñ Z9¶wy}fÍÉ6’ùç`"±ÅÐJ -ØǬ–Þ±jñTÕ×òÙå1”x'OÝ3gÞŠÔ³ÞƒÜâ’`É|ˆJÀåûÇÒF}™°DíLá}wcfƒv˜õ4¢Ø¿‚hÀ<‘Z›æþ/Ãçá;Ÿ¦jšZ‹vX‘ìÝÀg_Oö4g®Çå2u+ÁÖ0ØîÚs¼±mÅIY§é[«+Ê$$±¬vž†í hƒlËìð „à¥J.L~Aã¯c•±ú£˜”òvô8XÔhÆÕfª†C±T<3öˆ)”šä¶ªD]W]d"]ðÀ¿3 %É·þé6¼Û_QÐà -!€³´}¢œ/^BI>4} pR,³ºÒï‹!ÝÏÎ/ÄX+UÚ)êbLïVÜÅÙ”ef:»`ѧމ¬¬LiŸHgx&lš\µÌXÜ ÙØSzV”E‹ªaf«„ó=…×emEóAdêH{é~jÒ\è¢Å;6ôÆz¶ßö¹¨vß ;Ì+øΞwâþÚsüÝÆ—Â'Ž .*òú/ÅÂXg5ÐW<±âðÀÄpÐ?Äà Â5VÔT²Ç12ôð³vò´®§<Œu|r³ŽD£“m1·+cSAÁÛ…H+.ìÔ÷ˆ»{4ƒ²aû–"Þû:<SmR¥)ÊdÖS”ú/ê“sWI4·Ììq(/ÌÑG57?‚ãžòŽ§WáLŸÒi)9V„' ‚´ˆ¶ùoeGžÙöЯõSƒø,wÚmYó–…½sz!5´—²ìm4míèDXF¥Nf–ÉWÃÙ±¾ÑxŠ«EÔÑ”ó*AÊi% MYãçäM Ôë‚™ÉX¶ªäÉEPy9Ÿ“˜6°°gm- ï`/ˆxØIï$W õÐ7ŒÜû‹_䶼¸5se^ øTK"f|8™ü^w\wýú§©U' Nþ™œ¶4k)¬(ô~wü:„æྂHpÐpÕ沈¬;h‹¾èºØ.ÚVì_õ»ô½%¹jÓJGí£8æ<¨O\èÅßî|嶒ÙߣEg ”/ˆIj "H˜P„b—ÎCä$(óE9¤áï…çm€>X·a®Ö§e'žlz±Ð_¶�á/æk(ì.Ì//_u$ª$ƒYÏbõ,ÕG©âˆË<—ƒÎ²ÌÅñÐ tjÐ/UM±1î?uÞ‹¿p½½Š^;»6¬ÆÜdBF?Ë_†ä÷ årú˜ÌZ×óÞ¢¾z£í!U“È]H®%(½½1ñËá0çðþ QêI]iäü•9`§ÅÞ‰{BœƒÃcñ.¯ –B‰$6®ëÊà@¯.1Š:ãóP=€ ó -´åÓí±xa”³í>Ô˜—C߸Îå–P–\‹äq††ƒqOGˆ"ÇŸþÞ»åõ-³áßÒ0:ec§‰úXjü|WKäh²ê¯Ã#âY–Š§'ôS±Ç³cee¥ŠÛó4(›hÞ{;T„¿‚Qq%ø’Ã<©8#™ïéï²´Õ/ -4¼ÎeÈR-Å*¿R?ߛȎniF¼oz?LÞâ†ÖŽB3@“¬ý’\\TM}F‰’ëÓûm‰D OAÿ3²ï*ÅŠ¿�!ÇwqîÍ`‹¼B`ŒË¼bM^¦š,gwÆ2M/Ž.%á”éï“WTô¥aÔ_êAÖÔ¾‘©>yaya'‰‡“wDk_ՂР-Ím»v‚Ä'l*=´Ø<uSNÕ_Gå«—”£—W„{ -¥°µØ(ñŠ¶ý<´ÓxZ²»QÌÅ£¨œ{TÄ][¾ùͼGM”‚Wú}q’¼Ã9ß¼9)@`zÿg¾ô¾Ö±,¼ô3—–äpÞ� Šúyª ŸoA¿}I²ßPÀÇaB:Ëý…(?•nîW€±-ƒ÷°së×oèË£¤sÀE1h^ŸÍSÁ›�ëËv"ddYß@CÿË4˜o‚|ô[@¬¬‡l|/øyçc¥ð©+k–àœy–4ºl;R9ý;»ÒÑ)ÃKÞ<¹žž2«ÙNTp¸f_~€ÑF¶ä`´P¶s¢¹¹ÆðÓ^7–V7Òw¾ ÞD\J•ëCA~ô$g§ù^|%á.”Ù@ìT¥:WS÷OîAþåQÇáJÀLv_ÐåºO1ĵ]ò—0&ÿ %ã×#d>Ùï*¶ô ÝY¼íºššÄ†®CÇr3Žâ‡¿Û9ûkîg•ÄDxH\V8<#ÐdtÙ\+Ä_äu\(¶¨!uÓÇÄ <Ï$•÷¢ õ};”Š¹e¬ƒ214ê[ýVâÅw£á%×ðwß\raHˆâD«`òX -dÀb«/6¾ráùÃ*›DD×%_ë¶è¡T -ª}«Êd‚8µÜ' !ÇÁÃÞc†Ê¡bÃœDˆ•î¯xÙóL6fÎçúüÙášmHl%Ƶ Ø¿sl$ñ]$ÙŠQH0ä˜Úq³óÈ�5¾@b`KJK•‡OœðÌtΑ‹‹çi°¸š£±Ðµ¼ÚÉ©Ý<gîŠAqjªì;,ÌÔs6à_aûã¯ÑT©¡Ìˆmã-¥Áö -ËNcÆõæTFªâÒÖ -^ÜHÖô¢é$œß¢´r¤Ì’†R•þÉ5T¾<š0r "[TèsÁÛâ¤zà@§'>FA]@“³CŽNçLPgÆ` -Ä,Kò´zUmkë+”Á½HxbŠýj´´»’Ÿö’ú·²fm‘TÏoüÒ'€ ->¹aËrnÄÒ=Ðì*Ë(Ê0ºKë\€d_-GYMMoº¬K˼á&_¯/¬žƒy;–žÕ ®ü…M)ߊMU»«| -ïáPÔHÀ#ó[‰ð -XÜ[QîmÀ‹Ê¢zĶµWà…�È Ûº~`Û:Ênüd½èg¬ÂãéÑg™š+Ú5( x£àiY+1°Ô�~ò×~-ô:ñt¯iíg ƒ#{ªMø¦W íÏ÷÷Ö]]û;¯ì)ÅJÛSÇŽi1Ë õзìdÎ�G÷2ˆ®\¨„2ëÇsmiÊ{ÆJñÜ‚È«%D‹—K`5TÝäoŒ$~3t3ñø‚Õð–cà1¸|¸€;²ñ¤ŠU—m¬Š¬öd$7yÍ‚óöjÜ9!•ÿ7¢?ïÈä´ÙïO-)üÄyƒ™õ�íuîwœø¾É,@†”/—E¡ ËrËSIÕ'£P‹)@žé9ÎRzr†0Œ‡r=Uãbµ¤‹‹¬pù–ž¸ 5Ò§ÞUÛßm|ÉÕ*A0Tºê6¶›“™FèhÕ{X½®ïkF�1Xg_,ÓËå>¶;¢þ6Rlǘ:Ã�“pE½ÊŽŽösî‚ĸ–6FöEC‹/,6JÅÖ•…á0)ZhÇñ´äp÷s=²L‡£Q[;bµ'e_ÕcЩ_õQ/‡ÄÐy ÅôºQÏ„ìßżiîçàbÚ’˜u¹Ì#•ªÈææ7Ë2‚Ð`qAv/׿¸T›j[A¤m"37SðcŒl‘º©€¡Sã)eX£Jòï“ÿÖVT ‹ÊkÑí³Rߤ.+ª -š^„gT £šv'1Qdÿááa£0X•‚¬²Lêô"‹£Ó¸+›$ÆaÇÒ¯•†Ã棛»2?»½ÆeJ|zé}ÊÁÐö#¡Gž¼óQ]\_\rf¶õÕA—ôGH?¯†q¼�™ÇzÄPA¼Ã$'©.±žzƒÍ¤f`šk!Agp¼ñ§I¨ëÚPˆµÌ‘hò'&ëò&¢‘ÕÿmïûbÊÒ8œ ú¦™šiŒ�s:„ÕÃD‰âŠ÷aìVùkD©9oñ0.úÛÒ|bèkãí©£7ÇÞò÷‹¥3„×Îuj°{˜ý⼡¦Ô¶|Šå1Üü©g°O&u&“õ¼È,A"_¼°;†J[c4+‰ËYŒÉ®mìwf¡«õþ kÂ1iŽÚ…ï0Õ{z“GÚ-#\MXç°¨{c[ThÇŽÒø—\[ƒDŠSjPU]´PÒÐAç>#8ΰåâ†Êlo$“Ë…Êú¥š[žÄDøæqúÆÑÏíoä<ý{0ÒKb¢w%ü±çq‹š» Úr½fqQT3Ö‘²ñ -§Æ3ÛPlñ]_Øö&ÙûO&Â_yÆ•GBõ¥Çùßi|É# ‰+z�Þ=p+APAEZÏf,¯WœLèî(ŽýÆ™êéŠ}›ª/È“ïOoÍÈmr²r }ƒ'$…¨àÒ^V7N€Åþy¢—Ó -uIQSsöÏåVÞvb–o‰)1ë*7 Óº£ì�J£Æ¬È‚BFæùY¢(Fµ»+6Üšx£z)žÔôÆrÒ mi¨?Ò0çA„E¨U˜s€Ó\Ï¥Õ'_x¹hMõâ(²²ÚË3 ùðÆv$Ú¨‘Qê˜Llq—øžõ¸Ê‹8¾Ü'S5`…`JR”@C[ÌŠèªÖ~Óg”C×@–Çâø¶igaÃœ´£îâÜÞžä1ó¸U4œ[»¸a•©8ù…·/†IÓ?€ZëÝ3gêû‡‡eJ -:J:$òª¦B™}…BîXR¿ -ÌK€‰† -2£<î Þd7Û<}È1œ›à¥6Cm¤JÃAœ#ëÄ?L.Ààm Äœ¦Ê£Û¾·Î†BùÈ™[ä'÷¦¥�pÜOíIµÜ¬/B»T\ÕâO-+/O¬Ã¾ ´±³K(ð_•²Ódªo¨/ÑãéÙøN#+z¸ØFÁ„àƒúÉ•ùô3´óËñÈÈòð¤íðÀ@}wá'«35X iÃïòîÁdßW´òÒÅZ°B{·>9蜌©®Ì4JÉÃ2:ƒ:ULšOaQTœEÛ:/&jÇ#Ù+ÍFid>‘;µÇ{÷ÿŠ¢ÏÐi'Qµ§ ä‚œÅÁ`r]|^Ä©��@�IDAT:Œë>:±;ÂWÕ}–A•Z´Q…ɶYuÛ)µMú<›Í“V…ñµ�#KñÖVÀÍŸá$½wÉ2²Ã ¿E!Þ¡’ÍLEÝþò¬ Ý'1ù8B[JPáØöÚst|Üy¢~Õœgê¿Ô¤m…=l%ç Î~7Æ}ωç;}Í ñSºººÚG9¯Ê1ýûÃQ[›xbç[¥é �‡£[Õ0Ž“ϧ¿?ýZZøM×á£óiŽ>ÆÐæ¦ÉÄÑAšP7&Qq–T+Mo/õ¤f�! ÆxUÒȽHgé}«QÚ³¶Êê7ÚRuAï‰N;}|ƒ7qͬ·âþb#]}î?.âkmÜ ²3a8N¤×-ÕÞê˦Íú5k -”¤æ¢->‹�uäb”(ëðc9·Ð©¬ìŸ“uWª´«Nvì™Â ÑTræˆËÏO°ªw¢ã…Öôí(é%†—lµó‹Í#ÇûžózCéØ®Á4µ¶Ñú Y äE ¿ƒQ@ -4®¾ó£‰&0OIöbI tó†—+йÊÀRVBóÌ£c±Ø«ÐbK!Žº ñà�ä@.‚|R§¯ï)Œ’ÉŠ‰fp{镸ñůÎx‚’"x;?ÀÀüoÀÐQÐÍã:ãƒgoÇÉÿÐœ)#MÚtýçîÛiFÆ\¼}©$?.å]W¸ÜõÙÁ—ÀøSC°bßûSë“Ofj›¯ú=f‘‹ëõ#Ö´‘/¡ôÝXlGyéóˆãf§O8Éù¯�CÒ'A¸4šÐ×Q¹2k©)„¨œÒ—ú¼ý$ƒ6†—.mß{ü&=ƒ‘ù9 pP•„ѹüM8U\ éo65«t^Ýîµ'¯{úœïÕ×;–,1[‡“ýÀ˜{!4È`9zÆ¥œºî@Qê5~1EQ6u±…KJƒ&e”‚û£1ÔÐHÎa(n eu•P?ã@oóñžž\a}Óë,c¤ax±:Ax±ý½ãꔊ7Êi¨«ý÷üçdõè[Z%¶ä€ÝKÊ”1@ö}ËNÿà7Hðœ7kŽÜs¬ –Ï͘ÎÕP|¸ -ÛÕX¢†þŽµf‡nZÇ«Cg�Gbé•€7}ÊÔéí¼àx°<ªuáb=IlŸæ±%R¥vágÞO(¶zM«�ü]®?ù[B4²äÊ p\‚[>6íj'j é‚{ÆS3ÓHB�&¶ËlÝŽnÿy$5¶?�‚n»Ö·�ìÿõ:h£‘?]{ACºeH©Pe Já_Ç’hŒµ¤A¯Lâ…~‹µolكⲠ#ö–Iæd;àxN4ò7»¡�%¤ÄÔJA–fäý˜Ë-–b§Â™tËaéxDAO ‡ûI$T`¹Á*fÜŸ’Š½›ÞÉNŸ4!þ;pÒw1ñìHúökNBýÚGSËtŽÝ„•I2Éwuô®ŸV‰gÆgÀøš"–ó%yÂû£<€4îä¶/š^ Ï%îß[0oÙ'Цƒ/RŠPFE+®&‡°E!è -b ªãß…Š_¯AH†(ƒ&sŃ|S<+¥T59Ö•üxó‰RÒ‡.¬_ø¸Ì2¾Œmþ\d¹~Às!ô2/¼¤B"Uh ¿€’.ÕÐ&®âL™÷\V,ŒÁöOÖºÝQôð|''Ô“¨ì:cY>}Mÿ^[㹫yÊhôÄíõµ³ü¿^ú"FÝ‚yYŒDxÉã)BÙî‡NrE<Bhç½gdbóxÙºT¼K.¿…ðT.gïýŸâ¸ÄÓÝ9’|P4àØÙ\<òÿz?Þ©ýΩç;Ýhbx_îŒÖ†ýe¡pþ±]À²òèk¾€ÕC'0ÿÙIÛ‰|"—¤ôóŒMŠµšá²íÈÅW`)µK0™M¶‹S‡âÒH3âšÓ;cÉQPG@œè¨-ØÃÐ{CÂÓº±t¦ß6²Þƒ›êV¬à•––\%¾DŒ5âÃ$†yjkN7€¶¼^3õQ–~™å¬-n”ÖÆ2“—TTŒ-,òš=¡¸8çÌü%Ýï+óRV”TÊч].ÎÇÓb'¥Ó± $Oì+ñ$nG¼jéIØÏ©“ü…$¡DçØeSÃIB!-]z¹7¦ÛŸfeq"3%·x�1€B@ÖÓ¢{ <�裳ÒD™¶þS””Þ‹ò\xß1£¸þ®.º´$%'GÓ"ÆÍÒN{íb/žÚ‡îÁ³A5ˆrð…>`TNnœÀívL›,Å+@Š¿ÖÇ÷>¹;ÛJEvëM+®ô«"µìbŸ?ÃXÆëÝï¬'E¨tÒ×Âî�báL -:&Ù!Äîv»ݡ¨úK5?‘Sé xœ#á„/šóàmÈGIöòŠpÞ@’}ÈvÙܹ²¢Rõ–m×Gµn˜â<*±è!kd$CUÔRøÍOÆ[n¿û˜c±‹\n·˜œL &|Pu´nàÃ=/=Ösn_ì<ÒÞW’Ý«jŠjëíLמµ¾·ô¹©3¿wÿZÆXÆ‚|å(ÅvrrršÉŠ ¢‚ô“*ñ0—ŠÑK–x1‡žñhþŠ#ÿm»¾Ù𒣬,öŒïOfÁ…âå¦Æï®÷ˆãOŒv#†áT‚û hÞuÆ÷\-)è—Ç/Ú9¾pó‰±-cékôE`D¡ˆÊ^øUĤïçåGÊÑC¯©ºõ \#"G}°ˆs•¬[û¦x¶Y[{û>gÈ%Ì÷K}Í5T>Î:½;‰5gs£)k¢ë†JÜñ8‰Ý‡xæ-€œ4KKå`Ã"׿dIÐ×°`Åþቆ––)¤Ô‹káãÞƒ2Õû 0wŸÅQMf&~ÔV4©€ó-ØÜÖK,Û@ …£k¹Ø‰æššü’h9âÖš¡¶&“êà |ýÅG<'•xƉánãé¯d?8šºZ|w¹y×WùWÙðÏ“ˆ…¾Õf¤¦‘0¢A/ÂÔ2¶}ƒES5Pà\o0ìh–õGøa›5‡%?Z ýoñ0ŠˆæömBÂ5ùû‰•#Â&È"Óo8V mi…±‡?Ú‰Ý`3é¹àÚÈûdíï|‡þ#ø†sÄÓ¶(î+µõÁÛë/Ys»&²_Bìý«€—¤x!*.v{½†AÿK¨ÿ²móÛˆ!þ�^ù·‘D|J_+ò?raUQ/==‚‰VJ>)³ñ}ò;Iœ½¶‡h˜Mq\Àƒ@@à~83…Hï Žð™üD²ü0^‚CF…S¾hgI©wrY±¿okEñQâ!•ŠÙÿ!–åWZ6u54ýþ•ÕkˆL®÷Ó6B'T—óÈäÿâênúz·õÇV¼:ž[DVZÓïÇ„ðào§9H\E`bĦ?ÿG¾¢mß• ±“àG av ™´iúõÍíËåTØ -Nï/íóæ3¿Åèý'Yq>J‚¿à H÷I&õo¼C}�°¤ ¬9Áh?m*ö&à#(JŽoŠ¨Õ÷$UK î?ûY7üàÄ~c¹×^{¦•»³v„kÒ0†Ã:Î ¥%¨tzKážOïÞ<™7:ˆ;Ë�‚ò’k0Y!àm†‰!Å|Áý…P?‚Š§Ú¢odTÔIÜH ]ë).v“Öù±–éE‹œ€t<ÉÈŸò.nh¬;ðdEA;¬>ÙÿlÛ®ÁhD¹ï¤xæß ´ûAË¢wun†}¼E÷žKV霒±ëg‚|TüCÞ£ 3´Y¡ë�%0?ÎdÿʦíÎ+¡V¬9ÌAŽþÂ29” 5KáÆ#‘@Ëø¸X«r¸-9Ų‰—pª½déÍÒöQ<|äEzAòÌîIöDíÊr¶®ý†£mƒðŽ/BÇW€’øwÝkÍŸûˆžÖGŒ?ïÍ’É&VèÝäØÙõ#£?6N<¬t$7/+÷v¢â„ìó–{Ò§ü;'ÊH£ÁW±…ÀW –¿ûb¸¥…”AÑ?Ñ|`-¼e;xáMx+¾ÏÔŸw&ù@=G”l6?âDä\HN]ïN]¿XîÑÐNvJ,UŽ•ƒ¬ªß’\ûߺÀ2¯—Ä×O=볋t{Qä¯c ãÄÊOMB:„KE™Yóê`fÕŸzãó×íéòíûoç{{J"c•LŽd"ØÔ«xu8½Rl˜WÿÝÏßÒkXHÌ·€ØmÁÕÿòù™;Atå¿|~ÅÙÚ„rþq0€d(Ë(»â“ŸÌ;QgÛïõÞ¹ -;ÿjÀGXšºÂáí.¢µMnWÖ±¤w?Œ¸‘¾7;û!¥1¸w˦>1=PÞ|ÈòãÉ')ð¼ºì…·O-KÉ1R¦]"@ç½Êî £Äûþ.æigÓ¨ÐÍjÙŸÛ{'‡g-ùL -<&TŒ‘ˆÉ$µÿ|x]½½½bŒ¡.FõeÑ ã2 1Æí¼ÅUcií@(rÔ›“òßW2z1+�|£Z‰t*u†Wûâؘ+t§§öõq«¼u,%§*¦»“%žŽfœ‡œ{×hØôÞfZöÇ€öØg3Âs”n&áI/àýV„nÈP\0Ö¯¨Z¿~=»ôú�oÌ4 iU¦h±9K<Ǩ9$À¾°TXŽíͬǽ¯Çdw e7ƒ’À´t£ñ12äÃdw²Ñ(.Qþ„70¿0~#Ü1ΰ˵•ðè³%ǯ™L[¤™×€wŸGÓ<¸”MTj:]ï>æ0ÂÞÁj×îá©AÞŒëÜÇq…ÁŠ"'drHÛ‘Ik~™!4€DØP¤…2poŒ3¼Å!*Î_§1À#bóæå›6u=²fM½Fžé«ýñ߃'ô‡<Ç~ êÅ(Ø E„°j\…•·Æ’‰BÈ3]†À|1T7~¤èõðrÎ܈‘ùé‘á«eYâ9‘J¨j‰›ç~ƶçáŸ7ï–O ˆ-·ü¥¤Í™G{wÿõê°Rië9÷¶šPÇÙÆ&c¨1~@ŠvERåziiV6¥yˆñÍß;8J€éSˆ;@G•ÒµºÂ¢ßã³4~¦ûÎÛ~—¼ÔÐùª™%õHUX.¬ÜD–2+N܆¢ã;¢ ÐpÉÉ LýŸxžC/<C½xº‘]1E© -Šn@ë<9K&¤×CÓŸ½^Ï•ñ¥¤Rÿ¡T$ñ|ÏÑ êyƒ³¤s´%u‰Üš¬aR^™ï1½ç2Ô¥ë¹_Æ’Lì@d7ßþvÆöT\rúæ dzÊ°>?Ÿ‹Òß5P„häA$5”IsîªÖ—£TË¥ETßÉw’5%Ô�৺øT§´¦õpˆvRIO‚°�Û~¿È˜l �úû£ñþ„,>#x„ñ¿Ò4íTÙÑÇsóé�½+š £+‹÷ŽUΟŸ÷øH¡hQYäÈïwxê$Ø”™„Æ„`ïÕÚ#ë¨Æ©x¶ä÷¬Æî7bÒqÐé -ÓþÒRk¬wâ°ì‘VB‡jâ¶7¹=eÏa¿ÎÛo¿Ý88–>€àÈ0¬”ï×µôQßvÁ:T‰çØÞÑøCT*G‡m‚·z§¦*› 'âmz+HæÉ�Êo€Åzxïgíº… ÅÛ ¿Ñ ¬bPYOòŸûaT×…¼Õ--}Ëš–@ép³^õpwk<œ^œ]½š&ãôÔ½=yẖҬþ¯C¸à9T%J]]™‚É]zõ§>ï½æλS¼Í6éoð6µŒ—¥›£]п[Hî–ãoð_TòŽGž%øȬí¦Æþ - î[°Jºó*¨”éKAØ´Ü%Ø>¼+€|l•zCë#ëLð3ä›r¡†N]mx`ëÖý(¡•!jºÊЬz@ IšòˆkN…2Ù5ö/(jîYVѼ_zK¿Ëè=ôíd–»]ò%—¥¿wKógûPAÔ-ºÈa©°D¥ßUbÅU†{XDô<“eFŠÎÁeæû›£s’K@mŒ“’©?<:”¥)ß;gyÑØ?Ö¤çÑ€e‚ä?Ü~Ú2 „ªÉÓ”–ÕGy?`ð5 $MÜØ©€`âë§Æ%™xŸ8õ 1�R…ž|þ®2¾ç,ì°1N–Ÿ2,û•¥â´`¬X9Þá~eBKû›àLÞîÖYðq¶ þ`<Ǿ*–—ÚŠsïUw|á:ÄWóK|ܤ·l/ôÖ(ºuh?‹%±±ä>0èò:JR'£Ÿz²c²_Ê/YñJÃÆVÚ§œeg&°0ËtX À<È%EÁêÀòª‚<Öƒð¯Öµ8k8](ZÐôö"{g–ãf±‚œØ¶n]Þã#JË0v·! ñ$ù*a�‡ø -êZ%œ4‡ÅiÉÜ#z%2µÅøbÒ„ËûW§â¹[Qq÷1T”b†'8]ZÀÒ¥-8¾G<w&.—¨44ɾé`gÛ@Y›%ç}§‹Â¾¯äyþˆ{ÑEÛií 2IÀø·ÚE⳿@œö[2û¶…’hÑáþŒëÍ{õ0cöÇ…n -»Ç6üè;³T•†ÃÏš¨ûVgkD²Çpìz[d½¨e‰Gݦ‹×ЦÜÓR›e–Û9½ßæø…¶–Š‹²ãH2!<ÏÛ•HòÔdK–Ñšÿä9ßÄXzÞñ6'E�cºs5ü¿Fiì7¹ÒP'Y%‘6í�6¸M5þÞgë+*¥·¢xfºä,½¦W*ïŒþDDë@ˆ¿o¤åÔùÎvMï•÷S:<O}1ÇÛ_†·÷¯£ý›èb¾ÊqÔÿAÕè ¸s3 ŠË1|ènÃà ìÂ5æí@’›%¶:3…çuèáž9§"ïÔ}P-ý0Š‘~« -ú#.ŠÝ&Ù™t¯Ô‚"‰ES;dEy�•¨X°ÚYTŒÙSF÷ôvV eY,Ý ÉIdðé{üc?gž/¹” G¶¨ß—h) -»IÕPT_¼÷¥úp·H”F½=OYC:/ŒnëVß?³‰åÓ9ß_<Ã@ùð‹$~{úm!ËÖE·|æz8p°Òm¼cßÎ¦Ú äXþw4K}\¤õ È¢ê]]î"Æ_œqT±ÐáÇ^y¬d‘MÅ©6À¼†¬Ù`ãIì'dŽ”¶ÉÌzÄçèÙ\† -‡5ˆf>>Šÿ¦Ms//:q´ÃÉú -ËÍœBзoÄINtÉ’êЉ‰Òp)Ê¿%¹=OuìëSëç•ôØŒQ +–÷(n©á&ØD -’t†ž§é± $e˜´(¦I„]H -õ9Z6:NaÂÆÆe³}Ž(MÒŒSŠ,ï\ÁÎjôȾc½¯/û{ÖK×#DðqLMðÀ!d̃l—ÑPXCv£Ü´6ÉÐâCºÁ$}&5¤òÙ„)Ö—,¦ìGZZøÁ†ó+ÄlBYU_2F8@/ë²;îš6³:]¦çŠŒ8k¦Gü:ÕŸ|;Éq±9¿+ò÷½,1L©Üù¨$|1÷„ 3aȇ:¶ËÓeé -�ÆL±jj‡B¶Ô™‚ècf&R6ÿtËÕÛÓÙzTééѶ—ù³Žãû@]AäŠÊÂÔ@?æ•+whŽZ…‰(ˆv›Ÿ4²©NiìÄPsÅè• Õ=âpŒÎ2Y†Ãl@ì|Nڴ߇°É',èÍ¡”Ö2ëÄz·ýo9›Eø£X}ºº©¢¤ˆ‘O B"*ªG±k¿âè“ -(Ûþ¼Ìå-0¾Í˜lohj2ZÆâ£vÎBŽ6ëÓk½ÝÔ;¼Å¨lg|rÌöôØ/Ùª‰LÜ«@ Qv%Þ,úµ$ùvšçnÆ_¨ˆ¤5¸TVPÚ�‰ª.¢Ó7Ýä\Ú™thÓg£äXãÎn §÷ýG¼’‹9§ç…dÛ×s¦ 9xìHêÄ 9GÕr†;² -ÆDTMg'mëÇPAVòÒ‹˜uâû6—zψ[ínŒÊÒŸ°ÖKwRÖþxôáH¦–YÔü˜ !]þ…ã¸,Ó¾ -c³>Àó:,~‚Ð1ÒúäVBÂBTR›š‘HÃh´�õàmà¥+hªs(2YNI҃݉lg½^§ùeʼÊ2rÝi½ÄîSË›ƒ™f|åðü ^û.Í°/qš34Á)fI®(ÛX“§Á´À}!–Š ÞÁ^Š‰]ÈzËècàN�xb»!Q±G'ìXü²ÚZb°ó^\ÆT|4ñ4íV =`˜ï…ç7$;¾ã³ÃtÏîÍÞ54¾À%¹¾ˆˆ°žÁíŠ8ÔÔ1ñJ!ÌIàvpzÊÃy/Ï k‹óAÞP¯ÖãC/p;>b÷Ž¤ÿ?˜»ËpºZ<ø'nUÊ‚l>Ër½-šû´nçé.éë/}ï½kîøÜRQƒœõë´Æ6è,#q¦Ù¯Z™¨Ír[~ß�n -Ò‚©ömî+r{\çç@»2îGÉ9ùLÀDY!‹BþG»áRÃOµÿ—'N ç.®äd.N[Øé«ð”_³áÿ ¸ñŠ Õ¾‰Ç¹aItÀ!±K¸g®¬ðžOÙ;Éû�‰�f†ex<ù«U‹&Nï#ä|ïö ü~¿›Ùät�˄Ѐ‘p"3Œ.wÖès%{ͦ¤wlŒ2eLfFS7b’;:±csžh\ã¶Þäå’w©9Íå©þ‘¬hßÉk'Ϲù¶Ï\˜aqšfwUºØ&Sv^Ù¼9‹<@¾/„Ã{n7 §ÏÉ/JÌ<ÆbN€\ÿÐÒpààií¥‘¸ãæWÌ<OWèªjïiŸýÃ='ar毄@‡N7¼äýfR‚HÈ)B€„;1'SóB†§>Š¥ßª4ÃícëaÄæ¿Òkgxx©‚@©À03`µ&ÃÚÛóÓŸCI¡K(ÿŽÃ²™sX…w#–y‡iS²Cˆï8E€FÝpU¼ÁE¸#Þ<¨š›)“„Gp¸¼©÷°‘34–Ô÷etÝñÑÒ -ÊÊ£’˱ݮ!ù´e*cYýhÓaØ�ä/ø[€™ý*0_-`\ŸU=É[P°pù“==~Bï¸g0‰åÀÑB«ê÷·u|8Säfy±oï3¿þþ@¡'ܳùˆB÷Ähbo3<"L)tÔ>CýÕ”–¨ƒ!<uÏɽ8¹9C»Zc2ú6îë× çô xH‡Ëóé`xIŒó”ájé)T)Ïy¤‹›õ± ˜9lx¶pšèQ„/ŽãyH[@¬u Õ×wÖU¸yG�ys¼¦Î£ +‚˜3”‡Ç€Fè(ñµfîߥä4Å´]3t9\õáÛÒ¤¼9ïÉù—CV!˜`J—¹ÃMx+¤O‘¾ÕÑÚJ7£?‘ßñ¥ü÷Öƒ|ÞìõŠÂMC'SƒÏÁà?Š ëÊHn¼bØá|9êçe2û_–Ì~ýà«Ï>N/9_ÇÑL©Bës{G’K*»Àɱ³×>1uNòù{eÛùèÒ`Œ�½ý öë9[yp×àÑõé°ç)Û¶^6MÈ‚,¯¢‚RIÑ)Ä™ÌDÿpºá%×j!-—Je|P$åtH ¾Ó׿âƒwÊ€]ÅŠì èœØ|:˵·¯=—'«<€Ž°RdÏ›ÂÜxV3:ñ:¥$ûF£ ¸_P9“f¬Y[úbçCY…ããìo,²Ðw¼‹j»ãÙ™‡FSÔ”´'Øò2¯ùæ#Yv#* >ÌêëYÊøHÁûPZÍëf�´”0/ÏÒ!/ÞWã~#NÙ/Ó-îc9dÓøõg?t P¤íz&. yGzâ©Û†û8V[¯[¦ôÍl¶÷%‰õt@cæX‹½¾¢—|büÏ=“K#I}æá´ÆöK)ŒîSúF”ù¥DmPVÕ&+ú@DBËh_�åóm—-l̇Ⱦ¯ Ò]8D(H˜e`s©Y°t+“^Ž·šB¼'^½ ±‹ãdüÌKPKäEØ!‡AÒ&xÇ…ÖV+ãrÖ.[}Â÷Hà]3ôÌë¨û-4yîßYB‘Ž=‰xq'Œ`ɱ(C™»vxäɳµ½©©ÉÙþÜÓã—/]8Zî•SeHJæpø/Æ63š†u•ÈscY=Ó!x˜ƒWœ$.¡vzFc½¢Kh×ñ2oW þ†åôÇ*¿tFxˆÜ“î‰À öP<£÷ÕÜ™í›ÄщeâЕ>Ÿë-ôW®Aâ‡Qñx¥À2«TÚ™wk"cîñ¹Éõì{áY+›·©ZI‚ûµTW“;ÏGí=>r¼D2ÞãÅžÙ“}ó—¾´Ëíûàé.÷1]$hÞïê�ÚãZ—À%‚ým×ΡÖú݉Yn)Ѽ‚·'·=X§ ㇀XEǹ5k\ËFÖ6½µ_LçÝøJžÓ@Jõ ¹pt"m¹´ª*µ¢²Ò¬Á$Q¸õÔ-`°ÞÚ½wWMMM~2†çà WϺ¬+‹Õ„¼ùåúX*²y°ÑsãÃÇ늋ßQÏwP™€’˜ëbä‡DU§£_R›;Ôùa¢ù´±z<:¸“+ò“íe ¼[œ„Ç/—{¤ÞÓŸÏNŒáâ@h9.â<„cXÆÍô!àö.ØþnãK:ÿ=_þ÷jXŸ«]¢¨±_gø¤¢»³ -Ý‘É•E⿧}¿Ù„Ž0ƒ#ÄÚW l3¯ÇMìe8ó2Pý] 6«àVõ#ž÷Xa±Ø¿ë”±èià/«/¨Á`ê:¡gFbñ¸½c4ð¤'œâƒ ýÿ"°eû?ËøÂ×Í.É<qxßd±¯è„°–k:ýôSGö{\ÁÉw3„0]I%{t\)îH%<m[ÓMMg(°ë;£éØH"kOú\R*š<݈‘ß_}þ©¾@Yâm|ª±"ÆtWƒ$N‹~3¸ØBEã^1¨›ÄY0¦õPíF ØÅ -• »ÄPºAÌ £ÍEÙõ•6÷ÊÁcœƒ¤Ò>ÀÇ6 ‘õ¼Ñ/Ä´É-e†:pA ð–AAV ¡y‹¤YuV4£~>8Hw óUùËBû^Ú¨x½J“_äjãš^ÝMšUžÜgº©ªìpÛéîöîçÌɯ,¦ûg5öùù#V¿|ù¤A¯‰ägþ¹Ê#UræöÚ‹&Ùìâžæ…n2I9M0by£ -åbÁç¾Ãd¨Ûc! :Y¶‰áéK–«åcéc([ÅäaoÉ%²4ç@b)3Öï¿¿øð»5W³ö‚È•X™É›0ˆÐý˜²K`BÒ`³rå||Ž„žöQöL/+o¨+ðdÏléÔ_×Ö‡³õG:Òs -|uÈl:ƒúðì~Ú@?Û÷Þï F“ƒBÔyÑÿÏÝ{�ÆQÞiãÓg¶¯VZõ.Ù’%wÙ¶i¡Ä&@$BBB -Iîøß%w!G: %jìÐL‘®r—¬ÞÛjWÛËôùžwm9†Ü÷]À„ÿ$bÕîÌ;ï¼åWžßóž2H;1àhö³<Šb6f÷Ÿ/;·úƒ±žæ~¨2¾jH˜Ý¬ž:*æ‹¥ç¦[Û¬¿ÑoÔýWz½zWLi�ùΤ¥²ÝÈê¾Gë’‡˜¦ââìÆÏ¥ƒŠ"ð&³–~.ò #œe…KÜŽ©Ó Jòùñ˜V -ƒÏ‡Äm§ÞÓÖ=½ù|P÷ð{ÞxñmÜðe'(É}S€¥Ç�'Èæ@‹tIá}h·ó)»;\ãuÆwŽv%‚ׄÑtÉç#†x6Üj ÚÝcüVŸˆ¶èá¤Øš2Åz¯”Íb~ÿç•ÆÒr †¨UW”fU¦ ~.VŸUààïƒGbésP‘é…2Ù½ëêÙE¡?"åò"ýyøRròÑä+ÑqG]^ ²\ŸñL…º]°Èœ‹¸Œ+qô`]ž=Wšî@²KÖåº2:„ Û¿Ú1{¹»`ÊyÓR!e»_ËݧXñVT€ùà//D–ÕË æÓË -ý½cie’òEã¥Ë̤¡ ‹Ç9AÃ-¢§RxEÏ¥}Þ1_šu;Ú@ ¼5PèÜ«ïÙÙ½tÎŒ±j'âÅ�nßé¯(µf„’²´D±n]II³ŠrrÊ$F*µ×ϲFRéù¸×r»È6p›Çˆ\î¢Îc}— èâ‘ÑÁ‰ÏÕ”G«ªªˆ5ÉKêôs“ßG#ª °¥Ë°É¹QDÓÒå’º*aY„AGÈç…Zªƒçç¼³É;O[¼î‚å=ÓûÄ’oÐaˆàeñÖ+�¬¡ˆÄ\È3ì(Ð#ãÃ[K}>UsZ_¢µ&[gð”0.ëlnl4ú¦âŒB³>l^£6Íjï‹)³‹gÏúUï°é¦÷ ÅRí^‘›�œÏ†C#€¢E†a%(‘}¶ÎcÇ‚4}oh'µ=å´QJæì”J‹¨P˜´¾1¶Äôg>*¯Ù¯Tº -f(ryÁ_¼’ñ$R€©á²³¶Ã£²>c<¥-†Ä×ZäZ.³QÖ¶n·ÔQ…‰¸á¶o÷ ©)Š- LõèŒ×_1~ÐýÑŒçBazLgă¼!'QRQT–Ëçû¹Å•^ÛÈÉë[‰t‘óC—-SL™Écò“¿2Júé<Œ±<lÌÁsêº?èöÿßžÿŽé4qìH†µƒ´ú�²ÊÝ(õ�•‹ÒÖq°—cQ¤…jÎÝ…‘ä dl6%@»‚´ÉwðV.È›šµY©-\wêøò¥•™–±ðU^ËÊy²küOWƒ}j#�÷ï oƒ€ÁÓÖHV¡úŒšƒ0CD“Tà¿/Êa1·©^š×¡HvâÐ’…….g«“u‰wÞÙl<}ͤ ‹+ê LJR¬À ¼žt#¬'¢”ü*áÄ=½q2r¾÷œŒ%9®|“Rnø_°yÄ{ì‘ãB†áÐò¦tÞÔ¨e2$qe&ÚZw›³ÝƒÒ¾VTŸU"ö -yl'TÚ&Óé#ûi54- /L$¼†AÞvâºÍͧ7‰"IÃtIØÀœ>›˜‹Ä£5K×(Aöƒ×‡PPÈÜgÄI£“ѽvI( 𒤙jzn•óªª€Ÿ¤4òlZ!Z¸g,¶0 -̨·pÉw,ò:™'‹›8³&—_ÝGÙr‚û\°sÏ�g„ä*´õl>EUøÑ·'bÍkݥȓ]§TžjX¿£´ðk¢ìU±ºq J¤?®;¼bœ¼ßsÒr„â#Nk‰¶“^Ð…@`<ü’OèëÜ"{™fz7Ít¿pCaS$Qèö ¼M;ùbÌ\,¥5¦ £Óo¤ßÍ£À¸'Æ®&” £mÙ~½ýõ>OÏÊ‹+¬žëb]\$añÑ<*€ªÁXú"Ÿ¯ïk™ˆ¾)Yˆps"èçÅ|ž�a›Paùmlt>$nA)É–ˆ´é�òqèÙçû¹NÕjTP Lp‚çÝî½ÇþÕK.‘™JéÆå¨Lœ8F¡Ñ”’+@ß‘»HšrŸËrí•(~˜ý|5l1ºŽ¼»]ËŒ!GG’ò’\ -PNïé½û{ôïÿðâûÒÔ”<Ïî¦ÒFßÙ•þHÄs5ÄÐðÁ4ïÖÒìöÇ«ldB‘›™Ëm¯,¿®ò×a`Bèâç¶PâŸtW&6®÷¿Ä™cÏ[DÙʇ€ü -µ$®]â‹#vù‚ÛQÕŒX%!\7tÄ"#‚åÈFŒàY^Èû™÷ÙËAôò)d¸A×M¿@«ñð‹c”M‘9¢5Ç0ýLZ=ã1B‹®Är·3uE’Ëßf*??2V—/Z% * $cý}·žú«ça72zšåæÇ[i³1”dpí&o"ðo®ÃDÚ#6Êué`=’y½ãŽÑc¹¹.ŸÍ&2É $*lTèÏ*=ݸȾ™mA%½5êDÖ0©LZV¹_,Š¦Õ¤“Ý6’‰è¥+º™IGZ¡$V´C³™¶Ñœ)0ºàƒ÷°±u˜Ó8à8I@áðYºJÔLËP\âh^�GX:Q¾y -5š!pâ·£íd'E ã!á’ðʉŒOD}E˜¬• ñ]"™Ò–±ÖãwýrvGL*% baË@ê;Í•rß0¢¬¹øC¥ÆÐ*¥‘ó_‘¡õ*Àqʪü0Š!d²BAd²g¡sH’$R2gœÐ¢ÃjiÑ……ÁýCêp…”áƒ't›ÇU&`l<C-8{ñõžŠêÇ‚Ò²y[,Cª 2fvó„©ÙY}}[›ª«³¬_)N/jÜîrÕѵŒI£ñ4=Ò–ÊQ90 S³|e B_ì¡©Å0Hœ(/@%aÒ«å€{‚NÛBØÏ -ÃÛŠ |t^Äl…6ÚT_’+Ø“†U€pP1ô h›ÉÝpâú3ÜQ“N³JB8pLµØŸ”GÂk”þ¹„ÉîÒªü�i•µMÅ“)°óÃ:v@´ Óá¯_GÊ+’ä(³ÌË9ka|Ad7ß3|kï¸Ü?vX‚øQ…Ë–(õ:RwbÅ)u8Òåv{25¥ó)`w|•Þ><¼w¸-m¦/Ç¢f(––Ÿ\*0’àØ ¹ô|Úq‰È°—bpÌÁŽ×‘rNµW»ÝÙ$‰±>þÓD'<ŽÁ™ë»‚/¿0üñ+´¡¨’L˜z&ò\ìÙKʆ…šcó<°"‘5c;¼oÜò×΃¬ð—Ñë! üüØ£¥ã7\éJu†ÓEˆ;hRŽE_˜“Ÿ“BÖb™Nq_¹úYÅÞ¼dO4ãÛ;‘ð½v •ZVã<u?©© %ÉÙ -@ÝhBå—¸6¤‚È‹]¶M3™§’TæùÅ~G6Ax²çê<w¦Ð!Fîý¯„¯¾üâX1Ül`›³›ÓP,éãxÛe@Z–ƒ¥_tRŒ¯ÀÆ{a™ŸkçmHŒ™3L”ê!f>ýƒ²_NÔtÝŽÊ=18 bVq°6 FÄ€žx)À™±¸Åv„9e„-Êxëà®áø¬Ñ5KݧÂ,Á”>žÈM ¯É‘i~Ûh,UÞ“È,›µx…¿gÿÛ#‹/ÖFF5Š8VÀû mš Âv'~ú�ÖMÎ�…()«°8ïïm•Ü¡ÌḲ–Ô:T>ò¬À`3—¢/f"<�€ˆÙ…²îÍÐS{Ù+WIáÍÈ$^׌—L¸þ”ZSë¶~áksT“iÔ)« ü=€5"!z'8倞êê ¨6ÈŽ‹çæØ2ˆm*—4ˆ*žüÁa?üK´±“Z&Z IòóSyØ8]N‘7ÀI“&ÙûúøG1ì@b™Ã ¹‘géK1öæ#A n -ðt#$Ñä!PQ¼ %•W¡„ýžóÄß¡?I—ëÿ|Ø¥'m*;%S( ‡ñKs’>Õß18ï'Ý⨥ ¤Ò!JjÂ`Ž,„€ÛLx°+ ‰e–»¤¾“C„…(!RÝ0j,‹ŽÒ=3|6…@'ɼ"Ÿ{ðwkUV‚(^~ß~Ó×¾—ARýô99}º3úú[¾'[{ú6’ÝQæ–{¢ø±.²‹Êéwuí’Úø¶ÁØ#g5�\ÞŽÑèq¸º%"ϯë<LFQÕŒ·—]‚b©ÔÞÑÄ—�Dv+b:çIºÆæJoôdž›fù!‘£æ"�QA+–W`èM¼i>“ñòCö9‹ËÒª~µGdó`j*>~ǹ&±êÊ7Ü4O§ULI¶ÃæÌɆ¨OX:bÓL>ð¸$l�ZZ¬¬±vƒðãéKë²z—VUU•²o0ðÆÚ_‡ïlV>¦0òPpϞы/¾8›EÞý1«Ò™ïE©ï¦ŸÞ½âŠë< Ÿÿ¼‹L-]ºTXVZš•*Ðâ¡À �uuᚘæü¸IµƒgÁOSé ps%3²Ü‡¸©*3zÔníZZƒÃ!M€1*>ɤÝ+¨y±ƒ«oÏZr¹„¿}—-¢`…ãô'Òë?«7ÃÒ“AÞ‚*rðÖm‚r I.kè#÷àÓº(»5“•¡äE‹=¬©úc@a!Q1¡f 1ž6‘ød/kG^3%T!\Ydšx‘g^T5s�…5¬™êè˜J ÉÕMçÛ(ï_»¸ÃÕdTý‹OvOB‘YÊ0üî‘ÉΔ¥ÚHéD‚§¸Û0ùnɤX' t;¶#|qìÄýž¸GI„ «SHøYC×X»70äã -ƒÚ€éWJJXF•ñ¿¿¯'»é#ñ‰•£ðrà¼X¯Ñ¼Ñª+Ö8/ð£J\›´±ÂäXz8Ð âŸl0ƒÍH>ŒÄG }vÜQ.õÕ%îú%•¼à†pM†bs“j¦aAaÍNc";¯ÏD'ôÙíÔù%––©ƒ~88bQÆ}2šÍ½cÑ\*=}¡aÊÁ/¡LNó¸¨E»ÇcQ^p–£ÏãÇBÎj¾á”h€gì à ¡¶ýx__“«â;-žyXóð<°?’t(šu7øbˆ£<רÓyLÕ—Q£´[‘»¤^¨³}ç;e¥½»çšf—{Â[áN·f%×Û²žº}s}¯í諮ɳ߈RÓ+ð9Y±öˆÎʬ5½üK_ò„©•ˆÎj ¸y“SBxßKAB^Å&usÀ‚pXx^I¤¡ÀÁ}9ƒ‚ò»wv>üm©“ méÇÏ`ËI—¹÷½žr˜ÔÅŸº½ÊBx}A$Q/ûÚ·ÆËÌ-G”ÚX¹n¸Ö¢}C“½›ÊóÛá:®áUD홉§(ªhvÃmT›sT¦E›GS9Ž=n.«¥c um©n=>2b la`Ñ7ƒI34ªµ¨ÚZ±x,TÉÀ•£€aF;Oµ ÿÎK?uK‰,27¢üîr‡†¾%–(n9àº`9í…HÚÞþÉÉl†<2¥wˆn:�«Õ Ïâød\æ`]^êœxá ¯GÆYpg—&Ó'ÈLxžë„•Å¨]☠¬Ç~?‹¨¶û§y×+¯ÐÜxÛ -ÝÔk>xlšØ}º}²,Oé‚´ü³Q 15÷!D`nÖ&ªp;Ó4b2œlÌÍpŸE}lš´gúûäµÁï„54æ`œ¥HnÎFÁÊb§×ÆJhÕòÅ==ßÉ°^§¤Fåi¼ÓOðù7ž\/&b….‡‚#oªs ·…>öaBŸXT݉~A|‡w=ôÓ¾åŸùR¢\,@„À7ikÍM)ä±äI°©ÝaòžÁ…—´ì€ò›Ã‘>0˜É r!?dd’ಡÃ;âÐ,£ÞD1”F%«¦`ò Ñô›1ÙóP?ÐïS V2G_é*‚ÝVŽ¼N»›O÷“ë|Øòÿøñ6&ÎÎÑèê]‘Ȝ㉙{Fâ3 ¯S>2•)ÙL`5ˆF½›Zû<szòŠ–˜BP¾I±u˜í…†n=×ÿ’læCF¤ÿ�*Ægg -QUÞ7>þ77 -’MŸ-¤]B¡W"ÞþÔ[JMá¶+æúFš‰µ†"To:xv�õQ7ü²>êÜÔ¬M¤¬ n°X²q”p9àã44—χä‘Á«‘¸ö§‡~c³è_c(¸‘,ûô¬RoÖ7ü?{X HÑhŒÏ»x„YÈBõŽÉ‚r/ÀÐV€äe¡ÁR•ø}™]` ÆvqZêýü*|Ñ`¸‘LfK0¦�|”Ð#F&+”¸ñ´FÒ(´¤”ß¹ºJ9Êx+ê‹;(ʇªÒ ²%d¯ß_ì´p–Ói[t<”.ˆbgy×á¶[NXKgp0H*¡ÁTa™Å°~ -:Ð(Ñ?Þ²n]Ö#ð$ ¹nt£îÃ�®ÞÎÇ™;Nx7V3’£ºnìÅóÔb¨äB%.Åh‘LŸ®[{|…áI-àiÇ…>gIé’bî–©ºs¿xë`ƒÓå>ëóÀvy×6ø¸@"G•Bð²ÔÍU›N_üº¥ÙSáXÆ2»¦ßr¢'©?N‘½Â«±i0PëÞ Å4Gßz¥7®ÝËX‚ FW{}LuÆ ájÓn‘·Ã{ýè)YÆÎû;ä>\4gNô¤ -…Až:$Û'§ßñ$%›mm’§?F -ÈB…$%¢£|ŦËëÏ<Úí³ìß¹xòZ‹šÅÚ¼4_ ôwâ$ŸÀ7K^i6>‹z¥pDÓô ò˜cÖqZg#áü<ÕS· -¿ð{ÛÁl(ôíؑ˧þø!ýã}Y|m½�Í7ì:ÿ}Cb¾ËÛØïò{„2¾kcÛÓ©ïMiüíUeù_¯)(»hו5ûçV”GQ-õâwƒØ^Zû›£#ÛGŽ»|>h=2½¨{Õ6í9|°§õæiv>‹Ô˜M¬•dsTªèœöMíÙœ|ì¬BsГcý -ºd?„U÷+Úà¢C„ÖuŠ¶¾ˆñæÖxCs$[j½19™HÖù"k)~Á‚ë\#z‚@u†3 V´¨°2ˆd$ ·—‚kÝNî+zHù–½hÖ/÷GWBöÝA®=}˜vw•cýœÁuÓŠÞ‰Û(0¼Ç3‘x\EÙ0o®$B«W¯6ÔÎýoŸrQQ‰ò¡”ܨª,J«]M1–pË%X|ˆæ¦uoëpMá†5ª÷?eß*Ðpfï½'L¹}Áx9àað(ÌB,®çä虊ÅèÔ³/´±ãä¿Ruù?ühEâ³²kf¢çû<ƒ'ËŒ³·B7À -p�P.T˜Y6Ú]ˆ?œåXÓv\IÃTGÕZ€Í–;§./äÉïuÓêÃ(@èàk"OÝ>ž¡¾ç´ßóûàb˜ Ö¸cªÍv*ž>Ýw^dTUÃ| ¹€ûA’ã7yö6¾7xÁØÌo(šµr*•nÎáÙTÊøÉáѶc'7?RÇMÏœ}Óß.ûÄ«ÈBL¨*mL¬›ú›Ûž|¤¿¿S®¥{‰_d†ÕháøÈ&XãÓ×þȽ´…tYþ—“ -¾¿ZlO¿2Ò#”—ówéxq1ìLŠ -Ã5”M3—æ8a¸g²üµ±0 gyƧŸãƒúwFQÜYu`fä -€‰9$v>¹V„ú ‘Y‰*ÎRÔVxE£RñÆJÂNÅPvÃ#ëØþÒååVÀ1ݾ a¡8TR©8w5¬äÿø‡nä&’²™ýë€]Eå‚žu¦&”ŒPD Ô›Så°ZÖ¼4ƒ§Ã[òö¿òÜ”³¤3ÌÛ´¯A¸ Á׉EÛuCY¡RÚ¼¯|ã–Ñߺ\Éæw *5ÏN‡&£nÈûÐ#”lœW_îóu‡Æ”G_×¾°¢N…ëÞ=ÈWoLŸ¡RHÊ3Á¯o“r&U³;都;b.åxj°°‡`8?|m£+g:̬D5Û°T»é˜òT V‡3ìgQ•vV›j¸ÚõKʤ#¹'쟓»ïÏ›ä‚YÕ"ïž‹ �·§øCB�Y4ÙHøÅ÷ߧQy2ùv<ƒ*J€ÎƒƒF†¢¼†9i Ç@õšy(•žc3©õÇ¿€¤Ê\—E »V8&+aé�€p™Y…g Ï9‹.«‘«~é²²H_ϸIL/O½.±'ðŠcØ[aTúµŸuŠ–ð›y~˜TÍ‘g2}L¤|ÁËšq[µµwŸàú‰k, —"¼Î´˜’L.g?v,O%9j@RÀòË^HSeˆ£ÏAUà\¼ˆwÃò~&“6ðí7N%»^~ø𨙯ò±‰¡L{Ú XBÔù(…ž‡ô]±b0—Anä /<QWÐwoee6<BÚºàÓ7à:ÜО5H>6:¥ÜñÞ|×`³Ã¡Ük#·IñDgtF‰ŸÍ+‡Y§ÔF%=ò«ðÍÍï¸çé{ÿg%cqŠ—ŒÙ…Îâ^IVP¶»G-*4›9¡QjšÎ^&¼‹Z8!?)®$Ÿ¬÷zåCÁ°WäE%˜šSÜÈ"˜»7“˜¿¿˜¿Ÿ}Ó—1ªi”z¦-=‰b¤|¬ýceN¡}`lH½×½_¥ä›ûðÁ]ƒ=›ž˜0³&}>if.g Ž -LÍKm#$qûvd‚Êì3áa;$Á-wÙ ÜðC=þ¦+ÿÿÒªh,q$'Ç}@–?À„z¬±Ã0ˆhî3Qh¾ÀdÍa°•p^»Ó)¶,ð¯Bí{½ -Ù÷'¸xÚyx| «—êƒ59.Û<à%IBaœ·@, E’’<K{÷ѵÁi·š´³Ð°$#¶ÆY£ˆ3Á±ª‡áÓٜʥKÝ]¯ LQ±ñ®#ë››If7¾±{<ÏN%{œiîÞ€dÕ:)vQØõRŸËa±Üµ§_$NÅ"¹ä"ÂÀ.ÁÜÌúZ„.lp_§D:ÑY$™/ ÎX ¢ ñvñy5÷ÐþsXñ7”šâ~˜„ØÅÙûtÊ‚kGÝ®^œr?’wJa±á\ű¦ -lM§»ÀaqÁúÏ¿-—šéÄd® BË—cCnXÍÀÌvh`À[ÀæNÈtf5½õŠªµP¯§GK6ÍòŽlÈ¥“GÞL’±›JÆp󆦎Ç39]bn÷ÞéË5c—!¡Î}#ê>á¸on…¥56®ßÏœZÐÈç±Åµ!‹áY³ègS•›)ª2+˜Zâ/χ¾7¸(ˆ)³Ì¸o; úÁXwgq4ú‡€""}2×2ÈÂsq é†p„Ku®M¬®]Ÿ]ô^„Bgy>îr™%fXÝõÄ_^QÛüCGÇ$Ñ«+w%<£µ`<§Ím=Ü7¹.4”ecâN;|>:J |e Êö92¾evLÒ›êó߸ãä=99³`è+:uNQ8P¶+d\’ûü¨‚ Ô@ÄõêhXß) -S”¦çT³6Gg +¥jE´(‚Uº¶l`i`<3`á]Œ`cµœ'šiBÓƒ¬õè6aœÄæÏdd2ÉA—ÍIéI¥?Íè圥'|¾D&ê`p;„U—‰v,¹��@�IDAT¬0Ê$ÅÔÖn[Š¹ V38e\aÚSâµé敨–íšSPD»~®²Ry}hÊÔê¡hÏ™¼—¿u÷…ÕŒ°…eò*š3òÔ¾7ÊËcO&¾ÁA!P³¯£ø"4%WÂú¸ÀîIXÆ„äSKJ]@/œÚIé»0§1! 3¯¥ë -L÷JÓdz•TæuÆ%TCF¦˜ÝOX–v/gú_h~—–ÛÉdˆ›››S踔ÌR‘³%£„ö!†‰cÄPß&ê9ªp÷êJJÙ“ÛßÞnÛ¥”ÛÎËG SU Ôj)Ô„¦[/`Ÿø÷X*.ÁÚœKüáîØŒvà`FìA®þi@æ⸓WPº[ÖÕv‘åï@E£Xß=UùǺ¦Š€ìX ÒVEìß>AèÑyëE@„ Ý$9F¨!þî„':wwÜAYP¸àn—ç¦. ’hZÜè무r—?×¾�Õ…2ðÓ‡,Fbcéá>$°™1ud(ædt{…$5§%˜9â{³¹ùþ6¸˜Ôor´Uƒà÷Ó‰‰þÏ]ð.ÅçGTŸa:ßÄâ»H6ÌŒný ÃQE]×DAú ’š—@U9Á²•™a™îU쵚õ4Nï‹8\NMÓ‚bTzSM'ÂS“û¡ÉjÀ¾>’ó³/·±<@Ÿê½I}úZhæ=ÐñØÚÙ.‰ñ¥ÒÌa¡Á×IB 4Üèg¿pó\”—:Žì:^˜LZ„d©‚zAÕÕ QÕºQ�¾Xé{Z¶þé ² ¢_蛑9Ø}6`‘Áƒø»Ïü§:ȆS»b¯gûæ0 «ì /7Ã`6ëÇXN Ü”Ä:”8F@ OÑà$ѧ·ŸDü^BɳH—vz·±=á¢\ý´è¥dcöiC˜LŒCtôÃÜ™¸yrOþåk—9³¡D;Ü©¶`lè…é6�-UåUðD1LÙÞÄñ®yÍMŽ|…å„—`ɹ™Mo -|ž§I›w"sS‰Òþ1Fn&áŠ3q/ëïKظ®î8:¤;&ߌÚü�äj¡ÞøI˜?@õPX0©Õ°„Zyƒºo,9‰…³(zç'¨ +++É@·šñCbP•‹Ï¾ ƒáË0µJ±`—YUŒs´ð’ñ–¦±"i3SÓâo¾»VŸ|7^4ÛqŬ -ù?ùQ¼ÜÁ‡Äœ’$2qÕ@ÊŽ¢gÀ•rƒTºÎ’ti•}ð°öqÝJ¿_]Y,fbg/PÖ”äu*,ÛîàŒnDX-=lÒÒuX"¯–w6¦å^�¼~/*¢éÛQ.ëFYÑ°rp�´ÏÔª aŒ¹¸KeôGª§d$ÔŒ›à,}ˆ™RˆYúc[¤YfÙ@Hž:œkl¼D58æ~úæsv“3Þ<Ð7¹¬æ/„&d Ö]\Qß“JJtpy|ãÍÍÙ…êX(Z‡ÐkMä€bζ‰Ò~Ð…]Œò‰ã¸v â¸uv§=ÞSŠþUI¦fûâ3‘ ˜Ë(©¡¥³ÿšï`ïp¿ì²;ÎC©ñ[…¾ü»»ÿc -±hjÝ¿Zé—ÝμÅÈ_^…ø¬&6Õ†�®[!¡ûiY i?¨É^4îϲ©½â0é¶e³g8U‰ ¤EJ™Ó‘¾ïg’|Ò->µØ‘¤Ï¨àEŽép0ÌC Bu–µ’„Tâ%5ÈŒ{AÔnOebíCÏ=¼¹¹Ù„WÁ_Y»V½° -½YŸ—_ z==zó§V¨¸Þhd¢G¥øJtó%à“®šÑxúõ—ßBÙr–¡òJÚøQ;d_y.Ð9kìE%ÌîW^Ú*K30d¯‚ÁRd¬Z¤’Msã´‹T±©™›ðÝ·÷eK37ÃØ9‚ñºAª£‘þžƒRpìfä´¢-È* ™ÖäÀ»¹O>È~B™_P¶ÉT ZÌq@QÝN~|"{ÂjÕªro÷˜¬•>—å¨ZÓŸ{dïÞJEYE!mšK ½:¥Yìn¨§p‘¨ó»ÅÌTÁ'”é y›/ˆLo‰µ d!úŽSI—ôú3 «öÜW*µ¬A¹±«z`Î} XÝj${6*šq÷Pë5 âäZpãݞƅCòñ8@8€‰¿3ubª¬F騖ü0þâ†eøéX’¹Öc1‚ïÕe�Ã;Žso¼-×-ÑŸÝ<«†ËI+ëßýlÀlWÉ]iÃÜÅ :U0EÍv?þ®Âœ'eÌgUV’åù»sG•GLûc´@39š BÃNCH Û¢°}Ê+/)g”Çà¦ß“Ñ©ÇD‘>;ö(Èrß4UØWþSи±&È^›ƒ�íK§û`%_‚çk¨®©Â:‡‹_H4où5Ó0?§z\ÎÓoŽ_Rùj<~Í·*yôŽqªø-�‚‹šh@èê)P'{¶%Õä(P¿� ‡[ÏÕŠ¹.*ó_‹ÉUHÚÿã‹ŸZ¡QœâÔ¢7}=bY ½ ¿#”²Š| -/ÚH^cðì-”¿Âé¥kX“±ƒ4è,“×aIF¼2z4Ã=2þ‘ÚýµZè|.°ó=ÌÀ…Þq÷²ô Ïp_ÔŒÆÑ,¾‡³aÓ2rºþ”@1)À„¾ã¬ï¨,»Š´—’1³/8£¢ ¼Â›çý²ä.ÍP#/ÎhXŸŸßdó2øÜå¹’èCØh#ˆ•œØ(¾5k᪹¹ç¯ÆÒt_|T^yQ7ŠYçùÍͶ7ÃáŠÁPQô߯Á…ò#0Zý‡ª³ÿ¡a£÷é?õç{ÞÜ\â=ZÀy †W:ß–Ép™rN†IZeÔb3•Q¦2ôžõëÏœÕKúž±^·ä¸¦šiŒo·®We4éjŽ™Æ“ų£÷¹Œ´Õ¡%“Aä€;¥•õC?² uÉ3-SFº$çD½F!B$Û,YÕ>üÐÃû¶ø’›£tý<xßD-õ¬E,"æ8vš{YÿÅ04ÞÖÔX"P‘p0¶†¥ØùœËYk›0Eõ½j<ð´hò»œ”ú"¬Hà]jç¬ï�ÒusGF1Säó§ ¢ô¢töóð>?ð|Fâ~„ÃwͬŠ‰á©þVIí¢5}KkGèT¹3YÈ�9sû4â^œÊ#;Á.]Zšùf{jïý÷O²ü0vàÿDbè?Q µTµZ’gbðO¡@Ûiž¯1‡‚ãrzøÏ#ÿ -Vîi#s¿CÈñ+†v¥¡S„…ñ^[’zž¯+ÙfòÌó°,-¨:$®A°9¿¢Éê÷ˆø(Š)xä6N¿7Ên¯ÐêBÔ?œN¤]Ø'²¾¥–Šk‡ZŽêŽÔö¡Î}O'â©$º¶æ -R Ï¡¤ÃÎNÊÊl b!C)–§Ò«ó<E3¯½ûWç¿Ú«$Ùÿw\çä/†Î½Ëÿ*”l_™Ÿï–þ/è£r†±Xý(Ê?×5ãý»6ø!ÛÓ¶•_ž•ï8r9—U*Õ0}ÁªÒ5kþf|,Š›‡"ç<Þ3ZÚ -2ðPQýå…w)Šö$žãþ )žƒM«á†_¡ ¿°³ÀLÍS5.1ŽÍžx;üš¥êPe»V™Äš(* ·"h}ŒjkËƪó®M¨ú·°ü‡l,ý߸Í{�d•¹ÍY溬5¦Ôl9>2㮓ðµ÷ê“æ÷Ê|¾—Ã@™ÐK(�Oœ£ÉdÕ&IOFFþÔWèÜƘ‘Al’à¹?®9?Fµ·³wÀ¬üæ7Äá©ŒY:Õ@ûý¢¿M¶x.Žx¾=i³œþ¡Ã5ß>èþ áM–ÑT„LR&Gv¯Ç4Åê$×ïk?|\UÕ-ýTüæ“ê7?�”VC�Æ Iêæèª6‰ ?¨ =šD ‘zZÐD8>t¸Ù{N>ÒÐÿ׃Xš#ÉA¸:×aq@¸–Ú¬§Ô§ì¢¾¡ÔO*ÄNP¤õº:Û 4jË’¦Séð–ðS@” ¼u…jØÀƒúÁ<¸ó]X�že%îqq²'aÊSçÛ‹ø#�+Ì0ešÀoyˆ|�Ï!{XÄJ‚¹H,°©•þìâF\ÕǺ'šì,»|(;‡ün2g¬îÚ6ܺѲHi4™À!,‘ §óXÝÂ…žƒÜ†ÅEù*Ÿey榄¢I,Ï-«àÅþ,‡±Hì¨ó¬ê~O�ªLFÌNÓ ßþÄÜüì ~2mIvëwˆÂ\ˆi†ä}âEÈdA6ƒ),Éñ^þÊXb+ê‰qœf“ü#Ø%ŸB…Øh"š‚ÅzBÉ™Üé3¼¤©"’?¡Ô—‚/c"– -´õF*¬°À€1Œ½ªyk&Á™±Åpù¼`‰gÏ^ƒû:©®fn\´h¿«úÑ@GÇü’ºŸ n½ÅR5ª -€»oC -î™èîÉo}Ãã==ëÌóQ‚Cx0¿¼p x!^°¥w‚<Þ\ZÉ”ËQýà–•m“öž~8,àd͸lúeTŽ)s;¶ïzÃñjñ ”1·/t5¬”\cä ´IÅ}q°Æo:z¶¶Å‚ÖÍ7ÝÖÎè5 ØÝ^o¡¤Ä·NhÆ°¨slëÖÃØ|³‹/fâR„Ú›�“ø·Á±¶Þ -Äx+æpšfüÔ`o„’;íÞœ—©––~ -!ŒÓÛøQø÷¾Þ€èÏ…¬¡¬J¦b z=Pn²Çð(®I&"yí d‘ÝjØÚ@>ŽqWëp‰G×_‡·a~þ¿ï«Ã|„ÞŸ à‡Ý?Æóâ‰C.ëÂ8úÒÄü>ã]Áˆb[ÚÒz¬5IÈ`àv$‡ïή[—×ëOð[‚ÔÀtã6A`áÖÑp‚å9™*}–ÃQþæ½÷l_oxCY™¼m<Fñ–J'„éñ>ýÝ3ýú¾-¾¤á†žÙÃÑŽ€Îè[’šù‡‰áT÷‚¥¥| -,~}à H?g'CPOÆ}v7⬖åñ„äÅ׳K[Žì_U¿è—¬aÜ}¶9à - Ös¥¼lªæö‰žÉõ뛳ç9zt('QàYlŠô¥°@×`â‡EËÜÉÛDªßjÆÏi^üÿ„ûK¬¦¦Ï|iµjÑ7 c>ePU×ERB¼hÝ”Ȩ̂:Æ×2QtãN?¹È¡°€ -Z'³áSƒ'È(�a;d~˜Ëâ*†«ymâ]{Z[û×æV ÛCóNŽê@�‚ûþlk®™?ùÖ@ô¾¤˜Qwïˆè³æçµ:s%Ö®c`<›”,æRFµâ!Gq1`dupëºz–“ƒÝŸ›?“”l¿ç‘½¿J�“/èí3Ò'ÀÁôˆ´Ö,f{!êv£žRžÖrØ"-&WRg«PnðÊàT›Q×ÉEXû}KË‹5uzAÍV èR.V—}¤oøu€îê ÏðRv!-g -[QuN‘C½þùA*þ¬©½ôìóÎ79º,d°;1ñ‘T¤i¢0{:c\¥ÏÒY«ð¿ŽÄZ†2Ø?ðÔÑûo¹%‹õ}‰ãXÊp§uó6òà°1=íªÈÛremüú`Ñ5¥,?öq¯ˆKÇKÁäôÜzQŠª.²N_,àR‹XlXÍ0f7ä7\ßàO‚ÁëÙmð<Qùl¦Àé1@*ß³cÿÉßœ™+ªÁdz’±³LŽh¿‹²Jn2ŽÃZD½ŒcöÅÔA -Unj6a^<™°®«ƒB8¬TΟïÉ0×ãñ GŠ()Ï–£·´t6ÔÖò~‘b‰ùatA/•lÙÕ©¾ºµÙ¼ÉæãþóiÇæž°³À-V.Ì?A¨CTv½ºKýÄ:’‹Òù£øJ–ž�ÉÈQ𻕋¢»�ïe-èÓNwFÿù¾$ܦ[<€l"h’I?mTvå»V© år½¬Z¶æ’üÑxh¬Äí–oõUeYuÏèFƒ?Çå0ú¶¿d’„Üs�$.Tn1˜²Ì%>¦ã«;¾{Õüâìg@û(Æi^ÂÎ^Ð,mYQú¸Î›¨ý±±í]G›U‰sM·íݯ’ºzãþ°“³Áßð÷I<÷Z¦í£+ÅsÌžåÊÌüÌþnÛ©RÅ;O;8ŠccV'ÐÝ(}Žkô¸iém:+îºsnm¼#˜žKó\OK¹ÖkÏz�ä•^Iž âÒF?7M~aïy°>îÓTú ¯E¥X,j7Ô5¯G4ïÉqì™a³ýîI"v'3¹ uÿŽ[ȳŒcXM9EŽ¾±øíèW0ûsXuônÆ„”ûY~Gnðò/ßZK§%ÊïK~¬(oäíážž55¥=å)ry£SÉäq‚xŠ&r1/í.;JEaV±Ïß³¤À[Ô:nË)/w[ñ,Äiu•Ø̶¶êú¯4Yœû’ñØÔ,(Jn~˜ê«ó `?™.a-”W1·cþI"¢©DŠ’MFTM¹=ë Ýnl<Ïéˆß‰ìLÉ ²ZQÞT• -±n¡kë¯}“Ø(É:MaR–ö‡ÒÅ–Ü$‹MA_™1…Rý½©´òÇUŠ™o£åèì³ -‡›OnÌäû¥#Ç&)Ÿùº:M÷o}øç¡;îh6£i¼÷ܧ@Ôႉ3 b*|lÏ ˜ë>ÀŽù×KƒÝ+I¢ÏÌ@Â.‡H¿Ä¦Sír)Êá°çs¶™Z‚)MŽ†Çò™îrM2‡É3½óo\|R¡LSýêñtlkóáÕ/?Ÿ9§læ9(šÁ#7ó2œŒ°m’œbÐREK¦Ï…nmïµÿÓž‘·ß×Å·YåεÊÄ'²_òè3€¹Ô1åƒa-äA¸½Ã}Ýÿ²r¥íï·9³P8PÁ2öXU7ó7ëNŒ¥‰–z(p{Û—-kP‰õtáu·Vñ¢ý"–£¯Ç„À± šzü;]{y~Þ¸½óŒä¾dx(šøß•h~פ"‰)gÞ �o_…©Ú¶ÿ¾rF^{[LÏEþº[œÝ`™œ«êPöµ4ú)T¹]ºY0²wrza¯Î3‡r¤‘JŸóè¾Tp__ϱÖY:/»¸‰&d†¡Ào€ì½Ñ“LŠíQ½¼{*Y½µÿø$ÀßÚ¾‰p=¥3ß@Ñ€!k‰'ŠT¹mí+sìÀˆ¡ ¢oç™f¸Šà ¦·¤ÀôÈ ¥Ú£ã¦ôØ}÷°ô²‹ÎôßöEÒÐä3TZ?:03¿×I‰ µ¹ÄÔq_#ˆA«`Oʨً†Å|R‘å¶Å\Í‹’-“Òó»ãiQpå~åÅ�Q˜ çÞ“H‹)Yóš8`sÑC©¡j¿¤Æz×Æwâ6°ò¼}âï%ˆFôª]ÙŒž„Îdê›ðz»ßì9ÖA€ïä|ƒ)mf"ìåOX4È0,Ï@z¬¨µ˜bâiEQv6íÍKŠ‹§,ëÎì}ñ4¯ÙyVÌcJ[¸ §÷æfpÀ£IÕÌ[ž6µ«Òu!êÖêx‡Ô«(òâ2sÁ!²¬s‘Ü\Œxòf`öÜÐøÑT® ýG²I§™Å`Ç;9÷ÆT»ÿÅA>ǤÊc&è¤åøñKfÎTHqÙ Éw¿¾aƒ>÷ŠO;zàÍ=î¹KrNá¤Ó¥qK%XnÖ¬<)åD¾óÏpܱ/£Ï6íR {>Ç·ÊÜöQÒ6ÂH×;ןK1JôîR§-HþÖ1˜$ÁóÁè''KÜ^±ÖÅ÷÷hžé -Ùi¹ñO ©tAÀ_€”ænÊ0Ÿ1(cI©#ŸÔå³mpÁ!u o -1ñÌŒ}}}bë`¨þáãeâ¬ÜÒÝŸÀ8«O×ÎÙÙ)ß5[‹Ã·€‘½¨ˆÙXÔ^læÇiJþÅ9E9»š›³±ekd hGÖýF*ÇyÍòÑÑœwßa²¸Îuc¨CIgc"ýøLÖå„ocubÞâŽ"ð›ÎM°ÙŒx‚㽈ÖoˆÕ̽ró±Éšé„‰7!f}‚þ¿8É@®Ù1|¼‹ê/@5‰Ì}9ÒÇß©õwaés -~¸Â4Nç!ìQŒpó._N^ï 89¾n¦FGé‰Öìf÷zDFA.B¤þLp%/|>á ožsÍW>óX×ä¼»6mšæÍ ®®ÊŸ›ê}©(=4@Îi“QU\M¯eô©Ã‡;â/ m RØ& -‰ lPQMgZy²¥Oð(íÔ-öZ*c]ÊÍYë%=Ù´¾ÑXi‰š/·tØç9¸íÀŠú¢0#'Þn±gÀ�OY~àYᬠ-бd,ƒëݺSŸÜeA‚ñe]ËLÎðUToêÌÙ€„›K“ß Í𓚬߫|³®›WØ%ú;FBù2Óg‡´ÌXgCñadÝFr.rœ?ËÎÔ7Ávµuàŧ{É3 ï—\uÝx —BéZøC‚κÙJe–R‰g,û#äÁ·`AïçI;mÿÅèìüì¤$ßý(d®ñ@~Úe¹üí`t>Id^�^c„Sö.ØEÓ ;µ© ±‡w$x~#Hf?ä’ϬÎÏOÑ"#ƒ3Á„Õ© ÿÝßÿ 'óƒp ýpO(Xú®ñøÚ”i¿¨$‚™ë1ŸÓæ>A`ŠÓí9\Ú¡ú•ŸIʨÉßFÅd˜ãÄNzÉiеNþÃx}ß_rÏ-ÀyT“3¿Š&‚Ouä:Þrç»7Ë@1vÎR„I’–)ý°ÎÐû –VjœygSvñv"\¼jÕ!Jæú"ocF5aü&ÀAß3׿âô‡ç8(Sʽ™DâGžRïK`¦'.Evò®‰³ŠbÕAðzç™× -Ô -i×ôáp» -l[‰6öKzæðWgÌ�Pû`:nÔýb®26‘›#MU#தƒä=â7—VϽ!÷;¿ù—ßCÔ½T╘)ÿX6õ'QiuÄãHFîæsC(‚šDh"Ìn–N¥ÿòmˆQºr!$23â('èm5r‰éŽC¢à;0–¯„E\§ÓÖy€¼}£aaó¹ˆÕJ'Ï‘M2.òû”‹C)ÁCb<ÒwÍÌ™Sw¬«û»ÔÕ‚D\Dg^Lq,ÐA-8¸üm¸ïëçEµÆ%Ÿ¬_¸íÀDÞëçWÇ_¾v]¼jù¬uAUi™š…frÄÆ9ÎâDl©“ãCÇô:±øÆwå¶Fâ©gR²ofúö/¬\³èá-Ï… ¼½¾ µŸÀSz -;Ÿá¸|Ы=>|dwè²v¿ë ±½ó± ·VyÞp¢b0[BÝÅO)¦ÇS¤¨D„Gi^nŸSZà‚ÚÆn1aýˆbÔçŠ??ôø=ïuîw]êŸúW y€r´¯ÔY×…S¡ôKá–¯>Á]€8÷Ó¨4}ãn>ÍoÉÎtú¼'Ò¿–ª‡¬Ï›Y^ž5>þÇ/½Ï óc,Ã\üöxt徉øâÝøµÏ";pÿß+ÝUXt ‡ÉÓ0àî†7'Lz²ª,ÙV,)q…=Y(YeAêóYä×UUá)£ªÇ`«Ê~øCúϲøâ±eÀ…F×H3¬Ab•DÉØÈ%Š.…8’xaíf«¡H‹2â“`§'[¬î#é×^{¹sß…ØÁIRÀ†ÑÇPôŸ^:÷÷k늻NÒLäÂÞÑè¼DŠ¹I£õÛ�s)×Ëh²Ù×Þž]<§ûØÐ5l]ø�=bJÎgÓ#¯zTz(í8ðÚ¥bHöãPP¡JžÂ Bdž/ë6~SÏ„ÿwÇzˈEO>wúAîßv°domØxªw#äôƒ1u3,¹¬+ Lí�LÓ,3ÌÌ€ááû4±2ñp.”uÝj³WcQ#K"S¾…iʼ`´zÈ5=ËXÚo¿|ÐǹÕUZ_ƒï3{€£~²µ+(ƒö\ -5âÎZÿá5sk²*y @@ÙTùw¦¼Z’›‡Œõúáh4 ¨‘ÕC!XʯÃ{¹Ðâ¬Y*wQÈÃ-yº'\òtûD㜊ù% -e6£h×À2†cÂ]�—†òÑ"/ûÁ·›}ÿAyªÑ -Ê)M+�¨I—õo/;ï²³ˆ¥FÁ½å¾Ê~æ‘lÿ…Eón}"ýçóšVW¾ËöÉô©N½’çF¾7ý†ñb°r™D9š*É3ªéÙÈ=ƒí™·CñÌ•iŠ[7áIÖ×”ö.‡¢Ê‡•HšnïûñJJ©!IR ßB„t@µlÉvI’ˆÕ˜‘ãcؼ둇¨à³z÷øTýöP¨¨%t’ñE>ó÷Ú�8À�¼Ý–´øàßýìß;ÏÿöoX|QaQ‹ý<³ï£:íÚÑì.]5ïC¾æ?SNêžÎ®)ŒùdR‚*ËÉsÔ’lbÜÑ ÁêÄú[MÝä¢ I‘À•%øšþΙ~}_c¾ÓIÁ¾¥,(3cÝC}ƒ¤‚lD5jÀg[[ôö¼üœ.ØûÆà”ÆJ"·Zcá ú�=¯ç–å;8ú`į&ܶ=¡T l.b,4æožÜãKfS§\¡vÐW:„¼‹@>súöp7Ó*Ï¢Äð÷>Á<°²¢"»ÐM·ïð@œ¢E¥†Ce/ÌÌu'¦ÿFäg¾ýóñûBCîåfŒ6¢M¡„zÈ{nă5-íš®ì9üdw¨ä¬’*h}Ê×}Ý™húÍ':SM'ÈÂR}vi¥UÄÉ–ø#ÈL§—–®&÷7—Y¡Ê ÍšÅòl‘Á¡þ(0¹ÞÂÙàÓ½¤âÝ,ûcefÞ&ÔAÄ[‘^‹ãs -øÍ[w÷±æâ¾²œÌlc/FEÓÞþ£;/7éÊ\Z¨E‰ÞøÃûõ†Þ^ë÷ˆ§Oß+ymÆ}‘{#@ã9ö~9›¸aÎe×@Àå²Ûh»ivÊ4} <¿Ã¥ò lÁúf,„ø(X“¬EÈA¨B{1xänÕ[?¿‚ÕøR<ÏjCIZ5ß5KdŸ%ا¼4—wyZS}ð'6Cùp…/ȲÙh•VŒîùSçX•]È÷‘ÐÛýýþÞc£zIMŽÍÅŸÇ&d_ëøÐàéÊÑÙ ’sØŠ!}ù*ÂUýáTÑ0ÅYS¸ÊÁÓM ͪ°óôo'BúBÓ¨(Ì·%ÆìrjÉ}§Ÿ‡ôÁGù ch0šÊ¡†Ž†)%%¥Æ_¼ÿ¿ä@Šò™ŒùÿÁHXDsÔ<`|ƒ¤©I°„9l~Á¬¯|ûûU#±dIoÒòÆÌ´Ôu|„G-±ET¡IŸLFuŸÄW@P%³v¨'z¦û ‹/5œÚÉп†A‹tâ-sXþ6¹*¼[—8ÅÑ:›M~î÷¿×÷o~aгwx°Û_|6í,ªAu:½ý|ÃíV¸m“Ýñ” ˆ‰cŽÖ¸lÙ¢¯c| ‹ï`$0Å3R\–•÷ïD‰‰äà%vÐ¥O9„lLq‹¦š–ŒT/eôû -Ü]ÓI·éÎ(EaFw*Â^ rüSã‘Ñö‹û³;ÜÀ@Ô{(m,\í:HÉ|aă·')óŒ¢mÔJ}‹ìvaËVÔí?r„«-(ÐÍm›å¦²Zà+YÄ´Á -¤ÀÜä¾õoÿ½…!ßÂ^P»àL(þêúâœL¬aÍF‹Ð|¸Ç÷ê2õ2”äÊùþRÈøœŒñ-`[hj¼ÇVáH›=2'øú=Vq»±øV‡#‰un˜=›"ƒkwH³}%’„æâ°ðÇ,š»4¡ªŸ@hdnZ³ºVx|ƒÏî ÓÄŒlbõCJŽµß‰ÐFs%euÆÔK`.™ãÙisÓìHc܆…q9£ë¨ûÊYråÎ@¹Ø¡-/ßÍTFú¨' òïn-¼´ª&Œo*¦ê‚Ý2‚�«RjŠÏèÌ\‡‡%‘Ò? òœí8kùZŸÊ —±´á@Iw*ÃœK¶ïÉ4ä*¾Òå_Ê" ÷?ãrMÊZ -烕lŽ«:G²‹tc$åê¼eI}zÔȃjHÙyÁƒMÒÏy<“3‘êJ¦V…“©w¦bn^Ú@A#î�bÃŽeMw�:Õ´ÇL‰~F¼JÖø‡>;»"HîïÿOÇp:#X³ÀÐ /:ÆÕïf™èFô”¦«ì•(wo€™n2«÷½Õ}gƒånø@Ά”ø¨½,‚#Ød9M`þšÕ?÷¿Òs¼¯hlÌ óËk$#MË610=6Îdß%{"²Ý³X¡©¤üKº"ïðJ.†ñ†M·ðúïüÛÂ*§0ø^ãï#ÍbNœkš Ì-í¯z RDæÂKúûG;’X NÁ€Ø/Àì|åÈ[”é†[P«Ê%:Š½NÇÅÈ|Ï…*Ÿ6´9ëƒñg7µ¸ûáŠ*o齆bååÛ¹a¬vÈx[Y±¾Þ€'v`ÛÁ׆·{ÝËYZaq~Yâžã.Á?übãM} R Š£Ž�Pð’¥jopý‰þÍ•Ù‰OîX³[úc v_ )é}²½=œœ»r“ÍÆ£bíãIZ/z¾/4¤”48€Mý º,ÌÏH¬µ±½©:kÓŽ¬Hä¹²n¤ ÈGÈEðnËÓÐŒÉÅ6¬«CIKÔ‰¼aOCaÄlEW½¼iS€kÉZ·,=¿ñ¶ñX)Ͷ_[—o2JD¶q±ÚàGÙŒÂøV SÂÌ_†Z²öß»}B®M6÷³ìlÇåµ(y·(ð 0È¡¡@@wæÕŒ²”˜…D0àF”X«3Ú¼”¦®–šVÜ}¨ˆï8Íe÷8 ¥Öxo?VËøø‹†.TIëL£ÃTR«ñØX5#ÓIlÐ.᪪Â(å:’_MQµë©M}}}ö9g!|þIôíëvF:f…ñúsr”‡;;ŸL5ÐÌ‘�sC«æѼ|]½ë®Ÿ1ç|î+åvšYˆ$íÏmÈY»Ðâúfû™l!©i×¹û<BÁFe”·rúúÌXSñ³iô‘ƒç× Úïsð”Rª©ƒlç×Ãác½°õ³ßÇy²qEÕ²Y¾ÜèHd:>>ý·Êk‘芅&áªÈ ‰/GÑÌQ´Ý8;77±s,:ŠÐñˆŸA?ZXÌWÐ……x†¯´}�5.(BÍzX•À¾ÒnVÚæÌر´ÉÞLÆdŠµ9;S&Utæ{„T¨¾=y†Hhumavã$IÆóoøN™[®Ð }õ¦]#{×£"õ=[”™]8ˆ{ßœö–ÉxÇ8xÏï~Àoþ˯C¢‹AA8Ù†KU×-X0…6®tu]‰@$ÞúŽn0+uÕ´9²ë\ð:ÚêV&ïCA¨!-Ò¡„g�aËl…ýRot @+ųW?…÷Ag× BòùÓ´Ý®gÌ¥¢˜; -›…4Å-Û³ù¨Á/yèœsKQíU¾—“YxwvªeΚÈ‚åuí-‡ócý#JÎì_F3ɵ¨ž«ª¢V§q^1ÖߢTúÅÝw”6››AJŽM˜Iº–è0gPp% | aÍmQ-¦Û!˜}Sý‘Èá®Ê—ø .ÃçZ ê#èf§55uUjîjñ1¹ž«4XaŠV>Ô=ž]ß%Ýœü¢„ÞØÖþ§TÆñ*|ê`/‚šk‰“Jè2Ë#žŽø èûѵ–EbÝy¾Š‹A913£©‡ÁœÖscS“þ|_xâx›1ÞÔ2ôÀxàäMærT‰Üé‡PCKûqªj¿8ˆeê'�ôâ3'‹I¦»/Û«ñBµ_¤ý®»¬¢‹¿œôÁÐÂ,uæòS¨$Ì£\<ä–ݯ8¬BS‰§ PŸlÌ’Íñ‰$Ç~R¢Í:—ÅÞÝìÜ~ëIDÈõ ¨àèÀ–µ PåeÝf>wAAaʸöF¿u”§Ð瓬“?$Œ£Ë¼§‚ó<§¶+&eƒ×�úyŽ×h; ©?Ð:~ŸMÔZ<_®fŒ$øvô6ä·ÝAågÝé»°à‚†Wzz4GCÓYNÓòÐîhoû›Ó¬YÓðQxZ!¢ÈSØyÓpÆGÌdc¶vLDw@ü¬Ó`œOHù”lLÄç!™|!6÷› -Tõ´Ó-ñnˆf6 |ý*P}Ö`³_„jC²ÔFÔd<@;<g%‹¡åz¢êŒwI’S[ø$=ŸE-|•^sã×#åv úâŠrOÕõ^ kªÎ‰Ædâºbæ2§Â‹ÓŸ%ŒŒdŸþýL¾Â`üÇ,@vsx‘ BœnÂV[K,&’~1)ˆàåpÍnÓiö§&ùؼ@2¬¬J³6¶µ‘AB&8™4¬;ÇæÁè¹k\%³b×™ÙnmÍÅ׿vŒsQ öu¸ÿñÍí¨|ý‰Ìr?ÓÚrvÍ+D¬÷=v4ËçñxÏ59E,P¦9‰òÙ[¶¾0l -*Ê|Pÿ4JÉWTåî©hâþCµ…Ä¢#í -Vá´çª†Õ™Ö3a”N‰[eg™vN×îÒ|êû¶ìXSâ¹Ú%—\ÂGlF¥ZÆŒubˆ"çyTš×y‹æ1 x–Ø9îVß}ÕÉ$ùTpÿb0ÜØT1áaÂo'9á:¥l„G‘îÇ“~òÀ ½ì™¾©Å–£øìþ×£?RØ8ãycòGö”n¬FFûüðuoþ¯<‰ jáúç‰yr½§zÇÊ\KοZ‘™¯ÚŸÿùz‚ÈâDþ6}I< 庱)o"$‡v›™øÐÄ{T©‘Ï}‰Å+j}£c;<w¾Ã3wfxx~ž¶†ø¡RŒñź²™sÉb?}n“÷Ê`bûd÷?´¡¨(Ô20�h¸8“1ç:9úˆv‘äØ’úzÈÿÅZIE@¨¼"L™í[]šVÙ/>rl²+ 8ÁsÆÒLïHtôþ@×C½OæE{t²“dÓšÚ9Më^E^†²Ö"C¼}~Ž$}¨ ˜é>ù_¾†ÁpÂ.Ôš§ ‰tò`4ú9‹<zNE¨;·¢ìÍå€s6ºÛ3 K¶Q™=°9æpœYN+¤Vè—HÑ 1E.Ï‚ÌJ²ÞíBïù¤¹ ™R– •#ïó}€%¿‡‚¨›°®”#žýOSÁuõécjúÞ§__üõOûVÖ@RÌëÍâïO¾Oï‚œž[²|úsgúõ“ìsqKpN¡†Ÿ‡è™Ë9)o®úœvþ, ðB<O?0¶{kçºuë4`ñ$ª¢öc°ç7À5Õ–`!c³û½v™fj«AX3D©æ-¿Bp–vÃEÄü7wz…ö{Wb¡üð}ÅõÌl²ÿx\ ñy3Ò³÷ÇŠvŒÄ!Uéå«ýY^²p<úÚ‘‚yž”8ÆÉ•'Õ3±D ¨ºÚ"6œ¿ «ëñC-hM3e’…ƒ¸£[jÖDÓñ0d]‡¬¿>ïO!G÷˜H%—K¬ã¬vJ#·”#ù»2Ý[:‡s‹ó}y†J盂Ü>4î©ÎŒZΚu¬Å|ñÛ:p -ìDíù„a—Q,ûU ZJ›{&6]X[H¸'ðÿì¬Ö¼š2µQ¢ÿd ùDû胢èÁÏ°ßl�8ËPzŒè„ù Ãμ¼âƒOjÁåšf5ge(cªûþù™döŸíMx¢¸/ ÆwM'Ä8>›ÒÍO:YªrNÍ¢–Í*ix¡?D -i·yl/úâ—ëƧèŸC«¬4êqPô† íèaò©ìÒø¿×ù“»ßÆm’F87’TÕÏFÒÚ·õ¹«~‚‚•`Œ³¥s#Õïj]!äîaø[vo›Ñg!I',sx]ƒÍ'úƒ¼’±rº~ÃM Áé£ÄÿáîM૪Îõá=ï3Ÿ““œÌs€@˜ £EœŠÖY«b[µÖ¡Ã½ýßzïWí`oµZmëlÐjµ( -a&@„BÈ|’œ“3O{þžuÈÁ€øÿ¾û§»âINö°öÚk¿ë]ïû¼ÏƒG?œô -pãm1¡MdÔ[š~ò û~XW§à™RMኆ¾P6V*ÐX5¦+†�$n*ªãŒ$³øòh*øY #rÍoÂïýXTãª\:š-c+-J_÷ÒoN€žšƒŸÉ}h:Ó)'>‘`§UÍ:TÖ¸ˆÖ´EÍšÂк(žUˆ±¯>t8лb…M¬¹;ºˆsìÚ*¨¨´}ú¤¿äNClÆLL�»P¥ø6<à}šb>b²¨uÀ¦ÿ{áÌù@“úÎÖ,2ö½ÁR•U$Ž'ÂjÖØÅ“8ù2¼Ûëà<œíØ/ò»ÚøæÊÑ1˛ᙢ8Œ)pÃQ�4ÓR$¦Æax‰koðcJ4šEÕ²OI„X*ÇEe9ÌÙC1åNBù¥]c¹±P¤™nôÀÉ`÷)˜XÜÖx!‰Yøf·‹õ´B¸¨À,\,ìE ºXºÕÖyw‡ÙH“oY4)NŽ‡GwbQÅ`f=0t-U›öÚÈ ÅŸIã©Í—›Ë8 õ<öØ êà0ãemóªìPímñVÄHb`‘ªÑ¥8¼%gÚ@`.º³\3Ô”f 5´kû‰Ë—ÔÊÛúãþˆÌ¦bY÷šÃ0^i͸ս€Ñ] -Ò›¬¢`y¯;ñgªÄâE#Òƒ<©É“¬,_e.6oÅ - iImmâXMQS7øß�çÕ¢p£‚0ÇZ²5švÚœ ùõ#ê•¢À_R”1Ï™XS_RÕ—Tdá°–›ºMJ¼P3;àIß`€ŒÝ`-?ŽÆ"àpÿÓÊÑ·ã´C"ñµyË8ßÐäŠδ¬Û&�À0ÖÇ!(Nmì{¬pÍ»2ËuRŒ!ZsçðFí¼ÉHÊ]^Ú—ßcÍb*ænŒ•ŸEóQg¬¼tÖ,ò¤ãuöe#92ßÌ‹AZM5£§rO½ð/íë.|öp&J¨ô¶qí¹-G¨zª$·|ÜHª,…ã§`¢ì²òÉ#ÍãÑEÃ[JäTR]IûJƒnCìCõ¤ö€Yc-]©è¡+kÏ7ÌœäküIB£D ¡ÏYh+]Pðâ‹Ôòå:&*£qùÏÛek~ódç·øÞ°ÌÆt»la F3Îz…e~+‚Yk^Û•ok»1Ï–^Šƒ!|öÍ“_UpûÈ£'ÿ Š6ïø»’Û©YÅ2VcúÚ.ÂÊ‹eÈÿä“v~nûxHhiæ&Âk‹Œ K -J˜bÝíå.ÁÆéÆ÷Ÿ;ìíµi‚fÈþ”Ìw&�ò"‹“á ¥œ[À½À±tE«×¯ž ¤ ÿu�pEA¥›?œ€Ç§‡ƒzÁÄA†ÖÜ@ãêšÑ€õiÿu'cú¬‘"÷>`üžÇ¡Ãf�¢uÞ°‡€C¹BÒP¬¢ú�ÞËЖÉõ#’Š$Œà3Ë}LŠÛº© �±ÊO—±é“üè“)¥Z’ËìaŸƒC´–:ìAÂE¼p&!^Fº“‘9%kà{×½¶|ÇÑ{®˜–~™%¾�ªSQÑ>‹×Övd· dm…ä/“ºö>`yùP7ù¾‡—ï¬oé/ËœORä(bËN9)OÉò”ž·Íï·/ꉸ²µ¸/í^åbÏ™S¿kío_sÇÔ|_ÇcÆĶ�jD‰XDÉuRõioqCÛ`<ïK`¬º¬~d-/Wì’¢ÆT5焘EJšR;(]û;¤1ùOÅÁDEeV5Ä\€"°™IÕ„ˆ5Þ(�“P•ÇbIß?1Ô´è¹Æõb Hrñ쓧ªümC—Ì•é¬"·ò[3dUI(ÙË<ƒ=¾QªÃ<eŸnJ•|)Ÿº7cÐÉcYÓ(òõXW]‡þúi¾»/1!ZIqË~¸`oO‚ã÷x£V"NþÄ–ŽžTÓOU)W9LLRàYÓÄÍ%†û¤+×zøh‘c÷•U…=™¶¤ø†ýˆ”êÀ>¢¢¢“zIù•W -äÈÊOaé+µ¸ò½‹îzd¾?Î^@ôri‘ðz°Ü²’ø¯ÈPç§æÛŽÀ¨zöÃÝpjüªºº”‰§^ÐiÆRXlʲ¶kÖõ†n¶ -ÂÝðØû˜8•v€>¯}<%„ñNTâFVÚ¦ëÜ[(‹{‘aù?:ž<ó~?ï4çôûj¶ÛwÄð¸r%ÎÏz¯ÿÍ“ÁñÃF-îí -%ÑÎ ë–}·Æ‡3cÊ…‡¥æ#ò‘CCåÀVfY ©sO2t×ÁûY„À:è°íõÜçÿGmá)f4VN>=y,7ìëöö›a¿‹ˆÄQö.àee*™üÃê[P^x ±Yö%wäC¨¶Z4µÑ]¼ù…'Ÿ” Nòl=J +³mýBD=zÝ6Éu‰bêm3‡"–ÚH2–ª¼øqVÏqp?x ˆãRÛ"ñ%è'¦X,1âus¥®%Ìgˆ+v…(ýå{&[Ò³†L\tÂÄC5 ·åÆ!€cªŠwîɲ„̱`ˆ¦,QØFÖíÕ˜GRÀñV÷'uÿÍãGÇ6wJ¹P³òô’òÏÇú¨|‡I²ÐlÚNéT4GךçÜ÷£Kž¹à[6ö†åø³RÏ IɶéŠÌÊ ‰Ÿl¸ê,Ú<�¾ùžá÷¶ŒÍ¬Ãs$ýƒiÈ¥4”›î‡ËFõÚmûå¸QB‰zã–’Âô¾ýU@}üãéõtŠúñf€Ûõq¸÷KÌ&îÐÊÀ‰ÃS,î -¾·³úó»Çö’k ¸È,«;\Ëc@\ïGUÖÁíãrûÉßÐ}ÔêF¯E˜;�å;»ÃÄsç™ûW%ÍVzÞùÇÿü›Û‡Ü%9~—gJc4Ô³£7PÞüäõ£Ó9r<Ùò]&É^ -Uzýû7ïÝ„ñsKÍèƪN xÞÌÙàJ'þfü¿[VÝŒ"ù>ŠòÝSj±¤Jo(µ$ÿ7ÁH¯„ZpH!ÆT7*À>�ñÁKð�ßNq›îùÒ½¿ÿIÏ~ Esiá zfa,,ÂÄÕ¸jõGa mcQQ‘Aà›gÛŽ÷¦“˜mbØU<ÛÙ6a°Ð.Æó-|Œpbœí˜/ú»Úó%„R2r˜yÍ¥ª£=Ÿ–òŽBÌP¤ä75UZ)‰16Îú�JkB±ÜmLB¹Ï=qÚ$žˆ›4.*pué‚. 2�¤ìál‹0”ê¬u£§<×ÜL2§6"-¿5 Úh†¾KÇ8e¿yôSœNÙtÁ:Si$ç@aI"\ÓSžüÁ˜<ºxB,Éܶôᇡ¤û©§~Æ~”Sv¯‹&Gål1ÆŸ[èhŸ½ �‘ÈÚÀ "Jb…å8TÞºÆEÅÙ}sPM†s2ê(µ$î>Ê=Kl4;ù™5kÈ=§«¾b»Ö5¡^a3ÆDNôl¬oG|ô~wo´jë±ca2òhaÂ,AH¾LÅx§må³l¿C²¯{Lþ:Zñÿ™®ŸùaÂbf,sÈØbYÅ!zUÉbGñy¢jº’âïœäiP:–Nð#‹hwûÖ¿sbňÕÆbô·&¨L¨ ”x¶qª 8ÙÑÞ×Ñvj_ÚVC;‘‹)Çd· -¥Ykšö<nY—•KoÈ®eÁÓò‰Ý*>óÊO¶ö㈷¼ò†;°¬â9Á‘Ò&ÔÝzꜸO[‘9·?!߶¦Í©ÁÈ&øÙ>šF–l™¬Þú‹ç®·WMD5®ŠÝ;cìЊºOó™n"9š -2£Èzð²Ë.“—TWÁTIóPdÆîÁеUs/YP_ÿÏC/3×û*>shiÓ‡]ëõˆ¾m=4£3m`ŠäV²àù×áÓÕχï=Þ]çû磰©î,}†}¿VÛò‚ð…Ð}ÈûÝxá*ù�H ';cƒ¯§ -Ê*ëû"sÉêpÇ@|bƒ7\K’«™˜7©,ÄÂ.€·tÁ¦ÇÓ×·íxþ¥ø»‘ÙçËþü§/1r0oƒ©˜ífŸµ¯#obbnî Ls~/€…ÌXŠü$Tžvá1ý[EyyåÙJ#,›v£ùƒUQGÃÓ»$4sVš™-ÚËÏ|1ˆ’Àãˆ6ÐÉØÐÞ¼D`3¼ºyF[fÑL÷㽋¤\‡fR?3«Á`ƒá^Wl<•ÙnòsŽ;TþÜE“ ãÿÜRgðAÄÛ[â²Âj-à €¶MÓµ™î"G.1"ÃÇæaJrÐb½pÚó!ÿ½ñ53ædäk¡8HeÈàÆðB¼Ïv¤p7 tñYÀŸÂóTÕ`³‰êñ7VÙ#œöÙ7-u) \—•»šÜSK¶12=à²ðEãžOA¼‘ŸT˜A ˜ |(ƒû\jX7‘¿ ôþ6SKIß-BÄàô-ì0¾I'Ôò¡õè΄ØKJ”3{²î’¦“°dêÑ£Ö:´å„·e`M›q~§£ho±cÏ¢<gÇÊ•Ë4¨ÍZÝýýÉY6w.GénF“}ªÄ´’ã2ç$ŸM‡hÝÐê³å;/NѺ*2Ì/�uÛ–Ðõ«ÁÖv+×d6û¼mãÆliy¹ò!ÌïXCŽk Iº„ÓPít‰Áq—Çì§&ÑÏ;Ï×ùûjLúË—×*$‘AêöBŒô(B^(ؤ: èû ?WeÇ˵åÙDxô¬Kn‘ÒNØÿ/ÎÉWÐpó«Á²"+O )Ô¯¶½ëÚÞ|I~ -XW*KüýàæþBoƒ~ÿ _̤ÄNé) ¥XvÁÒï>x½ËÊ—h²´¬ª¾‚ûH_2c0þ©ë#Ì ˆÚ@tÓ#GÛHðQ€“,‘ab9Ä—4Àzô7‘m}!ÒnJ䌢cÀaž_9wYÚ¼n”ÍNh*±â=-´Ùhc¼ÀYíöÆSç$g&ƒ‰°O€·™œÎbxÙª!ªo�~¤ ²îfÄwN€Îó»Ën -µP¶Íݾ°ÌºñP¬Õµ«Rq-½��@�IDAT?íÜ™ý“$ÂÙÌÂ>§ø±á¸.sÛŽeoyá·¥<ŽA½/ÞëcHF™ld°6´dàfrLÔ›Pdò6<²]‹‚ðÈÄÉž›·÷†/²ˆ97bé´@6ÔN3+üV‘Õ7�#‰©›LÓ’À"‰Æc¾ãº*íNl·ÑOó¬3í%z¨?¯ÂŒf¤áa„8¼éÄÁÍΨö<(Lÿ3$ë)F‚>ˆÐD^”¢oÔÍÙcÌpÑï,ŒdŽ•+??>€ÄR‘»òú}±˜dúo·#w‰ƒ”Òè¾´cæ=„¾]ÎYroZ…¶ÌÎUEÐ,s›+‹AJÝ°kz‚²*b…ʙʨT¢JÈ^‚4îåNc ~ù‰–þòWðÏœøÿ´)'gÈÆ7€:}0ª…êÛ„Éï(ÿ0{ô½ÃçÎtÇ©ÏÝd‰}ìÔ«½áë.«»ô¦-HT²%œÀ‡àëîÂ<9û-;û4ÇáÔ ¾á?¨F´ŸÒˆ/Eéøï:7}´kVq:É™~g»=)Á,ì^µÇ0Ò“ÕÙöù*¾3Q‰fTŠüU0dm’4+£§LX|σ7«êåþ"Ž¦nÆ}¤ڰ✕d]Ó3í‡A8,åAf -³•Å¥Ö¢Ìß¿ìÏ:æKìM(c€=rkÑŇþºoO°n8IþÞJ^Îs\lÊ£À¶«»b}{X›ˆˆ Tñ]… û’Øé‰84…’.š+ ->l·àt´ú{‚˜ÝOÍÔÞ°<k75;#²(0.Ça”5ùW"%îuÃ{±º¶Zp°W‰'°ŽsØ9�ˆ×z‹¬î)uÛäÚ#7bÈØÜüx£eœEÌΊ€=mÈËË)½Ü.&oýå“Éh–å……òÇGüßýé¿.”yæ²D,5¯ô¼™ÅT$¼³>UVu Aä6˜R@³P¢lLÄà˜Œ%àb«< ^Š%äáÎ7#DQ‹Ôe bÄÝÀ^íB™®Dúeͱ#ƒMý]Ýwž?! #†þ¢{7˜’kŽu¶ %²+& fú];Åuݺä¿|¤äžÈ³ qwžs…²wŸøþ¼Irs,æ…jò3ì$ 3&' c¼Öd\b^‰†ŽÊðí6÷Dܪv/Lî]Ý!Ùr×þ£c²\ñÇ£é•G½Ó#JòF˜ÖYðtБ£2eòqj -J¾ç'roÇb:³jÁ3OE®ž8±ð| œ«=Ô4$"¦©:„@—9ö=ãŒ%¡“·Ø—µÌÂu1ÏI%R‡³EAVTíaUËsRÚL†gþ‘h}ãæ ™ç·ÐEoJ½´3$÷x '~¸Ð<LÈA”„ÏE¦ -h›h߶D4Œ"×`%ühfQþqœãs Ræü_×OB¿¸§/™‹íÚšûû5“h5tjÓ–?äö~ÿû'¹™Oí0â2îC.—yã@•zz"ùû׉˷åïÝÉ’b¨’Zr¬HsWã}^Åjêâ½X -k‘”ô/½¹pS•¥vó6r‹m¾oâ˜ó‡€* -;‹<h|¥‹H -þôíœÌjÐ)ƒî#5Gù¸<”¢jÎHbá†"2r°²hÑcõ#slêx,žfS©SKWÒÇBÝJ¹ÓøTr¹©ÀüVƒvRt˜Ý±ÇV5^1¬¤úQ±ãø¢žÐ*›Îx.‰&‡êáÐGfkw_G²cY‘-I0ÄÜùÀlÌtKRŠ1Ÿæõ`°+cŠü!ågvΙ’ÆC€³LÓ{öìI÷‡œ7ºŒ=±Ì`„ -žÕ$YÕÊl<åX…LiÆ»"Ë4U¥óm~#¿ÖØÒóvînÔ _‰ pàpà{×�•û¸hvÝËbŸëpÊ>ô{Äv{^„Êœ@nG£B+Œ¾y3N)ï.•æ#Žnéµ·:'©H’ -Œ\â“~"MØØZ~„K„ìâÀm<Buy†,ÁºÏ8ç0Æuiƒ¼ïOϵϽþ¡ßõóñ]WÃkíÕ¦U¦„G^'™ã°1Ý>ˆÁê'0Ý‘Ò-ôhìùOåQ9õÔb'ø‚ç]š§hÎ:ó1Ä*5•Õ!¡ãâ a0‹SÖ<Óàö¶Ü{/xÌSž]ºôÈš5kz¥qãº,º4¤¦•Vó¡%èOˆÊ±Ú”.Õ$Q ÀýÀ.„'3BHMýúP“E³~€.ù<÷›{ß`âGwΙûé“RðËÀçð=äzÅ„½ýs ŒëOr[»}QE÷Giù÷ó*+ÃûÁTI‘ʺc¤«,3'ú¦}vËìx‡ÍXPßÞf§Ûka'÷@ðÚ{{#ƒ(¹™¹dÕ¯Òc€ZûäÉV†6»Ôh<W„Ó¸©¹)‰.Còå|]C2Ë0HX.½ÁçW¶‘J´0óȲð¦vI£ÊasfÃQi�vû‘Qv äX*vÞ»î#/TÔ*[z‚›ðf]Wî -íñ%ÁÍA¦T”Ç,yðWQZ~NŒo2)!즛 @wäzœƒˆ‘R55dy™ÞdNi`U°UeIJµüÑ"0Û–ºžVéͬI«—£ŒØ¾_SëèèØyXÍOq -Ètõµ6PÄê*uɬJ{ñ’²dcDÅ®õg²–ˆ”šÊÄÇ€^« ú,Û¼Áy¼(\†*˜… 0sB¿jfÈ@jh“½ÂÝW[ŒÌáë.[¹’‰T^*¤äF`f±š>YºS¦è (ý÷£ÊozÐŽh*±piý©7x’Mß݉ÁÜ :Š2èi`‹¼•bĬDQäÙ¡ÀÀ~àyCS{t55Q8g\%åÕÌöÆWw/üËêVoŽÃb-{äÓ®ˆ'3±m%öì²|2±4œ¾o4ý@mèð÷È,U†éðk–¦³'Sj[f’ÙÖ‘?c‡«·¯£ì›Jl662K4¼dÿÁž¶`Ëùc0’�†š½Çm)½Ì㌪Œü2û�ðH²×€=êL.¬Šo³š<m¨dÌNm`žËžWJ~„ˆ’†#Fic§Ì3ôÞQÿ&)fcæUÛÈ$H½Y=›d4¯7Qü¯ãzŠF?\dbè&ÅøÊ8gE—_4ªKR@}¤é›ß™ZÐCžAæÞÖuû²-â\䈶M³¡´;‰·MåÙ*¯/ÒNéº[¥Ø^Y•Ò+¨”JOD-Á)TˆŒ©Ìy¾‰ŸL*U¤óP®ùSDãsw7û6̨ñþÁR»±Ðo(ÞÀsX¨.~ô„\FaóN-dÍb¯êEÇç(º– Î%j’›²æã}ò8ËØÿ2ûHÍÏÏþRNÀälÛ† ½€Ÿ‹zÍ'zþ6Ø›d¼£ƒ¯¶º;^à<Ê‚ÔÐÑcÇ ÓVŽŠ¥››L$ö¢`‚ª8ࣸW–½QËóþø¥{¿çÄø²‚)¨ÏœÅŸ–+IßpmVVÏ¡È_u» Áž2ôuºÆyƒ±X“5G³²².„,È–I(#Ì<ÐrTœ´ôùc‚Æ·f,‘…¯BÑFŠO©Eh£ˆš¯<ow¶7ä5Ñ‚†žÁ±ØÛ!Î…Ïx)ªu&a𵪱ÀòµR‘³¥î@51x?8ÿB<¤‰†Å’ƒêÁÐé O°7˜h†{Cg¥L;I@ÿƒ¾ àsÔ ¼nñÿ‚.;d-ì¤n) -×a)tÚ.Úl¿%öHDóõŠFÙ¡¢dµv¨\»‡\?:ŸZð_¡Ùn]†YyÚ|\’åML= ÿéóÆ*SˆmÏìéi¤Š‹ ¦‘Ø -£!@Åw!Ay!ÆkÚD‡Cœ<²ða{÷`•˜ÔÂÓÆŠLüwrkÒäšjÏ„¦xű›F¹O[ªfö×D¾®Úº®Gž0vŒ{�Ê-È|ÈfYK[øçcJÉb7*ËîE0ú!•wL‘õè{¥=Çv×UÔN†‰‡Yê•£ªâ2?a×»«¶º€Ä¯Om6+²Q¦z ™ÌJ¨ÆÅvžÙ Žá²òÊÃ}C¾¡Xî÷¾¶î×±ƒTCÐþÔý`2¢÷ôÇêÃõnÕÆÒÜa«°•£Ô×Y"‹ŽØÄ< ý¬½Ä¥Ã&¬™;(iĆ#þ ÜÈ}×wÍ̦i ä—ÈgXñrÉ©ÛöPÔ˸%EN%:i“…¨Zÿ+˜Çp.FäÝøÙi þ†0\+ €Âã¯;ATîWUæ ¹k.D(ÎñÇãËì*EB€Ž£ršëă26ÊÌûº¨÷|RäiªÙ^O[Ä‚qùæì*ˆìŠ[bÄ[?@ÐV£FÞÊ -6uÄK™/‡³*½2z9öúf_#…¡•Á#Œ!ï±ëjjNy”÷®ÛTæí°.eG;"‘µ³Š}³ššzªFOr‡£‘E´F_'Ó¦Vì{ÿзi©·¢Z²Àj5˜>«S(UiÕDóªÃÒ0£ª§ ÷Cö%^ÞÖ˜ÌÒva%—,ög©i€nE1ç¿«KÒ‡V#¾ã½òòÈŠƒˆ$uÊùbÁæ²L‘Ym<®n쀹(Pøñ'©ËFåÃÐå§ÛD®5r#ƒ~CWph0çËvþŸð¯<ðê«‘š+hOo²C¦~”±Þ œÀíªàØ&«á$ü¯—W—G——ST ¼Z¾k¨NaØ…,„A} -]ý…pŠ9¸KqKæÌÄ€sÎÒäG¤®î$" ÔßÃqNò‚ÊjÀ&ò ÿ³ŒÑî"gþk@ԛ̀ ˆwEú¶õÔGÔðqä>‡§(Á0óÇÙi~Õ*jw¦4xä=ÿ”Ã2 Þþ¼–_ªfö~L6ËÀa·ÊŠýƲҎ×Þ–‚4*œÿ0‹t»¤› Ã^7âd£�YƒÁoåʘ¤”›âõÇö -Êœ¥¤uFTñ¶Û<(þ£Ñ¥ôCGuÂ<%Ú©Žöþëiƒš‡DíËÞ%µ©ßŽxŽ™K°‘Á�Qc¨ípxfÆy¢®MCòòø z²#$Óн#Ù´l™+=)è2Õ@j«T\|Zè+sÎoÊ'I Vr(ƒC´Öa2ô]¨åRÒ©~Ü„(˜Iù¸Ô«µE±ìEZª Fy½ÉÖ>ô“B‚‚Ãâ Ij˜£•Ñˆ›ŽÇ”ôN‘ª?ÌAo¿’SrÊ7&ð›ÍGŠ,ÙÞ%¬:wÑRØžk’bµ8z׆Né‹Ë³Q|súj¦ˆ%¿?ž’áÞ3"sýVÅŠh©Æ/ûÞΉçËX²£t؇:ckrÄðcE·Q”O:3?*Ïæ[éK&&UÚó¯ŠWªEåãÑÈXÄï¦a÷5¡f#¦ÚI)éU¬Xö_j&8£µ ”]hSdзڜÞ[öâ‹ý'´†ì¯ò‰>Ú0«?ˆƒE”ÖnA”ùïaFû$÷õ‘j©)dÇá ,iÖ1ù£¡~Ë:T]Ëg5nG±[-±×õôôŒ¢‚9ôv¶°T÷["s':C8ô”&†w›7< þÀýXjO‡Ñds%VÀ‡'½„"sïj¯ú’ •ŸXÚ>ÄrÂbÄA«AòûrWÎ)O…#T7EÕÀÍǹ:P€¿Z’ä•K«scÛŽúí¼!,o•"¤½ã¼KÔ}Ú¶ÀªÑ íÜò.CS ®aÜŽÚ÷…Hæå9uëÄPL×Ežþ6«Q´:\hxÛíóåk†0 °F^,ô>™ÆFldrÊÎ*œlñ‰Õç»éóÛ}sŒ\~«=Åÿ*” œ'ˆ£‡W÷=oÚ»ù„cÚE :Ë|»ç‡¦5¯8‹$ÞÈÚ¦¦¶üŠðа‰4ýÚ‘ËDZ½›¦óßµÐÔ{½IDè—Xi&è´2“‚£˜ê«æx;)Ä‘ó6")G›™QI^iÆUSƒN'“OÓ€ô±ƒˆ©¡ð2hc8ƒ)8)ÌXu8væ²O½¸=ýdzÌfz™§æ+5.#ºýü#10õý¡]Šá]"|Ê{P¨oY‹·vØh®>Úº´zZ7&'Þ‹WSšÒ$ÒTJ1r4éë÷©1Ϭb10ƒ°×=úè -cW¬Ð²¯—UPð?n×¹<€0 Æ#ãz”_‘·$V4Â!›iÛDdç†I“ÊYU=ä`xú†-GІÏT¾ER‰¿')iÈfí¢ÅL<ÅS˜èsÙæÿ·s´Ã»½mT>o¯·È{œŽ¡v_Òuà€J™³–ö¥TO,™òÜKÎpS¦KI©#Ö8Wi -=³°á¸NÄaƒ™ng$ÐB²™¤ÑzpP¤,¥(œØâ *[²z¾BÑÙ€ýM‡|y’bgÙrøÃcó]ée9©.ë -ö †3K ¨µkÿ槓^^˜ë_ù»ßi!ÏÓŽo‹$hTˆ•šÞŽXòUâÉ‚GÚƒzooJŠ+-Í;<:›ŽV`J,ª»ìbŠ¾<FÕ¡¡èñÑ.Hjo€‘Ñw?ø㥨§¯ÂØ×͇:eb®ÉÞ°ÒNÔdÉ®ÕSòºð£jìZ 0•¯”ù^|<HªÇÈ>¨ôÐo{ø᱌IùÀœ ×ÌÌ Ð¸Fñù³gƒÉc!Ë£1J£g «cAa¡J&XÎcD·š'›»Ýyg‡î·Œ½Ü;ÕKÉ0 IÍ# -'¿8ßj ëu¡/¬°�ñp«S祆öΙ3ç3žß`Âd“àš]ta?9ÞEM(ÙpðuOßèZk0)çYþ -ÜU–œ[™ãàé ôHvă»oõœ$¼'×Ël×K¹«òò”<‹„=d¾ýÔÓqå,U†ZíhQ ‰$Û_`å¥0ÆI2=öhOo7 ɺeb™ÿ:$_¸ÜìÑà)Æ,'C%GòÏÏ<cTWÛmÄ}Rpp7':à=÷œ°ŠÝ—C&½üädîóL›ªãËVhÈ\û\~–ÛLáΔÆ#®‚ê?Þ&Ðü¬¤šJìqYºÆà ÖñÒKÎ%,à7þIo°og°¢¤cǯþËë5Kâã¡®Bú'½/Æå£RtW4y+ƶè¢<ïÚÓ¢®ç²ÉÿãsõE‚1ƒ5U*cüé×OônD;¡âq%³‘|4Mü5»"·aPŽ5Ù(þVè+µ•ÙÍðk>݈CqÍØQþˆD`ëF8;n `”ãþhW™Ëò¥&ω狸 ÕÒh‰BÎKž¨ZÜÉöÖõ‰Úq3Fã‹› é»�‚p ¹/º°ª>UWè´/©Ð'ì"û-,Ù/Suq-º)¡-GÌ7ÚþÛN.¯-O}Üè ^|(¡M‚xdAN6â·¥q§éjfy~ç¶Áø«†®|†âØù¨ˆÁ†¸¨#Åp|YK–NI)]¹Ðl¦ö'“¬+³0ŽAXq<UY€wÚ Ë5ÁðÜjÂ2…µQ¸áÛ`,ü¤è<JrÞúŽÀ>'ǵÆdÃC³úwÀV6eÈ?€±ã×ôÇV.&Hlkºx¾T6öïFŠziS¹¹oáwΩïè`ªE1‚“%çSÔA¬{šÛWQúôéü²Uë™—?4È™ -ÖGª!o—öï:1í²ËÔ =“íŒm>e(£ŒD(�)¦mæ:߶:O<W{ƒÏŠšÞ*@¶)©ékØÜÜNÒì]½½Ù”Õ6™’µJ?ll•ƒP9JOä¾FnDËHAà‰îÔl¹Ç´uÑ+ŠQ™ö붊ëïý5²Ïà¤nÀøuD8ngÖ±Cíˆû(s®_65Y‹Ä‚y²ÎŒûC°kíºGZ–=8KˆçW/þø[Vž+RÆ™‹lm·;•G7Öob³«Ã<#L'UwÌ º=*½þÊó‹¼ ½Q·ZPRC«T)–Í4JÑÇzjŠ÷]#4B8õÌ7€?~Óîaã×a–=}ñ™iÙÿªO€Ž€<˜2ÈO±àÐf4ã<õõ4ñøÉfpl3>&Ø¥HäoÆ -ôÖY6Ä«E“0edãë?ºysjÜÂ%q‹Hºéþû¢Ù¼¦Æá–Õo¯'Bª$$QT9¶K9ðFÙ#o ;¦;‹j'VFÓ0\X]¦·Ñ<åâL< =xGîÿEÿÌœ‹ôÔ×kil-gHIñêH‹y½^LFê^X*¥Ð&$9v‚ÐãE¡dZße‚ÌŽ{ëæÚ[e5E˜²'0,$cNÓt³&¢ª3vÚ0°Nz¥q´@$HŠwq"-@(pî:RT�«:|HrkQ1öGˆG €… ¶`¸¯�Ë’‰¨€9Zò0Ç6àY‹¢²²žO2;±é˜Uãd+9&q1Ä™@9ç̼“S´ä&|߆òÌLñ„¯Ü½»-ÏZßíz'E¥~DÿZ唣˜äQÑnÛ<.¯ïN°³D«Óx•bÌÏûv¯ëÆpבÑ+wØ<÷Q¶óŸTWÖ1*w=JgÕÜ>mn1oès00Ç<˜ŒûÀ'mêè/Yñˆ"~BT— ßï5C ØH -7-344™YÝš|#x<L=åò:ëë°ü$¥—ÞVËÜÖBí‰õ‡.ó¤ám8ì³Ûª;d‹`ò!T‚® +cñduÉEÕ$²’”æsƒþüñ“ƒfŽû[OS¨sd’ývµôfW9 -oQíÇ Mý‘È™o^pk¥èÊ‹ºU££ÏCŒ`ŽÒÇ¥(ƒÐ\[Y°·óÈ®—>úŒ9ÕûÖÒꜾ•˜ñ¢TY11Eé -Í´ ¹Ôðæªæ0™äÒíáÝ.¿0á&ç$#»ÖÖh4g¸¬ýä—ÿ[þ¯šP‚®mFâøoÑxê¼Ü5òy�[¿Ø±ÉžUóyü·dÛÖç¯Fâó^„zŠ ƒ²K(N;_‡nRSR@¥Ô<J7!¢‰™®5`È+–,»vç@ti²`ôrUR†gnNÅí#Ûl·#¹“†@€ ‘à—PR;ñlAgIôïK݈·ðO_<°÷ÚnÒN°×冫«ò|õ½"·Iøø^–µ!;''Š¾ ËÁL½E´™ËYÕX.x¹SÁë/(.†}ûôeÙ8x`Û§JbʃYf¶•¡j(Ä°;=Ÿí¤Óê¾0·ôÜHt©p’’ÊmÀÓî¡E-.*\�ú!Yíò%¬!ý~§ôSWæšÀ NBUÚlˆ÷ƺ7zÅù€«]©ðÚfWoö~HqŸZª#¢È•Ñóïxµ—`æñ¡-]M´ôó}Føøž×ج¼L©bÒÙ—“çZ[>±¦sè"Ln�À=)ƒ’4iÓÒŠÜAÒ/[û#÷#þ=$?F¸æŒO-Š#ê›KáZ+u¨:P)•+pÒ-oºÝ1bèÉý©abf�F7 À‘þ¶OæNœø™xÙ—l$ÖÜwËŒ÷ˆ€’Å5êJDãËACø‡D$ù– Tu’.!cÌ4%!=£tÇ7Þ^wºýJ}‡IËïÉ3 %µã`p³¹MôÑÞÁÏTŒËSÃÊXG9“*õÝ”®Ûú…xDuRy¾Üîõi¤:ïdkðP±òEëR] -RiÔ/ÑŸDv|œF-Œ¯«óP>_àóT *ÅiΚ ÂZ¼o9’®PÚÚÚRE{öñ–¹ö7å³ÊÄ1‰¿«¯½l¼ëÌŠ -pŠ|zoÍ‘ˆ;œÔ߇Êñ Z“_€zúÑCt<Q#äq€AŠ9n[~2!)Õ%6W!DÖ’#·/ü¿¼“_vß4G -ªÆ§ep¨Êii$a„±ùU7"Ì6«²N¹¬"PýƒƒIý/“‘8¿À¹—<c2†Àù}©Ed³KêÚRI5Ž‰,J£qJ¶ý½/ó~ÎIÌ—0 5ùå§H„¶ŒW-Éõíå±PÜpæFBjlûì¼<\& ŒÚ%Xƒ_Šå¤{ØyŒJi%ÐîND6ÏÊ:ë˜tJニ C,$}¨÷¡\ÌÂð=žÚûÚ?‘†J=Šý2ÕYä²}ðúËÉì1“Òl†¢Ät“~Ø\ànírÉÉd¼¸[7M. -…Ø¡¡”kPŠ0Í*ŸÓ¸úÝäâYµýò€©é…u¯«5Ån¸{‘Ÿ¿ØÖ2ÖzßÙüIÿù£G§„å唾Ç)öÙ"ñN\rŒÒÂèñ¸la? oÝäýþø“•EdùÃåæU¡€À…*¿½fa |ØH"êYa¬Àp9¬€˜‹”ع·2¿Ÿª¯g»9Ëx×·ƒ+acžk§ƒ›ó\æ&MV#,ÃŽÃøº�ƒ°Lç¡ôœb[{Ûc(ÎÏS, -DQÖ3—Vµ>E2µ²$ð(z¶í(¿í“¨›Ìy]¤[†Žx·Z<–TËê¥P!ÉÕå,LÕÉ„ñÖñæÃ?¬›’ÆÍŽ<_KÍJª÷Ûî-·‰‚Ô“¸Ímh·L™ø«'#ÁlˉÁãÍG9%^ôëxœo!Ê}yIJÕ8 -DïO<‚ç–öÈÔË‘ˆ£©º_6’[æ倉ŒbÊBðVU°ò=6S{áp_fÚ’Fµä•|Ïã&Tæišz* ÑPvHÕ›sLÜ©ê¸Ì1ßÄOp&fÇÚ·¡"¥XaÔw‰èÖ}$Õ„ä~rMbêîG0"ý:@ÍÆArª:Ëàk-9O‘ ˜Ë Ís œ†ùàž>¦iÌ/¬…9 èÏôÿ:ôI®‰NÝyÏ#þ/û ky×{äX;«VC²nìFÜðý¤¯km¹ÝV 3ÜQ »¹È!&Óc(¦„e]©Àb™ý0V¤yxo@aË›C^0,žÊë|Ñ÷{NŒ/iä‰H´\²N;%ªóeãŸÄÐþMë;NÍÜÈjxÁ“;L—cñB!ö«¦¾gLUÁ!É°tuæ˜Ìg9’õÑ⩳…%ˆbϱP”ÛÛÛ1t¶ä ªWôì9Ë …W:çæç‡^PBêTĈGƒG8‹çø°ÎUÊ197Ë]É{«ävSš=;JǨY¾2+((7&Â{Ï/ðòõôŒv»Óƒ“$+f¯=ž*Å3D6› Eö뀯çÈÈ8èîD‚s˜ˆ29F\ß;Çe -èU8w•f˜¬¨Ê¹TÏ1UN¶ÄRÇ¿ï¡Ô.Î9…„!6>Õj#!ï¼jtQâOOˆ¡Ü*„S 13™Ñ]‘”¾eœ-k¬™Ë£‘Àü,xÆ�$>©ÍÏö>Š‹gúuä'a‚ÍäwŠv4dJ²á4˜VÙ—å´uÚcRWPR „;‘c)>¥h)Ÿ¿ÿöòòt•P!^„̹Hh&bí3¤¢Êà/¿5ªlpb–)2*Ûž@6Ç FµÏ³™Î57n}ïXiIM?Ç1‘”œ‰Mw$$í¼ï[KB™ó‘ÏþD¼ªØ€OkGfäyHüRGåoXÿƸ›‘´6u}4©Ø-<.Ï™3àŒ'(†jDü-LÚMà™ƒåælUWÃG½{HÂvä1ß´Ÿu‡«IíbL.㓆<Èü%H6–M˯Ú^î:™üÅüEµõù»9¨Q¢ë¦1} -ƒfR3ÑQ-Æ•È?� Ânå(ê÷íkæUå}®3ôUô1 …Yf)C—IÚ@~>î“dA£éfu7°çÈš‰„Û->¼:Æ0ïVX…²o¡MHÞèhs ôAM(Mž>C5«õ�[d7µ‘k|Û9‰ù’† -¢9fæ™ü�¥'cŒê6n$NIÜ}²‘ýÞ›~4&=‡£§8E~„dz-/‡2¨|ù[xSÖUÍC©‹v%g‹KݨªÕT*ƒ|§q/XÒ&€l&Åf=6L€C®5r#a"ÓC”-F.Å�*ívORãtŽ @¤¡€‡ëV½§ŒSd�ÎçB5ø¯’®?ƒŒq+R= ´ùˆÅ’½èx«™k¬Ü:ÓÄÁÇfMýµÜ*¹1¼SóªUª˜K˜“1tŒ1¡xÌDÌÄ—¢D}bâðŒ]P…>¼¬†’I? Ž·È¹H/æ‰õmÑÉ_ô@ò%Æ%ˆW.µ<¹ Bê•hü% ¢ŠX††ÈdÄE=W¯Q?´ukšqêÞ2í&ŸÄ`&ÊŒ€¯ó.pµ³B@;L?šKgøQMv±'ð’þ’•é_÷wù>ªÃü·ZXt^iÙu#´Ù.\ö}‹¢S3f£¯ÊR“N¢â$4{ÚµËÌôEK]3ÛLù¶˜Y£ chsDÑ_gÇæ“,õiû¢"r .½GsÀ|“yÂp˜ÙôÝ.L:Í•E¦S¡ òw2î"ªºy�Â?òWðgœ R -˜X²_/<ÀÓ®AŽù&nxÉr ®ZF1´MS8B{G„BI¿w™{ªSàO"ß‚2Ü'‘ƒyÄôð}ŠÆ¿¢ÿþ„™íI&™øOÁc{oÑ°Læدó' -4K)û=Mï4™…«*õ/° -Ú+¨ò"²Ú$íÇÃ&“~ª¹ ¸o¬àZ°zþ7'°cΟ_ä=§t..À$¢IÖdåcR\d1÷Ÿ<+IòìˆL°;…At@'0°°·ÔÀJ`ôÊ¢Q— ¿ê‹]AÛÌ@0>�ÅJ<¶@·H✟YòîßÑ°C½ÁD‹€Î€™T£Á|%:&ïïp¬±©IRÏ~æ}‘ŽÏ|G.dŸ+KÝ-=VÏ̹e²UG¹„Ã:ˆÊXI`Ò©ã_|1uÑ=÷ÿ%ìã<J™ê„¿^æ\¼Þ¦ -ÆjªoöI:Y2Be,«5)q¶•ô\LyN‡\»ap0ï²Û¡9³¶/棸ÅUù骳¿s‚u1¸wJá>î4èðÞÙÅ¥©Lƒ1! ‰¦á¥¡Ç w1T4r¶‚úßß ~Ö¯ò¯ïbqmÕ˜¢8äÐó°4²"“C¥8k¤n½Àíè=ÿ¤Âo¦N}®†vû}çk’´Ùd/x›õ‚ãÝb¢tþ™S.hõ¾`̱²Rs÷šîî#Äè=R“~6¼AOˆpbÎ#ó/w³á ™p]…<×Õ/[†í õZëÊ‹ÎêAÁh\ª(jIqnù=Áhâï¬Â6÷<\7&u)iqÒ×™*Êágº…üŠ¼>óº¬HÀ9‹v³«ü¨?ªWçØáoiOmãp2z¢³ÌS,òÀ›Pÿ ȱêíN'æ3Pùƒ·‡ä’n~7¥ÈP„Ù@@ á(%ÔÔ¡JKO»³º1v=E½'má9‡6ÙQé•’°ô™r,U9uÅ¡›ÓNð5ü…KÈþ(Ç]‰ø-ꤌHäîäY~›¬(e¨Æ¼#X2n š}hdÓ%Vê2«&Œ ¹‚´ÓMZOQÖ‘»|¡?Ÿ3Ï×"Z#ÐOKPYÕ�”Ú÷† -ªù[¬¡×…BÒÙí&ƒ<=ûŒî•)j9û~ -cü|»˜&R‡‰ã�Àû^ó°òYzÀ˜÷Îo òéŠ!°²¤]P÷$iµF³YoJž¢³yÍ™ólˆåÚÍYׯïŒ!NJÑ+VÔ© 1½`\q@Ú¶®iûŸž> -Ka3uûÚú=‡kjèyùî–%cÆø—c?o°ï]x¨¿ÂD±UeÓEÑôåäïs󃌮^Ù“.®t4ÖñBïùŽ“7Ïp[ô*Ä¥œ½„-†¹KV¡¶€…©#E«û.Gr}atÀ«6Ï+&xÇ?´÷9žw=¡èòsð>UÄ«¾œëC¼C¸‹PÆ8é•’XöæQ`r#þô|÷:Ô@çĶÜî¾ÌµðyÚ¶ª½Ý‰0ÅmèÿŸÉ,{[ÊåïžY9èV´—@Ûþ<¢…n†û‰F©·úúu't½âá““ì¨Ð9Þ‚Åj%¤ëç “æ¢P -‚D”a‡·y^y®e!¡Æ$ßÜ>nîqÃN�ô°´¯7@›\쪗÷k"zB¤Ò4×ì^Žc¤lÄè’§m5Y¦n·èÜ#ˆòX¸í?–üÞ–ÞÞ,²ñdPñDÈÎ}/j.Æ€1o-¾¿l|»ÂL…yŠ¿‰(Ùbw<‚oævA~¾^s¨7ù×:ðô^VQе¨ºÄK*ÏvGuX9Ì®² ¾q=ÕÅ\ýì¯[gäÚj� X©ª]ƒÑY ƒñ¼Ž…á×ûljƒÇÀ³@úã((zb·x¾¦£Hj;-œPvæ]lzî¹A0,¶!{ú/½Ê–3÷ù"?'hÒÀmÁ`i4b|+¥ª(÷gZ¼í€M^€€÷T>²ËCјÿ·pùC4–·»¿ûÐàÅþ6ÈrÖ#¸¿-&¥ŽÛ8!†Š›'Q¡òxÿ[ÃâÅ'ˇGv@ ^ûvìP–Íš•Ämsù¥^÷`Ê ËòqHªÄDU{viuá)ÏgäñÀ¥8üGÁIx- -+ÚÁ ôDa"wc –÷™ý¶ôN’ þJ&�À„Ùl1›^û虧N‰Q2ô‰ãrÆ™Mì¿‚¥ú2¼Û)û·)’ô«¡¤o癡âÅNMâ]À;È*m6”×z<®ƒuxðÖÓ;:Äîˆæ0Mª"î9ŽÂ%(>XŠCMº¡äEþï;ß|¹-ÿâoóùn±†6tL\L<pÀoØÔÐjbt3÷‘ù|nú -y’ñï|ìÌò@Ô‚ ùê“X'éÆ\–ÒW+¬þø·›?pUB×ÞC¨åmÔoY•Ëp ²Œ£+&MO±””vÈ$jƒ¢®wD¤ð®ÌªãÝߥ¾¸ò'ˆ[JPYþ£ƒ×ßÜýڋ݈©¯7wä¢åÇ0¾· ˜â_B¡¼W–£GšÀÐćÐ%$±jYþ¶ëÿ±ãÌv“û#É4Ï)¾pÔR³…_Ž"Ãjp,»ðlÿÉ×õ"A?ôÇ.Äóy0C7!ÿÑÞ<»çgxÙ—bf„öÍPJ¿n”ÛŒáñiõ9ÿÿ¦m¸—é!ÿ’ÃáÎwW@ŒõsÇÕp 'Êrj“Yà…`®sCÝp˜çsŽùR¿&s² 2dè£:7¯Ý”uMÇstûKÜ뇱ÝÆo¨8ÉJL¼Õ³¸fªËÔ9²±¦ -GBGy=C`žä=ý²BçÌøBúÜòïÙCXïdÙLÁ@¯ÖZU"ÌÔuu>Ø”ê!Ó¯y¬‡ñ¢ Ž¦"¿GìvT*©þ„JôS]€‘0.ˆ]Ư½E9Ç3Þé,rÌúßhIånÑ4Ùk8˜×—‚¹Ÿ–Ù=ÁK÷[f¯w”¤E)f:šì\¼øôÐñ†=³|/©?�‘r!Œ<¸v¨ý¯ý¼íHx=J‘°€jX~ö«K0cf¡`öF¹ª¶åÇOôlýèXæa§Ï7û¢[ÀŽô+ÄdJ©Ôc‰²œí‹áyŽ|ÀýÐêˆ #Oå˜5ÔTð».D_?öñûí™ó¼O˜RÄÖvúo²Šü]×AþÉâ)Î!©ê�é\_˜sê´ìä¥7ÞË¿£‰Ïêrè÷˜ØÐ&¦rÁÛêßf¡âSCBð¶=¢é‡@ -Тh¬q´'놡a$“¢íNŽ7<ŠÆý}³(&«PŸ Þwòô,h¨2ÞtW_Æ°"ÌbC«~ˆÙ`BÕ9—Ê×w[RÝä™dîý™†6GNŽãn<—a\(Y"Ódåù݈C‡C)yh#çBfͱ_[îéÏúNȼÇþ' -‚#†*6š[©„;snòyh0\ECœ“Õõ"oJêz‘¬P•Ã? DHÙ¾=:[hiöù¬õý ÒøÍ'Àeð7⼘ñΉQV_ïÁaø÷*›„¹¾q‡c.\t1âØY -2ÁäH¥*#h„L¤€5q#)$ªª³¶¶EïxbAåç"<â7Ò ¥‚èk·¦(Û£ùY×}ŒoÓPø¢8à¦àÅÉÉbµ×ªóœ'È=“qìp¹„L¹9ùŽ8휂 þ—(¼xí‚Ò¬È÷gÛpßÁ³ õ#””O.(t“¼Ã¶3ãK*K¼öBg_´O1ó¼zˤI‰¶d²(–PÀ;ËS4uo,Ü‘Ž5¥+R²¼Ü©Qü¥Ð~춎Ák·gãÅgóâq!Ày×ÄøníÝ‰ä ’áBØî6qFìr¾µ]áZÄY+Pu8ªH·Åa‰sz§$3›�´OCÐȹÒçè^Šµ,ŠVaøïóYFSj3@€OÈ)çj]ˆÜ…ìÙÿÊÃœ³LÃD|. P)Þ¬“XíqêðþCdiLη±;ô0g%RêÏ°ñz"54²Ýäç¦@ìZ,‰ -9.ó1Ê£ÕÀ|äTôÈÙ<Õ†@² -ׯ€‰k®·&t<C~™÷ øÓd2é#F°÷-sÆå Žÿ3%²±„ÏG¹Ý.xÉúBˆ;mî+´§©%ÉË©N©û) -2¾ƒÃAÇ+žT\áÝÙyÕ¬F€:ôQpÞÑ? -O÷em‰‹ç Gõ&ý—õ›º>ya˜GƒÜÁÓNîa¬=œ¡®nò¦öÝ30¹Ó'²ÚxM0íuŠí«š;ò@,yBC·è4=ÁÊR’Ò @8ãçþxâõßµâÃÀ¹Q†äràyÝ(¼y‰¿<(*/÷çÚÞŸYp¯ÏW`e,—A0õ*UÓD…zdí¾þí³sÃzvU•5ª±Ï`^½YÒß…Á¤„Re@ÙünóZŒªI`8#Å*”u¿5Î#bQõÙ¾ûÚoäù–]xé[x1à†ª`¬“ˆKú~Ò¡E|…ïs_$Ñ%XôÅ‹F0žyƒÛº‚sía)BkA)Iÿõ#„áF>Ÿ3÷ÿ²ßí‹]`M¨€@Ú{žÇAž]z#4Þ˜Ïëê¡€ÆLæzCঠ!?+Ôç8Ìì;ò³ÑÍ¡xí6§º”Œ1ŒnŽù“'’L#÷;—?Ÿ3ãK…)ïɧ˜,MÝã§]…¥ûXÈ()Vyžˆ_’}÷wt¸b&×”íjÎ¥B³Pz²˜Ñ˜bˆ€Ið`>V‡Ro‘'|�Ù2Wí,óö÷E‘½Ô¦KŒ±œ±‚1¹Íµð¤Á¨eÍãʾP,B$Beå £èÚümG\ì�Ð|�îN^_Œ‘–‹–û ¤6ktZ'õ<–'7jºn£yHž+Z3¤Þ=:Ï߉Rè[Ê6â॒ÒÏÍ.öp_H5åÙÌóÁ¢Ó#ÔæºEäwõF³íº�ô”KPá5ÙÄóSTÝf³‰};½0àgMò@üo oÏAýá;œÍ8 QO‰œ> -AËŸ™ºZ’ù£î�wng¿ÝSªŠÂê‡|Ž>?ß8�ãí;¥±Trõ¢²¼N46¦_=2°ÅØ?âæ ÀŠâª>ÅÊrþ¤í…¨å‡£lò¦ó -òýÄ�½©»BÒMÓÜ"kÇx¤U4ö?rÆËJ–û“ÇMBˆ,ËT‰™·„Bå’ÆÏcÔ$¼üGvìÝúæ@ûøfW—K‹YÇP¬:Á!0ùpæ¢Q[Ì’Ðtåw/i#鯑ÛcXÑÔ47s -+¾§jÚ{låôÄ-‡ëë}Õ/þ70Ð]ñ6ñÜ_&8êéYGš·‚œã â•(³ý3‹GÓÒ-‡Ö¹¶aIÉA•£ùÈÙseH\BÂN>*£è?2™’c¿*bmríÿ¿}Ý*vÝo}�z¤ý3ÓÇá3q^ÒÁ’ªMIÆô~=xÙíg¬"2×&ÏuZyõrXðR8ÍDiÜ«Ô®¡ÈÀšÌª'³ïWõIôå [q 4ç):Ӻطkì~Òž÷á ÊâUÁ»áƒyRË2QÛáütë:ó-ŠÕ¿?ÛãÚfÛ÷Ä&˜hz*6Äa•ze*©o„vcû™ûž«ßÏYÂ4h¤á%¿“ <XsJ“Á®.$ÙSUíBò7²„ÌÏwípë!ˆ2?ÃÌ´P©ÄÌ¿TÈ2/ÝÑC™ÑѪ,Šù=Cs±”œqL<¡ôcí<-ê\¿Ïï/lÿ}^ò’wßð+”¢]ìo(Wõ†ÉÉCJ’ƒ§"Qa6ÊM¯r¹B³jŠ\L‡'IýËÐ=À9f¢?—lâ…/ê´¼}>Ôz/(óH&觑Èy®äÔE²ÀýÜPÄgóÌæåvPG{6Ο'÷•ÙÒÉ5;3]Ö´[’O åhO© ˉ`ÿç^r<âV›¬I“Œs·•7&ªö\°+¶7ši™½VSŒ«-†Ý˜V–Ó?Ôyœ’(JŠ‰ã´*°¢u¥R!2ÓF,ç&úï³Á S «ù@ÌÅ�=¨Ñ¥à3}¬9B?û‡Ã}·¾q°<GeB1Õ÷ÀX+÷½ü|Ù†—´‘LhGŽî:¼±8ëp±Èq†¢WÇRL âãË0qu‚ÙÌ?oÖãˆvciið‡7ìaX~•Ù¢ÿÁ‰¯úèØú«&¸{HÉä@Î9r[ïIÜ<¯Bßo‚7]çÌWU.[Æ°ºº Vu\¹€›v¼½öé§OG´Èáý,½©Z®ª O\à` ö³€bå÷YÉø¸E^×û{ë§!Üá^Å‘@ìú¦Pü6º´r&Ú”Ö7h[u.¥Ô+ôÚÞ $Þ=ó_ß &äçºã‰·7´‘ˆílý>¦pŒ¨–éïJºÒBŠ€MÍ+2yÈ!_‡^?x~=¹é>Œá €W’¶¥·}ù]\gt(çc�E}…å”Gâqê¿™�û*fWªUï8Û}ŽÒ8c'Ð¥"(1‚’xÊ·™ó~ŸçÔøžv·d¡ šB‚…Ÿ9zÔ<b?8]Ì-KıJúO<Ù6¼Šk@y÷""Vó:ý(2ëƒ)ix\EhÀé,†d}!êKQ™£'B27íâ;ÂN rÔ¼¸£\Äj€ßÛ4ÄÄNo ³á¥+VØ3æ_ß�Ï7Óùé%…ÙÞ•C|#ø -E -ttãëŠb•žöâõÅ£œíQ:ö®ÿ,[e+ËφG0l7ÍöW?bÓiŸ÷î(fÔ²Ê~ˆ˜ghè=l0Úþùåå§Å„3džünà„ý'Üò˜ïÀhÿÙäâ×_e=+º/d)ýÕk3A8mDÉož;+ˆ,ªæÉHûöUWŸ6t£–�Æw%M«‹HRÔœgú›I㟶™™ªâ,fžï6‹?E^ -üÏ -˜@ñÜ Ì´/ó¹'˜œÓ×Z}uÊÔ£~kB ²†ST 12¶ÏJ£˜J`l‰hœ,ÙÐDŠZ‰¬»°ï“¸¶wo˜@^{]{б턿zK÷Ð%ŸÇµ0§$ÇŒî3Àá… î†¢Un%Ù€º€R‚T” NÇ$!(©ÂZÚ`(c>&Í+u•;DQ®Æjª"×:v5?IÆ‘‰ÉFé6¨‡Ö¢Šz -¢õõ_#CúíÿËFî!ÒóÆÓí‹Ï!+°³ýøÃÅ3yÃ'ÝÐî³ Iª(ÙÍ\g”ŽÖ˜À$½ˆ…hÒ¶1L'Ÿcf¿¯úä™m¢Á6á}ÜlŒ±bô×{X½‚Õã+[_<¼òAg¢pbÐÌAÂ÷3“ˆ£È£eè¶äæ‚ïÛŸó£ÎN´_D‡Ž"É'Qìƒq ªH/(˜ÍNîGý6ŠeJàÅ}`¨± .¸W£™Áˆ÷á%Åó¢o.½¬Ü3ÀšL»¬¬ùížÃ;‘µó<µzŠÛ@M9&!²EQ!¦ËýmŠªìT5%Oö<âÀˆ¼qXè‹}ƒI15‘b2°¥ôí’x 'æß ßÇ¢ŠþJ/5“ÀÝ^Põ]’¤î㲂‚®¥üa´4>Ì¿‘’ñÍ£ îˆ@ÃHñHqÐ`.¼¡>EWFII“#VC°»iƒDÂ2[¨ÄáÌcÈàn}zÑ];ÂEØ®”¢¾ŸPµ"Ìà¥@«1¦v«7ºUIÿ*iúPXR×ô—@¸ú·¬ ˜Mb NkQUn‡)Øî]1ìõfšFŒ§2o È`PÞîK8õÔõ<ÍöÌ0ŠÛÿž¢ÅOÕà,Úîo<ÍxgÎC>µÀp -_8ߟç +VÆCÚƒçXFß�hÃMжº¢þnôÅ©ÉÖãùÛ˜óœ$¾¶§'2Ãfæo0Y„Z€a*í‚Ç2ò#~6úƒÒVÄäßÇP%õ1‹;ci.~Ï…‘øå ÷?ü¯m¡ÄT“^U‘l8GkWâ÷8Ü»·ë{<¥HoC)%“áO÷æC'ñœ6`¢ô¥»¿®î$Œ.ó÷oÂç²ÚÊðȸ,ñmï‹þøÀPì¿wû"¿Ù=yvBEñSe~i'7Ý× -Ê‘U–Ÿ®†C¬é ã>é@Ã$âêäôB¾éXG*Êfs_°—¬¤Në·¯²_d*Þ3Œ\¸c‘¼®-,µ¢ -úäì‘š ,ÞžßIsnŸ8zCOø.Îjù’“±b*¾àŽûÇeöÍ|v´õÚP¢¾ù“ùQ]øÓ_ùûñyÎÊ‹ÓTnEE¨}ô3íìJF#vÞ4 -¬øãð½%àë$%ÄÝF¬çÜU4ωÒê.pðz¯…á<8ËIJ¬,qZ ù‹QåS²s‘ÉUyò‰Hœ‚êdA\ejFŸÁIêÆçÛþuÞ<åÿ¬ÿ0<³¬z"ªÇDIÑWà c?íZŠa;Q=õþá}Ûº?|ã´7G2¡ûRlvÏÎ-±Þƒl¿©ÊÖ™ûlÄ|§!Z8^á( åÛ„îʈ)…ð’/V îy}0ùÎ%O“å!‰"Éä¬ÃS¼VFºàðЂl‹¬)!he‰F(Øžá,îâsê@ù£Pœ,2;ÎÃÐ~�3ö9¨‰®ÿ‡º÷€ªLÛ‡OŸš™LzO „^¤Cl Š}a-kwíºÖ]]õµ·µ¡ØËÚØP¤÷J€^'™–é3§×301 ¾ï÷Ûÿ/œŸ2“™3ç<§ÝÏ]®ûºªEtN›8º?zƒ 1™ãé3‹ËˆIúÜP¬kõ¨ÌLJ÷î(X��@�IDATiù¦þýru‰)Cº¦ ܸ;¦-=f¸´´»;•³äAjæâ£0u ¼á8X±:ÝžßÍqôi˼“½?]qÊ)Áß\ÈÃ4b'F#7‚×¹ªºEᘑ&Wˆp5ÝÀP>—T)5ìí6åô/‚~Ý$÷Á}-+=¬YP0é—1à@îËr†<¤˜Ð̧7C -|×þEuîç6Ò£ŠËGDu¢3RŒt‘Ýâ‡oÜTÆ60½HõA˜ƒI\ÝNèS€F •†‰Qè*ãM©ŽˆSøî'ÚÌ}µfúʋONRÇ4±h,Ü®£åX¸fa¡£¶ü¨‰ë÷ÎÉüyS\¹ÔÀ±7Ç‘êZdΡzÿ°¬7”÷›ÊÕ«USqÿià1@¨ußÈ4Ác7â÷eõçÊ…ˆÖŠ€ëÎàb즟ßyÉsÃÅÿ&ú¿<~b?:‘B8CÝm£ÕÄŽo¯Ú·Ÿ´¸¥îNIÊkÔ³p6ε -Ìix® z†nwè¦5úœK{·”w2BšW24Uq¢Ðk•5å Âpb®Yhþ£Žó¸ÜV¶{úk*{ªÈÇ—ž—°C•ÖĸI˜¿Ýå½Ä@› -¡zqhâϼ¾·ë±ÇNCå:ô‰‰Ñ˜t/m3‡²¼�u#àdoø:¥Ï¦ÿU×-yHŽQ¥¨ú5ñF#<›b`„»Á@¶lj®õà†WqTãÏDø” ¸T}6Ë¿Õ³¼¾#©|±¡-8½0³ahû¢Ãê3-ÐþK ¦&ž?þÌA2Cÿª‹1OøU]™¯èÊÛtœJ¡2Rü\Dì×êïºlè!–±ä˜ˆ!7™R¯U¤_2ðÍZ\¾Ô,íš_*©”´§BÒ±jP¶µ¿IÜÈ«À^%×æP$�[A‡ ¯žyzB¦9jjÌåw•d¤pÓ]>ý°Y˜˜ü~vIüÒóûX\ ÝÝ6WŒ?ßGx-–cúš°ýß°’ã\tÐ?¯ǿ+œÍÂ7Awxé,¨Ü®·›&evÓil¸~¤fþÛ…iºQ0p+Á£6CA¬F3Æl,Ê«¾•îvµŽ†*ðÚvßË¥¢ø—‰œ€|Zœ¥µÎ"H¥xB²Ò¢Jþ.žI¡X„ì¹=(³· ª¢ÓÈ›à‰7qº6wPºÍ>{Ä)Ï|&p “ãb<¶Æj0WhÊúX$"‚¨øV),õekqFcÛ8Šc9:roV»# §T¯ÖhyuMZÚv¤‘u‚ßýÉóÉFgð-ˆ¹ê]ÑØÒ\›õaËþŧ,0Ë])½k ª hÖ9¨µ&‹øãüZ}ÍRR&G#(Xê\.g�È=Úý¢Ãžbæ‰t¶…¨ô¯_Í·²SÐÕ7‘§âO“¶a2ÆÊúpV'ƒÖጯô3¦6^Œ°ÿ“•e.›cÛž<žííFƒý:h0~‹EM(V ½nT†}Grãýz\Ò$ßÃ?#ß:„r©¯ÕÖÑÍDd5m‰¡-àbMíP·\xû°lò|ñ=ÈB¼‘ï/u4x¡/ÿ4¥Äï¦MÜ·0¼É0ñˆã&Æ¢ã h(—_a¦Y2æòÞx<<¤Âͪ[UÕÁ¨(g§Bó;“/ôË%}R[‰á%E·í¾9Àó=,ïÕ¨þŸÖ?u{ñ� -%ñå…Ž*ä’ŸiÌWm¡xL_ÁÓ«Œ{ŽîdÚÛúˇe<hïjå<cæÁ A󅸂ÉSߢï–n_W}QNªORŒ;굩œöxÍíB>s4æû 2�ž+à-"ý8V"!ä¹eöÆXÜý=ųO‚vóÐiz¬Uôý@/Ùÿº@@„úƒµlm5ÛÛCN|þ»†ßQŒ§¹íï,†ŠˆnŠDØÜïêÚvt³¸’äcÉå"ëö^oÃ𥵌ͪQUxYŽ~ͱühƒ³¸lìÖŸ-‰á%¿¢ "!Tàý"-C-Ä™¥]¾hx™`e×wháý¨ž·”+ë—¹ûA°z:ä_~€Ö0!iã7UgøG[Âák×®•DÅð1&ª\óŠ¥ùŠ -©EUËa@Úð¹ í/PÕ~Ò îYklƒá%cÑW‚Št›;tõgð -¢�Òû¸~ÙÓiÑþéèŒâÁC}rÚzÈoÞƒÔ”ÕMkÜ ´ˆòSƒÃ†µ°zYAj®‘ Óf#Ï"®Úcž°†7q_Æ¡Lz« ‚²[–ã ¨ˆ3ŠúÍîþ>g£}ým\>è%ÿŒ‰ÞR„‰ÉaŒ(µº!}*)ÒûT@ünY^戸- â—$×%¯°>†g~nÞ¸ªÕò„#×/àWÑÿã–ÿµñ%ÆLKg¯RT…CùÎòJ1ïøë2ãÀ{ëÙ~ÓÊE&£5›b”vžÒö‡"bXu¿×{Pe”'U?·-ÕœÇqÀ{±‚i<§ÿ¶Ó¾ˆàIuø$—|ù€</ br;ëµÎ@Ëyã±"iX¹ªÇ¨Ê˜úÌMgn?ù0X“„QÊšxÖT@¬ît=à°ú<¨K|Ž<姺àñhâÿUà{ˆHáài.fÊŠ¶éñHìo0ÖæáÙT´·q"òjQœå.ƒ®D¨¶‚âØ¿ð<W]ÜÕh‰\òøãÑïÞz®…L½EiØ‚Ñ0¡ÈØ ¯7ÐT£Ãðät—o|EE¢¥VW9ÛøXTWÕêW½óRõl7úñÇi‚¦(5SÀHUjöKÕ«îžPpLèÙ'ñð£ÛÇP`Ó)öãžmEßÃ}þ¸òLZJJ£¡Aû=ìç -pÿ}Ê9W ’“xf–\DÃŒ^µ] ”÷ÃœaÇ›mÇ¿æ¿Ey§.¨ðÖ ²‘’Á . ri{cKñêDÆš0ò9F@—ãåh›^¦´ïÔŽ¦‘ZyCÞŠúÀ)a™¾fðô醅o>×iMqª~íSÿ¤T¶ºoQ{»[ -©s77U¯íŸ››T¶¦Ö·ù.±„Wá?„Éë^¦‘\{ò:8¯,Ç´FT~};cN(?$¿<‰_C’¶ÜÄðƒŠl¦bQÕ¿†ëí<ów”g›››ÛcÑtAôÅÄoÓiEYl=t°²ÎO@çá9PpΡëfHAÁ çbÍ/•ØfœSPO¸Ýšˆ¸ô™‰ç²-hƒ=Íž¼º½ûi†ž½Ã,%T=z®0F–”ñ£'œë³d]‰ú<p=+üozŒãºmSßÉ+€¯ô£ÏßQU¥é˜sÓX¯ÂÃ}ëÙ,éâEõ@Œ1¬(þ—•%<$‚—åØg)4Ñ”™ý/„©€èü:Ô¯/¢eê1k†pMeׯl¤˜‚æò�‘“Ï)ÑõxŠín›åvxsý¡¾B›ªJ<ª’€gß5%TAªƒˆ+l¯B(+&És)£ðž¦ŸG±îÚÚJ2Ë%ŒÁc€T”ä”)e·¢1pŒ¨Ït]úPÜòsY‡¾êPˆã˜²RÇ8„Ò)Š¢—ºý*rÀPm\å÷tîÿ‚™¬¯b�žìo.$¼9pŸ3MÀ¯ýLhw#Ü®„|¦$3ÏK…ÑëÖ·n€T;£Oç,×SPA{®Æð6V5%‹¢…·Û -3ŽöÆÉP1¼Ëš}¡'ôbFiÞÀ&6P‡^–õh)6¸R˜i˜3gvbrJþ&ùºÎé”janCÛ(KÚÏhÞXˆHIº$Ï–â‘õÒMX?qî’¿k‹v5Ä»µmùº´gIÀs`2På‡BB\µ#×…„ù( ²8ÖÜ©ÕÔø‡Yœc=€¢ÉëðžÑÞM]8rðà\¿ -Í -��3¢†¢»o$>Q-¯ìhkþyx±½©7¦{E³wÇqöa´º Mó ðÛ@T•“ãœ3dˆo¨W<#=Ÿ'¿?Y_Û#]kQ}P©µ´AZæ©7�½úØ#Å÷íÍ÷ƒ× -ZÅfàê™p©²¾’m•DqµávAÞG^«í|Ô¬[â5#‰±†~€ `g¬Œ3²cCx®ûZô™Ö^úé4f4 ‰øžýš§¾÷±ÄÜêLº®0ðY–IR¸#"øÞë÷ÿkã[ª‘¢ª²Àú›5ÿjÏ™ -³¬ÀÁ¸;ê™ý“3ÌÎÁ6ìrÏNò›r8t%È~Ó{D}ZÖ•"1¾B%²ò¬§�›¸;Ò‹ŠR#!í®´A£ -Égîhwƒ*+ÛzoS9í-6êjÆç‰mƒÇ7gÄ߉Åí!—ÈA·"K»§`,奶úòc3‘®!+A÷˶֘9© œã”]QŸóŠöæê‚ÌF2t†¥Á‚A½V‘Ù§÷vwAøÝ i–ÃÑNV‹¼Î[ÔHlíÄÒRwïc<4‚CÿnÞÜfZç -L?ûÖJã‘À·ðì¾/QKsFþ›ù7ÉPxŠà ^D×Ü0Ûä®HÏ$B¶"*âXÈŽæ¾Wèúò^éŒC{9ô/1¼+Z<cDU}Au§jâ:®íÓGD.u€sLì3¢mÉ$·Çêo6™ÐðÇ¢c ³bUGkõzÖ | ÓìR4¸(£G'¼y�ßëÂÒêîÈ0ì™!ÝrÈøkM ]¸*Ah„(áâ}Þè-{=ÁË÷E"õDÀ‚_Aˆ2 yÌ?H»àòzÊIWw`²,”ªd¶Ëil9þî�©Ñs¢,>ëÓĆ4XL‡ÁÆÏŽ˜�RƱ(2 ‰+j],.¼Ô%(ó.kª'Û&û ™À£¬ÅñÙT<•À¦±Ck|ÿ^?x°oiãîõ#úYÝ@êx²>—Ë3ßRû®0UÈDÎs*KgÓ‚„H@§Hk4™‡s7)¦kCe†ó†Ö¬9¡Ïy&ÂÂsCm¡hª&jv'Œ/ùh¡u¸¬C�o_dÕý%?`£KÀ`¬9èÌH^Ù† j¡}‰f¯ÕO|'è‡í?pù__b´°¬ $"•6F¯Õ9ýÔ5ù¬¬h°õ{WyÔB¾Ÿ5k -ÓÌ´?nåxfž¯aï7á}»—"A´‰2¢$òù[}áA;Ýá9-aæ1€¨/E«í¤&x²ä!ÿê¡Û›ã!}ÓŽ+w~{ï¶ç=ªÙfUUÈfYmötr³áKrsõÜ`v‹è@Î÷~–u\2ô5oz™s7Ö‚ìV«A‚ÔY’nYM:�ù!*tòœP •àMTq-¸¥iûwïmö�o¶ÅË3ݯþ©˜{6=#SÛòÍOÚ©×¼óÊ®8ÇÿdáÏBé£ðª( so†j٬ѹ‰Ü["gÝÕ•ÂÖt]’œQQÚI¼˜ä>�º¶ÈéøóÞ€ŸÑµº.@Âç2›0½Yõñ›®„Á‰†c¡÷Öðº9 j3~³ÓE”4rEËÈŸ†”Æ,AÑ.ÐÕØ'WÇ[Uþâôìl}á+ÏÕÂÄ…íëUÝñ¢½¾ÐµÍðZ<.¾Eöx<È -%&Ëžóº½3< …WܸжÛà‘<.ÐÛ‚b¿`\¹9„SiýÜ‚üACðÛÄ}ÙœÄh�ÙßÁÕ 8§‘;¾ÑÂoÌrlIvL’ïŽ^P_�Y?¨1psqF4×$®G¬< §Ða„EüšCã>âë“åï뻇~}Ð5âqœ?¢ÃvÞ°QS·#zÚÙå?£ÔLnŸ–nå.²ð&ãjà«áíyP4ˆ…%6ŠÒ Ó$Ÿ"éš+¢]{1°i‡äuùÿnDŸ!`ï‹Pc×ÍJ~ÇÊÝP¢ø¼+¦¿®Åµ}ÝÁMÒ"k¾‚O1¿³×èKÖ%†zÚ”OÍ´t=(£3Ñæ7A–*¹?âõ¸ ðìÒ¯ÕúRr¤(J³)¬ÊOŠbÁL/›fÐ]y¹PîeàŽ>’>ïÖ»¿Ð4:ŒÐáÒ*2+%ñýÐi0””qp¶€jH¬^ŠÖ²:‚Np7üJžCœ'bXŽÞ~òo( -g‚à|`4ƒy3®ŸšqT%w]{`Ž‘£_Ghý²ÆŠo€¤ƒTOuÒv9dÐ9¨Š_¥èÌ(¨õ쫘ÖÙƘ&»¼±UcûüÕ©måå¿Õ’û&¯Äp:Æžug¤g€[sN(Üš/£÷ÏžÁÛ;#./!Z'ÊÆ~¨ašáÓÒ[§˜©Dú‚hÑ)ñX¡ -wÐh¦i&v[gÞ“Ô’û Æyv -Ã^u°‚¡Qƒeßi–¼;R5ËXJ’¼Æ¡Å“•||ÿE¯éŽGWÂ�P: HͶ½{°®.žkÏyFg¯¢ˆµ0ÎûX•ž¸Òæï÷WlNF Õþp9<Ò»@¿>Q&Â6~¢=s®,…Ÿ®–x£ÔNo3êM(ï5™e˜ÔìÀ[ÞÔÂ`dÞ¿ÇÍñ°QômfbRù,iÿ�Ãñ^ô}îð8ŽscZdƘŒŒƒË*+Í'2h²C“&iÐHOÍ:Fy< gq=¿;ßÆßYèv™Ï3:ØÑÍÕµó{§%ÈØÈB*‹ão ʯ‘žò>úMŠ(±â üÏœUÖÇç|ŒóšYÕ¾tt_ûläÂ/× I.4 VèS¢ÌíÑXç³/.,¹ÄŒê0Èñ³ëbÎWn8ÐKRyæÁ£ïD~<H5¾-á;W-[ãô‡zÿ›SKîåݾðlU¦<«[�k“íHp÷ÒxæŸ^bçè °þï6Eðã -Š-,ý'ܵŽõË'Æ÷¨1Óö»¶ä¨uÿ£?{ -ÿѯÿèÐC@8Ò¨EÍ!—HÇB×¢’ú×j†¯Y]Ù¾êñáùö -÷zï„@;<Ñ/Ð{\Ñûv{#P£5bÍ,tà ™V�ù÷@u·©˜ƒ‚Âu!I¶7D ÛC´0¨ºÚ}�Û õÞvï÷ÛÖ·¦NÊ_Ž[?ä/ ÅL¾E½Î꟞ÈY&mtýJ\Ä�zx7i…ÄÛL\�<´ -϶˜Ñõþ‡ËdT~(–[…¶™ÁÈËÚÚƒ¡ŠÅ]Tί!mï}“÷ÖQSÒ3G•µ•û:¡c‚5å“M¿²Þ%Ù\þnÓõ·¿µ<ÛŠ& k¢Wýð|’CÐK1›ÙA¬‘JSiµFÆ6ÃÙ¤(Ò©Ã{ËQM`ñ™>Û—³=½_d6ý}hnbIŽ ¿Il“LX€ûý‰„=”]õ -Ö/ËJV - ¨QÁÔ“åŠé_3Ö¸7ixk<1q†} -N)š,”à4’›½¡Ýõš¤"…©Ùþnwè2¤ J‚ªòOÅŽJ>w(ŸšiòÀhÃàRX)ú;à±çé2=Ç5Ký€/´ÏGà€¸QÞñut´Qz2²IN±Äð¢çŽ˜H •‡ñ´&qËʺ¦ÀÏUÄíÊ&4’LÇ}uGYnñŽo» V†šbs°_&ù ͦ}ÆyT\²ÀóçÊ!‹”<O'Ëë®Îf™£òp꺖”¨OTÒR)ÖŽ«pü1G¢HÕ#òœ³ÁÃUZx“YÔEÑ5ÞcODU©#GZÒcŠé#¥œúˆðÑÒ¢‰gTãç‰ÉðD<ä^®ˆ¨F+“‘è &šµá:Öá:ÆuÕÝM~o€ÑïÐX8@¡¡Žó kä -‰ê¼šz5{êô"WãQÇ–¸úì¸þ™ïŽç]ûc:ýEŠÀÓŽ -ˆK3›äì.�g&ŽýØK8ìZ³åCÑérp\Ž‡2 ý ºJ=¨éÒý¼`x)÷ÌíK]ÿ#ªþà¤*DîÔX úÁ0Å1RÌøH®}8•@vbØØêž²¢%0ç£ÝmeȲÎQõ#˜ÍM ËK³ -ìMŽþ¹5ÎÀk;ü×ÆcÒ=Ç⬿òûö–ééã²²\’X+pÚÇY‘ؤHâ*5¦~k�9öÙ“K$8zIçMV|–ÏqÔ¸jœj±ï´˜§†OÖx0r¼×¤0ü*»LXlˆ«¤™ÁHj‹f7!ÒhÑõÔ=0’2ø®Þ5©!ªÜ¸Ì>^gßRâê<‰RŸ„giç÷ð�ž:ï‹zOùãü¦€@¶¤…òôÀϾˆ«ë.þrí«; Èt[Œæ*OTïpÁ,!¿.ÑÜ]¸˜¥*%ýËh`?ä0¯°„<_EtåošÿhÔ?ÈñÿDLžÀ:\cEÒÖo®SÙ|xª6xË—„à Ч”G½Û/)Z9<¶ÛáLBÒîŽâ>ÑíIí&“%{¬™ånÂŽŠ€]~Gó7BUÃÃ×&@^ Þø˘¼›á7Ðü“éý*˜®nQýzŸÇ“é¾} ùÊ$p9î“o©»óØVñ-Jcÿµ¸vy("I[]1ùM%À}æwÓ_ÿÛBÑÏÝ1yU4 FU.¶¹3еˆ½•c§´¶Åb Ib+¯œŽŽIKTÇA�ÐqqÒþȳªßÕŒ®ù|A Ì@âç…ôâäþ\&1)Há}ªÐêÓ@ëlÿNZU§Cêj†þÿ`9î'õ -ô–#ξúoV:iÝqQ"Œ~]êøÀ¢yK6í½23G«{ûöýt@n)ùcñ´‘Öœ¥ËÀʾ†¬ì8y˜({‘NaŽÊ:åî4¼²ØR.ZgÃ@‰Ä6ÅdcæìÙ4É{fÍ·±¼a†Š…éÖ:¾+0·Êkßp ÖX[šUôä‡bð’¦Jv>ø&"Æ@¿xy(öC#„!×+ -œ{ä 1;qZd±˜¬ì2´GtêÀÔ[;U�{…ì;Çdº)š1>DQ¯E¡§Ø½´èß -Odr -ö5š`Z7�[÷‡&£¿²árª‘ÉDÈÞ{¤_…°zÝQè6uWI¨ôyóT@þÉQ꤈¬|bŒ)Î9£r=¯mõu÷Íg$´)?ÿ!%®¿]œ™¹¹÷ö’ï»ãïÓŒéU ƒ…ÎiænV¤›º¹A2ÑÁ³Ž &¤O@àm#ðçéÈÑnSŒü -ðà’‰j5EÒÖÔÀ¥µaàÚHn×͆š7ý©k(ªüp¡®Ùý:¬ÑW¢áyRÐ&U–#Ze6÷áAÒËQt#$ɼò¸_ïµ½ävÉ«…QO˜±çó#w°½u�RhMì?ÂD7sªå'-ÝÊpì n<¨Ú½ P±æG){âš-Z´H=ó¶Û6¦ÉFËÚ¦¦0<&²é“j9<É5’AOž=[_ØàËFX5”Z»'‚.„|þiuØ’j`ògÏ¢Ï-Ê £dîFªîйº)//¶³;´ óP6¦ø çIɶÖF»¤nì ¼`RW¶zÂn£¬@¨‘•{99QÐÑ~ÆèÔ@^ãtÉr‡½�Ïý4X•z™WºÿׇwÜ=_r�$àw¨ Çø•hÉŠR§ñõFVÏŸ8ræ1½ß9sæàAÐ×€8àËTÞ±øÙÕËvš—ÖFò†0¼$“¸øij4®8�W,;ýÁË -"=ŠFÖÕ²j³ÒMY2ÆÐõ42`У³ÁýyÔŽŸò¥‡¦ÚG3ÔPx™÷_HÐ/A‹ë¨¬.…ºï#A*üöDxyò<©}±tw—ÿ‚º€<²àôY9#Ø«ÄŽ0<Cýôã®+ùØä…ËáM…,«_¢¯[)Q<¥ªêPõž|?>ñ¼ð<Rÿf8<½ÌÓ!9ú/ÑÕ°˜F1�E™•µ&‹É~J#¹mòJŠ–crS¼ä•ü 6ÿ¨æË_0ýìN^ÿëëÕíiwŒK2qÏz({@“· õ¤e×Àë$¿9z)ÏÏoïl?¸%.Óeš¤à ÂÊOXý^Zû™`‰?âÁ‹kâ©@`8 ò~ àñ‘æáí¹VÉýÀ°nÆäÑ`þê—+=ùyGØW "xn†1Yt°U5n^Uq°³ñg[È¿h€Ý¼s°òÁð&Œ$Ia$›|%ê™Hªš]”x‘`ñ]b¼d};m9 Þ‘›9×ÀÖ¥qPØÞVž›ÛSL!÷+rûÎùo>WŸL«$·’½ê40à¤`jA[zªÙ\Ö^Sc$õ -ÂÂòTq¶Å05›eìÉãJ^ò÷6 ¨q}¨KóXUžbJ‘k¿þ¤íh|zò·'Ú«ßå߃”•‹a)7ÏÎß$2F21y¢‘O19hŽ~ÊÂ2å yÝÆù·!#w~&’ÿ¼ü!Æ—‘ù©ýbnjصŒ.ïCWÉêZÜÚã.Ò4Ažú«ê¦’¯Z£yÄ ’õHßvpÔ–~iTˆ°`%z².ÙfS“?µìô³gA9a€…¡bÖB6‹Ùŵ�ÛõèäBÞ«Pv‹6Ü·b!ñ!´²¾€½Eq £#¢ô„`ð]hâúÑ„ -1Æ„>V Êc’zHRâ/Gxiá¹ïæ¶bW‰‡ì²ftYÝ£c -y9&³|°kWdxš©ã‡7uG"ÐÛ¾òù¥²Q©¡1«;üçZY‰Ü¨®ïùî³Jm½Õä—åx`] -c8EÈê;“æõ"ÄnXy‰;ó¿ï*"ç…<L?6º²©Ž.šÍ\hVåGáÿoËàØk29þŽ¯÷¹J]”Í&PÌçaQz%¥²O§Çˆ’tͦfß„eÝCñ9=cĈ( -íÍ´¥ø‹¥;|3o9–t&ˆŽ0zÈïfA€ùL1”P%øù¿àú,DÇR%ú$–µÞàAŽžüÂnÉÈ„Qt ´99¥#Þ<h˜ ?°Jâ:4±Ú;œ[¶aÜø¼g<PÞ ŒuÅÏR¯\HÎÙ6y%ç›RåL MÇ>‰”V#R þ¹Â®²^¯6þ‘ïõÙI÷ö•óo§õÛ&À³cô‹sSsß]PöQqzÁGý,æ;€fU)rbâ>úàÑPd§¨Ä—0¼Pað³±o¼ñw£ÿý÷ÐÄ"ZæÑ1Ñ Rz®ñ¥ympð;AJeàY¨jéñ@ü£Ó -Ò·ÇuSùfÐÓ"v‡üuÇXçXF( 8#™zZ“¸k™ÛÒ§/—ÊPÂöv9åæ‡>#¨PÁ ,©`6KEæp™ÉÔã£,fæ »ûÁGFtc%QZKÕY®/€H%ÀÂB¡4¶5ÀªõSr2|áAuœ*°O@|?¥Èm.Ë®ÏóK¹È÷M3:àùl´\fx9xŠ/ÞÑŒ¹‘€Òúï]ëÛœ4ÎðYz¬üWµÙìgdY¸»À°6>]úÄ»S¨‘î¼<J-ïµnòø@�t½ßUG[ û2ýž¢ÿì0;lÍÝjíÅgŒÚÌ¡b«Ñß¡ÒàµÞÅSúE€Ù‰2ÏÍÓÅ@Ź©©ÿí ÿm“B‘–[r¹]¥i<+uëÝ»íÐV ½*ælp·á¡Ô€‚Ëò¼Žƒõçž[B<Òžå`0†ÞuîQœÇý…)†F"irÌíö+cû¦ûæP–,‹‰½Œ§¹´z\ØþËOî!C†$Œw·Lx—µrò Ǥè·9©Æ#0È=;ÁRAÏÉω¡ýÌ4Àç†Æ¶¸+*G[ùµ(º!í¦¾0*+£«÷LpEìÕnÿ¸îhˆQ)}9ž/G~9ÇÁZ·®iv—•¥%Ž§Ú§5ÀÈ1ÃBé“&Ø*Z"ÒXo½6Ê©•-.½Õn¦7XqXÝpŠéiHÔå[ypÿÑ bé=Þ“ýýÆ—Ía²äxzÚÖѨF‰$°ÀýoEK’Ç“ÞÙ¶yMEù!GÜä¸Û¢âtµM` Ç4@æÁŽk‡žTÅG§…\$À¨œö‹S•<x¦{ž¥Ön0s3ºGöw|ºº´¤ý*«ÙÔÕ•©jìù°)·‚ïT�ÎGvVï]Ezþè{á5¾dðÀaªýÓ„Øü!C´UT<zfT‹]ŒXÀnáY£¤ˆN%ÄTÉ´üf6rÊBöy†àÂ0Þ(—iŸ.Rû]¡Ø¾ÎÂl繋øØá‡ÖŠL·Ø?c·qŽÒ–P(�"Ö5åãØ°8éGÔ.bŒV.í\Á,ìèÓRçaé9ÙÄÓ¾àÎûƒ3xRb`Dâº]¬°¬Skæó¡ò^†ûëYÃ;0.…—|¶/U–qKSç",N�|$Ú¨Kƒñæ¦[<%%%Z£„ -ï¬aÞWŽºíÓNTžÉÄÓîÈ-ø×—ŸÆÎMºz-«Z¥½¡¿²Û ×.¶¶iÈÚMV…Ô6QrVlJ;*h³eYm†Ñ‚ýóê¯éU½'ÛmfSËL{ÅBʼnшxk½";!7ÕU–f"ç“o%i,Ò:\–Ù�[^±£2ô€WîŒI©�{ #|›ÖZ„æ!¿s>¼)éý‘ºé¯ŽußοÍÔhªšSÙ·[ýΊ²´´#ò ÝÙ)Fþ 0Èuí&Ÿy(†3m‰BëÉXl¬+8<I“ñ6EÄçÆ‚8„ûƒPx;n<þåg÷IkؼäG秎 ]v£»‘5ðƒ1îaHÉ—X›Þ·Ýå¿3öCgü¤ù—Dô7Q(7ȵ€m‚Š®Æü&Øë–£øº"¯lÑÙ÷N>¶xf#Š-|<Ôby]H•ÅÌAý«òzE'ÙظhQ˜Oíß>© µ‹8S½Ç¼4ê÷ÊtÍIJbÓìèŒIWªw»ÎDA³ŒÞ›ð]ÓžŠê¤£Ñû÷ÇûýqÁù’A!×ÄÜ1\óg6Eü¦á%BkÀ=ɶîÊëNËÌÔ|ùCçh$(.ád‰‰?-mή:œ+<âø*HŠÝ˜‹p†ÙbÚ„>îß³Íë¯ùK}°ŸÝB?¤èj}<æ›g6¥Ÿ ±F�ûùg5OüÇ€)7ÒúFp£Õ4‚|áà ~~|¶5ÑxÜ9Ú‡áS· åêgF¡ƒ¨ü\QÆ7‚M¡æ½{ý$4N®û{¯ä<¬jé¾Ö�·IÉ*î×Nb8¼ÅJ ^Wý«5söé@÷·ªœ©cm¦…ðô ÝsX+SC7ˆÆZr?‹›=÷û$ØbÙø½(¶Ë9™BqýQä½Û�¿¸A±ØâZ8ü·8cZÈFÛv5›™.Kú˜ƒÎÆ] ß›yË=ãüŠ†‰E†‰í+ÃŒ•UU1Ò7£32zŠƒdüëš"9AΗT:C0Óù†¨´tDAzG]8œ‚çBœçq(.4ÈÚÍ¥8‡É1ö~=�….½$«ò]ZDØΧÐÅ<Ǥ„ÔÖÊeÚ{§nÈäÐ÷ÌsäHðƒ‹‘Ì¥Ã¡T«yº&Q^]`*"•;jH%¹`-–Ìâ—àÕ^”IJTk²þ�q®·FONš|³Ñž‚bè‡Huà,~aЩ{(¾bº ‚‰mØf÷Üþ‰üJ0òg^w[8˜Ü´Ò1ê+d¼_Õ:o*J±žÑà ½;hhÞšÑ(<ŽÏqõLäGמP|²ÏA«;.+ÝhvY?ìê}ôú'Ãß¿@™;ƒáÍ£íõd¼‰{ÀíN—ÐWŠe#ÎÈ -4w9 :Ó×Ï+W÷ý#©ÐÿýB—_sçLè)KÛ=ghQuŠTj¿0ô\*@¬ÒlÎ0iV6á20òáU6Í´Và©|QŽw²¬Ùnç»è—æh.F‚sóë2º®ot¶£ -‚àµkaÏ.ÈzÞž¼ íþ+·uE.S¢~/êsZ4ú1׈aE?9f¬Ô<iÎx¸ãgì ÷Ö6è9½4ê{-Y–#X¼Ö#Ç ÅVD^Ìn„lç ?ˆR¹Ö`Ä:@tbxI>‘`‚7¹s0ÚßœÃMPÕ]ÝØ1ÀhV‘:©ïTì'ðu?ŒâÞ"tŠÁq™-ë†gï5±¦´,w³/ixI˜þiµëbQ¡ïeùÈn‡R8ÁS1ÞaL£ê¾aÔ ¿È,ƇÀËÒ¥qÖXÐÙZ2moÕloÛEx‚¶´‘`o»mPvaŸâiWhW>¾Í0NµP$àö{~Pcâ’E.—»é1<$_:¥ØÜÙÜTµŸ±²lD[³µK"… ýó—^òâ¦}ö9xÜéQ–º`“ãL¾‚&2UÔ™ÙˆryÁhoØö³·få¢íMW¬ÎLÝ®«h+œ<}è.\» -w`öV´v—–ŸsŒìéݼ€øoÀ æbRì§é¢¼DøUM-µ½ /1>BFñ¥ˆnÊeY#¯Î:5®*Ý„Ô(9òŠƒÓÛUÿv@ŒÞÃä+Xý|‰ÑEé¹hÞ¹yQEøTòÔ:ÔÝóã&:DÏvXòé…)&¨Ð¥³)öü‚Äàj¹ìÂz_!a¨;ÖAÊQ¹?ËòÐ'èÈ8DL¬õNÄÏ44Ø“$\ ‚Ú„ZÆvW…oþÑZQ*OŽ™ÜŒDŸ®¨–h¯ ñç~‘ßGdúžª²îÌ8.‡—5 ý'möÉ¿÷ëqI;ÔøýFFÁõCžÄn€:IЦšY.%#·{+o¼üÂð©©‘~•›ÄŒÔ#§é]èÉç =,iW»Ôöq™Æx/BvbôæxØÇ´6v:4’““@<³q×Ü> -Äv¶¢©å&‹‘ w7œ9 ßõg›fæê4‰ƒ÷ Bk3¬Ö¢ Ž.YþšÓÄÏ¡ÏÖ€p¹Ç‹?n*Ï@ÚÙf…Y¥ß :·þd¨m‚$Ôg ŸKöÝÈä1Ï} §1{ÃæºBÛ¯ëÊfÿi(> -Ù xmæý]ñ–†óûô‰?óÙ×€¡)UÀ²iP÷>Þï`ô!,FBKÆèÛãÙÖëæ¡.Y¿F-7ÄÀ4£Ù[7WõO5Ø{ïQ9#N S›ÅªsZRZ¯jŠ5ø£>U×fP1z±qDiëù)|@ÊÐŒwÝs×1E!ëƒ(Ÿ™>xHvE(™‚H ÃÄ3~†Â9‘ŒcX<Ôþý´jÊÈ�øù<-ÉùíÛÓ:8¯k4HªÉñÃøé>9Þª©Ã¤µ±¸èŽãÁ°3n5…RQ§¬ò…H÷w¹óeÚkóÑãFøHz§¬¬L´‡Òé™7Ë°âàKvá!™F9hÀ‰](dá³AHSAX‹úÍ~o㨢<ÿÌÌžë•KK 6Æׄ -àÛˆ3ËÐÍU±Ó”}]ÝëÊÒ¬G!ñÐ-èr:…¸Bœ™¾P@j*ka`PiÝï¥OȾNÔ¥>¦÷cB1ƒÏ,´þ5Å ã¬(§ ÚÈÆ"î9Xu‰&£9e�"½[,¬}㸂´žÚJò¸ÜQ1KÓå€üFiŠº'Pj¬.ùo<åäïN„×8eʆÎÔ=íaù,‰×/hÔˆ\À–*´kÙ¢ÉTèü†ã©ûO+²W—Z-‘1ý>dõ-k7õ0Á퇼U“}=ÀZ´»)Ñ-w¼ó7^Û²)??Œ§²ARÐ2ÛÍïDþd©,{SQãz6à_¸=Òä!³ùŸÀy$¯w*üÛ fDbáA*C -Œ±1ßô÷YpìƒôïM’áƒrè,=†‚ÑpIÓ½°/v4d¡'+œŸmë´z2ȱS7³Ã\Y�//¥Å[¾ú£l‚j8âAéüxäŠu™õKFÍ"´]0ßc{Df,º©ãɲ -Ðô±Å£!É;s%Ò,ؽ¦É=’çô{±û6ÿYugºk¤ZŸ×?¤ox#£EE ÐA˜¾mÎvnìÀo±‹C‹¦]º&½öeº3»$†ºM=!ò¤£ûU{ÒžûXºÍó§åmÁ2–g§˜X®‚gõV˃÷ž‘¢þ.Ó–—ò,ú…š"µ�Òårw…oà)óOPÅ ÃägèðF„•°4•6lìå1Ž~ñ@4šC´äª}¡ .QìK<K¢–#!ЩòGÏw»ÝfžW_F®ì3üÞ¼óÕ€¾DçîõE_ƒQ{}³Ð\ñŒÜsµ9Fr¾{ŽÕÈp -)}«²û�ùù©“1à¤B N„ɧ(†¶®Ö–ŸßË«=ÓId°®Á]¶<kÑD³ -˜ÆñÒ -Q•ž ‹îïÁÓ< 4qÑÎ1Í—-¨¢Žð\Ȥm #9hµµ£ŽÐ€ûo®¨E_˜è†ÃÆÃWâäy1ÓqF51Fs·Ü½tGƒ FHBõO·½U'±_K&ök‡Õ®}„%õؘ]ø¼Û#³õŸÀ²nw¯ëu¢Ÿêƒm~•ÑNu-nþvÃ: ´þ%Œìk¸ƒ–±å™Éc@m�°¾4U ìL”NPŒIP,{óNg`”náM§ò@(ßËKþþx½ãkr»y›®‚ZW[VÔ -£-º÷ž×·oë'¾} åKzXÃ’·8ì¬QåJ‘&@1–*“Q%B‘à<#¯^Öà)=Ú�'G^‰ì<Ãó·Ÿ!¥¸§@�w*é·ÓQêý”Pnbæ?§¬L+ÚGóà}›8fÖA«Ì<—·¥Œc=áñx÷wÇ.ÅÃOÈÓ'êG¬…㢺O\ŽœçZ³ÊV‹\†IÌé7%¦¨Ó"²– -ÁÅm¼ EõT>#Eƒík`itå}ùß ,)~ª¬ûú!õ3ûzæ´[ƒ¥@@PûŠÑç¶v[ë“?)OçЬâ]¯2üã1UÙï«wtt³W¾ˆÜ&¯°ë�¡yÍÄP}aôDâó‚’r*J¼_äÈÀóBHb Ç0nä—Ç@Í gÎ@Ñ2N:ôc¡–±Øb°m9õÍÌ %I10g ž K7í뜿8û’'J9ëÑG!*™tøãS:ÿ"îÖ§ÒY•Ö”ˆf>Çc"ÀWCÜ\»³Ò†‚œü�Oƒ<`îðlkÕцmAÝÎ k4|Išƒ31c<;6@:¦…CŒo%†ÿR6éëïÄuL²Ã-¬ë¬õõ¤ÉbzÆ+àÓx í¯ˆ”OõOMug‚ëX¦¢?€úb!î‹£nËËJòÈä¼…kLãÊÑå¶è‰û0î<›²ö-¿Ckœ|ÿ†e•e.;kéÉç‚<§4!+Âó Ô aèƒÐ -“Œ…{Îᵨ�]%Hæ‡Ú»'CÝ'ˆp2‰¿Œ/¡›ó[4XÌ3‹ü|ðR€’XG<Úå/L¾™£HwibùåW[0±ÿÂ_iOßîö\Óá»Qé7aSÆ5ílg}È)iü]±°¶8ù»ãýz\ -näæ.˜0ÃNØbi:NnpòÙÀÉg—ɬ:XÔ˜\„vhš‘e#½=õÝu;¨È�)‰þ1Z©‡4Íœ‘á²FZ ‰”dïÂC{DÅ’œ€Í`È‚’ð^QüP«ÿºh`¾wN"‰Lž ’?T‡æÂpè M1»Ñ -³±.ÖYqE¿„'—X•è®Y¶‹UVõ˜"¨Q™Òžx4ßÑóToûüsùê«ÿf[æ€ ¯¬°W¼¾.*2㌶ ý˜»göËñã{úwGw4EyêÅàð¡ÿ7n)`8Í,©Ì·1&úŒ×ŒÑmÝ0¼‡%þ]åœl3³C�…ªá´xµ£®‹ä5ixïþù¶~fFyÐÊó0Ѽ[×ü`\:tië(“ÎŒ*r’Û8&X}¸Á ïŠtØ-Æ+áâíyQ”'ƒx_8;åçò^Å=r"êBÑóE™úEˆ0"ˆtÿ,„ìÁŸ>÷\Ý´[o5§QÆrOõCMÿ¨OúÙí ø©Nr»-a½zW9ϛ㢃ñv—Iþú[Ý¡ÉðPnŽË Òä´|†æ¾ „]>t<ʽ'¦ÍmÞSŽ‚Ñt¤Kô*Fghp>ƒ;oz#i·-/I|žÌäûà?†tÓPE£—+bäÅc®§ì΂ôyž~^eº"]õÉsFÎÁɺ¬#Aç&yÑ/Á~—H³|_ï½’A†~f £!‡%,±EUšµnûñçÏ?Tˆ$òQ6Þò -î“N†b#1QR‘š°›Ì'Cn÷ôýüüÜÒâ8·¨ˆÔ%¨UmÞ|CDÊÎ}1¹hH—Ø6{¯,ÔlÆ£3öY(%µ�³>-íiøY œ¢¥I[:£(mÿ}¼ÇÅø=H‚EÅêjô!·Ieàš¢k*$‚ñ -’lê£Æ†}ËÊ¢”tÖr°£Æ?¬x@~TbFäxZv‡«WÓ -)æÜzô²¦%t5ä¢_†¥ý4qCº'gšË&g`:ŒÕùêy#wûò…åëÚŠ@ï&¢G–aÒ¡èð7d¥ßa 7×&0§¢rYªð‡Œã¤ $Áz–…1ˆÅÕZ™Q'Bìs-—Îo_…âÓ¬knËn�#YvjþÝF†¹—†mD˜/ÄTݤǷ¨]ìàýVxv}"RôœéÅÙdœ$^Òäß—ŸA»c_U¥P#¤<0à‘£Ýœn¥=¨i5˜iw¯_RNƒû ǯww7D³ÒÓ-]ŠvǸ~¤¢ßÒ'ÏÁ2�Æ´v¶›*Óü×j¼«Åd³E¦al úúæUÐ<ž…ßBƒúEÅGç:Hõ_#E9«ä5–AûµÌ|…]íýþí—<$ ’Üy%),Èncâ%äñG|×{½Þï‰H~qÊ‹ÈÙï•EjYû–eM½.Y—(i yð9ŒCP ãÏÃuí±Fsš‰>]}O Ç›ŠVﯣý6B¢O&þœ‰g]‰¼ú«+2ô&ÜwFìc&ûŸ> |ÆÄAHŽãÝ*g‘™ÖÔwå~´ü׈(ùý‰þºÑ/Ò¨èt–~œ©™¾H¡ -Á¤<ÖŠ‡u΂ͦ‘}‹ÆoØT¹iñ‡ZüÉ3 ÒútÎÀºId%Öd¥uu -EGçÉÊ"÷iÏy:ÑÏß4b™ÌzÑžÖ†rú]ȪTÅ”âÔÆä9HÃG»›RK²R„ k?@òHb¥—÷`òï\ùáÜ#” “¿9Þ¯ÇÝøVz¢§Ä5õä\&ÁûHÇØæ%hílD{)8oõ™*ºLÀÿZ«å3ÒÍDŠäϾ>Õ`´L0Ôpü\&C4íóBÍ�”9·ÈNŒ¬¾¦5x)èò^âÊí‘#W¨Ûr÷ UÛÔøÂ`+*7¦úˆÌÔ…©L1ºj>àI³c2]È0j+ÂÐͱzó>BÓHÈ–©f[Œâ¬áhìZPy7#6¯Š"ÖŽ3¡Š‹ú”Ö´ùË‘›½-¼õÀý`âMå¸yG`;õQþrÔ5©ƒ¡œ²wQ(+çñ•gf</µ´©³¡Í?bºz¶�9 àÑœèÌ«M1ðYØ”~´p\¢<ÈÏVq:@‚zð¹6@b>¶jÚ¼¥Ùn<ÿíñ©Ý?Lg kÊÞI«N†ð-èæ §"i(ä@·4TÕ¥;`ô›4½¡—ÄRÊåûÓík§è*w°ÔÏkrûòåm½Œ#òÜMP¿¬é£oprŒ½¢¥FþÞ�™¦G{TÄ`§ 30$Å}+þýF籌ö–¶àL…¡>G:á!{¾ã}À¦!qE7eDº†‘½lí1p&^î)vüBÆŒü{¤sÖ¢hY†I¸øcÔ}¿ü%×Z“Ü÷wþb;Lð**D=™‰žZ²¹yÿ¦ÞtïñŸ¨ï«÷ösÚѨ¾“OOný—vÿˆ–J‹€+ÔÈ¡¢i9.I?¯)ÈÞzË0{ÂC$ç,s¦Õ%‚”žµíE<õõJ¤=ßäÈG"ý8qM@G(Žÿÿׄúuž -)Óž÷Zc"•ùž)'Þ§M͵}~¬1hÜWÕS}¯,\å`A[€4L&îgòþX¿9žŸ—œor@»;ý%2/Šg!Ì„·ñ‹¬èw©<ÿ¼Ûü^Ý«²¤Þ‹\òOÚ}Å™E3‰WI~OºÙh´Àn%‰ëT='%”ÉòãóRPÃ+Î$ë2š¼'ÕÄU_;c¼?{´»/ 6=ÇBàg@]Ì‚—Ö/¾yÁàßz3Šž -4ƒÊsÛtQß匼¦¯ -€o ÉË[,ý" -õ }: &|&†I¥8)Þ!ëÛ.,)ñ×z)u3w!·:4“sØ°Cìò)&=�ŠÞ—¡f°1ê%oÀµ¨®‚¡ÿÌO"4!Çካ!–Ó߇·|'¼•€Œ™‘NÙ$éÊ=`“z\3?²<ÒUf - - ¤‹Q(€Ë|‚ôÇà<“í…xÑdB!TœrX)¿p [¯´Æƒ ÄðuŠ*o䙀…ýJ–Ù/zcwð¼Ô…¨äÙˆ›z¶ü0‹¥é×Ä.$}0ˆ¦'¦¦6÷2¼Ôn(›„´ëpÃÿ¥Â¼:ÙÚ{ø7G¼¬íôŸfæ¸W <óÔ„ú0¢¡CI¯@ -æÀ$LHIÃKÒ-¾ˆ5LJָáÈÛ›Œ,›…ÞÁžë<dë<¥¼‹P .ƒ“?ÏTQa ç0‰iÈaƒ—–Z“•¿!ŠzcB®5aôɤº²½=]Ñ䀣ÅnBÐj`¶zr¡Éñ¯16Ñš›Œœ ÏAâœýØèŸêàÙ·Á*ø†ÕÀ¼Gèu@Ÿ‚£kïUB"矰̑èyo ÞXé L[#ásèF-À·ç!½cC ¦ç^;QÏI&0혈3ŠþÏâhÌJQ{8…8vdz$ɽünR®£Å`H)YÕx*—µ}k2¥µ¹+p=vëwÇë³cê?Ù8 õšý+Bîr Ë@zÂêZ>.ÍÔvî°¢îqY–®Ýuë᫽‰|œWó6U6ça=víûó:$Ê°‘cÔÆl³yÑtxöB8„¢Obh9½‚ó1»‰ÏÏþó…3;B³Hžg4ÇÀŠ÷�ò’ƒÐññn»·¥ Õ }G‡TU0r6öȃöžˆWž†Dà0ôx´Ø/÷VÔ%/äáG¡ <îúqE��@�IDAT>ñ3(€Zrì¨ýî¾n@F˜<à~:hšVŒIôÚýŒfÚ¾ìõ9§öͬ5ˆb Fi— Ú:Tm·Âgøʤ+‹çüª)×øõûÝ;>|c—Ð'c8khº‚KÐv[ä÷„~‰Ó湬"ß‹\ù¨þ?]-„þÌ‚ª¿`º;’\•O\í þ=.E>•Yö}H½l)%('Æ'Øê®ÁLÞ2‚a^MÎx3&ÉRÑïG.üÖÌl{×h(CÊçu„`°Úå~…ý3I9bÛnw𜣱¼~¿?ŽÜ¶ÜDšI’YZtôNî‹ÎÀ 6Ž{ü7Ó�>™a0*áP -¡·A')†Í7ß{»;Êÿq # ô‚Lü{Éó-f_n–žû¥móšF|¿¼a}èÀ>£4Îy!*ÿFÉý’ؽ„Ü{Ø„¾Þ·j1^"h–)ŽQxž-À½µScº;Ýþ]'›×KW$è6 .'Æ€{"±8ôyˆ0»Ý’øMðú»¢Êü„~§Cr#ˆ:r©pv9N”Y*÷p*(Ú*Hn^£j5Žé\0æ÷_Ö~ü1¢/m]œÒ/ÇøÇbvÃÐ’“úÑÃ7·ìkˆF¢K7v„hÿjN7 zP÷hSÞYe9¥—ý›ãù÷q3¾E“ω|haQãšòœ1?¢·Ò8iP£Ú¤¼Ð>;Õj3 «:¬ÛE¾''jNiZÐÂÃãc>©Æo£B´ú¦1‡ägö£ÄhœzÓgByv:r¬/¢˜ô¹NqŸ"µqâ|*¦~‡=‚Js<û' ®‚'ùD¥7tËÊ Ú + °ØGÒåzSï<Ï°²mxŠyãü…¨Ð 8¡·nXö5¹ÚjföKÂîRë[»/”âÚ¨®"mDmvÒ§õ±v%/4!è–ùÀª€A©²ðá}²\Ä�ã$Y—ü?’l²äÛ¢š!æØ¢jôýY9©ênnŽÌWCïÞ°‘1HD“ðš¢Kç"R4$`ùôQßRhæ:lØO…]šä§_ÔÕeJ¦ˆJÌݸ©²qåاáI?ŽC -"€[‚¾àT‰6×í…QþŸC‰ºGÎî{PÜÿ9xNqŒ%µ·á#E1餸N¯‚·Q¬°ê´Ó¯¿…õq_!EFW@˜ºq˜÷æö‹Ùƒ#×Û挱Z¸— u~¿¡öÛSPÇ‚4Ön¤$C1±ü‹ù#Ÿ“…nEÖÖc²�böµ4cÔEšúuX–_žZàØ×û¤)ƒQdŠcÒ´}šQYáëÐ6Ïî×Ï}M¯NÂC[?9þÝ&c¡<´ªõ·dIÂ[š##¦èÍÀ{èDôú%z[‡/ð%Ô|KLTš÷‘¥N`ïmg¬—1´qD,NÏÜ ¹PœQI6EpßšÙ3áõþí‰ôž<KI« -Ir'¼¦Gq<LIòÏëš}6#5³)ÂÞã%^ÿåp -‘"¿4�’¢?D)êÀ˜¿ÈëÔ‹pÎfm@z®÷oŽçû#’ÿtÃÄde&ÍÒùH- V¦|Œ6‹ŠdnîèíÒf‰Àz–£@¤EõSº~¥]Äç$†"VstÉbü§4ä$aÐZW°ß¦VWiŠ40‚mºA}ôãàØ;ýëÃa€TJ¥"kx©£bE;¶GŒf~æ fý&´4ïÁ¤~E@¢žB:žÑ -G4°{øðìˆlR}bXí+÷1ŽÓ(HÓYš&–DTa;Åë“u»ü}`¸ê|–¤ñ+Æq¯-®=Â(0u{wÂ{s¿³ñ§3úXõ¤¾²º=}E«÷‚D $öy¼m<Í<„»¼Ð¸{r‹sn^PÑ`· ™`åæê …÷<눚9çOœ1ÊÉúC`i²øLw@,ð\‚yÀÍn—Û²{"P#ÈE�pà|î€ó�äcJ>Í"ÎÎRH¬´<Žñ<5¯ÑÂûýc+âiíox²ç`å¢ÆúëäAŸ_‹,êb%P+2¼žá ô„ïdX»ù'¾,+Ê?‘.ªÇMWdâøû€]&fŠÝÖwoÇù~&'«Æ¿ž‘0¾šÈTÀ×ÝÖ6R?·ä¤fÏX³æ×,L´éØ1{³3¸âÂPIèÌ‘}$—$1 ¦Ø²ùõ^·ç쬬úë&e„’ߟ”¯(p¦¨t%¨øfÎLäǬ2óÜùÃ2Rse•Ú™ÂRÏ䦦<Z×Ù`ÄóðëÂÑRˆ\WE¡ì1I)EÖÔ€gFÖ(" †5æ?úëê'ô;F×Ø9~T¹ë d±‚ávô<þ%¤êvÜ#Ž9xZÏGtý¹;(}=¥(ŠÜ;F&~�SO)ȼòù›ãðáq1¾°•0:ôXè¯ALWoFøÓ¸~i‰BÚ±ÆÈ1V@Šh+žØAÑ’›<8ÀïúS·v…ØäôO]²¤Î@<\G{Míĉ…q3©Ì(&ë¹ÖB¬ÎNr8ZBÑè‡qJº½«r\%iÒMq{gý[Ï%Ô†“û¨è/8ÁP9¥û±:ýÞ×b–¨ÄSZÓRf XZŒ)4=ž/’¨P|ÚÔé³øÊ{%àE Pœ-õâáíØ&Éù¾xÊÈ´+—UvYßZgû´¾+ëM·[Ÿ‚ê3Œvˆ±˜zĪg±õOH‘›8vò1î|ý®Ý&–yÀðZUWo ˜M/ቔOÎsϺ;vìà`@®/éPÄÙŸÒý§!©–=е[ê±¹íÝ®ÝÉÜ5Ù¶Ñ$ Óz[Z/+Í(|-o/c{ô{%F~Â"†šnt:ùw¢Ÿ—eJõL,Ÿ¡hõ�¨±Þœ™!Ûê½€×e¯@««ú‹†)’³½ç-n4¹Þééíàpt7le4u.Ž"5éé”é -Òa-Ât=C¬‚÷Ær±OngÒк!_AÎÜ)0tAPTŸ2_¿¦Ý=rKG÷yF»+¦h¨l~Õ†¼er¿Çz%“Ž¯m©g>„ c%“Á¼ë7'úgÄã« ¶5ïw67ÀH&kk4ò…®«_šyM”Ú{ Éï€'kFqôÕözW—196¹±±KRÙ)Z¬CCL¬Ñ-ÑØ® °a¬²·LàÎI°�ÙàGi_8bÏ�ÅHÕ]ß!èÝN]Ò¿Ç!üæ8ðAI)™æˆBjP¤HÑLO"JËCÓØoîùãuŽÚÈgÙøEìx_ß}¡;ÊûçzŽ5Hr“ïpúÿNsìƒ8h;rÃ× '7ŸäZoçU„Pƒ™þH’}_O>ŒÛ[�¹õ¢²4ôc¡û °*Û»šiäf#Û|¶V˜hGwÂ%Ç@ÖÙã _…¢Ö™(ú½n0 -®¦vO�v7ÖÑ;ű¨pž…£:X¨ðá…ÝüøÓÌ™¿â‡I1hÃ¥·¡C“Kø˜ º‹SŒÜÍí.ÇLæÉú7àv!…ªZÒ_%†#9òúC] ÔnÒ>CäsÔÅFUûwkqz°%Y‡¤=”’ácb´þ¡7ª ;X–W¤¯HˆD¾'Ë‚*·u@¶yŒþÀùî°EÍçæ‚=ùw–zÀÎ",½³y!Îo=&žOû]BI‘FBX¿’îH7´:̯PhTÆRÙìO•Ì¼‘’ш<~gÓ‰ X=3%½‰Çs%k 6¡ u¾<"¿—§sÍ{)¼àf4:\q ½i×ðüÒüˆÂL6±Ú»à*¸4¼oÛb&÷I¼ck¦ùJàOÁåÊŒ±O7µ (EÑ—¼üER´3ù»ÿéusÆ“šr«³Mº&³ºø¾÷Dû?ýþDûþ•=mù“rí§‚žm÷…«$›Ò¸�)°ëO)ïc6pfZcZ'B:ô;½Ã¸ÑO?D’H5ìb²lóY¿??a¦„bñMÛ>«‘ù^¿9aß.kõý RlXVöX -M¨r÷tD¶¤~áwäŸ×-S«€œêy–H^b¸gÌ,k6š3s€TRÿnõ¶=I&ì?â`‹ç›jŒ¢‡†-æZŠª5!Aù{ƒÝÑâï‘ÅBZàÙ8½Ú׶ù·Þµ_(õêÔ °î?h68^Úê Œ^”Ãx¿Z¯J”ô¤º SÏöùN=è¤2ÀA“‹ß3¼d,ðòtE@ø:gb›wmq’ÔÆãkÖ°ÀŽL¥-YP<0£åô⎇eé;ßÕ˜4¼¤’¿¤ÑsÞú›îy–¶ñwFeª¨²…7~ôê—?òœÒçg2¢¥ój¸ÿSQ7¯”5Çp• --«QûÚŠÁÔ¦¿†B’¦ü ¹mþ)óàé'Ï1:@|Ô]Ñl3ò¯êzüû+†ù“ß“×!”[B)"!ËCýÕo_½Ã.¯BÓBUP<›Ó(ºV…âSjjjRv®[âÅ)XŽDœv¼FÚÊák1V;ÜþQpþ³4¥«¡!‘:!çknÐш?–á%mÆû|>$< ’WE3HT‚]Œ®ŸlOê¡÷¸1Éú�9|Ê!°¿ˆ¢ÞWš“cŸ“ÚD<Ü@ÿS™m4vZoO4Ñ<`Ó¿–Tê6´B~¥ÑªžryùŪ¬ß&[èù¿gx‰ö~op,JF¿“Æe|¶žÂÒªÓ]R’Û{ùdyÿ´ -Çe¥¼Žü<b¸gí©æçã~¶dHU•>£4³nJ£ò†—Ï#sÅfŠªº]kÁ?H;øÂhEátCÃÿÓ}"Ÿ#KmB¢º²ÙÝx“<¯Ù…i+Úº¦¼ŠšÇÍv–Éì=þÉE™N¨9¼„çq>GˆZ¤Ä½ñ˜øFIff`«ê½þñz\Œ/©¨ã‘Ù‡™BÊÀÊ¥§ýƵ'&!9%°wàà†€mžƒ¡þ!苾tÈ;ê7eæ�<óÓ€›2#DjÏšÈØçÍÁÙ›Z[С¢5îj É¡< ÕšëÐc&ÌÈGœy»ZV園Ø_¯oöþÙ%Rê<±T4¯]›˜ ¦ä ËVyz6ðF fQC= -’c»µ]9=c¤ÚŸçÈ»ÕfþÜ‚}¸ 7é¶À=ƒg]™rÞ€ìF_<þf í¸ñ¡q©¿£"ëIÚäÿãî=पÎ÷ñÛïôí}ÙÆR—^¤ -‹ E±Æ®±'±÷¨Xcïݨ±EeAé½-m{›-³ÓgîÜþÎÀ¬¢¿DÉ÷/Þrggn9çÜsßó–ç}ÞÄí‘чÉq3°Ç(Ú§¾„ ÓÇmq(^UÔ4îë]–ZŠc»ž‡/Æ€j~”6Ôw®è_Ô'®Eö õõ&>6!a¹¾ö{Ý}NƒßTSµ'±>f0Ì#¼n¼(§å?1r¤¢çEL*L03Wc¸ù{<Á›«|Ñ?óÿ´Ðdâ½F•”ü¤†Kî*NFçoVu~ÒRmä;²•efFbQå˜ôÏÜ2µ _áÿp3önÚ‹ø1 IÂÈ.šnaì—Í‹ÍÉ Ä‡:Q³g(Öùì·MóHuL(º£¥%ª+_Åúþ`”»&,Enà<Ôk¶"åŽ~x'œ„ÍdÄ~ Z¿—ßÿæµ<jQ£EŒmˆÝÚJº=ƒc]ï×úégïtç5ˆî“s{X3¶Œ9¶G²íò‰—_Þ‹8Vû‡ -(‹ø‡…çOBàL™ä¸D?¿tz}ÈuÿcØÕ±Úõs¿‹¡î#´ž€Vx5¬Q"°|.ä XÙ:ÌG¢\”Ä‚º6¢hL쑺[ K¯º2GS”¿·Ö?ƒ»ÂÉÜ�ÜC’7ºN8Nº^ö_r=ÒxØéŸàm@(”G‚îÜyGMâ:øR’²m7 0s>´^ï.DߦZrâñ»}þFhs(Bµšñ8´é ¨÷r<}·Õ–<“øcˆV89/¯³ÝŒî°ÐÆÚºúNâÞè2‡>é -X¸‡¥Ø„T•‰ßºúíÚºÔAu€¨…^ÖÔ9J™³ÍµøÚ³Qñ3Éƪ_¿´~²¬¾ÑœÞ'¡-bŠ~�ÓõpJ|‰¹^‚`›5·°$~oyó*úå‡Fû/:j.˜Ü7¯Ë¯FàWõÍËÁc{9Ú3 n³È™É¡”;(Áì³….(²‡¶š…/IR̳Yqþ…Ä÷‰=rÆŒ~¿jýç0¿Cðÿ¦Âï -4œ?0)ð,ì~`ü¢.ËHʹ(ºW üx4æ^h÷án¿©^7²!Yn#Á³£ƒ¤dÌw¶‡ÏÛãÎÛÚœ»£#:ÔÎY{¢^?`g'G ®�mŠ/¶¤M'#ÈÜ¿mGw³Ž -67JÖ{Ã**=CT|þ]€>ÃÇËþ19×z ȆúaXVõ û‚cu6„ɣȵki£ï™œÔÜy!§påKOÕO)´m›R’µk´–ì^ÓºCÜÈbYZÏ �ÚÞ|Ød8T,ff ¹?¿KŒ!ÙS:¦ÑÍÁ¨6AÛ¬öªª®ùÒý¸_ûç_®w‚HþdO4ö2§ï«ò}I}K£ôQ8Ó°xeéW{sˆë¬{¶mÛ¦ÁzZƒ²;1¯?UTµ>_ÌyL{ÌT€H?óì~Þ¯õóº—^"ý(&åvàëŸFñ„§A¦ó&/Ÿ·ž -kï趓¹ë¥…Þ,Åç‚ *7·Èy³h߇%‹ä#ªC Š>ç—þ}\(%I#ê}Ž³ˆDS^ÌΉh5Ëß{Ë;bÄóìëo) -‰Ž›l¸*Ñìf¾¼¿1úíÔ±N™¬Ú7Þþס ^‡÷؉@ÏßyÉûɰܬf·ª4B$]„¢“CtŠ]Yà²Å…)R±hQxÖ´1G¸8#±R+Í ƒÐñYyÞ÷öW‹:»—L)B ÒÞíbŽæÇ…ˆžú$TÁäètÅÌ]_d´=ߪE¼oaéZÐ@æ@ý› hna/G�£ -øµ§-Þ©N3Ï°» -:U–ÙƒŠßL,JoÄyÆÛUí=«;ý‡9è…Ûá=A½1m1²û.v8©]uo§EÞëÌæpQšû£¹ß/"XdLTwˆkŽËÁ–¶# Ñ%ۢ圤MdKGé¥Ö ¯FļËtC]¬QüB–Ö>FZÉgŠi,…l‡/ÕÌ´ÌìŒ;BþŒ)làþ�Ú°‹Éç¾þFºÜ¹~Pnn”øÅZ%Á•d‘çbá -j,˜Aòë°�q}ð,aÙXiá™<#´6Ùø]¹ÝÀÄh/q²=¯øV”ÿéùJ2ÚüUÝÞ3ÆD÷ù¥Zh$�‹ê…[¡FÖMrÌ€Â7 ØW/Q/x0ŠÐݾËcÚŠ+ÏžÞ¥åÎÐæšÑcnì?zL¯˜n¦´FÔ™FRÚµË^ÿÅ90§G´)ÁâhnÛÓÓ&ìèÞV\—:è g,W,Òھ΢üÚ²ncL~?¶z¯šŒòT—ûUù±…é•W'Ù½Ëý¢SäFkŠù©“æ¤{#Yæ–~¹½Ö”w£”¤“z¤è=cdAÍ�Uk¾Ëj…-² Íh3&o:QÆ…¼;-¡X+H‰÷Š¬0vÊUÇ%W&æ汞iM 6Ö&²Cì㌠bŒF©—&È«wàN"°4ªýÀî/Žgy¡øŠx¬†ü·ß‘€Ùçû=oå¥Z†ÿ>™Ñû?û¢›Ï¹t®fÜà! ¬FÆ“ÁˆùU‚dm³oMSDG³a>‚"£~ööÛÁ!p'ˆ!½Y²Ò!”â˜Øà·*ê†L¨Ê<©ºÐrt;½dd l'…}fvÿ±æ¼ -jÏœÃ9ûD¸t&å\d£ŠÂ:“0kX`µ¤fÙ¿jÃûïÓ·…�\64±tVÖReŽ¿Ä9`Ucî<Øì800Wî nÛTQÛ¤EıŸh§6‹•»ÑÉq£¡¶†äÈ“6Jl‹¨j /0+}TItv1$«n✣ö4Òd³T–mÕé½%•MÔÎî!‡ýœUDÐmžœ>Ư7sæLÖìQj˜;~Í|Sá,xN8vËå7ß|p¯ŸãK’c± …juȪ¢s -o…^¼y‹û¶ªâäTȶÇû‹V¾Sea7Üg÷9Ä–Ÿò†ÞÜF³‹ì ÝYwðóA€Àuk?;éê[®FôüzÝdø‚í¨K”Ž…înçˆÌ 8î@`ýŠ½–±ÓŸÀ"ñ4V@²(íîtׂ>²²¥¥V aöÒô½0ÿjÎ|ÙÊX¿hÞü¹›*žÕ5^&4×½¾(ÊD÷Àu•Ä1 ”4(ÜV±IÒ™vJËCæT°UpTþ¡ôäD[EQ,c Åfrb1o JDǽÄï'¾^òêãñzõIvÝÕ«-Ù„¨Ù tæ7=À’Á=¿ÍR²íö¼€¡;,!¼iݶ£A5æ]ÐJ,*G;d]ÚI~ŽBS�Z–ºát,©â¨¨Àu‚lö¶š}áì^÷(º9ÉN$³ \j*ûǺ ˜40šÔ‚1 ¶’iQµÅ0™ÕÜ"âD¬âølG˜¿ô’göIoa¬ÔËÀ»ÿ öfT"(‡ñ|3ú‚â°Í~“Ûçþ,A†CÀÏ¢À߆ˆý�ü´¦¼ÁfYšˆ9HÚ£ðÔHªd#:š`!}h_WOäáÏgXí…‚qSO!Qöîí&Iþh ¤4ûÕoA’Ã)#zxO"3bþz\9çÃ,+@ú2'+(£fÄ>×dmMMϞǼäÚÐÈ‘Þš-ðÜ£ÐƦ@É{2Ò,mºüÈ™††ÓšpYGkX¥à5h^µXöÓšYCÕmo’Mõ±ÀšÖÍE|›°¶%0muKàªU#‘¿È'úDƒ]!¤ì”ÞžàdŠø籑ñ*+/Ïè3a‚Ÿ!“æÄ‘H"AÓüoÖÔÕ‡œJ~+JNöÏ(utôIÿãºÅ+�.˜^z&ôÀb» ØÃÌÖkqïN—$‹Ò†…SAÌÎOƒ…pq$A0vàÙЩ©ƒâÏ+Ñ´¯ê}Ó‘J|;îÝŠÔí[QÃí -˜ÂW¿2Ía À؉'HØmÎOÁ(õÁ CHÈOÉ÷üû“^yÅ‹6»¶[¨‚±Ì+ÓoÉÂut„¥CîGëêzÄH]†©ïGvåc¦fÜ|²×´hÒb ·b’�œ¨oŒk…÷áÈ n—&ð¡:LE÷fôïD?Ž<ò×û×?«¡z‡2JDÉ‚–qðL&êÙµ!í«LS©!Vž„kJÉ‘ýp‡âú;ˆqËDF›× ŽmümêPðκuYÿæ›Ûº'Gžýëü«ÆjEdÙ@ÜÞ|ÐÐÕ4‚`køSíܼ¼ ŠÆx_ÿŠ´}1.×¹8ÐJpZ±eÒ£éømÇMóÝ"“ºh‡°ŒŽe‘¶ÏÒiÛFÓõ0MK*x ‚RTêÔmfKÇ·Ä÷Jº@´5Ô» -¾ß©ð·ñÈTr¡ºÂe±æ€¶Ià*ÁÖÙWw´™ º_ÕÁÃß -Â1ЂyœÓ3Þù*òôÿIüÀ‰a!Ð5¾5):=d)^Ô£*±XRUnèÐHù’u„� £Ô7’äÛvÈ}¦&N>Æ^UåE‚År>¸~Ç" V1TH%iñOY{5¼Ê›_l×l�áv!-9B[\ Mõ—B„Ð_ËÚ3îâ9¾œQ9&é{ý]t¡zæ=1ÃœŒkn4ivˆ§Þ×k±û”$”fd´ï‰IÅCÖù¢Úh§•ã4–M*æ>®ò–JëÁî˜ÝÀŽµßË²Æ ^`7@1¶að¡Þ -0JÑQoZ;ÀþP0œBÖN‰W—#g"ã·éÒ"Á9»Ý¸2†gb°~ÿ;tzOô¼1m@¨¬éÈTÛˆR¸ßº#¦ââ¨Çñ,Ú"1Ó[†´éÄ…È‚h³e\ëd˲v“¤ùV6Ø3£Ç¾R…ß~æCÚ>ágXëš2‚¢Ù>~•yè$Þµ¿âê?¾îQ̯² êë}žª†ëFŒÐö´J N†Ãßíñ}RÑ",¿ŒÖÙgN©âXn«3™`˹:“›À_ÑŒ<ôY0§‹ð~0çyS'‹ÞgÈ\‰Lïæ>!ÏàDÙƈ -O 4Û—RX³ƒAø^…*€šJÐi0àÑB¦ VHãL_ zuX�W� RiŸ`„åƒ1‘Þb¡˜Ü¯OÂ…‘/‰Ä !ÿ‚³N¬EiÏ›Ô4Œ¶lhfƒªàáŸzžD.¢à-({ú¤Ô®w‡Áÿ¡Í@ü„@T[ îÿ§ÎÿoûÁDüo/@´É‚±ÓFÖÒ·Ÿ³tÃæÆ66¿GV]MWôÑGµŽ¡C9kS“¡shåD$+±~Ã-“¡=]‰÷?å[Ð~:À•pŽ]àãðaÖ¤ñ,[‚é#Àßøo ¼€‹oG¡2C ‚�¾ÙÅ:úmïð¿àß“¼»ü°{aV¬…ûš×GQ2!Y5.2Yñ$TÂÔÏ^Ípi¬Óz%G)IIZZBf$šEUy)—;ìh°f. -÷6§¸¸=!ɘ Õý@ô yúoª"øì• “J)ÿ¢ŸÄ“Ö+–"›ƒË‚´'‚M!‹ÌŽS®øck«IꤥÇï‡,«ó±€`Fà ÙÖÁ -æÝ,m?œï62^äï=a#=è²1§Z;Y~Œ¢*ƒ8!õ-çKŒ+‡œøuË0QƱ¦eD*) _[9¦ç9ýËÁHâ n>%\¶h+›š>éèéx¦’’;™VÕ×@¤>ëL:³GÓŸÑ9f´J‡Áð[JÝþÁóÿh»822Ö ;ü\»b’=?;¦EG@àHóh«äòÚ“œàø.ñA?è}†,gMªT„ö¢¢øk;w'7ž‹‰l)pìñJ£A„SÂ@ æ,Â`žä•¸¥QššÚÕz„mo¬±egà%»Sg €L-óðÒ‚¤ÅP¶÷}�ͧ'î|h¯2T.ˆñûДŽú|P.?ä>òŒ_ß_Ua%0T£îÿê³5ž9Lyt+á<(óì–Øšw"Ô”9ñŸEYÌ6iãÊÆ{ºáAæf@ŽÂIäöJÒòöì©.öƒw=ºÇão"HW6u®Á{æ�”c,‚2“æ÷ÿ¿®¬Ô¶H¯!3pÞ•ˆ7¤aÝʼ©Æÿó«;ª'Cy9^Û/N² /s45ˆSÀKÃrÌ_/H;Uí©;IÉ·¨&jÀ”$PɆÎþKòxd›ííäõ§4;Ë(á‹á£ÝÈôCÌS‚¡?Ñ¡Óµ}œ©2͆ò!I2☻QmlŒªª½"ùÔ…åý3‰"ÚõЋÿ0ºÍPÖ`‹9Þ\l´)»èñt«É¢r…ïßå`(K¿€k îE„s<ì{dm2À]«ëà·~ånìŒ@]a6B]f÷GMóU]ò|‘‹IWV†(ZNexð‹oß¼‚ñœoâ¤Mc¯ºùÀ{Ådï=£0©nu‹”ÏP±¥€{Íu1æÆ0/(€Æ‘IDŽ§&NdÊ{ 9ÙSÆÓ�ÁÊ@©¥·2Zj´Llª=•›Í«`ˆÃå‰Tj“ÞÛ%(1³e¨ÃÑAã:ß]sãdh˜#€5^o¥#ØØm^Tl6¨÷Y÷¦ÁÐ'á1žÒò·ô 6/â�°Z×lˆ!s1˜èÇí ¹« l¡^~Nk^ÊRˆÛ#ü…˜Ôôš¦ÎÑ^FÞ‘Á²N]GUÔ}„;èeZ /ª1åÚ+g’ˆ©â¨ìØ5´÷ÛCfÇ)ÅÉ ¸—Ì'lm–» -)Ì=‘39 ¾ˆÇe *fúGÕ¤OÆÂTƒçùŽ›¾N,XDq(>Ũ±vc‡To8åQø’gAÆ”(º?ö\ŽuAeÑpÐ~ã4ÕôŽ.ʮDZæ -`¹…ˆ>lr íTlKÂ"û©ëÿš~#H“qõÔ(æ¸5 þÕÇêôü …IµÇ‹Äwß4ù¯·ÈS ¶øx—ò†¾ÞÇËq¦·ãÙ¿_,|É_ÙÞn·«¨’fáòÙÌCaINg”€]Ó²uÙ(g’˜k[BgYú6@ÉÔÀ&ƒoïF)ÇõïrÀ°ê0`n{j¾Å4‹ ¶¤Aó@ Ô†+·5—¦ -xÙ^ ùÂõÂÙê´Ûþ žördæhÀźUÚX„)ôRMNÚÁÄÿl…HŠ{¢(ÚJ ©¢}€öË;-÷HoKGúòM“÷TÓ@ðÏ™¨/Ms¢žI8m¢:sQNÈ:l),á>ó6x ݆‰¶ì•¼e€§”†ehêšð ~ÔT[Zç}ZAçý±ØƒeY6-¦þ‚òeçÁy¡Áñie^ - ÿÒª1©:à—…ñ�mžÄ3,,hvʤì:´4ž›O(=m<;Tç„PÀòZÁäu`q-Ü<ýQE„¦·€ÖÀã“àÓm@bÂ’Š™x.ÛCc uþ ¯Õ0µöË|Düçuu{çÏì–Ý}òm@ý8‹•Õ!iVE.;8'. A·Â˜ßÝÒ’àÝP»tC÷@$¹Fª_pQæßð§}Ô }•b±Ý£ñôbQv•2zl‰{‘ÅçÂ;IHzE1lËƶmë*B”·»=’epæR¤ƒöB…Ò/Ñè=î «êžzjEf²H -(R ž†×%û8ï¯`ç¦ -×EFG ñýîîœîÇþÚ?o;—(ŽÎ°©;;«÷tO:"Mé©gäÕ{ZºOúõ�°ÛÒY,°Á»:ÓU:!ܶ´m0HѼϬ]YýÑawá¯}º·ïë&ߣà00‡ùøËiXSZ&÷Hyny£w�²ý²&$è~<ùzƒÂj>ङÂB¬•@ÓÈøeddÐÝçÜÑçý·3ÿí GO^’òŒŒð—¯>ÛܪJ»Â¡è*˜ëÛECP‘šx Á[ˆCºîcêò&š¨/�Ë„\¥+aF®žDç‹ÚÎpÇ*·UD$ßgO®^V1"3©&ÑéR0€áz2Ó8›C¼p©¡À#0k>§ú&øQfѼø|©'<kÊI“kÞÍÐ’fÙàWbPâÆŠ2>1Ñjú·lBð’㾯OÆü;Í ôÐÆûtE�)¼~-|—k�…)ÉÆ}Qä´.ãï[Fçf®•Úd‡ÉàÉù€B!e˜F2£›˜òqÁK¢q’ß»o W|•ø|z8·A𒤋é0ž÷ª:Ò\é°&ÂÅ‘,¥¸±¦ÐX´)Šð(¨³)“Á`Õþ…1”-mâ5¾({n’\/„:—Á4º0}ë`«µ9û䂤¨ý¶=aŠ•nÿ -|Ý%xɵ3}¯¤ê7+ -óš'ä]„‰úˆ“K†b’#Ç$6³ñ;x3x™á21°2Ô_,¼å)°Gáº~íR\ÛX—¡‚œéL/ØrT0‘ Ó³žešZMáQ7Ô|lû¦ö'åQqß=¸‰û‘�aµÛ½ÏJÊÏ|°Þ¿?1 ããBŽ[۸ϋ¤FhÏ•œN?ôuA&°Ú³ô¾ÉÉuƒ³“6Osî;Zð®kê(e9n -„’ÀçâЂõïß%ô÷?öñ±àÌ“$šÌóLy(- Æã÷[Êð)y‘¨23–[rÕÚúИîÙ~¨O2�óm+‚©5x/y'¡1–c8-:ÃâÀž•Ÿ5ÿÄÛàíFh‚©¥¸²�@œç¼ÕM'�›ëpx÷9”è²{38¹çŽí‘TÀ—L™âû›œ8æxì±æ{¬F»V\luröLÖ]×Ö·OŸ0ftפޚ6°ËA’Ÿðú‡¾+ñ|^Þ¼æX×íþݺ6ßLðÛ>[ì\Ô+zX}ÄêdhÀO -<3œ¸íøÿõ¾¦• MæSތŒQø²Å3»ŒPM~ߦŠ¦`)Ê䜃À×ùE¾>¼1c'I´ (‰lWN_˜²OÁ„‹ÿªœòqyÑ÷¼¼¤]Û<ž\C³œ ¼ªß»‹h„ÂN2KàØÒÔÐþ®³eg”r<Y<Æ÷�¶0B¤ô<åú¢Ñµ¢ œk¡™;‘žü¡Õ_t‰BÏ°©Ó¢òåäÌC2boPä7µs‘œQ,ª6‡ßÛZXX?§›¶M(±VL¶¨¬™(ËN7›lìëѸN÷çAÚsô¶5œÌ ¶Æ!6¶Äï¤R³Óf¹°.À>éåЖû`/âV¨Ú‹´¯á¯„uí»öÈ`]U>€ÉŠßéj¸ž¡5q¡ÎQäÚvÇÆ÷0õk–‚ö1ÑnÂk,ø; eÆ€owa° meb§p¯$"úJÙŒr´ôOýt¹˜R}#^&vÙþ//ì“_Q†“½ÿîc¥HolöôV¦D6xdÊîF´ÖûßIÌ—DŸO”ýÖŽàt¤\˜`é ¤JñµÓpÛ8˜T®TS˜rX§Àñë¥ÊMßVhèÞðýp3u"¾‚ª$Òžhe*\3”VƒJ!µÁŽó9ÞÖݱzõŒ‡J(ãAÚYÑè?ñ?¾'¢j’ƒgýPzH2ISX‘ª¦e×ýTˆâ´‚鶌¤°®Oqùš®¤ýS§ýG¿—$‹OïΑÓ,É}|@3() NµZc/<:ßÞyçÉsjJV’%¶®¡ -ZŒk³•ÇÐèT0ÌSû¶64Ð1k¿mî½_ºë˜þRr©µžŽˆf¨!pnnÖ%éæâ´ªSïÕwÜÛè�«kY}ÇêJ.kÉiŸ|·²óò‘##3¬ž^™ÛQ„¤F´°÷[Xæb¬¤ü|LO‰ìéf—edèw~µØsR~q2²]&Â¥²ƒ› -àèÑnM{êP(å Q»Çõ)Š,k‘z(šr=²À¢€[MæíÉTNsP%9¹´†W¦öþæûïjð…ä]¼ªv1øHB¹û휬>ùmÏôúâXl,¨Çîà)®¾ÚÓZ ˆ˜VbµÊzŠÅßéÔñ— -DZÁŠÖìÁž°´=¤ôßài‰ NMºCa§Î -£±â—ò,×äøWœRÓŠ®Cöbb6øƃ¸ÇöªSô–w[Éo9!šaºùnˆÎ «ð ,3P-y€ŸF•è:dʤÝì’¤>Ž¤¤=Õ›,Uûb»<Ù®ôNhT'ñUˆq„ÂÄ“J<Çž—J½nªýg[ÝÖÇss»üÁ{57ËE¨8K\3¬~Iù¦ÚV9üRÍûv‡¨¤Œ<C7m6+·ìy…»í–ú¢ÃN{X—)©²WZÎðG }Ô€Í�%âÀ:oh;I ýHl Èmo‘7Gi)#@åÕX{Tu[ÀÇþÚ÷a"Z¬ã8,„ ùf]q^{9ž)1—]½{ý‘2¸aðá/‡[‹æ |Ç—õÍ=Ô)¶Î'e1†V˜jjlãØB=~2·Ä|Feûèº\;mÄàòÝ¡SNœC ÐcYW8¦}1´å.žA?zç¾öꃹ©.VmmÕS‘ÌD„ìEEâòåËMŸ3?¹9ªµI±!¿:¶Ìiµgj´q*xQ”=©#Ë&Ƴ_: -]¦Ý/¹hZ†@x‘ºIiá¨n¦wþŸvã]yk@êBþ<úúdUžï:Š*Ï7KÒR¨÷RFRv¦ÈhÃ,6û¥+ê;†TÀ’?ú<X¢9úOˆÐž„Uåˆô'd3²—hGpG�ÞI£X/…²éÆ13TÇì@˜ø:þ¢ŠÊJ`¾÷%&®“Ä÷„àís· ¡>žf.Ou²k Žœ·`pë˜1äæB°ˆÄ70±\ч6Òž€-u"‚}¬óµ¤^~1Ežâ“cæë!ÕQ;8P•r–1R\)±vâÁ‘áà$@õãŽrhýà�)Lµòy¨¶üïˆh8¹ª½—EZ -æ´^ñDóœH)$ýY¥™ÕtLYmµqd®^ž)©ù°,TɦёäÜô:]~ŠfWúåØ’™½óã/y!×5f‰"÷DH3æLnhˆ»g}ú±½æ -áÇ=n'²v–ç8;ñÌ|x%5ÔË¢�fO74önª´£H-ÑáÜŒ*ΟÁU�vNå@?Ê€è^Sîp´Ö÷òaÄB|@ðO¼œa¾ìH;þ�–Ã-EéÞ‘hC¬ =¬mhÉ‹"šáÐX¶¯³ÞCX¾ñE €ÛÊXR€)¿ð:ASxDhdùéa«¿´®Ž:NÜ„¸? Îs@‰<‹ØÞ±`Á '`Hß]öü,(1&¢°Q%fC¾ÄF,-Ý`«Pz«±Ž xtn[¸¯Œ÷sÂ<À>?Ìp!&LíÆiñÅ0…J¼Á]půw¢ý£‹Z`jOnnØUqAϬ݄«—äè¢@Û&ûÁÄ+¯ÿæëo*9Ò.Í7õ|‡9Ÿg̈{ô`8j<j&–"<Ó‚ÊÙ{�SÃ@Ç8á‹Gî‡à²H¿]7cäámnñôÐð.Õ‘Õþ%†˜Ù›À,µ=0euKœ +~oÂ�¼i[Ee˜Üø‘ >V�¯É‰%çÝIALš-HJ¯Ë[AWâ8dD½§húm ©üš|ï ÆÁï#úgGfy[ë;ûu÷w‘ób6Ž”©ûtSþ³¦2’tc4³éØ¿0~ìÔ'c”ã9ˆì³ð÷˜"}»ìõü.Âœâä|€ö5æÓ¢OµA@Ä3èaPÀ™¡¢²»‚º< -U0¦™[W½=Oùc°Ñœ¶¡6’…&Ä…8*A^^œ…™.[êÙx+@Þ®jêã-ÞÖƒœÕ:žóOvû{sÈBB·"(kaö{M.4Ã&YuqîÜy4ÐÙùYÛÛÜ‹ÊóR „ÈŒ“Ÿz'] “g¸MdNgؤß÷ -~ÿÉMÔŒC2È*ìÛ¹³ÍŠîGŠðIXz–BË”au_ñ¯lq[I‹[ƒhǼ«Áe 9¼$ß×Ç¢±''f€oøTl9ø8>©ñ<¸¨Þûð¢ê¶‹0^œ‘ÒŠñ1”x7ž\·..8 ÓZAÌçá4³T`X#Ùe²²[™H±‘‘Ø.êýxwÂûþ*(#G˜¶ö,2€CB)7AO稊Zà+)ùÁœûÉAùµüû𢓤ªJ~{kÀöDD\›Á–¾4›®»nD—òP0qb:, 'PJh¨Eî&ºŸ>Ê/cYIß=.¬-ÝýOÛAâQ'å¥5¼Š+aºÙ ©?b¿3LnLYä—ÀÁ§›Ä -¨d4jCëï‡TíK¹aFY˸k[ÿÓ{ÿ¿Ž;.-Oä½G'‰œÚDÜ¿Ãà¡šuEqWΫ göJ8SŸEvÙ3¨ŸööšÖà´îÙiŒKÌðÎ<ÊÊKÚV¯NWö?\¥6щžèpL”?!7 +¡á¾ ²ò#L�{ïSÿW“ŠŠâùÿ©.Þ)•ŠUD8ôº{©¸#:µû½ #VÛÆ¥Æå¦ïÏ+p.3åðИ@›ëmaÌKPBgø^‘dm¾· ³YÏ](oCeÀÉÓëÛZ‚›_øî;ÚŸ¤2+¯@)êfôï9&-}ø¼šI²÷L�B†CPõã]±Tœm ÄÆd�ÊÀlPöxF0:÷´ê©ßà²-"ÏÂl7fÿÐ*wh|B`ž••ÕÂ;à;¯‚`ÒÂ$HEÆàsõìzkjjøÚed²½4â xÚÀÃssIJÞ�è6]óajzmj±¢¡cö¦¶Èä -øˇ +ู’Ýóy{'œi¹Ì.З ˇ#ÚŸá{_ªn ˜ÌÅÅ–K׺Ãç¶âeù®š%/>±alžkÝdЖe´¢’‡‹hà¤,Ëf ÑäBõ\’D×kúzå|¯UUŸÑ(¿1B>9ððöÞ{ï©v][ˆÄ4ˆ“’;"ðS¼íÀô‚Ð˨¥Ôè> |¿a8v´Á³‘"ËŒ¡Gm³‘Š úÁï@Rê‡óƽëÙuè¯úO‹GÕNâiTvâ.Š÷Õài'¯A¨R9Q°Š[·‘În‹A.³T -„NoT5ȲIÑC’ˆ¶ëÏœ\]…9Õƒð#‹Úcü†®yÁ«ˆ=uâèM‘ˆ±P£¢KBZhY–×P-ªk+–ï$Å�†@[‡lЄÂpŒËý×_u½lÿõ™ÝOàA<§¡ÌeÍl>Ó,D”Ѭ¶ðÔ?+Ò Í0g9j$½ -ÁÖ2•kzØRãTÄtGlš`a0Ô¶û×·‘$ \Ë•[Á"îF昬j·C›)ƒ†EÈ–žNÊqÖà˜#^"tˆÆ‹™gB›v ùFP�—¦ý*àS€ˆƒó»w!#Âê”’ü!&ú(kÐ(&Ä‚ƒ„Ž™.x_a¦‘ö&*›ç{ „7x*<Š³¸ÆW°¤¾ó>#Æ?»f–w¯'ŠÄW˜Ñÿ2”ðF‚!EŸ -¡¥”i:Û¦HLÑŽN)›ŒqÅìk©^gDõW E'`_u1-´>‹Ê€³ÖÐÁA? -„Ù~s3íÙÃ+'1Û/ß‹bßR¦¸8ºaÃbqI?YY}_‹F_Dåýð™7ƒõ¬§B±·8ì~Xß:ÓA±¯Á·û$‹ð0òœžåÅÔg¿q{†¹9Ÿ—~`ª+£�‚ûO@™ÄÀ=ñ -Hãwƒòq 5Ú‘„÷„ù3<÷ëW<±¢‚!Ï%ñ,G¥�À”OSGMu‘g`èôŒÇëܪ¥@J¼M±±˜ Ÿ¡vл•cÆtYäZj¨£‹”ŽÅªIsÅu~üZ3¶ýÉ´;’‘åƮâW†d–›¿hkÍ®#|¾Qñݵ·þnŠÖµÁè¥XÐŽ@ öm3Ñ•‚ÒkHn$á1fXq4Ò¿‚t¿Î0¥†U«ÊãïÉlÛ�žZ˜E]5–™›AÔÐÜ^ï'ï9ß•ŠFÔÚ´Âøþ nASÙO±±%€0~ÑVÚ´ò§êOÎÎî Ù²Dc&‚6!þÝ?.ƒC¬?"êž™[ÐJò’mj €g–î2ÅUÅõB‘Êí¬D½q%{MŸçK§C˜¦ÙŒ@ŽÃib’&²CtE³wàŒqŸ¸bí`¦¹ËbZS£#°¬> f/3ü5°mƒ²Ÿ!År!P§þ…Ê|RŽ? x^Ù™ðŒÜxÖÕ1O?,$ê—Tµ¾fmu`¾sô4EÕÓóÆM{’{L¿\çjá/»Ã—ç§Æh×£ÔXCqh©ºhjª¤¿Úµ5ƒzƵô ¬u¢Øçw*/÷D¡«ljy;ARã y÷Có"oNóšÏ³¸Î‰±Œe÷êoÑõóÁ;ûV„gþÅi1§f -c•›ÝïÙM¡ô÷á6Ç“EèâÙôW Þ‘6÷e}ûØ[üeJtv$4OòÚì‹òجæ ùd¦SOúfMk¸Æ©@‘ÀõÒ—êV%0à -ËO?ãZû§]wÛ8 ?àïaê5¯¿•Êv˜Á ¼#=‰÷‚«A",™ԫ --mš4©o0L‰¦&K&“4EÔÔ'å‹‚^yöë•M®©=ÆúU“gsáf˜Ž t·“¾f ¨A+5ú°iªý´ä';)k>ì ‹Ñ~wÓ½ß2t¬Œá„aMS 8—S{ö¸ËN±Å ΩÄC¹êÑÑL»ßŒ.5fL—{ŠôÆÕ·¥ÀM“…ñB툳+£Ü0¹N´M–˜fV0Zá*cö ê]@Qa…ˆ5¥ŠÝ¶•×¤ˆV›âÁ¸ÄôbúŠvg§Ë–´æúóðÖQQ!‰#&~#KtdNÖ‘JNâ˜iOÊ”¹RÅ+PI`A>`˜DÀ¢ý‡‚Êù.jæð9ÿ§ÝùÅš/I.ð+(ä‡)&yÉwñ•ºå2|t€°ä1T¸CbDÑdj{E˜í.++”&!Pfz´€¿„Ø Ú¿PÇaa¡65Añ¤#£Ë -Sùä\'!¦° çï¯Ïwue¥kÄ6!XR‘ë 4²Q±øÃhLÿh\INã8`{g¤Æ³´â‰héÇ:Ÿ|Gü©`S†Ï˜º¶-vã¼{úÀ¬B[[—ïŒJ”£½BÅ`îcY -¾Š€Ïà UQÿ\6>6 -yÚBÕ\ -º”Q\¿ÈfMqxð$Rd³eÍÊ*›J} -Z/…?/€‰½S“õS³¨û-"}ßLV'[’þ:j#‚¥|'¸rýzûÀ£}܉É…`°±7Ñ$'P6$½ÜF›úc ±Á¿}?–¹ËQhórXïâ’&c-´~çÄY³l1CåP닆Ù_ :¿_ß•êào‰ ©ÛÀqy8æyo,\"¼ægUýxÕzžª))†`íLµïXÎHî‘–Œ©AÓd|‘'š³Qå‡-vûÕºm¦=Ѷ’Óο"l8ßAý¥#ðÆÂêŽé$ãÏh9€±¶ÃgW ¸¶D’L¢©Âòë›×DÍE9ª7CÛe3Å›vû#—tw7ÆaSC ä„e-×YÞeu'†é„Øw²‘ÝõÕí!ÌT_Qdµ‰aX-Ù(ï<$j0(FÐe= 0ê�Zh—žœ!UHZÖ,2MÊÏÉézÈX’x̬n£'Ä`£‘ôl¸¸œìµ²fÞ‘Í'W"&uŒÃâ_튫ý‘˶{Bíò„çUû£g×�ÞùcÇÿÜï±æÛöæ‹6ËP×ñ’®¤WÂ…ÅAo^¿¬®`´ùÈ ý#̾Ið©Mt â8³ÿ0ªè2Ñ[ŒÒAåPyøðÿUçšä²Ê¬xõyŠ"j?´Ë•šb«·Ø…ßAÀ_S“… þ“‹Æ·0L��@�IDATÖv|šý‘n»CSèç ›UT¸d°Ö5uæ ‚0rC{°Ï–ŽP~Ãù·KÖ˜ïâ ‡�’ªj]FRþ<¤=ÿÆ)ð£Ì#¾6ÏÎ ‡F‰ÁhVØ4óu-žÕ¿¨A¤f@é:8ú€<L§…0ð膽µ8-©þÑ -EOv9í2;kV`Eù?oo_j‘x;aâ7’DzYu[™ÄrE‘Í:(˜+RT«‚ŸÉ|W-²vSqNãÄŽTáNÂ’8ˆ³[^Å÷£Žkš½Ã}Ëà﬈¯37´F[—^Ø»·‡¼ÄzÉÐ:$Τr‚1 çZÓ?ˆ ®—&˜n;˘øÝ.C0A¿Wè@óŠÑñä“¢®û¦ Ö\‰1G£&n8'j,[ç0uïºÎ†®dé(%ô•ÛóP$f<§QjyÈ-VãÒ}KAÚSÌ*ŠË!Å>0¼d�OÕ¤/‘JK¢a¿·)Ôg~cà£&xpº#ýF‡•žç²WŠ=&ôÈ‚Ôâÿ^ÿu™Sý°¾î$T⨣mEùÔÅøþøeïwuÿþ!žúÿ¶xŸÏÐ!�åLaY~/,Ó°tÕ�ÿÒ5¯kÐñ¥6‡ÒAùæ)œ0¶T¨ÀNÿÏÛû}ƒª×N;Iò7ä΅ºrzºhI®ìþ¥škc¢-D � ``™·ë(–�W -—ò+M [aš>=(͵þ°•™8ígï!˺äÑϺñ«æe‰e&ËÃK%ušÊ{£i–yq™ÒaÅvÎ:5šq_,«àO1hªþƘüÈêC°–ÄÍI&mð)ÙU¡ºv˜à]¾Yâó ê–Sqܵààý0ì•ßîÎáÐýüÒS¦ß WÃmðY‚Q»±¾.²0ÁK¸(¤Ô‚³àï¼ÑþÞð÷9át±WàgE0‚Þ†"/[Û“×$Òzɵ7·JdFóSV’Ÿ F0ŒÜƒ‡:QQ›ìÕ¨fîCJcµ“gz÷ý±ƒ --%Zf¢ÇÚ¿ƒK%Ii¨N‘oU˜Ê¡EÉÄõIrˆ L€à³½(ŠS€Yñ*ý‹ÅÜÂ{¡E„_VM½Å®ÑsŒqªHÓW#Tý´Åä>8VÝ®îmÀâ"ä%å\…l¯¿AG6µy×öížnìV8t}K` -¸|ßEVbÜ!¯*¾ÆÛÙ¤Üsq?�Õ‘‡qiõ}§bYpR‘ýeÖ´xOèÌ�´±ÃŒ K[BâŽÉîϘ´‰Ì™P~¿`4f:¬î¦PtI²È¢¾}êµíHž74á…Ö†šuɹýâìfÝ»ÿ¼Óyœbô<NgoÉÎør!Ð� ôu¦ÙçäB^6rpâe"÷.?LD¾?Q7âÏ K9‘ˆ[Œ~aYÍ7_mîî¿$}u >Æ”Í)pÊ¥cÞWK1éÓñ™µè÷óúD‡D»‰ï¢>E‰ªa@2´yÓT�›ÜŽšUsz¦Ú—’c÷{B}àFû–Wþä@{Ág;ù $M`“û, ×Âñ_&æùíçn¿Xø’×uD²u;”à{Qj¸·§4ÛáÁOñ‡¸=ì…ºº½QúÉO«†oÙëϵ!pr„ÙN®?¤%Â&틪‚Û=ÃU7—16’‘—Ä´0‚?˜ã8XN0^GmëÜÁéÐrHyòR¬pÏXlô£ƒÎ8…!2¨ÅÆüUÈ$íwü\ k¥¦ECÉš9A¨¨*ËeôH[Bpä$+/À1gkLdeyvv¾êÒ"VûÄ0‹%=Ð+¤MB¼øT dÁðë®q(âŠéý]>ÄÿsBapæ}/u±uïY”rÆœŒ7G6º¨¢Ì…%q´í‹C{Zù„§©&SU´ÁèÇÛ¼M}yZVVÇú*¯S·CxY:`çD¥,ÿ‡í…‘ªó®ç€e„¢ÌT³”|ñÒœôms÷s‹Ûm›¶÷u6I#{b²YW‰ ß}!˜i¨{Õ™lòõ„ÿ´{»ŸIñP·a)Ÿ®Ïšíù©ñ }%‚Ï;›¶;æP¸ùBÑI®·²Á7.dè5;7´aźîB%q¿¡ù†mqúG¬Lm ÒÀòæ„tc< -ã\;ü'*='®q¢î¿yU²Å:[QTÍ1Ù ß›œçìB%Æ—¼kÉÉÉÙ±�gÕ™X`ÅÏ·’`æ‰Úïk7¹ž3pøÅ@| ȨhH"êhµÐͽ°K”áøª›ÏÇr|7,ÀVX–/4u7OýpÝ‹\Hæ,¬éÝÐ ¯˜n#á_´ý¤ùþŸ^™uaÑdÅ$ÅV DÕx Œ<ä mþß› -ûÀÝG4ùA‘æJe-Ôy,ÁKî·êí·=ª¯æE®:¦”º.s[mÜßB|OÍk¿ÞXýÝ’å‡/¹q¤“úT„™¦ïaÅ Y§™ÚƒdD°õHÏŸ3ìz 3€ícð;Ï×Yê¯ÞPû»™9®OAéYÔ ¿œ`É¢U¸Ï×D4Ò®OÞ|*²¨¯zùå#/‰˜ƒŒär'¼˜#dÞàŒ=t,ú²Ój}ËF–N/û¡ #×#i;aìZ®ò™´“{²?tÄ÷ÿS‰l0™–ïå5ö²Ì,Âä,¸oVÁ“þ¡XµsÌÆMˆ,ŽÿÁW2%ÿµ¬âB.¬I< Ʊ6ËË”/ÞÕüý•}BàЫÊæ? Ô@»-…~[ý@Ž é¹HQ®‡ŸÐ�A=@ A‡À¨LûÎöµËWO.ÎÜ™¼¤/ xìK -m&î3¾ À7¾YCáGï¶äÉœ#Ï'qlB˜"½9‡°›ÜÓPâwÉ0{ãep àg?šž2qwN‚ßüÏðë&k´ÖM£†ã¹7Bû+~2P›¸Æ‰¸'ó1‰ãOV�a0xs32Bð’ k|#ûÂñÓ¦žUEE -àƒu£{Ißs¯¼%é«FßÉKë½Gp?$Î=Q÷ã2åOZ´ð'Ÿ<ûĸ®æ Îñ8Þ2‚Š*ƒvû |»÷@>S3zÛ»TF}ù`[ã*¢¿‚²8×#ÖTcAž˜1îî;ø¹ãr\4ß8¤…¶LB„»Å£Çþãë¯#vE´P˜Ý×XºŸ$I§ÍñH#¨ˆ«ç8Ìé¦=vï�xz¢ú4x 8Ä”ª;¼æPUŽ™Y²P4ÎÊÿÜâÑýðÆzÔiêOi™ŽÅ Í•¤$ó"ó6¼Å£0ˆQ°ªÍGÈè]`÷ܸ7¾:´-††—ÁÙÁË{9)ð‘Œ9‰ª‰cŽÞ/®÷>€‡r -¤´·YR¢NŽ‘’-Â^3ôIâ>¸îç×#5g¢ªSáï©‹ˆ®E6Ñ -MZÚÿ'äåºxÎþà†*‘ËK[†¾î‡`½×B³éðá=þû‘5p¨„ÛÒž»i84z„Ûµœ©ŠXÛ`†ûyò«"“Ã4ì_G–ò~9½®¯Â#°@ea>ÙdŸ;§¯³sM«¦÷*þ(8jÚk®Jh¢ÝûFæ’a]°g¨Štûˆì´Í߶zûÚi~ Yá´oà–Š'|W†Ed&t"ÌÁ†h¥çòòïÝ3¤¯Ã¯¼ùFÎ4~íd>b -{ P89á^YÓSA²?F€”EÉBнû!øÚv3ã©ÈvÙÜ*SóóEY©ñûcè~ìoéó7í=EA,gt16j,cš» UÿnEAê^ò¾UÔyG³VÖîD]2÷¶öÎÅÇâ-hÁE|òï€p€1û¦3bÚT¹iɱžñoa¼µmv’}6¬Èû°g@¢J«î¯)Ý ï*Q&úN:}(Þ„¥94Œ!PTŽí—âØýKÆâ¸p;¼ðøãÚ°1#[¬ko)”UVfߪls^ ?É2YÓ>EåuvÁòJSO¤h%ÏcOÁÝè]”ss.‰hz²¨Å˜ßYÞM@vï°bJ�šÒùxÏF2Ä`˜H?fžÂMutá]›£ò àL8—·!9ãŸZDa\^Z\t¿^o§Sm•ÔdLÚéP—s ‘ù¼À&viÝM|>èƒ0ŒÆÐês,Å倿¹¢É5nk®k&ü‰c}ˆšš–Â/zú8Qö]8ôé¾hûÓnÉìŒödØlq;q^bŸ¤‰¬ý_¯?«„ð™3j"²6~‡óEWdòSv^VÖuÏÖhÄ´]'î“*r”äá%¼N'#™¡L³`´t£¯l(;Ï8Pªu{khAèþâH©.³‰ÆÀƒ~©ü¿×ñ,. º©ÂH½ -«!»-Ò>B%©ÓÜÝàTè({ÖäÞq«j6›ò<‡ûS6$ÚÌn -D¶º£zÏÑs° o¡4z£Èa¯ªM %×#}îð×p—Ôä‹‘,q^’ÀŸŽç’ìÚ|‹ TaAyÎSÎÜãDîÚ\ßz\C&ÅE›¢ƒ†B¯e y¦bј™nw–¸CjC–]ˆÎ%7ùm¡h:²C§ q�Žùjàç3†®½ÜiiCWÍöˆ&¡:¯)ZogaRZk‹7œ›ê†>ý´Ñc؈dp –ÇÀ¥)ÛasÙ½‘²Í>¹µ,Åú›Z° -“má–öèVdª‚$øypI›,²+Ý6SX‘aãAÐ0heg/ýýᶣƑù~:„´È#m`ö¿dú·ñ‘DƒÄ*JTÅfŠ0ös@Û¶§mÍ×-SQ}D¶c7ü,[a¶Zq«áëÝÑ¡ŠF ‚ç´ÕnÓMVìıdJ|&ûwžy&¥#ï`µ¾‚}92–‡¨Îź‚uë*›RYŽ -Ák‡P¨uë[jCÊ…�¬Ço!ë'Ï¥+) š‘{‘{“E…|î¾yCÆVäÅû#:ŸŸ’¸ÙWÝ= ›L–5%%î¶ Ç“Ô^·!üÄ2ׂýÁ†´ìGc²v3„æÝß„€ä×øíµÜ?:ô¿7ʼnOvYƒg8"”ñû“1o•;– Éë~4®Î«žÈìz®ÅŒn-?Êž ð‘-¬¬|ð†×Jº¾Xaô•áuTê2›[vêñ—«¼wŽ‡3µ¿7[¤J&äùD -‚@E�ó# I+ÞÖªõpw wÒ·ŠîtÈÏCæwÔ^ Õs(”?–¥è¿‘Ûò>žA.´îSá<;O&Hô{}I”6ÛÃ(aNú¼·32¥&…'iO h§g�|ØÆ3_�>ø7ÄÔΑ²Ö)SÚ€Iê$ͼoPÁÀ3+ ž“öm8¬Ì7 ú̹ ž©t¿±úY¨«wÿÖÖÖôCGývþhöæ#µ{&’‰šQúg¿Ÿ .òù¨wc¬L„Dü•ïò¶Ë჆ÀmE°67fGm÷G¬1ˆí�ìîE¼x-XÐë‘?0l˜©=ïÀïÝoaÔHªº¦„?CAÜ?ÁçE ¤â“A€ûtSP†ÑüýFÞ·¨nTª‡Ô õ °´¾?ò?ÿtLøŸŸþãG‚¹MAg,¼ÉÎÊ33{6ך_„d!§Ru€˜u ÇÄUT…3‚ÁSýº& Õ™eì¶+Ë‹ºVÜîPz¯òó¶´‡¯']r’•Òhÿ†Rå;ñî>”Ã)IŽÌÒ„¿–JN-ÄÜ+A|Ê1½,”ö——SëŽ7³Z(…2oÀ=@$Ú—6nú¨PjÞC[Û#]¾`òÛ¥¨X¡¿Ì0ô[ÁÂèE£ï*:÷xÈ\¹%'§åÀ•N@`ìrHv¸SèT5úÖˆlצÁéÎKà‹ŠRòcºI·á¡\b¦%qoX†MFÚB—a’ïé‹KKƒmk–n�4æU¸E+P~©®¨¨(„¤a²'ÎÅw1ÂK:<D¸ÒûvîßÕT³Ï"SËÚsc,,áz×ópÛ¶98î9PËZðwaP¯ôÉæ<‹{_űÈÕ“’]y0Ù†Z.‡a¨ÓÁ à´[AÏa2œÙcÛ$A÷ý’ŠÄ ;ùø¿4Tóu¼ÖY¨2rÆÐY³â%Ç` -â¡3ƒOIJŠ÷áT`³uÙ³„VÔçh|ºÝ~A`pû6,²Šöz{Ô|×Ý ÊÀÛƒû=™‰~l'°Ë—Áû‰¤gãðý¶KŠú¤Ò9&oÙrdÁÒÄy'êq/¢÷ySÈ”uß>ßÌþ) NZÒ/,´&ù›?P+i5J -0ùCϸúÆõR[hZqúþ¶ˆ±E¡ƒ@ßÕwª†S“ô6,ú‡ÄΉ:8?ÒnRÉ…ëhú¡÷‘â¾Z-àeÔé1Sö@gtdâ´y*‘€KŸE„Œ©îIüös÷]/èϽÀGØ©`ú,AµAýÎ Ó_DÂÅ+€Cx)]=�,e×ËN®A„…¤‡„bÔJèB²Ù"K™]("HÃ,u%f@Yk„˜™µ£3xÉ.Ol"ñ¡?Þp3PQéš5ëÐC 0[ãÚð© TéÃ(.¾Öæ0é?&í Õ”YÎ,G»�üwH"×�Ké€{`44µ'†˜±œš¤N›Ôjóþ•z¤G7\2¼°Kk'5Òén_ª�÷ý X† V"„,"]GÝ:ˆep*¢ÁXìÐ}‰ÆŒ¸4MlbP)©Ž 7JœµŠiÞ�Êú\o‡y°)Ñ7\{Û™¨NñØ:wàž-àiXXÝ–¹²Á[F\“0.âEVþ³Á×privW;â×DÏÆÞC -ó»¨köIÍþm«Çå¤ožŠò+?–Û®åZŒÍMǼa…‰RlI‚*}AP˺ؿØM룀?Õ§X-+°#ƒXí¯‚k@§Tø®Ù¦º:2·ñ=lEÏì°Xºæ'y¾Äz: A»+°˜*ΑS®VYaPϤ¾³†g‡pvkñ“~ Jw–fê¯yݱ7€ùƒÀ+®ú°´á¿mŽ`Ûô/4E*CgiQµ*1ÿÛçû[ú¼¸«!…<ǯ<ÓDY’6 Êo5x&‹†ö¿Ui*ßy&t:¤¢$ÒŸúÔ¶ê9ݬÏÄõNä==¯"žÜCú@(v}½t£¦PwÀ5¶�BÞ’SP~ç…ý¾è}u邳&õxnÂK°vµaÍÏs~Äü¹ãp\n?vsbŠ:S\ƒÔd:™ZÐ&P/¾ÝÜ#%NÙý<#3æ2°iIW¥ƒ›Î9„þ#ebÀí@Ûß!~U9¼x©~¹TSµpÔõÛ“í»¦0ÆqjBŒ9wk»oÉ‹`®fð"?SÚóí±„™œ ‘¼ -Qöù€”d"»ë½ˆº…T ×!e|ŒãS–c†A0µ@’ÿÖSÞ!°%rîGûs“’xÿ´ì,<·#5‚!„cx„ÚŸôìé¶m¸ä¦{‚!gú(«ìB±§§¥Å]#`±$ýÁ�~Ø'Ë-VÁ2\åb‹ˆûñHÛºo¤-«ÜðÜnך2V áD›s¹j’õï-)€huÝŸ˜”D`± V ,Q¤ƒœ>0É×ýú?õ™Œ}Nrö³š®íUÌÛËž|ÒÆU·–˜¦‡.)Ç ‹öo>8£ÐÔ‰Ã"·êØ ÙTæÍN¢Ímñ[\à‰6ÛšŸ+=û¼¶¥OPìⶕ;H ‘à¢Ó’„w}²ZWM§¢Sý]»ÏÎQM9ÑFð'?¦iò>-#ù]±3üLŸ,eŒÉ=eº«_?^Ä؉ûýÿ±'Ï{cGø"¸Ô$E’jyV½Õv°>A×™hYÄ™¡“þ·“SäÔÇfõ̯Æo&±" @2Te°Ê³š5j¬3,RLjÜ\‰hʉóûÊ`0USÙ3À?2�ë„ô]¨æÀ"È8nnó—Øyt¦ÔÕ2"øx)<H¶ÐP%MS¯jä/|’TLÕuÛÃXä~�yýOƪKCøOþoŽ!Aééˆ_1Þe4å6ÔÕºIáØ'›×¯ø¡àŤ-ôD}à¾c(‰¥šwÔÕQýÂû^24¿æ¨¿Â…Ð�-p.Å:ðxÉ%[âÂ#ÞD¢%uŸ4&OÕÃÌÃÙŒ5“Û›³G÷绦ÎQK]‹ïSA‘DªùQfNNWÐÎÆ8/D ¬‚å´éžà,©‘\‡ÜoVïüfbÒ-xÉï´ÀöÄ$#UwO$nÄWG^rÜ2kH¯"ýäoÒÿB|â7[§ÄtmY(Ho�6œ!Fš¨g®jöŽFµÝ¸˜œŸØH[좾U‘Á}`º0‡ŠÀ»í -P?E4…â’’-Êdçmh þac[èå í¡×7wî[ºl¸NÜÕRŽŒ³ÿFð’û“±G -ñÓ,+|8Æåò~X¾«ªÝߺ¶SöVËqì%Ð×?Cdý) =¶ÀWý``ÛÆ8Ý2C&%êÙÕPnž\“l¨£{†•¢nõéwÅÔ,|IBPh+ȱ óÚž{×»ƒ¯ÞM2ؾßt})Ãò—8<Áð>¬ÄXDç¾Èè˹¤¼øo`#´˜1Iö xh^Œfu¨¡£/é&Y´\VnrªÏdiñéÅUž‰K"ûí!÷‡Ãú-h>ÝN³<o뻺14aÉ’%ñùý&ª>îÇÜ˦s!-ñy‹f2/q…½ùIT¤Ã3“ª J~ïêãxûd¸N3ˆCgóVÌ›CQÿ"âª`u~8-Ð×p¥vBäÿDøígKkà“u¾ãâÍ鲞VÇ‚9‡Õä³òO™÷W&&ÒÊÐÉv¡‚Û-×ÊQŠF7“4qY±a"G%)6&ÿpä½ÄêÆ¿@4÷®YDæÒðáT×ñ‰óûȸƒiû|‘àçÑÏí•íJü–ØxÇr÷#H8Quäo,½©ÒqLè6˜Ä¸šZ -^à(´y¸Áøy˜A*qÛ¸e…H£¡Õ6ÛRÅÙšHílíg³¹»i¥ñ—¦iõŠJ«‹Î„À’T¶MÍO·y'-AQÑ£ïKHÚq?0Í!27ÉÁà'hìsø{]X•š9…%vA|øð‰¢î¨YwŽE`ž«ô†û%Lw2ö«ÛÚŠ×6ú¬mj\0}¿îÌNšnsã»øâA~#B™üŸ8ŽÐSʶlô‘¯Gç&o=\¢†¶¨ì††„¤X(Þu¾ˆjËÈ@‘äÊ»ìsôϼiŽÁb¸²‡ÀG™§÷g—fl=Zéh—6ÁwŸn"j0ÜP(õAÆ&3s'œvI¼I´éDÝÏž=ÛdjÎÁ_¼ÊEÓÇ‘g‰1ë…E8©%ªNFóoƒ^ójü‘góÑÓ52¸N&Ra"ˆÍ€PÜ•6~†ƒ¼£'êØ$Ú]…²X…~÷L°m˜XëÀ´ˆKœõî÷I,wt–¸+¯JJƒªO!Þ1ò%„ùq=Ÿ Hsì%DZdÁRU3ãÛàOrÌ$î¬ýÿDø¦9³QŠš: €ä„Üˉᑊ©ÀÅžr¥44¤ëa~ZíàdµkTU?>Ñ6�>wÁ_J4–°Ì—L˜öíá7ìVqüÅi*¶œá¹‹@~ lï3•¾@Æç“=Ñ%:¼õÝÀœmN -ÅÌ{ÖÀÿI0½„hCKçYà¯ùLÒò¸V«›Ÿh‚úòجx–µîpåÞ*ÌÞ⨌`lCèPõBqĵ'2±×¹}o #‹O÷û“Ï’j4ãaÁÕH¶ËòO¾ðÀµ¹öúšý‘!•@9àtšh-°ÄAF¤ncôZšæ†ðœ®O;ÄåöÜYÎœwÈu¿5ñ‘“[ß1†‰Þ©—fûP|yLSnJåÔ]4/ü“p*ú²ÄEODcòKðAý@óÑAÕ8÷¤‹/æI¡ÌÂò÷Xië;,ϽÀSækëÝ{׸ݤMäÁ'-‰ßµë"4»g×·G} ‚’h]äùŸÀÇ€°7&Rô#榯9²W|þ¶k>ýΨ.(æÕ4öÑ`Â;ý¨‚ ‰[ÅSÐuó5{Þ94ÛÑñmí¾ -Wr2ªo0ƒŽ¾½ofÁc;|¡»I²KâœmOÆoˆëA˜ß⢫Ëá?V”!“f„UíRÐ} -À~TÙž½2É‘wß¿H¬r ÍÍ«Ä솵*0àI«à~ÂõºÞÙc]û×þa¡/…;Á߇5U»þÛÀýwtJÇËÈ (mŽ.µ[UÅ| ˜;1_ƒUÚ5¦£K‡Ay£GÂiÌÄ¢Q2~ÖvÄÿYW8ê¤ø*i'›*ó1‚*·BòE€™=¾Y]ÑÓæôᘮÓÐc``Q/äÁ!¸/æ!ævÜÜ$)½p+€s€´i…ÿòB0{ >Ã�.ö™Á°ÏKî5ååßÌQÍéú“ "b±ècð·äØ9¼…};›q¾ÐCï¡håckN„‰Ž¬Cú “–v´ƒh€i.Ç•hQ9øjeC{Éq(Ò}V'Œ{l¤Í°ýXŽ~$Íšqå˜Ì]7ÇïªD0m\ÁÃRÛ9„£ûï‰Ï`5Ýî>ƒçú&zŸ6l©¯íõ‡Î&~ߤ̤¦ÝÛ7펲Z_h0^ -á[s#3=d1gŒikƒe~hçöI±N ˜Å‰/§¶}!/Ö(h¥ -mÉ‚{g&žB¨�о™œÍ6 ¢ê§ÀžIÏ´—²¼“~4FPVÏâõ[Í0ܵe›„ÎÖÈL3¿÷³kž™úðÚÆð�Rr{u‹(YØHêj¢-ÿÍ^çèRÖ4 -ÄQhtb#Ü"/½‹zÈÜ\1f†Ì˜KX•ºÓnn"ÊÙuXÇwß“òBJkkl lczöùkÌyu6ÁÂ=È‹É€B$[øàqº·áÿêó±’zȽ—4xû[Yú^dûÀhÛædÙGÅá͘¢½æSµKlªå¼q¥€PvR£CSt7^Ú4¸â¦ìr‡ú$8¬ÿ¯úrïc…–{$k[K,ôÏ~®ƒ´Nû!<'â0†üºÊžç‚Àc�Ë™Êû£|ܽ-Zë gðvȳíß{¶Üý˜ÿäóqŸpñ²,4]ˆ¥&)ÛµþÙ÷Ò–‰�ü´0¬Ò¥é´ZdãôD‰—QŠHJ{fwã\Õ,Ôp+Ê¢üZk˜>Sk“‹¯†v{›·ŒëÓ'®%oðz];:ÃçîóFîØŒ]RÕ å‹ŽÀ&N(̪ÕxöqUÑæC ¯‡;�>Ç2Ø Þ3>Pu挦ýmÝ - kùdϺá–ɨq%ˆ¾á60žbÂÊh;ÜŒ@ë^WVV\ø’šPð} ×F (,ïͱ¤>¶©1821a{¥Ñ!ô‡³b/4ÎÛS³¬«ìµÏ.?àÿÿÈûÀ¨ªìý×çMϤ'$BèÒ¤!(»`±¡bA×¾ºØ{A±#v]ÑEDš€R¤×„H/“L2}æõ÷ÿn`bÀ ëÿ÷vq&3oÞ»÷¾{Ï=ç;ç|Gš¸7N¹ß)�>G5µ h×+ØöAP>`µºÏ J�:¡‚,ï0ž_²Èw¨8»KD -.ÈSä4f´8ÌÒÜö$&diPâZ úÓ‚Ÿê&ÓŽ4œ®‚6i¢=xZ{\ €Þ—hk#ŒÐCö…fŽg[Ãñ†%Mqù#\¦ÐAG¸ëÃ÷á ¼tÛ/ nwes,"÷ÞüÄŠÃ%’~Ë<l>瀪aü`³`=g·ƒ‹<ïT°š6ÚñMñ¨2:°a5+á¥??µãyk‘6K*'Ã9ʺ0ÒÚ_�ˆåY8-/C¿±ïP_`3üÊÁvÕŒ¿{hË–{ýæþ—NÜ‚*ÄP(D”ÐɬCBÒÃ[·|·ãL—« E?‡BÒ€ªÝ7Û¼þ. ¸éÀ²eá€dìBÈU)±%pFŒœÑã7ÁNÿ¥n÷¶ Ѩa>„j™˜QœhwóL¬³Óaí½ÊÆŒ·�MÂÛkA%•‡Þ6³ -ʹ ! #yOKS¡tY®¤é,§G‰µ@¾ûwŽ–€ôçÇmýIf~áYNhSꪋ:÷-2—AÕÁò’5æ”WùËD¿žêR¥ƒ®P·ÓxJ¯#ê'sBÂ-¨&ç‘ì¡wT©QážSZgÝW S…&毧×À©pÀ]¯þ—` -NŽ1Ü£=›ÂK·$;ÿÑéĉ¶N¶VÝýŒ±«9+—,é’�>³8¼ïÄ›6ŸNR—Qdä.ìüP„ðE–u+( KêBN0ÑO7ÅŠ!Ô�Ϙ1>5s0µÓ@Í8h¦§ëÀ2úäæÞ‡‹y‰iˆ·%/#çÐ^‰ót”B:ß!Aߪãåk»Ra0&ÅÇࣈú*(Þµ>Å*Œâ(öºp’oª‚×Nâæ¦Ù©5Áb3I2ãõC{81QcM¾�î¥ `H†p.òj‹Óü›Xº�/<¸»”¨<Ãíx™a²€ñu€ÀÎ×54'+Cm¬ n† Dl¹qnª˜º±H|ÃÉÌ=YI\ÝPÄ ,’¶;ÓÄe؇†±,´&ƒy0F"QÆÃ)8Ñ.êQhÀ&Ì`’(B±®Aà(Î…Æê«—£ÛPº»MÂc‡CxBòöz“’hGïG3u"rxS]Ç6¿Uj “Oæsªš¢6{ðXm9p>kððLºs±«èØHîöIR‰°{ô>>75µj�`–kþô†ÀaüÉ…·ÁQt-I`CÌôwÓÇoö;t²i±M€.cæ‚%nĸéÓ÷¡ûÆ‘Ôô0"!vQÛi ->AY›ŸÕŸiˆÂ -+ ˆ•c]7F; 3ôrTÞ™ß�†Â‘í’I\‚³ƒdP‹Ÿ²X¦ÛG¢XvpgwG0¤QCÐÛÔ£»ÇþàWþ>áš/Ù àáy_2ôfD’Ê7.€xZeÕU™CTÍÕ¯»QÊ\¾Öfå³ ]Û, —eÖ÷Œ-ø©2É$£m„ˆ´˜©HEðµÃš2 ~¶9,#Ž÷°“-ÌãKå‹ÒòWº¤|²L¬Öõ3ów8ÈÌaR”ôMµm’ž¼v`»¤íCàÙ'ä1‰ñ"f¾na¦ÁQ3Zw‘!Io€'¶nWSÜ…‡è@Š2ê-RþúúUÍ;ß7N;ãDÌLÈãÄ°Þ‚Ú`sã±X‹óƒhheß-Ù–äga¾O^ù.døJ\~—ÖÙT€ûà³0k2û/¡¯Ú+ªñÐ=ܦ`ß‚ôdM”@Ó'm%t‘Þï—¶Ò{5—�ùäq„™¸ñ~Á¤Dßxž¯CØhx©žêŸéÞð{¿¡×Üp0ø´JKb?Qm¢{¹ª+„éɈìÝÄäÇ€¯F{ Š(ÙŒ§"ïÑezcµä[Ê -®÷5U}NI8×̉.9÷¥‹¾çzIcc:™·$‰öw6Õ…¯†™Ñ¬¥+Äö•—·àk‰6ÿÚ+ ’TOŽ²&ÒEÎ+³J«aŒöÓ®#ø;}›y•Bë†g&uÊpÔG(i3ÌÏ×2ŽpàþÚ=þŒßsýG_Ö$kSP¸4 sS•u¦l[mm3ß HAsœaãØNHRýŠ™V_Rr”L ˜<‰à!I9ÄËÿg²N¶Õ ½´K˜°kËþ“‡aõÞ|qu\ÕfjƤOd,P âZøGcOm˜÷ê«-²€|O²!º fr2d@U^Þ‚zøÛý¿G ô¿þó¶q°QZIE¥wOëÖÎOŒ°#¸Ë ¬ñtš‡ V$e™©0VåGÄ¢òV—²‹ ߈éõE -å´ÜÙeé 3ýEhS•À7ÏçÀ[À~B‹ù”*Kkéx -!‘.‡0ë$R–œ’p½PÁ»ˆåSYUóDk«¥D%Gâ;brmª ŒÜŒûV÷4¤^ï8¡2zÉ[_V„ót”yuBƒÇ†`FMŠÁidl@2vÂi�_ )þ#J™ï”6Ô.·y®S¬Vv{}xøŽÆÀE[ëCg¦ Ûop»äšXÉîÕ‘¸ùf$&=åÕþÀYød@,Hé¢×Õûüeä^¤]»›¢£°‰äáaW…e5f•ùvÏ>7-³KÃá®8…9¢¥Ó©xÕΘÄQ¯CY™‚¦_‹ô݇±QÔ;Ý„/þð×¥íˆÔ(&ÓÓ"$ݱ£"Ü3=);ÙTÃ'¹=Ž®úÞLÿ¿M¨iÖçàr˜ŠÈ’ƒØÐ&ºíìŒ%U¡f<›˜aÀŒ»«ï¶%ååEú{¨`}´¶ àÆ2±‘y€ìÏD?Æ‚ýî³h\}PÓÃh `ró!½>:’@DÛw*î×t(˜½¶&rúzàÇ›«üc7FÀ¹¿8Wu“ƒ;r!c•7é}µ¯gYä]5Í‹Èfèïlé+,À–q {eØÉsý·MÇÃ#ú¿ùßµáSPr6âvðWl‚·×'ëÚc};ô¸cîÆ1&ß|¼ÿÓ¡§``ÌiÅÖ*X“mÇò.·qÊÿÌGDÛL~bú_Û²ÔÔÔt¹p¸AîR e zƒÕW]IœÅDÑÛûÝ—a‚Ay2›¬òQ"n©µë<Wiª°âTpñ#§e.ý;?á°iÄ‘²#-ÚÂמ,=ÿêiï?¾é›0ØÎLht„kìè; 2&Eƒµ”±çþ#þ?¢´;g&AÓ³!£ÖÒ"ËÓXÏä÷÷ÎÂó´m}ÕªÅ{ p° >†åæJE¾àÓÀŸ„ÖýH—¬ÜGB>}aDò›µ¨qãƱ:ÍŒFµŠó©•syæm3_znÿþq2ÒÁ¸ár -È–£©B€<{×7Ð<u -âU·!‡îUovRÅø·¾ª:žã¡Ô»AT2 -euXáºKÐË·y#s•ŒŒ¯ Kâ_2U”«c@›G"Ez¶®©o±Üí; Xèç4¯hùwדh°¢²jq ²]Qµïû¦%íÆO4A€×D0nàº]63Ë&.œ5;ÜútrYÊØ¿³Vïzòã&CßìzŠ$êç.!!5ž¨b~™SHñ×Êgr‚p÷oÙpp÷ý³»T2VùºÌ.Jym&øå³Ú°æãw`ÕIv]³ËÊÏâ)$”…J•UÁ9œ¼§¬±;Âéž–Z=cÆLúœ©w}~<5õóÓʨ5š¨42´gc?–Ò»7=HF'<e]mèkÐZ¿€Øa2f?;Ž°šµX4Bí~<ЂÐh¾¿öbÙy7Þõq;}.YYßÜæC’Ô>†"t'¥§·(Oú³¼ÿê`S.ÀzDå‚»aÊAuiæ€Ië÷aû‚•ÂÜ…R]·»XÙØŒUÚ¥"–È?è ¾¦Öä‚}¾ìz]ï–.ÒY¢Nñ8]…p€=gž¯ÿÓCA„éž×ΞˆE0ºœGxb!š¨Uò©M¤OÏÇ%ˆ8‚þÄ^Ãeååí~=¯Ê].© ƒ(ño´¿z;•—Iª8Cꪲ†%¤â6¡>µXYpmÓXŽê¯vêCà?²îþãÍp#`c•¿«]ÒÁn4ÝâhkÝR’3=}<¼«GŽ- ¡G06¥‚¢.U¹i@VV|“?Ò“’”®™Yq,½#Ì‹öNJ–Ë9‡#\·OãR½ 7ÞY¨Y’œ\`qRá‹9šë|¯ÌÔcº§¦’ˆŠ6w-’¦igðõБ:"Ù÷rOšã+@Ë0£‘-çõŸK±ü¨|¥¾¸|a -/Ž�QËó$)8.ËWÛëR=G©˜ÏKËyxèÀ–+þWpô”CØŒiD'88^¬ÖÎF|e3Kv랧fMÍ€¹.¡rÜ,Þw†9ø*Ê÷<Ѧòî¦Ð=ÐZï‡Öº‚s4È+q.‹²;÷Ôú“¾tÓÜNÒÖ_:Hûr“³³ñÛ‘]Á`†šl†OÖ,K_ùËÄC·>ÿÖÝ6v³o8JyÒd‘ÚnR ê!YÚð¡Y)ûP䳋…À†a‰ÇÕ¡§€¶÷4¨ñyn8,"fPl:ù§Þ4†7Xó\"Þ±àãÂö.¢-V¼‘Kƒû¾—:Œ<ë8†n¤ó</2Ÿ 6|.vÏ[á0my²)ì}!ñû«BƒiÞ¼�Úw&ÔµÝSYôÔšï:Ó_êû1ß1û|Á“c4;Œ5õ5½SÝÛñ}³@>æ¼ÿù?Éâú¾îÖB„”¹+e òX¸IUßÎ[âñxŒhÏ´t; -Ðô0ÝòpQkeÙEœ®¾‚&Ç8Ü·D@†á•™Œ˜kP‹šYQ£ªYšíâ^ ó~ -–õú¿4(DÞû"WÖ`Œ*’ÐD¢œÞ+ÙA¬,ª.I¨ÌtLŽ å -ø‚©í4ÅãÃ#`<Y«GŸ"0 ¡ŒfLÂg1@Yßxo#` ]…µ\ìdŒE‰EÑ¿;hÃwŒ@ÍZÌœXCdòZ¤í‘ÉqìÝZ^ò4Neé‹$,Õc¨î ö=Õïѱ¿·S¤‹>âáÝyöï/E*ÇÝ4‡µl^ÞÁíöˆzäQX¬`±7vcÆôQ)ë#€ Ò×!œ‰÷ä•„¤ùŒè»¸ïd˜'Ë»tâ÷£tšF†Ki|ŒòÚ-8¨Ì»ñÓtU{Á§7ÁKNë`o×-‡7•rÃìô¿Fª´§uŘïÆZ¤:ß‘æÎœ´šÉù:°…|‘5GŠ~z¡Qðª†L¿ƒé'.U¿7èAs <ôõ(óLÌ”_FYpBM)0{¦Å—B®õ[‚?Íò”ïÙë_¨†ÔYq¿ô|ƒ»rΓáXÀ”‹ÍA -Ë]˜ÏÈãÞ„8zZ0a©*‹—ãÞ°tųB3í-Âtž8?í?¼ PÑ÷{L¡½ÌÇé¬ÆèG™µÍ /€,ŒgrF³“×ÊÁØ34Ï®=¸ü›½šª–±ºù,Ʋ„>1ºÍV8*†‚Ÿy.kÒ—CÀÈcŒE Æyú9«VýÄlFÆ‚˜•‰ð¡6ÆÆÀFÝH<ÇtÐS§ü9>"sˆ3Ø×˾aP™È!f]<ûž¯_=eÊ”æ2è“{¤Õ5µwÁ¦s%ðͳ#u©%¢Í¶ ª-ïNžƒv»È³'Y®h|‘õgêþ&à0ÐÙ²ÏqƒÒ¦/Ej÷ÿâˆ4CˆMzÖÏ‹XGЉ˜É{|±fS'Óáhhå!ðC_…9¶�}¨„àõa~ök«Ê³Dð..k¼ ")§ksDÍúOéø±ÎÔH–=6–ª§÷ÙSš#þݱøC`‡Dcˆ²ÜïÿÁ%Sy‚ižÛ±ðÌýóÖW$1›¤,<’¾F¨¾D}oÖ6¥SÄ3ü ýP™Çi]ݧNøN3Í&dâúä•Óè=(TÕ ªðöÝçÅUÕ†„ˆHé…uä#Uí\”ñÅL3kò!P>^ääl)¨´à³ëFÝ•™¾³)R/Xèwê6mÚCÌö#;ß=§àf“á»jk›LÏ;©az0i'¢ º®ß¼×ž€'ÀãôÙ‚äH ²²f-îöPÐS?¦sÝ°ÐÕÁÏhž ¬ô.‡fYƒËîÄ?#6츅´‚be<ÔºˆÑÑfDêtwR™,Üß±«·ÃHÝ#ëá=ýS3¢ðF¯M2E/BÄzuš˜Xµ wtάU´Ú\7ºLnm~JsÙ“£4º#x7eZ x»ñ'Eö3lï±1ú·Uì�³Ìa*ªWµêåäù-ãÇÚp_ôÄbT%„íýi9'Kžöú{ËÂ`ckC$"õtC˜âðn(„ÐÆA6âÜSÎ+aÞìƲP±¾¾Ê.fWÒòº/0!™¹ÓôC[ý0%Àllþ=‚/@¼j>¸jD¢o²6.uæ—™º™å,<¼QŠEÇO2RrN…kÖºÙ>¤Ëú;í’¾G<K‹vghŽÕõL$®lÖwøú¤‘ÂCÚ¶b/SpÊm¼ƒÞ Åá^<‹TÖšyÔ:',|èZ°¹¼Š²¦ïÄg%[ØSáܶÂÞÆÆ£¨ô»¦DgãÖP”Îq'}7´GL¹Ét1¡bŠz¡Ç‘ìÏÿ…aªÊùŠõyC¡,"sbž“° -¿ÂUhã3¤$l -ׂˆ=c¥Çnz°érÁ(ÕX÷…ÆäIJë3P¥[tsB!œÆ^-$¯ycóf</g!«´ -Q(ljH˜:2GçÿPÍ—hðÌ´Ç–é QVÕ(#-:#“µFòëK¨†x9²¸ˆ'¾Y³ädåmàS?ÀÉõ “£—X,Ió!1P™‚]9,\½ÇÛÊŠP>íZL¤µà¯¼Yj“�®#™Æ[œNE_ƒcgPÝ·X7uÈCf£ág^pðÝ| wŽÊ’9ÝSÐŽïÃ!ÚŒˆ†`ÍŠÂæJ¼‡?ŒàBPÁÜèt¼Ö 4ͳñ;ëðyË• ©Õj€/2—‚g~8ê!i =qOÀÚt’i‰ÙÒò;r_Â66Ðn¯¯l›ðŒqC˜â>ÂïNCѹŨÈ{H‹2a²ír¡ýr³fa²ÆZ_ßé¿å°¥¼“éV¤‘¶D:ßÁWä"$?¿pM弑½[·î®Ú÷ȹ/ï!Þoò2÷€•õ„ÄFD=‡äŒYH:鶞“Ô4¦"ò#áÀy[€“iHöØóHìuî)§Ÿx(`“Ðs¬€&®Âߘˣ/P°Ë(oÁ^%΂ƒÿQ•N-yÁQc²ew—1iêV//‚y‡Óf[¾pæL}sE0˜ü{pÂÞ ,¾í;¦F9ž“]å?¿l±¢Àñ›†öšLcŒôåYàýŸî Ú/)ÕÄ4…ÁLß•)Œ“æÍû©¿‰N ´Ä±·¢ú -»´URÍ0oØiƾ¹óï^DÝ6¸ tQ÷´NÕ|0(k>U¿³âPcبüH´ó×^w65åÄýc(ë÷îØQ>™Œg’²X&ý6kèFÔ $½8qBÖÔ?ËéËw¹JÞ}î¹}½çÉ`³ÑL÷˜¦£Ø/-ð³‹Dôä{òmˆóO‘UÕ ©^ªD—$®õ{^OøàKxŠAø¢ev<™RÙKŸ[¡ÚwFȵ–i0Ê7ìà |‰êË%«ûz¯{mcu O£Xè{°2Â"\ ¡ò®•ŸC°ú¶vb>ÿóÍç÷ÛK°§®eiõRÙBÝñÔÍ߈šÖb05h»#£sN{‰Ž3w~ÈÆ -ëŽl±³ìD`™¡ÑžL‰1§š7ÒFr¿¾Hþ&4s ¾=ÚMXbá {&ì--"óá3ÿ÷Õ5kÉǬ€Ç¹Ä#z—ydÁϘaZ9vL膮>Õj¨Æ/Ž»àIP¶äÌ¡O±˜B@Z‰}$P¡¸Ûñ{‡…åÞhÉŸ®6"^‡ýlÅq¸rÈzˆS Eëí}ÚØ‹c¦ðeª/|'4˜f3þp«Ûþ/éñv›ð�'Î@@:‚ÐÍî„òin G_s!îw6˜u.ŒÇš> - ™œ¯*úl˜k;pÞ]aotÆ_ø/¥³A¾ÎèªùhMÀ[ŽÓL”›ŠnÙÁí[Lå¸Z¢T0VÝ1XY3vÕGk¬º ½a¿™‰ç$ó®÷:Ž:kHù~ù뇗.(ƒöj 8îV0ôÛÙ¼€¬>Qe?V@»‰gr¢IH-o›RDNlF¯ÝÝ’¤’èçŸõõJL©.þ9*„\M0Áý¤é·îÓ×%M=°>&Ã1g‡ òÚúîêå;§F‰sÉ<\ý᫇ µ¬á hÚ…ƒJþû|È$äE;^|0 ¬™™¤‰6W5Ö¬iê[l*ùºH_‘ø¼Õ+¦ôOsš|ÎQ‘!®ŸÂo0¹h)ùl-(6Q'ñ†1žGìüªó»V@Èw¿ç8áÂ×f³ášl¯hr”à¬.˜†6„†¹›P2¢ÛON·xÑÖR_HyÑm#‰‹À求È}’ÜgÐ ÁŠŠxL~JëÔã9Zä#;¤ÖA[c«*R‹c;Lpp2Ù)»²KRRé §³ñ³ÏÆërÑú2ñ´ò©¼ ý¼OZ8‘$*Ò MDÝ誆ú&œ+àXøe×I<`ËÃ;ö>äo”Y¥2‚'¡á+qæëÑ={ŽÞR<ƒ(Äi\4]ÿ5ðYdqˤ΀“Lò›¼äÊÈÞàwÇ -÷Ä=ÉõïÁSo£OìãF�”ë’}K‚Òuœ“{ vNÓ"Ëé. —óBÄ憴¢ÑÖÇ6nWó5Qj"›R$UTý\#JÜ·WÒ®M ™&ëºÖCždhõHüÀ˜üü`ÿ\÷ÁÒ–l–•TžÐ$È5ê6¬ØƱIp/‚R¥ˆÌ‹€eï5djr½¨~J*X Ðê#Ðà†v/bgÑQw3ƒ¢H6ðÓ*" ÌNk¥\¹rÙ²J+£Oïu4i6¸ÛuáÇß—?ž!õ¹ HÆ";ÐjåÙ!)¢õ¡Ž¼ó -„Ê¥Ka‚ݺY²šçø$:½/h“Óél"Ƚÿì‡vôÆž+Á)„„—¡Â&²Ó¯Õ6–oš2¥››Ygne‹„aú•Væ”þoÓ®`УQ–Y˜Ôçà¹ìœ=þØ+%iñåµ¥RÆ“€ -„ÙÝH¸ºdÞ¿¢Qþçè7ßlŽ¬!ý&Ñ4£sRª£Ñ†Õü¯]ï—¾‡bö‹²æ—~ÛæwÛ ,'(\,–E–S±[Áôíò,OÕc'ýz��@�IDATôVÚWâÇ$ZVØlT·0M;ÂúL´è{¨‘û!<‚f@›C8 VTW§°´£ÑøCݳ<7µ®ë%®›x%^K`¨Þeºƒ†akÚÐt0{p„ø&0×T”ü§à�çv=PÐî0ö<&Jr h¬Aœò±÷(ÃPmñœü%šl|˜à&ÞR=Ž£YŠiK1e* M!Ss†y‹¤)“£Ço+ß·Žh‘DˆýÒCÙËM2l)YîïóŽ`²°u,ÛàÔê›Óá<Iú;–@{`½Õ§^¶ËíÞNp̽¾p•f>F˜W>ØF§*2áÄ &@ëœn£˜pçÇ>@.@ #%E*úâµKÉäJŒ×¯½¬¼Êïq´1øI*¢@aÞ*Äô¯Õ‹Ûº‰¡sÙ-vI0lq“·dY¥Øºh4|Y(WÆí€Ç‡â™tÆÐ$¡òÄ}”jÄ€©ß!j¼¦�&»3ÊÛ¹W·ó†Ç ´1¥›vR‹‡Îå8êYuÄ{[ -Y#–2ÜbT1¿CÝÐö4cdër‚E ³»´Â{ÊøþùÁ óñhHë}©Îoë·Õþ?Ûg‰øT"€HòJ;W^Á~k¼ìÎV]É\pÈ7ü«ç#7U¥µS/ï’µ}ÅüùAÎ_Têû -Å]ÆÆd}#jžsGß¼ÀÏÏüÏ|Rˆö‰™ô@Wqøa¡I;A‚Ž9`„ñ÷f0.@iÅà‡™‰ >I5[Oò8^ÿÏ´î×ïrÂ…ïí‹K,SežƒUƒxzo£:²7ðcaaÛä7¥õÑŒÍEJ«Øµ S ÅýÎ�þà†Ò´kBYžõ…XÈàÑÄÑ -<9/ÕP…š0\ nTw„œÎŸšp›{\úÖp Õ/(�µ %ˆ”‡ éÀ[‰ !‚`ÜÍwtä~Ç‚T%ƒU}ªw†»¿2Ás;hŸ_ÍÊB°Á«šôï‘Ó¢ÍËÚ»ÐöÜM¢$2`SYC&ã?Óx.Jã<p$†—Ú€X]˜¹_ "¢³iPsѸw„ûøääíuÆ9}¡6^Åùó~II•hW‹€$ž{“w¤LíÄ�÷FúóÅÛ–ÚvôëWÂé)Ù/Bsœóã-^6kÔƒQA“<v[;™¥^FVðC¦ß jC$>º `EE}¾ˆÁYžzàØ͇lfj‡NÃyŠ-€˜êWÚIQKãM©mÛ=íþMaP£< U[ÏçÅ¢ÑJ‹Mìâ -àmùàÈÃ}¶‰RÓ%Š˜ò�,•©Š©ÝÆT$}TKÕò)Ùö5ˆ½ìÍL_ -Î3V`¾D³Ý¦&oæåÙÎd;[—Ýíy“8Œf6À¯Q•²qp–{Û2ο©áÿƒ'‘òïHaÕ™Eq¾¹ˆAGÀe}Ý4óá²\Oqâ¡ÌÛÛ-‰(ÆÈ|·ø•Wj˜|í®”¶ºõÍ!_Pw.kjb»ˆ6õŠ#éÞͧ“5ÖÖ|jëZ¿ç3rŸápªRQŒ¤æöM£¡à©ó3Ý%(ì×fZqðþìÈáHØÚ…j§v»ãݟȆÑ×ßQÕ?ÃÊå;>Þ¹¿÷óŸ™ðÿî‰FäŠÇõIÒOÕÀÅLºÛD‰ephnoKðîE9ÅÊ܇]éŒNNVaûz‡V ûés«˜4AÑ›#rpW!ˇ¼zµO<mÜZYÂx²öŒ@LrŸ‘a5=<PKèÈ)´;ÍkJ¼LõÀ.(Šzè`=»²¦AAÕƒ=˜ÄÃÛ¼£À¥£¦NýÈ-a%ÎÁ±D2æL"XËSò}HÈI±ÙFÄuÙL²‹Æ§û|ßìûtvݨQ£˜#øn‚—4ŠZ4 ^80Ý·".ÂÚ9rì¨.=Ð?§K ‡p+d"ºeʃ @CšÛ’8¼’°³ÞcξZ˜K¢áÊ©ëÖúꨱIG pþF ×pyyûMS,ŒqðÿÚŒÝþô1"ã�)”j´9§g)uâ$¿¿[VæèNÛê=×é‚ž®çokk¡¬�ûzqžaäŒÚp÷µHB“‚ÁÕkê Ç2qB£_g{6’dŠÆ¦ƒ–dkº‡¢ƒ•Ó)ÛQí$}"Zí¼^!çƒX7‡ßQxµ«W¯6úO™Rê·¬¨öM*;hæÆÆÆhïvÉ«N4ØößéÜØàÝ¾8(V5ÈMÕ™–:Ž|uìÿ¹†dXÏÌO=€Eåí1~ü~wJÊ*´…à% ‰å�¯ã.¥(Àøòƒ¦eåM"a’Ãç⑳ÁLßh1ä%lܬ˜Þjs!Ø?KêÝ<:?s?SkQ©®m�±ú|â-7j²’Ž6ð˜0k£þT µ©ÖWYÓ’¹s”úú}VR/yËMiXókϼí;ýú§±KdÅœ&eä^_þÝ· ŸñÊ•++©¼¾ÛÓ\B{¬¯‹`*š!þJ#ø*`¯$»…Áã>þAÖw“¬Þk¨\¯¡î¢rŽîïýæ„”Ž_UHbDá<NHŠÁ…Ò»Ï(xžÃMªºpLÞaÒŠÖ %ÎLϺN!Ðkš�Ĺt8Æ&ÒŒ¨Ô· {Œôe#:¦·ì:¤|óº…_F·®ø6T‘ŸÛÃäU”•—Hw;)šZ�·ÑÊqP‹ÁÊ2íY“ÊD@XLc—•ê¶¯AÖ+“MyG&¨™g·+í\–x¾ÇÞ"˜±žŽ¨ÞzŠU`8d?Vu³l>Bª;6Xv¦·¿ÒWÔ%ÃÝyñÞóÏKùÛ(Ħ#zeÅÛÏXü……”)…;oã®ÁDh\JSâô‡o'Yš‰<cBÌEÓî>Én2g f'ÅäJârpÑüW^©¹þú‹Ú4ƒadÚláÒ¦ú’Åo¼$ý©‹)“1¤#!Ößó¼ß¢¢tK2—¬2Ìù) -e<<"/e?îoM›Œm¢-[£ƒƒŠ -†Î†Êñ5z)œèpÚ“»‚Í^; Òãòó#g;Õl+%wJNŽ¶s‰Mo<ûlˆC®E°ÚÑè~Ö>©Ðã‹üÑn;}¡=½R‡‚(qUÓYüRÊp(ÒÙãL_ª,f·eìÏÍÌŒå9±—5´æÛ¯ëÍWÙe'¤”øj«ß»LcY„¸1�÷ *5³Ð4•'˜“UÃõ©Y(Ë0+Ãô’Y¦CUX÷ ‚|‹³Ÿ‡xîÝÝ37WÛïtrÒSABg«ºPIæBbþ¬¯°î¨} Õ^Š¶&¡"W>ùÎ@åêÝñÈÁ‹ -Úý,Tó@Pn‡TÙS1Ê¢fÝâþÙÙG=ÇÄ8ôïÐs|@1ïw -Œ o%k¨¿¸›³~9²¾lNö(w çp8R‡j”v¾NÙº6Fb›:¸m‘‰‹´z%¿ó*ò•Þ{ëùÃk¥Õ×Ç}» 1h뛀zÂB^qÚ7ö›+WšyyyF^’(¥ Ü8Ìå°f¿€fùWd¢B‘¢ÀƒY¤ZßR¹cÃrî±7 ë`—˜vW£˜jA±Åbôçï©W‚™ƒÇž"þþÝ°ký“‘ -š©Ên±î‹‡èÓh!¾¼bUÚ¡¶¼¬;½ÁŽ2Í>*«ÒLkZÕ˜Ã82û4� gÆó*Yn,+<ÆÛúK%m˜±r%;('‡…�¦£aƪØ,¼Åæ¶ZâtN’•°¿qMRU[íi}mbŠuqẅ²ä—˜xZ²Ð †L•¤ŽðXÄŽŠ®V‰:ûöyÁiý»õØý…Îî›ÁÞ¯h‚¨Ù.»P‰ª— àZìÀH¦—šTlÒàôtoâ·Ä[kpÈ’3¨LhrNÃT×Ñ)®==iåˆ0…þ…cgCì:†3Ÿá(zƒij3c¥¶M•\LNOÀùm—~=ØP1“PqǙȊOð¿Ž‘ýó‘vëû©ÍÐFÐ0+tMÚƳât°µmAŒ±,0F_ñô"äf!OëRÜ¢–“p´¶š¸¸¤.,× ‡hQ™*e£xè{Ö¼3¹KÆ;wT\”bµÞRÞïÓD`im„©RÏØÜú0B¦!0ª›=¨ÊÓXÇì3:9ê½yæ_×ÔXóu»XÎÅØô˜EÐ)9”³M Àì#QÝeå˜829PŽÔ´"[p醲'öî IG4hE^¿aPËfìýþlÉêêRQÞ³ÙGÂÏHûɘ%Æ'Ñкæ4™Úb°Py€Ÿ» mÇôVÚ19oe¹ÿœÊ¸úb’ÈvB6œD•èHÅî×'JËoÇ>SÒ)‡dó,ûÂû@sœ€�÷U™nÇ-#ALÞú¾¤’ù}£Ï<Åtû锶h@ZÒÜæg1ÑÆÄkIS¤GÌ ?`ª/ž'ÃAÄ—‡ x‘Áêk»8>Bˆå²y6ã~‚Õ)¨wÂÊ´ÇÊ ©l fW¢&`âšäõýu…™K#Óóý’z•Sˆ]/Òîóâ†>,×.ΚéÚØúüõžhã¿û�'ï2$O×#Hý3®¥ÁËÿé·¯¾zðx‚‰Õ0 º9ykÇCV?Ij¨·Y/*ªÑÀqºØ°)ïg»/q hbþÛ`ò`‰ILÌ"´ÏéÕÞQAfæ7Ÿª<ˆ<ívÙÉß¿>mʯ -^r]bŠþèåšqH2 ÁèÞC{ø²¢ÝnNü’dˆ „g\‡ Ô[·c(<ËÕò[púüŽ¨l࿳YŸìÄóº±À½HžÝ"´|XèÇþÆÐ@š³]�sùd0ëçƒMlíÚ,²ÍTìÖ÷;ö=YXMQå\ë ¬48¸ŽÜnÝ t0 †ú|-0¯ƒ(4‚·9VW€´ÞÝ µ'KÒÄMñœ7-;iE 2òX¶· -•VÄ‚ú1]É@:j"¡±t¬UÔ«¬ºÐ·9|îØÆàï½ÑÒ¨78¤\´³YF¢<Te“©îQz““Eño 6YZÌ/LèE˜‰Û`>,‡…ö˜0-¿ ›ê»²NO©–B§jL ‚7÷ÔómQ¥§1ÄŒ©0¥VIì¹>ƒÖ¼lÕ²]µñøõˆ˜¹‰"µûeš£þ -¿÷c‡z÷nÖp—³¶¬AZ"ÊÆ’ú[[4ï6ºó§ûh@2:¿W~}Bð’¼W\}Úœ"ï$©"Ñ!׸þ Ä:8dššÕ¿¼i0Æøp$6§¯6Þ^#+³œ6ŸGl&¤YMþ¦‰à%Úb@ÒÎðˆÃ/këá^¹·¤D{? -÷ƒ"öql £½ÁèŸ<èJܼÞ1rl -a½u][CµÃeuíñ1M"…6B1C’áÏÖ8qtvzÙ’} ’ ¶"X¦#´Ü+ázñ1ß5FŸÌ°§öǵ–ïì.a4)§Õñú;W.Úv¬à%Š±Ú‘!ÙPÃÌHH[ÊÍ"Óp>[WV#ëmz RO6†?äøÝš/iY£ÆOµ9Ò„K-Ó‰;—Í¡Žk‘ò=9œãCàRÝä0›Õc‹á”? Âé|ž‹ÿ_êOÕg½^V˜(©Úx”Yàuð¶L^(—ê"_ -M{d¦c÷\ÃÆÄÓš`F'[³ÖH„°Ù£˜¿×üW^I‡]wg§€×W?¡~oS+%¸mëìŽ è"@cÃáëƒÉÔÀ÷~Œ•W“$…P‡ÆBà ¥ça·HF|™ªóɲÚ3¨];¢}µ\›´SuC“·¦Ù‰ø3 h× >u´[`K\Òy±›Wô€aL@ºòPLÔHuÎÔtõ†õ%_’ †ô”_]9µæ¢pÉn¨FÙµµºªÎÄ„-'í@F¢u°\iîΚ®‡0¨‹Úë²WÆLuI(X»šhäZm¤íŸî¯Iä™+€/}«Ê–i"§fDc=¯M©Hÿ -ÐŒ¶¤"0±,›å`#+}UU}2ÛeEhýŠªˆ|ZOß| G»Óôû‹’ºž¨}m†EœÇJºÊ2qCeHì¨pÀÇUUa³(ÖÁÊÕuR]µJiÌÉÐGÚ"ËúÒ9säc£;H;ó¥~üÿòÙ'»ë3$ðH#Ôî”\‘y!/¹â7yƶ,q¢bÐE²Ùxf ¢‚›bºÞe†zq¦™,°lýjTb^º¦gúa~jÌ»`ÏSæclÇ*x¤êýN/ìѼ±"‚k gÅúæP¸ìÓļ#B»Ã°³’bŒ’æâ„‹�J`¤ÛÇ Œ|›²y£ñƒY³ÂŸY§Ý_„™‚¬ ÌMs¸N1§NèƒÅâÀ?È`*þ2À¹àY"—î'ÖÏCçHôGÙφ?,*›_as9VaT°ÅpŽëF òmnF -G’1þ¨çB„o¢qPbg±1¡°Gƒÿ}Iž„Û`B€žëQ_Ó𘃣áªË Ueï×k½S-EGhK@s£]`lÈB "4i>ã þF‚gãQæ5¿®u…æ¶qL#*=€@Ëx$íWÕ›aEÇ€Õ@ÖÂ#îeYÕgrbC0 -ºt¼ÁÊITѪxkMáèÖÎû¥ïç'¢!â¡ŒjVvºåeˆ$„sˆ6jìENÜ!ÉòÚÝ»ëÒÓ™ 6ĵÈ+© gø ¨˜<¶Ô{ÓÓËÇ“Éväø¡6x22“ºEúÇr_ÍžâyóšqâñS§õQþvôÔ˜tµ@ÅÇ€±¿ŠüŒ´1½÷ ×’vC±PZø3•'³› - yg8oé‘F5/$r>ðçv)f¡Z%"n/‡© »{$ï™ÞÆf@ÎOoí¨îv–‹ðLî’uõ~hÏÿýB”´Ù—éÐî6$›æÕ”70á»xTzì…½3êçlx|—ƒÜ}©§î<ï¤Ã0M³¯ Ï_�ì;àÉŽ‰p,ß„r7É*&†`bQ`hÿi,ÅÕ;4éƒAyp¶1wz#'Ñ¢£ºwø¿ xÉ󘜕£µgc’±<ÓeééàùÙCÝEúÿ,øz»ù>qÃœŠ½Ñàºu&C¦rrÜÀï52Ñï¯@5‘ij%¯Ë|Ó‘;þ€O’£™VîŒõíS·‘9±¼Â?"¤ióñHÜØ÷&ûýåó<‡¤ÜVB“ç™Ü½_.$åhp…ÕÂ[=B0ÌoCº^ ~íòu >»ç,JÄË×XQðz5cGd=Ž<§X�¥íDªŽµ‘ܘKDö f1ß꯻¼µI´}IyJã ±ªm7ÍÊ™ÓMŽ¥u†W®JËu¼‘ù>ú£‡'Tø&:F ‚”¤¤lyji‚h†(ÜâVÕ¬@k+âòÇv‹UO§õ“‘ïúNd羡C‡Æ·ûÃÃÁ£’C«¡… gò â9½3ÎXne¹<àw1íúügÏö%ÝW¿@œúÚ0f¾aåèE0=ž•$êdZ05³<1*vvÃ%ð •ÖÁqcWbšÖ$éf}KÝ'¼êloÍÔ7ªja}†,ûY^@¦œ/Nˆ�þ=Úù•€‹éXt5H›[ïð‰1lýº!ò2~¢"µq®®ê×À[ÿI ŵðHLêbÄ%ÛäX ª¤6€¢qËø‚L""8%£³3ÉÁŽC ù_!ƒ>Ù<=ÚßÞ;ï»ÜÐL™Û5ƒ™xrš½v3ðgÁà¿BÐF߸*'UˆÑ2‘[ÒöÏŠêíÅõEzZ¾8ÎËã§ä$Y¸]¨÷¤ >ï¦Ü=œu+¬Éó´&¨Õàôô «$c»¡n¾u‘U îƒø`JûÔ¹°%U²Ü9òìr<–y±£&k6'�i4„¶šJ¬Ö¯³6¦†:<9«–%6¼c›·Ã}|M\]SÈû6àªÿZ¼ê±mû#þ^u°¡�¬ü×a£GÎœ\‘î°×{hoB’{’5&§¸ä$G#"er¢UQ¥ˆi¶Õ¥Ìá˜ì%H`au6v„“‚ /cªßB{#ý’ -=@ ¿…¾QY{Û%0ßEjâ!á£iq¹ÿVÃÐûZDöá1Y>iQG¶S€Ãž 2§àòßÓÈÏç ¥†ÕiIé9}’{pþϬ>Òfþ¤“¬‘(å(H¶wÄ3Pð,6^Õ®/@)xÒ7r¬ªME5°sê%%¬'dMv)î1$%Ýn ë3”4ÃbŒƒ…ˆífù¨lÏÔæü‡_d ,æI,âEŠ"Ïyxõö†ÈTƒ6oÅ„±œó K¦ú¬wÈ>-¾°fK€,fb’äž9ƒ§‘bªG$„/¼5X”AŠ_Ž‡ŒJÏê˜1Ùi-@=yR~ŸÇàw³¬QÿDÙȇ#Š¯’¶¤>”$ð7`7|IΈ@ën^xµqÃó<ÛM&?‘†GqÊiC¼Œh—w@U"dÐZš -kB&‘µÅ¡Q—h’<·2X8µ +¢uþ%.q´`#}<ö؆¢”ÇÍGµç¢ª´UĹ`÷™c‘•7È&Õ†*Õi”=µó¢¼–Þ›¹˜õÍN«ë$ÎäLÕIEéqHÂ;±Ÿ6ˆ®ËŠSÍøa•¶LÑÝd”ÁógÍ*>÷–{Îád©a_»äMµícÛyìß?TOóÊÊ�X'•!U?Í#pô=i]^ÚÎóæÑ\¯aI -ÏeAð²pÄÕ~¨ò¿‰:xdI†ÞtS˜Ù‚²§kÿ$ƒÜƒ<g†Fúëâh;~ _œ´‘ìv3™#F+yò1±åÄÞ ûÙMàëì;ДDXAOÕáOç8<¶_Ö¿¿.o<n*«JwœýÑÛ5ß\3ív'§Ô”È2",[÷‹Œ?hö¸¬xܬ]¸PO(Û×̹(ÉÔ6”€Âó`›Ú÷|‘KW!ÂÈœþ¬‹’‹_.)Ú3)]D9ä;³{‡ºO÷{O…†9S>%®˜[\vöÞȺ›–$Y÷Ù£GÛJ<•PºBqÊWun%Ϩ¢Cªv-]´=qn붶~OæFzÏž"RÐvå¿yëåêDÛÉy‹+où’ZS -í<÷œÛÂŒAd–îÄ–^Ó3gÇòCÞtQ°ŒBI*¬ -$Lm;Ymïq"ߟpáK‚ðé]Ä ÷AÚð×í•usô«M]]¤[mËXEŽ¬¯;Q€1i£U`¿ú+Ÿ™´–h7»|±þ À9X0ä ˜<½2—磄OÍJ˜×ªf,Dn9h5õѤTvb0ˆF¶¼¢é5à¬kbÚƒZL‹8Ì–Vù¯„ÆÕÁÕÞY]_äËHï|UG§ý9LÄ-19|³…âC¬ <D1ô„ $ŸæÅG€ƒwþŸ %#mG_A×Z<½^¿dtNY«†~Å£‘ó’lÖaïó+†„ˆ†jì -…Ñ¡4S9)²c]×Ü63ñH{©3hI‰>´õºÙºÁcGyÞŒ!q8DÑ–—ú§[JqþQ¿ ¶ù´ƒhˆÁ¤A×Hmãyï²={šz44˜iii´§Ï ³Pïqàí=SøE]À;eRƒÉ½‹›Â×BƒysEµ_ží&¼«)úpéQPRç{½'Qä¨0¹¶ÚL>[[+µ/i -ÚD&ƒ|{åA8/[kZdÁ‘s‹êÅíu…¹.zˆ@sÕHá.Dàô�YѯÞÑ9ó%çÿ«ëáKà,<Æ‚Ìáäì¥Ðæ¬ÎŸ×ëðX¥áÿ«÷ø_?Ÿàº©™b{ÉÍÔJ!s44Ì3àhÚ§RiÜo¾àW4²®½©ùIºv3ïP]õ¬óºf”aÐÌ¥åMúþeD5RYò¤ÓL{˜ðJ!N„Á£» -q†ÍpȆY•k·>¸æÇ%ÿLÔ“#cH¬f·='ÓBÇÉ&×´˜<ê,®—ëØ #z?9éxã„©v°çKª¾æê5 Ë�ªÈ‡\<}’&«‹dNRw”•EFôÂA¨.–)Ð˽XýÞ{Jká}¼{ü»ŸC&žØƒ€Û²˜Õè)˜ì×?Ô -ò -\Ý,<»¥2I¬L‹5}fš«!dÃ(¶¸G’Ô½DðnI´?뀿Ì6y K<ÀCFm²$pÊ2 Aò›9hp»¥kr‰Ç²²²RŒ›t -0:8¯îŠ7c¢Þ&åKUÒ_;‘—hÖÙ61ä·Ø@§§1±ˆŠ° -æG<M¿•çFRRTQÓêUY=á¨ÏAšÌBl*2—†…d¡µ Õ8YB(•®Ä(TÜ5÷;Xæt8¼nBëéQÅ ‰Ï36ç×Ã*ýSìÛòÃÑBWÔ4þMiÓ“_]}Ûk‹Êý³VU&/¯õ·Ç-σìâ„ Ü=ƒl.š°—Lt“¶¼x÷kO–úè´ebˆ6‚ß-š"„Û»×ø+ö~nڅ໵ʚõ¬¡¹Ý»“ -"è,Ú´BRÕ+¹1ÐXf·¬,Ç®Š -϶¦ðP”ñºã—UýYÿ–åï…‡ZÅ”™k£W»–^»ýòQ³æëê½þ òf<FÍY3gÖîÖ‚—üšÝ„àm¾š.ƒpˆª@'Ó`žnAê÷‹CEÓÙd~ù·¿Ý^¸æ¡m¾Ðåë*ƒ”wº ›ÑV” ù¤¯”×›±¯°ÉÄiAŽ˜d{"÷·\ûÏxáyøþýÙ¬¡j¦ê½9Ê\ËîlŠ)°®z{§·#úEaé΄`ꨣfß b ½KÒ‘Ž|Ør«) mÅøþ\ßÀ:º N5¥‘'—Ó¢†-5Ž¾; …:šÓp”E‚ŠübÏ“O»om«õAðÕeˆRR´ØBÀ_é†Z‰«ÂÙÀßÑ‘Ø°j\ó>;Ôp6øKÎ"Š -ùü¹Ev!Öj-”¤Â˜®¡fdfC Œ’N-èÓìPguQ×™ÐW£ÁäG¬DûÛ¸ü ùè„k¾à’uFMñjšÕkƒ!sEªË‚J¿Ê$x£o� J Ö5-öõ_KJÔ{õ¿ØëâXqXÁ(íGT‰`(î|}lnTµÅŒðRŠÅnY`|‡ÊŒn¾|Öv%”œ¡u@ ÜQx-ÂÉv·ð,ã|gmÊêÞÜz¤VV‡žCI†áÙ}Sµé3Ç e&8 ™"Ân"£®žšÞ`pññùB¬M»nÚ{N–¹¦ÈCµMÒùñ -¹*¹ý½pV¤k33´~IÐ9tTOÆ!æ0Ü´×cÚw4+w²q¶'ZtTMý> ½cG€]“…™ Mú¿¬zl<íµ1œLH•²¡½ÞÐ?";ɹ2'C¬N²ðôTYUF€…rœFÅ®){?(½™3Ìy(ôž¬àÂGËb ý%“/ÌG}Ðì.˜½ô=\þš|G4’‹®¹Ã±p9n¯ªú½Çeñ3ºôƒ‘¸ÿeBÒC&;ÊÑ|ŒŸ�1 .·kÓê{8ŸâÍ|K„ý¨k®ßa ž˜ƒF»Þ7EÌu[8_LÕ'õÉŒýV(‡ÄŽ£v]ÇØ.÷÷É'{£Tνk¡í\žžçwš *Ñ®&KY±¥D¨ˆ~/ïä§5ÞóÍŒ¡?Ý+Ý]z"ûsbFåÄ^…X¯î,OJE�¬;ƒ¾É¡ž -P.Cë{#Í&Ôƒh*#+fÝV)ÄÎȲS+†gd4Ï+ò»o€ÙF5õiC§öÙ¬Ì9ãZ™äïïñ¾ˆßßÑ`šæ¨â‚ô 7Mb•íÉKtþëq -Ïx` -?©^ÖŸwñ¨8¥ËA¯õ÷+:§5þD!x.oXNB -ý8T¯¤Œô8Éð<vT>Ø[7 -Ñ.õvÎÚ³iÐv»¥kmDꈟŒV|«Ýɉ*z;TŸá‹…£åB,6*-MãÌÿwÁ,>â¥à tçúc¯ýGý}B…/‰Å8SŸ„Zy)BO°ÉЗmûrÿ¦ž£:ˆ¬ëråw!쪼‹âUãÚ'bCé~¢-cl©¤£¬Å:ÕÕ¾+üh_ª³$5~~Èw‡ã¦#ˆ{•HéïC¸ñ˜,i(<™ŠµZ€øÚó°XUð?œ~‚Åq©£ÌrüM/¯¼‹E9!¢š3«òJ‚Ÿ–Ü÷؃hC¬%eœU£Ñ–)a:òRÞ|Ñ® -Iâ�û C1 -`õk„8%ƒ¸ý슆’ïɵ4kÖÖG€—‚^`èOÉè«ÀŒ]Xð35S]–bPæ—¾2õzÚ"L‹Üs2Ùl4L¤.7j°›x'LÿÓ\ÆêÔ½HÜ@.„YÁqÔpÆ{±tٌӔuò¾]%DË%×ø¬¨ˆ?žÓ‰èçžÛÎQ§as¼××S,ÎÂwž›Y<qút§ÒýB§CÓyTSÃÏü}«7x)/p/@³®Ttå¦>¿1@þر=CëÏ4~X}DýÆãnƒ)}©–Ï4hñrÄž)ë('éÈôúm®ÉÚÙ(‡ð½¡¢ûBÉa—Õ?ÂÁ±õ;¼UÛ7N'¢ÿ‹×XSÕ88b2— ˜›¥± üDŽ M×ÉÚ=EçC#ó~JX_\•Œ€üO²62®šŸ¥¸˜™µO/…iŸÝ(ÿ�1Ôö ÐË»dnn-|[÷Ÿ8TeùĤ¨Áô#æ_ôˆüNY6’:¥¬N¬÷ÄoH<nïNÚxx�W½ï«cCÇ`Ùtòªêø˜êTÉûƒË™y%®{ËÆ&ÕÇ,7¡Ú-XÛèI,³íû…iOÏkRÔa·YÔƒ5Õ…†lˆÅŒÿäh1sý=¯6Ï5X¨§À‹8ÜSNÝMÂDH©vÞ{p- ˆkQ¾d¢ÆÇì ‰š/1VÑu*+XnWy&Åë/×9vmLÑR{B'Ú„´a”§8¤¾î‰pê÷¡¸ã«Ê=•ïFcÆ «zNÕõ îå-fæ+žíѲÐD˜ip¶í ,rZ34qôY?ýÕõßà†Kvmꈈ B9Íç“$B;‡3›…»“§Sà\4J…Á'ôêo¥~jÀ$®I&•K©&øR™Á™6! “¡$%ÙöO$‚¯œ3«ÈÐé÷!ø|·k”ô`ó5‰ÆGh2ŸÙðÃÞÕï½ZOÇå±™Mgb`_EÊ$©³¼2ŸÖQßa†€°þ,Ťöò4wžÛÉW¬¯j(øxÕ£ÇqûHLþ5_]sú#H⛄MsW\ 'õ˜>ÖÑó¡uÃÍ6Cоթ¾dd�Aµ¡Ý€môTTikœ#HÊͬÙ÷Õ«¬~¼„ŒÄù¿å•dn¯m춻 …")!FÆ3©6~ ÖüˆŒòJ7‰�ˆCe:ÚXýû¨¢LÇÆd« þqÌxžYÃxxO]LK*à 7.«ÙµuërÑý–~þ'ÎYòn3eõuðX$ŒÝF°¨CèÞ>sê½—ìñµ¶<†öÈir1æ_!d¦XÙ‹@A9ÿë2ߧ~ÕüHHÖ°ôuˆ<(=žà%Jˆ®y󷢞b í¸ÝeaïiŠ©]B†>WÝ[w3¸CûOÖÒ’—^ª0´À¢„¼ ß#;ϵ¶*p2…pô¿¢™ÉwÊ1ãó¸®ïöÇ,7ÛYz“È À«"¯WÅ—‚aÚ¬âYàŸá�ªÍ÷®_uˆÀÿé9p„/=œÔ‡²üœ©9°bprY -•Ôµ+êøñS–ߎ»†1ÍWØôôçªh.uVšŸ˜åâgÀ£ßÇ!šóf¦ø#Zz6”WÎÆÐõЀ%êO<£w”±‹S¬UDz`ËAª ?¾r–fÁ52HL„•? •«·~æTMØ›‚ n–ÆW\¿ªÍóèŠ É�o†?NoLdöxlý]M -OÖ† %Ž"±`ªFXÕrÊÃÝÏÌ\Éņ÷€Óa’6Ÿl¨«;*Ój - ƒ¢¢"z�(*S3œKÍ}Bç¸WxP9r&?0ÎVhx'Q¦úMY¥V„Âœ'CÀÔ³œµUïPlòÙŽ‡iLJ«xÃ5K5SºÜ›'Ìœ‰l_ú„oX‘êûæ-m.7³íp5�¢Ë1Y}ìÀÎÃÕ;DBRuI(Xë¬înœ¬’—æ´XÓVÂð·yï󖓉Ãg{cäڈ̴±ÂãÐnoò6F– ¿kMÆ8+;Ã(¹„aÙKP»w-CëÅ&#LspÜHÝ^N·Yê ÅfFbÚÌÒ€·%9+VBËþ½™Ž¸ó½VªZdP¸ÒÛAšS£sü=€þv¦³†Ôí{¢jÎvK}h÷+Å^Ki%võ®°0ÎÃœ�¨Ð‚"©»RÜß%™&üÙA,:Ƹܺc!¥ ?Ïóq:¼°²^yBû]£`³PbÚu„ŽµõÈ:!PùŒÀfkPx.ù‚ºy1lÌ¿3!£8G`?AÀ01û9há#„QÃZkl -Ež¸¨{NSùê÷”–Gz -BƒvÝnÓê÷^ª8ÊïÐú¦ðû&|³Ò¹”¬IC°óv¤õEúËÉ,¥¾Î‰NÔUŠÿõÐ÷UÛ -1jä»æ„žë‰Æ¤Áüy~1y_ìÜY‹’bÍo+(䀓v¼ÀƒÀ»VÏÈ8jÁ躜 -͆ÁŽê³PlsX¹vëC¤’’ð£$Äõªq…òWk™Ïiý^fÄ”8Ê×CŒG(; âÔúûÄ{ÍàÒÀïKH‚XÞd¯�¥ß²ÊÀ_à”›,ƒ7�å/>NvnX� 8Î<v†yy¸+ãFâ,$“ŠÙ¿ecõËŠ ûM$ ¾ú™·Ïš_Zê.�¹&¡öíËO×ÔPñ%LÌ|wkmù!A07#º‚îØž»ÙœaÑv[Xê8Ê5‘"®óïíÜ)ƒ~Ió$Bˆd ÀÄDÇ䬆öƒ¼#·ù¤- Þþ¢$ú!'Ž¬ýл׹QIm·¨ÊàVÿí´³¿ÙÝœð2hEÉØLƵªÍèw©ö"Z#ÓYXØ"d‰o`¯_éµ»!rûv_øõÍÞÈ'„?KpªË¸íõ¡©»›â¹–ünÝ%N„£æ-Xï¡pèˆ:*ƒ²ôsxÏn¨Ÿx.T®fÔÉg܉D§ÊöœÀ;^�E€8Xµs6’ç¾ -›éi'H~<«ê¤<²¨Éßÿ—"Ô-$Rk?×Uêø“9 Ä9µš9Ãirã‡]wë¹ÄìOŒ ³ÜÕ»¶„%sŠG`¯þUÆ4Þƒ®ó J�_¡Ç«ýSPâwͯ½F^ -ëä&̬ßy<m¼z%ªwOéŸsY„Ý(Qž -!cîGh¡GïC°x¶Õú댺zšË¯j÷2:õ �æ$UgG‘r?omYuñüsT†z¾ŒT;MߦÇkwOç0©Ã6èê[Ï3 ÄW†¼»î`Ñ™˜¤ÿGµñ?øÇ ›l?2C9m”Y@eg·<¬D_t…g34WZç¶U&ÊO“¼W æ´É„?ª¼¦©®ÔW¹iò±Aü_–ù¦TùäOyšéƒØ;VÒÌ‘L‰¯ç*äü“ô*Ì5{ƒ8¥v$HàÄ}[¿BïEJ!;h<sæ¸&9ù<ü)žã1a¹°&͘>ýgÚ4¹?82!è‰|;†»ç¿„§"V±E6﵊òœÑiiQïú%‡Üœ9 šÂ -hƒíTy˜âé·‘Ó>Ìé,¤Ý—ÔÕÙÒE±‚QONb“.Xâ~hV3'1Y0¤Ö1ɪ•�ãO¿Ø`´ïìj ‚;![_²¦Bpô¤¨l"$ä[}ÁvÕ‡‡þ’ &}§õ}ÀGëPptŠÇžò’ÞÅ» Zï^N¡g‹(ŒYÜù;‚ †Å’ƲìGø~#T?mHö5‹/nÀÀ'$HÈ6\VÖ,X‰«1¶;@Ä4ÅN3ñ¸¾�É9²EG ]'“ûo©ßBèÑàéú+ÐðÛcL08Aƒ -MÛYOÚIur`9à’ïွ‡ãX¿h2W³Rüý‡—.-ÛZ¹ã—o<·'ØèKû¶Êß›8pÈ¿^yéþFù¬Ÿ5ÏSY—á£Uûd#A*ÿ7âämˆUíSØØc&ÇÈð‹ì@L{ÀS]²:]µ²Æ×5Á¹@0©‚\UZÛæá ÒxOe…þòèöÉEÇ3Ý¿.ñ«Þtðä¥!ëm+ªfœ×1@wÍGXU3Qœ<Š”•Úý;ñáQk8(Gä_DÈ †DÊ|fCùŽb<°ÇÀ÷|Á‘çRÈw9ŠS8æÅÚÊȆ Ý2ËÈú -NïÌ‚a¥EuÍ¿»êÐ>òykHåH3þ£/'LøV•jaò—�_»ÅÂ:ú®jE†BzÄìéÞDâÊÞÉ……-šé@hU˜€+Úi°!eg{¦tJÉ9™8Š#áâ? Š=0)B ®†ƒm”Eä'h~¿}á~oÇLkú§Ø9¯·0´&þp‡`¹ õíg Ý"n;€¤ë'ógAŠ±b9B°ü3aš¸'yB€Š¶ÐÄL&3A¾Õ”AÎÃÂÏ�ç}W«’t«ªPWƒ‚äBÝÂ_bªÞ™™IhíL"\‹½e[AD~êš<¡šF”¥sL“{·&9pÙ<8Éõ‡M¦34Z/)mrv‰NÙ\L|ÿÓ+hA -”Âxléï¢bÑ5Ɔ¨ˆ9š¿•ƒ×[v6žÇ“SPGgBǺÆþ[|¾,â¬HŒÓO×£¨QºÈ‚!d=<dÐÌZ®Y¤‰Úƒž³lÛÊZkûn§a·½j:`Ž^Åx¿1ä{s`6—ÛóL!×#aM ¸†[°¦Â¡ibÁì|_DÈï`a£+°¹FëQuvè(᤼¿Q5¼h”^«*Ü,Ì&]½ÝИ³Ð×C£Ï…)ô�J‚ßÎkÑʼ9¯"eœÈ#Ù1;ËJÑ]w9;u0:uê Ñbj2ÏFû Îõσõ–ÄäSQ#ÛcíßÆض•ÿ¿ß“ñ"éÃñÚÈ'ËoÊ° -ûDQ¨ÑmHc”:·Ç•uÝZÏ’Ä‚¨ àu=z4];<5|<aFJX!Œâ)X‘ùMoD(çÅU{›)M# -Ά¹YV~¢‘dÎ,×Äç‰W·Å¡ ñ ºŠˆÛÓÀ¦U»ˆrVæ+ÝÔe1…zýâš~TÉÖ²ªÒð7$´Žü–T°æúÌ0ÈÍì· -Q›I?×ýo¾ž0á{vN„ó<ƒ�~·ÆÒo'5Do!Ž‘²(•¹©®i¢îÀý+™Uèp‹Ð눂šÉÉöU¾ˆöªI<‚ÅÜñïÑrN¬N^$°Ñ¿™>ej„Ò/åyúlEŽÎö{<J’ü¨ù!øãpn= z9Ìå¾ÈFH;XóbbºôíåeM˜(ôãðzºÛ˜*¨ÔßÞß×0œ„™û�ˆ€ GÜüí]pUå?¯ïœsßÉÍ’4@yƒ -²jdɬӮ–nãØ.ÚµÛu;íìlµììt‡meU´jíÔuºSug¡Êê -Bl$D!R!¼“\ò¸Inrßç}ö÷O¼r ¤`'Ø6gnrsî9ç~ç;ÿïÿøý?§…)B³âØýèw -g�;±+¢{¦Ó–µ3‡^Þz¼…´µÅÞî;A<C†—B7tý(tãïª wu ?òÒý`Û:,qT~PÀù—æ‡x} éËh|mZWÅâmO××å'<º†RV é‘Ô²¾µ¨(™0’O) -xŸµMjÂjËî.È …°@K-Nxù Å+®û\4õ0þF¤HV‡0šà3Ë -Ú£Vz[×`ö§»Ü›4ä–O™u«jóÎ}ˆtB2'þÞ𜑩4P¸ÇFä¶ù•ŒŽ?-E›O‚µ.¸ %tͯ>ýï]°Ìÿ…LÑÊÒÛŒzÁ½øjÛŠ++“½rÜ„` ïGQÈ6^€ÀÅ‹^�¡òÏx¯Tpù¹È®lA#ÎíæÀ@xne0vK8ÜE$ZàòCIòÒÎgù¬¦U)k2–÷Þ´®¯ŽCYÞÓ’"›Zá󇼒¬$ÅS#Þ:]ߟòF†KvãõŽaDs Ë~XýW.ùþeæßر¢g>zB«Þ^ÓýÚË—¢Ðì*¼ôoÉd¤1Õæ>ûåê²h:Ó»÷«y¥tœ0žÍ€<Æä -¤/`ÖéKÜš‹éy -©rÈÛõDzåC¹Ýw"C^ïèE'�Å7êÍØ;)òl}ùE²?×Å6¡P3*ê””…Íû.¼YÈf:"—ÏûáA½nèҦ啞n@ÒnútnÏ#áüh£„|åòu¡-šÎ‘aPˆªÕÌ‹«Ò…ƒ?Ù2R ƒÂÚüâ‹# hºAíííÊ›ŸTÄ,§¥EËSÈN6?²~ýˆ‡ý.d~lIY-ðN®+€´E9XÇ*tHR<Ö/ƶ–Öw%Ö)Ìͩ¢í±ça ¾°=ﮞMêjŽ0ÀYUÓ¹Ë}¬_‹>s9)–úÈq$ Ìèü³=±ÎýžQÊ¥4Pv£Î‰ÿ ¯¹”§RöÖWð;¨äbu\óAÞŠí]‰jn¬ò_iÜf__â*)Oæpdì—¨¥À=§KAúÇœÉØ�—À 2»Ë´Øc&o‘lñlů‡ý=`X‘¬‘øa®¸‘;ñ›¼z‡nX!h¼a -Þ=ˆdJcÉÔŸ+²uÜ߀މ¼kºß%í»WNÅ7àAEW¦ns†»b ¯GíÂT„^üËvÊŒµ7ÇÜŸZm§~ÁªfÞæo�;ýÒ)ŠÂ™¿jN'öNãCÞR_ÀÖ“+€Ù݆ûùk×ä¿=ž4Í7Ú£Äîö!ªn ço› .m¦ø ‹ƒëÓe½¿»ÄF¯dçÚå�sc}½¼R|ÑO+0ÝÔgAŠhBòË2%¢™]ƒ³ÑHñC„‚@ÁZ Ò (È°¿”4´ïÜ]’Í=Éb[âæ0ǹM’Åž±",ö<DVÛ]^k¸±èbÛ/9ZŒ y¸píí!ýET»Q0GÆ{v®åXO¨ñ¥/òÐH%Ñ -ѧ‚‡GñCà,“kA²þÍ瞈’W²ëüÐzXÜ›%¹óÜŽ®9Ö"zPÈ“Á¥Ž"±¡cVݶnºw[²’Rc))\U˜::ŠlTÈ•R1eJXIŽ5ô¹ÜF“èί}MiI(âpÜyŠ¡'b(Y&dœUL€í�úÌî®ØH#Üm8îtÃû3V²–LåøG§Wï‚G *\ȵt Þ 1Æo"ç¸4m›*Œïî 'na™È>òxsç§}Ñ‚¹ÙËÄ¿ÔÍÍ€Â<·ú;˜š -lV¸—p#òa)jáÖ@g³>T<½VTXu:k,Ãúõ+£vÊ©þñ, -F¿7›”²$tÄÈõÓ¸å -mCC¡„«ÎNôY…èb?:�±`º-:‹P:kEö.À7aúÛ’¾Ç¼Â¦¯ÂWýÅ^Û1Å cY+‹x0(îBƒÌÝoy7ë&‡7–næ™njÚ€^�1K= -²š§‚ê+ÔµHß‘T+\™»‡9Â)Kƒ¡öö¢€`ãáféU3·Ûß"d´çEŸç[HCÜÅ»uÿŠ6Ô€Wt<jH=?v¬ßmîFÇ<2˜|@=øNt/×Hû’ÖÚÑ©�?Âý`„Xu)ÀJ–ºÞr¾@ûOnG€æ.ýFc´¿+Z#*J5Ò>é{NZS½Ø[æ)Ýð‰hì̽TÌüGÃåò1¤ƒœ•´C ¸ïžêLßO>oφwð‹$,y¦Ì„Å:,ÍZ!ˆì¼"¸¾¡”µÍÃ2úίÜçÈé+”ÜŽ¦£³7C9›Þoèþ,"¾ô@ꇿknäŽq-^'ÜøÒ— FÞèYMâ÷¤!9Ï}ÁÆÞDú›4Žð¦�·JØ/¹}<“dàV=€kG”ÁI¤ SÊr qý6°²;YBw†àŸõ4]½ÒÉH¬ã׿‹[6wî«y…ATõŒäuÐü>Œêœ_p½®&J|b˜D©¤ Ï¿Ýûˆ6þ%*ùeèì!Ç»Þ`jÌ__W<GûÑùh_°<ý($q' n�0·'`¿“ÒøŒÈØ—à]?šµ-8ÜçKú;Œ²2I“ü€]Š&³§ßk;Ú±^<…`>¦˜ðø€/t[û©Æßü¶®®Î ç‘ßÜDÆ&œó¾G¾ï´†?g Y;gM×ArÉÊ•ž@g§®•Ï\æäv¤åŠh•¾Ë0%š®ÿSQ::8—<sx%’3OJ²¾ç©p¬ŽÂ²Î¦×dy÷%TÒê½*·EÑ…!'c¦ûýràlwÛ &üÈâBç=½¾a×z°Pdç ZQ 2¡a×sO¶áš]´IoÁö‰sþÅaâ oZ«ÁGõž›èÛEzmŸÁ"\wà —KžÆc}ü¸F©:OþF÷ÐyÈeàGnŽ$j,•_i%íݜʒU:piT]ö€¿Ãê°b‘Ɖ,òû§ú3Íá}ݱÛ/Wkf¦C ºÐˆ~`ˆou¤:Ïä8Ú—ÔÑlZç‘åDZˆÎB]àñó>ñ�HØscHûíOÌDwgµèd•½µb†6f^‘š8ÓX†JÃI�`Z†ÒF;ER„FYû3K§µîOOq,«©`t¨ KòÚßöÞ[ÿM) Š*K¢Qçrs#wþkýú1¾WúRû{S_�¡[2åêAÉUÒ ¢© -Ê\¼ B -JÍ=À¼[ ~E%n^A®Ñ§hr„žîŒYXå—fuP™hL<ÅÒ¯å½Òu\íßi’q#cA[îsô€§ÁÑN1iˆb0Èùϼ¶>xflhFé ?Ǿ¯ðÈ¥è¶ß‡kcbBTˆÃy>•Î<•äØžHt¡k³ùŒ hðˆÅö+ä)L‹ÙÔÓpàƒ)ˆe¡KÎëÚ2`Ævš/€Äžò†Æ��|IDAT®:çˆEé¬ýãÛŠÈ]sî•&g¹Xª¢·rZ¤¿·ºn3˜¾íÏæ¼ö&4i¤ rTƒjA„Ô›ä샢ÆØÇôÊ©w�šôòüµ(ðÁÉ·ïéøyþƒHçÁKEâ‹jÃÃoE²ƒƒ”«k$Â[û™÷²cn—ã€Á˜tæDJ'÷ÜX€r5Ïy–»¦Û^ÏVÛ§[~ÎGÇ–\¡pPïâÚ ÐSF9/¨Svȹ‹B’ôó^ËT<¼øwœeG$EšaÚú/ÈÌ¢ò%/ºå¶C÷òò?"¡Ü}žÈWâÑ4µ€%†Ri¿2“ ò¤˜N8†åå™ØŽúB‡šåâˆÆFˆšèùp¤ÀÂHÆþQ@‚Ù¾¿ñgeÇ >J˜ðMkêþÄZåh†Ý-°È -³]Ó®�Ó™¡”V½RcÖζ›ÇŽeɈ¶t ”›Šò%ò3…ɾ]É‚)ßEš ž\/"¦ùHÑ5ö'£ÛÇ>{9y¬kb|w Ôt¿0\(ôhf—Ç#+§œÒ™µ|ÛÑV»¿´„û³r&gÌ…>4BŒÔÍ9®FJ-Æ{NZš1Í7âí$Ï«ReÀvÖ-, þøáÌDÚxÇÊéü‚On_J},½ï?˜€Œ0ƒÉv>¼dŒ>”ôÄSò±//§¬»¯'¾“ªÚµ¹!S0γ³ÇÛ¥êê€ßWô0Œ4šù„~Ì8Ò;€7CÿYrO¸_)É®ŽÙ>€Ö:´‹2 ªPVãþÞúeAï¹k£W*´9‚4OM¤ÞŸ? -röÄ—dg#ÉM›å÷HVBwýLpö÷ïâ浊EE!Å7MƒÕŠ)ìU¦vàp/PtlÚöõÿŠ’s2Y÷Ù0ÀdÔ€CžxÕŠÅŠ\ªðÁ»Z¾W4ú©ÑÿDã5‡µÈVR˜rR@Âð*ք̘x¨)¡b�ô³‚k¡ö!UGèG÷[XpõzZ[×Cye‘ZÝŽÂKšf¼øwPœuÈ·¿ÙÕðöá\!'ÿü“?Ž�ÍoC .1ÒÏ+´XGñ[¦Qå@à@°yÜ^¶,ÛRž}o<¹¹Ð&ç+k¦2ßCRÖ|ùÐl_íèX-]ñZz~ç¼n61¿¼AÓ¸7 üòÂqCKuîIçß–rÔs¿¢+ðoÛ¨ç@øB½ˆ<wK’ÓrõØÚ<¿ëý¾]ã{½ÊòúhÒÕ#%Ó?ˆ”r2(ìšÌ¯þR¡lFÜ'6£±™ûˆÖŽä‰Ê¼Þ -3 ®m1BF*ÃX6r<‘ U£4…M.+³•¦&^_¾Ü…¾¼Ð¼sçU±3åòâ»òdv¨kQR?³S±„ë X>£ÏDñýhÅ(0“r̈́훋£¿å¶ÃÝñµÐhY€¦ˆˆ"ØÃGßh¯ÏåúsûŒ÷ÚB%““þ5bˆ†æjGxTÌK®ðáà gr}’£Ãèòšë(¼ÎËj;2*K¶©*S“fÒFÅŠ€”é„oC„ehØÍF{uëå"„ñ®iò}˜>l„ö)4ʤ’ZEd?›“ØMÐÌôù¹#7tbœFRt»ÏÅ‚mï¼–Ù¸qãǨ!Z|kV¬ÿ:ºH«€ÏÞ -$’èafvZÁS ¸´ðIûO¿cÝ"8Ó1Þ¡ðΓ–#*íÃ]íPo¹œÓs=ߧIã{=ß¼k£‰N^Š^.yýàç%$šçí:a?Ò9é`túùùƳ%Å•þ5†ï·‹jý#Øå«=ÙHâ¤,T¯nôżNŠšD®ö³Äˆ5Õ+ÏþiÀ6pÎÙ1UúòíHô¦L{&ªÓƒŠPŽtÚ¦vw›;“I—5ºvJÑÃI?ç-:sQÅ›*=ÊäÿŸf(ÍÀtF>ÍŠ45ù^ëxÇ9Ü›XŠ®ÄY3v\Mñ°Õy¼$-ÌhúáúŸ=wµ‰Kš“Æ;×õöþ¤ñ½ÞîÈu|=d¨ÂPøæe -`Wºljt¡}VZ…úÊżù•>—û;å¨Q\³iÁɽG¯¿Ï±ò??ùó§Z¼¸ ¯ -î«£rôŸîÓ—îM÷5TTÄÆÂ=/ÝsôŠ²Hµé…²ÿß<ݱßiÒøŽ‘Éß'G`r&dÆFrÐ?¢ƒü†¤1æ=¬Ã����IEND®B`‚ \ No newline at end of file diff --git a/core/vendor/guzzlehttp/guzzle/docs/_templates/nav_links.html b/core/vendor/guzzlehttp/guzzle/docs/_templates/nav_links.html deleted file mode 100644 index 7950a0f8e9d4bfc851897810c0793232d37a1b7c..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/docs/_templates/nav_links.html +++ /dev/null @@ -1,3 +0,0 @@ -<li><a href="https://github.com/guzzle/guzzle">GitHub</a></li> -<li><a href="https://groups.google.com/forum/?hl=en#!forum/guzzle">Forum</a></li> -<li><a href="irc:irc.freenode.com/#guzzlephp">IRC</a></li> diff --git a/core/vendor/guzzlehttp/guzzle/docs/clients.rst b/core/vendor/guzzlehttp/guzzle/docs/clients.rst deleted file mode 100644 index 0614a25edf76d954a7e782508268ae4732df459b..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/docs/clients.rst +++ /dev/null @@ -1,1315 +0,0 @@ -======= -Clients -======= - -Clients are used to create requests, create transactions, send requests -through an HTTP handler, and return a response. You can add default request -options to a client that are applied to every request (e.g., default headers, -default query string parameters, etc.), and you can add event listeners and -subscribers to every request created by a client. - -Creating a client -================= - -The constructor of a client accepts an associative array of configuration -options. - -base_url - Configures a base URL for the client so that requests created - using a relative URL are combined with the ``base_url`` of the client - according to section `5.2 of RFC 3986 <http://tools.ietf.org/html/rfc3986#section-5.2>`_. - - .. code-block:: php - - // Create a client with a base URL - $client = new GuzzleHttp\Client(['base_url' => 'https://github.com']); - // Send a request to https://github.com/notifications - $response = $client->get('/notifications'); - - `Absolute URLs <http://tools.ietf.org/html/rfc3986#section-4.3>`_ sent - through a client will not use the base URL of the client. - -handler - Configures the `RingPHP handler <http://ringphp.readthedocs.org>`_ - used to transfer the HTTP requests of a client. Guzzle will, by default, - utilize a stacked handlers that chooses the best handler to use based on the - provided request options and based on the extensions available in the - environment. - -message_factory - Specifies the factory used to create HTTP requests and responses - (``GuzzleHttp\Message\MessageFactoryInterface``). - -defaults - Associative array of :ref:`request-options` that are applied to every - request created by the client. This allows you to specify things like - default headers (e.g., User-Agent), default query string parameters, SSL - configurations, and any other supported request options. - -emitter - Specifies an event emitter (``GuzzleHttp\Event\EmitterInterface``) instance - to be used by the client to emit request events. This option is useful if - you need to inject an emitter with listeners/subscribers already attached. - -Here's an example of creating a client with various options. - -.. code-block:: php - - use GuzzleHttp\Client; - - $client = new Client([ - 'base_url' => ['https://api.twitter.com/{version}/', ['version' => 'v1.1']], - 'defaults' => [ - 'headers' => ['Foo' => 'Bar'], - 'query' => ['testing' => '123'], - 'auth' => ['username', 'password'], - 'proxy' => 'tcp://localhost:80' - ] - ]); - -Sending Requests -================ - -Requests can be created using various methods of a client. You can create -**and** send requests using one of the following methods: - -- ``GuzzleHttp\Client::get``: Sends a GET request. -- ``GuzzleHttp\Client::head``: Sends a HEAD request -- ``GuzzleHttp\Client::post``: Sends a POST request -- ``GuzzleHttp\Client::put``: Sends a PUT request -- ``GuzzleHttp\Client::delete``: Sends a DELETE request -- ``GuzzleHttp\Client::options``: Sends an OPTIONS request - -Each of the above methods accepts a URL as the first argument and an optional -associative array of :ref:`request-options` as the second argument. - -Synchronous Requests --------------------- - -Guzzle sends synchronous (blocking) requests when the ``future`` request option -is not specified. This means that the request will complete immediately, and if -an error is encountered, a ``GuzzleHttp\Exception\RequestException`` will be -thrown. - -.. code-block:: php - - $client = new GuzzleHttp\Client(); - - $client->put('http://httpbin.org', [ - 'headers' => ['X-Foo' => 'Bar'], - 'body' => 'this is the body!', - 'save_to' => '/path/to/local/file', - 'allow_redirects' => false, - 'timeout' => 5 - ]); - -Synchronous Error Handling -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When a recoverable error is encountered while calling the ``send()`` method of -a client, a ``GuzzleHttp\Exception\RequestException`` is thrown. - -.. code-block:: php - - use GuzzleHttp\Client; - use GuzzleHttp\Exception\RequestException; - - $client = new Client(); - - try { - $client->get('http://httpbin.org'); - } catch (RequestException $e) { - echo $e->getRequest() . "\n"; - if ($e->hasResponse()) { - echo $e->getResponse() . "\n"; - } - } - -``GuzzleHttp\Exception\RequestException`` always contains a -``GuzzleHttp\Message\RequestInterface`` object that can be accessed using the -exception's ``getRequest()`` method. - -A response might be present in the exception. In the event of a networking -error, no response will be received. You can check if a ``RequestException`` -has a response using the ``hasResponse()`` method. If the exception has a -response, then you can access the associated -``GuzzleHttp\Message\ResponseInterface`` using the ``getResponse()`` method of -the exception. - -Asynchronous Requests ---------------------- - -You can send asynchronous requests by setting the ``future`` request option -to ``true`` (or a string that your handler understands). This creates a -``GuzzleHttp\Message\FutureResponse`` object that has not yet completed. Once -you have a future response, you can use a promise object obtained by calling -the ``then`` method of the response to take an action when the response has -completed or encounters an error. - -.. code-block:: php - - $response = $client->put('http://httpbin.org/get', ['future' => true]); - - // Call the function when the response completes - $response->then(function ($response) { - echo $response->getStatusCode(); - }); - -You can call the ``wait()`` method of a future response to block until it has -completed. You also use a future response object just like a normal response -object by accessing the methods of the response. Using a future response like a -normal response object, also known as *dereferencing*, will block until the -response has completed. - -.. code-block:: php - - $response = $client->put('http://httpbin.org/get', ['future' => true]); - - // Block until the response has completed - echo $response->getStatusCode(); - -.. important:: - - If an exception occurred while transferring the future response, then the - exception encountered will be thrown when dereferencing. - -.. note:: - - It depends on the RingPHP handler used by a client, but you typically need - to use the same RingPHP handler in order to utilize asynchronous requests - across multiple clients. - -Asynchronous Error Handling -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Handling errors with future response object promises is a bit different. When -using a promise, exceptions are forwarded to the ``$onError`` function provided -to the second argument of the ``then()`` function. - -.. code-block:: php - - $response = $client->put('http://httpbin.org/get', ['future' => true]); - - $response - ->then( - function ($response) { - // This is called when the request succeeded - echo 'Success: ' . $response->getStatusCode(); - // Returning a value will forward the value to the next promise - // in the chain. - return $response; - }, - function ($error) { - // This is called when the exception failed. - echo 'Exception: ' . $error->getMessage(); - // Throwing will "forward" the exception to the next promise - // in the chain. - throw $error; - } - ) - ->then( - function($response) { - // This is called after the first promise in the chain. It - // receives the value returned from the first promise. - echo $response->getReasonPhrase(); - }, - function ($error) { - // This is called if the first promise error handler in the - // chain rethrows the exception. - echo 'Error: ' . $error->getMessage(); - } - ); - -Please see the `React/Promises project documentation <https://github.com/reactphp/promise>`_ -for more information on how promise resolution and rejection forwarding works. - -HTTP Errors ------------ - -If the ``exceptions`` request option is not set to ``false``, then exceptions -are thrown for HTTP protocol errors as well: -``GuzzleHttp\Exception\ClientErrorResponseException`` for 4xx level HTTP -responses and ``GuzzleHttp\Exception\ServerException`` for 5xx level responses, -both of which extend from ``GuzzleHttp\Exception\BadResponseException``. - -Creating Requests ------------------ - -You can create a request without sending it. This is useful for building up -requests over time or sending requests in concurrently. - -.. code-block:: php - - $request = $client->createRequest('GET', 'http://httpbin.org', [ - 'headers' => ['X-Foo' => 'Bar'] - ]); - - // Modify the request as needed - $request->setHeader('Baz', 'bar'); - -After creating a request, you can send it with the client's ``send()`` method. - -.. code-block:: php - - $response = $client->send($request); - -Sending Requests With a Pool -============================ - -You can send requests concurrently using a fixed size pool via the -``GuzzleHttp\Pool`` class. The Pool class is an implementation of -``GuzzleHttp\Ring\Future\FutureInterface``, meaning it can be dereferenced at a -later time or cancelled before sending. The Pool constructor accepts a client -object, iterator or array that yields ``GuzzleHttp\Message\RequestInterface`` -objects, and an optional associative array of options that can be used to -affect the transfer. - -.. code-block:: php - - use GuzzleHttp\Pool; - - $requests = [ - $client->createRequest('GET', 'http://httpbin.org'), - $client->createRequest('DELETE', 'http://httpbin.org/delete'), - $client->createRequest('PUT', 'http://httpbin.org/put', ['body' => 'test']) - ]; - - $options = []; - - // Create a pool. Note: the options array is optional. - $pool = new Pool($client, $requests, $options); - - // Send the requests - $pool->wait(); - -The Pool constructor accepts the following associative array of options: - -- **pool_size**: Integer representing the maximum number of requests that are - allowed to be sent concurrently. -- **before**: Callable or array representing the event listeners to add to - each request's :ref:`before_event` event. -- **complete**: Callable or array representing the event listeners to add to - each request's :ref:`complete_event` event. -- **error**: Callable or array representing the event listeners to add to - each request's :ref:`error_event` event. -- **end**: Callable or array representing the event listeners to add to - each request's :ref:`end_event` event. - -The "before", "complete", "error", and "end" event options accept a callable or -an array of associative arrays where each associative array contains a "fn" key -with a callable value, an optional "priority" key representing the event -priority (with a default value of 0), and an optional "once" key that can be -set to true so that the event listener will be removed from the request after -it is first triggered. - -.. code-block:: php - - use GuzzleHttp\Pool; - use GuzzleHttp\Event\CompleteEvent; - - // Add a single event listener using a callable. - Pool::send($client, $requests, [ - 'complete' => function (CompleteEvent $event) { - echo 'Completed request to ' . $event->getRequest()->getUrl() . "\n"; - echo 'Response: ' . $event->getResponse()->getBody() . "\n\n"; - } - ]); - - // The above is equivalent to the following, but the following structure - // allows you to add multiple event listeners to the same event name. - Pool::send($client, $requests, [ - 'complete' => [ - [ - 'fn' => function (CompleteEvent $event) { /* ... */ }, - 'priority' => 0, // Optional - 'once' => false // Optional - ] - ] - ]); - -Asynchronous Response Handling ------------------------------- - -When sending requests concurrently using a pool, the request/response/error -lifecycle must be handled asynchronously. This means that you give the Pool -multiple requests and handle the response or errors that is associated with the -request using event callbacks. - -.. code-block:: php - - use GuzzleHttp\Pool; - use GuzzleHttp\Event\ErrorEvent; - - Pool::send($client, $requests, [ - 'complete' => function (CompleteEvent $event) { - echo 'Completed request to ' . $event->getRequest()->getUrl() . "\n"; - echo 'Response: ' . $event->getResponse()->getBody() . "\n\n"; - // Do something with the completion of the request... - }, - 'error' => function (ErrorEvent $event) { - echo 'Request failed: ' . $event->getRequest()->getUrl() . "\n"; - echo $event->getException(); - // Do something to handle the error... - } - ]); - -The ``GuzzleHttp\Event\ErrorEvent`` event object is emitted when an error -occurs during a transfer. With this event, you have access to the request that -was sent, the response that was received (if one was received), access to -transfer statistics, and the ability to intercept the exception with a -different ``GuzzleHttp\Message\ResponseInterface`` object. See :doc:`events` -for more information. - -Handling Errors After Transferring -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -It sometimes might be easier to handle all of the errors that occurred during a -transfer after all of the requests have been sent. Here we are adding each -failed request to an array that we can use to process errors later. - -.. code-block:: php - - use GuzzleHttp\Pool; - use GuzzleHttp\Event\ErrorEvent; - - $errors = []; - Pool::send($client, $requests, [ - 'error' => function (ErrorEvent $event) use (&$errors) { - $errors[] = $event; - } - ]); - - foreach ($errors as $error) { - // Handle the error... - } - -.. _batch-requests: - -Batching Requests ------------------ - -Sometimes you just want to send a few requests concurrently and then process -the results all at once after they've been sent. Guzzle provides a convenience -function ``GuzzleHttp\Pool::batch()`` that makes this very simple: - -.. code-block:: php - - use GuzzleHttp\Pool; - use GuzzleHttp\Client; - - $client = new Client(); - - $requests = [ - $client->createRequest('GET', 'http://httpbin.org/get'), - $client->createRequest('HEAD', 'http://httpbin.org/get'), - $client->createRequest('PUT', 'http://httpbin.org/put'), - ]; - - // Results is a GuzzleHttp\BatchResults object. - $results = Pool::batch($client, $requests); - - // Can be accessed by index. - echo $results[0]->getStatusCode(); - - // Can be accessed by request. - echo $results->getResult($requests[0])->getStatusCode(); - - // Retrieve all successful responses - foreach ($results->getSuccessful() as $response) { - echo $response->getStatusCode() . "\n"; - } - - // Retrieve all failures. - foreach ($results->getFailures() as $requestException) { - echo $requestException->getMessage() . "\n"; - } - -``GuzzleHttp\Pool::batch()`` accepts an optional associative array of options -in the third argument that allows you to specify the 'before', 'complete', -'error', and 'end' events as well as specify the maximum number of requests to -send concurrently using the 'pool_size' option key. - -.. _request-options: - -Request Options -=============== - -You can customize requests created by a client using **request options**. -Request options control various aspects of a request including, headers, -query string parameters, timeout settings, the body of a request, and much -more. - -All of the following examples use the following client: - -.. code-block:: php - - $client = new GuzzleHttp\Client(['base_url' => 'http://httpbin.org']); - -headers -------- - -:Summary: Associative array of headers to add to the request. Each key is the - name of a header, and each value is a string or array of strings - representing the header field values. -:Types: array -:Defaults: None - -.. code-block:: php - - // Set various headers on a request - $client->get('/get', [ - 'headers' => [ - 'User-Agent' => 'testing/1.0', - 'Accept' => 'application/json', - 'X-Foo' => ['Bar', 'Baz'] - ] - ]); - -body ----- - -:Summary: The ``body`` option is used to control the body of an entity - enclosing request (e.g., PUT, POST, PATCH). -:Types: - - string - - ``fopen()`` resource - - ``GuzzleHttp\Stream\StreamInterface`` - - ``GuzzleHttp\Post\PostBodyInterface`` -:Default: None - -This setting can be set to any of the following types: - -- string - - .. code-block:: php - - // You can send requests that use a string as the message body. - $client->put('/put', ['body' => 'foo']); - -- resource returned from ``fopen()`` - - .. code-block:: php - - // You can send requests that use a stream resource as the body. - $resource = fopen('http://httpbin.org', 'r'); - $client->put('/put', ['body' => $resource]); - -- Array - - Use an array to send POST style requests that use a - ``GuzzleHttp\Post\PostBodyInterface`` object as the body. - - .. code-block:: php - - // You can send requests that use a POST body containing fields & files. - $client->post('/post', [ - 'body' => [ - 'field' => 'abc', - 'other_field' => '123', - 'file_name' => fopen('/path/to/file', 'r') - ] - ]); - -- ``GuzzleHttp\Stream\StreamInterface`` - - .. code-block:: php - - // You can send requests that use a Guzzle stream object as the body - $stream = GuzzleHttp\Stream\Stream::factory('contents...'); - $client->post('/post', ['body' => $stream]); - -json ----- - -:Summary: The ``json`` option is used to easily upload JSON encoded data as the - body of a request. A Content-Type header of ``application/json`` will be - added if no Content-Type header is already present on the message. -:Types: - Any PHP type that can be operated on by PHP's ``json_encode()`` function. -:Default: None - -.. code-block:: php - - $request = $client->createRequest('PUT', '/put', ['json' => ['foo' => 'bar']]); - echo $request->getHeader('Content-Type'); - // application/json - echo $request->getBody(); - // {"foo":"bar"} - -.. note:: - - This request option does not support customizing the Content-Type header - or any of the options from PHP's `json_encode() <http://www.php.net/manual/en/function.json-encode.php>`_ - function. If you need to customize these settings, then you must pass the - JSON encoded data into the request yourself using the ``body`` request - option and you must specify the correct Content-Type header using the - ``headers`` request option. - -query ------ - -:Summary: Associative array of query string values to add to the request. -:Types: - - array - - ``GuzzleHttp\Query`` -:Default: None - -.. code-block:: php - - // Send a GET request to /get?foo=bar - $client->get('/get', ['query' => ['foo' => 'bar']]); - -Query strings specified in the ``query`` option are combined with any query -string values that are parsed from the URL. - -.. code-block:: php - - // Send a GET request to /get?abc=123&foo=bar - $client->get('/get?abc=123', ['query' => ['foo' => 'bar']]); - -auth ----- - -:Summary: Pass an array of HTTP authentication parameters to use with the - request. The array must contain the username in index [0], the password in - index [1], and you can optionally provide a built-in authentication type in - index [2]. Pass ``null`` to disable authentication for a request. -:Types: - - array - - string - - null -:Default: None - -The built-in authentication types are as follows: - -basic - Use `basic HTTP authentication <http://www.ietf.org/rfc/rfc2069.txt>`_ in - the ``Authorization`` header (the default setting used if none is - specified). - - .. code-block:: php - - $client->get('/get', ['auth' => ['username', 'password']]); - -digest - Use `digest authentication <http://www.ietf.org/rfc/rfc2069.txt>`_ (must be - supported by the HTTP handler). - - .. code-block:: php - - $client->get('/get', ['auth' => ['username', 'password', 'digest']]); - - *This is currently only supported when using the cURL handler, but creating - a replacement that can be used with any HTTP handler is planned.* - -.. important:: - - The authentication type (whether it's provided as a string or as the third - option in an array) is always converted to a lowercase string. Take this - into account when implementing custom authentication types and when - implementing custom message factories. - -Custom Authentication Schemes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can also provide a string representing a custom authentication type name. -When using a custom authentication type string, you will need to implement -the authentication method in an event listener that checks the ``auth`` request -option of a request before it is sent. Authentication listeners that require -a request is not modified after they are signed should have a very low priority -to ensure that they are fired last or near last in the event chain. - -.. code-block:: php - - use GuzzleHttp\Event\BeforeEvent; - use GuzzleHttp\Event\RequestEvents; - - /** - * Custom authentication listener that handles the "foo" auth type. - * - * Listens to the "before" event of a request and only modifies the request - * when the "auth" config setting of the request is "foo". - */ - class FooAuth implements GuzzleHttp\Event\SubscriberInterface - { - private $password; - - public function __construct($password) - { - $this->password = $password; - } - - public function getEvents() - { - return ['before' => ['sign', RequestEvents::SIGN_REQUEST]]; - } - - public function sign(BeforeEvent $e) - { - if ($e->getRequest()->getConfig()['auth'] == 'foo') { - $e->getRequest()->setHeader('X-Foo', 'Foo ' . $this->password); - } - } - } - - $client->getEmitter()->attach(new FooAuth('password')); - $client->get('/', ['auth' => 'foo']); - -Adapter Specific Authentication Schemes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you need to use authentication methods provided by cURL (e.g., NTLM, GSS, -etc.), then you need to specify a curl handler option in the ``options`` -request option array. See :ref:`config-option` for more information. - -.. _cookies-option: - -cookies -------- - -:Summary: Specifies whether or not cookies are used in a request or what cookie - jar to use or what cookies to send. -:Types: - - bool - - array - - ``GuzzleHttp\Cookie\CookieJarInterface`` -:Default: None - -Set to ``true`` to use a shared cookie session associated with the client. - -.. code-block:: php - - // Enable cookies using the shared cookie jar of the client. - $client->get('/get', ['cookies' => true]); - -Pass an associative array containing cookies to send in the request and start a -new cookie session. - -.. code-block:: php - - // Enable cookies and send specific cookies - $client->get('/get', ['cookies' => ['foo' => 'bar']]); - -Set to a ``GuzzleHttp\Cookie\CookieJarInterface`` object to use an existing -cookie jar. - -.. code-block:: php - - $jar = new GuzzleHttp\Cookie\CookieJar(); - $client->get('/get', ['cookies' => $jar]); - -.. _allow_redirects-option: - -allow_redirects ---------------- - -:Summary: Describes the redirect behavior of a request -:Types: - - bool - - array -:Default: - :: - - [ - 'max' => 5, - 'strict' => false, - 'referer' => true, - 'protocols' => ['http', 'https'] - ] - -Set to ``false`` to disable redirects. - -.. code-block:: php - - $res = $client->get('/redirect/3', ['allow_redirects' => false]); - echo $res->getStatusCode(); - // 302 - -Set to ``true`` (the default setting) to enable normal redirects with a maximum -number of 5 redirects. - -.. code-block:: php - - $res = $client->get('/redirect/3'); - echo $res->getStatusCode(); - // 200 - -Pass an associative array containing the 'max' key to specify the maximum -number of redirects, provide a 'strict' key value to specify whether or not to -use strict RFC compliant redirects (meaning redirect POST requests with POST -requests vs. doing what most browsers do which is redirect POST requests with -GET requests), provide a 'referer' key to specify whether or not the "Referer" -header should be added when redirecting, and provide a 'protocols' array that -specifies which protocols are supported for redirects (defaults to -``['http', 'https']``). - -.. code-block:: php - - $res = $client->get('/redirect/3', [ - 'allow_redirects' => [ - 'max' => 10, // allow at most 10 redirects. - 'strict' => true, // use "strict" RFC compliant redirects. - 'referer' => true, // add a Referer header - 'protocols' => ['https'] // only allow https URLs - ] - ]); - echo $res->getStatusCode(); - // 200 - -decode_content --------------- - -:Summary: Specify whether or not ``Content-Encoding`` responses (gzip, - deflate, etc.) are automatically decoded. -:Types: - - string - - bool -:Default: ``true`` - -This option can be used to control how content-encoded response bodies are -handled. By default, ``decode_content`` is set to true, meaning any gzipped -or deflated response will be decoded by Guzzle. - -When set to ``false``, the body of a response is never decoded, meaning the -bytes pass through the handler unchanged. - -.. code-block:: php - - // Request gzipped data, but do not decode it while downloading - $client->get('/foo.js', [ - 'headers' => ['Accept-Encoding' => 'gzip'], - 'decode_content' => false - ]); - -When set to a string, the bytes of a response are decoded and the string value -provided to the ``decode_content`` option is passed as the ``Accept-Encoding`` -header of the request. - -.. code-block:: php - - // Pass "gzip" as the Accept-Encoding header. - $client->get('/foo.js', ['decode_content' => 'gzip']); - -.. _save_to-option: - -save_to -------- - -:Summary: Specify where the body of a response will be saved. -:Types: - - string - - ``fopen()`` resource - - ``GuzzleHttp\Stream\StreamInterface`` -:Default: PHP temp stream - -Pass a string to specify the path to a file that will store the contents of the -response body: - -.. code-block:: php - - $client->get('/stream/20', ['save_to' => '/path/to/file']); - -Pass a resource returned from ``fopen()`` to write the response to a PHP stream: - -.. code-block:: php - - $resource = fopen('/path/to/file', 'w'); - $client->get('/stream/20', ['save_to' => $resource]); - -Pass a ``GuzzleHttp\Stream\StreamInterface`` object to stream the response body -to an open Guzzle stream: - -.. code-block:: php - - $resource = fopen('/path/to/file', 'w'); - $stream = GuzzleHttp\Stream\Stream::factory($resource); - $client->get('/stream/20', ['save_to' => $stream]); - -.. _events-option: - -events ------- - -:Summary: An associative array mapping event names to a callable. Or an - associative array containing the 'fn' key that maps to a callable, an - optional 'priority' key used to specify the event priority, and an optional - 'once' key used to specify if the event should remove itself the first time - it is triggered. -:Types: array -:Default: None - -.. code-block:: php - - use GuzzleHttp\Event\BeforeEvent; - use GuzzleHttp\Event\HeadersEvent; - use GuzzleHttp\Event\CompleteEvent; - use GuzzleHttp\Event\ErrorEvent; - - $client->get('/', [ - 'events' => [ - 'before' => function (BeforeEvent $e) { echo 'Before'; }, - 'complete' => function (CompleteEvent $e) { echo 'Complete'; }, - 'error' => function (ErrorEvent $e) { echo 'Error'; }, - ] - ]); - -Here's an example of using the associative array format for control over the -priority and whether or not an event should be triggered more than once. - -.. code-block:: php - - $client->get('/', [ - 'events' => [ - 'before' => [ - 'fn' => function (BeforeEvent $e) { echo 'Before'; }, - 'priority' => 100, - 'once' => true - ] - ] - ]); - -.. _subscribers-option: - -subscribers ------------ - -:Summary: Array of event subscribers to add to the request. Each value in the - array must be an instance of ``GuzzleHttp\Event\SubscriberInterface``. -:Types: array -:Default: None - -.. code-block:: php - - use GuzzleHttp\Subscriber\History; - use GuzzleHttp\Subscriber\Mock; - use GuzzleHttp\Message\Response; - - $history = new History(); - $mock = new Mock([new Response(200)]); - $client->get('/', ['subscribers' => [$history, $mock]]); - - echo $history; - // Outputs the request and response history - -.. _exceptions-option: - -exceptions ----------- - -:Summary: Set to ``false`` to disable throwing exceptions on an HTTP protocol - errors (i.e., 4xx and 5xx responses). Exceptions are thrown by default when - HTTP protocol errors are encountered. -:Types: bool -:Default: ``true`` - -.. code-block:: php - - $client->get('/status/500'); - // Throws a GuzzleHttp\Exception\ServerException - - $res = $client->get('/status/500', ['exceptions' => false]); - echo $res->getStatusCode(); - // 500 - -.. _timeout-option: - -timeout -------- - -:Summary: Float describing the timeout of the request in seconds. Use ``0`` - to wait indefinitely (the default behavior). -:Types: float -:Default: ``0`` - -.. code-block:: php - - // Timeout if a server does not return a response in 3.14 seconds. - $client->get('/delay/5', ['timeout' => 3.14]); - // PHP Fatal error: Uncaught exception 'GuzzleHttp\Exception\RequestException' - -.. _connect_timeout-option: - -connect_timeout ---------------- - -:Summary: Float describing the number of seconds to wait while trying to connect - to a server. Use ``0`` to wait indefinitely (the default behavior). -:Types: float -:Default: ``0`` - -.. code-block:: php - - // Timeout if the client fails to connect to the server in 3.14 seconds. - $client->get('/delay/5', ['connect_timeout' => 3.14]); - -.. note:: - - This setting must be supported by the HTTP handler used to send a request. - ``connect_timeout`` is currently only supported by the built-in cURL - handler. - -.. _verify-option: - -verify ------- - -:Summary: Describes the SSL certificate verification behavior of a request. - - - Set to ``true`` to enable SSL certificate verification and use the default - CA bundle provided by operating system. - - Set to ``false`` to disable certificate verification (this is insecure!). - - Set to a string to provide the path to a CA bundle to enable verification - using a custom certificate. -:Types: - - bool - - string -:Default: ``true`` - -.. code-block:: php - - // Use the system's CA bundle (this is the default setting) - $client->get('/', ['verify' => true]); - - // Use a custom SSL certificate on disk. - $client->get('/', ['verify' => '/path/to/cert.pem']); - - // Disable validation entirely (don't do this!). - $client->get('/', ['verify' => false]); - -Not all system's have a known CA bundle on disk. For example, Windows and -OS X do not have a single common location for CA bundles. When setting -"verify" to ``true``, Guzzle will do its best to find the most appropriate -CA bundle on your system. When using cURL or the PHP stream wrapper on PHP -versions >= 5.6, this happens by default. When using the PHP stream -wrapper on versions < 5.6, Guzzle tries to find your CA bundle in the -following order: - -1. Check if ``openssl.cafile`` is set in your php.ini file. -2. Check if ``curl.cainfo`` is set in your php.ini file. -3. Check if ``/etc/pki/tls/certs/ca-bundle.crt`` exists (Red Hat, CentOS, - Fedora; provided by the ca-certificates package) -4. Check if ``/etc/ssl/certs/ca-certificates.crt`` exists (Ubuntu, Debian; - provided by the ca-certificates package) -5. Check if ``/usr/local/share/certs/ca-root-nss.crt`` exists (FreeBSD; - provided by the ca_root_nss package) -6. Check if ``/usr/local/etc/openssl/cert.pem`` (OS X; provided by homebrew) -7. Check if ``C:\windows\system32\curl-ca-bundle.crt`` exists (Windows) -8. Check if ``C:\windows\curl-ca-bundle.crt`` exists (Windows) - -The result of this lookup is cached in memory so that subsequent calls -in the same process will return very quickly. However, when sending only -a single request per-process in something like Apache, you should consider -setting the ``openssl.cafile`` environment variable to the path on disk -to the file so that this entire process is skipped. - -If you do not need a specific certificate bundle, then Mozilla provides a -commonly used CA bundle which can be downloaded -`here <https://raw.githubusercontent.com/bagder/ca-bundle/master/ca-bundle.crt>`_ -(provided by the maintainer of cURL). Once you have a CA bundle available on -disk, you can set the "openssl.cafile" PHP ini setting to point to the path to -the file, allowing you to omit the "verify" request option. Much more detail on -SSL certificates can be found on the -`cURL website <http://curl.haxx.se/docs/sslcerts.html>`_. - -.. _cert-option: - -cert ----- - -:Summary: Set to a string to specify the path to a file containing a PEM - formatted client side certificate. If a password is required, then set to - an array containing the path to the PEM file in the first array element - followed by the password required for the certificate in the second array - element. -:Types: - - string - - array -:Default: None - -.. code-block:: php - - $client->get('/', ['cert' => ['/path/server.pem', 'password']]); - -.. _ssl_key-option: - -ssl_key -------- - -:Summary: Specify the path to a file containing a private SSL key in PEM - format. If a password is required, then set to an array containing the path - to the SSL key in the first array element followed by the password required - for the certificate in the second element. -:Types: - - string - - array -:Default: None - -.. note:: - - ``ssl_key`` is implemented by HTTP handlers. This is currently only - supported by the cURL handler, but might be supported by other third-part - handlers. - -.. _proxy-option: - -proxy ------ - -:Summary: Pass a string to specify an HTTP proxy, or an array to specify - different proxies for different protocols. -:Types: - - string - - array -:Default: None - -Pass a string to specify a proxy for all protocols. - -.. code-block:: php - - $client->get('/', ['proxy' => 'tcp://localhost:8125']); - -Pass an associative array to specify HTTP proxies for specific URI schemes -(i.e., "http", "https"). - -.. code-block:: php - - $client->get('/', [ - 'proxy' => [ - 'http' => 'tcp://localhost:8125', // Use this proxy with "http" - 'https' => 'tcp://localhost:9124' // Use this proxy with "https" - ] - ]); - -.. note:: - - You can provide proxy URLs that contain a scheme, username, and password. - For example, ``"http://username:password@192.168.16.1:10"``. - -.. _debug-option: - -debug ------ - -:Summary: Set to ``true`` or set to a PHP stream returned by ``fopen()`` to - enable debug output with the handler used to send a request. For example, - when using cURL to transfer requests, cURL's verbose of ``CURLOPT_VERBOSE`` - will be emitted. When using the PHP stream wrapper, stream wrapper - notifications will be emitted. If set to true, the output is written to - PHP's STDOUT. If a PHP stream is provided, output is written to the stream. -:Types: - - bool - - ``fopen()`` resource -:Default: None - -.. code-block:: php - - $client->get('/get', ['debug' => true]); - -Running the above example would output something like the following: - -:: - - * About to connect() to httpbin.org port 80 (#0) - * Trying 107.21.213.98... * Connected to httpbin.org (107.21.213.98) port 80 (#0) - > GET /get HTTP/1.1 - Host: httpbin.org - User-Agent: Guzzle/4.0 curl/7.21.4 PHP/5.5.7 - - < HTTP/1.1 200 OK - < Access-Control-Allow-Origin: * - < Content-Type: application/json - < Date: Sun, 16 Feb 2014 06:50:09 GMT - < Server: gunicorn/0.17.4 - < Content-Length: 335 - < Connection: keep-alive - < - * Connection #0 to host httpbin.org left intact - -.. _stream-option: - -stream ------- - -:Summary: Set to ``true`` to stream a response rather than download it all - up-front. -:Types: bool -:Default: ``false`` - -.. code-block:: php - - $response = $client->get('/stream/20', ['stream' => true]); - // Read bytes off of the stream until the end of the stream is reached - $body = $response->getBody(); - while (!$body->eof()) { - echo $body->read(1024); - } - -.. note:: - - Streaming response support must be implemented by the HTTP handler used by - a client. This option might not be supported by every HTTP handler, but the - interface of the response object remains the same regardless of whether or - not it is supported by the handler. - -.. _expect-option: - -expect ------- - -:Summary: Controls the behavior of the "Expect: 100-Continue" header. -:Types: - - bool - - integer -:Default: ``1048576`` - -Set to ``true`` to enable the "Expect: 100-Continue" header for all requests -that sends a body. Set to ``false`` to disable the "Expect: 100-Continue" -header for all requests. Set to a number so that the size of the payload must -be greater than the number in order to send the Expect header. Setting to a -number will send the Expect header for all requests in which the size of the -payload cannot be determined or where the body is not rewindable. - -By default, Guzzle will add the "Expect: 100-Continue" header when the size of -the body of a request is greater than 1 MB and a request is using HTTP/1.1. - -.. note:: - - This option only takes effect when using HTTP/1.1. The HTTP/1.0 and - HTTP/2.0 protocols do not support the "Expect: 100-Continue" header. - Support for handling the "Expect: 100-Continue" workflow must be - implemented by Guzzle HTTP handlers used by a client. - -.. _version-option: - -version -------- - -:Summary: Protocol version to use with the request. -:Types: string, float -:Default: ``1.1`` - -.. code-block:: php - - // Force HTTP/1.0 - $request = $client->createRequest('GET', '/get', ['version' => 1.0]); - echo $request->getProtocolVersion(); - // 1.0 - -.. _config-option: - -config ------- - -:Summary: Associative array of config options that are forwarded to a request's - configuration collection. These values are used as configuration options - that can be consumed by plugins and handlers. -:Types: array -:Default: None - -.. code-block:: php - - $request = $client->createRequest('GET', '/get', ['config' => ['foo' => 'bar']]); - echo $request->getConfig('foo'); - // 'bar' - -Some HTTP handlers allow you to specify custom handler-specific settings. For -example, you can pass custom cURL options to requests by passing an associative -array in the ``config`` request option under the ``curl`` key. - -.. code-block:: php - - // Use custom cURL options with the request. This example uses NTLM auth - // to authenticate with a server. - $client->get('/', [ - 'config' => [ - 'curl' => [ - CURLOPT_HTTPAUTH => CURLAUTH_NTLM, - CURLOPT_USERPWD => 'username:password' - ] - ] - ]); - -future ------- - -:Summary: Specifies whether or not a response SHOULD be an instance of a - ``GuzzleHttp\Message\FutureResponse`` object. -:Types: - - bool - - string -:Default: ``false`` - -By default, Guzzle requests should be synchronous. You can create asynchronous -future responses by passing the ``future`` request option as ``true``. The -response will only be executed when it is used like a normal response, the -``wait()`` method of the response is called, or the corresponding handler that -created the response is destructing and there are futures that have not been -resolved. - -.. important:: - - This option only has an effect if your handler can create and return future - responses. However, even if a response is completed synchronously, Guzzle - will ensure that a FutureResponse object is returned for API consistency. - -.. code-block:: php - - $response = $client->get('/foo', ['future' => true]) - ->then(function ($response) { - echo 'I got a response! ' . $response; - }); - -Event Subscribers -================= - -Requests emit lifecycle events when they are transferred. A client object has a -``GuzzleHttp\Common\EventEmitter`` object that can be used to add event -*listeners* and event *subscribers* to all requests created by the client. - -.. important:: - - **Every** event listener or subscriber added to a client will be added to - every request created by the client. - -.. code-block:: php - - use GuzzleHttp\Client; - use GuzzleHttp\Event\BeforeEvent; - - $client = new Client(); - - // Add a listener that will echo out requests before they are sent - $client->getEmitter()->on('before', function (BeforeEvent $e) { - echo 'About to send request: ' . $e->getRequest(); - }); - - $client->get('http://httpbin.org/get'); - // Outputs the request as a string because of the event - -See :doc:`events` for more information on the event system used in Guzzle. - -Environment Variables -===================== - -Guzzle exposes a few environment variables that can be used to customize the -behavior of the library. - -``GUZZLE_CURL_SELECT_TIMEOUT`` - Controls the duration in seconds that a curl_multi_* handler will use when - selecting on curl handles using ``curl_multi_select()``. Some systems - have issues with PHP's implementation of ``curl_multi_select()`` where - calling this function always results in waiting for the maximum duration of - the timeout. -``HTTP_PROXY`` - Defines the proxy to use when sending requests using the "http" protocol. -``HTTPS_PROXY`` - Defines the proxy to use when sending requests using the "https" protocol. - -Relevant ini Settings ---------------------- - -Guzzle can utilize PHP ini settings when configuring clients. - -``openssl.cafile`` - Specifies the path on disk to a CA file in PEM format to use when sending - requests over "https". See: https://wiki.php.net/rfc/tls-peer-verification#phpini_defaults diff --git a/core/vendor/guzzlehttp/guzzle/docs/conf.py b/core/vendor/guzzlehttp/guzzle/docs/conf.py deleted file mode 100644 index 917bdf4ca424889da423e7b272960a12701234e9..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/docs/conf.py +++ /dev/null @@ -1,28 +0,0 @@ -import sys, os -from sphinx.highlighting import lexers -from pygments.lexers.web import PhpLexer - - -lexers['php'] = PhpLexer(startinline=True, linenos=1) -lexers['php-annotations'] = PhpLexer(startinline=True, linenos=1) -primary_domain = 'php' - -extensions = [] -templates_path = ['_templates'] -source_suffix = '.rst' -master_doc = 'index' -project = u'Guzzle' -copyright = u'2014, Michael Dowling' -version = '5.0.0' -html_title = "Guzzle Documentation" -html_short_title = "Guzzle" - -exclude_patterns = ['_build'] -html_static_path = ['_static'] - -on_rtd = os.environ.get('READTHEDOCS', None) == 'True' - -if not on_rtd: # only import and set the theme if we're building docs locally - import sphinx_rtd_theme - html_theme = 'sphinx_rtd_theme' - html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] diff --git a/core/vendor/guzzlehttp/guzzle/docs/events.rst b/core/vendor/guzzlehttp/guzzle/docs/events.rst deleted file mode 100644 index 647dcd0f13b51daf46355e0ade40a902e7a1c4af..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/docs/events.rst +++ /dev/null @@ -1,520 +0,0 @@ -============ -Event System -============ - -Guzzle uses an event emitter to allow you to easily extend the behavior of a -request, change the response associated with a request, and implement custom -error handling. All events in Guzzle are managed and emitted by an -**event emitter**. - -Event Emitters -============== - -Clients, requests, and any other class that implements the -``GuzzleHttp\Event\HasEmitterInterface`` interface have a -``GuzzleHttp\Event\Emitter`` object. You can add event *listeners* and -event *subscribers* to an event *emitter*. - -emitter - An object that implements ``GuzzleHttp\Event\EmitterInterface``. This - object emits named events to event listeners. You may register event - listeners on subscribers on an emitter. - -event listeners - Callable functions that are registered on an event emitter for specific - events. Event listeners are registered on an emitter with a *priority* - setting. If no priority is provided, ``0`` is used by default. - -event subscribers - Classes that tell an event emitter what methods to listen to and what - functions on the class to invoke when the event is triggered. Event - subscribers subscribe event listeners to an event emitter. They should be - used when creating more complex event based logic in applications (i.e., - cookie handling is implemented using an event subscriber because it's - easier to share a subscriber than an anonymous function and because - handling cookies is a complex process). - -priority - Describes the order in which event listeners are invoked when an event is - emitted. The higher a priority value, the earlier the event listener will - be invoked (a higher priority means the listener is more important). If - no priority is provided, the priority is assumed to be ``0``. - - When specifying an event priority, you can pass ``"first"`` or ``"last"`` to - dynamically specify the priority based on the current event priorities - associated with the given event name in the emitter. Use ``"first"`` to set - the priority to the current highest priority plus one. Use ``"last"`` to - set the priority to the current lowest event priority minus one. It is - important to remember that these dynamic priorities are calculated only at - the point of insertion into the emitter and they are not rearranged after - subsequent listeners are added to an emitter. - -propagation - Describes whether or not other event listeners are triggered. Event - emitters will trigger every event listener registered to a specific event - in priority order until all of the listeners have been triggered **or** - until the propagation of an event is stopped. - -Getting an EventEmitter ------------------------ - -You can get the event emitter of ``GuzzleHttp\Event\HasEmitterInterface`` -object using the the ``getEmitter()`` method. Here's an example of getting a -client object's event emitter. - -.. code-block:: php - - $client = new GuzzleHttp\Client(); - $emitter = $client->getEmitter(); - -.. note:: - - You'll notice that the event emitter used in Guzzle is very similar to the - `Symfony2 EventDispatcher component <https://github.com/symfony/symfony/tree/master/src/Symfony/Component/EventDispatcher>`_. - This is because the Guzzle event system is based on the Symfony2 event - system with several changes. Guzzle uses its own event emitter to improve - performance, isolate Guzzle from changes to the Symfony, and provide a few - improvements that make it easier to use for an HTTP client (e.g., the - addition of the ``once()`` method). - -Adding Event Listeners ----------------------- - -After you have the emitter, you can register event listeners that listen to -specific events using the ``on()`` method. When registering an event listener, -you must tell the emitter what event to listen to (e.g., "before", "after", -"progress", "complete", "error", etc.), what callable to invoke when the -event is triggered, and optionally provide a priority. - -.. code-block:: php - - use GuzzleHttp\Event\BeforeEvent; - - $emitter->on('before', function (BeforeEvent $event) { - echo $event->getRequest(); - }); - -When a listener is triggered, it is passed an event that implements the -``GuzzleHttp\Event\EventInterface`` interface, the name of the event, and the -event emitter itself. The above example could more verbosely be written as -follows: - -.. code-block:: php - - use GuzzleHttp\Event\BeforeEvent; - - $emitter->on('before', function ( - BeforeEvent $event, - $name, - EmitterInterface $emitter - ) { - echo $event->getRequest(); - }); - -You can add an event listener that automatically removes itself after it is -triggered using the ``once()`` method of an event emitter. - -.. code-block:: php - - $client = new GuzzleHttp\Client(); - $client->getEmitter()->once('before', function () { - echo 'This will only happen once... per request!'; - }); - -Event Propagation ------------------ - -Event listeners can prevent other event listeners from being triggered by -stopping an event's propagation. - -Stopping event propagation can be useful, for example, if an event listener has -changed the state of the subject to such an extent that allowing subsequent -event listeners to be triggered could place the subject in an inconsistent -state. This technique is used in Guzzle extensively when intercepting error -events with responses. - -You can stop the propagation of an event using the ``stopPropagation()`` method -of a ``GuzzleHttp\Event\EventInterface`` object: - -.. code-block:: php - - use GuzzleHttp\Event\ErrorEvent; - - $emitter->on('error', function (ErrorEvent $event) { - $event->stopPropagation(); - }); - -After stopping the propagation of an event, any subsequent event listeners that -have not yet been triggered will not be triggered. You can check to see if the -propagation of an event was stopped using the ``isPropagationStopped()`` method -of the event. - -.. code-block:: php - - $client = new GuzzleHttp\Client(); - $emitter = $client->getEmitter(); - // Note: assume that the $errorEvent was created - if ($emitter->emit('error', $errorEvent)->isPropagationStopped()) { - echo 'It was stopped!'; - } - -.. hint:: - - When emitting events, the event that was emitted is returned from the - emitter. This allows you to easily chain calls as shown in the above - example. - -Event Subscribers ------------------ - -Event subscribers are classes that implement the -``GuzzleHttp\Event\SubscriberInterface`` object. They are used to register -one or more event listeners to methods of the class. Event subscribers tell -event emitters exactly which events to listen to and what method to invoke on -the class when the event is triggered by called the ``getEvents()`` method of -a subscriber. - -The following example registers event listeners to the ``before`` and -``complete`` event of a request. When the ``before`` event is emitted, the -``onBefore`` instance method of the subscriber is invoked. When the -``complete`` event is emitted, the ``onComplete`` event of the subscriber is -invoked. Each array value in the ``getEvents()`` return value MUST -contain the name of the method to invoke and can optionally contain the -priority of the listener (as shown in the ``before`` listener in the example). - -.. code-block:: php - - use GuzzleHttp\Event\EmitterInterface; - use GuzzleHttp\Event\SubscriberInterface; - use GuzzleHttp\Event\BeforeEvent; - use GuzzleHttp\Event\CompleteEvent; - - class SimpleSubscriber implements SubscriberInterface - { - public function getEvents() - { - return [ - // Provide name and optional priority - 'before' => ['onBefore', 100], - 'complete' => ['onComplete'], - // You can pass a list of listeners with different priorities - 'error' => [['beforeError', 'first'], ['afterError', 'last']] - ]; - } - - public function onBefore(BeforeEvent $event, $name) - { - echo 'Before!'; - } - - public function onComplete(CompleteEvent $event, $name) - { - echo 'Complete!'; - } - } - -To register the listeners the subscriber needs to be attached to the emitter: - -.. code-block:: php - - $client = new GuzzleHttp\Client(); - $emitter = $client->getEmitter(); - $subscriber = new SimpleSubscriber(); - $emitter->attach($subscriber); - - //to remove the listeners - $emitter->detach($subscriber); - -.. note:: - - You can specify event priorities using integers or ``"first"`` and - ``"last"`` to dynamically determine the priority. - -Event Priorities -================ - -When adding event listeners or subscribers, you can provide an optional event -priority. This priority is used to determine how early or late a listener is -triggered. Specifying the correct priority is an important aspect of ensuring -a listener behaves as expected. For example, if you wanted to ensure that -cookies associated with a redirect were added to a cookie jar, you'd need to -make sure that the listener that collects the cookies is triggered before the -listener that performs the redirect. - -In order to help make the process of determining the correct event priority of -a listener easier, Guzzle provides several pre-determined named event -priorities. These priorities are exposed as constants on the -``GuzzleHttp\Event\RequestEvents`` object. - -last - Use ``"last"`` as an event priority to set the priority to the current - lowest event priority minus one. - -first - Use ``"first"`` as an event priority to set the priority to the current - highest priority plus one. - -``GuzzleHttp\Event\RequestEvents::EARLY`` - Used when you want a listener to be triggered as early as possible in the - event chain. - -``GuzzleHttp\Event\RequestEvents::LATE`` - Used when you want a listener to be to be triggered as late as possible in - the event chain. - -``GuzzleHttp\Event\RequestEvents::PREPARE_REQUEST`` - Used when you want a listener to be trigger while a request is being - prepared during the ``before`` event. This event priority is used by the - ``GuzzleHttp\Subscriber\Prepare`` event subscriber which is responsible for - guessing a Content-Type, Content-Length, and Expect header of a request. - You should subscribe after this event is triggered if you want to ensure - that this subscriber has already been triggered. - -``GuzzleHttp\Event\RequestEvents::SIGN_REQUEST`` - Used when you want a listener to be triggered when a request is about to be - signed. Any listener triggered at this point should expect that the request - object will no longer be mutated. If you are implementing a custom - signature subscriber, then you should use this event priority to sign - requests. - -``GuzzleHttp\Event\RequestEvents::VERIFY_RESPONSE`` - Used when you want a listener to be triggered when a response is being - validated during the ``complete`` event. The - ``GuzzleHttp\Subscriber\HttpError`` event subscriber uses this event - priority to check if an exception should be thrown due to a 4xx or 5xx - level response status code. If you are doing any kind of verification of a - response during the complete event, it should happen at this priority. - -``GuzzleHttp\Event\RequestEvents::REDIRECT_RESPONSE`` - Used when you want a listener to be triggered when a response is being - redirected during the ``complete`` event. The - ``GuzzleHttp\Subscriber\Redirect`` event subscriber uses this event - priority when performing redirects. - -You can use the above event priorities as a guideline for determining the -priority of you event listeners. You can use these constants and add to or -subtract from them to ensure that a listener happens before or after the named -priority. - -.. note:: - - "first" and "last" priorities are not adjusted after they added to an - emitter. For example, if you add a listener with a priority of "first", - you can still add subsequent listeners with a higher priority which would - be triggered before the listener added with a priority of "first". - -Working With Request Events -=========================== - -Requests emit lifecycle events when they are transferred. - -.. important:: - - Excluding the ``end`` event, request lifecycle events may be triggered - multiple times due to redirects, retries, or reusing a request multiple - times. Use the ``once()`` method want the event to be triggered once. You - can also remove an event listener from an emitter by using the emitter which - is provided to the listener. - -.. _before_event: - -before ------- - -The ``before`` event is emitted before a request is sent. The event emitted is -a ``GuzzleHttp\Event\BeforeEvent``. - -.. code-block:: php - - use GuzzleHttp\Client; - use GuzzleHttp\Event\EmitterInterface; - use GuzzleHttp\Event\BeforeEvent; - - $client = new Client(['base_url' => 'http://httpbin.org']); - $request = $client->createRequest('GET', '/'); - $request->getEmitter()->on( - 'before', - function (BeforeEvent $e, $name, EmitterInterface $emitter) { - echo $name . "\n"; - // "before" - echo $e->getRequest()->getMethod() . "\n"; - // "GET" / "POST" / "PUT" / etc. - echo get_class($e->getClient()); - // "GuzzleHttp\Client" - } - ); - -You can intercept a request with a response before the request is sent over the -wire. The ``intercept()`` method of the ``BeforeEvent`` accepts a -``GuzzleHttp\Message\ResponseInterface``. Intercepting the event will prevent -the request from being sent over the wire and stops the propagation of the -``before`` event, preventing subsequent event listeners from being invoked. - -.. code-block:: php - - use GuzzleHttp\Client; - use GuzzleHttp\Event\BeforeEvent; - use GuzzleHttp\Message\Response; - - $client = new Client(['base_url' => 'http://httpbin.org']); - $request = $client->createRequest('GET', '/status/500'); - $request->getEmitter()->on('before', function (BeforeEvent $e) { - $response = new Response(200); - $e->intercept($response); - }); - - $response = $client->send($request); - echo $response->getStatusCode(); - // 200 - -.. attention:: - - Any exception encountered while executing the ``before`` event will trigger - the ``error`` event of a request. - -.. _complete_event: - -complete --------- - -The ``complete`` event is emitted after a transaction completes and an entire -response has been received. The event is a ``GuzzleHttp\Event\CompleteEvent``. - -You can intercept the ``complete`` event with a different response if needed -using the ``intercept()`` method of the event. This can be useful, for example, -for changing the response for caching. - -.. code-block:: php - - use GuzzleHttp\Client; - use GuzzleHttp\Event\CompleteEvent; - use GuzzleHttp\Message\Response; - - $client = new Client(['base_url' => 'http://httpbin.org']); - $request = $client->createRequest('GET', '/status/302'); - $cachedResponse = new Response(200); - - $request->getEmitter()->on( - 'complete', - function (CompleteEvent $e) use ($cachedResponse) { - if ($e->getResponse()->getStatusCode() == 302) { - // Intercept the original transaction with the new response - $e->intercept($cachedResponse); - } - } - ); - - $response = $client->send($request); - echo $response->getStatusCode(); - // 200 - -.. attention:: - - Any ``GuzzleHttp\Exception\RequestException`` encountered while executing - the ``complete`` event will trigger the ``error`` event of a request. - -.. _error_event: - -error ------ - -The ``error`` event is emitted when a request fails (whether it's from a -networking error or an HTTP protocol error). The event emitted is a -``GuzzleHttp\Event\ErrorEvent``. - -This event is useful for retrying failed requests. Here's an example of -retrying failed basic auth requests by re-sending the original request with -a username and password. - -.. code-block:: php - - use GuzzleHttp\Client; - use GuzzleHttp\Event\ErrorEvent; - - $client = new Client(['base_url' => 'http://httpbin.org']); - $request = $client->createRequest('GET', '/basic-auth/foo/bar'); - $request->getEmitter()->on('error', function (ErrorEvent $e) { - if ($e->getResponse()->getStatusCode() == 401) { - // Add authentication stuff as needed and retry the request - $e->getRequest()->setHeader('Authorization', 'Basic ' . base64_encode('foo:bar')); - // Get the client of the event and retry the request - $newResponse = $e->getClient()->send($e->getRequest()); - // Intercept the original transaction with the new response - $e->intercept($newResponse); - } - }); - -.. attention:: - - If an ``error`` event is intercepted with a response, then the ``complete`` - event of a request is triggered. If the ``complete`` event fails, then the - ``error`` event is triggered once again. - -.. _progress_event: - -progress --------- - -The ``progress`` event is emitted when data is uploaded or downloaded. The -event emitted is a ``GuzzleHttp\Event\ProgressEvent``. - -You can access the emitted progress values using the corresponding public -properties of the event object: - -- ``$downloadSize``: The number of bytes that will be downloaded (if known) -- ``$downloaded``: The number of bytes that have been downloaded -- ``$uploadSize``: The number of bytes that will be uploaded (if known) -- ``$uploaded``: The number of bytes that have been uploaded - -This event cannot be intercepted. - -.. code-block:: php - - use GuzzleHttp\Client; - use GuzzleHttp\Event\ProgressEvent; - - $client = new Client(['base_url' => 'http://httpbin.org']); - $request = $client->createRequest('PUT', '/put', [ - 'body' => str_repeat('.', 100000) - ]); - - $request->getEmitter()->on('progress', function (ProgressEvent $e) { - echo 'Downloaded ' . $e->downloaded . ' of ' . $e->downloadSize . ' ' - . 'Uploaded ' . $e->uploaded . ' of ' . $e->uploadSize . "\r"; - }); - - $client->send($request); - echo "\n"; - -.. _end_event: - -end ---- - -The ``end`` event is a terminal event, emitted once per request, that provides -access to the response that was received or the exception that was encountered. -The event emitted is a ``GuzzleHttp\Event\EndEvent``. - -This event can be intercepted, but keep in mind that the ``complete`` event -will not fire after intercepting this event. - -.. code-block:: php - - use GuzzleHttp\Client; - use GuzzleHttp\Event\EndEvent; - - $client = new Client(['base_url' => 'http://httpbin.org']); - $request = $client->createRequest('PUT', '/put', [ - 'body' => str_repeat('.', 100000) - ]); - - $request->getEmitter()->on('end', function (EndEvent $e) { - if ($e->getException()) { - echo 'Error: ' . $e->getException()->getMessage(); - } else { - echo 'Response: ' . $e->getResponse(); - } - }); - - $client->send($request); - echo "\n"; diff --git a/core/vendor/guzzlehttp/guzzle/docs/faq.rst b/core/vendor/guzzlehttp/guzzle/docs/faq.rst deleted file mode 100644 index a8e9ad060dc47d6dd0d445414de8d90d4c6423f1..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/docs/faq.rst +++ /dev/null @@ -1,199 +0,0 @@ -=== -FAQ -=== - -Why should I use Guzzle? -======================== - -Guzzle makes it easy to send HTTP requests and super simple to integrate with -web services. Guzzle manages things like persistent connections, represents -query strings as collections, makes it simple to send streaming POST requests -with fields and files, and abstracts away the underlying HTTP transport layer. -By providing an object oriented interface for HTTP clients, requests, responses, -headers, and message bodies, Guzzle makes it so that you no longer need to fool -around with cURL options, stream contexts, or sockets. - -**Asynchronous and Synchronous Requests** - -Guzzle allows you to send both asynchronous and synchronous requests using the -same interface and no direct dependency on an event loop. This flexibility -allows Guzzle to send an HTTP request using the most appropriate HTTP handler -based on the request being sent. For example, when sending synchronous -requests, Guzzle will by default send requests using cURL easy handles to -ensure you're using the fastest possible method for serially transferring HTTP -requests. When sending asynchronous requests, Guzzle might use cURL's multi -interface or any other asynchronous handler you configure. When you request -streaming data, Guzzle will by default use PHP's stream wrapper. - -**Streams** - -Request and response message bodies use :doc:`Guzzle Streams <streams>`, -allowing you to stream data without needing to load it all into memory. -Guzzle's stream layer provides a large suite of functionality: - -- You can modify streams at runtime using custom or a number of - pre-made decorators. -- You can emit progress events as data is read from a stream. -- You can validate the integrity of a stream using a rolling hash as data is - read from a stream. - -**Event System and Plugins** - -Guzzle's event system allows you to completely modify the behavior of a client -or request at runtime to cater them for any API. You can send a request with a -client, and the client can do things like automatically retry your request if -it fails, automatically redirect, log HTTP messages that are sent over the -wire, emit progress events as data is uploaded and downloaded, sign requests -using OAuth 1.0, verify the integrity of messages before and after they are -sent over the wire, and anything else you might need. - -**Testable** - -Another important aspect of Guzzle is that it's really -:doc:`easy to test clients <testing>`. You can mock HTTP responses and when -testing an handler implementation, Guzzle provides a mock node.js web server. - -**Ecosystem** - -Guzzle has a large `ecosystem of plugins <http://guzzle.readthedocs.org/en/latest/index.html#http-components>`_, -including `service descriptions <https://github.com/guzzle/guzzle-services>`_ -which allows you to abstract web services using service descriptions. These -service descriptions define how to serialize an HTTP request and how to parse -an HTTP response into a more meaningful model object. - -- `Guzzle Command <https://github.com/guzzle/command>`_: Provides the building - blocks for service description abstraction. -- `Guzzle Services <https://github.com/guzzle/guzzle-services>`_: Provides an - implementation of "Guzzle Command" that utilizes Guzzle's service description - format. - -Does Guzzle require cURL? -========================= - -No. Guzzle can use any HTTP handler to send requests. This means that Guzzle -can be used with cURL, PHP's stream wrapper, sockets, and non-blocking libraries -like `React <http://reactphp.org/>`_. You just need to configure a -`RingPHP <http://guzzle-ring.readthedocs.org/en/latest/>`_ handler to use a -different method of sending requests. - -.. note:: - - Guzzle has historically only utilized cURL to send HTTP requests. cURL is - an amazing HTTP client (arguably the best), and Guzzle will continue to use - it by default when it is available. It is rare, but some developers don't - have cURL installed on their systems or run into version specific issues. - By allowing swappable HTTP handlers, Guzzle is now much more customizable - and able to adapt to fit the needs of more developers. - -Can Guzzle send asynchronous requests? -====================================== - -Yes. Pass the ``future`` true request option to a request to send it -asynchronously. Guzzle will then return a ``GuzzleHttp\Message\FutureResponse`` -object that can be used synchronously by accessing the response object like a -normal response, and it can be used asynchronously using a promise that is -notified when the response is resolved with a real response or rejected with an -exception. - -.. code-block:: php - - $request = $client->createRequest('GET', ['future' => true]); - $client->send($request)->then(function ($response) { - echo 'Got a response! ' . $response; - }); - -You can force an asynchronous response to complete using the ``wait()`` method -of a response. - -.. code-block:: php - - $request = $client->createRequest('GET', ['future' => true]); - $futureResponse = $client->send($request); - $futureResponse->wait(); - -How can I add custom cURL options? -================================== - -cURL offer a huge number of `customizable options <http://us1.php.net/curl_setopt>`_. -While Guzzle normalizes many of these options across different handlers, there -are times when you need to set custom cURL options. This can be accomplished -by passing an associative array of cURL settings in the **curl** key of the -**config** request option. - -For example, let's say you need to customize the outgoing network interface -used with a client. - -.. code-block:: php - - $client->get('/', [ - 'config' => [ - 'curl' => [ - CURLOPT_INTERFACE => 'xxx.xxx.xxx.xxx' - ] - ] - ]); - -How can I add custom stream context options? -============================================ - -You can pass custom `stream context options <http://www.php.net/manual/en/context.php>`_ -using the **stream_context** key of the **config** request option. The -**stream_context** array is an associative array where each key is a PHP -transport, and each value is an associative array of transport options. - -For example, let's say you need to customize the outgoing network interface -used with a client and allow self-signed certificates. - -.. code-block:: php - - $client->get('/', [ - 'stream' => true, - 'config' => [ - 'stream_context' => [ - 'ssl' => [ - 'allow_self_signed' => true - ], - 'socket' => [ - 'bindto' => 'xxx.xxx.xxx.xxx' - ] - ] - ] - ]); - -Why am I getting an SSL verification error? -=========================================== - -You need to specify the path on disk to the CA bundle used by Guzzle for -verifying the peer certificate. See :ref:`verify-option`. - -What is this Maximum function nesting error? -============================================ - - Maximum function nesting level of '100' reached, aborting - -You could run into this error if you have the XDebug extension installed and -you execute a lot of requests in callbacks. This error message comes -specifically from the XDebug extension. PHP itself does not have a function -nesting limit. Change this setting in your php.ini to increase the limit:: - - xdebug.max_nesting_level = 1000 - -Why am I getting a 417 error response? -====================================== - -This can occur for a number of reasons, but if you are sending PUT, POST, or -PATCH requests with an ``Expect: 100-Continue`` header, a server that does not -support this header will return a 417 response. You can work around this by -setting the ``expect`` request option to ``false``: - -.. code-block:: php - - $client = new GuzzleHttp\Client(); - - // Disable the expect header on a single request - $response = $client->put('/', [], 'the body', [ - 'expect' => false - ]); - - // Disable the expect header on all client requests - $client->setDefaultOption('expect', false) diff --git a/core/vendor/guzzlehttp/guzzle/docs/handlers.rst b/core/vendor/guzzlehttp/guzzle/docs/handlers.rst deleted file mode 100644 index d452003fd859e2be2fc79f8853bcbb9af8a84993..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/docs/handlers.rst +++ /dev/null @@ -1,43 +0,0 @@ -================ -RingPHP Handlers -================ - -Guzzle uses RingPHP handlers to send HTTP requests over the wire. -RingPHP provides a low-level library that can be used to "glue" Guzzle with -any transport method you choose. By default, Guzzle utilizes cURL and PHP's -stream wrappers to send HTTP requests. - -RingPHP handlers makes it extremely simple to integrate Guzzle with any -HTTP transport. For example, you could quite easily bridge Guzzle and React -to use Guzzle in React's event loop. - -Using a handler ---------------- - -You can change the handler used by a client using the ``handler`` option in the -``GuzzleHttp\Client`` constructor. - -.. code-block:: php - - use GuzzleHttp\Client; - use GuzzleHttp\Ring\Client\MockHandler; - - // Create a mock handler that always returns a 200 response. - $handler = new MockHandler(['status' => 200]); - - // Configure to client to use the mock handler. - $client = new Client(['handler' => $handler]); - -At its core, handlers are simply PHP callables that accept a request array -and return a ``GuzzleHttp\Ring\Future\FutureArrayInterface``. This future array -can be used just like a normal PHP array, causing it to block, or you can use -the promise interface using the ``then()`` method of the future. Guzzle hooks -up to the RingPHP project using a very simple bridge class -(``GuzzleHttp\RingBridge``). - -Creating a handler ------------------- - -See the `RingPHP <http://ringphp.readthedocs.org>`_ project -documentation for more information on creating custom handlers that can be -used with Guzzle clients. diff --git a/core/vendor/guzzlehttp/guzzle/docs/http-messages.rst b/core/vendor/guzzlehttp/guzzle/docs/http-messages.rst deleted file mode 100644 index 0c6527a8d89fcb249acd6b8e17c2d6c157f89e3f..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/docs/http-messages.rst +++ /dev/null @@ -1,483 +0,0 @@ -============================= -Request and Response Messages -============================= - -Guzzle is an HTTP client that sends HTTP requests to a server and receives HTTP -responses. Both requests and responses are referred to as messages. - -Headers -======= - -Both request and response messages contain HTTP headers. - -Complex Headers ---------------- - -Some headers contain additional key value pair information. For example, Link -headers contain a link and several key value pairs: - -:: - - <http://foo.com>; rel="thing"; type="image/jpeg" - -Guzzle provides a convenience feature that can be used to parse these types of -headers: - -.. code-block:: php - - use GuzzleHttp\Message\Request; - - $request = new Request('GET', '/', [ - 'Link' => '<http:/.../front.jpeg>; rel="front"; type="image/jpeg"' - ]); - - $parsed = Request::parseHeader($request, 'Link'); - var_export($parsed); - -Will output: - -.. code-block:: php - - array ( - 0 => - array ( - 0 => '<http:/.../front.jpeg>', - 'rel' => 'front', - 'type' => 'image/jpeg', - ), - ) - -The result contains a hash of key value pairs. Header values that have no key -(i.e., the link) are indexed numerically while headers parts that form a key -value pair are added as a key value pair. - -See :ref:`headers` for information on how the headers of a request and response -can be accessed and modified. - -Body -==== - -Both request and response messages can contain a body. - -You can check to see if a request or response has a body using the -``getBody()`` method: - -.. code-block:: php - - $response = GuzzleHttp\get('http://httpbin.org/get'); - if ($response->getBody()) { - echo $response->getBody(); - // JSON string: { ... } - } - -The body used in request and response objects is a -``GuzzleHttp\Stream\StreamInterface``. This stream is used for both uploading -data and downloading data. Guzzle will, by default, store the body of a message -in a stream that uses PHP temp streams. When the size of the body exceeds -2 MB, the stream will automatically switch to storing data on disk rather than -in memory (protecting your application from memory exhaustion). - -You can change the body used in a request or response using the ``setBody()`` -method: - -.. code-block:: php - - use GuzzleHttp\Stream\Stream; - $request = $client->createRequest('PUT', 'http://httpbin.org/put'); - $request->setBody(Stream::factory('foo')); - -The easiest way to create a body for a request is using the static -``GuzzleHttp\Stream\Stream::factory()`` method. This method accepts various -inputs like strings, resources returned from ``fopen()``, and other -``GuzzleHttp\Stream\StreamInterface`` objects. - -The body of a request or response can be cast to a string or you can read and -write bytes off of the stream as needed. - -.. code-block:: php - - use GuzzleHttp\Stream\Stream; - $request = $client->createRequest('PUT', 'http://httpbin.org/put', ['body' => 'testing...']); - - echo $request->getBody()->read(4); - // test - echo $request->getBody()->read(4); - // ing. - echo $request->getBody()->read(1024); - // .. - var_export($request->eof()); - // true - -You can find out more about Guzzle stream objects in :doc:`streams`. - -Requests -======== - -Requests are sent from a client to a server. Requests include the method to -be applied to a resource, the identifier of the resource, and the protocol -version to use. - -Clients are used to create request messages. More precisely, clients use -a ``GuzzleHttp\Message\MessageFactoryInterface`` to create request messages. -You create requests with a client using the ``createRequest()`` method. - -.. code-block:: php - - // Create a request but don't send it immediately - $request = $client->createRequest('GET', 'http://httpbin.org/get'); - -Request Methods ---------------- - -When creating a request, you are expected to provide the HTTP method you wish -to perform. You can specify any method you'd like, including a custom method -that might not be part of RFC 7231 (like "MOVE"). - -.. code-block:: php - - // Create a request using a completely custom HTTP method - $request = $client->createRequest('MOVE', 'http://httpbin.org/move', ['exceptions' => false]); - - echo $request->getMethod(); - // MOVE - - $response = $client->send($request); - echo $response->getStatusCode(); - // 405 - -You can create and send a request using methods on a client that map to the -HTTP method you wish to use. - -:GET: ``$client->get('http://httpbin.org/get', [/** options **/])`` -:POST: ``$client->post('http://httpbin.org/post', [/** options **/])`` -:HEAD: ``$client->head('http://httpbin.org/get', [/** options **/])`` -:PUT: ``$client->put('http://httpbin.org/put', [/** options **/])`` -:DELETE: ``$client->delete('http://httpbin.org/delete', [/** options **/])`` -:OPTIONS: ``$client->options('http://httpbin.org/get', [/** options **/])`` -:PATCH: ``$client->patch('http://httpbin.org/put', [/** options **/])`` - -.. code-block:: php - - $response = $client->patch('http://httpbin.org/patch', ['body' => 'content']); - -Request URI ------------ - -The resource you are requesting with an HTTP request is identified by the -path of the request, the query string, and the "Host" header of the request. - -When creating a request, you can provide the entire resource URI as a URL. - -.. code-block:: php - - $response = $client->get('http://httbin.org/get?q=foo'); - -Using the above code, you will send a request that uses ``httpbin.org`` as -the Host header, sends the request over port 80, uses ``/get`` as the path, -and sends ``?q=foo`` as the query string. All of this is parsed automatically -from the provided URI. - -Sometimes you don't know what the entire request will be when it is created. -In these cases, you can modify the request as needed before sending it using -the ``createRequest()`` method of the client and methods on the request that -allow you to change it. - -.. code-block:: php - - $request = $client->createRequest('GET', 'http://httbin.org'); - -You can change the path of the request using ``setPath()``: - -.. code-block:: php - - $request->setPath('/get'); - echo $request->getPath(); - // /get - echo $request->getUrl(); - // http://httpbin.com/get - -Scheme -~~~~~~ - -The `scheme <http://tools.ietf.org/html/rfc3986#section-3.1>`_ of a request -specifies the protocol to use when sending the request. When using Guzzle, the -scheme can be set to "http" or "https". - -You can change the scheme of the request using the ``setScheme()`` method: - -.. code-block:: php - - $request = $client->createRequest('GET', 'http://httbin.org'); - $request->setScheme('https'); - echo $request->getScheme(); - // https - echo $request->getUrl(); - // https://httpbin.com/get - -Port -~~~~ - -No port is necessary when using the "http" or "https" schemes, but you can -override the port using ``setPort()``. If you need to modify the port used with -the specified scheme from the default setting, then you must use the -``setPort()`` method. - -.. code-block:: php - - $request = $client->createRequest('GET', 'http://httbin.org'); - $request->setPort(8080); - echo $request->getPort(); - // 8080 - echo $request->getUrl(); - // https://httpbin.com:8080/get - - // Set the port back to the default value for the scheme - $request->setPort(443); - echo $request->getUrl(); - // https://httpbin.com/get - -Query string -~~~~~~~~~~~~ - -You can get the query string of the request using the ``getQuery()`` method. -This method returns a ``GuzzleHttp\Query`` object. A Query object can be -accessed like a PHP array, iterated in a foreach statement like a PHP array, -and cast to a string. - -.. code-block:: php - - $request = $client->createRequest('GET', 'http://httbin.org'); - $query = $request->getQuery(); - $query['foo'] = 'bar'; - $query['baz'] = 'bam'; - $query['bam'] = ['test' => 'abc']; - - echo $request->getQuery(); - // foo=bar&baz=bam&bam%5Btest%5D=abc - - echo $request->getQuery()['foo']; - // bar - echo $request->getQuery()->get('foo'); - // bar - echo $request->getQuery()->get('foo'); - // bar - - var_export($request->getQuery()['bam']); - // array('test' => 'abc') - - foreach ($query as $key => $value) { - var_export($value); - } - - echo $request->getUrl(); - // https://httpbin.com/get?foo=bar&baz=bam&bam%5Btest%5D=abc - -Query Aggregators -^^^^^^^^^^^^^^^^^ - -Query objects can store scalar values or arrays of values. When an array of -values is added to a query object, the query object uses a query aggregator to -convert the complex structure into a string. Query objects will use -`PHP style query strings <http://www.php.net/http_build_query>`_ when complex -query string parameters are converted to a string. You can customize how -complex query string parameters are aggregated using the ``setAggregator()`` -method of a query string object. - -.. code-block:: php - - $query->setAggregator($query::duplicateAggregator()); - -In the above example, we've changed the query object to use the -"duplicateAggregator". This aggregator will allow duplicate entries to appear -in a query string rather than appending "[n]" to each value. So if you had a -query string with ``['a' => ['b', 'c']]``, the duplicate aggregator would -convert this to "a=b&a=c" while the default aggregator would convert this to -"a[0]=b&a[1]=c" (with urlencoded brackets). - -The ``setAggregator()`` method accepts a ``callable`` which is used to convert -a deeply nested array of query string variables into a flattened array of key -value pairs. The callable accepts an array of query data and returns a -flattened array of key value pairs where each value is an array of strings. -You can use the ``GuzzleHttp\Query::walkQuery()`` static function to easily -create custom query aggregators. - -Host -~~~~ - -You can change the host header of the request in a predictable way using the -``setHost()`` method of a request: - -.. code-block:: php - - $request->setHost('www.google.com'); - echo $request->getHost(); - // www.google.com - echo $request->getUrl(); - // https://www.google.com/get?foo=bar&baz=bam - -.. note:: - - The Host header can also be changed by modifying the Host header of a - request directly, but modifying the Host header directly could result in - sending a request to a different Host than what is specified in the Host - header (sometimes this is actually the desired behavior). - -Resource -~~~~~~~~ - -You can use the ``getResource()`` method of a request to return the path and -query string of a request in a single string. - -.. code-block:: php - - $request = $client->createRequest('GET', 'http://httpbin.org/get?baz=bar'); - echo $request->getResource(); - // /get?baz=bar - -Request Config --------------- - -Request messages contain a configuration collection that can be used by -event listeners and HTTP handlers to modify how a request behaves or is -transferred over the wire. For example, many of the request options that are -specified when creating a request are actually set as config options that are -only acted upon by handlers and listeners when the request is sent. - -You can get access to the request's config object using the ``getConfig()`` -method of a request. - -.. code-block:: php - - $request = $client->createRequest('GET', '/'); - $config = $request->getConfig(); - -The config object is a ``GuzzleHttp\Collection`` object that acts like -an associative array. You can grab values from the collection using array like -access. You can also modify and remove values using array like access. - -.. code-block:: php - - $config['foo'] = 'bar'; - echo $config['foo']; - // bar - - var_export(isset($config['foo'])); - // true - - unset($config['foo']); - var_export(isset($config['foo'])); - // false - - var_export($config['foo']); - // NULL - -HTTP handlers and event listeners can expose additional customization options -through request config settings. For example, in order to specify custom cURL -options to the cURL handler, you need to specify an associative array in the -``curl`` ``config`` request option. - -.. code-block:: php - - $client->get('/', [ - 'config' => [ - 'curl' => [ - CURLOPT_HTTPAUTH => CURLAUTH_NTLM, - CURLOPT_USERPWD => 'username:password' - ] - ] - ]); - -Consult the HTTP handlers and event listeners you are using to see if they -allow customization through request configuration options. - -Event Emitter -------------- - -Request objects implement ``GuzzleHttp\Event\HasEmitterInterface``, so they -have a method called ``getEmitter()`` that can be used to get an event emitter -used by the request. Any listener or subscriber attached to a request will only -be triggered for the lifecycle events of a specific request. Conversely, adding -an event listener or subscriber to a client will listen to all lifecycle events -of all requests created by the client. - -See :doc:`events` for more information. - -Responses -========= - -Responses are the HTTP messages a client receives from a server after sending -an HTTP request message. - -Start-Line ----------- - -The start-line of a response contains the protocol and protocol version, -status code, and reason phrase. - -.. code-block:: php - - $response = GuzzleHttp\get('http://httpbin.org/get'); - echo $response->getStatusCode(); - // 200 - echo $response->getReasonPhrase(); - // OK - echo $response->getProtocolVersion(); - // 1.1 - -Body ----- - -As described earlier, you can get the body of a response using the -``getBody()`` method. - -.. code-block:: php - - if ($body = $response->getBody()) { - echo $body; - // Cast to a string: { ... } - $body->seek(0); - // Rewind the body - $body->read(1024); - // Read bytes of the body - } - -When working with JSON responses, you can use the ``json()`` method of a -response: - -.. code-block:: php - - $json = $response->json(); - -.. note:: - - Guzzle uses the ``json_decode()`` method of PHP and uses arrays rather than - ``stdClass`` objects for objects. - -You can use the ``xml()`` method when working with XML data. - -.. code-block:: php - - $xml = $response->xml(); - -.. note:: - - Guzzle uses the ``SimpleXMLElement`` objects when converting response - bodies to XML. - -Effective URL -------------- - -The URL that was ultimately accessed that returned a response can be accessed -using the ``getEffectiveUrl()`` method of a response. This method will return -the URL of a request or the URL of the last redirected URL if any redirects -occurred while transferring a request. - -.. code-block:: php - - $response = GuzzleHttp\get('http://httpbin.org/get'); - echo $response->getEffectiveUrl(); - // http://httpbin.org/get - - $response = GuzzleHttp\get('http://httpbin.org/redirect-to?url=http://www.google.com'); - echo $response->getEffectiveUrl(); - // http://www.google.com diff --git a/core/vendor/guzzlehttp/guzzle/docs/index.rst b/core/vendor/guzzlehttp/guzzle/docs/index.rst deleted file mode 100644 index d456a5f08788af48ad6ef56d5bcb7872aedb1738..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/docs/index.rst +++ /dev/null @@ -1,98 +0,0 @@ -.. title:: Guzzle | PHP HTTP client and framework for consuming RESTful web services - -====== -Guzzle -====== - -Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and -trivial to integrate with web services. - -- Manages things like persistent connections, represents query strings as - collections, simplifies sending streaming POST requests with fields and - files, and abstracts away the underlying HTTP transport layer. -- Can send both synchronous and asynchronous requests using the same interface - without requiring a dependency on a specific event loop. -- Pluggable HTTP handlers allows Guzzle to integrate with any method you choose - for sending HTTP requests over the wire (e.g., cURL, sockets, PHP's stream - wrapper, non-blocking event loops like `React <http://reactphp.org/>`_, etc.). -- Guzzle makes it so that you no longer need to fool around with cURL options, - stream contexts, or sockets. - -.. code-block:: php - - $client = new GuzzleHttp\Client(); - $response = $client->get('http://guzzlephp.org'); - $res = $client->get('https://api.github.com/user', ['auth' => ['user', 'pass']]); - echo $res->getStatusCode(); - // "200" - echo $res->getHeader('content-type'); - // 'application/json; charset=utf8' - echo $res->getBody(); - // {"type":"User"...' - var_export($res->json()); - // Outputs the JSON decoded data - - // Send an asynchronous request. - $req = $client->createRequest('GET', 'http://httpbin.org', ['future' => true]); - $client->send($req)->then(function ($response) { - echo 'I completed! ' . $response; - }); - -User guide ----------- - -.. toctree:: - :maxdepth: 2 - - overview - quickstart - clients - http-messages - events - streams - handlers - testing - faq - -HTTP Components ---------------- - -There are a number of optional libraries you can use along with Guzzle's HTTP -layer to add capabilities to the client. - -`Log Subscriber <https://github.com/guzzle/log-subscriber>`_ - Logs HTTP requests and responses sent over the wire using customizable - log message templates. - -`OAuth Subscriber <https://github.com/guzzle/oauth-subscriber>`_ - Signs requests using OAuth 1.0. - -`Cache Subscriber <https://github.com/guzzle/cache-subscriber>`_ - Implements a private transparent proxy cache that caches HTTP responses. - -`Retry Subscriber <https://github.com/guzzle/retry-subscriber>`_ - Retries failed requests using customizable retry strategies (e.g., retry - based on response status code, cURL error codes, etc.) - -`Message Integrity Subscriber <https://github.com/guzzle/message-integrity-subscriber>`_ - Verifies the message integrity of HTTP responses using customizable - validators. This plugin can be used, for example, to verify the Content-MD5 - headers of responses. - -Service Description Commands ----------------------------- - -You can use the **Guzzle Command** library to encapsulate interaction with a -web service using command objects. Building on top of Guzzle's command -abstraction allows you to easily implement things like service description that -can be used to serialize requests and parse responses using a meta-description -of a web service. - -`Guzzle Command <https://github.com/guzzle/command>`_ - Provides the foundational elements used to build high-level, command based, - web service clients with Guzzle. - -`Guzzle Services <https://github.com/guzzle/guzzle-services>`_ - Provides an implementation of the *Guzzle Command* library that uses - Guzzle service descriptions to describe web services, serialize requests, - and parse responses into easy to use model structures. diff --git a/core/vendor/guzzlehttp/guzzle/docs/overview.rst b/core/vendor/guzzlehttp/guzzle/docs/overview.rst deleted file mode 100644 index 1355afa12275ed05249ba12be71d6993cfe03bb5..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/docs/overview.rst +++ /dev/null @@ -1,150 +0,0 @@ -======== -Overview -======== - -Requirements -============ - -#. PHP 5.4.0 -#. To use the PHP stream handler, ``allow_url_fopen`` must be enabled in your - system's php.ini. -#. To use the cURL handler, you must have a recent version of cURL >= 7.16.2 - compiled with OpenSSL and zlib. - -.. note:: - - Guzzle no longer requires cURL in order to send HTTP requests. Guzzle will - use the PHP stream wrapper to send HTTP requests if cURL is not installed. - Alternatively, you can provide your own HTTP handler used to send requests. - -.. _installation: - -Installation -============ - -The recommended way to install Guzzle is with `Composer <http://getcomposer.org>`_. Composer is a dependency -management tool for PHP that allows you to declare the dependencies your project needs and installs them into your -project. - -.. code-block:: bash - - # Install Composer - curl -sS https://getcomposer.org/installer | php - -You can add Guzzle as a dependency using the composer.phar CLI: - -.. code-block:: bash - - php composer.phar require guzzlehttp/guzzle:~5.0 - -Alternatively, you can specify Guzzle as a dependency in your project's -existing composer.json file: - -.. code-block:: js - - { - "require": { - "guzzlehttp/guzzle": "~5.0" - } - } - -After installing, you need to require Composer's autoloader: - -.. code-block:: php - - require 'vendor/autoload.php'; - -You can find out more on how to install Composer, configure autoloading, and -other best-practices for defining dependencies at `getcomposer.org <http://getcomposer.org>`_. - -Bleeding edge -------------- - -During your development, you can keep up with the latest changes on the master -branch by setting the version requirement for Guzzle to ``~5.0@dev``. - -.. code-block:: js - - { - "require": { - "guzzlehttp/guzzle": "~5.0@dev" - } - } - -License -======= - -Licensed using the `MIT license <http://opensource.org/licenses/MIT>`_. - - Copyright (c) 2014 Michael Dowling <https://github.com/mtdowling> - - 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. - -Contributing -============ - -Guidelines ----------- - -1. Guzzle follows PSR-0, PSR-1, and PSR-2. -2. Guzzle is meant to be lean and fast with very few dependencies. -3. Guzzle has a minimum PHP version requirement of PHP 5.4. Pull requests must - not require a PHP version greater than PHP 5.4. -4. All pull requests must include unit tests to ensure the change works as - expected and to prevent regressions. - -Running the tests ------------------ - -In order to contribute, you'll need to checkout the source from GitHub and -install Guzzle's dependencies using Composer: - -.. code-block:: bash - - git clone https://github.com/guzzle/guzzle.git - cd guzzle && curl -s http://getcomposer.org/installer | php && ./composer.phar install --dev - -Guzzle is unit tested with PHPUnit. Run the tests using the vendored PHPUnit -binary: - -.. code-block:: bash - - vendor/bin/phpunit - -.. note:: - - You'll need to install node.js v0.5.0 or newer in order to perform - integration tests on Guzzle's HTTP handlers. - -Reporting a security vulnerability -================================== - -We want to ensure that Guzzle is a secure HTTP client library for everyone. If -you've discovered a security vulnerability in Guzzle, we appreciate your help -in disclosing it to us in a `responsible manner <http://en.wikipedia.org/wiki/Responsible_disclosure>`_. - -Publicly disclosing a vulnerability can put the entire community at risk. If -you've discovered a security concern, please email us at -security@guzzlephp.org. We'll work with you to make sure that we understand the -scope of the issue, and that we fully address your concern. We consider -correspondence sent to security@guzzlephp.org our highest priority, and work to -address any issues that arise as quickly as possible. - -After a security vulnerability has been corrected, a security hotfix release will -be deployed as soon as possible. diff --git a/core/vendor/guzzlehttp/guzzle/docs/quickstart.rst b/core/vendor/guzzlehttp/guzzle/docs/quickstart.rst deleted file mode 100644 index 65a70ed230363ed160b76a60d8f91e3f48fcc1d8..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/docs/quickstart.rst +++ /dev/null @@ -1,448 +0,0 @@ -========== -Quickstart -========== - -This page provides a quick introduction to Guzzle and introductory examples. -If you have not already installed, Guzzle, head over to the :ref:`installation` -page. - -Make a Request -============== - -You can send requests with Guzzle using a ``GuzzleHttp\ClientInterface`` -object. - -Creating a Client ------------------ - -The procedural API is simple but not very testable; it's best left for quick -prototyping. If you want to use Guzzle in a more flexible and testable way, -then you'll need to use a ``GuzzleHttp\ClientInterface`` object. - -.. code-block:: php - - use GuzzleHttp\Client; - - $client = new Client(); - $response = $client->get('http://httpbin.org/get'); - - // You can use the same methods you saw in the procedural API - $response = $client->delete('http://httpbin.org/delete'); - $response = $client->head('http://httpbin.org/get'); - $response = $client->options('http://httpbin.org/get'); - $response = $client->patch('http://httpbin.org/patch'); - $response = $client->post('http://httpbin.org/post'); - $response = $client->put('http://httpbin.org/put'); - -You can create a request with a client and then send the request with the -client when you're ready. - -.. code-block:: php - - $request = $client->createRequest('GET', 'http://www.foo.com'); - $response = $client->send($request); - -Client objects provide a great deal of flexibility in how request are -transferred including default request options, subscribers that are attached -to each request, and a base URL that allows you to send requests with relative -URLs. You can find out all about clients in the :doc:`clients` page of the -documentation. - -Using Responses -=============== - -In the previous examples, we retrieved a ``$response`` variable. This value is -actually a ``GuzzleHttp\Message\ResponseInterface`` object and contains lots -of helpful information. - -You can get the status code and reason phrase of the response. - -.. code-block:: php - - $code = $response->getStatusCode(); - // 200 - - $reason = $response->getReasonPhrase(); - // OK - -By providing the ``future`` request option to a request, you can send requests -asynchronously using the promise interface of a future response. - -.. code-block:: php - - $client->get('http://httpbin.org', ['future' => true]) - ->then(function ($response) { - echo $response->getStatusCode(); - }); - -Response Body -------------- - -The body of a response can be retrieved and cast to a string. - -.. code-block:: php - - $body = $response->getBody(); - echo $body; - // { "some_json_data" ...} - -You can also read read bytes from body of a response like a stream. - -.. code-block:: php - - $body = $response->getBody(); - - while (!$body->eof()) { - echo $body->read(1024); - } - -JSON Responses -~~~~~~~~~~~~~~ - -You can more easily work with JSON responses using the ``json()`` method of a -response. - -.. code-block:: php - - $response = $client->get('http://httpbin.org/get'); - $json = $response->json(); - var_dump($json[0]['origin']); - -Guzzle internally uses PHP's ``json_decode()`` function to parse responses. If -Guzzle is unable to parse the JSON response body, then a -``GuzzleHttp\Exception\ParseException`` is thrown. - -XML Responses -~~~~~~~~~~~~~ - -You can use a response's ``xml()`` method to more easily work with responses -that contain XML data. - -.. code-block:: php - - $response = $client->get('https://github.com/mtdowling.atom'); - $xml = $response->xml(); - echo $xml->id; - // tag:github.com,2008:/mtdowling - -Guzzle internally uses a ``SimpleXMLElement`` object to parse responses. If -Guzzle is unable to parse the XML response body, then a -``GuzzleHttp\Exception\ParseException`` is thrown. - -Query String Parameters -======================= - -Sending query string parameters with a request is easy. You can set query -string parameters in the request's URL. - -.. code-block:: php - - $response = $client->get('http://httpbin.org?foo=bar'); - -You can also specify the query string parameters using the ``query`` request -option. - -.. code-block:: php - - $client->get('http://httpbin.org', [ - 'query' => ['foo' => 'bar'] - ]); - -And finally, you can build up the query string of a request as needed by -calling the ``getQuery()`` method of a request and modifying the request's -``GuzzleHttp\Query`` object as needed. - -.. code-block:: php - - $request = $client->createRequest('GET', 'http://httpbin.org'); - $query = $request->getQuery(); - $query->set('foo', 'bar'); - - // You can use the query string object like an array - $query['baz'] = 'bam'; - - // The query object can be cast to a string - echo $query; - // foo=bar&baz=bam - - // Setting a value to false or null will cause the "=" sign to be omitted - $query['empty'] = null; - echo $query; - // foo=bar&baz=bam&empty - - // Use an empty string to include the "=" sign with an empty value - $query['empty'] = ''; - echo $query; - // foo=bar&baz=bam&empty= - -.. _headers: - -Request and Response Headers ----------------------------- - -You can specify request headers when sending or creating requests with a -client. In the following example, we send the ``X-Foo-Header`` with a value of -``value`` by setting the ``headers`` request option. - -.. code-block:: php - - $response = $client->get('http://httpbin.org/get', [ - 'headers' => ['X-Foo-Header' => 'value'] - ]); - -You can view the headers of a response using header specific methods of a -response class. Headers work exactly the same way for request and response -object. - -You can retrieve a header from a request or response using the ``getHeader()`` -method of the object. This method is case-insensitive and by default will -return a string containing the header field value. - -.. code-block:: php - - $response = $client->get('http://www.yahoo.com'); - $length = $response->getHeader('Content-Length'); - -Header fields that contain multiple values can be retrieved as a string or as -an array. Retrieving the field values as a string will naively concatenate all -of the header values together with a comma. Because not all header fields -should be represented this way (e.g., ``Set-Cookie``), you can pass an optional -flag to the ``getHeader()`` method to retrieve the header values as an array. - -.. code-block:: php - - $values = $response->getHeader('Set-Cookie', true); - foreach ($values as $value) { - echo $value; - } - -You can test if a request or response has a specific header using the -``hasHeader()`` method. This method accepts a case-insensitive string and -returns true if the header is present or false if it is not. - -You can retrieve all of the headers of a message using the ``getHeaders()`` -method of a request or response. The return value is an associative array where -the keys represent the header name as it will be sent over the wire, and each -value is an array of strings associated with the header. - -.. code-block:: php - - $headers = $response->getHeaders(); - foreach ($message->getHeaders() as $name => $values) { - echo $name . ": " . implode(", ", $values); - } - -Modifying headers ------------------ - -The headers of a message can be modified using the ``setHeader()``, -``addHeader()``, ``setHeaders()``, and ``removeHeader()`` methods of a request -or response object. - -.. code-block:: php - - $request = $client->createRequest('GET', 'http://httpbin.org/get'); - - // Set a single value for a header - $request->setHeader('User-Agent', 'Testing!'); - - // Set multiple values for a header in one call - $request->setHeader('X-Foo', ['Baz', 'Bar']); - - // Add a header to the message - $request->addHeader('X-Foo', 'Bam'); - - echo $request->getHeader('X-Foo'); - // Baz, Bar, Bam - - // Remove a specific header using a case-insensitive name - $request->removeHeader('x-foo'); - echo $request->getHeader('X-Foo'); - // Echoes an empty string: '' - -Uploading Data -============== - -Guzzle provides several methods of uploading data. - -You can send requests that contain a stream of data by passing a string, -resource returned from ``fopen``, or a ``GuzzleHttp\Stream\StreamInterface`` -object to the ``body`` request option. - -.. code-block:: php - - $r = $client->post('http://httpbin.org/post', ['body' => 'raw data']); - -You can easily upload JSON data using the ``json`` request option. - -.. code-block:: php - - $r = $client->put('http://httpbin.org/put', ['json' => ['foo' => 'bar']]); - -POST Requests -------------- - -In addition to specifying the raw data of a request using the ``body`` request -option, Guzzle provides helpful abstractions over sending POST data. - -Sending POST Fields -~~~~~~~~~~~~~~~~~~~ - -Sending ``application/x-www-form-urlencoded`` POST requests requires that you -specify the body of a POST request as an array. - -.. code-block:: php - - $response = $client->post('http://httpbin.org/post', [ - 'body' => [ - 'field_name' => 'abc', - 'other_field' => '123' - ] - ]); - -You can also build up POST requests before sending them. - -.. code-block:: php - - $request = $client->createRequest('POST', 'http://httpbin.org/post'); - $postBody = $request->getBody(); - - // $postBody is an instance of GuzzleHttp\Post\PostBodyInterface - $postBody->setField('foo', 'bar'); - echo $postBody->getField('foo'); - // 'bar' - - echo json_encode($postBody->getFields()); - // {"foo": "bar"} - - // Send the POST request - $response = $client->send($request); - -Sending POST Files -~~~~~~~~~~~~~~~~~~ - -Sending ``multipart/form-data`` POST requests (POST requests that contain -files) is the same as sending ``application/x-www-form-urlencoded``, except -some of the array values of the POST fields map to PHP ``fopen`` resources, or -``GuzzleHttp\Stream\StreamInterface``, or -``GuzzleHttp\Post\PostFileInterface`` objects. - -.. code-block:: php - - use GuzzleHttp\Post\PostFile; - - $response = $client->post('http://httpbin.org/post', [ - 'body' => [ - 'field_name' => 'abc', - 'file_filed' => fopen('/path/to/file', 'r'), - 'other_file' => new PostFile('other_file', 'this is the content') - ] - ]); - -Just like when sending POST fields, you can also build up POST requests with -files before sending them. - -.. code-block:: php - - use GuzzleHttp\Post\PostFile; - - $request = $client->createRequest('POST', 'http://httpbin.org/post'); - $postBody = $request->getBody(); - $postBody->setField('foo', 'bar'); - $postBody->addFile(new PostFile('test', fopen('/path/to/file', 'r'))); - $response = $client->send($request); - -Cookies -======= - -Guzzle can maintain a cookie session for you if instructed using the -``cookies`` request option. - -- Set to ``true`` to use a shared cookie session associated with the client. -- Pass an associative array containing cookies to send in the request and start - a new cookie session. -- Set to a ``GuzzleHttp\Subscriber\CookieJar\CookieJarInterface`` object to use - an existing cookie jar. - -Redirects -========= - -Guzzle will automatically follow redirects unless you tell it not to. You can -customize the redirect behavior using the ``allow_redirects`` request option. - -- Set to true to enable normal redirects with a maximum number of 5 redirects. - This is the default setting. -- Set to false to disable redirects. -- Pass an associative array containing the 'max' key to specify the maximum - number of redirects and optionally provide a 'strict' key value to specify - whether or not to use strict RFC compliant redirects (meaning redirect POST - requests with POST requests vs. doing what most browsers do which is - redirect POST requests with GET requests). - -.. code-block:: php - - $response = $client->get('http://github.com'); - echo $response->getStatusCode(); - // 200 - echo $response->getEffectiveUrl(); - // 'https://github.com/' - -The following example shows that redirects can be disabled. - -.. code-block:: php - - $response = $client->get('http://github.com', ['allow_redirects' => false]); - echo $response->getStatusCode(); - // 301 - echo $response->getEffectiveUrl(); - // 'http://github.com/' - -Exceptions -========== - -Guzzle throws exceptions for errors that occur during a transfer. - -- In the event of a networking error (connection timeout, DNS errors, etc.), - a ``GuzzleHttp\Exception\RequestException`` is thrown. This exception - extends from ``GuzzleHttp\Exception\TransferException``. Catching this - exception will catch any exception that can be thrown while transferring - (non-parallel) requests. - - .. code-block:: php - - use GuzzleHttp\Exception\RequestException; - - try { - $client->get('https://github.com/_abc_123_404'); - } catch (RequestException $e) { - echo $e->getRequest(); - if ($e->hasResponse()) { - echo $e->getResponse(); - } - } - -- A ``GuzzleHttp\Exception\ClientException`` is thrown for 400 - level errors if the ``exceptions`` request option is set to true. This - exception extends from ``GuzzleHttp\Exception\BadResponseException`` and - ``GuzzleHttp\Exception\BadResponseException`` extends from - ``GuzzleHttp\Exception\RequestException``. - - .. code-block:: php - - use GuzzleHttp\Exception\ClientException; - - try { - $client->get('https://github.com/_abc_123_404'); - } catch (ClientException $e) { - echo $e->getRequest(); - echo $e->getResponse(); - } - -- A ``GuzzleHttp\Exception\ServerException`` is thrown for 500 level - errors if the ``exceptions`` request option is set to true. This - exception extends from ``GuzzleHttp\Exception\BadResponseException``. -- A ``GuzzleHttp\Exception\TooManyRedirectsException`` is thrown when too - many redirects are followed. This exception extends from ``GuzzleHttp\Exception\RequestException``. - -All of the above exceptions extend from -``GuzzleHttp\Exception\TransferException``. diff --git a/core/vendor/guzzlehttp/guzzle/docs/requirements.txt b/core/vendor/guzzlehttp/guzzle/docs/requirements.txt deleted file mode 100644 index fe7a4eab46595174330774b6bd97d8e5dd964ef7..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/docs/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -Sphinx>=1.2b1 -guzzle_sphinx_theme>=0.6.0 diff --git a/core/vendor/guzzlehttp/guzzle/docs/streams.rst b/core/vendor/guzzlehttp/guzzle/docs/streams.rst deleted file mode 100644 index 8fe9a6984c6b516495e1b0fc611ea0c4a44b67c5..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/docs/streams.rst +++ /dev/null @@ -1,213 +0,0 @@ -======= -Streams -======= - -Guzzle uses stream objects to represent request and response message bodies. -These stream objects allow you to work with various types of data all using a -common interface. - -HTTP messages consist of a start-line, headers, and a body. The body of an HTTP -message can be very small or extremely large. Attempting to represent the body -of a message as a string can easily consume more memory than intended because -the body must be stored completely in memory. Attempting to store the body of a -request or response in memory would preclude the use of that implementation from -being able to work with large message bodies. The StreamInterface is used in -order to hide the implementation details of where a stream of data is read from -or written to. - -Guzzle's StreamInterface exposes several methods that enable streams to be read -from, written to, and traversed effectively. - -Streams expose their capabilities using three methods: ``isReadable()``, -``isWritable()``, and ``isSeekable()``. These methods can be used by stream -collaborators to determine if a stream is capable of their requirements. - -Each stream instance has various capabilities: they can be read-only, -write-only, read-write, allow arbitrary random access (seeking forwards or -backwards to any location), or only allow sequential access (for example in the -case of a socket or pipe). - -Creating Streams -================ - -The best way to create a stream is using the static factory method, -``GuzzleHttp\Stream\Stream::factory()``. This factory accepts strings, -resources returned from ``fopen()``, an object that implements -``__toString()``, and an object that implements -``GuzzleHttp\Stream\StreamInterface``. - -.. code-block:: php - - use GuzzleHttp\Stream\Stream; - - $stream = Stream::factory('string data'); - echo $stream; - // string data - echo $stream->read(3); - // str - echo $stream->getContents(); - // ing data - var_export($stream->eof()); - // true - var_export($stream->tell()); - // 11 - -Metadata -======== - -Guzzle streams expose stream metadata through the ``getMetadata()`` method. -This method provides the data you would retrieve when calling PHP's -`stream_get_meta_data() function <http://php.net/manual/en/function.stream-get-meta-data.php>`_, -and can optionally expose other custom data. - -.. code-block:: php - - use GuzzleHttp\Stream\Stream; - - $resource = fopen('/path/to/file', 'r'); - $stream = Stream::factory($resource); - echo $stream->getMetadata('uri'); - // /path/to/file - var_export($stream->isReadable()); - // true - var_export($stream->isWritable()); - // false - var_export($stream->isSeekable()); - // true - -Stream Decorators -================= - -With the small and focused interface, add custom functionality to streams is -very simple with stream decorators. Guzzle provides several built-in decorators -that provide additional stream functionality. - -CachingStream -------------- - -The CachingStream is used to allow seeking over previously read bytes on -non-seekable streams. This can be useful when transferring a non-seekable -entity body fails due to needing to rewind the stream (for example, resulting -from a redirect). Data that is read from the remote stream will be buffered in -a PHP temp stream so that previously read bytes are cached first in memory, -then on disk. - -.. code-block:: php - - use GuzzleHttp\Stream\Stream; - use GuzzleHttp\Stream\CachingStream; - - $original = Stream::factory(fopen('http://www.google.com', 'r')); - $stream = new CachingStream($original); - - $stream->read(1024); - echo $stream->tell(); - // 1024 - - $stream->seek(0); - echo $stream->tell(); - // 0 - -LimitStream ------------ - -LimitStream can be used to read a subset or slice of an existing stream object. -This can be useful for breaking a large file into smaller pieces to be sent in -chunks (e.g. Amazon S3's multipart upload API). - -.. code-block:: php - - use GuzzleHttp\Stream\Stream; - use GuzzleHttp\Stream\LimitStream; - - $original = Stream::factory(fopen('/tmp/test.txt', 'r+')); - echo $original->getSize(); - // >>> 1048576 - - // Limit the size of the body to 1024 bytes and start reading from byte 2048 - $stream = new LimitStream($original, 1024, 2048); - echo $stream->getSize(); - // >>> 1024 - echo $stream->tell(); - // >>> 0 - -NoSeekStream ------------- - -NoSeekStream wraps a stream and does not allow seeking. - -.. code-block:: php - - use GuzzleHttp\Stream\Stream; - use GuzzleHttp\Stream\LimitStream; - - $original = Stream::factory('foo'); - $noSeek = new NoSeekStream($original); - - echo $noSeek->read(3); - // foo - var_export($noSeek->isSeekable()); - // false - $noSeek->seek(0); - var_export($noSeek->read(3)); - // NULL - -Creating Custom Decorators --------------------------- - -Creating a stream decorator is very easy thanks to the -``GuzzleHttp\Stream\StreamDecoratorTrait``. This trait provides methods that -implement ``GuzzleHttp\Stream\StreamInterface`` by proxying to an underlying -stream. Just ``use`` the ``StreamDecoratorTrait`` and implement your custom -methods. - -For example, let's say we wanted to call a specific function each time the last -byte is read from a stream. This could be implemented by overriding the -``read()`` method. - -.. code-block:: php - - use GuzzleHttp\Stream\StreamDecoratorTrait; - - class EofCallbackStream implements StreamInterface - { - use StreamDecoratorTrait; - - private $callback; - - public function __construct(StreamInterface $stream, callable $callback) - { - $this->stream = $stream; - $this->callback = $callback; - } - - public function read($length) - { - $result = $this->stream->read($length); - - // Invoke the callback when EOF is hit. - if ($this->eof()) { - call_user_func($this->callback); - } - - return $result; - } - } - -This decorator could be added to any existing stream and used like so: - -.. code-block:: php - - use GuzzleHttp\Stream\Stream; - - $original = Stream::factory('foo'); - $eofStream = new EofCallbackStream($original, function () { - echo 'EOF!'; - }); - - $eofStream->read(2); - $eofStream->read(1); - // echoes "EOF!" - $eofStream->seek(0); - $eofStream->read(3); - // echoes "EOF!" diff --git a/core/vendor/guzzlehttp/guzzle/docs/testing.rst b/core/vendor/guzzlehttp/guzzle/docs/testing.rst deleted file mode 100644 index e7098be45551553f4319c1a2f13d6a76a4921ee9..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/docs/testing.rst +++ /dev/null @@ -1,232 +0,0 @@ -====================== -Testing Guzzle Clients -====================== - -Guzzle provides several tools that will enable you to easily mock the HTTP -layer without needing to send requests over the internet. - -* Mock subscriber -* Mock handler -* Node.js web server for integration testing - -Mock Subscriber -=============== - -When testing HTTP clients, you often need to simulate specific scenarios like -returning a successful response, returning an error, or returning specific -responses in a certain order. Because unit tests need to be predictable, easy -to bootstrap, and fast, hitting an actual remote API is a test smell. - -Guzzle provides a mock subscriber that can be attached to clients or requests -that allows you to queue up a list of responses to use rather than hitting a -remote API. - -.. code-block:: php - - use GuzzleHttp\Client; - use GuzzleHttp\Subscriber\Mock; - use GuzzleHttp\Message\Response; - - $client = new Client(); - - // Create a mock subscriber and queue two responses. - $mock = new Mock([ - new Response(200, ['X-Foo' => 'Bar']), // Use response object - "HTTP/1.1 202 OK\r\nContent-Length: 0\r\n\r\n" // Use a response string - ]); - - // Add the mock subscriber to the client. - $client->getEmitter()->attach($mock); - // The first request is intercepted with the first response. - echo $client->get('/')->getStatusCode(); - //> 200 - // The second request is intercepted with the second response. - echo $client->get('/')->getStatusCode(); - //> 202 - -When no more responses are in the queue and a request is sent, an -``OutOfBoundsException`` is thrown. - -History Subscriber -================== - -When using things like the ``Mock`` subscriber, you often need to know if the -requests you expected to send were sent exactly as you intended. While the mock -subscriber responds with mocked responses, the ``GuzzleHttp\Subscriber\History`` -subscriber maintains a history of the requests that were sent by a client. - -.. code-block:: php - - use GuzzleHttp\Client; - use GuzzleHttp\Subscriber\History; - - $client = new Client(); - $history = new History(); - - // Add the history subscriber to the client. - $client->getEmitter()->attach($history); - - $client->get('http://httpbin.org/get'); - $client->head('http://httpbin.org/get'); - - // Count the number of transactions - echo count($history); - //> 2 - // Get the last request - $lastRequest = $history->getLastRequest(); - // Get the last response - $lastRequest = $history->getLastResponse(); - - // Iterate over the transactions that were sent - foreach ($history as $transaction) { - echo $transaction['request']->getMethod(); - //> GET, HEAD - echo $transaction['response']->getStatusCode(); - //> 200, 200 - } - -The history subscriber can also be printed, revealing the requests and -responses that were sent as a string, in order. - -.. code-block:: php - - echo $history; - -:: - - > GET /get HTTP/1.1 - Host: httpbin.org - User-Agent: Guzzle/4.0-dev curl/7.21.4 PHP/5.5.8 - - < HTTP/1.1 200 OK - Access-Control-Allow-Origin: * - Content-Type: application/json - Date: Tue, 25 Mar 2014 03:53:27 GMT - Server: gunicorn/0.17.4 - Content-Length: 270 - Connection: keep-alive - - { - "headers": { - "Connection": "close", - "X-Request-Id": "3d0f7d5c-c937-4394-8248-2b8e03fcccdb", - "User-Agent": "Guzzle/4.0-dev curl/7.21.4 PHP/5.5.8", - "Host": "httpbin.org" - }, - "origin": "76.104.247.1", - "args": {}, - "url": "http://httpbin.org/get" - } - - > HEAD /get HTTP/1.1 - Host: httpbin.org - User-Agent: Guzzle/4.0-dev curl/7.21.4 PHP/5.5.8 - - < HTTP/1.1 200 OK - Access-Control-Allow-Origin: * - Content-length: 270 - Content-Type: application/json - Date: Tue, 25 Mar 2014 03:53:27 GMT - Server: gunicorn/0.17.4 - Connection: keep-alive - -Mock Adapter -============ - -In addition to using the Mock subscriber, you can use the -``GuzzleHttp\Ring\Client\MockAdapter`` as the handler of a client to return the -same response over and over or return the result of a callable function. - -Test Web Server -=============== - -Using mock responses is almost always enough when testing a web service client. -When implementing custom :doc:`HTTP handlers <handlers>`, you'll need to send -actual HTTP requests in order to sufficiently test the handler. However, a -best practice is to contact a local web server rather than a server over the -internet. - -- Tests are more reliable -- Tests do not require a network connection -- Tests have no external dependencies - -Using the test server ---------------------- - -.. warning:: - - The following functionality is provided to help developers of Guzzle - develop HTTP handlers. There is no promise of backwards compatibility - when it comes to the node.js test server or the ``GuzzleHttp\Tests\Server`` - class. If you are using the test server or ``Server`` class outside of - guzzlehttp/guzzle, then you will need to configure autoloading and - ensure the web server is started manually. - -.. hint:: - - You almost never need to use this test web server. You should only ever - consider using it when developing HTTP handlers. The test web server - is not necessary for mocking requests. For that, please use the - Mock subcribers and History subscriber. - -Guzzle ships with a node.js test server that receives requests and returns -responses from a queue. The test server exposes a simple API that is used to -enqueue responses and inspect the requests that it has received. - -Any operation on the ``Server`` object will ensure that -the server is running and wait until it is able to receive requests before -returning. - -.. code-block:: php - - use GuzzleHttp\Client; - use GuzzleHttp\Tests\Server; - - // Start the server and queue a response - Server::enqueue("HTTP/1.1 200 OK\r\n\Content-Length: 0r\n\r\n"); - - $client = new Client(['base_url' => Server::$url]); - echo $client->get('/foo')->getStatusCode(); - // 200 - -``GuzzleHttp\Tests\Server`` provides a static interface to the test server. You -can queue an HTTP response or an array of responses by calling -``Server::enqueue()``. This method accepts a string representing an HTTP -response message, a ``GuzzleHttp\Message\ResponseInterface``, or an array of -HTTP message strings / ``GuzzleHttp\Message\ResponseInterface`` objects. - -.. code-block:: php - - // Queue single response - Server::enqueue("HTTP/1.1 200 OK\r\n\Content-Length: 0r\n\r\n"); - - // Clear the queue and queue an array of responses - Server::enqueue([ - "HTTP/1.1 200 OK\r\n\Content-Length: 0r\n\r\n", - "HTTP/1.1 404 Not Found\r\n\Content-Length: 0r\n\r\n" - ]); - -When a response is queued on the test server, the test server will remove any -previously queued responses. As the server receives requests, queued responses -are dequeued and returned to the request. When the queue is empty, the server -will return a 500 response. - -You can inspect the requests that the server has retrieved by calling -``Server::received()``. This method accepts an optional ``$hydrate`` parameter -that specifies if you are retrieving an array of HTTP requests as strings or an -array of ``GuzzleHttp\Message\RequestInterface`` objects. - -.. code-block:: php - - foreach (Server::received() as $response) { - echo $response; - } - -You can clear the list of received requests from the web server using the -``Server::flush()`` method. - -.. code-block:: php - - Server::flush(); - echo count(Server::received()); - // 0 diff --git a/core/vendor/guzzlehttp/guzzle/src/BatchResults.php b/core/vendor/guzzlehttp/guzzle/src/BatchResults.php deleted file mode 100644 index e5af433ddf18b34a4c30c281a262b4a9dd030988..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/BatchResults.php +++ /dev/null @@ -1,148 +0,0 @@ -<?php -namespace GuzzleHttp; - -/** - * Represents the result of a batch operation. This result container is - * iterable, countable, and you can can get a result by value using the - * getResult function. - * - * Successful results are anything other than exceptions. Failure results are - * exceptions. - * - * @package GuzzleHttp - */ -class BatchResults implements \Countable, \IteratorAggregate, \ArrayAccess -{ - private $hash; - - /** - * @param \SplObjectStorage $hash Hash of key objects to result values. - */ - public function __construct(\SplObjectStorage $hash) - { - $this->hash = $hash; - } - - /** - * Get the keys that are available on the batch result. - * - * @return array - */ - public function getKeys() - { - return iterator_to_array($this->hash); - } - - /** - * Gets a result from the container for the given object. When getting - * results for a batch of requests, provide the request object. - * - * @param object $forObject Object to retrieve the result for. - * - * @return mixed|null - */ - public function getResult($forObject) - { - return isset($this->hash[$forObject]) ? $this->hash[$forObject] : null; - } - - /** - * Get an array of successful results. - * - * @return array - */ - public function getSuccessful() - { - $results = []; - foreach ($this->hash as $key) { - if (!($this->hash[$key] instanceof \Exception)) { - $results[] = $this->hash[$key]; - } - } - - return $results; - } - - /** - * Get an array of failed results. - * - * @return array - */ - public function getFailures() - { - $results = []; - foreach ($this->hash as $key) { - if ($this->hash[$key] instanceof \Exception) { - $results[] = $this->hash[$key]; - } - } - - return $results; - } - - /** - * Allows iteration over all batch result values. - * - * @return \ArrayIterator - */ - public function getIterator() - { - $results = []; - foreach ($this->hash as $key) { - $results[] = $this->hash[$key]; - } - - return new \ArrayIterator($results); - } - - /** - * Counts the number of elements in the batch result. - * - * @return int - */ - public function count() - { - return count($this->hash); - } - - /** - * Checks if the batch contains a specific numerical array index. - * - * @param int $key Index to access - * - * @return bool - */ - public function offsetExists($key) - { - return $key < count($this->hash); - } - - /** - * Allows access of the batch using a numerical array index. - * - * @param int $key Index to access. - * - * @return mixed|null - */ - public function offsetGet($key) - { - $i = -1; - foreach ($this->hash as $obj) { - if ($key === ++$i) { - return $this->hash[$obj]; - } - } - - return null; - } - - public function offsetUnset($key) - { - throw new \RuntimeException('Not implemented'); - } - - public function offsetSet($key, $value) - { - throw new \RuntimeException('Not implemented'); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Client.php b/core/vendor/guzzlehttp/guzzle/src/Client.php index f1779eb4504e3a9c47de8800bd84d399fe585c30..74f5cfe824531b5803c5ef4d8e3f060dbad6692c 100644 --- a/core/vendor/guzzlehttp/guzzle/src/Client.php +++ b/core/vendor/guzzlehttp/guzzle/src/Client.php @@ -1,403 +1,393 @@ <?php namespace GuzzleHttp; -use GuzzleHttp\Event\HasEmitterTrait; -use GuzzleHttp\Message\MessageFactory; -use GuzzleHttp\Message\MessageFactoryInterface; -use GuzzleHttp\Message\RequestInterface; -use GuzzleHttp\Message\FutureResponse; -use GuzzleHttp\Ring\Client\Middleware; -use GuzzleHttp\Ring\Client\CurlMultiHandler; -use GuzzleHttp\Ring\Client\CurlHandler; -use GuzzleHttp\Ring\Client\StreamHandler; -use GuzzleHttp\Ring\Core; -use GuzzleHttp\Ring\Future\FutureInterface; -use GuzzleHttp\Exception\RequestException; -use React\Promise\FulfilledPromise; -use React\Promise\RejectedPromise; +use GuzzleHttp\Cookie\CookieJar; +use GuzzleHttp\Promise; +use GuzzleHttp\Psr7; +use Psr\Http\Message\UriInterface; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; +use \InvalidArgumentException as Iae; /** - * HTTP client + * @method ResponseInterface get($uri, array $options = []) + * @method ResponseInterface head($uri, array $options = []) + * @method ResponseInterface put($uri, array $options = []) + * @method ResponseInterface post($uri, array $options = []) + * @method ResponseInterface patch($uri, array $options = []) + * @method ResponseInterface delete($uri, array $options = []) + * @method Promise\PromiseInterface getAsync($uri, array $options = []) + * @method Promise\PromiseInterface headAsync($uri, array $options = []) + * @method Promise\PromiseInterface putAsync($uri, array $options = []) + * @method Promise\PromiseInterface postAsync($uri, array $options = []) + * @method Promise\PromiseInterface patchAsync($uri, array $options = []) + * @method Promise\PromiseInterface deleteAsync($uri, array $options = []) */ class Client implements ClientInterface { - use HasEmitterTrait; - - /** @var MessageFactoryInterface Request factory used by the client */ - private $messageFactory; - - /** @var Url Base URL of the client */ - private $baseUrl; - /** @var array Default request options */ - private $defaults; - - /** @var callable Request state machine */ - private $fsm; + private $config; /** * Clients accept an array of constructor parameters. * - * Here's an example of creating a client using an URI template for the - * client's base_url and an array of default request options to apply - * to each request: + * Here's an example of creating a client using a base_uri and an array of + * default request options to apply to each request: * * $client = new Client([ - * 'base_url' => [ - * 'http://www.foo.com/{version}/', - * ['version' => '123'] - * ], - * 'defaults' => [ - * 'timeout' => 10, - * 'allow_redirects' => false, - * 'proxy' => '192.168.16.1:10' - * ] + * 'base_uri' => 'http://www.foo.com/1.0/', + * 'timeout' => 0, + * 'allow_redirects' => false, + * 'proxy' => '192.168.16.1:10' * ]); * - * @param array $config Client configuration settings - * - base_url: Base URL of the client that is merged into relative URLs. - * Can be a string or an array that contains a URI template followed - * by an associative array of expansion variables to inject into the - * URI template. - * - handler: callable RingPHP handler used to transfer requests - * - message_factory: Factory used to create request and response object - * - defaults: Default request options to apply to each request - * - emitter: Event emitter used for request events - * - fsm: (internal use only) The request finite state machine. A - * function that accepts a transaction and optional final state. The - * function is responsible for transitioning a request through its - * lifecycle events. - */ - public function __construct(array $config = []) - { - $this->configureBaseUrl($config); - $this->configureDefaults($config); - - if (isset($config['emitter'])) { - $this->emitter = $config['emitter']; - } - - $this->messageFactory = isset($config['message_factory']) - ? $config['message_factory'] - : new MessageFactory(); - - if (isset($config['fsm'])) { - $this->fsm = $config['fsm']; - } else { - if (isset($config['handler'])) { - $handler = $config['handler']; - } elseif (isset($config['adapter'])) { - $handler = $config['adapter']; - } else { - $handler = static::getDefaultHandler(); - } - $this->fsm = new RequestFsm($handler, $this->messageFactory); - } - } - - /** - * Create a default handler to use based on the environment + * Client configuration settings include the following options: + * + * - handler: (callable) Function that transfers HTTP requests over the + * wire. The function is called with a Psr7\Http\Message\RequestInterface + * and array of transfer options, and must return a + * GuzzleHttp\Promise\PromiseInterface that is fulfilled with a + * Psr7\Http\Message\ResponseInterface on success. "handler" is a + * constructor only option that cannot be overridden in per/request + * options. If no handler is provided, a default handler will be created + * that enables all of the request options below by attaching all of the + * default middleware to the handler. + * - base_uri: (string|UriInterface) Base URI of the client that is merged + * into relative URIs. Can be a string or instance of UriInterface. + * - **: any request option * - * @throws \RuntimeException if no viable Handler is available. + * @param array $config Client configuration settings. + * + * @see \GuzzleHttp\RequestOptions for a list of available request options. */ - public static function getDefaultHandler() + public function __construct(array $config = []) { - $default = $future = $streaming = null; - - if (extension_loaded('curl')) { - $config = [ - 'select_timeout' => getenv('GUZZLE_CURL_SELECT_TIMEOUT') ?: 1 - ]; - if ($maxHandles = getenv('GUZZLE_CURL_MAX_HANDLES')) { - $config['max_handles'] = $maxHandles; - } - $future = new CurlMultiHandler($config); - if (function_exists('curl_reset')) { - $default = new CurlHandler(); - } - } - - if (ini_get('allow_url_fopen')) { - $streaming = new StreamHandler(); - } - - if (!($default = ($default ?: $future) ?: $streaming)) { - throw new \RuntimeException('Guzzle requires cURL, the ' - . 'allow_url_fopen ini setting, or a custom HTTP handler.'); - } - - $handler = $default; - - if ($streaming && $streaming !== $default) { - $handler = Middleware::wrapStreaming($default, $streaming); + if (!isset($config['handler'])) { + $config['handler'] = HandlerStack::create(); } - if ($future) { - $handler = Middleware::wrapFuture($handler, $future); + // Convert the base_uri to a UriInterface + if (isset($config['base_uri'])) { + $config['base_uri'] = Psr7\uri_for($config['base_uri']); } - return $handler; + $this->configureDefaults($config); } - /** - * Get the default User-Agent string to use with Guzzle - * - * @return string - */ - public static function getDefaultUserAgent() + public function __call($method, $args) { - static $defaultAgent = ''; - if (!$defaultAgent) { - $defaultAgent = 'Guzzle/' . self::VERSION; - if (extension_loaded('curl')) { - $defaultAgent .= ' curl/' . curl_version()['version']; - } - $defaultAgent .= ' PHP/' . PHP_VERSION; + if (count($args) < 1) { + throw new \InvalidArgumentException('Magic request methods require a URI and optional options array'); } - return $defaultAgent; - } - - public function getDefaultOption($keyOrPath = null) - { - return $keyOrPath === null - ? $this->defaults - : Utils::getPath($this->defaults, $keyOrPath); - } - - public function setDefaultOption($keyOrPath, $value) - { - Utils::setPath($this->defaults, $keyOrPath, $value); - } - - public function getBaseUrl() - { - return (string) $this->baseUrl; - } + $uri = $args[0]; + $opts = isset($args[1]) ? $args[1] : []; - public function createRequest($method, $url = null, array $options = []) - { - $options = $this->mergeDefaults($options); - // Use a clone of the client's emitter - $options['config']['emitter'] = clone $this->getEmitter(); - $url = $url || (is_string($url) && strlen($url)) - ? $this->buildUrl($url) - : (string) $this->baseUrl; - - return $this->messageFactory->createRequest($method, $url, $options); + return substr($method, -5) === 'Async' + ? $this->requestAsync(substr($method, 0, -5), $uri, $opts) + : $this->request($method, $uri, $opts); } - public function get($url = null, $options = []) + public function sendAsync(RequestInterface $request, array $options = []) { - return $this->send($this->createRequest('GET', $url, $options)); - } + // Merge the base URI into the request URI if needed. + $options = $this->prepareDefaults($options); - public function head($url = null, array $options = []) - { - return $this->send($this->createRequest('HEAD', $url, $options)); + return $this->transfer( + $request->withUri($this->buildUri($request->getUri(), $options)), + $options + ); } - public function delete($url = null, array $options = []) + public function send(RequestInterface $request, array $options = []) { - return $this->send($this->createRequest('DELETE', $url, $options)); + $options[RequestOptions::SYNCHRONOUS] = true; + return $this->sendAsync($request, $options)->wait(); } - public function put($url = null, array $options = []) + public function requestAsync($method, $uri = null, array $options = []) { - return $this->send($this->createRequest('PUT', $url, $options)); - } + $options = $this->prepareDefaults($options); + // Remove request modifying parameter because it can be done up-front. + $headers = isset($options['headers']) ? $options['headers'] : []; + $body = isset($options['body']) ? $options['body'] : null; + $version = isset($options['version']) ? $options['version'] : '1.1'; + // Merge the URI into the base URI. + $uri = $this->buildUri($uri, $options); + if (is_array($body)) { + $this->invalidBody(); + } + $request = new Psr7\Request($method, $uri, $headers, $body, $version); + // Remove the option so that they are not doubly-applied. + unset($options['headers'], $options['body'], $options['version']); - public function patch($url = null, array $options = []) - { - return $this->send($this->createRequest('PATCH', $url, $options)); + return $this->transfer($request, $options); } - public function post($url = null, array $options = []) + public function request($method, $uri = null, array $options = []) { - return $this->send($this->createRequest('POST', $url, $options)); + $options[RequestOptions::SYNCHRONOUS] = true; + return $this->requestAsync($method, $uri, $options)->wait(); } - public function options($url = null, array $options = []) + public function getConfig($option = null) { - return $this->send($this->createRequest('OPTIONS', $url, $options)); + return $option === null + ? $this->config + : (isset($this->config[$option]) ? $this->config[$option] : null); } - public function send(RequestInterface $request) + private function buildUri($uri, array $config) { - $isFuture = $request->getConfig()->get('future'); - $trans = new Transaction($this, $request, $isFuture); - $fn = $this->fsm; - - try { - $fn($trans); - if ($isFuture) { - // Turn the normal response into a future if needed. - return $trans->response instanceof FutureInterface - ? $trans->response - : new FutureResponse(new FulfilledPromise($trans->response)); - } - // Resolve deep futures if this is not a future - // transaction. This accounts for things like retries - // that do not have an immediate side-effect. - while ($trans->response instanceof FutureInterface) { - $trans->response = $trans->response->wait(); - } - return $trans->response; - } catch (\Exception $e) { - if ($isFuture) { - // Wrap the exception in a promise - return new FutureResponse(new RejectedPromise($e)); - } - throw RequestException::wrapException($trans->request, $e); + if (!isset($config['base_uri'])) { + return $uri instanceof UriInterface ? $uri : new Psr7\Uri($uri); } + + return Psr7\Uri::resolve(Psr7\uri_for($config['base_uri']), $uri); } /** - * Get an array of default options to apply to the client + * Configures the default options for a client. + * + * @param array $config * * @return array */ - protected function getDefaultOptions() + private function configureDefaults(array $config) { - $settings = [ - 'allow_redirects' => true, - 'exceptions' => true, + $defaults = [ + 'allow_redirects' => RedirectMiddleware::$defaultSettings, + 'http_errors' => true, 'decode_content' => true, - 'verify' => true + 'verify' => true, + 'cookies' => false ]; // Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set if ($proxy = getenv('HTTP_PROXY')) { - $settings['proxy']['http'] = $proxy; + $defaults['proxy']['http'] = $proxy; } if ($proxy = getenv('HTTPS_PROXY')) { - $settings['proxy']['https'] = $proxy; + $defaults['proxy']['https'] = $proxy; } - return $settings; + $this->config = $config + $defaults; + + if (!empty($config['cookies']) && $config['cookies'] === true) { + $this->config['cookies'] = new CookieJar(); + } + + // Add the default user-agent header. + if (!isset($this->config['headers'])) { + $this->config['headers'] = ['User-Agent' => default_user_agent()]; + } else { + // Add the User-Agent header if one was not already set. + foreach (array_keys($this->config['headers']) as $name) { + if (strtolower($name) === 'user-agent') { + return; + } + } + $this->config['headers']['User-Agent'] = default_user_agent(); + } } /** - * Expand a URI template and inherit from the base URL if it's relative + * Merges default options into the array. + * + * @param array $options Options to modify by reference * - * @param string|array $url URL or an array of the URI template to expand - * followed by a hash of template varnames. - * @return string - * @throws \InvalidArgumentException + * @return array */ - private function buildUrl($url) + private function prepareDefaults($options) { - // URI template (absolute or relative) - if (!is_array($url)) { - return strpos($url, '://') - ? (string) $url - : (string) $this->baseUrl->combine($url); + $defaults = $this->config; + + if (!empty($defaults['headers'])) { + // Default headers are only added if they are not present. + $defaults['_conditional'] = $defaults['headers']; + unset($defaults['headers']); } - if (!isset($url[1])) { - throw new \InvalidArgumentException('You must provide a hash of ' - . 'varname options in the second element of a URL array.'); + // Special handling for headers is required as they are added as + // conditional headers and as headers passed to a request ctor. + if (array_key_exists('headers', $options)) { + // Allows default headers to be unset. + if ($options['headers'] === null) { + $defaults['_conditional'] = null; + unset($options['headers']); + } elseif (!is_array($options['headers'])) { + throw new \InvalidArgumentException('headers must be an array'); + } } - // Absolute URL - if (strpos($url[0], '://')) { - return Utils::uriTemplate($url[0], $url[1]); + // Shallow merge defaults underneath options. + $result = $options + $defaults; + + // Remove null values. + foreach ($result as $k => $v) { + if ($v === null) { + unset($result[$k]); + } } - // Combine the relative URL with the base URL - return (string) $this->baseUrl->combine( - Utils::uriTemplate($url[0], $url[1]) - ); + return $result; } - private function configureBaseUrl(&$config) + /** + * Transfers the given request and applies request options. + * + * The URI of the request is not modified and the request options are used + * as-is without merging in default options. + * + * @param RequestInterface $request + * @param array $options + * + * @return Promise\PromiseInterface + */ + private function transfer(RequestInterface $request, array $options) { - if (!isset($config['base_url'])) { - $this->baseUrl = new Url('', ''); - } elseif (!is_array($config['base_url'])) { - $this->baseUrl = Url::fromString($config['base_url']); - } elseif (count($config['base_url']) < 2) { - throw new \InvalidArgumentException('You must provide a hash of ' - . 'varname options in the second element of a base_url array.'); - } else { - $this->baseUrl = Url::fromString( - Utils::uriTemplate( - $config['base_url'][0], - $config['base_url'][1] - ) - ); - $config['base_url'] = (string) $this->baseUrl; + // save_to -> sink + if (isset($options['save_to'])) { + $options['sink'] = $options['save_to']; + unset($options['save_to']); } - } - private function configureDefaults($config) - { - if (!isset($config['defaults'])) { - $this->defaults = $this->getDefaultOptions(); - } else { - $this->defaults = array_replace( - $this->getDefaultOptions(), - $config['defaults'] - ); + // exceptions -> http_error + if (isset($options['exceptions'])) { + $options['http_errors'] = $options['exceptions']; + unset($options['exceptions']); } - // Add the default user-agent header - if (!isset($this->defaults['headers'])) { - $this->defaults['headers'] = [ - 'User-Agent' => static::getDefaultUserAgent() - ]; - } elseif (!Core::hasHeader($this->defaults, 'User-Agent')) { - // Add the User-Agent header if one was not already set - $this->defaults['headers']['User-Agent'] = static::getDefaultUserAgent(); + $request = $this->applyOptions($request, $options); + $handler = $options['handler']; + + try { + return Promise\promise_for($handler($request, $options)); + } catch (\Exception $e) { + return Promise\rejection_for($e); } } /** - * Merges default options into the array passed by reference. + * Applies the array of request options to a request. * - * @param array $options Options to modify by reference + * @param RequestInterface $request + * @param array $options * - * @return array + * @return RequestInterface */ - private function mergeDefaults($options) + private function applyOptions(RequestInterface $request, array &$options) { - $defaults = $this->defaults; - - // Case-insensitively merge in default headers if both defaults and - // options have headers specified. - if (!empty($defaults['headers']) && !empty($options['headers'])) { - // Create a set of lowercased keys that are present. - $lkeys = []; - foreach (array_keys($options['headers']) as $k) { - $lkeys[strtolower($k)] = true; + $modify = []; + + if (isset($options['form_params'])) { + if (isset($options['multipart'])) { + throw new \InvalidArgumentException('You cannot use ' + . 'form_params and multipart at the same time. Use the ' + . 'form_params option if you want to send application/' + . 'x-www-form-urlencoded requests, and the multipart ' + . 'option to send multipart/form-data requests.'); } - // Merge in lowercase default keys when not present in above set. - foreach ($defaults['headers'] as $key => $value) { - if (!isset($lkeys[strtolower($key)])) { - $options['headers'][$key] = $value; - } + $options['body'] = http_build_query($options['form_params'], null, '&'); + unset($options['form_params']); + $options['_conditional']['Content-Type'] = 'application/x-www-form-urlencoded'; + } + + if (isset($options['multipart'])) { + $elements = $options['multipart']; + unset($options['multipart']); + $options['body'] = new Psr7\MultipartStream($elements); + // Use a multipart/form-data POST if a Content-Type is not set. + $options['_conditional']['Content-Type'] = 'multipart/form-data; boundary=' + . $options['body']->getBoundary(); + } + + if (!empty($options['decode_content']) + && $options['decode_content'] !== true + ) { + $modify['set_headers']['Accept-Encoding'] = $options['decode_content']; + } + + if (isset($options['headers'])) { + if (isset($modify['set_headers'])) { + $modify['set_headers'] = $options['headers'] + $modify['set_headers']; + } else { + $modify['set_headers'] = $options['headers']; } - // No longer need to merge in headers. - unset($defaults['headers']); + unset($options['headers']); } - $result = array_replace_recursive($defaults, $options); - foreach ($options as $k => $v) { - if ($v === null) { - unset($result[$k]); + if (isset($options['body'])) { + if (is_array($options['body'])) { + $this->invalidBody(); } + $modify['body'] = Psr7\stream_for($options['body']); + unset($options['body']); } - return $result; + if (!empty($options['auth'])) { + $value = $options['auth']; + $type = is_array($value) + ? (isset($value[2]) ? strtolower($value[2]) : 'basic') + : $value; + $config['auth'] = $value; + switch (strtolower($type)) { + case 'basic': + $modify['set_headers']['Authorization'] = 'Basic ' + . base64_encode("$value[0]:$value[1]"); + break; + case 'digest': + // @todo: Do not rely on curl + $options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_DIGEST; + $options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]"; + break; + } + } + + if (isset($options['query'])) { + $value = $options['query']; + if (is_array($value)) { + $value = http_build_query($value, null, '&', PHP_QUERY_RFC3986); + } + if (!is_string($value)) { + throw new Iae('query must be a string or array'); + } + $modify['query'] = $value; + unset($options['query']); + } + + if (isset($options['json'])) { + $modify['body'] = Psr7\stream_for(json_encode($options['json'])); + $options['_conditional']['Content-Type'] = 'application/json'; + unset($options['json']); + } + + $request = Psr7\modify_request($request, $modify); + + // Merge in conditional headers if they are not present. + if (isset($options['_conditional'])) { + // Build up the changes so it's in a single clone of the message. + $modify = []; + foreach ($options['_conditional'] as $k => $v) { + if (!$request->hasHeader($k)) { + $modify['set_headers'][$k] = $v; + } + } + $request = Psr7\modify_request($request, $modify); + // Don't pass this internal value along to middleware/handlers. + unset($options['_conditional']); + } + + return $request; } - /** - * @deprecated Use {@see GuzzleHttp\Pool} instead. - * @see GuzzleHttp\Pool - */ - public function sendAll($requests, array $options = []) + private function invalidBody() { - Pool::send($this, $requests, $options); + throw new \InvalidArgumentException('Passing in the "body" request ' + . 'option as an array to send a POST request has been deprecated. ' + . 'Please use the "form_params" request option to send a ' + . 'application/x-www-form-urlencoded request, or a the "multipart" ' + . 'request option to send a multipart/form-data request.'); } } diff --git a/core/vendor/guzzlehttp/guzzle/src/ClientInterface.php b/core/vendor/guzzlehttp/guzzle/src/ClientInterface.php index fac88645d657c75994c3a168351a021315d9865e..7d155e5645ea94cd0710a6676958dc90ca645e43 100644 --- a/core/vendor/guzzlehttp/guzzle/src/ClientInterface.php +++ b/core/vendor/guzzlehttp/guzzle/src/ClientInterface.php @@ -1,150 +1,84 @@ <?php namespace GuzzleHttp; -use GuzzleHttp\Event\HasEmitterInterface; -use GuzzleHttp\Exception\RequestException; -use GuzzleHttp\Message\RequestInterface; -use GuzzleHttp\Message\ResponseInterface; +use GuzzleHttp\Promise\PromiseInterface; +use GuzzleHttp\Exception\GuzzleException; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\UriInterface; /** - * Client interface for sending HTTP requests + * Client interface for sending HTTP requests. */ -interface ClientInterface extends HasEmitterInterface +interface ClientInterface { - const VERSION = '5.2.0'; + const VERSION = '6.0.2'; /** - * Create and return a new {@see RequestInterface} object. + * Send an HTTP request. * - * 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 URL can - * contain the query string as well. Use an array to provide a URL - * template and additional variables to use in the URL template expansion. - * - * @param string $method HTTP method - * @param string|array|Url $url URL or URI template - * @param array $options Array of request options to apply. - * - * @return RequestInterface - */ - public function createRequest($method, $url = null, array $options = []); - - /** - * Send a GET request - * - * @param string|array|Url $url URL or URI template - * @param array $options Array of request options to apply. - * - * @return ResponseInterface - * @throws RequestException When an error is encountered - */ - public function get($url = null, $options = []); - - /** - * Send a HEAD request - * - * @param string|array|Url $url URL or URI template - * @param array $options Array of request options to apply. + * @param RequestInterface $request Request to send + * @param array $options Request options to apply to the given + * request and to the transfer. * * @return ResponseInterface - * @throws RequestException When an error is encountered + * @throws GuzzleException */ - public function head($url = null, array $options = []); + public function send(RequestInterface $request, array $options = []); /** - * Send a DELETE request + * Asynchronously send an HTTP request. * - * @param string|array|Url $url URL or URI template - * @param array $options Array of request options to apply. + * @param RequestInterface $request Request to send + * @param array $options Request options to apply to the given + * request and to the transfer. * - * @return ResponseInterface - * @throws RequestException When an error is encountered + * @return PromiseInterface */ - public function delete($url = null, array $options = []); + public function sendAsync(RequestInterface $request, array $options = []); /** - * Send a PUT request + * Create and send an HTTP request. * - * @param string|array|Url $url URL or URI template - * @param array $options Array of request options to apply. - * - * @return ResponseInterface - * @throws RequestException When an error is encountered - */ - public function put($url = null, array $options = []); - - /** - * Send a PATCH request + * 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 URL can + * contain the query string as well. * - * @param string|array|Url $url URL or URI template - * @param array $options Array of request options to apply. + * @param string $method HTTP method + * @param string|UriInterface $uri URI object or string. + * @param array $options Request options to apply. * * @return ResponseInterface - * @throws RequestException When an error is encountered + * @throws GuzzleException */ - public function patch($url = null, array $options = []); + public function request($method, $uri, array $options = []); /** - * Send a POST request - * - * @param string|array|Url $url URL or URI template - * @param array $options Array of request options to apply. + * Create and send an asynchronous HTTP request. * - * @return ResponseInterface - * @throws RequestException When an error is encountered - */ - public function post($url = null, array $options = []); - - /** - * Send an OPTIONS request + * 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 URL can + * contain the query string as well. Use an array to provide a URL + * template and additional variables to use in the URL template expansion. * - * @param string|array|Url $url URL or URI template - * @param array $options Array of request options to apply. + * @param string $method HTTP method + * @param string|UriInterface $uri URI object or string. + * @param array $options Request options to apply. * - * @return ResponseInterface - * @throws RequestException When an error is encountered + * @return PromiseInterface */ - public function options($url = null, array $options = []); + public function requestAsync($method, $uri, array $options = []); /** - * Sends a single request - * - * @param RequestInterface $request Request to send + * Get a client configuration option. * - * @return \GuzzleHttp\Message\ResponseInterface - * @throws \LogicException When the handler does not populate a response - * @throws RequestException When an error is encountered - */ - public function send(RequestInterface $request); - - /** - * Get default request options of the client. + * These options include default request options of the client, a "handler" + * (if utilized by the concrete client), and a "base_uri" if utilized by + * the concrete client. * - * @param string|null $keyOrPath The Path to a particular default request - * option to retrieve or pass null to retrieve all default request - * options. The syntax uses "/" to denote a path through nested PHP - * arrays. For example, "headers/content-type". + * @param string|null $option The config option to retrieve. * * @return mixed */ - public function getDefaultOption($keyOrPath = null); - - /** - * Set a default request option on the client so that any request created - * by the client will use the provided default value unless overridden - * explicitly when creating a request. - * - * @param string|null $keyOrPath The Path to a particular configuration - * value to set. The syntax uses a path notation that allows you to - * specify nested configuration values (e.g., 'headers/content-type'). - * @param mixed $value Default request option value to set - */ - public function setDefaultOption($keyOrPath, $value); - - /** - * Get the base URL of the client. - * - * @return string Returns the base URL if present - */ - public function getBaseUrl(); + public function getConfig($option = null); } diff --git a/core/vendor/guzzlehttp/guzzle/src/Collection.php b/core/vendor/guzzlehttp/guzzle/src/Collection.php deleted file mode 100644 index 4aabd20b00eef7fd95f75a2af21da3c5bf161145..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Collection.php +++ /dev/null @@ -1,236 +0,0 @@ -<?php -namespace GuzzleHttp; - -/** - * Key value pair collection object - */ -class Collection implements - \ArrayAccess, - \IteratorAggregate, - \Countable, - ToArrayInterface -{ - use HasDataTrait; - - /** - * @param array $data Associative array of data to set - */ - public function __construct(array $data = []) - { - $this->data = $data; - } - - /** - * Create a new collection from an array, validate the keys, and add default - * values where missing - * - * @param array $config Configuration values to apply. - * @param array $defaults Default parameters - * @param array $required Required parameter names - * - * @return self - * @throws \InvalidArgumentException if a parameter is missing - */ - public static function fromConfig( - array $config = [], - array $defaults = [], - array $required = [] - ) { - $data = $config + $defaults; - - if ($missing = array_diff($required, array_keys($data))) { - throw new \InvalidArgumentException( - 'Config is missing the following keys: ' . - implode(', ', $missing)); - } - - return new self($data); - } - - /** - * Removes all key value pairs - */ - public function clear() - { - $this->data = []; - } - - /** - * Get a specific key value. - * - * @param string $key Key to retrieve. - * - * @return mixed|null Value of the key or NULL - */ - public function get($key) - { - return isset($this->data[$key]) ? $this->data[$key] : null; - } - - /** - * Set a key value pair - * - * @param string $key Key to set - * @param mixed $value Value to set - */ - public function set($key, $value) - { - $this->data[$key] = $value; - } - - /** - * 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 - */ - public function add($key, $value) - { - if (!array_key_exists($key, $this->data)) { - $this->data[$key] = $value; - } elseif (is_array($this->data[$key])) { - $this->data[$key][] = $value; - } else { - $this->data[$key] = array($this->data[$key], $value); - } - } - - /** - * Remove a specific key value pair - * - * @param string $key A key to remove - */ - public function remove($key) - { - unset($this->data[$key]); - } - - /** - * Get all keys in the collection - * - * @return array - */ - public function getKeys() - { - return array_keys($this->data); - } - - /** - * Returns whether or not the specified key is present. - * - * @param string $key The key for which to check the existence. - * - * @return bool - */ - public function hasKey($key) - { - return array_key_exists($key, $this->data); - } - - /** - * 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, true); - } - - /** - * Replace the data of the object with the value of an array - * - * @param array $data Associative array of data - */ - public function replace(array $data) - { - $this->data = $data; - } - - /** - * Add and merge in a Collection or array of key value pair data. - * - * @param Collection|array $data Associative array of key value pair data - */ - public function merge($data) - { - foreach ($data as $key => $value) { - $this->add($key, $value); - } - } - - /** - * Overwrite key value pairs in this collection with all of the data from - * an array or collection. - * - * @param array|\Traversable $data Values to override over this config - */ - public function overwriteWith($data) - { - if (is_array($data)) { - $this->data = $data + $this->data; - } elseif ($data instanceof Collection) { - $this->data = $data->toArray() + $this->data; - } else { - foreach ($data as $key => $value) { - $this->data[$key] = $value; - } - } - } - - /** - * Returns a Collection containing all the elements of the collection after - * applying the callback function to each one. - * - * The callable should accept three arguments: - * - (string) $key - * - (string) $value - * - (array) $context - * - * The callable must return a the altered or unaltered value. - * - * @param callable $closure Map function to apply - * @param array $context Context to pass to the callable - * - * @return Collection - */ - public function map(callable $closure, array $context = []) - { - $collection = new static(); - foreach ($this as $key => $value) { - $collection[$key] = $closure($key, $value, $context); - } - - return $collection; - } - - /** - * Iterates over each key value pair in the collection passing them to the - * callable. If the callable returns true, the current value from input is - * returned into the result Collection. - * - * The callable must accept two arguments: - * - (string) $key - * - (string) $value - * - * @param callable $closure Evaluation function - * - * @return Collection - */ - public function filter(callable $closure) - { - $collection = new static(); - foreach ($this->data as $key => $value) { - if ($closure($key, $value)) { - $collection[$key] = $value; - } - } - - return $collection; - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php b/core/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php index f8ac7dd350fc32653801d6a6cc7dd0ab0142adb9..4270469fc21ee1478354c6e7c50709fc774e200d 100644 --- a/core/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php +++ b/core/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php @@ -1,14 +1,13 @@ <?php namespace GuzzleHttp\Cookie; -use GuzzleHttp\Message\RequestInterface; -use GuzzleHttp\Message\ResponseInterface; -use GuzzleHttp\ToArrayInterface; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; /** * Cookie jar that stores cookies an an array */ -class CookieJar implements CookieJarInterface, ToArrayInterface +class CookieJar implements CookieJarInterface { /** @var SetCookie[] Loaded cookie data */ private $cookies = []; @@ -194,23 +193,24 @@ public function extractCookies( RequestInterface $request, ResponseInterface $response ) { - if ($cookieHeader = $response->getHeaderAsArray('Set-Cookie')) { + if ($cookieHeader = $response->getHeader('Set-Cookie')) { foreach ($cookieHeader as $cookie) { $sc = SetCookie::fromString($cookie); if (!$sc->getDomain()) { - $sc->setDomain($request->getHost()); + $sc->setDomain($request->getUri()->getHost()); } $this->setCookie($sc); } } } - public function addCookieHeader(RequestInterface $request) + public function withCookieHeader(RequestInterface $request) { $values = []; - $scheme = $request->getScheme(); - $host = $request->getHost(); - $path = $request->getPath(); + $uri = $request->getUri(); + $scheme = $uri->getScheme(); + $host = $uri->getHost(); + $path = $uri->getPath() ?: '/'; foreach ($this->cookies as $cookie) { if ($cookie->matchesPath($path) && @@ -223,9 +223,9 @@ public function addCookieHeader(RequestInterface $request) } } - if ($values) { - $request->setHeader('Cookie', implode('; ', $values)); - } + return $values + ? $request->withHeader('Cookie', implode('; ', $values)) + : $request; } /** diff --git a/core/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php b/core/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php index 4ea8567e8778979285a6b623b22e599a90728a00..2cf298a867b3713f6fa3143d5288ce2458c311c2 100644 --- a/core/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php +++ b/core/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php @@ -1,8 +1,8 @@ <?php namespace GuzzleHttp\Cookie; -use GuzzleHttp\Message\RequestInterface; -use GuzzleHttp\Message\ResponseInterface; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; /** * Stores HTTP cookies. @@ -17,14 +17,16 @@ interface CookieJarInterface extends \Countable, \IteratorAggregate { /** - * Add a Cookie header to a request. + * Create a request with added cookie headers. * * If no matching cookies are found in the cookie jar, then no Cookie - * header is added to the request. + * header is added to the request and the same request is returned. * - * @param RequestInterface $request Request object to update + * @param RequestInterface $request Request object to modify. + * + * @return RequestInterface returns the modified request. */ - public function addCookieHeader(RequestInterface $request); + public function withCookieHeader(RequestInterface $request); /** * Extract cookies from an HTTP response and store them in the CookieJar. @@ -72,4 +74,11 @@ public function clear($domain = null, $path = null, $name = null); * to RFC 2965. */ public function clearSessionCookies(); + + /** + * Converts the cookie jar to an array. + * + * @return array + */ + public function toArray(); } diff --git a/core/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php b/core/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php index e43185172d8bcc7226a395d896d4448e4b50626b..28cfad54d5b79201fd9b31e3092ac94da8cdc43f 100644 --- a/core/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php +++ b/core/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php @@ -1,8 +1,6 @@ <?php namespace GuzzleHttp\Cookie; -use GuzzleHttp\Utils; - /** * Persists non-session cookies using a JSON formatted file */ @@ -45,15 +43,14 @@ public function save($filename) { $json = []; foreach ($this as $cookie) { + /** @var SetCookie $cookie */ if ($cookie->getExpires() && !$cookie->getDiscard()) { $json[] = $cookie->toArray(); } } if (false === file_put_contents($filename, json_encode($json))) { - // @codeCoverageIgnoreStart throw new \RuntimeException("Unable to save file {$filename}"); - // @codeCoverageIgnoreEnd } } @@ -69,14 +66,12 @@ public function load($filename) { $json = file_get_contents($filename); if (false === $json) { - // @codeCoverageIgnoreStart throw new \RuntimeException("Unable to load file {$filename}"); - // @codeCoverageIgnoreEnd } - $data = Utils::jsonDecode($json, true); + $data = json_decode($json, true); if (is_array($data)) { - foreach (Utils::jsonDecode($json, true) as $cookie) { + foreach (json_decode($json, true) as $cookie) { $this->setCookie(new SetCookie($cookie)); } } elseif (strlen($data)) { diff --git a/core/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php b/core/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php index 71a02d56dce5b3fe917f573f2d2ba953e9ca5195..1688eb670295894bcda68641c9001cfb11c58544 100644 --- a/core/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php +++ b/core/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php @@ -1,8 +1,6 @@ <?php namespace GuzzleHttp\Cookie; -use GuzzleHttp\Utils; - /** * Persists cookies in the client session */ @@ -37,6 +35,7 @@ public function save() { $json = []; foreach ($this as $cookie) { + /** @var SetCookie $cookie */ if ($cookie->getExpires() && !$cookie->getDiscard()) { $json[] = $cookie->toArray(); } @@ -54,7 +53,7 @@ protected function load() ? $_SESSION[$this->sessionKey] : null; - $data = Utils::jsonDecode($cookieJar, true); + $data = json_decode($cookieJar, true); if (is_array($data)) { foreach ($data as $cookie) { $this->setCookie(new SetCookie($cookie)); diff --git a/core/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php b/core/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php index ac9a890813ebf478503c57b4f6af25732dc17231..8613e2584ebc71ba6a572562d9942a792a720eec 100644 --- a/core/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php +++ b/core/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php @@ -1,12 +1,10 @@ <?php namespace GuzzleHttp\Cookie; -use GuzzleHttp\ToArrayInterface; - /** * Set-Cookie object */ -class SetCookie implements ToArrayInterface +class SetCookie { /** @var array */ private static $defaults = [ @@ -350,8 +348,8 @@ public function validate() } // Check if any of the invalid characters are present in the cookie name - if (preg_match("/[=,; \t\r\n\013\014]/", $name)) { - return "Cookie name must not cannot invalid characters: =,; \\t\\r\\n\\013\\014"; + if (preg_match('/[\x00-\x20\x22\x28-\x29\x2c\x2f\x3a-\x40\x5b-\x5d\x7b\x7d\x7f]/', $name)) { + return 'Cookie name must not contain invalid characters: ASCII Control characters (0-31;127), space, tab and the following characters: ()<>@,;:\"/[]?={}'; } // Value must not be empty, but can be 0 diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/AbstractEvent.php b/core/vendor/guzzlehttp/guzzle/src/Event/AbstractEvent.php deleted file mode 100644 index 0d2f4dbf0ce4706bd3558b26df47485e4382ff15..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Event/AbstractEvent.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php -namespace GuzzleHttp\Event; - -/** - * Basic event class that can be extended. - */ -abstract class AbstractEvent implements EventInterface -{ - private $propagationStopped = false; - - public function isPropagationStopped() - { - return $this->propagationStopped; - } - - public function stopPropagation() - { - $this->propagationStopped = true; - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/AbstractRequestEvent.php b/core/vendor/guzzlehttp/guzzle/src/Event/AbstractRequestEvent.php deleted file mode 100644 index 8c8fbc94a4b9147eaafb2711e5bc80edaa60a3f2..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Event/AbstractRequestEvent.php +++ /dev/null @@ -1,61 +0,0 @@ -<?php -namespace GuzzleHttp\Event; - -use GuzzleHttp\Transaction; -use GuzzleHttp\ClientInterface; -use GuzzleHttp\Message\RequestInterface; - -/** - * Base class for request events, providing a request and client getter. - */ -abstract class AbstractRequestEvent extends AbstractEvent -{ - /** @var Transaction */ - protected $transaction; - - /** - * @param Transaction $transaction - */ - public function __construct(Transaction $transaction) - { - $this->transaction = $transaction; - } - - /** - * Get the HTTP client associated with the event. - * - * @return ClientInterface - */ - public function getClient() - { - return $this->transaction->client; - } - - /** - * Get the request object - * - * @return RequestInterface - */ - public function getRequest() - { - return $this->transaction->request; - } - - /** - * Get the number of transaction retries. - * - * @return int - */ - public function getRetryCount() - { - return $this->transaction->retries; - } - - /** - * @return Transaction - */ - protected function getTransaction() - { - return $this->transaction; - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/AbstractRetryableEvent.php b/core/vendor/guzzlehttp/guzzle/src/Event/AbstractRetryableEvent.php deleted file mode 100644 index bbbdfaf83131cd79ee808ad5f5425044b5bbbeda..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Event/AbstractRetryableEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php -namespace GuzzleHttp\Event; - -/** - * Abstract request event that can be retried. - */ -class AbstractRetryableEvent extends AbstractTransferEvent -{ - /** - * Mark the request as needing a retry and stop event propagation. - * - * This action allows you to retry a request without emitting the "end" - * event multiple times for a given request. When retried, the request - * emits a before event and is then sent again using the client that sent - * the original request. - * - * When retrying, it is important to limit the number of retries you allow - * to prevent infinite loops. - * - * This action can only be taken during the "complete" and "error" events. - * - * @param int $afterDelay If specified, the amount of time in milliseconds - * to delay before retrying. Note that this must - * be supported by the underlying RingPHP handler - * to work properly. Set to 0 or provide no value - * to retry immediately. - */ - public function retry($afterDelay = 0) - { - // Setting the transition state to 'retry' will cause the next state - // transition of the transaction to retry the request. - $this->transaction->state = 'retry'; - - if ($afterDelay) { - $this->transaction->request->getConfig()->set('delay', $afterDelay); - } - - $this->stopPropagation(); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/AbstractTransferEvent.php b/core/vendor/guzzlehttp/guzzle/src/Event/AbstractTransferEvent.php deleted file mode 100644 index 3b106df00782b84d87ec028673f23ca71435ab53..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Event/AbstractTransferEvent.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php -namespace GuzzleHttp\Event; - -use GuzzleHttp\Message\ResponseInterface; -use GuzzleHttp\Ring\Future\FutureInterface; - -/** - * Event that contains transfer statistics, and can be intercepted. - */ -abstract class AbstractTransferEvent extends AbstractRequestEvent -{ - /** - * Get all transfer information as an associative array if no $name - * argument is supplied, or gets a specific transfer statistic if - * a $name attribute is supplied (e.g., 'total_time'). - * - * @param string $name Name of the transfer stat to retrieve - * - * @return mixed|null|array - */ - public function getTransferInfo($name = null) - { - if (!$name) { - return $this->transaction->transferInfo; - } - - return isset($this->transaction->transferInfo[$name]) - ? $this->transaction->transferInfo[$name] - : null; - } - - /** - * Returns true/false if a response is available. - * - * @return bool - */ - public function hasResponse() - { - return !($this->transaction->response instanceof FutureInterface); - } - - /** - * Get the response. - * - * @return ResponseInterface|null - */ - public function getResponse() - { - return $this->hasResponse() ? $this->transaction->response : null; - } - - /** - * Intercept the request and associate a response - * - * @param ResponseInterface $response Response to set - */ - public function intercept(ResponseInterface $response) - { - $this->transaction->response = $response; - $this->transaction->exception = null; - $this->stopPropagation(); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/BeforeEvent.php b/core/vendor/guzzlehttp/guzzle/src/Event/BeforeEvent.php deleted file mode 100644 index f313c37561dc7797f9af786171547e96b48646d1..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Event/BeforeEvent.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php -namespace GuzzleHttp\Event; - -use GuzzleHttp\Message\ResponseInterface; - -/** - * Event object emitted before a request is sent. - * - * This event MAY be emitted multiple times (i.e., if a request is retried). - * You MAY change the Response associated with the request using the - * intercept() method of the event. - */ -class BeforeEvent extends AbstractRequestEvent -{ - /** - * Intercept the request and associate a response - * - * @param ResponseInterface $response Response to set - */ - public function intercept(ResponseInterface $response) - { - $this->transaction->response = $response; - $this->transaction->exception = null; - $this->stopPropagation(); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/CompleteEvent.php b/core/vendor/guzzlehttp/guzzle/src/Event/CompleteEvent.php deleted file mode 100644 index 56cc557e3e5ca0452cef00a4af7a0bf6e4ed6149..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Event/CompleteEvent.php +++ /dev/null @@ -1,14 +0,0 @@ -<?php -namespace GuzzleHttp\Event; - -/** - * Event object emitted after a request has been completed. - * - * This event MAY be emitted multiple times for a single request. You MAY - * change the Response associated with the request using the intercept() - * method of the event. - * - * This event allows the request to be retried if necessary using the retry() - * method of the event. - */ -class CompleteEvent extends AbstractRetryableEvent {} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/Emitter.php b/core/vendor/guzzlehttp/guzzle/src/Event/Emitter.php deleted file mode 100644 index 536317a9c90afac1f8bd0b0a24eabeb555970bc1..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Event/Emitter.php +++ /dev/null @@ -1,146 +0,0 @@ -<?php -namespace GuzzleHttp\Event; - -/** - * Guzzle event emitter. - * - * Some of this class is based on the Symfony EventDispatcher component, which - * ships with the following license: - * - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * @link https://github.com/symfony/symfony/tree/master/src/Symfony/Component/EventDispatcher - */ -class Emitter implements EmitterInterface -{ - /** @var array */ - private $listeners = []; - - /** @var array */ - private $sorted = []; - - public function on($eventName, callable $listener, $priority = 0) - { - if ($priority === 'first') { - $priority = isset($this->listeners[$eventName]) - ? max(array_keys($this->listeners[$eventName])) + 1 - : 1; - } elseif ($priority === 'last') { - $priority = isset($this->listeners[$eventName]) - ? min(array_keys($this->listeners[$eventName])) - 1 - : -1; - } - - $this->listeners[$eventName][$priority][] = $listener; - unset($this->sorted[$eventName]); - } - - public function once($eventName, callable $listener, $priority = 0) - { - $onceListener = function ( - EventInterface $event, - $eventName - ) use (&$onceListener, $eventName, $listener, $priority) { - $this->removeListener($eventName, $onceListener); - $listener($event, $eventName, $this); - }; - - $this->on($eventName, $onceListener, $priority); - } - - public function removeListener($eventName, callable $listener) - { - if (empty($this->listeners[$eventName])) { - return; - } - - foreach ($this->listeners[$eventName] as $priority => $listeners) { - if (false !== ($key = array_search($listener, $listeners, true))) { - unset( - $this->listeners[$eventName][$priority][$key], - $this->sorted[$eventName] - ); - } - } - } - - public function listeners($eventName = null) - { - // Return all events in a sorted priority order - if ($eventName === null) { - foreach (array_keys($this->listeners) as $eventName) { - if (empty($this->sorted[$eventName])) { - $this->listeners($eventName); - } - } - return $this->sorted; - } - - // Return the listeners for a specific event, sorted in priority order - if (empty($this->sorted[$eventName])) { - $this->sorted[$eventName] = []; - if (isset($this->listeners[$eventName])) { - krsort($this->listeners[$eventName], SORT_NUMERIC); - foreach ($this->listeners[$eventName] as $listeners) { - foreach ($listeners as $listener) { - $this->sorted[$eventName][] = $listener; - } - } - } - } - - return $this->sorted[$eventName]; - } - - public function hasListeners($eventName) - { - return !empty($this->listeners[$eventName]); - } - - public function emit($eventName, EventInterface $event) - { - if (isset($this->listeners[$eventName])) { - foreach ($this->listeners($eventName) as $listener) { - $listener($event, $eventName); - if ($event->isPropagationStopped()) { - break; - } - } - } - - return $event; - } - - public function attach(SubscriberInterface $subscriber) - { - foreach ($subscriber->getEvents() as $eventName => $listeners) { - if (is_array($listeners[0])) { - foreach ($listeners as $listener) { - $this->on( - $eventName, - [$subscriber, $listener[0]], - isset($listener[1]) ? $listener[1] : 0 - ); - } - } else { - $this->on( - $eventName, - [$subscriber, $listeners[0]], - isset($listeners[1]) ? $listeners[1] : 0 - ); - } - } - } - - public function detach(SubscriberInterface $subscriber) - { - foreach ($subscriber->getEvents() as $eventName => $listener) { - $this->removeListener($eventName, [$subscriber, $listener[0]]); - } - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/EmitterInterface.php b/core/vendor/guzzlehttp/guzzle/src/Event/EmitterInterface.php deleted file mode 100644 index 9783efd15ae4a7d984431ffeecf44ab6fbdc2095..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Event/EmitterInterface.php +++ /dev/null @@ -1,96 +0,0 @@ -<?php -namespace GuzzleHttp\Event; - -/** - * Guzzle event emitter. - */ -interface EmitterInterface -{ - /** - * Binds a listener to a specific event. - * - * @param string $eventName Name of the event to bind to. - * @param callable $listener Listener to invoke when triggered. - * @param int|string $priority The higher this value, the earlier an event - * listener will be triggered in the chain (defaults to 0). You can - * pass "first" or "last" to dynamically specify the event priority - * based on the current event priorities associated with the given - * event name in the emitter. Use "first" to set the priority to the - * current highest priority plus one. Use "last" to set the priority to - * the current lowest event priority minus one. - */ - public function on($eventName, callable $listener, $priority = 0); - - /** - * Binds a listener to a specific event. After the listener is triggered - * once, it is removed as a listener. - * - * @param string $eventName Name of the event to bind to. - * @param callable $listener Listener to invoke when triggered. - * @param int $priority The higher this value, the earlier an event - * listener will be triggered in the chain (defaults to 0) - */ - public function once($eventName, callable $listener, $priority = 0); - - /** - * Removes an event listener from the specified event. - * - * @param string $eventName The event to remove a listener from - * @param callable $listener The listener to remove - */ - public function removeListener($eventName, callable $listener); - - /** - * Gets the listeners of a specific event or all listeners if no event is - * specified. - * - * @param string $eventName The name of the event. Pass null (the default) - * to retrieve all listeners. - * - * @return array The event listeners for the specified event, or all event - * listeners by event name. The format of the array when retrieving a - * specific event list is an array of callables. The format of the array - * when retrieving all listeners is an associative array of arrays of - * callables. - */ - public function listeners($eventName = null); - - /** - * Checks if the emitter has listeners by the given name. - * - * @param string $eventName The name of the event to check. - * - * @return bool - */ - public function hasListeners($eventName); - - /** - * Emits an event to all registered listeners. - * - * Each event that is bound to the emitted eventName receives a - * EventInterface, the name of the event, and the event emitter. - * - * @param string $eventName The name of the event to dispatch. - * @param EventInterface $event The event to pass to the event handlers/listeners. - * - * @return EventInterface Returns the provided event object - */ - public function emit($eventName, EventInterface $event); - - /** - * Attaches an event subscriber. - * - * The subscriber is asked for all the events it is interested in and added - * as an event listener for each event. - * - * @param SubscriberInterface $subscriber Subscriber to attach. - */ - public function attach(SubscriberInterface $subscriber); - - /** - * Detaches an event subscriber. - * - * @param SubscriberInterface $subscriber Subscriber to detach. - */ - public function detach(SubscriberInterface $subscriber); -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/EndEvent.php b/core/vendor/guzzlehttp/guzzle/src/Event/EndEvent.php deleted file mode 100644 index c89432b02d7ac1b85a080bfcea4eba794dadb235..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Event/EndEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php -namespace GuzzleHttp\Event; - -/** - * A terminal event that is emitted when a request transaction has ended. - * - * This event is emitted for both successful responses and responses that - * encountered an exception. You need to check if an exception is present - * in your listener to know the difference. - * - * You MAY intercept the response associated with the event if needed, but keep - * in mind that the "complete" event will not be triggered as a result. - */ -class EndEvent extends AbstractTransferEvent -{ - /** - * Get the exception that was encountered (if any). - * - * This method should be used to check if the request was sent successfully - * or if it encountered errors. - * - * @return \Exception|null - */ - public function getException() - { - return $this->transaction->exception; - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/ErrorEvent.php b/core/vendor/guzzlehttp/guzzle/src/Event/ErrorEvent.php deleted file mode 100644 index 7432134d03e935ee71af57e1e3743166a46c1c87..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Event/ErrorEvent.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php -namespace GuzzleHttp\Event; - -use GuzzleHttp\Exception\RequestException; - -/** - * Event emitted when an error occurs while sending a request. - * - * This event MAY be emitted multiple times. You MAY intercept the exception - * and inject a response into the event to rescue the request using the - * intercept() method of the event. - * - * This event allows the request to be retried using the "retry" method of the - * event. - */ -class ErrorEvent extends AbstractRetryableEvent -{ - /** - * Get the exception that was encountered - * - * @return RequestException - */ - public function getException() - { - return $this->transaction->exception; - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/EventInterface.php b/core/vendor/guzzlehttp/guzzle/src/Event/EventInterface.php deleted file mode 100644 index 97247e84c65610b8e6afa9d8fb4cd3ac9e34e428..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Event/EventInterface.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php -namespace GuzzleHttp\Event; - -/** - * Base event interface used when dispatching events to listeners using an - * event emitter. - */ -interface EventInterface -{ - /** - * Returns whether or not stopPropagation was called on the event. - * - * @return bool - * @see Event::stopPropagation - */ - public function isPropagationStopped(); - - /** - * Stops the propagation of the event, preventing subsequent listeners - * registered to the same event from being invoked. - */ - public function stopPropagation(); -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/HasEmitterInterface.php b/core/vendor/guzzlehttp/guzzle/src/Event/HasEmitterInterface.php deleted file mode 100644 index a1a86124997870c6e1774f2d2092cfeb710967af..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Event/HasEmitterInterface.php +++ /dev/null @@ -1,15 +0,0 @@ -<?php -namespace GuzzleHttp\Event; - -/** - * Holds an event emitter - */ -interface HasEmitterInterface -{ - /** - * Get the event emitter of the object - * - * @return EmitterInterface - */ - public function getEmitter(); -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/HasEmitterTrait.php b/core/vendor/guzzlehttp/guzzle/src/Event/HasEmitterTrait.php deleted file mode 100644 index 8e36ce385c454aaee132869c5c6384890aafeea2..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Event/HasEmitterTrait.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php -namespace GuzzleHttp\Event; - -/** - * Trait that implements the methods of HasEmitterInterface - */ -trait HasEmitterTrait -{ - /** @var EmitterInterface */ - private $emitter; - - public function getEmitter() - { - if (!$this->emitter) { - $this->emitter = new Emitter(); - } - - return $this->emitter; - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/ListenerAttacherTrait.php b/core/vendor/guzzlehttp/guzzle/src/Event/ListenerAttacherTrait.php deleted file mode 100644 index 407dc92dd5e7bede72121563e4038e21152debe1..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Event/ListenerAttacherTrait.php +++ /dev/null @@ -1,88 +0,0 @@ -<?php -namespace GuzzleHttp\Event; - -/** - * Trait that provides methods for extract event listeners specified in an array - * and attaching them to an emitter owned by the object or one of its direct - * dependencies. - */ -trait ListenerAttacherTrait -{ - /** - * Attaches event listeners and properly sets their priorities and whether - * or not they are are only executed once. - * - * @param HasEmitterInterface $object Object that has the event emitter. - * @param array $listeners Array of hashes representing event - * event listeners. Each item contains - * "name", "fn", "priority", & "once". - */ - private function attachListeners(HasEmitterInterface $object, array $listeners) - { - $emitter = $object->getEmitter(); - foreach ($listeners as $el) { - if ($el['once']) { - $emitter->once($el['name'], $el['fn'], $el['priority']); - } else { - $emitter->on($el['name'], $el['fn'], $el['priority']); - } - } - } - - /** - * Extracts the allowed events from the provided array, and ignores anything - * else in the array. The event listener must be specified as a callable or - * as an array of event listener data ("name", "fn", "priority", "once"). - * - * @param array $source Array containing callables or hashes of data to be - * prepared as event listeners. - * @param array $events Names of events to look for in the provided $source - * array. Other keys are ignored. - * @return array - */ - private function prepareListeners(array $source, array $events) - { - $listeners = []; - foreach ($events as $name) { - if (isset($source[$name])) { - $this->buildListener($name, $source[$name], $listeners); - } - } - - return $listeners; - } - - /** - * Creates a complete event listener definition from the provided array of - * listener data. Also works recursively if more than one listeners are - * contained in the provided array. - * - * @param string $name Name of the event the listener is for. - * @param array|callable $data Event listener data to prepare. - * @param array $listeners Array of listeners, passed by reference. - * - * @throws \InvalidArgumentException if the event data is malformed. - */ - private function buildListener($name, $data, &$listeners) - { - static $defaults = ['priority' => 0, 'once' => false]; - - // If a callable is provided, normalize it to the array format. - if (is_callable($data)) { - $data = ['fn' => $data]; - } - - // Prepare the listener and add it to the array, recursively. - if (isset($data['fn'])) { - $data['name'] = $name; - $listeners[] = $data + $defaults; - } elseif (is_array($data)) { - foreach ($data as $listenerData) { - $this->buildListener($name, $listenerData, $listeners); - } - } else { - throw new \InvalidArgumentException('Each event listener must be a ' - . 'callable or an associative array containing a "fn" key.'); - } - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/ProgressEvent.php b/core/vendor/guzzlehttp/guzzle/src/Event/ProgressEvent.php deleted file mode 100644 index 3fd0de4ac0544fb66fcadd31c5b2003ed8889440..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Event/ProgressEvent.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php -namespace GuzzleHttp\Event; - -use GuzzleHttp\Transaction; - -/** - * Event object emitted when upload or download progress is made. - * - * You can access the progress values using their corresponding public - * properties: - * - * - $downloadSize: The number of bytes that will be downloaded (if known) - * - $downloaded: The number of bytes that have been downloaded - * - $uploadSize: The number of bytes that will be uploaded (if known) - * - $uploaded: The number of bytes that have been uploaded - */ -class ProgressEvent extends AbstractRequestEvent -{ - /** @var int Amount of data to be downloaded */ - public $downloadSize; - - /** @var int Amount of data that has been downloaded */ - public $downloaded; - - /** @var int Amount of data to upload */ - public $uploadSize; - - /** @var int Amount of data that has been uploaded */ - public $uploaded; - - /** - * @param Transaction $transaction Transaction being sent. - * @param int $downloadSize Amount of data to download (if known) - * @param int $downloaded Amount of data that has been downloaded - * @param int $uploadSize Amount of data to upload (if known) - * @param int $uploaded Amount of data that had been uploaded - */ - public function __construct( - Transaction $transaction, - $downloadSize, - $downloaded, - $uploadSize, - $uploaded - ) { - parent::__construct($transaction); - $this->downloadSize = $downloadSize; - $this->downloaded = $downloaded; - $this->uploadSize = $uploadSize; - $this->uploaded = $uploaded; - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/RequestEvents.php b/core/vendor/guzzlehttp/guzzle/src/Event/RequestEvents.php deleted file mode 100644 index f51d420654aec2dd12770b0773b835e9aefad539..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Event/RequestEvents.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -namespace GuzzleHttp\Event; - -/** - * Contains methods used to manage the request event lifecycle. - */ -final class RequestEvents -{ - // Generic event priorities - const EARLY = 10000; - const LATE = -10000; - - // "before" priorities - const PREPARE_REQUEST = -100; - const SIGN_REQUEST = -10000; - - // "complete" and "error" response priorities - const VERIFY_RESPONSE = 100; - const REDIRECT_RESPONSE = 200; - - /** - * Converts an array of event options into a formatted array of valid event - * configuration. - * - * @param array $options Event array to convert - * @param array $events Event names to convert in the options array. - * @param mixed $handler Event handler to utilize - * - * @return array - * @throws \InvalidArgumentException if the event config is invalid - * @internal - */ - public static function convertEventArray( - array $options, - array $events, - $handler - ) { - foreach ($events as $name) { - if (!isset($options[$name])) { - $options[$name] = [$handler]; - } elseif (is_callable($options[$name])) { - $options[$name] = [$options[$name], $handler]; - } elseif (is_array($options[$name])) { - if (isset($options[$name]['fn'])) { - $options[$name] = [$options[$name], $handler]; - } else { - $options[$name][] = $handler; - } - } else { - throw new \InvalidArgumentException('Invalid event format'); - } - } - - return $options; - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/SubscriberInterface.php b/core/vendor/guzzlehttp/guzzle/src/Event/SubscriberInterface.php deleted file mode 100644 index 33d77021301841aabc7277d6a293957a2006f66d..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Event/SubscriberInterface.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php -namespace GuzzleHttp\Event; - -/** - * SubscriberInterface provides an array of events to an - * EventEmitterInterface when it is registered. The emitter then binds the - * listeners specified by the EventSubscriber. - * - * This interface is based on the SubscriberInterface of the Symfony. - * @link https://github.com/symfony/symfony/tree/master/src/Symfony/Component/EventDispatcher - */ -interface SubscriberInterface -{ - /** - * Returns an array of event names this subscriber wants to listen to. - * - * The returned array keys MUST map to an event name. Each array value - * MUST be an array in which the first element is the name of a function - * on the EventSubscriber OR an array of arrays in the aforementioned - * format. The second element in the array is optional, and if specified, - * designates the event priority. - * - * For example, the following are all valid: - * - * - ['eventName' => ['methodName']] - * - ['eventName' => ['methodName', $priority]] - * - ['eventName' => [['methodName'], ['otherMethod']] - * - ['eventName' => [['methodName'], ['otherMethod', $priority]] - * - ['eventName' => [['methodName', $priority], ['otherMethod', $priority]] - * - * @return array - */ - public function getEvents(); -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Exception/ConnectException.php b/core/vendor/guzzlehttp/guzzle/src/Exception/ConnectException.php index a91c5a68ed933706c97328d727c9775cc63f51ac..d33b0cc19d6f808fd16ad5f24982348b219cb999 100644 --- a/core/vendor/guzzlehttp/guzzle/src/Exception/ConnectException.php +++ b/core/vendor/guzzlehttp/guzzle/src/Exception/ConnectException.php @@ -1,4 +1,37 @@ <?php namespace GuzzleHttp\Exception; -class ConnectException extends RequestException {} +use Psr\Http\Message\RequestInterface; + +/** + * Exception thrown when a connection cannot be established. + * + * Note that no response is present for a ConnectException + */ +class ConnectException extends RequestException +{ + public function __construct( + $message, + RequestInterface $request, + \Exception $previous = null, + array $handlerContext = [] + ) { + parent::__construct($message, $request, null, $previous, $handlerContext); + } + + /** + * @return null + */ + public function getResponse() + { + return null; + } + + /** + * @return bool + */ + public function hasResponse() + { + return false; + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Exception/CouldNotRewindStreamException.php b/core/vendor/guzzlehttp/guzzle/src/Exception/CouldNotRewindStreamException.php deleted file mode 100644 index fbe2dcd7cc908a99b7c89673cb049fd09db50693..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Exception/CouldNotRewindStreamException.php +++ /dev/null @@ -1,4 +0,0 @@ -<?php -namespace GuzzleHttp\Exception; - -class CouldNotRewindStreamException extends RequestException {} diff --git a/core/vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php b/core/vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php new file mode 100644 index 0000000000000000000000000000000000000000..c82998e0d7ef06fad4bbf048e2a61fc921b768ed --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php @@ -0,0 +1,4 @@ +<?php +namespace GuzzleHttp\Exception; + +interface GuzzleException {} diff --git a/core/vendor/guzzlehttp/guzzle/src/Exception/ParseException.php b/core/vendor/guzzlehttp/guzzle/src/Exception/ParseException.php deleted file mode 100644 index e593e3d5e5ddb0b006b763498b5a5ac30f04ff02..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Exception/ParseException.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php -namespace GuzzleHttp\Exception; - -use GuzzleHttp\Message\ResponseInterface; - -/** - * Exception when a client is unable to parse the response body as XML or JSON - */ -class ParseException extends TransferException -{ - /** @var ResponseInterface */ - private $response; - - public function __construct( - $message = '', - ResponseInterface $response = null, - \Exception $previous = null - ) { - parent::__construct($message, 0, $previous); - $this->response = $response; - } - /** - * Get the associated response - * - * @return ResponseInterface|null - */ - public function getResponse() - { - return $this->response; - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php b/core/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php index f81d2483638f5755617522a28cae449501fe43f5..23dd02c42717e47a7fb679566439a451191a9317 100644 --- a/core/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php +++ b/core/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php @@ -1,11 +1,9 @@ <?php namespace GuzzleHttp\Exception; -use GuzzleHttp\Message\RequestInterface; -use GuzzleHttp\Message\ResponseInterface; -use GuzzleHttp\Ring\Exception\ConnectException; -use GuzzleHttp\Exception\ConnectException as HttpConnectException; -use GuzzleHttp\Ring\Future\FutureInterface; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; +use GuzzleHttp\Promise\PromiseInterface; /** * HTTP Request exception @@ -18,23 +16,28 @@ class RequestException extends TransferException /** @var ResponseInterface */ private $response; + /** @var array */ + private $handlerContext; + public function __construct( $message, RequestInterface $request, ResponseInterface $response = null, - \Exception $previous = null + \Exception $previous = null, + array $handlerContext = [] ) { // Set the code of the exception if the response is set and not future. - $code = $response && !($response instanceof FutureInterface) + $code = $response && !($response instanceof PromiseInterface) ? $response->getStatusCode() : 0; parent::__construct($message, $code, $previous); $this->request = $request; $this->response = $response; + $this->handlerContext = $handlerContext; } /** - * Wrap non-RequesExceptions with a RequestException + * Wrap non-RequestExceptions with a RequestException * * @param RequestInterface $request * @param \Exception $e @@ -43,13 +46,9 @@ public function __construct( */ public static function wrapException(RequestInterface $request, \Exception $e) { - if ($e instanceof RequestException) { - return $e; - } elseif ($e instanceof ConnectException) { - return new HttpConnectException($e->getMessage(), $request, null, $e); - } else { - return new RequestException($e->getMessage(), $request, null, $e); - } + return $e instanceof RequestException + ? $e + : new RequestException($e->getMessage(), $request, null, $e); } /** @@ -58,16 +57,24 @@ public static function wrapException(RequestInterface $request, \Exception $e) * @param RequestInterface $request Request * @param ResponseInterface $response Response received * @param \Exception $previous Previous exception + * @param array $ctx Optional handler context. * * @return self */ public static function create( RequestInterface $request, ResponseInterface $response = null, - \Exception $previous = null + \Exception $previous = null, + array $ctx = [] ) { if (!$response) { - return new self('Error completing request', $request, null, $previous); + return new self( + 'Error completing request', + $request, + null, + $previous, + $ctx + ); } $level = floor($response->getStatusCode() / 100); @@ -82,11 +89,12 @@ public static function create( $className = __CLASS__; } - $message = $label . ' [url] ' . $request->getUrl() + $message = $label . ' [url] ' . $request->getUri() + . ' [http method] ' . $request->getMethod() . ' [status code] ' . $response->getStatusCode() . ' [reason phrase] ' . $response->getReasonPhrase(); - return new $className($message, $request, $response, $previous); + return new $className($message, $request, $response, $previous, $ctx); } /** @@ -118,4 +126,19 @@ public function hasResponse() { return $this->response !== null; } + + /** + * Get contextual information about the error from the underlying handler. + * + * The contents of this array will vary depending on which handler you are + * using. It may also be just an empty array. Relying on this data will + * couple you to a specific handler, but can give more debug information + * when needed. + * + * @return array + */ + public function getHandlerContext() + { + return $this->handlerContext; + } } diff --git a/core/vendor/guzzlehttp/streams/src/Exception/SeekException.php b/core/vendor/guzzlehttp/guzzle/src/Exception/SeekException.php similarity index 75% rename from core/vendor/guzzlehttp/streams/src/Exception/SeekException.php rename to core/vendor/guzzlehttp/guzzle/src/Exception/SeekException.php index 3f6d5ebad5180af15686dca6df791e2456463358..a77c28926cf94c4232feeda5d3062b87b15dce53 100644 --- a/core/vendor/guzzlehttp/streams/src/Exception/SeekException.php +++ b/core/vendor/guzzlehttp/guzzle/src/Exception/SeekException.php @@ -1,12 +1,12 @@ <?php -namespace GuzzleHttp\Stream\Exception; +namespace GuzzleHttp\Exception; -use GuzzleHttp\Stream\StreamInterface; +use Psr\Http\Message\StreamInterface; /** * Exception thrown when a seek fails on a stream. */ -class SeekException extends \RuntimeException +class SeekException extends \RuntimeException implements GuzzleException { private $stream; diff --git a/core/vendor/guzzlehttp/guzzle/src/Exception/StateException.php b/core/vendor/guzzlehttp/guzzle/src/Exception/StateException.php deleted file mode 100644 index a7652a384fd198eb2f07b8a73e2cb264232a1e42..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Exception/StateException.php +++ /dev/null @@ -1,4 +0,0 @@ -<?php -namespace GuzzleHttp\Exception; - -class StateException extends TransferException {}; diff --git a/core/vendor/guzzlehttp/guzzle/src/Exception/TransferException.php b/core/vendor/guzzlehttp/guzzle/src/Exception/TransferException.php index 92d7de6917285fbd7962f82b206c8cd1e926176e..b92071ca20630d1a988249a9befc247ab4e3d2de 100644 --- a/core/vendor/guzzlehttp/guzzle/src/Exception/TransferException.php +++ b/core/vendor/guzzlehttp/guzzle/src/Exception/TransferException.php @@ -1,4 +1,4 @@ <?php namespace GuzzleHttp\Exception; -class TransferException extends \RuntimeException {} +class TransferException extends \RuntimeException implements GuzzleException {} diff --git a/core/vendor/guzzlehttp/guzzle/src/Exception/XmlParseException.php b/core/vendor/guzzlehttp/guzzle/src/Exception/XmlParseException.php deleted file mode 100644 index a4a1a0af4721127535c146f3d8d6abd37e8a79da..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Exception/XmlParseException.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -namespace GuzzleHttp\Exception; - -use GuzzleHttp\Message\ResponseInterface; - -/** - * Exception when a client is unable to parse the response body as XML - */ -class XmlParseException extends ParseException -{ - /** @var \LibXMLError */ - protected $error; - - public function __construct( - $message = '', - ResponseInterface $response = null, - \Exception $previous = null, - \LibXMLError $error = null - ) { - parent::__construct($message, $response, $previous); - $this->error = $error; - } - - /** - * Get the associated error - * - * @return \LibXMLError|null - */ - public function getError() - { - return $this->error; - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php b/core/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..dcf374d8e1943d80eab326a712e1d5c228df6ab9 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php @@ -0,0 +1,507 @@ +<?php +namespace GuzzleHttp\Handler; + +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Exception\ConnectException; +use GuzzleHttp\Promise\FulfilledPromise; +use GuzzleHttp\Promise\RejectedPromise; +use GuzzleHttp\Psr7; +use GuzzleHttp\Psr7\LazyOpenStream; +use Psr\Http\Message\RequestInterface; + +/** + * Creates curl resources from a request + */ +class CurlFactory implements CurlFactoryInterface +{ + /** @var array */ + private $handles; + + /** @var int Total number of idle handles to keep in cache */ + private $maxHandles; + + /** + * @param int $maxHandles Maximum number of idle handles. + */ + public function __construct($maxHandles) + { + $this->maxHandles = $maxHandles; + } + + public function create(RequestInterface $request, array $options) + { + $easy = new EasyHandle; + $easy->request = $request; + $easy->options = $options; + $conf = $this->getDefaultConf($easy); + $this->applyMethod($easy, $conf); + $this->applyHandlerOptions($easy, $conf); + $this->applyHeaders($easy, $conf); + unset($conf['_headers']); + + if (isset($options['curl']['body_as_string'])) { + $options['_body_as_string'] = $options['curl']['body_as_string']; + unset($options['curl']['body_as_string']); + } + + // Add handler options from the request configuration options + if (isset($options['curl'])) { + $conf += $options['curl']; + } + + $conf[CURLOPT_HEADERFUNCTION] = $this->createHeaderFn($easy); + $easy->handle = $this->handles + ? array_pop($this->handles) + : curl_init(); + curl_setopt_array($easy->handle, $conf); + + return $easy; + } + + public function release(EasyHandle $easy) + { + $resource = $easy->handle; + unset($easy->handle); + + if (count($this->handles) >= $this->maxHandles) { + curl_close($resource); + } else { + // Remove all callback functions as they can hold onto references + // and are not cleaned up by curl_reset. Using curl_setopt_array + // does not work for some reason, so removing each one + // individually. + curl_setopt($resource, CURLOPT_HEADERFUNCTION, null); + curl_setopt($resource, CURLOPT_READFUNCTION, null); + curl_setopt($resource, CURLOPT_WRITEFUNCTION, null); + curl_setopt($resource, CURLOPT_PROGRESSFUNCTION, null); + curl_reset($resource); + $this->handles[] = $resource; + } + } + + /** + * Completes a cURL transaction, either returning a response promise or a + * rejected promise. + * + * @param callable $handler + * @param EasyHandle $easy + * @param CurlFactoryInterface $factory Dictates how the handle is released + * + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public static function finish( + callable $handler, + EasyHandle $easy, + CurlFactoryInterface $factory + ) { + if (!$easy->response || $easy->errno) { + return self::finishError($handler, $easy, $factory); + } + + // Return the response if it is present and there is no error. + $factory->release($easy); + + // Rewind the body of the response if possible. + $body = $easy->response->getBody(); + if ($body->isSeekable()) { + $body->rewind(); + } + + return new FulfilledPromise($easy->response); + } + + private static function finishError( + callable $handler, + EasyHandle $easy, + CurlFactoryInterface $factory + ) { + // Get error information and release the handle to the factory. + $ctx = [ + 'errno' => $easy->errno, + 'error' => curl_error($easy->handle), + ] + curl_getinfo($easy->handle); + $factory->release($easy); + + // Retry when nothing is present or when curl failed to rewind. + if (empty($easy->options['_err_message']) + && (!$easy->errno || $easy->errno == 65) + ) { + return self::retryFailedRewind($handler, $easy, $ctx); + } + + return self::createRejection($easy, $ctx); + } + + private static function createRejection(EasyHandle $easy, array $ctx) + { + static $connectionErrors = [ + CURLE_OPERATION_TIMEOUTED => true, + CURLE_COULDNT_RESOLVE_HOST => true, + CURLE_COULDNT_CONNECT => true, + CURLE_SSL_CONNECT_ERROR => true, + CURLE_GOT_NOTHING => true, + ]; + + // If an exception was encountered during the onHeaders event, then + // return a rejected promise that wraps that exception. + if ($easy->onHeadersException) { + return new RejectedPromise( + new RequestException( + 'An error was encountered during the on_headers event', + $easy->request, + $easy->response, + $easy->onHeadersException, + $ctx + ) + ); + } + + $message = sprintf( + 'cURL error %s: %s (%s)', + $ctx['errno'], + $ctx['error'], + 'see http://curl.haxx.se/libcurl/c/libcurl-errors.html' + ); + + // Create a connection exception if it was a specific error code. + $error = isset($connectionErrors[$easy->errno]) + ? new ConnectException($message, $easy->request, null, $ctx) + : new RequestException($message, $easy->request, $easy->response, null, $ctx); + + return new RejectedPromise($error); + } + + private function getDefaultConf(EasyHandle $easy) + { + $conf = [ + '_headers' => $easy->request->getHeaders(), + CURLOPT_CUSTOMREQUEST => $easy->request->getMethod(), + CURLOPT_URL => (string) $easy->request->getUri(), + CURLOPT_RETURNTRANSFER => false, + CURLOPT_HEADER => false, + CURLOPT_CONNECTTIMEOUT => 150, + ]; + + if (defined('CURLOPT_PROTOCOLS')) { + $conf[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; + } + + $version = $easy->request->getProtocolVersion(); + if ($version == 1.1) { + $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_1; + } elseif ($version == 2.0) { + $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0; + } else { + $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0; + } + + return $conf; + } + + private function applyMethod(EasyHandle $easy, array &$conf) + { + $body = $easy->request->getBody(); + $size = $body->getSize(); + + if ($size === null || $size > 0) { + $this->applyBody($easy->request, $easy->options, $conf); + return; + } + + $method = $easy->request->getMethod(); + if ($method === 'PUT' || $method === 'POST') { + // See http://tools.ietf.org/html/rfc7230#section-3.3.2 + if (!$easy->request->hasHeader('Content-Length')) { + $conf[CURLOPT_HTTPHEADER][] = 'Content-Length: 0'; + } + } elseif ($method === 'HEAD') { + $conf[CURLOPT_NOBODY] = true; + unset( + $conf[CURLOPT_WRITEFUNCTION], + $conf[CURLOPT_READFUNCTION], + $conf[CURLOPT_FILE], + $conf[CURLOPT_INFILE] + ); + } + } + + private function applyBody(RequestInterface $request, array $options, array &$conf) + { + $size = $request->hasHeader('Content-Length') + ? (int) $request->getHeaderLine('Content-Length') + : null; + + // Send the body as a string if the size is less than 1MB OR if the + // [curl][body_as_string] request value is set. + if (($size !== null && $size < 1000000) || + !empty($options['_body_as_string']) + ) { + $conf[CURLOPT_POSTFIELDS] = (string) $request->getBody(); + // Don't duplicate the Content-Length header + $this->removeHeader('Content-Length', $conf); + $this->removeHeader('Transfer-Encoding', $conf); + } else { + $conf[CURLOPT_UPLOAD] = true; + if ($size !== null) { + $conf[CURLOPT_INFILESIZE] = $size; + $this->removeHeader('Content-Length', $conf); + } + $body = $request->getBody(); + $conf[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body) { + return $body->read($length); + }; + } + + // If the Expect header is not present, prevent curl from adding it + if (!$request->hasHeader('Expect')) { + $conf[CURLOPT_HTTPHEADER][] = 'Expect:'; + } + + // cURL sometimes adds a content-type by default. Prevent this. + if (!$request->hasHeader('Content-Type')) { + $conf[CURLOPT_HTTPHEADER][] = 'Content-Type:'; + } + } + + private function applyHeaders(EasyHandle $easy, array &$conf) + { + foreach ($conf['_headers'] as $name => $values) { + foreach ($values as $value) { + $conf[CURLOPT_HTTPHEADER][] = "$name: $value"; + } + } + + // Remove the Accept header if one was not set + if (!$easy->request->hasHeader('Accept')) { + $conf[CURLOPT_HTTPHEADER][] = 'Accept:'; + } + } + + /** + * Remove a header from the options array. + * + * @param string $name Case-insensitive header to remove + * @param array $options Array of options to modify + */ + private function removeHeader($name, array &$options) + { + foreach (array_keys($options['_headers']) as $key) { + if (!strcasecmp($key, $name)) { + unset($options['_headers'][$key]); + return; + } + } + } + + private function applyHandlerOptions(EasyHandle $easy, array &$conf) + { + $options = $easy->options; + if (isset($options['verify'])) { + if ($options['verify'] === false) { + unset($conf[CURLOPT_CAINFO]); + $conf[CURLOPT_SSL_VERIFYHOST] = 0; + $conf[CURLOPT_SSL_VERIFYPEER] = false; + } else { + $conf[CURLOPT_SSL_VERIFYHOST] = 2; + $conf[CURLOPT_SSL_VERIFYPEER] = true; + if (is_string($options['verify'])) { + $conf[CURLOPT_CAINFO] = $options['verify']; + if (!file_exists($options['verify'])) { + throw new \InvalidArgumentException( + "SSL CA bundle not found: {$options['verify']}" + ); + } + } + } + } + + if (!empty($options['decode_content'])) { + $accept = $easy->request->getHeaderLine('Accept-Encoding'); + if ($accept) { + $conf[CURLOPT_ENCODING] = $accept; + } else { + $conf[CURLOPT_ENCODING] = ''; + // Don't let curl send the header over the wire + $conf[CURLOPT_HTTPHEADER][] = 'Accept-Encoding:'; + } + } + + if (isset($options['sink'])) { + $sink = $options['sink']; + if (!is_string($sink)) { + $sink = \GuzzleHttp\Psr7\stream_for($sink); + } elseif (!is_dir(dirname($sink))) { + // Ensure that the directory exists before failing in curl. + throw new \RuntimeException(sprintf( + 'Directory %s does not exist for sink value of %s', + dirname($sink), + $sink + )); + } else { + $sink = new LazyOpenStream($sink, 'w+'); + } + $easy->sink = $sink; + $conf[CURLOPT_WRITEFUNCTION] = function ($ch, $write) use ($sink) { + return $sink->write($write); + }; + } else { + // Use a default temp stream if no sink was set. + $conf[CURLOPT_FILE] = fopen('php://temp', 'w+'); + $easy->sink = Psr7\stream_for($conf[CURLOPT_FILE]); + } + + if (isset($options['timeout'])) { + $conf[CURLOPT_TIMEOUT_MS] = $options['timeout'] * 1000; + } + + if (isset($options['connect_timeout'])) { + $conf[CURLOPT_CONNECTTIMEOUT_MS] = $options['connect_timeout'] * 1000; + } + + if (isset($options['proxy'])) { + if (!is_array($options['proxy'])) { + $conf[CURLOPT_PROXY] = $options['proxy']; + } elseif ($scheme = $easy->request->getUri()->getScheme()) { + if (isset($options['proxy'][$scheme])) { + $conf[CURLOPT_PROXY] = $options['proxy'][$scheme]; + } + } + } + + if (isset($options['cert'])) { + $cert = $options['cert']; + if (is_array($cert)) { + $conf[CURLOPT_SSLCERTPASSWD] = $cert[1]; + $cert = $cert[0]; + } + if (!file_exists($cert)) { + throw new \InvalidArgumentException( + "SSL certificate not found: {$cert}" + ); + } + $conf[CURLOPT_SSLCERT] = $cert; + } + + if (isset($options['ssl_key'])) { + $sslKey = $options['ssl_key']; + if (is_array($sslKey)) { + $conf[CURLOPT_SSLKEYPASSWD] = $sslKey[1]; + $sslKey = $sslKey[0]; + } + if (!file_exists($sslKey)) { + throw new \InvalidArgumentException( + "SSL private key not found: {$sslKey}" + ); + } + $conf[CURLOPT_SSLKEY] = $sslKey; + } + + if (isset($options['progress'])) { + $progress = $options['progress']; + if (!is_callable($progress)) { + throw new \InvalidArgumentException( + 'progress client option must be callable' + ); + } + $conf[CURLOPT_NOPROGRESS] = false; + $conf[CURLOPT_PROGRESSFUNCTION] = function () use ($progress) { + $args = func_get_args(); + // PHP 5.5 pushed the handle onto the start of the args + if (is_resource($args[0])) { + array_shift($args); + } + call_user_func_array($progress, $args); + }; + } + + if (!empty($options['debug'])) { + $conf[CURLOPT_STDERR] = \GuzzleHttp\debug_resource($options['debug']); + $conf[CURLOPT_VERBOSE] = true; + } + } + + /** + * This function ensures that a response was set on a transaction. If one + * was not set, then the request is retried if possible. This error + * typically means you are sending a payload, curl encountered a + * "Connection died, retrying a fresh connect" error, tried to rewind the + * stream, and then encountered a "necessary data rewind wasn't possible" + * error, causing the request to be sent through curl_multi_info_read() + * without an error status. + */ + private static function retryFailedRewind( + callable $handler, + EasyHandle $easy, + array $ctx + ) { + try { + // Only rewind if the body has been read from. + $body = $easy->request->getBody(); + if ($body->tell() > 0) { + $body->rewind(); + } + } catch (\RuntimeException $e) { + $ctx['error'] = 'The connection unexpectedly failed without ' + . 'providing an error. The request would have been retried, ' + . 'but attempting to rewind the request body failed. ' + . 'Exception: ' . $e; + return self::createRejection($easy, $ctx); + } + + // Retry no more than 3 times before giving up. + if (!isset($easy->options['_curl_retries'])) { + $easy->options['_curl_retries'] = 1; + } elseif ($easy->options['_curl_retries'] == 2) { + $ctx['error'] = 'The cURL request was retried 3 times ' + . 'and did not succeed. The most likely reason for the failure ' + . 'is that cURL was unable to rewind the body of the request ' + . 'and subsequent retries resulted in the same error. Turn on ' + . 'the debug option to see what went wrong. See ' + . 'https://bugs.php.net/bug.php?id=47204 for more information.'; + return self::createRejection($easy, $ctx); + } else { + $easy->options['_curl_retries']++; + } + + return $handler($easy->request, $easy->options); + } + + private function createHeaderFn(EasyHandle $easy) + { + if (!isset($easy->options['on_headers'])) { + $onHeaders = null; + } elseif (!is_callable($easy->options['on_headers'])) { + throw new \InvalidArgumentException('on_headers must be callable'); + } else { + $onHeaders = $easy->options['on_headers']; + } + + return function ($ch, $h) use ( + $onHeaders, + $easy, + &$startingResponse + ) { + $value = trim($h); + if ($value === '') { + $startingResponse = true; + $easy->createResponse(); + if ($onHeaders) { + try { + $onHeaders($easy->response); + } catch (\Exception $e) { + // Associate the exception with the handle and trigger + // a curl header write error by returning 0. + $easy->onHeadersException = $e; + return -1; + } + } + } elseif ($startingResponse) { + $startingResponse = false; + $easy->headers = [$value]; + } else { + $easy->headers[] = $value; + } + return strlen($h); + }; + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php b/core/vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..b0fc236850b5db204532665f3db1f39ae907c4fa --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php @@ -0,0 +1,27 @@ +<?php +namespace GuzzleHttp\Handler; + +use Psr\Http\Message\RequestInterface; + +interface CurlFactoryInterface +{ + /** + * Creates a cURL handle resource. + * + * @param RequestInterface $request Request + * @param array $options Transfer options + * + * @return EasyHandle + * @throws \RuntimeException when an option cannot be applied + */ + public function create(RequestInterface $request, array $options); + + /** + * Release an easy handle, allowing it to be reused or closed. + * + * This function must call unset on the easy handle's "handle" property. + * + * @param EasyHandle $easy + */ + public function release(EasyHandle $easy); +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Handler/CurlHandler.php b/core/vendor/guzzlehttp/guzzle/src/Handler/CurlHandler.php new file mode 100644 index 0000000000000000000000000000000000000000..43577da66e25de9e8ab4f9f4fb74ebe20b4afd4a --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Handler/CurlHandler.php @@ -0,0 +1,45 @@ +<?php +namespace GuzzleHttp\Handler; + +use GuzzleHttp\Psr7; +use Psr\Http\Message\RequestInterface; + +/** + * HTTP handler that uses cURL easy handles as a transport layer. + * + * When using the CurlHandler, custom curl options can be specified as an + * associative array of curl option constants mapping to values in the + * **curl** key of the "client" key of the request. + */ +class CurlHandler +{ + /** @var CurlFactoryInterface */ + private $factory; + + /** + * Accepts an associative array of options: + * + * - factory: Optional curl factory used to create cURL handles. + * + * @param array $options Array of options to use with the handler + */ + public function __construct(array $options = []) + { + $this->factory = isset($options['handle_factory']) + ? $options['handle_factory'] + : new CurlFactory(3); + } + + public function __invoke(RequestInterface $request, array $options) + { + if (isset($options['delay'])) { + usleep($options['delay'] * 1000); + } + + $easy = $this->factory->create($request, $options); + curl_exec($easy->handle); + $easy->errno = curl_errno($easy->handle); + + return CurlFactory::finish($this, $easy, $this->factory); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php b/core/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php new file mode 100644 index 0000000000000000000000000000000000000000..417850b44d3b5e832db12cee2591981fc350ee9d --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php @@ -0,0 +1,197 @@ +<?php +namespace GuzzleHttp\Handler; + +use GuzzleHttp\Promise as P; +use GuzzleHttp\Promise\Promise; +use GuzzleHttp\Psr7; +use Psr\Http\Message\RequestInterface; + +/** + * Returns an asynchronous response using curl_multi_* functions. + * + * When using the CurlMultiHandler, custom curl options can be specified as an + * associative array of curl option constants mapping to values in the + * **curl** key of the provided request options. + * + * @property resource $_mh Internal use only. Lazy loaded multi-handle. + */ +class CurlMultiHandler +{ + /** @var CurlFactoryInterface */ + private $factory; + private $selectTimeout; + private $active; + private $handles = []; + private $delays = []; + + /** + * This handler accepts the following options: + * + * - handle_factory: An optional factory used to create curl handles + * - select_timeout: Optional timeout (in seconds) to block before timing + * out while selecting curl handles. Defaults to 1 second. + * + * @param array $options + */ + public function __construct(array $options = []) + { + $this->factory = isset($options['handle_factory']) + ? $options['handle_factory'] : new CurlFactory(50); + $this->selectTimeout = isset($options['select_timeout']) + ? $options['select_timeout'] : 1; + } + + public function __get($name) + { + if ($name === '_mh') { + return $this->_mh = curl_multi_init(); + } + + throw new \BadMethodCallException(); + } + + public function __destruct() + { + if (isset($this->_mh)) { + curl_multi_close($this->_mh); + unset($this->_mh); + } + } + + public function __invoke(RequestInterface $request, array $options) + { + $easy = $this->factory->create($request, $options); + $id = (int) $easy->handle; + + $promise = new Promise( + [$this, 'execute'], + function () use ($id) { return $this->cancel($id); } + ); + + $this->addRequest(['easy' => $easy, 'deferred' => $promise]); + + return $promise; + } + + /** + * Ticks the curl event loop. + */ + public function tick() + { + // Add any delayed handles if needed. + if ($this->delays) { + $currentTime = microtime(true); + foreach ($this->delays as $id => $delay) { + if ($currentTime >= $delay) { + unset($this->delays[$id]); + curl_multi_add_handle( + $this->_mh, + $this->handles[$id]['easy']->handle + ); + } + } + } + + // Step through the task queue which may add additional requests. + P\queue()->run(); + + if ($this->active && + curl_multi_select($this->_mh, $this->selectTimeout) === -1 + ) { + // Perform a usleep if a select returns -1. + // See: https://bugs.php.net/bug.php?id=61141 + usleep(250); + } + + while (curl_multi_exec($this->_mh, $this->active) === CURLM_CALL_MULTI_PERFORM); + + $this->processMessages(); + } + + /** + * Runs until all outstanding connections have completed. + */ + public function execute() + { + $queue = P\queue(); + + while ($this->handles || !$queue->isEmpty()) { + // If there are no transfers, then sleep for the next delay + if (!$this->active && $this->delays) { + usleep($this->timeToNext()); + } + $this->tick(); + } + } + + private function addRequest(array $entry) + { + $easy = $entry['easy']; + $id = (int) $easy->handle; + $this->handles[$id] = $entry; + if (empty($easy->options['delay'])) { + curl_multi_add_handle($this->_mh, $easy->handle); + } else { + $this->delays[$id] = microtime(true) + ($easy->options['delay'] / 1000); + } + } + + /** + * Cancels a handle from sending and removes references to it. + * + * @param int $id Handle ID to cancel and remove. + * + * @return bool True on success, false on failure. + */ + private function cancel($id) + { + // Cannot cancel if it has been processed. + if (!isset($this->handles[$id])) { + return false; + } + + $handle = $this->handles[$id]['easy']->handle; + unset($this->delays[$id], $this->handles[$id]); + curl_multi_remove_handle($this->_mh, $handle); + curl_close($handle); + + return true; + } + + private function processMessages() + { + while ($done = curl_multi_info_read($this->_mh)) { + $id = (int) $done['handle']; + curl_multi_remove_handle($this->_mh, $done['handle']); + + if (!isset($this->handles[$id])) { + // Probably was cancelled. + continue; + } + + $entry = $this->handles[$id]; + unset($this->handles[$id], $this->delays[$id]); + $entry['easy']->errno = $done['result']; + $entry['deferred']->resolve( + CurlFactory::finish( + $this, + $entry['easy'], + $this->factory + ) + ); + } + } + + private function timeToNext() + { + $currentTime = microtime(true); + $nextTime = PHP_INT_MAX; + foreach ($this->delays as $time) { + if ($time < $nextTime) { + $nextTime = $time; + } + } + + return max(0, $currentTime - $nextTime); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php b/core/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php new file mode 100644 index 0000000000000000000000000000000000000000..35228f009adb6e4c4f6b5495d2d5beba4edde005 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php @@ -0,0 +1,87 @@ +<?php +namespace GuzzleHttp\Handler; + +use GuzzleHttp\Psr7\Response; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\StreamInterface; + +/** + * Represents a cURL easy handle and the data it populates. + * + * @internal + */ +final class EasyHandle +{ + /** @var resource cURL resource */ + public $handle; + + /** @var StreamInterface Where data is being written */ + public $sink; + + /** @var array Received HTTP headers so far */ + public $headers = []; + + /** @var ResponseInterface Received response (if any) */ + public $response; + + /** @var RequestInterface Request being sent */ + public $request; + + /** @var array Request options */ + public $options = []; + + /** @var int cURL error number (if any) */ + public $errno = 0; + + /** @var \Exception Exception during on_headers (if any) */ + public $onHeadersException; + + /** + * Attach a response to the easy handle based on the received headers. + * + * @throws \RuntimeException if no headers have been received. + */ + public function createResponse() + { + if (empty($this->headers)) { + throw new \RuntimeException('No headers have been received'); + } + + // HTTP-version SP status-code SP reason-phrase + $startLine = explode(' ', array_shift($this->headers), 3); + $headers = \GuzzleHttp\headers_from_lines($this->headers); + $normalizedKeys = \GuzzleHttp\normalize_header_keys($headers); + + if (!empty($this->options['decode_content']) + && isset($normalizedKeys['content-encoding']) + ) { + unset($headers[$normalizedKeys['content-encoding']]); + if (isset($normalizedKeys['content-length'])) { + $bodyLength = (int) $this->sink->getSize(); + if ($bodyLength) { + $headers[$normalizedKeys['content-length']] = $bodyLength; + } else { + unset($headers[$normalizedKeys['content-length']]); + } + } + } + + // Attach a response to the easy handle with the parsed headers. + $this->response = new Response( + $startLine[1], + $headers, + $this->sink, + substr($startLine[0], 5), + isset($startLine[2]) ? (int) $startLine[2] : null + ); + } + + public function __get($name) + { + $msg = $name === 'handle' + ? 'The EasyHandle has been released' + : 'Invalid property: ' . $name; + throw new \BadMethodCallException($msg); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php b/core/vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php new file mode 100644 index 0000000000000000000000000000000000000000..9854546eb8faea3afa5b8513ed5f8acf06acffae --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php @@ -0,0 +1,152 @@ +<?php +namespace GuzzleHttp\Handler; + +use GuzzleHttp\HandlerStack; +use GuzzleHttp\Promise\PromiseInterface; +use GuzzleHttp\Promise\RejectedPromise; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * Handler that returns responses or throw exceptions from a queue. + */ +class MockHandler implements \Countable +{ + private $queue; + private $lastRequest; + private $lastOptions; + private $onFulfilled; + private $onRejected; + + /** + * Creates a new MockHandler that uses the default handler stack list of + * middlewares. + * + * @param array $queue Array of responses, callables, or exceptions. + * @param callable $onFulfilled Callback to invoke when the return value is fulfilled. + * @param callable $onRejected Callback to invoke when the return value is rejected. + * + * @return MockHandler + */ + public static function createWithMiddleware( + array $queue = null, + callable $onFulfilled = null, + callable $onRejected = null + ) { + return HandlerStack::create(new self($queue, $onFulfilled, $onRejected)); + } + + /** + * The passed in value must be an array of + * {@see Psr7\Http\Message\ResponseInterface} objects, Exceptions, + * callables, or Promises. + * + * @param array $queue + * @param callable $onFulfilled Callback to invoke when the return value is fulfilled. + * @param callable $onRejected Callback to invoke when the return value is rejected. + */ + public function __construct( + array $queue = null, + callable $onFulfilled = null, + callable $onRejected = null + ) { + $this->onFulfilled = $onFulfilled; + $this->onRejected = $onRejected; + + if ($queue) { + call_user_func_array([$this, 'append'], $queue); + } + } + + public function __invoke(RequestInterface $request, array $options) + { + if (!$this->queue) { + throw new \OutOfBoundsException('Mock queue is empty'); + } + + if (isset($options['delay'])) { + usleep($options['delay'] * 1000); + } + + $this->lastRequest = $request; + $this->lastOptions = $options; + $response = array_shift($this->queue); + + if (is_callable($response)) { + $response = $response($request, $options); + } + + $response = $response instanceof \Exception + ? new RejectedPromise($response) + : \GuzzleHttp\Promise\promise_for($response); + + if (!$this->onFulfilled && !$this->onRejected) { + return $response; + } + + return $response->then( + function ($value) { + if ($this->onFulfilled) { + call_user_func($this->onFulfilled, $value); + } + return $value; + }, + function ($reason) { + if ($this->onRejected) { + call_user_func($this->onRejected, $reason); + } + return new RejectedPromise($reason); + } + ); + } + + /** + * Adds one or more variadic requests, exceptions, callables, or promises + * to the queue. + */ + public function append() + { + foreach (func_get_args() as $value) { + if ($value instanceof ResponseInterface + || $value instanceof \Exception + || $value instanceof PromiseInterface + || is_callable($value) + ) { + $this->queue[] = $value; + } else { + throw new \InvalidArgumentException('Expected a response or ' + . 'exception. Found ' . \GuzzleHttp\describe_type($value)); + } + } + } + + /** + * Get the last received request. + * + * @return RequestInterface + */ + public function getLastRequest() + { + return $this->lastRequest; + } + + /** + * Get the last received request options. + * + * @return RequestInterface + */ + public function getLastOptions() + { + return $this->lastOptions; + } + + /** + * Returns the number of remaining items in the queue. + * + * @return int + */ + public function count() + { + return count($this->queue); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php b/core/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php new file mode 100644 index 0000000000000000000000000000000000000000..9bd76d251f3a330094370daecb0dc878e2099e7a --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php @@ -0,0 +1,54 @@ +<?php +namespace GuzzleHttp\Handler; + +use Psr\Http\Message\RequestInterface; + +/** + * Provides basic proxies for handlers. + */ +class Proxy +{ + /** + * Sends synchronous requests to a specific handler while sending all other + * requests to another handler. + * + * @param callable $default Handler used for normal responses + * @param callable $sync Handler used for synchronous responses. + * + * @return callable Returns the composed handler. + */ + public static function wrapSync( + callable $default, + callable $sync + ) { + return function (RequestInterface $request, array $options) use ($default, $sync) { + return empty($options['sync']) + ? $default($request, $options) + : $sync($request, $options); + }; + } + + /** + * Sends streaming requests to a streaming compatible handler while sending + * all other requests to a default handler. + * + * This, for example, could be useful for taking advantage of the + * performance benefits of curl while still supporting true streaming + * through the StreamHandler. + * + * @param callable $default Handler used for non-streaming responses + * @param callable $streaming Handler used for streaming responses + * + * @return callable Returns the composed handler. + */ + public static function wrapStreaming( + callable $default, + callable $streaming + ) { + return function (RequestInterface $request, array $options) use ($default, $streaming) { + return empty($options['stream']) + ? $default($request, $options) + : $streaming($request, $options); + }; + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php b/core/vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php new file mode 100644 index 0000000000000000000000000000000000000000..b94d1b7b62bd77af843d0de7ab391c1043663f5b --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php @@ -0,0 +1,411 @@ +<?php +namespace GuzzleHttp\Handler; + +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Exception\ConnectException; +use GuzzleHttp\Promise\FulfilledPromise; +use GuzzleHttp\Promise\RejectedPromise; +use GuzzleHttp\Promise\PromiseInterface; +use GuzzleHttp\Psr7; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\StreamInterface; + +/** + * HTTP handler that uses PHP's HTTP stream wrapper. + */ +class StreamHandler +{ + private $lastHeaders = []; + + /** + * Sends an HTTP request. + * + * @param RequestInterface $request Request to send. + * @param array $options Request transfer options. + * + * @return PromiseInterface + */ + public function __invoke(RequestInterface $request, array $options) + { + // Sleep if there is a delay specified. + if (isset($options['delay'])) { + usleep($options['delay'] * 1000); + } + + try { + // Does not support the expect header. + $request = $request->withoutHeader('Expect'); + $stream = $this->createStream($request, $options); + return $this->createResponse($request, $options, $stream); + } catch (\InvalidArgumentException $e) { + throw $e; + } catch (\Exception $e) { + // Determine if the error was a networking error. + $message = $e->getMessage(); + // This list can probably get more comprehensive. + if (strpos($message, 'getaddrinfo') // DNS lookup failed + || strpos($message, 'Connection refused') + || strpos($message, "couldn't connect to host") // error on HHVM + ) { + $e = new ConnectException($e->getMessage(), $request, $e); + } + return new RejectedPromise( + RequestException::wrapException($request, $e) + ); + } + } + + private function createResponse( + RequestInterface $request, + array $options, + $stream + ) { + $hdrs = $this->lastHeaders; + $this->lastHeaders = []; + $parts = explode(' ', array_shift($hdrs), 3); + $ver = explode('/', $parts[0])[1]; + $status = $parts[1]; + $reason = isset($parts[2]) ? $parts[2] : null; + $headers = \GuzzleHttp\headers_from_lines($hdrs); + list ($stream, $headers) = $this->checkDecode($options, $headers, $stream); + $stream = Psr7\stream_for($stream); + $sink = $this->createSink($stream, $options); + $response = new Psr7\Response($status, $headers, $sink, $ver, $reason); + + if (isset($options['on_headers'])) { + try { + $options['on_headers']($response); + } catch (\Exception $e) { + $msg = 'An error was encountered during the on_headers event'; + $ex = new RequestException($msg, $request, $response, $e); + return new RejectedPromise($ex); + } + } + + if ($sink !== $stream) { + $this->drain($stream, $sink); + } + + return new FulfilledPromise($response); + } + + private function createSink(StreamInterface $stream, array $options) + { + if (!empty($options['stream'])) { + return $stream; + } + + $sink = isset($options['sink']) + ? $options['sink'] + : fopen('php://temp', 'r+'); + + return is_string($sink) + ? new Psr7\Stream(Psr7\try_fopen($sink, 'r+')) + : Psr7\stream_for($sink); + } + + private function checkDecode(array $options, array $headers, $stream) + { + // Automatically decode responses when instructed. + if (!empty($options['decode_content'])) { + $normalizedKeys = \GuzzleHttp\normalize_header_keys($headers); + if (isset($normalizedKeys['content-encoding'])) { + $encoding = $headers[$normalizedKeys['content-encoding']]; + if ($encoding[0] == 'gzip' || $encoding[0] == 'deflate') { + $stream = new Psr7\InflateStream( + Psr7\stream_for($stream) + ); + // Remove content-encoding header + unset($headers[$normalizedKeys['content-encoding']]); + // Fix content-length header + if (isset($normalizedKeys['content-length'])) { + $length = (int) $stream->getSize(); + if ($length == 0) { + unset($headers[$normalizedKeys['content-length']]); + } else { + $headers[$normalizedKeys['content-length']] = [$length]; + } + } + } + } + } + + return [$stream, $headers]; + } + + /** + * Drains the source stream into the "sink" client option. + * + * @param StreamInterface $source + * @param StreamInterface $sink + * + * @return StreamInterface + * @throws \RuntimeException when the sink option is invalid. + */ + private function drain(StreamInterface $source, StreamInterface $sink) + { + Psr7\copy_to_stream($source, $sink); + $sink->seek(0); + $source->close(); + + return $sink; + } + + /** + * Create a resource and check to ensure it was created successfully + * + * @param callable $callback Callable that returns stream resource + * + * @return resource + * @throws \RuntimeException on error + */ + private function createResource(callable $callback) + { + $errors = null; + set_error_handler(function ($_, $msg, $file, $line) use (&$errors) { + $errors[] = [ + 'message' => $msg, + 'file' => $file, + 'line' => $line + ]; + return true; + }); + + $resource = $callback(); + restore_error_handler(); + + if (!$resource) { + $message = 'Error creating resource: '; + foreach ($errors as $err) { + foreach ($err as $key => $value) { + $message .= "[$key] $value" . PHP_EOL; + } + } + throw new \RuntimeException(trim($message)); + } + + return $resource; + } + + private function createStream(RequestInterface $request, array $options) + { + static $methods; + if (!$methods) { + $methods = array_flip(get_class_methods(__CLASS__)); + } + + // HTTP/1.1 streams using the PHP stream wrapper require a + // Connection: close header + if ($request->getProtocolVersion() == '1.1' + && !$request->hasHeader('Connection') + ) { + $request = $request->withHeader('Connection', 'close'); + } + + // Ensure SSL is verified by default + if (!isset($options['verify'])) { + $options['verify'] = true; + } + + $params = []; + $context = $this->getDefaultContext($request, $options); + + if (isset($options['on_headers']) && !is_callable($options['on_headers'])) { + throw new \InvalidArgumentException('on_headers must be callable'); + } + + if (!empty($options)) { + foreach ($options as $key => $value) { + $method = "add_{$key}"; + if (isset($methods[$method])) { + $this->{$method}($request, $context, $value, $params); + } + } + } + + if (isset($options['stream_context'])) { + if (!is_array($options['stream_context'])) { + throw new \InvalidArgumentException('stream_context must be an array'); + } + $context = array_replace_recursive( + $context, + $options['stream_context'] + ); + } + + $context = $this->createResource( + function () use ($context, $params) { + return stream_context_create($context, $params); + } + ); + + return $this->createResource( + function () use ($request, &$http_response_header, $context) { + $resource = fopen($request->getUri(), 'r', null, $context); + $this->lastHeaders = $http_response_header; + return $resource; + } + ); + } + + private function getDefaultContext(RequestInterface $request) + { + $headers = ''; + foreach ($request->getHeaders() as $name => $value) { + foreach ($value as $val) { + $headers .= "$name: $val\r\n"; + } + } + + $context = [ + 'http' => [ + 'method' => $request->getMethod(), + 'header' => $headers, + 'protocol_version' => $request->getProtocolVersion(), + 'ignore_errors' => true, + 'follow_location' => 0, + ], + ]; + + $body = (string) $request->getBody(); + + if (!empty($body)) { + $context['http']['content'] = $body; + // Prevent the HTTP handler from adding a Content-Type header. + if (!$request->hasHeader('Content-Type')) { + $context['http']['header'] .= "Content-Type:\r\n"; + } + } + + $context['http']['header'] = rtrim($context['http']['header']); + + return $context; + } + + private function add_proxy(RequestInterface $request, &$options, $value, &$params) + { + if (!is_array($value)) { + $options['http']['proxy'] = $value; + } else { + $scheme = $request->getUri()->getScheme(); + if (isset($value[$scheme])) { + $options['http']['proxy'] = $value[$scheme]; + } + } + } + + private function add_timeout(RequestInterface $request, &$options, $value, &$params) + { + $options['http']['timeout'] = $value; + } + + private function add_verify(RequestInterface $request, &$options, $value, &$params) + { + if ($value === true) { + // PHP 5.6 or greater will find the system cert by default. When + // < 5.6, use the Guzzle bundled cacert. + if (PHP_VERSION_ID < 50600) { + $options['ssl']['cafile'] = \GuzzleHttp\default_ca_bundle(); + } + } elseif (is_string($value)) { + $options['ssl']['cafile'] = $value; + if (!file_exists($value)) { + throw new \RuntimeException("SSL CA bundle not found: $value"); + } + } elseif ($value === false) { + $options['ssl']['verify_peer'] = false; + return; + } else { + throw new \InvalidArgumentException('Invalid verify request option'); + } + + $options['ssl']['verify_peer'] = true; + $options['ssl']['allow_self_signed'] = false; + } + + private function add_cert(RequestInterface $request, &$options, $value, &$params) + { + if (is_array($value)) { + $options['ssl']['passphrase'] = $value[1]; + $value = $value[0]; + } + + if (!file_exists($value)) { + throw new \RuntimeException("SSL certificate not found: {$value}"); + } + + $options['ssl']['local_cert'] = $value; + } + + private function add_progress(RequestInterface $request, &$options, $value, &$params) + { + $this->addNotification( + $params, + function ($code, $a, $b, $c, $transferred, $total) use ($value) { + if ($code == STREAM_NOTIFY_PROGRESS) { + $value($total, $transferred, null, null); + } + } + ); + } + + private function add_debug(RequestInterface $request, &$options, $value, &$params) + { + if ($value === false) { + return; + } + + static $map = [ + STREAM_NOTIFY_CONNECT => 'CONNECT', + STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED', + STREAM_NOTIFY_AUTH_RESULT => 'AUTH_RESULT', + STREAM_NOTIFY_MIME_TYPE_IS => 'MIME_TYPE_IS', + STREAM_NOTIFY_FILE_SIZE_IS => 'FILE_SIZE_IS', + STREAM_NOTIFY_REDIRECTED => 'REDIRECTED', + STREAM_NOTIFY_PROGRESS => 'PROGRESS', + STREAM_NOTIFY_FAILURE => 'FAILURE', + STREAM_NOTIFY_COMPLETED => 'COMPLETED', + STREAM_NOTIFY_RESOLVE => 'RESOLVE', + ]; + static $args = ['severity', 'message', 'message_code', + 'bytes_transferred', 'bytes_max']; + + $value = \GuzzleHttp\debug_resource($value); + $ident = $request->getMethod() . ' ' . $request->getUri(); + $this->addNotification( + $params, + function () use ($ident, $value, $map, $args) { + $passed = func_get_args(); + $code = array_shift($passed); + fprintf($value, '<%s> [%s] ', $ident, $map[$code]); + foreach (array_filter($passed) as $i => $v) { + fwrite($value, $args[$i] . ': "' . $v . '" '); + } + fwrite($value, "\n"); + } + ); + } + + private function addNotification(array &$params, callable $notify) + { + // Wrap the existing function if needed. + if (!isset($params['notification'])) { + $params['notification'] = $notify; + } else { + $params['notification'] = $this->callArray([ + $params['notification'], + $notify + ]); + } + } + + private function callArray(array $functions) + { + return function () use ($functions) { + $args = func_get_args(); + foreach ($functions as $fn) { + call_user_func_array($fn, $args); + } + }; + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/HandlerStack.php b/core/vendor/guzzlehttp/guzzle/src/HandlerStack.php new file mode 100644 index 0000000000000000000000000000000000000000..f85184953a3720fe4bb86f1f2434bafee25f65de --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/HandlerStack.php @@ -0,0 +1,272 @@ +<?php +namespace GuzzleHttp; + +use Psr\Http\Message\RequestInterface; + +/** + * Creates a composed Guzzle handler function by stacking middlewares on top of + * an HTTP handler function. + */ +class HandlerStack +{ + /** @var callable */ + private $handler; + + /** @var array */ + private $stack = []; + + /** @var callable|null */ + private $cached; + + /** + * Creates a default handler stack that can be used by clients. + * + * The returned handler will wrap the provided handler or use the most + * appropriate default handler for you system. The returned HandlerStack has + * support for cookies, redirects, HTTP error exceptions, and preparing a body + * before sending. + * + * The returned handler stack can be passed to a client in the "handler" + * option. + * + * @param callable $handler HTTP handler function to use with the stack. If no + * handler is provided, the best handler for your + * system will be utilized. + * + * @return HandlerStack + */ + public static function create(callable $handler = null) + { + $stack = new self($handler ?: choose_handler()); + $stack->push(Middleware::httpErrors(), 'http_errors'); + $stack->push(Middleware::redirect(), 'allow_redirects'); + $stack->push(Middleware::cookies(), 'cookies'); + $stack->push(Middleware::prepareBody(), 'prepare_body'); + + return $stack; + } + + /** + * @param callable $handler Underlying HTTP handler. + */ + public function __construct(callable $handler = null) + { + $this->handler = $handler; + } + + /** + * Invokes the handler stack as a composed handler + * + * @param RequestInterface $request + * @param array $options + */ + public function __invoke(RequestInterface $request, array $options) + { + if (!$this->cached) { + $this->cached = $this->resolve(); + } + + $handler = $this->cached; + return $handler($request, $options); + } + + /** + * Dumps a string representation of the stack. + * + * @return string + */ + public function __toString() + { + $depth = 0; + $stack = []; + if ($this->handler) { + $stack[] = "0) Handler: " . $this->debugCallable($this->handler); + } + + $result = ''; + foreach (array_reverse($this->stack) as $tuple) { + $depth++; + $str = "{$depth}) Name: '{$tuple[1]}', "; + $str .= "Function: " . $this->debugCallable($tuple[0]); + $result = "> {$str}\n{$result}"; + $stack[] = $str; + } + + foreach (array_keys($stack) as $k) { + $result .= "< {$stack[$k]}\n"; + } + + return $result; + } + + /** + * Set the HTTP handler that actually returns a promise. + * + * @param callable $handler Accepts a request and array of options and + * returns a Promise. + */ + public function setHandler(callable $handler) + { + $this->handler = $handler; + $this->cached = null; + } + + /** + * Returns true if the builder has a handler. + * + * @return bool + */ + public function hasHandler() + { + return (bool) $this->handler; + } + + /** + * Unshift a middleware to the bottom of the stack. + * + * @param callable $middleware Middleware function + * @param string $name Name to register for this middleware. + */ + public function unshift(callable $middleware, $name = null) + { + array_unshift($this->stack, [$middleware, $name]); + $this->cached = null; + } + + /** + * Push a middleware to the top of the stack. + * + * @param callable $middleware Middleware function + * @param string $name Name to register for this middleware. + */ + public function push(callable $middleware, $name = '') + { + $this->stack[] = [$middleware, $name]; + $this->cached = null; + } + + /** + * Add a middleware before another middleware by name. + * + * @param string $findName Middleware to find + * @param callable $middleware Middleware function + * @param string $withName Name to register for this middleware. + */ + public function before($findName, callable $middleware, $withName = '') + { + $this->splice($findName, $withName, $middleware, true); + } + + /** + * Add a middleware after another middleware by name. + * + * @param string $findName Middleware to find + * @param callable $middleware Middleware function + * @param string $withName Name to register for this middleware. + */ + public function after($findName, callable $middleware, $withName = '') + { + $this->splice($findName, $withName, $middleware, false); + } + + /** + * Remove a middleware by instance or name from the stack. + * + * @param callable|string $remove Middleware to remove by instance or name. + */ + public function remove($remove) + { + $this->cached = null; + $idx = is_callable($remove) ? 0 : 1; + $this->stack = array_values(array_filter( + $this->stack, + function ($tuple) use ($idx, $remove) { + return $tuple[$idx] !== $remove; + } + )); + } + + /** + * Compose the middleware and handler into a single callable function. + * + * @return callable + */ + public function resolve() + { + if (!($prev = $this->handler)) { + throw new \LogicException('No handler has been specified'); + } + + foreach (array_reverse($this->stack) as $fn) { + $prev = $fn[0]($prev); + } + + return $prev; + } + + /** + * @param $name + * @return int + */ + private function findByName($name) + { + foreach ($this->stack as $k => $v) { + if ($v[1] === $name) { + return $k; + } + } + + throw new \InvalidArgumentException("Middleware not found: $name"); + } + + /** + * Splices a function into the middleware list at a specific position. + * + * @param $findName + * @param $withName + * @param callable $middleware + * @param $before + */ + private function splice($findName, $withName, callable $middleware, $before) + { + $this->cached = null; + $idx = $this->findByName($findName); + $tuple = [$middleware, $withName]; + + if ($before) { + if ($idx === 0) { + array_unshift($this->stack, $tuple); + } else { + $replacement = [$tuple, $this->stack[$idx]]; + array_splice($this->stack, $idx, 1, $replacement); + } + } elseif ($idx === count($this->stack) - 1) { + $this->stack[] = $tuple; + } else { + $replacement = [$this->stack[$idx], $tuple]; + array_splice($this->stack, $idx, 1, $replacement); + } + } + + /** + * Provides a debug string for a given callable. + * + * @param array|callable $fn Function to write as a string. + * + * @return string + */ + private function debugCallable($fn) + { + if (is_string($fn)) { + return "callable({$fn})"; + } + + if (is_array($fn)) { + return is_string($fn[0]) + ? "callable({$fn[0]}::{$fn[1]})" + : "callable(['" . get_class($fn[0]) . "', '{$fn[1]}'])"; + } + + return 'callable(' . spl_object_hash($fn) . ')'; + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/HasDataTrait.php b/core/vendor/guzzlehttp/guzzle/src/HasDataTrait.php deleted file mode 100644 index 020dfc9aba2661461a142b820b6ffd8be38538e2..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/HasDataTrait.php +++ /dev/null @@ -1,75 +0,0 @@ -<?php -namespace GuzzleHttp; - -/** - * Trait implementing ToArrayInterface, \ArrayAccess, \Countable, - * \IteratorAggregate, and some path style methods. - */ -trait HasDataTrait -{ - /** @var array */ - protected $data = []; - - public function getIterator() - { - return new \ArrayIterator($this->data); - } - - public function offsetGet($offset) - { - return isset($this->data[$offset]) ? $this->data[$offset] : null; - } - - public function offsetSet($offset, $value) - { - $this->data[$offset] = $value; - } - - public function offsetExists($offset) - { - return isset($this->data[$offset]); - } - - public function offsetUnset($offset) - { - unset($this->data[$offset]); - } - - public function toArray() - { - return $this->data; - } - - public function count() - { - return count($this->data); - } - - /** - * Get a value from the collection using a path syntax to retrieve nested - * data. - * - * @param string $path Path to traverse and retrieve a value from - * - * @return mixed|null - */ - public function getPath($path) - { - return Utils::getPath($this->data, $path); - } - - /** - * Set a value into a nested array key. Keys will be created as needed to - * set the value. - * - * @param string $path Path to set - * @param mixed $value Value to set at the key - * - * @throws \RuntimeException when trying to setPath using a nested path - * that travels through a scalar value - */ - public function setPath($path, $value) - { - Utils::setPath($this->data, $path, $value); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Message/AbstractMessage.php b/core/vendor/guzzlehttp/guzzle/src/Message/AbstractMessage.php deleted file mode 100644 index 0c675758dd2a029289d106d8b808676f06e8730d..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Message/AbstractMessage.php +++ /dev/null @@ -1,253 +0,0 @@ -<?php -namespace GuzzleHttp\Message; - -use GuzzleHttp\Stream\StreamInterface; - -abstract class AbstractMessage implements MessageInterface -{ - /** @var array HTTP header collection */ - private $headers = []; - - /** @var array mapping a lowercase header name to its name over the wire */ - private $headerNames = []; - - /** @var StreamInterface Message body */ - private $body; - - /** @var string HTTP protocol version of the message */ - private $protocolVersion = '1.1'; - - public function __toString() - { - return static::getStartLineAndHeaders($this) - . "\r\n\r\n" . $this->getBody(); - } - - public function getProtocolVersion() - { - return $this->protocolVersion; - } - - public function getBody() - { - return $this->body; - } - - public function setBody(StreamInterface $body = null) - { - if ($body === null) { - // Setting a null body will remove the body of the request - $this->removeHeader('Content-Length'); - $this->removeHeader('Transfer-Encoding'); - } - - $this->body = $body; - } - - public function addHeader($header, $value) - { - if (is_array($value)) { - $current = array_merge($this->getHeaderAsArray($header), $value); - } else { - $current = $this->getHeaderAsArray($header); - $current[] = (string) $value; - } - - $this->setHeader($header, $current); - } - - public function addHeaders(array $headers) - { - foreach ($headers as $name => $header) { - $this->addHeader($name, $header); - } - } - - public function getHeader($header) - { - $name = strtolower($header); - return isset($this->headers[$name]) - ? implode(', ', $this->headers[$name]) - : ''; - } - - public function getHeaderAsArray($header) - { - $name = strtolower($header); - return isset($this->headers[$name]) ? $this->headers[$name] : []; - } - - public function getHeaders() - { - $headers = []; - foreach ($this->headers as $name => $values) { - $headers[$this->headerNames[$name]] = $values; - } - - return $headers; - } - - public function setHeader($header, $value) - { - $header = trim($header); - $name = strtolower($header); - $this->headerNames[$name] = $header; - - if (is_array($value)) { - foreach ($value as &$v) { - $v = trim($v); - } - $this->headers[$name] = $value; - } else { - $this->headers[$name] = [trim($value)]; - } - } - - public function setHeaders(array $headers) - { - $this->headers = $this->headerNames = []; - foreach ($headers as $key => $value) { - $this->setHeader($key, $value); - } - } - - public function hasHeader($header) - { - return isset($this->headers[strtolower($header)]); - } - - public function removeHeader($header) - { - $name = strtolower($header); - unset($this->headers[$name], $this->headerNames[$name]); - } - - /** - * Parse an array of header values containing ";" separated data into an - * array of associative arrays representing the header key value pair - * data of the header. When a parameter does not contain a value, but just - * contains a key, this function will inject a key with a '' string value. - * - * @param MessageInterface $message That contains the header - * @param string $header Header to retrieve from the message - * - * @return array Returns the parsed header values. - */ - public static function parseHeader(MessageInterface $message, $header) - { - static $trimmed = "\"' \n\t\r"; - $params = $matches = []; - - foreach (self::normalizeHeader($message, $header) as $val) { - $part = []; - foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) { - if (preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) { - $m = $matches[0]; - if (isset($m[1])) { - $part[trim($m[0], $trimmed)] = trim($m[1], $trimmed); - } else { - $part[] = trim($m[0], $trimmed); - } - } - } - if ($part) { - $params[] = $part; - } - } - - return $params; - } - - /** - * Converts an array of header values that may contain comma separated - * headers into an array of headers with no comma separated values. - * - * @param MessageInterface $message That contains the header - * @param string $header Header to retrieve from the message - * - * @return array Returns the normalized header field values. - */ - public static function normalizeHeader(MessageInterface $message, $header) - { - $h = $message->getHeaderAsArray($header); - for ($i = 0, $total = count($h); $i < $total; $i++) { - if (strpos($h[$i], ',') === false) { - continue; - } - foreach (preg_split('/,(?=([^"]*"[^"]*")*[^"]*$)/', $h[$i]) as $v) { - $h[] = trim($v); - } - unset($h[$i]); - } - - return $h; - } - - /** - * Gets the start-line and headers of a message as a string - * - * @param MessageInterface $message - * - * @return string - */ - public static function getStartLineAndHeaders(MessageInterface $message) - { - return static::getStartLine($message) - . self::getHeadersAsString($message); - } - - /** - * Gets the headers of a message as a string - * - * @param MessageInterface $message - * - * @return string - */ - public static function getHeadersAsString(MessageInterface $message) - { - $result = ''; - foreach ($message->getHeaders() as $name => $values) { - $result .= "\r\n{$name}: " . implode(', ', $values); - } - - return $result; - } - - /** - * Gets the start line of a message - * - * @param MessageInterface $message - * - * @return string - * @throws \InvalidArgumentException - */ - public static function getStartLine(MessageInterface $message) - { - if ($message instanceof RequestInterface) { - return trim($message->getMethod() . ' ' - . $message->getResource()) - . ' HTTP/' . $message->getProtocolVersion(); - } elseif ($message instanceof ResponseInterface) { - return 'HTTP/' . $message->getProtocolVersion() . ' ' - . $message->getStatusCode() . ' ' - . $message->getReasonPhrase(); - } else { - throw new \InvalidArgumentException('Unknown message type'); - } - } - - /** - * Accepts and modifies the options provided to the message in the - * constructor. - * - * Can be overridden in subclasses as necessary. - * - * @param array $options Options array passed by reference. - */ - protected function handleOptions(array &$options) - { - if (isset($options['protocol_version'])) { - $this->protocolVersion = $options['protocol_version']; - } - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Message/AppliesHeadersInterface.php b/core/vendor/guzzlehttp/guzzle/src/Message/AppliesHeadersInterface.php deleted file mode 100644 index ca42f20f309eb0f8611c474bf679366a8e3afb13..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Message/AppliesHeadersInterface.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php -namespace GuzzleHttp\Message; - -/** - * Applies headers to a request. - * - * This interface can be used with Guzzle streams to apply body specific - * headers to a request during the PREPARE_REQUEST priority of the before event - * - * NOTE: a body that implements this interface will prevent a default - * content-type from being added to a request during the before event. If you - * want a default content-type to be added, then it will need to be done - * manually (e.g., using {@see GuzzleHttp\Mimetypes}). - */ -interface AppliesHeadersInterface -{ - /** - * Apply headers to a request appropriate for the current state of the - * object. - * - * @param RequestInterface $request Request - */ - public function applyRequestHeaders(RequestInterface $request); -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Message/FutureResponse.php b/core/vendor/guzzlehttp/guzzle/src/Message/FutureResponse.php deleted file mode 100644 index 5e5037e1b921369f58846e3091a852e825ec96a9..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Message/FutureResponse.php +++ /dev/null @@ -1,158 +0,0 @@ -<?php -namespace GuzzleHttp\Message; - -use GuzzleHttp\Ring\Future\MagicFutureTrait; -use GuzzleHttp\Ring\Future\FutureInterface; -use GuzzleHttp\Stream\StreamInterface; - -/** - * Represents a response that has not been fulfilled. - * - * When created, you must provide a function that is used to dereference the - * future result and return it's value. The function has no arguments and MUST - * return an instance of a {@see GuzzleHttp\Message\ResponseInterface} object. - * - * You can optionally provide a function in the constructor that can be used to - * cancel the future from completing if possible. This function has no - * arguments and returns a boolean value representing whether or not the - * response could be cancelled. - * - * @property ResponseInterface $_value - */ -class FutureResponse implements ResponseInterface, FutureInterface -{ - use MagicFutureTrait; - - /** - * Returns a FutureResponse that wraps another future. - * - * @param FutureInterface $future Future to wrap with a new future - * @param callable $onFulfilled Invoked when the future fulfilled - * @param callable $onRejected Invoked when the future rejected - * @param callable $onProgress Invoked when the future progresses - * - * @return FutureResponse - */ - public static function proxy( - FutureInterface $future, - callable $onFulfilled = null, - callable $onRejected = null, - callable $onProgress = null - ) { - return new FutureResponse( - $future->then($onFulfilled, $onRejected, $onProgress), - [$future, 'wait'], - [$future, 'cancel'] - ); - } - - public function getStatusCode() - { - return $this->_value->getStatusCode(); - } - - public function setStatusCode($code) - { - $this->_value->setStatusCode($code); - } - - public function getReasonPhrase() - { - return $this->_value->getReasonPhrase(); - } - - public function setReasonPhrase($phrase) - { - $this->_value->setReasonPhrase($phrase); - } - - public function getEffectiveUrl() - { - return $this->_value->getEffectiveUrl(); - } - - public function setEffectiveUrl($url) - { - $this->_value->setEffectiveUrl($url); - } - - public function json(array $config = []) - { - return $this->_value->json($config); - } - - public function xml(array $config = []) - { - return $this->_value->xml($config); - } - - public function __toString() - { - try { - return $this->_value->__toString(); - } catch (\Exception $e) { - trigger_error($e->getMessage(), E_USER_WARNING); - return ''; - } - } - - public function getProtocolVersion() - { - return $this->_value->getProtocolVersion(); - } - - public function setBody(StreamInterface $body = null) - { - $this->_value->setBody($body); - } - - public function getBody() - { - return $this->_value->getBody(); - } - - public function getHeaders() - { - return $this->_value->getHeaders(); - } - - public function getHeader($header) - { - return $this->_value->getHeader($header); - } - - public function getHeaderAsArray($header) - { - return $this->_value->getHeaderAsArray($header); - } - - public function hasHeader($header) - { - return $this->_value->hasHeader($header); - } - - public function removeHeader($header) - { - $this->_value->removeHeader($header); - } - - public function addHeader($header, $value) - { - $this->_value->addHeader($header, $value); - } - - public function addHeaders(array $headers) - { - $this->_value->addHeaders($headers); - } - - public function setHeader($header, $value) - { - $this->_value->setHeader($header, $value); - } - - public function setHeaders(array $headers) - { - $this->_value->setHeaders($headers); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Message/MessageFactory.php b/core/vendor/guzzlehttp/guzzle/src/Message/MessageFactory.php deleted file mode 100644 index 85984e2dd9aa5d1722d0144eef772397c8d280eb..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Message/MessageFactory.php +++ /dev/null @@ -1,364 +0,0 @@ -<?php -namespace GuzzleHttp\Message; - -use GuzzleHttp\Cookie\CookieJar; -use GuzzleHttp\Cookie\CookieJarInterface; -use GuzzleHttp\Event\ListenerAttacherTrait; -use GuzzleHttp\Post\PostBody; -use GuzzleHttp\Post\PostFile; -use GuzzleHttp\Post\PostFileInterface; -use GuzzleHttp\Query; -use GuzzleHttp\Stream\Stream; -use GuzzleHttp\Subscriber\Cookie; -use GuzzleHttp\Subscriber\HttpError; -use GuzzleHttp\Subscriber\Redirect; -use GuzzleHttp\Url; -use \InvalidArgumentException as Iae; - -/** - * Default HTTP request factory used to create Request and Response objects. - */ -class MessageFactory implements MessageFactoryInterface -{ - use ListenerAttacherTrait; - - /** @var HttpError */ - private $errorPlugin; - - /** @var Redirect */ - private $redirectPlugin; - - /** @var array */ - private $customOptions; - - /** @var array Request options passed through to request Config object */ - private static $configMap = [ - 'connect_timeout' => 1, 'timeout' => 1, 'verify' => 1, 'ssl_key' => 1, - 'cert' => 1, 'proxy' => 1, 'debug' => 1, 'save_to' => 1, 'stream' => 1, - 'expect' => 1, 'future' => 1 - ]; - - /** @var array Default allow_redirects request option settings */ - private static $defaultRedirect = [ - 'max' => 5, - 'strict' => false, - 'referer' => false, - 'protocols' => ['http', 'https'] - ]; - - /** - * @param array $customOptions Associative array of custom request option - * names mapping to functions used to apply - * the option. The function accepts the request - * and the option value to apply. - */ - public function __construct(array $customOptions = []) - { - $this->errorPlugin = new HttpError(); - $this->redirectPlugin = new Redirect(); - $this->customOptions = $customOptions; - } - - public function createResponse( - $statusCode, - array $headers = [], - $body = null, - array $options = [] - ) { - if (null !== $body) { - $body = Stream::factory($body); - } - - return new Response($statusCode, $headers, $body, $options); - } - - public function createRequest($method, $url, array $options = []) - { - // Handle the request protocol version option that needs to be - // specified in the request constructor. - if (isset($options['version'])) { - $options['config']['protocol_version'] = $options['version']; - unset($options['version']); - } - - $request = new Request($method, $url, [], null, - isset($options['config']) ? $options['config'] : []); - - unset($options['config']); - - // Use a POST body by default - if ($method == 'POST' - && !isset($options['body']) - && !isset($options['json']) - ) { - $options['body'] = []; - } - - if ($options) { - $this->applyOptions($request, $options); - } - - return $request; - } - - /** - * Create a request or response object from an HTTP message string - * - * @param string $message Message to parse - * - * @return RequestInterface|ResponseInterface - * @throws \InvalidArgumentException if unable to parse a message - */ - public function fromMessage($message) - { - static $parser; - if (!$parser) { - $parser = new MessageParser(); - } - - // Parse a response - if (strtoupper(substr($message, 0, 4)) == 'HTTP') { - $data = $parser->parseResponse($message); - return $this->createResponse( - $data['code'], - $data['headers'], - $data['body'] === '' ? null : $data['body'], - $data - ); - } - - // Parse a request - if (!($data = ($parser->parseRequest($message)))) { - throw new \InvalidArgumentException('Unable to parse request'); - } - - return $this->createRequest( - $data['method'], - Url::buildUrl($data['request_url']), - [ - 'headers' => $data['headers'], - 'body' => $data['body'] === '' ? null : $data['body'], - 'config' => [ - 'protocol_version' => $data['protocol_version'] - ] - ] - ); - } - - /** - * Apply POST fields and files to a request to attempt to give an accurate - * representation. - * - * @param RequestInterface $request Request to update - * @param array $body Body to apply - */ - protected function addPostData(RequestInterface $request, array $body) - { - static $fields = ['string' => true, 'array' => true, 'NULL' => true, - 'boolean' => true, 'double' => true, 'integer' => true]; - - $post = new PostBody(); - foreach ($body as $key => $value) { - if (isset($fields[gettype($value)])) { - $post->setField($key, $value); - } elseif ($value instanceof PostFileInterface) { - $post->addFile($value); - } else { - $post->addFile(new PostFile($key, $value)); - } - } - - if ($request->getHeader('Content-Type') == 'multipart/form-data') { - $post->forceMultipartUpload(true); - } - - $request->setBody($post); - } - - protected function applyOptions( - RequestInterface $request, - array $options = [] - ) { - $config = $request->getConfig(); - $emitter = $request->getEmitter(); - - foreach ($options as $key => $value) { - - if (isset(self::$configMap[$key])) { - $config[$key] = $value; - continue; - } - - switch ($key) { - - case 'allow_redirects': - - if ($value === false) { - continue; - } - - if ($value === true) { - $value = self::$defaultRedirect; - } elseif (!is_array($value)) { - throw new Iae('allow_redirects must be true, false, or array'); - } else { - // Merge the default settings with the provided settings - $value += self::$defaultRedirect; - } - - $config['redirect'] = $value; - $emitter->attach($this->redirectPlugin); - break; - - case 'decode_content': - - if ($value === false) { - continue; - } - - $config['decode_content'] = true; - if ($value !== true) { - $request->setHeader('Accept-Encoding', $value); - } - break; - - case 'headers': - - if (!is_array($value)) { - throw new Iae('header value must be an array'); - } - foreach ($value as $k => $v) { - $request->setHeader($k, $v); - } - break; - - case 'exceptions': - - if ($value === true) { - $emitter->attach($this->errorPlugin); - } - break; - - case 'body': - - if (is_array($value)) { - $this->addPostData($request, $value); - } elseif ($value !== null) { - $request->setBody(Stream::factory($value)); - } - break; - - case 'auth': - - if (!$value) { - continue; - } - - if (is_array($value)) { - $type = isset($value[2]) ? strtolower($value[2]) : 'basic'; - } else { - $type = strtolower($value); - } - - $config['auth'] = $value; - - if ($type == 'basic') { - $request->setHeader( - 'Authorization', - 'Basic ' . base64_encode("$value[0]:$value[1]") - ); - } elseif ($type == 'digest') { - // @todo: Do not rely on curl - $config->setPath('curl/' . CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); - $config->setPath('curl/' . CURLOPT_USERPWD, "$value[0]:$value[1]"); - } - break; - - case 'query': - - if ($value instanceof Query) { - $original = $request->getQuery(); - // Do not overwrite existing query string variables by - // overwriting the object with the query string data passed - // in the URL - $value->overwriteWith($original->toArray()); - $request->setQuery($value); - } elseif (is_array($value)) { - // Do not overwrite existing query string variables - $query = $request->getQuery(); - foreach ($value as $k => $v) { - if (!isset($query[$k])) { - $query[$k] = $v; - } - } - } else { - throw new Iae('query must be an array or Query object'); - } - break; - - case 'cookies': - - if ($value === true) { - static $cookie = null; - if (!$cookie) { - $cookie = new Cookie(); - } - $emitter->attach($cookie); - } elseif (is_array($value)) { - $emitter->attach( - new Cookie(CookieJar::fromArray($value, $request->getHost())) - ); - } elseif ($value instanceof CookieJarInterface) { - $emitter->attach(new Cookie($value)); - } elseif ($value !== false) { - throw new Iae('cookies must be an array, true, or CookieJarInterface'); - } - break; - - case 'events': - - if (!is_array($value)) { - throw new Iae('events must be an array'); - } - - $this->attachListeners($request, - $this->prepareListeners( - $value, - ['before', 'complete', 'error', 'progress', 'end'] - ) - ); - break; - - case 'subscribers': - - if (!is_array($value)) { - throw new Iae('subscribers must be an array'); - } - - foreach ($value as $subscribers) { - $emitter->attach($subscribers); - } - break; - - case 'json': - - $request->setBody(Stream::factory(json_encode($value))); - if (!$request->hasHeader('Content-Type')) { - $request->setHeader('Content-Type', 'application/json'); - } - break; - - default: - - // Check for custom handler functions. - if (isset($this->customOptions[$key])) { - $fn = $this->customOptions[$key]; - $fn($request, $value); - continue; - } - - throw new Iae("No method can handle the {$key} config key"); - } - } - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Message/MessageFactoryInterface.php b/core/vendor/guzzlehttp/guzzle/src/Message/MessageFactoryInterface.php deleted file mode 100644 index 57c43e5cd7555d0c73612b8e9708634ed593c30a..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Message/MessageFactoryInterface.php +++ /dev/null @@ -1,71 +0,0 @@ -<?php -namespace GuzzleHttp\Message; - -use GuzzleHttp\Url; - -/** - * Request and response factory - */ -interface MessageFactoryInterface -{ - /** - * Creates a response - * - * @param string $statusCode HTTP status code - * @param array $headers Response headers - * @param mixed $body Response body - * @param array $options Response options - * - protocol_version: HTTP protocol version - * - header_factory: Factory used to create headers - * - And any other options used by a concrete message implementation - * - * @return ResponseInterface - */ - public function createResponse( - $statusCode, - array $headers = [], - $body = null, - array $options = [] - ); - - /** - * Create a new request based on the HTTP method. - * - * This method accepts an associative array of request options. Below is a - * brief description of each parameter. See - * http://docs.guzzlephp.org/clients.html#request-options for a much more - * in-depth description of each parameter. - * - * - headers: Associative array of headers to add to the request - * - body: string|resource|array|StreamInterface request body to send - * - json: mixed Uploads JSON encoded data using an application/json Content-Type header. - * - query: Associative array of query string values to add to the request - * - auth: array|string HTTP auth settings (user, pass[, type="basic"]) - * - version: The HTTP protocol version to use with the request - * - cookies: true|false|CookieJarInterface To enable or disable cookies - * - allow_redirects: true|false|array Controls HTTP redirects - * - save_to: string|resource|StreamInterface Where the response is saved - * - events: Associative array of event names to callables or arrays - * - subscribers: Array of event subscribers to add to the request - * - exceptions: Specifies whether or not exceptions are thrown for HTTP protocol errors - * - timeout: Timeout of the request in seconds. Use 0 to wait indefinitely - * - connect_timeout: Number of seconds to wait while trying to connect. (0 to wait indefinitely) - * - verify: SSL validation. True/False or the path to a PEM file - * - cert: Path a SSL cert or array of (path, pwd) - * - ssl_key: Path to a private SSL key or array of (path, pwd) - * - proxy: Specify an HTTP proxy or hash of protocols to proxies - * - debug: Set to true or a resource to view handler specific debug info - * - stream: Set to true to stream a response body rather than download it all up front - * - expect: true/false/integer Controls the "Expect: 100-Continue" header - * - config: Associative array of request config collection options - * - decode_content: true/false/string to control decoding content-encoding responses - * - * @param string $method HTTP method (GET, POST, PUT, etc.) - * @param string|Url $url HTTP URL to connect to - * @param array $options Array of options to apply to the request - * - * @return RequestInterface - * @link http://docs.guzzlephp.org/clients.html#request-options - */ - public function createRequest($method, $url, array $options = []); -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Message/MessageInterface.php b/core/vendor/guzzlehttp/guzzle/src/Message/MessageInterface.php deleted file mode 100644 index b2b472b6edddd0a3f8cd8f667d1691c9f4244fcf..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Message/MessageInterface.php +++ /dev/null @@ -1,136 +0,0 @@ -<?php -namespace GuzzleHttp\Message; - -use GuzzleHttp\Stream\StreamInterface; - -/** - * Request and response message interface - */ -interface MessageInterface -{ - /** - * Get a string representation of the message - * - * @return string - */ - public function __toString(); - - /** - * Get the HTTP protocol version of the message - * - * @return string - */ - public function getProtocolVersion(); - - /** - * Sets the body of the message. - * - * The body MUST be a StreamInterface object. Setting the body to null MUST - * remove the existing body. - * - * @param StreamInterface|null $body Body. - */ - public function setBody(StreamInterface $body = null); - - /** - * Get the body of the message - * - * @return StreamInterface|null - */ - public function getBody(); - - /** - * Gets all message headers. - * - * The keys represent the header name as it will be sent over the wire, and - * each value is an array of strings associated with the header. - * - * // Represent the headers as a string - * foreach ($message->getHeaders() as $name => $values) { - * echo $name . ": " . implode(", ", $values); - * } - * - * @return array Returns an associative array of the message's headers. - */ - public function getHeaders(); - - /** - * Retrieve a header by the given case-insensitive name. - * - * @param string $header Case-insensitive header name. - * - * @return string - */ - public function getHeader($header); - - /** - * Retrieves a header by the given case-insensitive name as an array of strings. - * - * @param string $header Case-insensitive header name. - * - * @return string[] - */ - public function getHeaderAsArray($header); - - /** - * Checks if a header exists by the given case-insensitive name. - * - * @param string $header Case-insensitive header name. - * - * @return bool Returns true if any header names match the given header - * name using a case-insensitive string comparison. Returns false if - * no matching header name is found in the message. - */ - public function hasHeader($header); - - /** - * Remove a specific header by case-insensitive name. - * - * @param string $header Case-insensitive header name. - */ - public function removeHeader($header); - - /** - * Appends a header value to any existing values associated with the - * given header name. - * - * @param string $header Header name to add - * @param string $value Value of the header - */ - public function addHeader($header, $value); - - /** - * Merges in an associative array of headers. - * - * Each array key MUST be a string representing the case-insensitive name - * of a header. Each value MUST be either a string or an array of strings. - * For each value, the value is appended to any existing header of the same - * name, or, if a header does not already exist by the given name, then the - * header is added. - * - * @param array $headers Associative array of headers to add to the message - */ - public function addHeaders(array $headers); - - /** - * Sets a header, replacing any existing values of any headers with the - * same case-insensitive name. - * - * The header values MUST be a string or an array of strings. - * - * @param string $header Header name - * @param string|array $value Header value(s) - */ - public function setHeader($header, $value); - - /** - * Sets headers, replacing any headers that have already been set on the - * message. - * - * The array keys MUST be a string. The array values must be either a - * string or an array of strings. - * - * @param array $headers Headers to set. - */ - public function setHeaders(array $headers); -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Message/MessageParser.php b/core/vendor/guzzlehttp/guzzle/src/Message/MessageParser.php deleted file mode 100644 index c3cc195e3293645732fa72edfedfb3cb7486670d..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Message/MessageParser.php +++ /dev/null @@ -1,171 +0,0 @@ -<?php -namespace GuzzleHttp\Message; - -/** - * Request and response parser used by Guzzle - */ -class MessageParser -{ - /** - * Parse an HTTP request message into an associative array of parts. - * - * @param string $message HTTP request to parse - * - * @return array|bool Returns false if the message is invalid - */ - public function parseRequest($message) - { - if (!($parts = $this->parseMessage($message))) { - return false; - } - - // Parse the protocol and protocol version - if (isset($parts['start_line'][2])) { - $startParts = explode('/', $parts['start_line'][2]); - $protocol = strtoupper($startParts[0]); - $version = isset($startParts[1]) ? $startParts[1] : '1.1'; - } else { - $protocol = 'HTTP'; - $version = '1.1'; - } - - $parsed = [ - 'method' => strtoupper($parts['start_line'][0]), - 'protocol' => $protocol, - 'protocol_version' => $version, - 'headers' => $parts['headers'], - 'body' => $parts['body'] - ]; - - $parsed['request_url'] = $this->getUrlPartsFromMessage( - (isset($parts['start_line'][1]) ? $parts['start_line'][1] : ''), $parsed); - - return $parsed; - } - - /** - * Parse an HTTP response message into an associative array of parts. - * - * @param string $message HTTP response to parse - * - * @return array|bool Returns false if the message is invalid - */ - public function parseResponse($message) - { - if (!($parts = $this->parseMessage($message))) { - return false; - } - - list($protocol, $version) = explode('/', trim($parts['start_line'][0])); - - return [ - 'protocol' => $protocol, - 'protocol_version' => $version, - 'code' => $parts['start_line'][1], - 'reason_phrase' => isset($parts['start_line'][2]) ? $parts['start_line'][2] : '', - 'headers' => $parts['headers'], - 'body' => $parts['body'] - ]; - } - - /** - * Parse a message into parts - * - * @param string $message Message to parse - * - * @return array|bool - */ - private function parseMessage($message) - { - if (!$message) { - return false; - } - - $startLine = null; - $headers = []; - $body = ''; - - // Iterate over each line in the message, accounting for line endings - $lines = preg_split('/(\\r?\\n)/', $message, -1, PREG_SPLIT_DELIM_CAPTURE); - for ($i = 0, $totalLines = count($lines); $i < $totalLines; $i += 2) { - - $line = $lines[$i]; - - // If two line breaks were encountered, then this is the end of body - if (empty($line)) { - if ($i < $totalLines - 1) { - $body = implode('', array_slice($lines, $i + 2)); - } - break; - } - - // Parse message headers - if (!$startLine) { - $startLine = explode(' ', $line, 3); - } elseif (strpos($line, ':')) { - $parts = explode(':', $line, 2); - $key = trim($parts[0]); - $value = isset($parts[1]) ? trim($parts[1]) : ''; - if (!isset($headers[$key])) { - $headers[$key] = $value; - } elseif (!is_array($headers[$key])) { - $headers[$key] = [$headers[$key], $value]; - } else { - $headers[$key][] = $value; - } - } - } - - return [ - 'start_line' => $startLine, - 'headers' => $headers, - 'body' => $body - ]; - } - - /** - * Create URL parts from HTTP message parts - * - * @param string $requestUrl Associated URL - * @param array $parts HTTP message parts - * - * @return array - */ - private function getUrlPartsFromMessage($requestUrl, array $parts) - { - // Parse the URL information from the message - $urlParts = ['path' => $requestUrl, 'scheme' => 'http']; - - // Check for the Host header - if (isset($parts['headers']['Host'])) { - $urlParts['host'] = $parts['headers']['Host']; - } elseif (isset($parts['headers']['host'])) { - $urlParts['host'] = $parts['headers']['host']; - } else { - $urlParts['host'] = null; - } - - if (false === strpos($urlParts['host'], ':')) { - $urlParts['port'] = ''; - } else { - $hostParts = explode(':', $urlParts['host']); - $urlParts['host'] = trim($hostParts[0]); - $urlParts['port'] = (int) trim($hostParts[1]); - if ($urlParts['port'] == 443) { - $urlParts['scheme'] = 'https'; - } - } - - // Check if a query is present - $path = $urlParts['path']; - $qpos = strpos($path, '?'); - if ($qpos) { - $urlParts['query'] = substr($path, $qpos + 1); - $urlParts['path'] = substr($path, 0, $qpos); - } else { - $urlParts['query'] = ''; - } - - return $urlParts; - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Message/Request.php b/core/vendor/guzzlehttp/guzzle/src/Message/Request.php deleted file mode 100644 index 4dbe32e6406878b69ea85aabe98742fef98735ee..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Message/Request.php +++ /dev/null @@ -1,195 +0,0 @@ -<?php -namespace GuzzleHttp\Message; - -use GuzzleHttp\Collection; -use GuzzleHttp\Event\HasEmitterTrait; -use GuzzleHttp\Subscriber\Prepare; -use GuzzleHttp\Url; - -/** - * HTTP request class to send requests - */ -class Request extends AbstractMessage implements RequestInterface -{ - use HasEmitterTrait; - - /** @var Url HTTP Url */ - private $url; - - /** @var string HTTP method */ - private $method; - - /** @var Collection Transfer options */ - private $transferOptions; - - /** - * @param string $method HTTP method - * @param string|Url $url HTTP URL to connect to. The URI scheme, - * host header, and URI are parsed from the full URL. If query string - * parameters are present they will be parsed as well. - * @param array|Collection $headers HTTP headers - * @param mixed $body Body to send with the request - * @param array $options Array of options to use with the request - * - emitter: Event emitter to use with the request - */ - public function __construct( - $method, - $url, - $headers = [], - $body = null, - array $options = [] - ) { - $this->setUrl($url); - $this->method = strtoupper($method); - $this->handleOptions($options); - $this->transferOptions = new Collection($options); - $this->addPrepareEvent(); - - if ($body !== null) { - $this->setBody($body); - } - - if ($headers) { - foreach ($headers as $key => $value) { - $this->setHeader($key, $value); - } - } - } - - public function __clone() - { - if ($this->emitter) { - $this->emitter = clone $this->emitter; - } - $this->transferOptions = clone $this->transferOptions; - $this->url = clone $this->url; - } - - public function setUrl($url) - { - $this->url = $url instanceof Url ? $url : Url::fromString($url); - $this->updateHostHeaderFromUrl(); - } - - public function getUrl() - { - return (string) $this->url; - } - - public function setQuery($query) - { - $this->url->setQuery($query); - } - - public function getQuery() - { - return $this->url->getQuery(); - } - - public function setMethod($method) - { - $this->method = strtoupper($method); - } - - public function getMethod() - { - return $this->method; - } - - public function getScheme() - { - return $this->url->getScheme(); - } - - public function setScheme($scheme) - { - $this->url->setScheme($scheme); - } - - public function getPort() - { - return $this->url->getPort(); - } - - public function setPort($port) - { - $this->url->setPort($port); - $this->updateHostHeaderFromUrl(); - } - - public function getHost() - { - return $this->url->getHost(); - } - - public function setHost($host) - { - $this->url->setHost($host); - $this->updateHostHeaderFromUrl(); - } - - public function getPath() - { - return '/' . ltrim($this->url->getPath(), '/'); - } - - public function setPath($path) - { - $this->url->setPath($path); - } - - public function getResource() - { - $resource = $this->getPath(); - if ($query = (string) $this->url->getQuery()) { - $resource .= '?' . $query; - } - - return $resource; - } - - public function getConfig() - { - return $this->transferOptions; - } - - protected function handleOptions(array &$options) - { - parent::handleOptions($options); - // Use a custom emitter if one is specified, and remove it from - // options that are exposed through getConfig() - if (isset($options['emitter'])) { - $this->emitter = $options['emitter']; - unset($options['emitter']); - } - } - - /** - * Adds a subscriber that ensures a request's body is prepared before - * sending. - */ - private function addPrepareEvent() - { - static $subscriber; - if (!$subscriber) { - $subscriber = new Prepare(); - } - - $this->getEmitter()->attach($subscriber); - } - - private function updateHostHeaderFromUrl() - { - $port = $this->url->getPort(); - $scheme = $this->url->getScheme(); - if ($host = $this->url->getHost()) { - if (($port == 80 && $scheme == 'http') || - ($port == 443 && $scheme == 'https') - ) { - $this->setHeader('Host', $host); - } else { - $this->setHeader('Host', "{$host}:{$port}"); - } - } - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Message/RequestInterface.php b/core/vendor/guzzlehttp/guzzle/src/Message/RequestInterface.php deleted file mode 100644 index f6a69d1e1f1c375e3961c37511fcb060afdcda1a..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Message/RequestInterface.php +++ /dev/null @@ -1,136 +0,0 @@ -<?php -namespace GuzzleHttp\Message; - -use GuzzleHttp\Event\HasEmitterInterface; -use GuzzleHttp\Query; - -/** - * Generic HTTP request interface - */ -interface RequestInterface extends MessageInterface, HasEmitterInterface -{ - /** - * Sets the request URL. - * - * The URL MUST be a string, or an object that implements the - * `__toString()` method. - * - * @param string $url Request URL. - * - * @throws \InvalidArgumentException If the URL is invalid. - */ - public function setUrl($url); - - /** - * Gets the request URL as a string. - * - * @return string Returns the URL as a string. - */ - public function getUrl(); - - /** - * Get the resource part of the the request, including the path, query - * string, and fragment. - * - * @return string - */ - public function getResource(); - - /** - * Get the collection of key value pairs that will be used as the query - * string in the request. - * - * @return Query - */ - public function getQuery(); - - /** - * Set the query string used by the request - * - * @param array|Query $query Query to set - */ - public function setQuery($query); - - /** - * Get the HTTP method of the request. - * - * @return string - */ - public function getMethod(); - - /** - * Set the HTTP method of the request. - * - * @param string $method HTTP method - */ - public function setMethod($method); - - /** - * Get the URI scheme of the request (http, https, etc.). - * - * @return string - */ - public function getScheme(); - - /** - * Set the URI scheme of the request (http, https, etc.). - * - * @param string $scheme Scheme to set - */ - public function setScheme($scheme); - - /** - * Get the port scheme of the request (e.g., 80, 443, etc.). - * - * @return int - */ - public function getPort(); - - /** - * Set the port of the request. - * - * Setting a port modifies the Host header of a request as necessary. - * - * @param int $port Port to set - */ - public function setPort($port); - - /** - * Get the host of the request. - * - * @return string - */ - public function getHost(); - - /** - * Set the host of the request including an optional port. - * - * Including a port in the host argument will explicitly change the port of - * the request. If no port is found, the default port of the current - * request scheme will be utilized. - * - * @param string $host Host to set (e.g. www.yahoo.com, www.yahoo.com:80) - */ - public function setHost($host); - - /** - * Get the path of the request (e.g. '/', '/index.html'). - * - * @return string - */ - public function getPath(); - - /** - * Set the path of the request (e.g. '/', '/index.html'). - * - * @param string|array $path Path to set or array of segments to implode - */ - public function setPath($path); - - /** - * Get the request's configuration options. - * - * @return \GuzzleHttp\Collection - */ - public function getConfig(); -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Message/Response.php b/core/vendor/guzzlehttp/guzzle/src/Message/Response.php deleted file mode 100644 index 42e10e64e28a64dd316f02544460bb1241dcf374..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Message/Response.php +++ /dev/null @@ -1,208 +0,0 @@ -<?php -namespace GuzzleHttp\Message; - -use GuzzleHttp\Exception\ParseException; -use GuzzleHttp\Exception\XmlParseException; -use GuzzleHttp\Stream\StreamInterface; -use GuzzleHttp\Utils; - -/** - * Guzzle HTTP response object - */ -class Response extends AbstractMessage implements ResponseInterface -{ - /** @var array Mapping of status codes to reason phrases */ - private static $statusTexts = [ - 100 => 'Continue', - 101 => 'Switching Protocols', - 102 => 'Processing', - 200 => 'OK', - 201 => 'Created', - 202 => 'Accepted', - 203 => 'Non-Authoritative Information', - 204 => 'No Content', - 205 => 'Reset Content', - 206 => 'Partial Content', - 207 => 'Multi-Status', - 208 => 'Already Reported', - 226 => 'IM Used', - 300 => 'Multiple Choices', - 301 => 'Moved Permanently', - 302 => 'Found', - 303 => 'See Other', - 304 => 'Not Modified', - 305 => 'Use Proxy', - 307 => 'Temporary Redirect', - 308 => 'Permanent Redirect', - 400 => 'Bad Request', - 401 => 'Unauthorized', - 402 => 'Payment Required', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not Allowed', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication Required', - 408 => 'Request Timeout', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition Failed', - 413 => 'Request Entity Too Large', - 414 => 'Request-URI Too Long', - 415 => 'Unsupported Media Type', - 416 => 'Requested Range Not Satisfiable', - 417 => 'Expectation Failed', - 422 => 'Unprocessable Entity', - 423 => 'Locked', - 424 => 'Failed Dependency', - 425 => 'Reserved for WebDAV advanced collections expired proposal', - 426 => 'Upgrade required', - 428 => 'Precondition Required', - 429 => 'Too Many Requests', - 431 => 'Request Header Fields Too Large', - 500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Timeout', - 505 => 'HTTP Version Not Supported', - 506 => 'Variant Also Negotiates (Experimental)', - 507 => 'Insufficient Storage', - 508 => 'Loop Detected', - 510 => 'Not Extended', - 511 => 'Network Authentication Required', - ]; - - /** @var string The reason phrase of the response (human readable code) */ - private $reasonPhrase; - - /** @var string The status code of the response */ - private $statusCode; - - /** @var string The effective URL that returned this response */ - private $effectiveUrl; - - /** - * @param int|string $statusCode The response status code (e.g. 200) - * @param array $headers The response headers - * @param StreamInterface $body The body of the response - * @param array $options Response message options - * - reason_phrase: Set a custom reason phrase - * - protocol_version: Set a custom protocol version - */ - public function __construct( - $statusCode, - array $headers = [], - StreamInterface $body = null, - array $options = [] - ) { - $this->statusCode = (int) $statusCode; - $this->handleOptions($options); - - // Assume a reason phrase if one was not applied as an option - if (!$this->reasonPhrase && - isset(self::$statusTexts[$this->statusCode]) - ) { - $this->reasonPhrase = self::$statusTexts[$this->statusCode]; - } - - if ($headers) { - $this->setHeaders($headers); - } - - if ($body) { - $this->setBody($body); - } - } - - public function getStatusCode() - { - return $this->statusCode; - } - - public function setStatusCode($code) - { - return $this->statusCode = (int) $code; - } - - public function getReasonPhrase() - { - return $this->reasonPhrase; - } - - public function setReasonPhrase($phrase) - { - return $this->reasonPhrase = $phrase; - } - - public function json(array $config = []) - { - try { - return Utils::jsonDecode( - (string) $this->getBody(), - isset($config['object']) ? !$config['object'] : true, - 512, - isset($config['big_int_strings']) ? JSON_BIGINT_AS_STRING : 0 - ); - } catch (\InvalidArgumentException $e) { - throw new ParseException( - $e->getMessage(), - $this - ); - } - } - - public function xml(array $config = []) - { - $disableEntities = libxml_disable_entity_loader(true); - $internalErrors = libxml_use_internal_errors(true); - - try { - // Allow XML to be retrieved even if there is no response body - $xml = new \SimpleXMLElement( - (string) $this->getBody() ?: '<root />', - isset($config['libxml_options']) ? $config['libxml_options'] : LIBXML_NONET, - false, - isset($config['ns']) ? $config['ns'] : '', - isset($config['ns_is_prefix']) ? $config['ns_is_prefix'] : false - ); - libxml_disable_entity_loader($disableEntities); - libxml_use_internal_errors($internalErrors); - } catch (\Exception $e) { - libxml_disable_entity_loader($disableEntities); - libxml_use_internal_errors($internalErrors); - throw new XmlParseException( - 'Unable to parse response body into XML: ' . $e->getMessage(), - $this, - $e, - (libxml_get_last_error()) ?: null - ); - } - - return $xml; - } - - public function getEffectiveUrl() - { - return $this->effectiveUrl; - } - - public function setEffectiveUrl($url) - { - $this->effectiveUrl = $url; - } - - /** - * Accepts and modifies the options provided to the response in the - * constructor. - * - * @param array $options Options array passed by reference. - */ - protected function handleOptions(array &$options = []) - { - parent::handleOptions($options); - if (isset($options['reason_phrase'])) { - $this->reasonPhrase = $options['reason_phrase']; - } - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Message/ResponseInterface.php b/core/vendor/guzzlehttp/guzzle/src/Message/ResponseInterface.php deleted file mode 100644 index c0ae9be93b618272fd66a9788766f556e862e41f..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Message/ResponseInterface.php +++ /dev/null @@ -1,111 +0,0 @@ -<?php -namespace GuzzleHttp\Message; - -/** - * Represents an HTTP response message. - */ -interface ResponseInterface extends MessageInterface -{ - /** - * Gets the response Status-Code. - * - * The Status-Code is a 3-digit integer result code of the server's attempt - * to understand and satisfy the request. - * - * @return int Status code. - */ - public function getStatusCode(); - - /** - * Sets the status code of this response. - * - * @param int $code The 3-digit integer result code to set. - */ - public function setStatusCode($code); - - /** - * Gets the response Reason-Phrase, a short textual description of the - * Status-Code. - * - * Because a Reason-Phrase is not a required element in response - * Status-Line, the Reason-Phrase value MAY be null. Implementations MAY - * choose to return the default RFC 2616 recommended reason phrase for the - * response's Status-Code. - * - * @return string|null Reason phrase, or null if unknown. - */ - public function getReasonPhrase(); - - /** - * Sets the Reason-Phrase of the response. - * - * If no Reason-Phrase is specified, implementations MAY choose to default - * to the RFC 2616 recommended reason phrase for the response's Status-Code. - * - * @param string $phrase The Reason-Phrase to set. - */ - public function setReasonPhrase($phrase); - - /** - * Get the effective URL that resulted in this response (e.g. the last - * redirect URL). - * - * @return string - */ - public function getEffectiveUrl(); - - /** - * Set the effective URL that resulted in this response (e.g. the last - * redirect URL). - * - * @param string $url Effective URL - */ - public function setEffectiveUrl($url); - - /** - * Parse the JSON response body and return the JSON decoded data. - * - * @param array $config Associative array of configuration settings used - * to control how the JSON data is parsed. Concrete implementations MAY - * add further configuration settings as needed, but they MUST implement - * functionality for the following options: - * - * - object: Set to true to parse JSON objects as PHP objects rather - * than associative arrays. Defaults to false. - * - big_int_strings: When set to true, large integers are converted to - * strings rather than floats. Defaults to false. - * - * Implementations are free to add further configuration settings as - * needed. - * - * @return mixed Returns the JSON decoded data based on the provided - * parse settings. - * @throws \RuntimeException if the response body is not in JSON format - */ - public function json(array $config = []); - - /** - * Parse the XML response body and return a \SimpleXMLElement. - * - * In order to prevent XXE attacks, this method disables loading external - * entities. If you rely on external entities, then you must parse the - * XML response manually by accessing the response body directly. - * - * @param array $config Associative array of configuration settings used - * to control how the XML is parsed. Concrete implementations MAY add - * further configuration settings as needed, but they MUST implement - * functionality for the following options: - * - * - ns: Set to a string to represent the namespace prefix or URI - * - ns_is_prefix: Set to true to specify that the NS is a prefix rather - * than a URI (defaults to false). - * - libxml_options: Bitwise OR of the libxml option constants - * listed at http://php.net/manual/en/libxml.constants.php - * (defaults to LIBXML_NONET) - * - * @return \SimpleXMLElement - * @throws \RuntimeException if the response body is not in XML format - * @link http://websec.io/2012/08/27/Preventing-XXE-in-PHP.html - */ - public function xml(array $config = []); -} diff --git a/core/vendor/guzzlehttp/guzzle/src/MessageFormatter.php b/core/vendor/guzzlehttp/guzzle/src/MessageFormatter.php new file mode 100644 index 0000000000000000000000000000000000000000..6b090a977391a3903e580b05ff882965239a388d --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/MessageFormatter.php @@ -0,0 +1,182 @@ +<?php +namespace GuzzleHttp; + +use Psr\Http\Message\MessageInterface; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * Formats log messages using variable substitutions for requests, responses, + * and other transactional data. + * + * The following variable substitutions are supported: + * + * - {request}: Full HTTP request message + * - {response}: Full HTTP response message + * - {ts}: ISO 8601 date in GMT + * - {date_iso_8601} ISO 8601 date in GMT + * - {date_common_log} Apache common log date using the configured timezone. + * - {host}: Host of the request + * - {method}: Method of the request + * - {uri}: URI of the request + * - {host}: Host of the request + * - {version}: Protocol version + * - {target}: Request target of the request (path + query + fragment) + * - {hostname}: Hostname of the machine that sent the request + * - {code}: Status code of the response (if available) + * - {phrase}: Reason phrase of the response (if available) + * - {error}: Any error messages (if available) + * - {req_header_*}: Replace `*` with the lowercased name of a request header to add to the message + * - {res_header_*}: Replace `*` with the lowercased name of a response header to add to the message + * - {req_headers}: Request headers + * - {res_headers}: Response headers + * - {req_body}: Request body + * - {res_body}: Response body + */ +class MessageFormatter +{ + /** + * Apache Common Log Format. + * @link http://httpd.apache.org/docs/2.4/logs.html#common + * @var string + */ + const CLF = "{hostname} {req_header_User-Agent} - [{date_common_log}] \"{method} {target} HTTP/{version}\" {code} {res_header_Content-Length}"; + const DEBUG = ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}"; + const SHORT = '[{ts}] "{method} {target} HTTP/{version}" {code}'; + + /** @var string Template used to format log messages */ + private $template; + + /** + * @param string $template Log message template + */ + public function __construct($template = self::CLF) + { + $this->template = $template ?: self::CLF; + } + + /** + * Returns a formatted message string. + * + * @param RequestInterface $request Request that was sent + * @param ResponseInterface $response Response that was received + * @param \Exception $error Exception that was received + * + * @return string + */ + public function format( + RequestInterface $request, + ResponseInterface $response = null, + \Exception $error = null + ) { + $cache = []; + + return preg_replace_callback( + '/{\s*([A-Za-z_\-\.0-9]+)\s*}/', + function (array $matches) use ($request, $response, $error, &$cache) { + + if (isset($cache[$matches[1]])) { + return $cache[$matches[1]]; + } + + $result = ''; + switch ($matches[1]) { + case 'request': + $result = Psr7\str($request); + break; + case 'response': + $result = $response ? Psr7\str($response) : ''; + break; + case 'req_headers': + $result = trim($request->getMethod() + . ' ' . $request->getRequestTarget()) + . ' HTTP/' . $request->getProtocolVersion() . "\r\n" + . $this->headers($request); + break; + case 'res_headers': + $result = $response ? + sprintf( + 'HTTP/%s %d %s', + $response->getProtocolVersion(), + $response->getStatusCode(), + $response->getReasonPhrase() + ) . "\r\n" . $this->headers($response) + : 'NULL'; + break; + case 'req_body': + $result = $request->getBody(); + break; + case 'res_body': + $result = $response ? $response->getBody() : 'NULL'; + break; + case 'ts': + case 'date_iso_8601': + $result = gmdate('c'); + break; + case 'date_common_log': + $result = date('d/M/Y:H:i:s O'); + break; + case 'method': + $result = $request->getMethod(); + break; + case 'version': + $result = $request->getProtocolVersion(); + break; + case 'uri': + case 'url': + $result = $request->getUri(); + break; + case 'target': + $result = $request->getRequestTarget(); + break; + case 'req_version': + $result = $request->getProtocolVersion(); + break; + case 'res_version': + $result = $response + ? $response->getProtocolVersion() + : 'NULL'; + break; + case 'host': + $result = $request->getHeaderLine('Host'); + break; + case 'hostname': + $result = gethostname(); + break; + case 'code': + $result = $response ? $response->getStatusCode() : 'NULL'; + break; + case 'phrase': + $result = $response ? $response->getReasonPhrase() : 'NULL'; + break; + case 'error': + $result = $error ? $error->getMessage() : 'NULL'; + break; + default: + // handle prefixed dynamic headers + if (strpos($matches[1], 'req_header_') === 0) { + $result = $request->getHeaderLine(substr($matches[1], 11)); + } elseif (strpos($matches[1], 'res_header_') === 0) { + $result = $response + ? $response->getHeaderLine(substr($matches[1], 11)) + : 'NULL'; + } + } + + $cache[$matches[1]] = $result; + return $result; + }, + $this->template + ); + } + + private function headers(MessageInterface $message) + { + $result = ''; + foreach ($message->getHeaders() as $name => $values) { + $result .= $name . ': ' . implode(', ', $values) . "\r\n"; + } + + return trim($result); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Middleware.php b/core/vendor/guzzlehttp/guzzle/src/Middleware.php new file mode 100644 index 0000000000000000000000000000000000000000..2f165f39ac941e9423b9faff5ecbabcc2a87f045 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Middleware.php @@ -0,0 +1,253 @@ +<?php +namespace GuzzleHttp; + +use GuzzleHttp\Cookie\CookieJarInterface; +use GuzzleHttp\Exception\ClientException; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Exception\ServerException; +use GuzzleHttp\Promise\RejectedPromise; +use GuzzleHttp\Psr7; +use Psr\Http\Message\ResponseInterface; +use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; + +/** + * Functions used to create and wrap handlers with handler middleware. + */ +final class Middleware +{ + /** + * Middleware that adds cookies to requests. + * + * The options array must be set to a CookieJarInterface in order to use + * cookies. This is typically handled for you by a client. + * + * @return callable Returns a function that accepts the next handler. + */ + public static function cookies() + { + return function (callable $handler) { + return function ($request, array $options) use ($handler) { + if (empty($options['cookies'])) { + return $handler($request, $options); + } elseif (!($options['cookies'] instanceof CookieJarInterface)) { + throw new \InvalidArgumentException('cookies must be an instance of GuzzleHttp\Cookie\CookieJarInterface'); + } + $cookieJar = $options['cookies']; + $request = $cookieJar->withCookieHeader($request); + return $handler($request, $options) + ->then(function ($response) use ($cookieJar, $request) { + $cookieJar->extractCookies($request, $response); + return $response; + } + ); + }; + }; + } + + /** + * Middleware that throws exceptions for 4xx or 5xx responses when the + * "http_error" request option is set to true. + * + * @return callable Returns a function that accepts the next handler. + */ + public static function httpErrors() + { + return function (callable $handler) { + return function ($request, array $options) use ($handler) { + if (empty($options['http_errors'])) { + return $handler($request, $options); + } + return $handler($request, $options)->then( + function (ResponseInterface $response) use ($request, $handler) { + $code = $response->getStatusCode(); + if ($code < 400) { + return $response; + } + throw $code > 499 + ? new ServerException("Server error: $code", $request, $response) + : new ClientException("Client error: $code", $request, $response); + } + ); + }; + }; + } + + /** + * Middleware that pushes history data to an ArrayAccess container. + * + * @param array $container Container to hold the history (by reference). + * + * @return callable Returns a function that accepts the next handler. + */ + public static function history(array &$container) + { + return function (callable $handler) use (&$container) { + return function ($request, array $options) use ($handler, &$container) { + return $handler($request, $options)->then( + function ($value) use ($request, &$container, $options) { + $container[] = [ + 'request' => $request, + 'response' => $value, + 'error' => null, + 'options' => $options + ]; + return $value; + }, + function ($reason) use ($request, &$container, $options) { + $container[] = [ + 'request' => $request, + 'response' => null, + 'error' => $reason, + 'options' => $options + ]; + return new RejectedPromise($reason); + } + ); + }; + }; + } + + /** + * Middleware that invokes a callback before and after sending a request. + * + * The provided listener cannot modify or alter the response. It simply + * "taps" into the chain to be notified before returning the promise. The + * before listener accepts a request and options array, and the after + * listener accepts a request, options array, and response promise. + * + * @param callable $before Function to invoke before forwarding the request. + * @param callable $after Function invoked after forwarding. + * + * @return callable Returns a function that accepts the next handler. + */ + public static function tap(callable $before = null, callable $after = null) + { + return function (callable $handler) use ($before, $after) { + return function ($request, array $options) use ($handler, $before, $after) { + if ($before) { + $before($request, $options); + } + $response = $handler($request, $options); + if ($after) { + $after($request, $options, $response); + } + return $response; + }; + }; + } + + /** + * Middleware that handles request redirects. + * + * @return callable Returns a function that accepts the next handler. + */ + public static function redirect() + { + return function (callable $handler) { + return new RedirectMiddleware($handler); + }; + } + + /** + * Middleware that retries requests based on the boolean result of + * invoking the provided "decider" function. + * + * If no delay function is provided, a simple implementation of exponential + * backoff will be utilized. + * + * @param callable $decider Function that accepts the number of retries, + * a request, [response], and [exception] and + * returns true if the request is to be retried. + * @param callable $delay Function that accepts the number of retries and + * returns the number of milliseconds to delay. + * + * @return callable Returns a function that accepts the next handler. + */ + public static function retry(callable $decider, callable $delay = null) + { + return function (callable $handler) use ($decider, $delay) { + return new RetryMiddleware($decider, $handler, $delay); + }; + } + + /** + * Middleware that logs requests, responses, and errors using a message + * formatter. + * + * @param LoggerInterface $logger Logs messages. + * @param MessageFormatter $formatter Formatter used to create message strings. + * @param string $logLevel Level at which to log requests. + * + * @return callable Returns a function that accepts the next handler. + */ + public static function log(LoggerInterface $logger, MessageFormatter $formatter, $logLevel = LogLevel::INFO) + { + return function (callable $handler) use ($logger, $formatter, $logLevel) { + return function ($request, array $options) use ($handler, $logger, $formatter, $logLevel) { + return $handler($request, $options)->then( + function ($response) use ($logger, $request, $formatter, $logLevel) { + $message = $formatter->format($request, $response); + $logger->log($logLevel, $message); + return $response; + }, + function ($reason) use ($logger, $request, $formatter) { + $response = $reason instanceof RequestException + ? $reason->getResponse() + : null; + $message = $formatter->format($request, $response, $reason); + $logger->notice($message); + return \GuzzleHttp\Promise\rejection_for($reason); + } + ); + }; + }; + } + + /** + * This middleware adds a default content-type if possible, a default + * content-length or transfer-encoding header, and the expect header. + * + * @return callable + */ + public static function prepareBody() + { + return function (callable $handler) { + return new PrepareBodyMiddleware($handler); + }; + } + + /** + * Middleware that applies a map function to the request before passing to + * the next handler. + * + * @param callable $fn Function that accepts a RequestInterface and returns + * a RequestInterface. + * @return callable + */ + public static function mapRequest(callable $fn) + { + return function (callable $handler) use ($fn) { + return function ($request, array $options) use ($handler, $fn) { + return $handler($fn($request), $options); + }; + }; + } + + /** + * Middleware that applies a map function to the resolved promise's + * response. + * + * @param callable $fn Function that accepts a ResponseInterface and + * returns a ResponseInterface. + * @return callable + */ + public static function mapResponse(callable $fn) + { + return function (callable $handler) use ($fn) { + return function ($request, array $options) use ($handler, $fn) { + return $handler($request, $options)->then($fn); + }; + }; + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Mimetypes.php b/core/vendor/guzzlehttp/guzzle/src/Mimetypes.php deleted file mode 100644 index 66ac63cfa51f50224b74a35774097b59f0725969..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Mimetypes.php +++ /dev/null @@ -1,963 +0,0 @@ -<?php -namespace GuzzleHttp; - -/** - * Provides mappings of file extensions to mimetypes - * @link http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types - */ -class Mimetypes -{ - /** @var self */ - protected static $instance; - - /** @var array Mapping of extension to mimetype */ - protected $mimetypes = array( - '3dml' => 'text/vnd.in3d.3dml', - '3g2' => 'video/3gpp2', - '3gp' => 'video/3gpp', - '7z' => 'application/x-7z-compressed', - 'aab' => 'application/x-authorware-bin', - 'aac' => 'audio/x-aac', - 'aam' => 'application/x-authorware-map', - 'aas' => 'application/x-authorware-seg', - 'abw' => 'application/x-abiword', - 'ac' => 'application/pkix-attr-cert', - 'acc' => 'application/vnd.americandynamics.acc', - 'ace' => 'application/x-ace-compressed', - 'acu' => 'application/vnd.acucobol', - 'acutc' => 'application/vnd.acucorp', - 'adp' => 'audio/adpcm', - 'aep' => 'application/vnd.audiograph', - 'afm' => 'application/x-font-type1', - 'afp' => 'application/vnd.ibm.modcap', - 'ahead' => 'application/vnd.ahead.space', - 'ai' => 'application/postscript', - 'aif' => 'audio/x-aiff', - 'aifc' => 'audio/x-aiff', - 'aiff' => 'audio/x-aiff', - 'air' => 'application/vnd.adobe.air-application-installer-package+zip', - 'ait' => 'application/vnd.dvb.ait', - 'ami' => 'application/vnd.amiga.ami', - 'apk' => 'application/vnd.android.package-archive', - 'application' => 'application/x-ms-application', - 'apr' => 'application/vnd.lotus-approach', - 'asa' => 'text/plain', - 'asax' => 'application/octet-stream', - 'asc' => 'application/pgp-signature', - 'ascx' => 'text/plain', - 'asf' => 'video/x-ms-asf', - 'ashx' => 'text/plain', - 'asm' => 'text/x-asm', - 'asmx' => 'text/plain', - 'aso' => 'application/vnd.accpac.simply.aso', - 'asp' => 'text/plain', - 'aspx' => 'text/plain', - 'asx' => 'video/x-ms-asf', - 'atc' => 'application/vnd.acucorp', - 'atom' => 'application/atom+xml', - 'atomcat' => 'application/atomcat+xml', - 'atomsvc' => 'application/atomsvc+xml', - 'atx' => 'application/vnd.antix.game-component', - 'au' => 'audio/basic', - 'avi' => 'video/x-msvideo', - 'aw' => 'application/applixware', - 'axd' => 'text/plain', - 'azf' => 'application/vnd.airzip.filesecure.azf', - 'azs' => 'application/vnd.airzip.filesecure.azs', - 'azw' => 'application/vnd.amazon.ebook', - 'bat' => 'application/x-msdownload', - 'bcpio' => 'application/x-bcpio', - 'bdf' => 'application/x-font-bdf', - 'bdm' => 'application/vnd.syncml.dm+wbxml', - 'bed' => 'application/vnd.realvnc.bed', - 'bh2' => 'application/vnd.fujitsu.oasysprs', - 'bin' => 'application/octet-stream', - 'bmi' => 'application/vnd.bmi', - 'bmp' => 'image/bmp', - 'book' => 'application/vnd.framemaker', - 'box' => 'application/vnd.previewsystems.box', - 'boz' => 'application/x-bzip2', - 'bpk' => 'application/octet-stream', - 'btif' => 'image/prs.btif', - 'bz' => 'application/x-bzip', - 'bz2' => 'application/x-bzip2', - 'c' => 'text/x-c', - 'c11amc' => 'application/vnd.cluetrust.cartomobile-config', - 'c11amz' => 'application/vnd.cluetrust.cartomobile-config-pkg', - 'c4d' => 'application/vnd.clonk.c4group', - 'c4f' => 'application/vnd.clonk.c4group', - 'c4g' => 'application/vnd.clonk.c4group', - 'c4p' => 'application/vnd.clonk.c4group', - 'c4u' => 'application/vnd.clonk.c4group', - 'cab' => 'application/vnd.ms-cab-compressed', - 'car' => 'application/vnd.curl.car', - 'cat' => 'application/vnd.ms-pki.seccat', - 'cc' => 'text/x-c', - 'cct' => 'application/x-director', - 'ccxml' => 'application/ccxml+xml', - 'cdbcmsg' => 'application/vnd.contact.cmsg', - 'cdf' => 'application/x-netcdf', - 'cdkey' => 'application/vnd.mediastation.cdkey', - 'cdmia' => 'application/cdmi-capability', - 'cdmic' => 'application/cdmi-container', - 'cdmid' => 'application/cdmi-domain', - 'cdmio' => 'application/cdmi-object', - 'cdmiq' => 'application/cdmi-queue', - 'cdx' => 'chemical/x-cdx', - 'cdxml' => 'application/vnd.chemdraw+xml', - 'cdy' => 'application/vnd.cinderella', - 'cer' => 'application/pkix-cert', - 'cfc' => 'application/x-coldfusion', - 'cfm' => 'application/x-coldfusion', - 'cgm' => 'image/cgm', - 'chat' => 'application/x-chat', - 'chm' => 'application/vnd.ms-htmlhelp', - 'chrt' => 'application/vnd.kde.kchart', - 'cif' => 'chemical/x-cif', - 'cii' => 'application/vnd.anser-web-certificate-issue-initiation', - 'cil' => 'application/vnd.ms-artgalry', - 'cla' => 'application/vnd.claymore', - 'class' => 'application/java-vm', - 'clkk' => 'application/vnd.crick.clicker.keyboard', - 'clkp' => 'application/vnd.crick.clicker.palette', - 'clkt' => 'application/vnd.crick.clicker.template', - 'clkw' => 'application/vnd.crick.clicker.wordbank', - 'clkx' => 'application/vnd.crick.clicker', - 'clp' => 'application/x-msclip', - 'cmc' => 'application/vnd.cosmocaller', - 'cmdf' => 'chemical/x-cmdf', - 'cml' => 'chemical/x-cml', - 'cmp' => 'application/vnd.yellowriver-custom-menu', - 'cmx' => 'image/x-cmx', - 'cod' => 'application/vnd.rim.cod', - 'com' => 'application/x-msdownload', - 'conf' => 'text/plain', - 'cpio' => 'application/x-cpio', - 'cpp' => 'text/x-c', - 'cpt' => 'application/mac-compactpro', - 'crd' => 'application/x-mscardfile', - 'crl' => 'application/pkix-crl', - 'crt' => 'application/x-x509-ca-cert', - 'cryptonote' => 'application/vnd.rig.cryptonote', - 'cs' => 'text/plain', - 'csh' => 'application/x-csh', - 'csml' => 'chemical/x-csml', - 'csp' => 'application/vnd.commonspace', - 'css' => 'text/css', - 'cst' => 'application/x-director', - 'csv' => 'text/csv', - 'cu' => 'application/cu-seeme', - 'curl' => 'text/vnd.curl', - 'cww' => 'application/prs.cww', - 'cxt' => 'application/x-director', - 'cxx' => 'text/x-c', - 'dae' => 'model/vnd.collada+xml', - 'daf' => 'application/vnd.mobius.daf', - 'dataless' => 'application/vnd.fdsn.seed', - 'davmount' => 'application/davmount+xml', - 'dcr' => 'application/x-director', - 'dcurl' => 'text/vnd.curl.dcurl', - 'dd2' => 'application/vnd.oma.dd2+xml', - 'ddd' => 'application/vnd.fujixerox.ddd', - 'deb' => 'application/x-debian-package', - 'def' => 'text/plain', - 'deploy' => 'application/octet-stream', - 'der' => 'application/x-x509-ca-cert', - 'dfac' => 'application/vnd.dreamfactory', - 'dic' => 'text/x-c', - 'dir' => 'application/x-director', - 'dis' => 'application/vnd.mobius.dis', - 'dist' => 'application/octet-stream', - 'distz' => 'application/octet-stream', - 'djv' => 'image/vnd.djvu', - 'djvu' => 'image/vnd.djvu', - 'dll' => 'application/x-msdownload', - 'dmg' => 'application/octet-stream', - 'dms' => 'application/octet-stream', - 'dna' => 'application/vnd.dna', - 'doc' => 'application/msword', - 'docm' => 'application/vnd.ms-word.document.macroenabled.12', - 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - 'dot' => 'application/msword', - 'dotm' => 'application/vnd.ms-word.template.macroenabled.12', - 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', - 'dp' => 'application/vnd.osgi.dp', - 'dpg' => 'application/vnd.dpgraph', - 'dra' => 'audio/vnd.dra', - 'dsc' => 'text/prs.lines.tag', - 'dssc' => 'application/dssc+der', - 'dtb' => 'application/x-dtbook+xml', - 'dtd' => 'application/xml-dtd', - 'dts' => 'audio/vnd.dts', - 'dtshd' => 'audio/vnd.dts.hd', - 'dump' => 'application/octet-stream', - 'dvi' => 'application/x-dvi', - 'dwf' => 'model/vnd.dwf', - 'dwg' => 'image/vnd.dwg', - 'dxf' => 'image/vnd.dxf', - 'dxp' => 'application/vnd.spotfire.dxp', - 'dxr' => 'application/x-director', - 'ecelp4800' => 'audio/vnd.nuera.ecelp4800', - 'ecelp7470' => 'audio/vnd.nuera.ecelp7470', - 'ecelp9600' => 'audio/vnd.nuera.ecelp9600', - 'ecma' => 'application/ecmascript', - 'edm' => 'application/vnd.novadigm.edm', - 'edx' => 'application/vnd.novadigm.edx', - 'efif' => 'application/vnd.picsel', - 'ei6' => 'application/vnd.pg.osasli', - 'elc' => 'application/octet-stream', - 'eml' => 'message/rfc822', - 'emma' => 'application/emma+xml', - 'eol' => 'audio/vnd.digital-winds', - 'eot' => 'application/vnd.ms-fontobject', - 'eps' => 'application/postscript', - 'epub' => 'application/epub+zip', - 'es3' => 'application/vnd.eszigno3+xml', - 'esf' => 'application/vnd.epson.esf', - 'et3' => 'application/vnd.eszigno3+xml', - 'etx' => 'text/x-setext', - 'exe' => 'application/x-msdownload', - 'exi' => 'application/exi', - 'ext' => 'application/vnd.novadigm.ext', - 'ez' => 'application/andrew-inset', - 'ez2' => 'application/vnd.ezpix-album', - 'ez3' => 'application/vnd.ezpix-package', - 'f' => 'text/x-fortran', - 'f4v' => 'video/x-f4v', - 'f77' => 'text/x-fortran', - 'f90' => 'text/x-fortran', - 'fbs' => 'image/vnd.fastbidsheet', - 'fcs' => 'application/vnd.isac.fcs', - 'fdf' => 'application/vnd.fdf', - 'fe_launch' => 'application/vnd.denovo.fcselayout-link', - 'fg5' => 'application/vnd.fujitsu.oasysgp', - 'fgd' => 'application/x-director', - 'fh' => 'image/x-freehand', - 'fh4' => 'image/x-freehand', - 'fh5' => 'image/x-freehand', - 'fh7' => 'image/x-freehand', - 'fhc' => 'image/x-freehand', - 'fig' => 'application/x-xfig', - 'fli' => 'video/x-fli', - 'flo' => 'application/vnd.micrografx.flo', - 'flv' => 'video/x-flv', - 'flw' => 'application/vnd.kde.kivio', - 'flx' => 'text/vnd.fmi.flexstor', - 'fly' => 'text/vnd.fly', - 'fm' => 'application/vnd.framemaker', - 'fnc' => 'application/vnd.frogans.fnc', - 'for' => 'text/x-fortran', - 'fpx' => 'image/vnd.fpx', - 'frame' => 'application/vnd.framemaker', - 'fsc' => 'application/vnd.fsc.weblaunch', - 'fst' => 'image/vnd.fst', - 'ftc' => 'application/vnd.fluxtime.clip', - 'fti' => 'application/vnd.anser-web-funds-transfer-initiation', - 'fvt' => 'video/vnd.fvt', - 'fxp' => 'application/vnd.adobe.fxp', - 'fxpl' => 'application/vnd.adobe.fxp', - 'fzs' => 'application/vnd.fuzzysheet', - 'g2w' => 'application/vnd.geoplan', - 'g3' => 'image/g3fax', - 'g3w' => 'application/vnd.geospace', - 'gac' => 'application/vnd.groove-account', - 'gdl' => 'model/vnd.gdl', - 'geo' => 'application/vnd.dynageo', - 'gex' => 'application/vnd.geometry-explorer', - 'ggb' => 'application/vnd.geogebra.file', - 'ggt' => 'application/vnd.geogebra.tool', - 'ghf' => 'application/vnd.groove-help', - 'gif' => 'image/gif', - 'gim' => 'application/vnd.groove-identity-message', - 'gmx' => 'application/vnd.gmx', - 'gnumeric' => 'application/x-gnumeric', - 'gph' => 'application/vnd.flographit', - 'gqf' => 'application/vnd.grafeq', - 'gqs' => 'application/vnd.grafeq', - 'gram' => 'application/srgs', - 'gre' => 'application/vnd.geometry-explorer', - 'grv' => 'application/vnd.groove-injector', - 'grxml' => 'application/srgs+xml', - 'gsf' => 'application/x-font-ghostscript', - 'gtar' => 'application/x-gtar', - 'gtm' => 'application/vnd.groove-tool-message', - 'gtw' => 'model/vnd.gtw', - 'gv' => 'text/vnd.graphviz', - 'gxt' => 'application/vnd.geonext', - 'h' => 'text/x-c', - 'h261' => 'video/h261', - 'h263' => 'video/h263', - 'h264' => 'video/h264', - 'hal' => 'application/vnd.hal+xml', - 'hbci' => 'application/vnd.hbci', - 'hdf' => 'application/x-hdf', - 'hh' => 'text/x-c', - 'hlp' => 'application/winhlp', - 'hpgl' => 'application/vnd.hp-hpgl', - 'hpid' => 'application/vnd.hp-hpid', - 'hps' => 'application/vnd.hp-hps', - 'hqx' => 'application/mac-binhex40', - 'hta' => 'application/octet-stream', - 'htc' => 'text/html', - 'htke' => 'application/vnd.kenameaapp', - 'htm' => 'text/html', - 'html' => 'text/html', - 'hvd' => 'application/vnd.yamaha.hv-dic', - 'hvp' => 'application/vnd.yamaha.hv-voice', - 'hvs' => 'application/vnd.yamaha.hv-script', - 'i2g' => 'application/vnd.intergeo', - 'icc' => 'application/vnd.iccprofile', - 'ice' => 'x-conference/x-cooltalk', - 'icm' => 'application/vnd.iccprofile', - 'ico' => 'image/x-icon', - 'ics' => 'text/calendar', - 'ief' => 'image/ief', - 'ifb' => 'text/calendar', - 'ifm' => 'application/vnd.shana.informed.formdata', - 'iges' => 'model/iges', - 'igl' => 'application/vnd.igloader', - 'igm' => 'application/vnd.insors.igm', - 'igs' => 'model/iges', - 'igx' => 'application/vnd.micrografx.igx', - 'iif' => 'application/vnd.shana.informed.interchange', - 'imp' => 'application/vnd.accpac.simply.imp', - 'ims' => 'application/vnd.ms-ims', - 'in' => 'text/plain', - 'ini' => 'text/plain', - 'ipfix' => 'application/ipfix', - 'ipk' => 'application/vnd.shana.informed.package', - 'irm' => 'application/vnd.ibm.rights-management', - 'irp' => 'application/vnd.irepository.package+xml', - 'iso' => 'application/octet-stream', - 'itp' => 'application/vnd.shana.informed.formtemplate', - 'ivp' => 'application/vnd.immervision-ivp', - 'ivu' => 'application/vnd.immervision-ivu', - 'jad' => 'text/vnd.sun.j2me.app-descriptor', - 'jam' => 'application/vnd.jam', - 'jar' => 'application/java-archive', - 'java' => 'text/x-java-source', - 'jisp' => 'application/vnd.jisp', - 'jlt' => 'application/vnd.hp-jlyt', - 'jnlp' => 'application/x-java-jnlp-file', - 'joda' => 'application/vnd.joost.joda-archive', - 'jpe' => 'image/jpeg', - 'jpeg' => 'image/jpeg', - 'jpg' => 'image/jpeg', - 'jpgm' => 'video/jpm', - 'jpgv' => 'video/jpeg', - 'jpm' => 'video/jpm', - 'js' => 'text/javascript', - 'json' => 'application/json', - 'kar' => 'audio/midi', - 'karbon' => 'application/vnd.kde.karbon', - 'kfo' => 'application/vnd.kde.kformula', - 'kia' => 'application/vnd.kidspiration', - 'kml' => 'application/vnd.google-earth.kml+xml', - 'kmz' => 'application/vnd.google-earth.kmz', - 'kne' => 'application/vnd.kinar', - 'knp' => 'application/vnd.kinar', - 'kon' => 'application/vnd.kde.kontour', - 'kpr' => 'application/vnd.kde.kpresenter', - 'kpt' => 'application/vnd.kde.kpresenter', - 'ksp' => 'application/vnd.kde.kspread', - 'ktr' => 'application/vnd.kahootz', - 'ktx' => 'image/ktx', - 'ktz' => 'application/vnd.kahootz', - 'kwd' => 'application/vnd.kde.kword', - 'kwt' => 'application/vnd.kde.kword', - 'lasxml' => 'application/vnd.las.las+xml', - 'latex' => 'application/x-latex', - 'lbd' => 'application/vnd.llamagraphics.life-balance.desktop', - 'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml', - 'les' => 'application/vnd.hhe.lesson-player', - 'lha' => 'application/octet-stream', - 'link66' => 'application/vnd.route66.link66+xml', - 'list' => 'text/plain', - 'list3820' => 'application/vnd.ibm.modcap', - 'listafp' => 'application/vnd.ibm.modcap', - 'log' => 'text/plain', - 'lostxml' => 'application/lost+xml', - 'lrf' => 'application/octet-stream', - 'lrm' => 'application/vnd.ms-lrm', - 'ltf' => 'application/vnd.frogans.ltf', - 'lvp' => 'audio/vnd.lucent.voice', - 'lwp' => 'application/vnd.lotus-wordpro', - 'lzh' => 'application/octet-stream', - 'm13' => 'application/x-msmediaview', - 'm14' => 'application/x-msmediaview', - 'm1v' => 'video/mpeg', - 'm21' => 'application/mp21', - 'm2a' => 'audio/mpeg', - 'm2v' => 'video/mpeg', - 'm3a' => 'audio/mpeg', - 'm3u' => 'audio/x-mpegurl', - 'm3u8' => 'application/vnd.apple.mpegurl', - 'm4a' => 'audio/mp4', - 'm4u' => 'video/vnd.mpegurl', - 'm4v' => 'video/mp4', - 'ma' => 'application/mathematica', - 'mads' => 'application/mads+xml', - 'mag' => 'application/vnd.ecowin.chart', - 'maker' => 'application/vnd.framemaker', - 'man' => 'text/troff', - 'mathml' => 'application/mathml+xml', - 'mb' => 'application/mathematica', - 'mbk' => 'application/vnd.mobius.mbk', - 'mbox' => 'application/mbox', - 'mc1' => 'application/vnd.medcalcdata', - 'mcd' => 'application/vnd.mcd', - 'mcurl' => 'text/vnd.curl.mcurl', - 'mdb' => 'application/x-msaccess', - 'mdi' => 'image/vnd.ms-modi', - 'me' => 'text/troff', - 'mesh' => 'model/mesh', - 'meta4' => 'application/metalink4+xml', - 'mets' => 'application/mets+xml', - 'mfm' => 'application/vnd.mfmp', - 'mgp' => 'application/vnd.osgeo.mapguide.package', - 'mgz' => 'application/vnd.proteus.magazine', - 'mid' => 'audio/midi', - 'midi' => 'audio/midi', - 'mif' => 'application/vnd.mif', - 'mime' => 'message/rfc822', - 'mj2' => 'video/mj2', - 'mjp2' => 'video/mj2', - 'mlp' => 'application/vnd.dolby.mlp', - 'mmd' => 'application/vnd.chipnuts.karaoke-mmd', - 'mmf' => 'application/vnd.smaf', - 'mmr' => 'image/vnd.fujixerox.edmics-mmr', - 'mny' => 'application/x-msmoney', - 'mobi' => 'application/x-mobipocket-ebook', - 'mods' => 'application/mods+xml', - 'mov' => 'video/quicktime', - 'movie' => 'video/x-sgi-movie', - 'mp2' => 'audio/mpeg', - 'mp21' => 'application/mp21', - 'mp2a' => 'audio/mpeg', - 'mp3' => 'audio/mpeg', - 'mp4' => 'video/mp4', - 'mp4a' => 'audio/mp4', - 'mp4s' => 'application/mp4', - 'mp4v' => 'video/mp4', - 'mpc' => 'application/vnd.mophun.certificate', - 'mpe' => 'video/mpeg', - 'mpeg' => 'video/mpeg', - 'mpg' => 'video/mpeg', - 'mpg4' => 'video/mp4', - 'mpga' => 'audio/mpeg', - 'mpkg' => 'application/vnd.apple.installer+xml', - 'mpm' => 'application/vnd.blueice.multipass', - 'mpn' => 'application/vnd.mophun.application', - 'mpp' => 'application/vnd.ms-project', - 'mpt' => 'application/vnd.ms-project', - 'mpy' => 'application/vnd.ibm.minipay', - 'mqy' => 'application/vnd.mobius.mqy', - 'mrc' => 'application/marc', - 'mrcx' => 'application/marcxml+xml', - 'ms' => 'text/troff', - 'mscml' => 'application/mediaservercontrol+xml', - 'mseed' => 'application/vnd.fdsn.mseed', - 'mseq' => 'application/vnd.mseq', - 'msf' => 'application/vnd.epson.msf', - 'msh' => 'model/mesh', - 'msi' => 'application/x-msdownload', - 'msl' => 'application/vnd.mobius.msl', - 'msty' => 'application/vnd.muvee.style', - 'mts' => 'model/vnd.mts', - 'mus' => 'application/vnd.musician', - 'musicxml' => 'application/vnd.recordare.musicxml+xml', - 'mvb' => 'application/x-msmediaview', - 'mwf' => 'application/vnd.mfer', - 'mxf' => 'application/mxf', - 'mxl' => 'application/vnd.recordare.musicxml', - 'mxml' => 'application/xv+xml', - 'mxs' => 'application/vnd.triscape.mxs', - 'mxu' => 'video/vnd.mpegurl', - 'n-gage' => 'application/vnd.nokia.n-gage.symbian.install', - 'n3' => 'text/n3', - 'nb' => 'application/mathematica', - 'nbp' => 'application/vnd.wolfram.player', - 'nc' => 'application/x-netcdf', - 'ncx' => 'application/x-dtbncx+xml', - 'ngdat' => 'application/vnd.nokia.n-gage.data', - 'nlu' => 'application/vnd.neurolanguage.nlu', - 'nml' => 'application/vnd.enliven', - 'nnd' => 'application/vnd.noblenet-directory', - 'nns' => 'application/vnd.noblenet-sealer', - 'nnw' => 'application/vnd.noblenet-web', - 'npx' => 'image/vnd.net-fpx', - 'nsf' => 'application/vnd.lotus-notes', - 'oa2' => 'application/vnd.fujitsu.oasys2', - 'oa3' => 'application/vnd.fujitsu.oasys3', - 'oas' => 'application/vnd.fujitsu.oasys', - 'obd' => 'application/x-msbinder', - 'oda' => 'application/oda', - 'odb' => 'application/vnd.oasis.opendocument.database', - 'odc' => 'application/vnd.oasis.opendocument.chart', - 'odf' => 'application/vnd.oasis.opendocument.formula', - 'odft' => 'application/vnd.oasis.opendocument.formula-template', - 'odg' => 'application/vnd.oasis.opendocument.graphics', - 'odi' => 'application/vnd.oasis.opendocument.image', - 'odm' => 'application/vnd.oasis.opendocument.text-master', - 'odp' => 'application/vnd.oasis.opendocument.presentation', - 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', - 'odt' => 'application/vnd.oasis.opendocument.text', - 'oga' => 'audio/ogg', - 'ogg' => 'audio/ogg', - 'ogv' => 'video/ogg', - 'ogx' => 'application/ogg', - 'onepkg' => 'application/onenote', - 'onetmp' => 'application/onenote', - 'onetoc' => 'application/onenote', - 'onetoc2' => 'application/onenote', - 'opf' => 'application/oebps-package+xml', - 'oprc' => 'application/vnd.palm', - 'org' => 'application/vnd.lotus-organizer', - 'osf' => 'application/vnd.yamaha.openscoreformat', - 'osfpvg' => 'application/vnd.yamaha.openscoreformat.osfpvg+xml', - 'otc' => 'application/vnd.oasis.opendocument.chart-template', - 'otf' => 'application/x-font-otf', - 'otg' => 'application/vnd.oasis.opendocument.graphics-template', - 'oth' => 'application/vnd.oasis.opendocument.text-web', - 'oti' => 'application/vnd.oasis.opendocument.image-template', - 'otp' => 'application/vnd.oasis.opendocument.presentation-template', - 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', - 'ott' => 'application/vnd.oasis.opendocument.text-template', - 'oxt' => 'application/vnd.openofficeorg.extension', - 'p' => 'text/x-pascal', - 'p10' => 'application/pkcs10', - 'p12' => 'application/x-pkcs12', - 'p7b' => 'application/x-pkcs7-certificates', - 'p7c' => 'application/pkcs7-mime', - 'p7m' => 'application/pkcs7-mime', - 'p7r' => 'application/x-pkcs7-certreqresp', - 'p7s' => 'application/pkcs7-signature', - 'p8' => 'application/pkcs8', - 'pas' => 'text/x-pascal', - 'paw' => 'application/vnd.pawaafile', - 'pbd' => 'application/vnd.powerbuilder6', - 'pbm' => 'image/x-portable-bitmap', - 'pcf' => 'application/x-font-pcf', - 'pcl' => 'application/vnd.hp-pcl', - 'pclxl' => 'application/vnd.hp-pclxl', - 'pct' => 'image/x-pict', - 'pcurl' => 'application/vnd.curl.pcurl', - 'pcx' => 'image/x-pcx', - 'pdb' => 'application/vnd.palm', - 'pdf' => 'application/pdf', - 'pfa' => 'application/x-font-type1', - 'pfb' => 'application/x-font-type1', - 'pfm' => 'application/x-font-type1', - 'pfr' => 'application/font-tdpfr', - 'pfx' => 'application/x-pkcs12', - 'pgm' => 'image/x-portable-graymap', - 'pgn' => 'application/x-chess-pgn', - 'pgp' => 'application/pgp-encrypted', - 'php' => 'text/x-php', - 'phps' => 'application/x-httpd-phps', - 'pic' => 'image/x-pict', - 'pkg' => 'application/octet-stream', - 'pki' => 'application/pkixcmp', - 'pkipath' => 'application/pkix-pkipath', - 'plb' => 'application/vnd.3gpp.pic-bw-large', - 'plc' => 'application/vnd.mobius.plc', - 'plf' => 'application/vnd.pocketlearn', - 'pls' => 'application/pls+xml', - 'pml' => 'application/vnd.ctc-posml', - 'png' => 'image/png', - 'pnm' => 'image/x-portable-anymap', - 'portpkg' => 'application/vnd.macports.portpkg', - 'pot' => 'application/vnd.ms-powerpoint', - 'potm' => 'application/vnd.ms-powerpoint.template.macroenabled.12', - 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', - 'ppam' => 'application/vnd.ms-powerpoint.addin.macroenabled.12', - 'ppd' => 'application/vnd.cups-ppd', - 'ppm' => 'image/x-portable-pixmap', - 'pps' => 'application/vnd.ms-powerpoint', - 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroenabled.12', - 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', - 'ppt' => 'application/vnd.ms-powerpoint', - 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroenabled.12', - 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', - 'pqa' => 'application/vnd.palm', - 'prc' => 'application/x-mobipocket-ebook', - 'pre' => 'application/vnd.lotus-freelance', - 'prf' => 'application/pics-rules', - 'ps' => 'application/postscript', - 'psb' => 'application/vnd.3gpp.pic-bw-small', - 'psd' => 'image/vnd.adobe.photoshop', - 'psf' => 'application/x-font-linux-psf', - 'pskcxml' => 'application/pskc+xml', - 'ptid' => 'application/vnd.pvi.ptid1', - 'pub' => 'application/x-mspublisher', - 'pvb' => 'application/vnd.3gpp.pic-bw-var', - 'pwn' => 'application/vnd.3m.post-it-notes', - 'pya' => 'audio/vnd.ms-playready.media.pya', - 'pyv' => 'video/vnd.ms-playready.media.pyv', - 'qam' => 'application/vnd.epson.quickanime', - 'qbo' => 'application/vnd.intu.qbo', - 'qfx' => 'application/vnd.intu.qfx', - 'qps' => 'application/vnd.publishare-delta-tree', - 'qt' => 'video/quicktime', - 'qwd' => 'application/vnd.quark.quarkxpress', - 'qwt' => 'application/vnd.quark.quarkxpress', - 'qxb' => 'application/vnd.quark.quarkxpress', - 'qxd' => 'application/vnd.quark.quarkxpress', - 'qxl' => 'application/vnd.quark.quarkxpress', - 'qxt' => 'application/vnd.quark.quarkxpress', - 'ra' => 'audio/x-pn-realaudio', - 'ram' => 'audio/x-pn-realaudio', - 'rar' => 'application/x-rar-compressed', - 'ras' => 'image/x-cmu-raster', - 'rb' => 'text/plain', - 'rcprofile' => 'application/vnd.ipunplugged.rcprofile', - 'rdf' => 'application/rdf+xml', - 'rdz' => 'application/vnd.data-vision.rdz', - 'rep' => 'application/vnd.businessobjects', - 'res' => 'application/x-dtbresource+xml', - 'resx' => 'text/xml', - 'rgb' => 'image/x-rgb', - 'rif' => 'application/reginfo+xml', - 'rip' => 'audio/vnd.rip', - 'rl' => 'application/resource-lists+xml', - 'rlc' => 'image/vnd.fujixerox.edmics-rlc', - 'rld' => 'application/resource-lists-diff+xml', - 'rm' => 'application/vnd.rn-realmedia', - 'rmi' => 'audio/midi', - 'rmp' => 'audio/x-pn-realaudio-plugin', - 'rms' => 'application/vnd.jcp.javame.midlet-rms', - 'rnc' => 'application/relax-ng-compact-syntax', - 'roff' => 'text/troff', - 'rp9' => 'application/vnd.cloanto.rp9', - 'rpss' => 'application/vnd.nokia.radio-presets', - 'rpst' => 'application/vnd.nokia.radio-preset', - 'rq' => 'application/sparql-query', - 'rs' => 'application/rls-services+xml', - 'rsd' => 'application/rsd+xml', - 'rss' => 'application/rss+xml', - 'rtf' => 'application/rtf', - 'rtx' => 'text/richtext', - 's' => 'text/x-asm', - 'saf' => 'application/vnd.yamaha.smaf-audio', - 'sbml' => 'application/sbml+xml', - 'sc' => 'application/vnd.ibm.secure-container', - 'scd' => 'application/x-msschedule', - 'scm' => 'application/vnd.lotus-screencam', - 'scq' => 'application/scvp-cv-request', - 'scs' => 'application/scvp-cv-response', - 'scurl' => 'text/vnd.curl.scurl', - 'sda' => 'application/vnd.stardivision.draw', - 'sdc' => 'application/vnd.stardivision.calc', - 'sdd' => 'application/vnd.stardivision.impress', - 'sdkd' => 'application/vnd.solent.sdkm+xml', - 'sdkm' => 'application/vnd.solent.sdkm+xml', - 'sdp' => 'application/sdp', - 'sdw' => 'application/vnd.stardivision.writer', - 'see' => 'application/vnd.seemail', - 'seed' => 'application/vnd.fdsn.seed', - 'sema' => 'application/vnd.sema', - 'semd' => 'application/vnd.semd', - 'semf' => 'application/vnd.semf', - 'ser' => 'application/java-serialized-object', - 'setpay' => 'application/set-payment-initiation', - 'setreg' => 'application/set-registration-initiation', - 'sfd-hdstx' => 'application/vnd.hydrostatix.sof-data', - 'sfs' => 'application/vnd.spotfire.sfs', - 'sgl' => 'application/vnd.stardivision.writer-global', - 'sgm' => 'text/sgml', - 'sgml' => 'text/sgml', - 'sh' => 'application/x-sh', - 'shar' => 'application/x-shar', - 'shf' => 'application/shf+xml', - 'sig' => 'application/pgp-signature', - 'silo' => 'model/mesh', - 'sis' => 'application/vnd.symbian.install', - 'sisx' => 'application/vnd.symbian.install', - 'sit' => 'application/x-stuffit', - 'sitx' => 'application/x-stuffitx', - 'skd' => 'application/vnd.koan', - 'skm' => 'application/vnd.koan', - 'skp' => 'application/vnd.koan', - 'skt' => 'application/vnd.koan', - 'sldm' => 'application/vnd.ms-powerpoint.slide.macroenabled.12', - 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', - 'slt' => 'application/vnd.epson.salt', - 'sm' => 'application/vnd.stepmania.stepchart', - 'smf' => 'application/vnd.stardivision.math', - 'smi' => 'application/smil+xml', - 'smil' => 'application/smil+xml', - 'snd' => 'audio/basic', - 'snf' => 'application/x-font-snf', - 'so' => 'application/octet-stream', - 'spc' => 'application/x-pkcs7-certificates', - 'spf' => 'application/vnd.yamaha.smaf-phrase', - 'spl' => 'application/x-futuresplash', - 'spot' => 'text/vnd.in3d.spot', - 'spp' => 'application/scvp-vp-response', - 'spq' => 'application/scvp-vp-request', - 'spx' => 'audio/ogg', - 'src' => 'application/x-wais-source', - 'sru' => 'application/sru+xml', - 'srx' => 'application/sparql-results+xml', - 'sse' => 'application/vnd.kodak-descriptor', - 'ssf' => 'application/vnd.epson.ssf', - 'ssml' => 'application/ssml+xml', - 'st' => 'application/vnd.sailingtracker.track', - 'stc' => 'application/vnd.sun.xml.calc.template', - 'std' => 'application/vnd.sun.xml.draw.template', - 'stf' => 'application/vnd.wt.stf', - 'sti' => 'application/vnd.sun.xml.impress.template', - 'stk' => 'application/hyperstudio', - 'stl' => 'application/vnd.ms-pki.stl', - 'str' => 'application/vnd.pg.format', - 'stw' => 'application/vnd.sun.xml.writer.template', - 'sub' => 'image/vnd.dvb.subtitle', - 'sus' => 'application/vnd.sus-calendar', - 'susp' => 'application/vnd.sus-calendar', - 'sv4cpio' => 'application/x-sv4cpio', - 'sv4crc' => 'application/x-sv4crc', - 'svc' => 'application/vnd.dvb.service', - 'svd' => 'application/vnd.svd', - 'svg' => 'image/svg+xml', - 'svgz' => 'image/svg+xml', - 'swa' => 'application/x-director', - 'swf' => 'application/x-shockwave-flash', - 'swi' => 'application/vnd.aristanetworks.swi', - 'sxc' => 'application/vnd.sun.xml.calc', - 'sxd' => 'application/vnd.sun.xml.draw', - 'sxg' => 'application/vnd.sun.xml.writer.global', - 'sxi' => 'application/vnd.sun.xml.impress', - 'sxm' => 'application/vnd.sun.xml.math', - 'sxw' => 'application/vnd.sun.xml.writer', - 't' => 'text/troff', - 'tao' => 'application/vnd.tao.intent-module-archive', - 'tar' => 'application/x-tar', - 'tcap' => 'application/vnd.3gpp2.tcap', - 'tcl' => 'application/x-tcl', - 'teacher' => 'application/vnd.smart.teacher', - 'tei' => 'application/tei+xml', - 'teicorpus' => 'application/tei+xml', - 'tex' => 'application/x-tex', - 'texi' => 'application/x-texinfo', - 'texinfo' => 'application/x-texinfo', - 'text' => 'text/plain', - 'tfi' => 'application/thraud+xml', - 'tfm' => 'application/x-tex-tfm', - 'thmx' => 'application/vnd.ms-officetheme', - 'tif' => 'image/tiff', - 'tiff' => 'image/tiff', - 'tmo' => 'application/vnd.tmobile-livetv', - 'torrent' => 'application/x-bittorrent', - 'tpl' => 'application/vnd.groove-tool-template', - 'tpt' => 'application/vnd.trid.tpt', - 'tr' => 'text/troff', - 'tra' => 'application/vnd.trueapp', - 'trm' => 'application/x-msterminal', - 'tsd' => 'application/timestamped-data', - 'tsv' => 'text/tab-separated-values', - 'ttc' => 'application/x-font-ttf', - 'ttf' => 'application/x-font-ttf', - 'ttl' => 'text/turtle', - 'twd' => 'application/vnd.simtech-mindmapper', - 'twds' => 'application/vnd.simtech-mindmapper', - 'txd' => 'application/vnd.genomatix.tuxedo', - 'txf' => 'application/vnd.mobius.txf', - 'txt' => 'text/plain', - 'u32' => 'application/x-authorware-bin', - 'udeb' => 'application/x-debian-package', - 'ufd' => 'application/vnd.ufdl', - 'ufdl' => 'application/vnd.ufdl', - 'umj' => 'application/vnd.umajin', - 'unityweb' => 'application/vnd.unity', - 'uoml' => 'application/vnd.uoml+xml', - 'uri' => 'text/uri-list', - 'uris' => 'text/uri-list', - 'urls' => 'text/uri-list', - 'ustar' => 'application/x-ustar', - 'utz' => 'application/vnd.uiq.theme', - 'uu' => 'text/x-uuencode', - 'uva' => 'audio/vnd.dece.audio', - 'uvd' => 'application/vnd.dece.data', - 'uvf' => 'application/vnd.dece.data', - 'uvg' => 'image/vnd.dece.graphic', - 'uvh' => 'video/vnd.dece.hd', - 'uvi' => 'image/vnd.dece.graphic', - 'uvm' => 'video/vnd.dece.mobile', - 'uvp' => 'video/vnd.dece.pd', - 'uvs' => 'video/vnd.dece.sd', - 'uvt' => 'application/vnd.dece.ttml+xml', - 'uvu' => 'video/vnd.uvvu.mp4', - 'uvv' => 'video/vnd.dece.video', - 'uvva' => 'audio/vnd.dece.audio', - 'uvvd' => 'application/vnd.dece.data', - 'uvvf' => 'application/vnd.dece.data', - 'uvvg' => 'image/vnd.dece.graphic', - 'uvvh' => 'video/vnd.dece.hd', - 'uvvi' => 'image/vnd.dece.graphic', - 'uvvm' => 'video/vnd.dece.mobile', - 'uvvp' => 'video/vnd.dece.pd', - 'uvvs' => 'video/vnd.dece.sd', - 'uvvt' => 'application/vnd.dece.ttml+xml', - 'uvvu' => 'video/vnd.uvvu.mp4', - 'uvvv' => 'video/vnd.dece.video', - 'uvvx' => 'application/vnd.dece.unspecified', - 'uvx' => 'application/vnd.dece.unspecified', - 'vcd' => 'application/x-cdlink', - 'vcf' => 'text/x-vcard', - 'vcg' => 'application/vnd.groove-vcard', - 'vcs' => 'text/x-vcalendar', - 'vcx' => 'application/vnd.vcx', - 'vis' => 'application/vnd.visionary', - 'viv' => 'video/vnd.vivo', - 'vor' => 'application/vnd.stardivision.writer', - 'vox' => 'application/x-authorware-bin', - 'vrml' => 'model/vrml', - 'vsd' => 'application/vnd.visio', - 'vsf' => 'application/vnd.vsf', - 'vss' => 'application/vnd.visio', - 'vst' => 'application/vnd.visio', - 'vsw' => 'application/vnd.visio', - 'vtu' => 'model/vnd.vtu', - 'vxml' => 'application/voicexml+xml', - 'w3d' => 'application/x-director', - 'wad' => 'application/x-doom', - 'wav' => 'audio/x-wav', - 'wax' => 'audio/x-ms-wax', - 'wbmp' => 'image/vnd.wap.wbmp', - 'wbs' => 'application/vnd.criticaltools.wbs+xml', - 'wbxml' => 'application/vnd.wap.wbxml', - 'wcm' => 'application/vnd.ms-works', - 'wdb' => 'application/vnd.ms-works', - 'weba' => 'audio/webm', - 'webm' => 'video/webm', - 'webp' => 'image/webp', - 'wg' => 'application/vnd.pmi.widget', - 'wgt' => 'application/widget', - 'wks' => 'application/vnd.ms-works', - 'wm' => 'video/x-ms-wm', - 'wma' => 'audio/x-ms-wma', - 'wmd' => 'application/x-ms-wmd', - 'wmf' => 'application/x-msmetafile', - 'wml' => 'text/vnd.wap.wml', - 'wmlc' => 'application/vnd.wap.wmlc', - 'wmls' => 'text/vnd.wap.wmlscript', - 'wmlsc' => 'application/vnd.wap.wmlscriptc', - 'wmv' => 'video/x-ms-wmv', - 'wmx' => 'video/x-ms-wmx', - 'wmz' => 'application/x-ms-wmz', - 'woff' => 'application/x-font-woff', - 'wpd' => 'application/vnd.wordperfect', - 'wpl' => 'application/vnd.ms-wpl', - 'wps' => 'application/vnd.ms-works', - 'wqd' => 'application/vnd.wqd', - 'wri' => 'application/x-mswrite', - 'wrl' => 'model/vrml', - 'wsdl' => 'application/wsdl+xml', - 'wspolicy' => 'application/wspolicy+xml', - 'wtb' => 'application/vnd.webturbo', - 'wvx' => 'video/x-ms-wvx', - 'x32' => 'application/x-authorware-bin', - 'x3d' => 'application/vnd.hzn-3d-crossword', - 'xap' => 'application/x-silverlight-app', - 'xar' => 'application/vnd.xara', - 'xbap' => 'application/x-ms-xbap', - 'xbd' => 'application/vnd.fujixerox.docuworks.binder', - 'xbm' => 'image/x-xbitmap', - 'xdf' => 'application/xcap-diff+xml', - 'xdm' => 'application/vnd.syncml.dm+xml', - 'xdp' => 'application/vnd.adobe.xdp+xml', - 'xdssc' => 'application/dssc+xml', - 'xdw' => 'application/vnd.fujixerox.docuworks', - 'xenc' => 'application/xenc+xml', - 'xer' => 'application/patch-ops-error+xml', - 'xfdf' => 'application/vnd.adobe.xfdf', - 'xfdl' => 'application/vnd.xfdl', - 'xht' => 'application/xhtml+xml', - 'xhtml' => 'application/xhtml+xml', - 'xhvml' => 'application/xv+xml', - 'xif' => 'image/vnd.xiff', - 'xla' => 'application/vnd.ms-excel', - 'xlam' => 'application/vnd.ms-excel.addin.macroenabled.12', - 'xlc' => 'application/vnd.ms-excel', - 'xlm' => 'application/vnd.ms-excel', - 'xls' => 'application/vnd.ms-excel', - 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroenabled.12', - 'xlsm' => 'application/vnd.ms-excel.sheet.macroenabled.12', - 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - 'xlt' => 'application/vnd.ms-excel', - 'xltm' => 'application/vnd.ms-excel.template.macroenabled.12', - 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', - 'xlw' => 'application/vnd.ms-excel', - 'xml' => 'application/xml', - 'xo' => 'application/vnd.olpc-sugar', - 'xop' => 'application/xop+xml', - 'xpi' => 'application/x-xpinstall', - 'xpm' => 'image/x-xpixmap', - 'xpr' => 'application/vnd.is-xpr', - 'xps' => 'application/vnd.ms-xpsdocument', - 'xpw' => 'application/vnd.intercon.formnet', - 'xpx' => 'application/vnd.intercon.formnet', - 'xsl' => 'application/xml', - 'xslt' => 'application/xslt+xml', - 'xsm' => 'application/vnd.syncml+xml', - 'xspf' => 'application/xspf+xml', - 'xul' => 'application/vnd.mozilla.xul+xml', - 'xvm' => 'application/xv+xml', - 'xvml' => 'application/xv+xml', - 'xwd' => 'image/x-xwindowdump', - 'xyz' => 'chemical/x-xyz', - 'yaml' => 'text/yaml', - 'yang' => 'application/yang', - 'yin' => 'application/yin+xml', - 'yml' => 'text/yaml', - 'zaz' => 'application/vnd.zzazz.deck+xml', - 'zip' => 'application/zip', - 'zir' => 'application/vnd.zul', - 'zirz' => 'application/vnd.zul', - 'zmm' => 'application/vnd.handheld-entertainment+xml' - ); - - /** - * Get a singleton instance of the class - * - * @return self - * @codeCoverageIgnore - */ - public static function getInstance() - { - if (!self::$instance) { - self::$instance = new self(); - } - - return self::$instance; - } - - /** - * Get a mimetype value from a file extension - * - * @param string $extension File extension - * - * @return string|null - * - */ - public function fromExtension($extension) - { - $extension = strtolower($extension); - - return isset($this->mimetypes[$extension]) - ? $this->mimetypes[$extension] - : null; - } - - /** - * Get a mimetype from a filename - * - * @param string $filename Filename to generate a mimetype from - * - * @return string|null - */ - public function fromFilename($filename) - { - return $this->fromExtension(pathinfo($filename, PATHINFO_EXTENSION)); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Pool.php b/core/vendor/guzzlehttp/guzzle/src/Pool.php index 49d9940fe2031759f8f81aa0ba3a5b61610d482f..bc41d6e6bf3ac19f81a7b25752a9b26ac9416704 100644 --- a/core/vendor/guzzlehttp/guzzle/src/Pool.php +++ b/core/vendor/guzzlehttp/guzzle/src/Pool.php @@ -1,97 +1,81 @@ <?php namespace GuzzleHttp; -use GuzzleHttp\Event\BeforeEvent; -use GuzzleHttp\Event\RequestEvents; -use GuzzleHttp\Message\RequestInterface; -use GuzzleHttp\Message\ResponseInterface; -use GuzzleHttp\Ring\Core; -use GuzzleHttp\Ring\Future\FutureInterface; -use GuzzleHttp\Event\ListenerAttacherTrait; -use GuzzleHttp\Event\EndEvent; -use React\Promise\Deferred; -use React\Promise\FulfilledPromise; -use React\Promise\PromiseInterface; -use React\Promise\RejectedPromise; +use GuzzleHttp\Promise\PromisorInterface; +use Psr\Http\Message\RequestInterface; +use GuzzleHttp\Promise\EachPromise; /** * Sends and iterator of requests concurrently using a capped pool size. * - * The Pool object implements FutureInterface, meaning it can be used later - * when necessary, the requests provided to the pool can be cancelled, and - * you can check the state of the pool to know if it has been dereferenced - * (sent) or has been cancelled. + * The pool will read from an iterator until it is cancelled or until the + * iterator is consumed. When a request is yielded, the request is sent after + * applying the "request_options" request options (if provided in the ctor). * - * When sending the pool, keep in mind that no results are returned: callers - * are expected to handle results asynchronously using Guzzle's event system. - * When requests complete, more are added to the pool to ensure that the - * requested pool size is always filled as much as possible. - * - * IMPORTANT: Do not provide a pool size greater that what the utilized - * underlying RingPHP handler can support. This will result is extremely poor - * performance. + * When a function is yielded by the iterator, the function is provided the + * "request_options" array that should be merged on top of any existing + * options, and the function MUST then return a wait-able promise. */ -class Pool implements FutureInterface +class Pool implements PromisorInterface { - use ListenerAttacherTrait; - - /** @var \GuzzleHttp\ClientInterface */ - private $client; - - /** @var \Iterator Yields requests */ - private $iter; - - /** @var Deferred */ - private $deferred; - - /** @var PromiseInterface */ - private $promise; - - private $waitQueue = []; - private $eventListeners = []; - private $poolSize; - private $isRealized = false; + /** @var EachPromise */ + private $each; /** - * The option values for 'before', 'complete', 'error' and 'end' can be a - * callable, an associative array containing event data, or an array of - * event data arrays. Event data arrays contain the following keys: - * - * - fn: callable to invoke that receives the event - * - priority: Optional event priority (defaults to 0) - * - once: Set to true so that the event is removed after it is triggered - * * @param ClientInterface $client Client used to send the requests. - * @param array|\Iterator $requests Requests to send in parallel - * @param array $options Associative array of options - * - pool_size: (callable|int) Maximum number of requests to send - * concurrently, or a callback that receives - * the current queue size and returns the - * number of new requests to send - * - before: (callable|array) Receives a BeforeEvent - * - complete: (callable|array) Receives a CompleteEvent - * - error: (callable|array) Receives a ErrorEvent - * - end: (callable|array) Receives an EndEvent + * @param array|\Iterator $requests Requests or functions that return + * requests to send concurrently. + * @param array $config Associative array of options + * - concurrency: (int) Maximum number of requests to send concurrently + * - options: Array of request options to apply to each request. + * - fulfilled: (callable) Function to invoke when a request completes. + * - rejected: (callable) Function to invoke when a request is rejected. */ public function __construct( ClientInterface $client, $requests, - array $options = [] + array $config = [] ) { - $this->client = $client; - $this->iter = $this->coerceIterable($requests); - $this->deferred = new Deferred(); - $this->promise = $this->deferred->promise(); - $this->poolSize = isset($options['pool_size']) - ? $options['pool_size'] : 25; - $this->eventListeners = $this->prepareListeners( - $options, - ['before', 'complete', 'error', 'end'] - ); + // Backwards compatibility. + if (isset($config['pool_size'])) { + $config['concurrency'] = $config['pool_size']; + } elseif (!isset($config['concurrency'])) { + $config['concurrency'] = 25; + } + + if (isset($config['options'])) { + $opts = $config['options']; + unset($config['options']); + } else { + $opts = []; + } + + $iterable = \GuzzleHttp\Promise\iter_for($requests); + $requests = function () use ($iterable, $client, $opts) { + foreach ($iterable as $rfn) { + if ($rfn instanceof RequestInterface) { + yield $client->sendAsync($rfn, $opts); + } elseif (is_callable($rfn)) { + yield $rfn($opts); + } else { + throw new \InvalidArgumentException('Each value yielded by ' + . 'the iterator must be a Psr7\Http\Message\RequestInterface ' + . 'or a callable that returns a promise that fulfills ' + . 'with a Psr7\Message\Http\ResponseInterface object.'); + } + } + }; + + $this->each = new EachPromise($requests(), $config); + } + + public function promise() + { + return $this->each->promise(); } /** - * Sends multiple requests in parallel and returns an array of responses + * Sends multiple requests concurrently and returns an array of responses * and exceptions that uses the same ordering as the provided requests. * * IMPORTANT: This method keeps every request and response in memory, and @@ -99,11 +83,12 @@ public function __construct( * indeterminate number of requests concurrently. * * @param ClientInterface $client Client used to send the requests - * @param array|\Iterator $requests Requests to send in parallel + * @param array|\Iterator $requests Requests to send concurrently. * @param array $options Passes through the options available in * {@see GuzzleHttp\Pool::__construct} * - * @return BatchResults Returns a container for the results. + * @return array Returns an array containing the response or an exception + * in the same order that the requests were sent. * @throws \InvalidArgumentException if the event format is incorrect. */ public static function batch( @@ -111,223 +96,28 @@ public static function batch( $requests, array $options = [] ) { - $hash = new \SplObjectStorage(); - foreach ($requests as $request) { - $hash->attach($request); - } - - // In addition to the normally run events when requests complete, add - // and event to continuously track the results of transfers in the hash. - (new self($client, $requests, RequestEvents::convertEventArray( - $options, - ['end'], - [ - 'priority' => RequestEvents::LATE, - 'fn' => function (EndEvent $e) use ($hash) { - $hash[$e->getRequest()] = $e->getException() - ? $e->getException() - : $e->getResponse(); - } - ] - )))->wait(); - - return new BatchResults($hash); - } - - /** - * Creates a Pool and immediately sends the requests. - * - * @param ClientInterface $client Client used to send the requests - * @param array|\Iterator $requests Requests to send in parallel - * @param array $options Passes through the options available in - * {@see GuzzleHttp\Pool::__construct} - */ - public static function send( - ClientInterface $client, - $requests, - array $options = [] - ) { - $pool = new self($client, $requests, $options); - $pool->wait(); + $res = []; + self::cmpCallback($options, 'fulfilled', $res); + self::cmpCallback($options, 'rejected', $res); + $pool = new static($client, $requests, $options); + $pool->promise()->wait(); + ksort($res); + + return $res; } - private function getPoolSize() + private static function cmpCallback(array &$options, $name, array &$results) { - return is_callable($this->poolSize) - ? call_user_func($this->poolSize, count($this->waitQueue)) - : $this->poolSize; - } - - /** - * Add as many requests as possible up to the current pool limit. - */ - private function addNextRequests() - { - $limit = max($this->getPoolSize() - count($this->waitQueue), 0); - while ($limit--) { - if (!$this->addNextRequest()) { - break; - } + if (!isset($options[$name])) { + $options[$name] = function ($v, $k) use (&$results) { + $results[$k] = $v; + }; + } else { + $currentFn = $options[$name]; + $options[$name] = function ($v, $k) use (&$results, $currentFn) { + $currentFn($v, $k); + $results[$k] = $v; + }; } } - - public function wait() - { - if ($this->isRealized) { - return false; - } - - // Seed the pool with N number of requests. - $this->addNextRequests(); - - // Stop if the pool was cancelled while transferring requests. - if ($this->isRealized) { - return false; - } - - // Wait on any outstanding FutureResponse objects. - while ($response = array_pop($this->waitQueue)) { - try { - $response->wait(); - } catch (\Exception $e) { - // Eat exceptions because they should be handled asynchronously - } - $this->addNextRequests(); - } - - // Clean up no longer needed state. - $this->isRealized = true; - $this->waitQueue = $this->eventListeners = []; - $this->client = $this->iter = null; - $this->deferred->resolve(true); - - return true; - } - - /** - * {@inheritdoc} - * - * Attempt to cancel all outstanding requests (requests that are queued for - * dereferencing). Returns true if all outstanding requests can be - * cancelled. - * - * @return bool - */ - public function cancel() - { - if ($this->isRealized) { - return false; - } - - $success = $this->isRealized = true; - foreach ($this->waitQueue as $response) { - if (!$response->cancel()) { - $success = false; - } - } - - return $success; - } - - /** - * Returns a promise that is invoked when the pool completed. There will be - * no passed value. - * - * {@inheritdoc} - */ - public function then( - callable $onFulfilled = null, - callable $onRejected = null, - callable $onProgress = null - ) { - return $this->promise->then($onFulfilled, $onRejected, $onProgress); - } - - public function promise() - { - return $this->promise; - } - - private function coerceIterable($requests) - { - if ($requests instanceof \Iterator) { - return $requests; - } elseif (is_array($requests)) { - return new \ArrayIterator($requests); - } - - throw new \InvalidArgumentException('Expected Iterator or array. ' - . 'Found ' . Core::describeType($requests)); - } - - /** - * Adds the next request to pool and tracks what requests need to be - * dereferenced when completing the pool. - */ - private function addNextRequest() - { - add_next: - - if ($this->isRealized || !$this->iter || !$this->iter->valid()) { - return false; - } - - $request = $this->iter->current(); - $this->iter->next(); - - if (!($request instanceof RequestInterface)) { - throw new \InvalidArgumentException(sprintf( - 'All requests in the provided iterator must implement ' - . 'RequestInterface. Found %s', - Core::describeType($request) - )); - } - - // Be sure to use "lazy" futures, meaning they do not send right away. - $request->getConfig()->set('future', 'lazy'); - $hash = spl_object_hash($request); - $this->attachListeners($request, $this->eventListeners); - $request->getEmitter()->on('before', [$this, '_trackRetries'], RequestEvents::EARLY); - $response = $this->client->send($request); - $this->waitQueue[$hash] = $response; - $promise = $response->promise(); - - // Don't recursively call itself for completed or rejected responses. - if ($promise instanceof FulfilledPromise - || $promise instanceof RejectedPromise - ) { - try { - $this->finishResponse($request, $response->wait(), $hash); - } catch (\Exception $e) { - $this->finishResponse($request, $e, $hash); - } - goto add_next; - } - - // Use this function for both resolution and rejection. - $thenFn = function ($value) use ($request, $hash) { - $this->finishResponse($request, $value, $hash); - if (!$request->getConfig()->get('_pool_retries')) { - $this->addNextRequests(); - } - }; - - $promise->then($thenFn, $thenFn); - - return true; - } - - public function _trackRetries(BeforeEvent $e) - { - $e->getRequest()->getConfig()->set('_pool_retries', $e->getRetryCount()); - } - - private function finishResponse($request, $value, $hash) - { - unset($this->waitQueue[$hash]); - $result = $value instanceof ResponseInterface - ? ['request' => $request, 'response' => $value, 'error' => null] - : ['request' => $request, 'response' => null, 'error' => $value]; - $this->deferred->progress($result); - } } diff --git a/core/vendor/guzzlehttp/guzzle/src/Post/MultipartBody.php b/core/vendor/guzzlehttp/guzzle/src/Post/MultipartBody.php deleted file mode 100644 index 1149e62354409666933e3ae5d980f740cee58d0a..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Post/MultipartBody.php +++ /dev/null @@ -1,109 +0,0 @@ -<?php -namespace GuzzleHttp\Post; - -use GuzzleHttp\Stream\AppendStream; -use GuzzleHttp\Stream\Stream; -use GuzzleHttp\Stream\StreamDecoratorTrait; -use GuzzleHttp\Stream\StreamInterface; - -/** - * Stream that when read returns bytes for a streaming multipart/form-data body - */ -class MultipartBody implements StreamInterface -{ - use StreamDecoratorTrait; - - private $boundary; - - /** - * @param array $fields Associative array of field names to values where - * each value is a string or array of strings. - * @param array $files Associative array of PostFileInterface objects - * @param string $boundary You can optionally provide a specific boundary - * @throws \InvalidArgumentException - */ - public function __construct( - array $fields = [], - array $files = [], - $boundary = null - ) { - $this->boundary = $boundary ?: uniqid(); - $this->stream = $this->createStream($fields, $files); - } - - /** - * Get the boundary - * - * @return string - */ - public function getBoundary() - { - return $this->boundary; - } - - public function isWritable() - { - return false; - } - - /** - * Get the string needed to transfer a POST field - */ - private function getFieldString($name, $value) - { - return sprintf( - "--%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s\r\n", - $this->boundary, - $name, - $value - ); - } - - /** - * Get the headers needed before transferring the content of a POST file - */ - private function getFileHeaders(PostFileInterface $file) - { - $headers = ''; - foreach ($file->getHeaders() as $key => $value) { - $headers .= "{$key}: {$value}\r\n"; - } - - return "--{$this->boundary}\r\n" . trim($headers) . "\r\n\r\n"; - } - - /** - * Create the aggregate stream that will be used to upload the POST data - */ - protected function createStream(array $fields, array $files) - { - $stream = new AppendStream(); - - foreach ($fields as $name => $fieldValues) { - foreach ((array) $fieldValues as $value) { - $stream->addStream( - Stream::factory($this->getFieldString($name, $value)) - ); - } - } - - foreach ($files as $file) { - - if (!$file instanceof PostFileInterface) { - throw new \InvalidArgumentException('All POST fields must ' - . 'implement PostFieldInterface'); - } - - $stream->addStream( - Stream::factory($this->getFileHeaders($file)) - ); - $stream->addStream($file->getContent()); - $stream->addStream(Stream::factory("\r\n")); - } - - // Add the trailing boundary with CRLF - $stream->addStream(Stream::factory("--{$this->boundary}--\r\n")); - - return $stream; - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Post/PostBody.php b/core/vendor/guzzlehttp/guzzle/src/Post/PostBody.php deleted file mode 100644 index ed14d1f7037fc2c940d46d7da8cc64b0c5e9d1fc..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Post/PostBody.php +++ /dev/null @@ -1,287 +0,0 @@ -<?php -namespace GuzzleHttp\Post; - -use GuzzleHttp\Message\RequestInterface; -use GuzzleHttp\Stream\Exception\CannotAttachException; -use GuzzleHttp\Stream\StreamInterface; -use GuzzleHttp\Stream\Stream; -use GuzzleHttp\Query; - -/** - * Holds POST fields and files and creates a streaming body when read methods - * are called on the object. - */ -class PostBody implements PostBodyInterface -{ - /** @var StreamInterface */ - private $body; - - /** @var callable */ - private $aggregator; - - private $fields = []; - - /** @var PostFileInterface[] */ - private $files = []; - private $forceMultipart = false; - private $detached = false; - - /** - * Applies request headers to a request based on the POST state - * - * @param RequestInterface $request Request to update - */ - public function applyRequestHeaders(RequestInterface $request) - { - if ($this->files || $this->forceMultipart) { - $request->setHeader( - 'Content-Type', - 'multipart/form-data; boundary=' . $this->getBody()->getBoundary() - ); - } elseif ($this->fields && !$request->hasHeader('Content-Type')) { - $request->setHeader( - 'Content-Type', - 'application/x-www-form-urlencoded' - ); - } - - if ($size = $this->getSize()) { - $request->setHeader('Content-Length', $size); - } - } - - public function forceMultipartUpload($force) - { - $this->forceMultipart = $force; - } - - public function setAggregator(callable $aggregator) - { - $this->aggregator = $aggregator; - } - - public function setField($name, $value) - { - $this->fields[$name] = $value; - $this->mutate(); - } - - public function replaceFields(array $fields) - { - $this->fields = $fields; - $this->mutate(); - } - - public function getField($name) - { - return isset($this->fields[$name]) ? $this->fields[$name] : null; - } - - public function removeField($name) - { - unset($this->fields[$name]); - $this->mutate(); - } - - public function getFields($asString = false) - { - if (!$asString) { - return $this->fields; - } - - $query = new Query($this->fields); - $query->setEncodingType(Query::RFC1738); - $query->setAggregator($this->getAggregator()); - - return (string) $query; - } - - public function hasField($name) - { - return isset($this->fields[$name]); - } - - public function getFile($name) - { - foreach ($this->files as $file) { - if ($file->getName() == $name) { - return $file; - } - } - - return null; - } - - public function getFiles() - { - return $this->files; - } - - public function addFile(PostFileInterface $file) - { - $this->files[] = $file; - $this->mutate(); - } - - public function clearFiles() - { - $this->files = []; - $this->mutate(); - } - - /** - * Returns the numbers of fields + files - * - * @return int - */ - public function count() - { - return count($this->files) + count($this->fields); - } - - public function __toString() - { - return (string) $this->getBody(); - } - - public function getContents($maxLength = -1) - { - return $this->getBody()->getContents(); - } - - public function close() - { - $this->detach(); - } - - public function detach() - { - $this->detached = true; - $this->fields = $this->files = []; - - if ($this->body) { - $this->body->close(); - $this->body = null; - } - } - - public function attach($stream) - { - throw new CannotAttachException(); - } - - public function eof() - { - return $this->getBody()->eof(); - } - - public function tell() - { - return $this->body ? $this->body->tell() : 0; - } - - public function isSeekable() - { - return true; - } - - public function isReadable() - { - return true; - } - - public function isWritable() - { - return false; - } - - public function getSize() - { - return $this->getBody()->getSize(); - } - - public function seek($offset, $whence = SEEK_SET) - { - return $this->getBody()->seek($offset, $whence); - } - - public function read($length) - { - return $this->getBody()->read($length); - } - - public function write($string) - { - return false; - } - - public function getMetadata($key = null) - { - return $key ? null : []; - } - - /** - * Return a stream object that is built from the POST fields and files. - * - * If one has already been created, the previously created stream will be - * returned. - */ - private function getBody() - { - if ($this->body) { - return $this->body; - } elseif ($this->files || $this->forceMultipart) { - return $this->body = $this->createMultipart(); - } elseif ($this->fields) { - return $this->body = $this->createUrlEncoded(); - } else { - return $this->body = Stream::factory(); - } - } - - /** - * Get the aggregator used to join multi-valued field parameters - * - * @return callable - */ - final protected function getAggregator() - { - if (!$this->aggregator) { - $this->aggregator = Query::phpAggregator(); - } - - return $this->aggregator; - } - - /** - * Creates a multipart/form-data body stream - * - * @return MultipartBody - */ - private function createMultipart() - { - // Flatten the nested query string values using the correct aggregator - return new MultipartBody( - call_user_func($this->getAggregator(), $this->fields), - $this->files - ); - } - - /** - * Creates an application/x-www-form-urlencoded stream body - * - * @return StreamInterface - */ - private function createUrlEncoded() - { - return Stream::factory($this->getFields(true)); - } - - /** - * Get rid of any cached data - */ - private function mutate() - { - $this->body = null; - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Post/PostBodyInterface.php b/core/vendor/guzzlehttp/guzzle/src/Post/PostBodyInterface.php deleted file mode 100644 index c2ec9a62c5b4cff75ac7bfd3a370d5e4a6c8de26..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Post/PostBodyInterface.php +++ /dev/null @@ -1,109 +0,0 @@ -<?php -namespace GuzzleHttp\Post; - -use GuzzleHttp\Message\AppliesHeadersInterface; -use GuzzleHttp\Stream\StreamInterface; - -/** - * Represents a POST body that is sent as either a multipart/form-data stream - * or application/x-www-urlencoded stream. - */ -interface PostBodyInterface extends StreamInterface, \Countable, AppliesHeadersInterface -{ - /** - * Set a specific field - * - * @param string $name Name of the field to set - * @param string|array $value Value to set - */ - public function setField($name, $value); - - /** - * Set the aggregation strategy that will be used to turn multi-valued - * fields into a string. - * - * The aggregation function accepts a deeply nested array of query string - * values and returns a flattened associative array of key value pairs. - * - * @param callable $aggregator - */ - public function setAggregator(callable $aggregator); - - /** - * Set to true to force a multipart upload even if there are no files. - * - * @param bool $force Set to true to force multipart uploads or false to - * remove this flag. - */ - public function forceMultipartUpload($force); - - /** - * Replace all existing form fields with an array of fields - * - * @param array $fields Associative array of fields to set - */ - public function replaceFields(array $fields); - - /** - * Get a specific field by name - * - * @param string $name Name of the POST field to retrieve - * - * @return string|null - */ - public function getField($name); - - /** - * Remove a field by name - * - * @param string $name Name of the field to remove - */ - public function removeField($name); - - /** - * Returns an associative array of names to values or a query string. - * - * @param bool $asString Set to true to retrieve the fields as a query - * string. - * - * @return array|string - */ - public function getFields($asString = false); - - /** - * Returns true if a field is set - * - * @param string $name Name of the field to set - * - * @return bool - */ - public function hasField($name); - - /** - * Get all of the files - * - * @return array Returns an array of PostFileInterface objects - */ - public function getFiles(); - - /** - * Get a POST file by name. - * - * @param string $name Name of the POST file to retrieve - * - * @return PostFileInterface|null - */ - public function getFile($name); - - /** - * Add a file to the POST - * - * @param PostFileInterface $file File to add - */ - public function addFile(PostFileInterface $file); - - /** - * Remove all files from the collection - */ - public function clearFiles(); -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Post/PostFile.php b/core/vendor/guzzlehttp/guzzle/src/Post/PostFile.php deleted file mode 100644 index e102c024308a7c85b7cb6b94559535fd56f61643..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Post/PostFile.php +++ /dev/null @@ -1,135 +0,0 @@ -<?php -namespace GuzzleHttp\Post; - -use GuzzleHttp\Mimetypes; -use GuzzleHttp\Stream\StreamInterface; -use GuzzleHttp\Stream\Stream; - -/** - * Post file upload - */ -class PostFile implements PostFileInterface -{ - private $name; - private $filename; - private $content; - private $headers = []; - - /** - * @param string $name Name of the form field - * @param mixed $content Data to send - * @param string|null $filename Filename content-disposition attribute - * @param array $headers Array of headers to set on the file - * (can override any default headers) - * @throws \RuntimeException when filename is not passed or can't be determined - */ - public function __construct( - $name, - $content, - $filename = null, - array $headers = [] - ) { - $this->headers = $headers; - $this->name = $name; - $this->prepareContent($content); - $this->prepareFilename($filename); - $this->prepareDefaultHeaders(); - } - - public function getName() - { - return $this->name; - } - - public function getFilename() - { - return $this->filename; - } - - public function getContent() - { - return $this->content; - } - - public function getHeaders() - { - return $this->headers; - } - - /** - * Prepares the contents of a POST file. - * - * @param mixed $content Content of the POST file - */ - private function prepareContent($content) - { - $this->content = $content; - - if (!($this->content instanceof StreamInterface)) { - $this->content = Stream::factory($this->content); - } elseif ($this->content instanceof MultipartBody) { - if (!$this->hasHeader('Content-Disposition')) { - $disposition = 'form-data; name="' . $this->name .'"'; - $this->headers['Content-Disposition'] = $disposition; - } - - if (!$this->hasHeader('Content-Type')) { - $this->headers['Content-Type'] = sprintf( - "multipart/form-data; boundary=%s", - $this->content->getBoundary() - ); - } - } - } - - /** - * Applies a file name to the POST file based on various checks. - * - * @param string|null $filename Filename to apply (or null to guess) - */ - private function prepareFilename($filename) - { - $this->filename = $filename; - - if (!$this->filename) { - $this->filename = $this->content->getMetadata('uri'); - } - - if (!$this->filename || substr($this->filename, 0, 6) === 'php://') { - $this->filename = $this->name; - } - } - - /** - * Applies default Content-Disposition and Content-Type headers if needed. - */ - private function prepareDefaultHeaders() - { - // Set a default content-disposition header if one was no provided - if (!$this->hasHeader('Content-Disposition')) { - $this->headers['Content-Disposition'] = sprintf( - 'form-data; name="%s"; filename="%s"', - $this->name, - basename($this->filename) - ); - } - - // Set a default Content-Type if one was not supplied - if (!$this->hasHeader('Content-Type')) { - $this->headers['Content-Type'] = Mimetypes::getInstance() - ->fromFilename($this->filename) ?: 'text/plain'; - } - } - - /** - * Check if a specific header exists on the POST file by name. - * - * @param string $name Case-insensitive header to check - * - * @return bool - */ - private function hasHeader($name) - { - return isset(array_change_key_case($this->headers)[strtolower($name)]); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Post/PostFileInterface.php b/core/vendor/guzzlehttp/guzzle/src/Post/PostFileInterface.php deleted file mode 100644 index 2e816c0884f6afd566b8cb28fbe7fbd909dee5a8..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Post/PostFileInterface.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php -namespace GuzzleHttp\Post; - -use GuzzleHttp\Stream\StreamInterface; - -/** - * Post file upload interface - */ -interface PostFileInterface -{ - /** - * Get the name of the form field - * - * @return string - */ - public function getName(); - - /** - * Get the full path to the file - * - * @return string - */ - public function getFilename(); - - /** - * Get the content - * - * @return StreamInterface - */ - public function getContent(); - - /** - * Gets all POST file headers. - * - * The keys represent the header name as it will be sent over the wire, and - * each value is a string. - * - * @return array Returns an associative array of the file's headers. - */ - public function getHeaders(); -} diff --git a/core/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php b/core/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php new file mode 100644 index 0000000000000000000000000000000000000000..e6d176b6156cd9816b2af04e34518e906f595e0d --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php @@ -0,0 +1,112 @@ +<?php +namespace GuzzleHttp; + +use GuzzleHttp\Promise\PromiseInterface; +use GuzzleHttp\Psr7; +use Psr\Http\Message\RequestInterface; + +/** + * Prepares requests that contain a body, adding the Content-Length, + * Content-Type, and Expect headers. + */ +class PrepareBodyMiddleware +{ + /** @var callable */ + private $nextHandler; + + /** @var array */ + private static $skipMethods = ['GET' => true, 'HEAD' => true]; + + /** + * @param callable $nextHandler Next handler to invoke. + */ + public function __construct(callable $nextHandler) + { + $this->nextHandler = $nextHandler; + } + + /** + * @param RequestInterface $request + * @param array $options + * + * @return PromiseInterface + */ + public function __invoke(RequestInterface $request, array $options) + { + $fn = $this->nextHandler; + + // Don't do anything if the request has no body. + if (isset(self::$skipMethods[$request->getMethod()]) + || $request->getBody()->getSize() === 0 + ) { + return $fn($request, $options); + } + + $modify = []; + + // Add a default content-type if possible. + if (!$request->hasHeader('Content-Type')) { + if ($uri = $request->getBody()->getMetadata('uri')) { + if ($type = Psr7\mimetype_from_filename($uri)) { + $modify['set_headers']['Content-Type'] = $type; + } + } + } + + // Add a default content-length or transfer-encoding header. + if (!isset(self::$skipMethods[$request->getMethod()]) + && !$request->hasHeader('Content-Length') + && !$request->hasHeader('Transfer-Encoding') + ) { + $size = $request->getBody()->getSize(); + if ($size !== null) { + $modify['set_headers']['Content-Length'] = $size; + } else { + $modify['set_headers']['Transfer-Encoding'] = 'chunked'; + } + } + + // Add the expect header if needed. + $this->addExpectHeader($request, $options, $modify); + + return $fn(Psr7\modify_request($request, $modify), $options); + } + + private function addExpectHeader( + RequestInterface $request, + array $options, + array &$modify + ) { + // Determine if the Expect header should be used + if ($request->hasHeader('Expect')) { + return; + } + + $expect = isset($options['expect']) ? $options['expect'] : null; + + // Return if disabled or if you're not using HTTP/1.1 or HTTP/2.0 + if ($expect === false || $request->getProtocolVersion() < 1.1) { + return; + } + + // The expect header is unconditionally enabled + if ($expect === true) { + $modify['set_headers']['Expect'] = '100-Continue'; + return; + } + + // By default, send the expect header when the payload is > 1mb + if ($expect === null) { + $expect = 1048576; + } + + // Always add if the body cannot be rewound, the size cannot be + // determined, or the size is greater than the cutoff threshold + $body = $request->getBody(); + $size = $body->getSize(); + + if ($size === null || $size >= (int) $expect || !$body->isSeekable()) { + $modify['set_headers']['Expect'] = '100-Continue'; + } + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Query.php b/core/vendor/guzzlehttp/guzzle/src/Query.php deleted file mode 100644 index d053def6e93f4c287685c9656c950aaa20579060..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Query.php +++ /dev/null @@ -1,204 +0,0 @@ -<?php -namespace GuzzleHttp; - -/** - * Manages query string variables and can aggregate them into a string - */ -class Query extends Collection -{ - const RFC3986 = 'RFC3986'; - const RFC1738 = 'RFC1738'; - - /** @var callable Encoding function */ - private $encoding = 'rawurlencode'; - /** @var callable */ - private $aggregator; - - /** - * Parse a query string into a Query object - * - * $urlEncoding is used to control how the query string is parsed and how - * it is ultimately serialized. The value can be set to one of the - * following: - * - * - true: (default) Parse query strings using RFC 3986 while still - * converting "+" to " ". - * - false: Disables URL decoding of the input string and URL encoding when - * the query string is serialized. - * - 'RFC3986': Use RFC 3986 URL encoding/decoding - * - 'RFC1738': Use RFC 1738 URL encoding/decoding - * - * @param string $query Query string to parse - * @param bool|string $urlEncoding Controls how the input string is decoded - * and encoded. - * @return self - */ - public static function fromString($query, $urlEncoding = true) - { - static $qp; - if (!$qp) { - $qp = new QueryParser(); - } - - $q = new static(); - - if ($urlEncoding !== true) { - $q->setEncodingType($urlEncoding); - } - - $qp->parseInto($q, $query, $urlEncoding); - - return $q; - } - - /** - * Convert the query string parameters to a query string string - * - * @return string - */ - public function __toString() - { - if (!$this->data) { - return ''; - } - - // The default aggregator is statically cached - static $defaultAggregator; - - if (!$this->aggregator) { - if (!$defaultAggregator) { - $defaultAggregator = self::phpAggregator(); - } - $this->aggregator = $defaultAggregator; - } - - $result = ''; - $aggregator = $this->aggregator; - $encoder = $this->encoding; - - foreach ($aggregator($this->data) as $key => $values) { - foreach ($values as $value) { - if ($result) { - $result .= '&'; - } - $result .= $encoder($key); - if ($value !== null) { - $result .= '=' . $encoder($value); - } - } - } - - return $result; - } - - /** - * Controls how multi-valued query string parameters are aggregated into a - * string. - * - * $query->setAggregator($query::duplicateAggregator()); - * - * @param callable $aggregator Callable used to convert a deeply nested - * array of query string variables into a flattened array of key value - * pairs. The callable accepts an array of query data and returns a - * flattened array of key value pairs where each value is an array of - * strings. - */ - public function setAggregator(callable $aggregator) - { - $this->aggregator = $aggregator; - } - - /** - * Specify how values are URL encoded - * - * @param string|bool $type One of 'RFC1738', 'RFC3986', or false to disable encoding - * - * @throws \InvalidArgumentException - */ - public function setEncodingType($type) - { - switch ($type) { - case self::RFC3986: - $this->encoding = 'rawurlencode'; - break; - case self::RFC1738: - $this->encoding = 'urlencode'; - break; - case false: - $this->encoding = function ($v) { return $v; }; - break; - default: - throw new \InvalidArgumentException('Invalid URL encoding type'); - } - } - - /** - * Query string aggregator that does not aggregate nested query string - * values and allows duplicates in the resulting array. - * - * Example: http://test.com?q=1&q=2 - * - * @return callable - */ - public static function duplicateAggregator() - { - return function (array $data) { - return self::walkQuery($data, '', function ($key, $prefix) { - return is_int($key) ? $prefix : "{$prefix}[{$key}]"; - }); - }; - } - - /** - * Aggregates nested query string variables using the same technique as - * ``http_build_query()``. - * - * @param bool $numericIndices Pass false to not include numeric indices - * when multi-values query string parameters are present. - * - * @return callable - */ - public static function phpAggregator($numericIndices = true) - { - return function (array $data) use ($numericIndices) { - return self::walkQuery( - $data, - '', - function ($key, $prefix) use ($numericIndices) { - return !$numericIndices && is_int($key) - ? "{$prefix}[]" - : "{$prefix}[{$key}]"; - } - ); - }; - } - - /** - * Easily create query aggregation functions by providing a key prefix - * function to this query string array walker. - * - * @param array $query Query string to walk - * @param string $keyPrefix Key prefix (start with '') - * @param callable $prefixer Function used to create a key prefix - * - * @return array - */ - public static function walkQuery(array $query, $keyPrefix, callable $prefixer) - { - $result = []; - foreach ($query as $key => $value) { - if ($keyPrefix) { - $key = $prefixer($key, $keyPrefix); - } - if (is_array($value)) { - $result += self::walkQuery($value, $key, $prefixer); - } elseif (isset($result[$key])) { - $result[$key][] = $value; - } else { - $result[$key] = array($value); - } - } - - return $result; - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/QueryParser.php b/core/vendor/guzzlehttp/guzzle/src/QueryParser.php deleted file mode 100644 index 90727cc6cf0ef93e99b5c4f935d18812845ffae0..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/QueryParser.php +++ /dev/null @@ -1,163 +0,0 @@ -<?php -namespace GuzzleHttp; - -/** - * Parses query strings into a Query object. - * - * While parsing, the parser will attempt to determine the most appropriate - * query string aggregator to use when serializing the parsed query string - * object back into a string. The hope is that parsing then serializing a - * query string should be a lossless operation. - * - * @internal Use Query::fromString() - */ -class QueryParser -{ - private $duplicates; - private $numericIndices; - - /** - * Parse a query string into a Query object. - * - * @param Query $query Query object to populate - * @param string $str Query string to parse - * @param bool|string $urlEncoding How the query string is encoded - */ - public function parseInto(Query $query, $str, $urlEncoding = true) - { - if ($str === '') { - return; - } - - $result = []; - $this->duplicates = false; - $this->numericIndices = true; - $decoder = self::getDecoder($urlEncoding); - - foreach (explode('&', $str) as $kvp) { - - $parts = explode('=', $kvp, 2); - $key = $decoder($parts[0]); - $value = isset($parts[1]) ? $decoder($parts[1]) : null; - - // Special handling needs to be taken for PHP nested array syntax - if (strpos($key, '[') !== false) { - $this->parsePhpValue($key, $value, $result); - continue; - } - - if (!isset($result[$key])) { - $result[$key] = $value; - } else { - $this->duplicates = true; - if (!is_array($result[$key])) { - $result[$key] = [$result[$key]]; - } - $result[$key][] = $value; - } - } - - $query->replace($result); - - if (!$this->numericIndices) { - $query->setAggregator(Query::phpAggregator(false)); - } elseif ($this->duplicates) { - $query->setAggregator(Query::duplicateAggregator()); - } - } - - /** - * Returns a callable that is used to URL decode query keys and values. - * - * @param string|bool $type One of true, false, RFC3986, and RFC1738 - * - * @return callable|string - */ - private static function getDecoder($type) - { - if ($type === true) { - return function ($value) { - return rawurldecode(str_replace('+', ' ', $value)); - }; - } elseif ($type == Query::RFC3986) { - return 'rawurldecode'; - } elseif ($type == Query::RFC1738) { - return 'urldecode'; - } else { - return function ($str) { return $str; }; - } - } - - /** - * Parses a PHP style key value pair. - * - * @param string $key Key to parse (e.g., "foo[a][b]") - * @param string|null $value Value to set - * @param array $result Result to modify by reference - */ - private function parsePhpValue($key, $value, array &$result) - { - $node =& $result; - $keyBuffer = ''; - - for ($i = 0, $t = strlen($key); $i < $t; $i++) { - switch ($key[$i]) { - case '[': - if ($keyBuffer) { - $this->prepareNode($node, $keyBuffer); - $node =& $node[$keyBuffer]; - $keyBuffer = ''; - } - break; - case ']': - $k = $this->cleanKey($node, $keyBuffer); - $this->prepareNode($node, $k); - $node =& $node[$k]; - $keyBuffer = ''; - break; - default: - $keyBuffer .= $key[$i]; - break; - } - } - - if (isset($node)) { - $this->duplicates = true; - $node[] = $value; - } else { - $node = $value; - } - } - - /** - * Prepares a value in the array at the given key. - * - * If the key already exists, the key value is converted into an array. - * - * @param array $node Result node to modify - * @param string $key Key to add or modify in the node - */ - private function prepareNode(&$node, $key) - { - if (!isset($node[$key])) { - $node[$key] = null; - } elseif (!is_array($node[$key])) { - $node[$key] = [$node[$key]]; - } - } - - /** - * Returns the appropriate key based on the node and key. - */ - private function cleanKey($node, $key) - { - if ($key === '') { - $key = $node ? (string) count($node) : 0; - // Found a [] key, so track this to ensure that we disable numeric - // indexing of keys in the resolved query aggregator. - $this->numericIndices = false; - } - - return $key; - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php b/core/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php new file mode 100644 index 0000000000000000000000000000000000000000..374931d9617aedd9d722d8344cd1645fec4cdd96 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php @@ -0,0 +1,198 @@ +<?php +namespace GuzzleHttp; + +use GuzzleHttp\Exception\BadResponseException; +use GuzzleHttp\Exception\TooManyRedirectsException; +use GuzzleHttp\Promise\PromiseInterface; +use GuzzleHttp\Psr7; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\UriInterface; + +/** + * Request redirect middleware. + * + * Apply this middleware like other middleware using + * {@see GuzzleHttp\Middleware::redirect()}. + */ +class RedirectMiddleware +{ + public static $defaultSettings = [ + 'max' => 5, + 'protocols' => ['http', 'https'], + 'strict' => false, + 'referer' => false + ]; + + /** @var callable */ + private $nextHandler; + + /** + * @param callable $nextHandler Next handler to invoke. + */ + public function __construct(callable $nextHandler) + { + $this->nextHandler = $nextHandler; + } + + /** + * @param RequestInterface $request + * @param array $options + * + * @return PromiseInterface + */ + public function __invoke(RequestInterface $request, array $options) + { + $fn = $this->nextHandler; + + if (empty($options['allow_redirects'])) { + return $fn($request, $options); + } + + if ($options['allow_redirects'] === true) { + $options['allow_redirects'] = self::$defaultSettings; + } elseif (!is_array($options['allow_redirects'])) { + throw new \InvalidArgumentException('allow_redirects must be true, false, or array'); + } else { + // Merge the default settings with the provided settings + $options['allow_redirects'] += self::$defaultSettings; + } + + if (empty($options['allow_redirects']['max'])) { + return $fn($request, $options); + } + + return $fn($request, $options) + ->then(function (ResponseInterface $response) use ($request, $options) { + return $this->checkRedirect($request, $options, $response); + }); + } + + /** + * @param RequestInterface $request + * @param array $options + * @param ResponseInterface|PromiseInterface $response + * + * @return ResponseInterface|PromiseInterface + */ + public function checkRedirect( + RequestInterface $request, + array $options, + ResponseInterface $response + ) { + if (substr($response->getStatusCode(), 0, 1) != '3' + || !$response->hasHeader('Location') + ) { + return $response; + } + + $this->guardMax($request, $options); + $nextRequest = $this->modifyRequest($request, $options, $response); + + if (isset($options['allow_redirects']['on_redirect'])) { + call_user_func( + $options['allow_redirects']['on_redirect'], + $request, + $response, + $nextRequest->getUri() + ); + } + + return $this($nextRequest, $options); + } + + private function guardMax(RequestInterface $request, array &$options) + { + $current = isset($options['__redirect_count']) + ? $options['__redirect_count'] + : 0; + $options['__redirect_count'] = $current + 1; + $max = $options['allow_redirects']['max']; + + if ($options['__redirect_count'] > $max) { + throw new TooManyRedirectsException( + "Will not follow more than {$max} redirects", + $request + ); + } + } + + /** + * @param RequestInterface $request + * @param array $options + * @param ResponseInterface $response + * + * @return RequestInterface + */ + public function modifyRequest( + RequestInterface $request, + array $options, + ResponseInterface $response + ) { + // Request modifications to apply. + $modify = []; + $protocols = $options['allow_redirects']['protocols']; + + // Use a GET request if this is an entity enclosing request and we are + // not forcing RFC compliance, but rather emulating what all browsers + // would do. + $statusCode = $response->getStatusCode(); + if ($statusCode == 303 || + ($statusCode <= 302 && $request->getBody() && !$options['allow_redirects']['strict']) + ) { + $modify['method'] = 'GET'; + $modify['body'] = ''; + } + + $modify['uri'] = $this->redirectUri($request, $response, $protocols); + Psr7\rewind_body($request); + + // Add the Referer header if it is told to do so and only + // add the header if we are not redirecting from https to http. + if ($options['allow_redirects']['referer'] + && $modify['uri']->getScheme() === $request->getUri()->getScheme() + ) { + $uri = $request->getUri()->withUserInfo('', ''); + $modify['set_headers']['Referer'] = (string) $uri; + } else { + $modify['remove_headers'][] = 'Referer'; + } + + return Psr7\modify_request($request, $modify); + } + + /** + * Set the appropriate URL on the request based on the location header + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @param array $protocols + * + * @return UriInterface + */ + private function redirectUri( + RequestInterface $request, + ResponseInterface $response, + array $protocols + ) { + $location = Psr7\Uri::resolve( + $request->getUri(), + $response->getHeaderLine('Location') + ); + + // Ensure that the redirect URI is allowed based on the protocols. + if (!in_array($location->getScheme(), $protocols)) { + throw new BadResponseException( + sprintf( + 'Redirect URI, %s, does not use one of the allowed redirect protocols: %s', + $location, + implode(', ', $protocols) + ), + $request, + $response + ); + } + + return $location; + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/RequestFsm.php b/core/vendor/guzzlehttp/guzzle/src/RequestFsm.php deleted file mode 100644 index b37c190d43118ad7823b70eb51d6f5fc10dde80d..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/RequestFsm.php +++ /dev/null @@ -1,153 +0,0 @@ -<?php -namespace GuzzleHttp; - -use GuzzleHttp\Event\BeforeEvent; -use GuzzleHttp\Event\ErrorEvent; -use GuzzleHttp\Event\CompleteEvent; -use GuzzleHttp\Event\EndEvent; -use GuzzleHttp\Exception\StateException; -use GuzzleHttp\Exception\RequestException; -use GuzzleHttp\Message\FutureResponse; -use GuzzleHttp\Message\MessageFactoryInterface; -use GuzzleHttp\Ring\Future\FutureInterface; - -/** - * Responsible for transitioning requests through lifecycle events. - */ -class RequestFsm -{ - private $handler; - private $mf; - private $maxTransitions; - - public function __construct( - callable $handler, - MessageFactoryInterface $messageFactory, - $maxTransitions = 200 - ) { - $this->mf = $messageFactory; - $this->maxTransitions = $maxTransitions; - $this->handler = $handler; - } - - /** - * Runs the state machine until a terminal state is entered or the - * optionally supplied $finalState is entered. - * - * @param Transaction $trans Transaction being transitioned. - * - * @throws \Exception if a terminal state throws an exception. - */ - public function __invoke(Transaction $trans) - { - $trans->_transitionCount = 0; - - if (!$trans->state) { - $trans->state = 'before'; - } - - transition: - - if (++$trans->_transitionCount > $this->maxTransitions) { - throw new StateException("Too many state transitions were " - . "encountered ({$trans->_transitionCount}). This likely " - . "means that a combination of event listeners are in an " - . "infinite loop."); - } - - switch ($trans->state) { - case 'before': goto before; - case 'complete': goto complete; - case 'error': goto error; - case 'retry': goto retry; - case 'send': goto send; - case 'end': goto end; - default: throw new StateException("Invalid state: {$trans->state}"); - } - - before: { - try { - $trans->request->getEmitter()->emit('before', new BeforeEvent($trans)); - $trans->state = 'send'; - if ((bool) $trans->response) { - $trans->state = 'complete'; - } - } catch (\Exception $e) { - $trans->state = 'error'; - $trans->exception = $e; - } - goto transition; - } - - complete: { - try { - if ($trans->response instanceof FutureInterface) { - // Futures will have their own end events emitted when - // dereferenced. - return; - } - $trans->state = 'end'; - $trans->response->setEffectiveUrl($trans->request->getUrl()); - $trans->request->getEmitter()->emit('complete', new CompleteEvent($trans)); - } catch (\Exception $e) { - $trans->state = 'error'; - $trans->exception = $e; - } - goto transition; - } - - error: { - try { - // Convert non-request exception to a wrapped exception - $trans->exception = RequestException::wrapException( - $trans->request, $trans->exception - ); - $trans->state = 'end'; - $trans->request->getEmitter()->emit('error', new ErrorEvent($trans)); - // An intercepted request (not retried) transitions to complete - if (!$trans->exception && $trans->state !== 'retry') { - $trans->state = 'complete'; - } - } catch (\Exception $e) { - $trans->state = 'end'; - $trans->exception = $e; - } - goto transition; - } - - retry: { - $trans->retries++; - $trans->response = null; - $trans->exception = null; - $trans->state = 'before'; - goto transition; - } - - send: { - $fn = $this->handler; - $trans->response = FutureResponse::proxy( - $fn(RingBridge::prepareRingRequest($trans)), - function ($value) use ($trans) { - RingBridge::completeRingResponse($trans, $value, $this->mf, $this); - $this($trans); - return $trans->response; - } - ); - return; - } - - end: { - $trans->request->getEmitter()->emit('end', new EndEvent($trans)); - // Throw exceptions in the terminal event if the exception - // was not handled by an "end" event listener. - if ($trans->exception) { - if (!($trans->exception instanceof RequestException)) { - $trans->exception = RequestException::wrapException( - $trans->request, $trans->exception - ); - } - throw $trans->exception; - } - } - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/RequestOptions.php b/core/vendor/guzzlehttp/guzzle/src/RequestOptions.php new file mode 100644 index 0000000000000000000000000000000000000000..e142b3f2cb90f523d6cfefcac0df3e6f6deb2728 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/RequestOptions.php @@ -0,0 +1,226 @@ +<?php +namespace GuzzleHttp; + +/** + * This class contains a list of built-in Guzzle request options. + * + * More documentation for each option can be found at http://guzzlephp.org/. + * + * @link http://docs.guzzlephp.org/en/v6/request-options.html + */ +final class RequestOptions +{ + /** + * allow_redirects: (bool|array) Controls redirect behavior. Pass false + * to disable redirects, pass true to enable redirects, pass an + * associative to provide custom redirect settings. Defaults to "false". + * This option only works if your handler has the RedirectMiddleware. When + * passing an associative array, you can provide the following key value + * pairs: + * + * - max: (int, default=5) maximum number of allowed redirects. + * - strict: (bool, default=false) Set to true to use strict redirects + * meaning redirect POST requests with POST requests vs. doing what most + * browsers do which is redirect POST requests with GET requests + * - referer: (bool, default=true) Set to false to disable the Referer + * header. + * - protocols: (array, default=['http', 'https']) Allowed redirect + * protocols. + * - on_redirect: (callable) PHP callable that is invoked when a redirect + * is encountered. The callable is invoked with the request, the redirect + * response that was received, and the effective URI. Any return value + * from the on_redirect function is ignored. + */ + const ALLOW_REDIRECTS = 'allow_redirects'; + + /** + * auth: (array) Pass an array of HTTP authentication parameters to use + * with the request. The array must contain the username in index [0], + * the password in index [1], and you can optionally provide a built-in + * authentication type in index [2]. Pass null to disable authentication + * for a request. + */ + const AUTH = 'auth'; + + /** + * body: (string|null|callable|iterator|object) Body to send in the + * request. + */ + const BODY = 'body'; + + /** + * cert: (string|array) Set to a string to specify the path to a file + * containing a PEM formatted SSL client side certificate. If a password + * is required, then set cert to an array containing the path to the PEM + * file in the first array element followed by the certificate password + * in the second array element. + */ + const CERT = 'cert'; + + /** + * cookies: (bool|GuzzleHttp\Cookie\CookieJarInterface, default=false) + * Specifies whether or not cookies are used in a request or what cookie + * jar to use or what cookies to send. This option only works if your + * handler has the `cookie` middleware. Valid values are `false` and + * an instance of {@see GuzzleHttp\Cookie\CookieJarInterface}. + */ + const COOKIES = 'cookies'; + + /** + * connect_timeout: (float, default=0) Float describing the number of + * seconds to wait while trying to connect to a server. Use 0 to wait + * indefinitely (the default behavior). + */ + const CONNECT_TIMEOUT = 'connect_timeout'; + + /** + * debug: (bool|resource) Set to true or set to a PHP stream returned by + * fopen() enable debug output with the HTTP handler used to send a + * request. + */ + const DEBUG = 'debug'; + + /** + * decode_content: (bool, default=true) Specify whether or not + * Content-Encoding responses (gzip, deflate, etc.) are automatically + * decoded. + */ + const DECODE_CONTENT = 'decode_content'; + + /** + * delay: (int) The amount of time to delay before sending in milliseconds. + */ + const DELAY = 'delay'; + + /** + * expect: (bool|integer) Controls the behavior of the + * "Expect: 100-Continue" header. + * + * Set to `true` to enable the "Expect: 100-Continue" header for all + * requests that sends a body. Set to `false` to disable the + * "Expect: 100-Continue" header for all requests. Set to a number so that + * the size of the payload must be greater than the number in order to send + * the Expect header. Setting to a number will send the Expect header for + * all requests in which the size of the payload cannot be determined or + * where the body is not rewindable. + * + * By default, Guzzle will add the "Expect: 100-Continue" header when the + * size of the body of a request is greater than 1 MB and a request is + * using HTTP/1.1. + */ + const EXPECT = 'expect'; + + /** + * form_params: (array) Associative array of form field names to values + * where each value is a string or array of strings. Sets the Content-Type + * header to application/x-www-form-urlencoded when no Content-Type header + * is already present. + */ + const FORM_PARAMS = 'form_params'; + + /** + * headers: (array) Associative array of HTTP headers. Each value MUST be + * a string or array of strings. + */ + const HEADERS = 'headers'; + + /** + * http_errors: (bool, default=true) Set to false to disable exceptions + * when a non- successful HTTP response is received. By default, + * exceptions will be thrown for 4xx and 5xx responses. This option only + * works if your handler has the `httpErrors` middleware. + */ + const HTTP_ERRORS = 'http_errors'; + + /** + * json: (mixed) Adds JSON data to a request. The provided value is JSON + * encoded and a Content-Type header of application/json will be added to + * the request if no Content-Type header is already present. + */ + const JSON = 'json'; + + /** + * multipart: (array) Array of associative arrays, each containing a + * required "name" key mapping to the form field, name, a required + * "contents" key mapping to a StreamInterface|resource|string, an + * optional "headers" associative array of custom headers, and an + * optional "filename" key mapping to a string to send as the filename in + * the part. If no "filename" key is present, then no "filename" attribute + * will be added to the part. + */ + const MULTIPART = 'multipart'; + + /** + * progress: (callable) Defines a function to invoke when transfer + * progress is made. The function accepts the following positional + * arguments: the total number of bytes expected to be downloaded, the + * number of bytes downloaded so far, the number of bytes expected to be + * uploaded, the number of bytes uploaded so far. + */ + const PROGRESS = 'progress'; + + /** + * proxy: (string|array) Pass a string to specify an HTTP proxy, or an + * array to specify different proxies for different protocols (where the + * key is the protocol and the value is a proxy string). + */ + const PROXY = 'proxy'; + + /** + * query: (array|string) Associative array of query string values to add + * to the request. This option uses PHP's http_build_query() to create + * the string representation. Pass a string value if you need more + * control than what this method provides + */ + const QUERY = 'query'; + + /** + * sink: (resource|string|StreamInterface) Where the data of the + * response is written to. Defaults to a PHP temp stream. Providing a + * string will write data to a file by the given name. + */ + const SINK = 'sink'; + + /** + * synchronous: (bool) Set to true to inform HTTP handlers that you intend + * on waiting on the response. This can be useful for optimizations. Note + * that a promise is still returned if you are using one of the async + * client methods. + */ + const SYNCHRONOUS = 'synchronous'; + + /** + * ssl_key: (array|string) Specify the path to a file containing a private + * SSL key in PEM format. If a password is required, then set to an array + * containing the path to the SSL key in the first array element followed + * by the password required for the certificate in the second element. + */ + const SSL_KEY = 'ssl_key'; + + /** + * stream: Set to true to attempt to stream a response rather than + * download it all up-front. + */ + const STREAM = 'stream'; + + /** + * verify: (bool|string, default=true) Describes the SSL certificate + * verification behavior of a request. Set to true to enable SSL + * certificate verification using the system CA bundle when available + * (the default). Set to false to disable certificate verification (this + * is insecure!). Set to a string to provide the path to a CA bundle on + * disk to enable verification using a custom certificate. + */ + const VERIFY = 'verify'; + + /** + * timeout: (float, default=0) Float describing the timeout of the + * request in seconds. Use 0 to wait indefinitely (the default behavior). + */ + const TIMEOUT = 'timeout'; + + /** + * version: (float) Specifies the HTTP protocol version to attempt to use. + */ + const VERSION = 'version'; +} diff --git a/core/vendor/guzzlehttp/guzzle/src/RetryMiddleware.php b/core/vendor/guzzlehttp/guzzle/src/RetryMiddleware.php new file mode 100644 index 0000000000000000000000000000000000000000..4b95a147449f4780e099a55a7c843a4eb280119a --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/RetryMiddleware.php @@ -0,0 +1,111 @@ +<?php +namespace GuzzleHttp; + +use GuzzleHttp\Promise\PromiseInterface; +use GuzzleHttp\Promise\RejectedPromise; +use GuzzleHttp\Psr7; +use Psr\Http\Message\RequestInterface; + +/** + * Middleware that retries requests based on the boolean result of + * invoking the provided "decider" function. + */ +class RetryMiddleware +{ + /** @var callable */ + private $nextHandler; + + /** @var callable */ + private $decider; + + /** + * @param callable $decider Function that accepts the number of retries, + * a request, [response], and [exception] and + * returns true if the request is to be + * retried. + * @param callable $nextHandler Next handler to invoke. + * @param callable $delay Function that accepts the number of retries + * and returns the number of milliseconds to + * delay. + */ + public function __construct( + callable $decider, + callable $nextHandler, + callable $delay = null + ) { + $this->decider = $decider; + $this->nextHandler = $nextHandler; + $this->delay = $delay ?: __CLASS__ . '::exponentialDelay'; + } + + /** + * Default exponential backoff delay function. + * + * @param $retries + * + * @return int + */ + public static function exponentialDelay($retries) + { + return (int) pow(2, $retries - 1); + } + + /** + * @param RequestInterface $request + * @param array $options + * + * @return PromiseInterface + */ + public function __invoke(RequestInterface $request, array $options) + { + if (!isset($options['retries'])) { + $options['retries'] = 0; + } + + $fn = $this->nextHandler; + return $fn($request, $options) + ->then( + $this->onFulfilled($request, $options), + $this->onRejected($request, $options) + ); + } + + private function onFulfilled(RequestInterface $req, array $options) + { + return function ($value) use ($req, $options) { + if (!call_user_func( + $this->decider, + $options['retries'], + $req, + $value, + null + )) { + return $value; + } + return $this->doRetry($req, $options); + }; + } + + private function onRejected(RequestInterface $req, array $options) + { + return function ($reason) use ($req, $options) { + if (!call_user_func( + $this->decider, + $options['retries'], + $req, + null, + $reason + )) { + return new RejectedPromise($reason); + } + return $this->doRetry($req, $options); + }; + } + + private function doRetry(RequestInterface $request, array $options) + { + $options['delay'] = call_user_func($this->delay, ++$options['retries']); + + return $this($request, $options); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/RingBridge.php b/core/vendor/guzzlehttp/guzzle/src/RingBridge.php deleted file mode 100644 index bc6841d42bc68a12a77b7a2abdc6a0f150b18915..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/RingBridge.php +++ /dev/null @@ -1,165 +0,0 @@ -<?php -namespace GuzzleHttp; - -use GuzzleHttp\Message\MessageFactoryInterface; -use GuzzleHttp\Message\RequestInterface; -use GuzzleHttp\Event\ProgressEvent; -use GuzzleHttp\Message\Request; -use GuzzleHttp\Ring\Core; -use GuzzleHttp\Stream\Stream; -use GuzzleHttp\Exception\RequestException; - -/** - * Provides the bridge between Guzzle requests and responses and Guzzle Ring. - */ -class RingBridge -{ - /** - * Creates a Ring request from a request object. - * - * This function does not hook up the "then" and "progress" events that - * would be required for actually sending a Guzzle request through a - * RingPHP handler. - * - * @param RequestInterface $request Request to convert. - * - * @return array Converted Guzzle Ring request. - */ - public static function createRingRequest(RequestInterface $request) - { - $options = $request->getConfig()->toArray(); - $url = $request->getUrl(); - // No need to calculate the query string twice (in URL and query). - $qs = ($pos = strpos($url, '?')) ? substr($url, $pos + 1) : null; - - return [ - 'scheme' => $request->getScheme(), - 'http_method' => $request->getMethod(), - 'url' => $url, - 'uri' => $request->getPath(), - 'headers' => $request->getHeaders(), - 'body' => $request->getBody(), - 'version' => $request->getProtocolVersion(), - 'client' => $options, - 'query_string' => $qs, - 'future' => isset($options['future']) ? $options['future'] : false - ]; - } - - /** - * Creates a Ring request from a request object AND prepares the callbacks. - * - * @param Transaction $trans Transaction to update. - * - * @return array Converted Guzzle Ring request. - */ - public static function prepareRingRequest(Transaction $trans) - { - // Clear out the transaction state when initiating. - $trans->exception = null; - $request = self::createRingRequest($trans->request); - - // Emit progress events if any progress listeners are registered. - if ($trans->request->getEmitter()->hasListeners('progress')) { - $emitter = $trans->request->getEmitter(); - $request['client']['progress'] = function ($a, $b, $c, $d) use ($trans, $emitter) { - $emitter->emit('progress', new ProgressEvent($trans, $a, $b, $c, $d)); - }; - } - - return $request; - } - - /** - * Handles the process of processing a response received from a ring - * handler. The created response is added to the transaction, and the - * transaction stat is set appropriately. - * - * @param Transaction $trans Owns request and response. - * @param array $response Ring response array - * @param MessageFactoryInterface $messageFactory Creates response objects. - */ - public static function completeRingResponse( - Transaction $trans, - array $response, - MessageFactoryInterface $messageFactory - ) { - $trans->state = 'complete'; - $trans->transferInfo = isset($response['transfer_stats']) - ? $response['transfer_stats'] : []; - - if (!empty($response['status'])) { - $options = []; - if (isset($response['version'])) { - $options['protocol_version'] = $response['version']; - } - if (isset($response['reason'])) { - $options['reason_phrase'] = $response['reason']; - } - $trans->response = $messageFactory->createResponse( - $response['status'], - isset($response['headers']) ? $response['headers'] : [], - isset($response['body']) ? $response['body'] : null, - $options - ); - if (isset($response['effective_url'])) { - $trans->response->setEffectiveUrl($response['effective_url']); - } - } elseif (empty($response['error'])) { - // When nothing was returned, then we need to add an error. - $response['error'] = self::getNoRingResponseException($trans->request); - } - - if (isset($response['error'])) { - $trans->state = 'error'; - $trans->exception = $response['error']; - } - } - - /** - * Creates a Guzzle request object using a ring request array. - * - * @param array $request Ring request - * - * @return Request - * @throws \InvalidArgumentException for incomplete requests. - */ - public static function fromRingRequest(array $request) - { - $options = []; - if (isset($request['version'])) { - $options['protocol_version'] = $request['version']; - } - - if (!isset($request['http_method'])) { - throw new \InvalidArgumentException('No http_method'); - } - - return new Request( - $request['http_method'], - Core::url($request), - isset($request['headers']) ? $request['headers'] : [], - isset($request['body']) ? Stream::factory($request['body']) : null, - $options - ); - } - - /** - * Get an exception that can be used when a RingPHP handler does not - * populate a response. - * - * @param RequestInterface $request - * - * @return RequestException - */ - public static function getNoRingResponseException(RequestInterface $request) - { - $message = <<<EOT -Sending the request did not return a response, exception, or populate the -transaction with a response. This is most likely due to an incorrectly -implemented RingPHP handler. If you are simply trying to mock responses, -then it is recommended to use the GuzzleHttp\Ring\Client\MockHandler. -EOT; - return new RequestException($message, $request); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Subscriber/Cookie.php b/core/vendor/guzzlehttp/guzzle/src/Subscriber/Cookie.php deleted file mode 100644 index cacabb66d7bf30024c647f3741825b64ee5baac1..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Subscriber/Cookie.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php -namespace GuzzleHttp\Subscriber; - -use GuzzleHttp\Cookie\CookieJar; -use GuzzleHttp\Cookie\CookieJarInterface; -use GuzzleHttp\Event\BeforeEvent; -use GuzzleHttp\Event\CompleteEvent; -use GuzzleHttp\Event\RequestEvents; -use GuzzleHttp\Event\SubscriberInterface; - -/** - * Adds, extracts, and persists cookies between HTTP requests - */ -class Cookie implements SubscriberInterface -{ - /** @var CookieJarInterface */ - private $cookieJar; - - /** - * @param CookieJarInterface $cookieJar Cookie jar used to hold cookies - */ - public function __construct(CookieJarInterface $cookieJar = null) - { - $this->cookieJar = $cookieJar ?: new CookieJar(); - } - - public function getEvents() - { - // Fire the cookie plugin complete event before redirecting - return [ - 'before' => ['onBefore'], - 'complete' => ['onComplete', RequestEvents::REDIRECT_RESPONSE + 10] - ]; - } - - /** - * Get the cookie cookieJar - * - * @return CookieJarInterface - */ - public function getCookieJar() - { - return $this->cookieJar; - } - - public function onBefore(BeforeEvent $event) - { - $this->cookieJar->addCookieHeader($event->getRequest()); - } - - public function onComplete(CompleteEvent $event) - { - $this->cookieJar->extractCookies( - $event->getRequest(), - $event->getResponse() - ); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Subscriber/History.php b/core/vendor/guzzlehttp/guzzle/src/Subscriber/History.php deleted file mode 100644 index 5cf06119f641080c578b2939a8050990136561f3..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Subscriber/History.php +++ /dev/null @@ -1,172 +0,0 @@ -<?php -namespace GuzzleHttp\Subscriber; - -use GuzzleHttp\Event\CompleteEvent; -use GuzzleHttp\Event\ErrorEvent; -use GuzzleHttp\Event\RequestEvents; -use GuzzleHttp\Event\SubscriberInterface; -use GuzzleHttp\Message\RequestInterface; -use GuzzleHttp\Message\ResponseInterface; - -/** - * Maintains a list of requests and responses sent using a request or client - */ -class History implements SubscriberInterface, \IteratorAggregate, \Countable -{ - /** @var int The maximum number of requests to maintain in the history */ - private $limit; - - /** @var array Requests and responses that have passed through the plugin */ - private $transactions = []; - - public function __construct($limit = 10) - { - $this->limit = $limit; - } - - public function getEvents() - { - return [ - 'complete' => ['onComplete', RequestEvents::EARLY], - 'error' => ['onError', RequestEvents::EARLY], - ]; - } - - /** - * Convert to a string that contains all request and response headers - * - * @return string - */ - public function __toString() - { - $lines = array(); - foreach ($this->transactions as $entry) { - $response = isset($entry['response']) ? $entry['response'] : ''; - $lines[] = '> ' . trim($entry['sent_request']) - . "\n\n< " . trim($response) . "\n"; - } - - return implode("\n", $lines); - } - - public function onComplete(CompleteEvent $event) - { - $this->add($event->getRequest(), $event->getResponse()); - } - - public function onError(ErrorEvent $event) - { - // Only track when no response is present, meaning this didn't ever - // emit a complete event - if (!$event->getResponse()) { - $this->add($event->getRequest()); - } - } - - /** - * Returns an Iterator that yields associative array values where each - * associative array contains the following key value pairs: - * - * - request: Representing the actual request that was received. - * - sent_request: A clone of the request that will not be mutated. - * - response: The response that was received (if available). - * - * @return \Iterator - */ - public function getIterator() - { - return new \ArrayIterator($this->transactions); - } - - /** - * Get all of the requests sent through the plugin. - * - * Requests can be modified after they are logged by the history - * subscriber. By default this method will return the actual request - * instances that were received. Pass true to this method if you wish to - * get copies of the requests that represent the request state when it was - * initially logged by the history subscriber. - * - * @param bool $asSent Set to true to get clones of the requests that have - * not been mutated since the request was received by - * the history subscriber. - * - * @return RequestInterface[] - */ - public function getRequests($asSent = false) - { - return array_map(function ($t) use ($asSent) { - return $asSent ? $t['sent_request'] : $t['request']; - }, $this->transactions); - } - - /** - * Get the number of requests in the history - * - * @return int - */ - public function count() - { - return count($this->transactions); - } - - /** - * Get the last request sent. - * - * Requests can be modified after they are logged by the history - * subscriber. By default this method will return the actual request - * instance that was received. Pass true to this method if you wish to get - * a copy of the request that represents the request state when it was - * initially logged by the history subscriber. - * - * @param bool $asSent Set to true to get a clone of the last request that - * has not been mutated since the request was received - * by the history subscriber. - * - * @return RequestInterface - */ - public function getLastRequest($asSent = false) - { - return $asSent - ? end($this->transactions)['sent_request'] - : end($this->transactions)['request']; - } - - /** - * Get the last response in the history - * - * @return ResponseInterface|null - */ - public function getLastResponse() - { - return end($this->transactions)['response']; - } - - /** - * Clears the history - */ - public function clear() - { - $this->transactions = array(); - } - - /** - * Add a request to the history - * - * @param RequestInterface $request Request to add - * @param ResponseInterface $response Response of the request - */ - private function add( - RequestInterface $request, - ResponseInterface $response = null - ) { - $this->transactions[] = [ - 'request' => $request, - 'sent_request' => clone $request, - 'response' => $response - ]; - if (count($this->transactions) > $this->limit) { - array_shift($this->transactions); - } - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Subscriber/HttpError.php b/core/vendor/guzzlehttp/guzzle/src/Subscriber/HttpError.php deleted file mode 100644 index ed9de5bcc0619eb691b11e1a8f9632ef08300c7f..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Subscriber/HttpError.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php -namespace GuzzleHttp\Subscriber; - -use GuzzleHttp\Event\CompleteEvent; -use GuzzleHttp\Event\RequestEvents; -use GuzzleHttp\Event\SubscriberInterface; -use GuzzleHttp\Exception\RequestException; - -/** - * Throws exceptions when a 4xx or 5xx response is received - */ -class HttpError implements SubscriberInterface -{ - public function getEvents() - { - return ['complete' => ['onComplete', RequestEvents::VERIFY_RESPONSE]]; - } - - /** - * Throw a RequestException on an HTTP protocol error - * - * @param CompleteEvent $event Emitted event - * @throws RequestException - */ - public function onComplete(CompleteEvent $event) - { - $code = (string) $event->getResponse()->getStatusCode(); - // Throw an exception for an unsuccessful response - if ($code[0] >= 4) { - throw RequestException::create( - $event->getRequest(), - $event->getResponse() - ); - } - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Subscriber/Mock.php b/core/vendor/guzzlehttp/guzzle/src/Subscriber/Mock.php deleted file mode 100644 index 39a3c442d7087f500da61f3d80e737443dbafbcc..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Subscriber/Mock.php +++ /dev/null @@ -1,132 +0,0 @@ -<?php -namespace GuzzleHttp\Subscriber; - -use GuzzleHttp\Event\RequestEvents; -use GuzzleHttp\Event\SubscriberInterface; -use GuzzleHttp\Event\BeforeEvent; -use GuzzleHttp\Exception\RequestException; -use GuzzleHttp\Message\MessageFactory; -use GuzzleHttp\Message\ResponseInterface; - -/** - * Queues mock responses or exceptions and delivers mock responses or - * exceptions in a fifo order. - */ -class Mock implements SubscriberInterface, \Countable -{ - /** @var array Array of mock responses / exceptions */ - private $queue = []; - - /** @var bool Whether or not to consume an entity body when mocking */ - private $readBodies; - - /** @var MessageFactory */ - private $factory; - - /** - * @param array $items Array of responses or exceptions to queue - * @param bool $readBodies Set to false to not consume the entity body of - * a request when a mock is served. - */ - public function __construct(array $items = [], $readBodies = true) - { - $this->factory = new MessageFactory(); - $this->readBodies = $readBodies; - $this->addMultiple($items); - } - - public function getEvents() - { - // Fire the event last, after signing - return ['before' => ['onBefore', RequestEvents::SIGN_REQUEST - 10]]; - } - - /** - * @throws \OutOfBoundsException|\Exception - */ - public function onBefore(BeforeEvent $event) - { - if (!$item = array_shift($this->queue)) { - throw new \OutOfBoundsException('Mock queue is empty'); - } elseif ($item instanceof RequestException) { - throw $item; - } - - // Emulate reading a response body - $request = $event->getRequest(); - if ($this->readBodies && $request->getBody()) { - while (!$request->getBody()->eof()) { - $request->getBody()->read(8096); - } - } - - $event->intercept($item); - } - - public function count() - { - return count($this->queue); - } - - /** - * Add a response to the end of the queue - * - * @param string|ResponseInterface $response Response or path to response file - * - * @return self - * @throws \InvalidArgumentException if a string or Response is not passed - */ - public function addResponse($response) - { - if (is_string($response)) { - $response = file_exists($response) - ? $this->factory->fromMessage(file_get_contents($response)) - : $this->factory->fromMessage($response); - } elseif (!($response instanceof ResponseInterface)) { - throw new \InvalidArgumentException('Response must a message ' - . 'string, response object, or path to a file'); - } - - $this->queue[] = $response; - - return $this; - } - - /** - * Add an exception to the end of the queue - * - * @param RequestException $e Exception to throw when the request is executed - * - * @return self - */ - public function addException(RequestException $e) - { - $this->queue[] = $e; - - return $this; - } - - /** - * Add multiple items to the queue - * - * @param array $items Items to add - */ - public function addMultiple(array $items) - { - foreach ($items as $item) { - if ($item instanceof RequestException) { - $this->addException($item); - } else { - $this->addResponse($item); - } - } - } - - /** - * Clear the queue - */ - public function clearQueue() - { - $this->queue = []; - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Subscriber/Prepare.php b/core/vendor/guzzlehttp/guzzle/src/Subscriber/Prepare.php deleted file mode 100644 index b5ed4e26092def223a216f0c00a02aa0d97cd17e..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Subscriber/Prepare.php +++ /dev/null @@ -1,130 +0,0 @@ -<?php -namespace GuzzleHttp\Subscriber; - -use GuzzleHttp\Event\BeforeEvent; -use GuzzleHttp\Event\RequestEvents; -use GuzzleHttp\Event\SubscriberInterface; -use GuzzleHttp\Message\AppliesHeadersInterface; -use GuzzleHttp\Message\RequestInterface; -use GuzzleHttp\Mimetypes; -use GuzzleHttp\Stream\StreamInterface; - -/** - * Prepares requests with a body before sending - * - * **Request Options** - * - * - expect: Set to true to enable the "Expect: 100-Continue" header for a - * request that send a body. Set to false to disable "Expect: 100-Continue". - * Set to a number so that the size of the payload must be greater than the - * number in order to send the Expect header. Setting to a number will send - * the Expect header for all requests in which the size of the payload cannot - * be determined or where the body is not rewindable. - */ -class Prepare implements SubscriberInterface -{ - public function getEvents() - { - return ['before' => ['onBefore', RequestEvents::PREPARE_REQUEST]]; - } - - public function onBefore(BeforeEvent $event) - { - $request = $event->getRequest(); - - // Set the appropriate Content-Type for a request if one is not set and - // there are form fields - if (!($body = $request->getBody())) { - return; - } - - $this->addContentLength($request, $body); - - if ($body instanceof AppliesHeadersInterface) { - // Synchronize the body with the request headers - $body->applyRequestHeaders($request); - } elseif (!$request->hasHeader('Content-Type')) { - $this->addContentType($request, $body); - } - - $this->addExpectHeader($request, $body); - } - - private function addContentType( - RequestInterface $request, - StreamInterface $body - ) { - if (!($uri = $body->getMetadata('uri'))) { - return; - } - - // Guess the content-type based on the stream's "uri" metadata value. - // The file extension is used to determine the appropriate mime-type. - if ($contentType = Mimetypes::getInstance()->fromFilename($uri)) { - $request->setHeader('Content-Type', $contentType); - } - } - - private function addContentLength( - RequestInterface $request, - StreamInterface $body - ) { - // Set the Content-Length header if it can be determined, and never - // send a Transfer-Encoding: chunked and Content-Length header in - // the same request. - if ($request->hasHeader('Content-Length')) { - // Remove transfer-encoding if content-length is set. - $request->removeHeader('Transfer-Encoding'); - return; - } - - if ($request->hasHeader('Transfer-Encoding')) { - return; - } - - if (null !== ($size = $body->getSize())) { - $request->setHeader('Content-Length', $size); - $request->removeHeader('Transfer-Encoding'); - } elseif ('1.1' == $request->getProtocolVersion()) { - // Use chunked Transfer-Encoding if there is no determinable - // content-length header and we're using HTTP/1.1. - $request->setHeader('Transfer-Encoding', 'chunked'); - $request->removeHeader('Content-Length'); - } - } - - private function addExpectHeader( - RequestInterface $request, - StreamInterface $body - ) { - // Determine if the Expect header should be used - if ($request->hasHeader('Expect')) { - return; - } - - $expect = $request->getConfig()['expect']; - - // Return if disabled or if you're not using HTTP/1.1 - if ($expect === false || $request->getProtocolVersion() !== '1.1') { - return; - } - - // The expect header is unconditionally enabled - if ($expect === true) { - $request->setHeader('Expect', '100-Continue'); - return; - } - - // By default, send the expect header when the payload is > 1mb - if ($expect === null) { - $expect = 1048576; - } - - // Always add if the body cannot be rewound, the size cannot be - // determined, or the size is greater than the cutoff threshold - $size = $body->getSize(); - if ($size === null || $size >= (int) $expect || !$body->isSeekable()) { - $request->setHeader('Expect', '100-Continue'); - } - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Subscriber/Redirect.php b/core/vendor/guzzlehttp/guzzle/src/Subscriber/Redirect.php deleted file mode 100644 index ff992268bb0cee21856afa1a88e4e08d6173ba46..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Subscriber/Redirect.php +++ /dev/null @@ -1,176 +0,0 @@ -<?php -namespace GuzzleHttp\Subscriber; - -use GuzzleHttp\Event\CompleteEvent; -use GuzzleHttp\Event\RequestEvents; -use GuzzleHttp\Event\SubscriberInterface; -use GuzzleHttp\Exception\BadResponseException; -use GuzzleHttp\Exception\CouldNotRewindStreamException; -use GuzzleHttp\Exception\TooManyRedirectsException; -use GuzzleHttp\Message\RequestInterface; -use GuzzleHttp\Message\ResponseInterface; -use GuzzleHttp\Url; - -/** - * Subscriber used to implement HTTP redirects. - * - * **Request options** - * - * - redirect: Associative array containing the 'max', 'strict', and 'referer' - * keys. - * - * - max: Maximum number of redirects allowed per-request - * - strict: You can use strict redirects by setting this value to ``true``. - * Strict redirects adhere to strict RFC compliant redirection (e.g., - * redirect POST with POST) vs doing what most clients do (e.g., redirect - * POST request with a GET request). - * - referer: Set to true to automatically add the "Referer" header when a - * redirect request is sent. - * - protocols: Array of allowed protocols. Defaults to 'http' and 'https'. - * When a redirect attempts to utilize a protocol that is not white listed, - * an exception is thrown. - */ -class Redirect implements SubscriberInterface -{ - public function getEvents() - { - return ['complete' => ['onComplete', RequestEvents::REDIRECT_RESPONSE]]; - } - - /** - * Rewind the entity body of the request if needed - * - * @param RequestInterface $redirectRequest - * @throws CouldNotRewindStreamException - */ - public static function rewindEntityBody(RequestInterface $redirectRequest) - { - // Rewind the entity body of the request if needed - if ($body = $redirectRequest->getBody()) { - // Only rewind the body if some of it has been read already, and - // throw an exception if the rewind fails - if ($body->tell() && !$body->seek(0)) { - throw new CouldNotRewindStreamException( - 'Unable to rewind the non-seekable request body after redirecting', - $redirectRequest - ); - } - } - } - - /** - * Called when a request receives a redirect response - * - * @param CompleteEvent $event Event emitted - * @throws TooManyRedirectsException - */ - public function onComplete(CompleteEvent $event) - { - $response = $event->getResponse(); - - if (substr($response->getStatusCode(), 0, 1) != '3' - || !$response->hasHeader('Location') - ) { - return; - } - - $request = $event->getRequest(); - $config = $request->getConfig(); - - // Increment the redirect and initialize the redirect state. - if ($redirectCount = $config['redirect_count']) { - $config['redirect_count'] = ++$redirectCount; - } else { - $config['redirect_scheme'] = $request->getScheme(); - $config['redirect_count'] = $redirectCount = 1; - } - - $max = $config->getPath('redirect/max') ?: 5; - - if ($redirectCount > $max) { - throw new TooManyRedirectsException( - "Will not follow more than {$redirectCount} redirects", - $request - ); - } - - $this->modifyRedirectRequest($request, $response); - $event->retry(); - } - - private function modifyRedirectRequest( - RequestInterface $request, - ResponseInterface $response - ) { - $config = $request->getConfig(); - $protocols = $config->getPath('redirect/protocols') ?: ['http', 'https']; - - // Use a GET request if this is an entity enclosing request and we are - // not forcing RFC compliance, but rather emulating what all browsers - // would do. - $statusCode = $response->getStatusCode(); - if ($statusCode == 303 || - ($statusCode <= 302 && $request->getBody() && !$config->getPath('redirect/strict')) - ) { - $request->setMethod('GET'); - $request->setBody(null); - } - - $previousUrl = $request->getUrl(); - $this->setRedirectUrl($request, $response, $protocols); - $this->rewindEntityBody($request); - - // Add the Referer header if it is told to do so and only - // add the header if we are not redirecting from https to http. - if ($config->getPath('redirect/referer') - && ($request->getScheme() == 'https' || $request->getScheme() == $config['redirect_scheme']) - ) { - $url = Url::fromString($previousUrl); - $url->setUsername(null); - $url->setPassword(null); - $request->setHeader('Referer', (string) $url); - } else { - $request->removeHeader('Referer'); - } - } - - /** - * Set the appropriate URL on the request based on the location header - * - * @param RequestInterface $request - * @param ResponseInterface $response - * @param array $protocols - */ - private function setRedirectUrl( - RequestInterface $request, - ResponseInterface $response, - array $protocols - ) { - $location = $response->getHeader('Location'); - $location = Url::fromString($location); - - // Combine location with the original URL if it is not absolute. - if (!$location->isAbsolute()) { - $originalUrl = Url::fromString($request->getUrl()); - // Remove query string parameters and just take what is present on - // the redirect Location header - $originalUrl->getQuery()->clear(); - $location = $originalUrl->combine($location); - } - - // Ensure that the redirect URL is allowed based on the protocols. - if (!in_array($location->getScheme(), $protocols)) { - throw new BadResponseException( - sprintf( - 'Redirect URL, %s, does not use one of the allowed redirect protocols: %s', - $location, - implode(', ', $protocols) - ), - $request, - $response - ); - } - - $request->setUrl($location); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/ToArrayInterface.php b/core/vendor/guzzlehttp/guzzle/src/ToArrayInterface.php deleted file mode 100644 index d57c0229a44a3352c738c57a3ad8b27d90f3bd9f..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/ToArrayInterface.php +++ /dev/null @@ -1,15 +0,0 @@ -<?php -namespace GuzzleHttp; - -/** - * An object that can be represented as an array - */ -interface ToArrayInterface -{ - /** - * Get the array representation of an object - * - * @return array - */ - public function toArray(); -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Transaction.php b/core/vendor/guzzlehttp/guzzle/src/Transaction.php deleted file mode 100644 index 30a3f9469161be2567ee9aedc9a4a78aa9df9fb8..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Transaction.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php -namespace GuzzleHttp; - -use GuzzleHttp\Message\RequestInterface; -use GuzzleHttp\Message\ResponseInterface; - -/** - * Represents the relationship between a client, request, and response. - * - * You can access the request, response, and client using their corresponding - * public properties. - */ -class Transaction -{ - /** - * HTTP client used to transfer the request. - * - * @var ClientInterface - */ - public $client; - - /** - * The request that is being sent. - * - * @var RequestInterface - */ - public $request; - - /** - * The response associated with the transaction. A response will not be - * present when a networking error occurs or an error occurs before sending - * the request. - * - * @var ResponseInterface|null - */ - public $response; - - /** - * Exception associated with the transaction. If this exception is present - * when processing synchronous or future commands, then it is thrown. When - * intercepting a failed transaction, you MUST set this value to null in - * order to prevent the exception from being thrown. - * - * @var \Exception - */ - public $exception; - - /** - * Associative array of handler specific transfer statistics and custom - * key value pair information. When providing similar information, handlers - * should follow the same key value pair naming conventions as PHP's - * curl_getinfo() (http://php.net/manual/en/function.curl-getinfo.php). - * - * @var array - */ - public $transferInfo = []; - - /** - * The number of transaction retries. - * - * @var int - */ - public $retries = 0; - - /** - * The transaction's current state. - * - * @var string - */ - public $state; - - /** - * Whether or not this is a future transaction. This value should not be - * changed after the future is constructed. - * - * @var bool - */ - public $future; - - /** - * The number of state transitions that this transaction has been through. - * - * @var int - * @internal This is for internal use only. If you modify this, then you - * are asking for trouble. - */ - public $_transitionCount = 0; - - /** - * @param ClientInterface $client Client that is used to send the requests - * @param RequestInterface $request Request to send - * @param bool $future Whether or not this is a future request. - */ - public function __construct( - ClientInterface $client, - RequestInterface $request, - $future = false - ) { - $this->client = $client; - $this->request = $request; - $this->_future = $future; - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Url.php b/core/vendor/guzzlehttp/guzzle/src/Url.php deleted file mode 100644 index a81bad2f0271e78a67fc8703482c0634013b35d1..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Url.php +++ /dev/null @@ -1,595 +0,0 @@ -<?php -namespace GuzzleHttp; - -use GuzzleHttp\Ring\Core; - -/** - * Parses and generates URLs based on URL parts - */ -class Url -{ - private $scheme; - private $host; - private $port; - private $username; - private $password; - private $path = ''; - private $fragment; - private static $defaultPorts = ['http' => 80, 'https' => 443, 'ftp' => 21]; - private static $pathPattern = '/[^a-zA-Z0-9\-\._~!\$&\'\(\)\*\+,;=%:@\/]+|%(?![A-Fa-f0-9]{2})/'; - private static $queryPattern = '/[^a-zA-Z0-9\-\._~!\$\'\(\)\*\+,;%:@\/\?=&]+|%(?![A-Fa-f0-9]{2})/'; - /** @var Query|string Query part of the URL */ - private $query; - - /** - * Factory method to create a new URL from a URL string - * - * @param string $url Full URL used to create a Url object - * - * @return Url - * @throws \InvalidArgumentException - */ - public static function fromString($url) - { - static $defaults = ['scheme' => null, 'host' => null, - 'path' => null, 'port' => null, 'query' => null, - 'user' => null, 'pass' => null, 'fragment' => null]; - - if (false === ($parts = parse_url($url))) { - throw new \InvalidArgumentException('Unable to parse malformed ' - . 'url: ' . $url); - } - - $parts += $defaults; - - // Convert the query string into a Query object - if ($parts['query'] || 0 !== strlen($parts['query'])) { - $parts['query'] = Query::fromString($parts['query']); - } - - return new static($parts['scheme'], $parts['host'], $parts['user'], - $parts['pass'], $parts['port'], $parts['path'], $parts['query'], - $parts['fragment']); - } - - /** - * Build a URL from parse_url parts. The generated URL will be a relative - * URL if a scheme or host are not provided. - * - * @param array $parts Array of parse_url parts - * - * @return string - */ - public static function buildUrl(array $parts) - { - $url = $scheme = ''; - - if (!empty($parts['scheme'])) { - $scheme = $parts['scheme']; - $url .= $scheme . ':'; - } - - if (!empty($parts['host'])) { - $url .= '//'; - if (isset($parts['user'])) { - $url .= $parts['user']; - if (isset($parts['pass'])) { - $url .= ':' . $parts['pass']; - } - $url .= '@'; - } - - $url .= $parts['host']; - - // Only include the port if it is not the default port of the scheme - if (isset($parts['port']) && - (!isset(self::$defaultPorts[$scheme]) || - $parts['port'] != self::$defaultPorts[$scheme]) - ) { - $url .= ':' . $parts['port']; - } - } - - // Add the path component if present - if (isset($parts['path']) && strlen($parts['path'])) { - // Always ensure that the path begins with '/' if set and something - // is before the path - if (!empty($parts['host']) && $parts['path'][0] != '/') { - $url .= '/'; - } - $url .= $parts['path']; - } - - // Add the query string if present - if (isset($parts['query'])) { - $queryStr = (string) $parts['query']; - if ($queryStr || $queryStr === '0') { - $url .= '?' . $queryStr; - } - } - - // Ensure that # is only added to the url if fragment contains anything. - if (isset($parts['fragment'])) { - $url .= '#' . $parts['fragment']; - } - - return $url; - } - - /** - * Create a new URL from URL parts - * - * @param string $scheme Scheme of the URL - * @param string $host Host of the URL - * @param string $username Username of the URL - * @param string $password Password of the URL - * @param int $port Port of the URL - * @param string $path Path of the URL - * @param Query|array|string $query Query string of the URL - * @param string $fragment Fragment of the URL - */ - public function __construct( - $scheme, - $host, - $username = null, - $password = null, - $port = null, - $path = null, - $query = null, - $fragment = null - ) { - $this->scheme = $scheme; - $this->host = $host; - $this->port = $port; - $this->username = $username; - $this->password = $password; - $this->fragment = $fragment; - - if ($query) { - $this->setQuery($query); - } - - $this->setPath($path); - } - - /** - * Clone the URL - */ - public function __clone() - { - if ($this->query instanceof Query) { - $this->query = clone $this->query; - } - } - - /** - * Returns the URL as a URL string - * - * @return string - */ - public function __toString() - { - return static::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->path, - 'query' => $this->query, - 'fragment' => $this->fragment, - ); - } - - /** - * Set the host of the request. - * - * @param string $host Host to set (e.g. www.yahoo.com, yahoo.com) - * - * @return Url - */ - public function setHost($host) - { - if (strpos($host, ':') === false) { - $this->host = $host; - } else { - list($host, $port) = explode(':', $host); - $this->host = $host; - $this->setPort($port); - } - } - - /** - * 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 - */ - public function setScheme($scheme) - { - // Remove the default port if one is specified - if ($this->port - && isset(self::$defaultPorts[$this->scheme]) - && self::$defaultPorts[$this->scheme] == $this->port - ) { - $this->port = null; - } - - $this->scheme = $scheme; - } - - /** - * 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 - */ - public function setPort($port) - { - $this->port = $port; - } - - /** - * Get the port part of the URl. - * - * If no port was set, this method will return the default port for the - * scheme of the URI. - * - * @return int|null - */ - public function getPort() - { - if ($this->port) { - return $this->port; - } elseif (isset(self::$defaultPorts[$this->scheme])) { - return self::$defaultPorts[$this->scheme]; - } - - return null; - } - - /** - * Set the path part of the URL. - * - * The provided URL is URL encoded as necessary. - * - * @param string $path Path string to set - */ - public function setPath($path) - { - $this->path = self::encodePath($path); - } - - /** - * Removes dot segments from a URL - * @link http://tools.ietf.org/html/rfc3986#section-5.2.4 - */ - public function removeDotSegments() - { - static $noopPaths = ['' => true, '/' => true, '*' => true]; - static $ignoreSegments = ['.' => true, '..' => true]; - - if (isset($noopPaths[$this->path])) { - return; - } - - $results = []; - $segments = $this->getPathSegments(); - foreach ($segments as $segment) { - if ($segment == '..') { - array_pop($results); - } elseif (!isset($ignoreSegments[$segment])) { - $results[] = $segment; - } - } - - $newPath = implode('/', $results); - - // Add the leading slash if necessary - if (substr($this->path, 0, 1) === '/' && - substr($newPath, 0, 1) !== '/' - ) { - $newPath = '/' . $newPath; - } - - // Add the trailing slash if necessary - if ($newPath != '/' && isset($ignoreSegments[end($segments)])) { - $newPath .= '/'; - } - - $this->path = $newPath; - } - - /** - * Add a relative path to the currently set path. - * - * @param string $relativePath Relative path to add - */ - public function addPath($relativePath) - { - if ($relativePath != '/' && - is_string($relativePath) && - strlen($relativePath) > 0 - ) { - // Add a leading slash if needed - if ($relativePath[0] !== '/' && - substr($this->path, -1, 1) !== '/' - ) { - $relativePath = '/' . $relativePath; - } - - $this->setPath($this->path . $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 explode('/', $this->path); - } - - /** - * Set the password part of the URL - * - * @param string $password Password to set - */ - public function setPassword($password) - { - $this->password = $password; - } - - /** - * 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 - */ - public function setUsername($username) - { - $this->username = $username; - } - - /** - * 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 Query object - * - * @return Query - */ - public function getQuery() - { - // Convert the query string to a query object if not already done. - if (!$this->query instanceof Query) { - $this->query = $this->query === null - ? new Query() - : Query::fromString($this->query); - } - - return $this->query; - } - - /** - * Set the query part of the URL. - * - * You may provide a query string as a string and pass $rawString as true - * to provide a query string that is not parsed until a call to getQuery() - * is made. Setting a raw query string will still encode invalid characters - * in a query string. - * - * @param Query|string|array $query Query string value to set. Can - * be a string that will be parsed into a Query object, an array - * of key value pairs, or a Query object. - * @param bool $rawString Set to true when providing a raw query string. - * - * @throws \InvalidArgumentException - */ - public function setQuery($query, $rawString = false) - { - if ($query instanceof Query) { - $this->query = $query; - } elseif (is_string($query)) { - if (!$rawString) { - $this->query = Query::fromString($query); - } else { - // Ensure the query does not have illegal characters. - $this->query = preg_replace_callback( - self::$queryPattern, - [__CLASS__, 'encodeMatch'], - $query - ); - } - - } elseif (is_array($query)) { - $this->query = new Query($query); - } else { - throw new \InvalidArgumentException('Query must be a Query, ' - . 'array, or string. Got ' . Core::describeType($query)); - } - } - - /** - * 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 - */ - public function setFragment($fragment) - { - $this->fragment = $fragment; - } - - /** - * Check if this is an absolute URL - * - * @return bool - */ - public function isAbsolute() - { - return $this->scheme && $this->host; - } - - /** - * Combine the URL with another URL and return a new URL instance. - * - * Follows the rules specific in RFC 3986 section 5.4. - * - * @param string $url Relative URL to combine with - * - * @return Url - * @throws \InvalidArgumentException - * @link http://tools.ietf.org/html/rfc3986#section-5.4 - */ - public function combine($url) - { - $url = static::fromString($url); - - // Use the more absolute URL as the base URL - if (!$this->isAbsolute() && $url->isAbsolute()) { - $url = $url->combine($this); - } - - $parts = $url->getParts(); - - // Passing a URL with a scheme overrides everything - if ($parts['scheme']) { - return clone $url; - } - - // Setting a host overrides the entire rest of the URL - if ($parts['host']) { - return new static( - $this->scheme, - $parts['host'], - $parts['user'], - $parts['pass'], - $parts['port'], - $parts['path'], - $parts['query'] instanceof Query - ? clone $parts['query'] - : $parts['query'], - $parts['fragment'] - ); - } - - if (!$parts['path'] && $parts['path'] !== '0') { - // The relative URL has no path, so check if it is just a query - $path = $this->path ?: ''; - $query = $parts['query'] ?: $this->query; - } else { - $query = $parts['query']; - if ($parts['path'][0] == '/' || !$this->path) { - // Overwrite the existing path if the rel path starts with "/" - $path = $parts['path']; - } else { - // If the relative URL does not have a path or the base URL - // path does not end in a "/" then overwrite the existing path - // up to the last "/" - $path = substr($this->path, 0, strrpos($this->path, '/') + 1) . $parts['path']; - } - } - - $result = new self( - $this->scheme, - $this->host, - $this->username, - $this->password, - $this->port, - $path, - $query instanceof Query ? clone $query : $query, - $parts['fragment'] - ); - - if ($path) { - $result->removeDotSegments(); - } - - return $result; - } - - /** - * Encodes the path part of a URL without double-encoding percent-encoded - * key value pairs. - * - * @param string $path Path to encode - * - * @return string - */ - public static function encodePath($path) - { - static $cb = [__CLASS__, 'encodeMatch']; - return preg_replace_callback(self::$pathPattern, $cb, $path); - } - - private static function encodeMatch(array $match) - { - return rawurlencode($match[0]); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/Utils.php b/core/vendor/guzzlehttp/guzzle/src/Utils.php deleted file mode 100644 index 285fe30ba2bf59306a08df2cfdc538ea5ec9646e..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/src/Utils.php +++ /dev/null @@ -1,145 +0,0 @@ -<?php -namespace GuzzleHttp; - -/** - * Utility methods used throughout Guzzle. - */ -final class Utils -{ - /** - * Gets a value from an array using a path syntax to retrieve nested data. - * - * This method does not allow for keys that contain "/". You must traverse - * the array manually or using something more advanced like JMESPath to - * work with keys that contain "/". - * - * // Get the bar key of a set of nested arrays. - * // This is equivalent to $collection['foo']['baz']['bar'] but won't - * // throw warnings for missing keys. - * GuzzleHttp\get_path($data, 'foo/baz/bar'); - * - * @param array $data Data to retrieve values from - * @param string $path Path to traverse and retrieve a value from - * - * @return mixed|null - */ - public static function getPath($data, $path) - { - $path = explode('/', $path); - - while (null !== ($part = array_shift($path))) { - if (!is_array($data) || !isset($data[$part])) { - return null; - } - $data = $data[$part]; - } - - return $data; - } - - /** - * Set a value in a nested array key. Keys will be created as needed to set - * the value. - * - * This function does not support keys that contain "/" or "[]" characters - * because these are special tokens used when traversing the data structure. - * A value may be prepended to an existing array by using "[]" as the final - * key of a path. - * - * GuzzleHttp\get_path($data, 'foo/baz'); // null - * GuzzleHttp\set_path($data, 'foo/baz/[]', 'a'); - * GuzzleHttp\set_path($data, 'foo/baz/[]', 'b'); - * GuzzleHttp\get_path($data, 'foo/baz'); - * // Returns ['a', 'b'] - * - * @param array $data Data to modify by reference - * @param string $path Path to set - * @param mixed $value Value to set at the key - * - * @throws \RuntimeException when trying to setPath using a nested path - * that travels through a scalar value. - */ - public static function setPath(&$data, $path, $value) - { - $current =& $data; - $queue = explode('/', $path); - while (null !== ($key = array_shift($queue))) { - if (!is_array($current)) { - throw new \RuntimeException("Trying to setPath {$path}, but " - . "{$key} is set and is not an array"); - } elseif (!$queue) { - if ($key == '[]') { - $current[] = $value; - } else { - $current[$key] = $value; - } - } elseif (isset($current[$key])) { - $current =& $current[$key]; - } else { - $current[$key] = []; - $current =& $current[$key]; - } - } - } - - /** - * Expands a URI template - * - * @param string $template URI template - * @param array $variables Template variables - * - * @return string - */ - public static function uriTemplate($template, array $variables) - { - if (function_exists('\\uri_template')) { - return \uri_template($template, $variables); - } - - static $uriTemplate; - if (!$uriTemplate) { - $uriTemplate = new UriTemplate(); - } - - return $uriTemplate->expand($template, $variables); - } - - /** - * Wrapper for JSON decode that implements error detection with helpful - * error messages. - * - * @param string $json JSON data to parse - * @param bool $assoc When true, returned objects will be converted - * into associative arrays. - * @param int $depth User specified recursion depth. - * @param int $options Bitmask of JSON decode options. - * - * @return mixed - * @throws \InvalidArgumentException if the JSON cannot be parsed. - * @link http://www.php.net/manual/en/function.json-decode.php - */ - public static function jsonDecode($json, $assoc = false, $depth = 512, $options = 0) - { - static $jsonErrors = [ - JSON_ERROR_DEPTH => 'JSON_ERROR_DEPTH - Maximum stack depth exceeded', - JSON_ERROR_STATE_MISMATCH => 'JSON_ERROR_STATE_MISMATCH - Underflow or the modes mismatch', - JSON_ERROR_CTRL_CHAR => 'JSON_ERROR_CTRL_CHAR - Unexpected control character found', - JSON_ERROR_SYNTAX => 'JSON_ERROR_SYNTAX - Syntax error, malformed JSON', - JSON_ERROR_UTF8 => 'JSON_ERROR_UTF8 - Malformed UTF-8 characters, possibly incorrectly encoded' - ]; - - $data = \json_decode($json, $assoc, $depth, $options); - - if (JSON_ERROR_NONE !== json_last_error()) { - $last = json_last_error(); - throw new \InvalidArgumentException( - 'Unable to parse JSON data: ' - . (isset($jsonErrors[$last]) - ? $jsonErrors[$last] - : 'Unknown error') - ); - } - - return $data; - } -} diff --git a/core/vendor/guzzlehttp/guzzle/src/functions.php b/core/vendor/guzzlehttp/guzzle/src/functions.php new file mode 100644 index 0000000000000000000000000000000000000000..f5ff1fcb26f7d95e1b92f8f416d2dfed90d3d4f5 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/functions.php @@ -0,0 +1,225 @@ +<?php +namespace GuzzleHttp; + +use GuzzleHttp\Handler\CurlHandler; +use GuzzleHttp\Handler\CurlMultiHandler; +use GuzzleHttp\Handler\Proxy; +use GuzzleHttp\Handler\StreamHandler; +use Psr\Http\Message\StreamInterface; + +/** + * Expands a URI template + * + * @param string $template URI template + * @param array $variables Template variables + * + * @return string + */ +function uri_template($template, array $variables) +{ + if (extension_loaded('uri_template')) { + // @codeCoverageIgnoreStart + return \uri_template($template, $variables); + // @codeCoverageIgnoreEnd + } + + static $uriTemplate; + if (!$uriTemplate) { + $uriTemplate = new UriTemplate(); + } + + return $uriTemplate->expand($template, $variables); +} + +/** + * Debug function used to describe the provided value type and class. + * + * @param mixed $input + * + * @return string Returns a string containing the type of the variable and + * if a class is provided, the class name. + */ +function describe_type($input) +{ + switch (gettype($input)) { + case 'object': + return 'object(' . get_class($input) . ')'; + case 'array': + return 'array(' . count($input) . ')'; + default: + ob_start(); + var_dump($input); + // normalize float vs double + return str_replace('double(', 'float(', rtrim(ob_get_clean())); + } +} + +/** + * Parses an array of header lines into an associative array of headers. + * + * @param array $lines Header lines array of strings in the following + * format: "Name: Value" + * @return array + */ +function headers_from_lines($lines) +{ + $headers = []; + + foreach ($lines as $line) { + $parts = explode(':', $line, 2); + $headers[trim($parts[0])][] = isset($parts[1]) + ? trim($parts[1]) + : null; + } + + return $headers; +} + +/** + * Returns a debug stream based on the provided variable. + * + * @param mixed $value Optional value + * + * @return resource + */ +function debug_resource($value = null) +{ + if (is_resource($value)) { + return $value; + } elseif (defined('STDOUT')) { + return STDOUT; + } + + return fopen('php://output', 'w'); +} + +/** + * Chooses and creates a default handler to use based on the environment. + * + * The returned handler is not wrapped by any default middlewares. + * + * @throws \RuntimeException if no viable Handler is available. + * @return callable Returns the best handler for the given system. + */ +function choose_handler() +{ + $handler = null; + if (extension_loaded('curl')) { + $handler = Proxy::wrapSync(new CurlMultiHandler(), new CurlHandler()); + } + + if (ini_get('allow_url_fopen')) { + $handler = $handler + ? Proxy::wrapStreaming($handler, new StreamHandler()) + : new StreamHandler(); + } elseif (!$handler) { + throw new \RuntimeException('GuzzleHttp requires cURL, the ' + . 'allow_url_fopen ini setting, or a custom HTTP handler.'); + } + + return $handler; +} + +/** + * Get the default User-Agent string to use with Guzzle + * + * @return string + */ +function default_user_agent() +{ + static $defaultAgent = ''; + + if (!$defaultAgent) { + $defaultAgent = 'GuzzleHttp/' . Client::VERSION; + if (extension_loaded('curl')) { + $defaultAgent .= ' curl/' . \curl_version()['version']; + } + $defaultAgent .= ' PHP/' . PHP_VERSION; + } + + return $defaultAgent; +} + +/** + * Returns the default cacert bundle for the current system. + * + * First, the openssl.cafile and curl.cainfo php.ini settings are checked. + * If those settings are not configured, then the common locations for + * bundles found on Red Hat, CentOS, Fedora, Ubuntu, Debian, FreeBSD, OS X + * and Windows are checked. If any of these file locations are found on + * disk, they will be utilized. + * + * Note: the result of this function is cached for subsequent calls. + * + * @return string + * @throws \RuntimeException if no bundle can be found. + */ +function default_ca_bundle() +{ + static $cached = null; + static $cafiles = [ + // Red Hat, CentOS, Fedora (provided by the ca-certificates package) + '/etc/pki/tls/certs/ca-bundle.crt', + // Ubuntu, Debian (provided by the ca-certificates package) + '/etc/ssl/certs/ca-certificates.crt', + // FreeBSD (provided by the ca_root_nss package) + '/usr/local/share/certs/ca-root-nss.crt', + // OS X provided by homebrew (using the default path) + '/usr/local/etc/openssl/cert.pem', + // Windows? + 'C:\\windows\\system32\\curl-ca-bundle.crt', + 'C:\\windows\\curl-ca-bundle.crt', + ]; + + if ($cached) { + return $cached; + } + + if ($ca = ini_get('openssl.cafile')) { + return $cached = $ca; + } + + if ($ca = ini_get('curl.cainfo')) { + return $cached = $ca; + } + + foreach ($cafiles as $filename) { + if (file_exists($filename)) { + return $cached = $filename; + } + } + + throw new \RuntimeException(<<< EOT +No system CA bundle could be found in any of the the common system locations. +PHP versions earlier than 5.6 are not properly configured to use the system's +CA bundle by default. In order to verify peer certificates, you will need to +supply the path on disk to a certificate bundle to the 'verify' request +option: http://docs.guzzlephp.org/en/latest/clients.html#verify. If you do not +need a specific certificate bundle, then Mozilla provides a commonly used CA +bundle which can be downloaded here (provided by the maintainer of cURL): +https://raw.githubusercontent.com/bagder/ca-bundle/master/ca-bundle.crt. Once +you have a CA bundle available on disk, you can set the 'openssl.cafile' PHP +ini setting to point to the path to the file, allowing you to omit the 'verify' +request option. See http://curl.haxx.se/docs/sslcerts.html for more +information. +EOT + ); +} + +/** + * Creates an associative array of lowercase header names to the actual + * header casing. + * + * @param array $headers + * + * @return array + */ +function normalize_header_keys(array $headers) +{ + $result = []; + foreach (array_keys($headers) as $key) { + $result[strtolower($key)] = $key; + } + + return $result; +} diff --git a/core/vendor/guzzlehttp/guzzle/src/functions_include.php b/core/vendor/guzzlehttp/guzzle/src/functions_include.php new file mode 100644 index 0000000000000000000000000000000000000000..a93393acc4e7c5e6021f070ad65dbbf2e3924241 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/functions_include.php @@ -0,0 +1,6 @@ +<?php + +// Don't redefine the functions if included multiple times. +if (!function_exists('GuzzleHttp\uri_template')) { + require __DIR__ . '/functions.php'; +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/BatchResultsTest.php b/core/vendor/guzzlehttp/guzzle/tests/BatchResultsTest.php deleted file mode 100644 index 080d44c01722e6eddfc986e318e1a846b9efe848..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/BatchResultsTest.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php -namespace GuzzleHttp\Tests; - -use GuzzleHttp\BatchResults; - -/** - * @covers \GuzzleHttp\BatchResults - */ -class BatchResultsTest extends \PHPUnit_Framework_TestCase -{ - public function testExposesResults() - { - $a = new \stdClass(); - $b = new \stdClass(); - $c = new \stdClass(); - $hash = new \SplObjectStorage(); - $hash[$a] = '1'; - $hash[$b] = '2'; - $hash[$c] = new \Exception('foo'); - - $batch = new BatchResults($hash); - $this->assertCount(3, $batch); - $this->assertEquals([$a, $b, $c], $batch->getKeys()); - $this->assertEquals([$hash[$c]], $batch->getFailures()); - $this->assertEquals(['1', '2'], $batch->getSuccessful()); - $this->assertEquals('1', $batch->getResult($a)); - $this->assertNull($batch->getResult(new \stdClass())); - $this->assertTrue(isset($batch[0])); - $this->assertFalse(isset($batch[10])); - $this->assertEquals('1', $batch[0]); - $this->assertEquals('2', $batch[1]); - $this->assertNull($batch[100]); - $this->assertInstanceOf('Exception', $batch[2]); - - $results = iterator_to_array($batch); - $this->assertEquals(['1', '2', $hash[$c]], $results); - } - - /** - * @expectedException \RuntimeException - */ - public function testCannotSetByIndex() - { - $hash = new \SplObjectStorage(); - $batch = new BatchResults($hash); - $batch[10] = 'foo'; - } - - /** - * @expectedException \RuntimeException - */ - public function testCannotUnsetByIndex() - { - $hash = new \SplObjectStorage(); - $batch = new BatchResults($hash); - unset($batch[10]); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/ClientTest.php b/core/vendor/guzzlehttp/guzzle/tests/ClientTest.php deleted file mode 100644 index 913e535b85d64d1fd987ec4df5984886ca94e556..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/ClientTest.php +++ /dev/null @@ -1,624 +0,0 @@ -<?php -namespace GuzzleHttp\Tests; - -use GuzzleHttp\Client; -use GuzzleHttp\Event\BeforeEvent; -use GuzzleHttp\Event\ErrorEvent; -use GuzzleHttp\Message\MessageFactory; -use GuzzleHttp\Message\Response; -use GuzzleHttp\Exception\RequestException; -use GuzzleHttp\Query; -use GuzzleHttp\Ring\Client\MockHandler; -use GuzzleHttp\Ring\Future\FutureArray; -use GuzzleHttp\Subscriber\History; -use GuzzleHttp\Subscriber\Mock; -use GuzzleHttp\Url; -use React\Promise\Deferred; - -/** - * @covers GuzzleHttp\Client - */ -class ClientTest extends \PHPUnit_Framework_TestCase -{ - /** @callable */ - private $ma; - - public function setup() - { - $this->ma = function () { - throw new \RuntimeException('Should not have been called.'); - }; - } - - public function testProvidesDefaultUserAgent() - { - $ua = Client::getDefaultUserAgent(); - $this->assertEquals(1, preg_match('#^Guzzle/.+ curl/.+ PHP/.+$#', $ua)); - } - - public function testUsesDefaultDefaultOptions() - { - $client = new Client(); - $this->assertTrue($client->getDefaultOption('allow_redirects')); - $this->assertTrue($client->getDefaultOption('exceptions')); - $this->assertTrue($client->getDefaultOption('verify')); - } - - public function testUsesProvidedDefaultOptions() - { - $client = new Client([ - 'defaults' => [ - 'allow_redirects' => false, - 'query' => ['foo' => 'bar'] - ] - ]); - $this->assertFalse($client->getDefaultOption('allow_redirects')); - $this->assertTrue($client->getDefaultOption('exceptions')); - $this->assertTrue($client->getDefaultOption('verify')); - $this->assertEquals(['foo' => 'bar'], $client->getDefaultOption('query')); - } - - public function testCanSpecifyBaseUrl() - { - $this->assertSame('', (new Client())->getBaseUrl()); - $this->assertEquals('http://foo', (new Client([ - 'base_url' => 'http://foo' - ]))->getBaseUrl()); - } - - public function testCanSpecifyBaseUrlUriTemplate() - { - $client = new Client(['base_url' => ['http://foo.com/{var}/', ['var' => 'baz']]]); - $this->assertEquals('http://foo.com/baz/', $client->getBaseUrl()); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testValidatesUriTemplateValue() - { - new Client(['base_url' => ['http://foo.com/']]); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage Foo - */ - public function testCanSpecifyHandler() - { - $client = new Client(['handler' => function () { - throw new \Exception('Foo'); - }]); - $client->get('http://httpbin.org'); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage Foo - */ - public function testCanSpecifyHandlerAsAdapter() - { - $client = new Client(['adapter' => function () { - throw new \Exception('Foo'); - }]); - $client->get('http://httpbin.org'); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage Foo - */ - public function testCanSpecifyMessageFactory() - { - $factory = $this->getMockBuilder('GuzzleHttp\Message\MessageFactoryInterface') - ->setMethods(['createRequest']) - ->getMockForAbstractClass(); - $factory->expects($this->once()) - ->method('createRequest') - ->will($this->throwException(new \Exception('Foo'))); - $client = new Client(['message_factory' => $factory]); - $client->get(); - } - - public function testCanSpecifyEmitter() - { - $emitter = $this->getMockBuilder('GuzzleHttp\Event\EmitterInterface') - ->setMethods(['listeners']) - ->getMockForAbstractClass(); - $emitter->expects($this->once()) - ->method('listeners') - ->will($this->returnValue('foo')); - - $client = new Client(['emitter' => $emitter]); - $this->assertEquals('foo', $client->getEmitter()->listeners()); - } - - public function testAddsDefaultUserAgentHeaderWithDefaultOptions() - { - $client = new Client(['defaults' => ['allow_redirects' => false]]); - $this->assertFalse($client->getDefaultOption('allow_redirects')); - $this->assertEquals( - ['User-Agent' => Client::getDefaultUserAgent()], - $client->getDefaultOption('headers') - ); - } - - public function testAddsDefaultUserAgentHeaderWithoutDefaultOptions() - { - $client = new Client(); - $this->assertEquals( - ['User-Agent' => Client::getDefaultUserAgent()], - $client->getDefaultOption('headers') - ); - } - - private function getRequestClient() - { - $client = $this->getMockBuilder('GuzzleHttp\Client') - ->setMethods(['send']) - ->getMock(); - $client->expects($this->once()) - ->method('send') - ->will($this->returnArgument(0)); - - return $client; - } - - public function requestMethodProvider() - { - return [ - ['GET', false], - ['HEAD', false], - ['DELETE', false], - ['OPTIONS', false], - ['POST', 'foo'], - ['PUT', 'foo'], - ['PATCH', 'foo'] - ]; - } - - /** - * @dataProvider requestMethodProvider - */ - public function testClientProvidesMethodShortcut($method, $body) - { - $client = $this->getRequestClient(); - if ($body) { - $request = $client->{$method}('http://foo.com', [ - 'headers' => ['X-Baz' => 'Bar'], - 'body' => $body, - 'query' => ['a' => 'b'] - ]); - } else { - $request = $client->{$method}('http://foo.com', [ - 'headers' => ['X-Baz' => 'Bar'], - 'query' => ['a' => 'b'] - ]); - } - $this->assertEquals($method, $request->getMethod()); - $this->assertEquals('Bar', $request->getHeader('X-Baz')); - $this->assertEquals('a=b', $request->getQuery()); - if ($body) { - $this->assertEquals($body, $request->getBody()); - } - } - - public function testClientMergesDefaultOptionsWithRequestOptions() - { - $f = $this->getMockBuilder('GuzzleHttp\Message\MessageFactoryInterface') - ->setMethods(array('createRequest')) - ->getMockForAbstractClass(); - - $o = null; - // Intercept the creation - $f->expects($this->once()) - ->method('createRequest') - ->will($this->returnCallback( - function ($method, $url, array $options = []) use (&$o) { - $o = $options; - return (new MessageFactory())->createRequest($method, $url, $options); - } - )); - - $client = new Client([ - 'message_factory' => $f, - 'defaults' => [ - 'headers' => ['Foo' => 'Bar'], - 'query' => ['baz' => 'bam'], - 'exceptions' => false - ] - ]); - - $request = $client->createRequest('GET', 'http://foo.com?a=b', [ - 'headers' => ['Hi' => 'there', '1' => 'one'], - 'allow_redirects' => false, - 'query' => ['t' => 1] - ]); - - $this->assertFalse($o['allow_redirects']); - $this->assertFalse($o['exceptions']); - $this->assertEquals('Bar', $request->getHeader('Foo')); - $this->assertEquals('there', $request->getHeader('Hi')); - $this->assertEquals('one', $request->getHeader('1')); - $this->assertEquals('a=b&baz=bam&t=1', $request->getQuery()); - } - - public function testClientMergesDefaultHeadersCaseInsensitively() - { - $client = new Client(['defaults' => ['headers' => ['Foo' => 'Bar']]]); - $request = $client->createRequest('GET', 'http://foo.com?a=b', [ - 'headers' => ['foo' => 'custom', 'user-agent' => 'test'] - ]); - $this->assertEquals('test', $request->getHeader('User-Agent')); - $this->assertEquals('custom', $request->getHeader('Foo')); - } - - public function testCanOverrideDefaultOptionWithNull() - { - $client = new Client(['defaults' => ['proxy' => 'invalid!']]); - $request = $client->createRequest('GET', 'http://foo.com?a=b', [ - 'proxy' => null - ]); - $this->assertFalse($request->getConfig()->hasKey('proxy')); - } - - public function testDoesNotOverwriteExistingUA() - { - $client = new Client(['defaults' => [ - 'headers' => ['User-Agent' => 'test'] - ]]); - $this->assertEquals( - ['User-Agent' => 'test'], - $client->getDefaultOption('headers') - ); - } - - public function testUsesBaseUrlWhenNoUrlIsSet() - { - $client = new Client(['base_url' => 'http://www.foo.com/baz?bam=bar']); - $this->assertEquals( - 'http://www.foo.com/baz?bam=bar', - $client->createRequest('GET')->getUrl() - ); - } - - public function testUsesBaseUrlCombinedWithProvidedUrl() - { - $client = new Client(['base_url' => 'http://www.foo.com/baz?bam=bar']); - $this->assertEquals( - 'http://www.foo.com/bar/bam', - $client->createRequest('GET', 'bar/bam')->getUrl() - ); - } - - public function testFalsyPathsAreCombinedWithBaseUrl() - { - $client = new Client(['base_url' => 'http://www.foo.com/baz?bam=bar']); - $this->assertEquals( - 'http://www.foo.com/0', - $client->createRequest('GET', '0')->getUrl() - ); - } - - public function testUsesBaseUrlCombinedWithProvidedUrlViaUriTemplate() - { - $client = new Client(['base_url' => 'http://www.foo.com/baz?bam=bar']); - $this->assertEquals( - 'http://www.foo.com/bar/123', - $client->createRequest('GET', ['bar/{bam}', ['bam' => '123']])->getUrl() - ); - } - - public function testSettingAbsoluteUrlOverridesBaseUrl() - { - $client = new Client(['base_url' => 'http://www.foo.com/baz?bam=bar']); - $this->assertEquals( - 'http://www.foo.com/foo', - $client->createRequest('GET', '/foo')->getUrl() - ); - } - - public function testSettingAbsoluteUriTemplateOverridesBaseUrl() - { - $client = new Client(['base_url' => 'http://www.foo.com/baz?bam=bar']); - $this->assertEquals( - 'http://goo.com/1', - $client->createRequest( - 'GET', - ['http://goo.com/{bar}', ['bar' => '1']] - )->getUrl() - ); - } - - public function testCanSetRelativeUrlStartingWithHttp() - { - $client = new Client(['base_url' => 'http://www.foo.com']); - $this->assertEquals( - 'http://www.foo.com/httpfoo', - $client->createRequest('GET', 'httpfoo')->getUrl() - ); - } - - public function testClientSendsRequests() - { - $mock = new MockHandler(['status' => 200, 'headers' => []]); - $client = new Client(['handler' => $mock]); - $response = $client->get('http://test.com'); - $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals('http://test.com', $response->getEffectiveUrl()); - } - - public function testSendingRequestCanBeIntercepted() - { - $response = new Response(200); - $client = new Client(['handler' => $this->ma]); - $client->getEmitter()->on( - 'before', - function (BeforeEvent $e) use ($response) { - $e->intercept($response); - } - ); - $this->assertSame($response, $client->get('http://test.com')); - $this->assertEquals('http://test.com', $response->getEffectiveUrl()); - } - - /** - * @expectedException \GuzzleHttp\Exception\RequestException - * @expectedExceptionMessage Argument 1 passed to GuzzleHttp\Message\FutureResponse::proxy() must implement interface GuzzleHttp\Ring\Future\FutureInterface - */ - public function testEnsuresResponseIsPresentAfterSending() - { - $handler = function () {}; - $client = new Client(['handler' => $handler]); - $client->get('http://httpbin.org'); - } - - /** - * @expectedException \GuzzleHttp\Exception\RequestException - * @expectedExceptionMessage Waiting did not resolve future - */ - public function testEnsuresResponseIsPresentAfterDereferencing() - { - $deferred = new Deferred(); - $handler = new MockHandler(function () use ($deferred) { - return new FutureArray( - $deferred->promise(), - function () {} - ); - }); - $client = new Client(['handler' => $handler]); - $response = $client->get('http://httpbin.org'); - $response->wait(); - } - - public function testClientHandlesErrorsDuringBeforeSend() - { - $client = new Client(); - $client->getEmitter()->on('before', function ($e) { - throw new \Exception('foo'); - }); - $client->getEmitter()->on('error', function (ErrorEvent $e) { - $e->intercept(new Response(200)); - }); - $this->assertEquals( - 200, - $client->get('http://test.com')->getStatusCode() - ); - } - - /** - * @expectedException \GuzzleHttp\Exception\RequestException - * @expectedExceptionMessage foo - */ - public function testClientHandlesErrorsDuringBeforeSendAndThrowsIfUnhandled() - { - $client = new Client(); - $client->getEmitter()->on('before', function (BeforeEvent $e) { - throw new RequestException('foo', $e->getRequest()); - }); - $client->get('http://httpbin.org'); - } - - /** - * @expectedException \GuzzleHttp\Exception\RequestException - * @expectedExceptionMessage foo - */ - public function testClientWrapsExceptions() - { - $client = new Client(); - $client->getEmitter()->on('before', function (BeforeEvent $e) { - throw new \Exception('foo'); - }); - $client->get('http://httpbin.org'); - } - - public function testCanInjectResponseForFutureError() - { - $calledFuture = false; - $deferred = new Deferred(); - $future = new FutureArray( - $deferred->promise(), - function () use ($deferred, &$calledFuture) { - $calledFuture = true; - $deferred->resolve(['error' => new \Exception('Noo!')]); - } - ); - $mock = new MockHandler($future); - $client = new Client(['handler' => $mock]); - $called = 0; - $response = $client->get('http://localhost:123/foo', [ - 'future' => true, - 'events' => [ - 'error' => function (ErrorEvent $e) use (&$called) { - $called++; - $e->intercept(new Response(200)); - } - ] - ]); - $this->assertEquals(0, $called); - $this->assertInstanceOf('GuzzleHttp\Message\FutureResponse', $response); - $this->assertEquals(200, $response->getStatusCode()); - $this->assertTrue($calledFuture); - $this->assertEquals(1, $called); - } - - public function testCanReturnFutureResults() - { - $called = false; - $deferred = new Deferred(); - $future = new FutureArray( - $deferred->promise(), - function () use ($deferred, &$called) { - $called = true; - $deferred->resolve(['status' => 201, 'headers' => []]); - } - ); - $mock = new MockHandler($future); - $client = new Client(['handler' => $mock]); - $response = $client->get('http://localhost:123/foo', ['future' => true]); - $this->assertFalse($called); - $this->assertInstanceOf('GuzzleHttp\Message\FutureResponse', $response); - $this->assertEquals(201, $response->getStatusCode()); - $this->assertTrue($called); - } - - public function testThrowsExceptionsWhenDereferenced() - { - $calledFuture = false; - $deferred = new Deferred(); - $future = new FutureArray( - $deferred->promise(), - function () use ($deferred, &$calledFuture) { - $calledFuture = true; - $deferred->resolve(['error' => new \Exception('Noop!')]); - } - ); - $client = new Client(['handler' => new MockHandler($future)]); - try { - $res = $client->get('http://localhost:123/foo', ['future' => true]); - $res->wait(); - $this->fail('Did not throw'); - } catch (RequestException $e) { - $this->assertEquals(1, $calledFuture); - } - } - - /** - * @expectedExceptionMessage Noo! - * @expectedException \GuzzleHttp\Exception\RequestException - */ - public function testThrowsExceptionsSynchronously() - { - $client = new Client([ - 'handler' => new MockHandler(['error' => new \Exception('Noo!')]) - ]); - $client->get('http://localhost:123/foo'); - } - - public function testCanSetDefaultValues() - { - $client = new Client(['foo' => 'bar']); - $client->setDefaultOption('headers/foo', 'bar'); - $this->assertNull($client->getDefaultOption('foo')); - $this->assertEquals('bar', $client->getDefaultOption('headers/foo')); - } - - public function testSendsAllInParallel() - { - $client = new Client(); - $client->getEmitter()->attach(new Mock([ - new Response(200), - new Response(201), - new Response(202), - ])); - $history = new History(); - $client->getEmitter()->attach($history); - - $requests = [ - $client->createRequest('GET', 'http://test.com'), - $client->createRequest('POST', 'http://test.com'), - $client->createRequest('PUT', 'http://test.com') - ]; - - $client->sendAll($requests); - $requests = array_map(function($r) { - return $r->getMethod(); - }, $history->getRequests()); - $this->assertContains('GET', $requests); - $this->assertContains('POST', $requests); - $this->assertContains('PUT', $requests); - } - - public function testCanDisableAuthPerRequest() - { - $client = new Client(['defaults' => ['auth' => 'foo']]); - $request = $client->createRequest('GET', 'http://test.com'); - $this->assertEquals('foo', $request->getConfig()['auth']); - $request = $client->createRequest('GET', 'http://test.com', ['auth' => null]); - $this->assertFalse($request->getConfig()->hasKey('auth')); - } - - public function testUsesProxyEnvironmentVariables() - { - $http = getenv('HTTP_PROXY'); - $https = getenv('HTTPS_PROXY'); - - $client = new Client(); - $this->assertNull($client->getDefaultOption('proxy')); - - putenv('HTTP_PROXY=127.0.0.1'); - $client = new Client(); - $this->assertEquals( - ['http' => '127.0.0.1'], - $client->getDefaultOption('proxy') - ); - - putenv('HTTPS_PROXY=127.0.0.2'); - $client = new Client(); - $this->assertEquals( - ['http' => '127.0.0.1', 'https' => '127.0.0.2'], - $client->getDefaultOption('proxy') - ); - - putenv("HTTP_PROXY=$http"); - putenv("HTTPS_PROXY=$https"); - } - - public function testReturnsFutureForErrorWhenRequested() - { - $client = new Client(['handler' => new MockHandler(['status' => 404])]); - $request = $client->createRequest('GET', 'http://localhost:123/foo', [ - 'future' => true - ]); - $res = $client->send($request); - $this->assertInstanceOf('GuzzleHttp\Message\FutureResponse', $res); - try { - $res->wait(); - $this->fail('did not throw'); - } catch (RequestException $e) { - $this->assertContains('404', $e->getMessage()); - } - } - - public function testReturnsFutureForResponseWhenRequested() - { - $client = new Client(['handler' => new MockHandler(['status' => 200])]); - $request = $client->createRequest('GET', 'http://localhost:123/foo', [ - 'future' => true - ]); - $res = $client->send($request); - $this->assertInstanceOf('GuzzleHttp\Message\FutureResponse', $res); - $this->assertEquals(200, $res->getStatusCode()); - } - - public function testCanUseUrlWithCustomQuery() - { - $client = new Client(); - $url = Url::fromString('http://foo.com/bar'); - $query = new Query(['baz' => '123%20']); - $query->setEncodingType(false); - $url->setQuery($query); - $r = $client->createRequest('GET', $url); - $this->assertEquals('http://foo.com/bar?baz=123%20', $r->getUrl()); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/CollectionTest.php b/core/vendor/guzzlehttp/guzzle/tests/CollectionTest.php deleted file mode 100644 index d137947db79d28a5054e3093fbb629d81196e53c..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/CollectionTest.php +++ /dev/null @@ -1,416 +0,0 @@ -<?php - -namespace GuzzleHttp\Tests; - -use GuzzleHttp\Collection; - -class CollectionTest extends \PHPUnit_Framework_TestCase -{ - /** @var Collection */ - protected $coll; - - protected function setUp() - { - $this->coll = new Collection(); - } - - public function testConstructorCanBeCalledWithNoParams() - { - $this->coll = new Collection(); - $p = $this->coll->toArray(); - $this->assertEmpty($p, '-> Collection must be empty when no data is passed'); - } - - public function testConstructorCanBeCalledWithParams() - { - $testData = array( - 'test' => 'value', - 'test_2' => 'value2' - ); - $this->coll = new Collection($testData); - $this->assertEquals($this->coll->toArray(), $testData); - $this->assertEquals($this->coll->toArray(), $this->coll->toArray()); - } - - 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); - } - - public function testCanAddValuesToExistingKeysByUsingArray() - { - $this->coll->add('test', 'value1'); - $this->assertEquals($this->coll->toArray(), array('test' => 'value1')); - $this->coll->add('test', 'value2'); - $this->assertEquals($this->coll->toArray(), array('test' => array('value1', 'value2'))); - $this->coll->add('test', 'value3'); - $this->assertEquals($this->coll->toArray(), array('test' => array('value1', 'value2', 'value3'))); - } - - public function testHandlesMergingInDisparateDataSources() - { - $params = array( - 'test' => 'value1', - 'test2' => 'value2', - 'test3' => array('value3', 'value4') - ); - $this->coll->merge($params); - $this->assertEquals($this->coll->toArray(), $params); - $this->coll->merge(new Collection(['test4' => 'hi'])); - $this->assertEquals( - $this->coll->toArray(), - $params + ['test4' => 'hi'] - ); - } - - 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->toArray(), array( - 'test2' => 'value2' - )); - - // Clear all parameters - $this->coll->clear(); - - $this->assertEquals($this->coll->toArray(), array()); - } - - 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()); - } - - public function testChecksIfHasKey() - { - $this->assertFalse($this->coll->hasKey('test')); - $this->coll->add('test', 'value'); - $this->assertEquals(true, $this->coll->hasKey('test')); - $this->coll->add('test2', 'value2'); - $this->assertEquals(true, $this->coll->hasKey('test')); - $this->assertEquals(true, $this->coll->hasKey('test2')); - $this->assertFalse($this->coll->hasKey('testing')); - $this->assertEquals(false, $this->coll->hasKey('AB-C', 'junk')); - } - - 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')); - } - - 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)); - } - - 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->toArray()); - } - - 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->assertNotSame($filtered, $this->coll); - - $this->assertEquals(array( - 'number' => 'ten', - 'same_number' => 'ten' - ), $filtered->toArray()); - } - - 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->assertNotSame($mapped, $this->coll); - - $this->assertEquals(array( - 'number_1' => 1, - 'number_2' => 4, - 'number_3' => 9 - ), $mapped->toArray()); - } - - 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')); - } - - public function testCanReplaceAllData() - { - $this->coll->replace(array('a' => '123')); - $this->assertEquals(array('a' => '123'), $this->coll->toArray()); - } - - public function testPreparesFromConfig() - { - $c = Collection::fromConfig(array( - 'a' => '123', - 'base_url' => 'http://www.test.com/' - ), array( - 'a' => 'xyz', - 'b' => 'lol' - ), array('a')); - - $this->assertInstanceOf('GuzzleHttp\Collection', $c); - $this->assertEquals(array( - 'a' => '123', - 'b' => 'lol', - 'base_url' => 'http://www.test.com/' - ), $c->toArray()); - - try { - $c = Collection::fromConfig(array(), array(), array('a')); - $this->fail('Exception not throw when missing config'); - } catch (\InvalidArgumentException $e) { - } - } - - function falseyDataProvider() - { - return array( - array(false, false), - array(null, null), - array('', ''), - array(array(), array()), - array(0, 0), - ); - } - - /** - * @dataProvider falseyDataProvider - */ - public function testReturnsCorrectData($a, $b) - { - $c = new Collection(array('value' => $a)); - $this->assertSame($b, $c->get('value')); - } - - public function testRetrievesNestedKeysUsingPath() - { - $data = array( - 'foo' => 'bar', - 'baz' => array( - 'mesa' => array( - 'jar' => 'jar' - ) - ) - ); - $collection = new Collection($data); - $this->assertEquals('bar', $collection->getPath('foo')); - $this->assertEquals('jar', $collection->getPath('baz/mesa/jar')); - $this->assertNull($collection->getPath('wewewf')); - $this->assertNull($collection->getPath('baz/mesa/jar/jar')); - } - - public function testFalseyKeysStillDescend() - { - $collection = new Collection(array( - '0' => array( - 'a' => 'jar' - ), - 1 => 'other' - )); - $this->assertEquals('jar', $collection->getPath('0/a')); - $this->assertEquals('other', $collection->getPath('1')); - } - - public function getPathProvider() - { - $data = array( - 'foo' => 'bar', - 'baz' => array( - 'mesa' => array( - 'jar' => 'jar', - 'array' => array('a', 'b', 'c') - ), - 'bar' => array( - 'baz' => 'bam', - 'array' => array('d', 'e', 'f') - ) - ), - 'bam' => array( - array('foo' => 1), - array('foo' => 2), - array('array' => array('h', 'i')) - ) - ); - $c = new Collection($data); - - return array( - // Simple path selectors - array($c, 'foo', 'bar'), - array($c, 'baz', $data['baz']), - array($c, 'bam', $data['bam']), - array($c, 'baz/mesa', $data['baz']['mesa']), - array($c, 'baz/mesa/jar', 'jar'), - // Does not barf on missing keys - array($c, 'fefwfw', null), - array($c, 'baz/mesa/array', $data['baz']['mesa']['array']) - ); - } - - /** - * @dataProvider getPathProvider - */ - public function testGetPath(Collection $c, $path, $expected, $separator = '/') - { - $this->assertEquals($expected, $c->getPath($path, $separator)); - } - - public function testOverridesSettings() - { - $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3)); - $c->overwriteWith(array('foo' => 10, 'bar' => 300)); - $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->toArray()); - } - - public function testOverwriteWithCollection() - { - $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3)); - $b = new Collection(array('foo' => 10, 'bar' => 300)); - $c->overwriteWith($b); - $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->toArray()); - } - - public function testOverwriteWithTraversable() - { - $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3)); - $b = new Collection(array('foo' => 10, 'bar' => 300)); - $c->overwriteWith($b->getIterator()); - $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->toArray()); - } - - public function testCanSetNestedPathValueThatDoesNotExist() - { - $c = new Collection(array()); - $c->setPath('foo/bar/baz/123', 'hi'); - $this->assertEquals('hi', $c['foo']['bar']['baz']['123']); - } - - public function testCanSetNestedPathValueThatExists() - { - $c = new Collection(array('foo' => array('bar' => 'test'))); - $c->setPath('foo/bar', 'hi'); - $this->assertEquals('hi', $c['foo']['bar']); - } - - /** - * @expectedException \RuntimeException - */ - public function testVerifiesNestedPathIsValidAtExactLevel() - { - $c = new Collection(array('foo' => 'bar')); - $c->setPath('foo/bar', 'hi'); - $this->assertEquals('hi', $c['foo']['bar']); - } - - /** - * @expectedException \RuntimeException - */ - public function testVerifiesThatNestedPathIsValidAtAnyLevel() - { - $c = new Collection(array('foo' => 'bar')); - $c->setPath('foo/bar/baz', 'test'); - } - - public function testCanAppendToNestedPathValues() - { - $c = new Collection(); - $c->setPath('foo/bar/[]', 'a'); - $c->setPath('foo/bar/[]', 'b'); - $this->assertEquals(['a', 'b'], $c['foo']['bar']); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Cookie/CookieJarTest.php b/core/vendor/guzzlehttp/guzzle/tests/Cookie/CookieJarTest.php deleted file mode 100644 index 1360419d9d84076c68476c49b0836cfbf240cd1e..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Cookie/CookieJarTest.php +++ /dev/null @@ -1,339 +0,0 @@ -<?php - -namespace GuzzleHttp\Tests\CookieJar; - -use GuzzleHttp\Cookie\CookieJar; -use GuzzleHttp\Cookie\SetCookie; -use GuzzleHttp\Message\Request; -use GuzzleHttp\Message\Response; - -/** - * @covers GuzzleHttp\Cookie\CookieJar - */ -class CookieJarTest extends \PHPUnit_Framework_TestCase -{ - /** @var CookieJar */ - private $jar; - - public function setUp() - { - $this->jar = new CookieJar(); - } - - protected function getTestCookies() - { - return [ - new SetCookie(['Name' => 'foo', 'Value' => 'bar', 'Domain' => 'foo.com', 'Path' => '/', 'Discard' => true]), - new SetCookie(['Name' => 'test', 'Value' => '123', 'Domain' => 'baz.com', 'Path' => '/foo', 'Expires' => 2]), - new SetCookie(['Name' => 'you', 'Value' => '123', 'Domain' => 'bar.com', 'Path' => '/boo', 'Expires' => time() + 1000]) - ]; - } - - public function testQuotesBadCookieValues() - { - $this->assertEquals('foo', CookieJar::getCookieValue('foo')); - $this->assertEquals('"foo,bar"', CookieJar::getCookieValue('foo,bar')); - } - - public function testCreatesFromArray() - { - $jar = CookieJar::fromArray([ - 'foo' => 'bar', - 'baz' => 'bam' - ], 'example.com'); - $this->assertCount(2, $jar); - } - - /** - * Provides test data for cookie cookieJar retrieval - */ - public function getCookiesDataProvider() - { - return [ - [['foo', 'baz', 'test', 'muppet', 'googoo'], '', '', '', false], - [['foo', 'baz', 'muppet', 'googoo'], '', '', '', true], - [['googoo'], 'www.example.com', '', '', false], - [['muppet', 'googoo'], 'test.y.example.com', '', '', false], - [['foo', 'baz'], 'example.com', '', '', false], - [['muppet'], 'x.y.example.com', '/acme/', '', false], - [['muppet'], 'x.y.example.com', '/acme/test/', '', false], - [['googoo'], 'x.y.example.com', '/test/acme/test/', '', false], - [['foo', 'baz'], 'example.com', '', '', false], - [['baz'], 'example.com', '', 'baz', false], - ]; - } - - public function testStoresAndRetrievesCookies() - { - $cookies = $this->getTestCookies(); - foreach ($cookies as $cookie) { - $this->assertTrue($this->jar->setCookie($cookie)); - } - - $this->assertEquals(3, count($this->jar)); - $this->assertEquals(3, count($this->jar->getIterator())); - $this->assertEquals($cookies, $this->jar->getIterator()->getArrayCopy()); - } - - public function testRemovesTemporaryCookies() - { - $cookies = $this->getTestCookies(); - foreach ($this->getTestCookies() as $cookie) { - $this->jar->setCookie($cookie); - } - $this->jar->clearSessionCookies(); - $this->assertEquals( - [$cookies[1], $cookies[2]], - $this->jar->getIterator()->getArrayCopy() - ); - } - - public function testRemovesSelectively() - { - foreach ($this->getTestCookies() as $cookie) { - $this->jar->setCookie($cookie); - } - - // Remove foo.com cookies - $this->jar->clear('foo.com'); - $this->assertEquals(2, count($this->jar)); - // Try again, removing no further cookies - $this->jar->clear('foo.com'); - $this->assertEquals(2, count($this->jar)); - - // Remove bar.com cookies with path of /boo - $this->jar->clear('bar.com', '/boo'); - $this->assertEquals(1, count($this->jar)); - - // Remove cookie by name - $this->jar->clear(null, null, 'test'); - $this->assertEquals(0, count($this->jar)); - } - - public function testDoesNotAddIncompleteCookies() - { - $this->assertEquals(false, $this->jar->setCookie(new SetCookie())); - $this->assertFalse($this->jar->setCookie(new SetCookie(array( - 'Name' => 'foo' - )))); - $this->assertFalse($this->jar->setCookie(new SetCookie(array( - 'Name' => false - )))); - $this->assertFalse($this->jar->setCookie(new SetCookie(array( - 'Name' => true - )))); - $this->assertFalse($this->jar->setCookie(new SetCookie(array( - 'Name' => 'foo', - 'Domain' => 'foo.com' - )))); - } - - public function testDoesAddValidCookies() - { - $this->assertTrue($this->jar->setCookie(new SetCookie(array( - 'Name' => 'foo', - 'Domain' => 'foo.com', - 'Value' => 0 - )))); - $this->assertTrue($this->jar->setCookie(new SetCookie(array( - 'Name' => 'foo', - 'Domain' => 'foo.com', - 'Value' => 0.0 - )))); - $this->assertTrue($this->jar->setCookie(new SetCookie(array( - 'Name' => 'foo', - 'Domain' => 'foo.com', - 'Value' => '0' - )))); - } - - public function testOverwritesCookiesThatAreOlderOrDiscardable() - { - $t = time() + 1000; - $data = array( - 'Name' => 'foo', - 'Value' => 'bar', - 'Domain' => '.example.com', - 'Path' => '/', - 'Max-Age' => '86400', - 'Secure' => true, - 'Discard' => true, - 'Expires' => $t - ); - - // Make sure that the discard cookie is overridden with the non-discard - $this->assertTrue($this->jar->setCookie(new SetCookie($data))); - $this->assertEquals(1, count($this->jar)); - - $data['Discard'] = false; - $this->assertTrue($this->jar->setCookie(new SetCookie($data))); - $this->assertEquals(1, count($this->jar)); - - $c = $this->jar->getIterator()->getArrayCopy(); - $this->assertEquals(false, $c[0]->getDiscard()); - - // Make sure it doesn't duplicate the cookie - $this->jar->setCookie(new SetCookie($data)); - $this->assertEquals(1, count($this->jar)); - - // Make sure the more future-ful expiration date supersede the other - $data['Expires'] = time() + 2000; - $this->assertTrue($this->jar->setCookie(new SetCookie($data))); - $this->assertEquals(1, count($this->jar)); - $c = $this->jar->getIterator()->getArrayCopy(); - $this->assertNotEquals($t, $c[0]->getExpires()); - } - - public function testOverwritesCookiesThatHaveChanged() - { - $t = time() + 1000; - $data = array( - 'Name' => 'foo', - 'Value' => 'bar', - 'Domain' => '.example.com', - 'Path' => '/', - 'Max-Age' => '86400', - 'Secure' => true, - 'Discard' => true, - 'Expires' => $t - ); - - // Make sure that the discard cookie is overridden with the non-discard - $this->assertTrue($this->jar->setCookie(new SetCookie($data))); - - $data['Value'] = 'boo'; - $this->assertTrue($this->jar->setCookie(new SetCookie($data))); - $this->assertEquals(1, count($this->jar)); - - // Changing the value plus a parameter also must overwrite the existing one - $data['Value'] = 'zoo'; - $data['Secure'] = false; - $this->assertTrue($this->jar->setCookie(new SetCookie($data))); - $this->assertEquals(1, count($this->jar)); - - $c = $this->jar->getIterator()->getArrayCopy(); - $this->assertEquals('zoo', $c[0]->getValue()); - } - - public function testAddsCookiesFromResponseWithRequest() - { - $response = new Response(200, array( - 'Set-Cookie' => "fpc=d=.Hm.yh4.1XmJWjJfs4orLQzKzPImxklQoxXSHOZATHUSEFciRueW_7704iYUtsXNEXq0M92Px2glMdWypmJ7HIQl6XIUvrZimWjQ3vIdeuRbI.FNQMAfcxu_XN1zSx7l.AcPdKL6guHc2V7hIQFhnjRW0rxm2oHY1P4bGQxFNz7f.tHm12ZD3DbdMDiDy7TBXsuP4DM-&v=2; expires=Fri, 02-Mar-2019 02:17:40 GMT;" - )); - $request = new Request('GET', 'http://www.example.com'); - $this->jar->extractCookies($request, $response); - $this->assertEquals(1, count($this->jar)); - } - - public function getMatchingCookiesDataProvider() - { - return array( - array('https://example.com', 'foo=bar; baz=foobar'), - array('http://example.com', ''), - array('https://example.com:8912', 'foo=bar; baz=foobar'), - array('https://foo.example.com', 'foo=bar; baz=foobar'), - array('http://foo.example.com/test/acme/', 'googoo=gaga') - ); - } - - /** - * @dataProvider getMatchingCookiesDataProvider - */ - public function testReturnsCookiesMatchingRequests($url, $cookies) - { - $bag = [ - new SetCookie([ - 'Name' => 'foo', - 'Value' => 'bar', - 'Domain' => 'example.com', - 'Path' => '/', - 'Max-Age' => '86400', - 'Secure' => true - ]), - new SetCookie([ - 'Name' => 'baz', - 'Value' => 'foobar', - 'Domain' => 'example.com', - 'Path' => '/', - 'Max-Age' => '86400', - 'Secure' => true - ]), - new SetCookie([ - 'Name' => 'test', - 'Value' => '123', - 'Domain' => 'www.foobar.com', - 'Path' => '/path/', - 'Discard' => true - ]), - new SetCookie([ - 'Name' => 'muppet', - 'Value' => 'cookie_monster', - 'Domain' => '.y.example.com', - 'Path' => '/acme/', - 'Expires' => time() + 86400 - ]), - new SetCookie([ - 'Name' => 'googoo', - 'Value' => 'gaga', - 'Domain' => '.example.com', - 'Path' => '/test/acme/', - 'Max-Age' => 1500 - ]) - ]; - - foreach ($bag as $cookie) { - $this->jar->setCookie($cookie); - } - - $request = new Request('GET', $url); - $this->jar->addCookieHeader($request); - $this->assertEquals($cookies, $request->getHeader('Cookie')); - } - - /** - * @expectedException \RuntimeException - * @expectedExceptionMessage Invalid cookie: Cookie name must not cannot invalid characters: - */ - public function testThrowsExceptionWithStrictMode() - { - $a = new CookieJar(true); - $a->setCookie(new SetCookie(['Name' => "abc\n", 'Value' => 'foo', 'Domain' => 'bar'])); - } - - public function testDeletesCookiesByName() - { - $cookies = $this->getTestCookies(); - $cookies[] = new SetCookie([ - 'Name' => 'other', - 'Value' => '123', - 'Domain' => 'bar.com', - 'Path' => '/boo', - 'Expires' => time() + 1000 - ]); - $jar = new CookieJar(); - foreach ($cookies as $cookie) { - $jar->setCookie($cookie); - } - $this->assertCount(4, $jar); - $jar->clear('bar.com', '/boo', 'other'); - $this->assertCount(3, $jar); - $names = array_map(function (SetCookie $c) { - return $c->getName(); - }, $jar->getIterator()->getArrayCopy()); - $this->assertEquals(['foo', 'test', 'you'], $names); - } - - public function testCanConvertToAndLoadFromArray() - { - $jar = new CookieJar(true); - foreach ($this->getTestCookies() as $cookie) { - $jar->setCookie($cookie); - } - $this->assertCount(3, $jar); - $arr = $jar->toArray(); - $this->assertCount(3, $arr); - $newCookieJar = new CookieJar(false, $arr); - $this->assertCount(3, $newCookieJar); - $this->assertSame($jar->toArray(), $newCookieJar->toArray()); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Cookie/FileCookieJarTest.php b/core/vendor/guzzlehttp/guzzle/tests/Cookie/FileCookieJarTest.php deleted file mode 100644 index 1d11337154280827f75d915c74da0eb21b72dfdc..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Cookie/FileCookieJarTest.php +++ /dev/null @@ -1,71 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\CookieJar; - -use GuzzleHttp\Cookie\FileCookieJar; -use GuzzleHttp\Cookie\SetCookie; - -/** - * @covers GuzzleHttp\Cookie\FileCookieJar - */ -class FileCookieJarTest extends \PHPUnit_Framework_TestCase -{ - private $file; - - public function setUp() - { - $this->file = tempnam('/tmp', 'file-cookies'); - } - - /** - * @expectedException \RuntimeException - */ - public function testValidatesCookieFile() - { - file_put_contents($this->file, 'true'); - new FileCookieJar($this->file); - } - - public function testLoadsFromFileFile() - { - $jar = new FileCookieJar($this->file); - $this->assertEquals([], $jar->getIterator()->getArrayCopy()); - unlink($this->file); - } - - public function testPersistsToFileFile() - { - $jar = new FileCookieJar($this->file); - $jar->setCookie(new SetCookie([ - 'Name' => 'foo', - 'Value' => 'bar', - 'Domain' => 'foo.com', - 'Expires' => time() + 1000 - ])); - $jar->setCookie(new SetCookie([ - 'Name' => 'baz', - 'Value' => 'bar', - 'Domain' => 'foo.com', - 'Expires' => time() + 1000 - ])); - $jar->setCookie(new SetCookie([ - 'Name' => 'boo', - 'Value' => 'bar', - 'Domain' => 'foo.com', - ])); - - $this->assertEquals(3, count($jar)); - unset($jar); - - // Make sure it wrote to the file - $contents = file_get_contents($this->file); - $this->assertNotEmpty($contents); - - // Load the cookieJar from the file - $jar = new FileCookieJar($this->file); - - // Weeds out temporary and session cookies - $this->assertEquals(2, count($jar)); - unset($jar); - unlink($this->file); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Cookie/SessionCookieJarTest.php b/core/vendor/guzzlehttp/guzzle/tests/Cookie/SessionCookieJarTest.php deleted file mode 100644 index ccc6d4eebaed80f8c783fcaa04105615fe0d6f2d..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Cookie/SessionCookieJarTest.php +++ /dev/null @@ -1,76 +0,0 @@ -<?php - -namespace GuzzleHttp\Tests\CookieJar; - -use GuzzleHttp\Cookie\SessionCookieJar; -use GuzzleHttp\Cookie\SetCookie; - -/** - * @covers GuzzleHttp\Cookie\SessionCookieJar - */ -class SessionCookieJarTest extends \PHPUnit_Framework_TestCase -{ - private $sessionVar; - - public function setUp() - { - $this->sessionVar = 'sessionKey'; - - if (!isset($_SESSION)) { - $_SESSION = array(); - } - } - - /** - * @expectedException \RuntimeException - */ - public function testValidatesCookieSession() - { - $_SESSION[$this->sessionVar] = 'true'; - new SessionCookieJar($this->sessionVar); - } - - public function testLoadsFromSession() - { - $jar = new SessionCookieJar($this->sessionVar); - $this->assertEquals([], $jar->getIterator()->getArrayCopy()); - unset($_SESSION[$this->sessionVar]); - } - - public function testPersistsToSession() - { - $jar = new SessionCookieJar($this->sessionVar); - $jar->setCookie(new SetCookie([ - 'Name' => 'foo', - 'Value' => 'bar', - 'Domain' => 'foo.com', - 'Expires' => time() + 1000 - ])); - $jar->setCookie(new SetCookie([ - 'Name' => 'baz', - 'Value' => 'bar', - 'Domain' => 'foo.com', - 'Expires' => time() + 1000 - ])); - $jar->setCookie(new SetCookie([ - 'Name' => 'boo', - 'Value' => 'bar', - 'Domain' => 'foo.com', - ])); - - $this->assertEquals(3, count($jar)); - unset($jar); - - // Make sure it wrote to the sessionVar in $_SESSION - $contents = $_SESSION[$this->sessionVar]; - $this->assertNotEmpty($contents); - - // Load the cookieJar from the file - $jar = new SessionCookieJar($this->sessionVar); - - // Weeds out temporary and session cookies - $this->assertEquals(2, count($jar)); - unset($jar); - unset($_SESSION[$this->sessionVar]); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Cookie/SetCookieTest.php b/core/vendor/guzzlehttp/guzzle/tests/Cookie/SetCookieTest.php deleted file mode 100644 index 3ddd082018bba044756cd023e4954f1ed73c8899..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Cookie/SetCookieTest.php +++ /dev/null @@ -1,364 +0,0 @@ -<?php - -namespace GuzzleHttp\Tests\CookieJar; - -use GuzzleHttp\Cookie\SetCookie; - -/** - * @covers GuzzleHttp\Cookie\SetCookie - */ -class SetCookieTest extends \PHPUnit_Framework_TestCase -{ - public function testInitializesDefaultValues() - { - $cookie = new SetCookie(); - $this->assertEquals('/', $cookie->getPath()); - } - - public function testConvertsDateTimeMaxAgeToUnixTimestamp() - { - $cookie = new SetCookie(['Expires' => 'November 20, 1984']); - $this->assertInternalType('integer', $cookie->getExpires()); - } - - public function testAddsExpiresBasedOnMaxAge() - { - $t = time(); - $cookie = new SetCookie(['Max-Age' => 100]); - $this->assertEquals($t + 100, $cookie->getExpires()); - } - - public function testHoldsValues() - { - $t = time(); - $data = array( - 'Name' => 'foo', - 'Value' => 'baz', - 'Path' => '/bar', - 'Domain' => 'baz.com', - 'Expires' => $t, - 'Max-Age' => 100, - 'Secure' => true, - 'Discard' => true, - 'HttpOnly' => true, - 'foo' => 'baz', - 'bar' => 'bam' - ); - - $cookie = new SetCookie($data); - $this->assertEquals($data, $cookie->toArray()); - - $this->assertEquals('foo', $cookie->getName()); - $this->assertEquals('baz', $cookie->getValue()); - $this->assertEquals('baz.com', $cookie->getDomain()); - $this->assertEquals('/bar', $cookie->getPath()); - $this->assertEquals($t, $cookie->getExpires()); - $this->assertEquals(100, $cookie->getMaxAge()); - $this->assertTrue($cookie->getSecure()); - $this->assertTrue($cookie->getDiscard()); - $this->assertTrue($cookie->getHttpOnly()); - $this->assertEquals('baz', $cookie->toArray()['foo']); - $this->assertEquals('bam', $cookie->toArray()['bar']); - - $cookie->setName('a'); - $cookie->setValue('b'); - $cookie->setPath('c'); - $cookie->setDomain('bar.com'); - $cookie->setExpires(10); - $cookie->setMaxAge(200); - $cookie->setSecure(false); - $cookie->setHttpOnly(false); - $cookie->setDiscard(false); - - $this->assertEquals('a', $cookie->getName()); - $this->assertEquals('b', $cookie->getValue()); - $this->assertEquals('c', $cookie->getPath()); - $this->assertEquals('bar.com', $cookie->getDomain()); - $this->assertEquals(10, $cookie->getExpires()); - $this->assertEquals(200, $cookie->getMaxAge()); - $this->assertFalse($cookie->getSecure()); - $this->assertFalse($cookie->getDiscard()); - $this->assertFalse($cookie->getHttpOnly()); - } - - public function testDeterminesIfExpired() - { - $c = new SetCookie(); - $c->setExpires(10); - $this->assertTrue($c->isExpired()); - $c->setExpires(time() + 10000); - $this->assertFalse($c->isExpired()); - } - - public function testMatchesDomain() - { - $cookie = new SetCookie(); - $this->assertTrue($cookie->matchesDomain('baz.com')); - - $cookie->setDomain('baz.com'); - $this->assertTrue($cookie->matchesDomain('baz.com')); - $this->assertFalse($cookie->matchesDomain('bar.com')); - - $cookie->setDomain('.baz.com'); - $this->assertTrue($cookie->matchesDomain('.baz.com')); - $this->assertTrue($cookie->matchesDomain('foo.baz.com')); - $this->assertFalse($cookie->matchesDomain('baz.bar.com')); - $this->assertTrue($cookie->matchesDomain('baz.com')); - - $cookie->setDomain('.127.0.0.1'); - $this->assertTrue($cookie->matchesDomain('127.0.0.1')); - - $cookie->setDomain('127.0.0.1'); - $this->assertTrue($cookie->matchesDomain('127.0.0.1')); - - $cookie->setDomain('.com.'); - $this->assertFalse($cookie->matchesDomain('baz.com')); - - $cookie->setDomain('.local'); - $this->assertTrue($cookie->matchesDomain('example.local')); - } - - public function testMatchesPath() - { - $cookie = new SetCookie(); - $this->assertTrue($cookie->matchesPath('/foo')); - - $cookie->setPath('/foo'); - $this->assertTrue($cookie->matchesPath('/foo')); - $this->assertTrue($cookie->matchesPath('/foo/bar')); - $this->assertFalse($cookie->matchesPath('/bar')); - } - - public function cookieValidateProvider() - { - return array( - array('foo', 'baz', 'bar', true), - array('0', '0', '0', true), - array('', 'baz', 'bar', 'The cookie name must not be empty'), - array('foo', '', 'bar', 'The cookie value must not be empty'), - array('foo', 'baz', '', 'The cookie domain must not be empty'), - array("foo\r", 'baz', '0', 'Cookie name must not cannot invalid characters: =,; \t\r\n\013\014'), - ); - } - - /** - * @dataProvider cookieValidateProvider - */ - public function testValidatesCookies($name, $value, $domain, $result) - { - $cookie = new SetCookie(array( - 'Name' => $name, - 'Value' => $value, - 'Domain' => $domain - )); - $this->assertSame($result, $cookie->validate()); - } - - public function testDoesNotMatchIp() - { - $cookie = new SetCookie(['Domain' => '192.168.16.']); - $this->assertFalse($cookie->matchesDomain('192.168.16.121')); - } - - public function testConvertsToString() - { - $t = 1382916008; - $cookie = new SetCookie([ - 'Name' => 'test', - 'Value' => '123', - 'Domain' => 'foo.com', - 'Expires' => $t, - 'Path' => '/abc', - 'HttpOnly' => true, - 'Secure' => true - ]); - $this->assertEquals( - 'test=123; Domain=foo.com; Path=/abc; Expires=Sun, 27 Oct 2013 23:20:08 GMT; Secure; HttpOnly', - (string) $cookie - ); - } - - /** - * 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' => '/', - 'PHPSESSID' => '6c951590e7a9359bcedde25cda73e43c', - 'Max-Age' => NULL, - 'Expires' => 'Sat, 26-Jul-2008 17:00:42 GMT', - 'Secure' => NULL, - 'Discard' => NULL, - 'Name' => 'ASIHTTPRequestTestCookie', - 'Value' => 'This+is+the+value', - 'HttpOnly' => false - ) - ), - array('', []), - array('foo', []), - // Test setting a blank value for a cookie - array(array( - 'foo=', 'foo =', 'foo =;', 'foo= ;', 'foo =', 'foo= '), - array( - 'Name' => 'foo', - 'Value' => '', - 'Discard' => null, - 'Domain' => null, - 'Expires' => null, - 'Max-Age' => null, - 'Path' => '/', - 'Secure' => null, - 'HttpOnly' => 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( - 'Name' => 'foo', - 'Value' => '1', - 'Discard' => null, - 'Domain' => null, - 'Expires' => null, - 'Max-Age' => null, - 'Path' => '/', - 'Secure' => null, - 'HttpOnly' => false - ) - ), - // 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( - 'Name' => 'justacookie', - 'Value' => 'foo', - 'Domain' => 'example.com', - 'Discard' => null, - 'Expires' => null, - 'Max-Age' => null, - 'Path' => '/', - 'Secure' => null, - 'HttpOnly' => false - ) - ), - array( - 'expires=tomorrow; secure; path=/Space Out/; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=.example.com', - array( - 'Name' => 'expires', - 'Value' => 'tomorrow', - 'Domain' => '.example.com', - 'Path' => '/Space Out/', - 'Expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', - 'Discard' => null, - 'Secure' => true, - 'Max-Age' => null, - 'HttpOnly' => false - ) - ), - array( - 'domain=unittests; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=example.com; path=/some value/', - array( - 'Name' => 'domain', - 'Value' => 'unittests', - 'Domain' => 'example.com', - 'Path' => '/some value/', - 'Expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', - 'Secure' => false, - 'Discard' => null, - 'Max-Age' => null, - 'HttpOnly' => false - ) - ), - array( - 'path=indexAction; path=/; domain=.foo.com; expires=Tue, 21-Nov-2006 08:33:44 GMT', - array( - 'Name' => 'path', - 'Value' => 'indexAction', - 'Domain' => '.foo.com', - 'Path' => '/', - 'Expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', - 'Secure' => false, - 'Discard' => null, - 'Max-Age' => null, - 'HttpOnly' => false - ) - ), - array( - 'secure=sha1; secure; SECURE; domain=some.really.deep.domain.com; version=1; Max-Age=86400', - array( - 'Name' => 'secure', - 'Value' => 'sha1', - 'Domain' => 'some.really.deep.domain.com', - 'Path' => '/', - 'Secure' => true, - 'Discard' => null, - 'Expires' => time() + 86400, - 'Max-Age' => 86400, - 'HttpOnly' => false, - 'version' => '1' - ) - ), - array( - 'PHPSESSID=123456789+abcd%2Cef; secure; discard; domain=.localdomain; path=/foo/baz; expires=Tue, 21-Nov-2006 08:33:44 GMT;', - array( - 'Name' => 'PHPSESSID', - 'Value' => '123456789+abcd%2Cef', - 'Domain' => '.localdomain', - 'Path' => '/foo/baz', - 'Expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', - 'Secure' => true, - 'Discard' => true, - 'Max-Age' => null, - 'HttpOnly' => false - ) - ), - ); - } - - /** - * @dataProvider cookieParserDataProvider - */ - public function testParseCookie($cookie, $parsed) - { - foreach ((array) $cookie as $v) { - $c = SetCookie::fromString($v); - $p = $c->toArray(); - - if (isset($p['Expires'])) { - // Remove expires values from the assertion if they are relatively equal - if (abs($p['Expires'] != strtotime($parsed['Expires'])) < 40) { - unset($p['Expires']); - unset($parsed['Expires']); - } - } - - if (!empty($parsed)) { - foreach ($parsed as $key => $value) { - $this->assertEquals($parsed[$key], $p[$key], 'Comparing ' . $key . ' ' . var_export($value, true) . ' : ' . var_export($parsed, true) . ' | ' . var_export($p, true)); - } - foreach ($p as $key => $value) { - $this->assertEquals($p[$key], $parsed[$key], 'Comparing ' . $key . ' ' . var_export($value, true) . ' : ' . var_export($parsed, true) . ' | ' . var_export($p, true)); - } - } else { - $this->assertEquals([ - 'Name' => null, - 'Value' => null, - 'Domain' => null, - 'Path' => '/', - 'Max-Age' => null, - 'Expires' => null, - 'Secure' => false, - 'Discard' => false, - 'HttpOnly' => false, - ], $p); - } - } - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Event/AbstractEventTest.php b/core/vendor/guzzlehttp/guzzle/tests/Event/AbstractEventTest.php deleted file mode 100644 index b8c06f15272ec70072e884cbedad4ce3385faf83..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Event/AbstractEventTest.php +++ /dev/null @@ -1,14 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Event; - -class AbstractEventTest extends \PHPUnit_Framework_TestCase -{ - public function testStopsPropagation() - { - $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractEvent') - ->getMockForAbstractClass(); - $this->assertFalse($e->isPropagationStopped()); - $e->stopPropagation(); - $this->assertTrue($e->isPropagationStopped()); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Event/AbstractRequestEventTest.php b/core/vendor/guzzlehttp/guzzle/tests/Event/AbstractRequestEventTest.php deleted file mode 100644 index 50536c5826eb4344e8ee44c551a30a83943c70ff..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Event/AbstractRequestEventTest.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Event; - -use GuzzleHttp\Client; -use GuzzleHttp\Transaction; -use GuzzleHttp\Message\Request; - -/** - * @covers GuzzleHttp\Event\AbstractRequestEvent - */ -class AbstractRequestEventTest extends \PHPUnit_Framework_TestCase -{ - public function testHasTransactionMethods() - { - $t = new Transaction(new Client(), new Request('GET', '/')); - $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractRequestEvent') - ->setConstructorArgs([$t]) - ->getMockForAbstractClass(); - $this->assertSame($t->client, $e->getClient()); - $this->assertSame($t->request, $e->getRequest()); - } - - public function testHasTransaction() - { - $t = new Transaction(new Client(), new Request('GET', '/')); - $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractRequestEvent') - ->setConstructorArgs([$t]) - ->getMockForAbstractClass(); - $r = new \ReflectionMethod($e, 'getTransaction'); - $r->setAccessible(true); - $this->assertSame($t, $r->invoke($e)); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Event/AbstractRetryableEventTest.php b/core/vendor/guzzlehttp/guzzle/tests/Event/AbstractRetryableEventTest.php deleted file mode 100644 index 6a39d8bb09635b05343e4dbb90cd9ef2f84b0933..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Event/AbstractRetryableEventTest.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Event; - -use GuzzleHttp\Client; -use GuzzleHttp\Transaction; -use GuzzleHttp\Message\Request; - -/** - * @covers GuzzleHttp\Event\AbstractRetryableEvent - */ -class AbstractRetryableEventTest extends \PHPUnit_Framework_TestCase -{ - public function testCanRetry() - { - $t = new Transaction(new Client(), new Request('GET', '/')); - $t->transferInfo = ['foo' => 'bar']; - $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractRetryableEvent') - ->setConstructorArgs([$t]) - ->getMockForAbstractClass(); - $e->retry(); - $this->assertTrue($e->isPropagationStopped()); - $this->assertEquals('retry', $t->state); - } - - public function testCanRetryAfterDelay() - { - $t = new Transaction(new Client(), new Request('GET', '/')); - $t->transferInfo = ['foo' => 'bar']; - $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractRetryableEvent') - ->setConstructorArgs([$t]) - ->getMockForAbstractClass(); - $e->retry(10); - $this->assertTrue($e->isPropagationStopped()); - $this->assertEquals('retry', $t->state); - $this->assertEquals(10, $t->request->getConfig()->get('delay')); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Event/AbstractTransferEventTest.php b/core/vendor/guzzlehttp/guzzle/tests/Event/AbstractTransferEventTest.php deleted file mode 100644 index 5313c8e7f36526ffbf0d1414082a223b4c97eb84..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Event/AbstractTransferEventTest.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Event; - -use GuzzleHttp\Client; -use GuzzleHttp\Message\Response; -use GuzzleHttp\Transaction; -use GuzzleHttp\Message\Request; - -/** - * @covers GuzzleHttp\Event\AbstractTransferEvent - */ -class AbstractTransferEventTest extends \PHPUnit_Framework_TestCase -{ - public function testHasStats() - { - $t = new Transaction(new Client(), new Request('GET', '/')); - $t->transferInfo = ['foo' => 'bar']; - $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractTransferEvent') - ->setConstructorArgs([$t]) - ->getMockForAbstractClass(); - $this->assertNull($e->getTransferInfo('baz')); - $this->assertEquals('bar', $e->getTransferInfo('foo')); - $this->assertEquals($t->transferInfo, $e->getTransferInfo()); - } - - public function testHasResponse() - { - $t = new Transaction(new Client(), new Request('GET', '/')); - $t->response = new Response(200); - $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractTransferEvent') - ->setConstructorArgs([$t]) - ->getMockForAbstractClass(); - $this->assertTrue($e->hasResponse()); - $this->assertSame($t->response, $e->getResponse()); - } - - public function testCanInterceptWithResponse() - { - $t = new Transaction(new Client(), new Request('GET', '/')); - $r = new Response(200); - $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractTransferEvent') - ->setConstructorArgs([$t]) - ->getMockForAbstractClass(); - $e->intercept($r); - $this->assertSame($t->response, $r); - $this->assertSame($t->response, $e->getResponse()); - $this->assertTrue($e->isPropagationStopped()); - } - - public function testReturnsNumberOfRetries() - { - $t = new Transaction(new Client(), new Request('GET', '/')); - $t->retries = 2; - $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractTransferEvent') - ->setConstructorArgs([$t]) - ->getMockForAbstractClass(); - $this->assertEquals(2, $e->getRetryCount()); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Event/BeforeEventTest.php b/core/vendor/guzzlehttp/guzzle/tests/Event/BeforeEventTest.php deleted file mode 100644 index 469e4e25169c499317f00da335cefe0f8450f779..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Event/BeforeEventTest.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Event; - -use GuzzleHttp\Transaction; -use GuzzleHttp\Client; -use GuzzleHttp\Event\BeforeEvent; -use GuzzleHttp\Message\Request; -use GuzzleHttp\Message\Response; - -/** - * @covers GuzzleHttp\Event\BeforeEvent - */ -class BeforeEventTest extends \PHPUnit_Framework_TestCase -{ - public function testInterceptsWithEvent() - { - $t = new Transaction(new Client(), new Request('GET', '/')); - $t->exception = new \Exception('foo'); - $e = new BeforeEvent($t); - $response = new Response(200); - $e->intercept($response); - $this->assertTrue($e->isPropagationStopped()); - $this->assertSame($t->response, $response); - $this->assertNull($t->exception); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Event/EmitterTest.php b/core/vendor/guzzlehttp/guzzle/tests/Event/EmitterTest.php deleted file mode 100644 index 5b7061bc600c24e311c6d8986029eeb4568f0b98..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Event/EmitterTest.php +++ /dev/null @@ -1,363 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Event; - -use GuzzleHttp\Event\Emitter; -use GuzzleHttp\Event\EventInterface; -use GuzzleHttp\Event\SubscriberInterface; - -/** - * @link https://github.com/symfony/symfony/blob/master/src/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php Based on this test. - */ -class EmitterTest extends \PHPUnit_Framework_TestCase -{ - /* Some pseudo events */ - const preFoo = 'pre.foo'; - const postFoo = 'post.foo'; - const preBar = 'pre.bar'; - const postBar = 'post.bar'; - - /** @var Emitter */ - private $emitter; - private $listener; - - protected function setUp() - { - $this->emitter = new Emitter(); - $this->listener = new TestEventListener(); - } - - protected function tearDown() - { - $this->emitter = null; - $this->listener = null; - } - - public function testInitialState() - { - $this->assertEquals(array(), $this->emitter->listeners()); - } - - public function testAddListener() - { - $this->emitter->on('pre.foo', array($this->listener, 'preFoo')); - $this->emitter->on('post.foo', array($this->listener, 'postFoo')); - $this->assertTrue($this->emitter->hasListeners(self::preFoo)); - $this->assertTrue($this->emitter->hasListeners(self::preFoo)); - $this->assertCount(1, $this->emitter->listeners(self::postFoo)); - $this->assertCount(1, $this->emitter->listeners(self::postFoo)); - $this->assertCount(2, $this->emitter->listeners()); - } - - public function testGetListenersSortsByPriority() - { - $listener1 = new TestEventListener(); - $listener2 = new TestEventListener(); - $listener3 = new TestEventListener(); - $listener1->name = '1'; - $listener2->name = '2'; - $listener3->name = '3'; - - $this->emitter->on('pre.foo', array($listener1, 'preFoo'), -10); - $this->emitter->on('pre.foo', array($listener2, 'preFoo'), 10); - $this->emitter->on('pre.foo', array($listener3, 'preFoo')); - - $expected = array( - array($listener2, 'preFoo'), - array($listener3, 'preFoo'), - array($listener1, 'preFoo'), - ); - - $this->assertSame($expected, $this->emitter->listeners('pre.foo')); - } - - public function testGetAllListenersSortsByPriority() - { - $listener1 = new TestEventListener(); - $listener2 = new TestEventListener(); - $listener3 = new TestEventListener(); - $listener4 = new TestEventListener(); - $listener5 = new TestEventListener(); - $listener6 = new TestEventListener(); - - $this->emitter->on('pre.foo', [$listener1, 'preFoo'], -10); - $this->emitter->on('pre.foo', [$listener2, 'preFoo']); - $this->emitter->on('pre.foo', [$listener3, 'preFoo'], 10); - $this->emitter->on('post.foo', [$listener4, 'preFoo'], -10); - $this->emitter->on('post.foo', [$listener5, 'preFoo']); - $this->emitter->on('post.foo', [$listener6, 'preFoo'], 10); - - $expected = [ - 'pre.foo' => [[$listener3, 'preFoo'], [$listener2, 'preFoo'], [$listener1, 'preFoo']], - 'post.foo' => [[$listener6, 'preFoo'], [$listener5, 'preFoo'], [$listener4, 'preFoo']], - ]; - - $this->assertSame($expected, $this->emitter->listeners()); - } - - public function testDispatch() - { - $this->emitter->on('pre.foo', array($this->listener, 'preFoo')); - $this->emitter->on('post.foo', array($this->listener, 'postFoo')); - $this->emitter->emit(self::preFoo, $this->getEvent()); - $this->assertTrue($this->listener->preFooInvoked); - $this->assertFalse($this->listener->postFooInvoked); - $this->assertInstanceOf('GuzzleHttp\Event\EventInterface', $this->emitter->emit(self::preFoo, $this->getEvent())); - $event = $this->getEvent(); - $return = $this->emitter->emit(self::preFoo, $event); - $this->assertSame($event, $return); - } - - public function testDispatchForClosure() - { - $invoked = 0; - $listener = function () use (&$invoked) { - $invoked++; - }; - $this->emitter->on('pre.foo', $listener); - $this->emitter->on('post.foo', $listener); - $this->emitter->emit(self::preFoo, $this->getEvent()); - $this->assertEquals(1, $invoked); - } - - public function testStopEventPropagation() - { - $otherListener = new TestEventListener(); - - // postFoo() stops the propagation, so only one listener should - // be executed - // Manually set priority to enforce $this->listener to be called first - $this->emitter->on('post.foo', array($this->listener, 'postFoo'), 10); - $this->emitter->on('post.foo', array($otherListener, 'preFoo')); - $this->emitter->emit(self::postFoo, $this->getEvent()); - $this->assertTrue($this->listener->postFooInvoked); - $this->assertFalse($otherListener->postFooInvoked); - } - - public function testDispatchByPriority() - { - $invoked = array(); - $listener1 = function () use (&$invoked) { - $invoked[] = '1'; - }; - $listener2 = function () use (&$invoked) { - $invoked[] = '2'; - }; - $listener3 = function () use (&$invoked) { - $invoked[] = '3'; - }; - $this->emitter->on('pre.foo', $listener1, -10); - $this->emitter->on('pre.foo', $listener2); - $this->emitter->on('pre.foo', $listener3, 10); - $this->emitter->emit(self::preFoo, $this->getEvent()); - $this->assertEquals(array('3', '2', '1'), $invoked); - } - - public function testRemoveListener() - { - $this->emitter->on('pre.bar', [$this->listener, 'preFoo']); - $this->assertNotEmpty($this->emitter->listeners(self::preBar)); - $this->emitter->removeListener('pre.bar', [$this->listener, 'preFoo']); - $this->assertEmpty($this->emitter->listeners(self::preBar)); - $this->emitter->removeListener('notExists', [$this->listener, 'preFoo']); - } - - public function testAddSubscriber() - { - $eventSubscriber = new TestEventSubscriber(); - $this->emitter->attach($eventSubscriber); - $this->assertNotEmpty($this->emitter->listeners(self::preFoo)); - $this->assertNotEmpty($this->emitter->listeners(self::postFoo)); - } - - public function testAddSubscriberWithMultiple() - { - $eventSubscriber = new TestEventSubscriberWithMultiple(); - $this->emitter->attach($eventSubscriber); - $listeners = $this->emitter->listeners('pre.foo'); - $this->assertNotEmpty($this->emitter->listeners(self::preFoo)); - $this->assertCount(2, $listeners); - } - - public function testAddSubscriberWithPriorities() - { - $eventSubscriber = new TestEventSubscriber(); - $this->emitter->attach($eventSubscriber); - - $eventSubscriber = new TestEventSubscriberWithPriorities(); - $this->emitter->attach($eventSubscriber); - - $listeners = $this->emitter->listeners('pre.foo'); - $this->assertNotEmpty($this->emitter->listeners(self::preFoo)); - $this->assertCount(2, $listeners); - $this->assertInstanceOf('GuzzleHttp\Tests\Event\TestEventSubscriberWithPriorities', $listeners[0][0]); - } - - public function testdetach() - { - $eventSubscriber = new TestEventSubscriber(); - $this->emitter->attach($eventSubscriber); - $this->assertNotEmpty($this->emitter->listeners(self::preFoo)); - $this->assertNotEmpty($this->emitter->listeners(self::postFoo)); - $this->emitter->detach($eventSubscriber); - $this->assertEmpty($this->emitter->listeners(self::preFoo)); - $this->assertEmpty($this->emitter->listeners(self::postFoo)); - } - - public function testdetachWithPriorities() - { - $eventSubscriber = new TestEventSubscriberWithPriorities(); - $this->emitter->attach($eventSubscriber); - $this->assertNotEmpty($this->emitter->listeners(self::preFoo)); - $this->assertNotEmpty($this->emitter->listeners(self::postFoo)); - $this->emitter->detach($eventSubscriber); - $this->assertEmpty($this->emitter->listeners(self::preFoo)); - $this->assertEmpty($this->emitter->listeners(self::postFoo)); - } - - public function testEventReceivesEventNameAsArgument() - { - $listener = new TestWithDispatcher(); - $this->emitter->on('test', array($listener, 'foo')); - $this->assertNull($listener->name); - $this->emitter->emit('test', $this->getEvent()); - $this->assertEquals('test', $listener->name); - } - - /** - * @see https://bugs.php.net/bug.php?id=62976 - * - * This bug affects: - * - The PHP 5.3 branch for versions < 5.3.18 - * - The PHP 5.4 branch for versions < 5.4.8 - * - The PHP 5.5 branch is not affected - */ - public function testWorkaroundForPhpBug62976() - { - $dispatcher = new Emitter(); - $dispatcher->on('bug.62976', new CallableClass()); - $dispatcher->removeListener('bug.62976', function () {}); - $this->assertNotEmpty($dispatcher->listeners('bug.62976')); - } - - public function testRegistersEventsOnce() - { - $this->emitter->once('pre.foo', array($this->listener, 'preFoo')); - $this->emitter->on('pre.foo', array($this->listener, 'preFoo')); - $this->assertCount(2, $this->emitter->listeners(self::preFoo)); - $this->emitter->emit(self::preFoo, $this->getEvent()); - $this->assertTrue($this->listener->preFooInvoked); - $this->assertCount(1, $this->emitter->listeners(self::preFoo)); - } - - public function testReturnsEmptyArrayForNonExistentEvent() - { - $this->assertEquals([], $this->emitter->listeners('doesnotexist')); - } - - public function testCanAddFirstAndLastListeners() - { - $b = ''; - $this->emitter->on('foo', function () use (&$b) { $b .= 'a'; }, 'first'); // 1 - $this->emitter->on('foo', function () use (&$b) { $b .= 'b'; }, 'last'); // 0 - $this->emitter->on('foo', function () use (&$b) { $b .= 'c'; }, 'first'); // 2 - $this->emitter->on('foo', function () use (&$b) { $b .= 'd'; }, 'first'); // 3 - $this->emitter->on('foo', function () use (&$b) { $b .= 'e'; }, 'first'); // 4 - $this->emitter->on('foo', function () use (&$b) { $b .= 'f'; }); // 0 - $this->emitter->emit('foo', $this->getEvent()); - $this->assertEquals('edcabf', $b); - } - - /** - * @return \GuzzleHttp\Event\EventInterface - */ - private function getEvent() - { - return $this->getMockBuilder('GuzzleHttp\Event\AbstractEvent') - ->getMockForAbstractClass(); - } -} - -class CallableClass -{ - public function __invoke() - { - } -} - -class TestEventListener -{ - public $preFooInvoked = false; - public $postFooInvoked = false; - - /* Listener methods */ - - public function preFoo(EventInterface $e) - { - $this->preFooInvoked = true; - } - - public function postFoo(EventInterface $e) - { - $this->postFooInvoked = true; - - $e->stopPropagation(); - } - - /** - * @expectedException \PHPUnit_Framework_Error_Deprecated - */ - public function testHasDeprecatedAddListener() - { - $emitter = new Emitter(); - $emitter->addListener('foo', function () {}); - } - - /** - * @expectedException \PHPUnit_Framework_Error_Deprecated - */ - public function testHasDeprecatedAddSubscriber() - { - $emitter = new Emitter(); - $emitter->addSubscriber('foo', new TestEventSubscriber()); - } -} - -class TestWithDispatcher -{ - public $name; - - public function foo(EventInterface $e, $name) - { - $this->name = $name; - } -} - -class TestEventSubscriber extends TestEventListener implements SubscriberInterface -{ - public function getEvents() - { - return [ - 'pre.foo' => ['preFoo'], - 'post.foo' => ['postFoo'] - ]; - } -} - -class TestEventSubscriberWithPriorities extends TestEventListener implements SubscriberInterface -{ - public function getEvents() - { - return [ - 'pre.foo' => ['preFoo', 10], - 'post.foo' => ['postFoo'] - ]; - } -} - -class TestEventSubscriberWithMultiple extends TestEventListener implements SubscriberInterface -{ - public function getEvents() - { - return ['pre.foo' => [['preFoo', 10],['preFoo', 20]]]; - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Event/ErrorEventTest.php b/core/vendor/guzzlehttp/guzzle/tests/Event/ErrorEventTest.php deleted file mode 100644 index e91b7f0c3a38ee7666ffffc35ec6fbf758e8ed73..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Event/ErrorEventTest.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Event; - -use GuzzleHttp\Transaction; -use GuzzleHttp\Client; -use GuzzleHttp\Event\ErrorEvent; -use GuzzleHttp\Exception\RequestException; -use GuzzleHttp\Message\Request; - -/** - * @covers GuzzleHttp\Event\ErrorEvent - */ -class ErrorEventTest extends \PHPUnit_Framework_TestCase -{ - public function testInterceptsWithEvent() - { - $t = new Transaction(new Client(), new Request('GET', '/')); - $except = new RequestException('foo', $t->request); - $t->exception = $except; - $e = new ErrorEvent($t); - $this->assertSame($e->getException(), $t->exception); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Event/HasEmitterTraitTest.php b/core/vendor/guzzlehttp/guzzle/tests/Event/HasEmitterTraitTest.php deleted file mode 100644 index 470991871610f5bd4801421a276466f7ab677674..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Event/HasEmitterTraitTest.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Event; - -use GuzzleHttp\Event\HasEmitterInterface; -use GuzzleHttp\Event\HasEmitterTrait; - -class AbstractHasEmitter implements HasEmitterInterface -{ - use HasEmitterTrait; -} - -/** - * @covers GuzzleHttp\Event\HasEmitterTrait - */ -class HasEmitterTraitTest extends \PHPUnit_Framework_TestCase -{ - public function testHelperAttachesSubscribers() - { - $mock = $this->getMockBuilder('GuzzleHttp\Tests\Event\AbstractHasEmitter') - ->getMockForAbstractClass(); - - $result = $mock->getEmitter(); - $this->assertInstanceOf('GuzzleHttp\Event\EmitterInterface', $result); - $result2 = $mock->getEmitter(); - $this->assertSame($result, $result2); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Event/ListenerAttacherTraitTest.php b/core/vendor/guzzlehttp/guzzle/tests/Event/ListenerAttacherTraitTest.php deleted file mode 100644 index c066788bcc60181b7573e4950b82f6c48660f0fe..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Event/ListenerAttacherTraitTest.php +++ /dev/null @@ -1,92 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Event; - -use GuzzleHttp\Event\HasEmitterInterface; -use GuzzleHttp\Event\HasEmitterTrait; -use GuzzleHttp\Event\ListenerAttacherTrait; - -class ObjectWithEvents implements HasEmitterInterface -{ - use HasEmitterTrait, ListenerAttacherTrait; - - public $listeners = []; - - public function __construct(array $args = []) - { - $this->listeners = $this->prepareListeners($args, ['foo', 'bar']); - $this->attachListeners($this, $this->listeners); - } -} - -class ListenerAttacherTraitTest extends \PHPUnit_Framework_TestCase -{ - public function testRegistersEvents() - { - $fn = function () {}; - $o = new ObjectWithEvents([ - 'foo' => $fn, - 'bar' => $fn, - ]); - - $this->assertEquals([ - ['name' => 'foo', 'fn' => $fn, 'priority' => 0, 'once' => false], - ['name' => 'bar', 'fn' => $fn, 'priority' => 0, 'once' => false], - ], $o->listeners); - - $this->assertCount(1, $o->getEmitter()->listeners('foo')); - $this->assertCount(1, $o->getEmitter()->listeners('bar')); - } - - public function testRegistersEventsWithPriorities() - { - $fn = function () {}; - $o = new ObjectWithEvents([ - 'foo' => ['fn' => $fn, 'priority' => 99, 'once' => true], - 'bar' => ['fn' => $fn, 'priority' => 50], - ]); - - $this->assertEquals([ - ['name' => 'foo', 'fn' => $fn, 'priority' => 99, 'once' => true], - ['name' => 'bar', 'fn' => $fn, 'priority' => 50, 'once' => false], - ], $o->listeners); - } - - public function testRegistersMultipleEvents() - { - $fn = function () {}; - $eventArray = [['fn' => $fn], ['fn' => $fn]]; - $o = new ObjectWithEvents([ - 'foo' => $eventArray, - 'bar' => $eventArray, - ]); - - $this->assertEquals([ - ['name' => 'foo', 'fn' => $fn, 'priority' => 0, 'once' => false], - ['name' => 'foo', 'fn' => $fn, 'priority' => 0, 'once' => false], - ['name' => 'bar', 'fn' => $fn, 'priority' => 0, 'once' => false], - ['name' => 'bar', 'fn' => $fn, 'priority' => 0, 'once' => false], - ], $o->listeners); - - $this->assertCount(2, $o->getEmitter()->listeners('foo')); - $this->assertCount(2, $o->getEmitter()->listeners('bar')); - } - - public function testRegistersEventsWithOnce() - { - $called = 0; - $fn = function () use (&$called) { $called++; }; - $o = new ObjectWithEvents(['foo' => ['fn' => $fn, 'once' => true]]); - $ev = $this->getMock('GuzzleHttp\Event\EventInterface'); - $o->getEmitter()->emit('foo', $ev); - $o->getEmitter()->emit('foo', $ev); - $this->assertEquals(1, $called); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testValidatesEvents() - { - $o = new ObjectWithEvents(['foo' => 'bar']); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Event/ProgressEventTest.php b/core/vendor/guzzlehttp/guzzle/tests/Event/ProgressEventTest.php deleted file mode 100644 index 664f8b6bba8e940b1a6697be2bc37d60bb030fd4..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Event/ProgressEventTest.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Event; - -use GuzzleHttp\Client; -use GuzzleHttp\Event\ProgressEvent; -use GuzzleHttp\Message\Request; -use GuzzleHttp\Transaction; - -/** - * @covers GuzzleHttp\Event\ProgressEvent - */ -class ProgressEventTest extends \PHPUnit_Framework_TestCase -{ - public function testContainsNumbers() - { - $t = new Transaction(new Client(), new Request('GET', 'http://a.com')); - $p = new ProgressEvent($t, 2, 1, 3, 0); - $this->assertSame($t->request, $p->getRequest()); - $this->assertSame($t->client, $p->getClient()); - $this->assertEquals(2, $p->downloadSize); - $this->assertEquals(1, $p->downloaded); - $this->assertEquals(3, $p->uploadSize); - $this->assertEquals(0, $p->uploaded); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Event/RequestEventsTest.php b/core/vendor/guzzlehttp/guzzle/tests/Event/RequestEventsTest.php deleted file mode 100644 index b3b96660fdba90ff281064220242c708fab73567..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Event/RequestEventsTest.php +++ /dev/null @@ -1,74 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Event; - -use GuzzleHttp\Event\RequestEvents; - -/** - * @covers GuzzleHttp\Event\RequestEvents - */ -class RequestEventsTest extends \PHPUnit_Framework_TestCase -{ - public function prepareEventProvider() - { - $cb = function () {}; - - return [ - [[], ['complete'], $cb, ['complete' => [$cb]]], - [ - ['complete' => $cb], - ['complete'], - $cb, - ['complete' => [$cb, $cb]] - ], - [ - ['prepare' => []], - ['error', 'foo'], - $cb, - [ - 'prepare' => [], - 'error' => [$cb], - 'foo' => [$cb] - ] - ], - [ - ['prepare' => []], - ['prepare'], - $cb, - [ - 'prepare' => [$cb] - ] - ], - [ - ['prepare' => ['fn' => $cb]], - ['prepare'], $cb, - [ - 'prepare' => [ - ['fn' => $cb], - $cb - ] - ] - ], - ]; - } - - /** - * @dataProvider prepareEventProvider - */ - public function testConvertsEventArrays( - array $in, - array $events, - $add, - array $out - ) { - $result = RequestEvents::convertEventArray($in, $events, $add); - $this->assertEquals($out, $result); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testValidatesEventFormat() - { - RequestEvents::convertEventArray(['foo' => false], ['foo'], []); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Exception/ParseExceptionTest.php b/core/vendor/guzzlehttp/guzzle/tests/Exception/ParseExceptionTest.php deleted file mode 100644 index 4ff9bfb6cecedf5ec707c0f0625dbbbee29c8d50..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Exception/ParseExceptionTest.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php - -namespace GuzzleHttp\Tests\Event; - -use GuzzleHttp\Exception\ParseException; -use GuzzleHttp\Message\Response; - -/** - * @covers GuzzleHttp\Exception\ParseException - */ -class ParseExceptionTest extends \PHPUnit_Framework_TestCase -{ - public function testHasResponse() - { - $res = new Response(200); - $e = new ParseException('foo', $res); - $this->assertSame($res, $e->getResponse()); - $this->assertEquals('foo', $e->getMessage()); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Exception/RequestExceptionTest.php b/core/vendor/guzzlehttp/guzzle/tests/Exception/RequestExceptionTest.php deleted file mode 100644 index bea9077bff14cd32b9bdeef3cdd62639a2e15a92..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Exception/RequestExceptionTest.php +++ /dev/null @@ -1,83 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Event; - -use GuzzleHttp\Exception\RequestException; -use GuzzleHttp\Message\Request; -use GuzzleHttp\Message\Response; -use GuzzleHttp\Ring\Exception\ConnectException; - -/** - * @covers GuzzleHttp\Exception\RequestException - */ -class RequestExceptionTest extends \PHPUnit_Framework_TestCase -{ - public function testHasRequestAndResponse() - { - $req = new Request('GET', '/'); - $res = new Response(200); - $e = new RequestException('foo', $req, $res); - $this->assertSame($req, $e->getRequest()); - $this->assertSame($res, $e->getResponse()); - $this->assertTrue($e->hasResponse()); - $this->assertEquals('foo', $e->getMessage()); - } - - public function testCreatesGenerateException() - { - $e = RequestException::create(new Request('GET', '/')); - $this->assertEquals('Error completing request', $e->getMessage()); - $this->assertInstanceOf('GuzzleHttp\Exception\RequestException', $e); - } - - public function testCreatesClientErrorResponseException() - { - $e = RequestException::create(new Request('GET', '/'), new Response(400)); - $this->assertEquals( - 'Client error response [url] / [status code] 400 [reason phrase] Bad Request', - $e->getMessage() - ); - $this->assertInstanceOf('GuzzleHttp\Exception\ClientException', $e); - } - - public function testCreatesServerErrorResponseException() - { - $e = RequestException::create(new Request('GET', '/'), new Response(500)); - $this->assertEquals( - 'Server error response [url] / [status code] 500 [reason phrase] Internal Server Error', - $e->getMessage() - ); - $this->assertInstanceOf('GuzzleHttp\Exception\ServerException', $e); - } - - public function testCreatesGenericErrorResponseException() - { - $e = RequestException::create(new Request('GET', '/'), new Response(600)); - $this->assertEquals( - 'Unsuccessful response [url] / [status code] 600 [reason phrase] ', - $e->getMessage() - ); - $this->assertInstanceOf('GuzzleHttp\Exception\RequestException', $e); - } - - public function testHasStatusCodeAsExceptionCode() { - $e = RequestException::create(new Request('GET', '/'), new Response(442)); - $this->assertEquals(442, $e->getCode()); - } - - public function testWrapsRequestExceptions() - { - $e = new \Exception('foo'); - $r = new Request('GET', 'http://www.oo.com'); - $ex = RequestException::wrapException($r, $e); - $this->assertInstanceOf('GuzzleHttp\Exception\RequestException', $ex); - $this->assertSame($e, $ex->getPrevious()); - } - - public function testWrapsConnectExceptions() - { - $e = new ConnectException('foo'); - $r = new Request('GET', 'http://www.oo.com'); - $ex = RequestException::wrapException($r, $e); - $this->assertInstanceOf('GuzzleHttp\Exception\ConnectException', $ex); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Exception/XmlParseExceptionTest.php b/core/vendor/guzzlehttp/guzzle/tests/Exception/XmlParseExceptionTest.php deleted file mode 100644 index 51b97425ec6911b1aed099043b31b43fa83ff859..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Exception/XmlParseExceptionTest.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php - -namespace GuzzleHttp\tests\Exception; - -use GuzzleHttp\Exception\XmlParseException; - -/** - * @covers GuzzleHttp\Exception\XmlParseException - */ -class XmlParseExceptionTest extends \PHPUnit_Framework_TestCase -{ - public function testHasError() - { - $error = new \LibXMLError(); - $e = new XmlParseException('foo', null, null, $error); - $this->assertSame($error, $e->getError()); - $this->assertEquals('foo', $e->getMessage()); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/IntegrationTest.php b/core/vendor/guzzlehttp/guzzle/tests/IntegrationTest.php deleted file mode 100644 index e26c64d9f8f6cd1947b7aec81d6cd26d40cebaf5..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/IntegrationTest.php +++ /dev/null @@ -1,123 +0,0 @@ -<?php -namespace GuzzleHttp\Tests; - -use GuzzleHttp\Client; -use GuzzleHttp\Event\AbstractTransferEvent; -use GuzzleHttp\Event\CompleteEvent; -use GuzzleHttp\Event\EndEvent; -use GuzzleHttp\Event\ErrorEvent; -use GuzzleHttp\Exception\RequestException; -use GuzzleHttp\Message\Response; -use GuzzleHttp\Pool; - -class IntegrationTest extends \PHPUnit_Framework_TestCase -{ - /** - * @issue https://github.com/guzzle/guzzle/issues/867 - */ - public function testDoesNotFailInEventSystemForNetworkError() - { - $c = new Client(); - $r = $c->createRequest( - 'GET', - Server::$url, - [ - 'timeout' => 1, - 'connect_timeout' => 1, - 'proxy' => 'http://127.0.0.1:123/foo' - ] - ); - - $events = []; - $fn = function(AbstractTransferEvent $event) use (&$events) { - $events[] = [ - get_class($event), - $event->hasResponse(), - $event->getResponse() - ]; - }; - - $pool = new Pool($c, [$r], [ - 'error' => $fn, - 'end' => $fn - ]); - - $pool->wait(); - - $this->assertCount(2, $events); - $this->assertEquals('GuzzleHttp\Event\ErrorEvent', $events[0][0]); - $this->assertFalse($events[0][1]); - $this->assertNull($events[0][2]); - - $this->assertEquals('GuzzleHttp\Event\EndEvent', $events[1][0]); - $this->assertFalse($events[1][1]); - $this->assertNull($events[1][2]); - } - - /** - * @issue https://github.com/guzzle/guzzle/issues/866 - */ - public function testProperyGetsTransferStats() - { - $transfer = []; - Server::enqueue([new Response(200)]); - $c = new Client(); - $response = $c->get(Server::$url . '/foo', [ - 'events' => [ - 'end' => function (EndEvent $e) use (&$transfer) { - $transfer = $e->getTransferInfo(); - } - ] - ]); - $this->assertEquals(Server::$url . '/foo', $response->getEffectiveUrl()); - $this->assertNotEmpty($transfer); - $this->assertArrayHasKey('url', $transfer); - } - - public function testNestedFutureResponsesAreResolvedWhenSending() - { - $c = new Client(); - $total = 3; - Server::enqueue([ - new Response(200), - new Response(201), - new Response(202) - ]); - $c->getEmitter()->on( - 'complete', - function (CompleteEvent $e) use (&$total) { - if (--$total) { - $e->retry(); - } - } - ); - $response = $c->get(Server::$url); - $this->assertEquals(202, $response->getStatusCode()); - $this->assertEquals('GuzzleHttp\Message\Response', get_class($response)); - } - - public function testNestedFutureErrorsAreResolvedWhenSending() - { - $c = new Client(); - $total = 3; - Server::enqueue([ - new Response(500), - new Response(501), - new Response(502) - ]); - $c->getEmitter()->on( - 'error', - function (ErrorEvent $e) use (&$total) { - if (--$total) { - $e->retry(); - } - } - ); - try { - $c->get(Server::$url); - $this->fail('Did not throw!'); - } catch (RequestException $e) { - $this->assertEquals(502, $e->getResponse()->getStatusCode()); - } - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Message/AbstractMessageTest.php b/core/vendor/guzzlehttp/guzzle/tests/Message/AbstractMessageTest.php deleted file mode 100644 index f02a576f5a0a4bcfeaa1bd0924c40651eb55d5b9..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Message/AbstractMessageTest.php +++ /dev/null @@ -1,269 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Message; - -use GuzzleHttp\Message\AbstractMessage; -use GuzzleHttp\Message\Request; -use GuzzleHttp\Message\Response; -use GuzzleHttp\Stream\Stream; - -/** - * @covers \GuzzleHttp\Message\AbstractMessage - */ -class AbstractMessageTest extends \PHPUnit_Framework_TestCase -{ - public function testHasProtocolVersion() - { - $m = new Request('GET', '/'); - $this->assertEquals(1.1, $m->getProtocolVersion()); - } - - public function testHasHeaders() - { - $m = new Request('GET', 'http://foo.com'); - $this->assertFalse($m->hasHeader('foo')); - $m->addHeader('foo', 'bar'); - $this->assertTrue($m->hasHeader('foo')); - } - - public function testInitializesMessageWithProtocolVersionOption() - { - $m = new Request('GET', '/', [], null, [ - 'protocol_version' => '10' - ]); - $this->assertEquals(10, $m->getProtocolVersion()); - } - - public function testHasBody() - { - $m = new Request('GET', 'http://foo.com'); - $this->assertNull($m->getBody()); - $s = Stream::factory('test'); - $m->setBody($s); - $this->assertSame($s, $m->getBody()); - $this->assertFalse($m->hasHeader('Content-Length')); - } - - public function testCanRemoveBodyBySettingToNullAndRemovesCommonBodyHeaders() - { - $m = new Request('GET', 'http://foo.com'); - $m->setBody(Stream::factory('foo')); - $m->setHeader('Content-Length', 3); - $m->setHeader('Transfer-Encoding', 'chunked'); - $m->setBody(null); - $this->assertNull($m->getBody()); - $this->assertFalse($m->hasHeader('Content-Length')); - $this->assertFalse($m->hasHeader('Transfer-Encoding')); - } - - public function testCastsToString() - { - $m = new Request('GET', 'http://foo.com'); - $m->setHeader('foo', 'bar'); - $m->setBody(Stream::factory('baz')); - $this->assertEquals("GET / HTTP/1.1\r\nHost: foo.com\r\nfoo: bar\r\n\r\nbaz", (string) $m); - } - - public function parseParamsProvider() - { - $res1 = array( - array( - '<http:/.../front.jpeg>', - 'rel' => 'front', - 'type' => 'image/jpeg', - ), - array( - '<http://.../back.jpeg>', - 'rel' => 'back', - 'type' => 'image/jpeg', - ), - ); - - return array( - array( - '<http:/.../front.jpeg>; rel="front"; type="image/jpeg", <http://.../back.jpeg>; rel=back; type="image/jpeg"', - $res1 - ), - array( - '<http:/.../front.jpeg>; rel="front"; type="image/jpeg",<http://.../back.jpeg>; rel=back; type="image/jpeg"', - $res1 - ), - array( - 'foo="baz"; bar=123, boo, test="123", foobar="foo;bar"', - array( - array('foo' => 'baz', 'bar' => '123'), - array('boo'), - array('test' => '123'), - array('foobar' => 'foo;bar') - ) - ), - array( - '<http://.../side.jpeg?test=1>; rel="side"; type="image/jpeg",<http://.../side.jpeg?test=2>; rel=side; type="image/jpeg"', - array( - array('<http://.../side.jpeg?test=1>', 'rel' => 'side', 'type' => 'image/jpeg'), - array('<http://.../side.jpeg?test=2>', 'rel' => 'side', 'type' => 'image/jpeg') - ) - ), - array( - '', - array() - ) - ); - } - - /** - * @dataProvider parseParamsProvider - */ - public function testParseParams($header, $result) - { - $request = new Request('GET', '/', ['foo' => $header]); - $this->assertEquals($result, Request::parseHeader($request, 'foo')); - } - - public function testAddsHeadersWhenNotPresent() - { - $h = new Request('GET', 'http://foo.com'); - $h->addHeader('foo', 'bar'); - $this->assertInternalType('string', $h->getHeader('foo')); - $this->assertEquals('bar', $h->getHeader('foo')); - } - - public function testAddsHeadersWhenPresentSameCase() - { - $h = new Request('GET', 'http://foo.com'); - $h->addHeader('foo', 'bar'); - $h->addHeader('foo', 'baz'); - $this->assertEquals('bar, baz', $h->getHeader('foo')); - $this->assertEquals(['bar', 'baz'], $h->getHeaderAsArray('foo')); - } - - public function testAddsMultipleHeaders() - { - $h = new Request('GET', 'http://foo.com'); - $h->addHeaders([ - 'foo' => ' bar', - 'baz' => [' bam ', 'boo'] - ]); - $this->assertEquals([ - 'foo' => ['bar'], - 'baz' => ['bam', 'boo'], - 'Host' => ['foo.com'] - ], $h->getHeaders()); - } - - public function testAddsHeadersWhenPresentDifferentCase() - { - $h = new Request('GET', 'http://foo.com'); - $h->addHeader('Foo', 'bar'); - $h->addHeader('fOO', 'baz'); - $this->assertEquals('bar, baz', $h->getHeader('foo')); - } - - public function testAddsHeadersWithArray() - { - $h = new Request('GET', 'http://foo.com'); - $h->addHeader('Foo', ['bar', 'baz']); - $this->assertEquals('bar, baz', $h->getHeader('foo')); - } - - public function testGetHeadersReturnsAnArrayOfOverTheWireHeaderValues() - { - $h = new Request('GET', 'http://foo.com'); - $h->addHeader('foo', 'bar'); - $h->addHeader('Foo', 'baz'); - $h->addHeader('boO', 'test'); - $result = $h->getHeaders(); - $this->assertInternalType('array', $result); - $this->assertArrayHasKey('Foo', $result); - $this->assertArrayNotHasKey('foo', $result); - $this->assertArrayHasKey('boO', $result); - $this->assertEquals(['bar', 'baz'], $result['Foo']); - $this->assertEquals(['test'], $result['boO']); - } - - public function testSetHeaderOverwritesExistingValues() - { - $h = new Request('GET', 'http://foo.com'); - $h->setHeader('foo', 'bar'); - $this->assertEquals('bar', $h->getHeader('foo')); - $h->setHeader('Foo', 'baz'); - $this->assertEquals('baz', $h->getHeader('foo')); - $this->assertArrayHasKey('Foo', $h->getHeaders()); - } - - public function testSetHeaderOverwritesExistingValuesUsingHeaderArray() - { - $h = new Request('GET', 'http://foo.com'); - $h->setHeader('foo', ['bar']); - $this->assertEquals('bar', $h->getHeader('foo')); - } - - public function testSetHeaderOverwritesExistingValuesUsingArray() - { - $h = new Request('GET', 'http://foo.com'); - $h->setHeader('foo', ['bar']); - $this->assertEquals('bar', $h->getHeader('foo')); - } - - public function testSetHeadersOverwritesAllHeaders() - { - $h = new Request('GET', 'http://foo.com'); - $h->setHeader('foo', 'bar'); - $h->setHeaders(['foo' => 'a', 'boo' => 'b']); - $this->assertEquals(['foo' => ['a'], 'boo' => ['b']], $h->getHeaders()); - } - - public function testChecksIfCaseInsensitiveHeaderIsPresent() - { - $h = new Request('GET', 'http://foo.com'); - $h->setHeader('foo', 'bar'); - $this->assertTrue($h->hasHeader('foo')); - $this->assertTrue($h->hasHeader('Foo')); - $h->setHeader('fOo', 'bar'); - $this->assertTrue($h->hasHeader('Foo')); - } - - public function testRemovesHeaders() - { - $h = new Request('GET', 'http://foo.com'); - $h->setHeader('foo', 'bar'); - $h->removeHeader('foo'); - $this->assertFalse($h->hasHeader('foo')); - $h->setHeader('Foo', 'bar'); - $h->removeHeader('FOO'); - $this->assertFalse($h->hasHeader('foo')); - } - - public function testReturnsCorrectTypeWhenMissing() - { - $h = new Request('GET', 'http://foo.com'); - $this->assertInternalType('string', $h->getHeader('foo')); - $this->assertInternalType('array', $h->getHeaderAsArray('foo')); - } - - public function testSetsIntegersAndFloatsAsHeaders() - { - $h = new Request('GET', 'http://foo.com'); - $h->setHeader('foo', 10); - $h->setHeader('bar', 10.5); - $h->addHeader('foo', 10); - $h->addHeader('bar', 10.5); - $this->assertSame('10, 10', $h->getHeader('foo')); - $this->assertSame('10.5, 10.5', $h->getHeader('bar')); - } - - public function testGetsResponseStartLine() - { - $m = new Response(200); - $this->assertEquals('HTTP/1.1 200 OK', Response::getStartLine($m)); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testThrowsWhenMessageIsUnknown() - { - $m = $this->getMockBuilder('GuzzleHttp\Message\AbstractMessage') - ->getMockForAbstractClass(); - AbstractMessage::getStartLine($m); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Message/FutureResponseTest.php b/core/vendor/guzzlehttp/guzzle/tests/Message/FutureResponseTest.php deleted file mode 100644 index 771631d56bb7da64f4910bc504714fa08942d5f6..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Message/FutureResponseTest.php +++ /dev/null @@ -1,160 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Message; - -use GuzzleHttp\Message\FutureResponse; -use GuzzleHttp\Message\Response; -use GuzzleHttp\Stream\Stream; -use React\Promise\Deferred; -use GuzzleHttp\Tests\Subscriber\MockTest; - -class FutureResponseTest extends \PHPUnit_Framework_TestCase -{ - /** - * @expectedException \RuntimeException - * @expectedExceptionMessage Class has no foo property - */ - public function testValidatesMagicMethod() - { - $f = MockTest::createFuture(function () {}); - $f->foo; - } - - public function testDoesTheSameAsResponseWhenDereferenced() - { - $str = Stream::factory('foo'); - $response = new Response(200, ['Foo' => 'bar'], $str); - $future = MockTest::createFuture(function () use ($response) { - return $response; - }); - $this->assertFalse($this->readAttribute($future, 'isRealized')); - $this->assertEquals(200, $future->getStatusCode()); - $this->assertTrue($this->readAttribute($future, 'isRealized')); - // Deref again does nothing. - $future->wait(); - $this->assertTrue($this->readAttribute($future, 'isRealized')); - $this->assertEquals('bar', $future->getHeader('Foo')); - $this->assertEquals(['bar'], $future->getHeaderAsarray('Foo')); - $this->assertSame($response->getHeaders(), $future->getHeaders()); - $this->assertSame( - $response->getBody(), - $future->getBody() - ); - $this->assertSame( - $response->getProtocolVersion(), - $future->getProtocolVersion() - ); - $this->assertSame( - $response->getEffectiveUrl(), - $future->getEffectiveUrl() - ); - $future->setEffectiveUrl('foo'); - $this->assertEquals('foo', $response->getEffectiveUrl()); - $this->assertSame( - $response->getReasonPhrase(), - $future->getReasonPhrase() - ); - - $this->assertTrue($future->hasHeader('foo')); - - $future->removeHeader('Foo'); - $this->assertFalse($future->hasHeader('foo')); - $this->assertFalse($response->hasHeader('foo')); - - $future->setBody(Stream::factory('true')); - $this->assertEquals('true', (string) $response->getBody()); - $this->assertTrue($future->json()); - $this->assertSame((string) $response, (string) $future); - - $future->setBody(Stream::factory('<a><b>c</b></a>')); - $this->assertEquals('c', (string) $future->xml()->b); - - $future->addHeader('a', 'b'); - $this->assertEquals('b', $future->getHeader('a')); - - $future->addHeaders(['a' => '2']); - $this->assertEquals('b, 2', $future->getHeader('a')); - - $future->setHeader('a', '2'); - $this->assertEquals('2', $future->getHeader('a')); - - $future->setHeaders(['a' => '3']); - $this->assertEquals(['a' => ['3']], $future->getHeaders()); - } - - public function testCanDereferenceManually() - { - $response = new Response(200, ['Foo' => 'bar']); - $future = MockTest::createFuture(function () use ($response) { - return $response; - }); - $this->assertSame($response, $future->wait()); - $this->assertTrue($this->readAttribute($future, 'isRealized')); - } - - public function testCanCancel() - { - $c = false; - $deferred = new Deferred(); - $future = new FutureResponse( - $deferred->promise(), - function () {}, - function () use (&$c) { - $c = true; - return true; - } - ); - - $this->assertFalse($this->readAttribute($future, 'isRealized')); - $future->cancel(); - $this->assertTrue($this->readAttribute($future, 'isRealized')); - $future->cancel(); - } - - public function testCanCancelButReturnsFalseForNoCancelFunction() - { - $future = MockTest::createFuture(function () {}); - $future->cancel(); - $this->assertTrue($this->readAttribute($future, 'isRealized')); - } - - /** - * @expectedException \GuzzleHttp\Ring\Exception\CancelledFutureAccessException - */ - public function testAccessingCancelledResponseThrows() - { - $future = MockTest::createFuture(function () {}); - $future->cancel(); - $future->getStatusCode(); - } - - public function testExceptionInToStringTriggersError() - { - $future = MockTest::createFuture(function () { - throw new \Exception('foo'); - }); - $err = ''; - set_error_handler(function () use (&$err) { - $err = func_get_args()[1]; - }); - echo $future; - restore_error_handler(); - $this->assertContains('foo', $err); - } - - public function testProxiesSetters() - { - $str = Stream::factory('foo'); - $response = new Response(200, ['Foo' => 'bar'], $str); - $future = MockTest::createFuture(function () use ($response) { - return $response; - }); - - $future->setStatusCode(202); - $this->assertEquals(202, $future->getStatusCode()); - $this->assertEquals(202, $response->getStatusCode()); - - $future->setReasonPhrase('foo'); - $this->assertEquals('foo', $future->getReasonPhrase()); - $this->assertEquals('foo', $response->getReasonPhrase()); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Message/MessageFactoryTest.php b/core/vendor/guzzlehttp/guzzle/tests/Message/MessageFactoryTest.php deleted file mode 100644 index aa2e45e021781518fc05e3d883c9ab3e56b16c5f..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Message/MessageFactoryTest.php +++ /dev/null @@ -1,601 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Message; - -use GuzzleHttp\Client; -use GuzzleHttp\Message\RequestInterface; -use GuzzleHttp\Post\PostFile; -use GuzzleHttp\Message\Response; -use GuzzleHttp\Message\MessageFactory; -use GuzzleHttp\Subscriber\Cookie; -use GuzzleHttp\Cookie\CookieJar; -use GuzzleHttp\Query; -use GuzzleHttp\Stream\Stream; -use GuzzleHttp\Subscriber\History; -use GuzzleHttp\Subscriber\Mock; - -/** - * @covers GuzzleHttp\Message\MessageFactory - */ -class MessageFactoryTest extends \PHPUnit_Framework_TestCase -{ - public function testCreatesResponses() - { - $f = new MessageFactory(); - $response = $f->createResponse(200, ['foo' => 'bar'], 'test', [ - 'protocol_version' => 1.0 - ]); - $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals(['foo' => ['bar']], $response->getHeaders()); - $this->assertEquals('test', $response->getBody()); - $this->assertEquals(1.0, $response->getProtocolVersion()); - } - - public function testCreatesRequestFromMessage() - { - $f = new MessageFactory(); - $req = $f->fromMessage("GET / HTTP/1.1\r\nBaz: foo\r\n\r\n"); - $this->assertEquals('GET', $req->getMethod()); - $this->assertEquals('/', $req->getPath()); - $this->assertEquals('foo', $req->getHeader('Baz')); - $this->assertNull($req->getBody()); - } - - public function testCreatesRequestFromMessageWithBody() - { - $req = (new MessageFactory())->fromMessage("GET / HTTP/1.1\r\nBaz: foo\r\n\r\ntest"); - $this->assertEquals('test', $req->getBody()); - } - - public function testCreatesRequestWithPostBody() - { - $req = (new MessageFactory())->createRequest('GET', 'http://www.foo.com', ['body' => ['abc' => '123']]); - $this->assertEquals('abc=123', $req->getBody()); - } - - public function testCreatesRequestWithPostBodyScalars() - { - $req = (new MessageFactory())->createRequest( - 'GET', - 'http://www.foo.com', - ['body' => [ - 'abc' => true, - '123' => false, - 'foo' => null, - 'baz' => 10, - 'bam' => 1.5, - 'boo' => [1]] - ] - ); - $this->assertEquals( - 'abc=1&123=&foo&baz=10&bam=1.5&boo%5B0%5D=1', - (string) $req->getBody() - ); - } - - public function testCreatesRequestWithPostBodyAndPostFiles() - { - $pf = fopen(__FILE__, 'r'); - $pfi = new PostFile('ghi', 'abc', __FILE__); - $req = (new MessageFactory())->createRequest('GET', 'http://www.foo.com', [ - 'body' => [ - 'abc' => '123', - 'def' => $pf, - 'ghi' => $pfi - ] - ]); - $this->assertInstanceOf('GuzzleHttp\Post\PostBody', $req->getBody()); - $s = (string) $req; - $this->assertContains('testCreatesRequestWithPostBodyAndPostFiles', $s); - $this->assertContains('multipart/form-data', $s); - $this->assertTrue(in_array($pfi, $req->getBody()->getFiles(), true)); - } - - public function testCreatesResponseFromMessage() - { - $response = (new MessageFactory())->fromMessage("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->getHeader('Content-Length')); - $this->assertEquals('test', $response->getBody(true)); - } - - public function testCanCreateHeadResponses() - { - $response = (new MessageFactory())->fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\n"); - $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals('OK', $response->getReasonPhrase()); - $this->assertEquals(null, $response->getBody()); - $this->assertEquals('4', $response->getHeader('Content-Length')); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testFactoryRequiresMessageForRequest() - { - (new MessageFactory())->fromMessage(''); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage foo - */ - public function testValidatesOptionsAreImplemented() - { - (new MessageFactory())->createRequest('GET', 'http://test.com', ['foo' => 'bar']); - } - - public function testOptionsAddsRequestOptions() - { - $request = (new MessageFactory())->createRequest( - 'GET', 'http://test.com', ['config' => ['baz' => 'bar']] - ); - $this->assertEquals('bar', $request->getConfig()->get('baz')); - } - - public function testCanDisableRedirects() - { - $request = (new MessageFactory())->createRequest('GET', '/', ['allow_redirects' => false]); - $this->assertEmpty($request->getEmitter()->listeners('complete')); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testValidatesRedirects() - { - (new MessageFactory())->createRequest('GET', '/', ['allow_redirects' => 'foo']); - } - - public function testCanEnableStrictRedirectsAndSpecifyMax() - { - $request = (new MessageFactory())->createRequest('GET', '/', [ - 'allow_redirects' => ['max' => 10, 'strict' => true] - ]); - $this->assertTrue($request->getConfig()['redirect']['strict']); - $this->assertEquals(10, $request->getConfig()['redirect']['max']); - } - - public function testCanAddCookiesFromHash() - { - $request = (new MessageFactory())->createRequest('GET', 'http://www.test.com/', [ - 'cookies' => ['Foo' => 'Bar'] - ]); - $cookies = null; - foreach ($request->getEmitter()->listeners('before') as $l) { - if ($l[0] instanceof Cookie) { - $cookies = $l[0]; - break; - } - } - if (!$cookies) { - $this->fail('Did not add cookie listener'); - } else { - $this->assertCount(1, $cookies->getCookieJar()); - } - } - - public function testAddsCookieUsingTrue() - { - $factory = new MessageFactory(); - $request1 = $factory->createRequest('GET', '/', ['cookies' => true]); - $request2 = $factory->createRequest('GET', '/', ['cookies' => true]); - $listeners = function ($r) { - return array_filter($r->getEmitter()->listeners('before'), function ($l) { - return $l[0] instanceof Cookie; - }); - }; - $this->assertSame($listeners($request1), $listeners($request2)); - } - - public function testAddsCookieFromCookieJar() - { - $jar = new CookieJar(); - $request = (new MessageFactory())->createRequest('GET', '/', ['cookies' => $jar]); - foreach ($request->getEmitter()->listeners('before') as $l) { - if ($l[0] instanceof Cookie) { - $this->assertSame($jar, $l[0]->getCookieJar()); - } - } - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testValidatesCookies() - { - (new MessageFactory())->createRequest('GET', '/', ['cookies' => 'baz']); - } - - public function testCanAddQuery() - { - $request = (new MessageFactory())->createRequest('GET', 'http://foo.com', [ - 'query' => ['Foo' => 'Bar'] - ]); - $this->assertEquals('Bar', $request->getQuery()->get('Foo')); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testValidatesQuery() - { - (new MessageFactory())->createRequest('GET', 'http://foo.com', [ - 'query' => 'foo' - ]); - } - - public function testCanSetDefaultQuery() - { - $request = (new MessageFactory())->createRequest('GET', 'http://foo.com?test=abc', [ - 'query' => ['Foo' => 'Bar', 'test' => 'def'] - ]); - $this->assertEquals('Bar', $request->getQuery()->get('Foo')); - $this->assertEquals('abc', $request->getQuery()->get('test')); - } - - public function testCanSetDefaultQueryWithObject() - { - $request = (new MessageFactory)->createRequest( - 'GET', - 'http://foo.com?test=abc', [ - 'query' => new Query(['Foo' => 'Bar', 'test' => 'def']) - ] - ); - $this->assertEquals('Bar', $request->getQuery()->get('Foo')); - $this->assertEquals('abc', $request->getQuery()->get('test')); - } - - public function testCanAddBasicAuth() - { - $request = (new MessageFactory())->createRequest('GET', 'http://foo.com', [ - 'auth' => ['michael', 'test'] - ]); - $this->assertTrue($request->hasHeader('Authorization')); - } - - public function testCanAddDigestAuth() - { - $request = (new MessageFactory())->createRequest('GET', 'http://foo.com', [ - 'auth' => ['michael', 'test', 'digest'] - ]); - $this->assertEquals('michael:test', $request->getConfig()->getPath('curl/' . CURLOPT_USERPWD)); - $this->assertEquals(CURLAUTH_DIGEST, $request->getConfig()->getPath('curl/' . CURLOPT_HTTPAUTH)); - } - - public function testCanDisableAuth() - { - $request = (new MessageFactory())->createRequest('GET', 'http://foo.com', [ - 'auth' => false - ]); - $this->assertFalse($request->hasHeader('Authorization')); - } - - public function testCanSetCustomAuth() - { - $request = (new MessageFactory())->createRequest('GET', 'http://foo.com', [ - 'auth' => 'foo' - ]); - $this->assertEquals('foo', $request->getConfig()['auth']); - } - - public function testCanAddEvents() - { - $foo = null; - $client = new Client(); - $client->getEmitter()->attach(new Mock([new Response(200)])); - $client->get('http://test.com', [ - 'events' => [ - 'before' => function () use (&$foo) { $foo = true; } - ] - ]); - $this->assertTrue($foo); - } - - public function testCanAddEventsWithPriority() - { - $foo = null; - $client = new Client(); - $client->getEmitter()->attach(new Mock(array(new Response(200)))); - $request = $client->createRequest('GET', 'http://test.com', [ - 'events' => [ - 'before' => [ - 'fn' => function () use (&$foo) { $foo = true; }, - 'priority' => 123 - ] - ] - ]); - $client->send($request); - $this->assertTrue($foo); - $l = $this->readAttribute($request->getEmitter(), 'listeners'); - $this->assertArrayHasKey(123, $l['before']); - } - - public function testCanAddEventsOnce() - { - $foo = 0; - $client = new Client(); - $client->getEmitter()->attach(new Mock([ - new Response(200), - new Response(200), - ])); - $fn = function () use (&$foo) { ++$foo; }; - $request = $client->createRequest('GET', 'http://test.com', [ - 'events' => ['before' => ['fn' => $fn, 'once' => true]] - ]); - $client->send($request); - $this->assertEquals(1, $foo); - $client->send($request); - $this->assertEquals(1, $foo); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testValidatesEventContainsFn() - { - $client = new Client(['base_url' => 'http://test.com']); - $client->createRequest('GET', '/', ['events' => ['before' => ['foo' => 'bar']]]); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testValidatesEventIsArray() - { - $client = new Client(['base_url' => 'http://test.com']); - $client->createRequest('GET', '/', ['events' => ['before' => '123']]); - } - - public function testCanAddSubscribers() - { - $mock = new Mock([new Response(200)]); - $client = new Client(); - $client->getEmitter()->attach($mock); - $request = $client->get('http://test.com', ['subscribers' => [$mock]]); - } - - public function testCanDisableExceptions() - { - $client = new Client(); - $this->assertEquals(500, $client->get('http://test.com', [ - 'subscribers' => [new Mock([new Response(500)])], - 'exceptions' => false - ])->getStatusCode()); - } - - public function testCanChangeSaveToLocation() - { - $saveTo = Stream::factory(); - $request = (new MessageFactory())->createRequest('GET', '/', ['save_to' => $saveTo]); - $this->assertSame($saveTo, $request->getConfig()->get('save_to')); - } - - public function testCanSetProxy() - { - $request = (new MessageFactory())->createRequest('GET', '/', ['proxy' => '192.168.16.121']); - $this->assertEquals('192.168.16.121', $request->getConfig()->get('proxy')); - } - - public function testCanSetHeadersOption() - { - $request = (new MessageFactory())->createRequest('GET', '/', ['headers' => ['Foo' => 'Bar']]); - $this->assertEquals('Bar', (string) $request->getHeader('Foo')); - } - - public function testCanSetHeaders() - { - $request = (new MessageFactory())->createRequest('GET', '/', [ - 'headers' => ['Foo' => ['Baz', 'Bar'], 'Test' => '123'] - ]); - $this->assertEquals('Baz, Bar', $request->getHeader('Foo')); - $this->assertEquals('123', $request->getHeader('Test')); - } - - public function testCanSetTimeoutOption() - { - $request = (new MessageFactory())->createRequest('GET', '/', ['timeout' => 1.5]); - $this->assertEquals(1.5, $request->getConfig()->get('timeout')); - } - - public function testCanSetConnectTimeoutOption() - { - $request = (new MessageFactory())->createRequest('GET', '/', ['connect_timeout' => 1.5]); - $this->assertEquals(1.5, $request->getConfig()->get('connect_timeout')); - } - - public function testCanSetDebug() - { - $request = (new MessageFactory())->createRequest('GET', '/', ['debug' => true]); - $this->assertTrue($request->getConfig()->get('debug')); - } - - public function testCanSetVerifyToOff() - { - $request = (new MessageFactory())->createRequest('GET', '/', ['verify' => false]); - $this->assertFalse($request->getConfig()->get('verify')); - } - - public function testCanSetVerifyToOn() - { - $request = (new MessageFactory())->createRequest('GET', '/', ['verify' => true]); - $this->assertTrue($request->getConfig()->get('verify')); - } - - public function testCanSetVerifyToPath() - { - $request = (new MessageFactory())->createRequest('GET', '/', ['verify' => '/foo.pem']); - $this->assertEquals('/foo.pem', $request->getConfig()->get('verify')); - } - - public function inputValidation() - { - return array_map(function ($option) { return array($option); }, array( - 'headers', 'events', 'subscribers', 'params' - )); - } - - /** - * @dataProvider inputValidation - * @expectedException \InvalidArgumentException - */ - public function testValidatesInput($option) - { - (new MessageFactory())->createRequest('GET', '/', [$option => 'foo']); - } - - public function testCanAddSslKey() - { - $request = (new MessageFactory())->createRequest('GET', '/', ['ssl_key' => '/foo.pem']); - $this->assertEquals('/foo.pem', $request->getConfig()->get('ssl_key')); - } - - public function testCanAddSslKeyPassword() - { - $request = (new MessageFactory())->createRequest('GET', '/', ['ssl_key' => ['/foo.pem', 'bar']]); - $this->assertEquals(['/foo.pem', 'bar'], $request->getConfig()->get('ssl_key')); - } - - public function testCanAddSslCert() - { - $request = (new MessageFactory())->createRequest('GET', '/', ['cert' => '/foo.pem']); - $this->assertEquals('/foo.pem', $request->getConfig()->get('cert')); - } - - public function testCanAddSslCertPassword() - { - $request = (new MessageFactory())->createRequest('GET', '/', ['cert' => ['/foo.pem', 'bar']]); - $this->assertEquals(['/foo.pem', 'bar'], $request->getConfig()->get('cert')); - } - - public function testCreatesBodyWithoutZeroString() - { - $request = (new MessageFactory())->createRequest('PUT', 'http://test.com', ['body' => '0']); - $this->assertSame('0', (string) $request->getBody()); - } - - public function testCanSetProtocolVersion() - { - $request = (new MessageFactory())->createRequest('GET', 'http://t.com', ['version' => 1.0]); - $this->assertEquals(1.0, $request->getProtocolVersion()); - } - - public function testCanAddJsonData() - { - $request = (new MessageFactory())->createRequest('PUT', 'http://f.com', [ - 'json' => ['foo' => 'bar'] - ]); - $this->assertEquals( - 'application/json', - $request->getHeader('Content-Type') - ); - $this->assertEquals('{"foo":"bar"}', (string) $request->getBody()); - } - - public function testCanAddJsonDataToAPostRequest() - { - $request = (new MessageFactory())->createRequest('POST', 'http://f.com', [ - 'json' => ['foo' => 'bar'] - ]); - $this->assertEquals( - 'application/json', - $request->getHeader('Content-Type') - ); - $this->assertEquals('{"foo":"bar"}', (string) $request->getBody()); - } - - public function testCanAddJsonDataAndNotOverwriteContentType() - { - $request = (new MessageFactory())->createRequest('PUT', 'http://f.com', [ - 'headers' => ['Content-Type' => 'foo'], - 'json' => null - ]); - $this->assertEquals('foo', $request->getHeader('Content-Type')); - $this->assertEquals('null', (string) $request->getBody()); - } - - public function testCanUseCustomRequestOptions() - { - $c = false; - $f = new MessageFactory([ - 'foo' => function (RequestInterface $request, $value) use (&$c) { - $c = true; - $this->assertEquals('bar', $value); - } - ]); - - $f->createRequest('PUT', 'http://f.com', [ - 'headers' => ['Content-Type' => 'foo'], - 'foo' => 'bar' - ]); - - $this->assertTrue($c); - } - - /** - * @ticket https://github.com/guzzle/guzzle/issues/706 - */ - public function testDoesNotApplyPostBodyRightAway() - { - $request = (new MessageFactory())->createRequest('POST', 'http://f.cn', [ - 'body' => ['foo' => ['bar', 'baz']] - ]); - $this->assertEquals('', $request->getHeader('Content-Type')); - $this->assertEquals('', $request->getHeader('Content-Length')); - $request->getBody()->setAggregator(Query::duplicateAggregator()); - $request->getBody()->applyRequestHeaders($request); - $this->assertEquals('foo=bar&foo=baz', $request->getBody()); - } - - public function testCanForceMultipartUploadWithContentType() - { - $client = new Client(); - $client->getEmitter()->attach(new Mock([new Response(200)])); - $history = new History(); - $client->getEmitter()->attach($history); - $client->post('http://foo.com', [ - 'headers' => ['Content-Type' => 'multipart/form-data'], - 'body' => ['foo' => 'bar'] - ]); - $this->assertContains( - 'multipart/form-data; boundary=', - $history->getLastRequest()->getHeader('Content-Type') - ); - $this->assertContains( - "Content-Disposition: form-data; name=\"foo\"\r\n\r\nbar", - (string) $history->getLastRequest()->getBody() - ); - } - - public function testDecodeDoesNotForceAcceptHeader() - { - $request = (new MessageFactory())->createRequest('POST', 'http://f.cn', [ - 'decode_content' => true - ]); - $this->assertEquals('', $request->getHeader('Accept-Encoding')); - $this->assertTrue($request->getConfig()->get('decode_content')); - } - - public function testDecodeCanAddAcceptHeader() - { - $request = (new MessageFactory())->createRequest('POST', 'http://f.cn', [ - 'decode_content' => 'gzip' - ]); - $this->assertEquals('gzip', $request->getHeader('Accept-Encoding')); - $this->assertTrue($request->getConfig()->get('decode_content')); - } - - public function testCanDisableDecoding() - { - $request = (new MessageFactory())->createRequest('POST', 'http://f.cn', [ - 'decode_content' => false - ]); - $this->assertEquals('', $request->getHeader('Accept-Encoding')); - $this->assertNull($request->getConfig()->get('decode_content')); - } -} - -class ExtendedFactory extends MessageFactory -{ - protected function add_foo() {} -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Message/MessageParserTest.php b/core/vendor/guzzlehttp/guzzle/tests/Message/MessageParserTest.php deleted file mode 100644 index 0bcc9430f2903a7dde579d713e51ef1ea09a729f..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Message/MessageParserTest.php +++ /dev/null @@ -1,276 +0,0 @@ -<?php - -namespace GuzzleHttp\Tests\Message; - -use GuzzleHttp\Message\MessageParser; - -/** - * @covers \GuzzleHttp\Message\MessageParser - */ -class MessageParserTest extends \PHPUnit_Framework_TestCase -{ - /** - * @dataProvider requestProvider - */ - public function testParsesRequests($message, $parts) - { - $parser = new MessageParser(); - $this->compareRequestResults($parts, $parser->parseRequest($message)); - } - - /** - * @dataProvider responseProvider - */ - public function testParsesResponses($message, $parts) - { - $parser = new MessageParser(); - $this->compareResponseResults($parts, $parser->parseResponse($message)); - } - - public function testParsesRequestsWithMissingProtocol() - { - $parser = new MessageParser(); - $parts = $parser->parseRequest("GET /\r\nHost: Foo.com\r\n\r\n"); - $this->assertEquals('GET', $parts['method']); - $this->assertEquals('HTTP', $parts['protocol']); - $this->assertEquals('1.1', $parts['protocol_version']); - } - - public function testParsesRequestsWithMissingVersion() - { - $parser = new MessageParser(); - $parts = $parser->parseRequest("GET / HTTP\r\nHost: Foo.com\r\n\r\n"); - $this->assertEquals('GET', $parts['method']); - $this->assertEquals('HTTP', $parts['protocol']); - $this->assertEquals('1.1', $parts['protocol_version']); - } - - public function testParsesResponsesWithMissingReasonPhrase() - { - $parser = new MessageParser(); - $parts = $parser->parseResponse("HTTP/1.1 200\r\n\r\n"); - $this->assertEquals('200', $parts['code']); - $this->assertEquals('', $parts['reason_phrase']); - $this->assertEquals('HTTP', $parts['protocol']); - $this->assertEquals('1.1', $parts['protocol_version']); - } - - public function requestProvider() - { - $auth = base64_encode('michael:foo'); - - return array( - - // Empty request - array('', false), - - // Converts casing of request. Does not require host header. - array("GET / HTTP/1.1\r\n\r\n", array( - 'method' => 'GET', - 'protocol' => 'HTTP', - 'protocol_version' => '1.1', - 'request_url' => array( - 'scheme' => 'http', - 'host' => '', - 'port' => '', - 'path' => '/', - 'query' => '' - ), - 'headers' => array(), - 'body' => '' - )), - // Path and query string, multiple header values per header and case sensitive storage - array("HEAD /path?query=foo HTTP/1.0\r\nHost: example.com\r\nX-Foo: foo\r\nx-foo: Bar\r\nX-Foo: foo\r\nX-Foo: Baz\r\n\r\n", array( - 'method' => 'HEAD', - 'protocol' => 'HTTP', - 'protocol_version' => '1.0', - 'request_url' => array( - 'scheme' => 'http', - 'host' => 'example.com', - 'port' => '', - 'path' => '/path', - 'query' => 'query=foo' - ), - 'headers' => array( - 'Host' => 'example.com', - 'X-Foo' => array('foo', 'foo', 'Baz'), - 'x-foo' => 'Bar' - ), - 'body' => '' - )), - // Includes a body - array("PUT / HTTP/1.0\r\nhost: example.com:443\r\nContent-Length: 4\r\n\r\ntest", array( - 'method' => 'PUT', - 'protocol' => 'HTTP', - 'protocol_version' => '1.0', - 'request_url' => array( - 'scheme' => 'https', - 'host' => 'example.com', - 'port' => '443', - 'path' => '/', - 'query' => '' - ), - 'headers' => array( - 'host' => 'example.com:443', - 'Content-Length' => '4' - ), - 'body' => 'test' - )), - // Includes Authorization headers - array("GET / HTTP/1.1\r\nHost: example.com:8080\r\nAuthorization: Basic {$auth}\r\n\r\n", array( - 'method' => 'GET', - 'protocol' => 'HTTP', - 'protocol_version' => '1.1', - 'request_url' => array( - 'scheme' => 'http', - 'host' => 'example.com', - 'port' => '8080', - 'path' => '/', - 'query' => '' - ), - 'headers' => array( - 'Host' => 'example.com:8080', - 'Authorization' => "Basic {$auth}" - ), - 'body' => '' - )), - // Include authorization header - array("GET / HTTP/1.1\r\nHost: example.com:8080\r\nauthorization: Basic {$auth}\r\n\r\n", array( - 'method' => 'GET', - 'protocol' => 'HTTP', - 'protocol_version' => '1.1', - 'request_url' => array( - 'scheme' => 'http', - 'host' => 'example.com', - 'port' => '8080', - 'path' => '/', - 'query' => '' - ), - 'headers' => array( - 'Host' => 'example.com:8080', - 'authorization' => "Basic {$auth}" - ), - 'body' => '' - )), - ); - } - - public function responseProvider() - { - return array( - // Empty request - array('', false), - - array("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", array( - 'protocol' => 'HTTP', - 'protocol_version' => '1.1', - 'code' => '200', - 'reason_phrase' => 'OK', - 'headers' => array( - 'Content-Length' => 0 - ), - 'body' => '' - )), - array("HTTP/1.0 400 Bad Request\r\nContent-Length: 0\r\n\r\n", array( - 'protocol' => 'HTTP', - 'protocol_version' => '1.0', - 'code' => '400', - 'reason_phrase' => 'Bad Request', - 'headers' => array( - 'Content-Length' => 0 - ), - 'body' => '' - )), - array("HTTP/1.0 100 Continue\r\n\r\n", array( - 'protocol' => 'HTTP', - 'protocol_version' => '1.0', - 'code' => '100', - 'reason_phrase' => 'Continue', - 'headers' => array(), - 'body' => '' - )), - array("HTTP/1.1 204 No Content\r\nX-Foo: foo\r\nx-foo: Bar\r\nX-Foo: foo\r\n\r\n", array( - 'protocol' => 'HTTP', - 'protocol_version' => '1.1', - 'code' => '204', - 'reason_phrase' => 'No Content', - 'headers' => array( - 'X-Foo' => array('foo', 'foo'), - 'x-foo' => 'Bar' - ), - 'body' => '' - )), - array("HTTP/1.1 200 Ok that is great!\r\nContent-Length: 4\r\n\r\nTest", array( - 'protocol' => 'HTTP', - 'protocol_version' => '1.1', - 'code' => '200', - 'reason_phrase' => 'Ok that is great!', - 'headers' => array( - 'Content-Length' => 4 - ), - 'body' => 'Test' - )), - ); - } - - public function compareRequestResults($result, $expected) - { - if (!$result) { - $this->assertFalse($expected); - return; - } - - $this->assertEquals($result['method'], $expected['method']); - $this->assertEquals($result['protocol'], $expected['protocol']); - $this->assertEquals($result['protocol_version'], $expected['protocol_version']); - $this->assertEquals($result['request_url'], $expected['request_url']); - $this->assertEquals($result['body'], $expected['body']); - $this->compareHttpHeaders($result['headers'], $expected['headers']); - } - - public function compareResponseResults($result, $expected) - { - if (!$result) { - $this->assertFalse($expected); - return; - } - - $this->assertEquals($result['protocol'], $expected['protocol']); - $this->assertEquals($result['protocol_version'], $expected['protocol_version']); - $this->assertEquals($result['code'], $expected['code']); - $this->assertEquals($result['reason_phrase'], $expected['reason_phrase']); - $this->assertEquals($result['body'], $expected['body']); - $this->compareHttpHeaders($result['headers'], $expected['headers']); - } - - protected function normalizeHeaders($headers) - { - $normalized = array(); - foreach ($headers as $key => $value) { - $key = strtolower($key); - if (!isset($normalized[$key])) { - $normalized[$key] = $value; - } elseif (!is_array($normalized[$key])) { - $normalized[$key] = array($value); - } else { - $normalized[$key][] = $value; - } - } - - foreach ($normalized as $key => &$value) { - if (is_array($value)) { - sort($value); - } - } - - return $normalized; - } - - public function compareHttpHeaders($result, $expected) - { - // Aggregate all headers case-insensitively - $result = $this->normalizeHeaders($result); - $expected = $this->normalizeHeaders($expected); - $this->assertEquals($result, $expected); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Message/RequestTest.php b/core/vendor/guzzlehttp/guzzle/tests/Message/RequestTest.php deleted file mode 100644 index a6241a42926f3772e1802567a846acbd619cc2dc..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Message/RequestTest.php +++ /dev/null @@ -1,132 +0,0 @@ -<?php - -namespace GuzzleHttp\Tests\Message; - -use GuzzleHttp\Event\Emitter; -use GuzzleHttp\Message\Request; -use GuzzleHttp\Query; -use GuzzleHttp\Stream\Stream; - -/** - * @covers GuzzleHttp\Message\Request - */ -class RequestTest extends \PHPUnit_Framework_TestCase -{ - public function testConstructorInitializesMessage() - { - $r = new Request('PUT', '/test', ['test' => '123'], Stream::factory('foo')); - $this->assertEquals('PUT', $r->getMethod()); - $this->assertEquals('/test', $r->getUrl()); - $this->assertEquals('123', $r->getHeader('test')); - $this->assertEquals('foo', $r->getBody()); - } - - public function testConstructorInitializesMessageWithProtocolVersion() - { - $r = new Request('GET', '', [], null, ['protocol_version' => 10]); - $this->assertEquals(10, $r->getProtocolVersion()); - } - - public function testConstructorInitializesMessageWithEmitter() - { - $e = new Emitter(); - $r = new Request('GET', '', [], null, ['emitter' => $e]); - $this->assertSame($r->getEmitter(), $e); - } - - public function testCloneIsDeep() - { - $r = new Request('GET', '/test', ['foo' => 'baz'], Stream::factory('foo')); - $r2 = clone $r; - - $this->assertNotSame($r->getEmitter(), $r2->getEmitter()); - $this->assertEquals('foo', $r2->getBody()); - - $r->getConfig()->set('test', 123); - $this->assertFalse($r2->getConfig()->hasKey('test')); - - $r->setPath('/abc'); - $this->assertEquals('/test', $r2->getPath()); - } - - public function testCastsToString() - { - $r = new Request('GET', 'http://test.com/test', ['foo' => 'baz'], Stream::factory('body')); - $s = explode("\r\n", (string) $r); - $this->assertEquals("GET /test HTTP/1.1", $s[0]); - $this->assertContains('Host: test.com', $s); - $this->assertContains('foo: baz', $s); - $this->assertContains('', $s); - $this->assertContains('body', $s); - } - - public function testSettingUrlOverridesHostHeaders() - { - $r = new Request('GET', 'http://test.com/test'); - $r->setUrl('https://baz.com/bar'); - $this->assertEquals('baz.com', $r->getHost()); - $this->assertEquals('baz.com', $r->getHeader('Host')); - $this->assertEquals('/bar', $r->getPath()); - $this->assertEquals('https', $r->getScheme()); - } - - public function testQueryIsMutable() - { - $r = new Request('GET', 'http://www.foo.com?baz=bar'); - $this->assertEquals('baz=bar', $r->getQuery()); - $this->assertInstanceOf('GuzzleHttp\Query', $r->getQuery()); - $r->getQuery()->set('hi', 'there'); - $this->assertEquals('/?baz=bar&hi=there', $r->getResource()); - } - - public function testQueryCanChange() - { - $r = new Request('GET', 'http://www.foo.com?baz=bar'); - $r->setQuery(new Query(['foo' => 'bar'])); - $this->assertEquals('foo=bar', $r->getQuery()); - } - - public function testCanChangeMethod() - { - $r = new Request('GET', 'http://www.foo.com'); - $r->setMethod('put'); - $this->assertEquals('PUT', $r->getMethod()); - } - - public function testCanChangeSchemeWithPort() - { - $r = new Request('GET', 'http://www.foo.com:80'); - $r->setScheme('https'); - $this->assertEquals('https://www.foo.com', $r->getUrl()); - } - - public function testCanChangeScheme() - { - $r = new Request('GET', 'http://www.foo.com'); - $r->setScheme('https'); - $this->assertEquals('https://www.foo.com', $r->getUrl()); - } - - public function testCanChangeHost() - { - $r = new Request('GET', 'http://www.foo.com:222'); - $r->setHost('goo'); - $this->assertEquals('http://goo:222', $r->getUrl()); - $this->assertEquals('goo:222', $r->getHeader('host')); - $r->setHost('goo:80'); - $this->assertEquals('http://goo', $r->getUrl()); - $this->assertEquals('goo', $r->getHeader('host')); - } - - public function testCanChangePort() - { - $r = new Request('GET', 'http://www.foo.com:222'); - $this->assertSame(222, $r->getPort()); - $this->assertEquals('www.foo.com', $r->getHost()); - $this->assertEquals('www.foo.com:222', $r->getHeader('host')); - $r->setPort(80); - $this->assertSame(80, $r->getPort()); - $this->assertEquals('www.foo.com', $r->getHost()); - $this->assertEquals('www.foo.com', $r->getHeader('host')); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Message/ResponseTest.php b/core/vendor/guzzlehttp/guzzle/tests/Message/ResponseTest.php deleted file mode 100644 index bbae24a17f8681d6fe3bfaaed034e8656de4d28f..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Message/ResponseTest.php +++ /dev/null @@ -1,120 +0,0 @@ -<?php - -namespace GuzzleHttp\Tests\Message; - -use GuzzleHttp\Exception\XmlParseException; -use GuzzleHttp\Message\Response; -use GuzzleHttp\Stream\Stream; - -/** - * @covers GuzzleHttp\Message\Response - */ -class ResponseTest extends \PHPUnit_Framework_TestCase -{ - public function testCanProvideCustomStatusCodeAndReasonPhrase() - { - $response = new Response(999, [], null, ['reason_phrase' => 'hi!']); - $this->assertEquals(999, $response->getStatusCode()); - $this->assertEquals('hi!', $response->getReasonPhrase()); - } - - public function testConvertsToString() - { - $response = new Response(200); - $this->assertEquals("HTTP/1.1 200 OK\r\n\r\n", (string) $response); - // Add another header - $response = new Response(200, ['X-Test' => 'Guzzle']); - $this->assertEquals("HTTP/1.1 200 OK\r\nX-Test: Guzzle\r\n\r\n", (string) $response); - $response = new Response(200, ['Content-Length' => 4], Stream::factory('test')); - $this->assertEquals("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest", (string) $response); - } - - public function testConvertsToStringAndSeeksToByteZero() - { - $response = new Response(200); - $s = Stream::factory('foo'); - $s->read(1); - $response->setBody($s); - $this->assertEquals("HTTP/1.1 200 OK\r\n\r\nfoo", (string) $response); - } - - public function testParsesJsonResponses() - { - $json = '{"foo": "bar"}'; - $response = new Response(200, [], Stream::factory($json)); - $this->assertEquals(['foo' => 'bar'], $response->json()); - $this->assertEquals(json_decode($json), $response->json(['object' => true])); - - $response = new Response(200); - $this->assertEquals(null, $response->json()); - } - - /** - * @expectedException \GuzzleHttp\Exception\ParseException - * @expectedExceptionMessage Unable to parse JSON data: JSON_ERROR_SYNTAX - Syntax error, malformed JSON - */ - public function testThrowsExceptionWhenFailsToParseJsonResponse() - { - $response = new Response(200, [], Stream::factory('{"foo": "')); - $response->json(); - } - - public function testParsesXmlResponses() - { - $response = new Response(200, [], Stream::factory('<abc><foo>bar</foo></abc>')); - $this->assertEquals('bar', (string) $response->xml()->foo); - // Always return a SimpleXMLElement from the xml method - $response = new Response(200); - $this->assertEmpty((string) $response->xml()->foo); - } - - /** - * @expectedException \GuzzleHttp\Exception\XmlParseException - * @expectedExceptionMessage Unable to parse response body into XML: String could not be parsed as XML - */ - public function testThrowsExceptionWhenFailsToParseXmlResponse() - { - $response = new Response(200, [], Stream::factory('<abc')); - try { - $response->xml(); - } catch (XmlParseException $e) { - $xmlParseError = $e->getError(); - $this->assertInstanceOf('\LibXMLError', $xmlParseError); - $this->assertContains("Couldn't find end of Start Tag abc line 1", $xmlParseError->message); - throw $e; - } - } - - public function testHasEffectiveUrl() - { - $r = new Response(200); - $this->assertNull($r->getEffectiveUrl()); - $r->setEffectiveUrl('http://www.test.com'); - $this->assertEquals('http://www.test.com', $r->getEffectiveUrl()); - } - - public function testPreventsComplexExternalEntities() - { - $xml = '<?xml version="1.0"?><!DOCTYPE scan[<!ENTITY test SYSTEM "php://filter/read=convert.base64-encode/resource=ResponseTest.php">]><scan>&test;</scan>'; - $response = new Response(200, [], Stream::factory($xml)); - - $oldCwd = getcwd(); - chdir(__DIR__); - try { - $xml = $response->xml(); - chdir($oldCwd); - $this->markTestIncomplete('Did not throw the expected exception! XML resolved as: ' . $xml->asXML()); - } catch (\Exception $e) { - chdir($oldCwd); - } - } - - public function testStatusAndReasonAreMutable() - { - $response = new Response(200); - $response->setStatusCode(201); - $this->assertEquals(201, $response->getStatusCode()); - $response->setReasonPhrase('Foo'); - $this->assertEquals('Foo', $response->getReasonPhrase()); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/MimetypesTest.php b/core/vendor/guzzlehttp/guzzle/tests/MimetypesTest.php deleted file mode 100644 index a18ec38136e866a63277ec1f98472b92c4e3130c..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/MimetypesTest.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php - -namespace GuzzleHttp\Tests; - -use GuzzleHttp\Mimetypes; - -/** - * @covers GuzzleHttp\Mimetypes - */ -class MimetypesTest extends \PHPUnit_Framework_TestCase -{ - public function testGetsFromExtension() - { - $this->assertEquals('text/x-php', Mimetypes::getInstance()->fromExtension('php')); - } - - public function testGetsFromFilename() - { - $this->assertEquals('text/x-php', Mimetypes::getInstance()->fromFilename(__FILE__)); - } - - public function testGetsFromCaseInsensitiveFilename() - { - $this->assertEquals('text/x-php', Mimetypes::getInstance()->fromFilename(strtoupper(__FILE__))); - } - - public function testReturnsNullWhenNoMatchFound() - { - $this->assertNull(Mimetypes::getInstance()->fromExtension('foobar')); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/PoolTest.php b/core/vendor/guzzlehttp/guzzle/tests/PoolTest.php deleted file mode 100644 index b5f02add8e1630fc333bb841defeb02889cb2027..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/PoolTest.php +++ /dev/null @@ -1,319 +0,0 @@ -<?php -namespace GuzzleHttp\Tests; - -use GuzzleHttp\Client; -use GuzzleHttp\Event\RequestEvents; -use GuzzleHttp\Pool; -use GuzzleHttp\Ring\Client\MockHandler; -use GuzzleHttp\Ring\Future\FutureArray; -use GuzzleHttp\Subscriber\History; -use GuzzleHttp\Event\BeforeEvent; -use GuzzleHttp\Event\CompleteEvent; -use GuzzleHttp\Event\ErrorEvent; -use GuzzleHttp\Event\EndEvent; -use GuzzleHttp\Message\Response; -use GuzzleHttp\Subscriber\Mock; -use React\Promise\Deferred; - -class PoolTest extends \PHPUnit_Framework_TestCase -{ - /** - * @expectedException \InvalidArgumentException - */ - public function testValidatesIterable() - { - new Pool(new Client(), 'foo'); - } - - public function testCanControlPoolSizeAndClient() - { - $c = new Client(); - $p = new Pool($c, [], ['pool_size' => 10]); - $this->assertSame($c, $this->readAttribute($p, 'client')); - $this->assertEquals(10, $this->readAttribute($p, 'poolSize')); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testValidatesEachElement() - { - $c = new Client(); - $requests = ['foo']; - $p = new Pool($c, new \ArrayIterator($requests)); - $p->wait(); - } - - public function testSendsAndRealizesFuture() - { - $c = $this->getClient(); - $p = new Pool($c, [$c->createRequest('GET', 'http://foo.com')]); - $this->assertTrue($p->wait()); - $this->assertFalse($p->wait()); - $this->assertTrue($this->readAttribute($p, 'isRealized')); - $this->assertFalse($p->cancel()); - } - - public function testSendsManyRequestsInCappedPool() - { - $c = $this->getClient(); - $p = new Pool($c, [$c->createRequest('GET', 'http://foo.com')]); - $this->assertTrue($p->wait()); - $this->assertFalse($p->wait()); - } - - public function testSendsRequestsThatHaveNotBeenRealized() - { - $c = $this->getClient(); - $p = new Pool($c, [$c->createRequest('GET', 'http://foo.com')]); - $this->assertTrue($p->wait()); - $this->assertFalse($p->wait()); - $this->assertFalse($p->cancel()); - } - - public function testCancelsInFlightRequests() - { - $c = $this->getClient(); - $h = new History(); - $c->getEmitter()->attach($h); - $p = new Pool($c, [ - $c->createRequest('GET', 'http://foo.com'), - $c->createRequest('GET', 'http://foo.com', [ - 'events' => [ - 'before' => [ - 'fn' => function () use (&$p) { - $this->assertTrue($p->cancel()); - }, - 'priority' => RequestEvents::EARLY - ] - ] - ]) - ]); - ob_start(); - $p->wait(); - $contents = ob_get_clean(); - $this->assertEquals(1, count($h)); - $this->assertEquals('Cancelling', $contents); - } - - private function getClient() - { - $deferred = new Deferred(); - $future = new FutureArray( - $deferred->promise(), - function() use ($deferred) { - $deferred->resolve(['status' => 200, 'headers' => []]); - }, function () { - echo 'Cancelling'; - } - ); - - return new Client(['handler' => new MockHandler($future)]); - } - - public function testBatchesRequests() - { - $client = new Client(['handler' => function () { - throw new \RuntimeException('No network access'); - }]); - - $responses = [ - new Response(301, ['Location' => 'http://foo.com/bar']), - new Response(200), - new Response(200), - new Response(404) - ]; - - $client->getEmitter()->attach(new Mock($responses)); - $requests = [ - $client->createRequest('GET', 'http://foo.com/baz'), - $client->createRequest('HEAD', 'http://httpbin.org/get'), - $client->createRequest('PUT', 'http://httpbin.org/put'), - ]; - - $a = $b = $c = $d = 0; - $result = Pool::batch($client, $requests, [ - 'before' => function (BeforeEvent $e) use (&$a) { $a++; }, - 'complete' => function (CompleteEvent $e) use (&$b) { $b++; }, - 'error' => function (ErrorEvent $e) use (&$c) { $c++; }, - 'end' => function (EndEvent $e) use (&$d) { $d++; } - ]); - - $this->assertEquals(4, $a); - $this->assertEquals(2, $b); - $this->assertEquals(1, $c); - $this->assertEquals(3, $d); - $this->assertCount(3, $result); - $this->assertInstanceOf('GuzzleHttp\BatchResults', $result); - - // The first result is actually the second (redirect) response. - $this->assertSame($responses[1], $result[0]); - // The second result is a 1:1 request:response map - $this->assertSame($responses[2], $result[1]); - // The third entry is the 404 RequestException - $this->assertSame($responses[3], $result[2]->getResponse()); - } - - public function testBatchesRequestsWithDynamicPoolSize() - { - $client = new Client(['handler' => function () { - throw new \RuntimeException('No network access'); - }]); - - $responses = [ - new Response(301, ['Location' => 'http://foo.com/bar']), - new Response(200), - new Response(200), - new Response(404) - ]; - - $client->getEmitter()->attach(new Mock($responses)); - $requests = [ - $client->createRequest('GET', 'http://foo.com/baz'), - $client->createRequest('HEAD', 'http://httpbin.org/get'), - $client->createRequest('PUT', 'http://httpbin.org/put'), - ]; - - $a = $b = $c = $d = 0; - $result = Pool::batch($client, $requests, [ - 'before' => function (BeforeEvent $e) use (&$a) { $a++; }, - 'complete' => function (CompleteEvent $e) use (&$b) { $b++; }, - 'error' => function (ErrorEvent $e) use (&$c) { $c++; }, - 'end' => function (EndEvent $e) use (&$d) { $d++; }, - 'pool_size' => function ($queueSize) { - static $options = [1, 2, 1]; - static $queued = 0; - - $this->assertEquals( - $queued, - $queueSize, - 'The number of queued requests should be equal to the sum of pool sizes so far.' - ); - - $next = array_shift($options); - $queued += $next; - - return $next; - } - ]); - - $this->assertEquals(4, $a); - $this->assertEquals(2, $b); - $this->assertEquals(1, $c); - $this->assertEquals(3, $d); - $this->assertCount(3, $result); - $this->assertInstanceOf('GuzzleHttp\BatchResults', $result); - - // The first result is actually the second (redirect) response. - $this->assertSame($responses[1], $result[0]); - // The second result is a 1:1 request:response map - $this->assertSame($responses[2], $result[1]); - // The third entry is the 404 RequestException - $this->assertSame($responses[3], $result[2]->getResponse()); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Each event listener must be a callable or - */ - public function testBatchValidatesTheEventFormat() - { - $client = new Client(); - $requests = [$client->createRequest('GET', 'http://foo.com/baz')]; - Pool::batch($client, $requests, ['complete' => 'foo']); - } - - public function testEmitsProgress() - { - $client = new Client(['handler' => function () { - throw new \RuntimeException('No network access'); - }]); - - $responses = [new Response(200), new Response(404)]; - $client->getEmitter()->attach(new Mock($responses)); - $requests = [ - $client->createRequest('GET', 'http://foo.com/baz'), - $client->createRequest('HEAD', 'http://httpbin.org/get') - ]; - - $pool = new Pool($client, $requests); - $count = 0; - $thenned = null; - $pool->then( - function ($value) use (&$thenned) { - $thenned = $value; - }, - null, - function ($result) use (&$count, $requests) { - $this->assertSame($requests[$count], $result['request']); - if ($count == 0) { - $this->assertNull($result['error']); - $this->assertEquals(200, $result['response']->getStatusCode()); - } else { - $this->assertInstanceOf( - 'GuzzleHttp\Exception\ClientException', - $result['error'] - ); - } - $count++; - } - ); - - $pool->wait(); - $this->assertEquals(2, $count); - $this->assertEquals(true, $thenned); - } - - public function testDoesNotThrowInErrorEvent() - { - $client = new Client(); - $responses = [new Response(404)]; - $client->getEmitter()->attach(new Mock($responses)); - $requests = [$client->createRequest('GET', 'http://foo.com/baz')]; - $result = Pool::batch($client, $requests); - $this->assertCount(1, $result); - $this->assertInstanceOf('GuzzleHttp\Exception\ClientException', $result[0]); - } - - public function testHasSendMethod() - { - $client = new Client(); - $responses = [new Response(404)]; - $history = new History(); - $client->getEmitter()->attach($history); - $client->getEmitter()->attach(new Mock($responses)); - $requests = [$client->createRequest('GET', 'http://foo.com/baz')]; - Pool::send($client, $requests); - $this->assertCount(1, $history); - } - - public function testDoesNotInfinitelyRecurse() - { - $client = new Client(['handler' => function () { - throw new \RuntimeException('No network access'); - }]); - - $last = null; - $client->getEmitter()->on( - 'before', - function (BeforeEvent $e) use (&$last) { - $e->intercept(new Response(200)); - if (function_exists('xdebug_get_stack_depth')) { - if ($last) { - $this->assertEquals($last, xdebug_get_stack_depth()); - } else { - $last = xdebug_get_stack_depth(); - } - } - } - ); - - $requests = []; - for ($i = 0; $i < 100; $i++) { - $requests[] = $client->createRequest('GET', 'http://foo.com'); - } - - $pool = new Pool($client, $requests); - $pool->wait(); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Post/MultipartBodyTest.php b/core/vendor/guzzlehttp/guzzle/tests/Post/MultipartBodyTest.php deleted file mode 100644 index 4b3b391643ae5104b30cb8ac26ab5e09ad4cedef..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Post/MultipartBodyTest.php +++ /dev/null @@ -1,120 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Post; - -use GuzzleHttp\Post\MultipartBody; -use GuzzleHttp\Post\PostFile; - -/** - * @covers GuzzleHttp\Post\MultipartBody - */ -class MultipartBodyTest extends \PHPUnit_Framework_TestCase -{ - protected function getTestBody() - { - return new MultipartBody(['foo' => 'bar'], [ - new PostFile('foo', 'abc', 'foo.txt') - ], 'abcdef'); - } - - public function testConstructorAddsFieldsAndFiles() - { - $b = $this->getTestBody(); - $this->assertEquals('abcdef', $b->getBoundary()); - $c = (string) $b; - $this->assertContains("--abcdef\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\nbar\r\n", $c); - $this->assertContains("--abcdef\r\nContent-Disposition: form-data; name=\"foo\"; filename=\"foo.txt\"\r\n" - . "Content-Type: text/plain\r\n\r\nabc\r\n--abcdef--", $c); - } - - public function testDoesNotModifyFieldFormat() - { - $m = new MultipartBody(['foo+baz' => 'bar+bam %20 boo'], [ - new PostFile('foo+bar', 'abc %20 123', 'foo.txt') - ], 'abcdef'); - $this->assertContains('name="foo+baz"', (string) $m); - $this->assertContains('name="foo+bar"', (string) $m); - $this->assertContains('bar+bam %20 boo', (string) $m); - $this->assertContains('abc %20 123', (string) $m); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testConstructorValidatesFiles() - { - new MultipartBody([], ['bar']); - } - - public function testConstructorCanCreateBoundary() - { - $b = new MultipartBody(); - $this->assertNotNull($b->getBoundary()); - } - - public function testWrapsStreamMethods() - { - $b = $this->getTestBody(); - $this->assertFalse($b->write('foo')); - $this->assertFalse($b->isWritable()); - $this->assertTrue($b->isReadable()); - $this->assertTrue($b->isSeekable()); - $this->assertEquals(0, $b->tell()); - } - - public function testCanDetachFieldsAndFiles() - { - $b = $this->getTestBody(); - $b->detach(); - $b->close(); - $this->assertEquals('', (string) $b); - } - - public function testIsSeekableReturnsTrueIfAllAreSeekable() - { - $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') - ->setMethods(['isSeekable', 'isReadable']) - ->getMockForAbstractClass(); - $s->expects($this->once()) - ->method('isSeekable') - ->will($this->returnValue(false)); - $s->expects($this->once()) - ->method('isReadable') - ->will($this->returnValue(true)); - $p = new PostFile('foo', $s, 'foo.php'); - $b = new MultipartBody([], [$p]); - $this->assertFalse($b->isSeekable()); - $this->assertFalse($b->seek(10)); - } - - public function testReadsFromBuffer() - { - $b = $this->getTestBody(); - $c = $b->read(1); - $c .= $b->read(1); - $c .= $b->read(1); - $c .= $b->read(1); - $c .= $b->read(1); - $this->assertEquals('--abc', $c); - } - - public function testCalculatesSize() - { - $b = $this->getTestBody(); - $this->assertEquals(strlen($b), $b->getSize()); - } - - public function testCalculatesSizeAndReturnsNullForUnknown() - { - $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') - ->setMethods(['getSize', 'isReadable']) - ->getMockForAbstractClass(); - $s->expects($this->once()) - ->method('getSize') - ->will($this->returnValue(null)); - $s->expects($this->once()) - ->method('isReadable') - ->will($this->returnValue(true)); - $b = new MultipartBody([], [new PostFile('foo', $s, 'foo.php')]); - $this->assertNull($b->getSize()); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Post/PostBodyTest.php b/core/vendor/guzzlehttp/guzzle/tests/Post/PostBodyTest.php deleted file mode 100644 index 0283a5ebf202af541c7f72ce838ef9eb4910d3f9..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Post/PostBodyTest.php +++ /dev/null @@ -1,255 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Post; - -use GuzzleHttp\Message\Request; -use GuzzleHttp\Post\PostBody; -use GuzzleHttp\Post\PostFile; -use GuzzleHttp\Query; - -/** - * @covers GuzzleHttp\Post\PostBody - */ -class PostBodyTest extends \PHPUnit_Framework_TestCase -{ - public function testWrapsBasicStreamFunctionality() - { - $b = new PostBody(); - $this->assertTrue($b->isSeekable()); - $this->assertTrue($b->isReadable()); - $this->assertFalse($b->isWritable()); - $this->assertFalse($b->write('foo')); - } - - public function testApplyingWithNothingDoesNothing() - { - $b = new PostBody(); - $m = new Request('POST', '/'); - $b->applyRequestHeaders($m); - $this->assertFalse($m->hasHeader('Content-Length')); - $this->assertFalse($m->hasHeader('Content-Type')); - } - - public function testCanForceMultipartUploadsWhenApplying() - { - $b = new PostBody(); - $b->forceMultipartUpload(true); - $m = new Request('POST', '/'); - $b->applyRequestHeaders($m); - $this->assertContains( - 'multipart/form-data', - $m->getHeader('Content-Type') - ); - } - - public function testApplyingWithFilesAddsMultipartUpload() - { - $b = new PostBody(); - $p = new PostFile('foo', fopen(__FILE__, 'r')); - $b->addFile($p); - $this->assertEquals([$p], $b->getFiles()); - $this->assertNull($b->getFile('missing')); - $this->assertSame($p, $b->getFile('foo')); - $m = new Request('POST', '/'); - $b->applyRequestHeaders($m); - $this->assertContains( - 'multipart/form-data', - $m->getHeader('Content-Type') - ); - $this->assertTrue($m->hasHeader('Content-Length')); - } - - public function testApplyingWithFieldsAddsMultipartUpload() - { - $b = new PostBody(); - $b->setField('foo', 'bar'); - $this->assertEquals(['foo' => 'bar'], $b->getFields()); - $m = new Request('POST', '/'); - $b->applyRequestHeaders($m); - $this->assertContains( - 'application/x-www-form', - $m->getHeader('Content-Type') - ); - $this->assertTrue($m->hasHeader('Content-Length')); - } - - public function testMultipartWithNestedFields() - { - $b = new PostBody(); - $b->setField('foo', ['bar' => 'baz']); - $b->forceMultipartUpload(true); - $this->assertEquals(['foo' => ['bar' => 'baz']], $b->getFields()); - $m = new Request('POST', '/'); - $b->applyRequestHeaders($m); - $this->assertContains( - 'multipart/form-data', - $m->getHeader('Content-Type') - ); - $this->assertTrue($m->hasHeader('Content-Length')); - $contents = $b->getContents(); - $this->assertContains('name="foo[bar]"', $contents); - $this->assertNotContains('name="foo"', $contents); - } - - public function testCountProvidesFieldsAndFiles() - { - $b = new PostBody(); - $b->setField('foo', 'bar'); - $b->addFile(new PostFile('foo', fopen(__FILE__, 'r'))); - $this->assertEquals(2, count($b)); - $b->clearFiles(); - $b->removeField('foo'); - $this->assertEquals(0, count($b)); - $this->assertEquals([], $b->getFiles()); - $this->assertEquals([], $b->getFields()); - } - - public function testHasFields() - { - $b = new PostBody(); - $b->setField('foo', 'bar'); - $b->setField('baz', '123'); - $this->assertEquals('bar', $b->getField('foo')); - $this->assertEquals('123', $b->getField('baz')); - $this->assertNull($b->getField('ahh')); - $this->assertTrue($b->hasField('foo')); - $this->assertFalse($b->hasField('test')); - $b->replaceFields(['abc' => '123']); - $this->assertFalse($b->hasField('foo')); - $this->assertTrue($b->hasField('abc')); - } - - public function testConvertsFieldsToQueryStyleBody() - { - $b = new PostBody(); - $b->setField('foo', 'bar'); - $b->setField('baz', '123'); - $this->assertEquals('foo=bar&baz=123', $b); - $this->assertEquals(15, $b->getSize()); - $b->seek(0); - $this->assertEquals('foo=bar&baz=123', $b->getContents()); - $b->seek(0); - $this->assertEquals('foo=bar&baz=123', $b->read(1000)); - $this->assertEquals(15, $b->tell()); - } - - public function testCanSpecifyQueryAggregator() - { - $b = new PostBody(); - $b->setField('foo', ['baz', 'bar']); - $this->assertEquals('foo%5B0%5D=baz&foo%5B1%5D=bar', (string) $b); - $b = new PostBody(); - $b->setField('foo', ['baz', 'bar']); - $agg = Query::duplicateAggregator(); - $b->setAggregator($agg); - $this->assertEquals('foo=baz&foo=bar', (string) $b); - } - - public function testDetachesAndCloses() - { - $b = new PostBody(); - $b->setField('foo', 'bar'); - $b->detach(); - $b->close(); - $this->assertEquals('', $b->read(10)); - } - - public function testDetachesWhenBodyIsPresent() - { - $b = new PostBody(); - $b->setField('foo', 'bar'); - $b->getContents(); - $b->detach(); - } - - public function testFlushAndMetadataPlaceholders() - { - $b = new PostBody(); - $this->assertEquals([], $b->getMetadata()); - $this->assertNull($b->getMetadata('foo')); - } - - public function testCreatesMultipartUploadWithMultiFields() - { - $b = new PostBody(); - $b->setField('testing', ['baz', 'bar']); - $b->setField('other', 'hi'); - $b->setField('third', 'there'); - $b->addFile(new PostFile('foo', fopen(__FILE__, 'r'))); - $s = (string) $b; - $this->assertContains(file_get_contents(__FILE__), $s); - $this->assertContains('testing=bar', $s); - $this->assertContains( - 'Content-Disposition: form-data; name="third"', - $s - ); - $this->assertContains( - 'Content-Disposition: form-data; name="other"', - $s - ); - } - - public function testMultipartWithBase64Fields() - { - $b = new PostBody(); - $b->setField('foo64', '/xA2JhWEqPcgyLRDdir9WSRi/khpb2Lh3ooqv+5VYoc='); - $b->forceMultipartUpload(true); - $this->assertEquals( - ['foo64' => '/xA2JhWEqPcgyLRDdir9WSRi/khpb2Lh3ooqv+5VYoc='], - $b->getFields() - ); - $m = new Request('POST', '/'); - $b->applyRequestHeaders($m); - $this->assertContains( - 'multipart/form-data', - $m->getHeader('Content-Type') - ); - $this->assertTrue($m->hasHeader('Content-Length')); - $contents = $b->getContents(); - $this->assertContains('name="foo64"', $contents); - $this->assertContains( - '/xA2JhWEqPcgyLRDdir9WSRi/khpb2Lh3ooqv+5VYoc=', - $contents - ); - } - - public function testMultipartWithAmpersandInValue() - { - $b = new PostBody(); - $b->setField('a', 'b&c=d'); - $b->forceMultipartUpload(true); - $this->assertEquals(['a' => 'b&c=d'], $b->getFields()); - $m = new Request('POST', '/'); - $b->applyRequestHeaders($m); - $this->assertContains( - 'multipart/form-data', - $m->getHeader('Content-Type') - ); - $this->assertTrue($m->hasHeader('Content-Length')); - $contents = $b->getContents(); - $this->assertContains('name="a"', $contents); - $this->assertContains('b&c=d', $contents); - } - - /** - * @expectedException \GuzzleHttp\Stream\Exception\CannotAttachException - */ - public function testCannotAttach() - { - $b = new PostBody(); - $b->attach('foo'); - } - - public function testDoesNotOverwriteExistingHeaderForUrlencoded() - { - $m = new Request('POST', 'http://foo.com', [ - 'content-type' => 'application/x-www-form-urlencoded; charset=utf-8' - ]); - $b = new PostBody(); - $b->setField('foo', 'bar'); - $b->applyRequestHeaders($m); - $this->assertEquals( - 'application/x-www-form-urlencoded; charset=utf-8', - $m->getHeader('Content-Type') - ); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Post/PostFileTest.php b/core/vendor/guzzlehttp/guzzle/tests/Post/PostFileTest.php deleted file mode 100644 index 800cee503a6435f4ce84e4bd096dd90049488f0e..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Post/PostFileTest.php +++ /dev/null @@ -1,61 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Post; - -use GuzzleHttp\Post\MultipartBody; -use GuzzleHttp\Post\PostFile; -use GuzzleHttp\Stream\Stream; - -/** - * @covers GuzzleHttp\Post\PostFile - */ -class PostFileTest extends \PHPUnit_Framework_TestCase -{ - public function testCreatesFromString() - { - $p = new PostFile('foo', 'hi', '/path/to/test.php'); - $this->assertInstanceOf('GuzzleHttp\Post\PostFileInterface', $p); - $this->assertEquals('hi', $p->getContent()); - $this->assertEquals('foo', $p->getName()); - $this->assertEquals('/path/to/test.php', $p->getFilename()); - $this->assertEquals( - 'form-data; name="foo"; filename="test.php"', - $p->getHeaders()['Content-Disposition'] - ); - } - - public function testGetsFilenameFromMetadata() - { - $p = new PostFile('foo', fopen(__FILE__, 'r')); - $this->assertEquals(__FILE__, $p->getFilename()); - } - - public function testDefaultsToNameWhenNoFilenameExists() - { - $p = new PostFile('foo', 'bar'); - $this->assertEquals('foo', $p->getFilename()); - } - - public function testCreatesFromMultipartFormData() - { - $mp = new MultipartBody([], [], 'baz'); - $p = new PostFile('foo', $mp); - $this->assertEquals( - 'form-data; name="foo"', - $p->getHeaders()['Content-Disposition'] - ); - $this->assertEquals( - 'multipart/form-data; boundary=baz', - $p->getHeaders()['Content-Type'] - ); - } - - public function testCanAddHeaders() - { - $p = new PostFile('foo', Stream::factory('hi'), 'test.php', [ - 'X-Foo' => '123', - 'Content-Disposition' => 'bar' - ]); - $this->assertEquals('bar', $p->getHeaders()['Content-Disposition']); - $this->assertEquals('123', $p->getHeaders()['X-Foo']); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/QueryParserTest.php b/core/vendor/guzzlehttp/guzzle/tests/QueryParserTest.php deleted file mode 100644 index e9075a80d0d088b1f6bfc45286fa0d4dded49411..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/QueryParserTest.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php - -namespace GuzzleHttp\Tests; - -use GuzzleHttp\Query; -use GuzzleHttp\QueryParser; - -class QueryParserTest extends \PHPUnit_Framework_TestCase -{ - public function parseQueryProvider() - { - return [ - // Does not need to parse when the string is empty - ['', []], - // Can parse mult-values items - ['q=a&q=b', ['q' => ['a', 'b']]], - // Can parse multi-valued items that use numeric indices - ['q[0]=a&q[1]=b', ['q' => ['a', 'b']]], - // Can parse duplicates and does not include numeric indices - ['q[]=a&q[]=b', ['q' => ['a', 'b']]], - // Ensures that the value of "q" is an array even though one value - ['q[]=a', ['q' => ['a']]], - // Does not modify "." to "_" like PHP's parse_str() - ['q.a=a&q.b=b', ['q.a' => 'a', 'q.b' => 'b']], - // Can decode %20 to " " - ['q%20a=a%20b', ['q a' => 'a b']], - // Can parse funky strings with no values by assigning each to null - ['q&a', ['q' => null, 'a' => null]], - // Does not strip trailing equal signs - ['data=abc=', ['data' => 'abc=']], - // Can store duplicates without affecting other values - ['foo=a&foo=b&?µ=c', ['foo' => ['a', 'b'], '?µ' => 'c']], - // Sets value to null when no "=" is present - ['foo', ['foo' => null]], - // Preserves "0" keys. - ['0', ['0' => null]], - // Sets the value to an empty string when "=" is present - ['0=', ['0' => '']], - // Preserves falsey keys - ['var=0', ['var' => '0']], - // Can deeply nest and store duplicate PHP values - ['a[b][c]=1&a[b][c]=2', [ - 'a' => ['b' => ['c' => ['1', '2']]] - ]], - // Can parse PHP style arrays - ['a[b]=c&a[d]=e', ['a' => ['b' => 'c', 'd' => 'e']]], - // Ensure it doesn't leave things behind with repeated values - // Can parse mult-values items - ['q=a&q=b&q=c', ['q' => ['a', 'b', 'c']]], - ]; - } - - /** - * @dataProvider parseQueryProvider - */ - public function testParsesQueries($input, $output) - { - $query = Query::fromString($input); - $this->assertEquals($output, $query->toArray()); - // Normalize the input and output - $query->setEncodingType(false); - $this->assertEquals(rawurldecode($input), (string) $query); - } - - public function testConvertsPlusSymbolsToSpacesByDefault() - { - $query = Query::fromString('var=foo+bar', true); - $this->assertEquals('foo bar', $query->get('var')); - } - - public function testCanControlDecodingType() - { - $qp = new QueryParser(); - $q = new Query(); - $qp->parseInto($q, 'var=foo+bar', Query::RFC3986); - $this->assertEquals('foo+bar', $q->get('var')); - $qp->parseInto($q, 'var=foo+bar', Query::RFC1738); - $this->assertEquals('foo bar', $q->get('var')); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/QueryTest.php b/core/vendor/guzzlehttp/guzzle/tests/QueryTest.php deleted file mode 100644 index 8b9d3448f72bc61c5720da7cb66e41d10f2dffb8..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/QueryTest.php +++ /dev/null @@ -1,171 +0,0 @@ -<?php - -namespace GuzzleHttp\Tests; - -use GuzzleHttp\Query; - -class QueryTest extends \PHPUnit_Framework_TestCase -{ - public function testCanCastToString() - { - $q = new Query(['foo' => 'baz', 'bar' => 'bam boozle']); - $this->assertEquals('foo=baz&bar=bam%20boozle', (string) $q); - } - - public function testCanDisableUrlEncoding() - { - $q = new Query(['bar' => 'bam boozle']); - $q->setEncodingType(false); - $this->assertEquals('bar=bam boozle', (string) $q); - } - - public function testCanSpecifyRfc1783UrlEncodingType() - { - $q = new Query(['bar abc' => 'bam boozle']); - $q->setEncodingType(Query::RFC1738); - $this->assertEquals('bar+abc=bam+boozle', (string) $q); - } - - public function testCanSpecifyRfc3986UrlEncodingType() - { - $q = new Query(['bar abc' => 'bam boozle', 'ሴ' => 'hi']); - $q->setEncodingType(Query::RFC3986); - $this->assertEquals('bar%20abc=bam%20boozle&%E1%88%B4=hi', (string) $q); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testValidatesEncodingType() - { - (new Query(['bar' => 'bam boozle']))->setEncodingType('foo'); - } - - public function testAggregatesMultipleValues() - { - $q = new Query(['foo' => ['bar', 'baz']]); - $this->assertEquals('foo%5B0%5D=bar&foo%5B1%5D=baz', (string) $q); - } - - public function testCanSetAggregator() - { - $q = new Query(['foo' => ['bar', 'baz']]); - $q->setAggregator(function (array $data) { - return ['foo' => ['barANDbaz']]; - }); - $this->assertEquals('foo=barANDbaz', (string) $q); - } - - public function testAllowsMultipleValuesPerKey() - { - $q = new Query(); - $q->add('facet', 'size'); - $q->add('facet', 'width'); - $q->add('facet.field', 'foo'); - // Use the duplicate aggregator - $q->setAggregator($q::duplicateAggregator()); - $this->assertEquals('facet=size&facet=width&facet.field=foo', (string) $q); - } - - public function testAllowsZeroValues() - { - $query = new Query(array( - 'foo' => 0, - 'baz' => '0', - 'bar' => null, - 'boo' => false - )); - $this->assertEquals('foo=0&baz=0&bar&boo=', (string) $query); - } - - private $encodeData = [ - 't' => [ - 'v1' => ['a', '1'], - 'v2' => 'b', - 'v3' => ['v4' => 'c', 'v5' => 'd'] - ] - ]; - - public function testEncodesDuplicateAggregator() - { - $agg = Query::duplicateAggregator(); - $result = $agg($this->encodeData); - $this->assertEquals(array( - 't[v1]' => ['a', '1'], - 't[v2]' => ['b'], - 't[v3][v4]' => ['c'], - 't[v3][v5]' => ['d'], - ), $result); - } - - public function testDuplicateEncodesNoNumericIndices() - { - $agg = Query::duplicateAggregator(); - $result = $agg($this->encodeData); - $this->assertEquals(array( - 't[v1]' => ['a', '1'], - 't[v2]' => ['b'], - 't[v3][v4]' => ['c'], - 't[v3][v5]' => ['d'], - ), $result); - } - - public function testEncodesPhpAggregator() - { - $agg = Query::phpAggregator(); - $result = $agg($this->encodeData); - $this->assertEquals(array( - 't[v1][0]' => ['a'], - 't[v1][1]' => ['1'], - 't[v2]' => ['b'], - 't[v3][v4]' => ['c'], - 't[v3][v5]' => ['d'], - ), $result); - } - - public function testPhpEncodesNoNumericIndices() - { - $agg = Query::phpAggregator(false); - $result = $agg($this->encodeData); - $this->assertEquals(array( - 't[v1][]' => ['a', '1'], - 't[v2]' => ['b'], - 't[v3][v4]' => ['c'], - 't[v3][v5]' => ['d'], - ), $result); - } - - public function testCanDisableUrlEncodingDecoding() - { - $q = Query::fromString('foo=bar+baz boo%20', false); - $this->assertEquals('bar+baz boo%20', $q['foo']); - $this->assertEquals('foo=bar+baz boo%20', (string) $q); - } - - public function testCanChangeUrlEncodingDecodingToRfc1738() - { - $q = Query::fromString('foo=bar+baz', Query::RFC1738); - $this->assertEquals('bar baz', $q['foo']); - $this->assertEquals('foo=bar+baz', (string) $q); - } - - public function testCanChangeUrlEncodingDecodingToRfc3986() - { - $q = Query::fromString('foo=bar%20baz', Query::RFC3986); - $this->assertEquals('bar baz', $q['foo']); - $this->assertEquals('foo=bar%20baz', (string) $q); - } - - public function testQueryStringsAllowSlashButDoesNotDecodeWhenDisable() - { - $q = Query::fromString('foo=bar%2Fbaz&bam=boo%20boo', Query::RFC3986); - $q->setEncodingType(false); - $this->assertEquals('foo=bar/baz&bam=boo boo', (string) $q); - } - - public function testQueryStringsAllowDecodingEncodingCompletelyDisabled() - { - $q = Query::fromString('foo=bar%2Fbaz&bam=boo boo!', false); - $this->assertEquals('foo=bar%2Fbaz&bam=boo boo!', (string) $q); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/RequestFsmTest.php b/core/vendor/guzzlehttp/guzzle/tests/RequestFsmTest.php deleted file mode 100644 index dd676840516abec7c3579bba25a93e82e5d90ed0..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/RequestFsmTest.php +++ /dev/null @@ -1,187 +0,0 @@ -<?php -namespace GuzzleHttp\Tests; - -use GuzzleHttp\Exception\RequestException; -use GuzzleHttp\Message\MessageFactory; -use GuzzleHttp\Message\Response; -use GuzzleHttp\RequestFsm; -use GuzzleHttp\Ring\Future\CompletedFutureArray; -use GuzzleHttp\Subscriber\Mock; -use GuzzleHttp\Transaction; -use GuzzleHttp\Client; -use GuzzleHttp\Message\Request; -use GuzzleHttp\Event\BeforeEvent; -use GuzzleHttp\Event\CompleteEvent; -use GuzzleHttp\Event\ErrorEvent; -use GuzzleHttp\Event\EndEvent; -use GuzzleHttp\Message\FutureResponse; -use GuzzleHttp\Message\RequestInterface; -use GuzzleHttp\Event\RequestEvents; -use React\Promise\Deferred; - -class RequestFsmTest extends \PHPUnit_Framework_TestCase -{ - private $mf; - - public function setup() - { - $this->mf = new MessageFactory(); - } - - public function testEmitsBeforeEventInTransition() - { - $fsm = new RequestFsm(function () { - return new CompletedFutureArray(['status' => 200]); - }, $this->mf); - $t = new Transaction(new Client(), new Request('GET', 'http://foo.com')); - $c = false; - $t->request->getEmitter()->on('before', function (BeforeEvent $e) use (&$c) { - $c = true; - }); - $fsm($t); - $this->assertTrue($c); - } - - public function testEmitsCompleteEventInTransition() - { - $fsm = new RequestFsm(function () { - return new CompletedFutureArray(['status' => 200]); - }, $this->mf); - $t = new Transaction(new Client(), new Request('GET', 'http://foo.com')); - $t->response = new Response(200); - $t->state = 'complete'; - $c = false; - $t->request->getEmitter()->on('complete', function (CompleteEvent $e) use (&$c) { - $c = true; - }); - $fsm($t); - $this->assertTrue($c); - } - - public function testDoesNotEmitCompleteForFuture() - { - $fsm = new RequestFsm(function () { - return new CompletedFutureArray(['status' => 200]); - }, $this->mf); - $t = new Transaction(new Client(), new Request('GET', 'http://foo.com')); - $deferred = new Deferred(); - $t->response = new FutureResponse($deferred->promise()); - $t->state = 'complete'; - $c = false; - $t->request->getEmitter()->on('complete', function (CompleteEvent $e) use (&$c) { - $c = true; - }); - $fsm($t); - $this->assertFalse($c); - } - - public function testTransitionsThroughSuccessfulTransfer() - { - $client = new Client(); - $client->getEmitter()->attach(new Mock([new Response(200)])); - $request = $client->createRequest('GET', 'http://ewfewwef.com'); - $this->addListeners($request, $calls); - $client->send($request); - $this->assertEquals(['before', 'complete', 'end'], $calls); - } - - public function testTransitionsThroughErrorsInBefore() - { - $fsm = new RequestFsm(function () { - return new CompletedFutureArray(['status' => 200]); - }, $this->mf); - $client = new Client(); - $request = $client->createRequest('GET', 'http://ewfewwef.com'); - $t = new Transaction($client, $request); - $calls = []; - $this->addListeners($t->request, $calls); - $t->request->getEmitter()->on('before', function (BeforeEvent $e) { - throw new \Exception('foo'); - }); - try { - $fsm($t); - $this->fail('did not throw'); - } catch (RequestException $e) { - $this->assertContains('foo', $t->exception->getMessage()); - $this->assertEquals(['before', 'error', 'end'], $calls); - } - } - - public function testTransitionsThroughErrorsInComplete() - { - $client = new Client(); - $client->getEmitter()->attach(new Mock([new Response(200)])); - $request = $client->createRequest('GET', 'http://ewfewwef.com'); - $this->addListeners($request, $calls); - $request->getEmitter()->once('complete', function (CompleteEvent $e) { - throw new \Exception('foo'); - }); - try { - $client->send($request); - $this->fail('did not throw'); - } catch (RequestException $e) { - $this->assertContains('foo', $e->getMessage()); - $this->assertEquals(['before', 'complete', 'error', 'end'], $calls); - } - } - - public function testTransitionsThroughErrorInterception() - { - $fsm = new RequestFsm(function () { - return new CompletedFutureArray(['status' => 404]); - }, $this->mf); - $client = new Client(); - $request = $client->createRequest('GET', 'http://ewfewwef.com'); - $t = new Transaction($client, $request); - $calls = []; - $this->addListeners($t->request, $calls); - $t->request->getEmitter()->on('error', function (ErrorEvent $e) { - $e->intercept(new Response(200)); - }); - $fsm($t); - $this->assertEquals(200, $t->response->getStatusCode()); - $this->assertNull($t->exception); - $this->assertEquals(['before', 'complete', 'error', 'complete', 'end'], $calls); - } - - private function addListeners(RequestInterface $request, &$calls) - { - $request->getEmitter()->on('before', function (BeforeEvent $e) use (&$calls) { - $calls[] = 'before'; - }, RequestEvents::EARLY); - $request->getEmitter()->on('complete', function (CompleteEvent $e) use (&$calls) { - $calls[] = 'complete'; - }, RequestEvents::EARLY); - $request->getEmitter()->on('error', function (ErrorEvent $e) use (&$calls) { - $calls[] = 'error'; - }, RequestEvents::EARLY); - $request->getEmitter()->on('end', function (EndEvent $e) use (&$calls) { - $calls[] = 'end'; - }, RequestEvents::EARLY); - } - - /** - * @expectedException \GuzzleHttp\Exception\RequestException - * @expectedExceptionMessage Too many state transitions - */ - public function testDetectsInfiniteLoops() - { - $client = new Client([ - 'fsm' => $fsm = new RequestFsm( - function () { - return new CompletedFutureArray(['status' => 200]); - }, - new MessageFactory(), - 3 - ) - ]); - $request = $client->createRequest('GET', 'http://foo.com:123'); - $request->getEmitter()->on('before', function () { - throw new \Exception('foo'); - }); - $request->getEmitter()->on('error', function ($e) { - $e->retry(); - }); - $client->send($request); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/RingBridgeTest.php b/core/vendor/guzzlehttp/guzzle/tests/RingBridgeTest.php deleted file mode 100644 index dc26a42aa665a12e3cf776bf59c8a70f0dc8b59e..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/RingBridgeTest.php +++ /dev/null @@ -1,195 +0,0 @@ -<?php -namespace GuzzleHttp\Tests; - -use GuzzleHttp\Client; -use GuzzleHttp\Event\ProgressEvent; -use GuzzleHttp\Message\MessageFactory; -use GuzzleHttp\RingBridge; -use GuzzleHttp\Stream\Stream; -use GuzzleHttp\Transaction; -use GuzzleHttp\Message\Request; -use GuzzleHttp\Message\Response; -use GuzzleHttp\Ring\Client\MockHandler; -use GuzzleHttp\Exception\RequestException; -use GuzzleHttp\Event\ErrorEvent; -use GuzzleHttp\RequestFsm; - -class RingBridgeTest extends \PHPUnit_Framework_TestCase -{ - public function testCreatesRingRequests() - { - $stream = Stream::factory('test'); - $request = new Request('GET', 'http://httpbin.org/get?a=b', [ - 'test' => 'hello' - ], $stream); - $request->getConfig()->set('foo', 'bar'); - $trans = new Transaction(new Client(), $request); - $factory = new MessageFactory(); - $fsm = new RequestFsm(function () {}, new MessageFactory()); - $r = RingBridge::prepareRingRequest($trans, $factory, $fsm); - $this->assertEquals('http', $r['scheme']); - $this->assertEquals('1.1', $r['version']); - $this->assertEquals('GET', $r['http_method']); - $this->assertEquals('http://httpbin.org/get?a=b', $r['url']); - $this->assertEquals('/get', $r['uri']); - $this->assertEquals('a=b', $r['query_string']); - $this->assertEquals([ - 'Host' => ['httpbin.org'], - 'test' => ['hello'] - ], $r['headers']); - $this->assertSame($stream, $r['body']); - $this->assertEquals(['foo' => 'bar'], $r['client']); - $this->assertFalse($r['future']); - } - - public function testCreatesRingRequestsWithNullQueryString() - { - $request = new Request('GET', 'http://httpbin.org'); - $trans = new Transaction(new Client(), $request); - $factory = new MessageFactory(); - $fsm = new RequestFsm(function () {}, new MessageFactory()); - $r = RingBridge::prepareRingRequest($trans, $factory, $fsm); - $this->assertNull($r['query_string']); - $this->assertEquals('/', $r['uri']); - $this->assertEquals(['Host' => ['httpbin.org']], $r['headers']); - $this->assertNull($r['body']); - $this->assertEquals([], $r['client']); - } - - public function testAddsProgress() - { - Server::enqueue([new Response(200)]); - $client = new Client(['base_url' => Server::$url]); - $request = $client->createRequest('GET'); - $called = false; - $request->getEmitter()->on( - 'progress', - function (ProgressEvent $e) use (&$called) { - $called = true; - } - ); - $this->assertEquals(200, $client->send($request)->getStatusCode()); - $this->assertTrue($called); - } - - public function testGetsResponseProtocolVersionAndEffectiveUrlAndReason() - { - $client = new Client([ - 'handler' => new MockHandler([ - 'status' => 200, - 'reason' => 'test', - 'headers' => [], - 'version' => '1.0', - 'effective_url' => 'http://foo.com' - ]) - ]); - $request = $client->createRequest('GET', 'http://foo.com'); - $response = $client->send($request); - $this->assertEquals('1.0', $response->getProtocolVersion()); - $this->assertEquals('http://foo.com', $response->getEffectiveUrl()); - $this->assertEquals('test', $response->getReasonPhrase()); - } - - public function testGetsStreamFromResponse() - { - $res = fopen('php://temp', 'r+'); - fwrite($res, 'foo'); - rewind($res); - $client = new Client([ - 'handler' => new MockHandler([ - 'status' => 200, - 'headers' => [], - 'body' => $res - ]) - ]); - $request = $client->createRequest('GET', 'http://foo.com'); - $response = $client->send($request); - $this->assertEquals('foo', (string) $response->getBody()); - } - - public function testEmitsErrorEventOnError() - { - $client = new Client(['base_url' => 'http://127.0.0.1:123']); - $request = $client->createRequest('GET'); - $called = false; - $request->getEmitter()->on('error', function () use (&$called) { - $called = true; - }); - $request->getConfig()['timeout'] = 0.001; - $request->getConfig()['connect_timeout'] = 0.001; - try { - $client->send($request); - $this->fail('did not throw'); - } catch (RequestException $e) { - $this->assertSame($request, $e->getRequest()); - $this->assertContains('cURL error', $e->getMessage()); - $this->assertTrue($called); - } - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testValidatesRingRequest() - { - RingBridge::fromRingRequest([]); - } - - public function testCreatesRequestFromRing() - { - $request = RingBridge::fromRingRequest([ - 'http_method' => 'GET', - 'uri' => '/', - 'headers' => [ - 'foo' => ['bar'], - 'host' => ['foo.com'] - ], - 'body' => 'test', - 'version' => '1.0' - ]); - $this->assertEquals('GET', $request->getMethod()); - $this->assertEquals('http://foo.com/', $request->getUrl()); - $this->assertEquals('1.0', $request->getProtocolVersion()); - $this->assertEquals('test', (string) $request->getBody()); - $this->assertEquals('bar', $request->getHeader('foo')); - } - - public function testCanInterceptException() - { - $client = new Client(['base_url' => 'http://127.0.0.1:123']); - $request = $client->createRequest('GET'); - $called = false; - $request->getEmitter()->on( - 'error', - function (ErrorEvent $e) use (&$called) { - $called = true; - $e->intercept(new Response(200)); - } - ); - $request->getConfig()['timeout'] = 0.001; - $request->getConfig()['connect_timeout'] = 0.001; - $this->assertEquals(200, $client->send($request)->getStatusCode()); - $this->assertTrue($called); - } - - public function testCreatesLongException() - { - $r = new Request('GET', 'http://www.google.com'); - $e = RingBridge::getNoRingResponseException($r); - $this->assertInstanceOf('GuzzleHttp\Exception\RequestException', $e); - $this->assertSame($r, $e->getRequest()); - } - - public function testEnsuresResponseOrExceptionWhenCompletingResponse() - { - $trans = new Transaction(new Client(), new Request('GET', 'http://f.co')); - $f = new MessageFactory(); - $fsm = new RequestFsm(function () {}, new MessageFactory()); - try { - RingBridge::completeRingResponse($trans, [], $f, $fsm); - } catch (RequestException $e) { - $this->assertSame($trans->request, $e->getRequest()); - $this->assertContains('RingPHP', $e->getMessage()); - } - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Server.php b/core/vendor/guzzlehttp/guzzle/tests/Server.php deleted file mode 100644 index 1de20e38b8c8605a8529f5c7ea2a128a49c2528d..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Server.php +++ /dev/null @@ -1,107 +0,0 @@ -<?php -namespace GuzzleHttp\Tests; - -use GuzzleHttp\Client; -use GuzzleHttp\Message\MessageFactory; -use GuzzleHttp\Message\Response; -use GuzzleHttp\Message\ResponseInterface; -use GuzzleHttp\RingBridge; -use GuzzleHttp\Tests\Ring\Client\Server as TestServer; - -/** - * Placeholder for the RingPHP-Client server that makes it easier to use. - */ -class Server -{ - public static $url = 'http://127.0.0.1:8125/'; - public static $port = 8125; - - /** - * 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 $responses Responses to queue. - * @throws \Exception - */ - public static function enqueue(array $responses) - { - static $factory; - if (!$factory) { - $factory = new MessageFactory(); - } - - $data = []; - foreach ($responses as $response) { - // Create the response object from a string - if (is_string($response)) { - $response = $factory->fromMessage($response); - } elseif (!($response instanceof ResponseInterface)) { - throw new \Exception('Responses must be strings or Responses'); - } - $data[] = self::convertResponse($response); - } - - TestServer::enqueue($data); - } - - /** - * Get all of the received requests - * - * @param bool $hydrate 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 \RuntimeException - */ - public static function received($hydrate = false) - { - $response = TestServer::received(); - - if ($hydrate) { - $c = new Client(); - $factory = new MessageFactory(); - $response = array_map(function($message) use ($factory, $c) { - return RingBridge::fromRingRequest($message); - }, $response); - } - - return $response; - } - - public static function flush() - { - TestServer::flush(); - } - - public static function stop() - { - TestServer::stop(); - } - - public static function wait($maxTries = 5) - { - TestServer::wait($maxTries); - } - - public static function start() - { - TestServer::start(); - } - - private static function convertResponse(Response $response) - { - $headers = array_map(function ($h) { - return implode(', ', $h); - }, $response->getHeaders()); - - return [ - 'status' => $response->getStatusCode(), - 'reason' => $response->getReasonPhrase(), - 'headers' => $headers, - 'body' => base64_encode((string) $response->getBody()) - ]; - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Subscriber/CookieTest.php b/core/vendor/guzzlehttp/guzzle/tests/Subscriber/CookieTest.php deleted file mode 100644 index bc17e2dc20a9f0b06161a5e9b2d381cb85d9e31c..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Subscriber/CookieTest.php +++ /dev/null @@ -1,74 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Subscriber; - -use GuzzleHttp\Transaction; -use GuzzleHttp\Client; -use GuzzleHttp\Cookie\CookieJar; -use GuzzleHttp\Event\CompleteEvent; -use GuzzleHttp\Message\Request; -use GuzzleHttp\Message\Response; -use GuzzleHttp\Subscriber\Cookie; -use GuzzleHttp\Subscriber\History; -use GuzzleHttp\Subscriber\Mock; - -/** - * @covers GuzzleHttp\Subscriber\Cookie - */ -class CookieTest extends \PHPUnit_Framework_TestCase -{ - public function testExtractsAndStoresCookies() - { - $request = new Request('GET', '/'); - $response = new Response(200); - $mock = $this->getMockBuilder('GuzzleHttp\Cookie\CookieJar') - ->setMethods(array('extractCookies')) - ->getMock(); - - $mock->expects($this->exactly(1)) - ->method('extractCookies') - ->with($request, $response); - - $plugin = new Cookie($mock); - $t = new Transaction(new Client(), $request); - $t->response = $response; - $plugin->onComplete(new CompleteEvent($t)); - } - - public function testProvidesCookieJar() - { - $jar = new CookieJar(); - $plugin = new Cookie($jar); - $this->assertSame($jar, $plugin->getCookieJar()); - } - - public function testCookiesAreExtractedFromRedirectResponses() - { - $jar = new CookieJar(); - $cookie = new Cookie($jar); - $history = new History(); - $mock = new Mock([ - "HTTP/1.1 302 Moved Temporarily\r\n" . - "Set-Cookie: test=583551; Domain=www.foo.com; 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" - ]); - $client = new Client(['base_url' => 'http://www.foo.com']); - $client->getEmitter()->attach($cookie); - $client->getEmitter()->attach($mock); - $client->getEmitter()->attach($history); - - $client->get(); - $request = $client->createRequest('GET', '/'); - $client->send($request); - - $this->assertEquals('test=583551', $request->getHeader('Cookie')); - $requests = $history->getRequests(); - // Confirm subsequent requests have the cookie. - $this->assertEquals('test=583551', $requests[2]->getHeader('Cookie')); - // Confirm the redirected request has the cookie. - $this->assertEquals('test=583551', $requests[1]->getHeader('Cookie')); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Subscriber/HistoryTest.php b/core/vendor/guzzlehttp/guzzle/tests/Subscriber/HistoryTest.php deleted file mode 100644 index d28e301cd06170a3033541916765ca0b7b2c70ce..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Subscriber/HistoryTest.php +++ /dev/null @@ -1,140 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Subscriber; - -use GuzzleHttp\Transaction; -use GuzzleHttp\Client; -use GuzzleHttp\Event\CompleteEvent; -use GuzzleHttp\Event\ErrorEvent; -use GuzzleHttp\Exception\RequestException; -use GuzzleHttp\Message\Request; -use GuzzleHttp\Message\Response; -use GuzzleHttp\Stream\Stream; -use GuzzleHttp\Subscriber\History; -use GuzzleHttp\Subscriber\Mock; - -/** - * @covers GuzzleHttp\Subscriber\History - */ -class HistoryTest extends \PHPUnit_Framework_TestCase -{ - public function testAddsForErrorEvent() - { - $request = new Request('GET', '/'); - $response = new Response(400); - $t = new Transaction(new Client(), $request); - $t->response = $response; - $e = new RequestException('foo', $request, $response); - $ev = new ErrorEvent($t, $e); - $h = new History(2); - $h->onError($ev); - // Only tracks when no response is present - $this->assertEquals([], $h->getRequests()); - } - - public function testLogsConnectionErrors() - { - $request = new Request('GET', '/'); - $t = new Transaction(new Client(), $request); - $e = new RequestException('foo', $request); - $ev = new ErrorEvent($t, $e); - $h = new History(); - $h->onError($ev); - $this->assertEquals([$request], $h->getRequests()); - } - - public function testMaintainsLimitValue() - { - $request = new Request('GET', '/'); - $response = new Response(200); - $t = new Transaction(new Client(), $request); - $t->response = $response; - $ev = new CompleteEvent($t); - $h = new History(2); - $h->onComplete($ev); - $h->onComplete($ev); - $h->onComplete($ev); - $this->assertEquals(2, count($h)); - $this->assertSame($request, $h->getLastRequest()); - $this->assertSame($response, $h->getLastResponse()); - foreach ($h as $trans) { - $this->assertInstanceOf('GuzzleHttp\Message\RequestInterface', $trans['request']); - $this->assertInstanceOf('GuzzleHttp\Message\ResponseInterface', $trans['response']); - } - return $h; - } - - /** - * @depends testMaintainsLimitValue - */ - public function testClearsHistory($h) - { - $this->assertEquals(2, count($h)); - $h->clear(); - $this->assertEquals(0, count($h)); - } - - public function testWorksWithMock() - { - $client = new Client(['base_url' => 'http://localhost/']); - $h = new History(); - $client->getEmitter()->attach($h); - $mock = new Mock([new Response(200), new Response(201), new Response(202)]); - $client->getEmitter()->attach($mock); - $request = $client->createRequest('GET', '/'); - $client->send($request); - $request->setMethod('PUT'); - $client->send($request); - $request->setMethod('POST'); - $client->send($request); - $this->assertEquals(3, count($h)); - - $result = implode("\n", array_map(function ($line) { - return strpos($line, 'User-Agent') === 0 - ? 'User-Agent:' - : trim($line); - }, explode("\n", $h))); - - $this->assertEquals("> GET / HTTP/1.1 -Host: localhost -User-Agent: - -< HTTP/1.1 200 OK - -> PUT / HTTP/1.1 -Host: localhost -User-Agent: - -< HTTP/1.1 201 Created - -> POST / HTTP/1.1 -Host: localhost -User-Agent: - -< HTTP/1.1 202 Accepted -", $result); - } - - public function testCanCastToString() - { - $client = new Client(['base_url' => 'http://localhost/']); - $h = new History(); - $client->getEmitter()->attach($h); - - $mock = new Mock(array( - new Response(301, array('Location' => '/redirect1', 'Content-Length' => 0)), - new Response(307, array('Location' => '/redirect2', 'Content-Length' => 0)), - new Response(200, array('Content-Length' => '2'), Stream::factory('HI')) - )); - - $client->getEmitter()->attach($mock); - $request = $client->createRequest('GET', '/'); - $client->send($request); - $this->assertEquals(3, count($h)); - - $h = str_replace("\r", '', $h); - $this->assertContains("> GET / HTTP/1.1\nHost: localhost\nUser-Agent:", $h); - $this->assertContains("< HTTP/1.1 301 Moved Permanently\nLocation: /redirect1", $h); - $this->assertContains("< HTTP/1.1 307 Temporary Redirect\nLocation: /redirect2", $h); - $this->assertContains("< HTTP/1.1 200 OK\nContent-Length: 2\n\nHI", $h); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Subscriber/HttpErrorTest.php b/core/vendor/guzzlehttp/guzzle/tests/Subscriber/HttpErrorTest.php deleted file mode 100644 index b0266340c4c58e67ba93ec0451e6c393726c8297..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Subscriber/HttpErrorTest.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Message; - -use GuzzleHttp\Client; -use GuzzleHttp\Event\CompleteEvent; -use GuzzleHttp\Message\Request; -use GuzzleHttp\Message\Response; -use GuzzleHttp\Subscriber\HttpError; -use GuzzleHttp\Transaction; -use GuzzleHttp\Subscriber\Mock; - -/** - * @covers GuzzleHttp\Subscriber\HttpError - */ -class HttpErrorTest extends \PHPUnit_Framework_TestCase -{ - public function testIgnoreSuccessfulRequests() - { - $event = $this->getEvent(); - $event->intercept(new Response(200)); - (new HttpError())->onComplete($event); - } - - /** - * @expectedException \GuzzleHttp\Exception\ClientException - */ - public function testThrowsClientExceptionOnFailure() - { - $event = $this->getEvent(); - $event->intercept(new Response(403)); - (new HttpError())->onComplete($event); - } - - /** - * @expectedException \GuzzleHttp\Exception\ServerException - */ - public function testThrowsServerExceptionOnFailure() - { - $event = $this->getEvent(); - $event->intercept(new Response(500)); - (new HttpError())->onComplete($event); - } - - private function getEvent() - { - return new CompleteEvent(new Transaction(new Client(), new Request('PUT', '/'))); - } - - /** - * @expectedException \GuzzleHttp\Exception\ClientException - */ - public function testFullTransaction() - { - $client = new Client(); - $client->getEmitter()->attach(new Mock([ - new Response(403) - ])); - $client->get('http://httpbin.org'); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Subscriber/MockTest.php b/core/vendor/guzzlehttp/guzzle/tests/Subscriber/MockTest.php deleted file mode 100644 index 5e8209396f294ea3352dd3da4f75b2a22a42f917..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Subscriber/MockTest.php +++ /dev/null @@ -1,192 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Subscriber; - -use GuzzleHttp\Client; -use GuzzleHttp\Subscriber\Mock; -use GuzzleHttp\Message\FutureResponse; -use GuzzleHttp\Transaction; -use GuzzleHttp\Event\BeforeEvent; -use GuzzleHttp\Exception\RequestException; -use GuzzleHttp\Message\MessageFactory; -use GuzzleHttp\Message\Request; -use GuzzleHttp\Message\Response; -use GuzzleHttp\Stream\Stream; -use React\Promise\Deferred; - -/** - * @covers GuzzleHttp\Subscriber\Mock - */ -class MockTest extends \PHPUnit_Framework_TestCase -{ - public static function createFuture( - callable $wait, - callable $cancel = null - ) { - $deferred = new Deferred(); - return new FutureResponse( - $deferred->promise(), - function () use ($deferred, $wait) { - $deferred->resolve($wait()); - }, - $cancel - ); - } - - public function testDescribesSubscribedEvents() - { - $mock = new Mock(); - $this->assertInternalType('array', $mock->getEvents()); - } - - public function testIsCountable() - { - $plugin = new Mock(); - $plugin->addResponse((new MessageFactory())->fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); - $this->assertEquals(1, count($plugin)); - } - - public function testCanClearQueue() - { - $plugin = new Mock(); - $plugin->addResponse((new MessageFactory())->fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); - $plugin->clearQueue(); - $this->assertEquals(0, count($plugin)); - } - - public function testRetrievesResponsesFromFiles() - { - $tmp = tempnam('/tmp', 'tfile'); - file_put_contents($tmp, "HTTP/1.1 201 OK\r\nContent-Length: 0\r\n\r\n"); - $plugin = new Mock(); - $plugin->addResponse($tmp); - unlink($tmp); - $this->assertEquals(1, count($plugin)); - $q = $this->readAttribute($plugin, 'queue'); - $this->assertEquals(201, $q[0]->getStatusCode()); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testThrowsExceptionWhenInvalidResponse() - { - (new Mock())->addResponse(false); - } - - public function testAddsMockResponseToRequestFromClient() - { - $response = new Response(200); - $t = new Transaction(new Client(), new Request('GET', '/')); - $m = new Mock([$response]); - $ev = new BeforeEvent($t); - $m->onBefore($ev); - $this->assertSame($response, $t->response); - } - - /** - * @expectedException \OutOfBoundsException - */ - public function testUpdateThrowsExceptionWhenEmpty() - { - $p = new Mock(); - $ev = new BeforeEvent(new Transaction(new Client(), new Request('GET', '/'))); - $p->onBefore($ev); - } - - public function testReadsBodiesFromMockedRequests() - { - $m = new Mock([new Response(200)]); - $client = new Client(['base_url' => 'http://test.com']); - $client->getEmitter()->attach($m); - $body = Stream::factory('foo'); - $client->put('/', ['body' => $body]); - $this->assertEquals(3, $body->tell()); - } - - public function testCanMockBadRequestExceptions() - { - $client = new Client(['base_url' => 'http://test.com']); - $request = $client->createRequest('GET', '/'); - $ex = new RequestException('foo', $request); - $mock = new Mock([$ex]); - $this->assertCount(1, $mock); - $request->getEmitter()->attach($mock); - - try { - $client->send($request); - $this->fail('Did not dequeue an exception'); - } catch (RequestException $e) { - $this->assertSame($e, $ex); - $this->assertSame($request, $ex->getRequest()); - } - } - - public function testCanMockFutureResponses() - { - $client = new Client(['base_url' => 'http://test.com']); - $request = $client->createRequest('GET', '/', ['future' => true]); - $response = new Response(200); - $future = self::createFuture(function () use ($response) { - return $response; - }); - $mock = new Mock([$future]); - $this->assertCount(1, $mock); - $request->getEmitter()->attach($mock); - $res = $client->send($request); - $this->assertSame($future, $res); - $this->assertFalse($this->readAttribute($res, 'isRealized')); - $this->assertSame($response, $res->wait()); - } - - public function testCanMockExceptionFutureResponses() - { - $client = new Client(['base_url' => 'http://test.com']); - $request = $client->createRequest('GET', '/', ['future' => true]); - $future = self::createFuture(function () use ($request) { - throw new RequestException('foo', $request); - }); - - $mock = new Mock([$future]); - $request->getEmitter()->attach($mock); - $response = $client->send($request); - $this->assertSame($future, $response); - $this->assertFalse($this->readAttribute($response, 'isRealized')); - - try { - $response->wait(); - $this->fail('Did not throw'); - } catch (RequestException $e) { - $this->assertContains('foo', $e->getMessage()); - } - } - - public function testCanMockFailedFutureResponses() - { - $client = new Client(['base_url' => 'http://test.com']); - $request = $client->createRequest('GET', '/', ['future' => true]); - - // The first mock will be a mocked future response. - $future = self::createFuture(function () use ($client) { - // When dereferenced, we will set a mocked response and send - // another request. - $client->get('http://httpbin.org', ['events' => [ - 'before' => function (BeforeEvent $e) { - $e->intercept(new Response(404)); - } - ]]); - }); - - $mock = new Mock([$future]); - $request->getEmitter()->attach($mock); - $response = $client->send($request); - $this->assertSame($future, $response); - $this->assertFalse($this->readAttribute($response, 'isRealized')); - - try { - $response->wait(); - $this->fail('Did not throw'); - } catch (RequestException $e) { - $this->assertEquals(404, $e->getResponse()->getStatusCode()); - } - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Subscriber/PrepareTest.php b/core/vendor/guzzlehttp/guzzle/tests/Subscriber/PrepareTest.php deleted file mode 100644 index d07fdb44c04fa9c2268ca59ae6f624c9aa0ae3e3..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Subscriber/PrepareTest.php +++ /dev/null @@ -1,213 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Message; - -use GuzzleHttp\Message\Response; -use GuzzleHttp\Tests\Server; -use GuzzleHttp\Transaction; -use GuzzleHttp\Client; -use GuzzleHttp\Event\BeforeEvent; -use GuzzleHttp\Message\Request; -use GuzzleHttp\Stream\NoSeekStream; -use GuzzleHttp\Stream\Stream; -use GuzzleHttp\Subscriber\Prepare; - -/** - * @covers GuzzleHttp\Subscriber\Prepare - */ -class PrepareTest extends \PHPUnit_Framework_TestCase -{ - public function testIgnoresRequestsWithNoBody() - { - $s = new Prepare(); - $t = $this->getTrans(); - $s->onBefore(new BeforeEvent($t)); - $this->assertFalse($t->request->hasHeader('Expect')); - } - - public function testAppliesPostBody() - { - $s = new Prepare(); - $t = $this->getTrans(); - $p = $this->getMockBuilder('GuzzleHttp\Post\PostBody') - ->setMethods(['applyRequestHeaders']) - ->getMockForAbstractClass(); - $p->expects($this->once()) - ->method('applyRequestHeaders'); - $t->request->setBody($p); - $s->onBefore(new BeforeEvent($t)); - } - - public function testAddsExpectHeaderWithTrue() - { - $s = new Prepare(); - $t = $this->getTrans(); - $t->request->getConfig()->set('expect', true); - $t->request->setBody(Stream::factory('foo')); - $s->onBefore(new BeforeEvent($t)); - $this->assertEquals('100-Continue', $t->request->getHeader('Expect')); - } - - public function testAddsExpectHeaderBySize() - { - $s = new Prepare(); - $t = $this->getTrans(); - $t->request->getConfig()->set('expect', 2); - $t->request->setBody(Stream::factory('foo')); - $s->onBefore(new BeforeEvent($t)); - $this->assertTrue($t->request->hasHeader('Expect')); - } - - public function testDoesNotModifyExpectHeaderIfPresent() - { - $s = new Prepare(); - $t = $this->getTrans(); - $t->request->setHeader('Expect', 'foo'); - $t->request->setBody(Stream::factory('foo')); - $s->onBefore(new BeforeEvent($t)); - $this->assertEquals('foo', $t->request->getHeader('Expect')); - } - - public function testDoesAddExpectHeaderWhenSetToFalse() - { - $s = new Prepare(); - $t = $this->getTrans(); - $t->request->getConfig()->set('expect', false); - $t->request->setBody(Stream::factory('foo')); - $s->onBefore(new BeforeEvent($t)); - $this->assertFalse($t->request->hasHeader('Expect')); - } - - public function testDoesNotAddExpectHeaderBySize() - { - $s = new Prepare(); - $t = $this->getTrans(); - $t->request->getConfig()->set('expect', 10); - $t->request->setBody(Stream::factory('foo')); - $s->onBefore(new BeforeEvent($t)); - $this->assertFalse($t->request->hasHeader('Expect')); - } - - public function testAddsExpectHeaderForNonSeekable() - { - $s = new Prepare(); - $t = $this->getTrans(); - $t->request->setBody(new NoSeekStream(Stream::factory('foo'))); - $s->onBefore(new BeforeEvent($t)); - $this->assertTrue($t->request->hasHeader('Expect')); - } - - public function testRemovesContentLengthWhenSendingWithChunked() - { - $s = new Prepare(); - $t = $this->getTrans(); - $t->request->setBody(Stream::factory('foo')); - $t->request->setHeader('Transfer-Encoding', 'chunked'); - $s->onBefore(new BeforeEvent($t)); - $this->assertFalse($t->request->hasHeader('Content-Length')); - } - - public function testUsesProvidedContentLengthAndRemovesXferEncoding() - { - $s = new Prepare(); - $t = $this->getTrans(); - $t->request->setBody(Stream::factory('foo')); - $t->request->setHeader('Content-Length', '3'); - $t->request->setHeader('Transfer-Encoding', 'chunked'); - $s->onBefore(new BeforeEvent($t)); - $this->assertEquals(3, $t->request->getHeader('Content-Length')); - $this->assertFalse($t->request->hasHeader('Transfer-Encoding')); - } - - public function testSetsContentTypeIfPossibleFromStream() - { - $body = $this->getMockBody(); - $sub = new Prepare(); - $t = $this->getTrans(); - $t->request->setBody($body); - $sub->onBefore(new BeforeEvent($t)); - $this->assertEquals( - 'image/jpeg', - $t->request->getHeader('Content-Type') - ); - $this->assertEquals(4, $t->request->getHeader('Content-Length')); - } - - public function testDoesNotOverwriteExistingContentType() - { - $s = new Prepare(); - $t = $this->getTrans(); - $t->request->setBody($this->getMockBody()); - $t->request->setHeader('Content-Type', 'foo/baz'); - $s->onBefore(new BeforeEvent($t)); - $this->assertEquals( - 'foo/baz', - $t->request->getHeader('Content-Type') - ); - } - - public function testSetsContentLengthIfPossible() - { - $s = new Prepare(); - $t = $this->getTrans(); - $t->request->setBody($this->getMockBody()); - $s->onBefore(new BeforeEvent($t)); - $this->assertEquals(4, $t->request->getHeader('Content-Length')); - } - - public function testSetsTransferEncodingChunkedIfNeeded() - { - $r = new Request('PUT', '/'); - $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') - ->setMethods(['getSize']) - ->getMockForAbstractClass(); - $s->expects($this->exactly(2)) - ->method('getSize') - ->will($this->returnValue(null)); - $r->setBody($s); - $t = $this->getTrans($r); - $s = new Prepare(); - $s->onBefore(new BeforeEvent($t)); - $this->assertEquals('chunked', $r->getHeader('Transfer-Encoding')); - } - - public function testContentLengthIntegrationTest() - { - Server::flush(); - Server::enqueue([new Response(200)]); - $client = new Client(['base_url' => Server::$url]); - $this->assertEquals(200, $client->put('/', [ - 'body' => 'test' - ])->getStatusCode()); - $request = Server::received(true)[0]; - $this->assertEquals('PUT', $request->getMethod()); - $this->assertEquals('4', $request->getHeader('Content-Length')); - $this->assertEquals('test', (string) $request->getBody()); - } - - private function getTrans($request = null) - { - return new Transaction( - new Client(), - $request ?: new Request('PUT', '/') - ); - } - - /** - * @return \GuzzleHttp\Stream\StreamInterface - */ - private function getMockBody() - { - $s = $this->getMockBuilder('GuzzleHttp\Stream\MetadataStreamInterface') - ->setMethods(['getMetadata', 'getSize']) - ->getMockForAbstractClass(); - $s->expects($this->any()) - ->method('getMetadata') - ->with('uri') - ->will($this->returnValue('/foo/baz/bar.jpg')); - $s->expects($this->exactly(2)) - ->method('getSize') - ->will($this->returnValue(4)); - - return $s; - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Subscriber/RedirectTest.php b/core/vendor/guzzlehttp/guzzle/tests/Subscriber/RedirectTest.php deleted file mode 100644 index 293cfc217aa58bd9aeff7bf402cc4283e471ef96..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/Subscriber/RedirectTest.php +++ /dev/null @@ -1,288 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Plugin\Redirect; - -use GuzzleHttp\Client; -use GuzzleHttp\Subscriber\History; -use GuzzleHttp\Subscriber\Mock; - -/** - * @covers GuzzleHttp\Subscriber\Redirect - */ -class RedirectTest extends \PHPUnit_Framework_TestCase -{ - public function testRedirectsRequests() - { - $mock = new Mock(); - $history = new History(); - $mock->addMultiple([ - "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n", - "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect2\r\nContent-Length: 0\r\n\r\n", - "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", - ]); - - $client = new Client(['base_url' => 'http://test.com']); - $client->getEmitter()->attach($history); - $client->getEmitter()->attach($mock); - - $request = $client->createRequest('GET', '/foo'); - // Ensure "end" is called only once - $called = 0; - $request->getEmitter()->on('end', function () use (&$called) { - $called++; - }); - $response = $client->send($request); - - $this->assertEquals(200, $response->getStatusCode()); - $this->assertContains('/redirect2', $response->getEffectiveUrl()); - - // Ensure that two requests were sent - $requests = $history->getRequests(true); - - $this->assertEquals('/foo', $requests[0]->getPath()); - $this->assertEquals('GET', $requests[0]->getMethod()); - $this->assertEquals('/redirect1', $requests[1]->getPath()); - $this->assertEquals('GET', $requests[1]->getMethod()); - $this->assertEquals('/redirect2', $requests[2]->getPath()); - $this->assertEquals('GET', $requests[2]->getMethod()); - - $this->assertEquals(1, $called); - } - - /** - * @expectedException \GuzzleHttp\Exception\TooManyRedirectsException - * @expectedExceptionMessage Will not follow more than - */ - public function testCanLimitNumberOfRedirects() - { - $mock = new Mock([ - "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n", - "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect2\r\nContent-Length: 0\r\n\r\n", - "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect3\r\nContent-Length: 0\r\n\r\n", - "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect4\r\nContent-Length: 0\r\n\r\n", - "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect5\r\nContent-Length: 0\r\n\r\n", - "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect6\r\nContent-Length: 0\r\n\r\n" - ]); - $client = new Client(); - $client->getEmitter()->attach($mock); - $client->get('http://www.example.com/foo'); - } - - public function testDefaultBehaviorIsToRedirectWithGetForEntityEnclosingRequests() - { - $h = new History(); - $mock = new Mock([ - "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", - "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", - "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", - ]); - $client = new Client(); - $client->getEmitter()->attach($mock); - $client->getEmitter()->attach($h); - $client->post('http://test.com/foo', [ - 'headers' => ['X-Baz' => 'bar'], - 'body' => 'testing' - ]); - - $requests = $h->getRequests(true); - $this->assertEquals('POST', $requests[0]->getMethod()); - $this->assertEquals('GET', $requests[1]->getMethod()); - $this->assertEquals('bar', (string) $requests[1]->getHeader('X-Baz')); - $this->assertEquals('GET', $requests[2]->getMethod()); - } - - public function testCanRedirectWithStrictRfcCompliance() - { - $h = new History(); - $mock = new Mock([ - "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", - "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", - "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", - ]); - $client = new Client(['base_url' => 'http://test.com']); - $client->getEmitter()->attach($mock); - $client->getEmitter()->attach($h); - $client->post('/foo', [ - 'headers' => ['X-Baz' => 'bar'], - 'body' => 'testing', - 'allow_redirects' => ['max' => 10, 'strict' => true] - ]); - - $requests = $h->getRequests(true); - $this->assertEquals('POST', $requests[0]->getMethod()); - $this->assertEquals('POST', $requests[1]->getMethod()); - $this->assertEquals('bar', (string) $requests[1]->getHeader('X-Baz')); - $this->assertEquals('POST', $requests[2]->getMethod()); - } - - public function testRewindsStreamWhenRedirectingIfNeeded() - { - $h = new History(); - $mock = new Mock([ - "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", - "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", - ]); - $client = new Client(['base_url' => 'http://test.com']); - $client->getEmitter()->attach($mock); - $client->getEmitter()->attach($h); - - $body = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') - ->setMethods(['seek', 'read', 'eof', 'tell']) - ->getMockForAbstractClass(); - $body->expects($this->once())->method('tell')->will($this->returnValue(1)); - $body->expects($this->once())->method('seek')->will($this->returnValue(true)); - $body->expects($this->any())->method('eof')->will($this->returnValue(true)); - $body->expects($this->any())->method('read')->will($this->returnValue('foo')); - $client->post('/foo', [ - 'body' => $body, - 'allow_redirects' => ['max' => 5, 'strict' => true] - ]); - } - - /** - * @expectedException \GuzzleHttp\Exception\CouldNotRewindStreamException - * @expectedExceptionMessage Unable to rewind the non-seekable request body after redirecting - */ - public function testThrowsExceptionWhenStreamCannotBeRewound() - { - $h = new History(); - $mock = new Mock([ - "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", - "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", - ]); - $client = new Client(); - $client->getEmitter()->attach($mock); - $client->getEmitter()->attach($h); - - $body = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') - ->setMethods(['seek', 'read', 'eof', 'tell']) - ->getMockForAbstractClass(); - $body->expects($this->once())->method('tell')->will($this->returnValue(1)); - $body->expects($this->once())->method('seek')->will($this->returnValue(false)); - $body->expects($this->any())->method('eof')->will($this->returnValue(true)); - $body->expects($this->any())->method('read')->will($this->returnValue('foo')); - $client->post('http://example.com/foo', [ - 'body' => $body, - 'allow_redirects' => ['max' => 10, 'strict' => true] - ]); - } - - public function testRedirectsCanBeDisabledPerRequest() - { - $client = new Client(['base_url' => 'http://test.com']); - $client->getEmitter()->attach(new Mock([ - "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", - "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", - ])); - $response = $client->put('/', ['body' => 'test', 'allow_redirects' => false]); - $this->assertEquals(301, $response->getStatusCode()); - } - - public function testCanRedirectWithNoLeadingSlashAndQuery() - { - $h = new History(); - $client = new Client(['base_url' => 'http://www.foo.com']); - $client->getEmitter()->attach(new Mock([ - "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect?foo=bar\r\nContent-Length: 0\r\n\r\n", - "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", - ])); - $client->getEmitter()->attach($h); - $client->get('?foo=bar'); - $requests = $h->getRequests(true); - $this->assertEquals('http://www.foo.com?foo=bar', $requests[0]->getUrl()); - $this->assertEquals('http://www.foo.com/redirect?foo=bar', $requests[1]->getUrl()); - } - - public function testHandlesRedirectsWithSpacesProperly() - { - $client = new Client(['base_url' => 'http://www.foo.com']); - $client->getEmitter()->attach(new Mock([ - "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect 1\r\nContent-Length: 0\r\n\r\n", - "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" - ])); - $h = new History(); - $client->getEmitter()->attach($h); - $client->get('/foo'); - $reqs = $h->getRequests(true); - $this->assertEquals('/redirect%201', $reqs[1]->getResource()); - } - - public function testAddsRefererWhenPossible() - { - $client = new Client(['base_url' => 'http://www.foo.com']); - $client->getEmitter()->attach(new Mock([ - "HTTP/1.1 301 Moved Permanently\r\nLocation: /bar\r\nContent-Length: 0\r\n\r\n", - "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" - ])); - $h = new History(); - $client->getEmitter()->attach($h); - $client->get('/foo', ['allow_redirects' => ['max' => 5, 'referer' => true]]); - $reqs = $h->getRequests(true); - $this->assertEquals('http://www.foo.com/foo', $reqs[1]->getHeader('Referer')); - } - - public function testDoesNotAddRefererWhenChangingProtocols() - { - $client = new Client(['base_url' => 'https://www.foo.com']); - $client->getEmitter()->attach(new Mock([ - "HTTP/1.1 301 Moved Permanently\r\n" - . "Location: http://www.foo.com/foo\r\n" - . "Content-Length: 0\r\n\r\n", - "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" - ])); - $h = new History(); - $client->getEmitter()->attach($h); - $client->get('/foo', ['allow_redirects' => ['max' => 5, 'referer' => true]]); - $reqs = $h->getRequests(true); - $this->assertFalse($reqs[1]->hasHeader('Referer')); - } - - public function testRedirectsWithGetOn303() - { - $h = new History(); - $mock = new Mock([ - "HTTP/1.1 303 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", - "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", - ]); - $client = new Client(); - $client->getEmitter()->attach($mock); - $client->getEmitter()->attach($h); - $client->post('http://test.com/foo', ['body' => 'testing']); - $requests = $h->getRequests(true); - $this->assertEquals('POST', $requests[0]->getMethod()); - $this->assertEquals('GET', $requests[1]->getMethod()); - } - - public function testRelativeLinkBasedLatestRequest() - { - $client = new Client(['base_url' => 'http://www.foo.com']); - $client->getEmitter()->attach(new Mock([ - "HTTP/1.1 301 Moved Permanently\r\nLocation: http://www.bar.com\r\nContent-Length: 0\r\n\r\n", - "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", - "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" - ])); - $response = $client->get('/'); - $this->assertEquals( - 'http://www.bar.com/redirect', - $response->getEffectiveUrl() - ); - } - - /** - * @expectedException \GuzzleHttp\Exception\BadResponseException - * @expectedExceptionMessage Redirect URL, https://foo.com/redirect2, does not use one of the allowed redirect protocols: http - */ - public function testThrowsWhenRedirectingToInvalidUrlProtocol() - { - $mock = new Mock([ - "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n", - "HTTP/1.1 301 Moved Permanently\r\nLocation: https://foo.com/redirect2\r\nContent-Length: 0\r\n\r\n" - ]); - $client = new Client(); - $client->getEmitter()->attach($mock); - $client->get('http://www.example.com/foo', [ - 'allow_redirects' => [ - 'protocols' => ['http'] - ] - ]); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/TransactionTest.php b/core/vendor/guzzlehttp/guzzle/tests/TransactionTest.php deleted file mode 100644 index 42965b1b5685ec8e260216db894e99ba4174e6f7..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/TransactionTest.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php -namespace GuzzleHttp\Tests; - -use GuzzleHttp\Client; -use GuzzleHttp\Message\Request; -use GuzzleHttp\Message\Response; -use GuzzleHttp\Transaction; - -class TransactionTest extends \PHPUnit_Framework_TestCase -{ - public function testHoldsData() - { - $client = new Client(); - $request = new Request('GET', 'http://www.foo.com'); - $t = new Transaction($client, $request); - $this->assertSame($client, $t->client); - $this->assertSame($request, $t->request); - $response = new Response(200); - $t->response = $response; - $this->assertSame($response, $t->response); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/UriTemplateTest.php b/core/vendor/guzzlehttp/guzzle/tests/UriTemplateTest.php deleted file mode 100644 index 3f7a7f063b7ffa0fe14c4b14851475a534ca4477..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/UriTemplateTest.php +++ /dev/null @@ -1,202 +0,0 @@ -<?php - -namespace GuzzleHttp\Tests; - -use GuzzleHttp\UriTemplate; - -/** - * @covers GuzzleHttp\UriTemplate - */ -class UriTemplateTest extends \PHPUnit_Framework_TestCase -{ - /** - * @return array - */ - public function templateProvider() - { - $params = array( - 'var' => 'value', - 'hello' => 'Hello World!', - 'empty' => '', - 'path' => '/foo/bar', - 'x' => '1024', - 'y' => '768', - 'null' => null, - 'list' => array('red', 'green', 'blue'), - 'keys' => array( - "semi" => ';', - "dot" => '.', - "comma" => ',' - ), - 'empty_keys' => array(), - ); - - return array_map(function ($t) use ($params) { - $t[] = $params; - return $t; - }, array( - array('foo', 'foo'), - array('{var}', 'value'), - array('{hello}', 'Hello%20World%21'), - array('{+var}', 'value'), - array('{+hello}', 'Hello%20World!'), - array('{+path}/here', '/foo/bar/here'), - array('here?ref={+path}', 'here?ref=/foo/bar'), - array('X{#var}', 'X#value'), - array('X{#hello}', 'X#Hello%20World!'), - array('map?{x,y}', 'map?1024,768'), - array('{x,hello,y}', '1024,Hello%20World%21,768'), - array('{+x,hello,y}', '1024,Hello%20World!,768'), - array('{+path,x}/here', '/foo/bar,1024/here'), - array('{#x,hello,y}', '#1024,Hello%20World!,768'), - array('{#path,x}/here', '#/foo/bar,1024/here'), - array('X{.var}', 'X.value'), - array('X{.x,y}', 'X.1024.768'), - array('{/var}', '/value'), - array('{/var,x}/here', '/value/1024/here'), - array('{;x,y}', ';x=1024;y=768'), - array('{;x,y,empty}', ';x=1024;y=768;empty'), - array('{?x,y}', '?x=1024&y=768'), - array('{?x,y,empty}', '?x=1024&y=768&empty='), - array('?fixed=yes{&x}', '?fixed=yes&x=1024'), - array('{&x,y,empty}', '&x=1024&y=768&empty='), - array('{var:3}', 'val'), - array('{var:30}', 'value'), - array('{list}', 'red,green,blue'), - array('{list*}', 'red,green,blue'), - array('{keys}', 'semi,%3B,dot,.,comma,%2C'), - array('{keys*}', 'semi=%3B,dot=.,comma=%2C'), - array('{+path:6}/here', '/foo/b/here'), - array('{+list}', 'red,green,blue'), - array('{+list*}', 'red,green,blue'), - array('{+keys}', 'semi,;,dot,.,comma,,'), - array('{+keys*}', 'semi=;,dot=.,comma=,'), - array('{#path:6}/here', '#/foo/b/here'), - array('{#list}', '#red,green,blue'), - array('{#list*}', '#red,green,blue'), - array('{#keys}', '#semi,;,dot,.,comma,,'), - array('{#keys*}', '#semi=;,dot=.,comma=,'), - array('X{.var:3}', 'X.val'), - array('X{.list}', 'X.red,green,blue'), - array('X{.list*}', 'X.red.green.blue'), - array('X{.keys}', 'X.semi,%3B,dot,.,comma,%2C'), - array('X{.keys*}', 'X.semi=%3B.dot=..comma=%2C'), - array('{/var:1,var}', '/v/value'), - array('{/list}', '/red,green,blue'), - array('{/list*}', '/red/green/blue'), - array('{/list*,path:4}', '/red/green/blue/%2Ffoo'), - array('{/keys}', '/semi,%3B,dot,.,comma,%2C'), - array('{/keys*}', '/semi=%3B/dot=./comma=%2C'), - array('{;hello:5}', ';hello=Hello'), - array('{;list}', ';list=red,green,blue'), - array('{;list*}', ';list=red;list=green;list=blue'), - array('{;keys}', ';keys=semi,%3B,dot,.,comma,%2C'), - array('{;keys*}', ';semi=%3B;dot=.;comma=%2C'), - array('{?var:3}', '?var=val'), - array('{?list}', '?list=red,green,blue'), - array('{?list*}', '?list=red&list=green&list=blue'), - array('{?keys}', '?keys=semi,%3B,dot,.,comma,%2C'), - array('{?keys*}', '?semi=%3B&dot=.&comma=%2C'), - array('{&var:3}', '&var=val'), - array('{&list}', '&list=red,green,blue'), - array('{&list*}', '&list=red&list=green&list=blue'), - array('{&keys}', '&keys=semi,%3B,dot,.,comma,%2C'), - array('{&keys*}', '&semi=%3B&dot=.&comma=%2C'), - array('{.null}', ''), - array('{.null,var}', '.value'), - array('X{.empty_keys*}', 'X'), - array('X{.empty_keys}', 'X'), - // Test that missing expansions are skipped - array('test{&missing*}', 'test'), - // Test that multiple expansions can be set - array('http://{var}/{var:2}{?keys*}', 'http://value/va?semi=%3B&dot=.&comma=%2C'), - // Test more complex query string stuff - array('http://www.test.com{+path}{?var,keys*}', 'http://www.test.com/foo/bar?var=value&semi=%3B&dot=.&comma=%2C') - )); - } - - /** - * @dataProvider templateProvider - */ - public function testExpandsUriTemplates($template, $expansion, $params) - { - $uri = new UriTemplate($template); - $this->assertEquals($expansion, $uri->expand($template, $params)); - } - - public function expressionProvider() - { - return array( - array( - '{+var*}', array( - 'operator' => '+', - 'values' => array( - array('value' => 'var', 'modifier' => '*') - ) - ), - ), - array( - '{?keys,var,val}', array( - 'operator' => '?', - 'values' => array( - array('value' => 'keys', 'modifier' => ''), - array('value' => 'var', 'modifier' => ''), - array('value' => 'val', 'modifier' => '') - ) - ), - ), - array( - '{+x,hello,y}', array( - 'operator' => '+', - 'values' => array( - array('value' => 'x', 'modifier' => ''), - array('value' => 'hello', 'modifier' => ''), - array('value' => 'y', 'modifier' => '') - ) - ) - ) - ); - } - - /** - * @dataProvider expressionProvider - */ - public function testParsesExpressions($exp, $data) - { - $template = new UriTemplate($exp); - - // Access the config object - $class = new \ReflectionClass($template); - $method = $class->getMethod('parseExpression'); - $method->setAccessible(true); - - $exp = substr($exp, 1, -1); - $this->assertEquals($data, $method->invokeArgs($template, array($exp))); - } - - /** - * @ticket https://github.com/guzzle/guzzle/issues/90 - */ - public function testAllowsNestedArrayExpansion() - { - $template = new UriTemplate(); - - $result = $template->expand('http://example.com{+path}{/segments}{?query,data*,foo*}', array( - 'path' => '/foo/bar', - 'segments' => array('one', 'two'), - 'query' => 'test', - 'data' => array( - 'more' => array('fun', 'ice cream') - ), - 'foo' => array( - 'baz' => array( - 'bar' => 'fizz', - 'test' => 'buzz' - ), - 'bam' => 'boo' - ) - )); - - $this->assertEquals('http://example.com/foo/bar/one,two?query=test&more%5B0%5D=fun&more%5B1%5D=ice%20cream&baz%5Bbar%5D=fizz&baz%5Btest%5D=buzz&bam=boo', $result); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/UrlTest.php b/core/vendor/guzzlehttp/guzzle/tests/UrlTest.php deleted file mode 100644 index efd374c1e033f0e55a7ae93b84456d4046194e38..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/UrlTest.php +++ /dev/null @@ -1,356 +0,0 @@ -<?php -namespace GuzzleHttp\Tests; - -use GuzzleHttp\Query; -use GuzzleHttp\Url; - -/** - * @covers GuzzleHttp\Url - */ -class UrlTest extends \PHPUnit_Framework_TestCase -{ - const RFC3986_BASE = "http://a/b/c/d;p?q"; - - public function testEmptyUrl() - { - $url = Url::fromString(''); - $this->assertEquals('', (string) $url); - } - - public function testPortIsDeterminedFromScheme() - { - $this->assertEquals(80, Url::fromString('http://www.test.com/')->getPort()); - $this->assertEquals(443, Url::fromString('https://www.test.com/')->getPort()); - $this->assertEquals(21, Url::fromString('ftp://www.test.com/')->getPort()); - $this->assertEquals(8192, Url::fromString('http://www.test.com:8192/')->getPort()); - $this->assertEquals(null, Url::fromString('foo://www.test.com/')->getPort()); - } - - public function testRemovesDefaultPortWhenSettingScheme() - { - $url = Url::fromString('http://www.test.com/'); - $url->setPort(80); - $url->setScheme('https'); - $this->assertEquals(443, $url->getPort()); - } - - public function testCloneCreatesNewInternalObjects() - { - $u1 = Url::fromString('http://www.test.com/'); - $u2 = clone $u1; - $this->assertNotSame($u1->getQuery(), $u2->getQuery()); - } - - public function testValidatesUrlPartsInFactory() - { - $url = Url::fromString('/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::fromString($url); - $this->assertEquals('http://michael:test@test.com/path/123?q=abc#test', (string) $u); - $this->assertTrue($u->isAbsolute()); - } - - public function testAllowsFalsyUrlParts() - { - $url = Url::fromString('http://a:50/0?0#0'); - $this->assertSame('a', $url->getHost()); - $this->assertEquals(50, $url->getPort()); - $this->assertSame('/0', $url->getPath()); - $this->assertEquals('0', (string) $url->getQuery()); - $this->assertSame('0', $url->getFragment()); - $this->assertEquals('http://a:50/0?0#0', (string) $url); - - $url = Url::fromString(''); - $this->assertSame('', (string) $url); - - $url = Url::fromString('0'); - $this->assertSame('0', (string) $url); - } - - public function testBuildsRelativeUrlsWithFalsyParts() - { - $url = Url::buildUrl(['path' => '/0']); - $this->assertSame('/0', $url); - - $url = Url::buildUrl(['path' => '0']); - $this->assertSame('0', $url); - - $url = Url::buildUrl(['host' => '', 'path' => '0']); - $this->assertSame('0', $url); - } - - public function testUrlStoresParts() - { - $url = Url::fromString('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('fragment', $url->getFragment()); - $this->assertEquals('a=1&b=2', (string) $url->getQuery()); - - $this->assertEquals(array( - 'fragment' => 'fragment', - 'host' => 'www.test.com', - 'pass' => 'pass', - 'path' => '/path/path2/', - 'port' => 8081, - 'query' => 'a=1&b=2', - 'scheme' => 'http', - 'user' => 'test' - ), $url->getParts()); - } - - public function testHandlesPathsCorrectly() - { - $url = Url::fromString('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)); - } - - public function testAddsQueryIfPresent() - { - $this->assertEquals('?foo=bar', Url::buildUrl(array( - 'query' => 'foo=bar' - ))); - } - - public function testAddsToPath() - { - // Does nothing here - $url = Url::fromString('http://e.com/base?a=1'); - $url->addPath(false); - $this->assertEquals('http://e.com/base?a=1', $url); - $url = Url::fromString('http://e.com/base?a=1'); - $url->addPath(''); - $this->assertEquals('http://e.com/base?a=1', $url); - $url = Url::fromString('http://e.com/base?a=1'); - $url->addPath('/'); - $this->assertEquals('http://e.com/base?a=1', $url); - $url = Url::fromString('http://e.com/base'); - $url->addPath('0'); - $this->assertEquals('http://e.com/base/0', $url); - - $url = Url::fromString('http://e.com/base?a=1'); - $url->addPath('relative'); - $this->assertEquals('http://e.com/base/relative?a=1', $url); - $url = Url::fromString('http://e.com/base?a=1'); - $url->addPath('/relative'); - $this->assertEquals('http://e.com/base/relative?a=1', $url); - } - - /** - * URL combination data provider - * - * @return array - */ - public function urlCombineDataProvider() - { - return [ - // Specific test cases - ['http://www.example.com/', 'http://www.example.com/', 'http://www.example.com/'], - ['http://www.example.com/path', '/absolute', 'http://www.example.com/absolute'], - ['http://www.example.com/path', '/absolute?q=2', 'http://www.example.com/absolute?q=2'], - ['http://www.example.com/', '?q=1', 'http://www.example.com/?q=1'], - ['http://www.example.com/path', 'http://test.com', 'http://test.com'], - ['http://www.example.com:8080/path', 'http://test.com', 'http://test.com'], - ['http://www.example.com:8080/path', '?q=2#abc', 'http://www.example.com:8080/path?q=2#abc'], - ['http://www.example.com/path', 'http://u:a@www.example.com/', 'http://u:a@www.example.com/'], - ['/path?q=2', 'http://www.test.com/', 'http://www.test.com/path?q=2'], - ['http://api.flickr.com/services/', 'http://www.flickr.com/services/oauth/access_token', 'http://www.flickr.com/services/oauth/access_token'], - ['https://www.example.com/path', '//foo.com/abc', 'https://foo.com/abc'], - ['https://www.example.com/0/', 'relative/foo', 'https://www.example.com/0/relative/foo'], - ['', '0', '0'], - // RFC 3986 test cases - [self::RFC3986_BASE, 'g:h', 'g:h'], - [self::RFC3986_BASE, 'g', 'http://a/b/c/g'], - [self::RFC3986_BASE, './g', 'http://a/b/c/g'], - [self::RFC3986_BASE, 'g/', 'http://a/b/c/g/'], - [self::RFC3986_BASE, '/g', 'http://a/g'], - [self::RFC3986_BASE, '//g', 'http://g'], - [self::RFC3986_BASE, '?y', 'http://a/b/c/d;p?y'], - [self::RFC3986_BASE, 'g?y', 'http://a/b/c/g?y'], - [self::RFC3986_BASE, '#s', 'http://a/b/c/d;p?q#s'], - [self::RFC3986_BASE, 'g#s', 'http://a/b/c/g#s'], - [self::RFC3986_BASE, 'g?y#s', 'http://a/b/c/g?y#s'], - [self::RFC3986_BASE, ';x', 'http://a/b/c/;x'], - [self::RFC3986_BASE, 'g;x', 'http://a/b/c/g;x'], - [self::RFC3986_BASE, 'g;x?y#s', 'http://a/b/c/g;x?y#s'], - [self::RFC3986_BASE, '', self::RFC3986_BASE], - [self::RFC3986_BASE, '.', 'http://a/b/c/'], - [self::RFC3986_BASE, './', 'http://a/b/c/'], - [self::RFC3986_BASE, '..', 'http://a/b/'], - [self::RFC3986_BASE, '../', 'http://a/b/'], - [self::RFC3986_BASE, '../g', 'http://a/b/g'], - [self::RFC3986_BASE, '../..', 'http://a/'], - [self::RFC3986_BASE, '../../', 'http://a/'], - [self::RFC3986_BASE, '../../g', 'http://a/g'], - [self::RFC3986_BASE, '../../../g', 'http://a/g'], - [self::RFC3986_BASE, '../../../../g', 'http://a/g'], - [self::RFC3986_BASE, '/./g', 'http://a/g'], - [self::RFC3986_BASE, '/../g', 'http://a/g'], - [self::RFC3986_BASE, 'g.', 'http://a/b/c/g.'], - [self::RFC3986_BASE, '.g', 'http://a/b/c/.g'], - [self::RFC3986_BASE, 'g..', 'http://a/b/c/g..'], - [self::RFC3986_BASE, '..g', 'http://a/b/c/..g'], - [self::RFC3986_BASE, './../g', 'http://a/b/g'], - [self::RFC3986_BASE, 'foo////g', 'http://a/b/c/foo////g'], - [self::RFC3986_BASE, './g/.', 'http://a/b/c/g/'], - [self::RFC3986_BASE, 'g/./h', 'http://a/b/c/g/h'], - [self::RFC3986_BASE, 'g/../h', 'http://a/b/c/h'], - [self::RFC3986_BASE, 'g;x=1/./y', 'http://a/b/c/g;x=1/y'], - [self::RFC3986_BASE, 'g;x=1/../y', 'http://a/b/c/y'], - [self::RFC3986_BASE, 'http:g', 'http:g'], - ]; - } - - /** - * @dataProvider urlCombineDataProvider - */ - public function testCombinesUrls($a, $b, $c) - { - $this->assertEquals($c, (string) Url::fromString($a)->combine($b)); - } - - public function testHasGettersAndSetters() - { - $url = Url::fromString('http://www.test.com/'); - $url->setHost('example.com'); - $this->assertEquals('example.com', $url->getHost()); - $url->setPort(8080); - $this->assertEquals('8080', $url->getPort()); - $url->setPath('/foo/bar'); - $this->assertEquals('/foo/bar', $url->getPath()); - $url->setPassword('a'); - $this->assertEquals('a', $url->getPassword()); - $url->setUsername('b'); - $this->assertEquals('b', $url->getUsername()); - $url->setFragment('abc'); - $this->assertEquals('abc', $url->getFragment()); - $url->setScheme('https'); - $this->assertEquals('https', $url->getScheme()); - $url->setQuery('a=123'); - $this->assertEquals('a=123', (string) $url->getQuery()); - $this->assertEquals( - 'https://b:a@example.com:8080/foo/bar?a=123#abc', - (string) $url - ); - $url->setQuery(new Query(['b' => 'boo'])); - $this->assertEquals('b=boo', $url->getQuery()); - $this->assertEquals( - 'https://b:a@example.com:8080/foo/bar?b=boo#abc', - (string) $url - ); - - $url->setQuery('a%20=bar!', true); - $this->assertEquals( - 'https://b:a@example.com:8080/foo/bar?a%20=bar!#abc', - (string) $url - ); - } - - public function testSetQueryAcceptsArray() - { - $url = Url::fromString('http://www.test.com'); - $url->setQuery(array('a' => 'b')); - $this->assertEquals('http://www.test.com?a=b', (string) $url); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testQueryMustBeValid() - { - $url = Url::fromString('http://www.test.com'); - $url->setQuery(false); - } - - public function testDefersParsingAndEncodingQueryUntilNecessary() - { - $url = Url::fromString('http://www.test.com'); - // Note that invalid characters are encoded. - $url->setQuery('foo#bar/', true); - $this->assertEquals('http://www.test.com?foo%23bar/', (string) $url); - $this->assertInternalType('string', $this->readAttribute($url, 'query')); - $this->assertEquals('foo%23bar%2F', (string) $url->getQuery()); - $this->assertInstanceOf('GuzzleHttp\Query', $this->readAttribute($url, 'query')); - } - - public function urlProvider() - { - return array( - array('/foo/..', '/'), - array('//foo//..', '//foo/'), - array('/foo//', '/foo//'), - array('/foo/../..', '/'), - array('/foo/../.', '/'), - array('/./foo/..', '/'), - array('/./foo', '/foo'), - array('/./foo/', '/foo/'), - array('*', '*'), - array('/foo', '/foo'), - array('/abc/123/../foo/', '/abc/foo/'), - array('/a/b/c/./../../g', '/a/g'), - array('/b/c/./../../g', '/g'), - array('/b/c/./../../g', '/g'), - array('/c/./../../g', '/g'), - array('/./../../g', '/g'), - array('foo', 'foo'), - ); - } - - /** - * @dataProvider urlProvider - */ - public function testRemoveDotSegments($path, $result) - { - $url = Url::fromString('http://www.example.com'); - $url->setPath($path); - $url->removeDotSegments(); - $this->assertEquals($result, $url->getPath()); - } - - public function testSettingHostWithPortModifiesPort() - { - $url = Url::fromString('http://www.example.com'); - $url->setHost('foo:8983'); - $this->assertEquals('foo', $url->getHost()); - $this->assertEquals(8983, $url->getPort()); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testValidatesUrlCanBeParsed() - { - Url::fromString('foo:////'); - } - - public function testConvertsSpecialCharsInPathWhenCastingToString() - { - $url = Url::fromString('http://foo.com/baz bar?a=b'); - $url->addPath('?'); - $this->assertEquals('http://foo.com/baz%20bar/%3F?a=b', (string) $url); - } - - public function testCorrectlyEncodesPathWithoutDoubleEncoding() - { - $url = Url::fromString('http://foo.com/baz%20 bar:boo/baz!'); - $this->assertEquals('/baz%20%20bar:boo/baz!', $url->getPath()); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/UtilsTest.php b/core/vendor/guzzlehttp/guzzle/tests/UtilsTest.php deleted file mode 100644 index d9bdc071fa5c985c1ae882a520247016747b1d5a..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/UtilsTest.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php -namespace GuzzleHttp\Tests; - -use GuzzleHttp\Utils; - -class UtilsTest extends \PHPUnit_Framework_TestCase -{ - public function testExpandsTemplate() - { - $this->assertEquals( - 'foo/123', - Utils::uriTemplate('foo/{bar}', ['bar' => '123']) - ); - } - - public function noBodyProvider() - { - return [['get'], ['head'], ['delete']]; - } - - public function testJsonDecodes() - { - $this->assertTrue(Utils::jsonDecode('true')); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Unable to parse JSON data: JSON_ERROR_SYNTAX - Syntax error, malformed JSON - */ - public function testJsonDecodesWithErrorMessages() - { - Utils::jsonDecode('!narf!'); - } -} diff --git a/core/vendor/guzzlehttp/guzzle/tests/bootstrap.php b/core/vendor/guzzlehttp/guzzle/tests/bootstrap.php deleted file mode 100644 index 8713f9624ac8aa783979101218ceebfbbe163489..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/bootstrap.php +++ /dev/null @@ -1,11 +0,0 @@ -<?php -require __DIR__ . '/../vendor/autoload.php'; -require __DIR__ . '/../vendor/guzzlehttp/ringphp/tests/Client/Server.php'; - -use GuzzleHttp\Tests\Server; - -Server::start(); - -register_shutdown_function(function () { - Server::stop(); -}); diff --git a/core/vendor/guzzlehttp/guzzle/tests/perf.php b/core/vendor/guzzlehttp/guzzle/tests/perf.php deleted file mode 100644 index 6de89c261496ede82ff3670b33e2dca9db42b785..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/guzzle/tests/perf.php +++ /dev/null @@ -1,61 +0,0 @@ -<?php -/* - * Runs a performance test against the node.js server for both serial and - * parallel requests. Requires PHP 5.5 or greater. - * - * # Basic usage - * make perf - * # With custom options - * REQUESTS=100 PARALLEL=5000 make perf - */ - -require __DIR__ . '/bootstrap.php'; - -use GuzzleHttp\Client; -use GuzzleHttp\Tests\Server; -use GuzzleHttp\Ring\Client\CurlMultiHandler; -use GuzzleHttp\Pool; - -// Wait until the server is responding -Server::wait(); - -// Get custom make variables -$total = isset($_SERVER['REQUESTS']) ? $_SERVER['REQUESTS'] : 1000; -$parallel = isset($_SERVER['PARALLEL']) ? $_SERVER['PARALLEL'] : 100; - -$client = new Client(['base_url' => Server::$url]); - -$t = microtime(true); -for ($i = 0; $i < $total; $i++) { - $client->get('/guzzle-server/perf'); -} -$totalTime = microtime(true) - $t; -$perRequest = ($totalTime / $total) * 1000; -printf("Serial: %f (%f ms / request) %d total\n", - $totalTime, $perRequest, $total); - -// Create a generator used to yield batches of requests -$reqs = function () use ($client, $total) { - for ($i = 0; $i < $total; $i++) { - yield $client->createRequest('GET', '/guzzle-server/perf'); - } -}; - -$t = microtime(true); -Pool::send($client, $reqs(), ['parallel' => $parallel]); -$totalTime = microtime(true) - $t; -$perRequest = ($totalTime / $total) * 1000; -printf("Batch: %f (%f ms / request) %d total with %d in parallel\n", - $totalTime, $perRequest, $total, $parallel); - -$handler = new CurlMultiHandler(['max_handles' => $parallel]); -$client = new Client(['handler' => $handler, 'base_url' => Server::$url]); -$t = microtime(true); -for ($i = 0; $i < $total; $i++) { - $client->get('/guzzle-server/perf'); -} -unset($client); -$totalTime = microtime(true) - $t; -$perRequest = ($totalTime / $total) * 1000; -printf("Future: %f (%f ms / request) %d total\n", - $totalTime, $perRequest, $total); diff --git a/core/vendor/guzzlehttp/promises/.travis.yml b/core/vendor/guzzlehttp/promises/.travis.yml new file mode 100644 index 0000000000000000000000000000000000000000..4f4d2b86b20007a245e817a60b4fb9eb764296f5 --- /dev/null +++ b/core/vendor/guzzlehttp/promises/.travis.yml @@ -0,0 +1,19 @@ +language: php + +php: + - 5.5 + - 5.6 + - 7.0 + - hhvm + +sudo: false + +install: + - travis_retry composer install --no-interaction --prefer-source + +script: make test + +matrix: + allow_failures: + - php: hhvm + fast_finish: true diff --git a/core/vendor/guzzlehttp/promises/CHANGELOG.md b/core/vendor/guzzlehttp/promises/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..2376c057dc1ef476fa8c6d520380f311f8851493 --- /dev/null +++ b/core/vendor/guzzlehttp/promises/CHANGELOG.md @@ -0,0 +1,11 @@ +# CHANGELOG + +## 1.0.1 - 2015-06-24 + +* Updating EachPromise to call next on the underlying promise iterator as late + as possible to ensure that generators that generate new requests based on + callbacks are not iterated until after callbacks are invoked. + +## 1.0.0 - 2015-05-12 + +* Initial release diff --git a/core/vendor/guzzlehttp/ringphp/LICENSE b/core/vendor/guzzlehttp/promises/LICENSE similarity index 94% rename from core/vendor/guzzlehttp/ringphp/LICENSE rename to core/vendor/guzzlehttp/promises/LICENSE index 71d3b783cb5b82e732f4555c5b7839036334607b..581d95f92024be7c805599690867b4d1e2e10f40 100644 --- a/core/vendor/guzzlehttp/ringphp/LICENSE +++ b/core/vendor/guzzlehttp/promises/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com> +Copyright (c) 2015 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/core/vendor/guzzlehttp/streams/Makefile b/core/vendor/guzzlehttp/promises/Makefile similarity index 62% rename from core/vendor/guzzlehttp/streams/Makefile rename to core/vendor/guzzlehttp/promises/Makefile index f4d42849e528e185aaaa1b2d478c541b629601b7..8d5b3ef95eb4679a3998176bcc96fa88f5d60cdd 100644 --- a/core/vendor/guzzlehttp/streams/Makefile +++ b/core/vendor/guzzlehttp/promises/Makefile @@ -1,10 +1,4 @@ -all: clean coverage - -release: tag - git push origin --tags - -tag: - chag tag --sign --debug CHANGELOG.rst +all: clean test test: vendor/bin/phpunit diff --git a/core/vendor/guzzlehttp/promises/README.md b/core/vendor/guzzlehttp/promises/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c6780abd2c8dac28d437c6d4ad9ae9bceedef7c2 --- /dev/null +++ b/core/vendor/guzzlehttp/promises/README.md @@ -0,0 +1,501 @@ +# Guzzle Promises + +[Promises/A+](https://promisesaplus.com/) implementation that handles promise +chaining and resolution iteratively, allowing for "infinite" promise chaining +while keeping the stack size constant. Read [this blog post](https://blog.domenic.me/youre-missing-the-point-of-promises/) +for a general introduction to promises. + +- [Features](#features) +- [Quick start](#quick-start) +- [Synchronous wait](#synchronous-wait) +- [Cancellation](#cancellation) +- [API](#api) + - [Promise](#promise) + - [FulfilledPromise](#fulfilledpromise) + - [RejectedPromise](#rejectedpromise) +- [Promise interop](#promise-interop) +- [Implementation notes](#implementation-notes) + + +# Features + +- [Promises/A+](https://promisesaplus.com/) implementation. +- Promise resolution and chaining is handled iteratively, allowing for + "infinite" promise chaining. +- Promises have a synchronous `wait` method. +- Promises can be cancelled. +- Works with any object that has a `then` function. +- C# style async/await coroutine promises using + `GuzzleHttp\Promise\coroutine()`. + + +# Quick start + +A *promise* represents the eventual result of an asynchronous operation. The +primary way of interacting with a promise is through its `then` method, which +registers callbacks to receive either a promise's eventual value or the reason +why the promise cannot be fulfilled. + + +## Callbacks + +Callbacks are registered with the `then` method by providing an optional +`$onFulfilled` followed by an optional `$onRejected` function. + + +```php +use GuzzleHttp\Promise\Promise; + +$promise = new Promise(); +$promise->then( + // $onFulfilled + function ($value) { + echo 'The promise was fulfilled.'; + }, + // $onRejected + function ($reason) { + echo 'The promise was rejected.'; + } +); +``` + +*Resolving* a promise means that you either fulfill a promise with a *value* or +reject a promise with a *reason*. Resolving a promises triggers callbacks +registered with the promises's `then` method. These callbacks are triggered +only once and in the order in which they were added. + + +## Resolving a promise + +Promises are fulfilled using the `resolve($value)` method. Resolving a promise +with any value other than a `GuzzleHttp\Promise\RejectedPromise` will trigger +all of the onFulfilled callbacks (resolving a promise with a rejected promise +will reject the promise and trigger the `$onRejected` callbacks). + +```php +use GuzzleHttp\Promise\Promise; + +$promise = new Promise(); +$promise + ->then(function ($value) { + // Return a value and don't break the chain + return "Hello, " . $value; + }) + // This then is executed after the first then and receives the value + // returned from the first then. + ->then(function ($value) { + echo $value; + }); + +// Resolving the promise triggers the $onFulfilled callbacks and outputs +// "Hello, reader". +$promise->resolve('reader.'); +``` + + +## Promise forwarding + +Promises can be chained one after the other. Each then in the chain is a new +promise. The return value of of a promise is what's forwarded to the next +promise in the chain. Returning a promise in a `then` callback will cause the +subsequent promises in the chain to only be fulfilled when the returned promise +has been fulfilled. The next promise in the chain will be invoked with the +resolved value of the promise. + +```php +use GuzzleHttp\Promise\Promise; + +$promise = new Promise(); +$nextPromise = new Promise(); + +$promise + ->then(function ($value) use ($nextPromise) { + echo $value; + return $nextPromise; + }) + ->then(function ($value) { + echo $value; + }); + +// Triggers the first callback and outputs "A" +$promise->resolve('A'); +// Triggers the second callback and outputs "B" +$nextPromise->resolve('B'); +``` + +## Promise rejection + +When a promise is rejected, the `$onRejected` callbacks are invoked with the +rejection reason. + +```php +use GuzzleHttp\Promise\Promise; + +$promise = new Promise(); +$promise->then(null, function ($reason) { + echo $reason; +}); + +$promise->reject('Error!'); +// Outputs "Error!" +``` + +## Rejection forwarding + +If an exception is thrown in an `$onRejected` callback, subsequent +`$onRejected` callbacks are invoked with the thrown exception as the reason. + +```php +use GuzzleHttp\Promise\Promise; + +$promise = new Promise(); +$promise->then(null, function ($reason) { + throw new \Exception($reason); +})->then(null, function ($reason) { + assert($reason->getMessage() === 'Error!'); +}); + +$promise->reject('Error!'); +``` + +You can also forward a rejection down the promise chain by returning a +`GuzzleHttp\Promise\RejectedPromise` in either an `$onFulfilled` or +`$onRejected` callback. + +```php +use GuzzleHttp\Promise\Promise; +use GuzzleHttp\Promise\RejectedPromise; + +$promise = new Promise(); +$promise->then(null, function ($reason) { + return new RejectedPromise($reason); +})->then(null, function ($reason) { + assert($reason === 'Error!'); +}); + +$promise->reject('Error!'); +``` + +If an exception is not thrown in a `$onRejected` callback and the callback +does not return a rejected promise, downstream `$onFulfilled` callbacks are +invoked using the value returned from the `$onRejected` callback. + +```php +use GuzzleHttp\Promise\Promise; +use GuzzleHttp\Promise\RejectedPromise; + +$promise = new Promise(); +$promise + ->then(null, function ($reason) { + return "It's ok"; + }) + ->then(function ($value) { + assert($value === "It's ok"); + }); + +$promise->reject('Error!'); +``` + +# Synchronous wait + +You can synchronously force promises to complete using a promise's `wait` +method. When creating a promise, you can provide a wait function that is used +to synchronously force a promise to complete. When a wait function is invoked +it is expected to deliver a value to the promise or reject the promise. If the +wait function does not deliver a value, then an exception is thrown. The wait +function provided to a promise constructor is invoked when the `wait` function +of the promise is called. + +```php +$promise = new Promise(function () use (&$promise) { + $promise->deliver('foo'); +}); + +// Calling wait will return the value of the promise. +echo $promise->wait(); // outputs "foo" +``` + +If an exception is encountered while invoking the wait function of a promise, +the promise is rejected with the exception and the exception is thrown. + +```php +$promise = new Promise(function () use (&$promise) { + throw new \Exception('foo'); +}); + +$promise->wait(); // throws the exception. +``` + +Calling `wait` on a promise that has been fulfilled will not trigger the wait +function. It will simply return the previously delivered value. + +```php +$promise = new Promise(function () { die('this is not called!'); }); +$promise->deliver('foo'); +echo $promise->wait(); // outputs "foo" +``` + +Calling `wait` on a promise that has been rejected will throw an exception. If +the rejection reason is an instance of `\Exception` the reason is thrown. +Otherwise, a `GuzzleHttp\Promise\RejectionException` is thrown and the reason +can be obtained by calling the `getReason` method of the exception. + +```php +$promise = new Promise(); +$promise->reject('foo'); +$promise->wait(); +``` + +> PHP Fatal error: Uncaught exception 'GuzzleHttp\Promise\RejectionException' with message 'The promise was rejected with value: foo' + + +## Unwrapping a promise + +When synchronously waiting on a promise, you are joining the state of the +promise into the current state of execution (i.e., return the value of the +promise if it was fulfilled or throw an exception if it was rejected). This is +called "unwrapping" the promise. Waiting on a promise will by default unwrap +the promise state. + +You can force a promise to resolve and *not* unwrap the state of the promise +by passing `false` to the first argument of the `wait` function: + +```php +$promise = new Promise(); +$promise->reject('foo'); +// This will not throw an exception. It simply ensures the promise has +// been resolved. +$promise->wait(false); +``` + +When unwrapping a promise, the delivered value of the promise will be waited +upon until the unwrapped value is not a promise. This means that if you resolve +promise A with a promise B and unwrap promise A, the value returned by the +wait function will be the value delivered to promise B. + +**Note**: when you do not unwrap the promise, no value is returned. + + +# Cancellation + +You can cancel a promise that has not yet been fulfilled using the `cancel()` +method of a promise. When creating a promise you can provide an optional +cancel function that when invoked cancels the action of computing a resolution +of the promise. + + +# API + + +## Promise + +When creating a promise object, you can provide an optional `$waitFn` and +`$cancelFn`. `$waitFn` is a function that is invoked with no arguments and is +expected to resolve the promise. `$cancelFn` is a function with no arguments +that is expected to cancel the computation of a promise. It is invoked when the +`cancel()` method of a promise is called. + +```php +use GuzzleHttp\Promise\Promise; + +$promise = new Promise( + function () use (&$promise) { + $promise->resolve('waited'); + }, + function () { + // do something that will cancel the promise computation (e.g., close + // a socket, cancel a database query, etc...) + } +); + +assert('waited' === $promise->wait()); +``` + +A promise has the following methods: + +- `then(callable $onFulfilled, callable $onRejected) : PromiseInterface` + + Creates a new promise that is fulfilled or rejected when the promise is + resolved. + +- `wait($unwrap = true) : mixed` + + Synchronously waits on the promise to complete. + + `$unwrap` controls whether or not the value of the promise is returned for a + fulfilled promise or if an exception is thrown if the promise is rejected. + This is set to `true` by default. + +- `cancel()` + + Attempts to cancel the promise if possible. The promise being cancelled and + the parent most ancestor that has not yet been resolved will also be + cancelled. Any promises waiting on the cancelled promise to resolve will also + be cancelled. + +- `getState() : string` + + Returns the state of the promise. One of `pending`, `fulfilled`, or + `rejected`. + +- `resolve($value)` + + Fulfills the promise with the given `$value`. + +- `reject($reason)` + + Rejects the promise with the given `$reason`. + + +## FulfilledPromise + +A fulfilled promise can be created to represent a promise that has been +fulfilled. + +```php +use GuzzleHttp\Promise\FulfilledPromise; + +$promise = new FulfilledPromise('value'); + +// Fulfilled callbacks are immediately invoked. +$promise->then(function ($value) { + echo $value; +}); +``` + + +## RejectedPromise + +A rejected promise can be created to represent a promise that has been +rejected. + +```php +use GuzzleHttp\Promise\RejectedPromise; + +$promise = new RejectedPromise('Error'); + +// Rejected callbacks are immediately invoked. +$promise->then(null, function ($reason) { + echo $reason; +}); +``` + + +# Promise interop + +This library works with foreign promises that have a `then` method. This means +you can use Guzzle promises with [React promises](https://github.com/reactphp/promise) +for example. When a foreign promise is returned inside of a then method +callback, promise resolution will occur recursively. + +```php +// Create a React promise +$deferred = new React\Promise\Deferred(); +$reactPromise = $deferred->promise(); + +// Create a Guzzle promise that is fulfilled with a React promise. +$guzzlePromise = new \GuzzleHttp\Promise\Promise(); +$guzzlePromise->then(function ($value) use ($reactPromise) { + // Do something something with the value... + // Return the React promise + return $reactPromise; +}); +``` + +Please note that wait and cancel chaining is no longer possible when forwarding +a foreign promise. You will need to wrap a third-party promise with a Guzzle +promise in order to utilize wait and cancel functions with foreign promises. + + +## Event Loop Integration + +In order to keep the stack size constant, Guzzle promises are resolved +asynchronously using a task queue. When waiting on promises synchronously, the +task queue will be automatically run to ensure that the blocking promise and +any forwarded promises are resolved. When using promises asynchronously in an +event loop, you will need to run the task queue on each tick of the loop. If +you do not run the task queue, then promises will not be resolved. + +You can run the task queue using the `run()` method of the global task queue +instance. + +```php +// Get the global task queue +$queue = \GuzzleHttp\Promise\queue(); +$queue->run(); +``` + +For example, you could use Guzzle promises with React using a periodic timer: + +```php +$loop = React\EventLoop\Factory::create(); +$loop->addPeriodicTimer(0, [$queue, 'run']); +``` + +*TODO*: Perhaps adding a `futureTick()` on each tick would be faster? + + +# Implementation notes + + +## Promise resolution and chaining is handled iteratively + +By shuffling pending handlers from one owner to another, promises are +resolved iteratively, allowing for "infinite" then chaining. + +```php +<?php +require 'vendor/autoload.php'; + +use GuzzleHttp\Promise\Promise; + +$parent = new Promise(); +$p = $parent; + +for ($i = 0; $i < 1000; $i++) { + $p = $p->then(function ($v) { + // The stack size remains constant (a good thing) + echo xdebug_get_stack_depth() . ', '; + return $v + 1; + }); +} + +$parent->resolve(0); +var_dump($p->wait()); // int(1000) + +``` + +When a promise is fulfilled or rejected with a non-promise value, the promise +then takes ownership of the handlers of each child promise and delivers values +down the chain without using recursion. + +When a promise is resolved with another promise, the original promise transfers +all of its pending handlers to the new promise. When the new promise is +eventually resolved, all of the pending handlers are delivered the forwarded +value. + + +## A promise is the deferred. + +Some promise libraries implement promises using a deferred object to represent +a computation and a promise object to represent the delivery of the result of +the computation. This is a nice separation of computation and delivery because +consumers of the promise cannot modify the value that will be eventually +delivered. + +One side effect of being able to implement promise resolution and chaining +iteratively is that you need to be able for one promise to reach into the state +of another promise to shuffle around ownership of handlers. In order to achieve +this without making the handlers of a promise publicly mutable, a promise is +also the deferred value, allowing promises of the same parent class to reach +into and modify the private properties of promises of the same type. While this +does allow consumers of the value to modify the resolution or rejection of the +deferred, it is a small price to pay for keeping the stack size constant. + +```php +$promise = new Promise(); +$promise->then(function ($value) { echo $value; }); +// The promise is the deferred value, so you can deliver a value to it. +$promise->deliver('foo'); +// prints "foo" +``` diff --git a/core/vendor/guzzlehttp/streams/composer.json b/core/vendor/guzzlehttp/promises/composer.json similarity index 55% rename from core/vendor/guzzlehttp/streams/composer.json rename to core/vendor/guzzlehttp/promises/composer.json index 6d7034370bda463f985656ea07b08e78661a141d..e1bad38b4dc7c8496a12560877067a38a455c0b7 100644 --- a/core/vendor/guzzlehttp/streams/composer.json +++ b/core/vendor/guzzlehttp/promises/composer.json @@ -1,8 +1,8 @@ { - "name": "guzzlehttp/streams", - "description": "Provides a simple abstraction over streams of data", - "homepage": "http://guzzlephp.org/", - "keywords": ["stream", "guzzle"], + "name": "guzzlehttp/promises", + "type": "library", + "description": "Guzzle promises library", + "keywords": ["promise"], "license": "MIT", "authors": [ { @@ -12,17 +12,20 @@ } ], "require": { - "php": ">=5.4.0" + "php": ">=5.5.0" }, "require-dev": { "phpunit/phpunit": "~4.0" }, "autoload": { - "psr-4": { "GuzzleHttp\\Stream\\": "src/" } + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + }, + "files": ["src/functions.php"] }, "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "1.0-dev" } } } diff --git a/core/vendor/guzzlehttp/guzzle/phpunit.xml.dist b/core/vendor/guzzlehttp/promises/phpunit.xml.dist similarity index 100% rename from core/vendor/guzzlehttp/guzzle/phpunit.xml.dist rename to core/vendor/guzzlehttp/promises/phpunit.xml.dist diff --git a/core/vendor/guzzlehttp/promises/src/AggregateException.php b/core/vendor/guzzlehttp/promises/src/AggregateException.php new file mode 100644 index 0000000000000000000000000000000000000000..6a5690c3762224eb73a6555b128074dd185d58bf --- /dev/null +++ b/core/vendor/guzzlehttp/promises/src/AggregateException.php @@ -0,0 +1,16 @@ +<?php +namespace GuzzleHttp\Promise; + +/** + * Exception thrown when too many errors occur in the some() or any() methods. + */ +class AggregateException extends RejectionException +{ + public function __construct($msg, array $reasons) + { + parent::__construct( + $reasons, + sprintf('%s; %d rejected promises', $msg, count($reasons)) + ); + } +} diff --git a/core/vendor/guzzlehttp/promises/src/CancellationException.php b/core/vendor/guzzlehttp/promises/src/CancellationException.php new file mode 100644 index 0000000000000000000000000000000000000000..cb360b8056abbf065ea27ef93a4e2fe895b3989f --- /dev/null +++ b/core/vendor/guzzlehttp/promises/src/CancellationException.php @@ -0,0 +1,9 @@ +<?php +namespace GuzzleHttp\Promise; + +/** + * Exception that is set as the reason for a promise that has been cancelled. + */ +class CancellationException extends RejectionException +{ +} diff --git a/core/vendor/guzzlehttp/promises/src/EachPromise.php b/core/vendor/guzzlehttp/promises/src/EachPromise.php new file mode 100644 index 0000000000000000000000000000000000000000..855d8337895bb74e46260ffc43dc41588f8d24a1 --- /dev/null +++ b/core/vendor/guzzlehttp/promises/src/EachPromise.php @@ -0,0 +1,202 @@ +<?php +namespace GuzzleHttp\Promise; + +/** + * Represents a promise that iterates over many promises and invokes + * side-effect functions in the process. + */ +class EachPromise implements PromisorInterface +{ + private $pending = []; + + /** @var \Iterator */ + private $iterable; + + /** @var callable|int */ + private $concurrency; + + /** @var callable */ + private $onFulfilled; + + /** @var callable */ + private $onRejected; + + /** @var Promise */ + private $aggregate; + + /** + * Configuration hash can include the following key value pairs: + * + * - fulfilled: (callable) Invoked when a promise fulfills. The function + * is invoked with three arguments: the fulfillment value, the index + * position from the iterable list of the promise, and the aggregate + * promise that manages all of the promises. The aggregate promise may + * be resolved from within the callback to short-circuit the promise. + * - rejected: (callable) Invoked when a promise is rejected. The + * function is invoked with three arguments: the rejection reason, the + * index position from the iterable list of the promise, and the + * aggregate promise that manages all of the promises. The aggregate + * promise may be resolved from within the callback to short-circuit + * the promise. + * - concurrency: (integer) Pass this configuration option to limit the + * allowed number of outstanding concurrently executing promises, + * creating a capped pool of promises. There is no limit by default. + * + * @param mixed $iterable Promises or values to iterate. + * @param array $config Configuration options + */ + public function __construct($iterable, array $config = []) + { + $this->iterable = iter_for($iterable); + + if (isset($config['concurrency'])) { + $this->concurrency = $config['concurrency']; + } + + if (isset($config['fulfilled'])) { + $this->onFulfilled = $config['fulfilled']; + } + + if (isset($config['rejected'])) { + $this->onRejected = $config['rejected']; + } + } + + public function promise() + { + if ($this->aggregate) { + return $this->aggregate; + } + + try { + $this->createPromise(); + $this->iterable->rewind(); + $this->refillPending(); + } catch (\Exception $e) { + $this->aggregate->reject($e); + } + + return $this->aggregate; + } + + private function createPromise() + { + $this->aggregate = new Promise(function () { + reset($this->pending); + // Consume a potentially fluctuating list of promises while + // ensuring that indexes are maintained (precluding array_shift). + while ($promise = current($this->pending)) { + next($this->pending); + $promise->wait(); + if ($this->aggregate->getState() !== PromiseInterface::PENDING) { + return; + } + } + }); + + // Clear the references when the promise is resolved. + $clearFn = function () { + $this->iterable = $this->concurrency = $this->pending = null; + $this->onFulfilled = $this->onRejected = null; + }; + + $this->aggregate->then($clearFn, $clearFn); + } + + private function refillPending() + { + if (!$this->concurrency) { + // Add all pending promises. + while ($this->addPending() && $this->advanceIterator()); + return; + } + + // Add only up to N pending promises. + $concurrency = is_callable($this->concurrency) + ? call_user_func($this->concurrency, count($this->pending)) + : $this->concurrency; + $concurrency = max($concurrency - count($this->pending), 0); + // Concurrency may be set to 0 to disallow new promises. + if (!$concurrency) { + return; + } + // Add the first pending promise. + $this->addPending(); + // Note this is special handling for concurrency=1 so that we do + // not advance the iterator after adding the first promise. This + // helps work around issues with generators that might not have the + // next value to yield until promise callbacks are called. + while (--$concurrency + && $this->advanceIterator() + && $this->addPending()); + } + + private function addPending() + { + if (!$this->iterable || !$this->iterable->valid()) { + return false; + } + + $promise = promise_for($this->iterable->current()); + $idx = $this->iterable->key(); + + $this->pending[$idx] = $promise->then( + function ($value) use ($idx) { + if ($this->onFulfilled) { + call_user_func( + $this->onFulfilled, $value, $idx, $this->aggregate + ); + } + $this->step($idx); + }, + function ($reason) use ($idx) { + if ($this->onRejected) { + call_user_func( + $this->onRejected, $reason, $idx, $this->aggregate + ); + } + $this->step($idx); + } + ); + + return true; + } + + private function advanceIterator() + { + try { + $this->iterable->next(); + return true; + } catch (\Exception $e) { + $this->aggregate->reject($e); + return false; + } + } + + private function step($idx) + { + // If the promise was already resolved, then ignore this step. + if ($this->aggregate->getState() !== PromiseInterface::PENDING) { + return; + } + + unset($this->pending[$idx]); + $this->advanceIterator(); + + if (!$this->checkIfFinished()) { + // Add more pending promises if possible. + $this->refillPending(); + } + } + + private function checkIfFinished() + { + if (!$this->pending && !$this->iterable->valid()) { + // Resolve the promise if there's nothing left to do. + $this->aggregate->resolve(null); + return true; + } + + return false; + } +} diff --git a/core/vendor/guzzlehttp/promises/src/FulfilledPromise.php b/core/vendor/guzzlehttp/promises/src/FulfilledPromise.php new file mode 100644 index 0000000000000000000000000000000000000000..5596296533975e499e6b639b19013edd2bb79028 --- /dev/null +++ b/core/vendor/guzzlehttp/promises/src/FulfilledPromise.php @@ -0,0 +1,80 @@ +<?php +namespace GuzzleHttp\Promise; + +/** + * A promise that has been fulfilled. + * + * Thenning off of this promise will invoke the onFulfilled callback + * immediately and ignore other callbacks. + */ +class FulfilledPromise implements PromiseInterface +{ + private $value; + + public function __construct($value) + { + if (method_exists($value, 'then')) { + throw new \InvalidArgumentException( + 'You cannot create a FulfilledPromise with a promise.'); + } + + $this->value = $value; + } + + public function then( + callable $onFulfilled = null, + callable $onRejected = null + ) { + // Return itself if there is no onFulfilled function. + if (!$onFulfilled) { + return $this; + } + + $queue = queue(); + $p = new Promise([$queue, 'run']); + $value = $this->value; + $queue->add(static function () use ($p, $value, $onFulfilled) { + if ($p->getState() === self::PENDING) { + try { + $p->resolve($onFulfilled($value)); + } catch (\Exception $e) { + $p->reject($e); + } + } + }); + + return $p; + } + + public function otherwise(callable $onRejected) + { + return $this->then(null, $onRejected); + } + + public function wait($unwrap = true, $defaultDelivery = null) + { + return $unwrap ? $this->value : null; + } + + public function getState() + { + return self::FULFILLED; + } + + public function resolve($value) + { + if ($value !== $this->value) { + throw new \LogicException("Cannot resolve a fulfilled promise"); + } + } + + public function reject($reason) + { + throw new \LogicException("Cannot reject a fulfilled promise"); + } + + public function cancel() + { + // pass + } +} diff --git a/core/vendor/guzzlehttp/promises/src/Promise.php b/core/vendor/guzzlehttp/promises/src/Promise.php new file mode 100644 index 0000000000000000000000000000000000000000..c2cf969da307d991483a8ca41775cf14f14bff49 --- /dev/null +++ b/core/vendor/guzzlehttp/promises/src/Promise.php @@ -0,0 +1,268 @@ +<?php +namespace GuzzleHttp\Promise; + +/** + * Promises/A+ implementation that avoids recursion when possible. + * + * @link https://promisesaplus.com/ + */ +class Promise implements PromiseInterface +{ + private $state = self::PENDING; + private $result; + private $cancelFn; + private $waitFn; + private $waitList; + private $handlers = []; + + /** + * @param callable $waitFn Fn that when invoked resolves the promise. + * @param callable $cancelFn Fn that when invoked cancels the promise. + */ + public function __construct( + callable $waitFn = null, + callable $cancelFn = null + ) { + $this->waitFn = $waitFn; + $this->cancelFn = $cancelFn; + } + + public function then( + callable $onFulfilled = null, + callable $onRejected = null + ) { + if ($this->state === self::PENDING) { + $p = new Promise(null, [$this, 'cancel']); + $this->handlers[] = [$p, $onFulfilled, $onRejected]; + $p->waitList = $this->waitList; + $p->waitList[] = $this; + return $p; + } + + // Return a fulfilled promise and immediately invoke any callbacks. + if ($this->state === self::FULFILLED) { + return $onFulfilled + ? promise_for($this->result)->then($onFulfilled) + : promise_for($this->result); + } + + // It's either cancelled or rejected, so return a rejected promise + // and immediately invoke any callbacks. + $rejection = rejection_for($this->result); + return $onRejected ? $rejection->then(null, $onRejected) : $rejection; + } + + public function otherwise(callable $onRejected) + { + return $this->then(null, $onRejected); + } + + public function wait($unwrap = true) + { + $this->waitIfPending(); + + if (!$unwrap) { + return null; + } + + if ($this->result instanceof PromiseInterface) { + return $this->result->wait($unwrap); + } elseif ($this->state === self::FULFILLED) { + return $this->result; + } else { + // It's rejected so "unwrap" and throw an exception. + throw exception_for($this->result); + } + } + + public function getState() + { + return $this->state; + } + + public function cancel() + { + if ($this->state !== self::PENDING) { + return; + } + + $this->waitFn = $this->waitList = null; + + if ($this->cancelFn) { + $fn = $this->cancelFn; + $this->cancelFn = null; + try { + $fn(); + } catch (\Exception $e) { + $this->reject($e); + } + } + + // Reject the promise only if it wasn't rejected in a then callback. + if ($this->state === self::PENDING) { + $this->reject(new CancellationException('Promise has been cancelled')); + } + } + + public function resolve($value) + { + $this->settle(self::FULFILLED, $value); + } + + public function reject($reason) + { + $this->settle(self::REJECTED, $reason); + } + + private function settle($state, $value) + { + if ($this->state !== self::PENDING) { + // Ignore calls with the same resolution. + if ($state === $this->state && $value === $this->result) { + return; + } + throw $this->state === $state + ? new \LogicException("The promise is already {$state}.") + : new \LogicException("Cannot change a {$this->state} promise to {$state}"); + } + + if ($value === $this) { + throw new \LogicException('Cannot fulfill or reject a promise with itself'); + } + + // Clear out the state of the promise but stash the handlers. + $this->state = $state; + $this->result = $value; + $handlers = $this->handlers; + $this->handlers = null; + $this->waitList = $this->waitFn = null; + $this->cancelFn = null; + + if (!$handlers) { + return; + } + + // If the value was not a settled promise or a thenable, then resolve + // it in the task queue using the correct ID. + if (!method_exists($value, 'then')) { + $id = $state === self::FULFILLED ? 1 : 2; + // It's a success, so resolve the handlers in the queue. + queue()->add(static function () use ($id, $value, $handlers) { + foreach ($handlers as $handler) { + self::callHandler($id, $value, $handler); + } + }); + } elseif ($value instanceof Promise + && $value->getState() === self::PENDING + ) { + // We can just merge our handlers onto the next promise. + $value->handlers = array_merge($value->handlers, $handlers); + } else { + // Resolve the handlers when the forwarded promise is resolved. + $value->then( + static function ($value) use ($handlers) { + foreach ($handlers as $handler) { + self::callHandler(1, $value, $handler); + } + }, + static function ($reason) use ($handlers) { + foreach ($handlers as $handler) { + self::callHandler(2, $reason, $handler); + } + } + ); + } + } + + /** + * Call a stack of handlers using a specific callback index and value. + * + * @param int $index 1 (resolve) or 2 (reject). + * @param mixed $value Value to pass to the callback. + * @param array $handler Array of handler data (promise and callbacks). + * + * @return array Returns the next group to resolve. + */ + private static function callHandler($index, $value, array $handler) + { + /** @var PromiseInterface $promise */ + $promise = $handler[0]; + + // The promise may have been cancelled or resolved before placing + // this thunk in the queue. + if ($promise->getState() !== self::PENDING) { + return; + } + + try { + if (isset($handler[$index])) { + $promise->resolve($handler[$index]($value)); + } elseif ($index === 1) { + // Forward resolution values as-is. + $promise->resolve($value); + } else { + // Forward rejections down the chain. + $promise->reject($value); + } + } catch (\Exception $reason) { + $promise->reject($reason); + } + } + + private function waitIfPending() + { + if ($this->state !== self::PENDING) { + return; + } elseif ($this->waitFn) { + $this->invokeWaitFn(); + } elseif ($this->waitList) { + $this->invokeWaitList(); + } else { + // If there's not wait function, then reject the promise. + $this->reject('Cannot wait on a promise that has ' + . 'no internal wait function. You must provide a wait ' + . 'function when constructing the promise to be able to ' + . 'wait on a promise.'); + } + + queue()->run(); + + if ($this->state === self::PENDING) { + $this->reject('Invoking the wait callback did not resolve the promise'); + } + } + + private function invokeWaitFn() + { + try { + $wfn = $this->waitFn; + $this->waitFn = null; + $wfn(true); + } catch (\Exception $reason) { + if ($this->state === self::PENDING) { + // The promise has not been resolved yet, so reject the promise + // with the exception. + $this->reject($reason); + } else { + // The promise was already resolved, so there's a problem in + // the application. + throw $reason; + } + } + } + + private function invokeWaitList() + { + $waitList = $this->waitList; + $this->waitList = null; + + foreach ($waitList as $result) { + descend: + $result->waitIfPending(); + if ($result->result instanceof Promise) { + $result = $result->result; + goto descend; + } + } + } +} diff --git a/core/vendor/guzzlehttp/promises/src/PromiseInterface.php b/core/vendor/guzzlehttp/promises/src/PromiseInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..8f5f4b99b26c64c7e4177e537e13a2703ca56a6d --- /dev/null +++ b/core/vendor/guzzlehttp/promises/src/PromiseInterface.php @@ -0,0 +1,93 @@ +<?php +namespace GuzzleHttp\Promise; + +/** + * A promise represents the eventual result of an asynchronous operation. + * + * The primary way of interacting with a promise is through its then method, + * which registers callbacks to receive either a promise’s eventual value or + * the reason why the promise cannot be fulfilled. + * + * @link https://promisesaplus.com/ + */ +interface PromiseInterface +{ + const PENDING = 'pending'; + const FULFILLED = 'fulfilled'; + const REJECTED = 'rejected'; + + /** + * Appends fulfillment and rejection handlers to the promise, and returns + * a new promise resolving to the return value of the called handler. + * + * @param callable $onFulfilled Invoked when the promise fulfills. + * @param callable $onRejected Invoked when the promise is rejected. + * + * @return PromiseInterface + */ + public function then( + callable $onFulfilled = null, + callable $onRejected = null + ); + + /** + * Appends a rejection handler callback to the promise, and returns a new + * promise resolving to the return value of the callback if it is called, + * or to its original fulfillment value if the promise is instead + * fulfilled. + * + * @param callable $onRejected Invoked when the promise is rejected. + * + * @return PromiseInterface + */ + public function otherwise(callable $onRejected); + + /** + * Get the state of the promise ("pending", "rejected", or "fulfilled"). + * + * The three states can be checked against the constants defined on + * PromiseInterface: PENDING, FULFILLED, and REJECTED. + * + * @return string + */ + public function getState(); + + /** + * Resolve the promise with the given value. + * + * @param mixed $value + * @throws \RuntimeException if the promise is already resolved. + */ + public function resolve($value); + + /** + * Reject the promise with the given reason. + * + * @param mixed $reason + * @throws \RuntimeException if the promise is already resolved. + */ + public function reject($reason); + + /** + * Cancels the promise if possible. + * + * @link https://github.com/promises-aplus/cancellation-spec/issues/7 + */ + public function cancel(); + + /** + * Waits until the promise completes if possible. + * + * Pass $unwrap as true to unwrap the result of the promise, either + * returning the resolved value or throwing the rejected exception. + * + * If the promise cannot be waited on, then the promise will be rejected. + * + * @param bool $unwrap + * + * @return mixed + * @throws \LogicException if the promise has no wait function or if the + * promise does not settle after waiting. + */ + public function wait($unwrap = true); +} diff --git a/core/vendor/guzzlehttp/promises/src/PromisorInterface.php b/core/vendor/guzzlehttp/promises/src/PromisorInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..b07fe32bc138c56fc393522434d852531012174e --- /dev/null +++ b/core/vendor/guzzlehttp/promises/src/PromisorInterface.php @@ -0,0 +1,15 @@ +<?php +namespace GuzzleHttp\Promise; + +/** + * Interface used with classes that return a promise. + */ +interface PromisorInterface +{ + /** + * Returns a promise. + * + * @return PromiseInterface + */ + public function promise(); +} diff --git a/core/vendor/guzzlehttp/promises/src/RejectedPromise.php b/core/vendor/guzzlehttp/promises/src/RejectedPromise.php new file mode 100644 index 0000000000000000000000000000000000000000..bd499e6912744f289f71614daf53d1a056e329f7 --- /dev/null +++ b/core/vendor/guzzlehttp/promises/src/RejectedPromise.php @@ -0,0 +1,84 @@ +<?php +namespace GuzzleHttp\Promise; + +/** + * A promise that has been rejected. + * + * Thenning off of this promise will invoke the onRejected callback + * immediately and ignore other callbacks. + */ +class RejectedPromise implements PromiseInterface +{ + private $reason; + + public function __construct($reason) + { + if (method_exists($reason, 'then')) { + throw new \InvalidArgumentException( + 'You cannot create a RejectedPromise with a promise.'); + } + + $this->reason = $reason; + } + + public function then( + callable $onFulfilled = null, + callable $onRejected = null + ) { + // If there's no onRejected callback then just return self. + if (!$onRejected) { + return $this; + } + + $queue = queue(); + $reason = $this->reason; + $p = new Promise([$queue, 'run']); + $queue->add(static function () use ($p, $reason, $onRejected) { + if ($p->getState() === self::PENDING) { + try { + // Return a resolved promise if onRejected does not throw. + $p->resolve($onRejected($reason)); + } catch (\Exception $e) { + // onRejected threw, so return a rejected promise. + $p->reject($e); + } + } + }); + + return $p; + } + + public function otherwise(callable $onRejected) + { + return $this->then(null, $onRejected); + } + + public function wait($unwrap = true, $defaultDelivery = null) + { + if ($unwrap) { + throw exception_for($this->reason); + } + } + + public function getState() + { + return self::REJECTED; + } + + public function resolve($value) + { + throw new \LogicException("Cannot resolve a rejected promise"); + } + + public function reject($reason) + { + if ($reason !== $this->reason) { + throw new \LogicException("Cannot reject a rejected promise"); + } + } + + public function cancel() + { + // pass + } +} diff --git a/core/vendor/guzzlehttp/promises/src/RejectionException.php b/core/vendor/guzzlehttp/promises/src/RejectionException.php new file mode 100644 index 0000000000000000000000000000000000000000..07c1136da166075fb7354548306b5755be8ceab2 --- /dev/null +++ b/core/vendor/guzzlehttp/promises/src/RejectionException.php @@ -0,0 +1,47 @@ +<?php +namespace GuzzleHttp\Promise; + +/** + * A special exception that is thrown when waiting on a rejected promise. + * + * The reason value is available via the getReason() method. + */ +class RejectionException extends \RuntimeException +{ + /** @var mixed Rejection reason. */ + private $reason; + + /** + * @param mixed $reason Rejection reason. + * @param string $description Optional description + */ + public function __construct($reason, $description = null) + { + $this->reason = $reason; + + $message = 'The promise was rejected'; + + if ($description) { + $message .= ' with reason: ' . $description; + } elseif (is_string($reason) + || (is_object($reason) && method_exists($reason, '__toString')) + ) { + $message .= ' with reason: ' . $this->reason; + } elseif ($reason instanceof \JsonSerializable) { + $message .= ' with reason: ' + . json_encode($this->reason, JSON_PRETTY_PRINT); + } + + parent::__construct($message); + } + + /** + * Returns the rejection reason. + * + * @return mixed + */ + public function getReason() + { + return $this->reason; + } +} diff --git a/core/vendor/guzzlehttp/promises/src/TaskQueue.php b/core/vendor/guzzlehttp/promises/src/TaskQueue.php new file mode 100644 index 0000000000000000000000000000000000000000..502636313b6850147f07cea6499cc93873b76299 --- /dev/null +++ b/core/vendor/guzzlehttp/promises/src/TaskQueue.php @@ -0,0 +1,79 @@ +<?php +namespace GuzzleHttp\Promise; + +/** + * A task queue that executes tasks in a FIFO order. + * + * This task queue class is used to settle promises asynchronously and + * maintains a constant stack size. You can use the task queue asynchronously + * by calling the `run()` function of the global task queue in an event loop. + * + * GuzzleHttp\Promise\queue()->run(); + */ +class TaskQueue +{ + private $enableShutdown = true; + private $queue = []; + + public function __construct($withShutdown = true) + { + if ($withShutdown) { + register_shutdown_function(function () { + if ($this->enableShutdown) { + // Only run the tasks if an E_ERROR didn't occur. + $err = error_get_last(); + if (!$err || ($err['type'] ^ E_ERROR)) { + $this->run(); + } + } + }); + } + } + + /** + * Returns true if the queue is empty. + * + * @return bool + */ + public function isEmpty() + { + return !$this->queue; + } + + /** + * Adds a task to the queue that will be executed the next time run is + * called. + * + * @param callable $task + */ + public function add(callable $task) + { + $this->queue[] = $task; + } + + /** + * Execute all of the pending task in the queue. + */ + public function run() + { + while ($task = array_shift($this->queue)) { + $task(); + } + } + + /** + * The task queue will be run and exhausted by default when the process + * exits IFF the exit is not the result of a PHP E_ERROR error. + * + * You can disable running the automatic shutdown of the queue by calling + * this function. If you disable the task queue shutdown process, then you + * MUST either run the task queue (as a result of running your event loop + * or manually using the run() method) or wait on each outstanding promise. + * + * Note: This shutdown will occur before any destructors are triggered. + */ + public function disableShutdown() + { + $this->enableShutdown = false; + } +} diff --git a/core/vendor/guzzlehttp/promises/src/functions.php b/core/vendor/guzzlehttp/promises/src/functions.php new file mode 100644 index 0000000000000000000000000000000000000000..eca27b21c498052b7d634496d6c5db997f6cc640 --- /dev/null +++ b/core/vendor/guzzlehttp/promises/src/functions.php @@ -0,0 +1,500 @@ +<?php +namespace GuzzleHttp\Promise; + +// Don't redefine the functions if included multiple times. +if (function_exists('GuzzleHttp\Promise\promise_for')) { + return; +} + +/** + * Get the global task queue used for promise resolution. + * + * This task queue MUST be run in an event loop in order for promises to be + * settled asynchronously. It will be automatically run when synchronously + * waiting on a promise. + * + * <code> + * while ($eventLoop->isRunning()) { + * GuzzleHttp\Promise\queue()->run(); + * } + * </code> + * + * @return TaskQueue + */ +function queue() +{ + static $queue; + + if (!$queue) { + $queue = new TaskQueue(); + } + + return $queue; +} + +/** + * Adds a function to run in the task queue when it is next `run()` and returns + * a promise that is fulfilled or rejected with the result. + * + * @param callable $task Task function to run. + * + * @return PromiseInterface + */ +function task(callable $task) +{ + $queue = queue(); + $promise = new Promise([$queue, 'run']); + $queue->add(function () use ($task, $promise) { + try { + $promise->resolve($task()); + } catch (\Exception $e) { + $promise->reject($e); + } + }); + + return $promise; +} + +/** + * Creates a promise for a value if the value is not a promise. + * + * @param mixed $value Promise or value. + * + * @return PromiseInterface + */ +function promise_for($value) +{ + if ($value instanceof PromiseInterface) { + return $value; + } + + // Return a Guzzle promise that shadows the given promise. + if (method_exists($value, 'then')) { + $wfn = method_exists($value, 'wait') ? [$value, 'wait'] : null; + $cfn = method_exists($value, 'cancel') ? [$value, 'cancel'] : null; + $promise = new Promise($wfn, $cfn); + $value->then([$promise, 'resolve'], [$promise, 'reject']); + return $promise; + } + + return new FulfilledPromise($value); +} + +/** + * Creates a rejected promise for a reason if the reason is not a promise. If + * the provided reason is a promise, then it is returned as-is. + * + * @param mixed $reason Promise or reason. + * + * @return PromiseInterface + */ +function rejection_for($reason) +{ + if ($reason instanceof PromiseInterface) { + return $reason; + } + + return new RejectedPromise($reason); +} + +/** + * Create an exception for a rejected promise value. + * + * @param mixed $reason + * + * @return \Exception + */ +function exception_for($reason) +{ + return $reason instanceof \Exception + ? $reason + : new RejectionException($reason); +} + +/** + * Returns an iterator for the given value. + * + * @param mixed $value + * + * @return \Iterator + */ +function iter_for($value) +{ + if ($value instanceof \Iterator) { + return $value; + } elseif (is_array($value)) { + return new \ArrayIterator($value); + } else { + return new \ArrayIterator([$value]); + } +} + +/** + * Synchronously waits on a promise to resolve and returns an inspection state + * array. + * + * Returns a state associative array containing a "state" key mapping to a + * valid promise state. If the state of the promise is "fulfilled", the array + * will contain a "value" key mapping to the fulfilled value of the promise. If + * the promise is rejected, the array will contain a "reason" key mapping to + * the rejection reason of the promise. + * + * @param PromiseInterface $promise Promise or value. + * + * @return array + */ +function inspect(PromiseInterface $promise) +{ + try { + return [ + 'state' => PromiseInterface::FULFILLED, + 'value' => $promise->wait() + ]; + } catch (RejectionException $e) { + return ['state' => 'rejected', 'reason' => $e->getReason()]; + } catch (\Exception $e) { + return ['state' => 'rejected', 'reason' => $e]; + } +} + +/** + * Waits on all of the provided promises, but does not unwrap rejected promises + * as thrown exception. + * + * Returns an array of inspection state arrays. + * + * @param PromiseInterface[] $promises Traversable of promises to wait upon. + * + * @return array + * @see GuzzleHttp\Promise\inspect for the inspection state array format. + */ +function inspect_all($promises) +{ + $results = []; + foreach ($promises as $key => $promise) { + $results[$key] = inspect($promise); + } + + return $results; +} + +/** + * Waits on all of the provided promises and returns the fulfilled values. + * + * Returns an array that contains the value of each promise (in the same order + * the promises were provided). An exception is thrown if any of the promises + * are rejected. + * + * @param mixed $promises Iterable of PromiseInterface objects to wait on. + * + * @return array + * @throws \Exception on error + */ +function unwrap($promises) +{ + $results = []; + foreach ($promises as $key => $promise) { + $results[$key] = $promise->wait(); + } + + return $results; +} + +/** + * Given an array of promises, return a promise that is fulfilled when all the + * items in the array are fulfilled. + * + * The promise's fulfillment value is an array with fulfillment values at + * respective positions to the original array. If any promise in the array + * rejects, the returned promise is rejected with the rejection reason. + * + * @param mixed $promises Promises or values. + * + * @return Promise + */ +function all($promises) +{ + $results = []; + return each( + $promises, + function ($value, $idx) use (&$results) { + $results[$idx] = $value; + }, + function ($reason, $idx, Promise $aggregate) { + $aggregate->reject($reason); + } + )->then(function () use (&$results) { + ksort($results); + return $results; + }); +} + +/** + * Initiate a competitive race between multiple promises or values (values will + * become immediately fulfilled promises). + * + * When count amount of promises have been fulfilled, the returned promise is + * fulfilled with an array that contains the fulfillment values of the winners + * in order of resolution. + * + * This prommise is rejected with a {@see GuzzleHttp\Promise\AggregateException} + * if the number of fulfilled promises is less than the desired $count. + * + * @param int $count Total number of promises. + * @param mixed $promises Promises or values. + * + * @return Promise + */ +function some($count, $promises) +{ + $results = []; + $rejections = []; + + return each( + $promises, + function ($value, $idx, PromiseInterface $p) use (&$results, $count) { + if ($p->getState() !== PromiseInterface::PENDING) { + return; + } + $results[$idx] = $value; + if (count($results) >= $count) { + $p->resolve(null); + } + }, + function ($reason) use (&$rejections) { + $rejections[] = $reason; + } + )->then( + function () use (&$results, &$rejections, $count) { + if (count($results) !== $count) { + throw new AggregateException( + 'Not enough promises to fulfill count', + $rejections + ); + } + ksort($results); + return array_values($results); + } + ); +} + +/** + * Like some(), with 1 as count. However, if the promise fulfills, the + * fulfillment value is not an array of 1 but the value directly. + * + * @param mixed $promises Promises or values. + * + * @return PromiseInterface + */ +function any($promises) +{ + return some(1, $promises)->then(function ($values) { return $values[0]; }); +} + +/** + * Returns a promise that is fulfilled when all of the provided promises have + * been fulfilled or rejected. + * + * The returned promise is fulfilled with an array of inspection state arrays. + * + * @param mixed $promises Promises or values. + * + * @return Promise + * @see GuzzleHttp\Promise\inspect for the inspection state array format. + */ +function settle($promises) +{ + $results = []; + + return each( + $promises, + function ($value, $idx) use (&$results) { + $results[$idx] = ['state' => 'fulfilled', 'value' => $value]; + }, + function ($reason, $idx) use (&$results) { + $results[$idx] = ['state' => 'rejected', 'reason' => $reason]; + } + )->then(function () use (&$results) { + ksort($results); + return $results; + }); +} + +/** + * Given an iterator that yields promises or values, returns a promise that is + * fulfilled with a null value when the iterator has been consumed or the + * aggregate promise has been fulfilled or rejected. + * + * $onFulfilled is a function that accepts the fulfilled value, iterator + * index, and the aggregate promise. The callback can invoke any necessary side + * effects and choose to resolve or reject the aggregate promise if needed. + * + * $onRejected is a function that accepts the rejection reason, iterator + * index, and the aggregate promise. The callback can invoke any necessary side + * effects and choose to resolve or reject the aggregate promise if needed. + * + * @param mixed $iterable Iterator or array to iterate over. + * @param callable $onFulfilled + * @param callable $onRejected + * + * @return Promise + */ +function each( + $iterable, + callable $onFulfilled = null, + callable $onRejected = null +) { + return (new EachPromise($iterable, [ + 'fulfilled' => $onFulfilled, + 'rejected' => $onRejected + ]))->promise(); +} + +/** + * Like each, but only allows a certain number of outstanding promises at any + * given time. + * + * $concurrency may be an integer or a function that accepts the number of + * pending promises and returns a numeric concurrency limit value to allow for + * dynamic a concurrency size. + * + * @param mixed $iterable + * @param int|callable $concurrency + * @param callable $onFulfilled + * @param callable $onRejected + * + * @return mixed + */ +function each_limit( + $iterable, + $concurrency, + callable $onFulfilled = null, + callable $onRejected = null +) { + return (new EachPromise($iterable, [ + 'fulfilled' => $onFulfilled, + 'rejected' => $onRejected, + 'concurrency' => $concurrency + ]))->promise(); +} + +/** + * Like each_limit, but ensures that no promise in the given $iterable argument + * is rejected. If any promise is rejected, then the aggregate promise is + * rejected with the encountered rejection. + * + * @param mixed $iterable + * @param int|callable $concurrency + * @param callable $onFulfilled + * + * @return mixed + */ +function each_limit_all( + $iterable, + $concurrency, + callable $onFulfilled = null +) { + return each_limit( + $iterable, + $concurrency, + $onFulfilled, + function ($reason, $idx, PromiseInterface $aggregate) { + $aggregate->reject($reason); + } + ); +} + +/** + * Returns true if a promise is fulfilled. + * + * @param PromiseInterface $promise + * + * @return bool + */ +function is_fulfilled(PromiseInterface $promise) +{ + return $promise->getState() === PromiseInterface::FULFILLED; +} + +/** + * Returns true if a promise is rejected. + * + * @param PromiseInterface $promise + * + * @return bool + */ +function is_rejected(PromiseInterface $promise) +{ + return $promise->getState() === PromiseInterface::REJECTED; +} + +/** + * Returns true if a promise is fulfilled or rejected. + * + * @param PromiseInterface $promise + * + * @return bool + */ +function is_settled(PromiseInterface $promise) +{ + return $promise->getState() !== PromiseInterface::PENDING; +} + +/** + * Creates a promise that is resolved using a generator that yields values or + * promises (somewhat similar to C#'s async keyword). + * + * When called, the coroutine function will start an instance of the generator + * and returns a promise that is fulfilled with its final yielded value. + * + * Control is returned back to the generator when the yielded promise settles. + * This can lead to less verbose code when doing lots of sequential async calls + * with minimal processing in between. + * + * use GuzzleHttp\Promise; + * + * function createPromise($value) { + * return new Promise\FulfilledPromise($value); + * } + * + * $promise = Promise\coroutine(function () { + * $value = (yield createPromise('a')); + * try { + * $value = (yield createPromise($value . 'b')); + * } catch (\Exception $e) { + * // The promise was rejected. + * } + * yield $value . 'c'; + * }); + * + * // Outputs "abc" + * $promise->then(function ($v) { echo $v; }); + * + * @param callable $generatorFn Generator function to wrap into a promise. + * + * @return Promise + * @link https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration + */ +function coroutine(callable $generatorFn) +{ + $generator = $generatorFn(); + return __next_coroutine($generator->current(), $generator)->then(); +} + +/** @internal */ +function __next_coroutine($yielded, \Generator $generator) +{ + return promise_for($yielded)->then( + function ($value) use ($generator) { + $nextYield = $generator->send($value); + return $generator->valid() + ? __next_coroutine($nextYield, $generator) + : $value; + }, + function ($reason) use ($generator) { + $nextYield = $generator->throw(exception_for($reason)); + // The throw was caught, so keep iterating on the coroutine + return __next_coroutine($nextYield, $generator); + } + ); +} diff --git a/core/vendor/guzzlehttp/promises/tests/AggregateExceptionTest.php b/core/vendor/guzzlehttp/promises/tests/AggregateExceptionTest.php new file mode 100644 index 0000000000000000000000000000000000000000..eaa77039ad71d458028d49040ef1fc69cce128b9 --- /dev/null +++ b/core/vendor/guzzlehttp/promises/tests/AggregateExceptionTest.php @@ -0,0 +1,14 @@ +<?php +namespace GuzzleHttp\Promise\Tests; + +use GuzzleHttp\Promise\AggregateException; + +class AggregateExceptionTest extends \PHPUnit_Framework_TestCase +{ + public function testHasReason() + { + $e = new AggregateException('foo', ['baz', 'bar']); + $this->assertContains('foo', $e->getMessage()); + $this->assertEquals(['baz', 'bar'], $e->getReason()); + } +} diff --git a/core/vendor/guzzlehttp/promises/tests/EachPromiseTest.php b/core/vendor/guzzlehttp/promises/tests/EachPromiseTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b913b3a713ef3057fb4f0baf12759a080d014fd8 --- /dev/null +++ b/core/vendor/guzzlehttp/promises/tests/EachPromiseTest.php @@ -0,0 +1,288 @@ +<?php +namespace GuzzleHttp\Promise\Tests; + +use GuzzleHttp\Promise\RejectedPromise; +use GuzzleHttp\Promise\FulfilledPromise; +use GuzzleHttp\Promise\Promise; +use GuzzleHttp\Promise\PromiseInterface; +use GuzzleHttp\Promise\EachPromise; +use GuzzleHttp\Promise as P; + +/** + * @covers GuzzleHttp\Promise\EachPromise + */ +class EachPromiseTest extends \PHPUnit_Framework_TestCase +{ + public function testReturnsSameInstance() + { + $each = new EachPromise([], ['concurrency' => 100]); + $this->assertSame($each->promise(), $each->promise()); + } + + public function testInvokesAllPromises() + { + $promises = [new Promise(), new Promise(), new Promise()]; + $called = []; + $each = new EachPromise($promises, [ + 'fulfilled' => function ($value) use (&$called) { + $called[] = $value; + } + ]); + $p = $each->promise(); + $promises[0]->resolve('a'); + $promises[1]->resolve('c'); + $promises[2]->resolve('b'); + P\queue()->run(); + $this->assertEquals(['a', 'c', 'b'], $called); + $this->assertEquals(PromiseInterface::FULFILLED, $p->getState()); + } + + public function testIsWaitable() + { + $a = new Promise(function () use (&$a) { $a->resolve('a'); }); + $b = new Promise(function () use (&$b) { $b->resolve('b'); }); + $called = []; + $each = new EachPromise([$a, $b], [ + 'fulfilled' => function ($value) use (&$called) { $called[] = $value; } + ]); + $p = $each->promise(); + $this->assertNull($p->wait()); + $this->assertEquals(PromiseInterface::FULFILLED, $p->getState()); + $this->assertEquals(['a', 'b'], $called); + } + + public function testCanResolveBeforeConsumingAll() + { + $called = 0; + $a = new Promise(function () use (&$a) { $a->resolve('a'); }); + $b = new Promise(function () { $this->fail(); }); + $each = new EachPromise([$a, $b], [ + 'fulfilled' => function ($value, $idx, Promise $aggregate) use (&$called) { + $this->assertSame($idx, 0); + $this->assertEquals('a', $value); + $aggregate->resolve(null); + $called++; + }, + 'rejected' => function (\Exception $reason) { + $this->fail($reason->getMessage()); + } + ]); + $p = $each->promise(); + $p->wait(); + $this->assertNull($p->wait()); + $this->assertEquals(1, $called); + $this->assertEquals(PromiseInterface::FULFILLED, $a->getState()); + $this->assertEquals(PromiseInterface::PENDING, $b->getState()); + // Resolving $b has no effect on the aggregate promise. + $b->resolve('foo'); + $this->assertEquals(1, $called); + } + + public function testLimitsPendingPromises() + { + $pending = [new Promise(), new Promise(), new Promise(), new Promise()]; + $promises = new \ArrayIterator($pending); + $each = new EachPromise($promises, ['concurrency' => 2]); + $p = $each->promise(); + $this->assertCount(2, $this->readAttribute($each, 'pending')); + $pending[0]->resolve('a'); + $this->assertCount(2, $this->readAttribute($each, 'pending')); + $this->assertTrue($promises->valid()); + $pending[1]->resolve('b'); + P\queue()->run(); + $this->assertCount(2, $this->readAttribute($each, 'pending')); + $this->assertTrue($promises->valid()); + $promises[2]->resolve('c'); + P\queue()->run(); + $this->assertCount(1, $this->readAttribute($each, 'pending')); + $this->assertEquals(PromiseInterface::PENDING, $p->getState()); + $promises[3]->resolve('d'); + P\queue()->run(); + $this->assertNull($this->readAttribute($each, 'pending')); + $this->assertEquals(PromiseInterface::FULFILLED, $p->getState()); + $this->assertFalse($promises->valid()); + } + + public function testDynamicallyLimitsPendingPromises() + { + $calls = []; + $pendingFn = function ($count) use (&$calls) { + $calls[] = $count; + return 2; + }; + $pending = [new Promise(), new Promise(), new Promise(), new Promise()]; + $promises = new \ArrayIterator($pending); + $each = new EachPromise($promises, ['concurrency' => $pendingFn]); + $p = $each->promise(); + $this->assertCount(2, $this->readAttribute($each, 'pending')); + $pending[0]->resolve('a'); + $this->assertCount(2, $this->readAttribute($each, 'pending')); + $this->assertTrue($promises->valid()); + $pending[1]->resolve('b'); + $this->assertCount(2, $this->readAttribute($each, 'pending')); + P\queue()->run(); + $this->assertTrue($promises->valid()); + $promises[2]->resolve('c'); + P\queue()->run(); + $this->assertCount(1, $this->readAttribute($each, 'pending')); + $this->assertEquals(PromiseInterface::PENDING, $p->getState()); + $promises[3]->resolve('d'); + P\queue()->run(); + $this->assertNull($this->readAttribute($each, 'pending')); + $this->assertEquals(PromiseInterface::FULFILLED, $p->getState()); + $this->assertEquals([0, 1, 1, 1], $calls); + $this->assertFalse($promises->valid()); + } + + public function testClearsReferencesWhenResolved() + { + $called = false; + $a = new Promise(function () use (&$a, &$called) { + $a->resolve('a'); + $called = true; + }); + $each = new EachPromise([$a], [ + 'concurrency' => function () { return 1; }, + 'fulfilled' => function () {}, + 'rejected' => function () {} + ]); + $each->promise()->wait(); + $this->assertNull($this->readAttribute($each, 'onFulfilled')); + $this->assertNull($this->readAttribute($each, 'onRejected')); + $this->assertNull($this->readAttribute($each, 'iterable')); + $this->assertNull($this->readAttribute($each, 'pending')); + $this->assertNull($this->readAttribute($each, 'concurrency')); + $this->assertTrue($called); + } + + public function testCanBeCancelled() + { + $this->markTestIncomplete(); + } + + public function testDoesNotBlowStackWithFulfilledPromises() + { + $pending = []; + for ($i = 0; $i < 100; $i++) { + $pending[] = new FulfilledPromise($i); + } + $values = []; + $each = new EachPromise($pending, [ + 'fulfilled' => function ($value) use (&$values) { + $values[] = $value; + } + ]); + $called = false; + $each->promise()->then(function () use (&$called) { + $called = true; + }); + $this->assertFalse($called); + P\queue()->run(); + $this->assertTrue($called); + $this->assertEquals(range(0, 99), $values); + } + + public function testDoesNotBlowStackWithRejectedPromises() + { + $pending = []; + for ($i = 0; $i < 100; $i++) { + $pending[] = new RejectedPromise($i); + } + $values = []; + $each = new EachPromise($pending, [ + 'rejected' => function ($value) use (&$values) { + $values[] = $value; + } + ]); + $called = false; + $each->promise()->then( + function () use (&$called) { $called = true; }, + function () { $this->fail('Should not have rejected.'); } + ); + $this->assertFalse($called); + P\queue()->run(); + $this->assertTrue($called); + $this->assertEquals(range(0, 99), $values); + } + + public function testReturnsPromiseForWhatever() + { + $called = []; + $arr = ['a', 'b']; + $each = new EachPromise($arr, [ + 'fulfilled' => function ($v) use (&$called) { $called[] = $v; } + ]); + $p = $each->promise(); + $this->assertNull($p->wait()); + $this->assertEquals(['a', 'b'], $called); + } + + public function testRejectsAggregateWhenNextThrows() + { + $iter = function () { + yield 'a'; + throw new \Exception('Failure'); + }; + $each = new EachPromise($iter()); + $p = $each->promise(); + $e = null; + $received = null; + $p->then(null, function ($reason) use (&$e) { $e = $reason; }); + P\queue()->run(); + $this->assertInstanceOf('Exception', $e); + $this->assertEquals('Failure', $e->getMessage()); + } + + public function testDoesNotCallNextOnIteratorUntilNeededWhenWaiting() + { + $results = []; + $values = [10]; + $remaining = 9; + $iter = function () use (&$values) { + while ($value = array_pop($values)) { + yield $value; + } + }; + $each = new EachPromise($iter(), [ + 'concurrency' => 1, + 'fulfilled' => function ($r) use (&$results, &$values, &$remaining) { + $results[] = $r; + if ($remaining > 0) { + $values[] = $remaining--; + } + } + ]); + $each->promise()->wait(); + $this->assertEquals(range(10, 1), $results); + } + + public function testDoesNotCallNextOnIteratorUntilNeededWhenAsync() + { + $firstPromise = new Promise(); + $pending = [$firstPromise]; + $values = [$firstPromise]; + $results = []; + $remaining = 9; + $iter = function () use (&$values) { + while ($value = array_pop($values)) { + yield $value; + } + }; + $each = new EachPromise($iter(), [ + 'concurrency' => 1, + 'fulfilled' => function ($r) use (&$results, &$values, &$remaining, &$pending) { + $results[] = $r; + if ($remaining-- > 0) { + $pending[] = $values[] = new Promise(); + } + } + ]); + $i = 0; + $each->promise(); + while ($promise = array_pop($pending)) { + $promise->resolve($i++); + P\queue()->run(); + } + $this->assertEquals(range(0, 9), $results); + } +} diff --git a/core/vendor/guzzlehttp/promises/tests/FulfilledPromiseTest.php b/core/vendor/guzzlehttp/promises/tests/FulfilledPromiseTest.php new file mode 100644 index 0000000000000000000000000000000000000000..554c15070d53a0588f073768c5af01b3fcb01313 --- /dev/null +++ b/core/vendor/guzzlehttp/promises/tests/FulfilledPromiseTest.php @@ -0,0 +1,108 @@ +<?php +namespace GuzzleHttp\Tests\Promise; + +use GuzzleHttp\Promise\Promise; +use GuzzleHttp\Promise\FulfilledPromise; + +/** + * @covers GuzzleHttp\Promise\FulfilledPromise + */ +class FulfilledPromiseTest extends \PHPUnit_Framework_TestCase +{ + public function testReturnsValueWhenWaitedUpon() + { + $p = new FulfilledPromise('foo'); + $this->assertEquals('fulfilled', $p->getState()); + $this->assertEquals('foo', $p->wait(true)); + } + + public function testCannotCancel() + { + $p = new FulfilledPromise('foo'); + $this->assertEquals('fulfilled', $p->getState()); + $p->cancel(); + $this->assertEquals('foo', $p->wait()); + } + + /** + * @expectedException \LogicException + * @exepctedExceptionMessage Cannot resolve a fulfilled promise + */ + public function testCannotResolve() + { + $p = new FulfilledPromise('foo'); + $p->resolve('bar'); + } + + /** + * @expectedException \LogicException + * @exepctedExceptionMessage Cannot reject a fulfilled promise + */ + public function testCannotReject() + { + $p = new FulfilledPromise('foo'); + $p->reject('bar'); + } + + public function testCanResolveWithSameValue() + { + $p = new FulfilledPromise('foo'); + $p->resolve('foo'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testCannotResolveWithPromise() + { + new FulfilledPromise(new Promise()); + } + + public function testReturnsSelfWhenNoOnFulfilled() + { + $p = new FulfilledPromise('a'); + $this->assertSame($p, $p->then()); + } + + public function testAsynchronouslyInvokesOnFulfilled() + { + $p = new FulfilledPromise('a'); + $r = null; + $f = function ($d) use (&$r) { $r = $d; }; + $p2 = $p->then($f); + $this->assertNotSame($p, $p2); + $this->assertNull($r); + \GuzzleHttp\Promise\queue()->run(); + $this->assertEquals('a', $r); + } + + public function testReturnsNewRejectedWhenOnFulfilledFails() + { + $p = new FulfilledPromise('a'); + $f = function () { throw new \Exception('b'); }; + $p2 = $p->then($f); + $this->assertNotSame($p, $p2); + try { + $p2->wait(); + $this->fail(); + } catch (\Exception $e) { + $this->assertEquals('b', $e->getMessage()); + } + } + + public function testOtherwiseIsSugarForRejections() + { + $c = null; + $p = new FulfilledPromise('foo'); + $p->otherwise(function ($v) use (&$c) { $c = $v; }); + $this->assertNull($c); + } + + public function testDoesNotTryToFulfillTwiceDuringTrampoline() + { + $fp = new FulfilledPromise('a'); + $t1 = $fp->then(function ($v) { return $v . ' b'; }); + $t1->resolve('why!'); + $this->assertEquals('why!', $t1->wait()); + } +} diff --git a/core/vendor/guzzlehttp/promises/tests/NotPromiseInstance.php b/core/vendor/guzzlehttp/promises/tests/NotPromiseInstance.php new file mode 100644 index 0000000000000000000000000000000000000000..6288aa88a10648a9de96a133f333a8df4402b873 --- /dev/null +++ b/core/vendor/guzzlehttp/promises/tests/NotPromiseInstance.php @@ -0,0 +1,50 @@ +<?php +namespace GuzzleHttp\Promise\Tests; + +use GuzzleHttp\Promise\Promise; +use GuzzleHttp\Promise\PromiseInterface; + +class NotPromiseInstance extends Thennable implements PromiseInterface +{ + private $nextPromise = null; + + public function __construct() + { + $this->nextPromise = new Promise(); + } + + public function then(callable $res = null, callable $rej = null) + { + return $this->nextPromise->then($res, $rej); + } + + public function otherwise(callable $onRejected) + { + return $this->then($onRejected); + } + + public function resolve($value) + { + $this->nextPromise->resolve($value); + } + + public function reject($reason) + { + $this->nextPromise->reject($reason); + } + + public function wait($unwrap = true, $defaultResolution = null) + { + + } + + public function cancel() + { + + } + + public function getState() + { + return $this->nextPromise->getState(); + } +} diff --git a/core/vendor/guzzlehttp/promises/tests/PromiseTest.php b/core/vendor/guzzlehttp/promises/tests/PromiseTest.php new file mode 100644 index 0000000000000000000000000000000000000000..946c62724f2ed006ab9f2884c5b7cc891d3c007e --- /dev/null +++ b/core/vendor/guzzlehttp/promises/tests/PromiseTest.php @@ -0,0 +1,579 @@ +<?php +namespace GuzzleHttp\Promise\Tests; + +use GuzzleHttp\Promise\CancellationException; +use GuzzleHttp\Promise as P; +use GuzzleHttp\Promise\Promise; +use GuzzleHttp\Promise\RejectedPromise; +use GuzzleHttp\Promise\RejectionException; + +/** + * @covers GuzzleHttp\Promise\Promise + */ +class PromiseTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \LogicException + * @expectedExceptionMessage The promise is already fulfilled + */ + public function testCannotResolveNonPendingPromise() + { + $p = new Promise(); + $p->resolve('foo'); + $p->resolve('bar'); + $this->assertEquals('foo', $p->wait()); + } + + public function testCanResolveWithSameValue() + { + $p = new Promise(); + $p->resolve('foo'); + $p->resolve('foo'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot change a fulfilled promise to rejected + */ + public function testCannotRejectNonPendingPromise() + { + $p = new Promise(); + $p->resolve('foo'); + $p->reject('bar'); + $this->assertEquals('foo', $p->wait()); + } + + public function testCanRejectWithSameValue() + { + $p = new Promise(); + $p->reject('foo'); + $p->reject('foo'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot change a fulfilled promise to rejected + */ + public function testCannotRejectResolveWithSameValue() + { + $p = new Promise(); + $p->resolve('foo'); + $p->reject('foo'); + } + + public function testInvokesWaitFunction() + { + $p = new Promise(function () use (&$p) { $p->resolve('10'); }); + $this->assertEquals('10', $p->wait()); + } + + /** + * @expectedException \GuzzleHttp\Promise\RejectionException + */ + public function testRejectsAndThrowsWhenWaitFailsToResolve() + { + $p = new Promise(function () {}); + $p->wait(); + } + + /** + * @expectedException \GuzzleHttp\Promise\RejectionException + * @expectedExceptionMessage The promise was rejected with reason: foo + */ + public function testThrowsWhenUnwrapIsRejectedWithNonException() + { + $p = new Promise(function () use (&$p) { $p->reject('foo'); }); + $p->wait(); + } + + /** + * @expectedException \UnexpectedValueException + * @expectedExceptionMessage foo + */ + public function testThrowsWhenUnwrapIsRejectedWithException() + { + $e = new \UnexpectedValueException('foo'); + $p = new Promise(function () use (&$p, $e) { $p->reject($e); }); + $p->wait(); + } + + public function testDoesNotUnwrapExceptionsWhenDisabled() + { + $p = new Promise(function () use (&$p) { $p->reject('foo'); }); + $this->assertEquals('pending', $p->getState()); + $p->wait(false); + $this->assertEquals('rejected', $p->getState()); + } + + public function testRejectsSelfWhenWaitThrows() + { + $e = new \UnexpectedValueException('foo'); + $p = new Promise(function () use ($e) { throw $e; }); + try { + $p->wait(); + $this->fail(); + } catch (\UnexpectedValueException $e) { + $this->assertEquals('rejected', $p->getState()); + } + } + + public function testWaitsOnNestedPromises() + { + $p = new Promise(function () use (&$p) { $p->resolve('_'); }); + $p2 = new Promise(function () use (&$p2) { $p2->resolve('foo'); }); + $p3 = $p->then(function () use ($p2) { return $p2; }); + $this->assertSame('foo', $p3->wait()); + } + + /** + * @expectedException \GuzzleHttp\Promise\RejectionException + */ + public function testThrowsWhenWaitingOnPromiseWithNoWaitFunction() + { + $p = new Promise(); + $p->wait(); + } + + public function testThrowsWaitExceptionAfterPromiseIsResolved() + { + $p = new Promise(function () use (&$p) { + $p->reject('Foo!'); + throw new \Exception('Bar?'); + }); + + try { + $p->wait(); + $this->fail(); + } catch (\Exception $e) { + $this->assertEquals('Bar?', $e->getMessage()); + } + } + + public function testGetsActualWaitValueFromThen() + { + $p = new Promise(function () use (&$p) { $p->reject('Foo!'); }); + $p2 = $p->then(null, function ($reason) { + return new RejectedPromise([$reason]); + }); + + try { + $p2->wait(); + $this->fail('Should have thrown'); + } catch (RejectionException $e) { + $this->assertEquals(['Foo!'], $e->getReason()); + } + } + + public function testWaitBehaviorIsBasedOnLastPromiseInChain() + { + $p3 = new Promise(function () use (&$p3) { $p3->resolve('Whoop'); }); + $p2 = new Promise(function () use (&$p2, $p3) { $p2->reject($p3); }); + $p = new Promise(function () use (&$p, $p2) { $p->reject($p2); }); + $this->assertEquals('Whoop', $p->wait()); + } + + public function testCannotCancelNonPending() + { + $p = new Promise(); + $p->resolve('foo'); + $p->cancel(); + $this->assertEquals('fulfilled', $p->getState()); + } + + /** + * @expectedException \GuzzleHttp\Promise\CancellationException + */ + public function testCancelsPromiseWhenNoCancelFunction() + { + $p = new Promise(); + $p->cancel(); + $this->assertEquals('rejected', $p->getState()); + $p->wait(); + } + + public function testCancelsPromiseWithCancelFunction() + { + $called = false; + $p = new Promise(null, function () use (&$called) { $called = true; }); + $p->cancel(); + $this->assertEquals('rejected', $p->getState()); + $this->assertTrue($called); + } + + public function testCancelsUppermostPendingPromise() + { + $called = false; + $p1 = new Promise(null, function () use (&$called) { $called = true; }); + $p2 = $p1->then(function () {}); + $p3 = $p2->then(function () {}); + $p4 = $p3->then(function () {}); + $p3->cancel(); + $this->assertEquals('rejected', $p1->getState()); + $this->assertEquals('rejected', $p2->getState()); + $this->assertEquals('rejected', $p3->getState()); + $this->assertEquals('pending', $p4->getState()); + $this->assertTrue($called); + + try { + $p3->wait(); + $this->fail(); + } catch (CancellationException $e) { + $this->assertContains('cancelled', $e->getMessage()); + } + + try { + $p4->wait(); + $this->fail(); + } catch (CancellationException $e) { + $this->assertContains('cancelled', $e->getMessage()); + } + + $this->assertEquals('rejected', $p4->getState()); + } + + public function testCancelsChildPromises() + { + $called1 = $called2 = $called3 = false; + $p1 = new Promise(null, function () use (&$called1) { $called1 = true; }); + $p2 = new Promise(null, function () use (&$called2) { $called2 = true; }); + $p3 = new Promise(null, function () use (&$called3) { $called3 = true; }); + $p4 = $p2->then(function () use ($p3) { return $p3; }); + $p5 = $p4->then(function () { $this->fail(); }); + $p4->cancel(); + $this->assertEquals('pending', $p1->getState()); + $this->assertEquals('rejected', $p2->getState()); + $this->assertEquals('rejected', $p4->getState()); + $this->assertEquals('pending', $p5->getState()); + $this->assertFalse($called1); + $this->assertTrue($called2); + $this->assertFalse($called3); + } + + public function testRejectsPromiseWhenCancelFails() + { + $called = false; + $p = new Promise(null, function () use (&$called) { + $called = true; + throw new \Exception('e'); + }); + $p->cancel(); + $this->assertEquals('rejected', $p->getState()); + $this->assertTrue($called); + try { + $p->wait(); + $this->fail(); + } catch (\Exception $e) { + $this->assertEquals('e', $e->getMessage()); + } + } + + public function testCreatesPromiseWhenFulfilledAfterThen() + { + $p = new Promise(); + $carry = null; + $p2 = $p->then(function ($v) use (&$carry) { $carry = $v; }); + $this->assertNotSame($p, $p2); + $p->resolve('foo'); + P\queue()->run(); + + $this->assertEquals('foo', $carry); + } + + public function testCreatesPromiseWhenFulfilledBeforeThen() + { + $p = new Promise(); + $p->resolve('foo'); + $carry = null; + $p2 = $p->then(function ($v) use (&$carry) { $carry = $v; }); + $this->assertNotSame($p, $p2); + $this->assertNull($carry); + \GuzzleHttp\Promise\queue()->run(); + $this->assertEquals('foo', $carry); + } + + public function testCreatesPromiseWhenFulfilledWithNoCallback() + { + $p = new Promise(); + $p->resolve('foo'); + $p2 = $p->then(); + $this->assertNotSame($p, $p2); + $this->assertInstanceOf('GuzzleHttp\Promise\FulfilledPromise', $p2); + } + + public function testCreatesPromiseWhenRejectedAfterThen() + { + $p = new Promise(); + $carry = null; + $p2 = $p->then(null, function ($v) use (&$carry) { $carry = $v; }); + $this->assertNotSame($p, $p2); + $p->reject('foo'); + P\queue()->run(); + $this->assertEquals('foo', $carry); + } + + public function testCreatesPromiseWhenRejectedBeforeThen() + { + $p = new Promise(); + $p->reject('foo'); + $carry = null; + $p2 = $p->then(null, function ($v) use (&$carry) { $carry = $v; }); + $this->assertNotSame($p, $p2); + $this->assertNull($carry); + P\queue()->run(); + $this->assertEquals('foo', $carry); + } + + public function testCreatesPromiseWhenRejectedWithNoCallback() + { + $p = new Promise(); + $p->reject('foo'); + $p2 = $p->then(); + $this->assertNotSame($p, $p2); + $this->assertInstanceOf('GuzzleHttp\Promise\RejectedPromise', $p2); + } + + public function testInvokesWaitFnsForThens() + { + $p = new Promise(function () use (&$p) { $p->resolve('a'); }); + $p2 = $p + ->then(function ($v) { return $v . '-1-'; }) + ->then(function ($v) { return $v . '2'; }); + $this->assertEquals('a-1-2', $p2->wait()); + } + + public function testStacksThenWaitFunctions() + { + $p1 = new Promise(function () use (&$p1) { $p1->resolve('a'); }); + $p2 = new Promise(function () use (&$p2) { $p2->resolve('b'); }); + $p3 = new Promise(function () use (&$p3) { $p3->resolve('c'); }); + $p4 = $p1 + ->then(function () use ($p2) { return $p2; }) + ->then(function () use ($p3) { return $p3; }); + $this->assertEquals('c', $p4->wait()); + } + + public function testForwardsFulfilledDownChainBetweenGaps() + { + $p = new Promise(); + $r = $r2 = null; + $p->then(null, null) + ->then(function ($v) use (&$r) { $r = $v; return $v . '2'; }) + ->then(function ($v) use (&$r2) { $r2 = $v; }); + $p->resolve('foo'); + P\queue()->run(); + $this->assertEquals('foo', $r); + $this->assertEquals('foo2', $r2); + } + + public function testForwardsRejectedPromisesDownChainBetweenGaps() + { + $p = new Promise(); + $r = $r2 = null; + $p->then(null, null) + ->then(null, function ($v) use (&$r) { $r = $v; return $v . '2'; }) + ->then(function ($v) use (&$r2) { $r2 = $v; }); + $p->reject('foo'); + P\queue()->run(); + $this->assertEquals('foo', $r); + $this->assertEquals('foo2', $r2); + } + + public function testForwardsThrownPromisesDownChainBetweenGaps() + { + $e = new \Exception(); + $p = new Promise(); + $r = $r2 = null; + $p->then(null, null) + ->then(null, function ($v) use (&$r, $e) { + $r = $v; + throw $e; + }) + ->then( + null, + function ($v) use (&$r2) { $r2 = $v; } + ); + $p->reject('foo'); + P\queue()->run(); + $this->assertEquals('foo', $r); + $this->assertSame($e, $r2); + } + + public function testForwardsReturnedRejectedPromisesDownChainBetweenGaps() + { + $p = new Promise(); + $rejected = new RejectedPromise('bar'); + $r = $r2 = null; + $p->then(null, null) + ->then(null, function ($v) use (&$r, $rejected) { + $r = $v; + return $rejected; + }) + ->then( + null, + function ($v) use (&$r2) { $r2 = $v; } + ); + $p->reject('foo'); + P\queue()->run(); + $this->assertEquals('foo', $r); + $this->assertEquals('bar', $r2); + try { + $p->wait(); + } catch (RejectionException $e) { + $this->assertEquals('foo', $e->getReason()); + } + } + + public function testForwardsHandlersToNextPromise() + { + $p = new Promise(); + $p2 = new Promise(); + $resolved = null; + $p + ->then(function ($v) use ($p2) { return $p2; }) + ->then(function ($value) use (&$resolved) { $resolved = $value; }); + $p->resolve('a'); + $p2->resolve('b'); + P\queue()->run(); + $this->assertEquals('b', $resolved); + } + + public function testRemovesReferenceFromChildWhenParentWaitedUpon() + { + $r = null; + $p = new Promise(function () use (&$p) { $p->resolve('a'); }); + $p2 = new Promise(function () use (&$p2) { $p2->resolve('b'); }); + $pb = $p->then( + function ($v) use ($p2, &$r) { + $r = $v; + return $p2; + }) + ->then(function ($v) { return $v . '.'; }); + $this->assertEquals('a', $p->wait()); + $this->assertEquals('b', $p2->wait()); + $this->assertEquals('b.', $pb->wait()); + $this->assertEquals('a', $r); + } + + public function testForwardsHandlersWhenFulfilledPromiseIsReturned() + { + $res = []; + $p = new Promise(); + $p2 = new Promise(); + $p2->resolve('foo'); + $p2->then(function ($v) use (&$res) { $res[] = 'A:' . $v; }); + // $res is A:foo + $p + ->then(function () use ($p2, &$res) { $res[] = 'B'; return $p2; }) + ->then(function ($v) use (&$res) { $res[] = 'C:' . $v; }); + $p->resolve('a'); + $p->then(function ($v) use (&$res) { $res[] = 'D:' . $v; }); + P\queue()->run(); + $this->assertEquals(['A:foo', 'B', 'D:a', 'C:foo'], $res); + } + + public function testForwardsHandlersWhenRejectedPromiseIsReturned() + { + $res = []; + $p = new Promise(); + $p2 = new Promise(); + $p2->reject('foo'); + $p2->then(null, function ($v) use (&$res) { $res[] = 'A:' . $v; }); + $p->then(null, function () use ($p2, &$res) { $res[] = 'B'; return $p2; }) + ->then(null, function ($v) use (&$res) { $res[] = 'C:' . $v; }); + $p->reject('a'); + $p->then(null, function ($v) use (&$res) { $res[] = 'D:' . $v; }); + P\queue()->run(); + $this->assertEquals(['A:foo', 'B', 'D:a', 'C:foo'], $res); + } + + public function testDoesNotForwardRejectedPromise() + { + $res = []; + $p = new Promise(); + $p2 = new Promise(); + $p2->cancel(); + $p2->then(function ($v) use (&$res) { $res[] = "B:$v"; return $v; }); + $p->then(function ($v) use ($p2, &$res) { $res[] = "B:$v"; return $p2; }) + ->then(function ($v) use (&$res) { $res[] = 'C:' . $v; }); + $p->resolve('a'); + $p->then(function ($v) use (&$res) { $res[] = 'D:' . $v; }); + P\queue()->run(); + $this->assertEquals(['B:a', 'D:a'], $res); + } + + public function testRecursivelyForwardsWhenOnlyThennable() + { + $res = []; + $p = new Promise(); + $p2 = new Thennable(); + $p2->resolve('foo'); + $p2->then(function ($v) use (&$res) { $res[] = 'A:' . $v; }); + $p->then(function () use ($p2, &$res) { $res[] = 'B'; return $p2; }) + ->then(function ($v) use (&$res) { $res[] = 'C:' . $v; }); + $p->resolve('a'); + $p->then(function ($v) use (&$res) { $res[] = 'D:' . $v; }); + P\queue()->run(); + $this->assertEquals(['A:foo', 'B', 'D:a', 'C:foo'], $res); + } + + public function testRecursivelyForwardsWhenNotInstanceOfPromise() + { + $res = []; + $p = new Promise(); + $p2 = new NotPromiseInstance(); + $p2->then(function ($v) use (&$res) { $res[] = 'A:' . $v; }); + $p->then(function () use ($p2, &$res) { $res[] = 'B'; return $p2; }) + ->then(function ($v) use (&$res) { $res[] = 'C:' . $v; }); + $p->resolve('a'); + $p->then(function ($v) use (&$res) { $res[] = 'D:' . $v; }); + P\queue()->run(); + $this->assertEquals(['B', 'D:a'], $res); + $p2->resolve('foo'); + P\queue()->run(); + $this->assertEquals(['B', 'D:a', 'A:foo', 'C:foo'], $res); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot fulfill or reject a promise with itself + */ + public function testCannotResolveWithSelf() + { + $p = new Promise(); + $p->resolve($p); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot fulfill or reject a promise with itself + */ + public function testCannotRejectWithSelf() + { + $p = new Promise(); + $p->reject($p); + } + + public function testDoesNotBlowStackWhenWaitingOnNestedThens() + { + $inner = new Promise(function () use (&$inner) { $inner->resolve(0); }); + $prev = $inner; + for ($i = 1; $i < 100; $i++) { + $prev = $prev->then(function ($i) { return $i + 1; }); + } + + $parent = new Promise(function () use (&$parent, $prev) { + $parent->resolve($prev); + }); + + $this->assertEquals(99, $parent->wait()); + } + + public function testOtherwiseIsSugarForRejections() + { + $p = new Promise(); + $p->reject('foo'); + $p->otherwise(function ($v) use (&$c) { $c = $v; }); + P\queue()->run(); + $this->assertEquals($c, 'foo'); + } +} diff --git a/core/vendor/guzzlehttp/promises/tests/RejectedPromiseTest.php b/core/vendor/guzzlehttp/promises/tests/RejectedPromiseTest.php new file mode 100644 index 0000000000000000000000000000000000000000..60f926e9275c5988f324ef5c190753235a55d862 --- /dev/null +++ b/core/vendor/guzzlehttp/promises/tests/RejectedPromiseTest.php @@ -0,0 +1,143 @@ +<?php +namespace GuzzleHttp\Promise\Tests; + +use GuzzleHttp\Promise\Promise; +use GuzzleHttp\Promise\RejectedPromise; + +/** + * @covers GuzzleHttp\Promise\RejectedPromise + */ +class RejectedPromiseTest extends \PHPUnit_Framework_TestCase +{ + public function testThrowsReasonWhenWaitedUpon() + { + $p = new RejectedPromise('foo'); + $this->assertEquals('rejected', $p->getState()); + try { + $p->wait(true); + $this->fail(); + } catch (\Exception $e) { + $this->assertEquals('rejected', $p->getState()); + $this->assertContains('foo', $e->getMessage()); + } + } + + public function testCannotCancel() + { + $p = new RejectedPromise('foo'); + $p->cancel(); + $this->assertEquals('rejected', $p->getState()); + } + + /** + * @expectedException \LogicException + * @exepctedExceptionMessage Cannot resolve a rejected promise + */ + public function testCannotResolve() + { + $p = new RejectedPromise('foo'); + $p->resolve('bar'); + } + + /** + * @expectedException \LogicException + * @exepctedExceptionMessage Cannot reject a rejected promise + */ + public function testCannotReject() + { + $p = new RejectedPromise('foo'); + $p->reject('bar'); + } + + public function testCanRejectWithSameValue() + { + $p = new RejectedPromise('foo'); + $p->reject('foo'); + } + + public function testThrowsSpecificException() + { + $e = new \Exception(); + $p = new RejectedPromise($e); + try { + $p->wait(true); + $this->fail(); + } catch (\Exception $e2) { + $this->assertSame($e, $e2); + } + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testCannotResolveWithPromise() + { + new RejectedPromise(new Promise()); + } + + public function testReturnsSelfWhenNoOnReject() + { + $p = new RejectedPromise('a'); + $this->assertSame($p, $p->then()); + } + + public function testInvokesOnRejectedAsynchronously() + { + $p = new RejectedPromise('a'); + $r = null; + $f = function ($reason) use (&$r) { $r = $reason; }; + $p->then(null, $f); + $this->assertNull($r); + \GuzzleHttp\Promise\queue()->run(); + $this->assertEquals('a', $r); + } + + public function testReturnsNewRejectedWhenOnRejectedFails() + { + $p = new RejectedPromise('a'); + $f = function () { throw new \Exception('b'); }; + $p2 = $p->then(null, $f); + $this->assertNotSame($p, $p2); + try { + $p2->wait(); + $this->fail(); + } catch (\Exception $e) { + $this->assertEquals('b', $e->getMessage()); + } + } + + public function testWaitingIsNoOp() + { + $p = new RejectedPromise('a'); + $p->wait(false); + } + + public function testOtherwiseIsSugarForRejections() + { + $p = new RejectedPromise('foo'); + $p->otherwise(function ($v) use (&$c) { $c = $v; }); + \GuzzleHttp\Promise\queue()->run(); + $this->assertSame('foo', $c); + } + + public function testCanResolveThenWithSuccess() + { + $actual = null; + $p = new RejectedPromise('foo'); + $p->otherwise(function ($v) { + return $v . ' bar'; + })->then(function ($v) use (&$actual) { + $actual = $v; + }); + \GuzzleHttp\Promise\queue()->run(); + $this->assertEquals('foo bar', $actual); + } + + public function testDoesNotTryToRejectTwiceDuringTrampoline() + { + $fp = new RejectedPromise('a'); + $t1 = $fp->then(null, function ($v) { return $v . ' b'; }); + $t1->resolve('why!'); + $this->assertEquals('why!', $t1->wait()); + } +} diff --git a/core/vendor/guzzlehttp/promises/tests/RejectionExceptionTest.php b/core/vendor/guzzlehttp/promises/tests/RejectionExceptionTest.php new file mode 100644 index 0000000000000000000000000000000000000000..36c6a88c56e4d6088431c80b396f86fc0b8b1d49 --- /dev/null +++ b/core/vendor/guzzlehttp/promises/tests/RejectionExceptionTest.php @@ -0,0 +1,47 @@ +<?php +namespace GuzzleHttp\Promise\Tests; + +use GuzzleHttp\Promise\RejectionException; + +class Thing1 +{ + public function __construct($message) + { + $this->message = $message; + } + + public function __toString() + { + return $this->message; + } +} + +class Thing2 implements \JsonSerializable +{ + public function jsonSerialize() + { + return '{}'; + } +} + +/** + * @covers GuzzleHttp\Promise\RejectionException + */ +class RejectionExceptionTest extends \PHPUnit_Framework_TestCase +{ + public function testCanGetReasonFromException() + { + $thing = new Thing1('foo'); + $e = new RejectionException($thing); + + $this->assertSame($thing, $e->getReason()); + $this->assertEquals('The promise was rejected with reason: foo', $e->getMessage()); + } + + public function testCanGetReasonMessageFromJson() + { + $reason = new Thing2(); + $e = new RejectionException($reason); + $this->assertContains("{}", $e->getMessage()); + } +} diff --git a/core/vendor/guzzlehttp/promises/tests/TaskQueueTest.php b/core/vendor/guzzlehttp/promises/tests/TaskQueueTest.php new file mode 100644 index 0000000000000000000000000000000000000000..845b263525511f7a93b731c80408824e49bbd496 --- /dev/null +++ b/core/vendor/guzzlehttp/promises/tests/TaskQueueTest.php @@ -0,0 +1,31 @@ +<?php +namespace GuzzleHttp\Promise\Test; + +use GuzzleHttp\Promise\TaskQueue; + +class TaskQueueTest extends \PHPUnit_Framework_TestCase +{ + public function testKnowsIfEmpty() + { + $tq = new TaskQueue(false); + $this->assertTrue($tq->isEmpty()); + } + + public function testKnowsIfFull() + { + $tq = new TaskQueue(false); + $tq->add(function () {}); + $this->assertFalse($tq->isEmpty()); + } + + public function testExecutesTasksInOrder() + { + $tq = new TaskQueue(false); + $called = []; + $tq->add(function () use (&$called) { $called[] = 'a'; }); + $tq->add(function () use (&$called) { $called[] = 'b'; }); + $tq->add(function () use (&$called) { $called[] = 'c'; }); + $tq->run(); + $this->assertEquals(['a', 'b', 'c'], $called); + } +} diff --git a/core/vendor/guzzlehttp/promises/tests/Thennable.php b/core/vendor/guzzlehttp/promises/tests/Thennable.php new file mode 100644 index 0000000000000000000000000000000000000000..398954d7dd917b917b1ffe0d06dbe7e721253089 --- /dev/null +++ b/core/vendor/guzzlehttp/promises/tests/Thennable.php @@ -0,0 +1,24 @@ +<?php +namespace GuzzleHttp\Promise\Tests; + +use GuzzleHttp\Promise\Promise; + +class Thennable +{ + private $nextPromise = null; + + public function __construct() + { + $this->nextPromise = new Promise(); + } + + public function then(callable $res = null, callable $rej = null) + { + return $this->nextPromise->then($res, $rej); + } + + public function resolve($value) + { + $this->nextPromise->resolve($value); + } +} diff --git a/core/vendor/guzzlehttp/promises/tests/bootstrap.php b/core/vendor/guzzlehttp/promises/tests/bootstrap.php new file mode 100644 index 0000000000000000000000000000000000000000..a63d264bf50d3182ff3a2959c554d4500b3e2f82 --- /dev/null +++ b/core/vendor/guzzlehttp/promises/tests/bootstrap.php @@ -0,0 +1,4 @@ +<?php +require __DIR__ . '/../vendor/autoload.php'; +require __DIR__ . '/Thennable.php'; +require __DIR__ . '/NotPromiseInstance.php'; diff --git a/core/vendor/guzzlehttp/promises/tests/functionsTest.php b/core/vendor/guzzlehttp/promises/tests/functionsTest.php new file mode 100644 index 0000000000000000000000000000000000000000..8e6fcf4fa35885601c2caa9cdd5d5b11d5fc247f --- /dev/null +++ b/core/vendor/guzzlehttp/promises/tests/functionsTest.php @@ -0,0 +1,694 @@ +<?php +namespace GuzzleHttp\Promise\Tests; + +use GuzzleHttp\Promise as P; +use GuzzleHttp\Promise\FulfilledPromise; +use GuzzleHttp\Promise\Promise; +use GuzzleHttp\Promise\RejectedPromise; + +class FunctionsTest extends \PHPUnit_Framework_TestCase +{ + public function testCreatesPromiseForValue() + { + $p = \GuzzleHttp\Promise\promise_for('foo'); + $this->assertInstanceOf('GuzzleHttp\Promise\FulfilledPromise', $p); + } + + public function testReturnsPromiseForPromise() + { + $p = new Promise(); + $this->assertSame($p, \GuzzleHttp\Promise\promise_for($p)); + } + + public function testReturnsPromiseForThennable() + { + $p = new Thennable(); + $wrapped = \GuzzleHttp\Promise\promise_for($p); + $this->assertNotSame($p, $wrapped); + $this->assertInstanceOf('GuzzleHttp\Promise\PromiseInterface', $wrapped); + $p->resolve('foo'); + P\queue()->run(); + $this->assertEquals('foo', $wrapped->wait()); + } + + public function testReturnsRejection() + { + $p = \GuzzleHttp\Promise\rejection_for('fail'); + $this->assertInstanceOf('GuzzleHttp\Promise\RejectedPromise', $p); + $this->assertEquals('fail', $this->readAttribute($p, 'reason')); + } + + public function testReturnsPromisesAsIsInRejectionFor() + { + $a = new Promise(); + $b = \GuzzleHttp\Promise\rejection_for($a); + $this->assertSame($a, $b); + } + + public function testWaitsOnAllPromisesIntoArray() + { + $e = new \Exception(); + $a = new Promise(function () use (&$a) { $a->resolve('a'); }); + $b = new Promise(function () use (&$b) { $b->reject('b'); }); + $c = new Promise(function () use (&$c, $e) { $c->reject($e); }); + $results = \GuzzleHttp\Promise\inspect_all([$a, $b, $c]); + $this->assertEquals([ + ['state' => 'fulfilled', 'value' => 'a'], + ['state' => 'rejected', 'reason' => 'b'], + ['state' => 'rejected', 'reason' => $e] + ], $results); + } + + /** + * @expectedException \GuzzleHttp\Promise\RejectionException + */ + public function testUnwrapsPromisesWithNoDefaultAndFailure() + { + $promises = [new FulfilledPromise('a'), new Promise()]; + \GuzzleHttp\Promise\unwrap($promises); + } + + public function testUnwrapsPromisesWithNoDefault() + { + $promises = [new FulfilledPromise('a')]; + $this->assertEquals(['a'], \GuzzleHttp\Promise\unwrap($promises)); + } + + public function testUnwrapsPromisesWithKeys() + { + $promises = [ + 'foo' => new FulfilledPromise('a'), + 'bar' => new FulfilledPromise('b'), + ]; + $this->assertEquals([ + 'foo' => 'a', + 'bar' => 'b' + ], \GuzzleHttp\Promise\unwrap($promises)); + } + + public function testAllAggregatesSortedArray() + { + $a = new Promise(); + $b = new Promise(); + $c = new Promise(); + $d = \GuzzleHttp\Promise\all([$a, $b, $c]); + $b->resolve('b'); + $a->resolve('a'); + $c->resolve('c'); + $d->then( + function ($value) use (&$result) { $result = $value; }, + function ($reason) use (&$result) { $result = $reason; } + ); + P\queue()->run(); + $this->assertEquals(['a', 'b', 'c'], $result); + } + + public function testAllThrowsWhenAnyRejected() + { + $a = new Promise(); + $b = new Promise(); + $c = new Promise(); + $d = \GuzzleHttp\Promise\all([$a, $b, $c]); + $b->resolve('b'); + $a->reject('fail'); + $c->resolve('c'); + $d->then( + function ($value) use (&$result) { $result = $value; }, + function ($reason) use (&$result) { $result = $reason; } + ); + P\queue()->run(); + $this->assertEquals('fail', $result); + } + + public function testSomeAggregatesSortedArrayWithMax() + { + $a = new Promise(); + $b = new Promise(); + $c = new Promise(); + $d = \GuzzleHttp\Promise\some(2, [$a, $b, $c]); + $b->resolve('b'); + $c->resolve('c'); + $a->resolve('a'); + $d->then(function ($value) use (&$result) { $result = $value; }); + P\queue()->run(); + $this->assertEquals(['b', 'c'], $result); + } + + public function testSomeRejectsWhenTooManyRejections() + { + $a = new Promise(); + $b = new Promise(); + $d = \GuzzleHttp\Promise\some(2, [$a, $b]); + $a->reject('bad'); + $b->resolve('good'); + P\queue()->run(); + $this->assertEquals($a::REJECTED, $d->getState()); + $d->then(null, function ($reason) use (&$called) { + $called = $reason; + }); + P\queue()->run(); + $this->assertInstanceOf('GuzzleHttp\Promise\AggregateException', $called); + $this->assertContains('bad', $called->getReason()); + } + + public function testCanWaitUntilSomeCountIsSatisfied() + { + $a = new Promise(function () use (&$a) { $a->resolve('a'); }); + $b = new Promise(function () use (&$b) { $b->resolve('b'); }); + $c = new Promise(function () use (&$c) { $c->resolve('c'); }); + $d = \GuzzleHttp\Promise\some(2, [$a, $b, $c]); + $this->assertEquals(['a', 'b'], $d->wait()); + } + + /** + * @expectedException \GuzzleHttp\Promise\AggregateException + * @expectedExceptionMessage Not enough promises to fulfill count + */ + public function testThrowsIfImpossibleToWaitForSomeCount() + { + $a = new Promise(function () use (&$a) { $a->resolve('a'); }); + $d = \GuzzleHttp\Promise\some(2, [$a]); + $d->wait(); + } + + /** + * @expectedException \GuzzleHttp\Promise\AggregateException + * @expectedExceptionMessage Not enough promises to fulfill count + */ + public function testThrowsIfResolvedWithoutCountTotalResults() + { + $a = new Promise(); + $b = new Promise(); + $d = \GuzzleHttp\Promise\some(3, [$a, $b]); + $a->resolve('a'); + $b->resolve('b'); + $d->wait(); + } + + public function testAnyReturnsFirstMatch() + { + $a = new Promise(); + $b = new Promise(); + $c = \GuzzleHttp\Promise\any([$a, $b]); + $b->resolve('b'); + $a->resolve('a'); + //P\queue()->run(); + //$this->assertEquals('fulfilled', $c->getState()); + $c->then(function ($value) use (&$result) { $result = $value; }); + P\queue()->run(); + $this->assertEquals('b', $result); + } + + public function testSettleFulfillsWithFulfilledAndRejected() + { + $a = new Promise(); + $b = new Promise(); + $c = new Promise(); + $d = \GuzzleHttp\Promise\settle([$a, $b, $c]); + $b->resolve('b'); + $c->resolve('c'); + $a->reject('a'); + P\queue()->run(); + $this->assertEquals('fulfilled', $d->getState()); + $d->then(function ($value) use (&$result) { $result = $value; }); + P\queue()->run(); + $this->assertEquals([ + ['state' => 'rejected', 'reason' => 'a'], + ['state' => 'fulfilled', 'value' => 'b'], + ['state' => 'fulfilled', 'value' => 'c'] + ], $result); + } + + public function testCanInspectFulfilledPromise() + { + $p = new FulfilledPromise('foo'); + $this->assertEquals([ + 'state' => 'fulfilled', + 'value' => 'foo' + ], \GuzzleHttp\Promise\inspect($p)); + } + + public function testCanInspectRejectedPromise() + { + $p = new RejectedPromise('foo'); + $this->assertEquals([ + 'state' => 'rejected', + 'reason' => 'foo' + ], \GuzzleHttp\Promise\inspect($p)); + } + + public function testCanInspectRejectedPromiseWithNormalException() + { + $e = new \Exception('foo'); + $p = new RejectedPromise($e); + $this->assertEquals([ + 'state' => 'rejected', + 'reason' => $e + ], \GuzzleHttp\Promise\inspect($p)); + } + + public function testCallsEachLimit() + { + $p = new Promise(); + $aggregate = \GuzzleHttp\Promise\each_limit($p, 2); + $p->resolve('a'); + P\queue()->run(); + $this->assertEquals($p::FULFILLED, $aggregate->getState()); + } + + public function testEachLimitAllRejectsOnFailure() + { + $p = [new FulfilledPromise('a'), new RejectedPromise('b')]; + $aggregate = \GuzzleHttp\Promise\each_limit_all($p, 2); + P\queue()->run(); + $this->assertEquals(P\PromiseInterface::REJECTED, $aggregate->getState()); + $result = \GuzzleHttp\Promise\inspect($aggregate); + $this->assertEquals('b', $result['reason']); + } + + public function testIterForReturnsIterator() + { + $iter = new \ArrayIterator(); + $this->assertSame($iter, \GuzzleHttp\Promise\iter_for($iter)); + } + + public function testKnowsIfFulfilled() + { + $p = new FulfilledPromise(null); + $this->assertTrue(P\is_fulfilled($p)); + $this->assertFalse(P\is_rejected($p)); + } + + public function testKnowsIfRejected() + { + $p = new RejectedPromise(null); + $this->assertTrue(P\is_rejected($p)); + $this->assertFalse(P\is_fulfilled($p)); + } + + public function testKnowsIfSettled() + { + $p = new RejectedPromise(null); + $this->assertTrue(P\is_settled($p)); + $p = new Promise(); + $this->assertFalse(P\is_settled($p)); + } + + public function testReturnsTrampoline() + { + $this->assertInstanceOf('GuzzleHttp\Promise\TaskQueue', P\queue()); + $this->assertSame(P\queue(), P\queue()); + } + + public function testCanScheduleThunk() + { + $tramp = P\queue(); + $promise = P\task(function () { return 'Hi!'; }); + $c = null; + $promise->then(function ($v) use (&$c) { $c = $v; }); + $this->assertNull($c); + $tramp->run(); + $this->assertEquals('Hi!', $c); + } + + public function testCanScheduleThunkWithRejection() + { + $tramp = P\queue(); + $promise = P\task(function () { throw new \Exception('Hi!'); }); + $c = null; + $promise->otherwise(function ($v) use (&$c) { $c = $v; }); + $this->assertNull($c); + $tramp->run(); + $this->assertEquals('Hi!', $c->getMessage()); + } + + public function testCanScheduleThunkWithWait() + { + $tramp = P\queue(); + $promise = P\task(function () { return 'a'; }); + $this->assertEquals('a', $promise->wait()); + $tramp->run(); + } + + public function testYieldsFromCoroutine() + { + $promise = P\coroutine(function () { + $value = (yield new P\FulfilledPromise('a')); + yield $value . 'b'; + }); + $promise->then(function ($value) use (&$result) { $result = $value; }); + P\queue()->run(); + $this->assertEquals('ab', $result); + } + + public function testCanCatchExceptionsInCoroutine() + { + $promise = P\coroutine(function () { + try { + yield new P\RejectedPromise('a'); + $this->fail('Should have thrown into the coroutine!'); + } catch (P\RejectionException $e) { + $value = (yield new P\FulfilledPromise($e->getReason())); + yield $value . 'b'; + } + }); + $promise->then(function ($value) use (&$result) { $result = $value; }); + P\queue()->run(); + $this->assertEquals(P\PromiseInterface::FULFILLED, $promise->getState()); + $this->assertEquals('ab', $result); + } + + public function testRejectsParentExceptionWhenException() + { + $promise = P\coroutine(function () { + yield new P\FulfilledPromise(0); + throw new \Exception('a'); + }); + $promise->then( + function () { $this->fail(); }, + function ($reason) use (&$result) { $result = $reason; } + ); + P\queue()->run(); + $this->assertInstanceOf('Exception', $result); + $this->assertEquals('a', $result->getMessage()); + } + + public function testCanRejectFromRejectionCallback() + { + $promise = P\coroutine(function () { + yield new P\FulfilledPromise(0); + yield new P\RejectedPromise('no!'); + }); + $promise->then( + function () { $this->fail(); }, + function ($reason) use (&$result) { $result = $reason; } + ); + P\queue()->run(); + $this->assertInstanceOf('GuzzleHttp\Promise\RejectionException', $result); + $this->assertEquals('no!', $result->getReason()); + } + + public function testCanAsyncReject() + { + $rej = new P\Promise(); + $promise = P\coroutine(function () use ($rej) { + yield new P\FulfilledPromise(0); + yield $rej; + }); + $promise->then( + function () { $this->fail(); }, + function ($reason) use (&$result) { $result = $reason; } + ); + $rej->reject('no!'); + P\queue()->run(); + $this->assertInstanceOf('GuzzleHttp\Promise\RejectionException', $result); + $this->assertEquals('no!', $result->getReason()); + } + + public function testCanCatchAndThrowOtherException() + { + $promise = P\coroutine(function () { + try { + yield new P\RejectedPromise('a'); + $this->fail('Should have thrown into the coroutine!'); + } catch (P\RejectionException $e) { + throw new \Exception('foo'); + } + }); + $promise->otherwise(function ($value) use (&$result) { $result = $value; }); + P\queue()->run(); + $this->assertEquals(P\PromiseInterface::REJECTED, $promise->getState()); + $this->assertContains('foo', $result->getMessage()); + } + + public function testCanCatchAndYieldOtherException() + { + $promise = P\coroutine(function () { + try { + yield new P\RejectedPromise('a'); + $this->fail('Should have thrown into the coroutine!'); + } catch (P\RejectionException $e) { + yield new P\RejectedPromise('foo'); + } + }); + $promise->otherwise(function ($value) use (&$result) { $result = $value; }); + P\queue()->run(); + $this->assertEquals(P\PromiseInterface::REJECTED, $promise->getState()); + $this->assertContains('foo', $result->getMessage()); + } + + public function createLotsOfSynchronousPromise() + { + return P\coroutine(function () { + $value = 0; + for ($i = 0; $i < 1000; $i++) { + $value = (yield new P\FulfilledPromise($i)); + } + yield $value; + }); + } + + public function testLotsOfSynchronousDoesNotBlowStack() + { + $promise = $this->createLotsOfSynchronousPromise(); + $promise->then(function ($v) use (&$r) { $r = $v; }); + P\queue()->run(); + $this->assertEquals(999, $r); + } + + public function testLotsOfSynchronousWaitDoesNotBlowStack() + { + $promise = $this->createLotsOfSynchronousPromise(); + $promise->then(function ($v) use (&$r) { $r = $v; }); + $this->assertEquals(999, $promise->wait()); + $this->assertEquals(999, $r); + } + + private function createLotsOfFlappingPromise() + { + return P\coroutine(function () { + $value = 0; + for ($i = 0; $i < 1000; $i++) { + try { + if ($i % 2) { + $value = (yield new P\FulfilledPromise($i)); + } else { + $value = (yield new P\RejectedPromise($i)); + } + } catch (\Exception $e) { + $value = (yield new P\FulfilledPromise($i)); + } + } + yield $value; + }); + } + + public function testLotsOfTryCatchingDoesNotBlowStack() + { + $promise = $this->createLotsOfFlappingPromise(); + $promise->then(function ($v) use (&$r) { $r = $v; }); + P\queue()->run(); + $this->assertEquals(999, $r); + } + + public function testLotsOfTryCatchingWaitingDoesNotBlowStack() + { + $promise = $this->createLotsOfFlappingPromise(); + $promise->then(function ($v) use (&$r) { $r = $v; }); + $this->assertEquals(999, $promise->wait()); + $this->assertEquals(999, $r); + } + + public function testAsyncPromisesWithCorrectlyYieldedValues() + { + $promises = [ + new P\Promise(), + new P\Promise(), + new P\Promise() + ]; + + $promise = P\coroutine(function () use ($promises) { + $value = null; + $this->assertEquals('skip', (yield new P\FulfilledPromise('skip'))); + foreach ($promises as $idx => $p) { + $value = (yield $p); + $this->assertEquals($value, $idx); + $this->assertEquals('skip', (yield new P\FulfilledPromise('skip'))); + } + $this->assertEquals('skip', (yield new P\FulfilledPromise('skip'))); + yield $value; + }); + + $promises[0]->resolve(0); + $promises[1]->resolve(1); + $promises[2]->resolve(2); + + $promise->then(function ($v) use (&$r) { $r = $v; }); + P\queue()->run(); + $this->assertEquals(2, $r); + } + + public function testYieldFinalWaitablePromise() + { + $p1 = new P\Promise(function () use (&$p1) { + $p1->resolve('skip me'); + }); + $p2 = new P\Promise(function () use (&$p2) { + $p2->resolve('hello!'); + }); + $co = P\coroutine(function() use ($p1, $p2) { + yield $p1; + yield $p2; + }); + P\queue()->run(); + $this->assertEquals('hello!', $co->wait()); + } + + public function testCanYieldFinalPendingPromise() + { + $p1 = new P\Promise(); + $p2 = new P\Promise(); + $co = P\coroutine(function() use ($p1, $p2) { + yield $p1; + yield $p2; + }); + $p1->resolve('a'); + $p2->resolve('b'); + $co->then(function ($value) use (&$result) { $result = $value; }); + P\queue()->run(); + $this->assertEquals('b', $result); + } + + public function testCanNestYieldsAndFailures() + { + $p1 = new P\Promise(); + $p2 = new P\Promise(); + $p3 = new P\Promise(); + $p4 = new P\Promise(); + $p5 = new P\Promise(); + $co = P\coroutine(function() use ($p1, $p2, $p3, $p4, $p5) { + try { + yield $p1; + } catch (\Exception $e) { + yield $p2; + try { + yield $p3; + yield $p4; + } catch (\Exception $e) { + yield $p5; + } + } + }); + $p1->reject('a'); + $p2->resolve('b'); + $p3->resolve('c'); + $p4->reject('d'); + $p5->resolve('e'); + $co->then(function ($value) use (&$result) { $result = $value; }); + P\queue()->run(); + $this->assertEquals('e', $result); + } + + public function testCanYieldErrorsAndSuccessesWithoutRecursion() + { + $promises = []; + for ($i = 0; $i < 20; $i++) { + $promises[] = new P\Promise(); + } + + $co = P\coroutine(function() use ($promises) { + for ($i = 0; $i < 20; $i += 4) { + try { + yield $promises[$i]; + yield $promises[$i + 1]; + } catch (\Exception $e) { + yield $promises[$i + 2]; + yield $promises[$i + 3]; + } + } + }); + + for ($i = 0; $i < 20; $i += 4) { + $promises[$i]->resolve($i); + $promises[$i + 1]->reject($i + 1); + $promises[$i + 2]->resolve($i + 2); + $promises[$i + 3]->resolve($i + 3); + } + + $co->then(function ($value) use (&$result) { $result = $value; }); + P\queue()->run(); + $this->assertEquals('19', $result); + } + + public function testCanWaitOnPromiseAfterFulfilled() + { + $f = function () { + static $i = 0; + $i++; + return $p = new P\Promise(function () use (&$p, $i) { + $p->resolve($i . '-bar'); + }); + }; + + $promises = []; + for ($i = 0; $i < 20; $i++) { + $promises[] = $f(); + } + + $p = P\coroutine(function () use ($promises) { + yield new P\FulfilledPromise('foo!'); + foreach ($promises as $promise) { + yield $promise; + } + }); + + $this->assertEquals('20-bar', $p->wait()); + } + + public function testCanWaitOnErroredPromises() + { + $p1 = new P\Promise(function () use (&$p1) { $p1->reject('a'); }); + $p2 = new P\Promise(function () use (&$p2) { $p2->resolve('b'); }); + $p3 = new P\Promise(function () use (&$p3) { $p3->resolve('c'); }); + $p4 = new P\Promise(function () use (&$p4) { $p4->reject('d'); }); + $p5 = new P\Promise(function () use (&$p5) { $p5->resolve('e'); }); + $p6 = new P\Promise(function () use (&$p6) { $p6->reject('f'); }); + + $co = P\coroutine(function() use ($p1, $p2, $p3, $p4, $p5, $p6) { + try { + yield $p1; + } catch (\Exception $e) { + yield $p2; + try { + yield $p3; + yield $p4; + } catch (\Exception $e) { + yield $p5; + yield $p6; + } + } + }); + + $res = P\inspect($co); + $this->assertEquals('f', $res['reason']); + } + + public function testCoroutineOtherwiseIntegrationTest() + { + $a = new P\Promise(); + $b = new P\Promise(); + $promise = P\coroutine(function () use ($a, $b) { + // Execute the pool of commands concurrently, and process errors. + yield $a; + yield $b; + })->otherwise(function (\Exception $e) { + // Throw errors from the operations as a specific Multipart error. + throw new \OutOfBoundsException('a', 0, $e); + }); + $a->resolve('a'); + $b->reject('b'); + $reason = P\inspect($promise)['reason']; + $this->assertInstanceOf('OutOfBoundsException', $reason); + $this->assertInstanceOf('GuzzleHttp\Promise\RejectionException', $reason->getPrevious()); + } +} diff --git a/core/vendor/guzzlehttp/psr7/.travis.yml b/core/vendor/guzzlehttp/psr7/.travis.yml new file mode 100644 index 0000000000000000000000000000000000000000..b88f8da270d8ac9940b2f81938df681a98666747 --- /dev/null +++ b/core/vendor/guzzlehttp/psr7/.travis.yml @@ -0,0 +1,20 @@ +language: php + +php: + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - hhvm + +sudo: false + +install: + - travis_retry composer install --no-interaction --prefer-source + +script: make test + +matrix: + allow_failures: + - php: hhvm + fast_finish: true diff --git a/core/vendor/guzzlehttp/psr7/CHANGELOG.md b/core/vendor/guzzlehttp/psr7/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..8f6d8b29240cdcc127c439febe83a96e89da22a6 --- /dev/null +++ b/core/vendor/guzzlehttp/psr7/CHANGELOG.md @@ -0,0 +1,19 @@ +# CHANGELOG + +## 1.1.0 - 2015-06-24 + +* URIs can now be relative. +* `multipart/form-data` headers are now overridden case-insensitively. +* URI paths no longer encode the following characters because they are allowed + in URIs: "(", ")", "*", "!", "'" +* A port is no longer added to a URI when the scheme is missing and no port is + present. + +## 1.0.0 - 2015-05-19 + +Initial release. + +Currently unsupported: + +- `Psr\Http\Message\ServerRequestInterface` +- `Psr\Http\Message\UploadedFileInterface` diff --git a/core/vendor/guzzlehttp/streams/LICENSE b/core/vendor/guzzlehttp/psr7/LICENSE similarity index 94% rename from core/vendor/guzzlehttp/streams/LICENSE rename to core/vendor/guzzlehttp/psr7/LICENSE index 71d3b783cb5b82e732f4555c5b7839036334607b..581d95f92024be7c805599690867b4d1e2e10f40 100644 --- a/core/vendor/guzzlehttp/streams/LICENSE +++ b/core/vendor/guzzlehttp/psr7/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com> +Copyright (c) 2015 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/core/vendor/guzzlehttp/psr7/Makefile b/core/vendor/guzzlehttp/psr7/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..9c210960ef383bc788dde290c8d83b929a497727 --- /dev/null +++ b/core/vendor/guzzlehttp/psr7/Makefile @@ -0,0 +1,13 @@ +all: clean test + +test: + vendor/bin/phpunit $(TEST) + +coverage: + vendor/bin/phpunit --coverage-html=artifacts/coverage $(TEST) + +view-coverage: + open artifacts/coverage/index.html + +clean: + rm -rf artifacts/* diff --git a/core/vendor/guzzlehttp/psr7/README.md b/core/vendor/guzzlehttp/psr7/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0b30d5ab09fe939bc89d6fe9ede157365822d015 --- /dev/null +++ b/core/vendor/guzzlehttp/psr7/README.md @@ -0,0 +1,580 @@ +# PSR-7 Message Implementation + +This repository contains a partial [PSR-7](http://www.php-fig.org/psr/psr-7/) +message implementation, several stream decorators, and some helpful +functionality like query string parsing. Currently missing +ServerRequestInterface and UploadedFileInterface; a pull request for these features is welcome. + + +# Stream implementation + +This package comes with a number of stream implementations and stream +decorators. + + +## AppendStream + +`GuzzleHttp\Psr7\AppendStream` + +Reads from multiple streams, one after the other. + +```php +use GuzzleHttp\Psr7; + +$a = Psr7\stream_for('abc, '); +$b = Psr7\stream_for('123.'); +$composed = new Psr7\AppendStream([$a, $b]); + +$composed->addStream(Psr7\stream_for(' Above all listen to me'). + +echo $composed(); // abc, 123. Above all listen to me. +``` + + +## BufferStream + +`GuzzleHttp\Psr7\BufferStream` + +Provides a buffer stream that can be written to to fill a buffer, and read +from to remove bytes from the buffer. + +This stream returns a "hwm" metadata value that tells upstream consumers +what the configured high water mark of the stream is, or the maximum +preferred size of the buffer. + +```php +use GuzzleHttp\Psr7; + +// When more than 1024 bytes are in the buffer, it will begin returning +// false to writes. This is an indication that writers should slow down. +$buffer = new Psr7\BufferStream(1024); +``` + + +## CachingStream + +The CachingStream is used to allow seeking over previously read bytes on +non-seekable streams. This can be useful when transferring a non-seekable +entity body fails due to needing to rewind the stream (for example, resulting +from a redirect). Data that is read from the remote stream will be buffered in +a PHP temp stream so that previously read bytes are cached first in memory, +then on disk. + +```php +use GuzzleHttp\Psr7; + +$original = Psr7\stream_for(fopen('http://www.google.com', 'r')); +$stream = new Psr7\CachingStream($original); + +$stream->read(1024); +echo $stream->tell(); +// 1024 + +$stream->seek(0); +echo $stream->tell(); +// 0 +``` + + +## DroppingStream + +`GuzzleHttp\Psr7\DroppingStream` + +Stream decorator that begins dropping data once the size of the underlying +stream becomes too full. + +```php +use GuzzleHttp\Psr7; + +// Create an empty stream +$stream = Psr7\stream_for(); + +// Start dropping data when the stream has more than 10 bytes +$dropping = new Psr7\DroppingStream($stream, 10); + +$stream->write('01234567890123456789'); +echo $stream; // 0123456789 +``` + + +## FnStream + +`GuzzleHttp\Psr7\FnStream` + +Compose stream implementations based on a hash of functions. + +Allows for easy testing and extension of a provided stream without needing to +to create a concrete class for a simple extension point. + +```php + +use GuzzleHttp\Psr7; + +$stream = Psr7\stream_for('hi'); +$fnStream = Psr7\FnStream::decorate($stream, [ + 'rewind' => function () use ($stream) { + echo 'About to rewind - '; + $stream->rewind(); + echo 'rewound!'; + } +]); + +$fnStream->rewind(); +// Outputs: About to rewind - rewound! +``` + + +## InflateStream + +`GuzzleHttp\Psr7\InflateStream` + +Uses PHP's zlib.inflate filter to inflate deflate or gzipped content. + +This stream decorator skips the first 10 bytes of the given stream to remove +the gzip header, converts the provided stream to a PHP stream resource, +then appends the zlib.inflate filter. The stream is then converted back +to a Guzzle stream resource to be used as a Guzzle stream. + + +## LazyOpenStream + +`GuzzleHttp\Psr7\LazyOpenStream` + +Lazily reads or writes to a file that is opened only after an IO operation +take place on the stream. + +```php +use GuzzleHttp\Psr7; + +$stream = new Psr7\LazyOpenStream('/path/to/file', 'r'); +// The file has not yet been opened... + +echo $stream->read(10); +// The file is opened and read from only when needed. +``` + + +## LimitStream + +`GuzzleHttp\Psr7\LimitStream` + +LimitStream can be used to read a subset or slice of an existing stream object. +This can be useful for breaking a large file into smaller pieces to be sent in +chunks (e.g. Amazon S3's multipart upload API). + +```php +use GuzzleHttp\Psr7; + +$original = Psr7\stream_for(fopen('/tmp/test.txt', 'r+')); +echo $original->getSize(); +// >>> 1048576 + +// Limit the size of the body to 1024 bytes and start reading from byte 2048 +$stream = new Psr7\LimitStream($original, 1024, 2048); +echo $stream->getSize(); +// >>> 1024 +echo $stream->tell(); +// >>> 0 +``` + + +## MultipartStream + +`GuzzleHttp\Psr7\MultipartStream` + +Stream that when read returns bytes for a streaming multipart or +multipart/form-data stream. + + +## NoSeekStream + +`GuzzleHttp\Psr7\NoSeekStream` + +NoSeekStream wraps a stream and does not allow seeking. + +```php +use GuzzleHttp\Psr7; + +$original = Psr7\stream_for('foo'); +$noSeek = new Psr7\NoSeekStream($original); + +echo $noSeek->read(3); +// foo +var_export($noSeek->isSeekable()); +// false +$noSeek->seek(0); +var_export($noSeek->read(3)); +// NULL +``` + + +## PumpStream + +`GuzzleHttp\Psr7\PumpStream` + +Provides a read only stream that pumps data from a PHP callable. + +When invoking the provided callable, the PumpStream will pass the amount of +data requested to read to the callable. The callable can choose to ignore +this value and return fewer or more bytes than requested. Any extra data +returned by the provided callable is buffered internally until drained using +the read() function of the PumpStream. The provided callable MUST return +false when there is no more data to read. + + +## Implementing stream decorators + +Creating a stream decorator is very easy thanks to the +`GuzzleHttp\Psr7\StreamDecoratorTrait`. This trait provides methods that +implement `Psr\Http\Message\StreamInterface` by proxying to an underlying +stream. Just `use` the `StreamDecoratorTrait` and implement your custom +methods. + +For example, let's say we wanted to call a specific function each time the last +byte is read from a stream. This could be implemented by overriding the +`read()` method. + +```php +use Psr\Http\Message\StreamInterface; +use GuzzleHttp\Psr7\StreamDecoratorTrait; + +class EofCallbackStream implements StreamInterface +{ + use StreamDecoratorTrait; + + private $callback; + + public function __construct(StreamInterface $stream, callable $cb) + { + $this->stream = $stream; + $this->callback = $cb; + } + + public function read($length) + { + $result = $this->stream->read($length); + + // Invoke the callback when EOF is hit. + if ($this->eof()) { + call_user_func($this->callback); + } + + return $result; + } +} +``` + +This decorator could be added to any existing stream and used like so: + +```php +use GuzzleHttp\Psr7; + +$original = Psr7\stream_for('foo'); + +$eofStream = new EofCallbackStream($original, function () { + echo 'EOF!'; +}); + +$eofStream->read(2); +$eofStream->read(1); +// echoes "EOF!" +$eofStream->seek(0); +$eofStream->read(3); +// echoes "EOF!" +``` + + +## PHP StreamWrapper + +You can use the `GuzzleHttp\Psr7\StreamWrapper` class if you need to use a +PSR-7 stream as a PHP stream resource. + +Use the `GuzzleHttp\Psr7\StreamWrapper::getResource()` method to create a PHP +stream from a PSR-7 stream. + +```php +use GuzzleHttp\Psr7\StreamWrapper; + +$stream = GuzzleHttp\Psr7\stream_for('hello!'); +$resource = StreamWrapper::getResource($stream); +echo fread($resource, 6); // outputs hello! +``` + + +# Function API + +There are various functions available under the `GuzzleHttp\Psr7` namespace. + + +## `function str` + +`function str(MessageInterface $message)` + +Returns the string representation of an HTTP message. + +```php +$request = new GuzzleHttp\Psr7\Request('GET', 'http://example.com'); +echo GuzzleHttp\Psr7\str($request); +``` + + +## `function uri_for` + +`function uri_for($uri)` + +This function accepts a string or `Psr\Http\Message\UriInterface` and returns a +UriInterface for the given value. If the value is already a `UriInterface`, it +is returned as-is. + +```php +$uri = GuzzleHttp\Psr7\uri_for('http://example.com'); +assert($uri === GuzzleHttp\Psr7\uri_for($uri)); +``` + + +## `function stream_for` + +`function stream_for($resource = '', array $options = [])` + +Create a new stream based on the input type. + +Options is an associative array that can contain the following keys: + +* - metadata: Array of custom metadata. +* - size: Size of the stream. + +This method accepts the following `$resource` types: + +- `Psr\Http\Message\StreamInterface`: Returns the value as-is. +- `string`: Creates a stream object that uses the given string as the contents. +- `resource`: Creates a stream object that wraps the given PHP stream resource. +- `Iterator`: If the provided value implements `Iterator`, then a read-only + stream object will be created that wraps the given iterable. Each time the + stream is read from, data from the iterator will fill a buffer and will be + continuously called until the buffer is equal to the requested read size. + Subsequent read calls will first read from the buffer and then call `next` + on the underlying iterator until it is exhausted. +- `object` with `__toString()`: If the object has the `__toString()` method, + the object will be cast to a string and then a stream will be returned that + uses the string value. +- `NULL`: When `null` is passed, an empty stream object is returned. +- `callable` When a callable is passed, a read-only stream object will be + created that invokes the given callable. The callable is invoked with the + number of suggested bytes to read. The callable can return any number of + bytes, but MUST return `false` when there is no more data to return. The + stream object that wraps the callable will invoke the callable until the + number of requested bytes are available. Any additional bytes will be + buffered and used in subsequent reads. + +```php +$stream = GuzzleHttp\Psr7\stream_for('foo'); +$stream = GuzzleHttp\Psr7\stream_for(fopen('/path/to/file', 'r')); + +$generator function ($bytes) { + for ($i = 0; $i < $bytes; $i++) { + yield ' '; + } +} + +$stream = GuzzleHttp\Psr7\stream_for($generator(100)); +``` + + +## `function parse_header` + +`function parse_header($header)` + +Parse an array of header values containing ";" separated data into an array of +associative arrays representing the header key value pair data of the header. +When a parameter does not contain a value, but just contains a key, this +function will inject a key with a '' string value. + + +## `function normalize_header` + +`function normalize_header($header)` + +Converts an array of header values that may contain comma separated headers +into an array of headers with no comma separated values. + + +## `function modify_request` + +`function modify_request(RequestInterface $request, array $changes)` + +Clone and modify a request with the given changes. This method is useful for +reducing the number of clones needed to mutate a message. + +The changes can be one of: + +- method: (string) Changes the HTTP method. +- set_headers: (array) Sets the given headers. +- remove_headers: (array) Remove the given headers. +- body: (mixed) Sets the given body. +- uri: (UriInterface) Set the URI. +- query: (string) Set the query string value of the URI. +- version: (string) Set the protocol version. + + +## `function rewind_body` + +`function rewind_body(MessageInterface $message)` + +Attempts to rewind a message body and throws an exception on failure. The body +of the message will only be rewound if a call to `tell()` returns a value other +than `0`. + + +## `function try_fopen` + +`function try_fopen($filename, $mode)` + +Safely opens a PHP stream resource using a filename. + +When fopen fails, PHP normally raises a warning. This function adds an error +handler that checks for errors and throws an exception instead. + + +## `function copy_to_string` + +`function copy_to_string(StreamInterface $stream, $maxLen = -1)` + +Copy the contents of a stream into a string until the given number of bytes +have been read. + + +## `function copy_to_stream` + +`function copy_to_stream(StreamInterface $source, StreamInterface $dest, $maxLen = -1)` + +Copy the contents of a stream into another stream until the given number of +bytes have been read. + + +## `function hash` + +`function hash(StreamInterface $stream, $algo, $rawOutput = false)` + +Calculate a hash of a Stream. This method reads the entire stream to calculate +a rolling hash (based on PHP's hash_init functions). + + +## `function readline` + +`function readline(StreamInterface $stream, $maxLength = null)` + +Read a line from the stream up to the maximum allowed buffer length. + + +## `function parse_request` + +`function parse_request($message)` + +Parses a request message string into a request object. + + +## `function parse_response` + +`function parse_response($message)` + +Parses a response message string into a response object. + + +## `function parse_query` + +`function parse_query($str, $urlEncoding = true)` + +Parse a query string into an associative array. + +If multiple values are found for the same key, the value of that key value pair +will become an array. This function does not parse nested PHP style arrays into +an associative array (e.g., `foo[a]=1&foo[b]=2` will be parsed into +`['foo[a]' => '1', 'foo[b]' => '2']`). + + +## `function build_query` + +`function build_query(array $params, $encoding = PHP_QUERY_RFC3986)` + +Build a query string from an array of key value pairs. + +This function can use the return value of parseQuery() to build a query string. +This function does not modify the provided keys when an array is encountered +(like http_build_query would). + + +## `function mimetype_from_filename` + +`function mimetype_from_filename($filename)` + +Determines the mimetype of a file by looking at its extension. + + +## `function mimetype_from_extension` + +`function mimetype_from_extension($extension)` + +Maps a file extensions to a mimetype. + + +# Static URI methods + +The `GuzzleHttp\Psr7\Uri` class has several static methods to manipulate URIs. + + +## `GuzzleHttp\Psr7\Uri::removeDotSegments` + +`public static function removeDotSegments($path) -> UriInterface` + +Removes dot segments from a path and returns the new path. + +See http://tools.ietf.org/html/rfc3986#section-5.2.4 + + +## `GuzzleHttp\Psr7\Uri::resolve` + +`public static function resolve(UriInterface $base, $rel) -> UriInterface` + +Resolve a base URI with a relative URI and return a new URI. + +See http://tools.ietf.org/html/rfc3986#section-5 + + +## `GuzzleHttp\Psr7\Uri::withQueryValue` + +`public static function withQueryValue(UriInterface $uri, $key, $value) -> UriInterface` + +Create a new URI with a specific query string value. + +Any existing query string values that exactly match the provided key are +removed and replaced with the given key value pair. + +Note: this function will convert "=" to "%3D" and "&" to "%26". + + +## `GuzzleHttp\Psr7\Uri::withoutQueryValue` + +`public static function withoutQueryValue(UriInterface $uri, $key, $value) -> UriInterface` + +Create a new URI with a specific query string value removed. + +Any existing query string values that exactly match the provided key are +removed. + +Note: this function will convert "=" to "%3D" and "&" to "%26". + + +## `GuzzleHttp\Psr7\Uri::fromParts` + +`public static function fromParts(array $parts) -> UriInterface` + +Create a `GuzzleHttp\Psr7\Uri` object from a hash of `parse_url` parts. + + +# Not Implemented + +A few aspects of PSR-7 are not implemented in this project. A pull request for +any of these features is welcome: + +- `Psr\Http\Message\ServerRequestInterface` +- `Psr\Http\Message\UploadedFileInterface` diff --git a/core/vendor/guzzlehttp/psr7/composer.json b/core/vendor/guzzlehttp/psr7/composer.json new file mode 100644 index 0000000000000000000000000000000000000000..c1fe18f08b2ae0cec6ba39ba547de88be97badeb --- /dev/null +++ b/core/vendor/guzzlehttp/psr7/composer.json @@ -0,0 +1,35 @@ +{ + "name": "guzzlehttp/psr7", + "type": "library", + "description": "PSR-7 message implementation", + "keywords": ["message", "stream", "http", "uri"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": ["src/functions.php"] + }, + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + } +} diff --git a/core/vendor/guzzlehttp/psr7/phpunit.xml.dist b/core/vendor/guzzlehttp/psr7/phpunit.xml.dist new file mode 100644 index 0000000000000000000000000000000000000000..500cd53a0ff1ed22ee88658e4fbcec6ea4e9040f --- /dev/null +++ b/core/vendor/guzzlehttp/psr7/phpunit.xml.dist @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<phpunit bootstrap="./tests/bootstrap.php" + colors="true"> + <testsuites> + <testsuite> + <directory>tests</directory> + </testsuite> + </testsuites> + <filter> + <whitelist> + <directory suffix=".php">src</directory> + <exclude> + <directory suffix="Interface.php">src/</directory> + </exclude> + </whitelist> + </filter> +</phpunit> diff --git a/core/vendor/guzzlehttp/streams/src/AppendStream.php b/core/vendor/guzzlehttp/psr7/src/AppendStream.php similarity index 74% rename from core/vendor/guzzlehttp/streams/src/AppendStream.php rename to core/vendor/guzzlehttp/psr7/src/AppendStream.php index 94bda7173f7e2ee02a676ed3764258c056d1a4c3..23039fd794bbcaaf1fda658301e90d0a4b209c46 100644 --- a/core/vendor/guzzlehttp/streams/src/AppendStream.php +++ b/core/vendor/guzzlehttp/psr7/src/AppendStream.php @@ -1,7 +1,7 @@ <?php -namespace GuzzleHttp\Stream; +namespace GuzzleHttp\Psr7; -use GuzzleHttp\Stream\Exception\CannotAttachException; +use Psr\Http\Message\StreamInterface; /** * Reads from multiple streams, one after the other. @@ -32,7 +32,7 @@ public function __construct(array $streams = []) public function __toString() { try { - $this->seek(0); + $this->rewind(); return $this->getContents(); } catch (\Exception $e) { return ''; @@ -62,7 +62,7 @@ public function addStream(StreamInterface $stream) public function getContents() { - return Utils::copyToString($this); + return copy_to_string($this); } /** @@ -92,11 +92,6 @@ public function detach() $this->detached = true; } - public function attach($stream) - { - throw new CannotAttachException(); - } - public function tell() { return $this->pos; @@ -132,6 +127,11 @@ public function eof() $this->streams[$this->current]->eof()); } + public function rewind() + { + $this->seek(0); + } + /** * Attempts to seek to the given position. Only supports SEEK_SET. * @@ -139,30 +139,31 @@ public function eof() */ public function seek($offset, $whence = SEEK_SET) { - if (!$this->seekable || $whence !== SEEK_SET) { - return false; + if (!$this->seekable) { + throw new \RuntimeException('This AppendStream is not seekable'); + } elseif ($whence !== SEEK_SET) { + throw new \RuntimeException('The AppendStream can only seek with SEEK_SET'); } - $success = true; $this->pos = $this->current = 0; // Rewind each stream - foreach ($this->streams as $stream) { - if (!$stream->seek(0)) { - $success = false; + foreach ($this->streams as $i => $stream) { + try { + $stream->rewind(); + } catch (\Exception $e) { + throw new \RuntimeException('Unable to seek stream ' + . $i . ' of the AppendStream', 0, $e); } } - if (!$success) { - return false; - } - // Seek to the actual position by reading from each stream while ($this->pos < $offset && !$this->eof()) { - $this->read(min(8096, $offset - $this->pos)); + $result = $this->read(min(8096, $offset - $this->pos)); + if ($result === '') { + break; + } } - - return $this->pos == $offset; } /** @@ -175,16 +176,28 @@ public function read($length) $buffer = ''; $total = count($this->streams) - 1; $remaining = $length; + $progressToNext = false; while ($remaining > 0) { + // Progress to the next stream if needed. - if ($this->streams[$this->current]->eof()) { - if ($this->current == $total) { + if ($progressToNext || $this->streams[$this->current]->eof()) { + $progressToNext = false; + if ($this->current === $total) { break; } $this->current++; } - $buffer .= $this->streams[$this->current]->read($remaining); + + $result = $this->streams[$this->current]->read($remaining); + + // Using a loose comparison here to match on '', false, and null + if ($result == null) { + $progressToNext = true; + continue; + } + + $buffer .= $result; $remaining = $length - strlen($buffer); } @@ -210,7 +223,7 @@ public function isSeekable() public function write($string) { - return false; + throw new \RuntimeException('Cannot write to an AppendStream'); } public function getMetadata($key = null) diff --git a/core/vendor/guzzlehttp/streams/src/BufferStream.php b/core/vendor/guzzlehttp/psr7/src/BufferStream.php similarity index 89% rename from core/vendor/guzzlehttp/streams/src/BufferStream.php rename to core/vendor/guzzlehttp/psr7/src/BufferStream.php index 0fffbd63a8c555a95dc7630b9a838c596591d68d..af4d4c2277786966521deda08580d241ba4cd522 100644 --- a/core/vendor/guzzlehttp/streams/src/BufferStream.php +++ b/core/vendor/guzzlehttp/psr7/src/BufferStream.php @@ -1,7 +1,7 @@ <?php -namespace GuzzleHttp\Stream; +namespace GuzzleHttp\Psr7; -use GuzzleHttp\Stream\Exception\CannotAttachException; +use Psr\Http\Message\StreamInterface; /** * Provides a buffer stream that can be written to to fill a buffer, and read @@ -10,8 +10,6 @@ * This stream returns a "hwm" metadata value that tells upstream consumers * what the configured high water mark of the stream is, or the maximum * preferred size of the buffer. - * - * @package GuzzleHttp\Stream */ class BufferStream implements StreamInterface { @@ -53,11 +51,6 @@ public function detach() $this->close(); } - public function attach($stream) - { - throw new CannotAttachException(); - } - public function getSize() { return strlen($this->buffer); @@ -78,9 +71,14 @@ public function isSeekable() return false; } + public function rewind() + { + $this->seek(0); + } + public function seek($offset, $whence = SEEK_SET) { - return false; + throw new \RuntimeException('Cannot seek a BufferStream'); } public function eof() @@ -90,7 +88,7 @@ public function eof() public function tell() { - return false; + throw new \RuntimeException('Cannot determine the position of a BufferStream'); } /** @@ -120,6 +118,7 @@ public function write($string) { $this->buffer .= $string; + // TODO: What should happen here? if (strlen($this->buffer) >= $this->hwm) { return false; } diff --git a/core/vendor/guzzlehttp/streams/src/CachingStream.php b/core/vendor/guzzlehttp/psr7/src/CachingStream.php similarity index 90% rename from core/vendor/guzzlehttp/streams/src/CachingStream.php rename to core/vendor/guzzlehttp/psr7/src/CachingStream.php index 60bb9056c432e60019bafd2b609428c15fd63727..8fc9d2a68f8f04cbd7c98dfed46aaed0d13e5111 100644 --- a/core/vendor/guzzlehttp/streams/src/CachingStream.php +++ b/core/vendor/guzzlehttp/psr7/src/CachingStream.php @@ -1,7 +1,7 @@ <?php -namespace GuzzleHttp\Stream; +namespace GuzzleHttp\Psr7; -use GuzzleHttp\Stream\Exception\SeekException; +use Psr\Http\Message\StreamInterface; /** * Stream decorator that can cache previously read bytes from a sequentially @@ -36,9 +36,14 @@ public function getSize() return max($this->stream->getSize(), $this->remoteStream->getSize()); } + public function rewind() + { + $this->seek(0); + } + /** * {@inheritdoc} - * @throws SeekException When seeking with SEEK_END or when seeking + * @throws \RuntimeException When seeking with SEEK_END or when seeking * past the total size of the buffer stream */ public function seek($offset, $whence = SEEK_SET) @@ -48,20 +53,18 @@ public function seek($offset, $whence = SEEK_SET) } elseif ($whence == SEEK_CUR) { $byte = $offset + $this->tell(); } else { - return false; + throw new \RuntimeException('CachingStream::seek() supports SEEK_SET and SEEK_CUR'); } // You cannot skip ahead past where you've read from the remote stream if ($byte > $this->stream->getSize()) { - throw new SeekException( - $this, - $byte, + throw new \RuntimeException( sprintf('Cannot seek to byte %d when the buffered stream only' . ' contains %d bytes', $byte, $this->stream->getSize()) ); } - return $this->stream->seek($byte); + $this->stream->seek($byte); } public function read($length) diff --git a/core/vendor/guzzlehttp/streams/src/DroppingStream.php b/core/vendor/guzzlehttp/psr7/src/DroppingStream.php similarity index 79% rename from core/vendor/guzzlehttp/streams/src/DroppingStream.php rename to core/vendor/guzzlehttp/psr7/src/DroppingStream.php index 56ee80c123a92b906e636adc71dfb95f74968dc5..8935c80d7298b82796025998b4451d4cd0a91df2 100644 --- a/core/vendor/guzzlehttp/streams/src/DroppingStream.php +++ b/core/vendor/guzzlehttp/psr7/src/DroppingStream.php @@ -1,5 +1,7 @@ <?php -namespace GuzzleHttp\Stream; +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; /** * Stream decorator that begins dropping data once the size of the underlying @@ -25,9 +27,9 @@ public function write($string) { $diff = $this->maxLength - $this->stream->getSize(); - // Begin returning false when the underlying stream is too large. + // Begin returning 0 when the underlying stream is too large. if ($diff <= 0) { - return false; + return 0; } // Write the stream or a subset of the stream if needed. @@ -35,8 +37,6 @@ public function write($string) return $this->stream->write($string); } - $this->stream->write(substr($string, 0, $diff)); - - return false; + return $this->stream->write(substr($string, 0, $diff)); } } diff --git a/core/vendor/guzzlehttp/streams/src/FnStream.php b/core/vendor/guzzlehttp/psr7/src/FnStream.php similarity index 92% rename from core/vendor/guzzlehttp/streams/src/FnStream.php rename to core/vendor/guzzlehttp/psr7/src/FnStream.php index 6b5872d7f6c599b4f5c4b1858b4a3073cf918fab..cc9b4453f716150dacb93db04289b65a014c9186 100644 --- a/core/vendor/guzzlehttp/streams/src/FnStream.php +++ b/core/vendor/guzzlehttp/psr7/src/FnStream.php @@ -1,5 +1,7 @@ <?php -namespace GuzzleHttp\Stream; +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; /** * Compose stream implementations based on a hash of functions. @@ -13,7 +15,7 @@ class FnStream implements StreamInterface private $methods; /** @var array Methods that must be implemented in the given array */ - private static $slots = ['__toString', 'close', 'detach', 'attach', + private static $slots = ['__toString', 'close', 'detach', 'rewind', 'getSize', 'tell', 'eof', 'isSeekable', 'seek', 'isWritable', 'write', 'isReadable', 'read', 'getContents', 'getMetadata']; @@ -85,11 +87,6 @@ public function detach() return call_user_func($this->_fn_detach); } - public function attach($stream) - { - return call_user_func($this->_fn_attach, $stream); - } - public function getSize() { return call_user_func($this->_fn_getSize); @@ -110,9 +107,14 @@ public function isSeekable() return call_user_func($this->_fn_isSeekable); } + public function rewind() + { + call_user_func($this->_fn_rewind); + } + public function seek($offset, $whence = SEEK_SET) { - return call_user_func($this->_fn_seek, $offset, $whence); + call_user_func($this->_fn_seek, $offset, $whence); } public function isWritable() diff --git a/core/vendor/guzzlehttp/streams/src/InflateStream.php b/core/vendor/guzzlehttp/psr7/src/InflateStream.php similarity index 87% rename from core/vendor/guzzlehttp/streams/src/InflateStream.php rename to core/vendor/guzzlehttp/psr7/src/InflateStream.php index 978af21031b9f83a174008e8750d4d0ebcc20b5f..2c8628b07b29db6b5015cac6e699d2c22120d4e8 100644 --- a/core/vendor/guzzlehttp/streams/src/InflateStream.php +++ b/core/vendor/guzzlehttp/psr7/src/InflateStream.php @@ -1,5 +1,7 @@ <?php -namespace GuzzleHttp\Stream; +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; /** * Uses PHP's zlib.inflate filter to inflate deflate or gzipped content. @@ -20,7 +22,7 @@ public function __construct(StreamInterface $stream) { // Skip the first 10 bytes $stream = new LimitStream($stream, -1, 10); - $resource = GuzzleStreamWrapper::getResource($stream); + $resource = StreamWrapper::getResource($stream); stream_filter_append($resource, 'zlib.inflate', STREAM_FILTER_READ); $this->stream = new Stream($resource); } diff --git a/core/vendor/guzzlehttp/streams/src/LazyOpenStream.php b/core/vendor/guzzlehttp/psr7/src/LazyOpenStream.php similarity index 84% rename from core/vendor/guzzlehttp/streams/src/LazyOpenStream.php rename to core/vendor/guzzlehttp/psr7/src/LazyOpenStream.php index 6242ee7b57fa0783cc2f79aa45c6391c2c689b52..02cec3af493cf3115dcb7cf8af0fc0a87dfbb0eb 100644 --- a/core/vendor/guzzlehttp/streams/src/LazyOpenStream.php +++ b/core/vendor/guzzlehttp/psr7/src/LazyOpenStream.php @@ -1,5 +1,7 @@ <?php -namespace GuzzleHttp\Stream; +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; /** * Lazily reads or writes to a file that is opened only after an IO operation @@ -32,6 +34,6 @@ public function __construct($filename, $mode) */ protected function createStream() { - return Stream::factory(Utils::open($this->filename, $this->mode)); + return stream_for(try_fopen($this->filename, $this->mode)); } } diff --git a/core/vendor/guzzlehttp/streams/src/LimitStream.php b/core/vendor/guzzlehttp/psr7/src/LimitStream.php similarity index 82% rename from core/vendor/guzzlehttp/streams/src/LimitStream.php rename to core/vendor/guzzlehttp/psr7/src/LimitStream.php index e9fad98573318b55a4b2f70805746956a52e1036..7f2298bcb0b1477b9cd46b4a9e3956b90d07015c 100644 --- a/core/vendor/guzzlehttp/streams/src/LimitStream.php +++ b/core/vendor/guzzlehttp/psr7/src/LimitStream.php @@ -1,7 +1,8 @@ <?php -namespace GuzzleHttp\Stream; +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; -use GuzzleHttp\Stream\Exception\SeekException; /** * Decorator used to return only a subset of a stream @@ -45,12 +46,7 @@ public function eof() return false; } - $tell = $this->stream->tell(); - if ($tell === false) { - return false; - } - - return $tell >= $this->offset + $this->limit; + return $this->stream->tell() >= $this->offset + $this->limit; } /** @@ -75,7 +71,11 @@ public function getSize() public function seek($offset, $whence = SEEK_SET) { if ($whence !== SEEK_SET || $offset < 0) { - return false; + throw new \RuntimeException(sprintf( + 'Cannot seek to offset % with whence %s', + $offset, + $whence + )); } $offset += $this->offset; @@ -86,7 +86,7 @@ public function seek($offset, $whence = SEEK_SET) } } - return $this->stream->seek($offset); + $this->stream->seek($offset); } /** @@ -103,8 +103,7 @@ public function tell() * * @param int $offset Offset to seek to and begin byte limiting from * - * @return self - * @throws SeekException + * @throws \RuntimeException if the stream cannot be seeked. */ public function setOffset($offset) { @@ -112,18 +111,16 @@ public function setOffset($offset) if ($current !== $offset) { // If the stream cannot seek to the offset position, then read to it - if (!$this->stream->seek($offset)) { - if ($current > $offset) { - throw new SeekException($this, $offset); - } else { - $this->stream->read($offset - $current); - } + if ($this->stream->isSeekable()) { + $this->stream->seek($offset); + } elseif ($current > $offset) { + throw new \RuntimeException("Could not seek to stream offset $offset"); + } else { + $this->stream->read($offset - $current); } } $this->offset = $offset; - - return $this; } /** @@ -132,13 +129,10 @@ public function setOffset($offset) * * @param int $limit Number of bytes to allow to be read from the stream. * Use -1 for no limit. - * @return self */ public function setLimit($limit) { $this->limit = $limit; - - return $this; } public function read($length) @@ -154,8 +148,8 @@ public function read($length) // Only return the amount of requested data, ensuring that the byte // limit is not exceeded return $this->stream->read(min($remaining, $length)); - } else { - return false; } + + return ''; } } diff --git a/core/vendor/guzzlehttp/psr7/src/MessageTrait.php b/core/vendor/guzzlehttp/psr7/src/MessageTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..123205cf6374ad216e0274a0fe0ea16f9bd6a785 --- /dev/null +++ b/core/vendor/guzzlehttp/psr7/src/MessageTrait.php @@ -0,0 +1,158 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Trait implementing functionality common to requests and responses. + */ +trait MessageTrait +{ + /** @var array Cached HTTP header collection with lowercase key to values */ + private $headers = []; + + /** @var array Actual key to list of values per header. */ + private $headerLines = []; + + /** @var string */ + private $protocol = '1.1'; + + /** @var StreamInterface */ + private $stream; + + public function getProtocolVersion() + { + return $this->protocol; + } + + public function withProtocolVersion($version) + { + if ($this->protocol === $version) { + return $this; + } + + $new = clone $this; + $new->protocol = $version; + return $new; + } + + public function getHeaders() + { + return $this->headerLines; + } + + public function hasHeader($header) + { + return isset($this->headers[strtolower($header)]); + } + + public function getHeader($header) + { + $name = strtolower($header); + return isset($this->headers[$name]) ? $this->headers[$name] : []; + } + + public function getHeaderLine($header) + { + return implode(', ', $this->getHeader($header)); + } + + public function withHeader($header, $value) + { + $new = clone $this; + $header = trim($header); + $name = strtolower($header); + + if (!is_array($value)) { + $new->headers[$name] = [trim($value)]; + } else { + $new->headers[$name] = $value; + foreach ($new->headers[$name] as &$v) { + $v = trim($v); + } + } + + // Remove the header lines. + foreach (array_keys($new->headerLines) as $key) { + if (strtolower($key) === $name) { + unset($new->headerLines[$key]); + } + } + + // Add the header line. + $new->headerLines[$header] = $new->headers[$name]; + + return $new; + } + + public function withAddedHeader($header, $value) + { + if (!$this->hasHeader($header)) { + return $this->withHeader($header, $value); + } + + $new = clone $this; + $new->headers[strtolower($header)][] = $value; + $new->headerLines[$header][] = $value; + return $new; + } + + public function withoutHeader($header) + { + if (!$this->hasHeader($header)) { + return $this; + } + + $new = clone $this; + $name = strtolower($header); + unset($new->headers[$name]); + + foreach (array_keys($new->headerLines) as $key) { + if (strtolower($key) === $name) { + unset($new->headerLines[$key]); + } + } + + return $new; + } + + public function getBody() + { + if (!$this->stream) { + $this->stream = stream_for(''); + } + + return $this->stream; + } + + public function withBody(StreamInterface $body) + { + if ($body === $this->stream) { + return $this; + } + + $new = clone $this; + $new->stream = $body; + return $new; + } + + private function setHeaders(array $headers) + { + $this->headerLines = $this->headers = []; + foreach ($headers as $header => $value) { + $header = trim($header); + $name = strtolower($header); + if (!is_array($value)) { + $value = trim($value); + $this->headers[$name][] = $value; + $this->headerLines[$header][] = $value; + } else { + foreach ($value as $v) { + $v = trim($v); + $this->headers[$name][] = $v; + $this->headerLines[$header][] = $v; + } + } + } + } +} diff --git a/core/vendor/guzzlehttp/psr7/src/MultipartStream.php b/core/vendor/guzzlehttp/psr7/src/MultipartStream.php new file mode 100644 index 0000000000000000000000000000000000000000..fd006ecf8a3aa2176169f09a7a11caf19a5071a7 --- /dev/null +++ b/core/vendor/guzzlehttp/psr7/src/MultipartStream.php @@ -0,0 +1,153 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; + +/** + * Stream that when read returns bytes for a streaming multipart or + * multipart/form-data stream. + */ +class MultipartStream implements StreamInterface +{ + use StreamDecoratorTrait; + + private $boundary; + + /** + * @param array $elements Array of associative arrays, each containing a + * required "name" key mapping to the form field, + * name, a required "contents" key mapping to a + * StreamInterface/resource/string, an optional + * "headers" associative array of custom headers, + * and an optional "filename" key mapping to a + * string to send as the filename in the part. + * @param string $boundary You can optionally provide a specific boundary + * + * @throws \InvalidArgumentException + */ + public function __construct(array $elements = [], $boundary = null) + { + $this->boundary = $boundary ?: uniqid(); + $this->stream = $this->createStream($elements); + } + + /** + * Get the boundary + * + * @return string + */ + public function getBoundary() + { + return $this->boundary; + } + + public function isWritable() + { + return false; + } + + /** + * Get the headers needed before transferring the content of a POST file + */ + private function getHeaders(array $headers) + { + $str = ''; + foreach ($headers as $key => $value) { + $str .= "{$key}: {$value}\r\n"; + } + + return "--{$this->boundary}\r\n" . trim($str) . "\r\n\r\n"; + } + + /** + * Create the aggregate stream that will be used to upload the POST data + */ + protected function createStream(array $elements) + { + $stream = new AppendStream(); + + foreach ($elements as $element) { + $this->addElement($stream, $element); + } + + // Add the trailing boundary with CRLF + $stream->addStream(stream_for("--{$this->boundary}--\r\n")); + + return $stream; + } + + private function addElement(AppendStream $stream, array $element) + { + foreach (['contents', 'name'] as $key) { + if (!array_key_exists($key, $element)) { + throw new \InvalidArgumentException("A '{$key}' key is required"); + } + } + + $element['contents'] = stream_for($element['contents']); + + if (empty($element['filename'])) { + $uri = $element['contents']->getMetadata('uri'); + if (substr($uri, 0, 6) !== 'php://') { + $element['filename'] = $uri; + } + } + + list($body, $headers) = $this->createElement( + $element['name'], + $element['contents'], + isset($element['filename']) ? $element['filename'] : null, + isset($element['headers']) ? $element['headers'] : [] + ); + + $stream->addStream(stream_for($this->getHeaders($headers))); + $stream->addStream($body); + $stream->addStream(stream_for("\r\n")); + } + + /** + * @return array + */ + private function createElement($name, $stream, $filename, array $headers) + { + // Set a default content-disposition header if one was no provided + $disposition = $this->getHeader($headers, 'content-disposition'); + if (!$disposition) { + $headers['Content-Disposition'] = $filename + ? sprintf('form-data; name="%s"; filename="%s"', + $name, + basename($filename)) + : "form-data; name=\"{$name}\""; + } + + // Set a default content-length header if one was no provided + $length = $this->getHeader($headers, 'content-length'); + if (!$length) { + if ($length = $stream->getSize()) { + $headers['Content-Length'] = (string) $length; + } + } + + // Set a default Content-Type if one was not supplied + $type = $this->getHeader($headers, 'content-type'); + if (!$type && $filename) { + if ($type = mimetype_from_filename($filename)) { + $headers['Content-Type'] = $type; + } + } + + return [$stream, $headers]; + } + + private function getHeader(array $headers, $key) + { + $lowercaseHeader = strtolower($key); + foreach ($headers as $k => $v) { + if (strtolower($k) === $lowercaseHeader) { + return $v; + } + } + + return null; + } +} diff --git a/core/vendor/guzzlehttp/streams/src/NoSeekStream.php b/core/vendor/guzzlehttp/psr7/src/NoSeekStream.php similarity index 67% rename from core/vendor/guzzlehttp/streams/src/NoSeekStream.php rename to core/vendor/guzzlehttp/psr7/src/NoSeekStream.php index 3d42395d76b45b8e97e983cc190b934439fd96da..233221805ec619a752486e34251555f53987f410 100644 --- a/core/vendor/guzzlehttp/streams/src/NoSeekStream.php +++ b/core/vendor/guzzlehttp/psr7/src/NoSeekStream.php @@ -1,5 +1,7 @@ <?php -namespace GuzzleHttp\Stream; +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; /** * Stream decorator that prevents a stream from being seeked @@ -10,16 +12,11 @@ class NoSeekStream implements StreamInterface public function seek($offset, $whence = SEEK_SET) { - return false; + throw new \RuntimeException('Cannot seek a NoSeekStream'); } public function isSeekable() { return false; } - - public function attach($stream) - { - $this->stream->attach($stream); - } } diff --git a/core/vendor/guzzlehttp/streams/src/PumpStream.php b/core/vendor/guzzlehttp/psr7/src/PumpStream.php similarity index 90% rename from core/vendor/guzzlehttp/streams/src/PumpStream.php rename to core/vendor/guzzlehttp/psr7/src/PumpStream.php index 9963207296b2c5fc899823c16e0199a8ae5278d9..ffb5440da16585076adb6aea04c281eb9fd492fc 100644 --- a/core/vendor/guzzlehttp/streams/src/PumpStream.php +++ b/core/vendor/guzzlehttp/psr7/src/PumpStream.php @@ -1,7 +1,7 @@ <?php -namespace GuzzleHttp\Stream; +namespace GuzzleHttp\Psr7; -use GuzzleHttp\Stream\Exception\CannotAttachException; +use Psr\Http\Message\StreamInterface; /** * Provides a read only stream that pumps data from a PHP callable. @@ -50,7 +50,11 @@ public function __construct(callable $source, array $options = []) public function __toString() { - return Utils::copyToString($this); + try { + return copy_to_string($this); + } catch (\Exception $e) { + return ''; + } } public function close() @@ -64,11 +68,6 @@ public function detach() $this->source = null; } - public function attach($stream) - { - throw new CannotAttachException(); - } - public function getSize() { return $this->size; @@ -89,9 +88,14 @@ public function isSeekable() return false; } + public function rewind() + { + $this->seek(0); + } + public function seek($offset, $whence = SEEK_SET) { - return false; + throw new \RuntimeException('Cannot seek a PumpStream'); } public function isWritable() @@ -101,7 +105,7 @@ public function isWritable() public function write($string) { - return false; + throw new \RuntimeException('Cannot write to a PumpStream'); } public function isReadable() diff --git a/core/vendor/guzzlehttp/psr7/src/Request.php b/core/vendor/guzzlehttp/psr7/src/Request.php new file mode 100644 index 0000000000000000000000000000000000000000..0189b14ce127d54fff281ba5669e55afaaf2a147 --- /dev/null +++ b/core/vendor/guzzlehttp/psr7/src/Request.php @@ -0,0 +1,149 @@ +<?php +namespace GuzzleHttp\Psr7; + +use InvalidArgumentException; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\StreamInterface; +use Psr\Http\Message\UriInterface; + +/** + * PSR-7 request implementation. + */ +class Request implements RequestInterface +{ + use MessageTrait { + withHeader as protected withParentHeader; + } + + /** @var string */ + private $method; + + /** @var null|string */ + private $requestTarget; + + /** @var null|UriInterface */ + private $uri; + + /** + * @param null|string $method HTTP method for the request. + * @param null|string $uri URI for the request. + * @param array $headers Headers for the message. + * @param string|resource|StreamInterface $body Message body. + * @param string $protocolVersion HTTP protocol version. + * + * @throws InvalidArgumentException for an invalid URI + */ + public function __construct( + $method, + $uri, + array $headers = [], + $body = null, + $protocolVersion = '1.1' + ) { + if (is_string($uri)) { + $uri = new Uri($uri); + } elseif (!($uri instanceof UriInterface)) { + throw new \InvalidArgumentException( + 'URI must be a string or Psr\Http\Message\UriInterface' + ); + } + + $this->method = strtoupper($method); + $this->uri = $uri; + $this->setHeaders($headers); + $this->protocol = $protocolVersion; + + $host = $uri->getHost(); + if ($host && !$this->hasHeader('Host')) { + $this->updateHostFromUri($host); + } + + if ($body) { + $this->stream = stream_for($body); + } + } + + public function getRequestTarget() + { + if ($this->requestTarget !== null) { + return $this->requestTarget; + } + + $target = $this->uri->getPath(); + if ($target == null) { + $target = '/'; + } + if ($this->uri->getQuery()) { + $target .= '?' . $this->uri->getQuery(); + } + + return $target; + } + + public function withRequestTarget($requestTarget) + { + if (preg_match('#\s#', $requestTarget)) { + throw new InvalidArgumentException( + 'Invalid request target provided; cannot contain whitespace' + ); + } + + $new = clone $this; + $new->requestTarget = $requestTarget; + return $new; + } + + public function getMethod() + { + return $this->method; + } + + public function withMethod($method) + { + $new = clone $this; + $new->method = strtoupper($method); + return $new; + } + + public function getUri() + { + return $this->uri; + } + + public function withUri(UriInterface $uri, $preserveHost = false) + { + if ($uri === $this->uri) { + return $this; + } + + $new = clone $this; + $new->uri = $uri; + + if (!$preserveHost) { + if ($host = $uri->getHost()) { + $new->updateHostFromUri($host); + } + } + + return $new; + } + + public function withHeader($header, $value) + { + /** @var Request $newInstance */ + $newInstance = $this->withParentHeader($header, $value); + return $newInstance; + } + + private function updateHostFromUri($host) + { + // Ensure Host is the first header. + // See: http://tools.ietf.org/html/rfc7230#section-5.4 + if ($port = $this->uri->getPort()) { + $host .= ':' . $port; + } + + $this->headerLines = ['Host' => [$host]] + $this->headerLines; + $this->headers = ['host' => [$host]] + $this->headers; + } +} diff --git a/core/vendor/guzzlehttp/psr7/src/Response.php b/core/vendor/guzzlehttp/psr7/src/Response.php new file mode 100644 index 0000000000000000000000000000000000000000..7a7a4698f8b91303173ea4c451b887ca649fb473 --- /dev/null +++ b/core/vendor/guzzlehttp/psr7/src/Response.php @@ -0,0 +1,130 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\ResponseInterface; + +/** + * PSR-7 response implementation. + */ +class Response implements ResponseInterface +{ + use MessageTrait; + + /** @var array Map of standard HTTP status code/reason phrases */ + private static $phrases = [ + 100 => 'Continue', + 101 => 'Switching Protocols', + 102 => 'Processing', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 207 => 'Multi-status', + 208 => 'Already Reported', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 306 => 'Switch 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 Time-out', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Large', + 415 => 'Unsupported Media Type', + 416 => 'Requested range not satisfiable', + 417 => 'Expectation Failed', + 418 => 'I\'m a teapot', + 422 => 'Unprocessable Entity', + 423 => 'Locked', + 424 => 'Failed Dependency', + 425 => 'Unordered Collection', + 426 => 'Upgrade Required', + 428 => 'Precondition Required', + 429 => 'Too Many Requests', + 431 => 'Request Header Fields Too Large', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Time-out', + 505 => 'HTTP Version not supported', + 506 => 'Variant Also Negotiates', + 507 => 'Insufficient Storage', + 508 => 'Loop Detected', + 511 => 'Network Authentication Required', + ]; + + /** @var null|string */ + private $reasonPhrase = ''; + + /** @var int */ + private $statusCode = 200; + + /** + * @param int $status Status code for the response, if any. + * @param array $headers Headers for the response, if any. + * @param mixed $body Stream body. + * @param string $version Protocol version. + * @param string $reason Reason phrase (a default will be used if possible). + */ + public function __construct( + $status = 200, + array $headers = [], + $body = null, + $version = '1.1', + $reason = null + ) { + $this->statusCode = (int) $status; + + if ($body) { + $this->stream = stream_for($body); + } + + $this->setHeaders($headers); + if (!$reason && isset(self::$phrases[$this->statusCode])) { + $this->reasonPhrase = self::$phrases[$status]; + } else { + $this->reasonPhrase = (string) $reason; + } + + $this->protocol = $version; + } + + public function getStatusCode() + { + return $this->statusCode; + } + + public function getReasonPhrase() + { + return $this->reasonPhrase; + } + + public function withStatus($code, $reasonPhrase = '') + { + $new = clone $this; + $new->statusCode = (int) $code; + if (!$reasonPhrase && isset(self::$phrases[$new->statusCode])) { + $reasonPhrase = self::$phrases[$new->statusCode]; + } + $new->reasonPhrase = $reasonPhrase; + return $new; + } +} diff --git a/core/vendor/guzzlehttp/streams/src/Stream.php b/core/vendor/guzzlehttp/psr7/src/Stream.php similarity index 63% rename from core/vendor/guzzlehttp/streams/src/Stream.php rename to core/vendor/guzzlehttp/psr7/src/Stream.php index 7adbc5e3f3ec44c1b6d8cb1bd72bec5a332e8fcb..0b0db0110bf2ac100542cca728475416cd98b57e 100644 --- a/core/vendor/guzzlehttp/streams/src/Stream.php +++ b/core/vendor/guzzlehttp/psr7/src/Stream.php @@ -1,8 +1,12 @@ <?php -namespace GuzzleHttp\Stream; +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; /** - * PHP stream implementation + * PHP stream implementation. + * + * @var $stream */ class Stream implements StreamInterface { @@ -30,61 +34,6 @@ class Stream implements StreamInterface ] ]; - /** - * Create a new stream based on the input type. - * - * This factory accepts the same associative array of options as described - * in the constructor. - * - * @param resource|string|StreamInterface $resource Entity body data - * @param array $options Additional options - * - * @return Stream - * @throws \InvalidArgumentException if the $resource arg is not valid. - */ - public static function factory($resource = '', array $options = []) - { - $type = gettype($resource); - - if ($type == 'string') { - $stream = fopen('php://temp', 'r+'); - if ($resource !== '') { - fwrite($stream, $resource); - fseek($stream, 0); - } - return new self($stream, $options); - } - - if ($type == 'resource') { - return new self($resource, $options); - } - - if ($resource instanceof StreamInterface) { - return $resource; - } - - if ($type == 'object' && method_exists($resource, '__toString')) { - return self::factory((string) $resource, $options); - } - - if (is_callable($resource)) { - return new PumpStream($resource, $options); - } - - if ($resource instanceof \Iterator) { - return new PumpStream(function () use ($resource) { - if (!$resource->valid()) { - return false; - } - $result = $resource->current(); - $resource->next(); - return $result; - }, $options); - } - - throw new \InvalidArgumentException('Invalid resource type: ' . $type); - } - /** * This constructor accepts an associative array of options. * @@ -113,7 +62,21 @@ public function __construct($stream, $options = []) ? $options['metadata'] : []; - $this->attach($stream); + $this->stream = $stream; + $meta = stream_get_meta_data($this->stream); + $this->seekable = $meta['seekable']; + $this->readable = isset(self::$readWriteHash['read'][$meta['mode']]); + $this->writable = isset(self::$readWriteHash['write'][$meta['mode']]); + $this->uri = $this->getMetadata('uri'); + } + + public function __get($name) + { + if ($name == 'stream') { + throw new \RuntimeException('The stream is detached'); + } + + throw new \BadMethodCallException('No value for ' . $name); } /** @@ -126,55 +89,56 @@ public function __destruct() public function __toString() { - if (!$this->stream) { + try { + $this->seek(0); + return (string) stream_get_contents($this->stream); + } catch (\Exception $e) { return ''; } - - $this->seek(0); - - return (string) stream_get_contents($this->stream); } public function getContents() { - return $this->stream ? stream_get_contents($this->stream) : ''; + $contents = stream_get_contents($this->stream); + + if ($contents === false) { + throw new \RuntimeException('Unable to read stream contents'); + } + + return $contents; } public function close() { - if (is_resource($this->stream)) { - fclose($this->stream); + if (isset($this->stream)) { + if (is_resource($this->stream)) { + fclose($this->stream); + } + $this->detach(); } - - $this->detach(); } public function detach() { + if (!isset($this->stream)) { + return null; + } + $result = $this->stream; - $this->stream = $this->size = $this->uri = null; + unset($this->stream); + $this->size = $this->uri = null; $this->readable = $this->writable = $this->seekable = false; return $result; } - public function attach($stream) - { - $this->stream = $stream; - $meta = stream_get_meta_data($this->stream); - $this->seekable = $meta['seekable']; - $this->readable = isset(self::$readWriteHash['read'][$meta['mode']]); - $this->writable = isset(self::$readWriteHash['write'][$meta['mode']]); - $this->uri = $this->getMetadata('uri'); - } - public function getSize() { if ($this->size !== null) { return $this->size; } - if (!$this->stream) { + if (!isset($this->stream)) { return null; } @@ -214,39 +178,59 @@ public function eof() public function tell() { - return $this->stream ? ftell($this->stream) : false; + $result = ftell($this->stream); + + if ($result === false) { + throw new \RuntimeException('Unable to determine stream position'); + } + + return $result; } - public function setSize($size) + public function rewind() { - $this->size = $size; - - return $this; + $this->seek(0); } public function seek($offset, $whence = SEEK_SET) { - return $this->seekable - ? fseek($this->stream, $offset, $whence) === 0 - : false; + if (!$this->seekable) { + throw new \RuntimeException('Stream is not seekable'); + } elseif (fseek($this->stream, $offset, $whence) === -1) { + throw new \RuntimeException('Unable to seek to stream position ' + . $offset . ' with whence ' . var_export($whence, true)); + } } public function read($length) { - return $this->readable ? fread($this->stream, $length) : false; + if (!$this->readable) { + throw new \RuntimeException('Cannot read from non-readable stream'); + } + + return fread($this->stream, $length); } public function write($string) { + if (!$this->writable) { + throw new \RuntimeException('Cannot write to a non-writable stream'); + } + // We can't know the size after writing anything $this->size = null; + $result = fwrite($this->stream, $string); + + if ($result === false) { + throw new \RuntimeException('Unable to write to stream'); + } - return $this->writable ? fwrite($this->stream, $string) : false; + return $result; } public function getMetadata($key = null) { - if (!$this->stream) { + if (!isset($this->stream)) { return $key ? null : []; } elseif (!$key) { return $this->customMetadata + stream_get_meta_data($this->stream); diff --git a/core/vendor/guzzlehttp/streams/src/StreamDecoratorTrait.php b/core/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php similarity index 83% rename from core/vendor/guzzlehttp/streams/src/StreamDecoratorTrait.php rename to core/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php index 39c19c58ccf1cf2ec95e807c0868383a7641313b..daec6f52ea740fe3312af92b63947bd9738168eb 100644 --- a/core/vendor/guzzlehttp/streams/src/StreamDecoratorTrait.php +++ b/core/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php @@ -1,6 +1,7 @@ <?php -namespace GuzzleHttp\Stream; -use GuzzleHttp\Stream\Exception\CannotAttachException; +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; /** * Stream decorator trait @@ -19,6 +20,10 @@ public function __construct(StreamInterface $stream) /** * Magic method used to create a new stream if streams are not added in * the constructor of a decorator (e.g., LazyOpenStream). + * + * @param string $name Name of the property (allows "stream" only). + * + * @return StreamInterface */ public function __get($name) { @@ -33,7 +38,9 @@ public function __get($name) public function __toString() { try { - $this->seek(0); + if ($this->isSeekable()) { + $this->seek(0); + } return $this->getContents(); } catch (\Exception $e) { // Really, PHP? https://bugs.php.net/bug.php?id=53648 @@ -45,7 +52,7 @@ public function __toString() public function getContents() { - return Utils::copyToString($this); + return copy_to_string($this); } /** @@ -58,7 +65,7 @@ public function getContents() */ public function __call($method, array $args) { - $result = call_user_func_array(array($this->stream, $method), $args); + $result = call_user_func_array([$this->stream, $method], $args); // Always return the wrapped object if the result is a return $this return $result === $this->stream ? $this : $result; @@ -79,11 +86,6 @@ public function detach() return $this->stream->detach(); } - public function attach($stream) - { - throw new CannotAttachException(); - } - public function getSize() { return $this->stream->getSize(); @@ -114,9 +116,14 @@ public function isSeekable() return $this->stream->isSeekable(); } + public function rewind() + { + $this->seek(0); + } + public function seek($offset, $whence = SEEK_SET) { - return $this->stream->seek($offset, $whence); + $this->stream->seek($offset, $whence); } public function read($length) @@ -137,7 +144,6 @@ public function write($string) */ protected function createStream() { - throw new \BadMethodCallException('createStream() not implemented in ' - . get_class($this)); + throw new \BadMethodCallException('Not implemented'); } } diff --git a/core/vendor/guzzlehttp/streams/src/GuzzleStreamWrapper.php b/core/vendor/guzzlehttp/psr7/src/StreamWrapper.php similarity index 94% rename from core/vendor/guzzlehttp/streams/src/GuzzleStreamWrapper.php rename to core/vendor/guzzlehttp/psr7/src/StreamWrapper.php index 4d049a6936d688dcafc6fc594acb395b3965b0d7..cf7b2232e463e21dc3eb6de440fdef70c6d057e7 100644 --- a/core/vendor/guzzlehttp/streams/src/GuzzleStreamWrapper.php +++ b/core/vendor/guzzlehttp/psr7/src/StreamWrapper.php @@ -1,10 +1,12 @@ <?php -namespace GuzzleHttp\Stream; +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\StreamInterface; /** * Converts Guzzle streams into PHP stream resources. */ -class GuzzleStreamWrapper +class StreamWrapper { /** @var resource */ public $context; @@ -87,7 +89,9 @@ public function stream_eof() public function stream_seek($offset, $whence) { - return $this->stream->seek($offset, $whence); + $this->stream->seek($offset, $whence); + + return true; } public function stream_stat() diff --git a/core/vendor/guzzlehttp/psr7/src/Uri.php b/core/vendor/guzzlehttp/psr7/src/Uri.php new file mode 100644 index 0000000000000000000000000000000000000000..58c30667207cea551d8e834dff73d0f5166e03ed --- /dev/null +++ b/core/vendor/guzzlehttp/psr7/src/Uri.php @@ -0,0 +1,604 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\UriInterface; + +/** + * Basic PSR-7 URI implementation. + * + * @link https://github.com/phly/http This class is based upon + * Matthew Weier O'Phinney's URI implementation in phly/http. + */ +class Uri implements UriInterface +{ + private static $schemes = [ + 'http' => 80, + 'https' => 443, + ]; + + private static $charUnreserved = 'a-zA-Z0-9_\-\.~'; + private static $charSubDelims = '!\$&\'\(\)\*\+,;='; + private static $replaceQuery = ['=' => '%3D', '&' => '%26']; + + /** @var string Uri scheme. */ + private $scheme = ''; + + /** @var string Uri user info. */ + private $userInfo = ''; + + /** @var string Uri host. */ + private $host = ''; + + /** @var int|null Uri port. */ + private $port; + + /** @var string Uri path. */ + private $path = ''; + + /** @var string Uri query string. */ + private $query = ''; + + /** @var string Uri fragment. */ + private $fragment = ''; + + /** + * @param string $uri URI to parse and wrap. + */ + public function __construct($uri = '') + { + if ($uri != null) { + $parts = parse_url($uri); + if ($parts === false) { + throw new \InvalidArgumentException("Unable to parse URI: $uri"); + } + $this->applyParts($parts); + } + } + + public function __toString() + { + return self::createUriString( + $this->scheme, + $this->getAuthority(), + $this->getPath(), + $this->query, + $this->fragment + ); + } + + /** + * Removes dot segments from a path and returns the new path. + * + * @param string $path + * + * @return string + * @link http://tools.ietf.org/html/rfc3986#section-5.2.4 + */ + public static function removeDotSegments($path) + { + static $noopPaths = ['' => true, '/' => true, '*' => true]; + static $ignoreSegments = ['.' => true, '..' => true]; + + if (isset($noopPaths[$path])) { + return $path; + } + + $results = []; + $segments = explode('/', $path); + foreach ($segments as $segment) { + if ($segment == '..') { + array_pop($results); + } elseif (!isset($ignoreSegments[$segment])) { + $results[] = $segment; + } + } + + $newPath = implode('/', $results); + // Add the leading slash if necessary + if (substr($path, 0, 1) === '/' && + substr($newPath, 0, 1) !== '/' + ) { + $newPath = '/' . $newPath; + } + + // Add the trailing slash if necessary + if ($newPath != '/' && isset($ignoreSegments[end($segments)])) { + $newPath .= '/'; + } + + return $newPath; + } + + /** + * Resolve a base URI with a relative URI and return a new URI. + * + * @param UriInterface $base Base URI + * @param string $rel Relative URI + * + * @return UriInterface + */ + public static function resolve(UriInterface $base, $rel) + { + if ($rel === null || $rel === '') { + return $base; + } + + if ($rel instanceof UriInterface) { + $relParts = [ + 'scheme' => $rel->getScheme(), + 'host' => $rel->getHost(), + 'port' => $rel->getPort(), + 'path' => $rel->getPath(), + 'query' => $rel->getQuery(), + 'fragment' => $rel->getFragment() + ]; + } else { + $relParts = parse_url($rel) + [ + 'scheme' => '', + 'host' => '', + 'port' => '', + 'path' => '', + 'query' => '', + 'fragment' => '' + ]; + } + + if (!empty($relParts['scheme']) && !empty($relParts['host'])) { + return $rel instanceof UriInterface + ? $rel + : self::fromParts($relParts); + } + + $parts = [ + 'scheme' => $base->getScheme(), + 'host' => $base->getHost(), + 'port' => $base->getPort(), + 'path' => $base->getPath(), + 'query' => $base->getQuery(), + 'fragment' => $base->getFragment() + ]; + + if (!empty($relParts['host'])) { + $parts['host'] = $relParts['host']; + $parts['port'] = $relParts['port']; + $parts['path'] = self::removeDotSegments($relParts['path']); + $parts['query'] = $relParts['query']; + $parts['fragment'] = $relParts['fragment']; + } elseif (!empty($relParts['path'])) { + if (substr($relParts['path'], 0, 1) == '/') { + $parts['path'] = self::removeDotSegments($relParts['path']); + $parts['query'] = $relParts['query']; + $parts['fragment'] = $relParts['fragment']; + } else { + if (!empty($parts['host']) && empty($parts['path'])) { + $mergedPath = '/'; + } else { + $mergedPath = substr($parts['path'], 0, strrpos($parts['path'], '/') + 1); + } + $parts['path'] = self::removeDotSegments($mergedPath . $relParts['path']); + $parts['query'] = $relParts['query']; + $parts['fragment'] = $relParts['fragment']; + } + } elseif (!empty($relParts['query'])) { + $parts['query'] = $relParts['query']; + } elseif ($relParts['fragment'] != null) { + $parts['fragment'] = $relParts['fragment']; + } + + return static::fromParts($parts); + } + + /** + * Create a new URI with a specific query string value removed. + * + * Any existing query string values that exactly match the provided key are + * removed. + * + * Note: this function will convert "=" to "%3D" and "&" to "%26". + * + * @param UriInterface $uri URI to use as a base. + * @param string $key Query string key value pair to remove. + * + * @return UriInterface + */ + public static function withoutQueryValue(UriInterface $uri, $key) + { + $current = $uri->getQuery(); + if (!$current) { + return $uri; + } + + $result = []; + foreach (explode('&', $current) as $part) { + if (explode('=', $part)[0] !== $key) { + $result[] = $part; + }; + } + + return $uri->withQuery(implode('&', $result)); + } + + /** + * Create a new URI with a specific query string value. + * + * Any existing query string values that exactly match the provided key are + * removed and replaced with the given key value pair. + * + * Note: this function will convert "=" to "%3D" and "&" to "%26". + * + * @param UriInterface $uri URI to use as a base. + * @param string $key Key to set. + * @param string $value Value to set. + * + * @return UriInterface + */ + public static function withQueryValue(UriInterface $uri, $key, $value) + { + $current = $uri->getQuery(); + $key = strtr($key, self::$replaceQuery); + + if (!$current) { + $result = []; + } else { + $result = []; + foreach (explode('&', $current) as $part) { + if (explode('=', $part)[0] !== $key) { + $result[] = $part; + }; + } + } + + if ($value !== null) { + $result[] = $key . '=' . strtr($value, self::$replaceQuery); + } else { + $result[] = $key; + } + + return $uri->withQuery(implode('&', $result)); + } + + /** + * Create a URI from a hash of parse_url parts. + * + * @param array $parts + * + * @return self + */ + public static function fromParts(array $parts) + { + $uri = new self(); + $uri->applyParts($parts); + return $uri; + } + + public function getScheme() + { + return $this->scheme; + } + + public function getAuthority() + { + if (empty($this->host)) { + return ''; + } + + $authority = $this->host; + if (!empty($this->userInfo)) { + $authority = $this->userInfo . '@' . $authority; + } + + if ($this->isNonStandardPort($this->scheme, $this->host, $this->port)) { + $authority .= ':' . $this->port; + } + + return $authority; + } + + public function getUserInfo() + { + return $this->userInfo; + } + + public function getHost() + { + return $this->host; + } + + public function getPort() + { + return $this->port; + } + + public function getPath() + { + return $this->path == null ? '' : $this->path; + } + + public function getQuery() + { + return $this->query; + } + + public function getFragment() + { + return $this->fragment; + } + + public function withScheme($scheme) + { + $scheme = $this->filterScheme($scheme); + + if ($this->scheme === $scheme) { + return $this; + } + + $new = clone $this; + $new->scheme = $scheme; + $new->port = $new->filterPort($new->scheme, $new->host, $new->port); + return $new; + } + + public function withUserInfo($user, $password = null) + { + $info = $user; + if ($password) { + $info .= ':' . $password; + } + + if ($this->userInfo === $info) { + return $this; + } + + $new = clone $this; + $new->userInfo = $info; + return $new; + } + + public function withHost($host) + { + if ($this->host === $host) { + return $this; + } + + $new = clone $this; + $new->host = $host; + return $new; + } + + public function withPort($port) + { + $port = $this->filterPort($this->scheme, $this->host, $port); + + if ($this->port === $port) { + return $this; + } + + $new = clone $this; + $new->port = $port; + return $new; + } + + public function withPath($path) + { + if (!is_string($path)) { + throw new \InvalidArgumentException( + 'Invalid path provided; must be a string' + ); + } + + $path = $this->filterPath($path); + + if ($this->path === $path) { + return $this; + } + + $new = clone $this; + $new->path = $path; + return $new; + } + + public function withQuery($query) + { + if (!is_string($query) && !method_exists($query, '__toString')) { + throw new \InvalidArgumentException( + 'Query string must be a string' + ); + } + + $query = (string) $query; + if (substr($query, 0, 1) === '?') { + $query = substr($query, 1); + } + + $query = $this->filterQueryAndFragment($query); + + if ($this->query === $query) { + return $this; + } + + $new = clone $this; + $new->query = $query; + return $new; + } + + public function withFragment($fragment) + { + if (substr($fragment, 0, 1) === '#') { + $fragment = substr($fragment, 1); + } + + $fragment = $this->filterQueryAndFragment($fragment); + + if ($this->fragment === $fragment) { + return $this; + } + + $new = clone $this; + $new->fragment = $fragment; + return $new; + } + + /** + * Apply parse_url parts to a URI. + * + * @param $parts Array of parse_url parts to apply. + */ + private function applyParts(array $parts) + { + $this->scheme = isset($parts['scheme']) + ? $this->filterScheme($parts['scheme']) + : ''; + $this->userInfo = isset($parts['user']) ? $parts['user'] : ''; + $this->host = isset($parts['host']) ? $parts['host'] : ''; + $this->port = !empty($parts['port']) + ? $this->filterPort($this->scheme, $this->host, $parts['port']) + : null; + $this->path = isset($parts['path']) + ? $this->filterPath($parts['path']) + : ''; + $this->query = isset($parts['query']) + ? $this->filterQueryAndFragment($parts['query']) + : ''; + $this->fragment = isset($parts['fragment']) + ? $this->filterQueryAndFragment($parts['fragment']) + : ''; + if (isset($parts['pass'])) { + $this->userInfo .= ':' . $parts['pass']; + } + } + + /** + * Create a URI string from its various parts + * + * @param string $scheme + * @param string $authority + * @param string $path + * @param string $query + * @param string $fragment + * @return string + */ + private static function createUriString($scheme, $authority, $path, $query, $fragment) + { + $uri = ''; + + if (!empty($scheme)) { + $uri .= $scheme . '://'; + } + + if (!empty($authority)) { + $uri .= $authority; + } + + if ($path != null) { + // Add a leading slash if necessary. + if ($uri && substr($path, 0, 1) !== '/') { + $uri .= '/'; + } + $uri .= $path; + } + + if ($query != null) { + $uri .= '?' . $query; + } + + if ($fragment != null) { + $uri .= '#' . $fragment; + } + + return $uri; + } + + /** + * Is a given port non-standard for the current scheme? + * + * @param string $scheme + * @param string $host + * @param int $port + * @return bool + */ + private static function isNonStandardPort($scheme, $host, $port) + { + if (!$scheme && $port) { + return true; + } + + if (!$host || !$port) { + return false; + } + + return !isset(static::$schemes[$scheme]) || $port !== static::$schemes[$scheme]; + } + + /** + * @param string $scheme + * + * @return string + */ + private function filterScheme($scheme) + { + $scheme = strtolower($scheme); + $scheme = rtrim($scheme, ':/'); + + return $scheme; + } + + /** + * @param string $scheme + * @param string $host + * @param int $port + * + * @return int|null + * + * @throws \InvalidArgumentException If the port is invalid. + */ + private function filterPort($scheme, $host, $port) + { + if (null !== $port) { + $port = (int) $port; + if (1 > $port || 0xffff < $port) { + throw new \InvalidArgumentException( + sprintf('Invalid port: %d. Must be between 1 and 65535', $port) + ); + } + } + + return $this->isNonStandardPort($scheme, $host, $port) ? $port : null; + } + + /** + * Filters the path of a URI + * + * @param $path + * + * @return string + */ + private function filterPath($path) + { + return preg_replace_callback( + '/(?:[^' . self::$charUnreserved . self::$charSubDelims . ':@\/%]+|%(?![A-Fa-f0-9]{2}))/', + [$this, 'rawurlencodeMatchZero'], + $path + ); + } + + /** + * Filters the query string or fragment of a URI. + * + * @param $str + * + * @return string + */ + private function filterQueryAndFragment($str) + { + return preg_replace_callback( + '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/\?]+|%(?![A-Fa-f0-9]{2}))/', + [$this, 'rawurlencodeMatchZero'], + $str + ); + } + + private function rawurlencodeMatchZero(array $match) + { + return rawurlencode($match[0]); + } +} diff --git a/core/vendor/guzzlehttp/psr7/src/functions.php b/core/vendor/guzzlehttp/psr7/src/functions.php new file mode 100644 index 0000000000000000000000000000000000000000..a1cec417866e7ef4429c7d27cdcf5e7447acdbe6 --- /dev/null +++ b/core/vendor/guzzlehttp/psr7/src/functions.php @@ -0,0 +1,799 @@ +<?php +namespace GuzzleHttp\Psr7; + +use Psr\Http\Message\MessageInterface; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\StreamInterface; +use Psr\Http\Message\UriInterface; + +/** + * Returns the string representation of an HTTP message. + * + * @param MessageInterface $message Message to convert to a string. + * + * @return string + */ +function str(MessageInterface $message) +{ + if ($message instanceof RequestInterface) { + $msg = trim($message->getMethod() . ' ' + . $message->getRequestTarget()) + . ' HTTP/' . $message->getProtocolVersion(); + if (!$message->hasHeader('host')) { + $msg .= "\r\nHost: " . $message->getUri()->getHost(); + } + } elseif ($message instanceof ResponseInterface) { + $msg = 'HTTP/' . $message->getProtocolVersion() . ' ' + . $message->getStatusCode() . ' ' + . $message->getReasonPhrase(); + } else { + throw new \InvalidArgumentException('Unknown message type'); + } + + foreach ($message->getHeaders() as $name => $values) { + $msg .= "\r\n{$name}: " . implode(', ', $values); + } + + return "{$msg}\r\n\r\n" . $message->getBody(); +} + +/** + * Returns a UriInterface for the given value. + * + * This function accepts a string or {@see Psr\Http\Message\UriInterface} and + * returns a UriInterface for the given value. If the value is already a + * `UriInterface`, it is returned as-is. + * + * @param string|UriInterface $uri + * + * @return UriInterface + * @throws \InvalidArgumentException + */ +function uri_for($uri) +{ + if ($uri instanceof UriInterface) { + return $uri; + } elseif (is_string($uri)) { + return new Uri($uri); + } + + throw new \InvalidArgumentException('URI must be a string or UriInterface'); +} + +/** + * Create a new stream based on the input type. + * + * Options is an associative array that can contain the following keys: + * - metadata: Array of custom metadata. + * - size: Size of the stream. + * + * @param resource|string|StreamInterface $resource Entity body data + * @param array $options Additional options + * + * @return Stream + * @throws \InvalidArgumentException if the $resource arg is not valid. + */ +function stream_for($resource = '', array $options = []) +{ + switch (gettype($resource)) { + case 'string': + $stream = fopen('php://temp', 'r+'); + if ($resource !== '') { + fwrite($stream, $resource); + fseek($stream, 0); + } + return new Stream($stream, $options); + case 'resource': + return new Stream($resource, $options); + case 'object': + if ($resource instanceof StreamInterface) { + return $resource; + } elseif ($resource instanceof \Iterator) { + return new PumpStream(function () use ($resource) { + if (!$resource->valid()) { + return false; + } + $result = $resource->current(); + $resource->next(); + return $result; + }, $options); + } elseif (method_exists($resource, '__toString')) { + return stream_for((string) $resource, $options); + } + break; + case 'NULL': + return new Stream(fopen('php://temp', 'r+'), $options); + } + + if (is_callable($resource)) { + return new PumpStream($resource, $options); + } + + throw new \InvalidArgumentException('Invalid resource type: ' . gettype($resource)); +} + +/** + * Parse an array of header values containing ";" separated data into an + * array of associative arrays representing the header key value pair + * data of the header. When a parameter does not contain a value, but just + * contains a key, this function will inject a key with a '' string value. + * + * @param string|array $header Header to parse into components. + * + * @return array Returns the parsed header values. + */ +function parse_header($header) +{ + static $trimmed = "\"' \n\t\r"; + $params = $matches = []; + + foreach (normalize_header($header) as $val) { + $part = []; + foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) { + if (preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) { + $m = $matches[0]; + if (isset($m[1])) { + $part[trim($m[0], $trimmed)] = trim($m[1], $trimmed); + } else { + $part[] = trim($m[0], $trimmed); + } + } + } + if ($part) { + $params[] = $part; + } + } + + return $params; +} + +/** + * Converts an array of header values that may contain comma separated + * headers into an array of headers with no comma separated values. + * + * @param string|array $header Header to normalize. + * + * @return array Returns the normalized header field values. + */ +function normalize_header($header) +{ + if (!is_array($header)) { + return array_map('trim', explode(',', $header)); + } + + $result = []; + foreach ($header as $value) { + foreach ((array) $value as $v) { + if (strpos($v, ',') === false) { + $result[] = $v; + continue; + } + foreach (preg_split('/,(?=([^"]*"[^"]*")*[^"]*$)/', $v) as $vv) { + $result[] = trim($vv); + } + } + } + + return $result; +} + +/** + * Clone and modify a request with the given changes. + * + * The changes can be one of: + * - method: (string) Changes the HTTP method. + * - set_headers: (array) Sets the given headers. + * - remove_headers: (array) Remove the given headers. + * - body: (mixed) Sets the given body. + * - uri: (UriInterface) Set the URI. + * - query: (string) Set the query string value of the URI. + * - version: (string) Set the protocol version. + * + * @param RequestInterface $request Request to clone and modify. + * @param array $changes Changes to apply. + * + * @return RequestInterface + */ +function modify_request(RequestInterface $request, array $changes) +{ + if (!$changes) { + return $request; + } + + $headers = $request->getHeaders(); + + if (!isset($changes['uri'])) { + $uri = $request->getUri(); + } else { + // Remove the host header if one is on the URI + if ($host = $changes['uri']->getHost()) { + $changes['set_headers']['Host'] = $host; + } + $uri = $changes['uri']; + } + + if (!empty($changes['remove_headers'])) { + $headers = _caseless_remove($changes['remove_headers'], $headers); + } + + if (!empty($changes['set_headers'])) { + $headers = _caseless_remove(array_keys($changes['set_headers']), $headers); + $headers = $changes['set_headers'] + $headers; + } + + if (isset($changes['query'])) { + $uri = $uri->withQuery($changes['query']); + } + + return new Request( + isset($changes['method']) ? $changes['method'] : $request->getMethod(), + $uri, + $headers, + isset($changes['body']) ? $changes['body'] : $request->getBody(), + isset($changes['version']) + ? $changes['version'] + : $request->getProtocolVersion() + ); +} + +/** + * Attempts to rewind a message body and throws an exception on failure. + * + * The body of the message will only be rewound if a call to `tell()` returns a + * value other than `0`. + * + * @param MessageInterface $message Message to rewind + * + * @throws \RuntimeException + */ +function rewind_body(MessageInterface $message) +{ + $body = $message->getBody(); + + if ($body->tell()) { + $body->rewind(); + } +} + +/** + * Safely opens a PHP stream resource using a filename. + * + * When fopen fails, PHP normally raises a warning. This function adds an + * error handler that checks for errors and throws an exception instead. + * + * @param string $filename File to open + * @param string $mode Mode used to open the file + * + * @return resource + * @throws \RuntimeException if the file cannot be opened + */ +function try_fopen($filename, $mode) +{ + $ex = null; + set_error_handler(function () use ($filename, $mode, &$ex) { + $ex = new \RuntimeException(sprintf( + 'Unable to open %s using mode %s: %s', + $filename, + $mode, + func_get_args()[1] + )); + }); + + $handle = fopen($filename, $mode); + restore_error_handler(); + + if ($ex) { + /** @var $ex \RuntimeException */ + throw $ex; + } + + return $handle; +} + +/** + * Copy the contents of a stream into a string until the given number of + * bytes have been read. + * + * @param StreamInterface $stream Stream to read + * @param int $maxLen Maximum number of bytes to read. Pass -1 + * to read the entire stream. + * @return string + * @throws \RuntimeException on error. + */ +function copy_to_string(StreamInterface $stream, $maxLen = -1) +{ + $buffer = ''; + + if ($maxLen === -1) { + while (!$stream->eof()) { + $buf = $stream->read(1048576); + // Using a loose equality here to match on '' and false. + if ($buf == null) { + break; + } + $buffer .= $buf; + } + return $buffer; + } + + $len = 0; + while (!$stream->eof() && $len < $maxLen) { + $buf = $stream->read($maxLen - $len); + // Using a loose equality here to match on '' and false. + if ($buf == null) { + break; + } + $buffer .= $buf; + $len = strlen($buffer); + } + + return $buffer; +} + +/** + * Copy the contents of a stream into another stream until the given number + * of bytes have been read. + * + * @param StreamInterface $source Stream to read from + * @param StreamInterface $dest Stream to write to + * @param int $maxLen Maximum number of bytes to read. Pass -1 + * to read the entire stream. + * + * @throws \RuntimeException on error. + */ +function copy_to_stream( + StreamInterface $source, + StreamInterface $dest, + $maxLen = -1 +) { + if ($maxLen === -1) { + while (!$source->eof()) { + if (!$dest->write($source->read(1048576))) { + break; + } + } + return; + } + + $bytes = 0; + while (!$source->eof()) { + $buf = $source->read($maxLen - $bytes); + if (!($len = strlen($buf))) { + break; + } + $bytes += $len; + $dest->write($buf); + if ($bytes == $maxLen) { + break; + } + } +} + +/** + * Calculate a hash of a Stream + * + * @param StreamInterface $stream Stream to calculate the hash for + * @param string $algo Hash algorithm (e.g. md5, crc32, etc) + * @param bool $rawOutput Whether or not to use raw output + * + * @return string Returns the hash of the stream + * @throws \RuntimeException on error. + */ +function hash( + StreamInterface $stream, + $algo, + $rawOutput = false +) { + $pos = $stream->tell(); + + if ($pos > 0) { + $stream->rewind(); + } + + $ctx = hash_init($algo); + while (!$stream->eof()) { + hash_update($ctx, $stream->read(1048576)); + } + + $out = hash_final($ctx, (bool) $rawOutput); + $stream->seek($pos); + + return $out; +} + +/** + * Read a line from the stream up to the maximum allowed buffer length + * + * @param StreamInterface $stream Stream to read from + * @param int $maxLength Maximum buffer length + * + * @return string|bool + */ +function readline(StreamInterface $stream, $maxLength = null) +{ + $buffer = ''; + $size = 0; + + while (!$stream->eof()) { + // Using a loose equality here to match on '' and false. + if (null == ($byte = $stream->read(1))) { + return $buffer; + } + $buffer .= $byte; + // Break when a new line is found or the max length - 1 is reached + if ($byte == PHP_EOL || ++$size == $maxLength - 1) { + break; + } + } + + return $buffer; +} + +/** + * Parses a request message string into a request object. + * + * @param string $message Request message string. + * + * @return Request + */ +function parse_request($message) +{ + $data = _parse_message($message); + if (!preg_match('/^[a-zA-Z]+\s+\/.*/', $data['start-line'])) { + throw new \InvalidArgumentException('Invalid request string'); + } + $parts = explode(' ', $data['start-line'], 3); + $version = isset($parts[2]) ? explode('/', $parts[2])[1] : '1.1'; + + return new Request( + $parts[0], + _parse_request_uri($parts[1], $data['headers']), + $data['headers'], + $data['body'], + $version + ); +} + +/** + * Parses a response message string into a response object. + * + * @param string $message Response message string. + * + * @return Response + */ +function parse_response($message) +{ + $data = _parse_message($message); + if (!preg_match('/^HTTP\/.* [0-9]{3} .*/', $data['start-line'])) { + throw new \InvalidArgumentException('Invalid response string'); + } + $parts = explode(' ', $data['start-line'], 3); + + return new Response( + $parts[1], + $data['headers'], + $data['body'], + explode('/', $parts[0])[1], + isset($parts[2]) ? $parts[2] : null + ); +} + +/** + * Parse a query string into an associative array. + * + * If multiple values are found for the same key, the value of that key + * value pair will become an array. This function does not parse nested + * PHP style arrays into an associative array (e.g., foo[a]=1&foo[b]=2 will + * be parsed into ['foo[a]' => '1', 'foo[b]' => '2']). + * + * @param string $str Query string to parse + * @param bool|string $urlEncoding How the query string is encoded + * + * @return array + */ +function parse_query($str, $urlEncoding = true) +{ + $result = []; + + if ($str === '') { + return $result; + } + + if ($urlEncoding === true) { + $decoder = function ($value) { + return rawurldecode(str_replace('+', ' ', $value)); + }; + } elseif ($urlEncoding == PHP_QUERY_RFC3986) { + $decoder = 'rawurldecode'; + } elseif ($urlEncoding == PHP_QUERY_RFC1738) { + $decoder = 'urldecode'; + } else { + $decoder = function ($str) { return $str; }; + } + + foreach (explode('&', $str) as $kvp) { + $parts = explode('=', $kvp, 2); + $key = $decoder($parts[0]); + $value = isset($parts[1]) ? $decoder($parts[1]) : null; + if (!isset($result[$key])) { + $result[$key] = $value; + } else { + if (!is_array($result[$key])) { + $result[$key] = [$result[$key]]; + } + $result[$key][] = $value; + } + } + + return $result; +} + +/** + * Build a query string from an array of key value pairs. + * + * This function can use the return value of parseQuery() to build a query + * string. This function does not modify the provided keys when an array is + * encountered (like http_build_query would). + * + * @param array $params Query string parameters. + * @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986 + * to encode using RFC3986, or PHP_QUERY_RFC1738 + * to encode using RFC1738. + * @return string + */ +function build_query(array $params, $encoding = PHP_QUERY_RFC3986) +{ + if (!$params) { + return ''; + } + + if ($encoding === false) { + $encoder = function ($str) { return $str; }; + } elseif ($encoding == PHP_QUERY_RFC3986) { + $encoder = 'rawurlencode'; + } elseif ($encoding == PHP_QUERY_RFC1738) { + $encoder = 'urlencode'; + } else { + throw new \InvalidArgumentException('Invalid type'); + } + + $qs = ''; + foreach ($params as $k => $v) { + $k = $encoder($k); + if (!is_array($v)) { + $qs .= $k; + if ($v !== null) { + $qs .= '=' . $encoder($v); + } + $qs .= '&'; + } else { + foreach ($v as $vv) { + $qs .= $k; + if ($vv !== null) { + $qs .= '=' . $encoder($vv); + } + $qs .= '&'; + } + } + } + + return $qs ? (string) substr($qs, 0, -1) : ''; +} + +/** + * Determines the mimetype of a file by looking at its extension. + * + * @param $filename + * + * @return null|string + */ +function mimetype_from_filename($filename) +{ + return mimetype_from_extension(pathinfo($filename, PATHINFO_EXTENSION)); +} + +/** + * Maps a file extensions to a mimetype. + * + * @param $extension string The file extension. + * + * @return string|null + * @link http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types + */ +function mimetype_from_extension($extension) +{ + static $mimetypes = [ + '7z' => 'application/x-7z-compressed', + 'aac' => 'audio/x-aac', + 'ai' => 'application/postscript', + 'aif' => 'audio/x-aiff', + 'asc' => 'text/plain', + 'asf' => 'video/x-ms-asf', + 'atom' => 'application/atom+xml', + 'avi' => 'video/x-msvideo', + 'bmp' => 'image/bmp', + 'bz2' => 'application/x-bzip2', + 'cer' => 'application/pkix-cert', + 'crl' => 'application/pkix-crl', + 'crt' => 'application/x-x509-ca-cert', + 'css' => 'text/css', + 'csv' => 'text/csv', + 'cu' => 'application/cu-seeme', + 'deb' => 'application/x-debian-package', + 'doc' => 'application/msword', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dvi' => 'application/x-dvi', + 'eot' => 'application/vnd.ms-fontobject', + 'eps' => 'application/postscript', + 'epub' => 'application/epub+zip', + 'etx' => 'text/x-setext', + 'flac' => 'audio/flac', + 'flv' => 'video/x-flv', + 'gif' => 'image/gif', + 'gz' => 'application/gzip', + 'htm' => 'text/html', + 'html' => 'text/html', + 'ico' => 'image/x-icon', + 'ics' => 'text/calendar', + 'ini' => 'text/plain', + 'iso' => 'application/x-iso9660-image', + 'jar' => 'application/java-archive', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'js' => 'text/javascript', + 'json' => 'application/json', + 'latex' => 'application/x-latex', + 'log' => 'text/plain', + 'm4a' => 'audio/mp4', + 'm4v' => 'video/mp4', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mov' => 'video/quicktime', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'mp4a' => 'audio/mp4', + 'mp4v' => 'video/mp4', + 'mpe' => 'video/mpeg', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpg4' => 'video/mp4', + 'oga' => 'audio/ogg', + 'ogg' => 'audio/ogg', + 'ogv' => 'video/ogg', + 'ogx' => 'application/ogg', + 'pbm' => 'image/x-portable-bitmap', + 'pdf' => 'application/pdf', + 'pgm' => 'image/x-portable-graymap', + 'png' => 'image/png', + 'pnm' => 'image/x-portable-anymap', + 'ppm' => 'image/x-portable-pixmap', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'ps' => 'application/postscript', + 'qt' => 'video/quicktime', + 'rar' => 'application/x-rar-compressed', + 'ras' => 'image/x-cmu-raster', + 'rss' => 'application/rss+xml', + 'rtf' => 'application/rtf', + 'sgm' => 'text/sgml', + 'sgml' => 'text/sgml', + 'svg' => 'image/svg+xml', + 'swf' => 'application/x-shockwave-flash', + 'tar' => 'application/x-tar', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'torrent' => 'application/x-bittorrent', + 'ttf' => 'application/x-font-ttf', + 'txt' => 'text/plain', + 'wav' => 'audio/x-wav', + 'webm' => 'video/webm', + 'wma' => 'audio/x-ms-wma', + 'wmv' => 'video/x-ms-wmv', + 'woff' => 'application/x-font-woff', + 'wsdl' => 'application/wsdl+xml', + 'xbm' => 'image/x-xbitmap', + 'xls' => 'application/vnd.ms-excel', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xml' => 'application/xml', + 'xpm' => 'image/x-xpixmap', + 'xwd' => 'image/x-xwindowdump', + 'yaml' => 'text/yaml', + 'yml' => 'text/yaml', + 'zip' => 'application/zip', + ]; + + $extension = strtolower($extension); + + return isset($mimetypes[$extension]) + ? $mimetypes[$extension] + : null; +} + +/** + * Parses an HTTP message into an associative array. + * + * The array contains the "start-line" key containing the start line of + * the message, "headers" key containing an associative array of header + * array values, and a "body" key containing the body of the message. + * + * @param string $message HTTP request or response to parse. + * + * @return array + * @internal + */ +function _parse_message($message) +{ + if (!$message) { + throw new \InvalidArgumentException('Invalid message'); + } + + // Iterate over each line in the message, accounting for line endings + $lines = preg_split('/(\\r?\\n)/', $message, -1, PREG_SPLIT_DELIM_CAPTURE); + $result = ['start-line' => array_shift($lines), 'headers' => [], 'body' => '']; + array_shift($lines); + + for ($i = 0, $totalLines = count($lines); $i < $totalLines; $i += 2) { + $line = $lines[$i]; + // If two line breaks were encountered, then this is the end of body + if (empty($line)) { + if ($i < $totalLines - 1) { + $result['body'] = implode('', array_slice($lines, $i + 2)); + } + break; + } + if (strpos($line, ':')) { + $parts = explode(':', $line, 2); + $key = trim($parts[0]); + $value = isset($parts[1]) ? trim($parts[1]) : ''; + $result['headers'][$key][] = $value; + } + } + + return $result; +} + +/** + * Constructs a URI for an HTTP request message. + * + * @param string $path Path from the start-line + * @param array $headers Array of headers (each value an array). + * + * @return string + * @internal + */ +function _parse_request_uri($path, array $headers) +{ + $hostKey = array_filter(array_keys($headers), function ($k) { + return strtolower($k) === 'host'; + }); + + // If no host is found, then a full URI cannot be constructed. + if (!$hostKey) { + return $path; + } + + $host = $headers[reset($hostKey)][0]; + $scheme = substr($host, -4) === ':443' ? 'https' : 'http'; + + return $scheme . '://' . $host . '/' . ltrim($path, '/'); +} + +/** @internal */ +function _caseless_remove($keys, array $data) +{ + $result = []; + + foreach ($keys as &$key) { + $key = strtolower($key); + } + + foreach ($data as $k => $v) { + if (!in_array(strtolower($k), $keys)) { + $result[$k] = $v; + } + } + + return $result; +} diff --git a/core/vendor/guzzlehttp/streams/tests/AppendStreamTest.php b/core/vendor/guzzlehttp/psr7/tests/AppendStreamTest.php similarity index 69% rename from core/vendor/guzzlehttp/streams/tests/AppendStreamTest.php rename to core/vendor/guzzlehttp/psr7/tests/AppendStreamTest.php index 78798d9f7a0f33ba444e8766ec966967ea8f8c3e..3c197dcea896a50a9bf6fdbeeb47f80ec973d861 100644 --- a/core/vendor/guzzlehttp/streams/tests/AppendStreamTest.php +++ b/core/vendor/guzzlehttp/psr7/tests/AppendStreamTest.php @@ -1,8 +1,8 @@ <?php -namespace GuzzleHttp\Tests\Stream; +namespace GuzzleHttp\Tests\Psr7; -use GuzzleHttp\Stream\AppendStream; -use GuzzleHttp\Stream\Stream; +use GuzzleHttp\Psr7\AppendStream; +use GuzzleHttp\Psr7; class AppendStreamTest extends \PHPUnit_Framework_TestCase { @@ -13,7 +13,7 @@ class AppendStreamTest extends \PHPUnit_Framework_TestCase public function testValidatesStreamsAreReadable() { $a = new AppendStream(); - $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + $s = $this->getMockBuilder('Psr\Http\Message\StreamInterface') ->setMethods(['isReadable']) ->getMockForAbstractClass(); $s->expects($this->once()) @@ -22,17 +22,25 @@ public function testValidatesStreamsAreReadable() $a->addStream($s); } + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage The AppendStream can only seek with SEEK_SET + */ public function testValidatesSeekType() { $a = new AppendStream(); - $this->assertFalse($a->seek(100, SEEK_CUR)); + $a->seek(100, SEEK_CUR); } + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Unable to seek stream 0 of the AppendStream + */ public function testTriesToRewindOnSeek() { $a = new AppendStream(); - $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') - ->setMethods(['isReadable', 'seek', 'isSeekable']) + $s = $this->getMockBuilder('Psr\Http\Message\StreamInterface') + ->setMethods(['isReadable', 'rewind', 'isSeekable']) ->getMockForAbstractClass(); $s->expects($this->once()) ->method('isReadable') @@ -41,23 +49,24 @@ public function testTriesToRewindOnSeek() ->method('isSeekable') ->will($this->returnValue(true)); $s->expects($this->once()) - ->method('seek') - ->will($this->returnValue(false)); + ->method('rewind') + ->will($this->throwException(new \RuntimeException())); $a->addStream($s); - $this->assertFalse($a->seek(10)); + $a->seek(10); } public function testSeeksToPositionByReading() { $a = new AppendStream([ - Stream::factory('foo'), - Stream::factory('bar'), - Stream::factory('baz'), + Psr7\stream_for('foo'), + Psr7\stream_for('bar'), + Psr7\stream_for('baz'), ]); - $this->assertTrue($a->seek(3)); + $a->seek(3); $this->assertEquals(3, $a->tell()); $this->assertEquals('bar', $a->read(3)); + $a->seek(6); $this->assertEquals(6, $a->tell()); $this->assertEquals('baz', $a->read(3)); @@ -65,10 +74,10 @@ public function testSeeksToPositionByReading() public function testDetachesEachStream() { - $s1 = Stream::factory('foo'); - $s2 = Stream::factory('foo'); + $s1 = Psr7\stream_for('foo'); + $s2 = Psr7\stream_for('bar'); $a = new AppendStream([$s1, $s2]); - $this->assertSame('foofoo', (string) $a); + $this->assertSame('foobar', (string) $a); $a->detach(); $this->assertSame('', (string) $a); $this->assertSame(0, $a->getSize()); @@ -76,19 +85,23 @@ public function testDetachesEachStream() public function testClosesEachStream() { - $s1 = Stream::factory('foo'); + $s1 = Psr7\stream_for('foo'); $a = new AppendStream([$s1]); $a->close(); $this->assertSame('', (string) $a); } + /** + * @expectedExceptionMessage Cannot write to an AppendStream + * @expectedException \RuntimeException + */ public function testIsNotWritable() { - $a = new AppendStream([Stream::factory('foo')]); + $a = new AppendStream([Psr7\stream_for('foo')]); $this->assertFalse($a->isWritable()); $this->assertTrue($a->isSeekable()); $this->assertTrue($a->isReadable()); - $this->assertFalse($a->write('foo')); + $a->write('foo'); } public function testDoesNotNeedStreams() @@ -100,15 +113,16 @@ public function testDoesNotNeedStreams() public function testCanReadFromMultipleStreams() { $a = new AppendStream([ - Stream::factory('foo'), - Stream::factory('bar'), - Stream::factory('baz'), + Psr7\stream_for('foo'), + Psr7\stream_for('bar'), + Psr7\stream_for('baz'), ]); $this->assertFalse($a->eof()); $this->assertSame(0, $a->tell()); $this->assertEquals('foo', $a->read(3)); $this->assertEquals('bar', $a->read(3)); $this->assertEquals('baz', $a->read(3)); + $this->assertSame('', $a->read(1)); $this->assertTrue($a->eof()); $this->assertSame(9, $a->tell()); $this->assertEquals('foobarbaz', (string) $a); @@ -117,12 +131,12 @@ public function testCanReadFromMultipleStreams() public function testCanDetermineSizeFromMultipleStreams() { $a = new AppendStream([ - Stream::factory('foo'), - Stream::factory('bar') + Psr7\stream_for('foo'), + Psr7\stream_for('bar') ]); $this->assertEquals(6, $a->getSize()); - $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + $s = $this->getMockBuilder('Psr\Http\Message\StreamInterface') ->setMethods(['isSeekable', 'isReadable']) ->getMockForAbstractClass(); $s->expects($this->once()) @@ -137,9 +151,12 @@ public function testCanDetermineSizeFromMultipleStreams() public function testCatchesExceptionsWhenCastingToString() { - $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') - ->setMethods(['read', 'isReadable', 'eof']) + $s = $this->getMockBuilder('Psr\Http\Message\StreamInterface') + ->setMethods(['isSeekable', 'read', 'isReadable', 'eof']) ->getMockForAbstractClass(); + $s->expects($this->once()) + ->method('isSeekable') + ->will($this->returnValue(true)); $s->expects($this->once()) ->method('read') ->will($this->throwException(new \RuntimeException('foo'))); @@ -166,13 +183,4 @@ public function testReturnsEmptyMetadata() $this->assertEquals([], $s->getMetadata()); $this->assertNull($s->getMetadata('foo')); } - - /** - * @expectedException \GuzzleHttp\Stream\Exception\CannotAttachException - */ - public function testCannotAttach() - { - $p = new AppendStream(); - $p->attach('a'); - } } diff --git a/core/vendor/guzzlehttp/streams/tests/BufferStreamTest.php b/core/vendor/guzzlehttp/psr7/tests/BufferStreamTest.php similarity index 83% rename from core/vendor/guzzlehttp/streams/tests/BufferStreamTest.php rename to core/vendor/guzzlehttp/psr7/tests/BufferStreamTest.php index f9bfea21d183369dc1f480673dc17ba2ba70b0f0..0a635d467106be06619a7ab5eb53057bb6268499 100644 --- a/core/vendor/guzzlehttp/streams/tests/BufferStreamTest.php +++ b/core/vendor/guzzlehttp/psr7/tests/BufferStreamTest.php @@ -1,7 +1,7 @@ <?php -namespace GuzzleHttp\Tests\Stream; +namespace GuzzleHttp\Tests\Psr7; -use GuzzleHttp\Stream\BufferStream; +use GuzzleHttp\Psr7\BufferStream; class BufferStreamTest extends \PHPUnit_Framework_TestCase { @@ -27,6 +27,10 @@ public function testRemovesReadDataFromBuffer() $this->assertEquals('', $b->read(10)); } + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Cannot determine the position of a BufferStream + */ public function testCanCastToStringOrGetContents() { $b = new BufferStream(); @@ -35,7 +39,7 @@ public function testCanCastToStringOrGetContents() $this->assertEquals('foo', $b->read(3)); $b->write('bar'); $this->assertEquals('bazbar', (string) $b); - $this->assertFalse($b->tell()); + $b->tell(); } public function testDetachClearsBuffer() @@ -43,7 +47,6 @@ public function testDetachClearsBuffer() $b = new BufferStream(); $b->write('foo'); $b->detach(); - $this->assertEquals(0, $b->tell()); $this->assertTrue($b->eof()); $this->assertEquals(3, $b->write('abc')); $this->assertEquals('abc', $b->read(10)); @@ -57,13 +60,4 @@ public function testExceedingHighwaterMarkReturnsFalseButStillBuffers() $this->assertEquals('hi hello', (string) $b); $this->assertEquals(4, $b->write('test')); } - - /** - * @expectedException \GuzzleHttp\Stream\Exception\CannotAttachException - */ - public function testCannotAttach() - { - $p = new BufferStream(); - $p->attach('a'); - } } diff --git a/core/vendor/guzzlehttp/streams/tests/CachingStreamTest.php b/core/vendor/guzzlehttp/psr7/tests/CachingStreamTest.php similarity index 73% rename from core/vendor/guzzlehttp/streams/tests/CachingStreamTest.php rename to core/vendor/guzzlehttp/psr7/tests/CachingStreamTest.php index ea969b3ad7e78b8027bc9bcde95054597eb44d71..e2ed0a744c473bb06486229eedda65cde0fdc8fa 100644 --- a/core/vendor/guzzlehttp/streams/tests/CachingStreamTest.php +++ b/core/vendor/guzzlehttp/psr7/tests/CachingStreamTest.php @@ -1,24 +1,21 @@ <?php -namespace GuzzleHttp\Tests\Stream; +namespace GuzzleHttp\Tests\Psr7; -use GuzzleHttp\Stream\Stream; -use GuzzleHttp\Stream\CachingStream; -use GuzzleHttp\Stream\Utils; +use GuzzleHttp\Psr7; +use GuzzleHttp\Psr7\CachingStream; /** - * @covers GuzzleHttp\Stream\CachingStream + * @covers GuzzleHttp\Psr7\CachingStream */ class CachingStreamTest extends \PHPUnit_Framework_TestCase { /** @var CachingStream */ protected $body; - - /** @var Stream */ protected $decorated; public function setUp() { - $this->decorated = Stream::factory('testing'); + $this->decorated = Psr7\stream_for('testing'); $this->body = new CachingStream($this->decorated); } @@ -30,7 +27,7 @@ public function tearDown() public function testUsesRemoteSizeIfPossible() { - $body = Stream::factory('test'); + $body = Psr7\stream_for('test'); $caching = new CachingStream($body); $this->assertEquals(4, $caching->getSize()); } @@ -44,15 +41,19 @@ public function testCannotSeekPastWhatHasBeenRead() $this->body->seek(10); } + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage CachingStream::seek() supports SEEK_SET and SEEK_CUR + */ public function testCannotUseSeekEnd() { - $this->assertFalse($this->body->seek(2, SEEK_END)); + $this->body->seek(2, SEEK_END); } public function testRewindUsesSeek() { - $a = Stream::factory('foo'); - $d = $this->getMockBuilder('GuzzleHttp\Stream\CachingStream') + $a = Psr7\stream_for('foo'); + $d = $this->getMockBuilder('GuzzleHttp\Psr7\CachingStream') ->setMethods(array('seek')) ->setConstructorArgs(array($a)) ->getMock(); @@ -86,7 +87,7 @@ public function testWritesToBufferStream() public function testSkipsOverwrittenBytes() { - $decorated = Stream::factory( + $decorated = Psr7\stream_for( implode("\n", array_map(function ($n) { return str_pad($n, 4, '0', STR_PAD_LEFT); }, range(0, 25))) @@ -94,26 +95,26 @@ public function testSkipsOverwrittenBytes() $body = new CachingStream($decorated); - $this->assertEquals("0000\n", Utils::readline($body)); - $this->assertEquals("0001\n", Utils::readline($body)); + $this->assertEquals("0000\n", Psr7\readline($body)); + $this->assertEquals("0001\n", Psr7\readline($body)); // Write over part of the body yet to be read, so skip some bytes $this->assertEquals(5, $body->write("TEST\n")); $this->assertEquals(5, $this->readAttribute($body, 'skipReadBytes')); // Read, which skips bytes, then reads - $this->assertEquals("0003\n", Utils::readline($body)); + $this->assertEquals("0003\n", Psr7\readline($body)); $this->assertEquals(0, $this->readAttribute($body, 'skipReadBytes')); - $this->assertEquals("0004\n", Utils::readline($body)); - $this->assertEquals("0005\n", Utils::readline($body)); + $this->assertEquals("0004\n", Psr7\readline($body)); + $this->assertEquals("0005\n", Psr7\readline($body)); // Overwrite part of the cached body (so don't skip any bytes) $body->seek(5); $this->assertEquals(5, $body->write("ABCD\n")); $this->assertEquals(0, $this->readAttribute($body, 'skipReadBytes')); - $this->assertEquals("TEST\n", Utils::readline($body)); - $this->assertEquals("0003\n", Utils::readline($body)); - $this->assertEquals("0004\n", Utils::readline($body)); - $this->assertEquals("0005\n", Utils::readline($body)); - $this->assertEquals("0006\n", Utils::readline($body)); + $this->assertEquals("TEST\n", Psr7\readline($body)); + $this->assertEquals("0003\n", Psr7\readline($body)); + $this->assertEquals("0004\n", Psr7\readline($body)); + $this->assertEquals("0005\n", Psr7\readline($body)); + $this->assertEquals("0006\n", Psr7\readline($body)); $this->assertEquals(5, $body->write("1234\n")); $this->assertEquals(5, $this->readAttribute($body, 'skipReadBytes')); @@ -128,7 +129,7 @@ public function testSkipsOverwrittenBytes() public function testClosesBothStreams() { $s = fopen('php://temp', 'r'); - $a = Stream::factory($s); + $a = Psr7\stream_for($s); $d = new CachingStream($a); $d->close(); $this->assertFalse(is_resource($s)); diff --git a/core/vendor/guzzlehttp/streams/tests/DroppingStreamTest.php b/core/vendor/guzzlehttp/psr7/tests/DroppingStreamTest.php similarity index 77% rename from core/vendor/guzzlehttp/streams/tests/DroppingStreamTest.php rename to core/vendor/guzzlehttp/psr7/tests/DroppingStreamTest.php index bb2cb22048ebdade5ce4e083adad50484ba0ba04..915b215237c3a1930c0014680c63f61a3a5243f4 100644 --- a/core/vendor/guzzlehttp/streams/tests/DroppingStreamTest.php +++ b/core/vendor/guzzlehttp/psr7/tests/DroppingStreamTest.php @@ -1,8 +1,8 @@ <?php -namespace GuzzleHttp\Tests\Stream; +namespace GuzzleHttp\Tests\Psr7; -use GuzzleHttp\Stream\BufferStream; -use GuzzleHttp\Stream\DroppingStream; +use GuzzleHttp\Psr7\BufferStream; +use GuzzleHttp\Psr7\DroppingStream; class DroppingStreamTest extends \PHPUnit_Framework_TestCase { @@ -11,7 +11,7 @@ public function testBeginsDroppingWhenSizeExceeded() $stream = new BufferStream(); $drop = new DroppingStream($stream, 5); $this->assertEquals(3, $drop->write('hel')); - $this->assertFalse($drop->write('lo')); + $this->assertEquals(2, $drop->write('lo')); $this->assertEquals(5, $drop->getSize()); $this->assertEquals('hello', $drop->read(5)); $this->assertEquals(0, $drop->getSize()); @@ -21,6 +21,6 @@ public function testBeginsDroppingWhenSizeExceeded() $this->assertEquals('12345', (string) $drop); $this->assertEquals(0, $drop->getSize()); $drop->write('hello'); - $this->assertFalse($drop->write('test')); + $this->assertSame(0, $drop->write('test')); } } diff --git a/core/vendor/guzzlehttp/streams/tests/FnStreamTest.php b/core/vendor/guzzlehttp/psr7/tests/FnStreamTest.php similarity index 90% rename from core/vendor/guzzlehttp/streams/tests/FnStreamTest.php rename to core/vendor/guzzlehttp/psr7/tests/FnStreamTest.php index 6cc336b916c6f17b47c743ef73d1a7dede91e623..66ae90a04afd905a0cf1278bc9b450924959e2d9 100644 --- a/core/vendor/guzzlehttp/streams/tests/FnStreamTest.php +++ b/core/vendor/guzzlehttp/psr7/tests/FnStreamTest.php @@ -1,11 +1,11 @@ <?php -namespace GuzzleHttp\Tests\Stream; +namespace GuzzleHttp\Tests\Psr7; -use GuzzleHttp\Stream\Stream; -use GuzzleHttp\Stream\FnStream; +use GuzzleHttp\Psr7; +use GuzzleHttp\Psr7\FnStream; /** - * @covers GuzzleHttp\Stream\FnStream + * @covers GuzzleHttp\Psr7\FnStream */ class FnStreamTest extends \PHPUnit_Framework_TestCase { @@ -50,7 +50,7 @@ public function testDoesNotRequireClose() public function testDecoratesStream() { - $a = Stream::factory('foo'); + $a = Psr7\stream_for('foo'); $b = FnStream::decorate($a, []); $this->assertEquals(3, $b->getSize()); $this->assertEquals($b->isWritable(), true); @@ -59,6 +59,7 @@ public function testDecoratesStream() $this->assertEquals($b->read(3), 'foo'); $this->assertEquals($b->tell(), 3); $this->assertEquals($a->tell(), 3); + $this->assertSame('', $a->read(1)); $this->assertEquals($b->eof(), true); $this->assertEquals($a->eof(), true); $b->seek(0); @@ -76,7 +77,7 @@ public function testDecoratesStream() public function testDecoratesWithCustomizations() { $called = false; - $a = Stream::factory('foo'); + $a = Psr7\stream_for('foo'); $b = FnStream::decorate($a, [ 'read' => function ($len) use (&$called, $a) { $called = true; diff --git a/core/vendor/guzzlehttp/psr7/tests/FunctionsTest.php b/core/vendor/guzzlehttp/psr7/tests/FunctionsTest.php new file mode 100644 index 0000000000000000000000000000000000000000..6675039abe13af5d3a857ff20e9645f9a8305af7 --- /dev/null +++ b/core/vendor/guzzlehttp/psr7/tests/FunctionsTest.php @@ -0,0 +1,574 @@ +<?php +namespace GuzzleHttp\Tests\Psr7; + +use GuzzleHttp\Psr7; +use GuzzleHttp\Psr7\FnStream; +use GuzzleHttp\Psr7\NoSeekStream; + +class FunctionsTest extends \PHPUnit_Framework_TestCase +{ + public function testCopiesToString() + { + $s = Psr7\stream_for('foobaz'); + $this->assertEquals('foobaz', Psr7\copy_to_string($s)); + $s->seek(0); + $this->assertEquals('foo', Psr7\copy_to_string($s, 3)); + $this->assertEquals('baz', Psr7\copy_to_string($s, 3)); + $this->assertEquals('', Psr7\copy_to_string($s)); + } + + public function testCopiesToStringStopsWhenReadFails() + { + $s1 = Psr7\stream_for('foobaz'); + $s1 = FnStream::decorate($s1, [ + 'read' => function () { return ''; } + ]); + $result = Psr7\copy_to_string($s1); + $this->assertEquals('', $result); + } + + public function testCopiesToStream() + { + $s1 = Psr7\stream_for('foobaz'); + $s2 = Psr7\stream_for(''); + Psr7\copy_to_stream($s1, $s2); + $this->assertEquals('foobaz', (string) $s2); + $s2 = Psr7\stream_for(''); + $s1->seek(0); + Psr7\copy_to_stream($s1, $s2, 3); + $this->assertEquals('foo', (string) $s2); + Psr7\copy_to_stream($s1, $s2, 3); + $this->assertEquals('foobaz', (string) $s2); + } + + public function testStopsCopyToStreamWhenWriteFails() + { + $s1 = Psr7\stream_for('foobaz'); + $s2 = Psr7\stream_for(''); + $s2 = FnStream::decorate($s2, ['write' => function () { return 0; }]); + Psr7\copy_to_stream($s1, $s2); + $this->assertEquals('', (string) $s2); + } + + public function testStopsCopyToSteamWhenWriteFailsWithMaxLen() + { + $s1 = Psr7\stream_for('foobaz'); + $s2 = Psr7\stream_for(''); + $s2 = FnStream::decorate($s2, ['write' => function () { return 0; }]); + Psr7\copy_to_stream($s1, $s2, 10); + $this->assertEquals('', (string) $s2); + } + + public function testStopsCopyToSteamWhenReadFailsWithMaxLen() + { + $s1 = Psr7\stream_for('foobaz'); + $s1 = FnStream::decorate($s1, ['read' => function () { return ''; }]); + $s2 = Psr7\stream_for(''); + Psr7\copy_to_stream($s1, $s2, 10); + $this->assertEquals('', (string) $s2); + } + + public function testReadsLines() + { + $s = Psr7\stream_for("foo\nbaz\nbar"); + $this->assertEquals("foo\n", Psr7\readline($s)); + $this->assertEquals("baz\n", Psr7\readline($s)); + $this->assertEquals("bar", Psr7\readline($s)); + } + + public function testReadsLinesUpToMaxLength() + { + $s = Psr7\stream_for("12345\n"); + $this->assertEquals("123", Psr7\readline($s, 4)); + $this->assertEquals("45\n", Psr7\readline($s)); + } + + public function testReadsLineUntilFalseReturnedFromRead() + { + $s = $this->getMockBuilder('GuzzleHttp\Psr7\Stream') + ->setMethods(['read', 'eof']) + ->disableOriginalConstructor() + ->getMock(); + $s->expects($this->exactly(2)) + ->method('read') + ->will($this->returnCallback(function () { + static $c = false; + if ($c) { + return false; + } + $c = true; + return 'h'; + })); + $s->expects($this->exactly(2)) + ->method('eof') + ->will($this->returnValue(false)); + $this->assertEquals("h", Psr7\readline($s)); + } + + public function testCalculatesHash() + { + $s = Psr7\stream_for('foobazbar'); + $this->assertEquals(md5('foobazbar'), Psr7\hash($s, 'md5')); + } + + /** + * @expectedException \RuntimeException + */ + public function testCalculatesHashThrowsWhenSeekFails() + { + $s = new NoSeekStream(Psr7\stream_for('foobazbar')); + $s->read(2); + Psr7\hash($s, 'md5'); + } + + public function testCalculatesHashSeeksToOriginalPosition() + { + $s = Psr7\stream_for('foobazbar'); + $s->seek(4); + $this->assertEquals(md5('foobazbar'), Psr7\hash($s, 'md5')); + $this->assertEquals(4, $s->tell()); + } + + public function testOpensFilesSuccessfully() + { + $r = Psr7\try_fopen(__FILE__, 'r'); + $this->assertInternalType('resource', $r); + fclose($r); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Unable to open /path/to/does/not/exist using mode r + */ + public function testThrowsExceptionNotWarning() + { + Psr7\try_fopen('/path/to/does/not/exist', 'r'); + } + + public function parseQueryProvider() + { + return [ + // Does not need to parse when the string is empty + ['', []], + // Can parse mult-values items + ['q=a&q=b', ['q' => ['a', 'b']]], + // Can parse multi-valued items that use numeric indices + ['q[0]=a&q[1]=b', ['q[0]' => 'a', 'q[1]' => 'b']], + // Can parse duplicates and does not include numeric indices + ['q[]=a&q[]=b', ['q[]' => ['a', 'b']]], + // Ensures that the value of "q" is an array even though one value + ['q[]=a', ['q[]' => 'a']], + // Does not modify "." to "_" like PHP's parse_str() + ['q.a=a&q.b=b', ['q.a' => 'a', 'q.b' => 'b']], + // Can decode %20 to " " + ['q%20a=a%20b', ['q a' => 'a b']], + // Can parse funky strings with no values by assigning each to null + ['q&a', ['q' => null, 'a' => null]], + // Does not strip trailing equal signs + ['data=abc=', ['data' => 'abc=']], + // Can store duplicates without affecting other values + ['foo=a&foo=b&?µ=c', ['foo' => ['a', 'b'], '?µ' => 'c']], + // Sets value to null when no "=" is present + ['foo', ['foo' => null]], + // Preserves "0" keys. + ['0', ['0' => null]], + // Sets the value to an empty string when "=" is present + ['0=', ['0' => '']], + // Preserves falsey keys + ['var=0', ['var' => '0']], + ['a[b][c]=1&a[b][c]=2', ['a[b][c]' => ['1', '2']]], + ['a[b]=c&a[d]=e', ['a[b]' => 'c', 'a[d]' => 'e']], + // Ensure it doesn't leave things behind with repeated values + // Can parse mult-values items + ['q=a&q=b&q=c', ['q' => ['a', 'b', 'c']]], + ]; + } + + /** + * @dataProvider parseQueryProvider + */ + public function testParsesQueries($input, $output) + { + $result = Psr7\parse_query($input); + $this->assertSame($output, $result); + } + + public function testDoesNotDecode() + { + $str = 'foo%20=bar'; + $data = Psr7\parse_query($str, false); + $this->assertEquals(['foo%20' => 'bar'], $data); + } + + /** + * @dataProvider parseQueryProvider + */ + public function testParsesAndBuildsQueries($input, $output) + { + $result = Psr7\parse_query($input, false); + $this->assertSame($input, Psr7\build_query($result, false)); + } + + public function testEncodesWithRfc1738() + { + $str = Psr7\build_query(['foo bar' => 'baz+'], PHP_QUERY_RFC1738); + $this->assertEquals('foo+bar=baz%2B', $str); + } + + public function testEncodesWithRfc3986() + { + $str = Psr7\build_query(['foo bar' => 'baz+'], PHP_QUERY_RFC3986); + $this->assertEquals('foo%20bar=baz%2B', $str); + } + + public function testDoesNotEncode() + { + $str = Psr7\build_query(['foo bar' => 'baz+'], false); + $this->assertEquals('foo bar=baz+', $str); + } + + public function testCanControlDecodingType() + { + $result = Psr7\parse_query('var=foo+bar', PHP_QUERY_RFC3986); + $this->assertEquals('foo+bar', $result['var']); + $result = Psr7\parse_query('var=foo+bar', PHP_QUERY_RFC1738); + $this->assertEquals('foo bar', $result['var']); + } + + public function testParsesRequestMessages() + { + $req = "GET /abc HTTP/1.0\r\nHost: foo.com\r\nFoo: Bar\r\nBaz: Bam\r\nBaz: Qux\r\n\r\nTest"; + $request = Psr7\parse_request($req); + $this->assertEquals('GET', $request->getMethod()); + $this->assertEquals('/abc', $request->getRequestTarget()); + $this->assertEquals('1.0', $request->getProtocolVersion()); + $this->assertEquals('foo.com', $request->getHeaderLine('Host')); + $this->assertEquals('Bar', $request->getHeaderLine('Foo')); + $this->assertEquals('Bam, Qux', $request->getHeaderLine('Baz')); + $this->assertEquals('Test', (string) $request->getBody()); + $this->assertEquals('http://foo.com/abc', (string) $request->getUri()); + } + + public function testParsesRequestMessagesWithHttpsScheme() + { + $req = "PUT /abc?baz=bar HTTP/1.1\r\nHost: foo.com:443\r\n\r\n"; + $request = Psr7\parse_request($req); + $this->assertEquals('PUT', $request->getMethod()); + $this->assertEquals('/abc?baz=bar', $request->getRequestTarget()); + $this->assertEquals('1.1', $request->getProtocolVersion()); + $this->assertEquals('foo.com:443', $request->getHeaderLine('Host')); + $this->assertEquals('', (string) $request->getBody()); + $this->assertEquals('https://foo.com/abc?baz=bar', (string) $request->getUri()); + } + + public function testParsesRequestMessagesWithUriWhenHostIsNotFirst() + { + $req = "PUT / HTTP/1.1\r\nFoo: Bar\r\nHost: foo.com\r\n\r\n"; + $request = Psr7\parse_request($req); + $this->assertEquals('PUT', $request->getMethod()); + $this->assertEquals('/', $request->getRequestTarget()); + $this->assertEquals('http://foo.com/', (string) $request->getUri()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesRequestMessages() + { + Psr7\parse_request("HTTP/1.1 200 OK\r\n\r\n"); + } + + public function testParsesResponseMessages() + { + $res = "HTTP/1.0 200 OK\r\nFoo: Bar\r\nBaz: Bam\r\nBaz: Qux\r\n\r\nTest"; + $response = Psr7\parse_response($res); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('OK', $response->getReasonPhrase()); + $this->assertEquals('1.0', $response->getProtocolVersion()); + $this->assertEquals('Bar', $response->getHeaderLine('Foo')); + $this->assertEquals('Bam, Qux', $response->getHeaderLine('Baz')); + $this->assertEquals('Test', (string) $response->getBody()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesResponseMessages() + { + Psr7\parse_response("GET / HTTP/1.1\r\n\r\n"); + } + + public function testDetermineMimetype() + { + $this->assertNull(Psr7\mimetype_from_extension('not-a-real-extension')); + $this->assertEquals( + 'application/json', + Psr7\mimetype_from_extension('json') + ); + $this->assertEquals( + 'image/jpeg', + Psr7\mimetype_from_filename('/tmp/images/IMG034821.JPEG') + ); + } + + public function testCreatesUriForValue() + { + $this->assertInstanceOf('GuzzleHttp\Psr7\Uri', Psr7\uri_for('/foo')); + $this->assertInstanceOf( + 'GuzzleHttp\Psr7\Uri', + Psr7\uri_for(new Psr7\Uri('/foo')) + ); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesUri() + { + Psr7\uri_for([]); + } + + public function testKeepsPositionOfResource() + { + $h = fopen(__FILE__, 'r'); + fseek($h, 10); + $stream = Psr7\stream_for($h); + $this->assertEquals(10, $stream->tell()); + $stream->close(); + } + + public function testCreatesWithFactory() + { + $stream = Psr7\stream_for('foo'); + $this->assertInstanceOf('GuzzleHttp\Psr7\Stream', $stream); + $this->assertEquals('foo', $stream->getContents()); + $stream->close(); + } + + public function testFactoryCreatesFromEmptyString() + { + $s = Psr7\stream_for(); + $this->assertInstanceOf('GuzzleHttp\Psr7\Stream', $s); + } + + public function testFactoryCreatesFromNull() + { + $s = Psr7\stream_for(null); + $this->assertInstanceOf('GuzzleHttp\Psr7\Stream', $s); + } + + public function testFactoryCreatesFromResource() + { + $r = fopen(__FILE__, 'r'); + $s = Psr7\stream_for($r); + $this->assertInstanceOf('GuzzleHttp\Psr7\Stream', $s); + $this->assertSame(file_get_contents(__FILE__), (string) $s); + } + + public function testFactoryCreatesFromObjectWithToString() + { + $r = new HasToString(); + $s = Psr7\stream_for($r); + $this->assertInstanceOf('GuzzleHttp\Psr7\Stream', $s); + $this->assertEquals('foo', (string) $s); + } + + public function testCreatePassesThrough() + { + $s = Psr7\stream_for('foo'); + $this->assertSame($s, Psr7\stream_for($s)); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testThrowsExceptionForUnknown() + { + Psr7\stream_for(new \stdClass()); + } + + public function testReturnsCustomMetadata() + { + $s = Psr7\stream_for('foo', ['metadata' => ['hwm' => 3]]); + $this->assertEquals(3, $s->getMetadata('hwm')); + $this->assertArrayHasKey('hwm', $s->getMetadata()); + } + + public function testCanSetSize() + { + $s = Psr7\stream_for('', ['size' => 10]); + $this->assertEquals(10, $s->getSize()); + } + + public function testCanCreateIteratorBasedStream() + { + $a = new \ArrayIterator(['foo', 'bar', '123']); + $p = Psr7\stream_for($a); + $this->assertInstanceOf('GuzzleHttp\Psr7\PumpStream', $p); + $this->assertEquals('foo', $p->read(3)); + $this->assertFalse($p->eof()); + $this->assertEquals('b', $p->read(1)); + $this->assertEquals('a', $p->read(1)); + $this->assertEquals('r12', $p->read(3)); + $this->assertFalse($p->eof()); + $this->assertEquals('3', $p->getContents()); + $this->assertTrue($p->eof()); + $this->assertEquals(9, $p->tell()); + } + + public function testConvertsRequestsToStrings() + { + $request = new Psr7\Request('PUT', 'http://foo.com/hi?123', [ + 'Baz' => 'bar', + 'Qux' => ' ipsum' + ], 'hello', '1.0'); + $this->assertEquals( + "PUT /hi?123 HTTP/1.0\r\nHost: foo.com\r\nBaz: bar\r\nQux: ipsum\r\n\r\nhello", + Psr7\str($request) + ); + } + + public function testConvertsResponsesToStrings() + { + $response = new Psr7\Response(200, [ + 'Baz' => 'bar', + 'Qux' => ' ipsum' + ], 'hello', '1.0', 'FOO'); + $this->assertEquals( + "HTTP/1.0 200 FOO\r\nBaz: bar\r\nQux: ipsum\r\n\r\nhello", + Psr7\str($response) + ); + } + + public function parseParamsProvider() + { + $res1 = array( + array( + '<http:/.../front.jpeg>', + 'rel' => 'front', + 'type' => 'image/jpeg', + ), + array( + '<http://.../back.jpeg>', + 'rel' => 'back', + 'type' => 'image/jpeg', + ), + ); + return array( + array( + '<http:/.../front.jpeg>; rel="front"; type="image/jpeg", <http://.../back.jpeg>; rel=back; type="image/jpeg"', + $res1 + ), + array( + '<http:/.../front.jpeg>; rel="front"; type="image/jpeg",<http://.../back.jpeg>; rel=back; type="image/jpeg"', + $res1 + ), + array( + 'foo="baz"; bar=123, boo, test="123", foobar="foo;bar"', + array( + array('foo' => 'baz', 'bar' => '123'), + array('boo'), + array('test' => '123'), + array('foobar' => 'foo;bar') + ) + ), + array( + '<http://.../side.jpeg?test=1>; rel="side"; type="image/jpeg",<http://.../side.jpeg?test=2>; rel=side; type="image/jpeg"', + array( + array('<http://.../side.jpeg?test=1>', 'rel' => 'side', 'type' => 'image/jpeg'), + array('<http://.../side.jpeg?test=2>', 'rel' => 'side', 'type' => 'image/jpeg') + ) + ), + array( + '', + array() + ) + ); + } + /** + * @dataProvider parseParamsProvider + */ + public function testParseParams($header, $result) + { + $this->assertEquals($result, Psr7\parse_header($header)); + } + + public function testParsesArrayHeaders() + { + $header = ['a, b', 'c', 'd, e']; + $this->assertEquals(['a', 'b', 'c', 'd', 'e'], Psr7\normalize_header($header)); + } + + public function testRewindsBody() + { + $body = Psr7\stream_for('abc'); + $res = new Psr7\Response(200, [], $body); + Psr7\rewind_body($res); + $this->assertEquals(0, $body->tell()); + $body->rewind(1); + Psr7\rewind_body($res); + $this->assertEquals(0, $body->tell()); + } + + /** + * @expectedException \RuntimeException + */ + public function testThrowsWhenBodyCannotBeRewound() + { + $body = Psr7\stream_for('abc'); + $body->read(1); + $body = FnStream::decorate($body, [ + 'rewind' => function () { throw new \RuntimeException('a'); } + ]); + $res = new Psr7\Response(200, [], $body); + Psr7\rewind_body($res); + } + + public function testCanModifyRequestWithUri() + { + $r1 = new Psr7\Request('GET', 'http://foo.com'); + $r2 = Psr7\modify_request($r1, [ + 'uri' => new Psr7\Uri('http://www.foo.com') + ]); + $this->assertEquals('http://www.foo.com', (string) $r2->getUri()); + $this->assertEquals('www.foo.com', (string) $r2->getHeaderLine('host')); + } + + public function testCanModifyRequestWithCaseInsensitiveHeader() + { + $r1 = new Psr7\Request('GET', 'http://foo.com', ['User-Agent' => 'foo']); + $r2 = Psr7\modify_request($r1, ['set_headers' => ['User-agent' => 'bar']]); + $this->assertEquals('bar', $r2->getHeaderLine('User-Agent')); + $this->assertEquals('bar', $r2->getHeaderLine('User-agent')); + } + + public function testReturnsAsIsWhenNoChanges() + { + $request = new Psr7\Request('GET', 'http://foo.com'); + $this->assertSame($request, Psr7\modify_request($request, [])); + } + + public function testReturnsUriAsIsWhenNoChanges() + { + $r1 = new Psr7\Request('GET', 'http://foo.com'); + $r2 = Psr7\modify_request($r1, ['set_headers' => ['foo' => 'bar']]); + $this->assertNotSame($r1, $r2); + $this->assertEquals('bar', $r2->getHeaderLine('foo')); + } + + public function testRemovesHeadersFromMessage() + { + $r1 = new Psr7\Request('GET', 'http://foo.com', ['foo' => 'bar']); + $r2 = Psr7\modify_request($r1, ['remove_headers' => ['foo']]); + $this->assertNotSame($r1, $r2); + $this->assertFalse($r2->hasHeader('foo')); + } + + public function testAddsQueryToUri() + { + $r1 = new Psr7\Request('GET', 'http://foo.com'); + $r2 = Psr7\modify_request($r1, ['query' => 'foo=bar']); + $this->assertNotSame($r1, $r2); + $this->assertEquals('foo=bar', $r2->getUri()->getQuery()); + } +} diff --git a/core/vendor/guzzlehttp/streams/tests/InflateStreamTest.php b/core/vendor/guzzlehttp/psr7/tests/InflateStreamTest.php similarity index 63% rename from core/vendor/guzzlehttp/streams/tests/InflateStreamTest.php rename to core/vendor/guzzlehttp/psr7/tests/InflateStreamTest.php index ead9356a53f27f68cce9a6123a3a6d8dba702782..927fc0b7bfded354d6b6aba3cf00144c585d3dad 100644 --- a/core/vendor/guzzlehttp/streams/tests/InflateStreamTest.php +++ b/core/vendor/guzzlehttp/psr7/tests/InflateStreamTest.php @@ -1,15 +1,15 @@ <?php -namespace GuzzleHttp\Tests\Stream; +namespace GuzzleHttp\Tests\Psr7; -use GuzzleHttp\Stream\InflateStream; -use GuzzleHttp\Stream\Stream; +use GuzzleHttp\Psr7; +use GuzzleHttp\Psr7\InflateStream; class InflateStreamtest extends \PHPUnit_Framework_TestCase { public function testInflatesStreams() { $content = gzencode('test'); - $a = Stream::factory($content); + $a = Psr7\stream_for($content); $b = new InflateStream($a); $this->assertEquals('test', (string) $b); } diff --git a/core/vendor/guzzlehttp/streams/tests/LazyOpenStreamTest.php b/core/vendor/guzzlehttp/psr7/tests/LazyOpenStreamTest.php similarity index 96% rename from core/vendor/guzzlehttp/streams/tests/LazyOpenStreamTest.php rename to core/vendor/guzzlehttp/psr7/tests/LazyOpenStreamTest.php index 79e0078e888065f9ee865679500be22413c9bbb9..fdef1423da0c51ff440ca854c34c41a3d738bc41 100644 --- a/core/vendor/guzzlehttp/streams/tests/LazyOpenStreamTest.php +++ b/core/vendor/guzzlehttp/psr7/tests/LazyOpenStreamTest.php @@ -1,7 +1,7 @@ <?php -namespace GuzzleHttp\Tests\Stream; +namespace GuzzleHttp\Tests\Psr7; -use GuzzleHttp\Stream\LazyOpenStream; +use GuzzleHttp\Psr7\LazyOpenStream; class LazyOpenStreamTest extends \PHPUnit_Framework_TestCase { diff --git a/core/vendor/guzzlehttp/streams/tests/LimitStreamTest.php b/core/vendor/guzzlehttp/psr7/tests/LimitStreamTest.php similarity index 61% rename from core/vendor/guzzlehttp/streams/tests/LimitStreamTest.php rename to core/vendor/guzzlehttp/psr7/tests/LimitStreamTest.php index efb1dc58ff362332c08a00f27c21faf59a8041c0..2198b7a9b3c5b21677b17d84ec52d69d67f55f22 100644 --- a/core/vendor/guzzlehttp/streams/tests/LimitStreamTest.php +++ b/core/vendor/guzzlehttp/psr7/tests/LimitStreamTest.php @@ -1,13 +1,14 @@ <?php -namespace GuzzleHttp\Tests\Http; +namespace GuzzleHttp\Tests\Psr7; -use GuzzleHttp\Stream\FnStream; -use GuzzleHttp\Stream\Stream; -use GuzzleHttp\Stream\LimitStream; -use GuzzleHttp\Stream\NoSeekStream; +use GuzzleHttp\Psr7; +use GuzzleHttp\Psr7\FnStream; +use GuzzleHttp\Psr7\Stream; +use GuzzleHttp\Psr7\LimitStream; +use GuzzleHttp\Psr7\NoSeekStream; /** - * @covers GuzzleHttp\Stream\LimitStream + * @covers GuzzleHttp\Psr7\LimitStream */ class LimitStreamTest extends \PHPUnit_Framework_TestCase { @@ -19,35 +20,52 @@ class LimitStreamTest extends \PHPUnit_Framework_TestCase public function setUp() { - $this->decorated = Stream::factory(fopen(__FILE__, 'r')); + $this->decorated = Psr7\stream_for(fopen(__FILE__, 'r')); $this->body = new LimitStream($this->decorated, 10, 3); } public function testReturnsSubset() { - $body = new LimitStream(Stream::factory('foo'), -1, 1); + $body = new LimitStream(Psr7\stream_for('foo'), -1, 1); $this->assertEquals('oo', (string) $body); $this->assertTrue($body->eof()); $body->seek(0); $this->assertFalse($body->eof()); $this->assertEquals('oo', $body->read(100)); + $this->assertSame('', $body->read(1)); $this->assertTrue($body->eof()); } public function testReturnsSubsetWhenCastToString() { - $body = Stream::factory('foo_baz_bar'); + $body = Psr7\stream_for('foo_baz_bar'); $limited = new LimitStream($body, 3, 4); $this->assertEquals('baz', (string) $limited); } + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Unable to seek to stream position 10 with whence 0 + */ + public function testEnsuresPositionCanBeekSeekedTo() + { + new LimitStream(Psr7\stream_for(''), 0, 10); + } + public function testReturnsSubsetOfEmptyBodyWhenCastToString() { - $body = Stream::factory(''); + $body = Psr7\stream_for('01234567891234'); $limited = new LimitStream($body, 0, 10); $this->assertEquals('', (string) $limited); } + public function testReturnsSpecificSubsetOBodyWhenCastToString() + { + $body = Psr7\stream_for('0123456789abcdef'); + $limited = new LimitStream($body, 3, 10); + $this->assertEquals('abc', (string) $limited); + } + public function testSeeksWhenConstructed() { $this->assertEquals(0, $this->body->tell()); @@ -56,26 +74,33 @@ public function testSeeksWhenConstructed() public function testAllowsBoundedSeek() { - $this->assertEquals(true, $this->body->seek(100)); + $this->body->seek(100); $this->assertEquals(10, $this->body->tell()); $this->assertEquals(13, $this->decorated->tell()); - $this->assertEquals(true, $this->body->seek(0)); + $this->body->seek(0); $this->assertEquals(0, $this->body->tell()); $this->assertEquals(3, $this->decorated->tell()); - $this->assertEquals(false, $this->body->seek(-10)); + try { + $this->body->seek(-10); + $this->fail(); + } catch (\RuntimeException $e) {} $this->assertEquals(0, $this->body->tell()); $this->assertEquals(3, $this->decorated->tell()); - $this->assertEquals(true, $this->body->seek(5)); + $this->body->seek(5); $this->assertEquals(5, $this->body->tell()); $this->assertEquals(8, $this->decorated->tell()); - $this->assertEquals(false, $this->body->seek(1000, SEEK_END)); + // Fail + try { + $this->body->seek(1000, SEEK_END); + $this->fail(); + } catch (\RuntimeException $e) {} } public function testReadsOnlySubsetOfData() { $data = $this->body->read(100); $this->assertEquals(10, strlen($data)); - $this->assertFalse($this->body->read(1000)); + $this->assertSame('', $this->body->read(1000)); $this->body->setOffset(10); $newData = $this->body->read(100); @@ -84,18 +109,26 @@ public function testReadsOnlySubsetOfData() } /** - * @expectedException \GuzzleHttp\Stream\Exception\SeekException - * @expectedExceptionMessage Could not seek the stream to position 2 + * @expectedException \RuntimeException + * @expectedExceptionMessage Could not seek to stream offset 2 */ public function testThrowsWhenCurrentGreaterThanOffsetSeek() { - $a = Stream::factory('foo_bar'); + $a = Psr7\stream_for('foo_bar'); $b = new NoSeekStream($a); $c = new LimitStream($b); $a->getContents(); $c->setOffset(2); } + public function testCanGetContentsWithoutSeeking() + { + $a = Psr7\stream_for('foo_bar'); + $b = new NoSeekStream($a); + $c = new LimitStream($b); + $this->assertEquals('foo_bar', $c->getContents()); + } + public function testClaimsConsumedWhenReadLimitIsReached() { $this->assertFalse($this->body->eof()); @@ -110,7 +143,7 @@ public function testContentLengthIsBounded() public function testGetContentsIsBasedOnSubset() { - $body = new LimitStream(Stream::factory('foobazbar'), 3, 3); + $body = new LimitStream(Psr7\stream_for('foobazbar'), 3, 3); $this->assertEquals('baz', $body->getContents()); } @@ -126,7 +159,7 @@ public function testReturnsNullIfSizeCannotBeDetermined() public function testLengthLessOffsetWhenNoLimitSize() { - $a = Stream::factory('foo_bar'); + $a = Psr7\stream_for('foo_bar'); $b = new LimitStream($a, -1, 4); $this->assertEquals(3, $b->getSize()); } diff --git a/core/vendor/guzzlehttp/psr7/tests/MultipartStreamTest.php b/core/vendor/guzzlehttp/psr7/tests/MultipartStreamTest.php new file mode 100644 index 0000000000000000000000000000000000000000..61edb067a00c99cb901b65c86c1799102f74e0ec --- /dev/null +++ b/core/vendor/guzzlehttp/psr7/tests/MultipartStreamTest.php @@ -0,0 +1,214 @@ +<?php +namespace GuzzleHttp\Tests; + +use GuzzleHttp\Psr7; +use GuzzleHttp\Psr7\MultipartStream; + +class MultipartStreamTest extends \PHPUnit_Framework_TestCase +{ + public function testCreatesDefaultBoundary() + { + $b = new MultipartStream(); + $this->assertNotEmpty($b->getBoundary()); + } + + public function testCanProvideBoundary() + { + $b = new MultipartStream([], 'foo'); + $this->assertEquals('foo', $b->getBoundary()); + } + + public function testIsNotWritable() + { + $b = new MultipartStream(); + $this->assertFalse($b->isWritable()); + } + + public function testCanCreateEmptyStream() + { + $b = new MultipartStream(); + $boundary = $b->getBoundary(); + $this->assertSame("--{$boundary}--\r\n", $b->getContents()); + $this->assertSame(strlen($boundary) + 6, $b->getSize()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesFilesArrayElement() + { + new MultipartStream([['foo' => 'bar']]); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testEnsuresFileHasName() + { + new MultipartStream([['contents' => 'bar']]); + } + + public function testSerializesFields() + { + $b = new MultipartStream([ + [ + 'name' => 'foo', + 'contents' => 'bar' + ], + [ + 'name' => 'baz', + 'contents' => 'bam' + ] + ], 'boundary'); + $this->assertEquals( + "--boundary\r\nContent-Disposition: form-data; name=\"foo\"\r\nContent-Length: 3\r\n\r\n" + . "bar\r\n--boundary\r\nContent-Disposition: form-data; name=\"baz\"\r\nContent-Length: 3" + . "\r\n\r\nbam\r\n--boundary--\r\n", (string) $b); + } + + public function testSerializesFiles() + { + $f1 = Psr7\FnStream::decorate(Psr7\stream_for('foo'), [ + 'getMetadata' => function () { + return '/foo/bar.txt'; + } + ]); + + $f2 = Psr7\FnStream::decorate(Psr7\stream_for('baz'), [ + 'getMetadata' => function () { + return '/foo/baz.jpg'; + } + ]); + + $f3 = Psr7\FnStream::decorate(Psr7\stream_for('bar'), [ + 'getMetadata' => function () { + return '/foo/bar.gif'; + } + ]); + + $b = new MultipartStream([ + [ + 'name' => 'foo', + 'contents' => $f1 + ], + [ + 'name' => 'qux', + 'contents' => $f2 + ], + [ + 'name' => 'qux', + 'contents' => $f3 + ], + ], 'boundary'); + + $expected = <<<EOT +--boundary +Content-Disposition: form-data; name="foo"; filename="bar.txt" +Content-Length: 3 +Content-Type: text/plain + +foo +--boundary +Content-Disposition: form-data; name="qux"; filename="baz.jpg" +Content-Length: 3 +Content-Type: image/jpeg + +baz +--boundary +Content-Disposition: form-data; name="qux"; filename="bar.gif" +Content-Length: 3 +Content-Type: image/gif + +bar +--boundary-- + +EOT; + + $this->assertEquals($expected, str_replace("\r", '', $b)); + } + + public function testSerializesFilesWithCustomHeaders() + { + $f1 = Psr7\FnStream::decorate(Psr7\stream_for('foo'), [ + 'getMetadata' => function () { + return '/foo/bar.txt'; + } + ]); + + $b = new MultipartStream([ + [ + 'name' => 'foo', + 'contents' => $f1, + 'headers' => [ + 'x-foo' => 'bar', + 'content-disposition' => 'custom' + ] + ] + ], 'boundary'); + + $expected = <<<EOT +--boundary +x-foo: bar +content-disposition: custom +Content-Length: 3 +Content-Type: text/plain + +foo +--boundary-- + +EOT; + + $this->assertEquals($expected, str_replace("\r", '', $b)); + } + + public function testSerializesFilesWithCustomHeadersAndMultipleValues() + { + $f1 = Psr7\FnStream::decorate(Psr7\stream_for('foo'), [ + 'getMetadata' => function () { + return '/foo/bar.txt'; + } + ]); + + $f2 = Psr7\FnStream::decorate(Psr7\stream_for('baz'), [ + 'getMetadata' => function () { + return '/foo/baz.jpg'; + } + ]); + + $b = new MultipartStream([ + [ + 'name' => 'foo', + 'contents' => $f1, + 'headers' => [ + 'x-foo' => 'bar', + 'content-disposition' => 'custom' + ] + ], + [ + 'name' => 'foo', + 'contents' => $f2, + 'headers' => ['cOntenT-Type' => 'custom'], + ] + ], 'boundary'); + + $expected = <<<EOT +--boundary +x-foo: bar +content-disposition: custom +Content-Length: 3 +Content-Type: text/plain + +foo +--boundary +cOntenT-Type: custom +Content-Disposition: form-data; name="foo"; filename="baz.jpg" +Content-Length: 3 + +baz +--boundary-- + +EOT; + + $this->assertEquals($expected, str_replace("\r", '', $b)); + } +} diff --git a/core/vendor/guzzlehttp/psr7/tests/NoSeekStreamTest.php b/core/vendor/guzzlehttp/psr7/tests/NoSeekStreamTest.php new file mode 100644 index 0000000000000000000000000000000000000000..a309317bdf0746b458a31dd9ecebdee3db07e255 --- /dev/null +++ b/core/vendor/guzzlehttp/psr7/tests/NoSeekStreamTest.php @@ -0,0 +1,40 @@ +<?php +namespace GuzzleHttp\Tests\Psr7; + +use GuzzleHttp\Psr7; +use GuzzleHttp\Psr7\NoSeekStream; + +/** + * @covers GuzzleHttp\Psr7\NoSeekStream + * @covers GuzzleHttp\Psr7\StreamDecoratorTrait + */ +class NoSeekStreamTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Cannot seek a NoSeekStream + */ + public function testCannotSeek() + { + $s = $this->getMockBuilder('Psr\Http\Message\StreamInterface') + ->setMethods(['isSeekable', 'seek']) + ->getMockForAbstractClass(); + $s->expects($this->never())->method('seek'); + $s->expects($this->never())->method('isSeekable'); + $wrapped = new NoSeekStream($s); + $this->assertFalse($wrapped->isSeekable()); + $wrapped->seek(2); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Cannot write to a non-writable stream + */ + public function testHandlesClose() + { + $s = Psr7\stream_for('foo'); + $wrapped = new NoSeekStream($s); + $wrapped->close(); + $wrapped->write('foo'); + } +} diff --git a/core/vendor/guzzlehttp/streams/tests/PumpStreamTest.php b/core/vendor/guzzlehttp/psr7/tests/PumpStreamTest.php similarity index 73% rename from core/vendor/guzzlehttp/streams/tests/PumpStreamTest.php rename to core/vendor/guzzlehttp/psr7/tests/PumpStreamTest.php index 2d20ce90c0d7674d946b0a2b3c6c871e2d3019a5..7358bb6e0b6bba5f44882a1d8bdcef47dd7f4c5b 100644 --- a/core/vendor/guzzlehttp/streams/tests/PumpStreamTest.php +++ b/core/vendor/guzzlehttp/psr7/tests/PumpStreamTest.php @@ -1,9 +1,9 @@ <?php -namespace GuzzleHttp\Tests\Stream; +namespace GuzzleHttp\Tests\Psr7; -use GuzzleHttp\Stream\LimitStream; -use GuzzleHttp\Stream\PumpStream; -use GuzzleHttp\Stream\Stream; +use GuzzleHttp\Psr7\LimitStream; +use GuzzleHttp\Psr7\PumpStream; +use GuzzleHttp\Psr7; class PumpStreamTest extends \PHPUnit_Framework_TestCase { @@ -21,7 +21,7 @@ public function testHasMetadataAndSize() public function testCanReadFromCallable() { - $p = Stream::factory(function ($size) { + $p = Psr7\stream_for(function ($size) { return 'a'; }); $this->assertEquals('a', $p->read(1)); @@ -33,7 +33,7 @@ public function testCanReadFromCallable() public function testStoresExcessDataInBuffer() { $called = []; - $p = Stream::factory(function ($size) use (&$called) { + $p = Psr7\stream_for(function ($size) use (&$called) { $called[] = $size; return 'abcdef'; }); @@ -46,32 +46,27 @@ public function testStoresExcessDataInBuffer() public function testInifiniteStreamWrappedInLimitStream() { - $p = Stream::factory(function () { return 'a'; }); + $p = Psr7\stream_for(function () { return 'a'; }); $s = new LimitStream($p, 5); $this->assertEquals('aaaaa', (string) $s); } public function testDescribesCapabilities() { - $p = Stream::factory(function () {}); + $p = Psr7\stream_for(function () {}); $this->assertTrue($p->isReadable()); $this->assertFalse($p->isSeekable()); $this->assertFalse($p->isWritable()); $this->assertNull($p->getSize()); - $this->assertFalse($p->write('aa')); $this->assertEquals('', $p->getContents()); $this->assertEquals('', (string) $p); $p->close(); $this->assertEquals('', $p->read(10)); $this->assertTrue($p->eof()); - } - /** - * @expectedException \GuzzleHttp\Stream\Exception\CannotAttachException - */ - public function testCannotAttach() - { - $p = Stream::factory(function () {}); - $p->attach('a'); + try { + $this->assertFalse($p->write('aa')); + $this->fail(); + } catch (\RuntimeException $e) {} } } diff --git a/core/vendor/guzzlehttp/psr7/tests/RequestTest.php b/core/vendor/guzzlehttp/psr7/tests/RequestTest.php new file mode 100644 index 0000000000000000000000000000000000000000..9defe68d9e8af32a7649c66c09a77dabef7cb8a2 --- /dev/null +++ b/core/vendor/guzzlehttp/psr7/tests/RequestTest.php @@ -0,0 +1,157 @@ +<?php +namespace GuzzleHttp\Tests\Psr7; + +use GuzzleHttp\Psr7\Request; +use GuzzleHttp\Psr7\Uri; + +/** + * @covers GuzzleHttp\Psr7\Request + */ +class RequestTest extends \PHPUnit_Framework_TestCase +{ + public function testRequestUriMayBeString() + { + $r = new Request('GET', '/'); + $this->assertEquals('/', (string) $r->getUri()); + } + + public function testRequestUriMayBeUri() + { + $uri = new Uri('/'); + $r = new Request('GET', $uri); + $this->assertSame($uri, $r->getUri()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidateRequestUri() + { + new Request('GET', true); + } + + public function testCanConstructWithBody() + { + $r = new Request('GET', '/', [], 'baz'); + $this->assertEquals('baz', (string) $r->getBody()); + } + + public function testCapitalizesMethod() + { + $r = new Request('get', '/'); + $this->assertEquals('GET', $r->getMethod()); + } + + public function testCapitalizesWithMethod() + { + $r = new Request('GET', '/'); + $this->assertEquals('PUT', $r->withMethod('put')->getMethod()); + } + + public function testWithUri() + { + $r1 = new Request('GET', '/'); + $u1 = $r1->getUri(); + $u2 = new Uri('http://www.example.com'); + $r2 = $r1->withUri($u2); + $this->assertNotSame($r1, $r2); + $this->assertSame($u2, $r2->getUri()); + $this->assertSame($u1, $r1->getUri()); + } + + public function testSameInstanceWhenSameUri() + { + $r1 = new Request('GET', 'http://foo.com'); + $r2 = $r1->withUri($r1->getUri()); + $this->assertSame($r1, $r2); + } + + public function testWithRequestTarget() + { + $r1 = new Request('GET', '/'); + $r2 = $r1->withRequestTarget('*'); + $this->assertEquals('*', $r2->getRequestTarget()); + $this->assertEquals('/', $r1->getRequestTarget()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testRequestTargetDoesNotAllowSpaces() + { + $r1 = new Request('GET', '/'); + $r1->withRequestTarget('/foo bar'); + } + + public function testRequestTargetDefaultsToSlash() + { + $r1 = new Request('GET', ''); + $this->assertEquals('/', $r1->getRequestTarget()); + $r2 = new Request('GET', '*'); + $this->assertEquals('*', $r2->getRequestTarget()); + $r3 = new Request('GET', 'http://foo.com/bar baz/'); + $this->assertEquals('/bar%20baz/', $r3->getRequestTarget()); + } + + public function testBuildsRequestTarget() + { + $r1 = new Request('GET', 'http://foo.com/baz?bar=bam'); + $this->assertEquals('/baz?bar=bam', $r1->getRequestTarget()); + } + + public function testHostIsAddedFirst() + { + $r = new Request('GET', 'http://foo.com/baz?bar=bam', ['Foo' => 'Bar']); + $this->assertEquals([ + 'Host' => ['foo.com'], + 'Foo' => ['Bar'] + ], $r->getHeaders()); + } + + public function testCanGetHeaderAsCsv() + { + $r = new Request('GET', 'http://foo.com/baz?bar=bam', [ + 'Foo' => ['a', 'b', 'c'] + ]); + $this->assertEquals('a, b, c', $r->getHeaderLine('Foo')); + $this->assertEquals('', $r->getHeaderLine('Bar')); + } + + public function testHostIsNotOverwrittenWhenPreservingHost() + { + $r = new Request('GET', 'http://foo.com/baz?bar=bam', ['Host' => 'a.com']); + $this->assertEquals(['Host' => ['a.com']], $r->getHeaders()); + $r2 = $r->withUri(new Uri('http://www.foo.com/bar'), true); + $this->assertEquals('a.com', $r2->getHeaderLine('Host')); + } + + public function testOverridesHostWithUri() + { + $r = new Request('GET', 'http://foo.com/baz?bar=bam'); + $this->assertEquals(['Host' => ['foo.com']], $r->getHeaders()); + $r2 = $r->withUri(new Uri('http://www.baz.com/bar')); + $this->assertEquals('www.baz.com', $r2->getHeaderLine('Host')); + } + + public function testAggregatesHeaders() + { + $r = new Request('GET', 'http://foo.com', [ + 'ZOO' => 'zoobar', + 'zoo' => ['foobar', 'zoobar'] + ]); + $this->assertEquals('zoobar, foobar, zoobar', $r->getHeaderLine('zoo')); + } + + public function testAddsPortToHeader() + { + $r = new Request('GET', 'http://foo.com:8124/bar'); + $this->assertEquals('foo.com:8124', $r->getHeaderLine('host')); + } + + public function testAddsPortToHeaderAndReplacePreviousPort() + { + $r = new Request('GET', 'http://foo.com:8124/bar'); + $r = $r->withUri(new Uri('http://foo.com:8125/bar')); + $this->assertEquals('foo.com:8125', $r->getHeaderLine('host')); + } +} diff --git a/core/vendor/guzzlehttp/psr7/tests/ResponseTest.php b/core/vendor/guzzlehttp/psr7/tests/ResponseTest.php new file mode 100644 index 0000000000000000000000000000000000000000..e8702924b1f4234e262065d96a24ce0ef7a73cf5 --- /dev/null +++ b/core/vendor/guzzlehttp/psr7/tests/ResponseTest.php @@ -0,0 +1,139 @@ +<?php +namespace GuzzleHttp\Tests\Psr7; + +use GuzzleHttp\Psr7\Response; +use GuzzleHttp\Psr7; + +/** + * @covers GuzzleHttp\Psr7\MessageTrait + * @covers GuzzleHttp\Psr7\Response + */ +class ResponseTest extends \PHPUnit_Framework_TestCase +{ + public function testAddsDefaultReason() + { + $r = new Response('200'); + $this->assertSame(200, $r->getStatusCode()); + $this->assertEquals('OK', $r->getReasonPhrase()); + } + + public function testCanGiveCustomReason() + { + $r = new Response(200, [], null, '1.1', 'bar'); + $this->assertEquals('bar', $r->getReasonPhrase()); + } + + public function testCanGiveCustomProtocolVersion() + { + $r = new Response(200, [], null, '1000'); + $this->assertEquals('1000', $r->getProtocolVersion()); + } + + public function testCanCreateNewResponseWithStatusAndNoReason() + { + $r = new Response(200); + $r2 = $r->withStatus(201); + $this->assertEquals(200, $r->getStatusCode()); + $this->assertEquals('OK', $r->getReasonPhrase()); + $this->assertEquals(201, $r2->getStatusCode()); + $this->assertEquals('Created', $r2->getReasonPhrase()); + } + + public function testCanCreateNewResponseWithStatusAndReason() + { + $r = new Response(200); + $r2 = $r->withStatus(201, 'Foo'); + $this->assertEquals(200, $r->getStatusCode()); + $this->assertEquals('OK', $r->getReasonPhrase()); + $this->assertEquals(201, $r2->getStatusCode()); + $this->assertEquals('Foo', $r2->getReasonPhrase()); + } + + public function testCreatesResponseWithAddedHeaderArray() + { + $r = new Response(); + $r2 = $r->withAddedHeader('foo', ['baz', 'bar']); + $this->assertFalse($r->hasHeader('foo')); + $this->assertEquals('baz, bar', $r2->getHeaderLine('foo')); + } + + public function testReturnsIdentityWhenRemovingMissingHeader() + { + $r = new Response(); + $this->assertSame($r, $r->withoutHeader('foo')); + } + + public function testAlwaysReturnsBody() + { + $r = new Response(); + $this->assertInstanceOf('Psr\Http\Message\StreamInterface', $r->getBody()); + } + + public function testCanSetHeaderAsArray() + { + $r = new Response(200, [ + 'foo' => ['baz ', ' bar '] + ]); + $this->assertEquals('baz, bar', $r->getHeaderLine('foo')); + $this->assertEquals(['baz', 'bar'], $r->getHeader('foo')); + } + + public function testSameInstanceWhenSameBody() + { + $r = new Response(200, [], 'foo'); + $b = $r->getBody(); + $this->assertSame($r, $r->withBody($b)); + } + + public function testNewInstanceWhenNewBody() + { + $r = new Response(200, [], 'foo'); + $b2 = Psr7\stream_for('abc'); + $this->assertNotSame($r, $r->withBody($b2)); + } + + public function testSameInstanceWhenSameProtocol() + { + $r = new Response(200); + $this->assertSame($r, $r->withProtocolVersion('1.1')); + } + + public function testNewInstanceWhenNewProtocol() + { + $r = new Response(200); + $this->assertNotSame($r, $r->withProtocolVersion('1.0')); + } + + public function testNewInstanceWhenRemovingHeader() + { + $r = new Response(200, ['Foo' => 'Bar']); + $r2 = $r->withoutHeader('Foo'); + $this->assertNotSame($r, $r2); + $this->assertFalse($r2->hasHeader('foo')); + } + + public function testNewInstanceWhenAddingHeader() + { + $r = new Response(200, ['Foo' => 'Bar']); + $r2 = $r->withAddedHeader('Foo', 'Baz'); + $this->assertNotSame($r, $r2); + $this->assertEquals('Bar, Baz', $r2->getHeaderLine('foo')); + } + + public function testNewInstanceWhenAddingHeaderThatWasNotThereBefore() + { + $r = new Response(200, ['Foo' => 'Bar']); + $r2 = $r->withAddedHeader('Baz', 'Bam'); + $this->assertNotSame($r, $r2); + $this->assertEquals('Bam', $r2->getHeaderLine('Baz')); + $this->assertEquals('Bar', $r2->getHeaderLine('Foo')); + } + + public function testRemovesPreviouslyAddedHeaderOfDifferentCase() + { + $r = new Response(200, ['Foo' => 'Bar']); + $r2 = $r->withHeader('foo', 'Bam'); + $this->assertNotSame($r, $r2); + $this->assertEquals('Bam', $r2->getHeaderLine('Foo')); + } +} diff --git a/core/vendor/guzzlehttp/streams/tests/StreamDecoratorTraitTest.php b/core/vendor/guzzlehttp/psr7/tests/StreamDecoratorTraitTest.php similarity index 80% rename from core/vendor/guzzlehttp/streams/tests/StreamDecoratorTraitTest.php rename to core/vendor/guzzlehttp/psr7/tests/StreamDecoratorTraitTest.php index 2ba79addfad62c336531b256631dd98c95957085..682079e0cc2fafbee5b5a58dff284c32abbaf19e 100644 --- a/core/vendor/guzzlehttp/streams/tests/StreamDecoratorTraitTest.php +++ b/core/vendor/guzzlehttp/psr7/tests/StreamDecoratorTraitTest.php @@ -1,9 +1,9 @@ <?php -namespace GuzzleHttp\Tests\Stream; +namespace GuzzleHttp\Tests\Psr7; -use GuzzleHttp\Stream\StreamInterface; -use GuzzleHttp\Stream\Stream; -use GuzzleHttp\Stream\StreamDecoratorTrait; +use Psr\Http\Message\StreamInterface; +use GuzzleHttp\Psr7; +use GuzzleHttp\Psr7\StreamDecoratorTrait; class Str implements StreamInterface { @@ -11,7 +11,7 @@ class Str implements StreamInterface } /** - * @covers GuzzleHttp\Stream\StreamDecoratorTrait + * @covers GuzzleHttp\Psr7\StreamDecoratorTrait */ class StreamDecoratorTraitTest extends \PHPUnit_Framework_TestCase { @@ -24,13 +24,13 @@ public function setUp() $this->c = fopen('php://temp', 'r+'); fwrite($this->c, 'foo'); fseek($this->c, 0); - $this->a = Stream::factory($this->c); + $this->a = Psr7\stream_for($this->c); $this->b = new Str($this->a); } public function testCatchesExceptionsWhenCastingToString() { - $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + $s = $this->getMockBuilder('Psr\Http\Message\StreamInterface') ->setMethods(['read']) ->getMockForAbstractClass(); $s->expects($this->once()) @@ -51,8 +51,6 @@ public function testToString() public function testHasSize() { $this->assertEquals(3, $this->b->getSize()); - $this->assertSame($this->b, $this->b->setSize(2)); - $this->assertEquals(2, $this->b->getSize()); } public function testReads() @@ -69,13 +67,13 @@ public function testCheckMethods() public function testSeeksAndTells() { - $this->assertTrue($this->b->seek(1)); + $this->b->seek(1); $this->assertEquals(1, $this->a->tell()); $this->assertEquals(1, $this->b->tell()); - $this->assertTrue($this->b->seek(0)); + $this->b->seek(0); $this->assertEquals(0, $this->a->tell()); $this->assertEquals(0, $this->b->tell()); - $this->assertTrue($this->b->seek(0, SEEK_END)); + $this->b->seek(0, SEEK_END); $this->assertEquals(3, $this->a->tell()); $this->assertEquals(3, $this->b->tell()); } @@ -100,14 +98,6 @@ public function testDetaches() $this->assertFalse($this->b->isReadable()); } - /** - * @expectedException \GuzzleHttp\Stream\Exception\CannotAttachException - */ - public function testCannotAttachByDefault() - { - $this->b->attach('a'); - } - public function testWrapsMetadata() { $this->assertSame($this->b->getMetadata(), $this->a->getMetadata()); diff --git a/core/vendor/guzzlehttp/streams/tests/StreamTest.php b/core/vendor/guzzlehttp/psr7/tests/StreamTest.php similarity index 53% rename from core/vendor/guzzlehttp/streams/tests/StreamTest.php rename to core/vendor/guzzlehttp/psr7/tests/StreamTest.php index 2985bfbb17b27bf3bebbab59f22e3eb46a4614bd..4fe92cc6b5ca18ca1caa0924e0ef9271aad623b0 100644 --- a/core/vendor/guzzlehttp/streams/tests/StreamTest.php +++ b/core/vendor/guzzlehttp/psr7/tests/StreamTest.php @@ -1,10 +1,11 @@ <?php -namespace GuzzleHttp\Tests\Stream; +namespace GuzzleHttp\Tests\Psr7; -use GuzzleHttp\Stream\Stream; +use GuzzleHttp\Psr7\NoSeekStream; +use GuzzleHttp\Psr7\Stream; /** - * @covers GuzzleHttp\Stream\Stream + * @covers GuzzleHttp\Psr7\Stream */ class StreamTest extends \PHPUnit_Framework_TestCase { @@ -71,16 +72,6 @@ public function testChecksEof() $stream->close(); } - public function testAllowsSettingManualSize() - { - $handle = fopen('php://temp', 'w+'); - fwrite($handle, 'data'); - $stream = new Stream($handle); - $stream->setSize(10); - $this->assertEquals(10, $stream->getSize()); - $stream->close(); - } - public function testGetSize() { $size = filesize(__FILE__); @@ -117,43 +108,34 @@ public function testProvidesStreamPosition() $stream->close(); } - public function testKeepsPositionOfResource() - { - $h = fopen(__FILE__, 'r'); - fseek($h, 10); - $stream = Stream::factory($h); - $this->assertEquals(10, $stream->tell()); - $stream->close(); - } - - public function testCanDetachAndAttachStream() + public function testCanDetachStream() { $r = fopen('php://temp', 'w+'); $stream = new Stream($r); $stream->write('foo'); $this->assertTrue($stream->isReadable()); $this->assertSame($r, $stream->detach()); - $this->assertNull($stream->detach()); + $stream->detach(); $this->assertFalse($stream->isReadable()); - $this->assertFalse($stream->read(10)); $this->assertFalse($stream->isWritable()); - $this->assertFalse($stream->write('bar')); $this->assertFalse($stream->isSeekable()); - $this->assertFalse($stream->seek(10)); - $this->assertFalse($stream->tell()); - $this->assertTrue($stream->eof()); - $this->assertNull($stream->getSize()); - $this->assertSame('', (string) $stream); - $this->assertSame('', $stream->getContents()); - - $stream->attach($r); - $stream->seek(0); - $this->assertEquals('foo', $stream->getContents()); - $this->assertTrue($stream->isReadable()); - $this->assertTrue($stream->isWritable()); - $this->assertTrue($stream->isSeekable()); + $throws = function (callable $fn) use ($stream) { + try { + $fn($stream); + $this->fail(); + } catch (\Exception $e) {} + }; + + $throws(function ($stream) { $stream->read(10); }); + $throws(function ($stream) { $stream->write('bar'); }); + $throws(function ($stream) { $stream->seek(10); }); + $throws(function ($stream) { $stream->tell(); }); + $throws(function ($stream) { $stream->eof(); }); + $throws(function ($stream) { $stream->getSize(); }); + $throws(function ($stream) { $stream->getContents(); }); + $this->assertSame('', (string) $stream); $stream->close(); } @@ -163,90 +145,17 @@ public function testCloseClearProperties() $stream = new Stream($handle); $stream->close(); - $this->assertEmpty($stream->getMetadata()); $this->assertFalse($stream->isSeekable()); $this->assertFalse($stream->isReadable()); $this->assertFalse($stream->isWritable()); $this->assertNull($stream->getSize()); + $this->assertEmpty($stream->getMetadata()); } - public function testCreatesWithFactory() - { - $stream = Stream::factory('foo'); - $this->assertInstanceOf('GuzzleHttp\Stream\Stream', $stream); - $this->assertEquals('foo', $stream->getContents()); - $stream->close(); - } - - public function testFactoryCreatesFromEmptyString() - { - $s = Stream::factory(); - $this->assertInstanceOf('GuzzleHttp\Stream\Stream', $s); - } - - public function testFactoryCreatesFromResource() - { - $r = fopen(__FILE__, 'r'); - $s = Stream::factory($r); - $this->assertInstanceOf('GuzzleHttp\Stream\Stream', $s); - $this->assertSame(file_get_contents(__FILE__), (string) $s); - } - - public function testFactoryCreatesFromObjectWithToString() + public function testDoesNotThrowInToString() { - $r = new HasToString(); - $s = Stream::factory($r); - $this->assertInstanceOf('GuzzleHttp\Stream\Stream', $s); + $s = \GuzzleHttp\Psr7\stream_for('foo'); + $s = new NoSeekStream($s); $this->assertEquals('foo', (string) $s); } - - public function testCreatePassesThrough() - { - $s = Stream::factory('foo'); - $this->assertSame($s, Stream::factory($s)); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testThrowsExceptionForUnknown() - { - Stream::factory(new \stdClass()); - } - - public function testReturnsCustomMetadata() - { - $s = Stream::factory('foo', ['metadata' => ['hwm' => 3]]); - $this->assertEquals(3, $s->getMetadata('hwm')); - $this->assertArrayHasKey('hwm', $s->getMetadata()); - } - - public function testCanSetSize() - { - $s = Stream::factory('', ['size' => 10]); - $this->assertEquals(10, $s->getSize()); - } - - public function testCanCreateIteratorBasedStream() - { - $a = new \ArrayIterator(['foo', 'bar', '123']); - $p = Stream::factory($a); - $this->assertInstanceOf('GuzzleHttp\Stream\PumpStream', $p); - $this->assertEquals('foo', $p->read(3)); - $this->assertFalse($p->eof()); - $this->assertEquals('b', $p->read(1)); - $this->assertEquals('a', $p->read(1)); - $this->assertEquals('r12', $p->read(3)); - $this->assertFalse($p->eof()); - $this->assertEquals('3', $p->getContents()); - $this->assertTrue($p->eof()); - $this->assertEquals(9, $p->tell()); - } -} - -class HasToString -{ - public function __toString() { - return 'foo'; - } } diff --git a/core/vendor/guzzlehttp/streams/tests/GuzzleStreamWrapperTest.php b/core/vendor/guzzlehttp/psr7/tests/StreamWrapperTest.php similarity index 81% rename from core/vendor/guzzlehttp/streams/tests/GuzzleStreamWrapperTest.php rename to core/vendor/guzzlehttp/psr7/tests/StreamWrapperTest.php index 33c3eccb4c090a9c57e09c2bef3f8d7102bf9382..0156e5988e49bfb914fe16abc722022dbc31251d 100644 --- a/core/vendor/guzzlehttp/streams/tests/GuzzleStreamWrapperTest.php +++ b/core/vendor/guzzlehttp/psr7/tests/StreamWrapperTest.php @@ -1,23 +1,24 @@ <?php -namespace GuzzleHttp\Tests\Stream; +namespace GuzzleHttp\Tests\Psr7; -use GuzzleHttp\Stream\GuzzleStreamWrapper; -use GuzzleHttp\Stream\Stream; +use GuzzleHttp\Psr7\StreamWrapper; +use GuzzleHttp\Psr7; /** - * @covers GuzzleHttp\Stream\GuzzleStreamWrapper + * @covers GuzzleHttp\Psr7\StreamWrapper */ -class GuzzleStreamWrapperTest extends \PHPUnit_Framework_TestCase +class StreamWrapperTest extends \PHPUnit_Framework_TestCase { public function testResource() { - $stream = Stream::factory('foo'); - $handle = GuzzleStreamWrapper::getResource($stream); + $stream = Psr7\stream_for('foo'); + $handle = StreamWrapper::getResource($stream); $this->assertSame('foo', fread($handle, 3)); $this->assertSame(3, ftell($handle)); $this->assertSame(3, fwrite($handle, 'bar')); $this->assertSame(0, fseek($handle, 0)); $this->assertSame('foobar', fread($handle, 6)); + $this->assertSame('', fread($handle, 1)); $this->assertTrue(feof($handle)); // This fails on HHVM for some reason @@ -61,7 +62,7 @@ public function testResource() */ public function testValidatesStream() { - $stream = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + $stream = $this->getMockBuilder('Psr\Http\Message\StreamInterface') ->setMethods(['isReadable', 'isWritable']) ->getMockForAbstractClass(); $stream->expects($this->once()) @@ -70,7 +71,7 @@ public function testValidatesStream() $stream->expects($this->once()) ->method('isWritable') ->will($this->returnValue(false)); - GuzzleStreamWrapper::getResource($stream); + StreamWrapper::getResource($stream); } /** @@ -83,7 +84,7 @@ public function testReturnsFalseWhenStreamDoesNotExist() public function testCanOpenReadonlyStream() { - $stream = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + $stream = $this->getMockBuilder('Psr\Http\Message\StreamInterface') ->setMethods(['isReadable', 'isWritable']) ->getMockForAbstractClass(); $stream->expects($this->once()) @@ -92,7 +93,7 @@ public function testCanOpenReadonlyStream() $stream->expects($this->once()) ->method('isWritable') ->will($this->returnValue(true)); - $r = GuzzleStreamWrapper::getResource($stream); + $r = StreamWrapper::getResource($stream); $this->assertInternalType('resource', $r); fclose($r); } diff --git a/core/vendor/guzzlehttp/psr7/tests/UriTest.php b/core/vendor/guzzlehttp/psr7/tests/UriTest.php new file mode 100644 index 0000000000000000000000000000000000000000..80755da0f6aef60452cbe124322a7dbd640b7c33 --- /dev/null +++ b/core/vendor/guzzlehttp/psr7/tests/UriTest.php @@ -0,0 +1,245 @@ +<?php +namespace GuzzleHttp\Tests\Psr7; + +use GuzzleHttp\Psr7\Uri; + +/** + * @covers GuzzleHttp\Psr7\Uri + */ +class UriTest extends \PHPUnit_Framework_TestCase +{ + const RFC3986_BASE = "http://a/b/c/d;p?q"; + + public function testParsesProvidedUrl() + { + $uri = new Uri('https://michael:test@test.com:443/path/123?q=abc#test'); + + // Standard port 443 for https gets ignored. + $this->assertEquals( + 'https://michael:test@test.com/path/123?q=abc#test', + (string) $uri + ); + + $this->assertEquals('test', $uri->getFragment()); + $this->assertEquals('test.com', $uri->getHost()); + $this->assertEquals('/path/123', $uri->getPath()); + $this->assertEquals(null, $uri->getPort()); + $this->assertEquals('q=abc', $uri->getQuery()); + $this->assertEquals('https', $uri->getScheme()); + $this->assertEquals('michael:test', $uri->getUserInfo()); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Unable to parse URI + */ + public function testValidatesUriCanBeParsed() + { + new Uri('///'); + } + + public function testCanTransformAndRetrievePartsIndividually() + { + $uri = (new Uri('')) + ->withFragment('#test') + ->withHost('example.com') + ->withPath('path/123') + ->withPort(8080) + ->withQuery('?q=abc') + ->withScheme('http') + ->withUserInfo('user', 'pass'); + + // Test getters. + $this->assertEquals('user:pass@example.com:8080', $uri->getAuthority()); + $this->assertEquals('test', $uri->getFragment()); + $this->assertEquals('example.com', $uri->getHost()); + $this->assertEquals('path/123', $uri->getPath()); + $this->assertEquals(8080, $uri->getPort()); + $this->assertEquals('q=abc', $uri->getQuery()); + $this->assertEquals('http', $uri->getScheme()); + $this->assertEquals('user:pass', $uri->getUserInfo()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testPortMustBeValid() + { + (new Uri(''))->withPort(100000); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testPathMustBeValid() + { + (new Uri(''))->withPath([]); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testQueryMustBeValid() + { + (new Uri(''))->withQuery(new \stdClass); + } + + public function testAllowsFalseyUrlParts() + { + $url = new Uri('http://a:1/0?0#0'); + $this->assertSame('a', $url->getHost()); + $this->assertEquals(1, $url->getPort()); + $this->assertSame('/0', $url->getPath()); + $this->assertEquals('0', (string) $url->getQuery()); + $this->assertSame('0', $url->getFragment()); + $this->assertEquals('http://a:1/0?0#0', (string) $url); + $url = new Uri(''); + $this->assertSame('', (string) $url); + $url = new Uri('0'); + $this->assertSame('0', (string) $url); + $url = new Uri('/'); + $this->assertSame('/', (string) $url); + } + + /** + * @dataProvider getResolveTestCases + */ + public function testResolvesUris($base, $rel, $expected) + { + $uri = new Uri($base); + $actual = Uri::resolve($uri, $rel); + $this->assertEquals($expected, (string) $actual); + } + + public function getResolveTestCases() + { + return [ + //[self::RFC3986_BASE, 'g:h', 'g:h'], + [self::RFC3986_BASE, 'g', 'http://a/b/c/g'], + [self::RFC3986_BASE, './g', 'http://a/b/c/g'], + [self::RFC3986_BASE, 'g/', 'http://a/b/c/g/'], + [self::RFC3986_BASE, '/g', 'http://a/g'], + [self::RFC3986_BASE, '//g', 'http://g'], + [self::RFC3986_BASE, '?y', 'http://a/b/c/d;p?y'], + [self::RFC3986_BASE, 'g?y', 'http://a/b/c/g?y'], + [self::RFC3986_BASE, '#s', 'http://a/b/c/d;p?q#s'], + [self::RFC3986_BASE, 'g#s', 'http://a/b/c/g#s'], + [self::RFC3986_BASE, 'g?y#s', 'http://a/b/c/g?y#s'], + [self::RFC3986_BASE, ';x', 'http://a/b/c/;x'], + [self::RFC3986_BASE, 'g;x', 'http://a/b/c/g;x'], + [self::RFC3986_BASE, 'g;x?y#s', 'http://a/b/c/g;x?y#s'], + [self::RFC3986_BASE, '', self::RFC3986_BASE], + [self::RFC3986_BASE, '.', 'http://a/b/c/'], + [self::RFC3986_BASE, './', 'http://a/b/c/'], + [self::RFC3986_BASE, '..', 'http://a/b/'], + [self::RFC3986_BASE, '../', 'http://a/b/'], + [self::RFC3986_BASE, '../g', 'http://a/b/g'], + [self::RFC3986_BASE, '../..', 'http://a/'], + [self::RFC3986_BASE, '../../', 'http://a/'], + [self::RFC3986_BASE, '../../g', 'http://a/g'], + [self::RFC3986_BASE, '../../../g', 'http://a/g'], + [self::RFC3986_BASE, '../../../../g', 'http://a/g'], + [self::RFC3986_BASE, '/./g', 'http://a/g'], + [self::RFC3986_BASE, '/../g', 'http://a/g'], + [self::RFC3986_BASE, 'g.', 'http://a/b/c/g.'], + [self::RFC3986_BASE, '.g', 'http://a/b/c/.g'], + [self::RFC3986_BASE, 'g..', 'http://a/b/c/g..'], + [self::RFC3986_BASE, '..g', 'http://a/b/c/..g'], + [self::RFC3986_BASE, './../g', 'http://a/b/g'], + [self::RFC3986_BASE, 'foo////g', 'http://a/b/c/foo////g'], + [self::RFC3986_BASE, './g/.', 'http://a/b/c/g/'], + [self::RFC3986_BASE, 'g/./h', 'http://a/b/c/g/h'], + [self::RFC3986_BASE, 'g/../h', 'http://a/b/c/h'], + [self::RFC3986_BASE, 'g;x=1/./y', 'http://a/b/c/g;x=1/y'], + [self::RFC3986_BASE, 'g;x=1/../y', 'http://a/b/c/y'], + //[self::RFC3986_BASE, 'http:g', 'http:g'], + ]; + } + + public function testAddAndRemoveQueryValues() + { + $uri = new Uri('http://foo.com/bar'); + $uri = Uri::withQueryValue($uri, 'a', 'b'); + $uri = Uri::withQueryValue($uri, 'c', 'd'); + $uri = Uri::withQueryValue($uri, 'e', null); + $this->assertEquals('a=b&c=d&e', $uri->getQuery()); + + $uri = Uri::withoutQueryValue($uri, 'c'); + $uri = Uri::withoutQueryValue($uri, 'e'); + $this->assertEquals('a=b', $uri->getQuery()); + $uri = Uri::withoutQueryValue($uri, 'a'); + $uri = Uri::withoutQueryValue($uri, 'a'); + $this->assertEquals('', $uri->getQuery()); + } + + public function testGetAuthorityReturnsCorrectPort() + { + // HTTPS non-standard port + $uri = new Uri('https://foo.co:99'); + $this->assertEquals('foo.co:99', $uri->getAuthority()); + + // HTTP non-standard port + $uri = new Uri('http://foo.co:99'); + $this->assertEquals('foo.co:99', $uri->getAuthority()); + + // No scheme + $uri = new Uri('foo.co:99'); + $this->assertEquals('foo.co:99', $uri->getAuthority()); + + // No host or port + $uri = new Uri('http:'); + $this->assertEquals('', $uri->getAuthority()); + + // No host or port + $uri = new Uri('http://foo.co'); + $this->assertEquals('foo.co', $uri->getAuthority()); + } + + public function pathTestProvider() + { + return [ + // Percent encode spaces. + ['http://foo.com/baz bar', 'http://foo.com/baz%20bar'], + // Don't encoding something that's already encoded. + ['http://foo.com/baz%20bar', 'http://foo.com/baz%20bar'], + // Percent encode invalid percent encodings + ['http://foo.com/baz%2-bar', 'http://foo.com/baz%252-bar'], + // Don't encode path segments + ['http://foo.com/baz/bar/bam?a', 'http://foo.com/baz/bar/bam?a'], + ['http://foo.com/baz+bar', 'http://foo.com/baz+bar'], + ['http://foo.com/baz:bar', 'http://foo.com/baz:bar'], + ['http://foo.com/baz@bar', 'http://foo.com/baz@bar'], + ['http://foo.com/baz(bar);bam/', 'http://foo.com/baz(bar);bam/'], + ['http://foo.com/a-zA-Z0-9.-_~!$&\'()*+,;=:@', 'http://foo.com/a-zA-Z0-9.-_~!$&\'()*+,;=:@'], + ]; + } + + /** + * @dataProvider pathTestProvider + */ + public function testUriEncodesPathProperly($input, $output) + { + $uri = new Uri($input); + $this->assertEquals((string) $uri, $output); + } + + public function testDoesNotAddPortWhenNoPort() + { + $this->assertEquals('bar', new Uri('//bar')); + $this->assertEquals('bar', (new Uri('//bar'))->getHost()); + } + + public function testAllowsForRelativeUri() + { + $uri = (new Uri)->withPath('foo'); + $this->assertEquals('foo', $uri->getPath()); + $this->assertEquals('foo', (string) $uri); + } + + public function testAddsSlashForRelativeUriStringWithHost() + { + $uri = (new Uri)->withPath('foo')->withHost('bar.com'); + $this->assertEquals('foo', $uri->getPath()); + $this->assertEquals('bar.com/foo', (string) $uri); + } +} diff --git a/core/vendor/guzzlehttp/psr7/tests/bootstrap.php b/core/vendor/guzzlehttp/psr7/tests/bootstrap.php new file mode 100644 index 0000000000000000000000000000000000000000..8601dd3302391e64a4953732d138ed07521ae050 --- /dev/null +++ b/core/vendor/guzzlehttp/psr7/tests/bootstrap.php @@ -0,0 +1,11 @@ +<?php +namespace GuzzleHttp\Tests\Psr7; + +require __DIR__ . '/../vendor/autoload.php'; + +class HasToString +{ + public function __toString() { + return 'foo'; + } +} diff --git a/core/vendor/guzzlehttp/ringphp/.gitignore b/core/vendor/guzzlehttp/ringphp/.gitignore deleted file mode 100644 index 290a94524176188548ce464e057987fbcdb919e6..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -vendor -build/artifacts/ -composer.lock -docs/_build/ diff --git a/core/vendor/guzzlehttp/ringphp/.travis.yml b/core/vendor/guzzlehttp/ringphp/.travis.yml deleted file mode 100644 index e43fbdd9cda3e0c4a84593ffafa2462c19a22fc3..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -language: php - -php: - - 5.4 - - 5.5 - - 5.6 - - 7.0 - - hhvm - -before_script: - - composer self-update - - composer install --no-interaction --prefer-source --dev - - ~/.nvm/nvm.sh install v0.6.14 - - ~/.nvm/nvm.sh run v0.6.14 - -script: - - make test - -matrix: - allow_failures: - - php: hhvm - fast_finish: true diff --git a/core/vendor/guzzlehttp/ringphp/CHANGELOG.md b/core/vendor/guzzlehttp/ringphp/CHANGELOG.md deleted file mode 100644 index 97458da57d2cfb3c7b90d65a85be4cb5dfbc04f7..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/CHANGELOG.md +++ /dev/null @@ -1,44 +0,0 @@ -# CHANGELOG - -## 1.0.7 - 2015-03-29 - -* PHP 7 fixes. - -## 1.0.6 - 2015-02-26 - -* Bug fix: futures now extend from React's PromiseInterface to ensure that they - are properly forwarded down the promise chain. -* The multi handle of the CurlMultiHandler is now created lazily. - -## 1.0.5 - 2014-12-10 - -* Adding more error information to PHP stream wrapper exceptions. -* Added digest auth integration test support to test server. - -## 1.0.4 - 2014-12-01 - -* Added support for older versions of cURL that do not have CURLOPT_TIMEOUT_MS. -* Setting debug to `false` does not enable debug output. -* Added a fix to the StreamHandler to return a `FutureArrayInterface` when an - error occurs. - -## 1.0.3 - 2014-11-03 - -* Setting the `header` stream option as a string to be compatible with GAE. -* Header parsing now ensures that header order is maintained in the parsed - message. - -## 1.0.2 - 2014-10-28 - -* Now correctly honoring a `version` option is supplied in a request. - See https://github.com/guzzle/RingPHP/pull/8 - -## 1.0.1 - 2014-10-26 - -* Fixed a header parsing issue with the `CurlHandler` and `CurlMultiHandler` - that caused cURL requests with multiple responses to merge repsonses together - (e.g., requests with digest authentication). - -## 1.0.0 - 2014-10-12 - -* Initial release. diff --git a/core/vendor/guzzlehttp/ringphp/Makefile b/core/vendor/guzzlehttp/ringphp/Makefile deleted file mode 100644 index 21c812e381309d1db478db1e505b6a84ddbbbdeb..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/Makefile +++ /dev/null @@ -1,46 +0,0 @@ -all: clean coverage docs - -docs: - cd docs && make html - -view-docs: - open docs/_build/html/index.html - -start-server: stop-server - node tests/Client/server.js &> /dev/null & - -stop-server: - @PID=$(shell ps axo pid,command \ - | grep 'tests/Client/server.js' \ - | grep -v grep \ - | cut -f 1 -d " "\ - ) && [ -n "$$PID" ] && kill $$PID || true - -test: start-server - vendor/bin/phpunit $(TEST) - $(MAKE) stop-server - -coverage: start-server - vendor/bin/phpunit --coverage-html=build/artifacts/coverage $(TEST) - $(MAKE) stop-server - -view-coverage: - open build/artifacts/coverage/index.html - -clean: - rm -rf build/artifacts/* - cd docs && make clean - -tag: - $(if $(TAG),,$(error TAG is not defined. Pass via "make tag TAG=4.2.1")) - @echo Tagging $(TAG) - chag update -m '$(TAG) ()' - git add -A - git commit -m '$(TAG) release' - chag tag - -perf: start-server - php tests/perf.php - $(MAKE) stop-server - -.PHONY: docs diff --git a/core/vendor/guzzlehttp/ringphp/README.rst b/core/vendor/guzzlehttp/ringphp/README.rst deleted file mode 100644 index 10374e813b1f31b2657a9d12e392b8ccd1d2e84c..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/README.rst +++ /dev/null @@ -1,46 +0,0 @@ -======= -RingPHP -======= - -Provides a simple API and specification that abstracts away the details of HTTP -into a single PHP function. RingPHP be used to power HTTP clients and servers -through a PHP function that accepts a request hash and returns a response hash -that is fulfilled using a `promise <https://github.com/reactphp/promise>`_, -allowing RingPHP to support both synchronous and asynchronous workflows. - -By abstracting the implementation details of different HTTP clients and -servers, RingPHP allows you to utilize pluggable HTTP clients and servers -without tying your application to a specific implementation. - -.. code-block:: php - - <?php - require 'vendor/autoload.php'; - - use GuzzleHttp\Ring\Client\CurlHandler; - - $handler = new CurlHandler(); - $response = $handler([ - 'http_method' => 'GET', - 'uri' => '/', - 'headers' => [ - 'host' => ['www.google.com'], - 'x-foo' => ['baz'] - ] - ]); - - $response->then(function (array $response) { - echo $response['status']; - }); - - $response->wait(); - -RingPHP is inspired by Clojure's `Ring <https://github.com/ring-clojure/ring>`_, -which, in turn, was inspired by Python's WSGI and Ruby's Rack. RingPHP is -utilized as the handler layer in `Guzzle <http://guzzlephp.org>`_ 5.0+ to send -HTTP requests. - -Documentation -------------- - -See http://ringphp.readthedocs.org/ for the full online documentation. diff --git a/core/vendor/guzzlehttp/ringphp/composer.json b/core/vendor/guzzlehttp/ringphp/composer.json deleted file mode 100644 index fcca6298e1fb274ec6d159326c2cbb5a20f30feb..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/composer.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "guzzlehttp/ringphp", - "description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.", - "license": "MIT", - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "require": { - "php": ">=5.4.0", - "guzzlehttp/streams": "~3.0", - "react/promise": "~2.0" - }, - "require-dev": { - "ext-curl": "*", - "phpunit/phpunit": "~4.0" - }, - "suggest": { - "ext-curl": "Guzzle will use specific adapters if cURL is present" - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Ring\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "GuzzleHttp\\Tests\\Ring\\": "tests/" - } - }, - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - } -} diff --git a/core/vendor/guzzlehttp/ringphp/docs/Makefile b/core/vendor/guzzlehttp/ringphp/docs/Makefile deleted file mode 100644 index 51270aa5d39fb03f562a2acb09df88f082741b01..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/docs/Makefile +++ /dev/null @@ -1,153 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = _build - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext - -help: - @echo "Please use \`make <target>' where <target> is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - -rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/GuzzleRing.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/GuzzleRing.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/GuzzleRing" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/GuzzleRing" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." diff --git a/core/vendor/guzzlehttp/ringphp/docs/client_handlers.rst b/core/vendor/guzzlehttp/ringphp/docs/client_handlers.rst deleted file mode 100644 index 3151f00216ce0afdd31d2fb0a936c07f61b045de..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/docs/client_handlers.rst +++ /dev/null @@ -1,173 +0,0 @@ -=============== -Client Handlers -=============== - -Client handlers accept a request array and return a future response array that -can be used synchronously as an array or asynchronously using a promise. - -Built-In Handlers ------------------ - -RingPHP comes with three built-in client handlers. - -Stream Handler -~~~~~~~~~~~~~~ - -The ``GuzzleHttp\Ring\Client\StreamHandler`` uses PHP's -`http stream wrapper <http://php.net/manual/en/wrappers.http.php>`_ to send -requests. - -.. note:: - - This handler cannot send requests concurrently. - -You can provide an associative array of custom stream context options to the -StreamHandler using the ``stream_context`` key of the ``client`` request -option. - -.. code-block:: php - - use GuzzleHttp\Ring\Client\StreamHandler; - - $response = $handler([ - 'http_method' => 'GET', - 'uri' => '/', - 'headers' => ['host' => ['httpbin.org']], - 'client' => [ - 'stream_context' => [ - 'http' => [ - 'request_fulluri' => true, - 'method' => 'HEAD' - ], - 'socket' => [ - 'bindto' => '127.0.0.1:0' - ], - 'ssl' => [ - 'verify_peer' => false - ] - ] - ] - ]); - - // Even though it's already completed, you can still use a promise - $response->then(function ($response) { - echo $response['status']; // 200 - }); - - // Or access the response using the future interface - echo $response['status']; // 200 - -cURL Handler -~~~~~~~~~~~~ - -The ``GuzzleHttp\Ring\Client\CurlHandler`` can be used with PHP 5.5+ to send -requests using cURL easy handles. This handler is great for sending requests -one at a time because the execute and select loop is implemented in C code -which executes faster and consumes less memory than using PHP's -``curl_multi_*`` interface. - -.. note:: - - This handler cannot send requests concurrently. - -When using the CurlHandler, custom curl options can be specified as an -associative array of `cURL option constants <http://php.net/manual/en/curl.constants.php>`_ -mapping to values in the ``client`` option of a requst using the **curl** key. - -.. code-block:: php - - use GuzzleHttp\Ring\Client\CurlHandler; - - $handler = new CurlHandler(); - - $request = [ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - 'client' => ['curl' => [CURLOPT_LOW_SPEED_LIMIT => 10]] - ]; - - $response = $handler($request); - - // The response can be used directly as an array. - echo $response['status']; // 200 - - // Or, it can be used as a promise (that has already fulfilled). - $response->then(function ($response) { - echo $response['status']; // 200 - }); - -cURL Multi Handler -~~~~~~~~~~~~~~~~~~ - -The ``GuzzleHttp\Ring\Client\CurlMultiHandler`` transfers requests using -cURL's `multi API <http://curl.haxx.se/libcurl/c/libcurl-multi.html>`_. The -``CurlMultiHandler`` is great for sending requests concurrently. - -.. code-block:: php - - use GuzzleHttp\Ring\Client\CurlMultiHandler; - - $handler = new CurlMultiHandler(); - - $request = [ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]] - ]; - - // this call returns a future array immediately. - $response = $handler($request); - - // Ideally, you should use the promise API to not block. - $response - ->then(function ($response) { - // Got the response at some point in the future - echo $response['status']; // 200 - // Don't break the chain - return $response; - })->then(function ($response) { - // ... - }); - - // If you really need to block, then you can use the response as an - // associative array. This will block until it has completed. - echo $response['status']; // 200 - -Just like the ``CurlHandler``, the ``CurlMultiHandler`` accepts custom curl -option in the ``curl`` key of the ``client`` request option. - -Mock Handler -~~~~~~~~~~~~ - -The ``GuzzleHttp\Ring\Client\MockHandler`` is used to return mock responses. -When constructed, the handler can be configured to return the same response -array over and over, a future response, or a the evaluation of a callback -function. - -.. code-block:: php - - use GuzzleHttp\Ring\Client\MockHandler; - - // Return a canned response. - $mock = new MockHandler(['status' => 200]); - $response = $mock([]); - assert(200 == $response['status']); - assert([] == $response['headers']); - -Implementing Handlers ---------------------- - -Client handlers are just PHP callables (functions or classes that have the -``__invoke`` magic method). The callable accepts a request array and MUST -return an instance of ``GuzzleHttp\Ring\Future\FutureArrayInterface`` so that -the response can be used by both blocking and non-blocking consumers. - -Handlers need to follow a few simple rules: - -1. Do not throw exceptions. If an error is encountered, return an array that - contains the ``error`` key that maps to an ``\Exception`` value. -2. If the request has a ``delay`` client option, then the handler should only - send the request after the specified delay time in seconds. Blocking - handlers may find it convenient to just let the - ``GuzzleHttp\Ring\Core::doSleep($request)`` function handle this for them. -3. Always return an instance of ``GuzzleHttp\Ring\Future\FutureArrayInterface``. -4. Complete any outstanding requests when the handler is destructed. diff --git a/core/vendor/guzzlehttp/ringphp/docs/client_middleware.rst b/core/vendor/guzzlehttp/ringphp/docs/client_middleware.rst deleted file mode 100644 index 7c52c8a695093f5fdcf9453cf3951a0c0477875d..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/docs/client_middleware.rst +++ /dev/null @@ -1,165 +0,0 @@ -================= -Client Middleware -================= - -Middleware intercepts requests before they are sent over the wire and can be -used to add functionality to handlers. - -Modifying Requests ------------------- - -Let's say you wanted to modify requests before they are sent over the wire -so that they always add specific headers. This can be accomplished by creating -a function that accepts a handler and returns a new function that adds the -composed behavior. - -.. code-block:: php - - use GuzzleHttp\Ring\Client\CurlHandler; - - $handler = new CurlHandler(); - - $addHeaderHandler = function (callable $handler, array $headers = []) { - return function (array $request) use ($handler, $headers) { - // Add our custom headers - foreach ($headers as $key => $value) { - $request['headers'][$key] = $value; - } - - // Send the request using the handler and return the response. - return $handler($request); - } - }; - - // Create a new handler that adds headers to each request. - $handler = $addHeaderHandler($handler, [ - 'X-AddMe' => 'hello', - 'Authorization' => 'Basic xyz' - ]); - - $response = $handler([ - 'http_method' => 'GET', - 'headers' => ['Host' => ['httpbin.org'] - ]); - -Modifying Responses -------------------- - -You can change a response as it's returned from a middleware. Remember that -responses returned from an handler (including middleware) must implement -``GuzzleHttp\Ring\Future\FutureArrayInterface``. In order to be a good citizen, -you should not expect that the responses returned through your middleware will -be completed synchronously. Instead, you should use the -``GuzzleHttp\Ring\Core::proxy()`` function to modify the response when the -underlying promise is resolved. This function is a helper function that makes it -easy to create a new instance of ``FutureArrayInterface`` that wraps an existing -``FutureArrayInterface`` object. - -Let's say you wanted to add headers to a response as they are returned from -your middleware, but you want to make sure you aren't causing future -responses to be dereferenced right away. You can achieve this by modifying the -incoming request and using the ``Core::proxy`` function. - -.. code-block:: php - - use GuzzleHttp\Ring\Core; - use GuzzleHttp\Ring\Client\CurlHandler; - - $handler = new CurlHandler(); - - $responseHeaderHandler = function (callable $handler, array $headers) { - return function (array $request) use ($handler, $headers) { - // Send the request using the wrapped handler. - return Core::proxy($handler($request), function ($response) use ($headers) { - // Add the headers to the response when it is available. - foreach ($headers as $key => $value) { - $response['headers'][$key] = (array) $value; - } - // Note that you can return a regular response array when using - // the proxy method. - return $response; - }); - } - }; - - // Create a new handler that adds headers to each response. - $handler = $responseHeaderHandler($handler, ['X-Header' => 'hello!']); - - $response = $handler([ - 'http_method' => 'GET', - 'headers' => ['Host' => ['httpbin.org'] - ]); - - assert($response['headers']['X-Header'] == 'hello!'); - -Built-In Middleware -------------------- - -RingPHP comes with a few basic client middlewares that modify requests -and responses. - -Streaming Middleware -~~~~~~~~~~~~~~~~~~~~ - -If you want to send all requests with the ``streaming`` option to a specific -handler but other requests to a different handler, then use the streaming -middleware. - -.. code-block:: php - - use GuzzleHttp\Ring\Client\CurlHandler; - use GuzzleHttp\Ring\Client\StreamHandler; - use GuzzleHttp\Ring\Client\Middleware; - - $defaultHandler = new CurlHandler(); - $streamingHandler = new StreamHandler(); - $streamingHandler = Middleware::wrapStreaming( - $defaultHandler, - $streamingHandler - ); - - // Send the request using the streaming handler. - $response = $streamingHandler([ - 'http_method' => 'GET', - 'headers' => ['Host' => ['www.google.com'], - 'stream' => true - ]); - - // Send the request using the default handler. - $response = $streamingHandler([ - 'http_method' => 'GET', - 'headers' => ['Host' => ['www.google.com'] - ]); - -Future Middleware -~~~~~~~~~~~~~~~~~ - -If you want to send all requests with the ``future`` option to a specific -handler but other requests to a different handler, then use the future -middleware. - -.. code-block:: php - - use GuzzleHttp\Ring\Client\CurlHandler; - use GuzzleHttp\Ring\Client\CurlMultiHandler; - use GuzzleHttp\Ring\Client\Middleware; - - $defaultHandler = new CurlHandler(); - $futureHandler = new CurlMultiHandler(); - $futureHandler = Middleware::wrapFuture( - $defaultHandler, - $futureHandler - ); - - // Send the request using the blocking CurlHandler. - $response = $futureHandler([ - 'http_method' => 'GET', - 'headers' => ['Host' => ['www.google.com'] - ]); - - // Send the request using the non-blocking CurlMultiHandler. - $response = $futureHandler([ - 'http_method' => 'GET', - 'headers' => ['Host' => ['www.google.com'], - 'future' => true - ]); diff --git a/core/vendor/guzzlehttp/ringphp/docs/conf.py b/core/vendor/guzzlehttp/ringphp/docs/conf.py deleted file mode 100644 index c6404aa1e1453a433bb4667500aa2cb7edff4d79..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/docs/conf.py +++ /dev/null @@ -1,23 +0,0 @@ -import sys, os -import sphinx_rtd_theme -from sphinx.highlighting import lexers -from pygments.lexers.web import PhpLexer - - -lexers['php'] = PhpLexer(startinline=True, linenos=1) -lexers['php-annotations'] = PhpLexer(startinline=True, linenos=1) -primary_domain = 'php' - -extensions = [] -templates_path = ['_templates'] -source_suffix = '.rst' -master_doc = 'index' -project = u'RingPHP' -copyright = u'2014, Michael Dowling' -version = '1.0.0-alpha' -exclude_patterns = ['_build'] - -html_title = "RingPHP" -html_short_title = "RingPHP" -html_theme = "sphinx_rtd_theme" -html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] diff --git a/core/vendor/guzzlehttp/ringphp/docs/futures.rst b/core/vendor/guzzlehttp/ringphp/docs/futures.rst deleted file mode 100644 index af29cb3780cddffef0534c9c53cbf47d2472e178..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/docs/futures.rst +++ /dev/null @@ -1,164 +0,0 @@ -======= -Futures -======= - -Futures represent a computation that may have not yet completed. RingPHP -uses hybrid of futures and promises to provide a consistent API that can be -used for both blocking and non-blocking consumers. - -Promises --------- - -You can get the result of a future when it is ready using the promise interface -of a future. Futures expose a promise API via a ``then()`` method that utilizes -`React's promise library <https://github.com/reactphp/promise>`_. You should -use this API when you do not wish to block. - -.. code-block:: php - - use GuzzleHttp\Ring\Client\CurlMultiHandler; - - $request = [ - 'http_method' => 'GET', - 'uri' => '/', - 'headers' => ['host' => ['httpbin.org']] - ]; - - $response = $handler($request); - - // Use the then() method to use the promise API of the future. - $response->then(function ($response) { - echo $response['status']; - }); - -You can get the promise used by a future, an instance of -``React\Promise\PromiseInterface``, by calling the ``promise()`` method. - -.. code-block:: php - - $response = $handler($request); - $promise = $response->promise(); - $promise->then(function ($response) { - echo $response['status']; - }); - -This promise value can be used with React's -`aggregate promise functions <https://github.com/reactphp/promise#functions>`_. - -Waiting -------- - -You can wait on a future to complete and retrieve the value, or *dereference* -the future, using the ``wait()`` method. Calling the ``wait()`` method of a -future will block until the result is available. The result is then returned or -an exception is thrown if and exception was encountered while waiting on the -the result. Subsequent calls to dereference a future will return the previously -completed result or throw the previously encountered exception. Futures can be -cancelled, which stops the computation if possible. - -.. code-block:: php - - use GuzzleHttp\Ring\Client\CurlMultiHandler; - - $response = $handler([ - 'http_method' => 'GET', - 'uri' => '/', - 'headers' => ['host' => ['httpbin.org']] - ]); - - // You can explicitly call block to wait on a result. - $realizedResponse = $response->wait(); - - // Future responses can be used like a regular PHP array. - echo $response['status']; - -In addition to explicitly calling the ``wait()`` function, using a future like -a normal value will implicitly trigger the ``wait()`` function. - -Future Responses ----------------- - -RingPHP uses futures to return asynchronous responses immediately. Client -handlers always return future responses that implement -``GuzzleHttp\Ring\Future\ArrayFutureInterface``. These future responses act -just like normal PHP associative arrays for blocking access and provide a -promise interface for non-blocking access. - -.. code-block:: php - - use GuzzleHttp\Ring\Client\CurlMultiHandler; - - $handler = new CurlMultiHandler(); - - $request = [ - 'http_method' => 'GET', - 'uri' => '/', - 'headers' => ['Host' => ['www.google.com']] - ]; - - $response = $handler($request); - - // Use the promise API for non-blocking access to the response. The actual - // response value will be delivered to the promise. - $response->then(function ($response) { - echo $response['status']; - }); - - // You can wait (block) until the future is completed. - $response->wait(); - - // This will implicitly call wait(), and will block too! - $response['status']; - -.. important:: - - Futures that are not completed by the time the underlying handler is - destructed will be completed when the handler is shutting down. - -Cancelling ----------- - -Futures can be cancelled if they have not already been dereferenced. - -RingPHP futures are typically implemented with the -``GuzzleHttp\Ring\Future\BaseFutureTrait``. This trait provides the cancellation -functionality that should be common to most implementations. Cancelling a -future response will try to prevent the request from sending over the wire. - -When a future is cancelled, the cancellation function is invoked and performs -the actual work needed to cancel the request from sending if possible -(e.g., telling an event loop to stop sending a request or to close a socket). -If no cancellation function is provided, then a request cannot be cancelled. If -a cancel function is provided, then it should accept the future as an argument -and return true if the future was successfully cancelled or false if it could -not be cancelled. - -Wrapping an existing Promise ----------------------------- - -You can easily create a future from any existing promise using the -``GuzzleHttp\Ring\Future\FutureValue`` class. This class's constructor -accepts a promise as the first argument, a wait function as the second -argument, and a cancellation function as the third argument. The dereference -function is used to force the promise to resolve (for example, manually ticking -an event loop). The cancel function is optional and is used to tell the thing -that created the promise that it can stop computing the result (for example, -telling an event loop to stop transferring a request). - -.. code-block:: php - - use GuzzleHttp\Ring\Future\FutureValue; - use React\Promise\Deferred; - - $deferred = new Deferred(); - $promise = $deferred->promise(); - - $f = new FutureValue( - $promise, - function () use ($deferred) { - // This function is responsible for blocking and resolving the - // promise. Here we pass in a reference to the deferred so that - // it can be resolved or rejected. - $deferred->resolve('foo'); - } - ); diff --git a/core/vendor/guzzlehttp/ringphp/docs/index.rst b/core/vendor/guzzlehttp/ringphp/docs/index.rst deleted file mode 100644 index 4bbce631c7089a011133baae5571ccba1a44296a..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/docs/index.rst +++ /dev/null @@ -1,50 +0,0 @@ -======= -RingPHP -======= - -Provides a simple API and specification that abstracts away the details of HTTP -into a single PHP function. RingPHP be used to power HTTP clients and servers -through a PHP function that accepts a request hash and returns a response hash -that is fulfilled using a `promise <https://github.com/reactphp/promise>`_, -allowing RingPHP to support both synchronous and asynchronous workflows. - -By abstracting the implementation details of different HTTP clients and -servers, RingPHP allows you to utilize pluggable HTTP clients and servers -without tying your application to a specific implementation. - -.. toctree:: - :maxdepth: 2 - - spec - futures - client_middleware - client_handlers - testing - -.. code-block:: php - - <?php - require 'vendor/autoload.php'; - - use GuzzleHttp\Ring\Client\CurlHandler; - - $handler = new CurlHandler(); - $response = $handler([ - 'http_method' => 'GET', - 'uri' => '/', - 'headers' => [ - 'host' => ['www.google.com'], - 'x-foo' => ['baz'] - ] - ]); - - $response->then(function (array $response) { - echo $response['status']; - }); - - $response->wait(); - -RingPHP is inspired by Clojure's `Ring <https://github.com/ring-clojure/ring>`_, -which, in turn, was inspired by Python's WSGI and Ruby's Rack. RingPHP is -utilized as the handler layer in `Guzzle <http://guzzlephp.org>`_ 5.0+ to send -HTTP requests. diff --git a/core/vendor/guzzlehttp/ringphp/docs/requirements.txt b/core/vendor/guzzlehttp/ringphp/docs/requirements.txt deleted file mode 100644 index 483a4e9600bd7a794750dd52ca24503fdf220133..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/docs/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -sphinx_rtd_theme diff --git a/core/vendor/guzzlehttp/ringphp/docs/spec.rst b/core/vendor/guzzlehttp/ringphp/docs/spec.rst deleted file mode 100644 index bc9107898f4a19e53a760c74259a95960218ebcf..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/docs/spec.rst +++ /dev/null @@ -1,311 +0,0 @@ -============= -Specification -============= - -RingPHP applications consist of handlers, requests, responses, and -middleware. - -Handlers --------- - -Handlers are implemented as a PHP ``callable`` that accept a request array -and return a response array (``GuzzleHttp\Ring\Future\FutureArrayInterface``). - -For example: - -.. code-block:: php - - use GuzzleHttp\Ring\Future\CompletedFutureArray; - - $mockHandler = function (array $request) { - return new CompletedFutureArray([ - 'status' => 200, - 'headers' => ['X-Foo' => ['Bar']], - 'body' => 'Hello!' - ]); - }; - -This handler returns the same response each time it is invoked. All RingPHP -handlers must return a ``GuzzleHttp\Ring\Future\FutureArrayInterface``. Use -``GuzzleHttp\Ring\Future\CompletedFutureArray`` when returning a response that -has already completed. - -Requests --------- - -A request array is a PHP associative array that contains the configuration -settings need to send a request. - -.. code-block:: php - - $request = [ - 'http_method' => 'GET', - 'scheme' => 'http', - 'uri' => '/', - 'body' => 'hello!', - 'client' => ['timeout' => 1.0], - 'headers' => [ - 'host' => ['httpbin.org'], - 'X-Foo' => ['baz', 'bar'] - ] - ]; - -The request array contains the following key value pairs: - -request_method - (string, required) The HTTP request method, must be all caps corresponding - to a HTTP request method, such as ``GET`` or ``POST``. - -scheme - (string) The transport protocol, must be one of ``http`` or ``https``. - Defaults to ``http``. - -uri - (string, required) The request URI excluding the query string. Must - start with "/". - -query_string - (string) The query string, if present (e.g., ``foo=bar``). - -version - (string) HTTP protocol version. Defaults to ``1.1``. - -headers - (required, array) Associative array of headers. Each key represents the - header name. Each value contains an array of strings where each entry of - the array SHOULD be sent over the wire on a separate header line. - -body - (string, fopen resource, ``Iterator``, ``GuzzleHttp\Stream\StreamInterface``) - The body of the request, if present. Can be a string, resource returned - from fopen, an ``Iterator`` that yields chunks of data, an object that - implemented ``__toString``, or a ``GuzzleHttp\Stream\StreamInterface``. - -future - (bool, string) Controls the asynchronous behavior of a response. - - Set to ``true`` or omit the ``future`` option to *request* that a request - will be completed asynchronously. Keep in mind that your request might not - necessarily be completed asynchronously based on the handler you are using. - Set the ``future`` option to ``false`` to request that a synchronous - response be provided. - - You can provide a string value to specify fine-tuned future behaviors that - may be specific to the underlying handlers you are using. There are, - however, some common future options that handlers should implement if - possible. - - lazy - Requests that the handler does not open and send the request - immediately, but rather only opens and sends the request once the - future is dereferenced. This option is often useful for sending a large - number of requests concurrently to allow handlers to take better - advantage of non-blocking transfers by first building up a pool of - requests. - - If an handler does not implement or understand a provided string value, - then the request MUST be treated as if the user provided ``true`` rather - than the string value. - - Future responses created by asynchronous handlers MUST attempt to complete - any outstanding future responses when they are destructed. Asynchronous - handlers MAY choose to automatically complete responses when the number - of outstanding requests reaches an handler-specific threshold. - -Client Specific Options -~~~~~~~~~~~~~~~~~~~~~~~ - -The following options are only used in ring client handlers. - -.. _client-options: - -client - (array) Associative array of client specific transfer options. The - ``client`` request key value pair can contain the following keys: - - cert - (string, array) Set to a string to specify the path to a file - containing a PEM formatted SSL client side certificate. If a password - is required, then set ``cert`` to an array containing the path to the - PEM file in the first array element followed by the certificate - password in the second array element. - - connect_timeout - (float) Float describing the number of seconds to wait while trying to - connect to a server. Use ``0`` to wait indefinitely (the default - behavior). - - debug - (bool, fopen() resource) Set to true or set to a PHP stream returned by - fopen() to enable debug output with the handler used to send a request. - If set to ``true``, the output is written to PHP's STDOUT. If a PHP - ``fopen`` resource handle is provided, the output is written to the - stream. - - "Debug output" is handler specific: different handlers will yield - different output and various various level of detail. For example, when - using cURL to transfer requests, cURL's `CURLOPT_VERBOSE <http://curl.haxx.se/libcurl/c/CURLOPT_VERBOSE.html>`_ - will be used. When using the PHP stream wrapper, `stream notifications <http://php.net/manual/en/function.stream-notification-callback.php>`_ - will be emitted. - - decode_content - (bool) Specify whether or not ``Content-Encoding`` responses - (gzip, deflate, etc.) are automatically decoded. Set to ``true`` to - automatically decode encoded responses. Set to ``false`` to not decode - responses. By default, content is *not* decoded automatically. - - delay - (int) The number of milliseconds to delay before sending the request. - This is often used for delaying before retrying a request. Handlers - SHOULD implement this if possible, but it is not a strict requirement. - - progress - (function) Defines a function to invoke when transfer progress is made. - The function accepts the following arguments: - - 1. The total number of bytes expected to be downloaded - 2. The number of bytes downloaded so far - 3. The number of bytes expected to be uploaded - 4. The number of bytes uploaded so far - - proxy - (string, array) Pass a string to specify an HTTP proxy, or an - associative array to specify different proxies for different protocols - where the scheme is the key and the value is the proxy address. - - .. code-block:: php - - $request = [ - 'http_method' => 'GET', - 'headers' => ['host' => ['httpbin.org']], - 'client' => [ - // Use different proxies for different URI schemes. - 'proxy' => [ - 'http' => 'http://proxy.example.com:5100', - 'https' => 'https://proxy.example.com:6100' - ] - ] - ]; - - ssl_key - (string, array) Specify the path to a file containing a private SSL key - in PEM format. If a password is required, then set to an array - containing the path to the SSL key in the first array element followed - by the password required for the certificate in the second element. - - save_to - (string, fopen resource, ``GuzzleHttp\Stream\StreamInterface``) - Specifies where the body of the response is downloaded. Pass a string to - open a local file on disk and save the output to the file. Pass an fopen - resource to save the output to a PHP stream resource. Pass a - ``GuzzleHttp\Stream\StreamInterface`` to save the output to a Guzzle - StreamInterface. Omitting this option will typically save the body of a - response to a PHP temp stream. - - stream - (bool) Set to true to stream a response rather than download it all - up-front. This option will only be utilized when the corresponding - handler supports it. - - timeout - (float) Float describing the timeout of the request in seconds. Use 0 to - wait indefinitely (the default behavior). - - verify - (bool, string) Describes the SSL certificate verification behavior of a - request. Set to true to enable SSL certificate verification using the - system CA bundle when available (the default). Set to false to disable - certificate verification (this is insecure!). Set to a string to provide - the path to a CA bundle on disk to enable verification using a custom - certificate. - - version - (string) HTTP protocol version to use with the request. - -Server Specific Options -~~~~~~~~~~~~~~~~~~~~~~~ - -The following options are only used in ring server handlers. - -server_port - (integer) The port on which the request is being handled. This is only - used with ring servers, and is required. - -server_name - (string) The resolved server name, or the server IP address. Required when - using a Ring server. - -remote_addr - (string) The IP address of the client or the last proxy that sent the - request. Required when using a Ring server. - -Responses ---------- - -A response is an array-like object that implements -``GuzzleHttp\Ring\Future\FutureArrayInterface``. Responses contain the -following key value pairs: - -body - (string, fopen resource, ``Iterator``, ``GuzzleHttp\Stream\StreamInterface``) - The body of the response, if present. Can be a string, resource returned - from fopen, an ``Iterator`` that yields chunks of data, an object that - implemented ``__toString``, or a ``GuzzleHttp\Stream\StreamInterface``. - -effective_url - (string) The URL that returned the resulting response. - -error - (``\Exception``) Contains an exception describing any errors that were - encountered during the transfer. - -headers - (Required, array) Associative array of headers. Each key represents the - header name. Each value contains an array of strings where each entry of - the array is a header line. The headers array MAY be an empty array in the - event an error occurred before a response was received. - -reason - (string) Optional reason phrase. This option should be provided when the - reason phrase does not match the typical reason phrase associated with the - ``status`` code. See `RFC 7231 <http://tools.ietf.org/html/rfc7231#section-6.1>`_ - for a list of HTTP reason phrases mapped to status codes. - -status - (Required, integer) The HTTP status code. The status code MAY be set to - ``null`` in the event an error occurred before a response was received - (e.g., a networking error). - -transfer_stats - (array) Provides an associative array of arbitrary transfer statistics if - provided by the underlying handler. - -version - (string) HTTP protocol version. Defaults to ``1.1``. - -Middleware ----------- - -Ring middleware augments the functionality of handlers by invoking them in the -process of generating responses. Middleware is typically implemented as a -higher-order function that takes one or more handlers as arguments followed by -an optional associative array of options as the last argument, returning a new -handler with the desired compound behavior. - -Here's an example of a middleware that adds a Content-Type header to each -request. - -.. code-block:: php - - use GuzzleHttp\Ring\Client\CurlHandler; - use GuzzleHttp\Ring\Core; - - $contentTypeHandler = function(callable $handler, $contentType) { - return function (array $request) use ($handler, $contentType) { - return $handler(Core::setHeader('Content-Type', $contentType)); - }; - }; - - $baseHandler = new CurlHandler(); - $wrappedHandler = $contentTypeHandler($baseHandler, 'text/html'); - $response = $wrappedHandler([/** request hash **/]); diff --git a/core/vendor/guzzlehttp/ringphp/docs/testing.rst b/core/vendor/guzzlehttp/ringphp/docs/testing.rst deleted file mode 100644 index 9df2562ed42817f4972d6ae57ba2dd86f27587e5..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/docs/testing.rst +++ /dev/null @@ -1,74 +0,0 @@ -======= -Testing -======= - -RingPHP tests client handlers using `PHPUnit <https://phpunit.de/>`_ and a -built-in node.js web server. - -Running Tests -------------- - -First, install the dependencies using `Composer <https://getcomposer.org>`_. - - composer.phar install - -Next, run the unit tests using ``Make``. - - make test - -The tests are also run on Travis-CI on each commit: https://travis-ci.org/guzzle/guzzle-ring - -Test Server ------------ - -Testing client handlers usually involves actually sending HTTP requests. -RingPHP provides a node.js web server that returns canned responses and -keep a list of the requests that have been received. The server can then -be queried to get a list of the requests that were sent by the client so that -you can ensure that the client serialized and transferred requests as intended. - -The server keeps a list of queued responses and returns responses that are -popped off of the queue as HTTP requests are received. When there are not -more responses to serve, the server returns a 500 error response. - -The test server uses the ``GuzzleHttp\Tests\Ring\Client\Server`` class to -control the server. - -.. code-block:: php - - use GuzzleHttp\Ring\Client\StreamHandler; - use GuzzleHttp\Tests\Ring\Client\Server; - - // First return a 200 followed by a 404 response. - Server::enqueue([ - ['status' => 200], - ['status' => 404] - ]); - - $handler = new StreamHandler(); - - $response = $handler([ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - 'uri' => '/' - ]); - - assert(200 == $response['status']); - - $response = $handler([ - 'http_method' => 'HEAD', - 'headers' => ['host' => [Server::$host]], - 'uri' => '/' - ]); - - assert(404 == $response['status']); - -After requests have been sent, you can get a list of the requests as they -were sent over the wire to ensure they were sent correctly. - -.. code-block:: php - - $received = Server::received(); - - assert('GET' == $received[0]['http_method']); - assert('HEAD' == $received[1]['http_method']); diff --git a/core/vendor/guzzlehttp/ringphp/phpunit.xml.dist b/core/vendor/guzzlehttp/ringphp/phpunit.xml.dist deleted file mode 100644 index 1d192902507b9f1c21f28fbdebdf3ddcbea0167c..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/phpunit.xml.dist +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<phpunit bootstrap="./tests/bootstrap.php" - colors="true"> - <testsuites> - <testsuite> - <directory>tests</directory> - </testsuite> - </testsuites> - <filter> - <whitelist> - <directory suffix=".php">src</directory> - </whitelist> - </filter> -</phpunit> diff --git a/core/vendor/guzzlehttp/ringphp/src/Client/ClientUtils.php b/core/vendor/guzzlehttp/ringphp/src/Client/ClientUtils.php deleted file mode 100644 index 2acf92eba0d04ecb9c7a1021513039931f47d9b6..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/src/Client/ClientUtils.php +++ /dev/null @@ -1,74 +0,0 @@ -<?php -namespace GuzzleHttp\Ring\Client; - -/** - * Client specific utility functions. - */ -class ClientUtils -{ - /** - * Returns the default cacert bundle for the current system. - * - * First, the openssl.cafile and curl.cainfo php.ini settings are checked. - * If those settings are not configured, then the common locations for - * bundles found on Red Hat, CentOS, Fedora, Ubuntu, Debian, FreeBSD, OS X - * and Windows are checked. If any of these file locations are found on - * disk, they will be utilized. - * - * Note: the result of this function is cached for subsequent calls. - * - * @return string - * @throws \RuntimeException if no bundle can be found. - */ - public static function getDefaultCaBundle() - { - static $cached = null; - static $cafiles = [ - // Red Hat, CentOS, Fedora (provided by the ca-certificates package) - '/etc/pki/tls/certs/ca-bundle.crt', - // Ubuntu, Debian (provided by the ca-certificates package) - '/etc/ssl/certs/ca-certificates.crt', - // FreeBSD (provided by the ca_root_nss package) - '/usr/local/share/certs/ca-root-nss.crt', - // OS X provided by homebrew (using the default path) - '/usr/local/etc/openssl/cert.pem', - // Windows? - 'C:\\windows\\system32\\curl-ca-bundle.crt', - 'C:\\windows\\curl-ca-bundle.crt', - ]; - - if ($cached) { - return $cached; - } - - if ($ca = ini_get('openssl.cafile')) { - return $cached = $ca; - } - - if ($ca = ini_get('curl.cainfo')) { - return $cached = $ca; - } - - foreach ($cafiles as $filename) { - if (file_exists($filename)) { - return $cached = $filename; - } - } - - throw new \RuntimeException(self::CA_ERR); - } - - const CA_ERR = " -No system CA bundle could be found in any of the the common system locations. -PHP versions earlier than 5.6 are not properly configured to use the system's -CA bundle by default. In order to verify peer certificates, you will need to -supply the path on disk to a certificate bundle to the 'verify' request -option: http://docs.guzzlephp.org/en/latest/clients.html#verify. If you do not -need a specific certificate bundle, then Mozilla provides a commonly used CA -bundle which can be downloaded here (provided by the maintainer of cURL): -https://raw.githubusercontent.com/bagder/ca-bundle/master/ca-bundle.crt. Once -you have a CA bundle available on disk, you can set the 'openssl.cafile' PHP -ini setting to point to the path to the file, allowing you to omit the 'verify' -request option. See http://curl.haxx.se/docs/sslcerts.html for more -information."; -} diff --git a/core/vendor/guzzlehttp/ringphp/src/Client/CurlFactory.php b/core/vendor/guzzlehttp/ringphp/src/Client/CurlFactory.php deleted file mode 100644 index 0377ec258e64c10ee3fb15a8b34dfaee4f5c909e..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/src/Client/CurlFactory.php +++ /dev/null @@ -1,546 +0,0 @@ -<?php -namespace GuzzleHttp\Ring\Client; - -use GuzzleHttp\Ring\Core; -use GuzzleHttp\Ring\Exception\ConnectException; -use GuzzleHttp\Ring\Exception\RingException; -use GuzzleHttp\Stream\LazyOpenStream; -use GuzzleHttp\Stream\StreamInterface; - -/** - * Creates curl resources from a request - */ -class CurlFactory -{ - /** - * Creates a cURL handle, header resource, and body resource based on a - * transaction. - * - * @param array $request Request hash - * @param null|resource $handle Optionally provide a curl handle to modify - * - * @return array Returns an array of the curl handle, headers array, and - * response body handle. - * @throws \RuntimeException when an option cannot be applied - */ - public function __invoke(array $request, $handle = null) - { - $headers = []; - $options = $this->getDefaultOptions($request, $headers); - $this->applyMethod($request, $options); - - if (isset($request['client'])) { - $this->applyHandlerOptions($request, $options); - } - - $this->applyHeaders($request, $options); - unset($options['_headers']); - - // Add handler options from the request's configuration options - if (isset($request['client']['curl'])) { - $options = $this->applyCustomCurlOptions( - $request['client']['curl'], - $options - ); - } - - if (!$handle) { - $handle = curl_init(); - } - - $body = $this->getOutputBody($request, $options); - curl_setopt_array($handle, $options); - - return [$handle, &$headers, $body]; - } - - /** - * Creates a response hash from a cURL result. - * - * @param callable $handler Handler that was used. - * @param array $request Request that sent. - * @param array $response Response hash to update. - * @param array $headers Headers received during transfer. - * @param resource $body Body fopen response. - * - * @return array - */ - public static function createResponse( - callable $handler, - array $request, - array $response, - array $headers, - $body - ) { - if (isset($response['transfer_stats']['url'])) { - $response['effective_url'] = $response['transfer_stats']['url']; - } - - if (!empty($headers)) { - $startLine = explode(' ', array_shift($headers), 3); - $headerList = Core::headersFromLines($headers); - $response['headers'] = $headerList; - $response['status'] = isset($startLine[1]) ? (int) $startLine[1] : null; - $response['reason'] = isset($startLine[2]) ? $startLine[2] : null; - $response['body'] = $body; - Core::rewindBody($response); - } - - return !empty($response['curl']['errno']) || !isset($response['status']) - ? self::createErrorResponse($handler, $request, $response) - : $response; - } - - private static function createErrorResponse( - callable $handler, - array $request, - array $response - ) { - static $connectionErrors = [ - CURLE_OPERATION_TIMEOUTED => true, - CURLE_COULDNT_RESOLVE_HOST => true, - CURLE_COULDNT_CONNECT => true, - CURLE_SSL_CONNECT_ERROR => true, - CURLE_GOT_NOTHING => true, - ]; - - // Retry when nothing is present or when curl failed to rewind. - if (!isset($response['err_message']) - && (empty($response['curl']['errno']) - || $response['curl']['errno'] == 65) - ) { - return self::retryFailedRewind($handler, $request, $response); - } - - $message = isset($response['err_message']) - ? $response['err_message'] - : sprintf('cURL error %s: %s', - $response['curl']['errno'], - isset($response['curl']['error']) - ? $response['curl']['error'] - : 'See http://curl.haxx.se/libcurl/c/libcurl-errors.html'); - - $error = isset($response['curl']['errno']) - && isset($connectionErrors[$response['curl']['errno']]) - ? new ConnectException($message) - : new RingException($message); - - return $response + [ - 'status' => null, - 'reason' => null, - 'body' => null, - 'headers' => [], - 'error' => $error, - ]; - } - - private function getOutputBody(array $request, array &$options) - { - // Determine where the body of the response (if any) will be streamed. - if (isset($options[CURLOPT_WRITEFUNCTION])) { - return $request['client']['save_to']; - } - - if (isset($options[CURLOPT_FILE])) { - return $options[CURLOPT_FILE]; - } - - if ($request['http_method'] != 'HEAD') { - // Create a default body if one was not provided - return $options[CURLOPT_FILE] = fopen('php://temp', 'w+'); - } - - return null; - } - - private function getDefaultOptions(array $request, array &$headers) - { - $url = Core::url($request); - $startingResponse = false; - - $options = [ - '_headers' => $request['headers'], - CURLOPT_CUSTOMREQUEST => $request['http_method'], - CURLOPT_URL => $url, - CURLOPT_RETURNTRANSFER => false, - CURLOPT_HEADER => false, - CURLOPT_CONNECTTIMEOUT => 150, - CURLOPT_HEADERFUNCTION => function ($ch, $h) use (&$headers, &$startingResponse) { - $value = trim($h); - if ($value === '') { - $startingResponse = true; - } elseif ($startingResponse) { - $startingResponse = false; - $headers = [$value]; - } else { - $headers[] = $value; - } - return strlen($h); - }, - ]; - - if (isset($request['version'])) { - $options[CURLOPT_HTTP_VERSION] = $request['version'] == 1.1 ? CURL_HTTP_VERSION_1_1 : CURL_HTTP_VERSION_1_0; - } - - if (defined('CURLOPT_PROTOCOLS')) { - $options[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; - } - - return $options; - } - - private function applyMethod(array $request, array &$options) - { - if (isset($request['body'])) { - $this->applyBody($request, $options); - return; - } - - switch ($request['http_method']) { - case 'PUT': - case 'POST': - // See http://tools.ietf.org/html/rfc7230#section-3.3.2 - if (!Core::hasHeader($request, 'Content-Length')) { - $options[CURLOPT_HTTPHEADER][] = 'Content-Length: 0'; - } - break; - case 'HEAD': - $options[CURLOPT_NOBODY] = true; - unset( - $options[CURLOPT_WRITEFUNCTION], - $options[CURLOPT_READFUNCTION], - $options[CURLOPT_FILE], - $options[CURLOPT_INFILE] - ); - } - } - - private function applyBody(array $request, array &$options) - { - $contentLength = Core::firstHeader($request, 'Content-Length'); - $size = $contentLength !== null ? (int) $contentLength : null; - - // Send the body as a string if the size is less than 1MB OR if the - // [client][curl][body_as_string] request value is set. - if (($size !== null && $size < 1000000) || - isset($request['client']['curl']['body_as_string']) || - is_string($request['body']) - ) { - $options[CURLOPT_POSTFIELDS] = Core::body($request); - // Don't duplicate the Content-Length header - $this->removeHeader('Content-Length', $options); - $this->removeHeader('Transfer-Encoding', $options); - } else { - $options[CURLOPT_UPLOAD] = true; - if ($size !== null) { - // Let cURL handle setting the Content-Length header - $options[CURLOPT_INFILESIZE] = $size; - $this->removeHeader('Content-Length', $options); - } - $this->addStreamingBody($request, $options); - } - - // If the Expect header is not present, prevent curl from adding it - if (!Core::hasHeader($request, 'Expect')) { - $options[CURLOPT_HTTPHEADER][] = 'Expect:'; - } - - // cURL sometimes adds a content-type by default. Prevent this. - if (!Core::hasHeader($request, 'Content-Type')) { - $options[CURLOPT_HTTPHEADER][] = 'Content-Type:'; - } - } - - private function addStreamingBody(array $request, array &$options) - { - $body = $request['body']; - - if ($body instanceof StreamInterface) { - $options[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body) { - return (string) $body->read($length); - }; - if (!isset($options[CURLOPT_INFILESIZE])) { - if ($size = $body->getSize()) { - $options[CURLOPT_INFILESIZE] = $size; - } - } - } elseif (is_resource($body)) { - $options[CURLOPT_INFILE] = $body; - } elseif ($body instanceof \Iterator) { - $buf = ''; - $options[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body, &$buf) { - if ($body->valid()) { - $buf .= $body->current(); - $body->next(); - } - $result = (string) substr($buf, 0, $length); - $buf = substr($buf, $length); - return $result; - }; - } else { - throw new \InvalidArgumentException('Invalid request body provided'); - } - } - - private function applyHeaders(array $request, array &$options) - { - foreach ($options['_headers'] as $name => $values) { - foreach ($values as $value) { - $options[CURLOPT_HTTPHEADER][] = "$name: $value"; - } - } - - // Remove the Accept header if one was not set - if (!Core::hasHeader($request, 'Accept')) { - $options[CURLOPT_HTTPHEADER][] = 'Accept:'; - } - } - - /** - * Takes an array of curl options specified in the 'curl' option of a - * request's configuration array and maps them to CURLOPT_* options. - * - * This method is only called when a request has a 'curl' config setting. - * - * @param array $config Configuration array of custom curl option - * @param array $options Array of existing curl options - * - * @return array Returns a new array of curl options - */ - private function applyCustomCurlOptions(array $config, array $options) - { - $curlOptions = []; - foreach ($config as $key => $value) { - if (is_int($key)) { - $curlOptions[$key] = $value; - } - } - - return $curlOptions + $options; - } - - /** - * Remove a header from the options array. - * - * @param string $name Case-insensitive header to remove - * @param array $options Array of options to modify - */ - private function removeHeader($name, array &$options) - { - foreach (array_keys($options['_headers']) as $key) { - if (!strcasecmp($key, $name)) { - unset($options['_headers'][$key]); - return; - } - } - } - - /** - * Applies an array of request client options to a the options array. - * - * This method uses a large switch rather than double-dispatch to save on - * high overhead of calling functions in PHP. - */ - private function applyHandlerOptions(array $request, array &$options) - { - foreach ($request['client'] as $key => $value) { - switch ($key) { - // Violating PSR-4 to provide more room. - case 'verify': - - if ($value === false) { - unset($options[CURLOPT_CAINFO]); - $options[CURLOPT_SSL_VERIFYHOST] = 0; - $options[CURLOPT_SSL_VERIFYPEER] = false; - continue; - } - - $options[CURLOPT_SSL_VERIFYHOST] = 2; - $options[CURLOPT_SSL_VERIFYPEER] = true; - - if (is_string($value)) { - $options[CURLOPT_CAINFO] = $value; - if (!file_exists($value)) { - throw new \InvalidArgumentException( - "SSL CA bundle not found: $value" - ); - } - } - break; - - case 'decode_content': - - if ($value === false) { - continue; - } - - $accept = Core::firstHeader($request, 'Accept-Encoding'); - if ($accept) { - $options[CURLOPT_ENCODING] = $accept; - } else { - $options[CURLOPT_ENCODING] = ''; - // Don't let curl send the header over the wire - $options[CURLOPT_HTTPHEADER][] = 'Accept-Encoding:'; - } - break; - - case 'save_to': - - if (is_string($value)) { - $value = new LazyOpenStream($value, 'w+'); - } - - if ($value instanceof StreamInterface) { - $options[CURLOPT_WRITEFUNCTION] = - function ($ch, $write) use ($value) { - return $value->write($write); - }; - } elseif (is_resource($value)) { - $options[CURLOPT_FILE] = $value; - } else { - throw new \InvalidArgumentException('save_to must be a ' - . 'GuzzleHttp\Stream\StreamInterface or resource'); - } - break; - - case 'timeout': - - if (defined('CURLOPT_TIMEOUT_MS')) { - $options[CURLOPT_TIMEOUT_MS] = $value * 1000; - } else { - $options[CURLOPT_TIMEOUT] = $value; - } - break; - - case 'connect_timeout': - - if (defined('CURLOPT_CONNECTTIMEOUT_MS')) { - $options[CURLOPT_CONNECTTIMEOUT_MS] = $value * 1000; - } else { - $options[CURLOPT_CONNECTTIMEOUT] = $value; - } - break; - - case 'proxy': - - if (!is_array($value)) { - $options[CURLOPT_PROXY] = $value; - } elseif (isset($request['scheme'])) { - $scheme = $request['scheme']; - if (isset($value[$scheme])) { - $options[CURLOPT_PROXY] = $value[$scheme]; - } - } - break; - - case 'cert': - - if (is_array($value)) { - $options[CURLOPT_SSLCERTPASSWD] = $value[1]; - $value = $value[0]; - } - - if (!file_exists($value)) { - throw new \InvalidArgumentException( - "SSL certificate not found: {$value}" - ); - } - - $options[CURLOPT_SSLCERT] = $value; - break; - - case 'ssl_key': - - if (is_array($value)) { - $options[CURLOPT_SSLKEYPASSWD] = $value[1]; - $value = $value[0]; - } - - if (!file_exists($value)) { - throw new \InvalidArgumentException( - "SSL private key not found: {$value}" - ); - } - - $options[CURLOPT_SSLKEY] = $value; - break; - - case 'progress': - - if (!is_callable($value)) { - throw new \InvalidArgumentException( - 'progress client option must be callable' - ); - } - - $options[CURLOPT_NOPROGRESS] = false; - $options[CURLOPT_PROGRESSFUNCTION] = - function () use ($value) { - $args = func_get_args(); - // PHP 5.5 pushed the handle onto the start of the args - if (is_resource($args[0])) { - array_shift($args); - } - call_user_func_array($value, $args); - }; - break; - - case 'debug': - - if ($value) { - $options[CURLOPT_STDERR] = Core::getDebugResource($value); - $options[CURLOPT_VERBOSE] = true; - } - break; - } - } - } - - /** - * This function ensures that a response was set on a transaction. If one - * was not set, then the request is retried if possible. This error - * typically means you are sending a payload, curl encountered a - * "Connection died, retrying a fresh connect" error, tried to rewind the - * stream, and then encountered a "necessary data rewind wasn't possible" - * error, causing the request to be sent through curl_multi_info_read() - * without an error status. - */ - private static function retryFailedRewind( - callable $handler, - array $request, - array $response - ) { - // If there is no body, then there is some other kind of issue. This - // is weird and should probably never happen. - if (!isset($request['body'])) { - $response['err_message'] = 'No response was received for a request ' - . 'with no body. This could mean that you are saturating your ' - . 'network.'; - return self::createErrorResponse($handler, $request, $response); - } - - if (!Core::rewindBody($request)) { - $response['err_message'] = 'The connection unexpectedly failed ' - . 'without providing an error. The request would have been ' - . 'retried, but attempting to rewind the request body failed.'; - return self::createErrorResponse($handler, $request, $response); - } - - // Retry no more than 3 times before giving up. - if (!isset($request['curl']['retries'])) { - $request['curl']['retries'] = 1; - } elseif ($request['curl']['retries'] == 2) { - $response['err_message'] = 'The cURL request was retried 3 times ' - . 'and did no succeed. cURL was unable to rewind the body of ' - . 'the request and subsequent retries resulted in the same ' - . 'error. Turn on the debug option to see what went wrong. ' - . 'See https://bugs.php.net/bug.php?id=47204 for more information.'; - return self::createErrorResponse($handler, $request, $response); - } else { - $request['curl']['retries']++; - } - - return $handler($request); - } -} diff --git a/core/vendor/guzzlehttp/ringphp/src/Client/CurlHandler.php b/core/vendor/guzzlehttp/ringphp/src/Client/CurlHandler.php deleted file mode 100644 index d8215c5cf4cbe1a1506b367b6148c2774c2100e2..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/src/Client/CurlHandler.php +++ /dev/null @@ -1,118 +0,0 @@ -<?php -namespace GuzzleHttp\Ring\Client; - -use GuzzleHttp\Ring\Future\CompletedFutureArray; -use GuzzleHttp\Ring\Core; - -/** - * HTTP handler that uses cURL easy handles as a transport layer. - * - * Requires PHP 5.5+ - * - * When using the CurlHandler, custom curl options can be specified as an - * associative array of curl option constants mapping to values in the - * **curl** key of the "client" key of the request. - */ -class CurlHandler -{ - /** @var callable */ - private $factory; - - /** @var array Array of curl easy handles */ - private $handles = []; - - /** @var array Array of owned curl easy handles */ - private $ownedHandles = []; - - /** @var int Total number of idle handles to keep in cache */ - private $maxHandles; - - /** - * Accepts an associative array of options: - * - * - factory: Optional callable factory used to create cURL handles. - * The callable is passed a request hash when invoked, and returns an - * array of the curl handle, headers resource, and body resource. - * - max_handles: Maximum number of idle handles (defaults to 5). - * - * @param array $options Array of options to use with the handler - */ - public function __construct(array $options = []) - { - $this->handles = $this->ownedHandles = []; - $this->factory = isset($options['handle_factory']) - ? $options['handle_factory'] - : new CurlFactory(); - $this->maxHandles = isset($options['max_handles']) - ? $options['max_handles'] - : 5; - } - - public function __destruct() - { - foreach ($this->handles as $handle) { - if (is_resource($handle)) { - curl_close($handle); - } - } - } - - public function __invoke(array $request) - { - $factory = $this->factory; - - // Ensure headers are by reference. They're updated elsewhere. - $result = $factory($request, $this->checkoutEasyHandle()); - $h = $result[0]; - $hd =& $result[1]; - $bd = $result[2]; - Core::doSleep($request); - curl_exec($h); - $response = ['transfer_stats' => curl_getinfo($h)]; - $response['curl']['error'] = curl_error($h); - $response['curl']['errno'] = curl_errno($h); - $response['transfer_stats'] = array_merge($response['transfer_stats'], $response['curl']); - $this->releaseEasyHandle($h); - - return new CompletedFutureArray( - CurlFactory::createResponse($this, $request, $response, $hd, $bd) - ); - } - - private function checkoutEasyHandle() - { - // Find an unused handle in the cache - if (false !== ($key = array_search(false, $this->ownedHandles, true))) { - $this->ownedHandles[$key] = true; - return $this->handles[$key]; - } - - // Add a new handle - $handle = curl_init(); - $id = (int) $handle; - $this->handles[$id] = $handle; - $this->ownedHandles[$id] = true; - - return $handle; - } - - private function releaseEasyHandle($handle) - { - $id = (int) $handle; - if (count($this->ownedHandles) > $this->maxHandles) { - curl_close($this->handles[$id]); - unset($this->handles[$id], $this->ownedHandles[$id]); - } else { - // curl_reset doesn't clear these out for some reason - static $unsetValues = [ - CURLOPT_HEADERFUNCTION => null, - CURLOPT_WRITEFUNCTION => null, - CURLOPT_READFUNCTION => null, - CURLOPT_PROGRESSFUNCTION => null, - ]; - curl_setopt_array($handle, $unsetValues); - curl_reset($handle); - $this->ownedHandles[$id] = false; - } - } -} diff --git a/core/vendor/guzzlehttp/ringphp/src/Client/CurlMultiHandler.php b/core/vendor/guzzlehttp/ringphp/src/Client/CurlMultiHandler.php deleted file mode 100644 index b45f6c3979a9db53e2aeb153ed3df1388c74b676..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/src/Client/CurlMultiHandler.php +++ /dev/null @@ -1,250 +0,0 @@ -<?php -namespace GuzzleHttp\Ring\Client; - -use GuzzleHttp\Ring\Future\FutureArray; -use React\Promise\Deferred; - -/** - * Returns an asynchronous response using curl_multi_* functions. - * - * This handler supports future responses and the "delay" request client - * option that can be used to delay before sending a request. - * - * When using the CurlMultiHandler, custom curl options can be specified as an - * associative array of curl option constants mapping to values in the - * **curl** key of the "client" key of the request. - * - * @property resource $_mh Internal use only. Lazy loaded multi-handle. - */ -class CurlMultiHandler -{ - /** @var callable */ - private $factory; - private $selectTimeout; - private $active; - private $handles = []; - private $delays = []; - private $maxHandles; - - /** - * This handler accepts the following options: - * - * - mh: An optional curl_multi resource - * - handle_factory: An optional callable used to generate curl handle - * resources. the callable accepts a request hash and returns an array - * of the handle, headers file resource, and the body resource. - * - select_timeout: Optional timeout (in seconds) to block before timing - * out while selecting curl handles. Defaults to 1 second. - * - max_handles: Optional integer representing the maximum number of - * open requests. When this number is reached, the queued futures are - * flushed. - * - * @param array $options - */ - public function __construct(array $options = []) - { - if (isset($options['mh'])) { - $this->_mh = $options['mh']; - } - $this->factory = isset($options['handle_factory']) - ? $options['handle_factory'] : new CurlFactory(); - $this->selectTimeout = isset($options['select_timeout']) - ? $options['select_timeout'] : 1; - $this->maxHandles = isset($options['max_handles']) - ? $options['max_handles'] : 100; - } - - public function __get($name) - { - if ($name === '_mh') { - return $this->_mh = curl_multi_init(); - } - - throw new \BadMethodCallException(); - } - - public function __destruct() - { - // Finish any open connections before terminating the script. - if ($this->handles) { - $this->execute(); - } - - if (isset($this->_mh)) { - curl_multi_close($this->_mh); - unset($this->_mh); - } - } - - public function __invoke(array $request) - { - $factory = $this->factory; - $result = $factory($request); - $entry = [ - 'request' => $request, - 'response' => [], - 'handle' => $result[0], - 'headers' => &$result[1], - 'body' => $result[2], - 'deferred' => new Deferred(), - ]; - - $id = (int) $result[0]; - - $future = new FutureArray( - $entry['deferred']->promise(), - [$this, 'execute'], - function () use ($id) { - return $this->cancel($id); - } - ); - - $this->addRequest($entry); - - // Transfer outstanding requests if there are too many open handles. - if (count($this->handles) >= $this->maxHandles) { - $this->execute(); - } - - return $future; - } - - /** - * Runs until all outstanding connections have completed. - */ - public function execute() - { - do { - - if ($this->active && - curl_multi_select($this->_mh, $this->selectTimeout) === -1 - ) { - // Perform a usleep if a select returns -1. - // See: https://bugs.php.net/bug.php?id=61141 - usleep(250); - } - - // Add any delayed futures if needed. - if ($this->delays) { - $this->addDelays(); - } - - do { - $mrc = curl_multi_exec($this->_mh, $this->active); - } while ($mrc === CURLM_CALL_MULTI_PERFORM); - - $this->processMessages(); - - // If there are delays but no transfers, then sleep for a bit. - if (!$this->active && $this->delays) { - usleep(500); - } - - } while ($this->active || $this->handles); - } - - private function addRequest(array &$entry) - { - $id = (int) $entry['handle']; - $this->handles[$id] = $entry; - - // If the request is a delay, then add the reques to the curl multi - // pool only after the specified delay. - if (isset($entry['request']['client']['delay'])) { - $this->delays[$id] = microtime(true) + ($entry['request']['client']['delay'] / 1000); - } elseif (empty($entry['request']['future'])) { - curl_multi_add_handle($this->_mh, $entry['handle']); - } else { - curl_multi_add_handle($this->_mh, $entry['handle']); - // "lazy" futures are only sent once the pool has many requests. - if ($entry['request']['future'] !== 'lazy') { - do { - $mrc = curl_multi_exec($this->_mh, $this->active); - } while ($mrc === CURLM_CALL_MULTI_PERFORM); - $this->processMessages(); - } - } - } - - private function removeProcessed($id) - { - if (isset($this->handles[$id])) { - curl_multi_remove_handle( - $this->_mh, - $this->handles[$id]['handle'] - ); - curl_close($this->handles[$id]['handle']); - unset($this->handles[$id], $this->delays[$id]); - } - } - - /** - * Cancels a handle from sending and removes references to it. - * - * @param int $id Handle ID to cancel and remove. - * - * @return bool True on success, false on failure. - */ - private function cancel($id) - { - // Cannot cancel if it has been processed. - if (!isset($this->handles[$id])) { - return false; - } - - $handle = $this->handles[$id]['handle']; - unset($this->delays[$id], $this->handles[$id]); - curl_multi_remove_handle($this->_mh, $handle); - curl_close($handle); - - return true; - } - - private function addDelays() - { - $currentTime = microtime(true); - - foreach ($this->delays as $id => $delay) { - if ($currentTime >= $delay) { - unset($this->delays[$id]); - curl_multi_add_handle( - $this->_mh, - $this->handles[$id]['handle'] - ); - } - } - } - - private function processMessages() - { - while ($done = curl_multi_info_read($this->_mh)) { - $id = (int) $done['handle']; - - if (!isset($this->handles[$id])) { - // Probably was cancelled. - continue; - } - - $entry = $this->handles[$id]; - $entry['response']['transfer_stats'] = curl_getinfo($done['handle']); - - if ($done['result'] !== CURLM_OK) { - $entry['response']['curl']['errno'] = $done['result']; - if (function_exists('curl_strerror')) { - $entry['response']['curl']['error'] = curl_strerror($done['result']); - } - } - - $result = CurlFactory::createResponse( - $this, - $entry['request'], - $entry['response'], - $entry['headers'], - $entry['body'] - ); - - $this->removeProcessed($id); - $entry['deferred']->resolve($result); - } - } -} diff --git a/core/vendor/guzzlehttp/ringphp/src/Client/Middleware.php b/core/vendor/guzzlehttp/ringphp/src/Client/Middleware.php deleted file mode 100644 index 6fa7318abae26d673d91626243f9ff0e43c443b7..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/src/Client/Middleware.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php -namespace GuzzleHttp\Ring\Client; - -/** - * Provides basic middleware wrappers. - * - * If a middleware is more complex than a few lines of code, then it should - * be implemented in a class rather than a static method. - */ -class Middleware -{ - /** - * Sends future requests to a future compatible handler while sending all - * other requests to a default handler. - * - * When the "future" option is not provided on a request, any future responses - * are automatically converted to synchronous responses and block. - * - * @param callable $default Handler used for non-streaming responses - * @param callable $future Handler used for future responses - * - * @return callable Returns the composed handler. - */ - public static function wrapFuture( - callable $default, - callable $future - ) { - return function (array $request) use ($default, $future) { - return empty($request['client']['future']) - ? $default($request) - : $future($request); - }; - } - - /** - * Sends streaming requests to a streaming compatible handler while sendin - * all other requests to a default handler. - * - * This, for example, could be useful for taking advantage of the - * performance benefits of curl while still supporting true streaming - * through the StreamHandler. - * - * @param callable $default Handler used for non-streaming responses - * @param callable $streaming Handler used for streaming responses - * - * @return callable Returns the composed handler. - */ - public static function wrapStreaming( - callable $default, - callable $streaming - ) { - return function (array $request) use ($default, $streaming) { - return empty($request['client']['stream']) - ? $default($request) - : $streaming($request); - }; - } -} diff --git a/core/vendor/guzzlehttp/ringphp/src/Client/MockHandler.php b/core/vendor/guzzlehttp/ringphp/src/Client/MockHandler.php deleted file mode 100644 index 56cc32da77f4b7fca6dc433709355b3d112aa38e..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/src/Client/MockHandler.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php -namespace GuzzleHttp\Ring\Client; - -use GuzzleHttp\Ring\Core; -use GuzzleHttp\Ring\Future\CompletedFutureArray; -use GuzzleHttp\Ring\Future\FutureArrayInterface; - -/** - * Ring handler that returns a canned response or evaluated function result. - */ -class MockHandler -{ - /** @var callable|array|FutureArrayInterface */ - private $result; - - /** - * Provide an array or future to always return the same value. Provide a - * callable that accepts a request object and returns an array or future - * to dynamically create a response. - * - * @param array|FutureArrayInterface|callable $result Mock return value. - */ - public function __construct($result) - { - $this->result = $result; - } - - public function __invoke(array $request) - { - Core::doSleep($request); - $response = is_callable($this->result) - ? call_user_func($this->result, $request) - : $this->result; - - if (is_array($response)) { - $response = new CompletedFutureArray($response + [ - 'status' => null, - 'body' => null, - 'headers' => [], - 'reason' => null, - 'effective_url' => null, - ]); - } elseif (!$response instanceof FutureArrayInterface) { - throw new \InvalidArgumentException( - 'Response must be an array or FutureArrayInterface. Found ' - . Core::describeType($request) - ); - } - - return $response; - } -} diff --git a/core/vendor/guzzlehttp/ringphp/src/Client/StreamHandler.php b/core/vendor/guzzlehttp/ringphp/src/Client/StreamHandler.php deleted file mode 100644 index 855aad7f647e2868e0984aea7049867fe54b5c79..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/src/Client/StreamHandler.php +++ /dev/null @@ -1,412 +0,0 @@ -<?php -namespace GuzzleHttp\Ring\Client; - -use GuzzleHttp\Ring\Core; -use GuzzleHttp\Ring\Exception\ConnectException; -use GuzzleHttp\Ring\Exception\RingException; -use GuzzleHttp\Ring\Future\CompletedFutureArray; -use GuzzleHttp\Stream\InflateStream; -use GuzzleHttp\Stream\StreamInterface; -use GuzzleHttp\Stream\Stream; -use GuzzleHttp\Stream\Utils; - -/** - * RingPHP client handler that uses PHP's HTTP stream wrapper. - */ -class StreamHandler -{ - private $options; - private $lastHeaders; - - public function __construct(array $options = []) - { - $this->options = $options; - } - - public function __invoke(array $request) - { - $url = Core::url($request); - Core::doSleep($request); - - try { - // Does not support the expect header. - $request = Core::removeHeader($request, 'Expect'); - $stream = $this->createStream($url, $request); - return $this->createResponse($request, $url, $stream); - } catch (RingException $e) { - return $this->createErrorResponse($url, $e); - } - } - - private function createResponse(array $request, $url, $stream) - { - $hdrs = $this->lastHeaders; - $this->lastHeaders = null; - $parts = explode(' ', array_shift($hdrs), 3); - $response = [ - 'status' => $parts[1], - 'reason' => isset($parts[2]) ? $parts[2] : null, - 'headers' => Core::headersFromLines($hdrs), - 'effective_url' => $url, - ]; - - $stream = $this->checkDecode($request, $response, $stream); - - // If not streaming, then drain the response into a stream. - if (empty($request['client']['stream'])) { - $dest = isset($request['client']['save_to']) - ? $request['client']['save_to'] - : fopen('php://temp', 'r+'); - $stream = $this->drain($stream, $dest); - } - - $response['body'] = $stream; - - return new CompletedFutureArray($response); - } - - private function checkDecode(array $request, array $response, $stream) - { - // Automatically decode responses when instructed. - if (!empty($request['client']['decode_content'])) { - switch (Core::firstHeader($response, 'Content-Encoding', true)) { - case 'gzip': - case 'deflate': - $stream = new InflateStream(Stream::factory($stream)); - break; - } - } - - return $stream; - } - - /** - * Drains the stream into the "save_to" client option. - * - * @param resource $stream - * @param string|resource|StreamInterface $dest - * - * @return Stream - * @throws \RuntimeException when the save_to option is invalid. - */ - private function drain($stream, $dest) - { - if (is_resource($stream)) { - if (!is_resource($dest)) { - $stream = Stream::factory($stream); - } else { - stream_copy_to_stream($stream, $dest); - fclose($stream); - rewind($dest); - return $dest; - } - } - - // Stream the response into the destination stream - $dest = is_string($dest) - ? new Stream(Utils::open($dest, 'r+')) - : Stream::factory($dest); - - Utils::copyToStream($stream, $dest); - $dest->seek(0); - $stream->close(); - - return $dest; - } - - /** - * Creates an error response for the given stream. - * - * @param string $url - * @param RingException $e - * - * @return array - */ - private function createErrorResponse($url, RingException $e) - { - // Determine if the error was a networking error. - $message = $e->getMessage(); - - // This list can probably get more comprehensive. - if (strpos($message, 'getaddrinfo') // DNS lookup failed - || strpos($message, 'Connection refused') - ) { - $e = new ConnectException($e->getMessage(), 0, $e); - } - - return new CompletedFutureArray([ - 'status' => null, - 'body' => null, - 'headers' => [], - 'effective_url' => $url, - 'error' => $e - ]); - } - - /** - * Create a resource and check to ensure it was created successfully - * - * @param callable $callback Callable that returns stream resource - * - * @return resource - * @throws \RuntimeException on error - */ - private function createResource(callable $callback) - { - $errors = null; - set_error_handler(function ($_, $msg, $file, $line) use (&$errors) { - $errors[] = [ - 'message' => $msg, - 'file' => $file, - 'line' => $line - ]; - return true; - }); - - $resource = $callback(); - restore_error_handler(); - - if (!$resource) { - $message = 'Error creating resource: '; - foreach ($errors as $err) { - foreach ($err as $key => $value) { - $message .= "[$key] $value" . PHP_EOL; - } - } - throw new RingException(trim($message)); - } - - return $resource; - } - - private function createStream($url, array $request) - { - static $methods; - if (!$methods) { - $methods = array_flip(get_class_methods(__CLASS__)); - } - - // HTTP/1.1 streams using the PHP stream wrapper require a - // Connection: close header - if ((!isset($request['version']) || $request['version'] == '1.1') - && !Core::hasHeader($request, 'Connection') - ) { - $request['headers']['Connection'] = ['close']; - } - - // Ensure SSL is verified by default - if (!isset($request['client']['verify'])) { - $request['client']['verify'] = true; - } - - $params = []; - $options = $this->getDefaultOptions($request); - - if (isset($request['client'])) { - foreach ($request['client'] as $key => $value) { - $method = "add_{$key}"; - if (isset($methods[$method])) { - $this->{$method}($request, $options, $value, $params); - } - } - } - - return $this->createStreamResource( - $url, - $request, - $options, - $this->createContext($request, $options, $params) - ); - } - - private function getDefaultOptions(array $request) - { - $headers = ""; - foreach ($request['headers'] as $name => $value) { - foreach ((array) $value as $val) { - $headers .= "$name: $val\r\n"; - } - } - - $context = [ - 'http' => [ - 'method' => $request['http_method'], - 'header' => $headers, - 'protocol_version' => isset($request['version']) ? $request['version'] : 1.1, - 'ignore_errors' => true, - 'follow_location' => 0, - ], - ]; - - $body = Core::body($request); - if (isset($body)) { - $context['http']['content'] = $body; - // Prevent the HTTP handler from adding a Content-Type header. - if (!Core::hasHeader($request, 'Content-Type')) { - $context['http']['header'] .= "Content-Type:\r\n"; - } - } - - $context['http']['header'] = rtrim($context['http']['header']); - - return $context; - } - - private function add_proxy(array $request, &$options, $value, &$params) - { - if (!is_array($value)) { - $options['http']['proxy'] = $value; - } else { - $scheme = isset($request['scheme']) ? $request['scheme'] : 'http'; - if (isset($value[$scheme])) { - $options['http']['proxy'] = $value[$scheme]; - } - } - } - - private function add_timeout(array $request, &$options, $value, &$params) - { - $options['http']['timeout'] = $value; - } - - private function add_verify(array $request, &$options, $value, &$params) - { - if ($value === true) { - // PHP 5.6 or greater will find the system cert by default. When - // < 5.6, use the Guzzle bundled cacert. - if (PHP_VERSION_ID < 50600) { - $options['ssl']['cafile'] = ClientUtils::getDefaultCaBundle(); - } - } elseif (is_string($value)) { - $options['ssl']['cafile'] = $value; - if (!file_exists($value)) { - throw new RingException("SSL CA bundle not found: $value"); - } - } elseif ($value === false) { - $options['ssl']['verify_peer'] = false; - return; - } else { - throw new RingException('Invalid verify request option'); - } - - $options['ssl']['verify_peer'] = true; - $options['ssl']['allow_self_signed'] = true; - } - - private function add_cert(array $request, &$options, $value, &$params) - { - if (is_array($value)) { - $options['ssl']['passphrase'] = $value[1]; - $value = $value[0]; - } - - if (!file_exists($value)) { - throw new RingException("SSL certificate not found: {$value}"); - } - - $options['ssl']['local_cert'] = $value; - } - - private function add_progress(array $request, &$options, $value, &$params) - { - $fn = function ($code, $_1, $_2, $_3, $transferred, $total) use ($value) { - if ($code == STREAM_NOTIFY_PROGRESS) { - $value($total, $transferred, null, null); - } - }; - - // Wrap the existing function if needed. - $params['notification'] = isset($params['notification']) - ? Core::callArray([$params['notification'], $fn]) - : $fn; - } - - private function add_debug(array $request, &$options, $value, &$params) - { - if ($value === false) { - return; - } - - static $map = [ - STREAM_NOTIFY_CONNECT => 'CONNECT', - STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED', - STREAM_NOTIFY_AUTH_RESULT => 'AUTH_RESULT', - STREAM_NOTIFY_MIME_TYPE_IS => 'MIME_TYPE_IS', - STREAM_NOTIFY_FILE_SIZE_IS => 'FILE_SIZE_IS', - STREAM_NOTIFY_REDIRECTED => 'REDIRECTED', - STREAM_NOTIFY_PROGRESS => 'PROGRESS', - STREAM_NOTIFY_FAILURE => 'FAILURE', - STREAM_NOTIFY_COMPLETED => 'COMPLETED', - STREAM_NOTIFY_RESOLVE => 'RESOLVE', - ]; - - static $args = ['severity', 'message', 'message_code', - 'bytes_transferred', 'bytes_max']; - - $value = Core::getDebugResource($value); - $ident = $request['http_method'] . ' ' . Core::url($request); - $fn = function () use ($ident, $value, $map, $args) { - $passed = func_get_args(); - $code = array_shift($passed); - fprintf($value, '<%s> [%s] ', $ident, $map[$code]); - foreach (array_filter($passed) as $i => $v) { - fwrite($value, $args[$i] . ': "' . $v . '" '); - } - fwrite($value, "\n"); - }; - - // Wrap the existing function if needed. - $params['notification'] = isset($params['notification']) - ? Core::callArray([$params['notification'], $fn]) - : $fn; - } - - private function applyCustomOptions(array $request, array &$options) - { - if (!isset($request['client']['stream_context'])) { - return; - } - - if (!is_array($request['client']['stream_context'])) { - throw new RingException('stream_context must be an array'); - } - - $options = array_replace_recursive( - $options, - $request['client']['stream_context'] - ); - } - - private function createContext(array $request, array $options, array $params) - { - $this->applyCustomOptions($request, $options); - return $this->createResource( - function () use ($request, $options, $params) { - return stream_context_create($options, $params); - }, - $request, - $options - ); - } - - private function createStreamResource( - $url, - array $request, - array $options, - $context - ) { - return $this->createResource( - function () use ($url, $context) { - if (false === strpos($url, 'http')) { - trigger_error("URL is invalid: {$url}", E_USER_WARNING); - return null; - } - $resource = fopen($url, 'r', null, $context); - $this->lastHeaders = $http_response_header; - return $resource; - }, - $request, - $options - ); - } -} diff --git a/core/vendor/guzzlehttp/ringphp/src/Core.php b/core/vendor/guzzlehttp/ringphp/src/Core.php deleted file mode 100644 index dd7d1a0c5a7f6ccab0e496e8774358aed785556f..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/src/Core.php +++ /dev/null @@ -1,364 +0,0 @@ -<?php -namespace GuzzleHttp\Ring; - -use GuzzleHttp\Stream\StreamInterface; -use GuzzleHttp\Ring\Future\FutureArrayInterface; -use GuzzleHttp\Ring\Future\FutureArray; - -/** - * Provides core functionality of Ring handlers and middleware. - */ -class Core -{ - /** - * Returns a function that calls all of the provided functions, in order, - * passing the arguments provided to the composed function to each function. - * - * @param callable[] $functions Array of functions to proxy to. - * - * @return callable - */ - public static function callArray(array $functions) - { - return function () use ($functions) { - $args = func_get_args(); - foreach ($functions as $fn) { - call_user_func_array($fn, $args); - } - }; - } - - /** - * Gets an array of header line values from a message for a specific header - * - * This method searches through the "headers" key of a message for a header - * using a case-insensitive search. - * - * @param array $message Request or response hash. - * @param string $header Header to retrieve - * - * @return array - */ - public static function headerLines($message, $header) - { - $result = []; - - if (!empty($message['headers'])) { - foreach ($message['headers'] as $name => $value) { - if (!strcasecmp($name, $header)) { - $result = array_merge($result, $value); - } - } - } - - return $result; - } - - /** - * Gets a header value from a message as a string or null - * - * This method searches through the "headers" key of a message for a header - * using a case-insensitive search. The lines of the header are imploded - * using commas into a single string return value. - * - * @param array $message Request or response hash. - * @param string $header Header to retrieve - * - * @return string|null Returns the header string if found, or null if not. - */ - public static function header($message, $header) - { - $match = self::headerLines($message, $header); - return $match ? implode(', ', $match) : null; - } - - /** - * Returns the first header value from a message as a string or null. If - * a header line contains multiple values separated by a comma, then this - * function will return the first value in the list. - * - * @param array $message Request or response hash. - * @param string $header Header to retrieve - * - * @return string|null Returns the value as a string if found. - */ - public static function firstHeader($message, $header) - { - if (!empty($message['headers'])) { - foreach ($message['headers'] as $name => $value) { - if (!strcasecmp($name, $header)) { - // Return the match itself if it is a single value. - $pos = strpos($value[0], ','); - return $pos ? substr($value[0], 0, $pos) : $value[0]; - } - } - } - - return null; - } - - /** - * Returns true if a message has the provided case-insensitive header. - * - * @param array $message Request or response hash. - * @param string $header Header to check - * - * @return bool - */ - public static function hasHeader($message, $header) - { - if (!empty($message['headers'])) { - foreach ($message['headers'] as $name => $value) { - if (!strcasecmp($name, $header)) { - return true; - } - } - } - - return false; - } - - /** - * Parses an array of header lines into an associative array of headers. - * - * @param array $lines Header lines array of strings in the following - * format: "Name: Value" - * @return array - */ - public static function headersFromLines($lines) - { - $headers = []; - - foreach ($lines as $line) { - $parts = explode(':', $line, 2); - $headers[trim($parts[0])][] = isset($parts[1]) - ? trim($parts[1]) - : null; - } - - return $headers; - } - - /** - * Removes a header from a message using a case-insensitive comparison. - * - * @param array $message Message that contains 'headers' - * @param string $header Header to remove - * - * @return array - */ - public static function removeHeader(array $message, $header) - { - if (isset($message['headers'])) { - foreach (array_keys($message['headers']) as $key) { - if (!strcasecmp($header, $key)) { - unset($message['headers'][$key]); - } - } - } - - return $message; - } - - /** - * Replaces any existing case insensitive headers with the given value. - * - * @param array $message Message that contains 'headers' - * @param string $header Header to set. - * @param array $value Value to set. - * - * @return array - */ - public static function setHeader(array $message, $header, array $value) - { - $message = self::removeHeader($message, $header); - $message['headers'][$header] = $value; - - return $message; - } - - /** - * Creates a URL string from a request. - * - * If the "url" key is present on the request, it is returned, otherwise - * the url is built up based on the scheme, host, uri, and query_string - * request values. - * - * @param array $request Request to get the URL from - * - * @return string Returns the request URL as a string. - * @throws \InvalidArgumentException if no Host header is present. - */ - public static function url(array $request) - { - if (isset($request['url'])) { - return $request['url']; - } - - $uri = (isset($request['scheme']) - ? $request['scheme'] : 'http') . '://'; - - if ($host = self::header($request, 'host')) { - $uri .= $host; - } else { - throw new \InvalidArgumentException('No Host header was provided'); - } - - if (isset($request['uri'])) { - $uri .= $request['uri']; - } - - if (isset($request['query_string'])) { - $uri .= '?' . $request['query_string']; - } - - return $uri; - } - - /** - * Reads the body of a message into a string. - * - * @param array|FutureArrayInterface $message Array containing a "body" key - * - * @return null|string Returns the body as a string or null if not set. - * @throws \InvalidArgumentException if a request body is invalid. - */ - public static function body($message) - { - if (!isset($message['body'])) { - return null; - } - - if ($message['body'] instanceof StreamInterface) { - return (string) $message['body']; - } - - switch (gettype($message['body'])) { - case 'string': - return $message['body']; - case 'resource': - return stream_get_contents($message['body']); - case 'object': - if ($message['body'] instanceof \Iterator) { - return implode('', iterator_to_array($message['body'])); - } elseif (method_exists($message['body'], '__toString')) { - return (string) $message['body']; - } - default: - throw new \InvalidArgumentException('Invalid request body: ' - . self::describeType($message['body'])); - } - } - - /** - * Rewind the body of the provided message if possible. - * - * @param array $message Message that contains a 'body' field. - * - * @return bool Returns true on success, false on failure - */ - public static function rewindBody($message) - { - if ($message['body'] instanceof StreamInterface) { - return $message['body']->seek(0); - } - - if ($message['body'] instanceof \Generator) { - return false; - } - - if ($message['body'] instanceof \Iterator) { - $message['body']->rewind(); - return true; - } - - if (is_resource($message['body'])) { - return rewind($message['body']); - } - - return is_string($message['body']) - || (is_object($message['body']) - && method_exists($message['body'], '__toString')); - } - - /** - * Debug function used to describe the provided value type and class. - * - * @param mixed $input - * - * @return string Returns a string containing the type of the variable and - * if a class is provided, the class name. - */ - public static function describeType($input) - { - switch (gettype($input)) { - case 'object': - return 'object(' . get_class($input) . ')'; - case 'array': - return 'array(' . count($input) . ')'; - default: - ob_start(); - var_dump($input); - // normalize float vs double - return str_replace('double(', 'float(', rtrim(ob_get_clean())); - } - } - - /** - * Sleep for the specified amount of time specified in the request's - * ['client']['delay'] option if present. - * - * This function should only be used when a non-blocking sleep is not - * possible. - * - * @param array $request Request to sleep - */ - public static function doSleep(array $request) - { - if (isset($request['client']['delay'])) { - usleep($request['client']['delay'] * 1000); - } - } - - /** - * Returns a proxied future that modifies the dereferenced value of another - * future using a promise. - * - * @param FutureArrayInterface $future Future to wrap with a new future - * @param callable $onFulfilled Invoked when the future fulfilled - * @param callable $onRejected Invoked when the future rejected - * @param callable $onProgress Invoked when the future progresses - * - * @return FutureArray - */ - public static function proxy( - FutureArrayInterface $future, - callable $onFulfilled = null, - callable $onRejected = null, - callable $onProgress = null - ) { - return new FutureArray( - $future->then($onFulfilled, $onRejected, $onProgress), - [$future, 'wait'], - [$future, 'cancel'] - ); - } - - /** - * Returns a debug stream based on the provided variable. - * - * @param mixed $value Optional value - * - * @return resource - */ - public static function getDebugResource($value = null) - { - if (is_resource($value)) { - return $value; - } elseif (defined('STDOUT')) { - return STDOUT; - } else { - return fopen('php://output', 'w'); - } - } -} diff --git a/core/vendor/guzzlehttp/ringphp/src/Exception/CancelledException.php b/core/vendor/guzzlehttp/ringphp/src/Exception/CancelledException.php deleted file mode 100644 index 95b353acfde5f628daeb4ad3c50e48ebecc14a66..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/src/Exception/CancelledException.php +++ /dev/null @@ -1,7 +0,0 @@ -<?php -namespace GuzzleHttp\Ring\Exception; - -/** - * Marker interface for cancelled exceptions. - */ -interface CancelledException {} diff --git a/core/vendor/guzzlehttp/ringphp/src/Exception/CancelledFutureAccessException.php b/core/vendor/guzzlehttp/ringphp/src/Exception/CancelledFutureAccessException.php deleted file mode 100644 index 4a14574df886c8937beb046709ae62f33e41542e..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/src/Exception/CancelledFutureAccessException.php +++ /dev/null @@ -1,4 +0,0 @@ -<?php -namespace GuzzleHttp\Ring\Exception; - -class CancelledFutureAccessException extends RingException implements CancelledException {} diff --git a/core/vendor/guzzlehttp/ringphp/src/Exception/ConnectException.php b/core/vendor/guzzlehttp/ringphp/src/Exception/ConnectException.php deleted file mode 100644 index 925cd133c83936dda16351881afebafd03ba6ae3..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/src/Exception/ConnectException.php +++ /dev/null @@ -1,7 +0,0 @@ -<?php -namespace GuzzleHttp\Ring\Exception; - -/** - * Occurs when the connection failed. - */ -class ConnectException extends RingException {} diff --git a/core/vendor/guzzlehttp/ringphp/src/Exception/RingException.php b/core/vendor/guzzlehttp/ringphp/src/Exception/RingException.php deleted file mode 100644 index eed0daf7dd097bc3c5c7b517194fc08fbc2a1c50..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/src/Exception/RingException.php +++ /dev/null @@ -1,4 +0,0 @@ -<?php -namespace GuzzleHttp\Ring\Exception; - -class RingException extends \RuntimeException {}; diff --git a/core/vendor/guzzlehttp/ringphp/src/Future/BaseFutureTrait.php b/core/vendor/guzzlehttp/ringphp/src/Future/BaseFutureTrait.php deleted file mode 100644 index e6a7ca77c3d9f406006ff2bbbc7849aeb78fde48..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/src/Future/BaseFutureTrait.php +++ /dev/null @@ -1,125 +0,0 @@ -<?php -namespace GuzzleHttp\Ring\Future; - -use GuzzleHttp\Ring\Exception\CancelledFutureAccessException; -use GuzzleHttp\Ring\Exception\RingException; -use React\Promise\PromiseInterface; - -/** - * Implements common future functionality built on top of promises. - */ -trait BaseFutureTrait -{ - /** @var callable */ - private $waitfn; - - /** @var callable */ - private $cancelfn; - - /** @var PromiseInterface */ - private $wrappedPromise; - - /** @var \Exception Error encountered. */ - private $error; - - /** @var mixed Result of the future */ - private $result; - - private $isRealized = false; - - /** - * @param PromiseInterface $promise Promise to shadow with the future. - * @param callable $wait Function that blocks until the deferred - * computation has been resolved. This - * function MUST resolve the deferred value - * associated with the supplied promise. - * @param callable $cancel If possible and reasonable, provide a - * function that can be used to cancel the - * future from completing. - */ - public function __construct( - PromiseInterface $promise, - callable $wait = null, - callable $cancel = null - ) { - $this->wrappedPromise = $promise; - $this->waitfn = $wait; - $this->cancelfn = $cancel; - } - - public function wait() - { - if (!$this->isRealized) { - $this->addShadow(); - if (!$this->isRealized && $this->waitfn) { - $this->invokeWait(); - } - if (!$this->isRealized) { - $this->error = new RingException('Waiting did not resolve future'); - } - } - - if ($this->error) { - throw $this->error; - } - - return $this->result; - } - - public function promise() - { - return $this->wrappedPromise; - } - - public function then( - callable $onFulfilled = null, - callable $onRejected = null, - callable $onProgress = null - ) { - return $this->wrappedPromise->then($onFulfilled, $onRejected, $onProgress); - } - - public function cancel() - { - if (!$this->isRealized) { - $cancelfn = $this->cancelfn; - $this->waitfn = $this->cancelfn = null; - $this->isRealized = true; - $this->error = new CancelledFutureAccessException(); - if ($cancelfn) { - $cancelfn($this); - } - } - } - - private function addShadow() - { - // Get the result and error when the promise is resolved. Note that - // calling this function might trigger the resolution immediately. - $this->wrappedPromise->then( - function ($value) { - $this->isRealized = true; - $this->result = $value; - $this->waitfn = $this->cancelfn = null; - }, - function ($error) { - $this->isRealized = true; - $this->error = $error; - $this->waitfn = $this->cancelfn = null; - } - ); - } - - private function invokeWait() - { - try { - $wait = $this->waitfn; - $this->waitfn = null; - $wait(); - } catch (\Exception $e) { - // Defer can throw to reject. - $this->error = $e; - $this->isRealized = true; - } - } -} diff --git a/core/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureArray.php b/core/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureArray.php deleted file mode 100644 index 0a90c939f7b981155089dbc4e7fa437310ba5efd..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureArray.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php -namespace GuzzleHttp\Ring\Future; - -/** - * Represents a future array that has been completed successfully. - */ -class CompletedFutureArray extends CompletedFutureValue implements FutureArrayInterface -{ - public function __construct(array $result) - { - parent::__construct($result); - } - - public function offsetExists($offset) - { - return isset($this->result[$offset]); - } - - public function offsetGet($offset) - { - return $this->result[$offset]; - } - - public function offsetSet($offset, $value) - { - $this->result[$offset] = $value; - } - - public function offsetUnset($offset) - { - unset($this->result[$offset]); - } - - public function count() - { - return count($this->result); - } - - public function getIterator() - { - return new \ArrayIterator($this->result); - } -} diff --git a/core/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureValue.php b/core/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureValue.php deleted file mode 100644 index 0d25af72d4d8d37e119065a1da30b0d8132a5a91..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureValue.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php -namespace GuzzleHttp\Ring\Future; - -use React\Promise\FulfilledPromise; -use React\Promise\RejectedPromise; - -/** - * Represents a future value that has been resolved or rejected. - */ -class CompletedFutureValue implements FutureInterface -{ - protected $result; - protected $error; - - private $cachedPromise; - - /** - * @param mixed $result Resolved result - * @param \Exception $e Error. Pass a GuzzleHttp\Ring\Exception\CancelledFutureAccessException - * to mark the future as cancelled. - */ - public function __construct($result, \Exception $e = null) - { - $this->result = $result; - $this->error = $e; - } - - public function wait() - { - if ($this->error) { - throw $this->error; - } - - return $this->result; - } - - public function cancel() {} - - public function promise() - { - if (!$this->cachedPromise) { - $this->cachedPromise = $this->error - ? new RejectedPromise($this->error) - : new FulfilledPromise($this->result); - } - - return $this->cachedPromise; - } - - public function then( - callable $onFulfilled = null, - callable $onRejected = null, - callable $onProgress = null - ) { - return $this->promise()->then($onFulfilled, $onRejected, $onProgress); - } -} diff --git a/core/vendor/guzzlehttp/ringphp/src/Future/FutureArray.php b/core/vendor/guzzlehttp/ringphp/src/Future/FutureArray.php deleted file mode 100644 index 3d64c9643ac154e25c0fedfac241f54e2e9148c2..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/src/Future/FutureArray.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php -namespace GuzzleHttp\Ring\Future; - -/** - * Represents a future array value that when dereferenced returns an array. - */ -class FutureArray implements FutureArrayInterface -{ - use MagicFutureTrait; - - public function offsetExists($offset) - { - return isset($this->_value[$offset]); - } - - public function offsetGet($offset) - { - return $this->_value[$offset]; - } - - public function offsetSet($offset, $value) - { - $this->_value[$offset] = $value; - } - - public function offsetUnset($offset) - { - unset($this->_value[$offset]); - } - - public function count() - { - return count($this->_value); - } - - public function getIterator() - { - return new \ArrayIterator($this->_value); - } -} diff --git a/core/vendor/guzzlehttp/ringphp/src/Future/FutureArrayInterface.php b/core/vendor/guzzlehttp/ringphp/src/Future/FutureArrayInterface.php deleted file mode 100644 index 58f5f7367069b10043a7e3661025ab788673b172..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/src/Future/FutureArrayInterface.php +++ /dev/null @@ -1,11 +0,0 @@ -<?php -namespace GuzzleHttp\Ring\Future; - -/** - * Future that provides array-like access. - */ -interface FutureArrayInterface extends - FutureInterface, - \ArrayAccess, - \Countable, - \IteratorAggregate {}; diff --git a/core/vendor/guzzlehttp/ringphp/src/Future/FutureInterface.php b/core/vendor/guzzlehttp/ringphp/src/Future/FutureInterface.php deleted file mode 100644 index 43d18117b1656a4643ce029ce6ebfee077052df8..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/src/Future/FutureInterface.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php -namespace GuzzleHttp\Ring\Future; - -use React\Promise\PromiseInterface; -use React\Promise\PromisorInterface; - -/** - * Represents the result of a computation that may not have completed yet. - * - * You can use the future in a blocking manner using the wait() function, or - * you can use a promise from the future to receive the result when the future - * has been resolved. - * - * When the future is dereferenced using wait(), the result of the computation - * is cached and returned for subsequent calls to wait(). If the result of the - * computation has not yet completed when wait() is called, the call to wait() - * will block until the future has completed. - */ -interface FutureInterface extends PromiseInterface, PromisorInterface -{ - /** - * Returns the result of the future either from cache or by blocking until - * it is complete. - * - * This method must block until the future has a result or is cancelled. - * Throwing an exception in the wait() method will mark the future as - * realized and will throw the exception each time wait() is called. - * Throwing an instance of GuzzleHttp\Ring\CancelledException will mark - * the future as realized, will not throw immediately, but will throw the - * exception if the future's wait() method is called again. - * - * @return mixed - */ - public function wait(); - - /** - * Cancels the future, if possible. - */ - public function cancel(); -} diff --git a/core/vendor/guzzlehttp/ringphp/src/Future/FutureValue.php b/core/vendor/guzzlehttp/ringphp/src/Future/FutureValue.php deleted file mode 100644 index 4cac9281b94cb3109f61d2ff045dd8a3dcb6fce1..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/src/Future/FutureValue.php +++ /dev/null @@ -1,12 +0,0 @@ -<?php -namespace GuzzleHttp\Ring\Future; - -/** - * Represents a future value that responds to wait() to retrieve the promised - * value, but can also return promises that are delivered the value when it is - * available. - */ -class FutureValue implements FutureInterface -{ - use BaseFutureTrait; -} diff --git a/core/vendor/guzzlehttp/ringphp/src/Future/MagicFutureTrait.php b/core/vendor/guzzlehttp/ringphp/src/Future/MagicFutureTrait.php deleted file mode 100644 index 58d779db49bf475fe06fc49ad831714f11054663..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/src/Future/MagicFutureTrait.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php -namespace GuzzleHttp\Ring\Future; - -/** - * Implements common future functionality that is triggered when the result - * property is accessed via a magic __get method. - * - * @property mixed $_value Actual data used by the future. Accessing this - * property will cause the future to block if needed. - */ -trait MagicFutureTrait -{ - use BaseFutureTrait; - - /** - * This function handles retrieving the dereferenced result when requested. - * - * @param string $name Should always be "data" or an exception is thrown. - * - * @return mixed Returns the dereferenced data. - * @throws \RuntimeException - * @throws \GuzzleHttp\Ring\Exception\CancelledException - */ - public function __get($name) - { - if ($name !== '_value') { - throw new \RuntimeException("Class has no {$name} property"); - } - - return $this->_value = $this->wait(); - } -} diff --git a/core/vendor/guzzlehttp/ringphp/tests/Client/CurlFactoryTest.php b/core/vendor/guzzlehttp/ringphp/tests/Client/CurlFactoryTest.php deleted file mode 100644 index 0f787c931b0fb25d8bd35499e7952b3ab2c6d70b..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/tests/Client/CurlFactoryTest.php +++ /dev/null @@ -1,792 +0,0 @@ -<?php -// Override curl_setopt_array() to get the last set curl options -namespace GuzzleHttp\Ring\Client { - function curl_setopt_array($handle, array $options) { - if (!empty($_SERVER['curl_test'])) { - $_SERVER['_curl'] = $options; - } else { - unset($_SERVER['_curl']); - } - \curl_setopt_array($handle, $options); - } -} - -namespace GuzzleHttp\Tests\Ring\Client { - -use GuzzleHttp\Ring\Client\CurlFactory; -use GuzzleHttp\Ring\Client\CurlMultiHandler; -use GuzzleHttp\Ring\Client\MockHandler; -use GuzzleHttp\Ring\Core; -use GuzzleHttp\Stream\FnStream; -use GuzzleHttp\Stream\NoSeekStream; -use GuzzleHttp\Stream\Stream; - -class CurlFactoryTest extends \PHPUnit_Framework_TestCase -{ - public static function setUpBeforeClass() - { - $_SERVER['curl_test'] = true; - unset($_SERVER['_curl']); - } - - public static function tearDownAfterClass() - { - unset($_SERVER['_curl'], $_SERVER['curl_test']); - } - - public function testCreatesCurlHandle() - { - Server::flush(); - Server::enqueue([[ - 'status' => 200, - 'headers' => [ - 'Foo' => ['Bar'], - 'Baz' => ['bam'], - 'Content-Length' => [2], - ], - 'body' => 'hi', - ]]); - - $stream = Stream::factory(); - - $request = [ - 'http_method' => 'PUT', - 'headers' => [ - 'host' => [Server::$url], - 'Hi' => [' 123'], - ], - 'body' => 'testing', - 'client' => ['save_to' => $stream], - ]; - - $f = new CurlFactory(); - $result = $f($request); - $this->assertInternalType('array', $result); - $this->assertCount(3, $result); - $this->assertInternalType('resource', $result[0]); - $this->assertInternalType('array', $result[1]); - $this->assertSame($stream, $result[2]); - curl_close($result[0]); - - $this->assertEquals('PUT', $_SERVER['_curl'][CURLOPT_CUSTOMREQUEST]); - $this->assertEquals( - 'http://http://127.0.0.1:8125/', - $_SERVER['_curl'][CURLOPT_URL] - ); - // Sends via post fields when the request is small enough - $this->assertEquals('testing', $_SERVER['_curl'][CURLOPT_POSTFIELDS]); - $this->assertEquals(0, $_SERVER['_curl'][CURLOPT_RETURNTRANSFER]); - $this->assertEquals(0, $_SERVER['_curl'][CURLOPT_HEADER]); - $this->assertEquals(150, $_SERVER['_curl'][CURLOPT_CONNECTTIMEOUT]); - $this->assertInstanceOf('Closure', $_SERVER['_curl'][CURLOPT_HEADERFUNCTION]); - - if (defined('CURLOPT_PROTOCOLS')) { - $this->assertEquals( - CURLPROTO_HTTP | CURLPROTO_HTTPS, - $_SERVER['_curl'][CURLOPT_PROTOCOLS] - ); - } - - $this->assertContains('Expect:', $_SERVER['_curl'][CURLOPT_HTTPHEADER]); - $this->assertContains('Accept:', $_SERVER['_curl'][CURLOPT_HTTPHEADER]); - $this->assertContains('Content-Type:', $_SERVER['_curl'][CURLOPT_HTTPHEADER]); - $this->assertContains('Hi: 123', $_SERVER['_curl'][CURLOPT_HTTPHEADER]); - $this->assertContains('host: http://127.0.0.1:8125/', $_SERVER['_curl'][CURLOPT_HTTPHEADER]); - } - - public function testSendsHeadRequests() - { - Server::flush(); - Server::enqueue([['status' => 200]]); - $a = new CurlMultiHandler(); - $response = $a([ - 'http_method' => 'HEAD', - 'headers' => ['host' => [Server::$host]], - ]); - $response->wait(); - $this->assertEquals(true, $_SERVER['_curl'][CURLOPT_NOBODY]); - $checks = [CURLOPT_WRITEFUNCTION, CURLOPT_READFUNCTION, CURLOPT_FILE, CURLOPT_INFILE]; - foreach ($checks as $check) { - $this->assertArrayNotHasKey($check, $_SERVER['_curl']); - } - $this->assertEquals('HEAD', Server::received()[0]['http_method']); - } - - public function testCanAddCustomCurlOptions() - { - Server::flush(); - Server::enqueue([['status' => 200]]); - $a = new CurlMultiHandler(); - $a([ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - 'client' => ['curl' => [CURLOPT_LOW_SPEED_LIMIT => 10]], - ]); - $this->assertEquals(10, $_SERVER['_curl'][CURLOPT_LOW_SPEED_LIMIT]); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage SSL CA bundle not found: /does/not/exist - */ - public function testValidatesVerify() - { - $f = new CurlFactory(); - $f([ - 'http_method' => 'GET', - 'headers' => ['host' => ['foo.com']], - 'client' => ['verify' => '/does/not/exist'], - ]); - } - - public function testCanSetVerifyToFile() - { - $f = new CurlFactory(); - $f([ - 'http_method' => 'GET', - 'headers' => ['host' => ['foo.com']], - 'client' => ['verify' => __FILE__], - ]); - $this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_CAINFO]); - $this->assertEquals(2, $_SERVER['_curl'][CURLOPT_SSL_VERIFYHOST]); - $this->assertEquals(true, $_SERVER['_curl'][CURLOPT_SSL_VERIFYPEER]); - } - - public function testAddsVerifyAsTrue() - { - $f = new CurlFactory(); - $f([ - 'http_method' => 'GET', - 'headers' => ['host' => ['foo.com']], - 'client' => ['verify' => true], - ]); - $this->assertEquals(2, $_SERVER['_curl'][CURLOPT_SSL_VERIFYHOST]); - $this->assertEquals(true, $_SERVER['_curl'][CURLOPT_SSL_VERIFYPEER]); - $this->assertArrayNotHasKey(CURLOPT_CAINFO, $_SERVER['_curl']); - } - - public function testCanDisableVerify() - { - $f = new CurlFactory(); - $f([ - 'http_method' => 'GET', - 'headers' => ['host' => ['foo.com']], - 'client' => ['verify' => false], - ]); - $this->assertEquals(0, $_SERVER['_curl'][CURLOPT_SSL_VERIFYHOST]); - $this->assertEquals(false, $_SERVER['_curl'][CURLOPT_SSL_VERIFYPEER]); - } - - public function testAddsProxy() - { - $f = new CurlFactory(); - $f([ - 'http_method' => 'GET', - 'headers' => ['host' => ['foo.com']], - 'client' => ['proxy' => 'http://bar.com'], - ]); - $this->assertEquals('http://bar.com', $_SERVER['_curl'][CURLOPT_PROXY]); - } - - public function testAddsViaScheme() - { - $f = new CurlFactory(); - $f([ - 'http_method' => 'GET', - 'scheme' => 'http', - 'headers' => ['host' => ['foo.com']], - 'client' => [ - 'proxy' => ['http' => 'http://bar.com', 'https' => 'https://t'], - ], - ]); - $this->assertEquals('http://bar.com', $_SERVER['_curl'][CURLOPT_PROXY]); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage SSL private key not found: /does/not/exist - */ - public function testValidatesSslKey() - { - $f = new CurlFactory(); - $f([ - 'http_method' => 'GET', - 'headers' => ['host' => ['foo.com']], - 'client' => ['ssl_key' => '/does/not/exist'], - ]); - } - - public function testAddsSslKey() - { - $f = new CurlFactory(); - $f([ - 'http_method' => 'GET', - 'headers' => ['host' => ['foo.com']], - 'client' => ['ssl_key' => __FILE__], - ]); - $this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLKEY]); - } - - public function testAddsSslKeyWithPassword() - { - $f = new CurlFactory(); - $f([ - 'http_method' => 'GET', - 'headers' => ['host' => ['foo.com']], - 'client' => ['ssl_key' => [__FILE__, 'test']], - ]); - $this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLKEY]); - $this->assertEquals('test', $_SERVER['_curl'][CURLOPT_SSLKEYPASSWD]); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage SSL certificate not found: /does/not/exist - */ - public function testValidatesCert() - { - $f = new CurlFactory(); - $f([ - 'http_method' => 'GET', - 'headers' => ['host' => ['foo.com']], - 'client' => ['cert' => '/does/not/exist'], - ]); - } - - public function testAddsCert() - { - $f = new CurlFactory(); - $f([ - 'http_method' => 'GET', - 'headers' => ['host' => ['foo.com']], - 'client' => ['cert' => __FILE__], - ]); - $this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLCERT]); - } - - public function testAddsCertWithPassword() - { - $f = new CurlFactory(); - $f([ - 'http_method' => 'GET', - 'headers' => ['host' => ['foo.com']], - 'client' => ['cert' => [__FILE__, 'test']], - ]); - $this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLCERT]); - $this->assertEquals('test', $_SERVER['_curl'][CURLOPT_SSLCERTPASSWD]); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage progress client option must be callable - */ - public function testValidatesProgress() - { - $f = new CurlFactory(); - $f([ - 'http_method' => 'GET', - 'headers' => ['host' => ['foo.com']], - 'client' => ['progress' => 'foo'], - ]); - } - - public function testEmitsDebugInfoToStream() - { - $res = fopen('php://memory', 'r+'); - Server::flush(); - Server::enqueue([['status' => 200]]); - $a = new CurlMultiHandler(); - $response = $a([ - 'http_method' => 'HEAD', - 'headers' => ['host' => [Server::$host]], - 'client' => ['debug' => $res], - ]); - $response->wait(); - rewind($res); - $output = str_replace("\r", '', stream_get_contents($res)); - $this->assertContains( - "> HEAD / HTTP/1.1\nhost: 127.0.0.1:8125\n\n", - $output - ); - $this->assertContains("< HTTP/1.1 200", $output); - fclose($res); - } - - public function testEmitsProgressToFunction() - { - Server::flush(); - Server::enqueue([['status' => 200]]); - $a = new CurlMultiHandler(); - $called = []; - $response = $a([ - 'http_method' => 'HEAD', - 'headers' => ['host' => [Server::$host]], - 'client' => [ - 'progress' => function () use (&$called) { - $called[] = func_get_args(); - }, - ], - ]); - $response->wait(); - $this->assertNotEmpty($called); - foreach ($called as $call) { - $this->assertCount(4, $call); - } - } - - private function addDecodeResponse($withEncoding = true) - { - $content = gzencode('test'); - $response = [ - 'status' => 200, - 'reason' => 'OK', - 'headers' => ['Content-Length' => [strlen($content)]], - 'body' => $content, - ]; - - if ($withEncoding) { - $response['headers']['Content-Encoding'] = ['gzip']; - } - - Server::flush(); - Server::enqueue([$response]); - - return $content; - } - - public function testDecodesGzippedResponses() - { - $this->addDecodeResponse(); - $handler = new CurlMultiHandler(); - $response = $handler([ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - 'client' => ['decode_content' => true], - ]); - $response->wait(); - $this->assertEquals('test', Core::body($response)); - $this->assertEquals('', $_SERVER['_curl'][CURLOPT_ENCODING]); - $sent = Server::received()[0]; - $this->assertNull(Core::header($sent, 'Accept-Encoding')); - } - - public function testDecodesGzippedResponsesWithHeader() - { - $this->addDecodeResponse(); - $handler = new CurlMultiHandler(); - $response = $handler([ - 'http_method' => 'GET', - 'headers' => [ - 'host' => [Server::$host], - 'Accept-Encoding' => ['gzip'], - ], - 'client' => ['decode_content' => true], - ]); - $response->wait(); - $this->assertEquals('gzip', $_SERVER['_curl'][CURLOPT_ENCODING]); - $sent = Server::received()[0]; - $this->assertEquals('gzip', Core::header($sent, 'Accept-Encoding')); - $this->assertEquals('test', Core::body($response)); - } - - public function testDoesNotForceDecode() - { - $content = $this->addDecodeResponse(); - $handler = new CurlMultiHandler(); - $response = $handler([ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - 'client' => ['decode_content' => false], - ]); - $response->wait(); - $sent = Server::received()[0]; - $this->assertNull(Core::header($sent, 'Accept-Encoding')); - $this->assertEquals($content, Core::body($response)); - } - - public function testProtocolVersion() - { - Server::flush(); - Server::enqueue([['status' => 200]]); - $a = new CurlMultiHandler(); - $a([ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - 'version' => 1.0, - ]); - $this->assertEquals(CURL_HTTP_VERSION_1_0, $_SERVER['_curl'][CURLOPT_HTTP_VERSION]); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testValidatesSaveTo() - { - $handler = new CurlMultiHandler(); - $handler([ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - 'client' => ['save_to' => true], - ]); - } - - public function testSavesToStream() - { - $stream = fopen('php://memory', 'r+'); - $this->addDecodeResponse(); - $handler = new CurlMultiHandler(); - $response = $handler([ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - 'client' => [ - 'decode_content' => true, - 'save_to' => $stream, - ], - ]); - $response->wait(); - rewind($stream); - $this->assertEquals('test', stream_get_contents($stream)); - } - - public function testSavesToGuzzleStream() - { - $stream = Stream::factory(); - $this->addDecodeResponse(); - $handler = new CurlMultiHandler(); - $response = $handler([ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - 'client' => [ - 'decode_content' => true, - 'save_to' => $stream, - ], - ]); - $response->wait(); - $this->assertEquals('test', (string) $stream); - } - - public function testSavesToFileOnDisk() - { - $tmpfile = tempnam(sys_get_temp_dir(), 'testfile'); - $this->addDecodeResponse(); - $handler = new CurlMultiHandler(); - $response = $handler([ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - 'client' => [ - 'decode_content' => true, - 'save_to' => $tmpfile, - ], - ]); - $response->wait(); - $this->assertEquals('test', file_get_contents($tmpfile)); - unlink($tmpfile); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testValidatesBody() - { - $handler = new CurlMultiHandler(); - $handler([ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - 'body' => false, - ]); - } - - public function testAddsLargePayloadFromStreamWithNoSizeUsingChunked() - { - $stream = Stream::factory('foo'); - $stream = FnStream::decorate($stream, [ - 'getSize' => function () { - return null; - } - ]); - $this->addDecodeResponse(); - $handler = new CurlMultiHandler(); - $response = $handler([ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - 'body' => $stream, - ]); - $response->wait(); - $sent = Server::received()[0]; - $this->assertEquals('chunked', Core::header($sent, 'Transfer-Encoding')); - $this->assertNull(Core::header($sent, 'Content-Length')); - $this->assertEquals('foo', $sent['body']); - } - - public function testAddsPayloadFromIterator() - { - $iter = new \ArrayIterator(['f', 'o', 'o']); - $this->addDecodeResponse(); - $handler = new CurlMultiHandler(); - $response = $handler([ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - 'body' => $iter, - ]); - $response->wait(); - $sent = Server::received()[0]; - $this->assertEquals('chunked', Core::header($sent, 'Transfer-Encoding')); - $this->assertNull(Core::header($sent, 'Content-Length')); - $this->assertEquals('foo', $sent['body']); - } - - public function testAddsPayloadFromResource() - { - $res = fopen('php://memory', 'r+'); - $data = str_repeat('.', 1000000); - fwrite($res, $data); - rewind($res); - $this->addDecodeResponse(); - $handler = new CurlMultiHandler(); - $response = $handler([ - 'http_method' => 'GET', - 'headers' => [ - 'host' => [Server::$host], - 'content-length' => [1000000], - ], - 'body' => $res, - ]); - $response->wait(); - $sent = Server::received()[0]; - $this->assertNull(Core::header($sent, 'Transfer-Encoding')); - $this->assertEquals(1000000, Core::header($sent, 'Content-Length')); - $this->assertEquals($data, $sent['body']); - } - - public function testAddsContentLengthFromStream() - { - $stream = Stream::factory('foo'); - $this->addDecodeResponse(); - $handler = new CurlMultiHandler(); - $response = $handler([ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - 'body' => $stream, - ]); - $response->wait(); - $sent = Server::received()[0]; - $this->assertEquals(3, Core::header($sent, 'Content-Length')); - $this->assertNull(Core::header($sent, 'Transfer-Encoding')); - $this->assertEquals('foo', $sent['body']); - } - - public function testDoesNotAddMultipleContentLengthHeaders() - { - $this->addDecodeResponse(); - $handler = new CurlMultiHandler(); - $response = $handler([ - 'http_method' => 'GET', - 'headers' => [ - 'host' => [Server::$host], - 'content-length' => [3], - ], - 'body' => 'foo', - ]); - $response->wait(); - $sent = Server::received()[0]; - $this->assertEquals(3, Core::header($sent, 'Content-Length')); - $this->assertNull(Core::header($sent, 'Transfer-Encoding')); - $this->assertEquals('foo', $sent['body']); - } - - public function testSendsPostWithNoBodyOrDefaultContentType() - { - Server::flush(); - Server::enqueue([['status' => 200]]); - $handler = new CurlMultiHandler(); - $response = $handler([ - 'http_method' => 'POST', - 'uri' => '/', - 'headers' => ['host' => [Server::$host]], - ]); - $response->wait(); - $received = Server::received()[0]; - $this->assertEquals('POST', $received['http_method']); - $this->assertNull(Core::header($received, 'content-type')); - $this->assertSame('0', Core::firstHeader($received, 'content-length')); - } - - public function testFailsWhenNoResponseAndNoBody() - { - $res = CurlFactory::createResponse(function () {}, [], [], [], null); - $this->assertInstanceOf('GuzzleHttp\Ring\Exception\RingException', $res['error']); - $this->assertContains( - 'No response was received for a request with no body', - $res['error']->getMessage() - ); - } - - public function testFailsWhenCannotRewindRetry() - { - $res = CurlFactory::createResponse(function () {}, [ - 'body' => new NoSeekStream(Stream::factory('foo')) - ], [], [], null); - $this->assertInstanceOf('GuzzleHttp\Ring\Exception\RingException', $res['error']); - $this->assertContains( - 'rewind the request body failed', - $res['error']->getMessage() - ); - } - - public function testRetriesWhenBodyCanBeRewound() - { - $callHandler = $called = false; - $res = CurlFactory::createResponse(function () use (&$callHandler) { - $callHandler = true; - return ['status' => 200]; - }, [ - 'body' => FnStream::decorate(Stream::factory('test'), [ - 'seek' => function () use (&$called) { - $called = true; - return true; - } - ]) - ], [], [], null); - - $this->assertTrue($callHandler); - $this->assertTrue($called); - $this->assertEquals('200', $res['status']); - } - - public function testFailsWhenRetryMoreThanThreeTimes() - { - $call = 0; - $mock = new MockHandler(function (array $request) use (&$mock, &$call) { - $call++; - return CurlFactory::createResponse($mock, $request, [], [], null); - }); - $response = $mock([ - 'http_method' => 'GET', - 'body' => 'test', - ]); - $this->assertEquals(3, $call); - $this->assertArrayHasKey('error', $response); - $this->assertContains( - 'The cURL request was retried 3 times', - $response['error']->getMessage() - ); - } - - public function testHandles100Continue() - { - Server::flush(); - Server::enqueue([ - [ - 'status' => '200', - 'reason' => 'OK', - 'headers' => [ - 'Test' => ['Hello'], - 'Content-Length' => ['4'], - ], - 'body' => 'test', - ], - ]); - - $request = [ - 'http_method' => 'PUT', - 'headers' => [ - 'Host' => [Server::$host], - 'Expect' => ['100-Continue'], - ], - 'body' => 'test', - ]; - - $handler = new CurlMultiHandler(); - $response = $handler($request)->wait(); - $this->assertEquals(200, $response['status']); - $this->assertEquals('OK', $response['reason']); - $this->assertEquals(['Hello'], $response['headers']['Test']); - $this->assertEquals(['4'], $response['headers']['Content-Length']); - $this->assertEquals('test', Core::body($response)); - } - - public function testCreatesConnectException() - { - $m = new \ReflectionMethod('GuzzleHttp\Ring\Client\CurlFactory', 'createErrorResponse'); - $m->setAccessible(true); - $response = $m->invoke( - null, - function () {}, - [], - [ - 'err_message' => 'foo', - 'curl' => [ - 'errno' => CURLE_COULDNT_CONNECT, - ] - ] - ); - $this->assertInstanceOf('GuzzleHttp\Ring\Exception\ConnectException', $response['error']); - } - - public function testParsesLastResponseOnly() - { - $response1 = [ - 'status' => 301, - 'headers' => [ - 'Content-Length' => ['0'], - 'Location' => ['/foo'] - ] - ]; - - $response2 = [ - 'status' => 200, - 'headers' => [ - 'Content-Length' => ['0'], - 'Foo' => ['bar'] - ] - ]; - - Server::flush(); - Server::enqueue([$response1, $response2]); - - $a = new CurlMultiHandler(); - $response = $a([ - 'http_method' => 'GET', - 'headers' => ['Host' => [Server::$host]], - 'client' => [ - 'curl' => [ - CURLOPT_FOLLOWLOCATION => true - ] - ] - ])->wait(); - - $this->assertEquals(1, $response['transfer_stats']['redirect_count']); - $this->assertEquals('http://127.0.0.1:8125/foo', $response['effective_url']); - $this->assertEquals(['bar'], $response['headers']['Foo']); - $this->assertEquals(200, $response['status']); - $this->assertFalse(Core::hasHeader($response, 'Location')); - } - - public function testMaintainsMultiHeaderOrder() - { - Server::flush(); - Server::enqueue([ - [ - 'status' => 200, - 'headers' => [ - 'Content-Length' => ['0'], - 'Foo' => ['a', 'b'], - 'foo' => ['c', 'd'], - ] - ] - ]); - - $a = new CurlMultiHandler(); - $response = $a([ - 'http_method' => 'GET', - 'headers' => ['Host' => [Server::$host]] - ])->wait(); - - $this->assertEquals( - ['a', 'b', 'c', 'd'], - Core::headerLines($response, 'Foo') - ); - } -} - -} diff --git a/core/vendor/guzzlehttp/ringphp/tests/Client/CurlHandlerTest.php b/core/vendor/guzzlehttp/ringphp/tests/Client/CurlHandlerTest.php deleted file mode 100644 index ba03b8cd37b66e34be473072b1a862e9f2cca58e..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/tests/Client/CurlHandlerTest.php +++ /dev/null @@ -1,96 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Ring\Client; - -use GuzzleHttp\Ring\Client\CurlHandler; - -class CurlHandlerTest extends \PHPUnit_Framework_TestCase -{ - protected function setUp() - { - if (!function_exists('curl_reset')) { - $this->markTestSkipped('curl_reset() is not available'); - } - } - - protected function getHandler($factory = null, $options = []) - { - return new CurlHandler($options); - } - - public function testCanSetMaxHandles() - { - $a = new CurlHandler(['max_handles' => 10]); - $this->assertEquals(10, $this->readAttribute($a, 'maxHandles')); - } - - public function testCreatesCurlErrors() - { - $handler = new CurlHandler(); - $response = $handler([ - 'http_method' => 'GET', - 'uri' => '/', - 'headers' => ['host' => ['localhost:123']], - 'client' => ['timeout' => 0.001, 'connect_timeout' => 0.001], - ]); - $this->assertNull($response['status']); - $this->assertNull($response['reason']); - $this->assertEquals([], $response['headers']); - $this->assertInstanceOf( - 'GuzzleHttp\Ring\Exception\RingException', - $response['error'] - ); - - $this->assertEquals( - 1, - preg_match('/^cURL error \d+: .*$/', $response['error']->getMessage()) - ); - } - - public function testReleasesAdditionalEasyHandles() - { - Server::flush(); - $response = [ - 'status' => 200, - 'headers' => ['Content-Length' => [4]], - 'body' => 'test', - ]; - - Server::enqueue([$response, $response, $response, $response]); - $a = new CurlHandler(['max_handles' => 2]); - - $fn = function () use (&$calls, $a, &$fn) { - if (++$calls < 4) { - $a([ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - 'client' => ['progress' => $fn], - ]); - } - }; - - $request = [ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - 'client' => [ - 'progress' => $fn, - ], - ]; - - $a($request); - $this->assertCount(2, $this->readAttribute($a, 'handles')); - } - - public function testReusesHandles() - { - Server::flush(); - $response = ['status' => 200]; - Server::enqueue([$response, $response]); - $a = new CurlHandler(); - $request = [ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - ]; - $a($request); - $a($request); - } -} diff --git a/core/vendor/guzzlehttp/ringphp/tests/Client/CurlMultiHandlerTest.php b/core/vendor/guzzlehttp/ringphp/tests/Client/CurlMultiHandlerTest.php deleted file mode 100644 index 9228f1ceac6ac273e68af635217213cb5146239c..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/tests/Client/CurlMultiHandlerTest.php +++ /dev/null @@ -1,165 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Ring\Client; - -use GuzzleHttp\Ring\Client\CurlMultiHandler; - -class CurlMultiHandlerTest extends \PHPUnit_Framework_TestCase -{ - public function testSendsRequest() - { - Server::enqueue([['status' => 200]]); - $a = new CurlMultiHandler(); - $response = $a([ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - ]); - $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response); - $this->assertEquals(200, $response['status']); - $this->assertArrayHasKey('transfer_stats', $response); - $realUrl = trim($response['transfer_stats']['url'], '/'); - $this->assertEquals(trim(Server::$url, '/'), $realUrl); - $this->assertArrayHasKey('effective_url', $response); - $this->assertEquals( - trim(Server::$url, '/'), - trim($response['effective_url'], '/') - ); - } - - public function testCreatesErrorResponses() - { - $url = 'http://localhost:123/'; - $a = new CurlMultiHandler(); - $response = $a([ - 'http_method' => 'GET', - 'headers' => ['host' => ['localhost:123']], - ]); - $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response); - $this->assertNull($response['status']); - $this->assertNull($response['reason']); - $this->assertEquals([], $response['headers']); - $this->assertArrayHasKey('error', $response); - $this->assertContains('cURL error ', $response['error']->getMessage()); - $this->assertArrayHasKey('transfer_stats', $response); - $this->assertEquals( - trim($url, '/'), - trim($response['transfer_stats']['url'], '/') - ); - $this->assertArrayHasKey('effective_url', $response); - $this->assertEquals( - trim($url, '/'), - trim($response['effective_url'], '/') - ); - } - - public function testSendsFuturesWhenDestructed() - { - Server::enqueue([['status' => 200]]); - $a = new CurlMultiHandler(); - $response = $a([ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - ]); - $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response); - $a->__destruct(); - $this->assertEquals(200, $response['status']); - } - - public function testCanSetMaxHandles() - { - $a = new CurlMultiHandler(['max_handles' => 2]); - $this->assertEquals(2, $this->readAttribute($a, 'maxHandles')); - } - - public function testCanSetSelectTimeout() - { - $a = new CurlMultiHandler(['select_timeout' => 2]); - $this->assertEquals(2, $this->readAttribute($a, 'selectTimeout')); - } - - public function testSendsFuturesWhenMaxHandlesIsReached() - { - $request = [ - 'http_method' => 'PUT', - 'headers' => ['host' => [Server::$host]], - 'future' => 'lazy', // passing this to control the test - ]; - $response = ['status' => 200]; - Server::flush(); - Server::enqueue([$response, $response, $response]); - $a = new CurlMultiHandler(['max_handles' => 3]); - for ($i = 0; $i < 5; $i++) { - $responses[] = $a($request); - } - $this->assertCount(3, Server::received()); - $responses[3]->cancel(); - $responses[4]->cancel(); - } - - public function testCanCancel() - { - Server::flush(); - $response = ['status' => 200]; - Server::enqueue(array_fill_keys(range(0, 10), $response)); - $a = new CurlMultiHandler(); - $responses = []; - - for ($i = 0; $i < 10; $i++) { - $response = $a([ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - 'future' => 'lazy', - ]); - $response->cancel(); - $responses[] = $response; - } - - $this->assertCount(0, Server::received()); - - foreach ($responses as $response) { - $this->assertTrue($this->readAttribute($response, 'isRealized')); - } - } - - public function testCannotCancelFinished() - { - Server::flush(); - Server::enqueue([['status' => 200]]); - $a = new CurlMultiHandler(); - $response = $a([ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - ]); - $response->wait(); - $response->cancel(); - } - - public function testDelaysInParallel() - { - Server::flush(); - Server::enqueue([['status' => 200]]); - $a = new CurlMultiHandler(); - $expected = microtime(true) + (100 / 1000); - $response = $a([ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - 'client' => ['delay' => 100], - ]); - $response->wait(); - $this->assertGreaterThanOrEqual($expected, microtime(true)); - } - - public function testSendsNonLazyFutures() - { - $request = [ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - 'future' => true, - ]; - Server::flush(); - Server::enqueue([['status' => 202]]); - $a = new CurlMultiHandler(); - $response = $a($request); - $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response); - $this->assertEquals(202, $response['status']); - } -} diff --git a/core/vendor/guzzlehttp/ringphp/tests/Client/MiddlewareTest.php b/core/vendor/guzzlehttp/ringphp/tests/Client/MiddlewareTest.php deleted file mode 100644 index a47bb30bab4e9b3f88b3da1ebfa0e3f200bb431c..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/tests/Client/MiddlewareTest.php +++ /dev/null @@ -1,65 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Ring\Client; - -use GuzzleHttp\Ring\Client\Middleware; -use GuzzleHttp\Ring\Future\CompletedFutureArray; - -class MiddlewareTest extends \PHPUnit_Framework_TestCase -{ - public function testFutureCallsDefaultHandler() - { - $future = new CompletedFutureArray(['status' => 200]); - $calledA = false; - $a = function (array $req) use (&$calledA, $future) { - $calledA = true; - return $future; - }; - $calledB = false; - $b = function (array $req) use (&$calledB) { $calledB = true; }; - $s = Middleware::wrapFuture($a, $b); - $s([]); - $this->assertTrue($calledA); - $this->assertFalse($calledB); - } - - public function testFutureCallsStreamingHandler() - { - $future = new CompletedFutureArray(['status' => 200]); - $calledA = false; - $a = function (array $req) use (&$calledA) { $calledA = true; }; - $calledB = false; - $b = function (array $req) use (&$calledB, $future) { - $calledB = true; - return $future; - }; - $s = Middleware::wrapFuture($a, $b); - $result = $s(['client' => ['future' => true]]); - $this->assertFalse($calledA); - $this->assertTrue($calledB); - $this->assertSame($future, $result); - } - - public function testStreamingCallsDefaultHandler() - { - $calledA = false; - $a = function (array $req) use (&$calledA) { $calledA = true; }; - $calledB = false; - $b = function (array $req) use (&$calledB) { $calledB = true; }; - $s = Middleware::wrapStreaming($a, $b); - $s([]); - $this->assertTrue($calledA); - $this->assertFalse($calledB); - } - - public function testStreamingCallsStreamingHandler() - { - $calledA = false; - $a = function (array $req) use (&$calledA) { $calledA = true; }; - $calledB = false; - $b = function (array $req) use (&$calledB) { $calledB = true; }; - $s = Middleware::wrapStreaming($a, $b); - $s(['client' => ['stream' => true]]); - $this->assertFalse($calledA); - $this->assertTrue($calledB); - } -} diff --git a/core/vendor/guzzlehttp/ringphp/tests/Client/MockHandlerTest.php b/core/vendor/guzzlehttp/ringphp/tests/Client/MockHandlerTest.php deleted file mode 100644 index 26bcd6cdc7ea86445f81f5389e963f4f9ab88f2d..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/tests/Client/MockHandlerTest.php +++ /dev/null @@ -1,86 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Ring\Client; - -use GuzzleHttp\Ring\Client\MockHandler; -use GuzzleHttp\Ring\Future\FutureArray; -use React\Promise\Deferred; - -class MockHandlerTest extends \PHPUnit_Framework_TestCase -{ - public function testReturnsArray() - { - $mock = new MockHandler(['status' => 200]); - $response = $mock([]); - $this->assertEquals(200, $response['status']); - $this->assertEquals([], $response['headers']); - $this->assertNull($response['body']); - $this->assertNull($response['reason']); - $this->assertNull($response['effective_url']); - } - - public function testReturnsFutures() - { - $deferred = new Deferred(); - $future = new FutureArray( - $deferred->promise(), - function () use ($deferred) { - $deferred->resolve(['status' => 200]); - } - ); - $mock = new MockHandler($future); - $response = $mock([]); - $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response); - $this->assertEquals(200, $response['status']); - } - - public function testReturnsFuturesWithThenCall() - { - $deferred = new Deferred(); - $future = new FutureArray( - $deferred->promise(), - function () use ($deferred) { - $deferred->resolve(['status' => 200]); - } - ); - $mock = new MockHandler($future); - $response = $mock([]); - $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response); - $this->assertEquals(200, $response['status']); - $req = null; - $promise = $response->then(function ($value) use (&$req) { - $req = $value; - $this->assertEquals(200, $req['status']); - }); - $this->assertInstanceOf('React\Promise\PromiseInterface', $promise); - $this->assertEquals(200, $req['status']); - } - - public function testReturnsFuturesAndProxiesCancel() - { - $c = null; - $deferred = new Deferred(); - $future = new FutureArray( - $deferred->promise(), - function () {}, - function () use (&$c) { - $c = true; - return true; - } - ); - $mock = new MockHandler($future); - $response = $mock([]); - $this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response); - $response->cancel(); - $this->assertTrue($c); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Response must be an array or FutureArrayInterface. Found - */ - public function testEnsuresMockIsValid() - { - $mock = new MockHandler('foo'); - $mock([]); - } -} diff --git a/core/vendor/guzzlehttp/ringphp/tests/Client/Server.php b/core/vendor/guzzlehttp/ringphp/tests/Client/Server.php deleted file mode 100644 index 14665a5565dfdadd93a146ea67bcfb14a31ea8c7..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/tests/Client/Server.php +++ /dev/null @@ -1,183 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Ring\Client; - -use GuzzleHttp\Ring\Client\StreamHandler; -use GuzzleHttp\Ring\Core; - -/** - * Class uses to control the test webserver. - * - * Queued responses will be served to requests using a FIFO order. All requests - * received by the server are stored on the node.js server and can be retrieved - * by calling {@see Server::received()}. - * - * Mock responses that don't require data to be transmitted over HTTP a great - * for testing. Mock response, however, cannot test the actual sending of an - * HTTP request using cURL. This test server allows the simulation of any - * number of HTTP request response transactions to test the actual sending of - * requests over the wire without having to leave an internal network. - */ -class Server -{ - public static $started; - public static $url = 'http://127.0.0.1:8125/'; - public static $host = '127.0.0.1:8125'; - public static $port = 8125; - - /** - * Flush the received requests from the server - * @throws \RuntimeException - */ - public static function flush() - { - self::send('DELETE', '/guzzle-server/requests'); - } - - /** - * 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 $responses An array of responses. The shape of a response - * is the shape described in the RingPHP spec. - * @throws \Exception - */ - public static function enqueue(array $responses) - { - $data = []; - - foreach ($responses as $response) { - if (!is_array($response)) { - throw new \Exception('Each response must be an array'); - } - - if (isset($response['body'])) { - $response['body'] = base64_encode($response['body']); - } - - $response += ['headers' => [], 'reason' => '', 'body' => '']; - $data[] = $response; - } - - self::send('PUT', '/guzzle-server/responses', json_encode($data)); - } - - /** - * Get all of the received requests as a RingPHP request structure. - * - * @return array - * @throws \RuntimeException - */ - public static function received() - { - if (!self::$started) { - return []; - } - - $response = self::send('GET', '/guzzle-server/requests'); - $body = Core::body($response); - $result = json_decode($body, true); - if ($result === false) { - throw new \RuntimeException('Error decoding response: ' - . json_last_error()); - } - - foreach ($result as &$res) { - if (isset($res['uri'])) { - $res['resource'] = $res['uri']; - } - if (isset($res['query_string'])) { - $res['resource'] .= '?' . $res['query_string']; - } - if (!isset($res['resource'])) { - $res['resource'] = ''; - } - // Ensure that headers are all arrays - if (isset($res['headers'])) { - foreach ($res['headers'] as &$h) { - $h = (array) $h; - } - unset($h); - } - } - - unset($res); - return $result; - } - - /** - * Stop running the node.js server - */ - public static function stop() - { - if (self::$started) { - self::send('DELETE', '/guzzle-server'); - } - - self::$started = false; - } - - public static function wait($maxTries = 20) - { - $tries = 0; - while (!self::isListening() && ++$tries < $maxTries) { - usleep(100000); - } - - if (!self::isListening()) { - throw new \RuntimeException('Unable to contact node.js server'); - } - } - - public static function start() - { - if (self::$started) { - return; - } - - try { - self::wait(); - } catch (\Exception $e) { - exec('node ' . __DIR__ . \DIRECTORY_SEPARATOR . 'server.js ' - . self::$port . ' >> /tmp/server.log 2>&1 &'); - self::wait(); - } - - self::$started = true; - } - - private static function isListening() - { - $response = self::send('GET', '/guzzle-server/perf', null, [ - 'connect_timeout' => 1, - 'timeout' => 1 - ]); - - return !isset($response['error']); - } - - private static function send( - $method, - $path, - $body = null, - array $client = [] - ) { - $handler = new StreamHandler(); - - $request = [ - 'http_method' => $method, - 'uri' => $path, - 'request_port' => 8125, - 'headers' => ['host' => ['127.0.0.1:8125']], - 'body' => $body, - 'client' => $client, - ]; - - if ($body) { - $request['headers']['content-length'] = [strlen($body)]; - } - - return $handler($request); - } -} diff --git a/core/vendor/guzzlehttp/ringphp/tests/Client/StreamHandlerTest.php b/core/vendor/guzzlehttp/ringphp/tests/Client/StreamHandlerTest.php deleted file mode 100644 index 7549a83f6410e66e4c508ea10e9557cfd0377eb7..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/tests/Client/StreamHandlerTest.php +++ /dev/null @@ -1,479 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Ring\Client; - -use GuzzleHttp\Ring\Client\ClientUtils; -use GuzzleHttp\Ring\Core; -use GuzzleHttp\Ring\Client\StreamHandler; - -class StreamHandlerTest extends \PHPUnit_Framework_TestCase -{ - public function testReturnsResponseForSuccessfulRequest() - { - $this->queueRes(); - $handler = new StreamHandler(); - $response = $handler([ - 'http_method' => 'GET', - 'uri' => '/', - 'headers' => [ - 'host' => [Server::$host], - 'Foo' => ['Bar'], - ], - ]); - - $this->assertEquals(200, $response['status']); - $this->assertEquals('OK', $response['reason']); - $this->assertEquals(['Bar'], $response['headers']['Foo']); - $this->assertEquals(['8'], $response['headers']['Content-Length']); - $this->assertEquals('hi there', Core::body($response)); - - $sent = Server::received()[0]; - $this->assertEquals('GET', $sent['http_method']); - $this->assertEquals('/', $sent['resource']); - $this->assertEquals(['127.0.0.1:8125'], $sent['headers']['host']); - $this->assertEquals('Bar', Core::header($sent, 'foo')); - } - - public function testAddsErrorToResponse() - { - $handler = new StreamHandler(); - $result = $handler([ - 'http_method' => 'GET', - 'headers' => ['host' => ['localhost:123']], - 'client' => ['timeout' => 0.01], - ]); - $this->assertInstanceOf( - 'GuzzleHttp\Ring\Future\CompletedFutureArray', - $result - ); - $this->assertNull($result['status']); - $this->assertNull($result['body']); - $this->assertEquals([], $result['headers']); - $this->assertInstanceOf( - 'GuzzleHttp\Ring\Exception\RingException', - $result['error'] - ); - } - - public function testEnsuresTheHttpProtocol() - { - $handler = new StreamHandler(); - $result = $handler([ - 'http_method' => 'GET', - 'url' => 'ftp://localhost:123', - ]); - $this->assertArrayHasKey('error', $result); - $this->assertContains( - 'URL is invalid: ftp://localhost:123', - $result['error']->getMessage() - ); - } - - public function testStreamAttributeKeepsStreamOpen() - { - $this->queueRes(); - $handler = new StreamHandler(); - $response = $handler([ - 'http_method' => 'PUT', - 'uri' => '/foo', - 'query_string' => 'baz=bar', - 'headers' => [ - 'host' => [Server::$host], - 'Foo' => ['Bar'], - ], - 'body' => 'test', - 'client' => ['stream' => true], - ]); - - $this->assertEquals(200, $response['status']); - $this->assertEquals('OK', $response['reason']); - $this->assertEquals('8', Core::header($response, 'Content-Length')); - $body = $response['body']; - $this->assertTrue(is_resource($body)); - $this->assertEquals('http', stream_get_meta_data($body)['wrapper_type']); - $this->assertEquals('hi there', stream_get_contents($body)); - fclose($body); - $sent = Server::received()[0]; - $this->assertEquals('PUT', $sent['http_method']); - $this->assertEquals('/foo', $sent['uri']); - $this->assertEquals('baz=bar', $sent['query_string']); - $this->assertEquals('/foo?baz=bar', $sent['resource']); - $this->assertEquals('127.0.0.1:8125', Core::header($sent, 'host')); - $this->assertEquals('Bar', Core::header($sent, 'foo')); - } - - public function testDrainsResponseIntoTempStream() - { - $this->queueRes(); - $handler = new StreamHandler(); - $response = $handler([ - 'http_method' => 'GET', - 'uri' => '/', - 'headers' => ['host' => [Server::$host]], - ]); - $body = $response['body']; - $this->assertEquals('php://temp', stream_get_meta_data($body)['uri']); - $this->assertEquals('hi', fread($body, 2)); - fclose($body); - } - - public function testDrainsResponseIntoSaveToBody() - { - $r = fopen('php://temp', 'r+'); - $this->queueRes(); - $handler = new StreamHandler(); - $response = $handler([ - 'http_method' => 'GET', - 'uri' => '/', - 'headers' => ['host' => [Server::$host]], - 'client' => ['save_to' => $r], - ]); - $body = $response['body']; - $this->assertEquals('php://temp', stream_get_meta_data($body)['uri']); - $this->assertEquals('hi', fread($body, 2)); - $this->assertEquals(' there', stream_get_contents($r)); - fclose($r); - } - - public function testDrainsResponseIntoSaveToBodyAtPath() - { - $tmpfname = tempnam('/tmp', 'save_to_path'); - $this->queueRes(); - $handler = new StreamHandler(); - $response = $handler([ - 'http_method' => 'GET', - 'uri' => '/', - 'headers' => ['host' => [Server::$host]], - 'client' => ['save_to' => $tmpfname], - ]); - $body = $response['body']; - $this->assertInstanceOf('GuzzleHttp\Stream\StreamInterface', $body); - $this->assertEquals($tmpfname, $body->getMetadata('uri')); - $this->assertEquals('hi', $body->read(2)); - $body->close(); - unlink($tmpfname); - } - - public function testAutomaticallyDecompressGzip() - { - Server::flush(); - $content = gzencode('test'); - Server::enqueue([ - [ - 'status' => 200, - 'reason' => 'OK', - 'headers' => [ - 'Content-Encoding' => ['gzip'], - 'Content-Length' => [strlen($content)], - ], - 'body' => $content, - ], - ]); - - $handler = new StreamHandler(); - $response = $handler([ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - 'uri' => '/', - 'client' => ['decode_content' => true], - ]); - $this->assertEquals('test', Core::body($response)); - } - - public function testDoesNotForceGzipDecode() - { - Server::flush(); - $content = gzencode('test'); - Server::enqueue([ - [ - 'status' => 200, - 'reason' => 'OK', - 'headers' => [ - 'Content-Encoding' => ['gzip'], - 'Content-Length' => [strlen($content)], - ], - 'body' => $content, - ], - ]); - - $handler = new StreamHandler(); - $response = $handler([ - 'http_method' => 'GET', - 'headers' => ['host' => [Server::$host]], - 'uri' => '/', - 'client' => ['stream' => true, 'decode_content' => false], - ]); - $this->assertSame($content, Core::body($response)); - } - - public function testProtocolVersion() - { - $this->queueRes(); - $handler = new StreamHandler(); - $handler([ - 'http_method' => 'GET', - 'uri' => '/', - 'headers' => ['host' => [Server::$host]], - 'version' => 1.0, - ]); - - $this->assertEquals(1.0, Server::received()[0]['version']); - } - - protected function getSendResult(array $opts) - { - $this->queueRes(); - $handler = new StreamHandler(); - $opts['stream'] = true; - return $handler([ - 'http_method' => 'GET', - 'uri' => '/', - 'headers' => ['host' => [Server::$host]], - 'client' => $opts, - ]); - } - - public function testAddsProxy() - { - $res = $this->getSendResult(['stream' => true, 'proxy' => '127.0.0.1:8125']); - $opts = stream_context_get_options($res['body']); - $this->assertEquals('127.0.0.1:8125', $opts['http']['proxy']); - } - - public function testAddsTimeout() - { - $res = $this->getSendResult(['stream' => true, 'timeout' => 200]); - $opts = stream_context_get_options($res['body']); - $this->assertEquals(200, $opts['http']['timeout']); - } - - public function testVerifiesVerifyIsValidIfPath() - { - $res = $this->getSendResult(['verify' => '/does/not/exist']); - $this->assertContains( - 'SSL CA bundle not found: /does/not/exist', - (string) $res['error'] - ); - } - - public function testVerifyCanBeDisabled() - { - $res = $this->getSendResult(['verify' => false]); - $this->assertArrayNotHasKey('error', $res); - } - - public function testVerifiesCertIfValidPath() - { - $res = $this->getSendResult(['cert' => '/does/not/exist']); - $this->assertContains( - 'SSL certificate not found: /does/not/exist', - (string) $res['error'] - ); - } - - public function testVerifyCanBeSetToPath() - { - $path = $path = ClientUtils::getDefaultCaBundle(); - $res = $this->getSendResult(['verify' => $path]); - $this->assertArrayNotHasKey('error', $res); - $opts = stream_context_get_options($res['body']); - $this->assertEquals(true, $opts['ssl']['verify_peer']); - $this->assertEquals($path, $opts['ssl']['cafile']); - $this->assertTrue(file_exists($opts['ssl']['cafile'])); - } - - public function testUsesSystemDefaultBundle() - { - $path = $path = ClientUtils::getDefaultCaBundle(); - $res = $this->getSendResult(['verify' => true]); - $this->assertArrayNotHasKey('error', $res); - $opts = stream_context_get_options($res['body']); - if (PHP_VERSION_ID < 50600) { - $this->assertEquals($path, $opts['ssl']['cafile']); - } - } - - public function testEnsuresVerifyOptionIsValid() - { - $res = $this->getSendResult(['verify' => 10]); - $this->assertContains( - 'Invalid verify request option', - (string) $res['error'] - ); - } - - public function testCanSetPasswordWhenSettingCert() - { - $path = __FILE__; - $res = $this->getSendResult(['cert' => [$path, 'foo']]); - $opts = stream_context_get_options($res['body']); - $this->assertEquals($path, $opts['ssl']['local_cert']); - $this->assertEquals('foo', $opts['ssl']['passphrase']); - } - - public function testDebugAttributeWritesToStream() - { - $this->queueRes(); - $f = fopen('php://temp', 'w+'); - $this->getSendResult(['debug' => $f]); - fseek($f, 0); - $contents = stream_get_contents($f); - $this->assertContains('<GET http://127.0.0.1:8125/> [CONNECT]', $contents); - $this->assertContains('<GET http://127.0.0.1:8125/> [FILE_SIZE_IS]', $contents); - $this->assertContains('<GET http://127.0.0.1:8125/> [PROGRESS]', $contents); - } - - public function testDebugAttributeWritesStreamInfoToBuffer() - { - $called = false; - $this->queueRes(); - $buffer = fopen('php://temp', 'r+'); - $this->getSendResult([ - 'progress' => function () use (&$called) { $called = true; }, - 'debug' => $buffer, - ]); - fseek($buffer, 0); - $contents = stream_get_contents($buffer); - $this->assertContains('<GET http://127.0.0.1:8125/> [CONNECT]', $contents); - $this->assertContains('<GET http://127.0.0.1:8125/> [FILE_SIZE_IS] message: "Content-Length: 8"', $contents); - $this->assertContains('<GET http://127.0.0.1:8125/> [PROGRESS] bytes_max: "8"', $contents); - $this->assertTrue($called); - } - - public function testEmitsProgressInformation() - { - $called = []; - $this->queueRes(); - $this->getSendResult([ - 'progress' => function () use (&$called) { - $called[] = func_get_args(); - }, - ]); - $this->assertNotEmpty($called); - $this->assertEquals(8, $called[0][0]); - $this->assertEquals(0, $called[0][1]); - } - - public function testEmitsProgressInformationAndDebugInformation() - { - $called = []; - $this->queueRes(); - $buffer = fopen('php://memory', 'w+'); - $this->getSendResult([ - 'debug' => $buffer, - 'progress' => function () use (&$called) { - $called[] = func_get_args(); - }, - ]); - $this->assertNotEmpty($called); - $this->assertEquals(8, $called[0][0]); - $this->assertEquals(0, $called[0][1]); - rewind($buffer); - $this->assertNotEmpty(stream_get_contents($buffer)); - fclose($buffer); - } - - public function testAddsProxyByProtocol() - { - $url = str_replace('http', 'tcp', Server::$url); - $res = $this->getSendResult(['proxy' => ['http' => $url]]); - $opts = stream_context_get_options($res['body']); - $this->assertEquals($url, $opts['http']['proxy']); - } - - public function testPerformsShallowMergeOfCustomContextOptions() - { - $res = $this->getSendResult([ - 'stream_context' => [ - 'http' => [ - 'request_fulluri' => true, - 'method' => 'HEAD', - ], - 'socket' => [ - 'bindto' => '127.0.0.1:0', - ], - 'ssl' => [ - 'verify_peer' => false, - ], - ], - ]); - - $opts = stream_context_get_options($res['body']); - $this->assertEquals('HEAD', $opts['http']['method']); - $this->assertTrue($opts['http']['request_fulluri']); - $this->assertFalse($opts['ssl']['verify_peer']); - $this->assertEquals('127.0.0.1:0', $opts['socket']['bindto']); - } - - public function testEnsuresThatStreamContextIsAnArray() - { - $res = $this->getSendResult(['stream_context' => 'foo']); - $this->assertContains( - 'stream_context must be an array', - (string) $res['error'] - ); - } - - public function testDoesNotAddContentTypeByDefault() - { - $this->queueRes(); - $handler = new StreamHandler(); - $handler([ - 'http_method' => 'PUT', - 'uri' => '/', - 'headers' => ['host' => [Server::$host], 'content-length' => [3]], - 'body' => 'foo', - ]); - $req = Server::received()[0]; - $this->assertEquals('', Core::header($req, 'Content-Type')); - $this->assertEquals(3, Core::header($req, 'Content-Length')); - } - - private function queueRes() - { - Server::flush(); - Server::enqueue([ - [ - 'status' => 200, - 'reason' => 'OK', - 'headers' => [ - 'Foo' => ['Bar'], - 'Content-Length' => [8], - ], - 'body' => 'hi there', - ], - ]); - } - - public function testSupports100Continue() - { - Server::flush(); - Server::enqueue([ - [ - 'status' => '200', - 'reason' => 'OK', - 'headers' => [ - 'Test' => ['Hello'], - 'Content-Length' => ['4'], - ], - 'body' => 'test', - ], - ]); - - $request = [ - 'http_method' => 'PUT', - 'headers' => [ - 'Host' => [Server::$host], - 'Expect' => ['100-Continue'], - ], - 'body' => 'test', - ]; - - $handler = new StreamHandler(); - $response = $handler($request); - $this->assertEquals(200, $response['status']); - $this->assertEquals('OK', $response['reason']); - $this->assertEquals(['Hello'], $response['headers']['Test']); - $this->assertEquals(['4'], $response['headers']['Content-Length']); - $this->assertEquals('test', Core::body($response)); - } -} diff --git a/core/vendor/guzzlehttp/ringphp/tests/Client/server.js b/core/vendor/guzzlehttp/ringphp/tests/Client/server.js deleted file mode 100644 index 6a03e33ab222ab499d81c0f50df3c5678ac51a3b..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/tests/Client/server.js +++ /dev/null @@ -1,241 +0,0 @@ -/** - * 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:8125 - * - * - Enqueue responses - * > PUT /guzzle-server/responses - * > Host: 127.0.0.1:8125 - * > - * > [{'status': 200, 'reason': 'OK', 'headers': {}, 'body': '' }] - * - * - Get the received requests - * > GET /guzzle-server/requests - * > Host: 127.0.0.1:8125 - * - * < HTTP/1.1 200 OK - * < - * < [{'http_method': 'GET', 'uri': '/', 'headers': {}, 'body': 'string'}] - * - * - Attempt access to the secure area - * > GET /secure/by-digest/qop-auth/guzzle-server/requests - * > Host: 127.0.0.1:8125 - * - * < HTTP/1.1 401 Unauthorized - * < WWW-Authenticate: Digest realm="Digest Test", qop="auth", nonce="0796e98e1aeef43141fab2a66bf4521a", algorithm="MD5", stale="false" - * < - * < 401 Unauthorized - * - * - Shutdown the server - * > DELETE /guzzle-server - * > Host: 127.0.0.1:8125 - * - * @package Guzzle PHP <http://www.guzzlephp.org> - * @license See the LICENSE file that was distributed with this source code. - */ - -var http = require('http'); -var url = require('url'); - -/** - * Guzzle node.js server - * @class - */ -var GuzzleServer = function(port, log) { - - this.port = port; - this.log = log; - this.responses = []; - this.requests = []; - var that = this; - - var md5 = function(input) { - var crypto = require('crypto'); - var hasher = crypto.createHash('md5'); - hasher.update(input); - return hasher.digest('hex'); - } - - /** - * Node.js HTTP server authentication module. - * - * It is only initialized on demand (by loadAuthentifier). This avoids - * requiring the dependency to http-auth on standard operations, and the - * performance hit at startup. - */ - var auth; - - /** - * Provides authentication handlers (Basic, Digest). - */ - var loadAuthentifier = function(type, options) { - var typeId = type; - if (type == 'digest') { - typeId += '.'+(options && options.qop ? options.qop : 'none'); - } - if (!loadAuthentifier[typeId]) { - if (!auth) { - try { - auth = require('http-auth'); - } catch (e) { - if (e.code == 'MODULE_NOT_FOUND') { - return; - } - } - } - switch (type) { - case 'digest': - var digestParams = { - realm: 'Digest Test', - login: 'me', - password: 'test' - }; - if (options && options.qop) { - digestParams.qop = options.qop; - } - loadAuthentifier[typeId] = auth.digest(digestParams, function(username, callback) { - callback(md5(digestParams.login + ':' + digestParams.realm + ':' + digestParams.password)); - }); - break - } - } - return loadAuthentifier[typeId]; - }; - - var firewallRequest = function(request, req, res, requestHandlerCallback) { - var securedAreaUriParts = request.uri.match(/^\/secure\/by-(digest)(\/qop-([^\/]*))?(\/.*)$/); - if (securedAreaUriParts) { - var authentifier = loadAuthentifier(securedAreaUriParts[1], { qop: securedAreaUriParts[2] }); - if (!authentifier) { - res.writeHead(501, 'HTTP authentication not implemented', { 'Content-Length': 0 }); - res.end(); - return; - } - authentifier.check(req, res, function(req, res) { - req.url = securedAreaUriParts[4]; - requestHandlerCallback(request, req, res); - }); - } else { - requestHandlerCallback(request, req, res); - } - }; - - var controlRequest = function(request, req, res) { - if (req.url == '/guzzle-server/perf') { - res.writeHead(200, 'OK', {'Content-Length': 16}); - res.end('Body of response'); - } else 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(); - if (that.log) { - console.log('Flushing requests'); - } - } else if (req.url == '/guzzle-server') { - // Shutdown the server - res.writeHead(200, 'OK', { 'Content-Length': 0, 'Connection': 'close' }); - res.end(); - if (that.log) { - console.log('Shutting down'); - } - that.server.close(); - } - } else if (req.method == 'GET') { - if (req.url === '/guzzle-server/requests') { - if (that.log) { - console.log('Sending received requests'); - } - // Get received requests - var body = JSON.stringify(that.requests); - res.writeHead(200, 'OK', { 'Content-Length': body.length }); - res.end(body); - } - } else if (req.method == 'PUT' && req.url == '/guzzle-server/responses') { - if (that.log) { - console.log('Adding responses...'); - } - if (!request.body) { - if (that.log) { - console.log('No response data was provided'); - } - res.writeHead(400, 'NO RESPONSES IN REQUEST', { 'Content-Length': 0 }); - } else { - that.responses = eval('(' + request.body + ')'); - for (var i = 0; i < that.responses.length; i++) { - if (that.responses[i].body) { - that.responses[i].body = new Buffer(that.responses[i].body, 'base64'); - } - } - if (that.log) { - console.log(that.responses); - } - res.writeHead(200, 'OK', { 'Content-Length': 0 }); - } - res.end(); - } - }; - - var receivedRequest = function(request, req, res) { - if (req.url.indexOf('/guzzle-server') === 0) { - controlRequest(request, req, res); - } else if (req.url.indexOf('/guzzle-server') == -1 && !that.responses.length) { - res.writeHead(500); - res.end('No responses in queue'); - } else { - if (that.log) { - console.log('Returning response from queue and adding request'); - } - that.requests.push(request); - var response = that.responses.shift(); - res.writeHead(response.status, response.reason, response.headers); - res.end(response.body); - } - }; - - this.start = function() { - - that.server = http.createServer(function(req, res) { - - var parts = url.parse(req.url, false); - var request = { - http_method: req.method, - scheme: parts.scheme, - uri: parts.pathname, - query_string: parts.query, - headers: req.headers, - version: req.httpVersion, - body: '' - }; - - // Receive each chunk of the request body - req.addListener('data', function(chunk) { - request.body += chunk; - }); - - // Called when the request completes - req.addListener('end', function() { - firewallRequest(request, req, res, receivedRequest); - }); - }); - - that.server.listen(this.port, '127.0.0.1'); - - if (this.log) { - console.log('Server running at http://127.0.0.1:8125/'); - } - }; -}; - -// Get the port from the arguments -port = process.argv.length >= 3 ? process.argv[2] : 8125; -log = process.argv.length >= 4 ? process.argv[3] : false; - -// Start the server -server = new GuzzleServer(port, log); -server.start(); diff --git a/core/vendor/guzzlehttp/ringphp/tests/CoreTest.php b/core/vendor/guzzlehttp/ringphp/tests/CoreTest.php deleted file mode 100644 index 49522f26b7e851c393d98878db9d4b5bfcd33023..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/tests/CoreTest.php +++ /dev/null @@ -1,336 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Ring; - -use GuzzleHttp\Ring\Core; -use GuzzleHttp\Ring\Future\CompletedFutureArray; -use GuzzleHttp\Ring\Future\FutureArray; -use GuzzleHttp\Stream\Stream; -use React\Promise\Deferred; - -class CoreTest extends \PHPUnit_Framework_TestCase -{ - public function testReturnsNullNoHeadersAreSet() - { - $this->assertNull(Core::header([], 'Foo')); - $this->assertNull(Core::firstHeader([], 'Foo')); - } - - public function testChecksIfHasHeader() - { - $message = [ - 'headers' => [ - 'Foo' => ['Bar', 'Baz'], - 'foo' => ['hello'], - 'bar' => ['1'] - ] - ]; - $this->assertTrue(Core::hasHeader($message, 'Foo')); - $this->assertTrue(Core::hasHeader($message, 'foo')); - $this->assertTrue(Core::hasHeader($message, 'FoO')); - $this->assertTrue(Core::hasHeader($message, 'bar')); - $this->assertFalse(Core::hasHeader($message, 'barr')); - } - - public function testReturnsFirstHeaderWhenSimple() - { - $this->assertEquals('Bar', Core::firstHeader([ - 'headers' => ['Foo' => ['Bar', 'Baz']], - ], 'Foo')); - } - - public function testReturnsFirstHeaderWhenMultiplePerLine() - { - $this->assertEquals('Bar', Core::firstHeader([ - 'headers' => ['Foo' => ['Bar, Baz']], - ], 'Foo')); - } - - public function testExtractsCaseInsensitiveHeader() - { - $this->assertEquals( - 'hello', - Core::header(['headers' => ['foo' => ['hello']]], 'FoO') - ); - } - - public function testExtractsCaseInsensitiveHeaderLines() - { - $this->assertEquals( - ['a', 'b', 'c', 'd'], - Core::headerLines([ - 'headers' => [ - 'foo' => ['a', 'b'], - 'Foo' => ['c', 'd'] - ] - ], 'foo') - ); - } - - public function testExtractsHeaderLines() - { - $this->assertEquals( - ['bar', 'baz'], - Core::headerLines([ - 'headers' => [ - 'Foo' => ['bar', 'baz'], - ], - ], 'Foo') - ); - } - - public function testExtractsHeaderAsString() - { - $this->assertEquals( - 'bar, baz', - Core::header([ - 'headers' => [ - 'Foo' => ['bar', 'baz'], - ], - ], 'Foo', true) - ); - } - - public function testReturnsNullWhenHeaderNotFound() - { - $this->assertNull(Core::header(['headers' => []], 'Foo')); - } - - public function testRemovesHeaders() - { - $message = [ - 'headers' => [ - 'foo' => ['bar'], - 'Foo' => ['bam'], - 'baz' => ['123'], - ], - ]; - - $this->assertSame($message, Core::removeHeader($message, 'bam')); - $this->assertEquals([ - 'headers' => ['baz' => ['123']], - ], Core::removeHeader($message, 'foo')); - } - - public function testCreatesUrl() - { - $req = [ - 'scheme' => 'http', - 'headers' => ['host' => ['foo.com']], - 'uri' => '/', - ]; - - $this->assertEquals('http://foo.com/', Core::url($req)); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage No Host header was provided - */ - public function testEnsuresHostIsAvailableWhenCreatingUrls() - { - Core::url([]); - } - - public function testCreatesUrlWithQueryString() - { - $req = [ - 'scheme' => 'http', - 'headers' => ['host' => ['foo.com']], - 'uri' => '/', - 'query_string' => 'foo=baz', - ]; - - $this->assertEquals('http://foo.com/?foo=baz', Core::url($req)); - } - - public function testUsesUrlIfSet() - { - $req = ['url' => 'http://foo.com']; - $this->assertEquals('http://foo.com', Core::url($req)); - } - - public function testReturnsNullWhenNoBody() - { - $this->assertNull(Core::body([])); - } - - public function testReturnsStreamAsString() - { - $this->assertEquals( - 'foo', - Core::body(['body' => Stream::factory('foo')]) - ); - } - - public function testReturnsString() - { - $this->assertEquals('foo', Core::body(['body' => 'foo'])); - } - - public function testReturnsResourceContent() - { - $r = fopen('php://memory', 'w+'); - fwrite($r, 'foo'); - rewind($r); - $this->assertEquals('foo', Core::body(['body' => $r])); - fclose($r); - } - - public function testReturnsIteratorContent() - { - $a = new \ArrayIterator(['a', 'b', 'cd', '']); - $this->assertEquals('abcd', Core::body(['body' => $a])); - } - - public function testReturnsObjectToString() - { - $this->assertEquals('foo', Core::body(['body' => new StrClass])); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testEnsuresBodyIsValid() - { - Core::body(['body' => false]); - } - - public function testParsesHeadersFromLines() - { - $lines = ['Foo: bar', 'Foo: baz', 'Abc: 123', 'Def: a, b']; - $this->assertEquals([ - 'Foo' => ['bar', 'baz'], - 'Abc' => ['123'], - 'Def' => ['a, b'], - ], Core::headersFromLines($lines)); - } - - public function testParsesHeadersFromLinesWithMultipleLines() - { - $lines = ['Foo: bar', 'Foo: baz', 'Foo: 123']; - $this->assertEquals([ - 'Foo' => ['bar', 'baz', '123'], - ], Core::headersFromLines($lines)); - } - - public function testCreatesArrayCallFunctions() - { - $called = []; - $a = function ($a, $b) use (&$called) { - $called['a'] = func_get_args(); - }; - $b = function ($a, $b) use (&$called) { - $called['b'] = func_get_args(); - }; - $c = Core::callArray([$a, $b]); - $c(1, 2); - $this->assertEquals([1, 2], $called['a']); - $this->assertEquals([1, 2], $called['b']); - } - - public function testRewindsGuzzleStreams() - { - $str = Stream::factory('foo'); - $this->assertTrue(Core::rewindBody(['body' => $str])); - } - - public function testRewindsStreams() - { - $str = Stream::factory('foo')->detach(); - $this->assertTrue(Core::rewindBody(['body' => $str])); - } - - public function testRewindsIterators() - { - $iter = new \ArrayIterator(['foo']); - $this->assertTrue(Core::rewindBody(['body' => $iter])); - } - - public function testRewindsStrings() - { - $this->assertTrue(Core::rewindBody(['body' => 'hi'])); - } - - public function testRewindsToStrings() - { - $this->assertTrue(Core::rewindBody(['body' => new StrClass()])); - } - - public function typeProvider() - { - return [ - ['foo', 'string(3) "foo"'], - [true, 'bool(true)'], - [false, 'bool(false)'], - [10, 'int(10)'], - [1.0, 'float(1)'], - [new StrClass(), 'object(GuzzleHttp\Tests\Ring\StrClass)'], - [['foo'], 'array(1)'] - ]; - } - - /** - * @dataProvider typeProvider - */ - public function testDescribesType($input, $output) - { - $this->assertEquals($output, Core::describeType($input)); - } - - public function testDoesSleep() - { - $t = microtime(true); - $expected = $t + (100 / 1000); - Core::doSleep(['client' => ['delay' => 100]]); - $this->assertGreaterThanOrEqual($expected, microtime(true)); - } - - public function testProxiesFuture() - { - $f = new CompletedFutureArray(['status' => 200]); - $res = null; - $proxied = Core::proxy($f, function ($value) use (&$res) { - $value['foo'] = 'bar'; - $res = $value; - return $value; - }); - $this->assertNotSame($f, $proxied); - $this->assertEquals(200, $f->wait()['status']); - $this->assertArrayNotHasKey('foo', $f->wait()); - $this->assertEquals('bar', $proxied->wait()['foo']); - $this->assertEquals(200, $proxied->wait()['status']); - } - - public function testProxiesDeferredFuture() - { - $d = new Deferred(); - $f = new FutureArray($d->promise()); - $f2 = Core::proxy($f); - $d->resolve(['foo' => 'bar']); - $this->assertEquals('bar', $f['foo']); - $this->assertEquals('bar', $f2['foo']); - } - - public function testProxiesDeferredFutureFailure() - { - $d = new Deferred(); - $f = new FutureArray($d->promise()); - $f2 = Core::proxy($f); - $d->reject(new \Exception('foo')); - try { - $f2['hello?']; - $this->fail('did not throw'); - } catch (\Exception $e) { - $this->assertEquals('foo', $e->getMessage()); - } - - } -} - -final class StrClass -{ - public function __toString() - { - return 'foo'; - } -} diff --git a/core/vendor/guzzlehttp/ringphp/tests/Future/CompletedFutureArrayTest.php b/core/vendor/guzzlehttp/ringphp/tests/Future/CompletedFutureArrayTest.php deleted file mode 100644 index 82d7efbf5caa958a54c786efc9a0fa572e4b3f21..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/tests/Future/CompletedFutureArrayTest.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Ring\Future; - -use GuzzleHttp\Ring\Future\CompletedFutureArray; - -class CompletedFutureArrayTest extends \PHPUnit_Framework_TestCase -{ - public function testReturnsAsArray() - { - $f = new CompletedFutureArray(['foo' => 'bar']); - $this->assertEquals('bar', $f['foo']); - $this->assertFalse(isset($f['baz'])); - $f['abc'] = '123'; - $this->assertTrue(isset($f['abc'])); - $this->assertEquals(['foo' => 'bar', 'abc' => '123'], iterator_to_array($f)); - $this->assertEquals(2, count($f)); - unset($f['abc']); - $this->assertEquals(1, count($f)); - $this->assertEquals(['foo' => 'bar'], iterator_to_array($f)); - } -} diff --git a/core/vendor/guzzlehttp/ringphp/tests/Future/CompletedFutureValueTest.php b/core/vendor/guzzlehttp/ringphp/tests/Future/CompletedFutureValueTest.php deleted file mode 100644 index 6ded40dfba63f90c7cfb08f70b32c991a64db9ac..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/tests/Future/CompletedFutureValueTest.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Ring\Future; - -use GuzzleHttp\Ring\Exception\CancelledFutureAccessException; -use GuzzleHttp\Ring\Future\CompletedFutureValue; - -class CompletedFutureValueTest extends \PHPUnit_Framework_TestCase -{ - public function testReturnsValue() - { - $f = new CompletedFutureValue('hi'); - $this->assertEquals('hi', $f->wait()); - $f->cancel(); - - $a = null; - $f->then(function ($v) use (&$a) { - $a = $v; - }); - $this->assertSame('hi', $a); - } - - public function testThrows() - { - $ex = new \Exception('foo'); - $f = new CompletedFutureValue(null, $ex); - $f->cancel(); - try { - $f->wait(); - $this->fail('did not throw'); - } catch (\Exception $e) { - $this->assertSame($e, $ex); - } - } - - public function testMarksAsCancelled() - { - $ex = new CancelledFutureAccessException(); - $f = new CompletedFutureValue(null, $ex); - try { - $f->wait(); - $this->fail('did not throw'); - } catch (\Exception $e) { - $this->assertSame($e, $ex); - } - } -} diff --git a/core/vendor/guzzlehttp/ringphp/tests/Future/FutureArrayTest.php b/core/vendor/guzzlehttp/ringphp/tests/Future/FutureArrayTest.php deleted file mode 100644 index 0e09f5afa837c54815ca3931a3bd7aa9bf737fa8..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/tests/Future/FutureArrayTest.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Ring\Future; - -use GuzzleHttp\Ring\Future\FutureArray; -use React\Promise\Deferred; - -class FutureArrayTest extends \PHPUnit_Framework_TestCase -{ - public function testLazilyCallsDeref() - { - $c = false; - $deferred = new Deferred(); - $f = new FutureArray( - $deferred->promise(), - function () use (&$c, $deferred) { - $c = true; - $deferred->resolve(['status' => 200]); - } - ); - $this->assertFalse($c); - $this->assertFalse($this->readAttribute($f, 'isRealized')); - $this->assertEquals(200, $f['status']); - $this->assertTrue($c); - } - - public function testActsLikeArray() - { - $deferred = new Deferred(); - $f = new FutureArray( - $deferred->promise(), - function () use (&$c, $deferred) { - $deferred->resolve(['status' => 200]); - } - ); - - $this->assertTrue(isset($f['status'])); - $this->assertEquals(200, $f['status']); - $this->assertEquals(['status' => 200], $f->wait()); - $this->assertEquals(1, count($f)); - $f['baz'] = 10; - $this->assertEquals(10, $f['baz']); - unset($f['baz']); - $this->assertFalse(isset($f['baz'])); - $this->assertEquals(['status' => 200], iterator_to_array($f)); - } - - /** - * @expectedException \RuntimeException - */ - public function testThrowsWhenAccessingInvalidProperty() - { - $deferred = new Deferred(); - $f = new FutureArray($deferred->promise(), function () {}); - $f->foo; - } -} diff --git a/core/vendor/guzzlehttp/ringphp/tests/Future/FutureValueTest.php b/core/vendor/guzzlehttp/ringphp/tests/Future/FutureValueTest.php deleted file mode 100644 index d59c543d0ff28af2ede0c720c059f96e87409704..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/tests/Future/FutureValueTest.php +++ /dev/null @@ -1,109 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Ring\Future; - -use GuzzleHttp\Ring\Exception\CancelledFutureAccessException; -use GuzzleHttp\Ring\Future\FutureValue; -use React\Promise\Deferred; - -class FutureValueTest extends \PHPUnit_Framework_TestCase -{ - public function testDerefReturnsValue() - { - $called = 0; - $deferred = new Deferred(); - - $f = new FutureValue( - $deferred->promise(), - function () use ($deferred, &$called) { - $called++; - $deferred->resolve('foo'); - } - ); - - $this->assertEquals('foo', $f->wait()); - $this->assertEquals(1, $called); - $this->assertEquals('foo', $f->wait()); - $this->assertEquals(1, $called); - $f->cancel(); - $this->assertTrue($this->readAttribute($f, 'isRealized')); - } - - /** - * @expectedException \GuzzleHttp\Ring\Exception\CancelledFutureAccessException - */ - public function testThrowsWhenAccessingCancelled() - { - $f = new FutureValue( - (new Deferred())->promise(), - function () {}, - function () { return true; } - ); - $f->cancel(); - $f->wait(); - } - - /** - * @expectedException \OutOfBoundsException - */ - public function testThrowsWhenDerefFailure() - { - $called = false; - $deferred = new Deferred(); - $f = new FutureValue( - $deferred->promise(), - function () use(&$called) { - $called = true; - } - ); - $deferred->reject(new \OutOfBoundsException()); - $f->wait(); - $this->assertFalse($called); - } - - /** - * @expectedException \GuzzleHttp\Ring\Exception\RingException - * @expectedExceptionMessage Waiting did not resolve future - */ - public function testThrowsWhenDerefDoesNotResolve() - { - $deferred = new Deferred(); - $f = new FutureValue( - $deferred->promise(), - function () use(&$called) { - $called = true; - } - ); - $f->wait(); - } - - public function testThrowingCancelledFutureAccessExceptionCancels() - { - $deferred = new Deferred(); - $f = new FutureValue( - $deferred->promise(), - function () use ($deferred) { - throw new CancelledFutureAccessException(); - } - ); - try { - $f->wait(); - $this->fail('did not throw'); - } catch (CancelledFutureAccessException $e) {} - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage foo - */ - public function testThrowingExceptionInDerefMarksAsFailed() - { - $deferred = new Deferred(); - $f = new FutureValue( - $deferred->promise(), - function () { - throw new \Exception('foo'); - } - ); - $f->wait(); - } -} diff --git a/core/vendor/guzzlehttp/ringphp/tests/bootstrap.php b/core/vendor/guzzlehttp/ringphp/tests/bootstrap.php deleted file mode 100644 index 017610fe0d86c2258a5baacd5b889ebfc002fb28..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/ringphp/tests/bootstrap.php +++ /dev/null @@ -1,11 +0,0 @@ -<?php -require __DIR__ . '/../vendor/autoload.php'; -require __DIR__ . '/Client/Server.php'; - -use GuzzleHttp\Tests\Ring\Client\Server; - -Server::start(); - -register_shutdown_function(function () { - Server::stop(); -}); diff --git a/core/vendor/guzzlehttp/streams/.gitignore b/core/vendor/guzzlehttp/streams/.gitignore deleted file mode 100644 index c33d3965fddada3e75ebb868d561a0768bb616e2..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/streams/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -.idea -.DS_STORE -coverage -phpunit.xml -composer.lock -vendor/ diff --git a/core/vendor/guzzlehttp/streams/.travis.yml b/core/vendor/guzzlehttp/streams/.travis.yml deleted file mode 100644 index a6f8a87b7dc46e42a2ca74885674dfab0ebb7c2b..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/streams/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -language: php - -php: - - 5.4 - - 5.5 - - 5.6 - - hhvm - -before_script: - - composer self-update - - composer install --no-interaction --prefer-source --dev - -script: vendor/bin/phpunit - -matrix: - allow_failures: - - php: hhvm diff --git a/core/vendor/guzzlehttp/streams/CHANGELOG.rst b/core/vendor/guzzlehttp/streams/CHANGELOG.rst deleted file mode 100644 index 0018ffe353f1ebb94097d811755289cfba1a4b5b..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/streams/CHANGELOG.rst +++ /dev/null @@ -1,94 +0,0 @@ -========= -Changelog -========= - -3.0.0 (2014-10-12) ------------------- - -* Now supports creating streams from functions and iterators. -* Supports creating buffered streams and asynchronous streams. -* Removed ``functions.php``. Use the corresponding functions provided by - ``GuzzleHttp\Streams\Utils`` instead. -* Moved ``GuzzleHttp\Stream\MetadataStreamInterface::getMetadata`` to - ``GuzzleHttp\Stream\StreamInterface``. MetadataStreamInterface is no longer - used and is marked as deprecated. -* Added ``attach()`` to ``GuzzleHttp\Stream\StreamInterface`` for PSR-7 - compatibility. -* Removed ``flush()`` from StreamInterface. -* Removed the ``$maxLength`` parameter from - ``GuzzleHttp\Stream\StreamInterface::getContents()``. This function now - returns the entire remainder of the stream. If you want to limit the maximum - amount of data read from the stream, use the - ``GuzzleHttp\Stream\Utils::copyToString()`` function. -* Streams that return an empty string, ``''``, are no longer considered a - failure. You MUST return ``false`` to mark the read as a failure, and ensure - that any decorators you create properly return ``true`` in response to the - ``eof()`` method when the stream is consumed. -* ``GuzzleHttp\Stream\Stream::__construct``, - ``GuzzleHttp\Stream\Stream::factory``, and - ``GuzzleHttp\Stream\Utils::create`` no longer accept a size in the second - argument. They now accept an associative array of options, including the - "size" key and "metadata" key which can be used to provide custom metadata. -* Added ``GuzzleHttp\Stream\BufferStream`` to add support for buffering data, - and when read, shifting data off of the buffer. -* Added ``GuzzleHttp\Stream\NullBuffer`` which can be used as a buffer that - does not actually store any data. -* Added ``GuzzleHttp\Stream\AsyncStream`` to provide support for non-blocking - streams that can be filled by a remote source (e.g., an event-loop). If a - ``drain`` option is provided, the stream can also act as if it is a blocking - stream. - -2.1.0 (2014-08-17) ------------------- - -* Added an InflateStream to inflate gzipped or deflated content. -* Added ``flush`` to stream wrapper. -* Added the ability to easily register the GuzzleStreamWrapper if needed. - -2.0.0 (2014-08-16) ------------------- - -* Deprecated functions.php and moved all of those methods to - ``GuzzleHttp\Streams\Utils``. Use ``GuzzleHttp\Stream\Stream::factory()`` - instead of ``GuzzleHttp\Stream\create()`` to create new streams. -* Added ``flush()`` to ``StreamInterface``. This method is used to flush any - underlying stream write buffers. -* Added ``FnStream`` to easily decorate stream behavior with callables. -* ``Utils::hash`` now throws an exception when the stream cannot seek to 0. - -1.5.1 (2014-09-10) ------------------- - -* Stream metadata is grabbed from the underlying stream each time - ``getMetadata`` is called rather than returning a value from a cache. -* Properly closing all underlying streams when AppendStream is closed. -* Seek functions no longer throw exceptions. -* LazyOpenStream now correctly returns the underlying stream resource when - detached. - -1.5.0 (2014-08-07) ------------------- - -* Added ``Stream\safe_open`` to open stream resources and throw exceptions - instead of raising errors. - -1.4.0 (2014-07-19) ------------------- - -* Added a LazyOpenStream - -1.3.0 (2014-07-15) ------------------- - -* Added an AppendStream to stream over multiple stream one after the other. - -1.2.0 (2014-07-15) ------------------- - -* Updated the ``detach()`` method to return the underlying stream resource or - ``null`` if it does not wrap a resource. -* Multiple fixes for how streams behave when the underlying resource is - detached -* Do not clear statcache when a stream does not have a 'uri' -* Added a fix to LimitStream -* Added a condition to ensure that functions.php can be required multiple times diff --git a/core/vendor/guzzlehttp/streams/README.rst b/core/vendor/guzzlehttp/streams/README.rst deleted file mode 100644 index baff63b37d59ac356f00f0b32b2f5c5ef22ebc9f..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/streams/README.rst +++ /dev/null @@ -1,36 +0,0 @@ -============== -Guzzle Streams -============== - -Provides a simple abstraction over streams of data. - -This library is used in `Guzzle 5 <https://github.com/guzzle/guzzle>`_, and is -(currently) compatible with the WIP PSR-7. - -Installation -============ - -This package can be installed easily using `Composer <http://getcomposer.org>`_. -Simply add the following to the composer.json file at the root of your project: - -.. code-block:: javascript - - { - "require": { - "guzzlehttp/streams": "~3.0" - } - } - -Then install your dependencies using ``composer.phar install``. - -Documentation -============= - -The documentation for this package can be found on the main Guzzle website at -http://docs.guzzlephp.org/en/guzzle4/streams.html. - -Testing -======= - -This library is tested using PHPUnit. You'll need to install the dependencies -using `Composer <http://getcomposer.org>`_ then run ``make test``. diff --git a/core/vendor/guzzlehttp/streams/phpunit.xml.dist b/core/vendor/guzzlehttp/streams/phpunit.xml.dist deleted file mode 100644 index 6e758c1927bf71d8c865ea8cd6bfc6bb928a0fbf..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/streams/phpunit.xml.dist +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<phpunit bootstrap="./vendor/autoload.php" - colors="true"> - <testsuites> - <testsuite> - <directory>tests</directory> - </testsuite> - </testsuites> - <filter> - <whitelist> - <directory suffix=".php">src</directory> - <exclude> - <file>src/functions.php</file> - </exclude> - </whitelist> - </filter> -</phpunit> diff --git a/core/vendor/guzzlehttp/streams/src/AsyncReadStream.php b/core/vendor/guzzlehttp/streams/src/AsyncReadStream.php deleted file mode 100644 index 25ad96021a760538351632eea7cdb6dae32cdb71..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/streams/src/AsyncReadStream.php +++ /dev/null @@ -1,207 +0,0 @@ -<?php -namespace GuzzleHttp\Stream; - -/** - * Represents an asynchronous read-only stream that supports a drain event and - * pumping data from a source stream. - * - * The AsyncReadStream can be used as a completely asynchronous stream, meaning - * the data you can read from the stream will immediately return only - * the data that is currently buffered. - * - * AsyncReadStream can also be used in a "blocking" manner if a "pump" function - * is provided. When a caller requests more bytes than are available in the - * buffer, then the pump function is used to block until the requested number - * of bytes are available or the remote source stream has errored, closed, or - * timed-out. This behavior isn't strictly "blocking" because the pump function - * can send other transfers while waiting on the desired buffer size to be - * ready for reading (e.g., continue to tick an event loop). - * - * @unstable This class is subject to change. - */ -class AsyncReadStream implements StreamInterface -{ - use StreamDecoratorTrait; - - /** @var callable|null Fn used to notify writers the buffer has drained */ - private $drain; - - /** @var callable|null Fn used to block for more data */ - private $pump; - - /** @var int|null Highwater mark of the underlying buffer */ - private $hwm; - - /** @var bool Whether or not drain needs to be called at some point */ - private $needsDrain; - - /** @var int The expected size of the remote source */ - private $size; - - /** - * In order to utilize high water marks to tell writers to slow down, the - * provided stream must answer to the "hwm" stream metadata variable, - * providing the high water mark. If no "hwm" metadata value is available, - * then the "drain" functionality is not utilized. - * - * This class accepts an associative array of configuration options. - * - * - drain: (callable) Function to invoke when the stream has drained, - * meaning the buffer is now writable again because the size of the - * buffer is at an acceptable level (e.g., below the high water mark). - * The function accepts a single argument, the buffer stream object that - * has drained. - * - pump: (callable) A function that accepts the number of bytes to read - * from the source stream. This function will block until all of the data - * that was requested has been read, EOF of the source stream, or the - * source stream is closed. - * - size: (int) The expected size in bytes of the data that will be read - * (if known up-front). - * - * @param StreamInterface $buffer Buffer that contains the data that has - * been read by the event loop. - * @param array $config Associative array of options. - * - * @throws \InvalidArgumentException if the buffer is not readable and - * writable. - */ - public function __construct( - StreamInterface $buffer, - array $config = [] - ) { - if (!$buffer->isReadable() || !$buffer->isWritable()) { - throw new \InvalidArgumentException( - 'Buffer must be readable and writable' - ); - } - - if (isset($config['size'])) { - $this->size = $config['size']; - } - - static $callables = ['pump', 'drain']; - foreach ($callables as $check) { - if (isset($config[$check])) { - if (!is_callable($config[$check])) { - throw new \InvalidArgumentException( - $check . ' must be callable' - ); - } - $this->{$check} = $config[$check]; - } - } - - $this->hwm = $buffer->getMetadata('hwm'); - - // Cannot drain when there's no high water mark. - if ($this->hwm === null) { - $this->drain = null; - } - - $this->stream = $buffer; - } - - /** - * Factory method used to create new async stream and an underlying buffer - * if no buffer is provided. - * - * This function accepts the same options as AsyncReadStream::__construct, - * but added the following key value pairs: - * - * - buffer: (StreamInterface) Buffer used to buffer data. If none is - * provided, a default buffer is created. - * - hwm: (int) High water mark to use if a buffer is created on your - * behalf. - * - max_buffer: (int) If provided, wraps the utilized buffer in a - * DroppingStream decorator to ensure that buffer does not exceed a given - * length. When exceeded, the stream will begin dropping data. Set the - * max_buffer to 0, to use a NullStream which does not store data. - * - write: (callable) A function that is invoked when data is written - * to the underlying buffer. The function accepts the buffer as the first - * argument, and the data being written as the second. The function MUST - * return the number of bytes that were written or false to let writers - * know to slow down. - * - drain: (callable) See constructor documentation. - * - pump: (callable) See constructor documentation. - * - * @param array $options Associative array of options. - * - * @return array Returns an array containing the buffer used to buffer - * data, followed by the ready to use AsyncReadStream object. - */ - public static function create(array $options = []) - { - $maxBuffer = isset($options['max_buffer']) - ? $options['max_buffer'] - : null; - - if ($maxBuffer === 0) { - $buffer = new NullStream(); - } elseif (isset($options['buffer'])) { - $buffer = $options['buffer']; - } else { - $hwm = isset($options['hwm']) ? $options['hwm'] : 16384; - $buffer = new BufferStream($hwm); - } - - if ($maxBuffer > 0) { - $buffer = new DroppingStream($buffer, $options['max_buffer']); - } - - // Call the on_write callback if an on_write function was provided. - if (isset($options['write'])) { - $onWrite = $options['write']; - $buffer = FnStream::decorate($buffer, [ - 'write' => function ($string) use ($buffer, $onWrite) { - $result = $buffer->write($string); - $onWrite($buffer, $string); - return $result; - } - ]); - } - - return [$buffer, new self($buffer, $options)]; - } - - public function getSize() - { - return $this->size; - } - - public function isWritable() - { - return false; - } - - public function write($string) - { - return false; - } - - public function read($length) - { - if (!$this->needsDrain && $this->drain) { - $this->needsDrain = $this->stream->getSize() >= $this->hwm; - } - - $result = $this->stream->read($length); - - // If we need to drain, then drain when the buffer is empty. - if ($this->needsDrain && $this->stream->getSize() === 0) { - $this->needsDrain = false; - $drainFn = $this->drain; - $drainFn($this->stream); - } - - $resultLen = strlen($result); - - // If a pump was provided, the buffer is still open, and not enough - // data was given, then block until the data is provided. - if ($this->pump && $resultLen < $length) { - $pumpFn = $this->pump; - $result .= $pumpFn($length - $resultLen); - } - - return $result; - } -} diff --git a/core/vendor/guzzlehttp/streams/src/Exception/CannotAttachException.php b/core/vendor/guzzlehttp/streams/src/Exception/CannotAttachException.php deleted file mode 100644 index e631b9fa44bb6410eef808e33d19fa32edd42193..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/streams/src/Exception/CannotAttachException.php +++ /dev/null @@ -1,4 +0,0 @@ -<?php -namespace GuzzleHttp\Stream\Exception; - -class CannotAttachException extends \RuntimeException {} diff --git a/core/vendor/guzzlehttp/streams/src/MetadataStreamInterface.php b/core/vendor/guzzlehttp/streams/src/MetadataStreamInterface.php deleted file mode 100644 index c1433ad83e8158407368ba7f2829cddf662bc9f7..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/streams/src/MetadataStreamInterface.php +++ /dev/null @@ -1,11 +0,0 @@ -<?php -namespace GuzzleHttp\Stream; - -/** - * This interface is deprecated and should no longer be used. Just use - * StreamInterface now that the getMetadata method has been added to - * StreamInterface. - * - * @deprecated - */ -interface MetadataStreamInterface extends StreamInterface {} diff --git a/core/vendor/guzzlehttp/streams/src/NullStream.php b/core/vendor/guzzlehttp/streams/src/NullStream.php deleted file mode 100644 index 41ee776683a0f0f26042cc1172f08ed886618d07..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/streams/src/NullStream.php +++ /dev/null @@ -1,78 +0,0 @@ -<?php -namespace GuzzleHttp\Stream; -use GuzzleHttp\Stream\Exception\CannotAttachException; - -/** - * Does not store any data written to it. - */ -class NullStream implements StreamInterface -{ - public function __toString() - { - return ''; - } - - public function getContents() - { - return ''; - } - - public function close() {} - - public function detach() {} - - public function attach($stream) - { - throw new CannotAttachException(); - } - - public function getSize() - { - return 0; - } - - public function isReadable() - { - return true; - } - - public function isWritable() - { - return true; - } - - public function isSeekable() - { - return true; - } - - public function eof() - { - return true; - } - - public function tell() - { - return 0; - } - - public function seek($offset, $whence = SEEK_SET) - { - return false; - } - - public function read($length) - { - return false; - } - - public function write($string) - { - return strlen($string); - } - - public function getMetadata($key = null) - { - return $key ? null : []; - } -} diff --git a/core/vendor/guzzlehttp/streams/src/StreamInterface.php b/core/vendor/guzzlehttp/streams/src/StreamInterface.php deleted file mode 100644 index fd19c6f25ae12a7757f85498d75812194d59b727..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/streams/src/StreamInterface.php +++ /dev/null @@ -1,159 +0,0 @@ -<?php -namespace GuzzleHttp\Stream; - -/** - * Describes a stream instance. - */ -interface StreamInterface -{ - /** - * Attempts to seek to the beginning of the stream and reads all data into - * a string until the end of the stream is reached. - * - * Warning: This could attempt to load a large amount of data into memory. - * - * @return string - */ - public function __toString(); - - /** - * Closes the stream and any underlying resources. - */ - public function close(); - - /** - * Separates any underlying resources from the stream. - * - * After the underlying resource has been detached, the stream object is in - * an unusable state. If you wish to use a Stream object as a PHP stream - * but keep the Stream object in a consistent state, use - * {@see GuzzleHttp\Stream\GuzzleStreamWrapper::getResource}. - * - * @return resource|null Returns the underlying PHP stream resource or null - * if the Stream object did not utilize an underlying - * stream resource. - */ - public function detach(); - - /** - * Replaces the underlying stream resource with the provided stream. - * - * Use this method to replace the underlying stream with another; as an - * example, in server-side code, if you decide to return a file, you - * would replace the original content-oriented stream with the file - * stream. - * - * Any internal state such as caching of cursor position should be reset - * when attach() is called, as the stream has changed. - * - * @param resource $stream - * - * @return void - */ - public function attach($stream); - - /** - * Get the size of the stream if known - * - * @return int|null Returns the size in bytes if known, or null if unknown - */ - public function getSize(); - - /** - * Returns the current position of the file read/write pointer - * - * @return int|bool Returns the position of the file pointer or false on error - */ - public function tell(); - - /** - * Returns true if the stream is at the end of the stream. - * - * @return bool - */ - public function eof(); - - /** - * Returns whether or not the stream is seekable - * - * @return bool - */ - public function isSeekable(); - - /** - * Seek to a position in the stream - * - * @param int $offset Stream offset - * @param int $whence Specifies how the cursor position will be calculated - * based on the seek offset. Valid values are identical - * to the built-in PHP $whence values for `fseek()`. - * SEEK_SET: Set position equal to offset bytes - * SEEK_CUR: Set position to current location plus offset - * SEEK_END: Set position to end-of-stream plus offset - * - * @return bool Returns true on success or false on failure - * @link http://www.php.net/manual/en/function.fseek.php - */ - public function seek($offset, $whence = SEEK_SET); - - /** - * Returns whether or not the stream is writable - * - * @return bool - */ - public function isWritable(); - - /** - * 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 returns false on failure (e.g., broken pipe, - * writer needs to slow down, buffer is full, etc.) - */ - public function write($string); - - /** - * Returns whether or not the stream is readable - * - * @return bool - */ - public function isReadable(); - - /** - * Read data from the stream - * - * @param int $length Read up to $length bytes from the object and return - * them. Fewer than $length bytes may be returned if - * underlying stream call returns fewer bytes. - * - * @return string Returns the data read from the stream. - */ - public function read($length); - - /** - * Returns the remaining contents of the stream as a string. - * - * Note: this could potentially load a large amount of data into memory. - * - * @return string - */ - public function getContents(); - - /** - * Get stream metadata as an associative array or retrieve a specific key. - * - * The keys returned are identical to the keys returned from PHP's - * stream_get_meta_data() function. - * - * @param string $key Specific metadata to retrieve. - * - * @return array|mixed|null Returns an associative array if no key is - * no key is provided. Returns a specific key - * value if a key is provided and the value is - * found, or null if the key is not found. - * @see http://php.net/manual/en/function.stream-get-meta-data.php - */ - public function getMetadata($key = null); -} diff --git a/core/vendor/guzzlehttp/streams/src/Utils.php b/core/vendor/guzzlehttp/streams/src/Utils.php deleted file mode 100644 index 94cb42d31d15d879c7414f223a36f729cbb1ca67..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/streams/src/Utils.php +++ /dev/null @@ -1,196 +0,0 @@ -<?php -namespace GuzzleHttp\Stream; - -use GuzzleHttp\Stream\Exception\SeekException; - -/** - * Static utility class because PHP's autoloaders don't support the concept - * of namespaced function autoloading. - */ -class Utils -{ - /** - * Safely opens a PHP stream resource using a filename. - * - * When fopen fails, PHP normally raises a warning. This function adds an - * error handler that checks for errors and throws an exception instead. - * - * @param string $filename File to open - * @param string $mode Mode used to open the file - * - * @return resource - * @throws \RuntimeException if the file cannot be opened - */ - public static function open($filename, $mode) - { - $ex = null; - set_error_handler(function () use ($filename, $mode, &$ex) { - $ex = new \RuntimeException(sprintf( - 'Unable to open %s using mode %s: %s', - $filename, - $mode, - func_get_args()[1] - )); - }); - - $handle = fopen($filename, $mode); - restore_error_handler(); - - if ($ex) { - /** @var $ex \RuntimeException */ - throw $ex; - } - - return $handle; - } - - /** - * Copy the contents of a stream into a string until the given number of - * bytes have been read. - * - * @param StreamInterface $stream Stream to read - * @param int $maxLen Maximum number of bytes to read. Pass -1 - * to read the entire stream. - * @return string - */ - public static function copyToString(StreamInterface $stream, $maxLen = -1) - { - $buffer = ''; - - if ($maxLen === -1) { - while (!$stream->eof()) { - $buf = $stream->read(1048576); - if ($buf === false) { - break; - } - $buffer .= $buf; - } - return $buffer; - } - - $len = 0; - while (!$stream->eof() && $len < $maxLen) { - $buf = $stream->read($maxLen - $len); - if ($buf === false) { - break; - } - $buffer .= $buf; - $len = strlen($buffer); - } - - return $buffer; - } - - /** - * Copy the contents of a stream into another stream until the given number - * of bytes have been read. - * - * @param StreamInterface $source Stream to read from - * @param StreamInterface $dest Stream to write to - * @param int $maxLen Maximum number of bytes to read. Pass -1 - * to read the entire stream. - */ - public static function copyToStream( - StreamInterface $source, - StreamInterface $dest, - $maxLen = -1 - ) { - if ($maxLen === -1) { - while (!$source->eof()) { - if (!$dest->write($source->read(1048576))) { - break; - } - } - return; - } - - $bytes = 0; - while (!$source->eof()) { - $buf = $source->read($maxLen - $bytes); - if (!($len = strlen($buf))) { - break; - } - $bytes += $len; - $dest->write($buf); - if ($bytes == $maxLen) { - break; - } - } - } - - /** - * Calculate a hash of a Stream - * - * @param StreamInterface $stream Stream to calculate the hash for - * @param string $algo Hash algorithm (e.g. md5, crc32, etc) - * @param bool $rawOutput Whether or not to use raw output - * - * @return string Returns the hash of the stream - * @throws SeekException - */ - public static function hash( - StreamInterface $stream, - $algo, - $rawOutput = false - ) { - $pos = $stream->tell(); - - if ($pos > 0 && !$stream->seek(0)) { - throw new SeekException($stream); - } - - $ctx = hash_init($algo); - while (!$stream->eof()) { - hash_update($ctx, $stream->read(1048576)); - } - - $out = hash_final($ctx, (bool) $rawOutput); - $stream->seek($pos); - - return $out; - } - - /** - * Read a line from the stream up to the maximum allowed buffer length - * - * @param StreamInterface $stream Stream to read from - * @param int $maxLength Maximum buffer length - * - * @return string|bool - */ - public static function readline(StreamInterface $stream, $maxLength = null) - { - $buffer = ''; - $size = 0; - - while (!$stream->eof()) { - if (false === ($byte = $stream->read(1))) { - return $buffer; - } - $buffer .= $byte; - // Break when a new line is found or the max length - 1 is reached - if ($byte == PHP_EOL || ++$size == $maxLength - 1) { - break; - } - } - - return $buffer; - } - - /** - * Alias of GuzzleHttp\Stream\Stream::factory. - * - * @param mixed $resource Resource to create - * @param array $options Associative array of stream options defined in - * {@see \GuzzleHttp\Stream\Stream::__construct} - * - * @return StreamInterface - * - * @see GuzzleHttp\Stream\Stream::factory - * @see GuzzleHttp\Stream\Stream::__construct - */ - public static function create($resource, array $options = []) - { - return Stream::factory($resource, $options); - } -} diff --git a/core/vendor/guzzlehttp/streams/tests/AsyncReadStreamTest.php b/core/vendor/guzzlehttp/streams/tests/AsyncReadStreamTest.php deleted file mode 100644 index 8c78995962a2327070ec2313c6da44249745c866..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/streams/tests/AsyncReadStreamTest.php +++ /dev/null @@ -1,186 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Stream; - -use GuzzleHttp\Stream\AsyncReadStream; -use GuzzleHttp\Stream\BufferStream; -use GuzzleHttp\Stream\FnStream; -use GuzzleHttp\Stream\Stream; - -class AsyncReadStreamTest extends \PHPUnit_Framework_TestCase -{ - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Buffer must be readable and writable - */ - public function testValidatesReadableBuffer() - { - new AsyncReadStream(FnStream::decorate( - Stream::factory(), - ['isReadable' => function () { return false; }] - )); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Buffer must be readable and writable - */ - public function testValidatesWritableBuffer() - { - new AsyncReadStream(FnStream::decorate( - Stream::factory(), - ['isWritable' => function () { return false; }] - )); - } - - public function testValidatesHwmMetadata() - { - $a = new AsyncReadStream(Stream::factory(), [ - 'drain' => function() {} - ]); - $this->assertNull($this->readAttribute($a, 'drain')); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage pump must be callable - */ - public function testValidatesPumpIsCallable() - { - new AsyncReadStream(new BufferStream(), ['pump' => true]); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage drain must be callable - */ - public function testValidatesDrainIsCallable() - { - new AsyncReadStream(new BufferStream(), ['drain' => true]); - } - - public function testCanInitialize() - { - $buffer = new BufferStream(); - $a = new AsyncReadStream($buffer, [ - 'size' => 10, - 'drain' => function () {}, - 'pump' => function () {}, - ]); - $this->assertSame($buffer, $this->readAttribute($a, 'stream')); - $this->assertTrue(is_callable($this->readAttribute($a, 'drain'))); - $this->assertTrue(is_callable($this->readAttribute($a, 'pump'))); - $this->assertTrue($a->isReadable()); - $this->assertFalse($a->isSeekable()); - $this->assertFalse($a->isWritable()); - $this->assertFalse($a->write('foo')); - $this->assertEquals(10, $a->getSize()); - } - - public function testReadsFromBufferWithNoDrainOrPump() - { - $buffer = new BufferStream(); - $a = new AsyncReadStream($buffer); - $buffer->write('foo'); - $this->assertNull($a->getSize()); - $this->assertEquals('foo', $a->read(10)); - $this->assertEquals('', $a->read(10)); - } - - public function testCallsPumpForMoreDataWhenRequested() - { - $called = 0; - $buffer = new BufferStream(); - $a = new AsyncReadStream($buffer, [ - 'pump' => function ($size) use (&$called) { - $called++; - return str_repeat('.', $size); - } - ]); - $buffer->write('foobar'); - $this->assertEquals('foo', $a->read(3)); - $this->assertEquals(0, $called); - $this->assertEquals('bar.....', $a->read(8)); - $this->assertEquals(1, $called); - $this->assertEquals('..', $a->read(2)); - $this->assertEquals(2, $called); - } - - public function testCallsDrainWhenNeeded() - { - $called = 0; - $buffer = new BufferStream(5); - $a = new AsyncReadStream($buffer, [ - 'drain' => function (BufferStream $b) use (&$called, $buffer) { - $this->assertSame($b, $buffer); - $called++; - } - ]); - - $buffer->write('foobar'); - $this->assertEquals(6, $buffer->getSize()); - $this->assertEquals(0, $called); - - $a->read(3); - $this->assertTrue($this->readAttribute($a, 'needsDrain')); - $this->assertEquals(3, $buffer->getSize()); - $this->assertEquals(0, $called); - - $a->read(3); - $this->assertEquals(0, $buffer->getSize()); - $this->assertFalse($this->readAttribute($a, 'needsDrain')); - $this->assertEquals(1, $called); - } - - public function testCreatesBufferWithNoConfig() - { - list($buffer, $async) = AsyncReadStream::create(); - $this->assertInstanceOf('GuzzleHttp\Stream\BufferStream', $buffer); - $this->assertInstanceOf('GuzzleHttp\Stream\AsyncReadStream', $async); - } - - public function testCreatesBufferWithSpecifiedBuffer() - { - $buf = new BufferStream(); - list($buffer, $async) = AsyncReadStream::create(['buffer' => $buf]); - $this->assertSame($buf, $buffer); - $this->assertInstanceOf('GuzzleHttp\Stream\AsyncReadStream', $async); - } - - public function testCreatesNullStream() - { - list($buffer, $async) = AsyncReadStream::create(['max_buffer' => 0]); - $this->assertInstanceOf('GuzzleHttp\Stream\NullStream', $buffer); - $this->assertInstanceOf('GuzzleHttp\Stream\AsyncReadStream', $async); - } - - public function testCreatesDroppingStream() - { - list($buffer, $async) = AsyncReadStream::create(['max_buffer' => 5]); - $this->assertInstanceOf('GuzzleHttp\Stream\DroppingStream', $buffer); - $this->assertInstanceOf('GuzzleHttp\Stream\AsyncReadStream', $async); - $buffer->write('12345678910'); - $this->assertEquals(5, $buffer->getSize()); - } - - public function testCreatesOnWriteStream() - { - $c = 0; - $b = new BufferStream(); - list($buffer, $async) = AsyncReadStream::create([ - 'buffer' => $b, - 'write' => function (BufferStream $buf, $data) use (&$c, $b) { - $this->assertSame($buf, $b); - $this->assertEquals('foo', $data); - $c++; - } - ]); - $this->assertInstanceOf('GuzzleHttp\Stream\FnStream', $buffer); - $this->assertInstanceOf('GuzzleHttp\Stream\AsyncReadStream', $async); - $this->assertEquals(0, $c); - $this->assertEquals(3, $buffer->write('foo')); - $this->assertEquals(1, $c); - $this->assertEquals(3, $buffer->write('foo')); - $this->assertEquals(2, $c); - $this->assertEquals('foofoo', (string) $buffer); - } -} diff --git a/core/vendor/guzzlehttp/streams/tests/Exception/SeekExceptionTest.php b/core/vendor/guzzlehttp/streams/tests/Exception/SeekExceptionTest.php deleted file mode 100644 index fd8cd1ad266a710daf11812b24d3ce803e977c9f..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/streams/tests/Exception/SeekExceptionTest.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Stream\Exception; - -use GuzzleHttp\Stream\Exception\SeekException; -use GuzzleHttp\Stream\Stream; - -class SeekExceptionTest extends \PHPUnit_Framework_TestCase -{ - public function testHasStream() - { - $s = Stream::factory('foo'); - $e = new SeekException($s, 10); - $this->assertSame($s, $e->getStream()); - $this->assertContains('10', $e->getMessage()); - } -} diff --git a/core/vendor/guzzlehttp/streams/tests/NoSeekStreamTest.php b/core/vendor/guzzlehttp/streams/tests/NoSeekStreamTest.php deleted file mode 100644 index 21b7c6d22707b4db796c5cc4deed60b4a3dc6152..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/streams/tests/NoSeekStreamTest.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Stream; - -use GuzzleHttp\Stream\Stream; -use GuzzleHttp\Stream\NoSeekStream; - -/** - * @covers GuzzleHttp\Stream\NoSeekStream - * @covers GuzzleHttp\Stream\StreamDecoratorTrait - */ -class NoSeekStreamTest extends \PHPUnit_Framework_TestCase -{ - public function testCannotSeek() - { - $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') - ->setMethods(['isSeekable', 'seek']) - ->getMockForAbstractClass(); - $s->expects($this->never())->method('seek'); - $s->expects($this->never())->method('isSeekable'); - $wrapped = new NoSeekStream($s); - $this->assertFalse($wrapped->isSeekable()); - $this->assertFalse($wrapped->seek(2)); - } - - public function testHandlesClose() - { - $s = Stream::factory('foo'); - $wrapped = new NoSeekStream($s); - $wrapped->close(); - $this->assertFalse($wrapped->write('foo')); - } - - public function testCanAttach() - { - $s1 = Stream::factory('foo'); - $s2 = Stream::factory('bar'); - $wrapped = new NoSeekStream($s1); - $wrapped->attach($s2->detach()); - $this->assertEquals('bar', (string) $wrapped); - } -} diff --git a/core/vendor/guzzlehttp/streams/tests/NullStreamTest.php b/core/vendor/guzzlehttp/streams/tests/NullStreamTest.php deleted file mode 100644 index 8e41431592ec2143c2491650a920f8058cdfb47d..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/streams/tests/NullStreamTest.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Stream; - -use GuzzleHttp\Stream\NullStream; - -class NullStreamTest extends \PHPUnit_Framework_TestCase -{ - public function testDoesNothing() - { - $b = new NullStream(); - $this->assertEquals('', $b->read(10)); - $this->assertEquals(4, $b->write('test')); - $this->assertEquals('', (string) $b); - $this->assertNull($b->getMetadata('a')); - $this->assertEquals([], $b->getMetadata()); - $this->assertEquals(0, $b->getSize()); - $this->assertEquals('', $b->getContents()); - $this->assertEquals(0, $b->tell()); - - $this->assertTrue($b->isReadable()); - $this->assertTrue($b->isWritable()); - $this->assertTrue($b->isSeekable()); - $this->assertFalse($b->seek(10)); - - $this->assertTrue($b->eof()); - $b->detach(); - $this->assertTrue($b->eof()); - $b->close(); - } - - /** - * @expectedException \GuzzleHttp\Stream\Exception\CannotAttachException - */ - public function testCannotAttach() - { - $p = new NullStream(); - $p->attach('a'); - } -} diff --git a/core/vendor/guzzlehttp/streams/tests/UtilsTest.php b/core/vendor/guzzlehttp/streams/tests/UtilsTest.php deleted file mode 100644 index 6e3e3b21608c0f47cfdf334e2aca668015382629..0000000000000000000000000000000000000000 --- a/core/vendor/guzzlehttp/streams/tests/UtilsTest.php +++ /dev/null @@ -1,155 +0,0 @@ -<?php -namespace GuzzleHttp\Tests\Stream; - -use GuzzleHttp\Stream\FnStream; -use GuzzleHttp\Stream\NoSeekStream; -use GuzzleHttp\Stream\Stream; -use GuzzleHttp\Stream\Utils; - -class UtilsTest extends \PHPUnit_Framework_TestCase -{ - public function testCopiesToString() - { - $s = Stream::factory('foobaz'); - $this->assertEquals('foobaz', Utils::copyToString($s)); - $s->seek(0); - $this->assertEquals('foo', Utils::copyToString($s, 3)); - $this->assertEquals('baz', Utils::copyToString($s, 3)); - $this->assertEquals('', Utils::copyToString($s)); - } - - public function testCopiesToStringStopsWhenReadFails() - { - $s1 = Stream::factory('foobaz'); - $s1 = FnStream::decorate($s1, [ - 'read' => function () { - return false; - } - ]); - $result = Utils::copyToString($s1); - $this->assertEquals('', $result); - } - - public function testCopiesToStream() - { - $s1 = Stream::factory('foobaz'); - $s2 = Stream::factory(''); - Utils::copyToStream($s1, $s2); - $this->assertEquals('foobaz', (string) $s2); - $s2 = Stream::factory(''); - $s1->seek(0); - Utils::copyToStream($s1, $s2, 3); - $this->assertEquals('foo', (string) $s2); - Utils::copyToStream($s1, $s2, 3); - $this->assertEquals('foobaz', (string) $s2); - } - - public function testStopsCopyToStreamWhenWriteFails() - { - $s1 = Stream::factory('foobaz'); - $s2 = Stream::factory(''); - $s2 = FnStream::decorate($s2, ['write' => function () { return 0; }]); - Utils::copyToStream($s1, $s2); - $this->assertEquals('', (string) $s2); - } - - public function testStopsCopyToSteamWhenWriteFailsWithMaxLen() - { - $s1 = Stream::factory('foobaz'); - $s2 = Stream::factory(''); - $s2 = FnStream::decorate($s2, ['write' => function () { return 0; }]); - Utils::copyToStream($s1, $s2, 10); - $this->assertEquals('', (string) $s2); - } - - public function testStopsCopyToSteamWhenReadFailsWithMaxLen() - { - $s1 = Stream::factory('foobaz'); - $s1 = FnStream::decorate($s1, ['read' => function () { return ''; }]); - $s2 = Stream::factory(''); - Utils::copyToStream($s1, $s2, 10); - $this->assertEquals('', (string) $s2); - } - - public function testReadsLines() - { - $s = Stream::factory("foo\nbaz\nbar"); - $this->assertEquals("foo\n", Utils::readline($s)); - $this->assertEquals("baz\n", Utils::readline($s)); - $this->assertEquals("bar", Utils::readline($s)); - } - - public function testReadsLinesUpToMaxLength() - { - $s = Stream::factory("12345\n"); - $this->assertEquals("123", Utils::readline($s, 4)); - $this->assertEquals("45\n", Utils::readline($s)); - } - - public function testReadsLineUntilFalseReturnedFromRead() - { - $s = $this->getMockBuilder('GuzzleHttp\Stream\Stream') - ->setMethods(['read', 'eof']) - ->disableOriginalConstructor() - ->getMock(); - $s->expects($this->exactly(2)) - ->method('read') - ->will($this->returnCallback(function () { - static $c = false; - if ($c) { - return false; - } - $c = true; - return 'h'; - })); - $s->expects($this->exactly(2)) - ->method('eof') - ->will($this->returnValue(false)); - $this->assertEquals("h", Utils::readline($s)); - } - - public function testCalculatesHash() - { - $s = Stream::factory('foobazbar'); - $this->assertEquals(md5('foobazbar'), Utils::hash($s, 'md5')); - } - - /** - * @expectedException \GuzzleHttp\Stream\Exception\SeekException - */ - public function testCalculatesHashThrowsWhenSeekFails() - { - $s = new NoSeekStream(Stream::factory('foobazbar')); - $s->read(2); - Utils::hash($s, 'md5'); - } - - public function testCalculatesHashSeeksToOriginalPosition() - { - $s = Stream::factory('foobazbar'); - $s->seek(4); - $this->assertEquals(md5('foobazbar'), Utils::hash($s, 'md5')); - $this->assertEquals(4, $s->tell()); - } - - public function testOpensFilesSuccessfully() - { - $r = Utils::open(__FILE__, 'r'); - $this->assertInternalType('resource', $r); - fclose($r); - } - - /** - * @expectedException \RuntimeException - * @expectedExceptionMessage Unable to open /path/to/does/not/exist using mode r - */ - public function testThrowsExceptionNotWarning() - { - Utils::open('/path/to/does/not/exist', 'r'); - } - - public function testProxiesToFactory() - { - $this->assertEquals('foo', (string) Utils::create('foo')); - } -} diff --git a/core/vendor/react/promise/.gitignore b/core/vendor/react/promise/.gitignore deleted file mode 100644 index c4bcb78f741ae92c67d5a4e4890fdef8217b1881..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -composer.lock -composer.phar -phpunit.xml -vendor/ diff --git a/core/vendor/react/promise/.travis.yml b/core/vendor/react/promise/.travis.yml deleted file mode 100644 index e26d91aae9cbee3a02823bd92e2c7995d384ac90..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -language: php - -php: - - 5.4 - - 5.5 - - 5.6 - - hhvm - -before_script: - - composer self-update - - composer install --dev --prefer-source - -script: phpunit --coverage-text diff --git a/core/vendor/react/promise/CHANGELOG.md b/core/vendor/react/promise/CHANGELOG.md deleted file mode 100644 index f2d16543dfb2311a8010445d1c2575f0ac4866c9..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/CHANGELOG.md +++ /dev/null @@ -1,60 +0,0 @@ -CHANGELOG -========= - -* 2.2.0 (2014-12-30) - - * Introduce new ExtendedPromiseInterface implemented by all promises - * Add new .done() method (part of the ExtendedPromiseInterface) - * Add new .otherwise() method (part of the ExtendedPromiseInterface) - * Add new .always() method (part of the ExtendedPromiseInterface) - * Add new .progress() method (part of the ExtendedPromiseInterface) - * Rename Deferred::progress to Deferred::notify to avoid confusion with - ExtendedPromiseInterface::progress (a Deferred::progress alias is still - available for backward compatibility) - * resolve() now always returns a ExtendedPromiseInterface - -* 2.1.0 (2014-10-15) - - * Introduce new CancellablePromiseInterface implemented by all promises - * Add new .cancel() method (part of the CancellablePromiseInterface) - -* 2.0.0 (2013-12-10) - - New major release. The goal was to streamline the API and to make it more - compliant with other promise libraries and especially with the new upcoming - [ES6 promises specification](https://github.com/domenic/promises-unwrapping/). - - * Add standalone Promise class. - * Add new React\Promise\race() function. - * BC break: Bump minimum PHP version to PHP 5.4. - * BC break: Remove ResolverInterface and PromiseInterface from Deferred. - * BC break: Change signature of PromiseInterface. - * BC break: Remove When and Util classes and move static methods to functions. - * BC break: FulfilledPromise and RejectedPromise now throw an exception when - initialized with a promise instead of a value/reason. - * BC break: React\Promise\Deferred::resolve() and React\Promise\Deferred::reject() - no longer return a promise. - -* 1.0.4 (2013-04-03) - - * Trigger PHP errors when invalid callback is passed. - * Fully resolve rejection value before calling rejection handler. - * Add When::lazy() to create lazy promises which will be initialized once a - consumer calls the then() method. - -* 1.0.3 (2012-11-17) - - * Add `PromisorInterface` for objects that have a `promise()` method. - -* 1.0.2 (2012-11-14) - - * Fix bug in When::any() not correctly unwrapping to a single result value - * $promiseOrValue argument of When::resolve() and When::reject() is now optional - -* 1.0.1 (2012-11-13) - - * Prevent deep recursion which was reaching `xdebug.max_nesting_level` default of 100 - -* 1.0.0 (2012-11-07) - - * First tagged release diff --git a/core/vendor/react/promise/LICENSE b/core/vendor/react/promise/LICENSE deleted file mode 100644 index 9bfcd4113c8ab9d446d87a08eda3531220e44d25..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2012 Jan Sorgalla - -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. diff --git a/core/vendor/react/promise/README.md b/core/vendor/react/promise/README.md deleted file mode 100644 index 0be869ebc57b8245507eb3e252aa506c49dd31c8..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/README.md +++ /dev/null @@ -1,824 +0,0 @@ -React/Promise -============= - -A lightweight implementation of -[CommonJS Promises/A](http://wiki.commonjs.org/wiki/Promises/A) for PHP. - -[](http://travis-ci.org/reactphp/promise) - -Table of Contents ------------------ - -1. [Introduction](#introduction) -2. [Concepts](#concepts) - * [Deferred](#deferred) - * [Promise](#promise) -3. [API](#api) - * [Deferred](#deferred-1) - * [Deferred::promise()](#deferredpromise) - * [Deferred::resolve()](#deferredresolve) - * [Deferred::reject()](#deferredreject) - * [Deferred::notify()](#deferrednotify) - * [PromiseInterface](#promiseinterface) - * [PromiseInterface::then()](#promiseinterfacethen) - * [ExtendedPromiseInterface](#extendedpromiseinterface) - * [ExtendedPromiseInterface::done()](#extendedpromiseinterfacedone) - * [ExtendedPromiseInterface::otherwise()](#extendedpromiseinterfaceotherwise) - * [ExtendedPromiseInterface::always()](#extendedpromiseinterfacealways) - * [ExtendedPromiseInterface::progress()](#extendedpromiseinterfaceprogress) - * [CancellablePromiseInterface](#cancellablepromiseinterface) - * [CancellablePromiseInterface::cancel()](#cancellablepromiseinterfacecancel) - * [Promise](#promise-1) - * [FulfilledPromise](#fulfilledpromise) - * [RejectedPromise](#rejectedpromise) - * [LazyPromise](#lazypromise) - * [Functions](#functions) - * [resolve()](#resolve) - * [reject()](#reject) - * [all()](#all) - * [race()](#race) - * [any()](#any) - * [some()](#some) - * [map()](#map) - * [reduce()](#reduce) - * [PromisorInterface](#promisorinterface) -4. [Examples](#examples) - * [How to use Deferred](#how-to-use-deferred) - * [How promise forwarding works](#how-promise-forwarding-works) - * [Resolution forwarding](#resolution-forwarding) - * [Rejection forwarding](#rejection-forwarding) - * [Mixed resolution and rejection forwarding](#mixed-resolution-and-rejection-forwarding) - * [Progress event forwarding](#progress-event-forwarding) - * [done() vs. then()](#done-vs-then) -5. [Credits](#credits) -6. [License](#license) - -Introduction ------------- - -React/Promise is a library implementing -[CommonJS Promises/A](http://wiki.commonjs.org/wiki/Promises/A) for PHP. - -It also provides several other useful promise-related concepts, such as joining -multiple promises and mapping and reducing collections of promises. - -If you've never heard about promises before, -[read this first](https://gist.github.com/3889970). - -Concepts --------- - -### Deferred - -A **Deferred** represents a computation or unit of work that may not have -completed yet. Typically (but not always), that computation will be something -that executes asynchronously and completes at some point in the future. - -### Promise - -While a deferred represents the computation itself, a **Promise** represents -the result of that computation. Thus, each deferred has a promise that acts as -a placeholder for its actual result. - -API ---- - -### Deferred - -A deferred represents an operation whose resolution is pending. It has separate -promise and resolver parts. - -```php -$deferred = new React\Promise\Deferred(); - -$promise = $deferred->promise(); - -$deferred->resolve(mixed $value = null); -$deferred->reject(mixed $reason = null); -$deferred->notify(mixed $update = null); -``` - -The `promise` method returns the promise of the deferred. - -The `resolve` and `reject` methods control the state of the deferred. - -The `notify` method is for progress notification. - -The constructor of the `Deferred` accepts an optional `$canceller` argument. -See [Promise](#promise-1) for more information. - -#### Deferred::promise() - -```php -$promise = $deferred->promise(); -``` - -Returns the promise of the deferred, which you can hand out to others while -keeping the authority to modify its state to yourself. - -#### Deferred::resolve() - -```php -$deferred->resolve(mixed $value = null); -``` - -Resolves the promise returned by `promise()`. All consumers are notified by -having `$onFulfilled` (which they registered via `$promise->then()`) called with -`$value`. - -If `$value` itself is a promise, the promise will transition to the state of -this promise once it is resolved. - -#### Deferred::reject() - -```php -$deferred->reject(mixed $reason = null); -``` - -Rejects the promise returned by `promise()`, signalling that the deferred's -computation failed. -All consumers are notified by having `$onRejected` (which they registered via -`$promise->then()`) called with `$reason`. - -If `$reason` itself is a promise, the promise will be rejected with the outcome -of this promise regardless whether it fulfills or rejects. - -#### Deferred::notify() - -```php -$deferred->notify(mixed $update = null); -``` - -Triggers progress notifications, to indicate to consumers that the computation -is making progress toward its result. - -All consumers are notified by having `$onProgress` (which they registered via -`$promise->then()`) called with `$update`. - -### PromiseInterface - -The promise interface provides the common interface for all promise -implementations. - -A promise represents an eventual outcome, which is either fulfillment (success) -and an associated value, or rejection (failure) and an associated reason. - -Once in the fulfilled or rejected state, a promise becomes immutable. -Neither its state nor its result (or error) can be modified. - -#### Implementations - -* [Promise](#promise-1) -* [FulfilledPromise](#fulfilledpromise) -* [RejectedPromise](#rejectedpromise) -* [LazyPromise](#lazypromise) - -#### PromiseInterface::then() - -```php -$transformedPromise = $promise->then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null); -``` - -Transforms a promise's value by applying a function to the promise's fulfillment -or rejection value. Returns a new promise for the transformed result. - -The `then()` method registers new fulfilled, rejection and progress handlers -with a promise (all parameters are optional): - - * `$onFulfilled` will be invoked once the promise is fulfilled and passed - the result as the first argument. - * `$onRejected` will be invoked once the promise is rejected and passed the - reason as the first argument. - * `$onProgress` will be invoked whenever the producer of the promise - triggers progress notifications and passed a single argument (whatever it - wants) to indicate progress. - -It returns a new promise that will fulfill with the return value of either -`$onFulfilled` or `$onRejected`, whichever is called, or will reject with -the thrown exception if either throws. - -A promise makes the following guarantees about handlers registered in -the same call to `then()`: - - 1. Only one of `$onFulfilled` or `$onRejected` will be called, - never both. - 2. `$onFulfilled` and `$onRejected` will never be called more - than once. - 3. `$onProgress` may be called multiple times. - -#### See also - -* [resolve()](#resolve) - Creating a resolved promise -* [reject()](#reject) - Creating a rejected promise -* [ExtendedPromiseInterface::done()](#extendedpromiseinterfacedone) -* [done() vs. then()](#done-vs-then) - -### ExtendedPromiseInterface - -The ExtendedPromiseInterface extends the PromiseInterface with useful shortcut -and utility methods which are not part of the Promises/A specification. - -#### Implementations - -* [Promise](#promise-1) -* [FulfilledPromise](#fulfilledpromise) -* [RejectedPromise](#rejectedpromise) -* [LazyPromise](#lazypromise) - -#### ExtendedPromiseInterface::done() - -```php -$promise->done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null); -``` - -Consumes the promise's ultimate value if the promise fulfills, or handles the -ultimate error. - -It will cause a fatal error if either `$onFulfilled` or `$onRejected` throw or -return a rejected promise. - -Since the purpose of `done()` is consumption rather than transformation, -`done()` always returns `null`. - -#### See also - -* [PromiseInterface::then()](#promiseinterfacethen) -* [done() vs. then()](#done-vs-then) - -#### ExtendedPromiseInterface::otherwise() - -```php -$promise->otherwise(callable $onRejected); -``` - -Registers a rejection handler for promise. It is a shortcut for: - -```php -$promise->then(null, $onRejected); -``` - -Additionally, you can type hint the `$reason` argument of `$onRejected` to catch -only specific errors. - -```php -$promise - ->otherwise(function (\RuntimeException $reason) { - // Only catch \RuntimeException instances - // All other types of errors will propagate automatically - }) - ->otherwise(function ($reason) { - // Catch other errors - )}; -``` - -#### ExtendedPromiseInterface::always() - -```php -$newPromise = $promise->always(callable $onFulfilledOrRejected); -``` - -Allows you to execute "cleanup" type tasks in a promise chain. - -It arranges for `$onFulfilledOrRejected` to be called, with no arguments, -when the promise is either fulfilled or rejected. - -* If `$promise` fulfills, and `$onFulfilledOrRejected` returns successfully, - `$newPromise` will fulfill with the same value as `$promise`. -* If `$promise` fulfills, and `$onFulfilledOrRejected` throws or returns a - rejected promise, `$newPromise` will reject with the thrown exception or - rejected promise's reason. -* If `$promise` rejects, and `$onFulfilledOrRejected` returns successfully, - `$newPromise` will reject with the same reason as `$promise`. -* If `$promise` rejects, and `$onFulfilledOrRejected` throws or returns a - rejected promise, `$newPromise` will reject with the thrown exception or - rejected promise's reason. - -`always()` behaves similarly to the synchronous finally statement. When combined -with `otherwise()`, `always()` allows you to write code that is similar to the familiar -synchronous catch/finally pair. - -Consider the following synchronous code: - -```php -try { - return doSomething(); -} catch(\Exception $e) { - return handleError($e); -} finally { - cleanup(); -} -``` - -Similar asynchronous code (with `doSomething()` that returns a promise) can be -written: - -```php -return doSomething() - ->otherwise('handleError') - ->always('cleanup'); -``` - -#### ExtendedPromiseInterface::progress() - -```php -$promise->progress(callable $onProgress); -``` - -Registers a handler for progress updates from promise. It is a shortcut for: - -```php -$promise->then(null, null, $onProgress); -``` - -### CancellablePromiseInterface - -A cancellable promise provides a mechanism for consumers to notify the creator -of the promise that they are not longer interested in the result of an -operation. - -#### CancellablePromiseInterface::cancel() - -``` php -$promise->cancel(); -``` - -The `cancel()` method notifies the creator of the promise that there is no -further interest in the results of the operation. - -Once a promise is settled (either fulfilled or rejected), calling `cancel()` on -a promise has no effect. - -#### Implementations - -* [Promise](#promise-1) -* [FulfilledPromise](#fulfilledpromise) -* [RejectedPromise](#rejectedpromise) -* [LazyPromise](#lazypromise) - -### Promise - -Creates a promise whose state is controlled by the functions passed to -`$resolver`. - -```php -$resolver = function (callable $resolve, callable $reject, callable $notify) { - // Do some work, possibly asynchronously, and then - // resolve or reject. You can notify of progress events - // along the way if you want/need. - - $resolve($awesomeResult); - // or $resolve($anotherPromise); - // or $reject($nastyError); - // or $notify($progressNotification); -}; - -$canceller = function (callable $resolve, callable $reject, callable $progress) { - // Cancel/abort any running operations like network connections, streams etc. - - $reject(new \Exception('Promise cancelled')); -}; - -$promise = new React\Promise\Promise($resolver, $canceller); -``` - -The promise constructor receives a resolver function and an optional canceller -function which both will be called with 3 arguments: - - * `$resolve($value)` - Primary function that seals the fate of the - returned promise. Accepts either a non-promise value, or another promise. - When called with a non-promise value, fulfills promise with that value. - When called with another promise, e.g. `$resolve($otherPromise)`, promise's - fate will be equivalent to that of `$otherPromise`. - * `$reject($reason)` - Function that rejects the promise. - * `$notify($update)` - Function that issues progress events for the promise. - -If the resolver or canceller throw an exception, the promise will be rejected -with that thrown exception as the rejection reason. - -The resolver function will be called immediately, the canceller function only -once all consumers called the `cancel()` method of the promise. - -### FulfilledPromise - -Creates a already fulfilled promise. - -```php -$promise = React\Promise\FulfilledPromise($value); -``` - -Note, that `$value` **cannot** be a promise. It's recommended to use -[resolve()](#resolve) for creating resolved promises. - -### RejectedPromise - -Creates a already rejected promise. - -```php -$promise = React\Promise\RejectedPromise($reason); -``` - -Note, that `$reason` **cannot** be a promise. It's recommended to use -[reject()](#reject) for creating rejected promises. - -### LazyPromise - -Creates a promise which will be lazily initialized by `$factory` once a consumer -calls the `then()` method. - -```php -$factory = function () { - $deferred = new React\Promise\Deferred(); - - // Do some heavy stuff here and resolve the deferred once completed - - return $deferred->promise(); -}; - -$promise = React\Promise\LazyPromise($factory); - -// $factory will only be executed once we call then() -$promise->then(function ($value) { -}); -``` - -### Functions - -Useful functions for creating, joining, mapping and reducing collections of -promises. - -#### resolve() - -```php -$promise = React\Promise\resolve(mixed $promiseOrValue); -``` - -Creates a promise for the supplied `$promiseOrValue`. - -If `$promiseOrValue` is a value, it will be the resolution value of the -returned promise. - -If `$promiseOrValue` is a promise, it will simply be returned. - -Note: The promise returned is always a promise implementing -[ExtendedPromiseInterface](#extendedpromiseinterface). If you pass in a custom -promise which only implements [PromiseInterface](#promiseinterface), this -promise will be assimilated to a extended promise following `$promiseOrValue`. - -#### reject() - -```php -$promise = React\Promise\reject(mixed $promiseOrValue); -``` - -Creates a rejected promise for the supplied `$promiseOrValue`. - -If `$promiseOrValue` is a value, it will be the rejection value of the -returned promise. - -If `$promiseOrValue` is a promise, its completion value will be the rejected -value of the returned promise. - -This can be useful in situations where you need to reject a promise without -throwing an exception. For example, it allows you to propagate a rejection with -the value of another promise. - -#### all() - -```php -$promise = React\Promise\all(array|React\Promise\PromiseInterface $promisesOrValues); -``` - -Returns a promise that will resolve only once all the items in -`$promisesOrValues` have resolved. The resolution value of the returned promise -will be an array containing the resolution values of each of the items in -`$promisesOrValues`. - -#### race() - -```php -$promise = React\Promise\race(array|React\Promise\PromiseInterface $promisesOrValues); -``` - -Initiates a competitive race that allows one winner. Returns a promise which is -resolved in the same way the first settled promise resolves. - -#### any() - -```php -$promise = React\Promise\any(array|React\Promise\PromiseInterface $promisesOrValues); -``` - -Returns a promise that will resolve when any one of the items in -`$promisesOrValues` resolves. The resolution value of the returned promise -will be the resolution value of the triggering item. - -The returned promise will only reject if *all* items in `$promisesOrValues` are -rejected. The rejection value will be an array of all rejection reasons. - -#### some() - -```php -$promise = React\Promise\some(array|React\Promise\PromiseInterface $promisesOrValues, integer $howMany); -``` - -Returns a promise that will resolve when `$howMany` of the supplied items in -`$promisesOrValues` resolve. The resolution value of the returned promise -will be an array of length `$howMany` containing the resolution values of the -triggering items. - -The returned promise will reject if it becomes impossible for `$howMany` items -to resolve (that is, when `(count($promisesOrValues) - $howMany) + 1` items -reject). The rejection value will be an array of -`(count($promisesOrValues) - $howMany) + 1` rejection reasons. - -#### map() - -```php -$promise = React\Promise\map(array|React\Promise\PromiseInterface $promisesOrValues, callable $mapFunc); -``` - -Traditional map function, similar to `array_map()`, but allows input to contain -promises and/or values, and `$mapFunc` may return either a value or a promise. - -The map function receives each item as argument, where item is a fully resolved -value of a promise or value in `$promisesOrValues`. - -#### reduce() - -```php -$promise = React\Promise\reduce(array|React\Promise\PromiseInterface $promisesOrValues, callable $reduceFunc , $initialValue = null); -``` - -Traditional reduce function, similar to `array_reduce()`, but input may contain -promises and/or values, and `$reduceFunc` may return either a value or a -promise, *and* `$initialValue` may be a promise or a value for the starting -value. - -### PromisorInterface - -The `React\Promise\PromisorInterface` provides a common interface for objects -that provide a promise. `React\Promise\Deferred` implements it, but since it -is part of the public API anyone can implement it. - -Examples --------- - -### How to use Deferred - -```php -function getAwesomeResultPromise() -{ - $deferred = new React\Promise\Deferred(); - - // Execute a Node.js-style function using the callback pattern - computeAwesomeResultAsynchronously(function ($error, $result) use ($deferred) { - if ($error) { - $deferred->reject($error); - } else { - $deferred->resolve($result); - } - }); - - // Return the promise - return $deferred->promise(); -} - -getAwesomeResultPromise() - ->then( - function ($value) { - // Deferred resolved, do something with $value - }, - function ($reason) { - // Deferred rejected, do something with $reason - }, - function ($update) { - // Progress notification triggered, do something with $update - } - ); -``` - -### How promise forwarding works - -A few simple examples to show how the mechanics of Promises/A forwarding works. -These examples are contrived, of course, and in real usage, promise chains will -typically be spread across several function calls, or even several levels of -your application architecture. - -#### Resolution forwarding - -Resolved promises forward resolution values to the next promise. -The first promise, `$deferred->promise()`, will resolve with the value passed -to `$deferred->resolve()` below. - -Each call to `then()` returns a new promise that will resolve with the return -value of the previous handler. This creates a promise "pipeline". - -```php -$deferred = new React\Promise\Deferred(); - -$deferred->promise() - ->then(function ($x) { - // $x will be the value passed to $deferred->resolve() below - // and returns a *new promise* for $x + 1 - return $x + 1; - }) - ->then(function ($x) { - // $x === 2 - // This handler receives the return value of the - // previous handler. - return $x + 1; - }) - ->then(function ($x) { - // $x === 3 - // This handler receives the return value of the - // previous handler. - return $x + 1; - }) - ->then(function ($x) { - // $x === 4 - // This handler receives the return value of the - // previous handler. - echo 'Resolve ' . $x; - }); - -$deferred->resolve(1); // Prints "Resolve 4" -``` - -#### Rejection forwarding - -Rejected promises behave similarly, and also work similarly to try/catch: -When you catch an exception, you must rethrow for it to propagate. - -Similarly, when you handle a rejected promise, to propagate the rejection, -"rethrow" it by either returning a rejected promise, or actually throwing -(since promise translates thrown exceptions into rejections) - -```php -$deferred = new React\Promise\Deferred(); - -$deferred->promise() - ->then(function ($x) { - throw new \Exception($x + 1); - }) - ->then(null, function (\Exception $x) { - // Propagate the rejection - throw $x; - }) - ->then(null, function (\Exception $x) { - // Can also propagate by returning another rejection - return React\Promise\reject((integer) $x->getMessage() + 1); - }) - ->then(null, function ($x) { - echo 'Reject ' . $x; // 3 - }); - -$deferred->resolve(1); // Prints "Reject 3" -``` - -#### Mixed resolution and rejection forwarding - -Just like try/catch, you can choose to propagate or not. Mixing resolutions and -rejections will still forward handler results in a predictable way. - -```php -$deferred = new React\Promise\Deferred(); - -$deferred->promise() - ->then(function ($x) { - return $x + 1; - }) - ->then(function ($x) { - throw \Exception($x + 1); - }) - ->then(null, function (\Exception $x) { - // Handle the rejection, and don't propagate. - // This is like catch without a rethrow - return (integer) $x->getMessage() + 1; - }) - ->then(function ($x) { - echo 'Mixed ' . $x; // 4 - }); - -$deferred->resolve(1); // Prints "Mixed 4" -``` - -#### Progress event forwarding - -In the same way as resolution and rejection handlers, your progress handler -**MUST** return a progress event to be propagated to the next link in the chain. -If you return nothing, `null` will be propagated. - -Also in the same way as resolutions and rejections, if you don't register a -progress handler, the update will be propagated through. - -If your progress handler throws an exception, the exception will be propagated -to the next link in the chain. The best thing to do is to ensure your progress -handlers do not throw exceptions. - -This gives you the opportunity to transform progress events at each step in the -chain so that they are meaningful to the next step. It also allows you to choose -not to transform them, and simply let them propagate untransformed, by not -registering a progress handler. - -```php -$deferred = new React\Promise\Deferred(); - -$deferred->promise() - ->progress(function ($update) { - return $update + 1; - }) - ->progress(function ($update) { - echo 'Progress ' . $update; // 2 - }); - -$deferred->notify(1); // Prints "Progress 2" -``` - -### done() vs. then() - -The golden rule is: - - Either return your promise, or call done() on it. - -At a first glance, `then()` and `done()` seem very similar. However, there are -important distinctions. - -The intent of `then()` is to transform a promise's value and to pass or return -a new promise for the transformed value along to other parts of your code. - -The intent of `done()` is to consume a promise's value, transferring -responsibility for the value to your code. - -In addition to transforming a value, `then()` allows you to recover from, or -propagate intermediate errors. Any errors that are not handled will be caught -by the promise machinery and used to reject the promise returned by `then()`. - -Calling `done()` transfers all responsibility for errors to your code. If an -error (either a thrown exception or returned rejection) escapes the -`$onFulfilled` or `$onRejected` callbacks you provide to done, it will be -rethrown in an uncatchable way causing a fatal error. - -```php -function getJsonResult() -{ - return queryApi() - ->then( - // Transform API results to an object - function ($jsonResultString) { - return json_decode($jsonResultString); - }, - // Transform API errors to an exception - function ($jsonErrorString) { - $object = json_decode($jsonErrorString); - throw new ApiErrorException($object->errorMessage); - } - ); -} - -// Here we provide no rejection handler. -// If the promise returned has been rejected, -// a React\Promise\UnhandledRejectionException will be thrown -getJsonResult() - ->done( - // Consume transformed object - function ($jsonResultObject) { - // Do something with $jsonObject - } - ); - -// Here we provide a rejection handler which will either throw while debugging -// or log the exception. -getJsonResult() - ->done( - function ($jsonObject) { - // Do something with $jsonObject - }, - function (ApiErrorException $exception) { - if (isDebug()) { - throw $e; - } else { - logException($exception); - } - } - ); -``` - -Note that if a rejection value is not an instance of `\Exception`, it will be -wrapped in an exception of the type `React\Promise\UnhandledRejectionException`. - -You can get the original rejection reason by calling `$exception->getReason()`. - -Credits -------- - -React/Promise is a port of [when.js](https://github.com/cujojs/when) -by [Brian Cavalier](https://github.com/briancavalier). - -Also, large parts of the documentation have been ported from the when.js -[Wiki](https://github.com/cujojs/when/wiki) and the -[API docs](https://github.com/cujojs/when/blob/master/docs/api.md). - -License -------- - -React/Promise is released under the [MIT](https://github.com/reactphp/promise/blob/master/LICENSE) license. diff --git a/core/vendor/react/promise/composer.json b/core/vendor/react/promise/composer.json deleted file mode 100644 index c428b7ba655893d9c80b93718d737d9c4970a253..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/composer.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "react/promise", - "description": "A lightweight implementation of CommonJS Promises/A for PHP", - "license": "MIT", - "authors": [ - {"name": "Jan Sorgalla", "email": "jsorgalla@googlemail.com"} - ], - "require": { - "php": ">=5.4.0" - }, - "autoload": { - "psr-4": { - "React\\Promise\\": "src/" - }, - "files": ["src/functions_include.php"] - }, - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - } -} diff --git a/core/vendor/react/promise/phpunit.xml.dist b/core/vendor/react/promise/phpunit.xml.dist deleted file mode 100644 index 0200d463fa368ab7abb74ead4279eb2662e71344..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/phpunit.xml.dist +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<phpunit backupGlobals="false" - backupStaticAttributes="false" - colors="true" - convertErrorsToExceptions="true" - convertNoticesToExceptions="true" - convertWarningsToExceptions="true" - processIsolation="false" - stopOnFailure="false" - syntaxCheck="false" - bootstrap="tests/bootstrap.php" -> - <testsuites> - <testsuite name="Promise Test Suite"> - <directory>./tests/</directory> - </testsuite> - </testsuites> - - <filter> - <whitelist> - <directory>./src/</directory> - </whitelist> - </filter> -</phpunit> diff --git a/core/vendor/react/promise/src/CancellablePromiseInterface.php b/core/vendor/react/promise/src/CancellablePromiseInterface.php deleted file mode 100644 index 896db2d372a5f99cd5c4fae90ebe39516e969f30..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/src/CancellablePromiseInterface.php +++ /dev/null @@ -1,11 +0,0 @@ -<?php - -namespace React\Promise; - -interface CancellablePromiseInterface extends PromiseInterface -{ - /** - * @return void - */ - public function cancel(); -} diff --git a/core/vendor/react/promise/src/Deferred.php b/core/vendor/react/promise/src/Deferred.php deleted file mode 100644 index f23980c31184ba084b1b48375c0f6c35cd2f927b..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/src/Deferred.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php - -namespace React\Promise; - -class Deferred implements PromisorInterface -{ - private $promise; - private $resolveCallback; - private $rejectCallback; - private $notifyCallback; - private $canceller; - - public function __construct(callable $canceller = null) - { - $this->canceller = $canceller; - } - - public function promise() - { - if (null === $this->promise) { - $this->promise = new Promise(function ($resolve, $reject, $notify) { - $this->resolveCallback = $resolve; - $this->rejectCallback = $reject; - $this->notifyCallback = $notify; - }, $this->canceller); - } - - return $this->promise; - } - - public function resolve($value = null) - { - $this->promise(); - - call_user_func($this->resolveCallback, $value); - } - - public function reject($reason = null) - { - $this->promise(); - - call_user_func($this->rejectCallback, $reason); - } - - public function notify($update = null) - { - $this->promise(); - - call_user_func($this->notifyCallback, $update); - } - - /** - * @deprecated 2.2.0 - * @see Deferred::notify() - */ - public function progress($update = null) - { - $this->notify($update); - } -} diff --git a/core/vendor/react/promise/src/ExtendedPromiseInterface.php b/core/vendor/react/promise/src/ExtendedPromiseInterface.php deleted file mode 100644 index 9cb6435987525754edab701518767203ba1ad559..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/src/ExtendedPromiseInterface.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -namespace React\Promise; - -interface ExtendedPromiseInterface extends PromiseInterface -{ - /** - * @return void - */ - public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null); - - /** - * @return ExtendedPromiseInterface - */ - public function otherwise(callable $onRejected); - - /** - * @return ExtendedPromiseInterface - */ - public function always(callable $onFulfilledOrRejected); - - /** - * @return ExtendedPromiseInterface - */ - public function progress(callable $onProgress); -} diff --git a/core/vendor/react/promise/src/FulfilledPromise.php b/core/vendor/react/promise/src/FulfilledPromise.php deleted file mode 100644 index cd3665929bb5e611b6bdf2d0e19651c2403a4791..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/src/FulfilledPromise.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php - -namespace React\Promise; - -class FulfilledPromise implements ExtendedPromiseInterface, CancellablePromiseInterface -{ - private $value; - - public function __construct($value = null) - { - if ($value instanceof PromiseInterface) { - throw new \InvalidArgumentException('You cannot create React\Promise\FulfilledPromise with a promise. Use React\Promise\resolve($promiseOrValue) instead.'); - } - - $this->value = $value; - } - - public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) - { - try { - $value = $this->value; - - if (null !== $onFulfilled) { - $value = $onFulfilled($value); - } - - return resolve($value); - } catch (\Exception $exception) { - return new RejectedPromise($exception); - } - } - - public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) - { - if (null === $onFulfilled) { - return; - } - - $result = $onFulfilled($this->value); - - if ($result instanceof ExtendedPromiseInterface) { - $result->done(); - } - } - - public function otherwise(callable $onRejected) - { - return new FulfilledPromise($this->value); - } - - public function always(callable $onFulfilledOrRejected) - { - return $this->then(function ($value) use ($onFulfilledOrRejected) { - return resolve($onFulfilledOrRejected())->then(function () use ($value) { - return $value; - }); - }); - } - - public function progress(callable $onProgress) - { - return new FulfilledPromise($this->value); - } - - public function cancel() - { - } -} diff --git a/core/vendor/react/promise/src/LazyPromise.php b/core/vendor/react/promise/src/LazyPromise.php deleted file mode 100644 index 68524a929d9e34b8fe5d240bc9295f875c98e53a..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/src/LazyPromise.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php - -namespace React\Promise; - -class LazyPromise implements ExtendedPromiseInterface, CancellablePromiseInterface -{ - private $factory; - private $promise; - - public function __construct(callable $factory) - { - $this->factory = $factory; - } - - public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) - { - return $this->promise()->then($onFulfilled, $onRejected, $onProgress); - } - - - public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) - { - return $this->promise()->done($onFulfilled, $onRejected, $onProgress); - } - - public function otherwise(callable $onRejected) - { - return $this->promise()->otherwise($onRejected); - } - - public function always(callable $onFulfilledOrRejected) - { - return $this->promise()->always($onFulfilledOrRejected); - } - - public function progress(callable $onProgress) - { - return $this->promise()->progress($onProgress); - } - - public function cancel() - { - return $this->promise()->cancel(); - } - - private function promise() - { - if (null === $this->promise) { - try { - $this->promise = resolve(call_user_func($this->factory)); - } catch (\Exception $exception) { - $this->promise = new RejectedPromise($exception); - } - } - - return $this->promise; - } -} diff --git a/core/vendor/react/promise/src/Promise.php b/core/vendor/react/promise/src/Promise.php deleted file mode 100644 index b2635bf307f3135c61bb5bba4eeef847c3ccc743..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/src/Promise.php +++ /dev/null @@ -1,182 +0,0 @@ -<?php - -namespace React\Promise; - -class Promise implements ExtendedPromiseInterface, CancellablePromiseInterface -{ - private $canceller; - private $result; - - private $handlers = []; - private $progressHandlers = []; - - private $requiredCancelRequests = 0; - private $cancelRequests = 0; - - public function __construct(callable $resolver, callable $canceller = null) - { - $this->canceller = $canceller; - $this->call($resolver); - } - - public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) - { - if (null !== $this->result) { - return $this->result->then($onFulfilled, $onRejected, $onProgress); - } - - if (null === $this->canceller) { - return new static($this->resolver($onFulfilled, $onRejected, $onProgress)); - } - - $this->requiredCancelRequests++; - - return new static($this->resolver($onFulfilled, $onRejected, $onProgress), function ($resolve, $reject, $progress) { - if (++$this->cancelRequests < $this->requiredCancelRequests) { - return; - } - - $this->cancel(); - }); - } - - public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) - { - if (null !== $this->result) { - return $this->result->done($onFulfilled, $onRejected, $onProgress); - } - - $this->handlers[] = function (PromiseInterface $promise) use ($onFulfilled, $onRejected) { - $promise - ->done($onFulfilled, $onRejected); - }; - - if ($onProgress) { - $this->progressHandlers[] = $onProgress; - } - } - - public function otherwise(callable $onRejected) - { - return $this->then(null, function ($reason) use ($onRejected) { - if (!_checkTypehint($onRejected, $reason)) { - return new RejectedPromise($reason); - } - - return $onRejected($reason); - }); - } - - public function always(callable $onFulfilledOrRejected) - { - return $this->then(function ($value) use ($onFulfilledOrRejected) { - return resolve($onFulfilledOrRejected())->then(function () use ($value) { - return $value; - }); - }, function ($reason) use ($onFulfilledOrRejected) { - return resolve($onFulfilledOrRejected())->then(function () use ($reason) { - return new RejectedPromise($reason); - }); - }); - } - - public function progress(callable $onProgress) - { - return $this->then(null, null, $onProgress); - } - - public function cancel() - { - if (null === $this->canceller || null !== $this->result) { - return; - } - - $this->call($this->canceller); - } - - private function resolver(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) - { - return function ($resolve, $reject, $notify) use ($onFulfilled, $onRejected, $onProgress) { - if ($onProgress) { - $progressHandler = function ($update) use ($notify, $onProgress) { - try { - $notify($onProgress($update)); - } catch (\Exception $e) { - $notify($e); - } - }; - } else { - $progressHandler = $notify; - } - - $this->handlers[] = function (PromiseInterface $promise) use ($onFulfilled, $onRejected, $resolve, $reject, $progressHandler) { - $promise - ->then($onFulfilled, $onRejected) - ->done($resolve, $reject, $progressHandler); - }; - - $this->progressHandlers[] = $progressHandler; - }; - } - - private function resolve($value = null) - { - if (null !== $this->result) { - return; - } - - $this->settle(resolve($value)); - } - - private function reject($reason = null) - { - if (null !== $this->result) { - return; - } - - $this->settle(reject($reason)); - } - - private function notify($update = null) - { - if (null !== $this->result) { - return; - } - - foreach ($this->progressHandlers as $handler) { - $handler($update); - } - } - - private function settle(ExtendedPromiseInterface $promise) - { - $result = $promise; - - foreach ($this->handlers as $handler) { - $handler($result); - } - - $this->progressHandlers = $this->handlers = []; - - $this->result = $result; - } - - private function call(callable $callback) - { - try { - $callback( - function ($value = null) { - $this->resolve($value); - }, - function ($reason = null) { - $this->reject($reason); - }, - function ($update = null) { - $this->notify($update); - } - ); - } catch (\Exception $e) { - $this->reject($e); - } - } -} diff --git a/core/vendor/react/promise/src/PromiseInterface.php b/core/vendor/react/promise/src/PromiseInterface.php deleted file mode 100644 index d80d11421ad1f60c4e05002288a07b91f6312b27..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/src/PromiseInterface.php +++ /dev/null @@ -1,11 +0,0 @@ -<?php - -namespace React\Promise; - -interface PromiseInterface -{ - /** - * @return PromiseInterface - */ - public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null); -} diff --git a/core/vendor/react/promise/src/PromisorInterface.php b/core/vendor/react/promise/src/PromisorInterface.php deleted file mode 100644 index 9341a4fa83cd840f7baa12a36987aa6ade856e88..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/src/PromisorInterface.php +++ /dev/null @@ -1,11 +0,0 @@ -<?php - -namespace React\Promise; - -interface PromisorInterface -{ - /** - * @return PromiseInterface - */ - public function promise(); -} diff --git a/core/vendor/react/promise/src/RejectedPromise.php b/core/vendor/react/promise/src/RejectedPromise.php deleted file mode 100644 index 350286df430044717fb36cb9519d56c2fe1424e2..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/src/RejectedPromise.php +++ /dev/null @@ -1,74 +0,0 @@ -<?php - -namespace React\Promise; - -class RejectedPromise implements ExtendedPromiseInterface, CancellablePromiseInterface -{ - private $reason; - - public function __construct($reason = null) - { - if ($reason instanceof PromiseInterface) { - throw new \InvalidArgumentException('You cannot create React\Promise\RejectedPromise with a promise. Use React\Promise\reject($promiseOrValue) instead.'); - } - - $this->reason = $reason; - } - - public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) - { - try { - if (null === $onRejected) { - return new RejectedPromise($this->reason); - } - - return resolve($onRejected($this->reason)); - } catch (\Exception $exception) { - return new RejectedPromise($exception); - } - } - - public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) - { - if (null === $onRejected) { - throw UnhandledRejectionException::resolve($this->reason); - } - - $result = $onRejected($this->reason); - - if ($result instanceof self) { - throw UnhandledRejectionException::resolve($result->reason); - } - - if ($result instanceof ExtendedPromiseInterface) { - $result->done(); - } - } - - public function otherwise(callable $onRejected) - { - if (!_checkTypehint($onRejected, $this->reason)) { - return new RejectedPromise($this->reason); - } - - return $this->then(null, $onRejected); - } - - public function always(callable $onFulfilledOrRejected) - { - return $this->then(null, function ($reason) use ($onFulfilledOrRejected) { - return resolve($onFulfilledOrRejected())->then(function () use ($reason) { - return new RejectedPromise($reason); - }); - }); - } - - public function progress(callable $onProgress) - { - return new RejectedPromise($this->reason); - } - - public function cancel() - { - } -} diff --git a/core/vendor/react/promise/src/UnhandledRejectionException.php b/core/vendor/react/promise/src/UnhandledRejectionException.php deleted file mode 100644 index ed166b30a53ab0083db657a0e5a60cfd6027ebbc..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/src/UnhandledRejectionException.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php - -namespace React\Promise; - -class UnhandledRejectionException extends \RuntimeException -{ - private $reason; - - public static function resolve($reason) - { - if ($reason instanceof \Exception) { - return $reason; - } - - return new static($reason); - } - - public function __construct($reason) - { - $this->reason = $reason; - - $message = sprintf('Unhandled Rejection: %s', json_encode($reason)); - - parent::__construct($message, 0); - } - - public function getReason() - { - return $this->reason; - } -} diff --git a/core/vendor/react/promise/src/functions.php b/core/vendor/react/promise/src/functions.php deleted file mode 100644 index 2eae6050e146ede391b66dd5756e0fdeed11be5c..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/src/functions.php +++ /dev/null @@ -1,196 +0,0 @@ -<?php - -namespace React\Promise; - -function resolve($promiseOrValue = null) -{ - if (!$promiseOrValue instanceof PromiseInterface) { - return new FulfilledPromise($promiseOrValue); - } - - if ($promiseOrValue instanceof ExtendedPromiseInterface) { - return $promiseOrValue; - } - - return new Promise(function ($resolve, $reject, $notify) use ($promiseOrValue) { - $promiseOrValue->then($resolve, $reject, $notify); - }); -} - -function reject($promiseOrValue = null) -{ - if ($promiseOrValue instanceof PromiseInterface) { - return resolve($promiseOrValue)->then(function ($value) { - return new RejectedPromise($value); - }); - } - - return new RejectedPromise($promiseOrValue); -} - -function all($promisesOrValues) -{ - return map($promisesOrValues, function ($val) { - return $val; - }); -} - -function race($promisesOrValues) -{ - return resolve($promisesOrValues) - ->then(function ($array) { - if (!is_array($array) || !$array) { - return resolve(); - } - - return new Promise(function ($resolve, $reject, $notify) use ($array) { - foreach ($array as $promiseOrValue) { - resolve($promiseOrValue) - ->done($resolve, $reject, $notify); - } - }); - }); -} - -function any($promisesOrValues) -{ - return some($promisesOrValues, 1) - ->then(function ($val) { - return array_shift($val); - }); -} - -function some($promisesOrValues, $howMany) -{ - return resolve($promisesOrValues) - ->then(function ($array) use ($howMany) { - if (!is_array($array) || !$array || $howMany < 1) { - return resolve([]); - } - - return new Promise(function ($resolve, $reject, $notify) use ($array, $howMany) { - $len = count($array); - $toResolve = min($howMany, $len); - $toReject = ($len - $toResolve) + 1; - $values = []; - $reasons = []; - - foreach ($array as $i => $promiseOrValue) { - $fulfiller = function ($val) use ($i, &$values, &$toResolve, $toReject, $resolve) { - if ($toResolve < 1 || $toReject < 1) { - return; - } - - $values[$i] = $val; - - if (0 === --$toResolve) { - $resolve($values); - } - }; - - $rejecter = function ($reason) use ($i, &$reasons, &$toReject, $toResolve, $reject) { - if ($toResolve < 1 || $toReject < 1) { - return; - } - - $reasons[$i] = $reason; - - if (0 === --$toReject) { - $reject($reasons); - } - }; - - resolve($promiseOrValue) - ->done($fulfiller, $rejecter, $notify); - } - }); - }); -} - -function map($promisesOrValues, callable $mapFunc) -{ - return resolve($promisesOrValues) - ->then(function ($array) use ($mapFunc) { - if (!is_array($array) || !$array) { - return resolve([]); - } - - return new Promise(function ($resolve, $reject, $notify) use ($array, $mapFunc) { - $toResolve = count($array); - $values = []; - - foreach ($array as $i => $promiseOrValue) { - resolve($promiseOrValue) - ->then($mapFunc) - ->done( - function ($mapped) use ($i, &$values, &$toResolve, $resolve) { - $values[$i] = $mapped; - - if (0 === --$toResolve) { - $resolve($values); - } - }, - $reject, - $notify - ); - } - }); - }); -} - -function reduce($promisesOrValues, callable $reduceFunc , $initialValue = null) -{ - return resolve($promisesOrValues) - ->then(function ($array) use ($reduceFunc, $initialValue) { - if (!is_array($array)) { - $array = []; - } - - $total = count($array); - $i = 0; - - // Wrap the supplied $reduceFunc with one that handles promises and then - // delegates to the supplied. - $wrappedReduceFunc = function ($current, $val) use ($reduceFunc, $total, &$i) { - return resolve($current) - ->then(function ($c) use ($reduceFunc, $total, &$i, $val) { - return resolve($val) - ->then(function ($value) use ($reduceFunc, $total, &$i, $c) { - return $reduceFunc($c, $value, $i++, $total); - }); - }); - }; - - return array_reduce($array, $wrappedReduceFunc, $initialValue); - }); -} - -// Internal functions -function _checkTypehint(callable $callback, $object) -{ - if (!is_object($object)) { - return true; - } - - if (is_array($callback)) { - $callbackReflection = new \ReflectionMethod($callback[0], $callback[1]); - } elseif (is_object($callback) && !$callback instanceof \Closure) { - $callbackReflection = new \ReflectionMethod($callback, '__invoke'); - } else { - $callbackReflection = new \ReflectionFunction($callback); - } - - $parameters = $callbackReflection->getParameters(); - - if (!isset($parameters[0])) { - return true; - } - - $expectedException = $parameters[0]; - - if (!$expectedException->getClass()) { - return true; - } - - return $expectedException->getClass()->isInstance($object); -} diff --git a/core/vendor/react/promise/src/functions_include.php b/core/vendor/react/promise/src/functions_include.php deleted file mode 100644 index c71decbf9895632b5c8fbc457857c2bd185a1fb1..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/src/functions_include.php +++ /dev/null @@ -1,5 +0,0 @@ -<?php - -if (!function_exists('React\Promise\resolve')) { - require __DIR__.'/functions.php'; -} diff --git a/core/vendor/react/promise/tests/DeferredTest.php b/core/vendor/react/promise/tests/DeferredTest.php deleted file mode 100644 index 16212e9e1cb8b1b06c8fdc3ba8c19635a6048ef7..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/DeferredTest.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php - -namespace React\Promise; - -use React\Promise\PromiseAdapter\CallbackPromiseAdapter; - -class DeferredTest extends TestCase -{ - use PromiseTest\FullTestTrait; - - public function getPromiseTestAdapter(callable $canceller = null) - { - $d = new Deferred($canceller); - - return new CallbackPromiseAdapter([ - 'promise' => [$d, 'promise'], - 'resolve' => [$d, 'resolve'], - 'reject' => [$d, 'reject'], - 'notify' => [$d, 'progress'], - 'settle' => [$d, 'resolve'], - ]); - } - - /** @test */ - public function progressIsAnAliasForNotify() - { - $deferred = new Deferred(); - - $sentinel = new \stdClass(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($sentinel); - - $deferred->promise() - ->then($this->expectCallableNever(), $this->expectCallableNever(), $mock); - - $deferred->progress($sentinel); - } -} diff --git a/core/vendor/react/promise/tests/FulfilledPromiseTest.php b/core/vendor/react/promise/tests/FulfilledPromiseTest.php deleted file mode 100644 index 97fc8f6c6d3c2f0c0b5fa278a6bf23f991753ec3..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/FulfilledPromiseTest.php +++ /dev/null @@ -1,50 +0,0 @@ -<?php - -namespace React\Promise; - -use React\Promise\PromiseAdapter\CallbackPromiseAdapter; - -class FulfilledPromiseTest extends TestCase -{ - use PromiseTest\PromiseSettledTestTrait, - PromiseTest\PromiseFulfilledTestTrait; - - public function getPromiseTestAdapter(callable $canceller = null) - { - $promise = null; - - return new CallbackPromiseAdapter([ - 'promise' => function () use (&$promise) { - if (!$promise) { - throw new \LogicException('FulfilledPromise must be resolved before obtaining the promise'); - } - - return $promise; - }, - 'resolve' => function ($value = null) use (&$promise) { - if (!$promise) { - $promise = new FulfilledPromise($value); - } - }, - 'reject' => function () { - throw new \LogicException('You cannot call reject() for React\Promise\FulfilledPromise'); - }, - 'notify' => function () { - // no-op - }, - 'settle' => function ($value = null) use (&$promise) { - if (!$promise) { - $promise = new FulfilledPromise($value); - } - }, - ]); - } - - /** @test */ - public function shouldThrowExceptionIfConstructedWithAPromise() - { - $this->setExpectedException('\InvalidArgumentException'); - - return new FulfilledPromise(new FulfilledPromise()); - } -} diff --git a/core/vendor/react/promise/tests/FunctionAllTest.php b/core/vendor/react/promise/tests/FunctionAllTest.php deleted file mode 100644 index fcd0a5df15637ff7492cc0c596eedc4ec5702964..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/FunctionAllTest.php +++ /dev/null @@ -1,97 +0,0 @@ -<?php - -namespace React\Promise; - -class FunctionAllTest extends TestCase -{ - /** @test */ - public function shouldResolveEmptyInput() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo([])); - - all([]) - ->then($mock); - } - - /** @test */ - public function shouldResolveValuesArray() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo([1, 2, 3])); - - all([1, 2, 3]) - ->then($mock); - } - - /** @test */ - public function shouldResolvePromisesArray() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo([1, 2, 3])); - - all([resolve(1), resolve(2), resolve(3)]) - ->then($mock); - } - - /** @test */ - public function shouldResolveSparseArrayInput() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo([null, 1, null, 1, 1])); - - all([null, 1, null, 1, 1]) - ->then($mock); - } - - /** @test */ - public function shouldRejectIfAnyInputPromiseRejects() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(2)); - - all([resolve(1), reject(2), resolve(3)]) - ->then($this->expectCallableNever(), $mock); - } - - /** @test */ - public function shouldAcceptAPromiseForAnArray() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo([1, 2, 3])); - - all(resolve([1, 2, 3])) - ->then($mock); - } - - /** @test */ - public function shouldResolveToEmptyArrayWhenInputPromiseDoesNotResolveToArray() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo([])); - - all(resolve(1)) - ->then($mock); - } -} diff --git a/core/vendor/react/promise/tests/FunctionAnyTest.php b/core/vendor/react/promise/tests/FunctionAnyTest.php deleted file mode 100644 index bf8a0db478582c9a2d03e88ff7c54fe69e21991a..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/FunctionAnyTest.php +++ /dev/null @@ -1,116 +0,0 @@ -<?php - -namespace React\Promise; - -class FunctionAnyTest extends TestCase -{ - /** @test */ - public function shouldResolveToNullWithEmptyInputArray() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(null)); - - any([]) - ->then($mock); - } - - /** @test */ - public function shouldResolveWithAnInputValue() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - any([1, 2, 3]) - ->then($mock); - } - - /** @test */ - public function shouldResolveWithAPromisedInputValue() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - any([resolve(1), resolve(2), resolve(3)]) - ->then($mock); - } - - /** @test */ - public function shouldRejectWithAllRejectedInputValuesIfAllInputsAreRejected() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo([0 => 1, 1 => 2, 2 => 3])); - - any([reject(1), reject(2), reject(3)]) - ->then($this->expectCallableNever(), $mock); - } - - /** @test */ - public function shouldResolveWhenFirstInputPromiseResolves() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - any([resolve(1), reject(2), reject(3)]) - ->then($mock); - } - - /** @test */ - public function shouldAcceptAPromiseForAnArray() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - any(resolve([1, 2, 3])) - ->then($mock); - } - - /** @test */ - public function shouldResolveToNullArrayWhenInputPromiseDoesNotResolveToArray() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(null)); - - any(resolve(1)) - ->then($mock); - } - - /** @test */ - public function shouldNotRelyOnArryIndexesWhenUnwrappingToASingleResolutionValue() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(2)); - - $d1 = new Deferred(); - $d2 = new Deferred(); - - any(['abc' => $d1->promise(), 1 => $d2->promise()]) - ->then($mock); - - $d2->resolve(2); - $d1->resolve(1); - } -} diff --git a/core/vendor/react/promise/tests/FunctionCheckTypehintTest.php b/core/vendor/react/promise/tests/FunctionCheckTypehintTest.php deleted file mode 100644 index 8449bc1f7a39e2f0e3049e307f8e41bf920d8d08..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/FunctionCheckTypehintTest.php +++ /dev/null @@ -1,118 +0,0 @@ -<?php - -namespace React\Promise; - -class FunctionCheckTypehintTest extends TestCase -{ - /** @test */ - public function shouldAcceptClosureCallbackWithTypehint() - { - $this->assertTrue(_checkTypehint(function (\InvalidArgumentException $e) { - }, new \InvalidArgumentException())); - $this->assertfalse(_checkTypehint(function (\InvalidArgumentException $e) { - }, new \Exception())); - } - - /** @test */ - public function shouldAcceptFunctionStringCallbackWithTypehint() - { - $this->assertTrue(_checkTypehint('React\Promise\testCallbackWithTypehint', new \InvalidArgumentException())); - $this->assertfalse(_checkTypehint('React\Promise\testCallbackWithTypehint', new \Exception())); - } - - /** @test */ - public function shouldAcceptInvokableObjectCallbackWithTypehint() - { - $this->assertTrue(_checkTypehint(new TestCallbackWithTypehintClass(), new \InvalidArgumentException())); - $this->assertfalse(_checkTypehint(new TestCallbackWithTypehintClass(), new \Exception())); - } - - /** @test */ - public function shouldAcceptObjectMethodCallbackWithTypehint() - { - $this->assertTrue(_checkTypehint([new TestCallbackWithTypehintClass(), 'testCallback'], new \InvalidArgumentException())); - $this->assertfalse(_checkTypehint([new TestCallbackWithTypehintClass(), 'testCallback'], new \Exception())); - } - - /** @test */ - public function shouldAcceptStaticClassCallbackWithTypehint() - { - $this->assertTrue(_checkTypehint(['React\Promise\TestCallbackWithTypehintClass', 'testCallbackStatic'], new \InvalidArgumentException())); - $this->assertfalse(_checkTypehint(['React\Promise\TestCallbackWithTypehintClass', 'testCallbackStatic'], new \Exception())); - } - - /** @test */ - public function shouldAcceptClosureCallbackWithoutTypehint() - { - $this->assertTrue(_checkTypehint(function (\InvalidArgumentException $e) { - }, new \InvalidArgumentException())); - } - - /** @test */ - public function shouldAcceptFunctionStringCallbackWithoutTypehint() - { - $this->assertTrue(_checkTypehint('React\Promise\testCallbackWithoutTypehint', new \InvalidArgumentException())); - } - - /** @test */ - public function shouldAcceptInvokableObjectCallbackWithoutTypehint() - { - $this->assertTrue(_checkTypehint(new TestCallbackWithoutTypehintClass(), new \InvalidArgumentException())); - } - - /** @test */ - public function shouldAcceptObjectMethodCallbackWithoutTypehint() - { - $this->assertTrue(_checkTypehint([new TestCallbackWithoutTypehintClass(), 'testCallback'], new \InvalidArgumentException())); - } - - /** @test */ - public function shouldAcceptStaticClassCallbackWithoutTypehint() - { - $this->assertTrue(_checkTypehint(['React\Promise\TestCallbackWithoutTypehintClass', 'testCallbackStatic'], new \InvalidArgumentException())); - } -} - -function testCallbackWithTypehint(\InvalidArgumentException $e) -{ -} - -function testCallbackWithoutTypehint() -{ -} - -class TestCallbackWithTypehintClass -{ - public function __invoke(\InvalidArgumentException $e) - { - - } - - public function testCallback(\InvalidArgumentException $e) - { - - } - - public static function testCallbackStatic(\InvalidArgumentException $e) - { - - } -} - -class TestCallbackWithoutTypehintClass -{ - public function __invoke() - { - - } - - public function testCallback() - { - - } - - public static function testCallbackStatic() - { - - } -} diff --git a/core/vendor/react/promise/tests/FunctionMapTest.php b/core/vendor/react/promise/tests/FunctionMapTest.php deleted file mode 100644 index b8bf3a8327b80e6fb6de96a64d4ad014d39525ee..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/FunctionMapTest.php +++ /dev/null @@ -1,125 +0,0 @@ -<?php - -namespace React\Promise; - -class FunctionMapTest extends TestCase -{ - protected function mapper() - { - return function ($val) { - return $val * 2; - }; - } - - protected function promiseMapper() - { - return function ($val) { - return resolve($val * 2); - }; - } - - /** @test */ - public function shouldMapInputValuesArray() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo([2, 4, 6])); - - map( - [1, 2, 3], - $this->mapper() - )->then($mock); - } - - /** @test */ - public function shouldMapInputPromisesArray() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo([2, 4, 6])); - - map( - [resolve(1), resolve(2), resolve(3)], - $this->mapper() - )->then($mock); - } - - /** @test */ - public function shouldMapMixedInputArray() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo([2, 4, 6])); - - map( - [1, resolve(2), 3], - $this->mapper() - )->then($mock); - } - - /** @test */ - public function shouldMapInputWhenMapperReturnsAPromise() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo([2, 4, 6])); - - map( - [1, 2, 3], - $this->promiseMapper() - )->then($mock); - } - - /** @test */ - public function shouldAcceptAPromiseForAnArray() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo([2, 4, 6])); - - map( - resolve([1, resolve(2), 3]), - $this->mapper() - )->then($mock); - } - - /** @test */ - public function shouldResolveToEmptyArrayWhenInputPromiseDoesNotResolveToArray() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo([])); - - map( - resolve(1), - $this->mapper() - )->then($mock); - } - - /** @test */ - public function shouldRejectWhenInputContainsRejection() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(2)); - - map( - [resolve(1), reject(2), resolve(3)], - $this->mapper() - )->then($this->expectCallableNever(), $mock); - } -} diff --git a/core/vendor/react/promise/tests/FunctionRaceTest.php b/core/vendor/react/promise/tests/FunctionRaceTest.php deleted file mode 100644 index 553220c5e337b6d1b5b020fb1cfd40a134f72d9b..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/FunctionRaceTest.php +++ /dev/null @@ -1,122 +0,0 @@ -<?php - -namespace React\Promise; - -class FunctionRaceTest extends TestCase -{ - /** @test */ - public function shouldResolveEmptyInput() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(null)); - - race( - [] - )->then($mock); - } - - /** @test */ - public function shouldResolveValuesArray() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - race( - [1, 2, 3] - )->then($mock); - } - - /** @test */ - public function shouldResolvePromisesArray() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(2)); - - $d1 = new Deferred(); - $d2 = new Deferred(); - $d3 = new Deferred(); - - race( - [$d1->promise(), $d2->promise(), $d3->promise()] - )->then($mock); - - $d2->resolve(2); - - $d1->resolve(1); - $d3->resolve(3); - } - - /** @test */ - public function shouldResolveSparseArrayInput() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(null)); - - race( - [null, 1, null, 2, 3] - )->then($mock); - } - - /** @test */ - public function shouldRejectIfFirstSettledPromiseRejects() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(2)); - - $d1 = new Deferred(); - $d2 = new Deferred(); - $d3 = new Deferred(); - - race( - [$d1->promise(), $d2->promise(), $d3->promise()] - )->then($this->expectCallableNever(), $mock); - - $d2->reject(2); - - $d1->resolve(1); - $d3->resolve(3); - } - - /** @test */ - public function shouldAcceptAPromiseForAnArray() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - race( - resolve([1, 2, 3]) - )->then($mock); - } - - /** @test */ - public function shouldResolveToNullWhenInputPromiseDoesNotResolveToArray() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(null)); - - race( - resolve(1) - )->then($mock); - } -} diff --git a/core/vendor/react/promise/tests/FunctionReduceTest.php b/core/vendor/react/promise/tests/FunctionReduceTest.php deleted file mode 100644 index 715e84778c5384268e389f04a3e70414dda5361f..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/FunctionReduceTest.php +++ /dev/null @@ -1,290 +0,0 @@ -<?php - -namespace React\Promise; - -class FunctionReduceTest extends TestCase -{ - protected function plus() - { - return function ($sum, $val) { - return $sum + $val; - }; - } - - protected function append() - { - return function ($sum, $val) { - return $sum . $val; - }; - } - - /** @test */ - public function shouldReduceValuesWithoutInitialValue() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(6)); - - reduce( - [1, 2, 3], - $this->plus() - )->then($mock); - } - - /** @test */ - public function shouldReduceValuesWithInitialValue() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(7)); - - reduce( - [1, 2, 3], - $this->plus(), - 1 - )->then($mock); - } - - /** @test */ - public function shouldReduceValuesWithInitialPromise() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(7)); - - reduce( - [1, 2, 3], - $this->plus(), - resolve(1) - )->then($mock); - } - - /** @test */ - public function shouldReducePromisedValuesWithoutInitialValue() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(6)); - - reduce( - [resolve(1), resolve(2), resolve(3)], - $this->plus() - )->then($mock); - } - - /** @test */ - public function shouldReducePromisedValuesWithInitialValue() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(7)); - - reduce( - [resolve(1), resolve(2), resolve(3)], - $this->plus(), - 1 - )->then($mock); - } - - /** @test */ - public function shouldReducePromisedValuesWithInitialPromise() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(7)); - - reduce( - [resolve(1), resolve(2), resolve(3)], - $this->plus(), - resolve(1) - )->then($mock); - } - - /** @test */ - public function shouldReduceEmptyInputWithInitialValue() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - reduce( - [], - $this->plus(), - 1 - )->then($mock); - } - - /** @test */ - public function shouldReduceEmptyInputWithInitialPromise() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - reduce( - [], - $this->plus(), - resolve(1) - )->then($mock); - } - - /** @test */ - public function shouldRejectWhenInputContainsRejection() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(2)); - - reduce( - [resolve(1), reject(2), resolve(3)], - $this->plus(), - resolve(1) - )->then($this->expectCallableNever(), $mock); - } - - /** @test */ - public function shouldResolveWithNullWhenInputIsEmptyAndNoInitialValueOrPromiseProvided() - { - // Note: this is different from when.js's behavior! - // In when.reduce(), this rejects with a TypeError exception (following - // JavaScript's [].reduce behavior. - // We're following PHP's array_reduce behavior and resolve with NULL. - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(null)); - - reduce( - [], - $this->plus() - )->then($mock); - } - - /** @test */ - public function shouldAllowSparseArrayInputWithoutInitialValue() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(3)); - - reduce( - [null, null, 1, null, 1, 1], - $this->plus() - )->then($mock); - } - - /** @test */ - public function shouldAllowSparseArrayInputWithInitialValue() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(4)); - - reduce( - [null, null, 1, null, 1, 1], - $this->plus(), - 1 - )->then($mock); - } - - /** @test */ - public function shouldReduceInInputOrder() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo('123')); - - reduce( - [1, 2, 3], - $this->append(), - '' - )->then($mock); - } - - /** @test */ - public function shouldAcceptAPromiseForAnArray() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo('123')); - - reduce( - resolve([1, 2, 3]), - $this->append(), - '' - )->then($mock); - } - - /** @test */ - public function shouldResolveToInitialValueWhenInputPromiseDoesNotResolveToAnArray() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - reduce( - resolve(1), - $this->plus(), - 1 - )->then($mock); - } - - /** @test */ - public function shouldProvideCorrectBasisValue() - { - $insertIntoArray = function ($arr, $val, $i) { - $arr[$i] = $val; - - return $arr; - }; - - $d1 = new Deferred(); - $d2 = new Deferred(); - $d3 = new Deferred(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo([1, 2, 3])); - - reduce( - [$d1->promise(), $d2->promise(), $d3->promise()], - $insertIntoArray, - [] - )->then($mock); - - $d3->resolve(3); - $d1->resolve(1); - $d2->resolve(2); - } -} diff --git a/core/vendor/react/promise/tests/FunctionRejectTest.php b/core/vendor/react/promise/tests/FunctionRejectTest.php deleted file mode 100644 index 84b8ec6a04245afeb27a8d9004ebb7f7f3886bfa..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/FunctionRejectTest.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php - -namespace React\Promise; - -class FunctionRejectTest extends TestCase -{ - /** @test */ - public function shouldRejectAnImmediateValue() - { - $expected = 123; - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($expected)); - - reject($expected) - ->then( - $this->expectCallableNever(), - $mock - ); - } - - /** @test */ - public function shouldRejectAFulfilledPromise() - { - $expected = 123; - - $resolved = new FulfilledPromise($expected); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($expected)); - - reject($resolved) - ->then( - $this->expectCallableNever(), - $mock - ); - } - - /** @test */ - public function shouldRejectARejectedPromise() - { - $expected = 123; - - $resolved = new RejectedPromise($expected); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($expected)); - - reject($resolved) - ->then( - $this->expectCallableNever(), - $mock - ); - } -} diff --git a/core/vendor/react/promise/tests/FunctionResolveTest.php b/core/vendor/react/promise/tests/FunctionResolveTest.php deleted file mode 100644 index 576c30931f01ff12434ace75d5813be0681d0c33..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/FunctionResolveTest.php +++ /dev/null @@ -1,102 +0,0 @@ -<?php - -namespace React\Promise; - -class FunctionResolveTest extends TestCase -{ - /** @test */ - public function shouldResolveAnImmediateValue() - { - $expected = 123; - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($expected)); - - resolve($expected) - ->then( - $mock, - $this->expectCallableNever() - ); - } - - /** @test */ - public function shouldResolveAFulfilledPromise() - { - $expected = 123; - - $resolved = new FulfilledPromise($expected); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($expected)); - - resolve($resolved) - ->then( - $mock, - $this->expectCallableNever() - ); - } - - /** @test */ - public function shouldRejectARejectedPromise() - { - $expected = 123; - - $resolved = new RejectedPromise($expected); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($expected)); - - resolve($resolved) - ->then( - $this->expectCallableNever(), - $mock - ); - } - - /** @test */ - public function shouldSupportDeepNestingInPromiseChains() - { - $d = new Deferred(); - $d->resolve(false); - - $result = resolve(resolve($d->promise()->then(function ($val) { - $d = new Deferred(); - $d->resolve($val); - - $identity = function ($val) { - return $val; - }; - - return resolve($d->promise()->then($identity))->then( - function ($val) { - return !$val; - } - ); - }))); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(true)); - - $result->then($mock); - } - - /** @test */ - public function returnsExtendePromiseForSimplePromise() - { - $promise = $this->getMock('React\Promise\PromiseInterface'); - - $this->assertInstanceOf('React\Promise\ExtendedPromiseInterface', resolve($promise)); - } -} diff --git a/core/vendor/react/promise/tests/FunctionSomeTest.php b/core/vendor/react/promise/tests/FunctionSomeTest.php deleted file mode 100644 index 09e5350493efa244d0ec8364e99eb074d3603adc..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/FunctionSomeTest.php +++ /dev/null @@ -1,126 +0,0 @@ -<?php - -namespace React\Promise; - -class FunctionSomeTest extends TestCase -{ - /** @test */ - public function shouldResolveEmptyInput() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo([])); - - some( - [], - 1 - )->then($mock); - } - - /** @test */ - public function shouldResolveValuesArray() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo([1, 2])); - - some( - [1, 2, 3], - 2 - )->then($mock); - } - - /** @test */ - public function shouldResolvePromisesArray() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo([1, 2])); - - some( - [resolve(1), resolve(2), resolve(3)], - 2 - )->then($mock); - } - - /** @test */ - public function shouldResolveSparseArrayInput() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo([null, 1])); - - some( - [null, 1, null, 2, 3], - 2 - )->then($mock); - } - - /** @test */ - public function shouldRejectIfAnyInputPromiseRejectsBeforeDesiredNumberOfInputsAreResolved() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo([1 => 2, 2 => 3])); - - some( - [resolve(1), reject(2), reject(3)], - 2 - )->then($this->expectCallableNever(), $mock); - } - - /** @test */ - public function shouldAcceptAPromiseForAnArray() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo([1, 2])); - - some( - resolve([1, 2, 3]), - 2 - )->then($mock); - } - - /** @test */ - public function shouldResolveWithEmptyArrayIfHowManyIsLessThanOne() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo([])); - - some( - [1], - 0 - )->then($mock); - } - - /** @test */ - public function shouldResolveToEmptyArrayWhenInputPromiseDoesNotResolveToArray() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo([])); - - some( - resolve(1), - 1 - )->then($mock); - } -} diff --git a/core/vendor/react/promise/tests/LazyPromiseTest.php b/core/vendor/react/promise/tests/LazyPromiseTest.php deleted file mode 100644 index b6308818a7714899c619d46fae8a2e45d1866ab0..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/LazyPromiseTest.php +++ /dev/null @@ -1,107 +0,0 @@ -<?php - -namespace React\Promise; - -use React\Promise\PromiseAdapter\CallbackPromiseAdapter; - -class LazyPromiseTest extends TestCase -{ - use PromiseTest\FullTestTrait; - - public function getPromiseTestAdapter(callable $canceller = null) - { - $d = new Deferred($canceller); - - $factory = function () use ($d) { - return $d->promise(); - }; - - return new CallbackPromiseAdapter([ - 'promise' => function () use ($factory) { - return new LazyPromise($factory); - }, - 'resolve' => [$d, 'resolve'], - 'reject' => [$d, 'reject'], - 'notify' => [$d, 'progress'], - 'settle' => [$d, 'resolve'], - ]); - } - - /** @test */ - public function shouldNotCallFactoryIfThenIsNotInvoked() - { - $factory = $this->createCallableMock(); - $factory - ->expects($this->never()) - ->method('__invoke'); - - new LazyPromise($factory); - } - - /** @test */ - public function shouldCallFactoryIfThenIsInvoked() - { - $factory = $this->createCallableMock(); - $factory - ->expects($this->once()) - ->method('__invoke'); - - $p = new LazyPromise($factory); - $p->then(); - } - - /** @test */ - public function shouldReturnPromiseFromFactory() - { - $factory = $this->createCallableMock(); - $factory - ->expects($this->once()) - ->method('__invoke') - ->will($this->returnValue(new FulfilledPromise(1))); - - $onFulfilled = $this->createCallableMock(); - $onFulfilled - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $p = new LazyPromise($factory); - - $p->then($onFulfilled); - } - - /** @test */ - public function shouldReturnPromiseIfFactoryReturnsNull() - { - $factory = $this->createCallableMock(); - $factory - ->expects($this->once()) - ->method('__invoke') - ->will($this->returnValue(null)); - - $p = new LazyPromise($factory); - $this->assertInstanceOf('React\\Promise\\PromiseInterface', $p->then()); - } - - /** @test */ - public function shouldReturnRejectedPromiseIfFactoryThrowsException() - { - $exception = new \Exception(); - - $factory = $this->createCallableMock(); - $factory - ->expects($this->once()) - ->method('__invoke') - ->will($this->throwException($exception)); - - $onRejected = $this->createCallableMock(); - $onRejected - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($exception)); - - $p = new LazyPromise($factory); - - $p->then($this->expectCallableNever(), $onRejected); - } -} diff --git a/core/vendor/react/promise/tests/PromiseAdapter/CallbackPromiseAdapter.php b/core/vendor/react/promise/tests/PromiseAdapter/CallbackPromiseAdapter.php deleted file mode 100644 index bdedf4658e455eac097fe41325d1e0a3e45c4d5b..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/PromiseAdapter/CallbackPromiseAdapter.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -namespace React\Promise\PromiseAdapter; - -use React\Promise; - -class CallbackPromiseAdapter implements PromiseAdapterInterface -{ - private $callbacks; - - public function __construct(array $callbacks) - { - $this->callbacks = $callbacks; - } - - public function promise() - { - return call_user_func_array($this->callbacks['promise'], func_get_args()); - } - - public function resolve() - { - return call_user_func_array($this->callbacks['resolve'], func_get_args()); - } - - public function reject() - { - return call_user_func_array($this->callbacks['reject'], func_get_args()); - } - - public function notify() - { - return call_user_func_array($this->callbacks['notify'], func_get_args()); - } - - public function settle() - { - return call_user_func_array($this->callbacks['settle'], func_get_args()); - } -} diff --git a/core/vendor/react/promise/tests/PromiseAdapter/PromiseAdapterInterface.php b/core/vendor/react/promise/tests/PromiseAdapter/PromiseAdapterInterface.php deleted file mode 100644 index 9157cd4ea7253a7c7fe7d467aea8fe0c1ccf77a8..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/PromiseAdapter/PromiseAdapterInterface.php +++ /dev/null @@ -1,14 +0,0 @@ -<?php - -namespace React\Promise\PromiseAdapter; - -use React\Promise; - -interface PromiseAdapterInterface -{ - public function promise(); - public function resolve(); - public function reject(); - public function notify(); - public function settle(); -} diff --git a/core/vendor/react/promise/tests/PromiseTest.php b/core/vendor/react/promise/tests/PromiseTest.php deleted file mode 100644 index faba7046839ccdd1a96590f2dd4dfbbcc472554e..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/PromiseTest.php +++ /dev/null @@ -1,116 +0,0 @@ -<?php - -namespace React\Promise; - -use React\Promise\PromiseAdapter\CallbackPromiseAdapter; - -class PromiseTest extends TestCase -{ - use PromiseTest\FullTestTrait; - - public function getPromiseTestAdapter(callable $canceller = null) - { - $resolveCallback = $rejectCallback = $progressCallback = null; - - $promise = new Promise(function ($resolve, $reject, $progress) use (&$resolveCallback, &$rejectCallback, &$progressCallback) { - $resolveCallback = $resolve; - $rejectCallback = $reject; - $progressCallback = $progress; - }, $canceller); - - return new CallbackPromiseAdapter([ - 'promise' => function () use ($promise) { - return $promise; - }, - 'resolve' => $resolveCallback, - 'reject' => $rejectCallback, - 'notify' => $progressCallback, - 'settle' => $resolveCallback, - ]); - } - - /** @test */ - public function shouldRejectIfResolverThrowsException() - { - $exception = new \Exception('foo'); - - $promise = new Promise(function () use ($exception) { - throw $exception; - }); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($exception)); - - $promise - ->then($this->expectCallableNever(), $mock); - } - - /** @test */ - public function shouldFulfillIfFullfilledWithSimplePromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo('foo')); - - $adapter->promise() - ->then($mock); - - $adapter->resolve(new SimpleFulfilledTestPromise()); - } - - /** @test */ - public function shouldRejectIfRejectedWithSimplePromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo('foo')); - - $adapter->promise() - ->then($this->expectCallableNever(), $mock); - - $adapter->resolve(new SimpleRejectedTestPromise()); - } -} - -class SimpleFulfilledTestPromise implements PromiseInterface -{ - public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) - { - try { - if ($onFulfilled) { - $onFulfilled('foo'); - } - - return new self('foo'); - } catch (\Exception $exception) { - return new RejectedPromise($exception); - } - } -} - -class SimpleRejectedTestPromise implements PromiseInterface -{ - public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) - { - try { - if ($onRejected) { - $onRejected('foo'); - } - - return new self('foo'); - } catch (\Exception $exception) { - return new RejectedPromise($exception); - } - } -} diff --git a/core/vendor/react/promise/tests/PromiseTest/CancelTestTrait.php b/core/vendor/react/promise/tests/PromiseTest/CancelTestTrait.php deleted file mode 100644 index d6c095662c8fccdb51ffc25d6baa13dc5da60111..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/PromiseTest/CancelTestTrait.php +++ /dev/null @@ -1,206 +0,0 @@ -<?php - -namespace React\Promise\PromiseTest; - -use React\Promise; - -trait CancelTestTrait -{ - /** - * @return \React\Promise\PromiseAdapter\PromiseAdapterInterface - */ - abstract public function getPromiseTestAdapter(callable $canceller = null); - - /** @test */ - public function cancelShouldCallCancellerWithResolverArguments() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->isType('callable'), $this->isType('callable'), $this->isType('callable')); - - $adapter = $this->getPromiseTestAdapter($mock); - - $adapter->promise()->cancel(); - } - - /** @test */ - public function cancelShouldFulfillPromiseIfCancellerFulfills() - { - $adapter = $this->getPromiseTestAdapter(function ($resolve) { - $resolve(1); - }); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $adapter->promise() - ->then($mock, $this->expectCallableNever()); - - $adapter->promise()->cancel(); - } - - /** @test */ - public function cancelShouldRejectPromiseIfCancellerRejects() - { - $adapter = $this->getPromiseTestAdapter(function ($resolve, $reject) { - $reject(1); - }); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $adapter->promise() - ->then($this->expectCallableNever(), $mock); - - $adapter->promise()->cancel(); - } - - /** @test */ - public function cancelShouldRejectPromiseWithExceptionIfCancellerThrows() - { - $e = new \Exception(); - - $adapter = $this->getPromiseTestAdapter(function () use ($e) { - throw $e; - }); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($e)); - - $adapter->promise() - ->then($this->expectCallableNever(), $mock); - - $adapter->promise()->cancel(); - } - - /** @test */ - public function cancelShouldProgressPromiseIfCancellerNotifies() - { - $adapter = $this->getPromiseTestAdapter(function ($resolve, $reject, $progress) { - $progress(1); - }); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $adapter->promise() - ->then($this->expectCallableNever(), $this->expectCallableNever(), $mock); - - $adapter->promise()->cancel(); - } - - /** @test */ - public function cancelShouldCallCancellerOnlyOnceIfCancellerResolves() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->will($this->returnCallback(function ($resolve) { - $resolve(); - })); - - $adapter = $this->getPromiseTestAdapter($mock); - - $adapter->promise()->cancel(); - $adapter->promise()->cancel(); - } - - /** @test */ - public function cancelShouldHaveNoEffectIfCancellerDoesNothing() - { - $adapter = $this->getPromiseTestAdapter(function () {}); - - $adapter->promise() - ->then($this->expectCallableNever(), $this->expectCallableNever()); - - $adapter->promise()->cancel(); - $adapter->promise()->cancel(); - } - - /** @test */ - public function cancelShouldCallCancellerFromDeepNestedPromiseChain() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke'); - - $adapter = $this->getPromiseTestAdapter($mock); - - $promise = $adapter->promise() - ->then(function () { - return new Promise\Promise(function () {}); - }) - ->then(function () { - $d = new Promise\Deferred(); - - return $d->promise(); - }) - ->then(function () { - return new Promise\Promise(function () {}); - }); - - $promise->cancel(); - } - - /** @test */ - public function cancelCalledOnChildrenSouldOnlyCancelWhenAllChildrenCancelled() - { - $adapter = $this->getPromiseTestAdapter($this->expectCallableNever()); - - $child1 = $adapter->promise() - ->then() - ->then(); - - $adapter->promise() - ->then(); - - $child1->cancel(); - } - - /** @test */ - public function cancelShouldTriggerCancellerWhenAllChildrenCancel() - { - $adapter = $this->getPromiseTestAdapter($this->expectCallableOnce()); - - $child1 = $adapter->promise() - ->then() - ->then(); - - $child2 = $adapter->promise() - ->then(); - - $child1->cancel(); - $child2->cancel(); - } - - /** @test */ - public function cancelShouldAlwaysTriggerCancellerWhenCalledOnRootPromise() - { - $adapter = $this->getPromiseTestAdapter($this->expectCallableOnce()); - - $adapter->promise() - ->then() - ->then(); - - $adapter->promise() - ->then(); - - $adapter->promise()->cancel(); - } -} diff --git a/core/vendor/react/promise/tests/PromiseTest/FullTestTrait.php b/core/vendor/react/promise/tests/PromiseTest/FullTestTrait.php deleted file mode 100644 index 3ce45d61fae47689c45d79e55b4a781d74a488fc..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/PromiseTest/FullTestTrait.php +++ /dev/null @@ -1,15 +0,0 @@ -<?php - -namespace React\Promise\PromiseTest; - -trait FullTestTrait -{ - use PromisePendingTestTrait, - PromiseSettledTestTrait, - PromiseFulfilledTestTrait, - PromiseRejectedTestTrait, - ResolveTestTrait, - RejectTestTrait, - NotifyTestTrait, - CancelTestTrait; -} diff --git a/core/vendor/react/promise/tests/PromiseTest/NotifyTestTrait.php b/core/vendor/react/promise/tests/PromiseTest/NotifyTestTrait.php deleted file mode 100644 index 4501df67683f2f1f0d2885c410311c4d733166c9..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/PromiseTest/NotifyTestTrait.php +++ /dev/null @@ -1,336 +0,0 @@ -<?php - -namespace React\Promise\PromiseTest; - -trait NotifyTestTrait -{ - /** - * @return \React\Promise\PromiseAdapter\PromiseAdapterInterface - */ - abstract public function getPromiseTestAdapter(callable $canceller = null); - - /** @test */ - public function notifyShouldProgress() - { - $adapter = $this->getPromiseTestAdapter(); - - $sentinel = new \stdClass(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($sentinel); - - $adapter->promise() - ->then($this->expectCallableNever(), $this->expectCallableNever(), $mock); - - $adapter->notify($sentinel); - } - - /** @test */ - public function notifyShouldPropagateProgressToDownstreamPromises() - { - $adapter = $this->getPromiseTestAdapter(); - - $sentinel = new \stdClass(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->will($this->returnArgument(0)); - - $mock2 = $this->createCallableMock(); - $mock2 - ->expects($this->once()) - ->method('__invoke') - ->with($sentinel); - - $adapter->promise() - ->then( - $this->expectCallableNever(), - $this->expectCallableNever(), - $mock - ) - ->then( - $this->expectCallableNever(), - $this->expectCallableNever(), - $mock2 - ); - - $adapter->notify($sentinel); - } - - /** @test */ - public function notifyShouldPropagateTransformedProgressToDownstreamPromises() - { - $adapter = $this->getPromiseTestAdapter(); - - $sentinel = new \stdClass(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->will($this->returnValue($sentinel)); - - $mock2 = $this->createCallableMock(); - $mock2 - ->expects($this->once()) - ->method('__invoke') - ->with($sentinel); - - $adapter->promise() - ->then( - $this->expectCallableNever(), - $this->expectCallableNever(), - $mock - ) - ->then( - $this->expectCallableNever(), - $this->expectCallableNever(), - $mock2 - ); - - $adapter->notify(1); - } - - /** @test */ - public function notifyShouldPropagateCaughtExceptionValueAsProgress() - { - $adapter = $this->getPromiseTestAdapter(); - - $exception = new \Exception(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->will($this->throwException($exception)); - - $mock2 = $this->createCallableMock(); - $mock2 - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($exception)); - - $adapter->promise() - ->then( - $this->expectCallableNever(), - $this->expectCallableNever(), - $mock - ) - ->then( - $this->expectCallableNever(), - $this->expectCallableNever(), - $mock2 - ); - - $adapter->notify(1); - } - - /** @test */ - public function notifyShouldForwardProgressEventsWhenIntermediaryCallbackTiedToAResolvedPromiseReturnsAPromise() - { - $adapter = $this->getPromiseTestAdapter(); - $adapter2 = $this->getPromiseTestAdapter(); - - $promise2 = $adapter2->promise(); - - $sentinel = new \stdClass(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($sentinel); - - // resolve BEFORE attaching progress handler - $adapter->resolve(); - - $adapter->promise() - ->then(function () use ($promise2) { - return $promise2; - }) - ->then( - $this->expectCallableNever(), - $this->expectCallableNever(), - $mock - ); - - $adapter2->notify($sentinel); - } - - /** @test */ - public function notifyShouldForwardProgressEventsWhenIntermediaryCallbackTiedToAnUnresolvedPromiseReturnsAPromise() - { - $adapter = $this->getPromiseTestAdapter(); - $adapter2 = $this->getPromiseTestAdapter(); - - $promise2 = $adapter2->promise(); - - $sentinel = new \stdClass(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($sentinel); - - $adapter->promise() - ->then(function () use ($promise2) { - return $promise2; - }) - ->then( - $this->expectCallableNever(), - $this->expectCallableNever(), - $mock - ); - - // resolve AFTER attaching progress handler - $adapter->resolve(); - $adapter2->notify($sentinel); - } - - /** @test */ - public function notifyShouldForwardProgressWhenResolvedWithAnotherPromise() - { - $adapter = $this->getPromiseTestAdapter(); - $adapter2 = $this->getPromiseTestAdapter(); - - $sentinel = new \stdClass(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->will($this->returnValue($sentinel)); - - $mock2 = $this->createCallableMock(); - $mock2 - ->expects($this->once()) - ->method('__invoke') - ->with($sentinel); - - $adapter->promise() - ->then( - $this->expectCallableNever(), - $this->expectCallableNever(), - $mock - ) - ->then( - $this->expectCallableNever(), - $this->expectCallableNever(), - $mock2 - ); - - $adapter->resolve($adapter2->promise()); - $adapter2->notify($sentinel); - } - - /** @test */ - public function notifyShouldAllowResolveAfterProgress() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->at(0)) - ->method('__invoke') - ->with($this->identicalTo(1)); - $mock - ->expects($this->at(1)) - ->method('__invoke') - ->with($this->identicalTo(2)); - - $adapter->promise() - ->then( - $mock, - $this->expectCallableNever(), - $mock - ); - - $adapter->notify(1); - $adapter->resolve(2); - } - - /** @test */ - public function notifyShouldAllowRejectAfterProgress() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->at(0)) - ->method('__invoke') - ->with($this->identicalTo(1)); - $mock - ->expects($this->at(1)) - ->method('__invoke') - ->with($this->identicalTo(2)); - - $adapter->promise() - ->then( - $this->expectCallableNever(), - $mock, - $mock - ); - - $adapter->notify(1); - $adapter->reject(2); - } - - /** @test */ - public function notifyShouldReturnSilentlyOnProgressWhenAlreadyRejected() - { - $adapter = $this->getPromiseTestAdapter(); - - $adapter->reject(1); - - $this->assertNull($adapter->notify()); - } - - /** @test */ - public function notifyShouldInvokeProgressHandler() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $adapter->promise()->progress($mock); - $adapter->notify(1); - } - - /** @test */ - public function notifyShouldInvokeProgressHandlerFromDone() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $this->assertNull($adapter->promise()->done(null, null, $mock)); - $adapter->notify(1); - } - - /** @test */ - public function notifyShouldThrowExceptionThrownProgressHandlerFromDone() - { - $adapter = $this->getPromiseTestAdapter(); - - $this->setExpectedException('\Exception', 'UnhandledRejectionException'); - - $this->assertNull($adapter->promise()->done(null, null, function () { - throw new \Exception('UnhandledRejectionException'); - })); - $adapter->notify(1); - } -} diff --git a/core/vendor/react/promise/tests/PromiseTest/PromiseFulfilledTestTrait.php b/core/vendor/react/promise/tests/PromiseTest/PromiseFulfilledTestTrait.php deleted file mode 100644 index 428230b97ac1c7e9baac52a9d683545cb9ec58e3..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/PromiseTest/PromiseFulfilledTestTrait.php +++ /dev/null @@ -1,351 +0,0 @@ -<?php - -namespace React\Promise\PromiseTest; - -trait PromiseFulfilledTestTrait -{ - /** - * @return \React\Promise\PromiseAdapter\PromiseAdapterInterface - */ - abstract public function getPromiseTestAdapter(callable $canceller = null); - - /** @test */ - public function fulfilledPromiseShouldBeImmutable() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $adapter->resolve(1); - $adapter->resolve(2); - - $adapter->promise() - ->then( - $mock, - $this->expectCallableNever() - ); - } - - /** @test */ - public function fulfilledPromiseShouldInvokeNewlyAddedCallback() - { - $adapter = $this->getPromiseTestAdapter(); - - $adapter->resolve(1); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $adapter->promise() - ->then($mock, $this->expectCallableNever()); - } - - /** @test */ - public function thenShouldForwardResultWhenCallbackIsNull() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $adapter->resolve(1); - $adapter->promise() - ->then( - null, - $this->expectCallableNever() - ) - ->then( - $mock, - $this->expectCallableNever() - ); - } - - /** @test */ - public function thenShouldForwardCallbackResultToNextCallback() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(2)); - - $adapter->resolve(1); - $adapter->promise() - ->then( - function ($val) { - return $val + 1; - }, - $this->expectCallableNever() - ) - ->then( - $mock, - $this->expectCallableNever() - ); - } - - /** @test */ - public function thenShouldForwardPromisedCallbackResultValueToNextCallback() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(2)); - - $adapter->resolve(1); - $adapter->promise() - ->then( - function ($val) { - return \React\Promise\resolve($val + 1); - }, - $this->expectCallableNever() - ) - ->then( - $mock, - $this->expectCallableNever() - ); - } - - /** @test */ - public function thenShouldSwitchFromCallbacksToErrbacksWhenCallbackReturnsARejection() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(2)); - - $adapter->resolve(1); - $adapter->promise() - ->then( - function ($val) { - return \React\Promise\reject($val + 1); - }, - $this->expectCallableNever() - ) - ->then( - $this->expectCallableNever(), - $mock - ); - } - - /** @test */ - public function thenShouldSwitchFromCallbacksToErrbacksWhenCallbackThrows() - { - $adapter = $this->getPromiseTestAdapter(); - - $exception = new \Exception(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->will($this->throwException($exception)); - - $mock2 = $this->createCallableMock(); - $mock2 - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($exception)); - - $adapter->resolve(1); - $adapter->promise() - ->then( - $mock, - $this->expectCallableNever() - ) - ->then( - $this->expectCallableNever(), - $mock2 - ); - } - - /** @test */ - public function cancelShouldReturnNullForFulfilledPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $adapter->resolve(); - - $this->assertNull($adapter->promise()->cancel()); - } - - /** @test */ - public function cancelShouldHaveNoEffectForFulfilledPromise() - { - $adapter = $this->getPromiseTestAdapter($this->expectCallableNever()); - - $adapter->resolve(); - - $adapter->promise()->cancel(); - } - - /** @test */ - public function doneShouldInvokeFulfillmentHandlerForFulfilledPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $adapter->resolve(1); - $this->assertNull($adapter->promise()->done($mock)); - } - - /** @test */ - public function doneShouldThrowExceptionThrownFulfillmentHandlerForFulfilledPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $this->setExpectedException('\Exception', 'UnhandledRejectionException'); - - $adapter->resolve(1); - $this->assertNull($adapter->promise()->done(function () { - throw new \Exception('UnhandledRejectionException'); - })); - } - - /** @test */ - public function doneShouldThrowUnhandledRejectionExceptionWhenFulfillmentHandlerRejectsForFulfilledPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $this->setExpectedException('React\\Promise\\UnhandledRejectionException'); - - $adapter->resolve(1); - $this->assertNull($adapter->promise()->done(function () { - return \React\Promise\reject(); - })); - } - - /** @test */ - public function otherwiseShouldNotInvokeRejectionHandlerForFulfilledPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $adapter->resolve(1); - $adapter->promise()->otherwise($this->expectCallableNever()); - } - - /** @test */ - public function alwaysShouldNotSuppressValueForFulfilledPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $value = new \stdClass(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($value)); - - $adapter->resolve($value); - $adapter->promise() - ->always(function () {}) - ->then($mock); - } - - /** @test */ - public function alwaysShouldNotSuppressValueWhenHandlerReturnsANonPromiseForFulfilledPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $value = new \stdClass(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($value)); - - $adapter->resolve($value); - $adapter->promise() - ->always(function () { - return 1; - }) - ->then($mock); - } - - /** @test */ - public function alwaysShouldNotSuppressValueWhenHandlerReturnsAPromiseForFulfilledPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $value = new \stdClass(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($value)); - - $adapter->resolve($value); - $adapter->promise() - ->always(function () { - return \React\Promise\resolve(1); - }) - ->then($mock); - } - - /** @test */ - public function alwaysShouldRejectWhenHandlerThrowsForFulfilledPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $exception = new \Exception(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($exception)); - - $adapter->resolve(1); - $adapter->promise() - ->always(function () use ($exception) { - throw $exception; - }) - ->then(null, $mock); - } - - /** @test */ - public function alwaysShouldRejectWhenHandlerRejectsForFulfilledPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $exception = new \Exception(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($exception)); - - $adapter->resolve(1); - $adapter->promise() - ->always(function () use ($exception) { - return \React\Promise\reject($exception); - }) - ->then(null, $mock); - } -} diff --git a/core/vendor/react/promise/tests/PromiseTest/PromisePendingTestTrait.php b/core/vendor/react/promise/tests/PromiseTest/PromisePendingTestTrait.php deleted file mode 100644 index a4f48ee2530456239bb1e9ba64c6712a95ac8b07..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/PromiseTest/PromisePendingTestTrait.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php - -namespace React\Promise\PromiseTest; - -trait PromisePendingTestTrait -{ - /** - * @return \React\Promise\PromiseAdapter\PromiseAdapterInterface - */ - abstract public function getPromiseTestAdapter(callable $canceller = null); - - /** @test */ - public function thenShouldReturnAPromiseForPendingPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $this->assertInstanceOf('React\\Promise\\PromiseInterface', $adapter->promise()->then()); - } - - /** @test */ - public function thenShouldReturnAllowNullForPendingPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $this->assertInstanceOf('React\\Promise\\PromiseInterface', $adapter->promise()->then(null, null, null)); - } - - /** @test */ - public function cancelShouldReturnNullForPendingPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $this->assertNull($adapter->promise()->cancel()); - } - - /** @test */ - public function doneShouldReturnNullForPendingPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $this->assertNull($adapter->promise()->done()); - } - - /** @test */ - public function doneShouldReturnAllowNullForPendingPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $this->assertNull($adapter->promise()->done(null, null, null)); - } - - /** @test */ - public function otherwiseShouldNotInvokeRejectionHandlerForPendingPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $adapter->settle(); - $adapter->promise()->otherwise($this->expectCallableNever()); - } - - /** @test */ - public function alwaysShouldReturnAPromiseForPendingPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $this->assertInstanceOf('React\\Promise\\PromiseInterface', $adapter->promise()->always(function () {})); - } -} diff --git a/core/vendor/react/promise/tests/PromiseTest/PromiseRejectedTestTrait.php b/core/vendor/react/promise/tests/PromiseTest/PromiseRejectedTestTrait.php deleted file mode 100644 index 64255e8311abc17e0922634d4f4cd0f058cb4e91..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/PromiseTest/PromiseRejectedTestTrait.php +++ /dev/null @@ -1,492 +0,0 @@ -<?php - -namespace React\Promise\PromiseTest; - -use React\Promise\Deferred; - -trait PromiseRejectedTestTrait -{ - /** - * @return \React\Promise\PromiseAdapter\PromiseAdapterInterface - */ - abstract public function getPromiseTestAdapter(callable $canceller = null); - - /** @test */ - public function rejectedPromiseShouldBeImmutable() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $adapter->reject(1); - $adapter->reject(2); - - $adapter->promise() - ->then( - $this->expectCallableNever(), - $mock - ); - } - - /** @test */ - public function rejectedPromiseShouldInvokeNewlyAddedCallback() - { - $adapter = $this->getPromiseTestAdapter(); - - $adapter->reject(1); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $adapter->promise() - ->then($this->expectCallableNever(), $mock); - } - - /** @test */ - public function shouldForwardUndefinedRejectionValue() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with(null); - - $adapter->reject(1); - $adapter->promise() - ->then( - $this->expectCallableNever(), - function () { - // Presence of rejection handler is enough to switch back - // to resolve mode, even though it returns undefined. - // The ONLY way to propagate a rejection is to re-throw or - // return a rejected promise; - } - ) - ->then( - $mock, - $this->expectCallableNever() - ); - } - - /** @test */ - public function shouldSwitchFromErrbacksToCallbacksWhenErrbackDoesNotExplicitlyPropagate() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(2)); - - $adapter->reject(1); - $adapter->promise() - ->then( - $this->expectCallableNever(), - function ($val) { - return $val + 1; - } - ) - ->then( - $mock, - $this->expectCallableNever() - ); - } - - /** @test */ - public function shouldSwitchFromErrbacksToCallbacksWhenErrbackReturnsAResolution() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(2)); - - $adapter->reject(1); - $adapter->promise() - ->then( - $this->expectCallableNever(), - function ($val) { - return \React\Promise\resolve($val + 1); - } - ) - ->then( - $mock, - $this->expectCallableNever() - ); - } - - /** @test */ - public function shouldPropagateRejectionsWhenErrbackThrows() - { - $adapter = $this->getPromiseTestAdapter(); - - $exception = new \Exception(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->will($this->throwException($exception)); - - $mock2 = $this->createCallableMock(); - $mock2 - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($exception)); - - $adapter->reject(1); - $adapter->promise() - ->then( - $this->expectCallableNever(), - $mock - ) - ->then( - $this->expectCallableNever(), - $mock2 - ); - } - - /** @test */ - public function shouldPropagateRejectionsWhenErrbackReturnsARejection() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(2)); - - $adapter->reject(1); - $adapter->promise() - ->then( - $this->expectCallableNever(), - function ($val) { - return \React\Promise\reject($val + 1); - } - ) - ->then( - $this->expectCallableNever(), - $mock - ); - } - - /** @test */ - public function doneShouldInvokeRejectionHandlerForRejectedPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $adapter->reject(1); - $this->assertNull($adapter->promise()->done(null, $mock)); - } - - /** @test */ - public function doneShouldThrowExceptionThrownByRejectionHandlerForRejectedPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $this->setExpectedException('\Exception', 'UnhandledRejectionException'); - - $adapter->reject(1); - $this->assertNull($adapter->promise()->done(null, function () { - throw new \Exception('UnhandledRejectionException'); - })); - } - - /** @test */ - public function doneShouldThrowUnhandledRejectionExceptionWhenRejectedWithNonExceptionForRejectedPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $this->setExpectedException('React\\Promise\\UnhandledRejectionException'); - - $adapter->reject(1); - $this->assertNull($adapter->promise()->done()); - } - - /** @test */ - public function doneShouldThrowUnhandledRejectionExceptionWhenRejectionHandlerRejectsForRejectedPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $this->setExpectedException('React\\Promise\\UnhandledRejectionException'); - - $adapter->reject(1); - $this->assertNull($adapter->promise()->done(null, function () { - return \React\Promise\reject(); - })); - } - - /** @test */ - public function doneShouldThrowRejectionExceptionWhenRejectionHandlerRejectsWithExceptionForRejectedPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $this->setExpectedException('\Exception', 'UnhandledRejectionException'); - - $adapter->reject(1); - $this->assertNull($adapter->promise()->done(null, function () { - return \React\Promise\reject(new \Exception('UnhandledRejectionException')); - })); - } - - /** @test */ - public function doneShouldThrowExceptionProvidedAsRejectionValueForRejectedPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $this->setExpectedException('\Exception', 'UnhandledRejectionException'); - - $adapter->reject(new \Exception('UnhandledRejectionException')); - $this->assertNull($adapter->promise()->done()); - } - - /** @test */ - public function doneShouldThrowWithDeepNestingPromiseChainsForRejectedPromise() - { - $this->setExpectedException('\Exception', 'UnhandledRejectionException'); - - $exception = new \Exception('UnhandledRejectionException'); - - $d = new Deferred(); - $d->resolve(); - - $result = \React\Promise\resolve(\React\Promise\resolve($d->promise()->then(function () use ($exception) { - $d = new Deferred(); - $d->resolve(); - - return \React\Promise\resolve($d->promise()->then(function () {}))->then( - function () use ($exception) { - throw $exception; - } - ); - }))); - - $result->done(); - } - - /** @test */ - public function doneShouldRecoverWhenRejectionHandlerCatchesExceptionForRejectedPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $adapter->reject(new \Exception('UnhandledRejectionException')); - $this->assertNull($adapter->promise()->done(null, function (\Exception $e) { - - })); - } - - /** @test */ - public function otherwiseShouldInvokeRejectionHandlerForRejectedPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $adapter->reject(1); - $adapter->promise()->otherwise($mock); - } - - /** @test */ - public function otherwiseShouldInvokeNonTypeHintedRejectionHandlerIfReasonIsAnExceptionForRejectedPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $exception = new \Exception(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($exception)); - - $adapter->reject($exception); - $adapter->promise() - ->otherwise(function ($reason) use ($mock) { - $mock($reason); - }); - } - - /** @test */ - public function otherwiseShouldInvokeRejectionHandlerIfReasonMatchesTypehintForRejectedPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $exception = new \InvalidArgumentException(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($exception)); - - $adapter->reject($exception); - $adapter->promise() - ->otherwise(function (\InvalidArgumentException $reason) use ($mock) { - $mock($reason); - }); - } - - /** @test */ - public function otherwiseShouldNotInvokeRejectionHandlerIfReaonsDoesNotMatchTypehintForRejectedPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $exception = new \Exception(); - - $mock = $this->expectCallableNever(); - - $adapter->reject($exception); - $adapter->promise() - ->otherwise(function (\InvalidArgumentException $reason) use ($mock) { - $mock($reason); - }); - } - - /** @test */ - public function alwaysShouldNotSuppressRejectionForRejectedPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $exception = new \Exception(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($exception)); - - $adapter->reject($exception); - $adapter->promise() - ->always(function () {}) - ->then(null, $mock); - } - - /** @test */ - public function alwaysShouldNotSuppressRejectionWhenHandlerReturnsANonPromiseForRejectedPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $exception = new \Exception(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($exception)); - - $adapter->reject($exception); - $adapter->promise() - ->always(function () { - return 1; - }) - ->then(null, $mock); - } - - /** @test */ - public function alwaysShouldNotSuppressRejectionWhenHandlerReturnsAPromiseForRejectedPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $exception = new \Exception(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($exception)); - - $adapter->reject($exception); - $adapter->promise() - ->always(function () { - return \React\Promise\resolve(1); - }) - ->then(null, $mock); - } - - /** @test */ - public function alwaysShouldRejectWhenHandlerThrowsForRejectedPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $exception1 = new \Exception(); - $exception2 = new \Exception(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($exception2)); - - $adapter->reject($exception1); - $adapter->promise() - ->always(function () use ($exception2) { - throw $exception2; - }) - ->then(null, $mock); - } - - /** @test */ - public function alwaysShouldRejectWhenHandlerRejectsForRejectedPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $exception1 = new \Exception(); - $exception2 = new \Exception(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($exception2)); - - $adapter->reject($exception1); - $adapter->promise() - ->always(function () use ($exception2) { - return \React\Promise\reject($exception2); - }) - ->then(null, $mock); - } - - /** @test */ - public function cancelShouldReturnNullForRejectedPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $adapter->reject(); - - $this->assertNull($adapter->promise()->cancel()); - } - - /** @test */ - public function cancelShouldHaveNoEffectForRejectedPromise() - { - $adapter = $this->getPromiseTestAdapter($this->expectCallableNever()); - - $adapter->reject(); - - $adapter->promise()->cancel(); - } -} diff --git a/core/vendor/react/promise/tests/PromiseTest/PromiseSettledTestTrait.php b/core/vendor/react/promise/tests/PromiseTest/PromiseSettledTestTrait.php deleted file mode 100644 index e363b6d91c410baf93ed0c0827654a5c8f08944f..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/PromiseTest/PromiseSettledTestTrait.php +++ /dev/null @@ -1,86 +0,0 @@ -<?php - -namespace React\Promise\PromiseTest; - -trait PromiseSettledTestTrait -{ - /** - * @return \React\Promise\PromiseAdapter\PromiseAdapterInterface - */ - abstract public function getPromiseTestAdapter(callable $canceller = null); - - /** @test */ - public function thenShouldReturnAPromiseForSettledPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $adapter->settle(); - $this->assertInstanceOf('React\\Promise\\PromiseInterface', $adapter->promise()->then()); - } - - /** @test */ - public function thenShouldReturnAllowNullForSettledPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $adapter->settle(); - $this->assertInstanceOf('React\\Promise\\PromiseInterface', $adapter->promise()->then(null, null, null)); - } - - /** @test */ - public function cancelShouldReturnNullForSettledPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $adapter->settle(); - - $this->assertNull($adapter->promise()->cancel()); - } - - /** @test */ - public function cancelShouldHaveNoEffectForSettledPromise() - { - $adapter = $this->getPromiseTestAdapter($this->expectCallableNever()); - - $adapter->settle(); - - $adapter->promise()->cancel(); - } - - /** @test */ - public function doneShouldReturnNullForSettledPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $adapter->settle(); - $this->assertNull($adapter->promise()->done(null, function () {})); - } - - /** @test */ - public function doneShouldReturnAllowNullForSettledPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $adapter->settle(); - $this->assertNull($adapter->promise()->done(null, function () {}, null)); - } - - /** @test */ - public function progressShouldNotInvokeProgressHandlerForSettledPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $adapter->settle(); - $adapter->promise()->progress($this->expectCallableNever()); - $adapter->notify(); - } - - /** @test */ - public function alwaysShouldReturnAPromiseForSettledPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $adapter->settle(); - $this->assertInstanceOf('React\\Promise\\PromiseInterface', $adapter->promise()->always(function () {})); - } -} diff --git a/core/vendor/react/promise/tests/PromiseTest/RejectTestTrait.php b/core/vendor/react/promise/tests/PromiseTest/RejectTestTrait.php deleted file mode 100644 index 7d6f65fd0ae33eb3b0108ce3b33cfa956286f3ab..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/PromiseTest/RejectTestTrait.php +++ /dev/null @@ -1,363 +0,0 @@ -<?php - -namespace React\Promise\PromiseTest; - -use React\Promise; -use React\Promise\Deferred; - -trait RejectTestTrait -{ - /** - * @return \React\Promise\PromiseAdapter\PromiseAdapterInterface - */ - abstract public function getPromiseTestAdapter(callable $canceller = null); - - /** @test */ - public function rejectShouldRejectWithAnImmediateValue() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $adapter->promise() - ->then($this->expectCallableNever(), $mock); - - $adapter->reject(1); - } - - /** @test */ - public function rejectShouldRejectWithFulfilledPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $adapter->promise() - ->then($this->expectCallableNever(), $mock); - - $adapter->reject(Promise\resolve(1)); - } - - /** @test */ - public function rejectShouldRejectWithRejectedPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $adapter->promise() - ->then($this->expectCallableNever(), $mock); - - $adapter->reject(Promise\reject(1)); - } - - /** @test */ - public function rejectShouldForwardReasonWhenCallbackIsNull() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $adapter->promise() - ->then( - $this->expectCallableNever() - ) - ->then( - $this->expectCallableNever(), - $mock - ); - - $adapter->reject(1); - } - - /** @test */ - public function rejectShouldMakePromiseImmutable() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $adapter->promise() - ->then( - $this->expectCallableNever(), - $mock - ); - - $adapter->reject(1); - $adapter->reject(2); - } - - /** @test */ - public function notifyShouldInvokeOtherwiseHandler() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $adapter->promise() - ->otherwise($mock); - - $adapter->reject(1); - } - - /** @test */ - public function doneShouldInvokeRejectionHandler() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $this->assertNull($adapter->promise()->done(null, $mock)); - $adapter->reject(1); - } - - /** @test */ - public function doneShouldThrowExceptionThrownByRejectionHandler() - { - $adapter = $this->getPromiseTestAdapter(); - - $this->setExpectedException('\Exception', 'UnhandledRejectionException'); - - $this->assertNull($adapter->promise()->done(null, function () { - throw new \Exception('UnhandledRejectionException'); - })); - $adapter->reject(1); - } - - /** @test */ - public function doneShouldThrowUnhandledRejectionExceptionWhenRejectedWithNonException() - { - $adapter = $this->getPromiseTestAdapter(); - - $this->setExpectedException('React\\Promise\\UnhandledRejectionException'); - - $this->assertNull($adapter->promise()->done()); - $adapter->reject(1); - } - - /** @test */ - public function doneShouldThrowUnhandledRejectionExceptionWhenRejectionHandlerRejects() - { - $adapter = $this->getPromiseTestAdapter(); - - $this->setExpectedException('React\\Promise\\UnhandledRejectionException'); - - $this->assertNull($adapter->promise()->done(null, function () { - return \React\Promise\reject(); - })); - $adapter->reject(1); - } - - /** @test */ - public function doneShouldThrowRejectionExceptionWhenRejectionHandlerRejectsWithException() - { - $adapter = $this->getPromiseTestAdapter(); - - $this->setExpectedException('\Exception', 'UnhandledRejectionException'); - - $this->assertNull($adapter->promise()->done(null, function () { - return \React\Promise\reject(new \Exception('UnhandledRejectionException')); - })); - $adapter->reject(1); - } - - /** @test */ - public function doneShouldThrowUnhandledRejectionExceptionWhenRejectionHandlerRetunsPendingPromiseWhichRejectsLater() - { - $adapter = $this->getPromiseTestAdapter(); - - $this->setExpectedException('React\\Promise\\UnhandledRejectionException'); - - $d = new Deferred(); - $promise = $d->promise(); - - $this->assertNull($adapter->promise()->done(null, function () use ($promise) { - return $promise; - })); - $adapter->reject(1); - $d->reject(1); - } - - /** @test */ - public function doneShouldThrowExceptionProvidedAsRejectionValue() - { - $adapter = $this->getPromiseTestAdapter(); - - $this->setExpectedException('\Exception', 'UnhandledRejectionException'); - - $this->assertNull($adapter->promise()->done()); - $adapter->reject(new \Exception('UnhandledRejectionException')); - } - - /** @test */ - public function doneShouldThrowWithDeepNestingPromiseChains() - { - $this->setExpectedException('\Exception', 'UnhandledRejectionException'); - - $exception = new \Exception('UnhandledRejectionException'); - - $d = new Deferred(); - - $result = \React\Promise\resolve(\React\Promise\resolve($d->promise()->then(function () use ($exception) { - $d = new Deferred(); - $d->resolve(); - - return \React\Promise\resolve($d->promise()->then(function () {}))->then( - function () use ($exception) { - throw $exception; - } - ); - }))); - - $result->done(); - - $d->resolve(); - } - - /** @test */ - public function doneShouldRecoverWhenRejectionHandlerCatchesException() - { - $adapter = $this->getPromiseTestAdapter(); - - $this->assertNull($adapter->promise()->done(null, function (\Exception $e) { - - })); - $adapter->reject(new \Exception('UnhandledRejectionException')); - } - - /** @test */ - public function alwaysShouldNotSuppressRejection() - { - $adapter = $this->getPromiseTestAdapter(); - - $exception = new \Exception(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($exception)); - - $adapter->promise() - ->always(function () {}) - ->then(null, $mock); - - $adapter->reject($exception); - } - - /** @test */ - public function alwaysShouldNotSuppressRejectionWhenHandlerReturnsANonPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $exception = new \Exception(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($exception)); - - $adapter->promise() - ->always(function () { - return 1; - }) - ->then(null, $mock); - - $adapter->reject($exception); - } - - /** @test */ - public function alwaysShouldNotSuppressRejectionWhenHandlerReturnsAPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $exception = new \Exception(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($exception)); - - $adapter->promise() - ->always(function () { - return \React\Promise\resolve(1); - }) - ->then(null, $mock); - - $adapter->reject($exception); - } - - /** @test */ - public function alwaysShouldRejectWhenHandlerThrowsForRejection() - { - $adapter = $this->getPromiseTestAdapter(); - - $exception = new \Exception(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($exception)); - - $adapter->promise() - ->always(function () use ($exception) { - throw $exception; - }) - ->then(null, $mock); - - $adapter->reject($exception); - } - - /** @test */ - public function alwaysShouldRejectWhenHandlerRejectsForRejection() - { - $adapter = $this->getPromiseTestAdapter(); - - $exception = new \Exception(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($exception)); - - $adapter->promise() - ->always(function () use ($exception) { - return \React\Promise\reject($exception); - }) - ->then(null, $mock); - - $adapter->reject($exception); - } -} diff --git a/core/vendor/react/promise/tests/PromiseTest/ResolveTestTrait.php b/core/vendor/react/promise/tests/PromiseTest/ResolveTestTrait.php deleted file mode 100644 index b05f7fe84bbf588fa1d1ea2b504fbde817c9c826..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/PromiseTest/ResolveTestTrait.php +++ /dev/null @@ -1,258 +0,0 @@ -<?php - -namespace React\Promise\PromiseTest; - -use React\Promise; - -trait ResolveTestTrait -{ - /** - * @return \React\Promise\PromiseAdapter\PromiseAdapterInterface - */ - abstract public function getPromiseTestAdapter(callable $canceller = null); - - /** @test */ - public function resolveShouldResolve() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $adapter->promise() - ->then($mock); - - $adapter->resolve(1); - } - - /** @test */ - public function resolveShouldResolveWithPromisedValue() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $adapter->promise() - ->then($mock); - - $adapter->resolve(Promise\resolve(1)); - } - - /** @test */ - public function resolveShouldRejectWhenResolvedWithRejectedPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $adapter->promise() - ->then($this->expectCallableNever(), $mock); - - $adapter->resolve(Promise\reject(1)); - } - - /** @test */ - public function resolveShouldForwardValueWhenCallbackIsNull() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $adapter->promise() - ->then( - null, - $this->expectCallableNever() - ) - ->then( - $mock, - $this->expectCallableNever() - ); - - $adapter->resolve(1); - } - - /** @test */ - public function resolveShouldMakePromiseImmutable() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $adapter->promise() - ->then( - $mock, - $this->expectCallableNever() - ); - - $adapter->resolve(1); - $adapter->resolve(2); - } - - /** @test */ - public function doneShouldInvokeFulfillmentHandler() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $this->assertNull($adapter->promise()->done($mock)); - $adapter->resolve(1); - } - - /** @test */ - public function doneShouldThrowExceptionThrownFulfillmentHandler() - { - $adapter = $this->getPromiseTestAdapter(); - - $this->setExpectedException('\Exception', 'UnhandledRejectionException'); - - $this->assertNull($adapter->promise()->done(function () { - throw new \Exception('UnhandledRejectionException'); - })); - $adapter->resolve(1); - } - - /** @test */ - public function doneShouldThrowUnhandledRejectionExceptionWhenFulfillmentHandlerRejects() - { - $adapter = $this->getPromiseTestAdapter(); - - $this->setExpectedException('React\\Promise\\UnhandledRejectionException'); - - $this->assertNull($adapter->promise()->done(function () { - return \React\Promise\reject(); - })); - $adapter->resolve(1); - } - - /** @test */ - public function alwaysShouldNotSuppressValue() - { - $adapter = $this->getPromiseTestAdapter(); - - $value = new \stdClass(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($value)); - - $adapter->promise() - ->always(function () {}) - ->then($mock); - - $adapter->resolve($value); - } - - /** @test */ - public function alwaysShouldNotSuppressValueWhenHandlerReturnsANonPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $value = new \stdClass(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($value)); - - $adapter->promise() - ->always(function () { - return 1; - }) - ->then($mock); - - $adapter->resolve($value); - } - - /** @test */ - public function alwaysShouldNotSuppressValueWhenHandlerReturnsAPromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $value = new \stdClass(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($value)); - - $adapter->promise() - ->always(function () { - return \React\Promise\resolve(1); - }) - ->then($mock); - - $adapter->resolve($value); - } - - /** @test */ - public function alwaysShouldRejectWhenHandlerThrowsForFulfillment() - { - $adapter = $this->getPromiseTestAdapter(); - - $exception = new \Exception(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($exception)); - - $adapter->promise() - ->always(function () use ($exception) { - throw $exception; - }) - ->then(null, $mock); - - $adapter->resolve(1); - } - - /** @test */ - public function alwaysShouldRejectWhenHandlerRejectsForFulfillment() - { - $adapter = $this->getPromiseTestAdapter(); - - $exception = new \Exception(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($exception)); - - $adapter->promise() - ->always(function () use ($exception) { - return \React\Promise\reject($exception); - }) - ->then(null, $mock); - - $adapter->resolve(1); - } -} diff --git a/core/vendor/react/promise/tests/RejectedPromiseTest.php b/core/vendor/react/promise/tests/RejectedPromiseTest.php deleted file mode 100644 index c886b00954204aa28aeb8ea891c8f1f457204864..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/RejectedPromiseTest.php +++ /dev/null @@ -1,50 +0,0 @@ -<?php - -namespace React\Promise; - -use React\Promise\PromiseAdapter\CallbackPromiseAdapter; - -class RejectedPromiseTest extends TestCase -{ - use PromiseTest\PromiseSettledTestTrait, - PromiseTest\PromiseRejectedTestTrait; - - public function getPromiseTestAdapter(callable $canceller = null) - { - $promise = null; - - return new CallbackPromiseAdapter([ - 'promise' => function () use (&$promise) { - if (!$promise) { - throw new \LogicException('RejectedPromise must be rejected before obtaining the promise'); - } - - return $promise; - }, - 'resolve' => function () { - throw new \LogicException('You cannot call resolve() for React\Promise\RejectedPromise'); - }, - 'reject' => function ($reason = null) use (&$promise) { - if (!$promise) { - $promise = new RejectedPromise($reason); - } - }, - 'notify' => function () { - // no-op - }, - 'settle' => function ($reason = null) use (&$promise) { - if (!$promise) { - $promise = new RejectedPromise($reason); - } - }, - ]); - } - - /** @test */ - public function shouldThrowExceptionIfConstructedWithAPromise() - { - $this->setExpectedException('\InvalidArgumentException'); - - return new RejectedPromise(new RejectedPromise()); - } -} diff --git a/core/vendor/react/promise/tests/Stub/CallableStub.php b/core/vendor/react/promise/tests/Stub/CallableStub.php deleted file mode 100644 index 0120893358c986052decc6655ef82523fd0c6f31..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/Stub/CallableStub.php +++ /dev/null @@ -1,10 +0,0 @@ -<?php - -namespace React\Promise\Stub; - -class CallableStub -{ - public function __invoke() - { - } -} diff --git a/core/vendor/react/promise/tests/TestCase.php b/core/vendor/react/promise/tests/TestCase.php deleted file mode 100644 index 60ea46d7dc1c00e6633ae2d0d0fb936763cb5f01..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/TestCase.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php - -namespace React\Promise; - -class TestCase extends \PHPUnit_Framework_TestCase -{ - public function expectCallableExactly($amount) - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->exactly($amount)) - ->method('__invoke'); - - return $mock; - } - - public function expectCallableOnce() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke'); - - return $mock; - } - - public function expectCallableNever() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->never()) - ->method('__invoke'); - - return $mock; - } - - public function createCallableMock() - { - return $this->getMock('React\\Promise\Stub\CallableStub'); - } -} diff --git a/core/vendor/react/promise/tests/bootstrap.php b/core/vendor/react/promise/tests/bootstrap.php deleted file mode 100644 index 9b7f872a5fc6732ef685bed412ed1ec6847ee3e4..0000000000000000000000000000000000000000 --- a/core/vendor/react/promise/tests/bootstrap.php +++ /dev/null @@ -1,7 +0,0 @@ -<?php - -$loader = @include __DIR__.'/../vendor/autoload.php'; -if (!$loader) { - $loader = require __DIR__.'/../../../../vendor/autoload.php'; -} -$loader->addPsr4('React\\Promise\\', __DIR__);