ws/style/uses/docs
authorMichael Wallner <mike@php.net>
Wed, 18 May 2016 10:48:33 +0000 (12:48 +0200)
committerMichael Wallner <mike@php.net>
Thu, 19 May 2016 17:49:04 +0000 (19:49 +0200)
.gitignore
examples/generator.php
lib/API.php
lib/API/Call.php
lib/API/ContentType.php
lib/API/Invoker.php
lib/API/Iterator.php
lib/API/Links.php
lib/Exception/RequestException.php

index c2ecf0aa7b7e90e342e742ef474530329b3004c8..8b137891791fe96927ad78e64b0aad7bded08bdc 100644 (file)
@@ -1,5 +1 @@
-#
-/nbproject/
-/tmp/
-composer.lock
-vendor/
+
index d488e97b2ba6ab14a9ee64a66ea8ec87a82ea427..9d962d4103076eaf99035926926b708639a1a6e7 100755 (executable)
@@ -14,7 +14,7 @@ $api = new API([
        "Authorization" => "token ".getenv("GITHUB_TOKEN")
 ], null, $cli, $log);
 
-$api(function() use($api) {
+$api(function($api) {
        $count = 0;
        $events = yield $api->repos->m6w6->{"ext-http"}->issues->events();
        while ($events) {
index f0b7620994d6516cb8f52e5efee4a2b5a908f700..ba384c5ad4eb0d591ebc3a1aa671099b841214b9 100644 (file)
 
 namespace seekat;
 
-use seekat\API\ContentType;
-use seekat\Exception\RequestException;
+use Countable;
+use Generator;
+use http\{
+       Client,
+       Client\Request,
+       Client\Response,
+       Header,
+       Message\Body,
+       QueryString,
+       Url
+};
+use InvalidArgumentException;
+use IteratorAggregate;
+use Psr\Log\{
+       LoggerInterface,
+       NullLogger
+};
+use seekat\{
+       API\Call,
+       API\ContentType,
+       API\Invoker,
+       API\Iterator,
+       API\Links,
+       Exception\RequestException
+};
+use React\Promise\{
+       ExtendedPromiseInterface,
+       function reject,
+       function resolve
+};
+use Throwable;
+use UnexpectedValueException;
 
-use http\Url;
-use http\Header;
-use http\Client;
-use http\Client\Request;
-use http\Client\Response;
-use http\Message\Body;
-use http\QueryString;
-
-use Psr\Log\LoggerInterface;
-use Psr\Log\NullLogger;
-
-use React\Promise\ExtendedPromiseInterface;
-use function React\Promise\resolve;
-use function React\Promise\reject;
-use function React\Promise\map;
-
-class API implements \IteratorAggregate, \Countable {
+class API implements IteratorAggregate, Countable {
        /**
         * The current API endpoint URL
-        * @var \http\Url
+        * @var Url
         */
        private $__url;
-       
+
        /**
         * Logger
-        * @var \Psr\Log\LoggerInterface
+        * @var LoggerInterface
         */
        private $__log;
-       
+
        /**
         * The HTTP client
-        * @var \http\Client
+        * @var Client
         */
        private $__client;
-       
+
        /**
         * Default headers to send out to the API endpoint
         * @var array
         */
        private $__headers;
-       
+
        /**
         * Current endpoint data's Content-Type
-        * @var \http\Header
+        * @var Header
         */
        private $__type;
-       
+
        /**
         * Current endpoint's data
         * @var array|object
         */
        private $__data;
-       
+
        /**
         * Current endpoints links
-        * @var seekat\API\Links
+        * @var Links
         */
        private $__links;
-       
+
        /**
         * Create a new API endpoint root
         *
-        * @var array $headers Standard request headers, defaults to ["Accept" => "application/vnd.github.v3+json"]
-        * @var \http\Url The API's endpoint, defaults to https://api.github.com
-        * @var \http\Client $client The HTTP client to use for executing requests
-        * @var \Psr\Log\LoggerInterface $log A logger
+        * @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
+        * @param Client $client The HTTP client to use for executing requests
+        * @param LoggerInterface $log A logger
         */
        function __construct(array $headers = null, Url $url = null, Client $client = null, LoggerInterface $log = null) {
                $this->__log = $log ?? new NullLogger;
                $this->__url = $url ?? new Url("https://api.github.com");
                $this->__client = $client ?? new Client;
                $this->__headers = (array) $headers + [
-               "Accept" => "application/vnd.github.v3+json"
+                       "Accept" => "application/vnd.github.v3+json"
                ];
        }
-       
+
        /**
         * Ascend one level deep into the API endpoint
         *
-        * @var string|int $seg The "path" element to ascend into
-        * @return \seekat\API Endpoint clone referring to {$parent}/{$seg}
+        * @param string|int $seg The "path" element to ascend into
+        * @return API Endpoint clone referring to {$parent}/{$seg}
         */
        function __get($seg) : API {
                if (substr($seg, -4) === "_url") {
@@ -97,34 +111,34 @@ class API implements \IteratorAggregate, \Countable {
                        $that->__url->path .= "/".urlencode($seg);
                        $this->exists($seg, $that->__data);
                }
-               
+
                $this->__log->debug(__FUNCTION__."($seg)", [
                        "url" => [
                                (string) $this->__url,
                                (string) $that->__url
                        ],
                ]);
-               
+
                return $that;
        }
-       
+
        /**
         * Call handler that actually queues a data fetch and returns a promise
         *
-        * @var string $method The API's "path" element to ascend into
-        * @var array $args Array of arguments forwarded to \seekat\API::get()
-        * @return \React\Promise\ExtendedPromiseInterface
+        * @param string $method The API's "path" element to ascend into
+        * @param array $args Array of arguments forwarded to \seekat\API::get()
+        * @return ExtendedPromiseInterface
         */
        function __call(string $method, array $args) : ExtendedPromiseInterface {
                /* We cannot implement an explicit then() method,
                 * because the Promise implementation might think
                 * we're actually implementing Thenable,
-                * which might cause an infite loop.
+                * which might cause an infinite loop.
                 */
                if ($method === "then") {
                        return $this->get()->then(...$args);
                }
-               
+
                /*
                 * very short-hand version:
                 * ->users->m6w6->gists->get()->then(...)
@@ -134,39 +148,39 @@ class API implements \IteratorAggregate, \Countable {
                if (is_callable(current($args))) {
                        return $this->$method->get()->then(current($args));
                }
-               
+
                /* standard access */
                if ($this->exists($method)) {
                        return $this->$method->get(...$args);
                }
-               
+
                /* fetch resource, unless already localized, and try for {$method}_url */
-               return $this->$method->get(...$args)->otherwise(function($error) use($method, $args) {
+               return $this->$method->get(...$args)->otherwise(function(Throwable $error) use($method, $args) {
                        if ($this->exists($method."_url", $url)) {
-                               
+
                                $this->__log->info(__FUNCTION__."($method): ". $error->getMessage(), [
                                        "url" => (string) $this->__url
                                ]);
-                               
+
                                $url = new Url(uri_template($url, (array) current($args)));
                                return $this->withUrl($url)->get(...$args);
                        }
-                       
+
                        $this->__log->error(__FUNCTION__."($method): ". $error->getMessage(), [
                                "url" => (string) $this->__url
                        ]);
-                       
+
                        throw $error;
                });
        }
-       
+
        /**
         * Clone handler ensuring the underlying url will be cloned, too
         */
        function __clone() {
                $this->__url = clone $this->__url;
        }
-       
+
        /**
         * The string handler for the endpoint's data
         *
@@ -176,36 +190,37 @@ class API implements \IteratorAggregate, \Countable {
                if (is_scalar($this->__data)) {
                        return (string) $this->__data;
                }
-               
+
                /* FIXME */
                return json_encode($this->__data);
        }
-       
+
        /**
         * Import handler for the endpoint's underlying data
         *
-        * \seekat\Deferred will call this when the request will have finished.
+        * \seekat\Call will call this when the request will have finished.
         *
-        * @var \http\Client\Response $response
-        * @return \seekat\API self
+        * @param Response $response
+        * @return API self
+        * @throws UnexpectedValueException
+        * @throws RequestException
+        * @throws \Exception
         */
        function import(Response $response) : API {
-               //addcslashes($response, "\0..\40\42\47\134\140\177..\377")
-               
                $this->__log->info(__FUNCTION__.": ". $response->getInfo(), [
                        "url" => (string) $this->__url
                ]);
-               
+
                if ($response->getResponseCode() >= 400) {
                        $e = new RequestException($response);
-                       
+
                        $this->__log->critical(__FUNCTION__.": ".$e->getMessage(), [
                                "url" => (string) $this->__url,
                        ]);
-                       
+
                        throw $e;
                }
-               
+
                if (!($type = $response->getHeader("Content-Type", Header::class))) {
                        $e = new RequestException($response);
                        $this->__log->error(
@@ -214,65 +229,66 @@ class API implements \IteratorAggregate, \Countable {
                        ]);
                        throw $e;
                }
-               
+
                try {
                        $this->__type = new ContentType($type);
                        $this->__data = $this->__type->parseBody($response->getBody());
-                       
+
                        if (($link = $response->getHeader("Link", Header::class))) {
-                               $this->__links = new API\Links($link);
+                               $this->__links = new Links($link);
                        }
                } catch (\Exception $e) {
                        $this->__log->error(__FUNCTION__.": ".$e->getMessage(), [
                                "url" => (string) $this->__url
                        ]);
-                       
+
                        throw $e;
                }
-               
+
                return $this;
        }
-       
+
        /**
         * Export the endpoint's underlying data
         *
+        * @param
         * @return mixed
         */
        function export(&$type = null) {
                $type = clone $this->__type;
                return $this->__data;
        }
-       
+
        /**
         * Create a copy of the endpoint with specific data
         *
-        * @var mixed $data
-        * @return \seekat\API clone
+        * @param mixed $data
+        * @return API clone
         */
        function withData($data) : API {
                $that = clone $this;
                $that->__data = $data;
                return $that;
        }
-       
+
        /**
         * Create a copy of the endpoint with a specific Url, but with data reset
         *
-        * @var \http\Url $url
-        * @return \seekat\API clone
+        * @param Url $url
+        * @return API clone
         */
        function withUrl(Url $url) : API {
                $that = $this->withData(null);
                $that->__url = $url;
                return $that;
        }
-       
+
        /**
         * Create a copy of the endpoint with a specific header added/replaced
         *
-        * @var string $name
-        * @var mixed $value
-        * @return \seekat\API clone
+        * @param string $name
+        * @param mixed $value
+        * @return API clone
         */
        function withHeader(string $name, $value) : API {
                $that = clone $this;
@@ -283,16 +299,15 @@ class API implements \IteratorAggregate, \Countable {
                }
                return $that;
        }
-       
+
        /**
         * Create a copy of the endpoint with a customized accept header
         *
-        * Changes the returned endpoint's accept header to
- *     "application/vnd.github.v3.{$type}"
+        * Changes the returned endpoint's accept header to "application/vnd.github.v3.{$type}"
         *
-        * @var string $type The expected return data type, e.g. "raw", "html", etc.
-        * @var bool $keepdata Whether to keep already fetched data
-        * @return \seekat\API clone
+        * @param string $type The expected return data type, e.g. "raw", "html", etc.
+        * @param bool $keepdata Whether to keep already fetched data
+        * @return API clone
         */
        function as(string $type, bool $keepdata = true) : API {
                switch(substr($type, 0, 1)) {
@@ -311,16 +326,16 @@ class API implements \IteratorAggregate, \Countable {
                }
                return $that;
        }
-       
+
        /**
         * Create an iterator over the endpoint's underlying data
         *
-        * @return \seekat\API\Iterator
+        * @return Iterator
         */
-       function getIterator() : API\Iterator {
-               return new API\Iterator($this);
+       function getIterator() : Iterator {
+               return new Iterator($this);
        }
-       
+
        /**
         * Count the underlying data's entries
         *
@@ -329,78 +344,78 @@ class API implements \IteratorAggregate, \Countable {
        function count() : int {
                return count($this->__data);
        }
-       
+
        /**
         * Perform a GET request against the endpoint's underlying URL
         *
-        * @var mixed $args The HTTP query string parameters
-        * @var array $headers The request's additional HTTP headers
-        * @return \React\Promise\ExtendedPromiseInterface
+        * @param mixed $args The HTTP query string parameters
+        * @param array $headers The request's additional HTTP headers
+        * @return ExtendedPromiseInterface
         */
        function get($args = null, array $headers = null) : ExtendedPromiseInterface {
                return $this->__xfer("GET", $args, null, $headers);
        }
-       
+
        /**
         * Perform a DELETE request against the endpoint's underlying URL
         *
-        * @var mixed $args The HTTP query string parameters
-        * @var array $headers The request's additional HTTP headers
-        * @return \React\Promise\ExtendedPromiseInterface
+        * @param mixed $args The HTTP query string parameters
+        * @param array $headers The request's additional HTTP headers
+        * @return ExtendedPromiseInterface
         */
        function delete($args = null, array $headers = null) : ExtendedPromiseInterface {
                return $this->__xfer("DELETE", $args, null, $headers);
        }
-       
+
        /**
         * Perform a POST request against the endpoint's underlying URL
         *
-        * @var mixed $body The HTTP message's body
-        * @var mixed $args The HTTP query string parameters
-        * @var array $headers The request's additional HTTP headers
-        * @return \React\Promise\ExtendedPromiseInterface
+        * @param mixed $body The HTTP message's body
+        * @param mixed $args The HTTP query string parameters
+        * @param array $headers The request's additional HTTP headers
+        * @return ExtendedPromiseInterface
         */
        function post($body = null, $args = null, array $headers = null) : ExtendedPromiseInterface {
                return $this->__xfer("POST", $args, $body, $headers);
        }
-       
+
        /**
         * Perform a PUT request against the endpoint's underlying URL
         *
-        * @var mixed $body The HTTP message's body
-        * @var mixed $args The HTTP query string parameters
-        * @var array $headers The request's additional HTTP headers
-        * @return \React\Promise\ExtendedPromiseInterface
+        * @param mixed $body The HTTP message's body
+        * @param mixed $args The HTTP query string parameters
+        * @param array $headers The request's additional HTTP headers
+        * @return ExtendedPromiseInterface
         */
        function put($body = null, $args = null, array $headers = null) : ExtendedPromiseInterface {
                return $this->__xfer("PUT", $args, $body, $headers);
        }
-       
+
        /**
         * Perform a PATCH request against the endpoint's underlying URL
         *
-        * @var mixed $body The HTTP message's body
-        * @var mixed $args The HTTP query string parameters
-        * @var array $headers The request's additional HTTP headers
-        * @return \React\Promise\ExtendedPromiseInterface
+        * @param mixed $body The HTTP message's body
+        * @param mixed $args The HTTP query string parameters
+        * @param array $headers The request's additional HTTP headers
+        * @return ExtendedPromiseInterface
         */
        function patch($body = null, $args = null, array $headers = null) : ExtendedPromiseInterface {
                return $this->__xfer("PATCH", $args, $body, $headers);
        }
-       
+
        /**
         * Accessor to any hypermedia links
         *
-        * @return null|\seekat\API\Links
+        * @return null|Links
         */
        function links() {
                return $this->__links;
        }
-       
+
        /**
         * Perform a GET request against the link's "first" relation
         *
-        * @return \React\Promise\ExtendedPromiseInterface
+        * @return ExtendedPromiseInterface
         */
        function first() : ExtendedPromiseInterface {
                if ($this->links() && ($first = $this->links()->getFirst())) {
@@ -408,11 +423,11 @@ class API implements \IteratorAggregate, \Countable {
                }
                return reject($this->links());
        }
-       
+
        /**
         * Perform a GET request against the link's "prev" relation
         *
-        * @return \React\Promise\ExtendedPromiseInterface
+        * @return ExtendedPromiseInterface
         */
        function prev() : ExtendedPromiseInterface {
                if ($this->links() && ($prev = $this->links()->getPrev())) {
@@ -420,11 +435,11 @@ class API implements \IteratorAggregate, \Countable {
                }
                return reject($this->links());
        }
-       
+
        /**
         * Perform a GET request against the link's "next" relation
         *
-        * @return \React\Promise\ExtendedPromiseInterface
+        * @return ExtendedPromiseInterface
         */
        function next() : ExtendedPromiseInterface {
                if ($this->links() && ($next = $this->links()->getNext())) {
@@ -432,11 +447,11 @@ class API implements \IteratorAggregate, \Countable {
                }
                return reject($this->links());
        }
-       
+
        /**
         * Perform a GET request against the link's "last" relation
         *
-        * @return \React\Promise\ExtendedPromiseInterface
+        * @return ExtendedPromiseInterface
         */
        function last() : ExtendedPromiseInterface {
                if ($this->links() && ($last = $this->links()->getLast())) {
@@ -444,11 +459,11 @@ class API implements \IteratorAggregate, \Countable {
                }
                return reject($this->links());
        }
-       
+
        /**
         * Perform all queued HTTP transfers
         *
-        * @return \seekat\API self
+        * @return API self
         */
        function send() : API {
                $this->__log->debug(__FUNCTION__.": start loop");
@@ -458,19 +473,20 @@ class API implements \IteratorAggregate, \Countable {
                $this->__log->debug(__FUNCTION__.": end loop");
                return $this;
        }
-       
+
        /**
         * Run the send loop through a generator
         *
-        * @param callable|\Generator $cbg A \Generator or a factory of a \Generator yielding promises
-        * @return \React\Promise\ExtendedPromiseInterface The promise of the generator's return value
+        * @param callable|Generator $cbg A \Generator or a factory of a \Generator yielding promises
+        * @return ExtendedPromiseInterface The promise of the generator's return value
+        * @throws InvalidArgumentException
         */
        function __invoke($cbg) : ExtendedPromiseInterface {
                $this->__log->debug(__FUNCTION__);
-               
-               $invoker = new API\Invoker($this->__client);
 
-               if ($cbg instanceof \Generator) {
+               $invoker = new Invoker($this->__client);
+
+               if ($cbg instanceof Generator) {
                        return $invoker->iterate($cbg)->promise();
                }
 
@@ -480,7 +496,7 @@ class API implements \IteratorAggregate, \Countable {
                        })->promise();
                }
 
-               throw \InvalidArgumentException(
+               throw InvalidArgumentException(
                        "Expected callable or Generator, got ".(
                                is_object($cbg)
                                        ? "instance of ".get_class($cbg)
@@ -488,12 +504,12 @@ class API implements \IteratorAggregate, \Countable {
                        )
                );
        }
-       
+
        /**
         * Check for a specific key in the endpoint's underlying data
         *
-        * @var string $seg
-        * @var mixed &$val
+        * @param string $seg
+        * @param mixed &$val
         * @return bool
         */
        function exists($seg, &$val = null) : bool {
@@ -507,7 +523,7 @@ class API implements \IteratorAggregate, \Countable {
                        $val = null;
                        $exists = false;
                }
-               
+
                $this->__log->debug(__FUNCTION__."($seg) in ".(
                        is_object($this->__data)
                                ? get_class($this->__data)
@@ -520,18 +536,18 @@ class API implements \IteratorAggregate, \Countable {
                        "url" => (string) $this->__url,
                        "val" => $val,
                ]);
-               
+
                return $exists;
        }
-       
+
        /**
         * Queue the actual HTTP transfer through \seekat\API\Deferred and return the promise
         *
-        * @var string $method The HTTP request method
-        * @var mixed $args The HTTP query string parameters
-        * @var mixed $body Thee HTTP message's body
-        * @var array $headers The request's additional HTTP headers
-        * @return \React\Promise\ExtendedPromiseInterface
+        * @param string $method The HTTP request method
+        * @param mixed $args The HTTP query string parameters
+        * @param mixed $body Thee HTTP message's body
+        * @param array $headers The request's additional HTTP headers
+        * @return ExtendedPromiseInterface
         */
        private function __xfer(string $method, $args = null, $body = null, array $headers = null) : ExtendedPromiseInterface {
                if (isset($this->__data)) {
@@ -541,24 +557,24 @@ class API implements \IteratorAggregate, \Countable {
                                "body" => $body,
                                "headers" => $headers,
                        ]);
-                       
+
                        return resolve($this);
                }
-               
+
                $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)));
-               
+
                $this->__log->info(__FUNCTION__."($method) -> request", [
                        "url" => (string) $this->__url,
                        "args" => $this->__url->query,
                        "body" => $body,
                        "headers" => $headers,
                ]);
-               
-               return (new API\Call($this, $this->__client, $request))->promise();
+
+               return (new Call($this, $this->__client, $request))->promise();
        }
 }
index 56e15dcab4a759d9b5e86d4d9fd1972039df93d0..787f98db3b24238654bf1a42e0d628247cdc1ed5 100644 (file)
@@ -3,9 +3,11 @@
 namespace seekat\API;
 
 use Exception;
-use http\Client;
-use http\Client\Request;
-use http\Client\Response;
+use http\ {
+       Client,
+       Client\Request,
+       Client\Response
+};
 use React\Promise\Deferred;
 use seekat\API;
 use SplObserver;
@@ -15,7 +17,7 @@ class Call extends Deferred implements SplObserver
 {
        /**
         * The endpoint
-        * @var \seekat\API
+        * @var API
         */
        private $api;
 
@@ -40,9 +42,9 @@ class Call extends Deferred implements SplObserver
        /**
         * Create a deferred promise for the response of $request
         *
-        * @var \seekat\API $api The endpoint of the request
-        * @var Client $client The HTTP client to send the request
-        * @var Request The request to execute
+        * @param API $api The endpoint of the request
+        * @param Client $client The HTTP client to send the request
+        * @param Request $request The request to execute
         */
        function __construct(API $api, Client $client, Request $request) {
                $this->api = $api;
@@ -52,7 +54,7 @@ class Call extends Deferred implements SplObserver
                parent::__construct(function($resolve, $reject) {
                        return $this->cancel($resolve, $reject);
                });
-               
+
                $client->attach($this);
                $client->enqueue($request);
                /* start off */
@@ -64,9 +66,9 @@ class Call extends Deferred implements SplObserver
         *
         * Import the response's data on success and resolve the promise.
         *
-        * @var SplSubject $client The observed HTTP client
-        * @var Request The request which generated the update
-        * @var object $progress The progress information
+        * @param SplSubject $client The observed HTTP client
+        * @param Request $request The request which generated the update
+        * @param object $progress The progress information
         */
        function update(SplSubject $client, Request $request = null, $progress = null) {
                if ($request !== $this->request) {
index b2eb5fbc0690f74af90b67214d37a6ca93b452a1..929dc0c2c80907eea6ab59aba6dc489b98e1647b 100644 (file)
@@ -2,13 +2,26 @@
 
 namespace seekat\API;
 
-use http\Header;
-use http\Message\Body;
+use http\{
+       Header,
+       Message\Body
+};
+
+use InvalidArgumentException;
+use UnexpectedValueException;
 
 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",
@@ -20,20 +33,43 @@ class ContentType
                "text/plain"=> "self::fromData",
        ];
 
+       /**
+        * Content type abbreviation
+        * @var string
+        */
        private $type;
 
+       /**
+        * 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;
        }
 
+       /**
+        * Check whether a handler is registered for a particular content type
+        * @param string $type The (abbreviated) content type
+        * @return bool
+        */
        static function registered(string $type) : bool {
                return isset(self::$types[$type]);
        }
 
+       /**
+        * Unregister a content type handler
+        * @param string $type
+        */
        static function unregister(string $type) {
                unset(self::$types[$type]);
        }
 
+       /**
+        * Get/set the API version to use
+        * @param int $v if not null, update the API version
+        * @return int the previously set version
+        */
        static function version(int $v = null) : int {
                $api = self::$version;
                if (isset($v)) {
@@ -41,29 +77,48 @@ class ContentType
                }
                return $api;
        }
-       
+
+       /**
+        * @param Body $json
+        * @return mixed
+        * @throws UnexpectedValueException
+        */
        private static function fromJson(Body $json) {
                $decoded = json_decode($json);
                if (!isset($decoded) && json_last_error()) {
-                       throw new \UnexpectedValueException("Could not decode JSON: ".
+                       throw new UnexpectedValueException("Could not decode JSON: ".
                                json_last_error_msg());
                }
                return $decoded;
        }
 
+       /**
+        * @param Body $base64
+        * @return string
+        * @throws UnexpectedValueException
+        */
        private static function fromBase64(Body $base64) : string {
                if (false === ($decoded = base64_decode($base64))) {
-                       throw new \UnexpectedValueExcpeption("Could not decode BASE64");
+                       throw new UnexpectedValueException("Could not decode BASE64");
                }
+               return $decoded;
        }
 
+       /**
+        * @param Body $data
+        * @return string
+        */
        private static function fromData(Body $data) : string {
                return (string) $data;
        }
 
+       /**
+        * @param Header $contentType
+        * @throws InvalidArgumentException
+        */
        function __construct(Header $contentType) {
                if (strcasecmp($contentType->name, "Content-Type")) {
-                       throw new \InvalidArgumentException(
+                       throw new InvalidArgumentException(
                                "Expected Content-Type header, got ". $contentType->name);
                }
                $vapi = static::version();
@@ -75,15 +130,25 @@ class ContentType
                        )/x", "\\1", current(array_keys($contentType->getParams()->params)));
        }
 
+       /**
+        * Get the (abbreviated) content type name
+        * @return string
+        */
        function getType() : string {
                return $this->type;
        }
 
+       /**
+        * Parse a response message's body according to its content type
+        * @param Body $data
+        * @return mixed
+        * @throws UnexpectedValueException
+        */
        function parseBody(Body $data) {
                $type = $this->getType();
                if (static::registered($type)) {
                        return call_user_func(self::$types[$type], $data, $type);
                }
-               throw new \UnexpectedValueException("Unhandled content type '$type'");
+               throw new UnexpectedValueException("Unhandled content type '$type'");
        }
 }
index e52eedc7a3ec48421779b1427e28f734172bada5..c36315eca0c18a4f71bb697bbddf80ff668ce681 100644 (file)
@@ -4,11 +4,12 @@ namespace seekat\API;
 
 use Generator;
 use http\Client;
-use React\Promise\Deferred;
-use React\Promise\PromiseInterface;
-use React\Promise\ExtendedPromiseInterface;
-
-use function React\Promise\all;
+use React\Promise\{
+       Deferred,
+       ExtendedPromiseInterface,
+       PromiseInterface,
+       function all
+};
 
 class Invoker extends Deferred
 {
@@ -32,7 +33,7 @@ class Invoker extends Deferred
 
        /**
         * Create a new generator invoker
-        * @param \http\Client $client
+        * @param Client $client
         */
        function __construct(Client $client) {
                $this->client = $client;
@@ -45,8 +46,8 @@ class Invoker extends Deferred
        /**
         * Invoke $generator to create a \Generator which yields promises
         *
-        * @param callable $generator as function() : \Generator, creating a generator yielding promises
-        * @return \seekat\API\Invoker
+        * @param callable $generator as function():\Generator, creating a generator yielding promises
+        * @return Invoker
         */
        function invoke(callable $generator) : Invoker {
                $this->iterate($generator());
@@ -56,8 +57,8 @@ class Invoker extends Deferred
        /**
         * Iterate over $gen, a \Generator yielding promises
         *
-        * @param \Generator $gen
-        * @return \seekat\API\Invoker
+        * @param Generator $gen
+        * @return Invoker
         */
        function iterate(Generator $gen) : Invoker {
                $this->cancelled = false;
@@ -77,8 +78,8 @@ class Invoker extends Deferred
 
        /**
         * Get the generator's result
-        * 
-        * @return \React\Promise\ExtendedPromiseInterface
+        *
+        * @return ExtendedPromiseInterface
         */
        function result() : ExtendedPromiseInterface {
                return $this->promise();
@@ -86,9 +87,9 @@ class Invoker extends Deferred
 
        /**
         * Promise handler
-        * 
-        * @param array|\React\Promise\PromiseInterface $promise
-        * @param \Generator $gen
+        *
+        * @param array|PromiseInterface $promise
+        * @param Generator $gen
         */
        private function give($promise, Generator $gen) {
                if ($promise instanceof PromiseInterface) {
index f0179b48087b2718e5a67426ae8aad915119128c..6c4a9cc59be6a0c9bd6cfbd2c11bd249929cd0d0 100644 (file)
@@ -2,13 +2,15 @@
 
 namespace seekat\API;
 
+use http\Url;
+use Iterator as BaseIterator;
 use seekat\API;
 
-class Iterator implements \Iterator
+class Iterator implements BaseIterator
 {
        /**
         * The endpoint
-        * @var \seekat\API
+        * @var API
         */
        private $api;
 
@@ -33,7 +35,7 @@ class Iterator implements \Iterator
        /**
         * Create a new iterator over $data returning \seekat\API instances
         *
-        * @var \seekat\API $api The endpoint
+        * @var API $api The endpoint
         * @var array|object $data
         */
        function __construct(API $api) {
@@ -53,7 +55,7 @@ class Iterator implements \Iterator
        /**
         * Get the current data entry
         *
-        * @return \seekat\API
+        * @return API
         */
        function current() {
                return $this->cur;
@@ -63,7 +65,7 @@ class Iterator implements \Iterator
                if (list($key, $cur) = each($this->data)) {
                        $this->key = $key;
                        if ($this->api->$key->exists("url", $url)) {
-                               $url = new \http\Url($url);
+                               $url = new Url($url);
                                $this->cur = $this->api->withUrl($url)->withData($cur);
                        } else {
                                $this->cur = $this->api->$key->withData($cur);
index 5546e65f8f33aa7d0391ed7770e9142c4b1465c4..325f4b7289875e1a415f911489d4b4dafd254da9 100644 (file)
@@ -2,19 +2,23 @@
 
 namespace seekat\API;
 
-use http\Header;
-use http\Params;
-use http\QueryString;
-use http\Url;
+use http\ {
+       Header,
+       Params,
+       QueryString,
+       Url
+};
+use Serializable;
+use UnexpectedValueException;
 
-class Links implements \Serializable
+class Links implements Serializable
 {
        /**
         * Parsed "Link" relations
-        * @var \http\Params
+        * @var Params
         */
        private $params;
-       
+
        /**
         * Parsed "Link" relations
         * @var array
@@ -24,23 +28,33 @@ class Links implements \Serializable
        /**
         * Parse the hypermedia link header
         *
-        * @var string $header_value The value of the "Link" header
+        * @param Header $links The Link header
+        * @throws UnexpectedValueException
         */
        function __construct(Header $links) {
                if (strcasecmp($links->name, "Link")) {
-                       throw new \UnexpectedValueException("Expected 'Link' header, got: '{$links->name}'");
+                       throw new UnexpectedValueException("Expected 'Link' header, got: '{$links->name}'");
                }
                $this->unserialize($links->value);
        }
 
+       /**
+        * @return string
+        */
        function __toString() : string {
                return $this->serialize();
        }
 
+       /**
+        * @return string
+        */
        function serialize() {
                return (string) $this->params;
        }
 
+       /**
+        * @param string $links
+        */
        function unserialize($links) {
                $this->params = new Params($links, ",", ";", "=",
                        Params::PARSE_RFC5988 | Params::PARSE_ESCAPED);
@@ -65,7 +79,7 @@ class Links implements \Serializable
         *
         * Returns the link's "last" relation if it exists and "next" is not set.
         *
-        * @return \http\Url
+        * @return Url
         */
        function getNext() {
                if (isset($this->relations["next"])) {
@@ -82,7 +96,7 @@ class Links implements \Serializable
         *
         * Returns the link's "first" relation if it exists and "prev" is not set.
         *
-        * @return \http\Url
+        * @return Url
         */
        function getPrev() {
                if (isset($this->relations["prev"])) {
@@ -97,7 +111,7 @@ class Links implements \Serializable
        /**
         * Get the URL of the link's "last" relation
         *
-        * @return \http\Url
+        * @return Url
         */
        function getLast() {
                if (isset($this->relations["last"])) {
@@ -109,7 +123,7 @@ class Links implements \Serializable
        /**
         * Get the URL of the link's "first" relation
         *
-        * @return \http\Url
+        * @return Url
         */
        function getFirst() {
                if (isset($this->relations["first"])) {
index 877664e13ea458f17ed18ed639c2a4360f9d7c5b..a9da6d6c731bc8ed60d8ab011e04d66feb472a19 100644 (file)
@@ -2,16 +2,30 @@
 
 namespace seekat\Exception;
 
+use Exception as BaseException;
+use http\ {
+       Client\Response,
+       Header
+};
 use seekat\Exception;
 
-use http\Header;
-use http\Client\Response;
-
-class RequestException extends \Exception implements Exception
+class RequestException extends BaseException implements Exception
 {
+       /**
+        * JSON errors
+        * @var array
+        */
        private $errors = [];
+
+       /**
+        * The response of the request which caused the exception
+        * @var Response
+        */
        private $response;
 
+       /**
+        * @param Response $response
+        */
        function __construct(Response $response) {
                $this->response = $response;
 
@@ -36,10 +50,19 @@ class RequestException extends \Exception implements Exception
                parent::__construct($message, $response->getResponseCode(), null);
        }
 
+       /**
+        * Get JSON errors
+        * @return array
+        */
        function getErrors() : array {
                return $this->errors;
        }
 
+       /**
+        * Combine any errors into a single string
+        * @staticvar array $reasons
+        * @return string
+        */
        function getErrorsAsString() {
                static $reasons = [
                        "missing" => "The resource %1\$s does not exist\n",
@@ -63,6 +86,9 @@ class RequestException extends \Exception implements Exception
                return $errors;
        }
 
+       /**
+        * @return string
+        */
        function __toString() : string {
                return parent::__toString() . "\n". $this->getErrorsAsString();
        }