PHP8
authorMichael Wallner <mike@php.net>
Tue, 14 Sep 2021 08:09:37 +0000 (10:09 +0200)
committerMichael Wallner <mike@php.net>
Tue, 14 Sep 2021 08:09:37 +0000 (10:09 +0200)
43 files changed:
.editorconfig
.gitignore
README.md
composer.json
examples/amploop.php
examples/cache.php
examples/gistlog.php
examples/watchowned.php [new file with mode: 0755]
http.stub.php [new file with mode: 0644]
lib/API.php
lib/API/Call.php
lib/API/Call/Cache.php
lib/API/Call/Cache/Control.php
lib/API/Call/Cache/Service.php
lib/API/Call/Cache/Service/Hollow.php
lib/API/Call/Cache/Service/ItemPool.php
lib/API/Call/Cache/Service/Simple.php
lib/API/Call/Deferred.php
lib/API/Call/Result.php
lib/API/Consumer.php
lib/API/ContentType.php
lib/API/ContentType/Handler.php [new file with mode: 0644]
lib/API/ContentType/Handler/Base64.php [new file with mode: 0644]
lib/API/ContentType/Handler/Json.php [new file with mode: 0644]
lib/API/ContentType/Handler/Stream.php [new file with mode: 0644]
lib/API/ContentType/Handler/Text.php [new file with mode: 0644]
lib/API/Future.php
lib/API/Future/functions.php
lib/API/Iterator.php
lib/API/Links.php
lib/API/Links/functions.php
lib/Exception.php
lib/Exception/InvalidArgumentException.php
lib/Exception/RequestException.php
lib/Exception/UnexpectedValueException.php
lib/Exception/functions.php
lib/functions.php
phpunit.xml.dist [new file with mode: 0644]
tests/CacheTest.php
tests/CallTest.php
tests/ContentTypeTest.php
tests/ErrorsTest.php
tests/bootstrap.php

index 9b444aed05bf1b5de1a0aa8f5ea6756669f6f48d..c3ea8c4482c01690bbd4201923777248d0a33103 100644 (file)
@@ -15,9 +15,7 @@ trim_trailing_whitespace = false
 indent_style = space
 indent_size = 4
 
-[package.xml]
+[*.xml]
 indent_style = space
 indent_size = 1
 
-[config.w32]
-end_of_line = crlf
index ad7b598f5d59052243286b4fa18d2a1805ae930c..ac182fd34868d72d061be3a8ecb65395212c325f 100644 (file)
@@ -11,3 +11,6 @@
 /perf*
 /tmp/
 /vendor/
+*.diff
+*.cache
+*.bak
index cdca98036a595b9799788e324deba9eca24f0605..ca82ab313e3378e675d4fd5ae777f8cc9cc8e6ad 100644 (file)
--- a/README.md
+++ b/README.md
@@ -4,6 +4,13 @@
 
 Fluent Github API access with PHP-7 and [ext-http](https://github.com/m6w6/ext-http).
 
+Support for the following promise providers built in:
+ * [ReactPHP](https://github.com/reactphp/promise)
+ * [AmPHP](https://github.com/amphp/amp)
+
+Supports plugging into your favourite event loop through
+[http\Client's custom event loop interface](https://mdref.m6w6.name/http/Client/Curl/User).
+
 Simple example:
 
 ```php
@@ -11,7 +18,7 @@ Simple example:
 
 use seekat\API;
 
-$api = new API;
+$api = new API(API\Future\react());
 
 $api->repos->m6w6->seekat->readme->as("html")->then(function($readme) {
        echo $readme;
@@ -41,7 +48,7 @@ $cli->configure([
 $log = new Monolog\Logger("seekat");
 $log->pushHandler(new Monolog\Handler\StreamHandler(STDERR, Monolog\Logger::WARNING));
 
-$api = new API([
+$api = new API(API\Future\react(), [
        "Authorization" => "token ".getenv("GITHUB_TOKEN")
 ], null, $cli, $log);
 
@@ -77,9 +84,6 @@ $api(function($api) {
 ```
 
 
-> ***Note:*** WIP
-
-
 ## Installing
 
 ### Composer
index ad06cbf463ea00bf92e492430a40617ad3233aec..0c7dab4a0c23668ba123fdc93a1204027004aec0 100644 (file)
     "minimum-stability": "dev",
     "prefer-stable": true,
     "require": {
-        "php": "^7.0",
-        "ext-http": "^3.0",
-        "seebz/uri-template": "dev-master",
-               "psr/log": "^1.0",
+        "php": ">=7.2",
+        "ext-http": ">=3.2",
+        "ext-json": "*",
+        "psr/log": "^1.0",
         "psr/cache": "^1.0",
-        "psr/simple-cache": "^1.0"
+        "psr/simple-cache": "^1.0",
+        "rize/uri-template": "^0.3.3"
     },
     "require-dev": {
+        "php": "^8",
         "amphp/amp": "^2",
         "react/promise": "^2",
-        "monolog/monolog": "^1",
-        "phpunit/phpunit": "^5"
+        "monolog/monolog": "*",
+        "phpunit/phpunit": "*",
+        "squizlabs/php_codesniffer": "*",
+        "phpcompatibility/php-compatibility": "*"
     }
 }
index 5ecdd0dfe232d9d6ac4261b9a3d525b3b676d77a..1abbaa11e42d264dacaf4478607f1f6d49ea2bb2 100755 (executable)
@@ -84,12 +84,14 @@ $cli->configure([
                        $this->driver->setState((string) $socket, $id);
                        break;
                case self::POLL_INOUT:
-                       $id[] = $this->driver->onReadable($socket, function($id, $socket) {
-                               $this->run($socket, self::POLL_IN);
-                       });
-                       $id[] = $this->driver->onWritable($socket, function($id, $socket) {
-                               $this->run($socket, self::POLL_OUT);
-                       });
+                       $id = [
+                               $this->driver->onReadable($socket, function($id, $socket) {
+                                       $this->run($socket, self::POLL_IN);
+                               }),
+                               $this->driver->onWritable($socket, function($id, $socket) {
+                                       $this->run($socket, self::POLL_OUT);
+                               })
+                       ];
                        $this->driver->setState((string) $socket, $id);
                        break;
                case self::POLL_REMOVE:
@@ -110,4 +112,4 @@ AmpLoop::run(function() use($api) {
        list($m6w6, $seekat) = yield [$api->users->m6w6(), $api->repos->m6w6->seekat()];
        printf("Hi, my name is %s!\n", $m6w6->login);
        printf("Have fun with %s; %s!\n", $seekat->name, $seekat->description);
-});
\ No newline at end of file
+});
index 797fe60fb6fa184f281e3568d276bf937b213dc0..c71af446c10ae63d2d23b98b6a359e6db2c0c5ef 100755 (executable)
@@ -32,6 +32,9 @@ $cache = new class($redis) implements \seekat\API\Call\Cache\Service {
        function store(string $key, \http\Client\Response $response): bool {
                return $this->redis->set($key, $response);
        }
+       function del(string $key) {
+               return $this->redis->delete($key);
+       }
 };
 
 $api = new seekat\API(seekat\API\Future\react(), [
index 3a1b8ec7922f5b351f303249bf20e6ced6611718..6cdbe23faa070576085c585c34b79b013f19fe51 100755 (executable)
@@ -5,7 +5,7 @@ require __DIR__."/../vendor/autoload.php";
 
 
 $log = new Monolog\Logger("seekat");
-$log->pushHandler(new Monolog\Handler\StreamHandler(STDERR, Monolog\Logger::DEBUG));
+$log->pushHandler(new Monolog\Handler\StreamHandler(STDERR, Monolog\Logger::WARNING));
 
 $api = new seekat\API(
        seekat\API\Future\react(),
diff --git a/examples/watchowned.php b/examples/watchowned.php
new file mode 100755 (executable)
index 0000000..d1ff879
--- /dev/null
@@ -0,0 +1,46 @@
+#!/usr/bin/env php
+<?php
+
+use http\Client;
+use Monolog\Handler\StreamHandler;
+use Monolog\Logger;
+use seekat\API;
+use seekat\API\Links;
+
+require_once __DIR__."/../vendor/autoload.php";
+
+$log = new Monolog\Logger("seekat");
+$log->pushHandler((new Monolog\Handler\StreamHandler(STDERR))
+       ->setLevel(Monolog\Logger::WARNING));
+
+$cli = new http\Client("curl", "seekat");
+
+$api = new API(API\Future\amp(), [
+       "Authorization" => "token ".getenv("GITHUB_TOKEN")
+], null, $cli, $log);
+
+$api(function($api) {
+       $count = 0;
+       $subscribed = yield $api->user->subscriptions(["per_page" => 3]);
+
+       while ($subscribed) {
+               /* pro-actively queue the next request */
+               $next = Links\next($subscribed);
+               foreach ($subscribed->getData() as $subscription) {
+                       if ($subscription->fork) {
+                               printf("skipping fork %s\n", $subscription->full_name);
+                               continue;
+                       }
+                       ++$count;
+                       printf("watching %s owned by %s (repo is %s)\n",
+                               $subscription->full_name,
+                               $subscription->owner->login,
+                               $subscription->private ? "private" : "public"
+                       );
+               }
+               $subscribed = yield $next;
+       }
+       return $count;
+})->onResolve(function($error, $count) {
+       printf("Listed %d repos\n", $count);
+});
diff --git a/http.stub.php b/http.stub.php
new file mode 100644 (file)
index 0000000..7a114c9
--- /dev/null
@@ -0,0 +1,3424 @@
+<?php
+/**
+ * Extended HTTP support. Again.
+ * 
+ * * Introduces the http namespace.
+ * * PHP stream based message bodies.
+ * * Encapsulated env request/response.
+ * * Modular client support.
+ */
+namespace http;
+use http;
+/**
+ * The HTTP client. See http\Client\Curl's [options](http/Client/Curl#Options:) which is the only driver currently supported.
+ */
+class Client implements \SplSubject, \Countable {
+       /**
+        * Debug callback's $data contains human readable text.
+        */
+       const DEBUG_INFO = 0;
+       /**
+        * Debug callback's $data contains data received.
+        */
+       const DEBUG_IN = 1;
+       /**
+        * Debug callback's $data contains data sent.
+        */
+       const DEBUG_OUT = 2;
+       /**
+        * Debug callback's $data contains headers.
+        */
+       const DEBUG_HEADER = 16;
+       /**
+        * Debug callback's $data contains a body part.
+        */
+       const DEBUG_BODY = 32;
+       /**
+        * Debug callback's $data contains SSL data.
+        */
+       const DEBUG_SSL = 64;
+       /**
+        * Attached observers.
+        * 
+        * @private
+        * @var \SplObjectStorage
+        */
+       private $observers = NULL;
+       /**
+        * Set options.
+        * 
+        * @protected
+        * @var array
+        */
+       protected $options = NULL;
+       /**
+        * Request/response history.
+        * 
+        * @protected
+        * @var \http\Message
+        */
+       protected $history = NULL;
+       /**
+        * Whether to record history in http\Client::$history.
+        * 
+        * @public
+        * @var bool
+        */
+       public $recordHistory = false;
+       /**
+        * Create a new HTTP client.
+        * 
+        * Currently only "curl" is supported as a $driver, and used by default.
+        * Persisted resources identified by $persistent_handle_id will be re-used if available.
+        * 
+        * @param string $driver The HTTP client driver to employ. Currently only the default driver, "curl", is supported.
+        * @param string $persistent_handle_id If supplied, created curl handles will be persisted with this identifier for later reuse.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\UnexpectedValueException
+        * @throws \http\Exception\RuntimeException
+        */
+       function __construct(string $driver = NULL, string $persistent_handle_id = NULL) {}
+       /**
+        * Add custom cookies.
+        * See http\Client::setCookies().
+        * 
+        * @param array $cookies Custom cookies to add.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Client self.
+        */
+       function addCookies(array $cookies = NULL) {}
+       /**
+        * Add specific SSL options.
+        * See http\Client::setSslOptions(), http\Client::setOptions() and http\Client\Curl\$ssl options.
+        * 
+        * @param array $ssl_options Add this SSL options.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Client self.
+        */
+       function addSslOptions(array $ssl_options = NULL) {}
+       /**
+        * Implements SplSubject. Attach another observer.
+        * Attached observers will be notified with progress of each transfer.
+        * 
+        * @param \SplObserver $observer An implementation of SplObserver.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\UnexpectedValueException
+        * @return \http\Client self.
+        */
+       function attach(\SplObserver $observer) {}
+       /**
+        * Configure the client's low level options.
+        * 
+        * > ***NOTE:***
+        * > This method has been added in v2.3.0.
+        * 
+        * @param array $configuration Key/value pairs of low level options.
+        *    See f.e. the [configuration options for the Curl driver](http/Client/Curl#Configuration:).
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\UnexpectedValueException
+        * @return \http\Client self.
+        */
+       function configure(array $configuration) {}
+       /**
+        * Implements Countable. Retrieve the number of enqueued requests.
+        * 
+        * > ***NOTE:***
+        * > The enqueued requests are counted without regard whether they are finished or not.
+        * 
+        * @return int number of enqueued requests.
+        */
+       function count() {}
+       /**
+        * Dequeue the http\Client\Request $request.
+        * 
+        * See http\Client::requeue(), if you want to requeue the request, instead of calling http\Client::dequeue() and then http\Client::enqueue().
+        * 
+        * @param \http\Client\Request $request The request to cancel.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\BadMethodCallException
+        * @throws \http\Exception\RuntimeException
+        * @return \http\Client self.
+        */
+       function dequeue(\http\Client\Request $request) {}
+       /**
+        * Implements SplSubject. Detach $observer, which has been previously attached.
+        * 
+        * @param \SplObserver $observer Previously attached instance of SplObserver implementation.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\UnexpectedValueException
+        * @return \http\Client self.
+        */
+       function detach(\SplObserver $observer) {}
+       /**
+        * Enable usage of an event library like libevent, which might improve performance with big socket sets.
+        * 
+        * > ***NOTE:***
+        * > This method has been deprecated in 2.3.0, please use http\Client::configure() instead.
+        * 
+        * @param bool $enable Whether to enable libevent usage.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\UnexpectedValueException
+        * @return \http\Client self.
+        * @deprecated
+        */
+       function enableEvents(bool $enable = true) {}
+       /**
+        * Enable sending pipelined requests to the same host if the driver supports it.
+        * 
+        * > ***NOTE:***
+        * > This method has been deprecated in 2.3.0, please use http\Client::configure() instead.
+        * 
+        * @param bool $enable Whether to enable pipelining.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\UnexpectedValueException
+        * @return \http\Client self.
+        * @deprecated
+        */
+       function enablePipelining(bool $enable = true) {}
+       /**
+        * Add another http\Client\Request to the request queue.
+        * If the optional callback $cb returns true, the request will be automatically dequeued.
+        * 
+        * > ***Note:***
+        * > The http\Client\Response object resulting from the request is always stored
+        * > internally to be retrieved at a later time, __even__ when $cb is used.
+        * >
+        * > If you are about to send a lot of requests and do __not__ need the response
+        * > after executing the callback, you can use http\Client::getResponse() within
+        * > the callback to keep the memory usage level as low as possible.
+        * 
+        * See http\Client::dequeue() and http\Client::send().
+        * 
+        * @param \http\Client\Request $request The request to enqueue.
+        * @param callable $cb as function(\http\Response $response) : ?bool
+        *   A callback to automatically call when the request has finished.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\BadMethodCallException
+        * @throws \http\Exception\RuntimeException
+        * @return \http\Client self.
+        */
+       function enqueue(\http\Client\Request $request, callable $cb = NULL) {}
+       /**
+        * Get a list of available configuration options and their default values.
+        * 
+        * See f.e. the [configuration options for the Curl driver](http/Client/Curl#Configuration:).
+        * 
+        * @throws \http\Exception\InvalidArgumentException
+        * @return array list of key/value pairs of available configuration options and their default values.
+        */
+       function getAvailableConfiguration() {}
+       /**
+        * List available drivers.
+        * 
+        * @return array list of supported drivers.
+        */
+       function getAvailableDrivers() {}
+       /**
+        * Retrieve a list of available request options and their default values.
+        * 
+        * See f.e. the [request options for the Curl driver](http/Client/Curl#Options:).
+        * 
+        * @throws \http\Exception\InvalidArgumentException
+        * @return array list of key/value pairs of available request options and their default values.
+        */
+       function getAvailableOptions() {}
+       /**
+        * Get priorly set custom cookies.
+        * See http\Client::setCookies().
+        * 
+        * @return array custom cookies.
+        */
+       function getCookies() {}
+       /**
+        * Simply returns the http\Message chain representing the request/response history.
+        * 
+        * > ***NOTE:***
+        * > The history is only recorded while http\Client::$recordHistory is true.
+        * 
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Message the request/response message chain representing the client's history.
+        */
+       function getHistory() {}
+       /**
+        * Returns the SplObjectStorage holding attached observers.
+        * 
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\UnexpectedValueException
+        * @return \SplObjectStorage observer storage.
+        */
+       function getObservers() {}
+       /**
+        * Get priorly set options.
+        * See http\Client::setOptions().
+        * 
+        * @return array options.
+        */
+       function getOptions() {}
+       /**
+        * Retrieve the progress information for $request.
+        * 
+        * @param \http\Client\Request $request The request to retrieve the current progress information for.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\UnexpectedValueException
+        * @return object|NULL object stdClass instance holding progress information.
+        *               or NULL if $request is not enqueued.
+        */
+       function getProgressInfo(\http\Client\Request $request) {}
+       /**
+        * Retrieve the corresponding response of an already finished request, or the last received response if $request is not set.
+        * 
+        * > ***NOTE:***
+        * > If $request is NULL, then the response is removed from the internal storage (stack-like operation).
+        * 
+        * @param \http\Client\Request $request The request to fetch the stored response for.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\UnexpectedValueException
+        * @return \http\Client\Response|NULL \http\Client\Response the stored response for the request, or the last that was received.
+        *               or NULL if no more response was available to pop, when no $request was given.
+        */
+       function getResponse(\http\Client\Request $request = NULL) {}
+       /**
+        * Retrieve priorly set SSL options.
+        * See http\Client::getOptions() and http\Client::setSslOptions().
+        * 
+        * @return array SSL options.
+        */
+       function getSslOptions() {}
+       /**
+        * Get transfer related information for a running or finished request.
+        * 
+        * @param \http\Client\Request $request The request to probe for transfer info.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\UnexpectedValueException
+        * @return object stdClass instance holding transfer related information.
+        */
+       function getTransferInfo(\http\Client\Request $request) {}
+       /**
+        * Implements SplSubject. Notify attached observers about progress with $request.
+        * 
+        * @param \http\Client\Request $request The request to notify about.
+        * @param object $progress stdClass instance holding progress information.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\UnexpectedValueException
+        * @return \http\Client self.
+        */
+       function notify(\http\Client\Request $request = NULL, $progress = NULL) {}
+       /**
+        * Perform outstanding transfer actions.
+        * See http\Client::wait() for the completing interface.
+        * 
+        * @return bool true if there are more transfers to complete.
+        */
+       function once() {}
+       /**
+        * Requeue an http\Client\Request.
+        * 
+        * The difference simply is, that this method, in contrast to http\Client::enqueue(), does not throw an http\Exception when the request to queue is already enqueued and dequeues it automatically prior enqueueing it again.
+        * 
+        * @param \http\Client\Request $request The request to queue.
+        * @param callable $cb as function(\http\Response $response) : ?bool
+        *   A callback to automatically call when the request has finished.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\RuntimeException
+        * @return \http\Client self.
+        */
+       function requeue(\http\Client\Request $request, callable $cb = NULL) {}
+       /**
+        * Reset the client to the initial state.
+        * 
+        * @return \http\Client self.
+        */
+       function reset() {}
+       /**
+        * Send all enqueued requests.
+        * See http\Client::once() and http\Client::wait() for a more fine grained interface.
+        * 
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\RuntimeException
+        * @return \http\Client self.
+        */
+       function send() {}
+       /**
+        * Set custom cookies.
+        * See http\Client::addCookies() and http\Client::getCookies().
+        * 
+        * @param array $cookies Set the custom cookies to this array.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Client self.
+        */
+       function setCookies(array $cookies = NULL) {}
+       /**
+        * Set client debugging callback.
+        * 
+        * > ***NOTE:***
+        * > This method has been added in v2.6.0, resp. v3.1.0.
+        * 
+        * @param callable $callback as function(http\Client $c, http\Client\Request $r, int $type, string $data)
+        *   The debug callback. For $type see http\Client::DEBUG_* constants.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Client self.
+        */
+       function setDebug(callable $callback) {}
+       /**
+        * Set client options.
+        * See http\Client\Curl.
+        * 
+        * > ***NOTE:***
+        * > Only options specified prior enqueueing a request are applied to the request.
+        * 
+        * @param array $options The options to set.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Client self.
+        */
+       function setOptions(array $options = NULL) {}
+       /**
+        * Specifically set SSL options.
+        * See http\Client::setOptions() and http\Client\Curl\$ssl options.
+        * 
+        * @param array $ssl_options Set SSL options to this array.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Client self.
+        */
+       function setSslOptions(array $ssl_options = NULL) {}
+       /**
+        * Wait for $timeout seconds for transfers to provide data.
+        * This is the completion call to http\Client::once().
+        * 
+        * @param float $timeout Seconds to wait for data on open sockets.
+        * @return bool success.
+        */
+       function wait(float $timeout = 0) {}
+}
+/**
+ * A class representing a list of cookies with specific attributes.
+ */
+class Cookie  {
+       /**
+        * Do not decode cookie contents.
+        */
+       const PARSE_RAW = 1;
+       /**
+        * The cookies' flags have the secure attribute set.
+        */
+       const SECURE = 16;
+       /**
+        * The cookies' flags have the httpOnly attribute set.
+        */
+       const HTTPONLY = 32;
+       /**
+        * Create a new cookie list.
+        * 
+        * @param mixed $cookies The string or list of cookies to parse or set.
+        * @param int $flags Parse flags. See http\Cookie::PARSE_* constants.
+        * @param array $allowed_extras List of extra attribute names to recognize.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\RuntimeException
+        */
+       function __construct($cookies = NULL, int $flags = 0, array $allowed_extras = NULL) {}
+       /**
+        * String cast handler. Alias of http\Cookie::toString().
+        * 
+        * @return string the cookie(s) represented as string.
+        */
+       function __toString() {}
+       /**
+        * Add a cookie.
+        * See http\Cookie::setCookie() and http\Cookie::addCookies().
+        * 
+        * @param string $cookie_name The key of the cookie.
+        * @param string $cookie_value The value of the cookie.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Cookie self.
+        */
+       function addCookie(string $cookie_name, string $cookie_value) {}
+       /**
+        * (Re)set the cookies.
+        * See http\Cookie::setCookies().
+        * 
+        * @param array $cookies Add cookies of this array of form ["name" => "value"].
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Cookie self.
+        */
+       function addCookies(array $cookies) {}
+       /**
+        * Add an extra attribute to the cookie list.
+        * See http\Cookie::setExtra().
+        * 
+        * @param string $extra_name The key of the extra attribute.
+        * @param string $extra_value The value of the extra attribute.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Cookie self.
+        */
+       function addExtra(string $extra_name, string $extra_value) {}
+       /**
+        * Add several extra attributes.
+        * See http\Cookie::addExtra().
+        * 
+        * @param array $extras A list of extra attributes of the form ["key" => "value"].
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Cookie self.
+        */
+       function addExtras(array $extras) {}
+       /**
+        * Retrieve a specific cookie value.
+        * See http\Cookie::setCookie().
+        * 
+        * @param string $cookie_name The key of the cookie to look up.
+        * @return string|NULL string the cookie value.
+        *               or NULL if $cookie_name could not be found.
+        */
+       function getCookie(string $cookie_name) {}
+       /**
+        * Get the list of cookies.
+        * See http\Cookie::setCookies().
+        * 
+        * @return array the list of cookies of form ["name" => "value"].
+        */
+       function getCookies() {}
+       /**
+        * Retrieve the effective domain of the cookie list.
+        * See http\Cookie::setDomain().
+        * 
+        * @return string the effective domain.
+        */
+       function getDomain() {}
+       /**
+        * Get the currently set expires attribute.
+        * See http\Cookie::setExpires().
+        * 
+        * > ***NOTE:***
+        * > A return value of -1 means that the attribute is not set.
+        * 
+        * @return int the currently set expires attribute as seconds since the epoch.
+        */
+       function getExpires() {}
+       /**
+        * Retrieve an extra attribute.
+        * See http\Cookie::setExtra().
+        * 
+        * @param string $name The key of the extra attribute.
+        * @return string the value of the extra attribute.
+        */
+       function getExtra(string $name) {}
+       /**
+        * Retrieve the list of extra attributes.
+        * See http\Cookie::setExtras().
+        * 
+        * @return array the list of extra attributes of the form ["key" => "value"].
+        */
+       function getExtras() {}
+       /**
+        * Get the currently set flags.
+        * See http\Cookie::SECURE and http\Cookie::HTTPONLY constants.
+        * 
+        * @return int the currently set flags bitmask.
+        */
+       function getFlags() {}
+       /**
+        * Get the currently set max-age attribute of the cookie list.
+        * See http\Cookie::setMaxAge().
+        * 
+        * > ***NOTE:***
+        * > A return value of -1 means that the attribute is not set.
+        * 
+        * @return int the currently set max-age.
+        */
+       function getMaxAge() {}
+       /**
+        * Retrieve the path the cookie(s) of this cookie list are effective at.
+        * See http\Cookie::setPath().
+        * 
+        * @return string the effective path.
+        */
+       function getPath() {}
+       /**
+        * (Re)set a cookie.
+        * See http\Cookie::addCookie() and http\Cookie::setCookies().
+        * 
+        * > ***NOTE:***
+        * > The cookie will be deleted from the list if $cookie_value is NULL.
+        * 
+        * @param string $cookie_name The key of the cookie.
+        * @param string $cookie_value The value of the cookie.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Cookie self.
+        */
+       function setCookie(string $cookie_name, string $cookie_value) {}
+       /**
+        * (Re)set the cookies.
+        * See http\Cookie::addCookies().
+        * 
+        * @param array $cookies Set the cookies to this array.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Cookie self.
+        */
+       function setCookies(array $cookies = NULL) {}
+       /**
+        * Set the effective domain of the cookie list.
+        * See http\Cookie::setPath().
+        * 
+        * @param string $value The domain the cookie(s) belong to.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Cookie self.
+        */
+       function setDomain(string $value = NULL) {}
+       /**
+        * Set the traditional expires timestamp.
+        * See http\Cookie::setMaxAge() for a safer alternative.
+        * 
+        * @param int $value The expires timestamp as seconds since the epoch.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Cookie self.
+        */
+       function setExpires(int $value = -1) {}
+       /**
+        * (Re)set an extra attribute.
+        * See http\Cookie::addExtra().
+        * 
+        * > ***NOTE:***
+        * > The attribute will be removed from the extras list if $extra_value is NULL.
+        * 
+        * @param string $extra_name The key of the extra attribute.
+        * @param string $extra_value The value of the extra attribute.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Cookie self.
+        */
+       function setExtra(string $extra_name, string $extra_value = NULL) {}
+       /**
+        * (Re)set the extra attributes.
+        * See http\Cookie::addExtras().
+        * 
+        * @param array $extras Set the extra attributes to this array.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Cookie self.
+        */
+       function setExtras(array $extras = NULL) {}
+       /**
+        * Set the flags to specified $value.
+        * See http\Cookie::SECURE and http\Cookie::HTTPONLY constants.
+        * 
+        * @param int $value The new flags bitmask.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Cookie self.
+        */
+       function setFlags(int $value = 0) {}
+       /**
+        * Set the maximum age the cookie may have on the client side.
+        * This is a client clock departure safe alternative to the "expires" attribute.
+        * See http\Cookie::setExpires().
+        * 
+        * @param int $value The max-age in seconds.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Cookie self.
+        */
+       function setMaxAge(int $value = -1) {}
+       /**
+        * Set the path the cookie(s) of this cookie list should be effective at.
+        * See http\Cookie::setDomain().
+        * 
+        * @param string $path The URL path the cookie(s) should take effect within.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Cookie self.
+        */
+       function setPath(string $path = NULL) {}
+       /**
+        * Get the cookie list as array.
+        * 
+        * @return array the cookie list as array.
+        */
+       function toArray() {}
+       /**
+        * Retrieve the string representation of the cookie list.
+        * See http\Cookie::toArray().
+        * 
+        * @return string the cookie list as string.
+        */
+       function toString() {}
+}
+/**
+ * 
+ */
+namespace http\Encoding;
+namespace http;
+/**
+ * The http\Env class provides static methods to manipulate and inspect the server's current request's HTTP environment.
+ */
+class Env  {
+       /**
+        * Retrieve the current HTTP request's body.
+        * 
+        * @param string $body_class_name A user class extending http\Message\Body.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\UnexpectedValueException
+        * @return \http\Message\Body instance representing the request body
+        */
+       function getRequestBody(string $body_class_name = NULL) {}
+       /**
+        * Retrieve one or all headers of the current HTTP request.
+        * 
+        * @param string $header_name The key of a header to retrieve.
+        * @return NULL|string|array NULL if $header_name was not found
+        *               or string the compound header when $header_name was found
+        *               or array of all headers if $header_name was not specified
+        */
+       function getRequestHeader(string $header_name = NULL) {}
+       /**
+        * Get the HTTP response code to send.
+        * 
+        * @return int the HTTP response code.
+        */
+       function getResponseCode() {}
+       /**
+        * Get one or all HTTP response headers to be sent.
+        * 
+        * @param string $header_name The name of the response header to retrieve.
+        * @return string|NULL|array string the compound value of the response header to send
+        *               or NULL if the header was not found
+        *               or array of all response headers, if $header_name was not specified
+        */
+       function getResponseHeader(string $header_name = NULL) {}
+       /**
+        * Retrieve a list of all known HTTP response status.
+        * 
+        * @return array mapping of the form \[
+        *   ...
+        *   int $code => string $status
+        *   ...
+        *   \]
+        */
+       function getResponseStatusForAllCodes() {}
+       /**
+        * Retrieve the string representation of specified HTTP response code.
+        * 
+        * @param int $code The HTTP response code to get the string representation for.
+        * @return string the HTTP response status message (may be empty, if no message for this code was found)
+        */
+       function getResponseStatusForCode(int $code) {}
+       /**
+        * Generic negotiator. For specific client negotiation see http\Env::negotiateContentType() and related methods.
+        * 
+        * > ***NOTE:***
+        * > The first element of $supported serves as a default if no operand matches.
+        * 
+        * @param string $params HTTP header parameter's value to negotiate.
+        * @param array $supported List of supported negotiation operands.
+        * @param string $prim_typ_sep A "primary type separator", i.e. that would be a hyphen for content language negotiation (en-US, de-DE, etc.).
+        * @param array $result Out parameter recording negotiation results.
+        * @return NULL|string NULL if negotiation fails.
+        *               or string the closest match negotiated, or the default (first entry of $supported).
+        */
+       function negotiate(string $params, array $supported, string $prim_typ_sep = NULL, array &$result = NULL) {}
+       /**
+        * Negotiate the client's preferred character set.
+        * 
+        * > ***NOTE:***
+        * > The first element of $supported character sets serves as a default if no character set matches.
+        * 
+        * @param array $supported List of supported content character sets.
+        * @param array $result Out parameter recording negotiation results.
+        * @return NULL|string NULL if negotiation fails.
+        *               or string the negotiated character set.
+        */
+       function negotiateCharset(array $supported, array &$result = NULL) {}
+       /**
+        * Negotiate the client's preferred MIME content type.
+        * 
+        * > ***NOTE:***
+        * > The first element of $supported content types serves as a default if no content-type matches.
+        * 
+        * @param array $supported List of supported MIME content types.
+        * @param array $result Out parameter recording negotiation results.
+        * @return NULL|string NULL if negotiation fails.
+        *               or string the negotiated content type.
+        */
+       function negotiateContentType(array $supported, array &$result = NULL) {}
+       /**
+        * Negotiate the client's preferred encoding.
+        * 
+        * > ***NOTE:***
+        * > The first element of $supported encodings serves as a default if no encoding matches.
+        * 
+        * @param array $supported List of supported content encodings.
+        * @param array $result Out parameter recording negotiation results.
+        * @return NULL|string NULL if negotiation fails.
+        *               or string the negotiated encoding.
+        */
+       function negotiateEncoding(array $supported, array &$result = NULL) {}
+       /**
+        * Negotiate the client's preferred language.
+        * 
+        * > ***NOTE:***
+        * > The first element of $supported languages serves as a default if no language matches.
+        * 
+        * @param array $supported List of supported content languages.
+        * @param array $result Out parameter recording negotiation results.
+        * @return NULL|string NULL if negotiation fails.
+        *               or string the negotiated language.
+        */
+       function negotiateLanguage(array $supported, array &$result = NULL) {}
+       /**
+        * Set the HTTP response code to send.
+        * 
+        * @param int $code The HTTP response status code.
+        * @return bool Success.
+        */
+       function setResponseCode(int $code) {}
+       /**
+        * Set a response header, either replacing a prior set header, or appending the new header value, depending on $replace.
+        * 
+        * If no $header_value is specified, or $header_value is NULL, then a previously set header with the same key will be deleted from the list.
+        * 
+        * If $response_code is not 0, the response status code is updated accordingly.
+        * 
+        * @param string $header_name
+        * @param mixed $header_value
+        * @param int $response_code
+        * @param bool $replace
+        * @return bool Success.
+        */
+       function setResponseHeader(string $header_name, $header_value = NULL, int $response_code = NULL, bool $replace = NULL) {}
+}
+/**
+ * The http extension's Exception interface.
+ * 
+ * Use it to catch any Exception thrown by pecl/http.
+ * 
+ * The individual exception classes extend their equally named native PHP extensions, if such exist, and implement this empty interface. For example the http\Exception\BadMethodCallException extends SPL's BadMethodCallException.
+ */
+interface Exception  {
+}
+/**
+ * The http\Header class provides methods to manipulate, match, negotiate and serialize HTTP headers.
+ */
+class Header implements \Serializable {
+       /**
+        * None of the following match constraints applies.
+        */
+       const MATCH_LOOSE = 0;
+       /**
+        * Perform case sensitive matching.
+        */
+       const MATCH_CASE = 1;
+       /**
+        * Match only on word boundaries (according by CType alpha-numeric).
+        */
+       const MATCH_WORD = 16;
+       /**
+        * Match the complete string.
+        */
+       const MATCH_FULL = 32;
+       /**
+        * Case sensitively match the full string (same as MATCH_CASE|MATCH_FULL).
+        */
+       const MATCH_STRICT = 33;
+       /**
+        * The name of the HTTP header.
+        * 
+        * @public
+        * @var string
+        */
+       public $name = NULL;
+       /**
+        * The value of the HTTP header.
+        * 
+        * @public
+        * @var mixed
+        */
+       public $value = NULL;
+       /**
+        * Create an http\Header instance for use of simple matching or negotiation. If the value of the header is an array it may be compounded to a single comma separated string.
+        * 
+        * @param string $name The HTTP header name.
+        * @param mixed $value The value of the header.
+        * 
+        * # Throws:
+        */
+       function __construct(string $name = NULL, $value = NULL) {}
+       /**
+        * String cast handler. Alias of http\Header::serialize().
+        * 
+        * @return string the serialized form of the HTTP header (i.e. "Name: value").
+        */
+       function __toString() {}
+       /**
+        * Create a parameter list out of the HTTP header value.
+        * 
+        * @param mixed $ps The parameter separator(s).
+        * @param mixed $as The argument separator(s).
+        * @param mixed $vs The value separator(s).
+        * @param int $flags The modus operandi. See http\Params constants.
+        * @return \http\Params instance
+        */
+       function getParams($ps = NULL, $as = NULL, $vs = NULL, int $flags = NULL) {}
+       /**
+        * Match the HTTP header's value against provided $value according to $flags.
+        * 
+        * @param string $value The comparison value.
+        * @param int $flags The modus operandi. See http\Header constants.
+        * @return bool whether $value matches the header value according to $flags.
+        */
+       function match(string $value, int $flags = NULL) {}
+       /**
+        * Negotiate the header's value against a list of supported values in $supported.
+        * Negotiation operation is adopted according to the header name, i.e. if the
+        * header being negotiated is Accept, then a slash is used as primary type
+        * separator, and if the header is Accept-Language respectively, a hyphen is
+        * used instead.
+        * 
+        * > ***NOTE:***
+        * > The first element of $supported serves as a default if no operand matches.
+        * 
+        * @param array $supported The list of supported values to negotiate.
+        * @param array $result Out parameter recording the negotiation results.
+        * @return NULL|string NULL if negotiation fails.
+        *               or string the closest match negotiated, or the default (first entry of $supported).
+        */
+       function negotiate(array $supported, array &$result = NULL) {}
+       /**
+        * Parse HTTP headers.
+        * See also http\Header\Parser.
+        * 
+        * @param string $header The complete string of headers.
+        * @param string $header_class A class extending http\Header.
+        * @return array|false array of parsed headers, where the elements are instances of $header_class if specified.
+        *               or false if parsing fails.
+        */
+       function parse(string $header, string $header_class = NULL) {}
+       /**
+        * Implements Serializable.
+        * 
+        * @return string serialized representation of HTTP header (i.e. "Name: value")
+        */
+       function serialize() {}
+       /**
+        * Convenience method. Alias of http\Header::serialize().
+        * 
+        * @return string the serialized form of the HTTP header (i.e. "Name: value").
+        */
+       function toString() {}
+       /**
+        * Implements Serializable.
+        * 
+        * @param string $serialized The serialized HTTP header (i.e. "Name: value")
+        */
+       function unserialize($serialized) {}
+}
+/**
+ * The message class builds the foundation for any request and response message.
+ * 
+ * See http\Client\Request and http\Client\Response, as well as http\Env\Request and http\Env\Response.
+ */
+class Message implements \Countable, \Serializable, \Iterator {
+       /**
+        * No specific type of message.
+        */
+       const TYPE_NONE = 0;
+       /**
+        * A request message.
+        */
+       const TYPE_REQUEST = 1;
+       /**
+        * A response message.
+        */
+       const TYPE_RESPONSE = 2;
+       /**
+        * The message type. See http\Message::TYPE_* constants.
+        * 
+        * @protected
+        * @var int
+        */
+       protected $type = \http\Message::TYPE_NONE;
+       /**
+        * The message's body.
+        * 
+        * @protected
+        * @var \http\Message\Body
+        */
+       protected $body = NULL;
+       /**
+        * The request method if the message is of type request.
+        * 
+        * @protected
+        * @var string
+        */
+       protected $requestMethod = "";
+       /**
+        * The request url if the message is of type request.
+        * 
+        * @protected
+        * @var string
+        */
+       protected $requestUrl = "";
+       /**
+        * The response status phrase if the message is of type response.
+        * 
+        * @protected
+        * @var string
+        */
+       protected $responseStatus = "";
+       /**
+        * The response code if the message is of type response.
+        * 
+        * @protected
+        * @var int
+        */
+       protected $responseCode = 0;
+       /**
+        * A custom HTTP protocol version.
+        * 
+        * @protected
+        * @var string
+        */
+       protected $httpVersion = NULL;
+       /**
+        * Any message headers.
+        * 
+        * @protected
+        * @var array
+        */
+       protected $headers = NULL;
+       /**
+        * Any parent message.
+        * 
+        * @protected
+        * @var \http\Message
+        */
+       protected $parentMessage;
+       /**
+        * Create a new HTTP message.
+        * 
+        * @param mixed $message Either a resource or a string, representing the HTTP message.
+        * @param bool $greedy Whether to read from a $message resource until EOF.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\BadMessageException
+        */
+       function __construct($message = NULL, bool $greedy = true) {}
+       /**
+        * Retrieve the message serialized to a string.
+        * Alias of http\Message::toString().
+        * 
+        * @return string the single serialized HTTP message.
+        */
+       function __toString() {}
+       /**
+        * Append the data of $body to the message's body.
+        * See http\Message::setBody() and http\Message\Body::append().
+        * 
+        * @param \http\Message\Body $body The message body to add.
+        * @return \http\Message self.
+        */
+       function addBody(\http\Message\Body $body) {}
+       /**
+        * Add an header, appending to already existing headers.
+        * See http\Message::addHeaders() and http\Message::setHeader().
+        * 
+        * @param string $name The header name.
+        * @param mixed $value The header value.
+        * @return \http\Message self.
+        */
+       function addHeader(string $name, $value) {}
+       /**
+        * Add headers, optionally appending values, if header keys already exist.
+        * See http\Message::addHeader() and http\Message::setHeaders().
+        * 
+        * @param array $headers The HTTP headers to add.
+        * @param bool $append Whether to append values for existing headers.
+        * @return \http\Message self.
+        */
+       function addHeaders(array $headers, bool $append = false) {}
+       /**
+        * Implements Countable.
+        * 
+        * @return int the count of messages in the chain above the current message.
+        */
+       function count() {}
+       /**
+        * Implements iterator.
+        * See http\Message::valid() and http\Message::rewind().
+        * 
+        * @return \http\Message the current message in the iterated message chain.
+        */
+       function current() {}
+       /**
+        * Detach a clone of this message from any message chain.
+        * 
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Message clone.
+        */
+       function detach() {}
+       /**
+        * Retrieve the message's body.
+        * See http\Message::setBody().
+        * 
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\UnexpectedValueException
+        * @return \http\Message\Body the message body.
+        */
+       function getBody() {}
+       /**
+        * Retrieve a single header, optionally hydrated into a http\Header extending class.
+        * 
+        * @param string $header The header's name.
+        * @param string $into_class The name of a class extending http\Header.
+        * @return mixed|\http\Header mixed the header value if $into_class is NULL.
+        *               or \http\Header descendant.
+        */
+       function getHeader(string $header, string $into_class = NULL) {}
+       /**
+        * Retrieve all message headers.
+        * See http\Message::setHeaders() and http\Message::getHeader().
+        * 
+        * @return array the message's headers.
+        */
+       function getHeaders() {}
+       /**
+        * Retrieve the HTTP protocol version of the message.
+        * See http\Message::setHttpVersion().
+        * 
+        * @return string the HTTP protocol version, e.g. "1.0"; defaults to "1.1".
+        */
+       function getHttpVersion() {}
+       /**
+        * Retrieve the first line of a request or response message.
+        * See http\Message::setInfo and also:
+        * 
+        * * http\Message::getType()
+        * * http\Message::getHttpVersion()
+        * * http\Message::getResponseCode()
+        * * http\Message::getResponseStatus()
+        * * http\Message::getRequestMethod()
+        * * http\Message::getRequestUrl()
+        * 
+        * @return string|NULL string the HTTP message information.
+        *               or NULL if the message is neither of type request nor response.
+        */
+       function getInfo() {}
+       /**
+        * Retrieve any parent message.
+        * See http\Message::reverse().
+        * 
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\BadMethodCallException
+        * @return \http\Message the parent message.
+        */
+       function getParentMessage() {}
+       /**
+        * Retrieve the request method of the message.
+        * See http\Message::setRequestMethod() and http\Message::getRequestUrl().
+        * 
+        * @return string|false string the request method.
+        *               or false if the message was not of type request.
+        */
+       function getRequestMethod() {}
+       /**
+        * Retrieve the request URL of the message.
+        * See http\Message::setRequestUrl().
+        * 
+        * @return string|false string the request URL; usually the path and the querystring.
+        *               or false if the message was not of type request.
+        */
+       function getRequestUrl() {}
+       /**
+        * Retrieve the response code of the message.
+        * See http\Message::setResponseCode() and http\Message::getResponseStatus().
+        * 
+        * @return int|false int the response status code.
+        *               or false if the message is not of type response.
+        */
+       function getResponseCode() {}
+       /**
+        * Retrieve the response status of the message.
+        * See http\Message::setResponseStatus() and http\Message::getResponseCode().
+        * 
+        * @return string|false string the response status phrase.
+        *               or false if the message is not of type response.
+        */
+       function getResponseStatus() {}
+       /**
+        * Retrieve the type of the message.
+        * See http\Message::setType() and http\Message::getInfo().
+        * 
+        * @return int the message type. See http\Message::TYPE_* constants.
+        */
+       function getType() {}
+       /**
+        * Check whether this message is a multipart message based on it's content type.
+        * If the message is a multipart message and a reference $boundary is given, the boundary string of the multipart message will be stored in $boundary.
+        * 
+        * See http\Message::splitMultipartBody().
+        * 
+        * @param string $boundary A reference where the boundary string will be stored.
+        * @return bool whether this is a message with a multipart "Content-Type".
+        */
+       function isMultipart(string &$boundary = NULL) {}
+       /**
+        * Implements Iterator.
+        * See http\Message::current() and http\Message::rewind().
+        * 
+        * @return int a non-sequential integer key.
+        */
+       function key() {}
+       /**
+        * Implements Iterator.
+        * See http\Message::valid() and http\Message::rewind().
+        */
+       function next() {}
+       /**
+        * Prepend message(s) $message to this message, or the top most message of this message chain.
+        * 
+        * > ***NOTE:***
+        * > The message chains must not overlap.
+        * 
+        * @param \http\Message $message The message (chain) to prepend as parent messages.
+        * @param bool $top Whether to prepend to the top-most parent message.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\UnexpectedValueException
+        * @return \http\Message self.
+        */
+       function prepend(\http\Message $message, bool $top = true) {}
+       /**
+        * Reverse the message chain and return the former top-most message.
+        * 
+        * > ***NOTE:***
+        * > Message chains are ordered in reverse-parsed order by default, i.e. the last parsed message is the message you'll receive from any call parsing HTTP messages.
+        * >
+        * > This call re-orders the messages of the chain and returns the message that was parsed first with any later parsed messages re-parentized.
+        * 
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Message the other end of the message chain.
+        */
+       function reverse() {}
+       /**
+        * Implements Iterator.
+        */
+       function rewind() {}
+       /**
+        * Implements Serializable.
+        * 
+        * @return string the serialized HTTP message.
+        */
+       function serialize() {}
+       /**
+        * Set the message's body.
+        * See http\Message::getBody() and http\Message::addBody().
+        * 
+        * @param \http\Message\Body $body The new message body.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\UnexpectedValueException
+        * @return \http\Message self.
+        */
+       function setBody(\http\Message\Body $body) {}
+       /**
+        * Set a single header.
+        * See http\Message::getHeader() and http\Message::addHeader().
+        * 
+        * > ***NOTE:***
+        * > Prior to v2.5.6/v3.1.0 headers with the same name were merged into a single
+        * > header with values concatenated by comma.
+        * 
+        * @param string $header The header's name.
+        * @param mixed $value The header's value. Removes the header if NULL.
+        * @return \http\Message self.
+        */
+       function setHeader(string $header, $value = NULL) {}
+       /**
+        * Set the message headers.
+        * See http\Message::getHeaders() and http\Message::addHeaders().
+        * 
+        * > ***NOTE:***
+        * > Prior to v2.5.6/v3.1.0 headers with the same name were merged into a single
+        * > header with values concatenated by comma.
+        * 
+        * @param array $headers The message's headers.
+        * @return \http\Message null.
+        */
+       function setHeaders(array $headers = NULL) {}
+       /**
+        * Set the HTTP protocol version of the message.
+        * See http\Message::getHttpVersion().
+        * 
+        * @param string $http_version The protocol version, e.g. "1.1", optionally prefixed by "HTTP/".
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\BadHeaderException
+        * @return \http\Message self.
+        */
+       function setHttpVersion(string $http_version) {}
+       /**
+        * Set the complete message info, i.e. type and response resp. request information, at once.
+        * See http\Message::getInfo().
+        * 
+        * @param string $http_info The message info (first line of an HTTP message).
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\BadHeaderException
+        * @return \http\Message self.
+        */
+       function setInfo(string $http_info) {}
+       /**
+        * Set the request method of the message.
+        * See http\Message::getRequestMethod() and http\Message::setRequestUrl().
+        * 
+        * @param string $method The request method.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\BadMethodCallException
+        * @return \http\Message self.
+        */
+       function setRequestMethod(string $method) {}
+       /**
+        * Set the request URL of the message.
+        * See http\Message::getRequestUrl() and http\Message::setRequestMethod().
+        * 
+        * @param string $url The request URL.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\BadMethodCallException
+        * @return \http\Message self.
+        */
+       function setRequestUrl(string $url) {}
+       /**
+        * Set the response status code.
+        * See http\Message::getResponseCode() and http\Message::setResponseStatus().
+        * 
+        * > ***NOTE:***
+        * > This method also resets the response status phrase to the default for that code.
+        * 
+        * @param int $response_code The response code.
+        * @param bool $strict Whether to check that the response code is between 100 and 599 inclusive.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\BadMethodCallException
+        * @return \http\Message self.
+        */
+       function setResponseCode(int $response_code, bool $strict = true) {}
+       /**
+        * Set the response status phrase.
+        * See http\Message::getResponseStatus() and http\Message::setResponseCode().
+        * 
+        * @param string $response_status The status phrase.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\BadMethodCallException
+        * @return \http\Message self.
+        */
+       function setResponseStatus(string $response_status) {}
+       /**
+        * Set the message type and reset the message info.
+        * See http\Message::getType() and http\Message::setInfo().
+        * 
+        * @param int $type The desired message type. See the http\Message::TYPE_* constants.
+        * @return \http\Message self.
+        */
+       function setType(int $type) {}
+       /**
+        * Splits the body of a multipart message.
+        * See http\Message::isMultipart() and http\Message\Body::addPart().
+        * 
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\BadMethodCallException
+        * @throws \http\Exception\BadMessageException
+        * @return \http\Message a message chain of all messages of the multipart body.
+        */
+       function splitMultipartBody() {}
+       /**
+        * Stream the message through a callback.
+        * 
+        * @param callable $callback The callback of the form function(http\Message $from, string $data).
+        * @return \http\Message self.
+        */
+       function toCallback(callable $callback) {}
+       /**
+        * Stream the message into stream $stream, starting from $offset, streaming $maxlen at most.
+        * 
+        * @param resource $stream The resource to write to.
+        * @return \http\Message self.
+        */
+       function toStream($stream) {}
+       /**
+        * Retrieve the message serialized to a string.
+        * 
+        * @param bool $include_parent Whether to include all parent messages.
+        * @return string the HTTP message chain serialized to a string.
+        */
+       function toString(bool $include_parent = false) {}
+       /**
+        * Implements Serializable.
+        * 
+        * @param string $data The serialized message.
+        */
+       function unserialize($data) {}
+       /**
+        * Implements Iterator.
+        * See http\Message::current() and http\Message::rewind().
+        * 
+        * @return bool whether http\Message::current() would not return NULL.
+        */
+       function valid() {}
+}
+/**
+ * Parse, interpret and compose HTTP (header) parameters.
+ */
+class Params implements \ArrayAccess {
+       /**
+        * The default parameter separator (",").
+        */
+       const DEF_PARAM_SEP = ',';
+       /**
+        * The default argument separator (";").
+        */
+       const DEF_ARG_SEP = ';';
+       /**
+        * The default value separator ("=").
+        */
+       const DEF_VAL_SEP = '=';
+       /**
+        * Empty param separator to parse cookies.
+        */
+       const COOKIE_PARAM_SEP = '';
+       /**
+        * Do not interpret the parsed parameters.
+        */
+       const PARSE_RAW = 0;
+       /**
+        * Interpret input as default formatted parameters.
+        */
+       const PARSE_DEFAULT = 17;
+       /**
+        * Parse backslash escaped (quoted) strings.
+        */
+       const PARSE_ESCAPED = 1;
+       /**
+        * Urldecode single units of parameters, arguments and values.
+        */
+       const PARSE_URLENCODED = 4;
+       /**
+        * Parse sub dimensions indicated by square brackets.
+        */
+       const PARSE_DIMENSION = 8;
+       /**
+        * Parse URL querystring (same as http\Params::PARSE_URLENCODED|http\Params::PARSE_DIMENSION).
+        */
+       const PARSE_QUERY = 12;
+       /**
+        * Parse [RFC5987](http://tools.ietf.org/html/rfc5987) style encoded character set and language information embedded in HTTP header params.
+        */
+       const PARSE_RFC5987 = 16;
+       /**
+        * Parse [RFC5988](http://tools.ietf.org/html/rfc5988) (Web Linking) tags of Link headers.
+        */
+       const PARSE_RFC5988 = 32;
+       /**
+        * The (parsed) parameters.
+        * 
+        * @public
+        * @var array
+        */
+       public $params = NULL;
+       /**
+        * The parameter separator(s).
+        * 
+        * @public
+        * @var array
+        */
+       public $param_sep = \http\Params::DEF_PARAM_SEP;
+       /**
+        * The argument separator(s).
+        * 
+        * @public
+        * @var array
+        */
+       public $arg_sep = \http\Params::DEF_ARG_SEP;
+       /**
+        * The value separator(s).
+        * 
+        * @public
+        * @var array
+        */
+       public $val_sep = \http\Params::DEF_VAL_SEP;
+       /**
+        * The modus operandi of the parser. See http\Params::PARSE_* constants.
+        * 
+        * @public
+        * @var int
+        */
+       public $flags = \http\Params::PARSE_DEFAULT;
+       /**
+        * Instantiate a new HTTP (header) parameter set.
+        * 
+        * @param mixed $params Pre-parsed parameters or a string to be parsed.
+        * @param mixed $ps The parameter separator(s).
+        * @param mixed $as The argument separator(s).
+        * @param mixed $vs The value separator(s).
+        * @param int $flags The modus operandi. See http\Params::PARSE_* constants.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\RuntimeException
+        */
+       function __construct($params = NULL, $ps = NULL, $as = NULL, $vs = NULL, int $flags = NULL) {}
+       /**
+        * String cast handler. Alias of http\Params::toString().
+        * Returns a stringified version of the parameters.
+        * 
+        * @return string version of the parameters.
+        */
+       function __toString() {}
+       /**
+        * Implements ArrayAccess.
+        * 
+        * @param string $name The offset to look after.
+        * @return bool Existence.
+        */
+       function offsetExists($name) {}
+       /**
+        * Implements ArrayAccess.
+        * 
+        * @param string $name The offset to retrieve.
+        * @return mixed contents at offset.
+        */
+       function offsetGet($name) {}
+       /**
+        * Implements ArrayAccess.
+        * 
+        * @param string $name The offset to modify.
+        * @param mixed $value The value to set.
+        */
+       function offsetSet($name, $value) {}
+       /**
+        * Implements ArrayAccess.
+        * 
+        * @param string $name The offset to delete.
+        */
+       function offsetUnset($name) {}
+       /**
+        * Convenience method that simply returns http\Params::$params.
+        * 
+        * @return array of parameters.
+        */
+       function toArray() {}
+       /**
+        * Returns a stringified version of the parameters.
+        * 
+        * @return string version of the parameters.
+        */
+       function toString() {}
+}
+/**
+ * The http\QueryString class provides versatile facilities to retrieve, use and manipulate query strings and form data.
+ */
+class QueryString implements \Serializable, \ArrayAccess, \IteratorAggregate {
+       /**
+        * Cast requested value to bool.
+        */
+       const TYPE_BOOL = 17;
+       /**
+        * Cast requested value to int.
+        */
+       const TYPE_INT = 4;
+       /**
+        * Cast requested value to float.
+        */
+       const TYPE_FLOAT = 5;
+       /**
+        * Cast requested value to string.
+        */
+       const TYPE_STRING = 6;
+       /**
+        * Cast requested value to an array.
+        */
+       const TYPE_ARRAY = 7;
+       /**
+        * Cast requested value to an object.
+        */
+       const TYPE_OBJECT = 8;
+       /**
+        * The global instance. See http\QueryString::getGlobalInstance().
+        * 
+        * @private
+        * @var \http\QueryString
+        */
+       private $instance = NULL;
+       /**
+        * The data.
+        * 
+        * @private
+        * @var array
+        */
+       private $queryArray = NULL;
+       /**
+        * Create an independent querystring instance.
+        * 
+        * @param mixed $params The query parameters to use or parse.
+        * @throws \http\Exception\BadQueryStringException
+        */
+       function __construct($params = NULL) {}
+       /**
+        * Get the string representation of the querystring (x-www-form-urlencoded).
+        * 
+        * @return string the x-www-form-urlencoded querystring.
+        */
+       function __toString() {}
+       /**
+        * Retrieve an querystring value.
+        * 
+        * See http\QueryString::TYPE_* constants.
+        * 
+        * @param string $name The key to retrieve the value for.
+        * @param mixed $type The type to cast the value to. See http\QueryString::TYPE_* constants.
+        * @param mixed $defval The default value to return if the key $name does not exist.
+        * @param bool $delete Whether to delete the entry from the querystring after retrieval.
+        * @return \http\QueryString|string|mixed|mixed|string \http\QueryString if called without arguments.
+        *               or string the whole querystring if $name is of zero length.
+        *               or mixed $defval if the key $name does not exist.
+        *               or mixed the querystring value cast to $type if $type was specified and the key $name exists.
+        *               or string the querystring value if the key $name exists and $type is not specified or equals http\QueryString::TYPE_STRING.
+        */
+       function get(string $name = NULL, $type = NULL, $defval = NULL, bool $delete = false) {}
+       /**
+        * Retrieve an array value with at offset $name.
+        * 
+        * @param string $name The key to look up.
+        * @param mixed $defval The default value to return if the offset $name does not exist.
+        * @param bool $delete Whether to remove the key and value from the querystring after retrieval.
+        * @return array|mixed array the (casted) value.
+        *               or mixed $defval if offset $name does not exist.
+        */
+       function getArray(string $name, $defval = NULL, bool $delete = false) {}
+       /**
+        * Retrieve a boolean value at offset $name.
+        * 
+        * @param string $name The key to look up.
+        * @param mixed $defval The default value to return if the offset $name does not exist.
+        * @param bool $delete Whether to remove the key and value from the querystring after retrieval.
+        * @return bool|mixed bool the (casted) value.
+        *               or mixed $defval if offset $name does not exist.
+        */
+       function getBool(string $name, $defval = NULL, bool $delete = false) {}
+       /**
+        * Retrieve a float value at offset $name.
+        * 
+        * @param string $name The key to look up.
+        * @param mixed $defval The default value to return if the offset $name does not exist.
+        * @param bool $delete Whether to remove the key and value from the querystring after retrieval.
+        * @return float|mixed float the (casted) value.
+        *               or mixed $defval if offset $name does not exist.
+        */
+       function getFloat(string $name, $defval = NULL, bool $delete = false) {}
+       /**
+        * Retrieve the global querystring instance referencing $_GET.
+        * 
+        * @throws \http\Exception\UnexpectedValueException
+        * @return \http\QueryString the http\QueryString::$instance
+        */
+       function getGlobalInstance() {}
+       /**
+        * Retrieve a int value at offset $name.
+        * 
+        * @param string $name The key to look up.
+        * @param mixed $defval The default value to return if the offset $name does not exist.
+        * @param bool $delete Whether to remove the key and value from the querystring after retrieval.
+        * @return int|mixed int the (casted) value.
+        *               or mixed $defval if offset $name does not exist.
+        */
+       function getInt(string $name, $defval = NULL, bool $delete = false) {}
+       /**
+        * Implements IteratorAggregate.
+        * 
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \InvalidArgumentException
+        */
+       function getIterator() {}
+       /**
+        * Retrieve a object value with at offset $name.
+        * 
+        * @param string $name The key to look up.
+        * @param mixed $defval The default value to return if the offset $name does not exist.
+        * @param bool $delete Whether to remove the key and value from the querystring after retrieval.
+        * @return object|mixed object the (casted) value.
+        *               or mixed $defval if offset $name does not exist.
+        */
+       function getObject(string $name, $defval = NULL, bool $delete = false) {}
+       /**
+        * Retrieve a string value with at offset $name.
+        * 
+        * @param string $name The key to look up.
+        * @param mixed $defval The default value to return if the offset $name does not exist.
+        * @param bool $delete Whether to remove the key and value from the querystring after retrieval.
+        * @return string|mixed string the (casted) value.
+        *               or mixed $defval if offset $name does not exist.
+        */
+       function getString(string $name, $defval = NULL, bool $delete = false) {}
+       /**
+        * Set additional $params to a clone of this instance.
+        * See http\QueryString::set().
+        * 
+        * > ***NOTE:***
+        * > This method returns a clone (copy) of this instance.
+        * 
+        * @param mixed $params Additional params as object, array or string to parse.
+        * @throws \http\Exception\BadQueryStringException
+        * @return \http\QueryString clone.
+        */
+       function mod($params = NULL) {}
+       /**
+        * Implements ArrayAccess.
+        * 
+        * @param string $name The offset to look up.
+        * @return bool whether the key $name isset.
+        */
+       function offsetExists($name) {}
+       /**
+        * Implements ArrayAccess.
+        * 
+        * @param mixed $offset The offset to look up.
+        * @return mixed|NULL mixed the value locate at offset $name.
+        *               or NULL if key $name could not be found.
+        */
+       function offsetGet($offset) {}
+       /**
+        * Implements ArrayAccess.
+        * 
+        * @param string $name The key to set the value for.
+        * @param mixed $data The data to place at offset $name.
+        */
+       function offsetSet($name, $data) {}
+       /**
+        * Implements ArrayAccess.
+        * 
+        * @param string $name The offset to look up.
+        */
+       function offsetUnset($name) {}
+       /**
+        * Implements Serializable.
+        * See http\QueryString::toString().
+        * 
+        * @return string the x-www-form-urlencoded querystring.
+        */
+       function serialize() {}
+       /**
+        * Set additional querystring entries.
+        * 
+        * @param mixed $params Additional params as object, array or string to parse.
+        * @return \http\QueryString self.
+        */
+       function set($params) {}
+       /**
+        * Simply returns http\QueryString::$queryArray.
+        * 
+        * @return array the $queryArray property.
+        */
+       function toArray() {}
+       /**
+        * Get the string representation of the querystring (x-www-form-urlencoded).
+        * 
+        * @return string the x-www-form-urlencoded querystring.
+        */
+       function toString() {}
+       /**
+        * Implements Serializable.
+        * 
+        * @param string $serialized The x-www-form-urlencoded querystring.
+        * @throws \http\Exception
+        */
+       function unserialize($serialized) {}
+       /**
+        * Translate character encodings of the querystring with ext/iconv.
+        * 
+        * > ***NOTE:***
+        * > This method is only available when ext/iconv support was enabled at build time.
+        * 
+        * @param string $from_enc The encoding to convert from.
+        * @param string $to_enc The encoding to convert to.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\BadConversionException
+        * @return \http\QueryString self.
+        */
+       function xlate(string $from_enc, string $to_enc) {}
+}
+/**
+ * The http\Url class provides versatile means to parse, construct and manipulate URLs.
+ */
+class Url  {
+       /**
+        * Replace parts of the old URL with parts of the new.
+        */
+       const REPLACE = 0;
+       /**
+        * Whether a relative path should be joined into the old path.
+        */
+       const JOIN_PATH = 1;
+       /**
+        * Whether the querystrings should be joined.
+        */
+       const JOIN_QUERY = 2;
+       /**
+        * Strip the user information from the URL.
+        */
+       const STRIP_USER = 4;
+       /**
+        * Strip the password from the URL.
+        */
+       const STRIP_PASS = 8;
+       /**
+        * Strip user and password information from URL (same as STRIP_USER|STRIP_PASS).
+        */
+       const STRIP_AUTH = 12;
+       /**
+        * Do not include the port.
+        */
+       const STRIP_PORT = 32;
+       /**
+        * Do not include the URL path.
+        */
+       const STRIP_PATH = 64;
+       /**
+        * Do not include the URL querystring.
+        */
+       const STRIP_QUERY = 128;
+       /**
+        * Strip the fragment (hash) from the URL.
+        */
+       const STRIP_FRAGMENT = 256;
+       /**
+        * Strip everything except scheme and host information.
+        */
+       const STRIP_ALL = 492;
+       /**
+        * Import initial URL parts from the SAPI environment.
+        */
+       const FROM_ENV = 4096;
+       /**
+        * Whether to sanitize the URL path (consolidate double slashes, directory jumps etc.)
+        */
+       const SANITIZE_PATH = 8192;
+       /**
+        * Parse UTF-8 encoded multibyte sequences.
+        */
+       const PARSE_MBUTF8 = 131072;
+       /**
+        * Parse locale encoded multibyte sequences (on systems with wide character support).
+        */
+       const PARSE_MBLOC = 65536;
+       /**
+        * Parse and convert multibyte hostnames according to IDNA (with IDNA support).
+        */
+       const PARSE_TOIDN = 1048576;
+       /**
+        * Explicitly request IDNA2003 implementation if available (libidn, idnkit or ICU).
+        */
+       const PARSE_TOIDN_2003 = 9437184;
+       /**
+        * Explicitly request IDNA2008 implementation if available (libidn2, idnkit2 or ICU).
+        */
+       const PARSE_TOIDN_2008 = 5242880;
+       /**
+        * Percent encode multibyte sequences in the userinfo, path, query and fragment parts of the URL.
+        */
+       const PARSE_TOPCT = 2097152;
+       /**
+        * Continue parsing when encountering errors.
+        */
+       const IGNORE_ERRORS = 268435456;
+       /**
+        * Suppress errors/exceptions.
+        */
+       const SILENT_ERRORS = 536870912;
+       /**
+        * Standard flags used by default internally for e.g. http\Message::setRequestUrl().
+        *   Enables joining path and query, sanitizing path, multibyte/unicode, international domain names and transient percent encoding.
+        */
+       const STDFLAGS = 3350531;
+       /**
+        * The URL's scheme.
+        * 
+        * @public
+        * @var string
+        */
+       public $scheme = NULL;
+       /**
+        * Authenticating user.
+        * 
+        * @public
+        * @var string
+        */
+       public $user = NULL;
+       /**
+        * Authentication password.
+        * 
+        * @public
+        * @var string
+        */
+       public $pass = NULL;
+       /**
+        * Hostname/domain.
+        * 
+        * @public
+        * @var string
+        */
+       public $host = NULL;
+       /**
+        * Port.
+        * 
+        * @public
+        * @var string
+        */
+       public $port = NULL;
+       /**
+        * URL path.
+        * 
+        * @public
+        * @var string
+        */
+       public $path = NULL;
+       /**
+        * URL querystring.
+        * 
+        * @public
+        * @var string
+        */
+       public $query = NULL;
+       /**
+        * URL fragment (hash).
+        * 
+        * @public
+        * @var string
+        */
+       public $fragment = NULL;
+       /**
+        * Create an instance of an http\Url.
+        * 
+        * > ***NOTE:***
+        * > Prior to v3.0.0, the default for the $flags parameter was http\Url::FROM_ENV.
+        * 
+        * See also http\Env\Url.
+        * 
+        * @param mixed $old_url Initial URL parts. Either an array, object, http\Url instance or string to parse.
+        * @param mixed $new_url Overriding URL parts. Either an array, object, http\Url instance or string to parse.
+        * @param int $flags The modus operandi of constructing the url. See http\Url constants.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\BadUrlException
+        */
+       function __construct($old_url = NULL, $new_url = NULL, int $flags = 0) {}
+       /**
+        * String cast handler. Alias of http\Url::toString().
+        * 
+        * @return string the URL as string.
+        */
+       function __toString() {}
+       /**
+        * Clone this URL and apply $parts to the cloned URL.
+        * 
+        * > ***NOTE:***
+        * > This method returns a clone (copy) of this instance.
+        * 
+        * @param mixed $parts New URL parts.
+        * @param int $flags Modus operandi of URL construction. See http\Url constants.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\BadUrlException
+        * @return \http\Url clone.
+        */
+       function mod($parts, int $flags = \http\Url::JOIN_PATH|http\Url::JOIN_QUERY|http\Url::SANITIZE_PATH) {}
+       /**
+        * Retrieve the URL parts as array.
+        * 
+        * @return array the URL parts.
+        */
+       function toArray() {}
+       /**
+        * Get the string prepresentation of the URL.
+        * 
+        * @return string the URL as string.
+        */
+       function toString() {}
+}
+/**
+ * The http\Client\Curl namespace holds option value constants specific to the curl driver of the http\Client.
+ * 
+ * Head down for the [list of available request options](http/Client/Curl#Options:) as well as the
+ * [list of available client configuration options](http/Client/Curl#Configuration:).
+ */
+namespace http\Client\Curl;
+/**
+ * Bitmask of available libcurl features.
+ *   See http\Client\Curl\Features namespace.
+ */
+const FEATURES = 12568477;
+/**
+ * List of library versions of or linked into libcurl,
+ *   e.g. "libcurl/7.50.0 OpenSSL/1.0.2h zlib/1.2.8 libidn/1.32 nghttp2/1.12.0".
+ *   See http\Client\Curl\Versions namespace.
+ */
+const VERSIONS = 'libcurl/7.68.0 OpenSSL/1.1.1f zlib/1.2.11 brotli/1.0.7 libidn2/2.2.0 libpsl/0.21.0 (+libidn2/2.2.0) libssh/0.9.3/openssl/zlib nghttp2/1.40.0 librtmp/2.3';
+/**
+ * Use HTTP/1.0 protocol version.
+ */
+const HTTP_VERSION_1_0 = 1;
+/**
+ * Use HTTP/1.1 protocol version.
+ */
+const HTTP_VERSION_1_1 = 2;
+/**
+ * Attempt to use HTTP/2 protocol version. Available if libcurl is v7.33.0 or more recent and was built with nghttp2 support.
+ */
+const HTTP_VERSION_2_0 = 3;
+/**
+ * Attempt to use version 2 for HTTPS, version 1.1 for HTTP. Available if libcurl is v7.47.0 or more recent and was built with http2 support.
+ */
+const HTTP_VERSION_2TLS = 4;
+/**
+ * Declare prior knowledge that the server supports plain (non-TLS) HTTP/2. Available if libcurl is v7.49.0 or more recent and was built with http2 support.
+ */
+const HTTP_VERSION_2_PRIOR_KNOWLEDGE = 5;
+/**
+ * Force usage of HTTP/3. See also http\Client\Curl::$altsvc. Available if libcurl is v7.66.0 or more recent.
+ */
+const HTTP_VERSION_3 = 30;
+/**
+ * Use any HTTP protocol version.
+ */
+const HTTP_VERSION_ANY = 0;
+/**
+ * Use TLS v1.0 encryption. Available if libcurl is v7.34.0 or more recent.
+ */
+const SSL_VERSION_TLSv1_0 = 4;
+/**
+ * Use TLS v1.1 encryption. Available if libcurl is v7.34.0 or more recent.
+ */
+const SSL_VERSION_TLSv1_1 = 5;
+/**
+ * Use TLS v1.2 encryption. Available if libcurl is v7.34.0 or more recent.
+ */
+const SSL_VERSION_TLSv1_2 = 6;
+/**
+ * Use TLS v1.3 encryption. Available if libcurl is v7.52.0 or more recent.
+ */
+const SSL_VERSION_TLSv1_3 = 7;
+/**
+ * Use any TLS v1 encryption.
+ */
+const SSL_VERSION_TLSv1 = 1;
+/**
+ * Use SSL v2 encryption.
+ */
+const SSL_VERSION_SSLv2 = 2;
+/**
+ * Use SSL v3 encryption.
+ */
+const SSL_VERSION_SSLv3 = 3;
+/**
+ * Use any encryption.
+ */
+const SSL_VERSION_ANY = 0;
+/**
+ * Use max default encryption. To be bitwise ORed to a http\Client\Curl\SSL_VERSION_ constant. Available if libcurl is v7.54.0 or more recent.
+ */
+const SSL_VERSION_MAX_DEFAULT = 65536;
+/**
+ * Use upt to TLS v1.0 encryption. To be bitwise ORed to a http\Client\Curl\SSL_VERSION_ constant. Available if libcurl is v7.54.0 or more recent.
+ */
+const SSL_VERSION_MAX_TLSv1_0 = 262144;
+/**
+ * Use up to TLS v1.1 encryption. To be bitwise ORed to a http\Client\Curl\SSL_VERSION_ constant. Available if libcurl is v7.54.0 or more recent.
+ */
+const SSL_VERSION_MAX_TLSv1_1 = 327680;
+/**
+ * Use up to TLS v1.2 encryption. To be bitwise ORed to a http\Client\Curl\SSL_VERSION_ constant. Available if libcurl is v7.54.0 or more recent.
+ */
+const SSL_VERSION_MAX_TLSv1_2 = 393216;
+/**
+ * Use up to TLS v1.3 encryption. To be bitwise ORed to a http\Client\Curl\SSL_VERSION_ constant. Available if libcurl is v7.54.0 or more recent.
+ */
+const SSL_VERSION_MAX_TLSv1_3 = 458752;
+/**
+ * Use TLS SRP authentication. Available if libcurl is v7.21.4 or more recent and was built with gnutls or openssl with TLS-SRP support.
+ */
+const TLSAUTH_SRP = 1;
+/**
+ * Use IPv4 resolver.
+ */
+const IPRESOLVE_V4 = 1;
+/**
+ * Use IPv6 resolver.
+ */
+const IPRESOLVE_V6 = 2;
+/**
+ * Use any resolver.
+ */
+const IPRESOLVE_ANY = 0;
+/**
+ * Don't use authentication.
+ */
+const AUTH_NONE = 0;
+/**
+ * Use Basic authentication.
+ */
+const AUTH_BASIC = 1;
+/**
+ * Use Digest authentication.
+ */
+const AUTH_DIGEST = 2;
+/**
+ * Use IE (lower v7) quirks with Digest authentication. Available if libcurl is v7.19.3 or more recent.
+ */
+const AUTH_DIGEST_IE = 16;
+/**
+ * Use NTLM authentication.
+ */
+const AUTH_NTLM = 8;
+/**
+ * Use GSS-Negotiate authentication.
+ */
+const AUTH_GSSNEG = 4;
+/**
+ * Use HTTP Netgotiate authentication (SPNEGO, RFC4559). Available if libcurl is v7.38.0 or more recent.
+ */
+const AUTH_SPNEGO = 4;
+/**
+ * Bearer authentication. Set bearer with http\Client\Curl::$xoauth2_bearer request option. Available if libcurl is v7.61.0 or more recent.
+ */
+const AUTH_BEARER = 64;
+/**
+ * Use AWS SIGv4 authentication. Available if libcurl is v7.75.0 or more recent.
+ */
+const AUTH_AWS_SIGV4 = NULL;
+/**
+ * Use any authentication.
+ */
+const AUTH_ANY = -17;
+/**
+ * Use SOCKSv4 proxy protocol.
+ */
+const PROXY_SOCKS4 = 4;
+/**
+ * Use SOCKSv4a proxy protocol.
+ */
+const PROXY_SOCKS4A = 6;
+/**
+ * Use SOCKS5h proxy protocol.
+ */
+const PROXY_SOCKS5_HOSTNAME = 7;
+/**
+ * Use SOCKS5 proxy protoccol.
+ */
+const PROXY_SOCKS5 = 5;
+/**
+ * Use HTTP/1.1 proxy protocol.
+ */
+const PROXY_HTTP = 0;
+/**
+ * Use HTTP/1.0 proxy protocol. Available if libcurl is v7.19.4 or more recent.
+ */
+const PROXY_HTTP_1_0 = 1;
+/**
+ * Keep POSTing on 301 redirects. Available if libcurl is v7.19.1 or more recent.
+ */
+const POSTREDIR_301 = 1;
+/**
+ * Keep POSTing on 302 redirects. Available if libcurl is v7.19.1 or more recent.
+ */
+const POSTREDIR_302 = 2;
+/**
+ * Keep POSTing on 303 redirects. Available if libcurl is v7.19.1 or more recent.
+ */
+const POSTREDIR_303 = 4;
+/**
+ * Keep POSTing on any redirect. Available if libcurl is v7.19.1 or more recent.
+ */
+const POSTREDIR_ALL = 7;
+/**
+ * Do only read from but not write to the Alt-Svc cache file. Available if libcurl is v7.64.1 or more recent.
+ */
+const ALTSVC_READONLYFILE = 4;
+/**
+ * Accept alternative services offered over HTTP/1.1. Available if libcurl is v7.64.1 or more recent.
+ */
+const ALTSVC_H1 = 8;
+/**
+ * Accept alternative services offered over HTTP/2. Available if libcurl is v7.64.1 or more recent.
+ */
+const ALTSVC_H2 = 16;
+/**
+ * Accept alternative services offered over HTTP/3. Available if libcurl is v7.64.1 or more recent.
+ */
+const ALTSVC_H3 = 32;
+/**
+ * Enable the cache. Available if libcurl is v7.74.0 or more recent.
+ */
+const HSTS_ENABLE = NULL;
+/**
+ * Do only read from but not write to the HSTS cache file. Available if libcurl is v7.74.0 or more recent.
+ */
+const HSTS_READONLYFILE = NULL;
+namespace http\Client;
+/**
+ * The http\Client\Request class provides an HTTP message implementation tailored to represent a request message to be sent by the client.
+ * 
+ * See http\Client::enqueue().
+ */
+class Request extends \http\Message {
+       /**
+        * Array of options for this request, which override client options.
+        * 
+        * @protected
+        * @var array
+        */
+       protected $options = NULL;
+       /**
+        * Create a new client request message to be enqueued and sent by http\Client.
+        * 
+        * @param string $meth The request method.
+        * @param string $url The request URL.
+        * @param array $headers HTTP headers.
+        * @param \http\Message\Body $body Request body.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\UnexpectedValueException
+        */
+       function __construct(string $meth = NULL, string $url = NULL, array $headers = NULL, \http\Message\Body $body = NULL) {}
+       /**
+        * Add querystring data.
+        * See http\Client\Request::setQuery() and http\Message::setRequestUrl().
+        * 
+        * @param mixed $query_data Additional querystring data.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\BadQueryStringException
+        * @return \http\Client\Request self.
+        */
+       function addQuery($query_data) {}
+       /**
+        * Add specific SSL options.
+        * See http\Client\Request::setSslOptions(), http\Client\Request::setOptions() and http\Client\Curl\$ssl options.
+        * 
+        * @param array $ssl_options Add this SSL options.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Client\Request self.
+        */
+       function addSslOptions(array $ssl_options = NULL) {}
+       /**
+        * Extract the currently set "Content-Type" header.
+        * See http\Client\Request::setContentType().
+        * 
+        * @return string|NULL string the currently set content type.
+        *               or NULL if no "Content-Type" header is set.
+        */
+       function getContentType() {}
+       /**
+        * Get priorly set options.
+        * See http\Client\Request::setOptions().
+        * 
+        * @return array options.
+        */
+       function getOptions() {}
+       /**
+        * Retrieve the currently set querystring.
+        * 
+        * @return string|NULL string the currently set querystring.
+        *               or NULL if no querystring is set.
+        */
+       function getQuery() {}
+       /**
+        * Retrieve priorly set SSL options.
+        * See http\Client\Request::getOptions() and http\Client\Request::setSslOptions().
+        * 
+        * @return array SSL options.
+        */
+       function getSslOptions() {}
+       /**
+        * Set the MIME content type of the request message.
+        * 
+        * @param string $content_type The MIME type used as "Content-Type".
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\UnexpectedValueException
+        * @return \http\Client\Request self.
+        */
+       function setContentType(string $content_type) {}
+       /**
+        * Set client options.
+        * See http\Client::setOptions() and http\Client\Curl.
+        * 
+        * Request specific options override general options which were set in the client.
+        * 
+        * > ***NOTE:***
+        * > Only options specified prior enqueueing a request are applied to the request.
+        * 
+        * @param array $options The options to set.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Client\Request self.
+        */
+       function setOptions(array $options = NULL) {}
+       /**
+        * (Re)set the querystring.
+        * See http\Client\Request::addQuery() and http\Message::setRequestUrl().
+        * 
+        * @param mixed $query_data
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\BadQueryStringException
+        * @return \http\Client\Request self.
+        */
+       function setQuery($query_data) {}
+       /**
+        * Specifically set SSL options.
+        * See http\Client\Request::setOptions() and http\Client\Curl\$ssl options.
+        * 
+        * @param array $ssl_options Set SSL options to this array.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Client\Request self.
+        */
+       function setSslOptions(array $ssl_options = NULL) {}
+}
+/**
+ * The http\Client\Response class represents an HTTP message the client returns as answer from a server to an http\Client\Request.
+ */
+class Response extends \http\Message {
+       /**
+        * Extract response cookies.
+        * Parses any "Set-Cookie" response headers into an http\Cookie list. See http\Cookie::__construct().
+        * 
+        * @param int $flags Cookie parser flags.
+        * @param array $allowed_extras List of keys treated as extras.
+        * @return array list of http\Cookie instances.
+        */
+       function getCookies(int $flags = 0, array $allowed_extras = NULL) {}
+       /**
+        * Retrieve transfer related information after the request has completed.
+        * See http\Client::getTransferInfo().
+        * 
+        * @param string $name A key to retrieve out of the transfer info.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\BadMethodCallException
+        * @throws \http\Exception\UnexpectedValueException
+        * @return object|mixed object stdClass instance with all transfer info if $name was not given.
+        *               or mixed the specific transfer info for $name.
+        */
+       function getTransferInfo(string $name = NULL) {}
+}
+namespace http\Client\Curl;
+/**
+ * Interface to an user event loop implementation for http\Client::configure()'s $use_eventloop option.
+ * 
+ * > ***NOTE:***
+ * > This interface was added in v2.6.0, resp. v3.1.0.
+ */
+interface User  {
+       /**
+        * No action.
+        */
+       const POLL_NONE = 0;
+       /**
+        * Poll for read readiness.
+        */
+       const POLL_IN = 1;
+       /**
+        * Poll for write readiness.
+        */
+       const POLL_OUT = 2;
+       /**
+        * Poll for read/write readiness.
+        */
+       const POLL_INOUT = 3;
+       /**
+        * Stop polling for activity on this descriptor.
+        */
+       const POLL_REMOVE = 4;
+       /**
+        * Initialize the event loop.
+        * 
+        * @param callable $run as function(http\Client $c, resource $s = null, int $action = http\Client\Curl\User::POLL_NONE) : int
+        *   Internal callback returning the number of unfinished requests pending.
+        * 
+        * 
+        * > ***NOTE***:
+        * > The callback should be run when a timeout occurs or a watched socket needs action.
+        */
+       function init(callable $run);
+       /**
+        * Run the loop as long as it does not block.
+        * 
+        * > ***NOTE:***
+        * > This method is called by http\Client::once(), so it does not need to have an actual implementation if http\Client::once() is never called.
+        */
+       function once();
+       /**
+        * Run the loop.
+        * 
+        * > ***NOTE:***
+        * > This method is called by http\Client::send(), so it does not need to have an actual implementation if http\Client::send() is never called.
+        */
+       function send();
+       /**
+        * Register (or deregister) a socket watcher.
+        * 
+        * @param resource $socket The socket descriptor to watch.
+        * @param int $poll http\Client\Curl\User::POLL_* constant.
+        */
+       function socket($socket, int $poll);
+       /**
+        * Register a timeout watcher.
+        * 
+        * @param int $timeout_ms Desired maximum timeout in milliseconds.
+        */
+       function timer(int $timeout_ms);
+       /**
+        * Wait/poll/select (block the loop) until events fire.
+        * 
+        * > ***NOTE:***
+        * > This method is called by http\Client::wait(), so it does not need to have an actual implementation if http\Client::wait() is never called.
+        * 
+        * @param int $timeout_ms Block for at most this milliseconds.
+        */
+       function wait(int $timeout_ms = null);
+}
+/**
+ * CURL feature constants.
+ * 
+ * > ***NOTE:***
+ * > These constants have been added in v2.6.0, resp. v3.1.0.
+ */
+namespace http\Client\Curl\Features;
+/**
+ * Whether libcurl supports asynchronous domain name resolution.
+ */
+const ASYNCHDNS = 128;
+/**
+ * Whether libcurl supports the Generic Security Services Application Program Interface. Available if libcurl is v7.38.0 or more recent.
+ */
+const GSSAPI = 131072;
+/**
+ * Whether libcurl supports HTTP Generic Security Services negotiation.
+ */
+const GSSNEGOTIATE = 32;
+/**
+ * Whether libcurl supports the HTTP/2 protocol. Available if libcurl is v7.33.0 or more recent.
+ */
+const HTTP2 = 65536;
+/**
+ * Whether libcurl supports international domain names.
+ */
+const IDN = 1024;
+/**
+ * Whether libcurl supports IPv6.
+ */
+const IPV6 = 1;
+/**
+ * Whether libcurl supports the old Kerberos protocol.
+ */
+const KERBEROS4 = 2;
+/**
+ * Whether libcurl supports the more recent Kerberos v5 protocol. Available if libcurl is v7.40.0 or more recent.
+ */
+const KERBEROS5 = 262144;
+/**
+ * Whether libcurl supports large files.
+ */
+const LARGEFILE = 512;
+/**
+ * Whether libcurl supports gzip/deflate compression.
+ */
+const LIBZ = 8;
+/**
+ * Whether libcurl supports the NT Lan Manager authentication.
+ */
+const NTLM = 16;
+/**
+ * Whether libcurl supports NTLM delegation to a winbind helper. Available if libcurl is v7.22.0 or more recent.
+ */
+const NTLM_WB = 32768;
+/**
+ * Whether libcurl supports the Public Suffix List for cookie host handling. Available if libcurl is v7.47.0 or more recent.
+ */
+const PSL = 1048576;
+/**
+ * Whether libcurl supports the Simple and Protected GSSAPI Negotiation Mechanism.
+ */
+const SPNEGO = 256;
+/**
+ * Whether libcurl supports SSL/TLS protocols.
+ */
+const SSL = 4;
+/**
+ * Whether libcurl supports the Security Support Provider Interface.
+ */
+const SSPI = 2048;
+/**
+ * Whether libcurl supports TLS Secure Remote Password authentication. Available if libcurl is v7.21.4 or more recent.
+ */
+const TLSAUTH_SRP = 16384;
+/**
+ * Whether libcurl supports connections to unix sockets. Available if libcurl is v7.40.0 or more recent.
+ */
+const UNIX_SOCKETS = 524288;
+/**
+ * CURL version constants.
+ */
+namespace http\Client\Curl\Versions;
+/**
+ * Version string of libcurl, e.g. "7.50.0".
+ */
+const CURL = '7.68.0';
+/**
+ * Version string of the SSL/TLS library, e.g. "OpenSSL/1.0.2h".
+ */
+const SSL = 'OpenSSL/1.1.1f';
+/**
+ * Version string of the zlib compression library, e.g. "1.2.8".
+ */
+const LIBZ = '1.2.11';
+/**
+ * Version string of the c-ares library, e.g. "1.11.0".
+ */
+const ARES = NULL;
+/**
+ * Version string of the IDN library, e.g. "1.32".
+ */
+const IDN = '2.2.0';
+/**
+ * Version string of the iconv library. Added in v4.1.0.
+ */
+const ICONV = NULL;
+/**
+ * Version string of the brotli library. Added in v4.1.0. Available if libcurl is v7.57.0 or more recent.
+ */
+const BROTLI = '1.0.7';
+/**
+ * Version string of nghttp2. Added in v4.1.0. Available if libcurl is v7.66.0 or more recent.
+ */
+const NGHTTP2 = '1.40.0';
+/**
+ * Version string of quiche/nghttp3. Added in v4.1.0. Available if libcurl is v7.66.0 or more recent.
+ */
+const QUIC = NULL;
+/**
+ * Default path to the certificate bundle file. Added in v4.1.0. Available if libcurl is v7.70.0 or more recent.
+ */
+const CAINFO = NULL;
+/**
+ * Default path to the certificate bundle directory. Added in v4.1.0. Available if libcurl is v7.70.0 or more recent.
+ */
+const CAPATH = NULL;
+/**
+ * Version string of the zstd library. Added in v4.1.0. Available if libcurl is v7.72.0 or more recent.
+ */
+const ZSTD = NULL;
+/**
+ * Version string of the hyper library. Added in v4.1.0. Available if libcurl is v7.75.0 or more recent.
+ */
+const HYPER = NULL;
+namespace http\Encoding;
+/**
+ * Base class for encoding stream implementations.
+ */
+abstract class Stream  {
+       /**
+        * Do no intermittent flushes.
+        */
+       const FLUSH_NONE = 0;
+       /**
+        * Flush at appropriate transfer points.
+        */
+       const FLUSH_SYNC = 1048576;
+       /**
+        * Flush at each IO operation.
+        */
+       const FLUSH_FULL = 2097152;
+       /**
+        * Base constructor for encoding stream implementations.
+        * 
+        * @param int $flags See http\Encoding\Stream and implementation specific constants.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\RuntimeException
+        */
+       function __construct(int $flags = 0) {}
+       /**
+        * Check whether the encoding stream is already done.
+        * 
+        * @return bool whether the encoding stream is completed.
+        */
+       function done() {}
+       /**
+        * Finish and reset the encoding stream.
+        * Returns any pending data.
+        * 
+        * @return string any pending data.
+        */
+       function finish() {}
+       /**
+        * Flush the encoding stream.
+        * Returns any pending data.
+        * 
+        * @return string any pending data.
+        */
+       function flush() {}
+       /**
+        * Update the encoding stream with more input.
+        * 
+        * @param string $data The data to pass through the stream.
+        * @return string processed data.
+        */
+       function update(string $data) {}
+}
+namespace http\Encoding\Stream;
+/**
+ * A [brotli](https://brotli.org) decoding stream.
+ * 
+ * > ***NOTE:***
+ * > This class has been added in v3.2.0.
+ */
+class Debrotli extends \http\Encoding\Stream {
+       /**
+        * Decode brotli encoded data.
+        * 
+        * @param string $data The data to uncompress.
+        * @return string the uncompressed data.
+        */
+       function decode(string $data) {}
+}
+/**
+ * A stream decoding data encoded with chunked transfer encoding.
+ */
+class Dechunk extends \http\Encoding\Stream {
+       /**
+        * Decode chunked encoded data.
+        * 
+        * @param string $data The data to decode.
+        * @param int $decoded_len Out parameter with the length of $data that's been decoded.
+        *   Should be ```strlen($data)``` if not truncated.
+        * @return string|string|string|false string the decoded data.
+        *               or string the unencoded data.
+        *               or string the truncated decoded data.
+        *               or false if $data cannot be decoded.
+        */
+       function decode(string $data, int &$decoded_len = 0) {}
+}
+/**
+ * A deflate stream supporting deflate, zlib and gzip encodings.
+ */
+class Deflate extends \http\Encoding\Stream {
+       /**
+        * Gzip encoding. RFC1952
+        */
+       const TYPE_GZIP = 16;
+       /**
+        * Zlib encoding. RFC1950
+        */
+       const TYPE_ZLIB = 0;
+       /**
+        * Deflate encoding. RFC1951
+        */
+       const TYPE_RAW = 32;
+       /**
+        * Default compression level.
+        */
+       const LEVEL_DEF = 0;
+       /**
+        * Least compression level.
+        */
+       const LEVEL_MIN = 1;
+       /**
+        * Greatest compression level.
+        */
+       const LEVEL_MAX = 9;
+       /**
+        * Default compression strategy.
+        */
+       const STRATEGY_DEF = 0;
+       /**
+        * Filtered compression strategy.
+        */
+       const STRATEGY_FILT = 256;
+       /**
+        * Huffman strategy only.
+        */
+       const STRATEGY_HUFF = 512;
+       /**
+        * Run-length encoding strategy.
+        */
+       const STRATEGY_RLE = 768;
+       /**
+        * Encoding with fixed Huffman codes only.
+        * 
+        * > **A note on the compression strategy:**
+        * >
+        * > The strategy parameter is used to tune the compression algorithm.
+        * >
+        * > Use the value DEFAULT_STRATEGY for normal data, FILTERED for data produced by a filter (or predictor), HUFFMAN_ONLY to force Huffman encoding only (no string match), or RLE to limit match distances to one (run-length encoding).
+        * >
+        * > Filtered data consists mostly of small values with a somewhat random distribution. In this case, the compression algorithm is tuned to compress them better. The effect of FILTERED is to force more Huffman coding and less string matching; it is somewhat intermediate between DEFAULT_STRATEGY and HUFFMAN_ONLY.
+        * >
+        * > RLE is designed to be almost as fast as HUFFMAN_ONLY, but give better compression for PNG image data.
+        * >
+        * > FIXED prevents the use of dynamic Huffman codes, allowing for a simpler decoder for special applications.
+        * >
+        * > The strategy parameter only affects the compression ratio but not the correctness of the compressed output even if it is not set appropriately.
+        * >
+        * >_Source: [zlib Manual](http://www.zlib.net/manual.html)_
+        */
+       const STRATEGY_FIXED = 1024;
+       /**
+        * Encode data with deflate/zlib/gzip encoding.
+        * 
+        * @param string $data The data to compress.
+        * @param int $flags Any compression tuning flags. See http\Encoding\Stream\Deflate and http\Encoding\Stream constants.
+        * @return string the compressed data.
+        */
+       function encode(string $data, int $flags = 0) {}
+}
+/**
+ * A [brotli](https://brotli.org) encoding stream.
+ * 
+ * > ***NOTE:***
+ * > This class has been added in v3.2.0.
+ */
+class Enbrotli extends \http\Encoding\Stream {
+       /**
+        * Default compression level.
+        */
+       const LEVEL_DEF = 4;
+       /**
+        * Least compression level.
+        */
+       const LEVEL_MIN = 1;
+       /**
+        * Greatest compression level.
+        */
+       const LEVEL_MAX = 11;
+       /**
+        * Default window bits.
+        */
+       const WBITS_DEF = 352;
+       /**
+        * Minimum window bits.
+        */
+       const WBITS_MIN = 160;
+       /**
+        * Maximum window bits.
+        */
+       const WBITS_MAX = 384;
+       /**
+        * Default compression mode.
+        */
+       const MODE_GENERIC = 0;
+       /**
+        * Compression mode for UTF-8 formatted text.
+        */
+       const MODE_TEXT = 4096;
+       /**
+        * Compression mode used in WOFF 2.0.
+        */
+       const MODE_FONT = 8192;
+       /**
+        * Encode data with brotli encoding.
+        * 
+        * @param string $data The data to compress.
+        * @param int $flags Any compression tuning flags. See http\Encoding\Stream\Enbrotli and http\Encoding\Stream constants.
+        * @return string the compressed data.
+        */
+       function encode(string $data, int $flags = 0) {}
+}
+/**
+ * A inflate stream supporting deflate, zlib and gzip encodings.
+ */
+class Inflate extends \http\Encoding\Stream {
+       /**
+        * Decode deflate/zlib/gzip encoded data.
+        * 
+        * @param string $data The data to uncompress.
+        * @return string the uncompressed data.
+        */
+       function decode(string $data) {}
+}
+namespace http\Env;
+/**
+ * The http\Env\Request class' instances represent the server's current HTTP request.
+ * 
+ * See http\Message for inherited members.
+ */
+class Request extends \http\Message {
+       /**
+        * The request's query parameters. ($_GET)
+        * 
+        * @protected
+        * @var \http\QueryString
+        */
+       protected $query = NULL;
+       /**
+        * The request's form parameters. ($_POST)
+        * 
+        * @protected
+        * @var \http\QueryString
+        */
+       protected $form = NULL;
+       /**
+        * The request's form uploads. ($_FILES)
+        * 
+        * @protected
+        * @var array
+        */
+       protected $files = NULL;
+       /**
+        * The request's cookies. ($_COOKIE)
+        * 
+        * @protected
+        * @var array
+        */
+       protected $cookie = NULL;
+       /**
+        * Create an instance of the server's current HTTP request.
+        * 
+        * Upon construction, the http\Env\Request acquires http\QueryString instances of query parameters ($\_GET) and form parameters ($\_POST).
+        * 
+        * It also compiles an array of uploaded files ($\_FILES) more comprehensive than the original $\_FILES array, see http\Env\Request::getFiles() for that matter.
+        * 
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\UnexpectedValueException
+        */
+       function __construct() {}
+       /**
+        * Retrieve an URL query value ($_GET).
+        * 
+        * See http\QueryString::get() and http\QueryString::TYPE_* constants.
+        * 
+        * @param string $name The key to retrieve the value for.
+        * @param mixed $type The type to cast the value to. See http\QueryString::TYPE_* constants.
+        * @param mixed $defval The default value to return if the key $name does not exist.
+        * @param bool $delete Whether to delete the entry from the querystring after retrieval.
+        * @return \http\QueryString|string|mixed|mixed|string \http\QueryString if called without arguments.
+        *               or string the whole querystring if $name is of zero length.
+        *               or mixed $defval if the key $name does not exist.
+        *               or mixed the querystring value cast to $type if $type was specified and the key $name exists.
+        *               or string the querystring value if the key $name exists and $type is not specified or equals http\QueryString::TYPE_STRING.
+        */
+       function getCookie(string $name = NULL, $type = NULL, $defval = NULL, bool $delete = false) {}
+       /**
+        * Retrieve the uploaded files list ($_FILES).
+        * 
+        * @return array the consolidated upload files array.
+        */
+       function getFiles() {}
+       /**
+        * Retrieve a form value ($_POST).
+        * 
+        * See http\QueryString::get() and http\QueryString::TYPE_* constants.
+        * 
+        * @param string $name The key to retrieve the value for.
+        * @param mixed $type The type to cast the value to. See http\QueryString::TYPE_* constants.
+        * @param mixed $defval The default value to return if the key $name does not exist.
+        * @param bool $delete Whether to delete the entry from the querystring after retrieval.
+        * @return \http\QueryString|string|mixed|mixed|string \http\QueryString if called without arguments.
+        *               or string the whole querystring if $name is of zero length.
+        *               or mixed $defval if the key $name does not exist.
+        *               or mixed the querystring value cast to $type if $type was specified and the key $name exists.
+        *               or string the querystring value if the key $name exists and $type is not specified or equals http\QueryString::TYPE_STRING.
+        */
+       function getForm(string $name = NULL, $type = NULL, $defval = NULL, bool $delete = false) {}
+       /**
+        * Retrieve an URL query value ($_GET).
+        * 
+        * See http\QueryString::get() and http\QueryString::TYPE_* constants.
+        * 
+        * @param string $name The key to retrieve the value for.
+        * @param mixed $type The type to cast the value to. See http\QueryString::TYPE_* constants.
+        * @param mixed $defval The default value to return if the key $name does not exist.
+        * @param bool $delete Whether to delete the entry from the querystring after retrieval.
+        * @return \http\QueryString|string|mixed|mixed|string \http\QueryString if called without arguments.
+        *               or string the whole querystring if $name is of zero length.
+        *               or mixed $defval if the key $name does not exist.
+        *               or mixed the querystring value cast to $type if $type was specified and the key $name exists.
+        *               or string the querystring value if the key $name exists and $type is not specified or equals http\QueryString::TYPE_STRING.
+        */
+       function getQuery(string $name = NULL, $type = NULL, $defval = NULL, bool $delete = false) {}
+}
+/**
+ * The http\Env\Response class' instances represent the server's current HTTP response.
+ * 
+ * See http\Message for inherited members.
+ */
+class Response extends \http\Message {
+       /**
+        * Do not use content encoding.
+        */
+       const CONTENT_ENCODING_NONE = 0;
+       /**
+        * Support "Accept-Encoding" requests with gzip and deflate encoding.
+        */
+       const CONTENT_ENCODING_GZIP = 1;
+       /**
+        * No caching info available.
+        */
+       const CACHE_NO = 0;
+       /**
+        * The cache was hit.
+        */
+       const CACHE_HIT = 1;
+       /**
+        * The cache was missed.
+        */
+       const CACHE_MISS = 2;
+       /**
+        * A request instance which overrides the environments default request.
+        * 
+        * @protected
+        * @var \http\Env\Request
+        */
+       protected $request = NULL;
+       /**
+        * The response's MIME content type.
+        * 
+        * @protected
+        * @var string
+        */
+       protected $contentType = NULL;
+       /**
+        * The response's MIME content disposition.
+        * 
+        * @protected
+        * @var string
+        */
+       protected $contentDisposition = NULL;
+       /**
+        * See http\Env\Response::CONTENT_ENCODING_* constants.
+        * 
+        * @protected
+        * @var int
+        */
+       protected $contentEncoding = NULL;
+       /**
+        * How the client should treat this response in regards to caching.
+        * 
+        * @protected
+        * @var string
+        */
+       protected $cacheControl = NULL;
+       /**
+        * A custom ETag.
+        * 
+        * @protected
+        * @var string
+        */
+       protected $etag = NULL;
+       /**
+        * A "Last-Modified" time stamp.
+        * 
+        * @protected
+        * @var int
+        */
+       protected $lastModified = NULL;
+       /**
+        * Any throttling delay.
+        * 
+        * @protected
+        * @var int
+        */
+       protected $throttleDelay = NULL;
+       /**
+        * The chunk to send every $throttleDelay seconds.
+        * 
+        * @protected
+        * @var int
+        */
+       protected $throttleChunk = NULL;
+       /**
+        * The response's cookies.
+        * 
+        * @protected
+        * @var array
+        */
+       protected $cookies = NULL;
+       /**
+        * Create a new env response message instance.
+        * 
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\UnexpectedValueException
+        */
+       function __construct() {}
+       /**
+        * Output buffer handler.
+        * Appends output data to the body.
+        * 
+        * @param string $data The data output.
+        * @param int $ob_flags Output buffering flags passed from the output buffering control layer.
+        * @return bool success.
+        */
+       function __invoke(string $data, int $ob_flags = 0) {}
+       /**
+        * Manually test the header $header_name of the environment's request for a cache hit.
+        * http\Env\Response::send() checks that itself, though.
+        * 
+        * @param string $header_name The request header to test.
+        * @return int a http\Env\Response::CACHE_* constant.
+        */
+       function isCachedByEtag(string $header_name = "If-None-Match") {}
+       /**
+        * Manually test the header $header_name of the environment's request for a cache hit.
+        * http\Env\Response::send() checks that itself, though.
+        * 
+        * @param string $header_name The request header to test.
+        * @return int a http\Env\Response::CACHE_* constant.
+        */
+       function isCachedByLastModified(string $header_name = "If-Modified-Since") {}
+       /**
+        * Send the response through the SAPI or $stream.
+        * Flushes all output buffers.
+        * 
+        * @param resource $stream A writable stream to send the response through.
+        * @return bool success.
+        */
+       function send($stream = NULL) {}
+       /**
+        * Make suggestions to the client how it should cache the response.
+        * 
+        * @param string $cache_control (A) "Cache-Control" header value(s).
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Env\Response self.
+        */
+       function setCacheControl(string $cache_control) {}
+       /**
+        * Set the reponse's content disposition parameters.
+        * 
+        * @param array $disposition_params MIME content disposition as http\Params array.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Env\Response self.
+        */
+       function setContentDisposition(array $disposition_params) {}
+       /**
+        * Enable support for "Accept-Encoding" requests with deflate or gzip.
+        * The response will be compressed if the client indicates support and wishes that.
+        * 
+        * @param int $content_encoding See http\Env\Response::CONTENT_ENCODING_* constants.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Env\Response self.
+        */
+       function setContentEncoding(int $content_encoding) {}
+       /**
+        * Set the MIME content type of the response.
+        * 
+        * @param string $content_type The response's content type.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Env\Response self.
+        */
+       function setContentType(string $content_type) {}
+       /**
+        * Add cookies to the response to send.
+        * 
+        * @param mixed $cookie The cookie to send.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\UnexpectedValueException
+        * @return \http\Env\Response self.
+        */
+       function setCookie($cookie) {}
+       /**
+        * Override the environment's request.
+        * 
+        * @param \http\Message $env_request The overriding request message.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Env\Response self.
+        */
+       function setEnvRequest(\http\Message $env_request) {}
+       /**
+        * Set a custom ETag.
+        * 
+        * > ***NOTE:***
+        * > This will be used for caching and pre-condition checks.
+        * 
+        * @param string $etag A ETag.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Env\Response self.
+        */
+       function setEtag(string $etag) {}
+       /**
+        * Set a custom last modified time stamp.
+        * 
+        * > ***NOTE:***
+        * > This will be used for caching and pre-condition checks.
+        * 
+        * @param int $last_modified A unix timestamp.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return \http\Env\Response self.
+        */
+       function setLastModified(int $last_modified) {}
+       /**
+        * Enable throttling.
+        * Send $chunk_size bytes every $delay seconds.
+        * 
+        * > ***NOTE:***
+        * > If you need throttling by regular means, check for other options in your stack, because this method blocks the executing process/thread until the response has completely been sent.
+        * 
+        * @param int $chunk_size Bytes to send.
+        * @param float $delay Seconds to sleep.
+        * @return \http\Env\Response self.
+        */
+       function setThrottleRate(int $chunk_size, float $delay = 1) {}
+}
+/**
+ * URL class using the HTTP environment by default.
+ * 
+ * > ***NOTE:***
+ * > This class has been added in v3.0.0.
+ * 
+ * Always adds http\Url::FROM_ENV to the $flags constructor argument. See also http\Url.
+ */
+class Url extends \http\Url {
+}
+namespace http\Exception;
+/**
+ * A bad conversion (e.g. character conversion) was encountered.
+ */
+class BadConversionException extends \DomainException implements \http\Exception {
+}
+/**
+ * A bad HTTP header was encountered.
+ */
+class BadHeaderException extends \DomainException implements \http\Exception {
+}
+/**
+ * A bad HTTP message was encountered.
+ */
+class BadMessageException extends \DomainException implements \http\Exception {
+}
+/**
+ * A method was called on an object, which was in an invalid or unexpected state.
+ */
+class BadMethodCallException extends \BadMethodCallException implements \http\Exception {
+}
+/**
+ * A bad querystring was encountered.
+ */
+class BadQueryStringException extends \DomainException implements \http\Exception {
+}
+/**
+ * A bad HTTP URL was encountered.
+ */
+class BadUrlException extends \DomainException implements \http\Exception {
+}
+/**
+ * One or more invalid arguments were passed to a method.
+ */
+class InvalidArgumentException extends \InvalidArgumentException implements \http\Exception {
+}
+/**
+ * A generic runtime exception.
+ */
+class RuntimeException extends \RuntimeException implements \http\Exception {
+}
+/**
+ * An unexpected value was encountered.
+ */
+class UnexpectedValueException extends \UnexpectedValueException implements \http\Exception {
+}
+namespace http\Header;
+/**
+ * The parser which is underlying http\Header and http\Message.
+ * 
+ * > ***NOTE:***
+ * > This class has been added in v2.3.0.
+ */
+class Parser  {
+       /**
+        * Finish up parser at end of (incomplete) input.
+        */
+       const CLEANUP = 1;
+       /**
+        * Parse failure.
+        */
+       const STATE_FAILURE = -1;
+       /**
+        * Expecting HTTP info (request/response line) or headers.
+        */
+       const STATE_START = 0;
+       /**
+        * Expecting a key or already parsing a key.
+        */
+       const STATE_KEY = 1;
+       /**
+        * Expecting a value or already parsing the value.
+        */
+       const STATE_VALUE = 2;
+       /**
+        * At EOL of an header, checking whether a folded header line follows.
+        */
+       const STATE_VALUE_EX = 3;
+       /**
+        * A header was completed.
+        */
+       const STATE_HEADER_DONE = 4;
+       /**
+        * Finished parsing the headers.
+        * 
+        * > ***NOTE:***
+        * > Most of this states won't be returned to the user, because the parser immediately jumps to the next expected state.
+        */
+       const STATE_DONE = 5;
+       /**
+        * Retrieve the current state of the parser.
+        * See http\Header\Parser::STATE_* constants.
+        * 
+        * @throws \http\Exception\InvalidArgumentException
+        * @return int http\Header\Parser::STATE_* constant.
+        */
+       function getState() {}
+       /**
+        * Parse a string.
+        * 
+        * @param string $data The (part of the) header to parse.
+        * @param int $flags Any combination of [parser flags](http/Header/Parser#Parser.flags:).
+        * @param array $header Successfully parsed headers.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return int http\Header\Parser::STATE_* constant.
+        */
+       function parse(string $data, int $flags, array &$header = NULL) {}
+       /**
+        * Parse a stream.
+        * 
+        * @param resource $stream The header stream to parse from.
+        * @param int $flags Any combination of [parser flags](http/Header/Parser#Parser.flags:).
+        * @param array $headers The headers parsed.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\UnexpectedValueException
+        * @return int http\Header\Parser::STATE_* constant.
+        */
+       function stream($stream, int $flags, array &$headers) {}
+}
+namespace http\Message;
+/**
+ * The message body, represented as a PHP (temporary) stream.
+ * 
+ * > ***NOTE:***
+ * > Currently, http\Message\Body::addForm() creates multipart/form-data bodies.
+ */
+class Body implements \Serializable {
+       /**
+        * Create a new message body, optionally referencing $stream.
+        * 
+        * @param resource $stream A stream to be used as message body.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\UnexpectedValueException
+        */
+       function __construct($stream = NULL) {}
+       /**
+        * String cast handler.
+        * 
+        * @return string the message body.
+        */
+       function __toString() {}
+       /**
+        * Add form fields and files to the message body.
+        * 
+        * > ***NOTE:***
+        * > Currently, http\Message\Body::addForm() creates "multipart/form-data" bodies.
+        * 
+        * @param array $fields List of form fields to add.
+        * @param array $files List of form files to add.
+        * 
+        * $fields must look like:
+        * 
+        *     [
+        *       "field_name" => "value",
+        *       "multi_field" => [
+        *         "value1",
+        *         "value2"
+        *       ]
+        *     ]
+        * 
+        * $files must look like:
+        * 
+        *     [
+        *       [
+        *         "name" => "field_name",
+        *         "type" => "content/type",
+        *         "file" => "/path/to/file.ext"
+        *       ], [
+        *         "name" => "field_name2",
+        *         "type" => "text/plain",
+        *         "file" => "file.ext",
+        *         "data" => "string"
+        *       ], [
+        *         "name" => "field_name3",
+        *         "type" => "image/jpeg",
+        *         "file" => "file.ext",
+        *         "data" => fopen("/home/mike/Pictures/mike.jpg","r")
+        *     ]
+        * 
+        * As you can see, a file structure must contain a "file" entry, which holds a file path, and an optional "data" entry, which may either contain a resource to read from or the actual data as string.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\RuntimeException
+        * @return \http\Message\Body self.
+        */
+       function addForm(array $fields = NULL, array $files = NULL) {}
+       /**
+        * Add a part to a multipart body.
+        * 
+        * @param \http\Message $part The message part.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\RuntimeException
+        * @return \http\Message\Body self.
+        */
+       function addPart(\http\Message $part) {}
+       /**
+        * Append plain bytes to the message body.
+        * 
+        * @param string $data The data to append to the body.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\RuntimeException
+        * @return \http\Message\Body self.
+        */
+       function append(string $data) {}
+       /**
+        * Retrieve the ETag of the body.
+        * 
+        * @return string|string|false string an Apache style ETag of inode, mtime and size in hex concatenated by hyphens if the message body stream is stat-able.
+        *               or string a content hash (which algorithm is determined by INI http.etag.mode) if the stream is not stat-able.
+        *               or false if http.etag.mode is not a known hash algorithm.
+        */
+       function etag() {}
+       /**
+        * Retrieve any boundary of the message body.
+        * See http\Message::splitMultipartBody().
+        * 
+        * @return string|NULL string the message body boundary.
+        *               or NULL if this message body has no boundary.
+        */
+       function getBoundary() {}
+       /**
+        * Retrieve the underlying stream resource.
+        * 
+        * @return resource the underlying stream.
+        */
+       function getResource() {}
+       /**
+        * Implements Serializable.
+        * Alias of http\Message\Body::__toString().
+        * 
+        * @return string serialized message body.
+        */
+       function serialize() {}
+       /**
+        * Stat size, atime, mtime and/or ctime.
+        * 
+        * @param string $field A single stat field to retrieve.
+        * @return int|object int the requested stat field.
+        *               or object stdClass instance holding all four stat fields.
+        */
+       function stat(string $field = NULL) {}
+       /**
+        * Stream the message body through a callback.
+        * 
+        * @param callable $callback The callback of the form function(http\Message\Body $from, string $data).
+        * @param int $offset Start to stream from this offset.
+        * @param int $maxlen Stream at most $maxlen bytes, or all if $maxlen is less than 1.
+        * @return \http\Message\Body self.
+        */
+       function toCallback(callable $callback, int $offset = 0, int $maxlen = 0) {}
+       /**
+        * Stream the message body into another stream $stream, starting from $offset, streaming $maxlen at most.
+        * 
+        * @param resource $stream The resource to write to.
+        * @param int $offset The starting offset.
+        * @param int $maxlen The maximum amount of data to stream. All content if less than 1.
+        * @return \http\Message\Body self.
+        */
+       function toStream($stream, int $offset = 0, int $maxlen = 0) {}
+       /**
+        * Retrieve the message body serialized to a string.
+        * Alias of http\Message\Body::__toString().
+        * 
+        * @return string message body.
+        */
+       function toString() {}
+       /**
+        * Implements Serializable.
+        * 
+        * @param string $serialized The serialized message body.
+        */
+       function unserialize($serialized) {}
+}
+/**
+ * The parser which is underlying http\Message.
+ * 
+ * > ***NOTE:***
+ * > This class was added in v2.2.0.
+ */
+class Parser  {
+       /**
+        * Finish up parser at end of (incomplete) input.
+        */
+       const CLEANUP = 1;
+       /**
+        * Soak up the rest of input if no entity length is deducible.
+        */
+       const DUMB_BODIES = 2;
+       /**
+        * Redirect messages do not contain any body despite of indication of such.
+        */
+       const EMPTY_REDIRECTS = 4;
+       /**
+        * Continue parsing while input is available.
+        */
+       const GREEDY = 8;
+       /**
+        * Parse failure.
+        */
+       const STATE_FAILURE = -1;
+       /**
+        * Expecting HTTP info (request/response line) or headers.
+        */
+       const STATE_START = 0;
+       /**
+        * Parsing headers.
+        */
+       const STATE_HEADER = 1;
+       /**
+        * Completed parsing headers.
+        */
+       const STATE_HEADER_DONE = 2;
+       /**
+        * Parsing the body.
+        */
+       const STATE_BODY = 3;
+       /**
+        * Soaking up all input as body.
+        */
+       const STATE_BODY_DUMB = 4;
+       /**
+        * Reading body as indicated by `Content-Length` or `Content-Range`.
+        */
+       const STATE_BODY_LENGTH = 5;
+       /**
+        * Parsing `chunked` encoded body.
+        */
+       const STATE_BODY_CHUNKED = 6;
+       /**
+        * Finished parsing the body.
+        */
+       const STATE_BODY_DONE = 7;
+       /**
+        * Updating Content-Length based on body size.
+        */
+       const STATE_UPDATE_CL = 8;
+       /**
+        * Finished parsing the message.
+        * 
+        * > ***NOTE:***
+        * > Most of this states won't be returned to the user, because the parser immediately jumps to the next expected state.
+        */
+       const STATE_DONE = 9;
+       /**
+        * Retrieve the current state of the parser.
+        * See http\Message\Parser::STATE_* constants.
+        * 
+        * @throws \http\Exception\InvalidArgumentException
+        * @return int http\Message\Parser::STATE_* constant.
+        */
+       function getState() {}
+       /**
+        * Parse a string.
+        * 
+        * @param string $data The (part of the) message to parse.
+        * @param int $flags Any combination of [parser flags](http/Message/Parser#Parser.flags:).
+        * @param \http\Message $message The current state of the message parsed.
+        * @throws \http\Exception\InvalidArgumentException
+        * @return int http\Message\Parser::STATE_* constant.
+        */
+       function parse(string $data, int $flags, \http\Message &$message) {}
+       /**
+        * Parse a stream.
+        * 
+        * @param resource $stream The message stream to parse from.
+        * @param int $flags Any combination of [parser flags](http/Message/Parser#Parser.flags:).
+        * @param \http\Message $message The current state of the message parsed.
+        * @throws \http\Exception\InvalidArgumentException
+        * @throws \http\Exception\UnexpectedValueException
+        * @return int http\Message\Parser::STATE_* constant.
+        */
+       function stream($stream, int $flags, \http\Message &$message) {}
+}
index 6dc12b4a1c32422cd897f69fe82618d9174b4623..8e4f04cecce318581066c1d49c373e8ebf53c495 100644 (file)
@@ -4,19 +4,20 @@ namespace seekat;
 
 use Countable;
 use Generator;
-use http\{
-       Client, Client\Request, Message\Body, QueryString, Url
-};
+use http\{Client, Client\Request, Message\Body, QueryString, Url};
+use Iterator;
 use IteratorAggregate;
-use Psr\Log\{
-       LoggerInterface, NullLogger
-};
-use seekat\API\{
-       Call, Consumer, ContentType, Future, Iterator, Links
-};
+use Psr\Log\{LoggerInterface, NullLogger};
+use seekat\API\{Call, Consumer, ContentType, Future, Links};
 use seekat\Exception\InvalidArgumentException;
 
 class API implements IteratorAggregate, Countable {
+       /**
+        * API version
+        * @var int
+        */
+       private $version = 3;
+
        /**
         * The current API endpoint URL
         * @var Url
@@ -74,6 +75,8 @@ class API implements IteratorAggregate, Countable {
        /**
         * Create a new API endpoint root
         *
+        * @codeCoverageIgnore
+        *
         * @param Future $future pretending to fulfill promises
         * @param array $headers Standard request headers, defaults to ["Accept" => "application/vnd.github.v3+json"]
         * @param Url $url The API's endpoint, defaults to https://api.github.com
@@ -87,8 +90,9 @@ class API implements IteratorAggregate, Countable {
                $this->logger = $log ?? new NullLogger;
                $this->url = $url ?? new Url("https://api.github.com");
                $this->client = $client ?? new Client;
+               $this->type = new ContentType($this->version, "json");
                $this->headers = (array) $headers + [
-                       "Accept" => "application/vnd.github.v3+json"
+                       "Accept" => $this->type->getContentType()
                ];
        }
 
@@ -114,6 +118,7 @@ class API implements IteratorAggregate, Countable {
                                (string) $this->url,
                                (string) $that->url
                        ],
+                       "data" => $that->data
                ]);
 
                return $that;
@@ -157,7 +162,7 @@ class API implements IteratorAggregate, Countable {
                $this->logger->debug(__FUNCTION__);
 
                $consumer = new Consumer($this->getFuture(), function() {
-                       $this->client->send();
+                               $this->client->send();
                });
 
                invoke:
@@ -188,12 +193,7 @@ class API implements IteratorAggregate, Countable {
         * @return string
         */
        function __toString() : string {
-               if (is_scalar($this->data)) {
-                       return (string) $this->data;
-               }
-
-               /* FIXME */
-               return json_encode($this->data);
+               return (string) $this->type->encode($this->data);
        }
 
        /**
@@ -202,7 +202,15 @@ class API implements IteratorAggregate, Countable {
         * @return Iterator
         */
        function getIterator() : Iterator {
-               return new Iterator($this);
+               foreach ($this->data as $key => $cur) {
+                       if ($this->__get($key)->exists("url", $url)) {
+                               $url = new Url($url);
+                               $val = $this->withUrl($url)->withData($cur);
+                       } else {
+                               $val = $this->__get($key)->withData($cur);
+                       }
+                       yield $key => $val;
+               }
        }
 
        /**
@@ -211,7 +219,20 @@ class API implements IteratorAggregate, Countable {
         * @return int
         */
        function count() : int {
-               return count($this->data);
+               if (is_array($this->data)) {
+                       $count = count($this->data);
+               } else if ($this->data instanceof Countable) {
+                       $count = count($this->data);
+               } else if (is_object($this->data)) {
+                       $count = count((array) $this->data);
+               } else {
+                       $count = 0;
+               }
+               $this->logger->debug("count()", [
+                       "of type" => typeof($this->data),
+                       "count" => $count
+               ]);
+               return $count;
        }
 
        /**
@@ -258,6 +279,13 @@ class API implements IteratorAggregate, Countable {
                return $this->links;
        }
 
+       /**
+        * @return int
+        */
+       function getVersion() : int {
+               return $this->version;
+       }
+
        /**
         * Export the endpoint's underlying data
         *
@@ -341,7 +369,11 @@ class API implements IteratorAggregate, Countable {
         * @return API clone
         */
        function as(string $type, bool $keepdata = true) : API {
-               $that = ContentType::apply($this, $type);
+               $ct = new ContentType($this->version, $type);
+
+               $that = $ct->apply($this);
+               $that->type = $ct;
+
                if (!$keepdata) {
                        $that->data = null;
                }
@@ -454,7 +486,7 @@ class API implements IteratorAggregate, Countable {
                        $seg, typeof($this->data, false), $exists ? "true" : "false"
                ), [
                        "url" => (string) $this->url,
-                       "val" => $val,
+                       "val" => typeof($val, false),
                ]);
 
                return $exists;
@@ -485,10 +517,7 @@ class API implements IteratorAggregate, Countable {
 
                $url = $this->url->mod(["query" => new QueryString($args)]);
                $request = new Request($method, $url, ((array) $headers) + $this->headers,
-                        $body = is_array($body) ? json_encode($body) : (
-                               is_resource($body) ? new Body($body) : (
-                                       is_scalar($body) ? (new Body)->append($body) :
-                                               $body)));
+                        $body = $this->type->encode(is_resource($body) ? new Body($body) : $body));
 
                $this->logger->info("request -> deferred", [
                        "method" => $method,
index 8b0f62fd726d67ca30f4c6dcbacfc57469d55d41..38b1559f6172b2bffe7d28586c44e0c8d09ecc96 100644 (file)
@@ -5,8 +5,7 @@ namespace seekat\API;
 use http\Url;
 use seekat\API;
 
-final class Call
-{
+final class Call {
        /**
         * @var API
         */
@@ -24,7 +23,7 @@ final class Call
 
        function __invoke(array $args) {
                if ($this->api->exists($this->call."_url", $url)) {
-                       $url = new Url(uri_template($url, (array)current($args)));
+                       $url = new Url(uri_template($url, (array) current($args)));
                        $promise = $this->api->withUrl($url)->get(...$args);
                } else {
                        $promise = $this->api->{$this->call}->get(...$args);
index b6d062c7a69810e0a09cd9229a1155b92add50f5..3947259b51d5a09ef57a7157599bc3c6d50f3e9c 100644 (file)
@@ -9,8 +9,7 @@ use seekat\API\Call\Cache\Service;
 use seekat\API\Call\Cache\Service\Hollow;
 
 
-final class Cache
-{
+final class Cache {
        /**
         * @var Service
         */
@@ -68,7 +67,37 @@ final class Cache
                        }
                        return false;
                }
+               $response->setHeader("X-Cache-Hit", $response->getHeader("X-Cache-Hit") + 1);
                return true;
        }
 
+       /**
+        * Update call data
+        * @param Request $request
+        * @param Response $response
+        * @return bool
+        */
+       public function update(Request $request, Response &$response) : bool {
+               $ctl = new Control($request);
+               if (!$ctl->isValid()) {
+                       return false;
+               }
+
+               if ($response->getResponseCode() !== 304) {
+                       return $this->save($request, $response);
+               }
+
+               /** @var Response $cached */
+               if (!$this->cache->fetch($ctl->getKey(), $cached)) {
+                       return $this->save($request, $response);
+               }
+
+               if ($response->getHeader("ETag") !== $cached->getHeader("ETag")) {
+                       return $this->save($request, $response);
+               }
+
+               $cached->setHeader("X-Cache-Update", $cached->getHeader("X-Cache-Update") + 1);
+               $response = $cached;
+               return true;
+       }
 }
index 99871d669b1b6f713e43a40e7caf7ec496685a2a..7ebac9da53b063c6aa1af463ae82343623ab01a1 100644 (file)
@@ -9,8 +9,7 @@ use http\Params;
 use http\QueryString;
 use http\Url;
 
-final class Control
-{
+final class Control {
        /**
         * @var string
         */
index 0b801552f8cdb2c3f193244956ca81f69c6be764..31db9e99d7b3c04095218e46f545d3eca7ad241b 100644 (file)
@@ -4,9 +4,9 @@ namespace seekat\API\Call\Cache;
 
 use http\Client\Response;
 
-interface Service
-{
+interface Service {
        function fetch(string $key, Response &$response = null) : bool;
        function store(string $key, Response $response) : bool;
+       function del(string $key);
        function clear();
 }
index 47175ff20e3e3b6070104035f21215d233fcce5f..f08d6d65b919bb114c4263f489719aa8f723f682 100644 (file)
@@ -5,8 +5,7 @@ namespace seekat\API\Call\Cache\Service;
 use http\Client\Response;
 use seekat\API\Call\Cache\Service;
 
-final class Hollow implements Service
-{
+final class Hollow implements Service {
        private $storage = [];
 
        public function fetch(string $key, Response &$response = null) : bool {
@@ -22,6 +21,10 @@ final class Hollow implements Service
                return true;
        }
 
+       public function del(string $key) {
+               unset($this->storage[$key]);
+       }
+
        public function clear() {
                $this->storage = [];
        }
index 63f12de84fc9d7971c0265a62e9c1c3f8c3bf2e2..264cfbd173ea4414eaeeb36324333ab38a0f3c29 100644 (file)
@@ -7,8 +7,7 @@ use Psr\Cache\CacheItemInterface;
 use Psr\Cache\CacheItemPoolInterface;
 use seekat\API\Call\Cache\Service;
 
-final class ItemPool implements Service
-{
+final class ItemPool implements Service {
        /**
         * @var CacheItemPoolInterface
         */
@@ -23,6 +22,12 @@ final class ItemPool implements Service
                $this->cache = $cache;
        }
 
+       /**
+        * @param string $key
+        * @param Response|null $response
+        * @return bool
+        * @throws \Psr\Cache\InvalidArgumentException
+        */
        public function fetch(string $key, Response &$response = null) : bool {
                $this->item = $this->cache->getItem($key);
                if ($this->item->isHit()) {
@@ -37,6 +42,14 @@ final class ItemPool implements Service
                return $this->cache->save($this->item);
        }
 
+       /**
+        * @param string $key
+        * @throws \Psr\Cache\InvalidArgumentException
+        */
+       public function del(string $key) {
+               $this->cache->deleteItem($key);
+       }
+
        public function clear() {
                $this->cache->clear();
        }
index 901d7aa4d8e46b6205714d75e3fefb2e20931664..4358f1f2f17f37233a6df9a612aa206e1f728227 100644 (file)
@@ -6,8 +6,7 @@ use http\Client\Response;
 use Psr\SimpleCache\CacheInterface;
 use seekat\API\Call\Cache\Service;
 
-final class Simple implements Service
-{
+final class Simple implements Service {
        /**
         * @var CacheInterface
         */
@@ -26,6 +25,10 @@ final class Simple implements Service
                return $this->cache->set($key, $response);
        }
 
+       public function del(string $key) {
+               $this->cache->delete($key);
+       }
+
        public function clear() {
                $this->cache->clear();
        }
index 304ec363bf1d7acdbf59ecf80b2e418c12b217a8..bfe8c27e4da46dacbcb9f558615a21a7a1ee02ad 100644 (file)
@@ -2,14 +2,11 @@
 
 namespace seekat\API\Call;
 
-use http\{
-       Client, Client\Request, Client\Response
-};
+use http\{Client, Client\Request, Client\Response};
 use Psr\Log\LoggerInterface;
 use seekat\API;
 
-final class Deferred
-{
+final class Deferred {
        /**
         * The response importer
         *
@@ -65,11 +62,6 @@ final class Deferred
         */
        private $reject;
 
-       /**
-        * @var \Closure
-        */
-       private $update;
-
        /**
         * Create a deferred promise for the response of $request
         *
@@ -100,51 +92,93 @@ final class Deferred
        }
 
        function __invoke() {
-               if ($this->cache->load($this->request, $cached)) {
-                       $this->logger->info("deferred -> cached", [
-                               "method" => $this->request->getRequestMethod(),
-                               "url" => $this->request->getRequestUrl(),
-                       ]);
+               if (!$this->cached($cached)) {
+                       $this->refresh($cached);
+               }
 
-                       $this->response = $cached;
-                       $this->complete();
+               return $this->promise;
+       }
+
+       /**
+        * Peek into cache
+        *
+        * @param Response $cached
+        * @return bool
+        */
+       private function cached(Response &$cached = null) : bool {
+               $fresh = $this->cache->load($this->request, $cachedResponse);
+
+               if (!$cachedResponse) {
+                       return false;
                } else {
-                       $this->client->enqueue($this->request, function(Response $response) use($cached) {
-                               if ($response->getResponseCode() == 304) {
-                                       $this->response = $cached;
-                               } else {
-                                       $this->response = $response;
-                               }
-                               $this->complete();
-                               return true;
-                       });
-                       $this->logger->info("deferred -> enqueued", [
+                       $cached = $cachedResponse;
+
+                       $this->logger->info("deferred -> cached", [
                                "method" => $this->request->getRequestMethod(),
                                "url" => $this->request->getRequestUrl(),
                        ]);
-                       /* start off */
-                       $this->client->once();
+
+
+                       if (!$fresh) {
+                               $this->logger->info("cached -> stale", [
+                                       "method" => $this->request->getRequestMethod(),
+                                       "url"    => $this->request->getRequestUrl(),
+                               ]);
+                               return false;
+                       }
                }
 
-               return $this->promise;
+               $this->response = $cached;
+               $this->complete("cached");
+               return true;
+       }
+
+       /**
+        * Refresh
+        *
+        * @param Response|null $cached
+        */
+       private function refresh(Response $cached = null) {
+               $this->client->enqueue($this->request, function(Response $response) use($cached) {
+                       $this->response = $response;
+                       $this->complete();
+                       return true;
+               });
+
+               $this->logger->info(($cached ? "stale" : "deferred") . " -> enqueued", [
+                       "method" => $this->request->getRequestMethod(),
+                       "url" => $this->request->getRequestUrl(),
+               ]);
+
+               /* start off */
+               $this->client->once();
        }
 
        /**
         * Completion callback
         */
-       private function complete() {
+       private function complete(string $by = "enqueued") {
                if ($this->response) {
-                       try {
-                               $api = ($this->result)($this->response);
-
-                               $this->cache->save($this->request, $this->response);
+                       $this->logger->info("$by -> response", [
+                               "url"  => $this->request->getRequestUrl(),
+                               "info" => $this->response->getInfo(),
+                       ]);
 
-                               ($this->resolve)($api);
+                       try {
+                               $this->cache->update($this->request, $this->response);
+                               ($this->resolve)(($this->result)($this->response));
                        } catch (\Throwable $e) {
                                ($this->reject)($e);
                        }
                } else {
-                       ($this->reject)($this->client->getTransferInfo($this->request)->error);
+                       $info = $this->client->getTransferInfo($this->request);
+
+                       $this->logger->warning("$by -> no response", [
+                               "url"  => $this->request->getRequestUrl(),
+                               "info" => $info
+                       ]);
+
+                       ($this->reject)($info->error);
                }
        }
 
index 6e1d8f818f0251196d7d6cf158b6a76917ee485a..c1cee55e6a32edcb02ee6304ee37f38699afde48 100644 (file)
@@ -7,25 +7,31 @@ use http\Header;
 use seekat\API;
 use seekat\Exception\RequestException;
 
-final class Result
-{
+final class Result {
        private $api;
 
        function __construct(API $api) {
                $this->api = $api;
        }
 
+       /**
+        * @param Response $response
+        * @return API
+        * @throws RequestException
+        */
        function __invoke(Response $response) : API {
-               $hit = $response->getHeader("X-Cache-Time") ? "cached" : "enqueued";
-               $this->api->getLogger()->info("$hit -> response", [
-                       "url"  => (string) $this->api->getUrl(),
-                       "info" => $response->getInfo(),
-               ]);
-
                $links = $this->checkResponseMeta($response);
                $type = $this->checkResponseType($response);
                $data = $this->checkResponseBody($response, $type);
 
+               $this->api->getLogger()->info("response -> info", [
+                       "type" => $type->getType(),
+                       "links" => $links->getRelations(),
+               ]);
+               $this->api->getLogger()->debug("response -> data", [
+                       "data" => $data,
+               ]);
+
                return $this->api = $this->api->with(compact("type", "data", "links"));
        }
 
@@ -52,6 +58,11 @@ final class Result
                return new API\Links($link);
        }
 
+       /**
+        * @param Response $response
+        * @return API\ContentType
+        * @throws RequestException
+        */
        private function checkResponseType(Response $response) {
                if (!($type = $response->getHeader("Content-Type", Header::class))) {
                        $e = new RequestException($response);
@@ -63,12 +74,18 @@ final class Result
                        throw $e;
                }
 
-               return new API\ContentType($type);
+               return new API\ContentType($this->api->getVersion(), $type->value);
        }
 
+       /**
+        * @param Response $response
+        * @param API\ContentType $type
+        * @return mixed
+        * @throws \Exception
+        */
        private function checkResponseBody(Response $response, API\ContentType $type) {
                try {
-                       $data = $type->parseBody($response->getBody());
+                       $data = $type->decode($response->getBody());
                } catch (\Exception $e) {
                        $this->api->getLogger()->error("response -> error: ".$e->getMessage(), [
                                "url" => (string) $this->api->getUrl(),
index 926eb736e756f066d61d9b7db1b497dfa0b18a4e..3af614caa9355335c70278f36395f2f5449f8d17 100644 (file)
@@ -2,15 +2,12 @@
 
 namespace seekat\API;
 
+use Exception;
 use Generator;
-use http\Client;
 use seekat\API;
-use seekat\Exception\{
-       InvalidArgumentException, UnexpectedValueException, function exception
-};
+use seekat\Exception\{function exception};
 
-final class Consumer
-{
+final class Consumer {
        /**
         * Loop
         * @var callable
@@ -79,7 +76,6 @@ final class Consumer
         */
        function __invoke(Generator $gen) {
                $this->cancelled = false;
-
                foreach ($gen as $promise) {
                        if ($this->cancelled) {
                                break;
@@ -87,15 +83,14 @@ final class Consumer
                        $this->give($promise, $gen);
                }
 
-               #($this->loop)();
-
-               if (!$this->cancelled) {
-                       $this->result = $gen->getReturn();
-               }
-               if (isset($this->result)) {
-                       ($this->resolve)($this->result);
-               } else {
+               if ($this->cancelled) {
                        ($this->reject)("Cancelled");
+               } else {
+                       try {
+                               $this->result = $gen->getReturn();
+                       } catch (Exception $e) {
+                       }
+                       ($this->resolve)($this->result);
                }
 
                return $this->context->promise();
index d13b11a6d925a4581fefd96d203b9779c5ed4f1a..e69f775ee454ef8e9dd8fcd33b74cbad03d346a5 100644 (file)
@@ -2,36 +2,19 @@
 
 namespace seekat\API;
 
-use seekat\{
-       API, Exception\InvalidArgumentException, Exception\UnexpectedValueException
-};
-use http\{
-       Header, Message\Body
-};
-
-final class ContentType
-{
+use http\Header;
+use http\Message\Body;
+use http\Params;
+use seekat\API;
+use seekat\API\ContentType\Handler;
+use seekat\Exception\InvalidArgumentException;
+use seekat\Exception\UnexpectedValueException;
+
+final class ContentType {
        /**
-        * API version
         * @var int
         */
-       static private $version = 3;
-
-       /**
-        * Content type handler map
-        * @var array
-        */
-       static private $types = [
-               "json"          => "self::fromJson",
-               "base64"        => "self::fromBase64",
-               "sha"           => "self::fromData",
-               "raw"           => "self::fromData",
-               "html"          => "self::fromData",
-               "diff"          => "self::fromData",
-               "patch"         => "self::fromData",
-               "text/plain"=> "self::fromData",
-               "application/octet-stream" => "self::fromStream",
-       ];
+       private $version;
 
        /**
         * Content type abbreviation
@@ -39,13 +22,19 @@ final class ContentType
         */
        private $type;
 
+       /**
+        * Content type handler map
+        * @var array
+        */
+       static private $types = [];
+
        /**
         * Register a content type handler
-        * @param string $type The content type (abbreviation)
-        * @param callable $handler The handler as function(Body $body):mixed;
         */
-       static function register(string $type, callable $handler) {
-               self::$types[$type] = $handler;
+       static function register(Handler $handler) {
+               foreach ($handler->types() as $type) {
+                       self::$types[$type] = $handler;
+               }
        }
 
        /**
@@ -66,123 +55,132 @@ final class ContentType
        }
 
        /**
-        * Get/set the API version to use
-        * @param int $v if not null, update the API version
-        * @return int the previously set version
+        * API Version
+        *
+        * @param int $version
+        * @param string $type
         */
-       static function version(int $v = null) : int {
-               $api = self::$version;
-               if (isset($v)) {
-                       self::$version = $v;
+       function __construct(int $version = 3, string $type = null) {
+               $this->version = $version;
+               if (isset($type)) {
+                       $this->setContentType($this->extractTypeFromParams(new Params($type)));
                }
-               return $api;
        }
 
        /**
-        * @param API $api
-        * @param string $type
-        * @return API
+        * @param Header $contentType
         */
-       static function apply(API $api, string $type) : API {
-               $part = "[^()<>@,;:\\\"\/\[\]?.=[:space:][:cntrl:]]+";
-               if (preg_match("/^$part\/$part\$/", $type)) {
-                       $that = $api->withHeader("Accept", $type);
-               } else {
-                       switch (substr($type, 0, 1)) {
-                               case "+":
-                               case ".":
-                               case "":
-                                       break;
-                               default:
-                                       $type = ".$type";
-                                       break;
-                       }
-                       $vapi = static::version();
-                       $that = $api->withHeader("Accept", "application/vnd.github.v$vapi$type");
-               }
-               return $that;
+       function setContentTypeHeader(Header $contentType) {
+               $this->type = $this->extractTypeFromHeader($contentType);
        }
 
        /**
-        * @param Body $json
-        * @return mixed
-        * @throws UnexpectedValueException
+        * @param string $contentType
         */
-       private static function fromJson(Body $json) {
-               $decoded = json_decode($json);
-               if (!isset($decoded) && json_last_error()) {
-                       throw new UnexpectedValueException("Could not decode JSON: ".
-                               json_last_error_msg());
-               }
-               return $decoded;
+       function setContentType(string $contentType) {
+               $this->type = $this->extractType($contentType);
        }
 
        /**
-        * @param Body $base64
-        * @return string
-        * @throws UnexpectedValueException
+        * @return int
         */
-       private static function fromBase64(Body $base64) : string {
-               if (false === ($decoded = base64_decode($base64, true))) {
-                       throw new UnexpectedValueException("Could not decode BASE64");
-               }
-               return $decoded;
+       function getVersion() : int {
+               return $this->version;
        }
 
-
        /**
-        * @param Body $stream
-        * @return resource stream
+        * Get the (abbreviated) content type name
+        * @return string
         */
-       private static function fromStream(Body $stream) {
-               return $stream->getResource();
+       function getType() : string {
+               return $this->type;
        }
 
        /**
-        * @param Body $data
+        * Get the (full) content type
         * @return string
         */
-       private static function fromData(Body $data) : string {
-               return (string) $data;
+       function getContentType() : string {
+               return $this->composeType($this->type);
        }
 
        /**
-        * @param Header $contentType
-        * @throws InvalidArgumentException
+        * @param API $api
+        * @return API clone
         */
-       function __construct(Header $contentType) {
-               if (strcasecmp($contentType->name, "Content-Type")) {
-                       throw new InvalidArgumentException(
-                               "Expected Content-Type header, got ". $contentType->name);
-               }
-               $vapi = static::version();
-               $this->type = preg_replace("/
-                       (?:application\/(?:vnd\.github(?:\.v$vapi)?)?)
-                       (?|
-                                       \.                                      ([^.+]+)
-                               |       (?:\.[^.+]+)?\+?        (json)
-                       )/x", "\\1", current(array_keys($contentType->getParams()->params)));
+       function apply(API $api) : API {
+               return $api->withHeader("Accept", $this->getContentType());
        }
 
        /**
-        * Get the (abbreviated) content type name
-        * @return string
+        * Decode a response message's body according to its content type
+        * @param Body $data
+        * @return mixed
+        * @throws UnexpectedValueException
         */
-       function getType() : string {
-               return $this->type;
+       function decode(Body $data) {
+               $type = $this->getType();
+               if (static::registered($type)) {
+                       return self::$types[$type]->decode($data);
+               }
+               throw new UnexpectedValueException("Unhandled content type '$type'");
        }
 
        /**
-        * Parse a response message's body according to its content type
-        * @param Body $data
-        * @return mixed
+        * Encode a request message's body according to its content type
+        * @param mixed $data
+        * @return Body
         * @throws UnexpectedValueException
         */
-       function parseBody(Body $data) {
+       function encode($data) : Body {
                $type = $this->getType();
                if (static::registered($type)) {
-                       return call_user_func(self::$types[$type], $data, $type);
+                       return self::$types[$type]->encode($data);
                }
                throw new UnexpectedValueException("Unhandled content type '$type'");
        }
+
+       private function composeType(string $type) : string {
+               $part = "[^()<>@,;:\\\"\/\[\]?.=[:space:][:cntrl:]]+";
+               if (preg_match("/^$part\/$part\$/", $type)) {
+                       return $type;
+               }
+
+               switch (substr($type, 0, 1)) {
+                       case "+":
+                       case ".":
+                       case "":
+                               break;
+                       default:
+                               $type = ".$type";
+                               break;
+               }
+               return "application/vnd.github.v{$this->version}$type";
+       }
+
+       private function extractType(string $type) : string {
+               return preg_replace("/
+                       (?:application\/(?:vnd\.github(?:\.v{$this->version})?)?)
+                       (?|
+                                       \.                                      ([^.+]+)
+                               |       (?:\.[^.+]+)?\+?        (json)
+                       )/x", "\\1", $type);
+       }
+
+       private function extractTypeFromParams(Params $params) : string {
+               return $this->extractType(current(array_keys($params->params)));
+       }
+
+       private function extractTypeFromHeader(Header $header) : string {
+               if (strcasecmp($header->name, "Content-Type")) {
+                       throw new InvalidArgumentException(
+                               "Expected Content-Type header, got ". $header->name);
+               }
+               return $this->extractTypeFromParams($header->getParams());
+       }
 }
+
+ContentType::register(new Handler\Text);
+ContentType::register(new Handler\Json);
+ContentType::register(new Handler\Base64);
+ContentType::register(new Handler\Stream);
diff --git a/lib/API/ContentType/Handler.php b/lib/API/ContentType/Handler.php
new file mode 100644 (file)
index 0000000..d6cd7c6
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+
+namespace seekat\API\ContentType;
+
+use http\Message\Body;
+
+interface Handler {
+       /**
+        * List handled types
+        * @return array
+        */
+       function types() : array;
+
+       /**
+        * Decode HTTP message body
+        * @param Body $body
+        * @return mixed
+        */
+       function decode(Body $body);
+
+       /**
+        * Encode HTTP message body
+        * @param $data
+        * @return Body
+        */
+       function encode($data) : Body;
+}
diff --git a/lib/API/ContentType/Handler/Base64.php b/lib/API/ContentType/Handler/Base64.php
new file mode 100644 (file)
index 0000000..979c97d
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+namespace seekat\API\ContentType\Handler;
+
+use http\Message\Body;
+use seekat\API\ContentType\Handler;
+use seekat\Exception\InvalidArgumentException;
+use seekat\Exception\UnexpectedValueException;
+use function seekat\typeof;
+
+final class Base64 implements Handler {
+       /**
+        * @inheritdoc
+        */
+       function types() : array {
+               return ["base64"];
+       }
+
+       /**
+        * @inheritdoc
+        * @param string $data
+        */
+       function encode($data): Body {
+               if (!is_scalar($data)) {
+                       throw new InvalidArgumentException(
+                               "BASE64 encoding argument must be scalar, got ".typeof($data));
+               }
+               return (new Body)->append(base64_encode($data));
+       }
+
+       /**
+        * @inheritdoc
+        * @return string
+        */
+       function decode(Body $body) {
+               $data = base64_decode($body, true);
+
+               if (false === $data) {
+                       $e = error_get_last();
+                       throw new UnexpectedValueException("Could not decode BASE64: ".
+                               ($e ? $e["message"] : "Unknown error"));
+               }
+
+               return $data;
+       }
+}
diff --git a/lib/API/ContentType/Handler/Json.php b/lib/API/ContentType/Handler/Json.php
new file mode 100644 (file)
index 0000000..d5b6a58
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+
+namespace seekat\API\ContentType\Handler;
+
+use http\Message\Body;
+use seekat\API\ContentType\Handler;
+use seekat\Exception\InvalidArgumentException;
+use seekat\Exception\UnexpectedValueException;
+use function seekat\typeof;
+
+final class Json implements Handler {
+       /**
+        * @var int
+        */
+       private $flags;
+
+       /**
+        * @inheritdoc
+        */
+       function types() : array {
+               return ["json"];
+       }
+
+       /**
+        * @param int $flags json_encode() flags
+        */
+       function __construct(int $flags = 0) {
+               $this->flags = $flags;
+       }
+
+       /**
+        * @inheritdoc
+        */
+       function encode($data): Body {
+               if (is_scalar($data)) {
+                       $json = $data;
+               } else {
+                       $json = json_encode($data, $this->flags);
+               }
+
+               if (false === $json) {
+                       throw new InvalidArgumentException(
+                               "JSON encoding failed for argument ".typeof($data).
+                               " \$data; ".json_last_error_msg());
+               }
+               return (new Body)->append($json);
+       }
+
+       /**
+        * @inheritdoc
+        */
+       function decode(Body $body) {
+               $data = json_decode($body);
+               if (!isset($data) && json_last_error()) {
+                       throw new UnexpectedValueException("Could not decode JSON: ".
+                               json_last_error_msg());
+               }
+               return $data;
+       }
+}
diff --git a/lib/API/ContentType/Handler/Stream.php b/lib/API/ContentType/Handler/Stream.php
new file mode 100644 (file)
index 0000000..6a8535e
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+
+namespace seekat\API\ContentType\Handler;
+
+use http\Message\Body;
+use seekat\API\ContentType\Handler;
+
+final class Stream implements Handler {
+       /**
+        * @inheritdoc
+        */
+       function types() : array {
+               return ["application/octet-stream"];
+       }
+
+       /**
+        * @inheritdoc
+        * @param resource $data
+        */
+       function encode($data): Body {
+               return new Body($data);
+       }
+
+       /**
+        * @inheritdoc
+        * @return resource
+        */
+       function decode(Body $body) {
+               return $body->getResource();
+       }
+}
diff --git a/lib/API/ContentType/Handler/Text.php b/lib/API/ContentType/Handler/Text.php
new file mode 100644 (file)
index 0000000..25e6e80
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+
+namespace seekat\API\ContentType\Handler;
+
+use http\Message\Body;
+use seekat\API\ContentType\Handler;
+use seekat\Exception\InvalidArgumentException;
+use function seekat\typeof;
+
+final class Text implements Handler {
+       /**
+        * @inheritdoc
+        */
+       function types() : array {
+               return ["sha", "raw", "html", "diff", "patch", "text/plain"];
+       }
+
+       /**
+        * @inheritdoc
+        * @param string $data
+        */
+       function encode($data): Body {
+               if (isset($data) && !is_scalar($data)) {
+                       throw new InvalidArgumentException(
+                               "Text encoding argument must be scalar, got ".typeof($data));
+               }
+               return (new Body)->append($data);
+       }
+
+       /**
+        * @inheritdoc
+        * @return string
+        */
+       function decode(Body $body) {
+               return (string) $body;
+       }
+}
index 6032e028d42b55117661b3c6c42410965812231c..e0d36b72ad44c6bd1d50317576980bf781379bd8 100644 (file)
@@ -2,8 +2,7 @@
 
 namespace seekat\API;
 
-interface Future
-{
+interface Future {
        /**
         * @param callable $onCancel
         * @return mixed Promisor providing a promise() method
@@ -26,7 +25,7 @@ interface Future
         * @param mixed $promise
         * @return bool
         */
-       function cancelPromise($promise) : bool;
+       function cancelPromise($promise);
 
        /**
         * @param mixed $promise
index b9559128da8d7df09be7df6a9a12cc4757cbedec..3cddb221b06c1188713a7e9065cc9f3b7bed397d 100644 (file)
@@ -37,7 +37,7 @@ function reject(Future $future, $reason) {
  */
 function resolver(Future $future, $context) {
        return function($value) use($future, $context) {
-               return $future->resolve($context, $value);
+               $future->resolve($context, $value);
        };
 }
 
@@ -48,7 +48,7 @@ function resolver(Future $future, $context) {
  */
 function rejecter(Future $future, $context) {
        return function($reason) use($future, $context) {
-               return $future->reject($context, $reason);
+               $future->reject($context, $reason);
        };
 }
 
@@ -89,10 +89,9 @@ function react() {
                        return $promise->then($onResult, $onError);
                }
 
-               function cancelPromise($promise) : bool {
+               function cancelPromise($promise) {
                        /* @var $promise \React\Promise\Promise */
                        $promise->cancel();
-                       return true;
                }
 
                function resolve($context, $value) {
@@ -120,7 +119,12 @@ function amp() {
                 * @return AmpDeferred
                 */
                function createContext(callable $onCancel = null) {
-                       return new AmpDeferred();
+                       $context = new AmpDeferred();
+                       /** @noinspection PhpUndefinedFieldInspection */
+                       $context->promise()->cancel = function() use($onCancel, $context) {
+                               $onCancel();
+                       };
+                       return $context;
                }
 
                function getPromise($context) {
@@ -147,8 +151,10 @@ function amp() {
                        return $promise;
                }
 
-               function cancelPromise($promise) : bool {
-                       return false;
+               function cancelPromise($promise) {
+                       /** @var $promise AmpPromise */
+                       /** @noinspection PhpUndefinedFieldInspection */
+                       ($promise->cancel)();
                }
 
                function resolve($context, $value) {
@@ -162,7 +168,7 @@ function amp() {
                }
 
                function all($context, array $promises) {
-                       return \Amp\all($promises);
+                       return \Amp\Promise\all($promises);
                }
        };
 }
index f8b649536b8c8f38a8f786377b19023bf621f759..b2187478e3e1eb8835d53fea62d58e525ff9d879 100644 (file)
@@ -3,9 +3,10 @@
 namespace seekat\API;
 
 use http\Url;
+use Iterator as BaseIterator;
 use seekat\API;
 
-final class Iterator implements \Iterator
+class Iterator implements BaseIterator
 {
        /**
         * The endpoint
index 322eb8e847115557cacbf33e799a442da71470fa..20fbc6d846ec3226beb3ee518ade5e6161da69e3 100644 (file)
@@ -2,17 +2,11 @@
 
 namespace seekat\API;
 
+use http\{Header, Params, QueryString, Url};
 use seekat\Exception\UnexpectedValueException;
-use http\ {
-       Header,
-       Params,
-       QueryString,
-       Url
-};
 use Serializable;
 
-final class Links implements Serializable
-{
+final class Links implements Serializable {
        /**
         * Parsed "Link" relations
         * @var Params
index f2b87d78123796f487dcc08746abbf453631aa3e..8b3d4e555406e0600b9dd912be7faa44f0db22ff 100644 (file)
@@ -2,7 +2,6 @@
 
 namespace seekat\API\Links;
 
-use AsyncInterop\Promise;
 use seekat\API;
 use seekat\API\Call\Cache;
 use seekat\API\Future;
index 27c7d5382fc2f0e580889be47decdd933c84cb33..6099d4c26266cc84d6d32bf15ab930518efdd929 100644 (file)
@@ -2,6 +2,5 @@
 
 namespace seekat;
 
-interface Exception
-{
+interface Exception {
 }
index 104bdb873fd1f033c0737f74d55484bcd49f47b6..1fe37293226a88e00e00749c02b6d3c93ce652bd 100644 (file)
@@ -4,6 +4,5 @@ namespace seekat\Exception;
 
 use seekat\Exception;
 
-class InvalidArgumentException extends \InvalidArgumentException implements Exception
-{
+class InvalidArgumentException extends \InvalidArgumentException implements Exception {
 }
index 3ef263c00e8d4d72ef700eee5ca5858e15e3dc42..06bc96032b5b37b6bb3830fd7847c46bcabe8f6f 100644 (file)
@@ -2,17 +2,13 @@
 
 namespace seekat\Exception;
 
-use http\ {
-       Client\Response,
-       Header
-};
+use http\{Client\Response, Header};
 use seekat\Exception;
 
 /**
  * @code-coverage-ignore
  */
-class RequestException extends \Exception implements Exception
-{
+class RequestException extends \Exception implements Exception {
        /**
         * JSON errors
         * @var array
index 314c9c9c107518283bcffccf8c8f9a77e4dffe62..4ce502e59fc04c2d3e988894173496fb4d38fcb2 100644 (file)
@@ -4,6 +4,5 @@ namespace seekat\Exception;
 
 use seekat\Exception;
 
-class UnexpectedValueException extends \UnexpectedValueException implements Exception
-{
+class UnexpectedValueException extends \UnexpectedValueException implements Exception {
 }
index 9045cb3df3473f1f41df7a9422fb5f4a8c37b432..0c7171a0176886845395e0c8056256317e8af36d 100644 (file)
@@ -3,7 +3,7 @@
 namespace seekat\Exception;
 
 /**
- * @param $message
+ * @param string|\Throwable $message
  * @return \Throwable
  */
 function exception(&$message) : \Throwable {
@@ -18,10 +18,10 @@ function exception(&$message) : \Throwable {
 
 /**
  * Canonical error message from a string or Exception
- * @param string|Exception $error
+ * @param string|\Throwable $error
  * @return string
  */
-function message(&$error) : string {
+function message(&$error) : ?string {
        if ($error instanceof \Throwable) {
                $message = $error->getMessage();
        } else {
index fdd9ea0a02bf16a0079b8782c065f3b53063390d..9130fd40b53074d1cc46cf97605acde67e4a6b65 100644 (file)
@@ -1,21 +1,28 @@
 <?php
 
-namespace seekat;
-
-/**
- * Generate a human readable represenation of a variable
- * @param mixed $arg
- * @param bool $export whether to var_export the $arg
- * @return string
- */
-function typeof($arg, $export = false) {
-       $type = (is_object($arg)
-               ? "instance of ".get_class($arg)
-               : gettype($arg)
-       );
-       if ($export) {
-               $type .= ": ".var_export($arg, true);
+namespace {
+       if (!function_exists("uri_template")) {
+               function uri_template(string $str, array $arr = []) : string {
+                       $tpl = new \Rize\UriTemplate;
+                       return $tpl->expand($str, $arr);
+               }
        }
-       return $type;
 }
 
+namespace seekat {
+       /**
+        * Generate a human readable representation of a variable
+        * @param mixed $arg
+        * @param bool $export whether to var_export the $arg
+        * @return string
+        */
+       function typeof($arg, $export = false) {
+               $type = is_object($arg)
+                       ? "instance of ".get_class($arg)
+                       : gettype($arg);
+               if ($export) {
+                       $type .= ": ".var_export($arg, true);
+               }
+               return $type;
+       }
+}
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
new file mode 100644 (file)
index 0000000..ae40e1b
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="tests/bootstrap.php" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" stopOnError="false" stopOnFailure="false" stopOnIncomplete="false" stopOnSkipped="false" stopOnRisky="true" colors="true" printerClass="CombinedTestdoxPrinter" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
+  <coverage processUncoveredFiles="true">
+    <include>
+      <directory suffix=".php">lib</directory>
+    </include>
+    <report>
+      <clover outputFile="clover.xml"/>
+      <html outputDirectory="code-coverage-report"/>
+    </report>
+  </coverage>
+  <testsuites>
+    <testsuite name="seekat tests">
+      <directory>tests</directory>
+    </testsuite>
+  </testsuites>
+  <listeners>
+    <listener class="DebugLogListener"/>
+    <listener class="TestdoxListener"/>
+  </listeners>
+  <logging/>
+</phpunit>
index 6ceffbdf8707be3928bc962fb320fa8e1bf41967..ddf7f545c59340c26363ec915fbcfe05208b0a1c 100644 (file)
@@ -6,11 +6,11 @@ class CacheTest extends BaseTest
        use AssertSuccess;
 
        /**
-        * @var seekat\API\Cache\Service
+        * @var seekat\API\Call\Cache\Service
         */
        private $cache;
 
-       function setUp() {
+       function setUp() : void {
                $this->cache = new seekat\API\Call\Cache\Service\Hollow;
        }
 
@@ -26,7 +26,7 @@ class CacheTest extends BaseTest
                $this->assertEquals("m6w6", $m6w6->login);
                $this->assertEquals("m6w6", $m6w6_->login);
 
-               $this->assertInternalType("array", $data);
+               $this->assertIsArray($data);
                $this->assertCount(1, $data);
                $this->assertEquals($data, $this->cache->getStorage());
        }
index 4227e1ea54f1f1302167f165ce79a9cf552c0ba9..16268e93db567144e95aaf4e3233810a70c20984 100644 (file)
@@ -56,9 +56,9 @@ class CallTest extends BaseTest
                $m6w6 = $this->assertSuccess($api->users->m6w6);
                $followers = $this->assertSuccess($api->users->m6w6->followers);
                $data = $followers->export()["data"];
-               $this->assertInternalType("array", $data);
-               $this->assertInternalType("object", $data[0]);
-               $this->assertInternalType("object", $followers->{0});
+               $this->assertIsArray($data);
+               $this->assertIsObject($data[0]);
+               $this->assertIsObject($followers->{0});
                $this->assertGreaterThan(30, (string) $m6w6->followers);
                $this->assertGreaterThan(0, count($followers));
        }
@@ -71,9 +71,9 @@ class CallTest extends BaseTest
                $m6w6 = $this->assertSuccess($api->users->m6w6);
                $followers = $this->assertSuccess($m6w6->followers_url);
                $data = $followers->export()["data"];
-               $this->assertInternalType("array", $data);
-               $this->assertInternalType("object", $data[0]);
-               $this->assertInternalType("object", $followers->{0});
+               $this->assertIsArray($data);
+               $this->assertIsObject($data[0]);
+               $this->assertIsObject($followers->{0});
                $this->assertGreaterThan(30, (string) $m6w6->followers);
                $this->assertGreaterThan(0, count($followers));
        }
@@ -92,7 +92,7 @@ class CallTest extends BaseTest
                $followers = $results[0];
                $this->assertInstanceOf(API::class, $followers);
                $data = $followers->export()["data"];
-               $this->assertInternalType("array", $data);
+               $this->assertIsArray($data);
                $this->assertGreaterThan(0, count($data));
        }
 
index 2bdeb486712467b645aa03c6692a335977c269ac..3945245d0ff50af33fc62e9c7460d5caacc52317 100644 (file)
@@ -11,17 +11,17 @@ class ContentTypeTest extends BaseTest
         * @dataProvider provideAPI
         */
        function testIsAbleToApplyVersionedContentTypeToApi($api) {
-               $api = ContentType::apply($api, "json");
+               $api = (new ContentType($api->getVersion(), "json"))->apply($api);
                $this->assertEquals(
                        $this->getVersionedContentType("json")->value,
                        $api->export()["headers"]["Accept"]);
 
-               $api = ContentType::apply($api, "+raw");
+               $api = (new ContentType($api->getVersion(), "+raw"))->apply($api);
                $this->assertEquals(
                        $this->getVersionedContentType("+raw")->value,
                        $api->export()["headers"]["Accept"]);
 
-               $api = ContentType::apply($api, ".html");
+               $api = (new ContentType($api->getVersion(), ".html"))->apply($api);
                $this->assertEquals(
                        $this->getVersionedContentType(".html")->value,
                        $api->export()["headers"]["Accept"]);
@@ -32,24 +32,26 @@ class ContentTypeTest extends BaseTest
         * @dataProvider provideAPI
         */
        function testIsAbleToApplyBasicContentTypeToApi($api) {
-               $api = ContentType::apply($api, "text/plain");
+               $api = (new ContentType($api->getVersion(), "text/plain"))->apply($api);
                $this->assertEquals("text/plain", $api->export()["headers"]["Accept"]);
        }
 
-       /**
-        * @group testdox
-        */
-       function testUserCanOverrideApiVersion() {
-               $this->assertEquals(3, ContentType::version(2));
-               $this->assertEquals(2, ContentType::version(3));
-       }
-
        /**
         * @group testdox
         */
        function testAllowsToRegisterAndUnregisterContentTypeHandlers() {
                $this->assertFalse(ContentType::registered("foobar"));
-               ContentType::register("foobar", function() {});
+               ContentType::register(new class implements ContentType\Handler {
+                       function types() : array {
+                               return ["foobar"];
+                       }
+                       function encode($data): Body {
+                               return new Body;
+                       }
+                       function decode(Body $body) {
+                               return (string) $body;
+                       }
+               });
                $this->assertTrue(ContentType::registered("foobar"));
                ContentType::unregister("foobar");
                $this->assertFalse(ContentType::registered("foobar"));
@@ -59,25 +61,28 @@ class ContentTypeTest extends BaseTest
         * @group testdox
         */
        function testAcceptsContentTypeHeader() {
-               new ContentType(new Header("Content-Type"));
-               new ContentType(new Header("content-type"));
+               $ct = new ContentType;
+               $ct->setContentTypeHeader(new Header("Content-Type", "TitleCase"));
+               $this->assertSame("TitleCase", $ct->getType());
+               $ct->setContentTypeHeader(new Header("content-type", "lowercase"));
+               $this->assertSame("lowercase", $ct->getType());
        }
 
        /**
         * @group testdox
-        * @expectedException \seekat\Exception\InvalidArgumentException
         */
        function testDoesNotAcceptNonContentTypeHeader() {
-               new ContentType(new Header("ContentType"));
+               $this->expectException(\seekat\Exception\InvalidArgumentException::class);
+               (new ContentType)->setContentTypeHeader(new Header("ContentType"));
        }
 
        /**
         * @group testdox
-        * @expectedException \seekat\Exception\UnexpectedValueException
         */
        function testThrowsOnUnknownContentType() {
-               $ct = new ContentType($this->getVersionedContentType("foo"));
-               $ct->parseBody((new Body)->append("foo"));
+               $this->expectException(\seekat\Exception\UnexpectedValueException::class);
+               $ct = new ContentType(3, "foo");
+               $ct->decode((new Body)->append("foo"));
        }
 
        /**
@@ -85,8 +90,8 @@ class ContentTypeTest extends BaseTest
         */
        function testHandlesJson() {
                $this->assertTrue(ContentType::registered("json"));
-               $ct = new ContentType(new Header("Content-Type", "application/json"));
-               $result = $ct->parseBody((new Body())->append("[1,2,3]"));
+               $ct = new ContentType(3, "application/json");
+               $result = $ct->decode((new Body())->append("[1,2,3]"));
                $this->assertEquals([1, 2, 3], $result);
                return $ct;
        }
@@ -94,10 +99,10 @@ class ContentTypeTest extends BaseTest
        /**
         * @group testdox
         * @depends testHandlesJson
-        * @expectedException \seekat\Exception\UnexpectedValueException
         */
        function testThrowsOnInvalidJson(ContentType $ct) {
-               $ct->parseBody((new Body)->append("yaml:\n - data"));
+               $this->expectException(\seekat\Exception\UnexpectedValueException::class);
+               $ct->decode((new Body)->append("yaml:\n - data"));
        }
 
        /**
@@ -105,8 +110,8 @@ class ContentTypeTest extends BaseTest
         */
        function testHandlesBase64() {
                $this->assertTrue(ContentType::registered("base64"));
-               $ct = new ContentType($this->getVersionedContentType("base64"));
-               $result = $ct->parseBody((new Body())->append(base64_encode("This is a test")));
+               $ct = new ContentType(3,"base64");
+               $result = $ct->decode((new Body())->append(base64_encode("This is a test")));
                $this->assertEquals("This is a test", $result);
                return $ct;
        }
@@ -114,10 +119,10 @@ class ContentTypeTest extends BaseTest
        /**
         * @group testdox
         * @depends testHandlesBase64
-        * @expectedException \seekat\Exception\UnexpectedValueException
         */
        function testThrowsOnInvalidBase64(ContentType $ct) {
-               $ct->parseBody((new Body)->append("[1,2,3]"));
+               $this->expectException(\seekat\Exception\UnexpectedValueException::class);
+               $ct->decode((new Body)->append("[1,2,3]"));
        }
 
        /**
@@ -125,9 +130,9 @@ class ContentTypeTest extends BaseTest
         */
        function testHandlesOctetStream() {
                $this->assertTrue(ContentType::registered("application/octet-stream"));
-               $ct = new ContentType(new Header("Content-Type", "application/octet-stream"));
-               $result = $ct->parseBody((new Body)->append("This is a test"));
-               $this->assertInternalType("resource", $result);
+               $ct = new ContentType(3,"application/octet-stream");
+               $result = $ct->decode((new Body)->append("This is a test"));
+               $this->assertIsResource($result);
                rewind($result);
                $this->assertEquals("This is a test", stream_get_contents($result));
        }
@@ -137,9 +142,9 @@ class ContentTypeTest extends BaseTest
         */
        function testHandlesData() {
                $this->assertTrue(ContentType::registered("text/plain"));
-               $ct = new ContentType(new Header("Content-Type", "text/plain"));
-               $result = $ct->parseBody((new Body)->append("This is a test"));
-               $this->assertInternalType("string", $result);
+               $ct = new ContentType(3,"text/plain");
+               $result = $ct->decode((new Body)->append("This is a test"));
+               $this->assertIsString($result);
                $this->assertEquals("This is a test", $result);
        }
 
@@ -148,7 +153,7 @@ class ContentTypeTest extends BaseTest
         * @return Header Content-Type header
         */
        private function getVersionedContentType($type) {
-               switch ($type{0}) {
+               switch ($type[0]) {
                        case ".":
                        case "+":
                        case "":
@@ -157,6 +162,6 @@ class ContentTypeTest extends BaseTest
                                $type = ".$type";
                }
                return new Header("Content-Type",
-                       sprintf("application/vnd.github.v%d%s", ContentType::version(), $type));
+                       sprintf("application/vnd.github.v3%s", $type));
        }
 }
index b99fe928313ae8212b0394cbb521265cb893faff..88215b0228e1afae247ba0d6354dcb2ba7877993 100644 (file)
@@ -12,10 +12,7 @@ class ErrorsTest extends BaseTest
        function testCancellation($api) {
                $promise = $api->users->m6w6();
 
-               if (!$api->getFuture()->cancelPromise($promise)) {
-                       return;
-               }
-
+               $api->getFuture()->cancelPromise($promise);
                $this->assertCancelled($promise);
        }
 
index f25f4ade6f78aa9768691a1fef2300d0ad761950..e7d7afb621ef9958e7043184e07caf4d164bdd24 100644 (file)
@@ -43,9 +43,6 @@ function logger() : \Monolog\Logger {
 
 class BaseTest extends \PHPUnit\Framework\TestCase
 {
-       /**
-        * @return Generator
-        */
        function provideAPI() {
                $auth = \seekat\API\auth("token", getenv("GITHUB_TOKEN"));
                $headers = headers();
@@ -54,8 +51,8 @@ class BaseTest extends \PHPUnit\Framework\TestCase
                $logger = logger();
 
                return [
-                       "with ReactPHP" => [new \seekat\API(\seekat\API\Future\react(), $headers, $url, $client, $logger)],
-                       "with AmPHP"   => [new \seekat\API(\seekat\API\Future\amp(), $headers, $url, $client, $logger)],
+                       "using ReactPHP" => [new \seekat\API(\seekat\API\Future\react(), $headers, $url, $client, $logger)],
+                       "using AmPHP"   => [new \seekat\API(\seekat\API\Future\amp(), $headers, $url, $client, $logger)],
                ];
        }
 }
@@ -128,23 +125,23 @@ trait AssertFailure
        }
 }
 
-class CombinedTestdoxPrinter extends PHPUnit_TextUI_ResultPrinter
+class CombinedTestdoxPrinter extends PHPUnit\TextUI\DefaultResultPrinter
 {
-       function isTestClass(PHPUnit_Framework_TestSuite $suite) {
+       function isTestClass(PHPUnit\Framework\TestSuite $suite) {
                $suiteName = $suite->getName();
                return false === strpos($suiteName, "::")
                        && substr($suiteName, -4) === "Test";
        }
 
-       function startTestSuite(PHPUnit_Framework_TestSuite $suite) {
+       function startTestSuite(PHPUnit\Framework\TestSuite $suite) : void {
                if ($this->isTestClass($suite)) {
                        $this->column = 0;
                }
 
-               return parent::startTestSuite($suite);
+               parent::startTestSuite($suite);
        }
 
-       function endTestSuite(PHPUnit_Framework_TestSuite $suite) {
+       function endTestSuite(PHPUnit\Framework\TestSuite $suite) : void {
                /* print % progress */
                if ($this->isTestClass($suite)) {
                        if ($this->numTestsRun != $this->numTests) {
@@ -162,35 +159,82 @@ class CombinedTestdoxPrinter extends PHPUnit_TextUI_ResultPrinter
        }
 }
 
-class TestdoxListener extends PHPUnit_Util_TestDox_ResultPrinter_Text
+class TestdoxListener implements PHPUnit\Framework\TestListener
 {
        private $groups;
+       private $testdox;
 
        function __construct() {
-               parent::__construct("php://stdout", ["testdox"]);
-               $this->groups = new ReflectionProperty("PHPUnit_Util_TestDox_ResultPrinter", "groups");
+               $this->testdox = new PHPUnit\Util\TestDox\TextResultPrinter("php://stdout", ["testdox"]);
+               $this->groups = new ReflectionProperty("\PHPUnit\Util\TestDox\ResultPrinter", "groups");
                $this->groups->setAccessible(true);
        }
 
-       function startTest(PHPUnit_Framework_Test $test) {
+       function startTest(PHPUnit\Framework\Test $test) : void {
                /* always show test class, even if no testdox test */
                if ($test instanceof \PHPUnit\Framework\TestCase) {
                        if ($test->getGroups() == ["default"]) {
-                               $this->groups->setValue($this, ["default"]);
+                               $this->groups->setValue($this->testdox, ["default"]);
                        }
                }
 
-               parent::startTest($test);
-               $this->groups->setValue($this, ["testdox"]);
+               $this->testdox->startTest($test);
+               $this->groups->setValue($this->testdox, ["testdox"]);
+       }
+
+       public function addError(\PHPUnit\Framework\Test $test, Throwable $t, float $time): void
+       {
+               $this->testdox->addError($test, $t, $time);
+       }
+
+       public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, float $time): void
+       {
+               $this->testdox->addWarning($test, $e, $time);
+       }
+
+       public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, float $time): void
+       {
+               $this->testdox->addFailure($test, $e, $time);
+       }
+
+       public function addIncompleteTest(\PHPUnit\Framework\Test $test, Throwable $t, float $time): void
+       {
+               $this->testdox->addIncompleteTest($test, $t, $time);
+       }
 
+       public function addRiskyTest(\PHPUnit\Framework\Test $test, Throwable $t, float $time): void
+       {
+               $this->testdox->addRiskyTest($test, $t, $time);
+       }
+
+       public function addSkippedTest(\PHPUnit\Framework\Test $test, Throwable $t, float $time): void
+       {
+               $this->testdox->addSkippedTest($test, $t, $time);
+       }
+
+       public function startTestSuite(\PHPUnit\Framework\TestSuite $suite): void
+       {
+               $this->testdox->startTestSuite($suite);
+       }
+
+       public function endTestSuite(\PHPUnit\Framework\TestSuite $suite): void
+       {
+               $this->testdox->endTestSuite($suite);
+       }
+
+       public function endTest(\PHPUnit\Framework\Test $test, float $time): void
+       {
+               $this->testdox->endTest($test, $time);
        }
 }
 
-class DebugLogListener extends PHPUnit\Framework\BaseTestListener
+class DebugLogListener implements \PHPUnit\Framework\TestListener
 {
+       use \PHPUnit\Framework\TestListenerDefaultImplementation;
+
        private $printLog = false;
 
-       function endTest(PHPUnit_Framework_Test $test, $time) {
+       function endTest(PHPUnit\Framework\Test $test, float $time) : void {
                /* @var $handler \Monolog\Handler\FingersCrossedHandler */
                $handler = logger()->getHandlers()[0];
                if ($this->printLog) {
@@ -201,11 +245,11 @@ class DebugLogListener extends PHPUnit\Framework\BaseTestListener
                }
        }
 
-       function addError(PHPUnit_Framework_Test $test, Exception $e, $time) {
+       function addError(PHPUnit\Framework\Test $test, \Throwable $e, float $time) : void {
                $this->printLog = true;
        }
 
-       function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) {
+       function addFailure(PHPUnit\Framework\Test $test, PHPUnit\Framework\AssertionFailedError $e, float $time) : void {
                $this->printLog = true;
        }