Index: typo3/sysext/cms/tslib/class.tslib_fe.php =================================================================== --- typo3/sysext/cms/tslib/class.tslib_fe.php (revision 7722) +++ typo3/sysext/cms/tslib/class.tslib_fe.php (working copy) @@ -411,6 +411,7 @@ protected $pageCache; protected $pageCacheTags = array(); + protected $get_cache_timeout_cache = null; /** * Class constructor @@ -2800,15 +2801,10 @@ * @return void */ function realPageCacheContent() { - $cache_timeout = $this->get_cache_timeout(); // seconds until a cached page is too old - $timeOutTime = $GLOBALS['EXEC_TIME']+$cache_timeout; - if ($this->config['config']['cache_clearAtMidnight']) { - $midnightTime = mktime (0,0,0,date('m',$timeOutTime),date('d',$timeOutTime),date('Y',$timeOutTime)); - if ($midnightTime > $GLOBALS['EXEC_TIME']) { // If the midnight time of the expire-day is greater than the current time, we may set the timeOutTime to the new midnighttime. - $timeOutTime = $midnightTime; - } - } + $cacheTimeout = $this->get_cache_timeout(); // seconds until a cached page is too old + $timeOutTime = $GLOBALS['EXEC_TIME']+$cacheTimeout; $this->tempContent = false; + $this->setPageCacheContent($this->content, $this->config, $timeOutTime); // Hook for cache post processing (eg. writing static files!) @@ -4624,17 +4620,33 @@ * @return integer The cache timeout for the current page. */ function get_cache_timeout() { - // Cache period was set for the page: - if ($this->page['cache_timeout']) { - $cacheTimeout = $this->page['cache_timeout']; - // Cache period was set for the whole site: - } elseif ($this->cacheTimeOutDefault) { - $cacheTimeout = $this->cacheTimeOutDefault; - // No cache period set at all, so we take one day (60*60*24 seconds = 86400 seconds): - } else { - $cacheTimeout = 86400; + if ($this->get_cache_timeout_cache == null) { + if ($this->page['cache_timeout']) { + // Cache period was set for the page: + $cacheTimeout = $this->page['cache_timeout']; + } elseif ($this->cacheTimeOutDefault) { + // Cache period was set for the whole site: + $cacheTimeout = $this->cacheTimeOutDefault; + } else { + // No cache period set at all, so we take one day (60*60*24 seconds = 86400 seconds): + $cacheTimeout = 86400; + } + + if ($this->config['config']['cache_clearAtMidnight']) { + $timeOutTime = $GLOBALS['EXEC_TIME']+$cacheTimeout; + $midnightTime = mktime(0, 0, 0, date('m',$timeOutTime), date('d',$timeOutTime), date('Y',$timeOutTime)); + if ($midnightTime > $GLOBALS['EXEC_TIME']) { // If the midnight time of the expire-day is greater than the current time, we may set the timeOutTime to the new midnighttime. + $cacheTimeout = $midnightTime-$GLOBALS['EXEC_TIME']; + } + } else { + // if cache_clearAtMidnight is not set calculate the timeout time for records on the page + $calculatedCacheTimeout = $this->calculatePageCacheTimeout(); + $cacheTimeout = ($calculatedCacheTimeout < $cacheTimeout ? $calculatedCacheTimeout : $cacheTimeout); + } + $this->get_cache_timeout_cache = $cacheTimeout; } - return $cacheTimeout; + + return $this->get_cache_timeout_cache; } /** @@ -4828,6 +4840,96 @@ $GLOBALS['HTTP_POST_VARS'] = $_POST; } } + + /** + * Calculates page cache timeout according to the records with + * starttime/endtime on the page. + * + * @return int Page cache timeout or PHP_INT_MAX if cannot be determined + * @author Dmitry Dulepov + */ + protected function calculatePageCacheTimeout() { + $result = PHP_INT_MAX; + + // Get the configuration + $tablesToConsider = $this->getCurrentPageCacheConfiguration(); + + // Get the time, rounded to the minute (do not polute MySQL cache!) + // It is ok that we do not take seconds into account here because this + // value will be substracted later. So we never get the time "before" + // the cache change. + $now = $GLOBALS['ACCESS_TIME']; + + // Find timeout by checking every table + foreach ($tablesToConsider as $tableDef) { + $result = min($result, $this->getFirstTimeValueForRecord($tableDef, $now)); + } + + // We return + 1 second just to ensure that cache is definitely regenerated + return ($result == PHP_INT_MAX ? PHP_INT_MAX : $result - $now + 1); + } + + /** + * Obtains a list of table/pid pairs to consider for page caching. + * + * @return array Array of 'tablename:pid' pairs. There is at least a current page id in the array + * @author Dmitry Dulepov + * @see tslib_fe::calculatePageCacheTimeout() + */ + protected function getCurrentPageCacheConfiguration() { + $result = array('tt_content:' . $this->id); + if (isset($this->config['config']['cache.'][$this->id])) { + $result = array_merge($result, t3lib_div::trimExplode(',', $this->config['config']['cache.'][$this->id])); + } + if (isset($this->config['config']['cache.']['all'])) { + $result = array_merge($result, t3lib_div::trimExplode(',', $this->config['config']['cache.']['all'])); + } + return array_unique($result); + } + + /** + * Find the minimum starttime or endtime value in the table and pid that is + * greater than the current time. + * + * @param string $tableDef Table definition + * @param int "Now" time value + * @return int Value of the next start/stop time or PHP_INT_MAX if not found + * @author Dmitry Dulepov + * @see tslib_fe::calculatePageCacheTimeout() + */ + protected function getFirstTimeValueForRecord($tableDef, $now) { + $result = PHP_INT_MAX; + + list($tableName, $pid) = t3lib_div::trimExplode(':', $tableDef); + + // Additional fields + $showHidden = ($tableName =='pages' ? $this->showHiddenPage : $this->showHiddenRecords); + $enableFields = $this->sys_page->enableFields($tableName, $showHidden, + array('starttime' => true, 'endtime' => true)); + + // Walk fields. There is no need to load TCA because we need only enable columns! + foreach (array('starttime', 'endtime') as $field) { + + // If the field is defined + if (isset($GLOBALS['TCA'][$tableName]['ctrl']['enablecolumns'][$field])) { + + // Get the name of the field + $fieldName = $GLOBALS['TCA'][$tableName]['ctrl']['enablecolumns'][$field]; + + // Find the minimum value + list($row) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( + 'MIN(' . $fieldName . ') AS timefield', + $tableName, 'pid=' . intval($pid) . ' AND ' . + $fieldName . '>' . $now . $enableFields); + + // If the value is found, use it + if ($row && !is_null($row['timefield'])) { + $result = min($result, $row['timefield']); + } + } + } + return $result; + } }