Skip to content

Commit

Permalink
FEATURE: Use CacheTag to generate cache tags
Browse files Browse the repository at this point in the history
  • Loading branch information
dlubitz committed Mar 13, 2024
1 parent c561b48 commit d757e59
Show file tree
Hide file tree
Showing 9 changed files with 391 additions and 777 deletions.
15 changes: 3 additions & 12 deletions Neos.Fusion/Classes/Core/Cache/RuntimeContentCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

use Neos\Flow\Annotations as Flow;
use Neos\Utility\TypeHandling;
use Neos\Utility\Unicode\Functions;
use Neos\Fusion\Core\Runtime;
use Neos\Fusion\Exception;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
Expand Down Expand Up @@ -82,22 +81,14 @@ public function injectSerializer(NormalizerInterface&DenormalizerInterface $seri
/**
* Adds a tag built from the given key and value.
*
* @param string $key
* @param string $value
* @return void
* @throws Exception
*/
public function addTag($key, $value)
public function addTag(string $tag): void
{
$key = trim($key);
if ($key === '') {
throw new Exception('Tag Key must not be empty', 1448264366);
}
$value = trim($value);
if ($value === '') {
$tag = trim($tag);
if ($tag === '') {
throw new Exception('Tag Value must not be empty', 1448264367);
}
$tag = Functions::ucfirst($key) . 'DynamicTag_' . $value;
$this->tags[$tag] = true;
}

Expand Down
10 changes: 4 additions & 6 deletions Neos.Fusion/Classes/Core/Runtime.php
Original file line number Diff line number Diff line change
Expand Up @@ -217,16 +217,14 @@ public function injectSettings(array $settings)
*
* During Fusion rendering the method can be used to add tag dynamicaly for the current cache segment.
*
* @param string $key
* @param string $value
* @return void
* @throws Exceptions
*/
public function addCacheTag($key, $value)
public function addCacheTag(string $tag): void
{
if ($this->runtimeContentCache->getEnableContentCache() === false) {
return;
}
$this->runtimeContentCache->addTag($key, $value);
$this->runtimeContentCache->addTag($tag);
}

/**
Expand Down Expand Up @@ -519,7 +517,7 @@ protected function evaluateObjectOrRetrieveFromCache($fusionObject, $fusionPath,
{
$output = null;
$evaluationStatus = self::EVALUATION_SKIPPED;
list($cacheHit, $cachedResult) = $this->runtimeContentCache->preEvaluate($cacheContext, $fusionObject);
[$cacheHit, $cachedResult] = $this->runtimeContentCache->preEvaluate($cacheContext, $fusionObject);
if ($cacheHit) {
return $cachedResult;
}
Expand Down
134 changes: 134 additions & 0 deletions Neos.Neos/Classes/Fusion/Cache/CacheTag.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
<?php

declare(strict_types=1);

namespace Neos\Neos\Fusion\Cache;

use Neos\ContentRepository\Core\Factory\ContentRepositoryId;
use Neos\ContentRepository\Core\NodeType\NodeTypeName;
use Neos\ContentRepository\Core\Projection\ContentGraph\Node;
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId;
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId;
use Neos\Flow\Annotations as Flow;

/**
* The cache tag value object
*/
#[Flow\Proxy(false)]
class CacheTag
{
protected const PATTERN = '/^[a-zA-Z0-9_%\-&]{1,250}$/';

private function __construct(
public readonly string $value
) {
}

final public static function forNodeAggregate(
ContentRepositoryId $contentRepositoryId,
ContentStreamId $contentStreamId,
NodeAggregateId $nodeAggregateId,
): self {
return new self(
'Node_'
. self::getHashForContentStreamIdAndContentRepositoryId($contentStreamId, $contentRepositoryId)
. '_' . $nodeAggregateId->value

);
}

final public static function forNodeAggregateFromNode(Node $node): self
{
return self::forNodeAggregate(
$node->subgraphIdentity->contentRepositoryId,
$node->subgraphIdentity->contentStreamId,
$node->nodeAggregateId
);
}

final public static function forDescendantOfNode(
ContentRepositoryId $contentRepositoryId,
ContentStreamId $contentStreamId,
NodeAggregateId $nodeAggregateId,
): self {
return new self(
'DescendantOf_'
. self::getHashForContentStreamIdAndContentRepositoryId($contentStreamId, $contentRepositoryId)
. '_' . $nodeAggregateId->value
);
}

final public static function forDescendantOfNodeFromNode(Node $node): self
{
return self::forDescendantOfNode(
$node->subgraphIdentity->contentRepositoryId,
$node->subgraphIdentity->contentStreamId,
$node->nodeAggregateId
);
}

final public static function forAncestorNode(
ContentRepositoryId $contentRepositoryId,
ContentStreamId $contentStreamId,
NodeAggregateId $nodeAggregateId,
): self {
return new self(
'Ancestor'
. self::getHashForContentStreamIdAndContentRepositoryId($contentStreamId, $contentRepositoryId)
. '_' . $nodeAggregateId->value
);
}

final public static function forAncestorNodeFromNode(Node $node): self
{
return self::forAncestorNode(
$node->subgraphIdentity->contentRepositoryId,
$node->subgraphIdentity->contentStreamId,
$node->nodeAggregateId
);
}

final public static function forNodeTypeName(
ContentRepositoryId $contentRepositoryId,
ContentStreamId $contentStreamId,
NodeTypeName $nodeTypeName,
): self {
return new self(
'NodeType_'
. self::getHashForContentStreamIdAndContentRepositoryId($contentStreamId, $contentRepositoryId)
. '_' . \strtr($nodeTypeName->value, '.:', '_-')
);
}

final public static function forDynamicNodeAggregate(
ContentRepositoryId $contentRepositoryId,
ContentStreamId $contentStreamId,
NodeAggregateId $nodeAggregateId,
): self {
return new self(
'DynamicNodeTag_'
. self::getHashForContentStreamIdAndContentRepositoryId($contentStreamId, $contentRepositoryId)
. '_' . $nodeAggregateId->value

);
}

final public static function fromString(string $string): self
{
if (preg_match(self::PATTERN, $string) !== 1) {
throw new \InvalidArgumentException(
'Given value "' . $string . '" is no valid cache tag, must match the defined pattern.',
1658093413
);
}

return new self($string);
}

protected static function getHashForContentStreamIdAndContentRepositoryId(
ContentStreamId $contentStreamId,
ContentRepositoryId $contentRepositoryId,
): string {
return sha1($contentStreamId->value . '@' . $contentRepositoryId->value);
}
}
98 changes: 98 additions & 0 deletions Neos.Neos/Classes/Fusion/Cache/CacheTagSet.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php

declare(strict_types=1);

namespace Neos\Neos\Fusion\Cache;

use Neos\ContentRepository\Core\Factory\ContentRepositoryId;
use Neos\ContentRepository\Core\NodeType\NodeTypeName;
use Neos\ContentRepository\Core\NodeType\NodeTypeNames;
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId;
use Neos\Flow\Annotations as Flow;
use Neos\ContentRepository\Core\Projection\ContentGraph\Nodes;
use Neos\ContentRepository\Core\Projection\ContentGraph\Node;

/**
* The cache tag value object set
*/
#[Flow\Proxy(false)]
final class CacheTagSet
{
/**
* Unique cache tags, indexed by their value
* @var array<string,CacheTag>
*/
private array $tags;

public function __construct(CacheTag ...$tags)
{
$uniqueTags = [];
foreach ($tags as $tag) {
$uniqueTags[$tag->value] = $tag;
}

$this->tags = $uniqueTags;
}

public static function forDescendantOfNodesFromNodes(
Nodes $nodes
): self {
return new self(...array_map(
fn(Node $node): CacheTag => CacheTag::forDescendantOfNodeFromNode(
$node
),
iterator_to_array($nodes)
));
}

public static function forNodeAggregatesFromNodes(
Nodes $nodes
): self {
return new self(...array_map(
fn(Node $node): CacheTag => CacheTag::forNodeAggregateFromNode(
$node
),
iterator_to_array($nodes)
));
}


public static function forNodeTypeNames(
ContentRepositoryId $contentRepositoryId,
ContentStreamId $contentStreamId,
NodeTypeNames $nodeTypeNames
): self {
return new self(...array_map(
fn(NodeTypeName $nodeTypeName): CacheTag => CacheTag::forNodeTypeName(
$contentRepositoryId,
$contentStreamId,
$nodeTypeName
),
iterator_to_array($nodeTypeNames)
));
}

public function add(CacheTag $cacheTag): self
{
$tags = $this->tags;
$tags[$cacheTag->value] = $cacheTag;

return new self(...$tags);
}

/**
* @return array<int,string>
*/
public function toStringArray(): array
{
return array_map(
fn(CacheTag $tag): string => $tag->value,
array_values($this->tags)
);
}

public function union(self $other): self
{
return new self(...array_merge($this->tags, $other->tags));
}
}
Loading

0 comments on commit d757e59

Please sign in to comment.