The cache build process generally follows these steps:
- render the whole document as usual (for example a page) but insert special markers before and after the rendered segments
- parse the rendered document and extract segments by the previously added markers
This results in two artifacts:
- an array of content segments which are later stored as cache entries (if they may be cached)
- a string called "output" which is the originally rendered output but without the markers
We use non-visible ASCII characters as markers / tokens in order to minimize potential conflicts with the actual content.
Note: If you choose a different cache backend for this content cache, make sure that it is one implementing
TaggableBackendInterface.
/** * Flush caches according to the previously registered node changes. * * @return void */ public function shutdownObject() { if ($this->tagsToFlush !== array()) { foreach ($this->tagsToFlush as $tag => $logMessage) { $affectedEntries = $this->contentCache->flushByTag($tag); if ($affectedEntries > 0) { $this->systemLogger->log(sprintf('Content cache: Removed %s entries %s', $affectedEntries, $logMessage), LOG_DEBUG); } } } }
/** * @test */ public function exceptionInAlreadyCachedSegmentShouldNotLeaveSegmentMarkersInOutput() { $object = new TestModel(42, 'Object value 1'); $view = $this->buildView(); $view->setOption('enableContentCache', true); $view->setTypoScriptPath('contentCache/nestedCacheSegmentsWithConditionalException'); $view->assign('object', $object); $view->assign('throwException', false); $firstRenderResult = $view->render(); $this->assertEquals('Cached segment|counter=1|It depends|End segment', $firstRenderResult); $this->contentCache->flushByTag('Inner'); $view->assign('throwException', true); $secondRenderResult = $view->render(); $this->assertStringStartsWith('Cached segment|counter=1|Exception', $secondRenderResult); }
/** * Post process output for caching information * * The content cache stores cache segments with markers inside the generated content. This method creates cache * segments and will process the final outer result (currentPathIsEntryPoint) to remove all cache markers and * store cache entries. * * @param array $evaluateContext The current evaluation context * @param object $tsObject The current TypoScript object (for "this" in evaluations) * @param mixed $output The generated output after caching information was removed * @return mixed The post-processed output with cache segment markers or cleaned for the entry point */ public function postProcess(array $evaluateContext, $tsObject, $output) { if ($this->enableContentCache && $evaluateContext['cacheForPathEnabled'] && $evaluateContext['cacheForPathDisabled']) { $contextArray = $this->runtime->getCurrentContext(); if (isset($evaluateContext['configuration']['context'])) { $contextVariables = []; foreach ($evaluateContext['configuration']['context'] as $contextVariableName) { $contextVariables[$contextVariableName] = $contextArray[$contextVariableName]; } } else { $contextVariables = $contextArray; } $cacheTags = $this->buildCacheTags($evaluateContext['configuration'], $evaluateContext['typoScriptPath'], $tsObject); $cacheMetadata = array_pop($this->cacheMetadata); $output = $this->contentCache->createDynamicCachedSegment($output, $evaluateContext['typoScriptPath'], $contextVariables, $evaluateContext['cacheIdentifierValues'], $cacheTags, $cacheMetadata['lifetime'], $evaluateContext['cacheDiscriminator']); } elseif ($this->enableContentCache && $evaluateContext['cacheForPathEnabled']) { $cacheTags = $this->buildCacheTags($evaluateContext['configuration'], $evaluateContext['typoScriptPath'], $tsObject); $cacheMetadata = array_pop($this->cacheMetadata); $output = $this->contentCache->createCacheSegment($output, $evaluateContext['typoScriptPath'], $evaluateContext['cacheIdentifierValues'], $cacheTags, $cacheMetadata['lifetime']); } elseif ($this->enableContentCache && $evaluateContext['cacheForPathDisabled'] && $this->inCacheEntryPoint) { $contextArray = $this->runtime->getCurrentContext(); if (isset($evaluateContext['configuration']['context'])) { $contextVariables = []; foreach ($evaluateContext['configuration']['context'] as $contextVariableName) { if (isset($contextArray[$contextVariableName])) { $contextVariables[$contextVariableName] = $contextArray[$contextVariableName]; } else { $contextVariables[$contextVariableName] = null; } } } else { $contextVariables = $contextArray; } $output = $this->contentCache->createUncachedSegment($output, $evaluateContext['typoScriptPath'], $contextVariables); } if ($evaluateContext['cacheForPathEnabled'] && $evaluateContext['currentPathIsEntryPoint']) { $output = $this->contentCache->processCacheSegments($output, $this->enableContentCache); $this->inCacheEntryPoint = null; $this->addCacheSegmentMarkersToPlaceholders = false; } return $output; }