Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: PHP Test

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

permissions:
contents: read

jobs:
test:
name: "Test (PHP ${{ matrix.php-versions }}, Neos ${{ matrix.neos-versions }})"

strategy:
fail-fast: false
matrix:
php-versions: ['8.2', '8.3']
neos-versions: ['8.3']

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Validate composer.json and composer.lock
run: composer validate --strict

- name: Cache Composer packages
id: composer-cache
uses: actions/cache@v3
with:
path: vendor
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-php-

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: mbstring, xml, json, zlib, iconv, intl, pdo_sqlite
ini-values: date.timezone="Africa/Tunis", opcache.fast_shutdown=0, apc.enable_cli=on

- name: Set Neos Version
run: composer require neos/neos ^${{ matrix.neos-versions }} --no-progress --no-interaction

- name: Install dependencies
run: composer install --prefer-dist --no-progress

- name: Run test suite
run: composer test
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
composer.lock
Packages
vendor
14 changes: 4 additions & 10 deletions Classes/Aspects/ContentCacheAspect.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?php

namespace Flowpack\FullPageCache\Aspects;

use Neos\Cache\Frontend\StringFrontend;
Expand All @@ -12,14 +13,7 @@
*/
class ContentCacheAspect
{
private $hadUncachedSegments = false;

private $cacheTags = [];

/**
* @var null|int
*/
private $shortestLifetime = null;
private bool $hadUncachedSegments = false;

/**
* @Flow\Inject
Expand All @@ -30,7 +24,7 @@ class ContentCacheAspect
/**
* @Flow\Before("method(Neos\Fusion\Core\Cache\ContentCache->(createUncachedSegment)())")
*/
public function grabUncachedSegment(JoinPointInterface $joinPoint)
public function grabUncachedSegment(JoinPointInterface $joinPoint): void
{
$this->hadUncachedSegments = true;
}
Expand All @@ -41,7 +35,7 @@ public function grabUncachedSegment(JoinPointInterface $joinPoint)
*
* @throws \Neos\Utility\Exception\PropertyNotAccessibleException
*/
public function interceptNodeCacheFlush(JoinPointInterface $joinPoint)
public function interceptNodeCacheFlush(JoinPointInterface $joinPoint): void
{
$object = $joinPoint->getProxy();

Expand Down
25 changes: 15 additions & 10 deletions Classes/Cache/MetadataAwareStringFrontend.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?php

namespace Flowpack\FullPageCache\Cache;

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

Expand All @@ -38,6 +39,10 @@ class MetadataAwareStringFrontend extends StringFrontend
* Set a cache entry and store additional metadata (tags and lifetime)
*
* {@inheritdoc}
*
* @param string $content
* @param string[] $tags
* @return void
*/
public function set(string $entryIdentifier, $content, array $tags = [], int $lifetime = null)
{
Expand All @@ -47,6 +52,8 @@ public function set(string $entryIdentifier, $content, array $tags = [], int $li

/**
* {@inheritdoc}
*
* @return string|false
*/
public function get(string $entryIdentifier)
{
Expand All @@ -60,6 +67,7 @@ public function get(string $entryIdentifier)

/**
* {@inheritdoc}
* @return array<string,string>
*/
public function getByTag(string $tag): array
{
Expand All @@ -76,16 +84,12 @@ public function getByTag(string $tag): array
*
* @param string $content
* @param string $entryIdentifier The identifier metadata
* @param array $tags The tags metadata
* @param string[] $tags The tags metadata
* @param integer $lifetime The lifetime metadata
* @return string The content including the serialized metadata
* @throws InvalidDataTypeException
*/
protected function insertMetadata($content, $entryIdentifier, array $tags, $lifetime)
protected function insertMetadata(string $content, string $entryIdentifier, array $tags, ?int $lifetime)
{
if (!is_string($content)) {
throw new InvalidDataTypeException('Given data is of type "' . gettype($content) . '", but a string is expected for string cache.', 1433155737);
}
$metadata = [
'identifier' => $entryIdentifier,
'tags' => $tags,
Expand All @@ -105,7 +109,7 @@ protected function insertMetadata($content, $entryIdentifier, array $tags, $life
* @return string The content without metadata
* @throws InvalidDataTypeException
*/
protected function extractMetadata($entryIdentifier, $content)
protected function extractMetadata($entryIdentifier, $content): string
{
$separatorIndex = strpos($content, self::SEPARATOR);
if ($separatorIndex === false) {
Expand All @@ -115,6 +119,7 @@ protected function extractMetadata($entryIdentifier, $content)
} else {
throw $exception;
}
return $content;
}

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

/**
* @return array Metadata of all loaded entries (indexed by identifier)
* @return array<string, array{identifier:string, tags?: string[], lifetime?: int|null}> Metadata of all loaded entries (indexed by identifier)
*/
public function getAllMetadata()
public function getAllMetadata(): array
{
return $this->metadata;
}
Expand Down
34 changes: 34 additions & 0 deletions Classes/Domain/Dto/CacheEntry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace Flowpack\FullPageCache\Domain\Dto;

use GuzzleHttp\Psr7\Message;
use Psr\Http\Message\ResponseInterface;

readonly class CacheEntry
{
public function __construct(
public int $timestamp,
public string $responseAsString,
) {
}

public static function createFromResponse(ResponseInterface $response): CacheEntry
{
$responseAsString = Message::toString($response);
$response->getBody()->rewind();

return new self(
time(),
$responseAsString
);
}

public function getResponse(): ResponseInterface
{
return Message::parseResponse($this->responseAsString)
->withHeader('Age', (string)(time() - $this->timestamp));
}
}
20 changes: 20 additions & 0 deletions Classes/Domain/Dto/FusionCacheInformation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace Flowpack\FullPageCache\Domain\Dto;

readonly class FusionCacheInformation
{
/**
* @param bool $hasUncachedSegments
* @param string[] $tags
* @param int|null $lifetime
*/
public function __construct(
public bool $hasUncachedSegments,
public array $tags,
public ?int $lifetime
) {
}
}
21 changes: 11 additions & 10 deletions Classes/Middleware/FusionAutoconfigurationMiddleware.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
<?php

declare(strict_types=1);

namespace Flowpack\FullPageCache\Middleware;

use Flowpack\FullPageCache\Domain\Dto\FusionCacheInformation;
use Neos\Flow\Annotations as Flow;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
Expand Down Expand Up @@ -47,34 +49,33 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
$response = $response->withoutHeader(self::HEADER_ENABLED);
}

list($hasUncachedSegments, $tags, $lifetime) = $this->getFusionCacheInformations();
$cacheMetadata = $this->getFusionCacheInformations();

if ($response->hasHeader('Set-Cookie') || $hasUncachedSegments) {
if ($response->hasHeader('Set-Cookie') || $cacheMetadata->hasUncachedSegments) {
return $response;
}

$response = $response
->withHeader(RequestCacheMiddleware::HEADER_ENABLED, "");

if ($tags) {
if ($cacheMetadata->tags) {
$response = $response
->withHeader(RequestCacheMiddleware::HEADER_TAGS, $tags);
->withHeader(RequestCacheMiddleware::HEADER_TAGS, $cacheMetadata->tags);
}

if ($lifetime) {

if ($cacheMetadata->lifetime) {
$response = $response
->withHeader(RequestCacheMiddleware::HEADER_LIFTIME, $lifetime);
->withHeader(RequestCacheMiddleware::HEADER_LIFETIME, (string)$cacheMetadata->lifetime);
}

return $response;
}

/**
* Get cache tags and lifetime from the cache metadata that was extracted by the special cache frontend for content cache
*
* @return array with first "hasUncachedSegments", "tags" and "lifetime"
*/
public function getFusionCacheInformations(): array
public function getFusionCacheInformations(): FusionCacheInformation
{
$lifetime = null;
$tags = [];
Expand All @@ -93,6 +94,6 @@ public function getFusionCacheInformations(): array
}
$hasUncachedSegments = $this->contentCacheAspect->hasUncachedSegments();

return [$hasUncachedSegments, $tags, $lifetime];
return new FusionCacheInformation($hasUncachedSegments, $tags, $lifetime);
}
}
Loading