[BUGFIX] DataHandler: Cache TCA field eval array 86/35186/16
authorStephan Großberndt <stephan@grossberndt.de>
Wed, 24 Dec 2014 00:21:32 +0000 (01:21 +0100)
committerJigal van Hemert <jigal.van.hemert@typo3.org>
Wed, 18 Mar 2015 20:25:15 +0000 (21:25 +0100)
Introduce a runtime cache for the eval values of TCA input and text
fields. This improves performance for bulk inserts since
DataHandler->checkValue_input and checkValue_text are called for each
according field of each record and the available evaluations do not
change during a call. If no eval is set, do not execute
checkValue_*_Eval functions at all.

Additionally align usages of getMemoryCache() to runtimeCache and
keys of nestedElementCalls and elementsToBeDeleted in cache_runtime.

Resolves: #63670
Releases: master, 6.2
Change-Id: I3d2122a61e3194402743fa0b364d6bd80d4e72dc
Reviewed-on: http://review.typo3.org/35186
Reviewed-by: Nicole Cordes <typo3@cordes.co>
Reviewed-by: Markus Klein <klein.t3@reelworx.at>
Tested-by: Markus Klein <klein.t3@reelworx.at>
Reviewed-by: Jigal van Hemert <jigal.van.hemert@typo3.org>
Tested-by: Jigal van Hemert <jigal.van.hemert@typo3.org>
typo3/sysext/core/Classes/DataHandling/DataHandler.php

index eb9bc4f..7ce751e 100644 (file)
@@ -653,7 +653,7 @@ class DataHandler {
         *
         * @var array
         */
-       protected static $recordsToClearCacheFor = array();
+       static protected $recordsToClearCacheFor = array();
 
        /**
         * Database layer. Identical to $GLOBALS['TYPO3_DB']
@@ -663,10 +663,32 @@ class DataHandler {
        protected $databaseConnection;
 
        /**
+        * Runtime Cache to store and retrieve data computed for a single request
+        *
+        * @var \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend
+        */
+       protected $runtimeCache = NULL;
+
+       /**
+        * Prefix for the cache entries of 'eval'-strings from table fields since the runtimeCache has a global scope.
+        *
+        * @var string
+        */
+       protected $cachePrefixFieldEval = 'core-datahandler-eval-';
+
+       /**
+        * Prefix for the cache entries of nested element calls since the runtimeCache has a global scope.
+        *
+        * @var string
+        */
+       protected $cachePrefixNestedElementCalls = 'core-datahandler-nestedElementCalls-';
+
+       /**
         *
         */
        public function __construct() {
                $this->databaseConnection = $GLOBALS['TYPO3_DB'];
+               $this->runtimeCache = $this->getRuntimeCache();
        }
 
        /**
@@ -1627,9 +1649,17 @@ class DataHandler {
         * @return array Modified $res array
         */
        public function checkValue_text($res, $value, $tcaFieldConf, $PP, $field = '') {
-               $evalCodesArray = GeneralUtility::trimExplode(',', $tcaFieldConf['eval'], TRUE);
-               $res = $this->checkValue_text_Eval($value, $evalCodesArray, $tcaFieldConf['is_in']);
-               return $res;
+               if (!isset($tcaFieldConf['eval']) || $tcaFieldConf['eval'] === '') {
+                       return array('value' => $value);
+               }
+               $cacheId = $this->cachePrefixFieldEval . $tcaFieldConf['eval'];
+               if ($this->runtimeCache->has($cacheId)) {
+                       $evalCodesArray = $this->runtimeCache->get($cacheId);
+               } else {
+                       $evalCodesArray = GeneralUtility::trimExplode(',', $tcaFieldConf['eval'], TRUE);
+                       $this->runtimeCache->set($cacheId, $evalCodesArray);
+               }
+               return $this->checkValue_text_Eval($value, $evalCodesArray, $tcaFieldConf['is_in']);
        }
 
        /**
@@ -1670,19 +1700,33 @@ class DataHandler {
                                $value = $tcaFieldConf['range']['lower'];
                        }
                }
-               // Process evaluation settings:
-               $evalCodesArray = GeneralUtility::trimExplode(',', $tcaFieldConf['eval'], TRUE);
-               $res = $this->checkValue_input_Eval($value, $evalCodesArray, $tcaFieldConf['is_in']);
-               // Process UNIQUE settings:
-               // Field is NOT set for flexForms - which also means that uniqueInPid and unique is NOT available for flexForm fields! Also getUnique should not be done for versioning and if PID is -1 ($realPid<0) then versioning is happening...
-               if ($field && $realPid >= 0) {
-                       if ($res['value'] && in_array('uniqueInPid', $evalCodesArray, TRUE)) {
-                               $res['value'] = $this->getUnique($table, $field, $res['value'], $id, $realPid);
+
+               if (empty($tcaFieldConf['eval'])) {
+                       $res = array('value' => $value);
+               } else {
+                       // Process evaluation settings:
+                       $cacheId = $this->cachePrefixFieldEval . $tcaFieldConf['eval'];
+                       if ($this->runtimeCache->has($cacheId)) {
+                               $evalCodesArray = $this->runtimeCache->get($cacheId);
+                       } else {
+                               $evalCodesArray = GeneralUtility::trimExplode(',', $tcaFieldConf['eval'], TRUE);
+                               $this->runtimeCache->set($cacheId, $evalCodesArray);
                        }
-                       if ($res['value'] && in_array('unique', $evalCodesArray, TRUE)) {
-                               $res['value'] = $this->getUnique($table, $field, $res['value'], $id);
+
+                       $res = $this->checkValue_input_Eval($value, $evalCodesArray, $tcaFieldConf['is_in']);
+
+                       // Process UNIQUE settings:
+                       // Field is NOT set for flexForms - which also means that uniqueInPid and unique is NOT available for flexForm fields! Also getUnique should not be done for versioning and if PID is -1 ($realPid<0) then versioning is happening...
+                       if ($field && $realPid >= 0 && !empty($res['value'])) {
+                               if (in_array('uniqueInPid', $evalCodesArray, TRUE)) {
+                                       $res['value'] = $this->getUnique($table, $field, $res['value'], $id, $realPid);
+                               }
+                               if ($res['value'] && in_array('unique', $evalCodesArray, TRUE)) {
+                                       $res['value'] = $this->getUnique($table, $field, $res['value'], $id);
+                               }
                        }
                }
+
                // Handle native date/time fields
                if ($isDateOrDateTimeField) {
                        // Convert the timestamp back to a date/time
@@ -7320,11 +7364,11 @@ class DataHandler {
        }
 
        /**
-        * Gets an instance of the memory cache.
+        * Gets an instance of the runtime cache.
         *
         * @return VariableFrontend
         */
-       protected function getMemoryCache() {
+       protected function getRuntimeCache() {
                return $this->getCacheManager()->getCache('cache_runtime');
        }
 
@@ -7337,7 +7381,7 @@ class DataHandler {
         * @return bool
         */
        protected function isNestedElementCallRegistered($table, $id, $identifier) {
-               $nestedElementCalls = (array)$this->getMemoryCache()->get('nestedElementCalls');
+               $nestedElementCalls = (array)$this->runtimeCache->get($this->cachePrefixNestedElementCalls);
                return isset($nestedElementCalls[$identifier][$table][$id]);
        }
 
@@ -7351,9 +7395,9 @@ class DataHandler {
         * @return void
         */
        protected function registerNestedElementCall($table, $id, $identifier) {
-               $nestedElementCalls = (array)$this->getMemoryCache()->get('nestedElementCalls');
+               $nestedElementCalls = (array)$this->runtimeCache->get($this->cachePrefixNestedElementCalls);
                $nestedElementCalls[$identifier][$table][$id] = TRUE;
-               $this->getMemoryCache()->set('nestedElementCalls', $nestedElementCalls);
+               $this->runtimeCache->set($this->cachePrefixNestedElementCalls, $nestedElementCalls);
        }
 
        /**
@@ -7362,7 +7406,7 @@ class DataHandler {
         * @return void
         */
        protected function resetNestedElementCalls() {
-               $this->getMemoryCache()->remove('nestedElementCalls');
+               $this->runtimeCache->remove($this->cachePrefixNestedElementCalls);
        }
 
        /**
@@ -7377,7 +7421,7 @@ class DataHandler {
         * @see versionizeRecord
         */
        protected function isElementToBeDeleted($table, $id) {
-               $elementsToBeDeleted = (array)$this->getMemoryCache()->get('core-t3lib_TCEmain-elementsToBeDeleted');
+               $elementsToBeDeleted = (array)$this->runtimeCache->get('core-datahandler-elementsToBeDeleted');
                return isset($elementsToBeDeleted[$table][$id]);
        }
 
@@ -7388,8 +7432,8 @@ class DataHandler {
         * @see process_datamap
         */
        protected function registerElementsToBeDeleted() {
-               $elementsToBeDeleted = (array)$this->getMemoryCache()->get('core-t3lib_TCEmain-elementsToBeDeleted');
-               $this->getMemoryCache()->set('core-t3lib_TCEmain-elementsToBeDeleted', array_merge($elementsToBeDeleted, $this->getCommandMapElements('delete')));
+               $elementsToBeDeleted = (array)$this->runtimeCache->get('core-datahandler-elementsToBeDeleted');
+               $this->runtimeCache->set('core-datahandler-elementsToBeDeleted', array_merge($elementsToBeDeleted, $this->getCommandMapElements('delete')));
        }
 
        /**
@@ -7399,7 +7443,7 @@ class DataHandler {
         * @see process_datamap
         */
        protected function resetElementsToBeDeleted() {
-               $this->getMemoryCache()->remove('core-t3lib_TCEmain-elementsToBeDeleted');
+               $this->runtimeCache->remove('core-datahandler-elementsToBeDeleted');
        }
 
        /**