diff --git a/Classes/Configuration/ConfigurationReader.php b/Classes/Configuration/ConfigurationReader.php index 30ede256..304347d7 100644 --- a/Classes/Configuration/ConfigurationReader.php +++ b/Classes/Configuration/ConfigurationReader.php @@ -87,6 +87,7 @@ class ConfigurationReader { 'fileName/defaultToHTMLsuffixOnPrev' => FALSE, 'init/appendMissingSlash' => 'ifNotFile,redirect[301]', 'init/emptySegmentValue' => '', + 'init/recalculateChashIfMissing' => false, 'pagePath/spaceCharacter' => '-', // undocumented & deprecated! ); diff --git a/Classes/Decoder/UrlDecoder.php b/Classes/Decoder/UrlDecoder.php index cc2e3db7..9391f930 100644 --- a/Classes/Decoder/UrlDecoder.php +++ b/Classes/Decoder/UrlDecoder.php @@ -33,6 +33,7 @@ use DmitryDulepov\Realurl\Cache\UrlCacheEntry; use DmitryDulepov\Realurl\Configuration\ConfigurationReader; use DmitryDulepov\Realurl\EncodeDecoderBase; +use TYPO3\CMS\Core\SingletonInterface; use TYPO3\CMS\Core\Utility\ArrayUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\MathUtility; @@ -40,12 +41,13 @@ use TYPO3\CMS\Frontend\Page\PageRepository; /** - * This class contains URL decoder for the RealURL. + * This class contains URL decoder for the RealURL. It is singleton because the + * same instance must run in two different hooks. * * @package DmitryDulepov\Realurl\Decoder * @author Dmitry Dulepov */ -class UrlDecoder extends EncodeDecoderBase { +class UrlDecoder extends EncodeDecoderBase implements SingletonInterface { const REDIRECT_STATUS_HEADER = 'HTTP/1.0 301 TYPO3 RealURL Redirect'; const REDIRECT_INFO_HEADER = 'X-TYPO3-RealURL-Info'; @@ -56,6 +58,9 @@ class UrlDecoder extends EncodeDecoderBase { /** @var TypoScriptFrontendController */ protected $caller; + /** @var UrlCacheEntry */ + protected $createdCacheEntry = null; + /** @var int */ protected $detectedLanguageId = 0; @@ -145,6 +150,35 @@ public function decodeUrl(array $params) { } } + /** + * Stores decoded record to the URL cache. This function is called after + * TSFE validates cHash. This way we avoid storing URLs with wrong cHash + * in the cache that would always lead to a 404 until URL cache is cleared. + * + * Currently this function uses 'configArrayPostProc' because this hook + * is closest to the TypoScriptFrontendController::makeCacheHash(). If + * execution comes here, than cHash is validated (assuming that + * $TYPO3_CONF_VARS['FE']['pageNotFoundOnCHashError'] is true). However + * it may not be the best place. Another place could be + * TypoScriptFrontendController::hook_eofe(), which runs after the content + * is generated. Advantage is that the URL to the current page could be + * already generated by the encoding process, which is guaranteed to make + * the URL correctly. On the other hand, if content generation fails for + * some reason, we will not have entries in any case and the next call + * to this URL will have to make costly decoding again. To avoid the + * problem, we do it in the earlier hook. Later, when expiration of URL + * entries is implemented, we could set expiration time for this record + * and the encoder will unexpire it if it generates the same URL. The + * advantage of this is that there will be a proper redirect and no + * duplicate content due to possibly different encoded URL. + */ + public function storeCacheRecord() { + // If it is still not there (could have been added by other process!), than store it + if ($this->createdCacheEntry && !$this->isExpiredPath && !$this->getFromUrlCache($this->speakingUri)) { + $this->putToUrlCache($this->createdCacheEntry); + } + } + /** * Calculates and adds cHash to the entry. This function is only called * if we had to decode the entry, which was not in the cache. Even if we @@ -157,13 +191,15 @@ public function decodeUrl(array $params) { */ protected function calculateChash(UrlCacheEntry $cacheEntry) { $requestVariables = $cacheEntry->getRequestVariables(); - $cacheHashCalculator = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\Page\\CacheHashCalculator'); - /* @var \TYPO3\CMS\Frontend\Page\CacheHashCalculator $cacheHashCalculator */ - $cHashParameters = $cacheHashCalculator->getRelevantParameters(GeneralUtility::implodeArrayForUrl('', $requestVariables)); + if (!isset($requestVariables['cHash']) && $this->configuration->get('init/recalculateChashIfMissing')) { + $cacheHashCalculator = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\Page\\CacheHashCalculator'); + /* @var \TYPO3\CMS\Frontend\Page\CacheHashCalculator $cacheHashCalculator */ + $cHashParameters = $cacheHashCalculator->getRelevantParameters(GeneralUtility::implodeArrayForUrl('', $requestVariables)); - if (count($cHashParameters) > 0) { - $requestVariables['cHash'] = $cacheHashCalculator->calculateCacheHash($cHashParameters); - $cacheEntry->setRequestVariables($requestVariables); + if (count($cHashParameters) > 0) { + $requestVariables['cHash'] = $cacheHashCalculator->calculateCacheHash($cHashParameters); + $cacheEntry->setRequestVariables($requestVariables); + } } } @@ -1263,10 +1299,7 @@ protected function runDecoding() { $this->checkExpiration($cacheEntry); $this->setRequestVariables($cacheEntry); - // If it is still not there (could have been added by other process!), than update - if (!$this->isExpiredPath && !$this->getFromUrlCache($this->speakingUri)) { - $this->putToUrlCache($cacheEntry); - } + $this->createdCacheEntry = $cacheEntry; } /** diff --git a/ext_localconf.php b/ext_localconf.php index 92d3a0bf..4aa39ffa 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -38,6 +38,7 @@ function includeRealurlConfiguration() { $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tstemplate.php']['linkData-PostProc']['realurl'] = 'DmitryDulepov\\Realurl\\Encoder\\UrlEncoder->encodeUrl'; $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typoLink_PostProc']['realurl'] = 'DmitryDulepov\\Realurl\\Encoder\\UrlEncoder->postProcessEncodedUrl'; $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['checkAlternativeIdMethods-PostProc']['realurl'] = 'DmitryDulepov\\Realurl\\Decoder\\UrlDecoder->decodeUrl'; +$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['configArrayPostProc']['realurl'] = 'DmitryDulepov\\Realurl\\Decoder\\UrlDecoder->storeCacheRecord'; includeRealurlConfiguration();