Skip to content

Commit fe168e4

Browse files
committed
feat: Enhance documentation and examples for caching, retries, and debugging features
1 parent 2525b95 commit fe168e4

File tree

13 files changed

+401
-66
lines changed

13 files changed

+401
-66
lines changed

README.md

Lines changed: 57 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -335,8 +335,8 @@ $data = await(retry(
335335

336336
Fetch PHP automatically retries transient failures with exponential backoff.
337337

338-
- Default: No retries enabled (set `maxRetries` to null by default)
339-
- Default delay: 100ms base with exponential backoff (when retries configured)
338+
- Default: 1 retry attempt (`ClientHandler::DEFAULT_RETRIES`) with a 100 ms base delay
339+
- Default delay: 100 ms base with exponential backoff (when retries configured)
340340
- Retry triggers:
341341
- Network/connect errors (e.g., ConnectException)
342342
- HTTP status codes: 408, 429, 500, 502, 503, 504, 507, 509, 520-523, 525, 527, 530 (customizable)
@@ -569,15 +569,14 @@ $response = $handler->get('https://api.example.com/users');
569569

570570
```php
571571
$handler->withCache(null, [
572-
'ttl' => 3600, // Default TTL in seconds (overridden by Cache-Control)
573-
'respect_headers' => true, // Respect Cache-Control headers (default: true)
574-
'is_shared_cache' => false, // Act as shared cache (respects s-maxage)
575-
'stale_while_revalidate' => 60, // Serve stale for 60s while revalidating
576-
'stale_if_error' => 300, // Serve stale for 300s if backend fails
572+
'default_ttl' => 3600, // Default TTL in seconds (overridden by Cache-Control)
573+
'respect_cache_headers' => true, // Honor Cache-Control headers (default: true)
574+
'is_shared_cache' => false, // Act as shared cache (respects s-maxage)
575+
'stale_while_revalidate' => 60, // Serve stale for 60s while revalidating
576+
'stale_if_error' => 300, // Serve stale for 300s if backend fails
577577
'vary_headers' => ['Accept', 'Accept-Language'], // Headers to vary cache by
578-
'cache_methods' => ['GET', 'HEAD'], // Cacheable HTTP methods
579-
'cache_status_codes' => [200, 301], // Cacheable status codes
580-
'force_refresh' => false, // Bypass cache and force fresh request
578+
'cache_methods' => ['GET', 'HEAD'], // Cacheable HTTP methods
579+
'cache_status_codes' => [200, 301], // Cacheable status codes
581580
]);
582581
```
583582

@@ -595,6 +594,23 @@ $response = $handler->withOptions(['cache' => ['ttl' => 600]])
595594
// Custom cache key
596595
$response = $handler->withOptions(['cache' => ['key' => 'custom:users']])
597596
->get('https://api.example.com/users');
597+
598+
// Cache POST/PUT payloads (requires allowing the method globally)
599+
$handler->withCache(null, [
600+
'cache_methods' => ['GET', 'HEAD', 'POST'],
601+
]);
602+
$report = $handler->withOptions([
603+
'cache' => [
604+
'ttl' => 120,
605+
'cache_body' => true, // include the JSON body in the cache key
606+
],
607+
])->post('https://api.example.com/reports', ['range' => 'weekly']);
608+
609+
Useful patterns:
610+
611+
- **Force refresh**: set `force_refresh => true` on the request to ignore stored entries.
612+
- **Cache POST/PUT**: allow the verb in `cache_methods` via `withCache()` and set `cache_body => true` so the request body participates in the cache key.
613+
- **Static assets**: pin a custom `key` for predictable lookups regardless of URL params.
598614
```
599615

600616
## Connection Pooling & HTTP/2
@@ -614,9 +630,12 @@ $handler->withConnectionPool([
614630
'enabled' => true,
615631
'max_connections' => 50, // Total connections across all hosts
616632
'max_per_host' => 10, // Max connections per host
617-
'connection_ttl' => 60, // Connection lifetime in seconds
618-
'idle_timeout' => 30, // Idle connection timeout in seconds
633+
'max_idle_per_host' => 5, // Idle sockets kept per host
634+
'keep_alive_timeout' => 60, // Connection lifetime in seconds
635+
'connection_timeout' => 5, // Dial timeout in seconds
619636
'dns_cache_ttl' => 300, // DNS cache TTL in seconds
637+
'connection_warmup' => false,
638+
'warmup_connections' => 0,
620639
]);
621640
```
622641

@@ -638,7 +657,7 @@ $handler->withHttp2([
638657
```php
639658
// Get pool statistics
640659
$stats = $handler->getPoolStats();
641-
// Returns: connections_created, connections_reused, total_requests, total_latency_ms
660+
// Returns: connections_created, connections_reused, total_requests, average_latency, reuse_rate
642661

643662
// Close all active connections
644663
$handler->closeAllConnections();
@@ -678,34 +697,45 @@ $handler->withLogLevel('info'); // default: debug
678697

679698
$response = $handler->get('https://api.example.com/users');
680699

681-
// Get debug info from last request
682-
$debug = $handler->getLastDebugInfo();
700+
// Preferred: read per-response debug snapshot
701+
$responseDebug = $response->getDebugInfo();
702+
703+
// Legacy fallback for BC: handler-level snapshot (may lag in concurrent flows)
704+
$lastDebug = $handler->getLastDebugInfo();
683705
```
684706

685707
## Testing Support
686708

687709
Fetch PHP includes built-in testing utilities for mocking HTTP responses:
688710

689711
```php
712+
use Fetch\Testing\MockServer;
690713
use Fetch\Testing\MockResponse;
691-
use Fetch\Testing\MockResponseSequence;
692714

693715
// Mock a single response
694-
$handler = fetch_client()->getHandler();
695-
$handler->mock(MockResponse::make(['id' => 1, 'name' => 'John'], 200));
716+
MockServer::fake([
717+
'GET https://api.example.com/users/1' => MockResponse::json([
718+
'id' => 1,
719+
'name' => 'Ada Lovelace',
720+
]),
721+
]);
696722

697-
$response = $handler->get('https://api.example.com/users/1');
698-
// Returns mocked response without making actual HTTP request
723+
$response = fetch('https://api.example.com/users/1');
724+
// Returns mocked response without making an actual HTTP request
725+
MockServer::assertSent('GET https://api.example.com/users/1');
699726

700727
// Mock a sequence of responses
701-
$sequence = new MockResponseSequence([
702-
MockResponse::make(['id' => 1], 200),
703-
MockResponse::make(['id' => 2], 200),
704-
MockResponse::make(null, 404),
728+
MockServer::fake([
729+
'https://api.example.com/users/*' => MockResponse::sequence([
730+
MockResponse::json(['id' => 1]),
731+
MockResponse::json(['id' => 2]),
732+
MockResponse::notFound(),
733+
]),
705734
]);
706735

707-
$handler->mock($sequence);
708-
// Each subsequent request returns the next response in sequence
736+
fetch('https://api.example.com/users/alpha'); // gets id 1
737+
fetch('https://api.example.com/users/beta'); // gets id 2
738+
fetch('https://api.example.com/users/omega'); // 404 from sequence
709739
```
710740

711741
## Advanced Response Features
@@ -764,7 +794,7 @@ $handler->resetPool();
764794

765795
// Get pool statistics
766796
$stats = $handler->getPoolStats();
767-
// Returns: connections_created, connections_reused, total_requests, total_latency_ms
797+
// Returns: connections_created, connections_reused, total_requests, average_latency, reuse_rate
768798
```
769799

770800
## Async Notes

docs/api/client-handler.md

Lines changed: 182 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ class ClientHandler implements ClientHandlerInterface
1818
HandlesUris,
1919
ManagesPromises,
2020
ManagesRetries,
21-
PerformsHttpRequests;
21+
PerformsHttpRequests,
22+
HandlesMocking,
23+
ManagesConnectionPool,
24+
ManagesDebugAndProfiling;
2225

2326
// ...
2427
}
@@ -381,6 +384,50 @@ Configures the request body for POST/PUT/PATCH/DELETE requests.
381384
protected function configureRequestBody(mixed $body = null, string|ContentType $contentType = ContentType::JSON): void
382385
```
383386

387+
## Caching
388+
389+
### `withCache()`
390+
391+
Enable caching with an optional cache backend and configuration.
392+
393+
```php
394+
public function withCache(?CacheInterface $cache = null, array $options = []): self
395+
```
396+
397+
`$options` mirror the keys described in [HTTP Caching](/guide/http-caching) (`default_ttl`, `stale_while_revalidate`, `cache_methods`, etc.).
398+
399+
### `withoutCache()`
400+
401+
Disable caching for the handler.
402+
403+
```php
404+
public function withoutCache(): self
405+
```
406+
407+
### `getCache()`
408+
409+
Access the active cache backend (if any).
410+
411+
```php
412+
public function getCache(): ?CacheInterface
413+
```
414+
415+
### `isCacheEnabled()`
416+
417+
Check whether caching is currently enabled.
418+
419+
```php
420+
public function isCacheEnabled(): bool
421+
```
422+
423+
### `getCacheManager()`
424+
425+
Return the underlying `CacheManager` instance for advanced inspection.
426+
427+
```php
428+
public function getCacheManager(): ?\Fetch\Cache\CacheManager
429+
```
430+
384431
## Query Parameters
385432

386433
### `withQueryParameters()`
@@ -862,24 +909,153 @@ Sanitizes options for logging.
862909
protected function sanitizeOptions(array $options): array
863910
```
864911

865-
## Utility Methods
912+
## Debugging & Profiling
866913

867-
### `reset()`
914+
### `withLogLevel()`
868915

869-
Resets the handler state.
916+
Set the PSR-3 log level used for request/response traces.
870917

871918
```php
872-
public function reset(): ClientHandler
919+
public function withLogLevel(string $level): self
920+
```
921+
922+
### `withDebug()`
923+
924+
Enable or disable debug snapshots. Passing an array merges with `DebugInfo::getDefaultOptions()`.
925+
926+
```php
927+
public function withDebug(array|bool $options = true): self
928+
```
929+
930+
### `getDebugOptions()`
931+
932+
Retrieve the current debug options array.
933+
934+
```php
935+
public function getDebugOptions(): array
936+
```
937+
938+
### `getLastDebugInfo()`
939+
940+
Legacy accessor for the last captured `DebugInfo` instance (responses expose `getDebugInfo()` directly).
941+
942+
```php
943+
public function getLastDebugInfo(): ?\Fetch\Support\DebugInfo
873944
```
874945

875946
### `debug()`
876947

877-
Returns debug information about the request.
948+
Return a quick diagnostic array (URI, method, timeout, configured retries, etc.).
878949

879950
```php
880951
public function debug(): array
881952
```
882953

954+
### `withProfiler()`
955+
956+
Attach a `Fetch\Support\FetchProfiler` (or any `ProfilerInterface`) to capture timings/memory metrics.
957+
958+
```php
959+
public function withProfiler(FetchProfiler|ProfilerInterface $profiler): self
960+
```
961+
962+
### `getProfiler()`
963+
964+
Access the currently attached profiler instance.
965+
966+
```php
967+
public function getProfiler(): ?ProfilerInterface
968+
```
969+
970+
## Connection Pool & DNS
971+
972+
Pooling and DNS cache instances are shared across handlers (via `GlobalServices`). Changing the configuration affects every handler in the process.
973+
974+
### `withConnectionPool()`
975+
976+
Enable/disable pooling or provide a configuration array.
977+
978+
```php
979+
public function withConnectionPool(array|bool $config = true): self
980+
```
981+
982+
Accepted keys mirror `Fetch\Pool\PoolConfiguration` (`max_connections`, `max_per_host`, `keep_alive_timeout`, `connection_timeout`, `dns_cache_ttl`, etc.).
983+
984+
### `withHttp2()`
985+
986+
Enable HTTP/2 support and optional curl tuning.
987+
988+
```php
989+
public function withHttp2(array|bool $config = true): self
990+
```
991+
992+
### `getConnectionPool()`
993+
994+
```php
995+
public function getConnectionPool(): ?\Fetch\Pool\ConnectionPool
996+
```
997+
998+
### `getDnsCache()`
999+
1000+
```php
1001+
public function getDnsCache(): ?\Fetch\Pool\DnsCache
1002+
```
1003+
1004+
### `getHttp2Config()`
1005+
1006+
```php
1007+
public function getHttp2Config(): ?\Fetch\Pool\Http2Configuration
1008+
```
1009+
1010+
### `isPoolingEnabled()` / `isHttp2Enabled()`
1011+
1012+
```php
1013+
public function isPoolingEnabled(): bool
1014+
public function isHttp2Enabled(): bool
1015+
```
1016+
1017+
### `getPoolStats()` / `getDnsCacheStats()`
1018+
1019+
```php
1020+
public function getPoolStats(): array
1021+
public function getDnsCacheStats(): array
1022+
```
1023+
1024+
### `getConnectionDebugStats()`
1025+
1026+
Convenience helper (used by debug snapshots) that combines pool and DNS state.
1027+
1028+
```php
1029+
public function getConnectionDebugStats(): array
1030+
```
1031+
1032+
### `clearDnsCache()`
1033+
1034+
Clear the DNS cache (optionally for a single hostname).
1035+
1036+
```php
1037+
public function clearDnsCache(?string $hostname = null): self
1038+
```
1039+
1040+
### `closeAllConnections()` / `resetPool()`
1041+
1042+
Tear down pooled connections or fully reset the shared pool state.
1043+
1044+
```php
1045+
public function closeAllConnections(): self
1046+
public function resetPool(): self
1047+
```
1048+
1049+
## Utility Methods
1050+
1051+
### `reset()`
1052+
1053+
Resets the handler state.
1054+
1055+
```php
1056+
public function reset(): ClientHandler
1057+
```
1058+
8831059
## Testing Utilities
8841060

8851061
### `createMockResponse()`

0 commit comments

Comments
 (0)