From 2121556150be871684b5046af7cf250b8219128d Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Tue, 14 Sep 2021 10:09:37 +0200 Subject: [PATCH] PHP8 --- .editorconfig | 4 +- .gitignore | 3 + README.md | 14 +- composer.json | 18 +- examples/amploop.php | 16 +- examples/cache.php | 3 + examples/gistlog.php | 2 +- examples/watchowned.php | 46 + http.stub.php | 3424 ++++++++++++++++++++ lib/API.php | 79 +- lib/API/Call.php | 5 +- lib/API/Call/Cache.php | 33 +- lib/API/Call/Cache/Control.php | 3 +- lib/API/Call/Cache/Service.php | 4 +- lib/API/Call/Cache/Service/Hollow.php | 7 +- lib/API/Call/Cache/Service/ItemPool.php | 17 +- lib/API/Call/Cache/Service/Simple.php | 7 +- lib/API/Call/Deferred.php | 108 +- lib/API/Call/Result.php | 37 +- lib/API/Consumer.php | 25 +- lib/API/ContentType.php | 216 +- lib/API/ContentType/Handler.php | 27 + lib/API/ContentType/Handler/Base64.php | 46 + lib/API/ContentType/Handler/Json.php | 60 + lib/API/ContentType/Handler/Stream.php | 31 + lib/API/ContentType/Handler/Text.php | 37 + lib/API/Future.php | 5 +- lib/API/Future/functions.php | 22 +- lib/API/Iterator.php | 3 +- lib/API/Links.php | 10 +- lib/API/Links/functions.php | 1 - lib/Exception.php | 3 +- lib/Exception/InvalidArgumentException.php | 3 +- lib/Exception/RequestException.php | 8 +- lib/Exception/UnexpectedValueException.php | 3 +- lib/Exception/functions.php | 6 +- lib/functions.php | 39 +- phpunit.xml.dist | 22 + tests/CacheTest.php | 6 +- tests/CallTest.php | 14 +- tests/ContentTypeTest.php | 77 +- tests/ErrorsTest.php | 5 +- tests/bootstrap.php | 86 +- 43 files changed, 4228 insertions(+), 357 deletions(-) create mode 100755 examples/watchowned.php create mode 100644 http.stub.php create mode 100644 lib/API/ContentType/Handler.php create mode 100644 lib/API/ContentType/Handler/Base64.php create mode 100644 lib/API/ContentType/Handler/Json.php create mode 100644 lib/API/ContentType/Handler/Stream.php create mode 100644 lib/API/ContentType/Handler/Text.php create mode 100644 phpunit.xml.dist diff --git a/.editorconfig b/.editorconfig index 9b444ae..c3ea8c4 100644 --- a/.editorconfig +++ b/.editorconfig @@ -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 diff --git a/.gitignore b/.gitignore index ad7b598..ac182fd 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ /perf* /tmp/ /vendor/ +*.diff +*.cache +*.bak diff --git a/README.md b/README.md index cdca980..ca82ab3 100644 --- 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 diff --git a/composer.json b/composer.json index ad06cbf..0c7dab4 100644 --- a/composer.json +++ b/composer.json @@ -24,17 +24,21 @@ "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": "*" } } diff --git a/examples/amploop.php b/examples/amploop.php index 5ecdd0d..1abbaa1 100755 --- a/examples/amploop.php +++ b/examples/amploop.php @@ -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 +}); diff --git a/examples/cache.php b/examples/cache.php index 797fe60..c71af44 100755 --- a/examples/cache.php +++ b/examples/cache.php @@ -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(), [ diff --git a/examples/gistlog.php b/examples/gistlog.php index 3a1b8ec..6cdbe23 100755 --- a/examples/gistlog.php +++ b/examples/gistlog.php @@ -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 index 0000000..d1ff879 --- /dev/null +++ b/examples/watchowned.php @@ -0,0 +1,46 @@ +#!/usr/bin/env php +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 index 0000000..7a114c9 --- /dev/null +++ b/http.stub.php @@ -0,0 +1,3424 @@ + ***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) {} +} diff --git a/lib/API.php b/lib/API.php index 6dc12b4..8e4f04c 100644 --- a/lib/API.php +++ b/lib/API.php @@ -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, diff --git a/lib/API/Call.php b/lib/API/Call.php index 8b0f62f..38b1559 100644 --- a/lib/API/Call.php +++ b/lib/API/Call.php @@ -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); diff --git a/lib/API/Call/Cache.php b/lib/API/Call/Cache.php index b6d062c..3947259 100644 --- a/lib/API/Call/Cache.php +++ b/lib/API/Call/Cache.php @@ -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; + } } diff --git a/lib/API/Call/Cache/Control.php b/lib/API/Call/Cache/Control.php index 99871d6..7ebac9d 100644 --- a/lib/API/Call/Cache/Control.php +++ b/lib/API/Call/Cache/Control.php @@ -9,8 +9,7 @@ use http\Params; use http\QueryString; use http\Url; -final class Control -{ +final class Control { /** * @var string */ diff --git a/lib/API/Call/Cache/Service.php b/lib/API/Call/Cache/Service.php index 0b80155..31db9e9 100644 --- a/lib/API/Call/Cache/Service.php +++ b/lib/API/Call/Cache/Service.php @@ -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(); } diff --git a/lib/API/Call/Cache/Service/Hollow.php b/lib/API/Call/Cache/Service/Hollow.php index 47175ff..f08d6d6 100644 --- a/lib/API/Call/Cache/Service/Hollow.php +++ b/lib/API/Call/Cache/Service/Hollow.php @@ -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 = []; } diff --git a/lib/API/Call/Cache/Service/ItemPool.php b/lib/API/Call/Cache/Service/ItemPool.php index 63f12de..264cfbd 100644 --- a/lib/API/Call/Cache/Service/ItemPool.php +++ b/lib/API/Call/Cache/Service/ItemPool.php @@ -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(); } diff --git a/lib/API/Call/Cache/Service/Simple.php b/lib/API/Call/Cache/Service/Simple.php index 901d7aa..4358f1f 100644 --- a/lib/API/Call/Cache/Service/Simple.php +++ b/lib/API/Call/Cache/Service/Simple.php @@ -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(); } diff --git a/lib/API/Call/Deferred.php b/lib/API/Call/Deferred.php index 304ec36..bfe8c27 100644 --- a/lib/API/Call/Deferred.php +++ b/lib/API/Call/Deferred.php @@ -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); } } diff --git a/lib/API/Call/Result.php b/lib/API/Call/Result.php index 6e1d8f8..c1cee55 100644 --- a/lib/API/Call/Result.php +++ b/lib/API/Call/Result.php @@ -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(), diff --git a/lib/API/Consumer.php b/lib/API/Consumer.php index 926eb73..3af614c 100644 --- a/lib/API/Consumer.php +++ b/lib/API/Consumer.php @@ -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(); diff --git a/lib/API/ContentType.php b/lib/API/ContentType.php index d13b11a..e69f775 100644 --- a/lib/API/ContentType.php +++ b/lib/API/ContentType.php @@ -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 index 0000000..d6cd7c6 --- /dev/null +++ b/lib/API/ContentType/Handler.php @@ -0,0 +1,27 @@ +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 index 0000000..d5b6a58 --- /dev/null +++ b/lib/API/ContentType/Handler/Json.php @@ -0,0 +1,60 @@ +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 index 0000000..6a8535e --- /dev/null +++ b/lib/API/ContentType/Handler/Stream.php @@ -0,0 +1,31 @@ +getResource(); + } +} diff --git a/lib/API/ContentType/Handler/Text.php b/lib/API/ContentType/Handler/Text.php new file mode 100644 index 0000000..25e6e80 --- /dev/null +++ b/lib/API/ContentType/Handler/Text.php @@ -0,0 +1,37 @@ +append($data); + } + + /** + * @inheritdoc + * @return string + */ + function decode(Body $body) { + return (string) $body; + } +} diff --git a/lib/API/Future.php b/lib/API/Future.php index 6032e02..e0d36b7 100644 --- a/lib/API/Future.php +++ b/lib/API/Future.php @@ -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 diff --git a/lib/API/Future/functions.php b/lib/API/Future/functions.php index b955912..3cddb22 100644 --- a/lib/API/Future/functions.php +++ b/lib/API/Future/functions.php @@ -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); } }; } diff --git a/lib/API/Iterator.php b/lib/API/Iterator.php index f8b6495..b218747 100644 --- a/lib/API/Iterator.php +++ b/lib/API/Iterator.php @@ -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 diff --git a/lib/API/Links.php b/lib/API/Links.php index 322eb8e..20fbc6d 100644 --- a/lib/API/Links.php +++ b/lib/API/Links.php @@ -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 diff --git a/lib/API/Links/functions.php b/lib/API/Links/functions.php index f2b87d7..8b3d4e5 100644 --- a/lib/API/Links/functions.php +++ b/lib/API/Links/functions.php @@ -2,7 +2,6 @@ namespace seekat\API\Links; -use AsyncInterop\Promise; use seekat\API; use seekat\API\Call\Cache; use seekat\API\Future; diff --git a/lib/Exception.php b/lib/Exception.php index 27c7d53..6099d4c 100644 --- a/lib/Exception.php +++ b/lib/Exception.php @@ -2,6 +2,5 @@ namespace seekat; -interface Exception -{ +interface Exception { } diff --git a/lib/Exception/InvalidArgumentException.php b/lib/Exception/InvalidArgumentException.php index 104bdb8..1fe3729 100644 --- a/lib/Exception/InvalidArgumentException.php +++ b/lib/Exception/InvalidArgumentException.php @@ -4,6 +4,5 @@ namespace seekat\Exception; use seekat\Exception; -class InvalidArgumentException extends \InvalidArgumentException implements Exception -{ +class InvalidArgumentException extends \InvalidArgumentException implements Exception { } diff --git a/lib/Exception/RequestException.php b/lib/Exception/RequestException.php index 3ef263c..06bc960 100644 --- a/lib/Exception/RequestException.php +++ b/lib/Exception/RequestException.php @@ -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 diff --git a/lib/Exception/UnexpectedValueException.php b/lib/Exception/UnexpectedValueException.php index 314c9c9..4ce502e 100644 --- a/lib/Exception/UnexpectedValueException.php +++ b/lib/Exception/UnexpectedValueException.php @@ -4,6 +4,5 @@ namespace seekat\Exception; use seekat\Exception; -class UnexpectedValueException extends \UnexpectedValueException implements Exception -{ +class UnexpectedValueException extends \UnexpectedValueException implements Exception { } diff --git a/lib/Exception/functions.php b/lib/Exception/functions.php index 9045cb3..0c7171a 100644 --- a/lib/Exception/functions.php +++ b/lib/Exception/functions.php @@ -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 { diff --git a/lib/functions.php b/lib/functions.php index fdd9ea0..9130fd4 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -1,21 +1,28 @@ 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 index 0000000..ae40e1b --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,22 @@ + + + + + lib + + + + + + + + + tests + + + + + + + + diff --git a/tests/CacheTest.php b/tests/CacheTest.php index 6ceffbd..ddf7f54 100644 --- a/tests/CacheTest.php +++ b/tests/CacheTest.php @@ -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()); } diff --git a/tests/CallTest.php b/tests/CallTest.php index 4227e1e..16268e9 100644 --- a/tests/CallTest.php +++ b/tests/CallTest.php @@ -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)); } diff --git a/tests/ContentTypeTest.php b/tests/ContentTypeTest.php index 2bdeb48..3945245 100644 --- a/tests/ContentTypeTest.php +++ b/tests/ContentTypeTest.php @@ -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)); } } diff --git a/tests/ErrorsTest.php b/tests/ErrorsTest.php index b99fe92..88215b0 100644 --- a/tests/ErrorsTest.php +++ b/tests/ErrorsTest.php @@ -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); } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index f25f4ad..e7d7afb 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -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; } -- 2.30.2