Skip to content

Commit 9497696

Browse files
πŸ“ Add docstrings to 5-support-rate-limiting-headers
Docstrings generation was requested by @Vitexus. * #25 (comment) The following files were modified: * `lib/ApiClient.php` * `lib/RateLimit/JsonRateLimitStore.php` * `lib/RateLimit/PdoRateLimitStore.php` * `lib/RateLimit/RateLimitStore.php` * `lib/RateLimit/RateLimitStoreInterface.php` * `lib/RateLimit/RateLimiter.php` * `lib/RateLimit/SqlDialect.php`
1 parent 531e4c3 commit 9497696

7 files changed

Lines changed: 185 additions & 40 deletions

File tree

β€Žlib/ApiClient.phpβ€Ž

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,18 @@ class ApiClient extends \GuzzleHttp\Client
4545
private RateLimiter $rateLimiter;
4646

4747
/**
48-
* {@inheritDoc}
48+
* Initialize the API client with configuration, certificate validation, and a rate limiter.
4949
*
50-
* $config['clientid'] - obtained from Developer Portal - when you registered your app with us.
51-
* $config['cert'] = ['/path/to/cert.p12','certificat password']
52-
* $config['clientpubip'] = the closest IP address to the real end-user
53-
* $config['mocking'] = true to use /rbcz/premium/mock/* endpoints
50+
* Accepted $config keys:
51+
* - 'clientid': client ID from the Developer Portal.
52+
* - 'cert': array with [pathToP12, password]; when omitted, CERT_FILE and CERT_PASS configuration values are used.
53+
* - 'clientpubip': client public IP (nearest to the end user).
54+
* - 'mocking': bool to enable mock endpoints.
55+
* - 'debug': debug flag.
5456
*
55-
* @throws \Exception CERT_FILE is not set
56-
* @throws \Exception CERT_PASS is not set
57+
* @param array $config Client configuration.
58+
* @throws \Exception If certificate file path (CERT_FILE) is not provided.
59+
* @throws \Exception If certificate password (CERT_PASS) is not provided.
5760
*/
5861
public function __construct(array $config = [])
5962
{
@@ -202,27 +205,25 @@ public static function checkCertificatePassword(string $certFile, string $passwo
202205
}
203206

204207
/**
205-
* Request Identifier.
208+
* Produce a short request identifier used for diagnostics and testing.
206209
*
207-
* @todo Obtain using RateLimiter
210+
* @deprecated since version 0.1 β€” Do not use in production environments.
208211
*
209-
* @deprecated since version 0.1 - Do not use in production Environment!
210-
*
211-
* @return string
212+
* @return string The generated request identifier composed from a source token and the current timestamp, truncated to at most 59 characters.
212213
*/
213214
public static function getxRequestId()
214215
{
215216
return substr(self::sourceString().'#'.time(), -59);
216217
}
217218

218219
/**
219-
* Send an HTTP request.
220-
*
221-
* @param array $options Request options to apply to the given
222-
* request and to the transfer. See \GuzzleHttp\RequestOptions.
220+
* Send an HTTP request while enforcing and updating client rate limits.
223221
*
224-
* @throws GuzzleException
225-
* @throws RateLimitExceededException
222+
* @param \Psr\Http\Message\RequestInterface $request The HTTP request to send.
223+
* @param array $options Request options to apply to the transfer. See \GuzzleHttp\RequestOptions.
224+
* @return \Psr\Http\Message\ResponseInterface The HTTP response.
225+
* @throws \GuzzleHttp\Exception\GuzzleException
226+
* @throws RateLimitExceededException If the client is rate limited and wait mode is disabled.
226227
*/
227228
public function send(\Psr\Http\Message\RequestInterface $request, array $options = []): \Psr\Http\Message\ResponseInterface
228229
{
@@ -253,4 +254,4 @@ public function send(\Psr\Http\Message\RequestInterface $request, array $options
253254

254255
return $response;
255256
}
256-
}
257+
}

β€Žlib/RateLimit/JsonRateLimitStore.phpβ€Ž

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ class JsonRateLimitStore implements RateLimitStoreInterface
2020
private string $filename;
2121
private array $data = [];
2222

23+
/**
24+
* Create a JsonRateLimitStore backed by the given file and load any existing data.
25+
*
26+
* If the file exists, its JSON contents are decoded into the in-memory store; decoding failures result in an empty dataset.
27+
*
28+
* @param string $filename Path to the JSON file used to persist rate limit data.
29+
*/
2330
public function __construct(string $filename)
2431
{
2532
$this->filename = $filename;
@@ -30,11 +37,26 @@ public function __construct(string $filename)
3037
}
3138
}
3239

40+
/**
41+
* Retrieve the stored rate-limit entry for a client and window.
42+
*
43+
* @param string $clientId The client identifier.
44+
* @param string $window The rate-limit window identifier.
45+
* @return array{remaining:int,timestamp:int}|null The entry for the specified client and window: an array with keys `remaining` and `timestamp`, or `null` if not found.
46+
*/
3347
public function get(string $clientId, string $window): ?array
3448
{
3549
return $this->data[$clientId][$window] ?? null;
3650
}
3751

52+
/**
53+
* Store the remaining quota and associated timestamp for a client's rate-limit window and persist it to the configured JSON file.
54+
*
55+
* @param string $clientId Identifier of the client.
56+
* @param string $window Identifier of the rate-limit window.
57+
* @param int $remaining Number of remaining requests for the specified window.
58+
* @param int $timestamp Unix timestamp associated with the stored entry.
59+
*/
3860
public function set(string $clientId, string $window, int $remaining, int $timestamp): void
3961
{
4062
$this->data[$clientId][$window] = [
@@ -45,13 +67,24 @@ public function set(string $clientId, string $window, int $remaining, int $times
4567
$this->save();
4668
}
4769

70+
/**
71+
* Retrieve all rate-limit window entries for a client.
72+
*
73+
* @param string $clientId The client identifier.
74+
* @return array An associative array of windows to entries where each entry contains keys `remaining` (int) and `timestamp` (int); returns an empty array if the client has no entries.
75+
*/
4876
public function allForClient(string $clientId): array
4977
{
5078
return $this->data[$clientId] ?? [];
5179
}
5280

81+
/**
82+
* Persist the in-memory rate limit data to the configured JSON file.
83+
*
84+
* Writes the current $data as pretty-printed JSON into $this->filename.
85+
*/
5386
private function save(): void
5487
{
5588
file_put_contents($this->filename, json_encode($this->data, \JSON_PRETTY_PRINT));
5689
}
57-
}
90+
}

β€Žlib/RateLimit/PdoRateLimitStore.phpβ€Ž

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,25 @@ class PdoRateLimitStore implements RateLimitStoreInterface
1919
{
2020
private \PDO $pdo;
2121

22+
/**
23+
* Store the PDO connection and ensure the rate_limits table exists.
24+
*
25+
* Initializes the store by saving the provided PDO instance and creating the
26+
* `rate_limits` table if it does not already exist.
27+
*/
2228
public function __construct(\PDO $pdo)
2329
{
2430
$this->pdo = $pdo;
2531
$this->init();
2632
}
2733

34+
/**
35+
* Fetches the remaining token count and expiry timestamp for a client's rate-limit window.
36+
*
37+
* @param string $clientId Identifier of the client.
38+
* @param string $window Identifier of the rate-limit window.
39+
* @return array{remaining:int, timestamp:int}|null The row for the specified client and window with integer `remaining` and `timestamp`, or `null` if no record exists.
40+
*/
2841
public function get(string $clientId, string $window): ?array
2942
{
3043
$stmt = $this->pdo->prepare(<<<'EOD'
@@ -40,6 +53,14 @@ public function get(string $clientId, string $window): ?array
4053
return $row ?: null;
4154
}
4255

56+
/**
57+
* Store or update the rate-limit record for a client and window.
58+
*
59+
* @param string $clientId Identifier of the client.
60+
* @param string $window Identifier of the rate-limit window (e.g., "1m", "hourly").
61+
* @param int $remaining Number of remaining allowed requests for the window.
62+
* @param int $timestamp UNIX timestamp associated with the record (seconds since epoch).
63+
*/
4364
public function set(string $clientId, string $window, int $remaining, int $timestamp): void
4465
{
4566
$stmt = $this->pdo->prepare(<<<'EOD'
@@ -51,6 +72,12 @@ public function set(string $clientId, string $window, int $remaining, int $times
5172
$stmt->execute([$clientId, $window, $remaining, $timestamp]);
5273
}
5374

75+
/**
76+
* Fetches all rate-limit entries for a client, indexed by window.
77+
*
78+
* @param string $clientId The client identifier.
79+
* @return array<string, array{remaining:int,timestamp:int}> Associative array keyed by window name; each value contains `remaining` (int) and `timestamp` (int).
80+
*/
5481
public function allForClient(string $clientId): array
5582
{
5683
$stmt = $this->pdo->prepare(<<<'EOD'
@@ -74,6 +101,12 @@ public function allForClient(string $clientId): array
74101
return $results;
75102
}
76103

104+
/**
105+
* Ensure the `rate_limits` table exists in the connected PDO database.
106+
*
107+
* Creates a table with columns: `client_id` (TEXT), `window` (TEXT), `remaining` (INTEGER),
108+
* `timestamp` (INTEGER) and a composite primary key on (`client_id`, `window`).
109+
*/
77110
private function init(): void
78111
{
79112
$this->pdo->exec(<<<'EOD'
@@ -88,4 +121,4 @@ private function init(): void
88121

89122
EOD);
90123
}
91-
}
124+
}

β€Žlib/RateLimit/RateLimitStore.phpβ€Ž

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ abstract class RateLimitStore
1919
{
2020
protected string $path;
2121

22+
/**
23+
* Initialize the store with the backing file path and ensure the file exists.
24+
*
25+
* If the file at the provided path does not exist, creates it containing an empty JSON array.
26+
*
27+
* @param string $path Filesystem path to the JSON file used to persist rate-limit data.
28+
*/
2229
public function __construct(string $path)
2330
{
2431
$this->path = $path;
@@ -28,18 +35,46 @@ public function __construct(string $path)
2835
}
2936
}
3037

31-
abstract public function get(string $key): array;
32-
abstract public function increment(string $key, string $field, int $ttlSeconds): int;
38+
/**
39+
* Retrieve stored rate-limit entries for the specified key.
40+
*
41+
* @param string $key The identifier of the rate-limit bucket or resource.
42+
* @return array An array of rate-limit records for the key, or an empty array if none exist.
43+
*/
44+
abstract public function get(string $key): array;
45+
/**
46+
* Increment the numeric counter for a rate-limited key and field, applying a time-to-live.
47+
*
48+
* @param string $key The identifier for the rate-limited entity.
49+
* @param string $field The specific counter field to increment.
50+
* @param int $ttlSeconds Time to live for the counter in seconds.
51+
* @return int The counter value after the increment.
52+
*/
53+
abstract public function increment(string $key, string $field, int $ttlSeconds): int;
3354

55+
/**
56+
* Read and decode the JSON contents of the store file.
57+
*
58+
* Decodes the file contents as an associative array; returns an empty array if the file is empty.
59+
*
60+
* @return array The decoded data as an associative array, or an empty array when the file has no content.
61+
*/
3462
protected function read(): array
3563
{
3664
$data = file_get_contents($this->path);
3765

3866
return $data ? json_decode($data, true) : [];
3967
}
4068

69+
/**
70+
* Writes the provided data to the storage file as pretty-printed JSON.
71+
*
72+
* Encodes `$data` to JSON using `JSON_PRETTY_PRINT` and overwrites the file at `$this->path`.
73+
*
74+
* @param array $data The data to encode and store.
75+
*/
4176
protected function write(array $data): void
4277
{
4378
file_put_contents($this->path, json_encode($data, \JSON_PRETTY_PRINT));
4479
}
45-
}
80+
}

β€Žlib/RateLimit/RateLimitStoreInterface.phpβ€Ž

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,32 @@
1818
interface RateLimitStoreInterface
1919
{
2020
/**
21-
* Returns stored data for the given client identifier and window (second/day).
22-
* Array:
23-
* remaining => int
24-
* timestamp => int (unix time when the value was received).
25-
*/
21+
* Retrieve stored rate-limit data for a client and window.
22+
*
23+
* @param string $clientId The client identifier.
24+
* @param string $window The rate limit window (e.g., "second" or "day").
25+
* @return array|null An array with keys `remaining` (int) and `timestamp` (int, Unix time) or `null` if no data is stored.
26+
*/
2627
public function get(string $clientId, string $window): ?array;
2728

2829
/**
29-
* Stores data for the client and window (second/day).
30-
*/
30+
* Store rate-limit data for a client and time window.
31+
*
32+
* Stores the number of remaining requests and the associated Unix timestamp for
33+
* the specified client identifier and window (e.g., "second" or "day").
34+
*
35+
* @param string $clientId Identifier of the client.
36+
* @param string $window Time window name (for example "second" or "day").
37+
* @param int $remaining Number of remaining requests for the client in this window.
38+
* @param int $timestamp Unix timestamp representing when the stored data expires or was recorded.
39+
*/
3140
public function set(string $clientId, string $window, int $remaining, int $timestamp): void;
3241

3342
/**
34-
* Returns all data for one client (for debugging).
35-
*/
43+
* Retrieve stored rate-limit data for the given client (intended for debugging).
44+
*
45+
* @param string $clientId The client identifier.
46+
* @return array An associative array keyed by window name (e.g., 'second', 'day') where each value is an array with keys `remaining` (int) and `timestamp` (int, Unix time). Returns an empty array if no data exists for the client.
47+
*/
3648
public function allForClient(string $clientId): array;
37-
}
49+
}

β€Žlib/RateLimit/RateLimiter.phpβ€Ž

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,34 @@ class RateLimiter
2020
private RateLimitStoreInterface $store;
2121
private bool $waitMode;
2222

23+
/**
24+
* Create a RateLimiter configured with a storage backend and a handling mode for exceeded limits.
25+
*
26+
* @param RateLimitStoreInterface $store Storage backend for per-client rate-limit state.
27+
* @param bool $waitMode If true, the limiter will wait until the limit window resets; if false, it will throw a RateLimitExceededException when limits are exceeded.
28+
*/
2329
public function __construct(RateLimitStoreInterface $store, bool $waitMode = true)
2430
{
2531
$this->store = $store;
2632
$this->waitMode = $waitMode;
2733
}
34+
/**
35+
* Indicates whether the limiter is configured to wait when a rate limit is exceeded.
36+
*
37+
* @return bool `true` if the limiter waits until the rate-limit window expires, `false` otherwise.
38+
*/
2839
public function isWaitMode(): bool
2940
{
3041
return $this->waitMode;
3142
}
3243

3344
/**
34-
* clientId = fingerprint of the certificate (sha1, serial+issuer, etc.)
35-
* secondLimits/dayLimits are obtained from API response headers.
45+
* Stores remaining rate-limit counts for a client for both the one-second and 24-hour windows.
46+
*
47+
* @param string $clientId Fingerprint identifying the client (e.g., certificate SHA1, serial+issuer).
48+
* @param int $remainingSecond Remaining requests in the current one-second window.
49+
* @param int $remainingDay Remaining requests in the current 24-hour window.
50+
* @param int $timestamp UNIX timestamp (seconds) when the limits were observed.
3651
*/
3752
public function handleRateLimits(
3853
string $clientId,
@@ -45,7 +60,13 @@ public function handleRateLimits(
4560
}
4661

4762
/**
48-
* Verification before the next request.
63+
* Ensures the client is allowed to make the next request by enforcing per-second and per-day rate limits.
64+
*
65+
* If a window is exhausted and wait mode is enabled, pauses execution for the required seconds to clear the window;
66+
* otherwise throws a RateLimitExceededException.
67+
*
68+
* @param string $clientId Identifier of the client whose rate limits are checked.
69+
* @throws RateLimitExceededException If a rate limit is exceeded and wait mode is disabled.
4970
*/
5071
public function checkBeforeRequest(string $clientId): void
5172
{
@@ -82,4 +103,4 @@ public function checkBeforeRequest(string $clientId): void
82103
}
83104
}
84105
}
85-
}
106+
}

β€Žlib/RateLimit/SqlDialect.phpβ€Ž

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@
1717

1818
interface SqlDialect
1919
{
20-
public function now(): int; // PHP timestamp β†’ we store it directly as int
20+
/**
21+
* Get the current time as a Unix timestamp.
22+
*
23+
* @return int The current Unix timestamp (seconds since the Unix epoch).
24+
*/
25+
public function now(): int; /**
26+
* Generate a SQL parameter placeholder for the given parameter name.
27+
*
28+
* @param string $name Logical parameter name (without any placeholder prefix characters).
29+
* @return string The SQL placeholder string to use in queries (for example `:name`, `?`, or `@name` depending on the dialect).
30+
*/
2131
public function placeholder(string $name): string; // :name, ?, @name ...
22-
}
32+
}

0 commit comments

Comments
Β (0)