From 682c16fb4c7d55acf4e245cab2b7c64197f4ff17 Mon Sep 17 00:00:00 2001
From: Benjamin Mack <benni@typo3.org>
Date: Wed, 31 Dec 2014 01:17:16 +0100
Subject: [PATCH] [TASK] Cache cache_imagesizes using the Caching Framework
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Streamline the code by moving the functionality to cache image sizes
processed by GraphicalFunctions (local-only so far) from the database
table cache_imagesizes to the Caching Framework.

The following changes are done:
* use sha1 instead of md5
* use Caching Framework instead of hardcoded DB table
* store only necessary data in the cache

The new Caching Framework configuration is stored
in a new group called "lowlevel" which is only emptied
when pressing the clear cache button in the install tool.

The former table cache_imagesizes has been filled automatically and
never been cleaned except manually in the Install Tool area "Cleanup".
This means there could be a lot of leftover entries. Now the cache is
cleared when using the common clear caches button in the install tool.

Resolves: #28484
Releases: master
Change-Id: Ia68410eb382163e90654718aeb17165dc48e40cc
Reviewed-on: http://review.typo3.org/35686
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Stephan Großberndt <stephan@grossberndt.de>
Reviewed-by: Helmut Hummel <helmut.hummel@typo3.org>
Tested-by: Helmut Hummel <helmut.hummel@typo3.org>
---
 .../Classes/Imaging/GraphicalFunctions.php    | 108 ++++++++++--------
 .../Configuration/DefaultConfiguration.php    |   8 ++
 typo3/sysext/core/ext_tables.sql              |  13 ---
 .../Controller/Action/Tool/CleanUp.php        |   4 -
 4 files changed, 69 insertions(+), 64 deletions(-)

diff --git a/typo3/sysext/core/Classes/Imaging/GraphicalFunctions.php b/typo3/sysext/core/Classes/Imaging/GraphicalFunctions.php
index 81b0f13cf384..b22cf5583522 100644
--- a/typo3/sysext/core/Classes/Imaging/GraphicalFunctions.php
+++ b/typo3/sysext/core/Classes/Imaging/GraphicalFunctions.php
@@ -227,7 +227,7 @@ class GraphicalFunctions {
 	public $absPrefix = '';
 
 	/**
-	 * ImageMagick scaling command; "-geometry" eller "-sample". Used in makeText() and imageMagickConvert()
+	 * ImageMagick scaling command; "-geometry" or "-sample". Used in makeText() and imageMagickConvert()
 	 *
 	 * @var string
 	 */
@@ -2234,7 +2234,7 @@ class GraphicalFunctions {
 				if (file_exists($output)) {
 					$info[3] = $output;
 					$info[2] = $newExt;
-					// params could realisticly change some imagedata!
+					// params might change some image data!
 					if ($params) {
 						$info = $this->getImageDimensions($info[3]);
 					}
@@ -2261,7 +2261,7 @@ class GraphicalFunctions {
 			if ($returnArr = $this->getCachedImageDimensions($imageFile)) {
 				return $returnArr;
 			} else {
-				if ($temp = @getImageSize($imageFile)) {
+				if ($temp = @getimagesize($imageFile)) {
 					$returnArr = array($temp[0], $temp[1], strtolower($reg[0]), $imageFile);
 				} else {
 					$returnArr = $this->imageMagickIdentify($imageFile);
@@ -2276,61 +2276,75 @@ class GraphicalFunctions {
 	}
 
 	/**
-	 * Cache the result of the getImageDimensions function into the database. Does not check if the
-	 * file exists!
+	 * Caches the result of the getImageDimensions function into the database. Does not check if the file exists.
 	 *
 	 * @param array $identifyResult Result of the getImageDimensions function
-	 * @return bool TRUE if operation was successful
+	 *
+	 * @return bool always TRUE
 	 */
-	public function cacheImageDimensions($identifyResult) {
-		// Create md5 hash of filemtime and filesize
-		$fileStatus = stat($identifyResult[3]);
-		$md5Hash = md5($fileStatus['mtime'] . $fileStatus['size']);
-		$result = FALSE;
-		if ($md5Hash) {
-			$fieldArray = array(
-				'md5hash' => $md5Hash,
-				'md5filename' => md5($identifyResult[3]),
-				'tstamp' => $GLOBALS['EXEC_TIME'],
-				'filename' => $identifyResult[3],
-				'imagewidth' => $identifyResult[0],
-				'imageheight' => $identifyResult[1]
+	public function cacheImageDimensions(array $identifyResult) {
+		$filePath = $identifyResult[3];
+		$statusHash = $this->generateCacheKeyForImageFile($filePath);
+
+		/** @var \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend $cache */
+		$cache = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\CacheManager::class)->getCache('cache_imagesizes');
+		$imageDimensions = array(
+			'hash'        => $statusHash,
+			'imagewidth'  => $identifyResult[0],
+			'imageheight' => $identifyResult[1],
+		);
+		$cache->set($statusHash, $imageDimensions);
+
+		return TRUE;
+	}
+
+	/**
+	 * Fetches the cached image dimensions from the cache. Does not check if the image file exists.
+	 *
+	 * @param string $filePath the image file path
+	 *
+	 * @return array|bool an array where [0]/[1] is w/h, [2] is extension and [3] is the file name,
+	 *                    or FALSE for a cache miss
+	 */
+	public function getCachedImageDimensions($filePath) {
+		$statusHash = $this->generateCacheKeyForImageFile($filePath);
+		/** @var \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend $cache */
+		$cache = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\CacheManager::class)->getCache('cache_imagesizes');
+		$cachedImageDimensions = $cache->get($statusHash);
+		if (!isset($cachedImageDimensions['hash'])) {
+			return FALSE;
+		}
+
+		if ($cachedImageDimensions['hash'] !== $statusHash) {
+			// The file has changed. Delete the cache entry.
+			$cache->remove($filePath);
+			$result = FALSE;
+		} else {
+			preg_match('/([^\\.]*)$/', $filePath, $imageExtension);
+			$result = array(
+				(int)$cachedImageDimensions['imagewidth'],
+				(int)$cachedImageDimensions['imageheight'],
+				strtolower($imageExtension[0]),
+				$filePath
 			);
-			$GLOBALS['TYPO3_DB']->exec_INSERTquery('cache_imagesizes', $fieldArray);
-			if (!($err = $GLOBALS['TYPO3_DB']->sql_error())) {
-				$result = TRUE;
-			}
 		}
+
 		return $result;
 	}
 
 	/**
-	 * Fetch the cached imageDimensions from the MySQL database. Does not check if the image file exists!
+	 * Creates the key for the image dimensions cache for an image file.
 	 *
-	 * @param string $imageFile The image filepath
-	 * @return array Returns an array where [0]/[1] is w/h, [2] is extension and [3] is the filename.
+	 * This method does not check if the image file actually exists.
+	 *
+	 * @param string $filePath
+	 *
+	 * @return string the hash key (an SHA1 hash), will not be empty
 	 */
-	public function getCachedImageDimensions($imageFile) {
-		// Create md5 hash of filemtime and filesize
-		$fileStatus = stat($imageFile);
-		$md5Hash = md5($fileStatus['mtime'] . $fileStatus['size']);
-		$cachedImageDimensions = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('md5hash, md5filename, imagewidth, imageheight', 'cache_imagesizes', 'md5filename=' . $GLOBALS['TYPO3_DB']->fullQuoteStr(md5($imageFile), 'cache_imagesizes'));
-		$result = FALSE;
-		if (is_array($cachedImageDimensions)) {
-			if ($cachedImageDimensions['md5hash'] != $md5Hash) {
-				// File has changed, delete the row
-				$GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_imagesizes', 'md5filename=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($cachedImageDimensions['md5filename'], 'cache_imagesizes'));
-			} else {
-				preg_match('/([^\\.]*)$/', $imageFile, $imageExtension);
-				$result = array(
-					(int)$cachedImageDimensions['imagewidth'],
-					(int)$cachedImageDimensions['imageheight'],
-					strtolower($imageExtension[0]),
-					$imageFile
-				);
-			}
-		}
-		return $result;
+	protected function generateCacheKeyForImageFile($filePath) {
+		$fileStatus = stat($filePath);
+
+		return sha1($fileStatus['mtime'] . $fileStatus['size']);
 	}
 
 	/**
diff --git a/typo3/sysext/core/Configuration/DefaultConfiguration.php b/typo3/sysext/core/Configuration/DefaultConfiguration.php
index 746d5e28c78b..11973abe8f2a 100644
--- a/typo3/sysext/core/Configuration/DefaultConfiguration.php
+++ b/typo3/sysext/core/Configuration/DefaultConfiguration.php
@@ -175,6 +175,14 @@ return array(
 					'options' => array(),
 					'groups' => array('pages', 'all')
 				),
+				'cache_imagesizes' => array(
+					'frontend' => \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend::class,
+					'backend' => \TYPO3\CMS\Core\Cache\Backend\Typo3DatabaseBackend::class,
+					'options' => array(
+						'defaultLifetime' => 0,
+					),
+					'groups' => array('lowlevel'),
+				),
 				'l10n' => array(
 					'frontend' => \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend::class,
 					'backend' => \TYPO3\CMS\Core\Cache\Backend\SimpleFileBackend::class,
diff --git a/typo3/sysext/core/ext_tables.sql b/typo3/sysext/core/ext_tables.sql
index d5538e7d1f61..f05f5e23e5c1 100644
--- a/typo3/sysext/core/ext_tables.sql
+++ b/typo3/sysext/core/ext_tables.sql
@@ -90,19 +90,6 @@ CREATE TABLE be_users (
 	KEY username (username)
 );
 
-#
-# Table structure for table 'cache_imagesizes'
-#
-CREATE TABLE cache_imagesizes (
-	md5hash varchar(32) DEFAULT '' NOT NULL,
-	md5filename varchar(32) DEFAULT '' NOT NULL,
-	tstamp int(11) DEFAULT '0' NOT NULL,
-	filename varchar(255) DEFAULT '' NOT NULL,
-	imagewidth mediumint(11) unsigned DEFAULT '0' NOT NULL,
-	imageheight mediumint(11) unsigned DEFAULT '0' NOT NULL,
-	PRIMARY KEY (md5filename)
-) ENGINE=InnoDB;
-
 #
 # Table structure for table 'pages'
 #
diff --git a/typo3/sysext/install/Classes/Controller/Action/Tool/CleanUp.php b/typo3/sysext/install/Classes/Controller/Action/Tool/CleanUp.php
index 64c4a4765bea..83959f69e554 100644
--- a/typo3/sysext/install/Classes/Controller/Action/Tool/CleanUp.php
+++ b/typo3/sysext/install/Classes/Controller/Action/Tool/CleanUp.php
@@ -62,10 +62,6 @@ class CleanUp extends Action\AbstractAction {
 				'name' => 'be_sessions',
 				'description' => 'Backend user sessions'
 			),
-			array(
-				'name' => 'cache_imagesizes',
-				'description' => 'Cached image sizes',
-			),
 			array(
 				'name' => 'cache_md5params',
 				'description' => 'Frontend redirects',
-- 
GitLab