[BUGFIX] Track pids of deleted/moved records to clear cache 13/45213/5
authorJan Helke <typo3@helke.de>
Fri, 11 Dec 2015 13:08:33 +0000 (14:08 +0100)
committerOliver Hader <oliver.hader@typo3.org>
Mon, 14 Dec 2015 23:02:48 +0000 (00:02 +0100)
Clearing caches is done after all operations. The pid can't be read from
the deleted or from the old location of the moved record so an array
remembers these pids for the clear cache operation.

Resolves: #61017
Releases: master, 6.2
Change-Id: I4552d2b9b2a6ea475563fd62a62f29a57fe9df70
Reviewed-on: https://review.typo3.org/45213
Reviewed-by: Markus Klein <markus.klein@typo3.org>
Reviewed-by: Daniel Goerz <ervaude@gmail.com>
Reviewed-by: Oliver Hader <oliver.hader@typo3.org>
Tested-by: Oliver Hader <oliver.hader@typo3.org>
typo3/sysext/core/Classes/DataHandling/DataHandler.php

index b81ded1..14dc618 100644 (file)
@@ -660,6 +660,14 @@ class DataHandler {
        static protected $recordsToClearCacheFor = array();
 
        /**
+        * Internal cache for pids of records which were deleted. It's not possible
+        * to retrieve the parent folder/page at a later stage
+        *
+        * @var array
+        */
+       protected static $recordPidsForDeletedRecords = array();
+
+       /**
         * Database layer. Identical to $GLOBALS['TYPO3_DB']
         *
         * @var DatabaseConnection
@@ -3906,7 +3914,8 @@ class DataHandler {
                if ($destPid >= 0) {
                        if ($table != 'pages' || $this->destNotInsideSelf($destPid, $uid)) {
                                // Clear cache before moving
-                               $this->registerRecordIdForPageCacheClearing($table, $uid);
+                               list($parentUid) = BackendUtility::getTSCpid($table, $uid, '');
+                               $this->registerRecordIdForPageCacheClearing($table, $uid, $parentUid);
                                // Setting PID
                                $updateFields['pid'] = $destPid;
                                // Table is sorted by 'sortby'
@@ -4451,7 +4460,8 @@ class DataHandler {
                        if ($mayEditAccess) {
                                if ($noRecordCheck || $this->doesRecordExist($table, $uid, 'delete')) {
                                        // Clear cache before deleting the record, else the correct page cannot be identified by clear_cache
-                                       $this->registerRecordIdForPageCacheClearing($table, $uid);
+                                       list($parentUid) = BackendUtility::getTSCpid($table, $uid, '');
+                                       $this->registerRecordIdForPageCacheClearing($table, $uid, $parentUid);
                                        $propArr = $this->getRecordProperties($table, $uid);
                                        $pagePropArr = $this->getRecordProperties('pages', $propArr['pid']);
                                        $deleteRow = $GLOBALS['TCA'][$table]['ctrl']['delete'];
@@ -6702,6 +6712,22 @@ class DataHandler {
        }
 
        /**
+        * Gets UID of parent record. If record is deleted it will be looked up in
+        * an array built before the record was deleted
+        *
+        * @param string $table Table where record lives/lived
+        * @param int $uid Record UID
+        * @return int[] Parent UIDs
+        */
+       protected function getOriginalParentOfRecord($table, $uid) {
+               if (isset(self::$recordPidsForDeletedRecords[$table][$uid])) {
+                       return self::$recordPidsForDeletedRecords[$table][$uid];
+               }
+               list($parentUid) = BackendUtility::getTSCpid($table, $uid, '');
+               return array($parentUid);
+       }
+
+       /**
         * Return TSconfig for a page id
         *
         * @param integer $tscPID Page id (PID) from which to get configuration.
@@ -7148,15 +7174,22 @@ class DataHandler {
         * Else just clear the cache for the parent page of the record.
         *
         * @param string $table Table name of record that was just updated.
-        * @param integer $uid UID of updated / inserted record
+        * @param int $uid UID of updated / inserted record
+        * @param int $pid REAL PID of page of a deleted/moved record to get TSconfig in ClearCache.
         * @return void
         * @internal This method is not meant to be called directly but only from the core itself or from hooks
         */
-       public function registerRecordIdForPageCacheClearing($table, $uid) {
+       public function registerRecordIdForPageCacheClearing($table, $uid, $pid = null) {
                if (!is_array(static::$recordsToClearCacheFor[$table])) {
                        static::$recordsToClearCacheFor[$table] = array();
                }
                static::$recordsToClearCacheFor[$table][] = (int)$uid;
+               if ($pid !== null) {
+                       if (!is_array(static::$recordPidsForDeletedRecords[$table])) {
+                               static::$recordPidsForDeletedRecords[$table] = array();
+                       }
+                       static::$recordPidsForDeletedRecords[$table][$uid][] = (int)$pid;
+               }
        }
 
        /**
@@ -7189,87 +7222,19 @@ class DataHandler {
        protected function processClearCacheQueue() {
                $tagsToClear = array();
                $clearCacheCommands = array();
+
                foreach (static::$recordsToClearCacheFor as $table => $uids) {
                        foreach (array_unique($uids) as $uid) {
-                               $pageUid = 0;
                                if (!isset($GLOBALS['TCA'][$table]) || $uid <= 0) {
                                        return;
                                }
-                               // Get Page TSconfig relavant:
-                               list($tscPID) = BackendUtility::getTSCpid($table, $uid, '');
-                               $TSConfig = $this->getTCEMAIN_TSconfig($tscPID);
-                               if (empty($TSConfig['clearCache_disable'])) {
-                                       // If table is "pages":
-                                       $pageIdsThatNeedCacheFlush = array();
-                                       if ($table === 'pages' || $table === 'pages_language_overlay') {
-                                               if ($table === 'pages_language_overlay') {
-                                                       $pageUid = $this->getPID($table, $uid);
-                                               } else {
-                                                       $pageUid = $uid;
-                                               }
-                                               // Builds list of pages on the SAME level as this page (siblings)
-                                               $res_tmp = $GLOBALS['TYPO3_DB']->exec_SELECTquery('A.pid AS pid, B.uid AS uid', 'pages A, pages B', 'A.uid=' . (int)$pageUid . ' AND B.pid=A.pid AND B.deleted=0');
-                                               $pid_tmp = 0;
-                                               while ($row_tmp = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp)) {
-                                                       $pageIdsThatNeedCacheFlush[] = (int)$row_tmp['uid'];
-                                                       $pid_tmp = $row_tmp['pid'];
-                                                       // Add children as well:
-                                                       if ($TSConfig['clearCache_pageSiblingChildren']) {
-                                                               $res_tmp2 = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', 'pages', 'pid=' . (int)$row_tmp['uid'] . ' AND deleted=0');
-                                                               while ($row_tmp2 = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp2)) {
-                                                                       $pageIdsThatNeedCacheFlush[] = (int)$row_tmp2['uid'];
-                                                               }
-                                                               $GLOBALS['TYPO3_DB']->sql_free_result($res_tmp2);
-                                                       }
-                                               }
-                                               $GLOBALS['TYPO3_DB']->sql_free_result($res_tmp);
-                                               // Finally, add the parent page as well:
-                                               $pageIdsThatNeedCacheFlush[] = (int)$pid_tmp;
-                                               // Add grand-parent as well:
-                                               if ($TSConfig['clearCache_pageGrandParent']) {
-                                                       $res_tmp = $GLOBALS['TYPO3_DB']->exec_SELECTquery('pid', 'pages', 'uid=' . (int)$pid_tmp);
-                                                       if ($row_tmp = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp)) {
-                                                               $pageIdsThatNeedCacheFlush[] = (int)$row_tmp['pid'];
-                                                       }
-                                                       $GLOBALS['TYPO3_DB']->sql_free_result($res_tmp);
-                                               }
-                                       } else {
-                                               // For other tables than "pages", delete cache for the records "parent page".
-                                               $pageIdsThatNeedCacheFlush[] = $pageUid = (int)$this->getPID($table, $uid);
-                                       }
-                                       // Call pre-processing function for clearing of cache for page ids:
-                                       if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'])) {
-                                               foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'] as $funcName) {
-                                                       $_params = array('pageIdArray' => &$pageIdsThatNeedCacheFlush, 'table' => $table, 'uid' => $uid, 'functionID' => 'clear_cache()');
-                                                       // Returns the array of ids to clear, FALSE if nothing should be cleared! Never an empty array!
-                                                       GeneralUtility::callUserFunction($funcName, $_params, $this);
-                                               }
-                                       }
-                                       // Delete cache for selected pages:
-                                       foreach ($pageIdsThatNeedCacheFlush as $pageId) {
-                                               // Workspaces always use "-1" as the page id which do not
-                                               // point to real pages and caches at all. Flushing caches for
-                                               // those records does not make sense and decreases performance
-                                               if ($pageId >= 0) {
-                                                       $tagsToClear['pageId_' . $pageId] = TRUE;
-                                               }
-                                       }
-                                       // Queue delete cache for current table and record
-                                       $tagsToClear[$table] = TRUE;
-                                       $tagsToClear[$table . '_' . $uid] = TRUE;
-                               }
-                               // Clear cache for pages entered in TSconfig:
-                               if (!empty($TSConfig['clearCacheCmd'])) {
-                                       $commands = GeneralUtility::trimExplode(',', $TSConfig['clearCacheCmd'], TRUE);
-                                       $clearCacheCommands = array_unique($commands);
-                                       unset($commands);
-                               }
-                               // Call post processing function for clear-cache:
-                               if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'])) {
-                                       $_params = array('table' => $table, 'uid' => $uid, 'uid_page' => $pageUid, 'TSConfig' => $TSConfig);
-                                       foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'] as $_funcRef) {
-                                               GeneralUtility::callUserFunction($_funcRef, $_params, $this);
-                                       }
+                               // For move commands we may get more than 1 parent.
+                               $pageUids = $this->getOriginalParentOfRecord($table, $uid);
+                               foreach ($pageUids as $originalParent) {
+                                       list($tagsToClearFromPrepare, $clearCacheCommandsFromPrepare)
+                                                       = $this->prepareCacheFlush($table, $uid, $originalParent);
+                                       $tagsToClear = array_merge($tagsToClear, $tagsToClearFromPrepare);
+                                       $clearCacheCommands = array_merge($clearCacheCommands, $clearCacheCommandsFromPrepare);
                                }
                        }
                }
@@ -7288,7 +7253,101 @@ class DataHandler {
                // Reset the cache clearing array
                static::$recordsToClearCacheFor = array();
 
+               // Reset the original pid array
+               static::$recordPidsForDeletedRecords = array();
+       }
 
+       /**
+        * Prepare the cache clearing
+        *
+        * @param string $table Table name of record that needs to be cleared
+        * @param int $uid UID of record for which the cache needs to be cleared
+        * @param int $pid Original pid of the page of the record which the cache needs to be cleared
+        * @return array Array with tagsToClear and clearCacheCommands
+        * @internal This function is internal only it may be changed/removed also in minor version numbers.
+        */
+       protected function prepareCacheFlush($table, $uid, $pid) {
+               $tagsToClear = array();
+               $clearCacheCommands = array();
+               $pageUid = 0;
+               // Get Page TSconfig relavant:
+               $TSConfig = $this->getTCEMAIN_TSconfig($pid);
+               if (empty($TSConfig['clearCache_disable'])) {
+                       // If table is "pages":
+                       $pageIdsThatNeedCacheFlush = array();
+                       if ($table === 'pages' || $table === 'pages_language_overlay') {
+                               if ($table === 'pages_language_overlay') {
+                                       $pageUid = $this->getPID($table, $uid);
+                               } else {
+                                       $pageUid = $uid;
+                               }
+                               // Builds list of pages on the SAME level as this page (siblings)
+                               $res_tmp = $GLOBALS['TYPO3_DB']->exec_SELECTquery('A.pid AS pid, B.uid AS uid', 'pages A, pages B', 'A.uid=' . (int)$pageUid . ' AND B.pid=A.pid AND B.deleted=0');
+                               $pid_tmp = 0;
+                               while ($row_tmp = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp)) {
+                                       $pageIdsThatNeedCacheFlush[] = (int)$row_tmp['uid'];
+                                       $pid_tmp = $row_tmp['pid'];
+                                       // Add children as well:
+                                       if ($TSConfig['clearCache_pageSiblingChildren']) {
+                                               $res_tmp2 = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', 'pages', 'pid=' . (int)$row_tmp['uid'] . ' AND deleted=0');
+                                               while ($row_tmp2 = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp2)) {
+                                                       $pageIdsThatNeedCacheFlush[] = (int)$row_tmp2['uid'];
+                                               }
+                                               $GLOBALS['TYPO3_DB']->sql_free_result($res_tmp2);
+                                       }
+                               }
+                               $GLOBALS['TYPO3_DB']->sql_free_result($res_tmp);
+                               // Finally, add the parent page as well:
+                               $pageIdsThatNeedCacheFlush[] = (int)$pid_tmp;
+                               // Add grand-parent as well:
+                               if ($TSConfig['clearCache_pageGrandParent']) {
+                                       $res_tmp = $GLOBALS['TYPO3_DB']->exec_SELECTquery('pid', 'pages', 'uid=' . (int)$pid_tmp);
+                                       if ($row_tmp = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp)) {
+                                               $pageIdsThatNeedCacheFlush[] = (int)$row_tmp['pid'];
+                                       }
+                                       $GLOBALS['TYPO3_DB']->sql_free_result($res_tmp);
+                               }
+                       } else {
+                               // For other tables than "pages", delete cache for the records "parent page".
+                               $pageIdsThatNeedCacheFlush[] = $pageUid = (int)$this->getPID($table, $uid);
+                       }
+                       // Call pre-processing function for clearing of cache for page ids:
+                       if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'])) {
+                               foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'] as $funcName) {
+                                       $_params = array('pageIdArray' => &$pageIdsThatNeedCacheFlush, 'table' => $table, 'uid' => $uid, 'functionID' => 'clear_cache()');
+                                       // Returns the array of ids to clear, FALSE if nothing should be cleared! Never an empty array!
+                                       GeneralUtility::callUserFunction($funcName, $_params, $this);
+                               }
+                       }
+                       // Delete cache for selected pages:
+                       foreach ($pageIdsThatNeedCacheFlush as $pageId) {
+                               // Workspaces always use "-1" as the page id which do not
+                               // point to real pages and caches at all. Flushing caches for
+                               // those records does not make sense and decreases performance
+                               if ($pageId >= 0) {
+                                       $tagsToClear['pageId_' . $pageId] = TRUE;
+                               }
+                       }
+                       // Queue delete cache for current table and record
+                       $tagsToClear[$table] = TRUE;
+                       $tagsToClear[$table . '_' . $uid] = TRUE;
+               }
+               // Clear cache for pages entered in TSconfig:
+               if (!empty($TSConfig['clearCacheCmd'])) {
+                       $commands = GeneralUtility::trimExplode(',', $TSConfig['clearCacheCmd'], TRUE);
+                       $clearCacheCommands = array_unique($commands);
+               }
+               // Call post processing function for clear-cache:
+               if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'])) {
+                       $_params = array('table' => $table, 'uid' => $uid, 'uid_page' => $pageUid, 'TSConfig' => $TSConfig);
+                       foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'] as $_funcRef) {
+                               GeneralUtility::callUserFunction($_funcRef, $_params, $this);
+                       }
+               }
+               return array(
+                       $tagsToClear,
+                       $clearCacheCommands
+               );
        }
 
        /**