Skip to content

Commit 1804655

Browse files
authored
Merge pull request #27 from programmatordev/OPA-34-create-cacheplugin-for-httpClient
Create CachePlugin for HttpClient
2 parents 6072624 + 356515c commit 1804655

14 files changed

+166
-243
lines changed

composer.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212
],
1313
"require": {
1414
"php": ">=8.1",
15+
"php-http/cache-plugin": "^1.8",
1516
"php-http/client-common": "^2.7",
1617
"php-http/discovery": "^1.18",
18+
"php-http/logger-plugin": "^1.3",
1719
"psr/cache": "^3.0",
1820
"psr/http-client": "^1.0",
1921
"psr/http-factory": "^1.0",

src/Config.php

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
namespace ProgrammatorDev\OpenWeatherMap;
44

5+
use ProgrammatorDev\OpenWeatherMap\Exception\ValidationException;
56
use ProgrammatorDev\OpenWeatherMap\HttpClient\HttpClientBuilder;
6-
use ProgrammatorDev\OpenWeatherMap\HttpClient\Plugin\LoggerPlugin;
77
use ProgrammatorDev\OpenWeatherMap\Language\Language;
88
use ProgrammatorDev\OpenWeatherMap\MeasurementSystem\MeasurementSystem;
99
use ProgrammatorDev\OpenWeatherMap\Validator\BlankValidatorTrait;
@@ -24,8 +24,6 @@ public function __construct(array $options = [])
2424
$resolver = new OptionsResolver();
2525
$this->configureOptions($resolver);
2626
$this->options = $resolver->resolve($options);
27-
28-
$this->configureAware();
2927
}
3028

3129
private function configureOptions(OptionsResolver $resolver): void
@@ -54,20 +52,14 @@ private function configureOptions(OptionsResolver $resolver): void
5452
$resolver->setAllowedValues('language', Language::getList());
5553
}
5654

57-
private function configureAware(): void
58-
{
59-
if ($this->getLogger() !== null) {
60-
$this->getHttpClientBuilder()->addPlugin(
61-
new LoggerPlugin($this->getLogger())
62-
);
63-
}
64-
}
65-
6655
public function getApplicationKey(): string
6756
{
6857
return $this->options['applicationKey'];
6958
}
7059

60+
/**
61+
* @throws ValidationException
62+
*/
7163
public function setApplicationKey(string $applicationKey): self
7264
{
7365
$this->validateBlank('applicationKey', $applicationKey);
@@ -82,6 +74,9 @@ public function getMeasurementSystem(): string
8274
return $this->options['measurementSystem'];
8375
}
8476

77+
/**
78+
* @throws ValidationException
79+
*/
8580
public function setMeasurementSystem(string $measurementSystem): self
8681
{
8782
$this->validateChoice('measurementSystem', $measurementSystem, MeasurementSystem::getList());
@@ -96,6 +91,9 @@ public function getLanguage(): string
9691
return $this->options['language'];
9792
}
9893

94+
/**
95+
* @throws ValidationException
96+
*/
9997
public function setLanguage(string $language): self
10098
{
10199
$this->validateChoice('language', $language, Language::getList());

src/Endpoint/AbstractEndpoint.php

Lines changed: 55 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,22 @@
22

33
namespace ProgrammatorDev\OpenWeatherMap\Endpoint;
44

5-
use Http\Client\Common\HttpMethodsClient;
5+
use Http\Client\Common\Plugin\CachePlugin;
6+
use Http\Client\Common\Plugin\LoggerPlugin;
67
use Http\Client\Exception;
7-
use ProgrammatorDev\OpenWeatherMap\Endpoint\Util\WithCacheInvalidationTrait;
8+
use ProgrammatorDev\OpenWeatherMap\Config;
89
use ProgrammatorDev\OpenWeatherMap\Endpoint\Util\WithCacheTtlTrait;
910
use ProgrammatorDev\OpenWeatherMap\Entity\Error;
1011
use ProgrammatorDev\OpenWeatherMap\Exception\BadRequestException;
1112
use ProgrammatorDev\OpenWeatherMap\Exception\NotFoundException;
1213
use ProgrammatorDev\OpenWeatherMap\Exception\TooManyRequestsException;
1314
use ProgrammatorDev\OpenWeatherMap\Exception\UnauthorizedException;
1415
use ProgrammatorDev\OpenWeatherMap\Exception\UnexpectedErrorException;
16+
use ProgrammatorDev\OpenWeatherMap\HttpClient\HttpClientBuilder;
17+
use ProgrammatorDev\OpenWeatherMap\HttpClient\Listener\LoggerCacheListener;
1518
use ProgrammatorDev\OpenWeatherMap\HttpClient\ResponseMediator;
1619
use ProgrammatorDev\OpenWeatherMap\OpenWeatherMap;
1720
use Psr\Cache\CacheItemPoolInterface;
18-
use Psr\Cache\InvalidArgumentException;
1921
use Psr\Http\Message\ResponseInterface;
2022
use Psr\Http\Message\StreamInterface;
2123
use Psr\Http\Message\UriInterface;
@@ -24,31 +26,30 @@
2426
class AbstractEndpoint
2527
{
2628
use WithCacheTtlTrait;
27-
use WithCacheInvalidationTrait;
2829

29-
private HttpMethodsClient $httpClient;
30+
private Config $config;
3031

31-
private ?LoggerInterface $logger;
32+
private HttpClientBuilder $httpClientBuilder;
3233

3334
private ?CacheItemPoolInterface $cache;
3435

35-
private bool $cacheInvalidation = false;
36-
37-
protected \DateInterval|int|null $cacheTtl = 60 * 10; // 10 minutes
36+
private ?LoggerInterface $logger;
3837

3938
protected string $measurementSystem;
4039

4140
protected string $language;
4241

42+
protected ?int $cacheTtl = 60 * 10; // 10 minutes
43+
4344
public function __construct(protected OpenWeatherMap $api)
4445
{
45-
$config = $this->api->getConfig();
46+
$this->config = $this->api->getConfig();
4647

47-
$this->httpClient = $config->getHttpClientBuilder()->getHttpClient();
48-
$this->logger = $config->getLogger();
49-
$this->cache = $config->getCache();
50-
$this->measurementSystem = $config->getMeasurementSystem();
51-
$this->language = $config->getLanguage();
48+
$this->httpClientBuilder = $this->config->getHttpClientBuilder();
49+
$this->cache = $this->config->getCache();
50+
$this->logger = $this->config->getLogger();
51+
$this->measurementSystem = $this->config->getMeasurementSystem();
52+
$this->language = $this->config->getLanguage();
5253
}
5354

5455
/**
@@ -58,7 +59,6 @@ public function __construct(protected OpenWeatherMap $api)
5859
* @throws TooManyRequestsException
5960
* @throws UnauthorizedException
6061
* @throws UnexpectedErrorException
61-
* @throws InvalidArgumentException
6262
*/
6363
protected function sendRequest(
6464
string $method,
@@ -68,79 +68,61 @@ protected function sendRequest(
6868
StreamInterface|string $body = null
6969
): array
7070
{
71-
$uri = $this->buildUrl($baseUrl, $query);
72-
73-
// If there is a cache adapter, save responses into cache
74-
if ($this->cache !== null) {
75-
$cacheKey = $this->getCacheKey($uri);
76-
77-
// Invalidate cache to force new
78-
if ($this->cacheInvalidation === true) {
79-
$this->logger?->info('Cache invalidated', ['key' => $cacheKey]);
80-
81-
$this->cache->deleteItem($cacheKey);
82-
}
71+
$this->configurePlugins();
8372

84-
$cacheItem = $this->cache->getItem($cacheKey);
85-
86-
if ($cacheItem->isHit()) {
87-
$this->logger?->info(\sprintf('Cache hit: %s %s', $method, $uri), ['key' => $cacheKey]);
88-
}
89-
else {
90-
$response = ResponseMediator::toArray(
91-
$this->handleRequest($method, $uri, $headers, $body)
92-
);
93-
94-
$cacheItem->set($response);
95-
$cacheItem->expiresAfter($this->cacheTtl);
73+
$uri = $this->buildUrl($baseUrl, $query);
74+
$response = $this->httpClientBuilder->getHttpClient()->send($method, $uri, $headers, $body);
9675

97-
$this->cache->save($cacheItem);
76+
if (($statusCode = $response->getStatusCode()) >= 400) {
77+
$this->handleResponseErrors($response, $statusCode);
78+
}
9879

99-
$this->logger?->info('Cached response', ['ttl' => $this->cacheTtl, 'key' => $cacheKey]);
100-
}
80+
return ResponseMediator::toArray($response);
81+
}
10182

102-
return $cacheItem->get();
83+
private function configurePlugins(): void
84+
{
85+
// Plugin order is important
86+
// CachePlugin should come first, otherwise the LoggerPlugin will log requests even if they are cached
87+
if ($this->cache !== null) {
88+
$this->httpClientBuilder->addPlugin(
89+
new CachePlugin($this->cache, $this->httpClientBuilder->getStreamFactory(), [
90+
'default_ttl' => $this->cacheTtl,
91+
'cache_lifetime' => 0,
92+
'cache_listeners' => ($this->logger !== null)
93+
? [new LoggerCacheListener($this->logger)]
94+
: []
95+
])
96+
);
10397
}
10498

105-
return ResponseMediator::toArray(
106-
$this->handleRequest($method, $uri, $headers, $body)
107-
);
99+
if ($this->logger !== null) {
100+
$this->httpClientBuilder->addPlugin(
101+
new LoggerPlugin($this->logger)
102+
);
103+
}
108104
}
109105

110106
/**
111-
* @throws Exception
112107
* @throws NotFoundException
113108
* @throws UnexpectedErrorException
114109
* @throws TooManyRequestsException
115-
* @throws BadRequestException
116110
* @throws UnauthorizedException
111+
* @throws BadRequestException
117112
*/
118-
private function handleRequest(
119-
string $method,
120-
string $uri,
121-
array $headers,
122-
StreamInterface|string $body = null
123-
): ResponseInterface
113+
private function handleResponseErrors(ResponseInterface $response, int $statusCode): void
124114
{
125-
$response = $this->httpClient->send($method, $uri, $headers, $body);
126-
$statusCode = $response->getStatusCode();
127-
128-
// If API returns an error, throw exception
129-
if ($statusCode >= 400) {
130-
$error = new Error(
131-
ResponseMediator::toArray($response)
132-
);
133-
134-
match ($statusCode) {
135-
400 => throw new BadRequestException($error),
136-
401 => throw new UnauthorizedException($error),
137-
404 => throw new NotFoundException($error),
138-
429 => throw new TooManyRequestsException($error),
139-
default => throw new UnexpectedErrorException($error)
140-
};
141-
}
115+
$error = new Error(
116+
ResponseMediator::toArray($response)
117+
);
142118

143-
return $response;
119+
match ($statusCode) {
120+
400 => throw new BadRequestException($error),
121+
401 => throw new UnauthorizedException($error),
122+
404 => throw new NotFoundException($error),
123+
429 => throw new TooManyRequestsException($error),
124+
default => throw new UnexpectedErrorException($error)
125+
};
144126
}
145127

146128
private function buildUrl(UriInterface|string $baseUrl, array $query): string
@@ -156,9 +138,4 @@ private function buildUrl(UriInterface|string $baseUrl, array $query): string
156138

157139
return \sprintf('%s?%s', $baseUrl, http_build_query($query));
158140
}
159-
160-
private function getCacheKey(string $value): string
161-
{
162-
return md5($value);
163-
}
164141
}

src/Endpoint/AirPollutionEndpoint.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
use ProgrammatorDev\OpenWeatherMap\Validator\CoordinateValidatorTrait;
1515
use ProgrammatorDev\OpenWeatherMap\Validator\LessThanValidatorTrait;
1616
use ProgrammatorDev\OpenWeatherMap\Validator\RangeValidatorTrait;
17-
use Psr\Cache\InvalidArgumentException;
1817

1918
class AirPollutionEndpoint extends AbstractEndpoint
2019
{
@@ -35,7 +34,6 @@ class AirPollutionEndpoint extends AbstractEndpoint
3534
* @throws TooManyRequestsException
3635
* @throws UnauthorizedException
3736
* @throws UnexpectedErrorException
38-
* @throws InvalidArgumentException
3937
* @throws ValidationException
4038
*/
4139
public function getCurrent(float $latitude, float $longitude): CurrentAirPollution
@@ -61,7 +59,6 @@ public function getCurrent(float $latitude, float $longitude): CurrentAirPolluti
6159
* @throws TooManyRequestsException
6260
* @throws UnauthorizedException
6361
* @throws UnexpectedErrorException
64-
* @throws InvalidArgumentException
6562
* @throws ValidationException
6663
*/
6764
public function getForecast(float $latitude, float $longitude): AirPollutionList
@@ -87,7 +84,6 @@ public function getForecast(float $latitude, float $longitude): AirPollutionList
8784
* @throws TooManyRequestsException
8885
* @throws UnauthorizedException
8986
* @throws UnexpectedErrorException
90-
* @throws InvalidArgumentException
9187
* @throws ValidationException
9288
*/
9389
public function getHistory(

src/Endpoint/GeocodingEndpoint.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
use ProgrammatorDev\OpenWeatherMap\Validator\BlankValidatorTrait;
1616
use ProgrammatorDev\OpenWeatherMap\Validator\CoordinateValidatorTrait;
1717
use ProgrammatorDev\OpenWeatherMap\Validator\GreaterThanValidatorTrait;
18-
use Psr\Cache\InvalidArgumentException;
1918

2019
class GeocodingEndpoint extends AbstractEndpoint
2120
{
@@ -32,7 +31,7 @@ class GeocodingEndpoint extends AbstractEndpoint
3231

3332
private string $urlGeocodingReverse = 'https://api.openweathermap.org/geo/1.0/reverse';
3433

35-
protected \DateInterval|int|null $cacheTtl = 60 * 60 * 24 * 30; // 30 days
34+
protected ?int $cacheTtl = 60 * 60 * 24 * 30; // 30 days
3635

3736
/**
3837
* @return Location[]
@@ -42,7 +41,6 @@ class GeocodingEndpoint extends AbstractEndpoint
4241
* @throws TooManyRequestsException
4342
* @throws UnauthorizedException
4443
* @throws UnexpectedErrorException
45-
* @throws InvalidArgumentException
4644
* @throws ValidationException
4745
*/
4846
public function getCoordinatesByLocationName(string $locationName, int $numResults = self::NUM_RESULTS): array
@@ -69,7 +67,6 @@ public function getCoordinatesByLocationName(string $locationName, int $numResul
6967
* @throws TooManyRequestsException
7068
* @throws UnauthorizedException
7169
* @throws UnexpectedErrorException
72-
* @throws InvalidArgumentException
7370
* @throws ValidationException
7471
*/
7572
public function getCoordinatesByZipCode(string $zipCode, string $countryCode): ZipCodeLocation
@@ -96,7 +93,6 @@ public function getCoordinatesByZipCode(string $zipCode, string $countryCode): Z
9693
* @throws TooManyRequestsException
9794
* @throws UnauthorizedException
9895
* @throws UnexpectedErrorException
99-
* @throws InvalidArgumentException
10096
* @throws ValidationException
10197
*/
10298
public function getLocationNameByCoordinates(float $latitude, float $longitude, int $numResults = self::NUM_RESULTS): array

src/Endpoint/OneCallEndpoint.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
use ProgrammatorDev\OpenWeatherMap\Exception\ValidationException;
1717
use ProgrammatorDev\OpenWeatherMap\Validator\CoordinateValidatorTrait;
1818
use ProgrammatorDev\OpenWeatherMap\Validator\LessThanValidatorTrait;
19-
use Psr\Cache\InvalidArgumentException;
2019

2120
class OneCallEndpoint extends AbstractEndpoint
2221
{
@@ -38,7 +37,6 @@ class OneCallEndpoint extends AbstractEndpoint
3837
* @throws TooManyRequestsException
3938
* @throws UnauthorizedException
4039
* @throws UnexpectedErrorException
41-
* @throws InvalidArgumentException
4240
* @throws ValidationException
4341
*/
4442
public function getWeather(float $latitude, float $longitude): OneCall
@@ -66,7 +64,6 @@ public function getWeather(float $latitude, float $longitude): OneCall
6664
* @throws TooManyRequestsException
6765
* @throws UnauthorizedException
6866
* @throws UnexpectedErrorException
69-
* @throws InvalidArgumentException
7067
* @throws ValidationException
7168
*/
7269
public function getHistoryMoment(float $latitude, float $longitude, \DateTimeInterface $dateTime): HistoryMoment
@@ -96,7 +93,6 @@ public function getHistoryMoment(float $latitude, float $longitude, \DateTimeInt
9693
* @throws TooManyRequestsException
9794
* @throws UnauthorizedException
9895
* @throws UnexpectedErrorException
99-
* @throws InvalidArgumentException
10096
* @throws ValidationException
10197
*/
10298
public function getHistoryDaySummary(float $latitude, float $longitude, \DateTimeInterface $dateTime): HistoryDaySummary

0 commit comments

Comments
 (0)