From f2ad9d92206533d6989a9cd5b74da3cfa0778c42 Mon Sep 17 00:00:00 2001 From: Bailey Herbert Date: Thu, 21 Feb 2019 22:57:15 -0700 Subject: [PATCH] Add support for the Retry-After header --- README.md | 23 +++++++++++++ src/Envato/Endpoint.php | 7 ++-- .../Exceptions/TooManyRequestsException.php | 34 +++++++++++++++++++ src/Envato/ResultSet.php | 2 +- 4 files changed, 63 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 915e803..119a861 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ An API client for Envato in PHP, with simplified OAuth, token storage, and reque - [Persistent OAuth](#persistent-oauth) - [Sending Requests](#sending-requests) - [Getting Request Time](#getting-request-time) + - [Rate Limiting](#rate-limiting) - [Catalog](#catalog) - [Look up a public collection](#look-up-a-public-collection) - [Look up a single item](#look-up-a-single-item) @@ -178,6 +179,28 @@ $response = $client->profile->portfolio([ To determine how long a request took to execute (in seconds), you can reference the `$response->time` property. +### Rate Limiting + +If you're being rate limited, the client will throw a `TooManyRequestsException` exception. The exception instance has +methods to help work with the rate limit. + +```php +use Herbert\Envato\Exceptions\TooManyRequestsException; + +try { + $item = $client->catalog->item(['id' => 1234567]); +} +catch (TooManyRequestsException $e) { + // Get the number of seconds remaining (float) + $secondsRemaining = $e->getSecondsRemaining(); + + // + $timestamp = $e->getRetryTime(); + $e->wait(); +} +``` + + ## Catalog ### Look up a public collection diff --git a/src/Envato/Endpoint.php b/src/Envato/Endpoint.php index a89e264..7eb1c01 100644 --- a/src/Envato/Endpoint.php +++ b/src/Envato/Endpoint.php @@ -109,7 +109,10 @@ private function perform(&$uri, array &$variables) { // 429 Too Many Requests elseif ($response->getStatusCode() == 429) { - throw new TooManyRequestsException(); + throw new TooManyRequestsException( + $response->hasHeader('Retry-After') ? + intval($response->getHeader('Retry-After')[0]) : 0 + ); } // Generate response object @@ -152,4 +155,4 @@ private function createHttpClient() { } } -} \ No newline at end of file +} diff --git a/src/Envato/Exceptions/TooManyRequestsException.php b/src/Envato/Exceptions/TooManyRequestsException.php index 90382cd..2dedc16 100644 --- a/src/Envato/Exceptions/TooManyRequestsException.php +++ b/src/Envato/Exceptions/TooManyRequestsException.php @@ -3,6 +3,40 @@ namespace Herbert\Envato\Exceptions { class TooManyRequestsException extends EnvatoException { + private $epoch; + + public function __construct($seconds) { + $this->epoch = microtime(true) + $seconds; + } + + /** + * Returns the number of seconds remaining until the next available request. + * @return float + */ + public function getSecondsRemaining() { + return max(0, $this->epoch - microtime(true)); + } + + /** + * Returns an epoch (unix) timestamp containing the time when the request can be retried. Note that this + * timestamp is in seconds, but contains microseconds. + * + * @return float + */ + public function getRetryTime() { + return $this->epoch; + } + + /** + * Sleeps the script until the `Retry-After` time. + */ + public function wait() { + $seconds = ceil($this->getSecondsRemaining()); + + if ($seconds > 0) { + sleep($seconds); + } + } } } diff --git a/src/Envato/ResultSet.php b/src/Envato/ResultSet.php index 37cfa9f..6f1ba6a 100644 --- a/src/Envato/ResultSet.php +++ b/src/Envato/ResultSet.php @@ -28,4 +28,4 @@ public function __get($property) { } } -} \ No newline at end of file +}