-
Notifications
You must be signed in to change notification settings - Fork 16
add phpstan to ci build #74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
99bba50
add phpstan to ci build
dbu 7079a08
fixes for phpstan level 1
dbu 66aa9f2
phpstan level 2
dbu 297fda5
fix phpstan level 2
dbu 664c7fd
phpstan level 3
dbu aa82a78
fix phpstan level 3
dbu 757753a
phpstan level 4
dbu 220a4b5
prefer array_key_exists over isset for more robust code
dbu 95a502b
phpstan level 7
dbu cb8755b
cleanup type hints and phpdoc
dbu a519bf9
phpstan level 8
dbu 3f0266a
be more defensive about the value of the cache hit
dbu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[*.yml] | ||
indent_size = 2 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
name: Static analysis | ||
|
||
on: | ||
push: | ||
branches: | ||
- master | ||
pull_request: | ||
|
||
jobs: | ||
phpstan: | ||
name: PHPStan | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- name: Checkout code | ||
uses: actions/checkout@v2 | ||
|
||
- name: PHPStan | ||
uses: docker://oskarstark/phpstan-ga | ||
with: | ||
args: analyze --no-progress |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,8 @@ name: tests | |
|
||
on: | ||
push: | ||
branches: | ||
- master | ||
pull_request: | ||
|
||
jobs: | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
parameters: | ||
level: 8 | ||
paths: | ||
- src | ||
treatPhpDocTypesAsCertain: false |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ | |
use Http\Client\Common\Plugin\Cache\Listener\CacheListener; | ||
use Http\Message\StreamFactory; | ||
use Http\Promise\FulfilledPromise; | ||
use Http\Promise\Promise; | ||
use Psr\Cache\CacheItemInterface; | ||
use Psr\Cache\CacheItemPoolInterface; | ||
use Psr\Http\Message\RequestInterface; | ||
|
@@ -39,20 +40,20 @@ final class CachePlugin implements Plugin | |
private $streamFactory; | ||
|
||
/** | ||
* @var array | ||
* @var mixed[] | ||
*/ | ||
private $config; | ||
|
||
/** | ||
* Cache directives indicating if a response can not be cached. | ||
* | ||
* @var array | ||
* @var string[] | ||
*/ | ||
private $noCacheFlags = ['no-cache', 'private', 'no-store']; | ||
|
||
/** | ||
* @param StreamFactory|StreamFactoryInterface $streamFactory | ||
* @param array $config { | ||
* @param mixed[] $config { | ||
* | ||
* @var bool $respect_cache_headers Whether to look at the cache directives or ignore them | ||
* @var int $default_ttl (seconds) If we do not respect cache headers or can't calculate a good ttl, use this | ||
|
@@ -61,9 +62,9 @@ final class CachePlugin implements Plugin | |
* @var int $cache_lifetime (seconds) To support serving a previous stale response when the server answers 304 | ||
* we have to store the cache for a longer time than the server originally says it is valid for. | ||
* We store a cache item for $cache_lifetime + max age of the response. | ||
* @var array $methods list of request methods which can be cached | ||
* @var array $blacklisted_paths list of regex for URLs explicitly not to be cached | ||
* @var array $respect_response_cache_directives list of cache directives this plugin will respect while caching responses | ||
* @var string[] $methods list of request methods which can be cached | ||
* @var string[] $blacklisted_paths list of regex for URLs explicitly not to be cached | ||
* @var string[] $respect_response_cache_directives list of cache directives this plugin will respect while caching responses | ||
* @var CacheKeyGenerator $cache_key_generator an object to generate the cache key. Defaults to a new instance of SimpleGenerator | ||
* @var CacheListener[] $cache_listeners an array of objects to act on the response based on the results of the cache check. | ||
* Defaults to an empty array | ||
|
@@ -78,7 +79,7 @@ public function __construct(CacheItemPoolInterface $pool, $streamFactory, array | |
$this->pool = $pool; | ||
$this->streamFactory = $streamFactory; | ||
|
||
if (isset($config['respect_cache_headers']) && isset($config['respect_response_cache_directives'])) { | ||
if (\array_key_exists('respect_cache_headers', $config) && \array_key_exists('respect_response_cache_directives', $config)) { | ||
throw new \InvalidArgumentException('You can\'t provide config option "respect_cache_headers" and "respect_response_cache_directives". Use "respect_response_cache_directives" instead.'); | ||
} | ||
|
||
|
@@ -96,14 +97,14 @@ public function __construct(CacheItemPoolInterface $pool, $streamFactory, array | |
* cache responses with `private` cache directive. | ||
* | ||
* @param StreamFactory|StreamFactoryInterface $streamFactory | ||
* @param array $config For all possible config options see the constructor docs | ||
* @param mixed[] $config For all possible config options see the constructor docs | ||
* | ||
* @return CachePlugin | ||
*/ | ||
public static function clientCache(CacheItemPoolInterface $pool, $streamFactory, array $config = []) | ||
{ | ||
// Allow caching of private requests | ||
if (isset($config['respect_response_cache_directives'])) { | ||
if (\array_key_exists('respect_response_cache_directives', $config)) { | ||
$config['respect_response_cache_directives'][] = 'no-cache'; | ||
$config['respect_response_cache_directives'][] = 'max-age'; | ||
$config['respect_response_cache_directives'] = array_unique($config['respect_response_cache_directives']); | ||
|
@@ -119,7 +120,7 @@ public static function clientCache(CacheItemPoolInterface $pool, $streamFactory, | |
* cache responses with the `private`or `no-cache` directives. | ||
* | ||
* @param StreamFactory|StreamFactoryInterface $streamFactory | ||
* @param array $config For all possible config options see the constructor docs | ||
* @param mixed[] $config For all possible config options see the constructor docs | ||
* | ||
* @return CachePlugin | ||
*/ | ||
|
@@ -128,6 +129,11 @@ public static function serverCache(CacheItemPoolInterface $pool, $streamFactory, | |
return new self($pool, $streamFactory, $config); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
* | ||
* @return Promise Resolves a PSR-7 Response or fails with an Http\Client\Exception (The same as HttpAsyncClient) | ||
*/ | ||
protected function doHandleRequest(RequestInterface $request, callable $next, callable $first) | ||
{ | ||
$method = strtoupper($request->getMethod()); | ||
|
@@ -146,22 +152,24 @@ protected function doHandleRequest(RequestInterface $request, callable $next, ca | |
|
||
if ($cacheItem->isHit()) { | ||
$data = $cacheItem->get(); | ||
// The array_key_exists() is to be removed in 2.0. | ||
if (array_key_exists('expiresAt', $data) && (null === $data['expiresAt'] || time() < $data['expiresAt'])) { | ||
// This item is still valid according to previous cache headers | ||
$response = $this->createResponseFromCacheItem($cacheItem); | ||
$response = $this->handleCacheListeners($request, $response, true, $cacheItem); | ||
|
||
return new FulfilledPromise($response); | ||
} | ||
if (is_array($data)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. phpstan does not complain, but CacheItem::get is defined to return |
||
// The array_key_exists() is to be removed in 2.0. | ||
if (array_key_exists('expiresAt', $data) && (null === $data['expiresAt'] || time() < $data['expiresAt'])) { | ||
// This item is still valid according to previous cache headers | ||
$response = $this->createResponseFromCacheItem($cacheItem); | ||
$response = $this->handleCacheListeners($request, $response, true, $cacheItem); | ||
|
||
return new FulfilledPromise($response); | ||
} | ||
|
||
// Add headers to ask the server if this cache is still valid | ||
if ($modifiedSinceValue = $this->getModifiedSinceHeaderValue($cacheItem)) { | ||
$request = $request->withHeader('If-Modified-Since', $modifiedSinceValue); | ||
} | ||
// Add headers to ask the server if this cache is still valid | ||
if ($modifiedSinceValue = $this->getModifiedSinceHeaderValue($cacheItem)) { | ||
$request = $request->withHeader('If-Modified-Since', $modifiedSinceValue); | ||
} | ||
|
||
if ($etag = $this->getETag($cacheItem)) { | ||
$request = $request->withHeader('If-None-Match', $etag); | ||
if ($etag = $this->getETag($cacheItem)) { | ||
$request = $request->withHeader('If-None-Match', $etag); | ||
} | ||
} | ||
} | ||
|
||
|
@@ -207,22 +215,20 @@ protected function doHandleRequest(RequestInterface $request, callable $next, ca | |
$this->pool->save($cacheItem); | ||
} | ||
|
||
return $this->handleCacheListeners($request, $response, false, isset($cacheItem) ? $cacheItem : null); | ||
return $this->handleCacheListeners($request, $response, false, $cacheItem); | ||
}); | ||
} | ||
|
||
/** | ||
* Calculate the timestamp when this cache item should be dropped from the cache. The lowest value that can be | ||
* returned is $maxAge. | ||
* | ||
* @param int|null $maxAge | ||
* | ||
* @return int|null Unix system time passed to the PSR-6 cache | ||
*/ | ||
private function calculateCacheItemExpiresAfter($maxAge) | ||
private function calculateCacheItemExpiresAfter(?int $maxAge): ?int | ||
{ | ||
if (null === $this->config['cache_lifetime'] && null === $maxAge) { | ||
return; | ||
return null; | ||
} | ||
|
||
return $this->config['cache_lifetime'] + $maxAge; | ||
|
@@ -232,14 +238,12 @@ private function calculateCacheItemExpiresAfter($maxAge) | |
* Calculate the timestamp when a response expires. After that timestamp, we need to send a | ||
* If-Modified-Since / If-None-Match request to validate the response. | ||
* | ||
* @param int|null $maxAge | ||
* | ||
* @return int|null Unix system time. A null value means that the response expires when the cache item expires | ||
*/ | ||
private function calculateResponseExpiresAt($maxAge) | ||
private function calculateResponseExpiresAt(?int $maxAge): ?int | ||
{ | ||
if (null === $maxAge) { | ||
return; | ||
return null; | ||
} | ||
|
||
return time() + $maxAge; | ||
|
@@ -268,10 +272,8 @@ protected function isCacheable(ResponseInterface $response) | |
|
||
/** | ||
* Verify that we can cache this request. | ||
* | ||
* @return bool | ||
*/ | ||
private function isCacheableRequest(RequestInterface $request) | ||
private function isCacheableRequest(RequestInterface $request): bool | ||
{ | ||
$uri = $request->getUri()->__toString(); | ||
foreach ($this->config['blacklisted_paths'] as $regex) { | ||
|
@@ -290,7 +292,7 @@ private function isCacheableRequest(RequestInterface $request) | |
* | ||
* @return bool|string The value of the directive, true if directive without value, false if directive not present | ||
*/ | ||
private function getCacheControlDirective(ResponseInterface $response, $name) | ||
private function getCacheControlDirective(ResponseInterface $response, string $name) | ||
{ | ||
$headers = $response->getHeader('Cache-Control'); | ||
foreach ($headers as $header) { | ||
|
@@ -307,22 +309,19 @@ private function getCacheControlDirective(ResponseInterface $response, $name) | |
return false; | ||
} | ||
|
||
/** | ||
* @return string | ||
*/ | ||
private function createCacheKey(RequestInterface $request) | ||
private function createCacheKey(RequestInterface $request): string | ||
{ | ||
$key = $this->config['cache_key_generator']->generate($request); | ||
|
||
return hash($this->config['hash_algo'], $key); | ||
} | ||
|
||
/** | ||
* Get a ttl in seconds. It could return null if we do not respect cache headers and got no defaultTtl. | ||
* Get a ttl in seconds. | ||
* | ||
* @return int|null | ||
* Returns null if we do not respect cache headers and got no defaultTtl. | ||
*/ | ||
private function getMaxAge(ResponseInterface $response) | ||
private function getMaxAge(ResponseInterface $response): ?int | ||
{ | ||
if (!in_array('max-age', $this->config['respect_response_cache_directives'], true)) { | ||
return $this->config['default_ttl']; | ||
|
@@ -333,7 +332,7 @@ private function getMaxAge(ResponseInterface $response) | |
if (!is_bool($maxAge)) { | ||
$ageHeaders = $response->getHeader('Age'); | ||
foreach ($ageHeaders as $age) { | ||
return $maxAge - ((int) $age); | ||
return ((int) $maxAge) - ((int) $age); | ||
} | ||
|
||
return (int) $maxAge; | ||
|
@@ -351,7 +350,7 @@ private function getMaxAge(ResponseInterface $response) | |
/** | ||
* Configure an options resolver. | ||
*/ | ||
private function configureOptions(OptionsResolver $resolver) | ||
private function configureOptions(OptionsResolver $resolver): void | ||
{ | ||
$resolver->setDefaults([ | ||
'cache_lifetime' => 86400 * 30, // 30 days | ||
|
@@ -398,10 +397,7 @@ private function configureOptions(OptionsResolver $resolver) | |
}); | ||
} | ||
|
||
/** | ||
* @return ResponseInterface | ||
*/ | ||
private function createResponseFromCacheItem(CacheItemInterface $cacheItem) | ||
private function createResponseFromCacheItem(CacheItemInterface $cacheItem): ResponseInterface | ||
{ | ||
$data = $cacheItem->get(); | ||
|
||
|
@@ -415,22 +411,18 @@ private function createResponseFromCacheItem(CacheItemInterface $cacheItem) | |
throw new RewindStreamException('Cannot rewind stream.', 0, $e); | ||
} | ||
|
||
$response = $response->withBody($stream); | ||
|
||
return $response; | ||
return $response->withBody($stream); | ||
} | ||
|
||
/** | ||
* Get the value of the "If-Modified-Since" header. | ||
* | ||
* @return string|null | ||
* Get the value for the "If-Modified-Since" header. | ||
*/ | ||
private function getModifiedSinceHeaderValue(CacheItemInterface $cacheItem) | ||
private function getModifiedSinceHeaderValue(CacheItemInterface $cacheItem): ?string | ||
{ | ||
$data = $cacheItem->get(); | ||
// The isset() is to be removed in 2.0. | ||
if (!isset($data['createdAt'])) { | ||
return; | ||
return null; | ||
} | ||
|
||
$modified = new \DateTime('@'.$data['createdAt']); | ||
|
@@ -441,33 +433,28 @@ private function getModifiedSinceHeaderValue(CacheItemInterface $cacheItem) | |
|
||
/** | ||
* Get the ETag from the cached response. | ||
* | ||
* @return string|null | ||
*/ | ||
private function getETag(CacheItemInterface $cacheItem) | ||
private function getETag(CacheItemInterface $cacheItem): ?string | ||
{ | ||
$data = $cacheItem->get(); | ||
// The isset() is to be removed in 2.0. | ||
if (!isset($data['etag'])) { | ||
return; | ||
return null; | ||
} | ||
|
||
foreach ($data['etag'] as $etag) { | ||
if (!empty($etag)) { | ||
return $etag; | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
/** | ||
* Call the cache listeners, if they are set. | ||
* | ||
* @param bool $cacheHit | ||
* @param CacheItemInterface|null $cacheItem | ||
* | ||
* @return ResponseInterface | ||
* Call the registered cache listeners. | ||
*/ | ||
private function handleCacheListeners(RequestInterface $request, ResponseInterface $response, $cacheHit, $cacheItem) | ||
private function handleCacheListeners(RequestInterface $request, ResponseInterface $response, bool $cacheHit, ?CacheItemInterface $cacheItem): ResponseInterface | ||
{ | ||
foreach ($this->config['cache_listeners'] as $cacheListener) { | ||
$response = $cacheListener->onCacheResponse($request, $response, $cacheHit, $cacheItem); | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this prevents us from running every build twice