Skip to content

Commit d360e3a

Browse files
authored
Merge pull request #25 from Flowpack/fixesAndImprovements
TASK: Add PhpStan L8 and CodeSniffer to CI
2 parents a92fbfb + 4c1d446 commit d360e3a

8 files changed

Lines changed: 134 additions & 37 deletions

File tree

‎.github/workflows/build.yml‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
name: PHP Test
2+
3+
on:
4+
push:
5+
branches: [ "main" ]
6+
pull_request:
7+
branches: [ "main" ]
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
test:
14+
name: "Test (PHP ${{ matrix.php-versions }}, Neos ${{ matrix.neos-versions }})"
15+
16+
strategy:
17+
fail-fast: false
18+
matrix:
19+
php-versions: ['8.2', '8.3', '8.4', '8.5']
20+
neos-versions: ['8.3','8.4']
21+
22+
runs-on: ubuntu-latest
23+
24+
steps:
25+
- uses: actions/checkout@v4
26+
27+
- name: Validate composer.json and composer.lock
28+
run: composer validate --strict
29+
30+
- name: Cache Composer packages
31+
id: composer-cache
32+
uses: actions/cache@v3
33+
with:
34+
path: vendor
35+
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
36+
restore-keys: |
37+
${{ runner.os }}-php-
38+
39+
- name: Setup PHP
40+
uses: shivammathur/setup-php@v2
41+
with:
42+
php-version: ${{ matrix.php-versions }}
43+
extensions: mbstring, xml, json, zlib, iconv, intl, pdo_sqlite
44+
ini-values: date.timezone="Africa/Tunis", opcache.fast_shutdown=0, apc.enable_cli=on
45+
46+
- name: Set Neos Version
47+
run: composer require neos/neos ^${{ matrix.neos-versions }} --no-progress --no-interaction
48+
49+
- name: Install dependencies
50+
run: composer install --prefer-dist --no-progress
51+
52+
- name: Run test suite
53+
run: composer test

‎.gitignore‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
composer.lock
2+
Packages
3+
vendor

‎Classes/Aspects/ContentCacheAspect.php‎

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,7 @@
1313
*/
1414
class ContentCacheAspect
1515
{
16-
private $hadUncachedSegments = false;
17-
18-
private $cacheTags = [];
19-
20-
/**
21-
* @var null|int
22-
*/
23-
private $shortestLifetime = null;
16+
private bool $hadUncachedSegments = false;
2417

2518
/**
2619
* @Flow\Inject
@@ -31,7 +24,7 @@ class ContentCacheAspect
3124
/**
3225
* @Flow\Before("method(Neos\Fusion\Core\Cache\ContentCache->(createUncachedSegment)())")
3326
*/
34-
public function grabUncachedSegment(JoinPointInterface $joinPoint)
27+
public function grabUncachedSegment(JoinPointInterface $joinPoint): void
3528
{
3629
$this->hadUncachedSegments = true;
3730
}
@@ -45,7 +38,7 @@ public function grabUncachedSegment(JoinPointInterface $joinPoint)
4538
*
4639
* @throws \Neos\Utility\Exception\PropertyNotAccessibleException
4740
*/
48-
public function interceptLegacyNodeCacheFlush(JoinPointInterface $joinPoint)
41+
public function interceptLegacyNodeCacheFlush(JoinPointInterface $joinPoint): void
4942
{
5043
$object = $joinPoint->getProxy();
5144

@@ -60,7 +53,7 @@ public function interceptLegacyNodeCacheFlush(JoinPointInterface $joinPoint)
6053
*
6154
* @throws \Neos\Utility\Exception\PropertyNotAccessibleException
6255
*/
63-
public function interceptNodeCacheFlush(JoinPointInterface $joinPoint)
56+
public function interceptNodeCacheFlush(JoinPointInterface $joinPoint): void
6457
{
6558
$tags = $joinPoint->getMethodArgument('tagsToFlush');
6659
$tags = array_map([$this, 'sanitizeTag'], array_keys($tags));

‎Classes/Cache/MetadataAwareStringFrontend.php‎

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<?php
2+
23
namespace Flowpack\FullPageCache\Cache;
34

45
use Neos\Cache\Frontend\StringFrontend;
@@ -18,7 +19,7 @@ class MetadataAwareStringFrontend extends StringFrontend
1819
/**
1920
* Store metadata of all loaded cache entries indexed by identifier
2021
*
21-
* @var array
22+
* @var array<string, array{identifier:string, tags: string[], lifetime: int|null}>
2223
*/
2324
protected $metadata = [];
2425

@@ -38,6 +39,10 @@ class MetadataAwareStringFrontend extends StringFrontend
3839
* Set a cache entry and store additional metadata (tags and lifetime)
3940
*
4041
* {@inheritdoc}
42+
*
43+
* @param string $content
44+
* @param string[] $tags
45+
* @return void
4146
*/
4247
public function set(string $entryIdentifier, $content, array $tags = [], ?int $lifetime = null)
4348
{
@@ -47,6 +52,8 @@ public function set(string $entryIdentifier, $content, array $tags = [], ?int $l
4752

4853
/**
4954
* {@inheritdoc}
55+
*
56+
* @return string|false
5057
*/
5158
public function get(string $entryIdentifier)
5259
{
@@ -60,6 +67,7 @@ public function get(string $entryIdentifier)
6067

6168
/**
6269
* {@inheritdoc}
70+
* @return array<string,string>
6371
*/
6472
public function getByTag(string $tag): array
6573
{
@@ -76,16 +84,12 @@ public function getByTag(string $tag): array
7684
*
7785
* @param string $content
7886
* @param string $entryIdentifier The identifier metadata
79-
* @param array $tags The tags metadata
87+
* @param string[] $tags The tags metadata
8088
* @param integer $lifetime The lifetime metadata
8189
* @return string The content including the serialized metadata
82-
* @throws InvalidDataTypeException
8390
*/
84-
protected function insertMetadata($content, $entryIdentifier, array $tags, $lifetime)
91+
protected function insertMetadata(string $content, string $entryIdentifier, array $tags, ?int $lifetime)
8592
{
86-
if (!is_string($content)) {
87-
throw new InvalidDataTypeException('Given data is of type "' . gettype($content) . '", but a string is expected for string cache.', 1433155737);
88-
}
8993
$metadata = [
9094
'identifier' => $entryIdentifier,
9195
'tags' => $tags,
@@ -105,7 +109,7 @@ protected function insertMetadata($content, $entryIdentifier, array $tags, $life
105109
* @return string The content without metadata
106110
* @throws InvalidDataTypeException
107111
*/
108-
protected function extractMetadata($entryIdentifier, $content)
112+
protected function extractMetadata($entryIdentifier, $content): string
109113
{
110114
$separatorIndex = strpos($content, self::SEPARATOR);
111115
if ($separatorIndex === false) {
@@ -115,6 +119,7 @@ protected function extractMetadata($entryIdentifier, $content)
115119
} else {
116120
throw $exception;
117121
}
122+
return $content;
118123
}
119124

120125
$metadataJson = substr($content, 0, $separatorIndex);
@@ -134,9 +139,9 @@ protected function extractMetadata($entryIdentifier, $content)
134139
}
135140

136141
/**
137-
* @return array Metadata of all loaded entries (indexed by identifier)
142+
* @return array<string, array{identifier:string, tags?: string[], lifetime?: int|null}> Metadata of all loaded entries (indexed by identifier)
138143
*/
139-
public function getAllMetadata()
144+
public function getAllMetadata(): array
140145
{
141146
return $this->metadata;
142147
}

‎Classes/Middleware/FusionAutoconfigurationMiddleware.php‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
<?php
2+
23
declare(strict_types=1);
34

45
namespace Flowpack\FullPageCache\Middleware;
56

7+
use Flowpack\FullPageCache\Domain\Dto\FusionCacheInformation;
68
use Neos\Flow\Annotations as Flow;
79
use Psr\Http\Message\ResponseInterface;
810
use Psr\Http\Message\ServerRequestInterface;
@@ -47,7 +49,10 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
4749
$response = $response->withoutHeader(self::HEADER_ENABLED);
4850
}
4951

50-
list($hasUncachedSegments, $tags, $lifetime) = $this->getFusionCacheInformations();
52+
$cacheMetadata = $this->getFusionCacheInformations();
53+
$hasUncachedSegments = $cacheMetadata['hasUncachedSegments'];
54+
$tags = $cacheMetadata['tags'];
55+
$lifetime = $cacheMetadata['lifetime'];
5156

5257
if ($response->hasHeader('Set-Cookie') || $hasUncachedSegments) {
5358
return $response;
@@ -61,23 +66,24 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
6166
->withHeader(RequestCacheMiddleware::HEADER_TAGS, $tags);
6267
}
6368

69+
6470
if ($lifetime) {
6571
$response = $response
66-
->withHeader(RequestCacheMiddleware::HEADER_LIFTIME, $lifetime);
72+
->withHeader(RequestCacheMiddleware::HEADER_LIFETIME, (string)$lifetime);
6773
}
6874

6975
return $response;
7076
}
7177

7278
/**
7379
* Get cache tags and lifetime from the cache metadata that was extracted by the special cache frontend for content cache
74-
*
75-
* @return array with first "hasUncachedSegments", "tags" and "lifetime"
80+
* @return array{hasUncachedSegments:bool, tags:string[], lifetime: ?int}
7681
*/
7782
public function getFusionCacheInformations(): array
7883
{
7984
$lifetime = null;
8085
$tags = [];
86+
8187
$entriesMetadata = $this->contentCache->getAllMetadata();
8288
foreach ($entriesMetadata as $identifier => $metadata) {
8389
$entryTags = isset($metadata['tags']) ? $metadata['tags'] : [];
@@ -93,6 +99,6 @@ public function getFusionCacheInformations(): array
9399
}
94100
$hasUncachedSegments = $this->contentCacheAspect->hasUncachedSegments();
95101

96-
return [$hasUncachedSegments, $tags, $lifetime];
102+
return ['hasUncachedSegments' => $hasUncachedSegments, 'tags' => $tags, 'lifetime' => $lifetime];
97103
}
98104
}

‎Classes/Middleware/RequestCacheMiddleware.php‎

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,30 @@
11
<?php
2+
23
declare(strict_types=1);
34

45
namespace Flowpack\FullPageCache\Middleware;
56

7+
use Flowpack\FullPageCache\Domain\Dto\CacheEntry;
68
use Neos\Flow\Annotations as Flow;
79
use Neos\Cache\Frontend\VariableFrontend;
810
use Psr\Http\Message\ResponseInterface;
911
use Psr\Http\Message\ServerRequestInterface;
1012
use Psr\Http\Server\MiddlewareInterface;
1113
use Psr\Http\Server\RequestHandlerInterface;
1214
use GuzzleHttp\Psr7\Message;
13-
use function GuzzleHttp\Psr7\str;
1415

16+
/**
17+
* @phpstan-type CacheEntryShape array{timestamp:int, response:string}
18+
*/
1519
class RequestCacheMiddleware implements MiddlewareInterface
1620
{
1721
public const HEADER_ENABLED = 'X-FullPageCache-Enabled';
1822

1923
public const HEADER_INFO = 'X-FullPageCache-Info';
2024

25+
// @deprecated use HEADER_LIFETIME instead
2126
public const HEADER_LIFTIME = 'X-FullPageCache-Lifetime';
27+
public const HEADER_LIFETIME = 'X-FullPageCache-Lifetime';
2228

2329
public const HEADER_TAGS = 'X-FullPageCache-Tags';
2430

@@ -35,29 +41,35 @@ class RequestCacheMiddleware implements MiddlewareInterface
3541
protected $cacheFrontend;
3642

3743
/**
38-
* @var array
44+
* @var string[]
3945
* @Flow\InjectConfiguration(path="request.queryParams.allow")
4046
*/
4147
protected $allowedQueryParams;
4248

4349
/**
44-
* @var array
50+
* @var string[]
4551
* @Flow\InjectConfiguration(path="request.queryParams.ignore")
4652
*/
4753
protected $ignoredQueryParams;
4854

4955
/**
50-
* @var array
56+
* @var string[]
5157
* @Flow\InjectConfiguration(path="request.cookieParams.ignore")
5258
*/
5359
protected $ignoredCookieParams;
5460

5561
/**
56-
* @var boolean
62+
* @var int
5763
* @Flow\InjectConfiguration(path="maxPublicCacheTime")
5864
*/
5965
protected $maxPublicCacheTime;
6066

67+
/**
68+
* @var int
69+
* @Flow\InjectConfiguration(path="maxSharedCacheTime")
70+
*/
71+
protected $maxSharedCacheTime;
72+
6173
public function process(ServerRequestInterface $request, RequestHandlerInterface $next): ResponseInterface
6274
{
6375
if (!$this->enabled) {
@@ -70,22 +82,24 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
7082
return $next->handle($request)->withHeader(self::HEADER_INFO, 'SKIP');
7183
}
7284

73-
if ($cacheEntry = $this->cacheFrontend->get($entryIdentifier)) {
85+
/** @var ?CacheEntryShape $cacheEntry */
86+
$cacheEntry = $this->cacheFrontend->get($entryIdentifier);
87+
if ($cacheEntry) {
7488
$age = time() - $cacheEntry['timestamp'];
7589
$response = Message::parseResponse($cacheEntry['response']);
7690
return $response
77-
->withHeader('Age', $age)
91+
->withHeader('Age', (string)$age)
7892
->withHeader(self::HEADER_INFO, 'HIT: ' . $entryIdentifier);
7993
}
8094

8195
$response = $next->handle($request->withHeader(self::HEADER_ENABLED, ''));
8296

8397
if ($response->hasHeader(self::HEADER_ENABLED)) {
84-
$lifetime = $response->hasHeader(self::HEADER_LIFTIME) ? (int)$response->getHeaderLine(self::HEADER_LIFTIME) : null;
98+
$lifetime = $response->hasHeader(self::HEADER_LIFETIME) ? (int)$response->getHeaderLine(self::HEADER_LIFETIME) : null;
8599
$tags = $response->hasHeader(self::HEADER_TAGS) ? $response->getHeader(self::HEADER_TAGS) : [];
86100
$response = $response
87101
->withoutHeader(self::HEADER_ENABLED)
88-
->withoutHeader(self::HEADER_LIFTIME)
102+
->withoutHeader(self::HEADER_LIFETIME)
89103
->withoutHeader(self::HEADER_TAGS);
90104

91105
$publicLifetime = 0;
@@ -105,8 +119,9 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
105119
->withHeader('Cache-Control', 'public, max-age=' . $publicLifetime);
106120
}
107121

108-
$this->cacheFrontend->set($entryIdentifier,[ 'timestamp' => time(), 'response' => Message::toString($response) ], $tags, $lifetime);
109-
$response->getBody()->rewind();
122+
/** @var CacheEntryShape $cacheEntry */
123+
$cacheEntry = [ 'timestamp' => time(), 'response' => Message::toString($response) ];
124+
$this->cacheFrontend->set($entryIdentifier, $cacheEntry, $tags, $lifetime);
110125
return $response->withHeader(self::HEADER_INFO, 'MISS: ' . $entryIdentifier);
111126
}
112127

‎composer.json‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
"neos/neos": "^8.0 || ^9.0 || dev-master",
88
"guzzlehttp/psr7": "^1.7, !=1.8.0 || ~2.0"
99
},
10+
"require-dev": {
11+
"phpstan/phpstan": "^2.2",
12+
"squizlabs/php_codesniffer": "^3.7"
13+
},
1014
"autoload": {
1115
"psr-4": {
1216
"Flowpack\\FullPageCache\\": "Classes/"
@@ -16,5 +20,18 @@
1620
"neos": {
1721
"package-key": "Flowpack.FullPageCache"
1822
}
23+
},
24+
"scripts": {
25+
"fix:style": "phpcbf --colors --standard=PSR12 Classes",
26+
"test:style": "phpcs --colors -n --standard=PSR12 Classes",
27+
"test:stan": "phpstan analyse Classes",
28+
"cc": "phpstan clear cache",
29+
"fix": ["composer fix:style"],
30+
"test": ["composer test:style" , "composer test:stan"]
31+
},
32+
"config": {
33+
"allow-plugins": {
34+
"neos/composer-plugin": true
35+
}
1936
}
2037
}

‎phpstan.neon‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
parameters:
2+
level: 8
3+
paths:
4+
- Classes
5+
reportUnmatchedIgnoredErrors: false

0 commit comments

Comments
 (0)