Index: t3lib/class.t3lib_compressor.php IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP <+>\n * (c) 2011 Kai Vogel \n * All rights reserved\n *\n * This script is part of the TYPO3 project. The TYPO3 project is\n * free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * The GNU General Public License can be found at\n * http://www.gnu.org/copyleft/gpl.html.\n * A copy is found in the textfile GPL.txt and important notices to the license\n * from the author is found in LICENSE.txt distributed with these scripts.\n *\n *\n * This script is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * This copyright notice MUST APPEAR in all copies of the script!\n ***************************************************************/\n\n/**\n * Compressor\n * This merges and compresses CSS and JavaScript files of the TYPO3 Backend.\n *\n * @author\tSteffen Gebert \n * @package TYPO3\n * @subpackage t3lib\n */\nclass t3lib_Compressor {\n\n\tprotected $targetDirectory = 'typo3temp/compressor/';\n\n\tprotected $relativePath = '';\n\n\tprotected $rootPath = '';\n\n\tprotected $backPath = '';\n\n\t\t// gzipped versions are only created if $TYPO3_CONF_VARS[TYPO3_MODE]['compressionLevel'] is set\n\tprotected $createGzipped = FALSE;\n\t\t// default compression level is -1\n\tprotected $gzipCompressionLevel = -1;\n\n\tprotected $htaccessTemplate = '\n\t\n\t\tExpiresActive on\n\t\tExpiresDefault \"access plus 7 days\"\n\t\n\tFileETag MTime Size\n';\n\n\t/**\n\t * Constructor\n\t */\n\tpublic function __construct() {\n\t\t\t// we check for existance of our targetDirectory\n\t\tif (!is_dir(PATH_site . $this->targetDirectory)) {\n\t\t\tt3lib_div::mkdir(PATH_site . $this->targetDirectory);\n\t\t}\n\n\t\t\t// if enabled, we check whether we should auto-create the .htaccess file\n\t\tif ($GLOBALS['TYPO3_CONF_VARS']['SYS']['generateApacheHtaccess']) {\n\t\t\t\t// check whether .htaccess exists\n\t\t\t$htaccessPath = PATH_site . $this->targetDirectory . '.htaccess';\n\t\t\tif (!file_exists($htaccessPath)) {\n\t\t\t\tt3lib_div::writeFile($htaccessPath, $this->htaccessTemplate);\n\t\t\t}\n\t\t}\n\n\t\t\t// decide whether we should create gzipped versions or not\n\t\t$compressionLevel = $GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['compressionLevel'];\n\t\t\t// we need zlib for gzencode()\n\t\tif (extension_loaded('zlib') && $compressionLevel) {\n\t\t\t$this->createGzipped = TRUE;\n\t\t\t\t// $compressionLevel can also be TRUE\n\t\t\tif (t3lib_utility_Math::canBeInterpretedAsInteger($compressionLevel)) {\n\t\t\t\t$this->gzipCompressionLevel = intval($compressionLevel);\n\t\t\t}\n\t\t}\n\n\t\t$this->setInitialPaths();\n\t}\n\n\t/**\n\t * Sets initial values for paths.\n\t *\n\t * @return void\n\t */\n\tpublic function setInitialPaths() {\n\t\t$this->setInitialRelativePath();\n\t\t$this->setInitialRootPath();\n\t\t$this->setInitialBackPath();\n\t}\n\n\t/**\n\t * Sets relative back path\n\t *\n\t * @return void\n\t */\n\tprotected function setInitialBackPath() {\n\t\t$backPath = (TYPO3_MODE === 'BE' ? $GLOBALS['BACK_PATH'] : '');\n\t\t$this->setBackPath($backPath);\n\t}\n\n\t/**\n\t * Sets absolute path to working directory\n\t *\n\t * @return void\n\t */\n\tprotected function setInitialRootPath() {\n\t\t$rootPath = (TYPO3_MODE === 'BE' ? PATH_typo3 : PATH_site);\n\t\t$this->setRootPath($rootPath);\n\t}\n\n\t/**\n\t * Sets relative path to PATH_site\n\t *\n\t * @return void\n\t */\n\tprotected function setInitialRelativePath() {\n\t\t$relativePath = (TYPO3_MODE === 'BE' ? $GLOBALS['BACK_PATH'] . '../' : '');\n\t\t$this->setRelativePath($relativePath);\n\t}\n\n\t/**\n\t * Sets relative path to PATH_site\n\t *\n\t * @param string $relativePath Relative path to site root\n\t * @return void\n\t */\n\tpublic function setRelativePath($relativePath) {\n\t\tif (is_string($relativePath)) {\n\t\t\t$this->relativePath = $relativePath;\n\t\t}\n\t}\n\n\t/**\n\t * Sets absolute path to working directory\n\t *\n\t * @param string $rootPath Absolute path\n\t * @return void\n\t */\n\tpublic function setRootPath($rootPath) {\n\t\tif (is_string($rootPath)) {\n\t\t\t$this->rootPath = $rootPath;\n\t\t}\n\t}\n\n\t/**\n\t * Sets relative back path\n\t *\n\t * @param string $backPath Back path\n\t * @return void\n\t */\n\tpublic function setBackPath($backPath) {\n\t\tif (is_string($backPath)) {\n\t\t\t$this->backPath = $backPath;\n\t\t}\n\t}\n\n\t/**\n\t * Concatenates the Stylesheet files\n\t *\n\t * Options:\n\t * baseDirectories If set, only include files below one of the base directories\n\t *\n\t * @param array $cssFiles CSS files to process\n\t * @param array $options Additional options\n\t * @return array CSS files\n\t */\n\tpublic function concatenateCssFiles(array $cssFiles, array $options = array()) {\n\t\t$filesToInclude = array();\n\t\tforeach ($cssFiles as $key => $fileOptions) {\n\t\t\t\t// no concatenation allowed for this file, so continue\n\t\t\tif (!empty($fileOptions['excludeFromConcatenation'])) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t\t// we remove BACK_PATH from $filename, so make it relative to root path\n\t\t\t$filenameFromMainDir = $this->getFilenameFromMainDir($fileOptions['file']);\n\t\t\t\t// if $options['baseDirectories'] set, we only include files below these directories\n\t\t\tif ((!isset($options['baseDirectories'])\n\t\t\t || $this->checkBaseDirectory($filenameFromMainDir, array_merge($options['baseDirectories'], array($this->targetDirectory))))\n\t\t\t && ($fileOptions['media'] === 'all')\n\t\t\t) {\n\t\t\t\t$filesToInclude[] = $filenameFromMainDir;\n\t\t\t\t\t// remove the file from the incoming file array\n\t\t\t\tunset($cssFiles[$key]);\n\t\t\t}\n\t\t}\n\n\t\tif (count($filesToInclude)) {\n\t\t\t$targetFile = $this->createMergedCssFile($filesToInclude);\n\t\t\t$targetFileRelative = $this->relativePath . $targetFile;\n\t\t\t$concatenatedOptions = array(\n\t\t\t\t'file' => $targetFileRelative,\n\t\t\t\t'rel' => 'stylesheet',\n\t\t\t\t'media' => 'all',\n\t\t\t\t'compress' => TRUE,\n\t\t\t);\n\t\t\t\t// place the merged stylesheet on top of the stylesheets\n\t\t\t$cssFiles = array_merge(array($targetFileRelative => $concatenatedOptions), $cssFiles);\n\t\t}\n\t\treturn $cssFiles;\n\t}\n\n\t/**\n\t * Concatenates the JavaScript files\n\t *\n\t * @param array $jsFiles JavaScript files to process\n\t * @return array JS files\n\t */\n\tpublic function concatenateJsFiles(array $jsFiles) {\n\t\t$filesToInclude = array();\n\t\tforeach ($jsFiles as $key => $fileOptions) {\n\t\t\t\t// invalid section found or no concatenation allowed, so continue\n\t\t\tif (empty($fileOptions['section']) || !empty($fileOptions['excludeFromConcatenation'])) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t\t// we remove BACK_PATH from $filename, so make it relative to root path\n\t\t\t$filesToInclude[$fileOptions['section']][] = $this->getFilenameFromMainDir($fileOptions['file']);\n\t\t\t\t// remove the file from the incoming file array\n\t\t\tunset($jsFiles[$key]);\n\t\t}\n\n\t\tif (!empty($filesToInclude)) {\n\t\t\tforeach ($filesToInclude as $section => $files) {\n\t\t\t\t$targetFile = $this->createMergedJsFile($files);\n\t\t\t\t$targetFileRelative = $this->relativePath . $targetFile;\n\t\t\t\t$concatenatedOptions = array(\n\t\t\t\t\t'file' => $targetFileRelative,\n\t\t\t\t\t'type' => 'text/javascript',\n\t\t\t\t\t'section' => $section,\n\t\t\t\t\t'compress' => TRUE,\n\t\t\t\t\t'forceOnTop' => FALSE,\n\t\t\t\t\t'allWrap' => '',\n\t\t\t\t);\n\t\t\t\t\t// place the merged javascript on top of the JS files\n\t\t\t\t$jsFiles = array_merge(array($targetFileRelative => $concatenatedOptions), $jsFiles);\n\t\t\t}\n\t\t}\n\t\treturn $jsFiles;\n\t}\n\n\t/**\n\t * Creates a merged CSS file\n\t *\n\t * @param array $filesToInclude Files which should be merged, paths relative to root path\n\t * @return mixed Filename of the merged file\n\t */\n\tprotected function createMergedCssFile(array $filesToInclude) {\n\t\treturn $this->createMergedFile($filesToInclude, 'css');\n\t}\n\n\t/**\n\t * Creates a merged JS file\n\t *\n\t * @param array $filesToInclude Files which should be merged, paths relative to root path\n\t * @return mixed Filename of the merged file\n\t */\n\tprotected function createMergedJsFile(array $filesToInclude) {\n\t\treturn $this->createMergedFile($filesToInclude, 'js');\n\t}\n\n\t/**\n\t * Creates a merged file with given file type\n\t *\n\t * @param array $filesToInclude Files which should be merged, paths relative to root path\n\t * @param string $type File type\n\t * @return mixed Filename of the merged file\n\t */\n\tprotected function createMergedFile(array $filesToInclude, $type = 'css') {\n\t\t\t// Get file type\n\t\t$type = strtolower(trim($type, '. '));\n\t\tif (empty($type)) {\n\t\t\tthrow new InvalidArgumentException('Error in t3lib_Compressor: No valid file type given for merged file', 1308957498);\n\t\t}\n\n\t\t\t// we add up the filenames, filemtimes and filsizes to later build a checksum over\n\t\t\t// it and include it in the temporary file name\n\t\t$unique = '';\n\n\t\tforeach ($filesToInclude as $key => $filename) {\n\t\t\tif (t3lib_div::isValidUrl($filename)) {\n\t\t\t\t$filesToInclude[$key] = $this->retrieveExternalFile($filename);\n\t\t\t\t$filename = $filesToInclude[$key];\n\t\t\t}\n\t\t\t$filepath = t3lib_div::resolveBackPath($this->rootPath . $filename);\n\t\t\t$unique .= $filename . filemtime($filepath) . filesize($filepath);\n\t\t}\n\t\t$targetFile = $this->targetDirectory . 'merged-' . md5($unique) . '.' . $type;\n\n\t\t\t// if the file doesn't already exist, we create it\n\t\tif (!file_exists(PATH_site . $targetFile)) {\n\t\t\t$concatenated = '';\n\t\t\t\t// concatenate all the files together\n\t\t\tforeach ($filesToInclude as $filename) {\n\t\t\t\t$contents = t3lib_div::getUrl(t3lib_div::resolveBackPath($this->rootPath . $filename));\n\t\t\t\t\t// only fix paths if files aren't already in typo3temp (already processed)\n\t\t\t\tif ($type === 'css' && !t3lib_div::isFirstPartOfStr($filename, $this->targetDirectory)) {\n\t\t\t\t\t$contents = $this->cssFixRelativeUrlPaths($contents, dirname($filename) . '/');\n\t\t\t\t}\n\t\t\t\t$concatenated .= LF . $contents;\n\t\t\t}\n\t\t\t\t// move @charset, @import and @namespace statements to top of new file\n\t\t\tif ($type === 'css') {\n\t\t\t\t$concatenated = $this->cssFixStatements($concatenated);\n\t\t\t}\n\t\t\tt3lib_div::writeFile(PATH_site . $targetFile, $concatenated);\n\t\t}\n\t\treturn $targetFile;\n\t}\n\n\t/**\n\t * Compress multiple css files\n\t *\n\t * @param array $cssFiles The files to compress (array key = filename), relative to requested page\n\t * @return array The CSS files after compression (array key = new filename), relative to requested page\n\t */\n\tpublic function compressCssFiles(array $cssFiles) {\n\t\t$filesAfterCompression = array();\n\t\tforeach ($cssFiles as $key => $fileOptions) {\n\t\t\t\t// if compression is enabled\n\t\t\tif ($fileOptions['compress']) {\n\t\t\t\t$filename = $this->compressCssFile($fileOptions['file']);\n\t\t\t\t$fileOptions['file'] = $filename;\n\t\t\t\t$filesAfterCompression[$filename] = $fileOptions;\n\t\t\t} else {\n\t\t\t\t$filesAfterCompression[$key] = $fileOptions;\n\t\t\t}\n\t\t}\n\t\treturn $filesAfterCompression;\n\t}\n\n\t/**\n\t * Compresses a CSS file\n\t *\n\t * Options:\n\t * baseDirectories If set, only include files below one of the base directories\n\t *\n\t * removes comments and whitespaces\n\t * Adopted from http://drupal.org/files/issues/minify_css.php__1.txt\n\t *\n\t * @param string $filename Source filename, relative to requested page\n\t * @return string Compressed filename, relative to requested page\n\t */\n\tpublic function compressCssFile($filename) {\n\t\t\t// generate the unique name of the file\n\t\t$filenameAbsolute = t3lib_div::resolveBackPath($this->rootPath . $this->getFilenameFromMainDir($filename));\n\t\t$unique = $filenameAbsolute . filemtime($filenameAbsolute) . filesize($filenameAbsolute);\n\n\t\t$pathinfo = pathinfo($filename);\n\t\t$targetFile = $this->targetDirectory . $pathinfo['filename'] . '-' . md5($unique) . '.css';\n\t\t\t// only create it, if it doesn't exist, yet\n\t\tif (!file_exists(PATH_site . $targetFile) || ($this->createGzipped && !file_exists(PATH_site . $targetFile . '.gzip'))) {\n\t\t\t$contents = t3lib_div::getUrl($filenameAbsolute);\n\t\t\t\t// Perform some safe CSS optimizations.\n\t\t\t$contents = str_replace(\"\\r\", '', $contents); // Strip any and all carriage returns.\n\t\t\t\t// Match and process strings, comments and everything else, one chunk at a time.\n\t\t\t\t// To understand this regex, read: \"Mastering Regular Expressions 3rd Edition\" chapter 6.\n\t\t\t$contents = preg_replace_callback('%\n\t\t\t\t# One-regex-to-rule-them-all! - version: 20100220_0100\n\t\t\t\t# Group 1: Match a double quoted string.\n\t\t\t\t(\"[^\"\\\\\\\\]*+(?:\\\\\\\\.[^\"\\\\\\\\]*+)*+\") | # or...\n\t\t\t\t# Group 2: Match a single quoted string.\n\t\t\t\t(\\'[^\\'\\\\\\\\]*+(?:\\\\\\\\.[^\\'\\\\\\\\]*+)*+\\') | # or...\n\t\t\t\t# Group 3: Match a regular non-MacIE5-hack comment.\n\t\t\t\t(/\\*[^\\\\\\\\*]*+\\*++(?:[^\\\\\\\\*/][^\\\\\\\\*]*+\\*++)*+/) | # or...\n\t\t\t\t# Group 4: Match a MacIE5-type1 comment.\n\t\t\t\t(/\\*(?:[^*\\\\\\\\]*+\\**+(?!/))*+\\\\\\\\[^*]*+\\*++(?:[^*/][^*]*+\\*++)*+/(?targetDirectory) === FALSE) {\n\t\t\t\t$filenameRelativeToMainDir = substr($filename, strlen($this->backPath));\n\t\t\t\t$contents = $this->cssFixRelativeUrlPaths($contents, dirname($filenameRelativeToMainDir) . '/');\n\t\t\t}\n\t\t\t$this->writeFileAndCompressed($targetFile, $contents);\n\t\t}\n\n\t\treturn $this->relativePath . $this->returnFileReference($targetFile);\n\t}\n\n\t/**\n\t * Callback function for preg_replace\n\t *\n\t * @see compressCssFile\n\t * @param array $matches\n\t * @return string the compressed string\n\t */\n\tpublic static function compressCssPregCallback($matches) {\n\t\tif ($matches[1]) { // Group 1: Double quoted string.\n\t\t\treturn $matches[1]; // Return the string unmodified.\n\t\t} elseif ($matches[2]) { // Group 2: Single quoted string.\n\t\t\treturn $matches[2]; // Return the string unmodified.\n\t\t} elseif ($matches[3]) { // Group 3: Regular non-MacIE5-hack comment.\n\t\t\treturn \"\\n\"; // Return single space.\n\t\t} elseif ($matches[4]) { // Group 4: MacIE5-hack-type-1 comment.\n\t\t\treturn \"\\n/*\\\\T1*/\\n\"; // Return minimal MacIE5-hack-type-1 comment.\n\t\t}\n\t\telseif ($matches[5]) { // Group 5,6,7: MacIE5-hack-type-2 comment\n\t\t\t$matches[6] = preg_replace('/\\s++([+>{};,)])/S', '$1', $matches[6]); // Clean pre-punctuation.\n\t\t\t$matches[6] = preg_replace('/([+>{}:;,(])\\s++/S', '$1', $matches[6]); // Clean post-punctuation.\n\t\t\t$matches[6] = preg_replace('/;?\\}/S', \"}\\n\", $matches[6]); // Add a touch of formatting.\n\t\t\treturn \"\\n/*T2\\\\*/\" . $matches[6] . \"\\n/*T2E*/\\n\"; // Minify and reassemble composite type2 comment.\n\t\t} elseif (isset($matches[8])) { // Group 8: Non-string, non-comment. Safe to clean whitespace here.\n\t\t\t$matches[8] = preg_replace('/^\\s++/', '', $matches[8]); // Strip all leading whitespace.\n\t\t\t$matches[8] = preg_replace('/\\s++$/', '', $matches[8]); // Strip all trailing whitespace.\n\t\t\t$matches[8] = preg_replace('/\\s{2,}+/', ' ', $matches[8]); // Consolidate multiple whitespace.\n\t\t\t$matches[8] = preg_replace('/\\s++([+>{};,)])/S', '$1', $matches[8]); // Clean pre-punctuation.\n\t\t\t$matches[8] = preg_replace('/([+>{}:;,(])\\s++/S', '$1', $matches[8]); // Clean post-punctuation.\n\t\t\t$matches[8] = preg_replace('/;?\\}/S', \"}\\n\", $matches[8]); // Add a touch of formatting.\n\t\t\treturn $matches[8];\n\t\t}\n\t\treturn $matches[0] . \"\\n/* ERROR! Unexpected _proccess_css_minify() parameter */\\n\"; // never get here\n\t}\n\n\t/**\n\t * Compress multiple javascript files\n\t *\n\t * @param array $jsFiles The files to compress (array key = filename), relative to requested page\n\t * @return array The js files after compression (array key = new filename), relative to requested page\n\t */\n\tpublic function compressJsFiles(array $jsFiles) {\n\t\t$filesAfterCompression = array();\n\t\tforeach ($jsFiles as $key => $fileOptions) {\n\t\t\t\t// if compression is enabled\n\t\t\tif ($fileOptions['compress']) {\n\t\t\t\t$fileOptions['file'] = $this->compressJsFile($fileOptions['file']);\n\t\t\t}\n\t\t\t$filesAfterCompression[$key] = $fileOptions;\n\t\t}\n\t\treturn $filesAfterCompression;\n\t}\n\n\t/**\n\t * Compresses a javascript file\n\t *\n\t * @param string $filename Source filename, relative to requested page\n\t * @return string Filename of the compressed file, relative to requested page\n\t */\n\tpublic function compressJsFile($filename) {\n\t\t\t// generate the unique name of the file\n\t\t$filenameAbsolute = t3lib_div::resolveBackPath($this->rootPath . $this->getFilenameFromMainDir($filename));\n\t\t$unique = $filenameAbsolute . filemtime($filenameAbsolute) . filesize($filenameAbsolute);\n\n\t\t$pathinfo = pathinfo($filename);\n\t\t$targetFile = $this->targetDirectory . $pathinfo['filename'] . '-' . md5($unique) . '.js';\n\t\t\t// only create it, if it doesn't exist, yet\n\t\tif (!file_exists(PATH_site . $targetFile) || ($this->createGzipped && !file_exists(PATH_site . $targetFile . '.gzip'))) {\n\t\t\t$contents = t3lib_div::getUrl($filenameAbsolute);\n\t\t\t$this->writeFileAndCompressed($targetFile, $contents);\n\t\t}\n\t\treturn $this->relativePath . $this->returnFileReference($targetFile);\n\t}\n\n\t/**\n\t * Finds the relative path to a file, relative to the root path.\n\t *\n\t * @param string $filename the name of the file\n\t * @return string the path to the file relative to the root path\n\t */\n\tprotected function getFilenameFromMainDir($filename) {\n\t\t\t// if BACK_PATH is empty return $filename\n\t\tif (empty($this->backPath)) {\n\t\t\treturn $filename;\n\t\t}\n\n\t\t\t// if the file exists in the root path, just return the $filename\n\t\tif (strpos($filename, $this->backPath) === 0) {\n\t\t\t$file = str_replace($this->backPath, '', $filename);\n\t\t\tif (is_file($this->rootPath . $file)) {\n\t\t\t\treturn $file;\n\t\t\t}\n\t\t}\n\n\t\t\t// build the file path relatively to the PATH_site\n\t\t$backPath = str_replace(TYPO3_mainDir, '', $this->backPath);\n\t\t$file = str_replace($backPath, '', $filename);\n\t\tif (substr($file, 0, 3) === '../') {\n\t\t\t$file = t3lib_div::resolveBackPath(PATH_typo3 . $file);\n\t\t} else {\n\t\t\t$file = PATH_site . $file;\n\t\t}\n\n\t\t\t// check if the file exists, and if so, return the path relative to TYPO3_mainDir\n\t\tif (is_file($file)) {\n\t\t\t$mainDirDepth = substr_count(TYPO3_mainDir, '/');\n\t\t\treturn str_repeat('../', $mainDirDepth) . str_replace(PATH_site, '', $file);\n\t\t}\n\n\t\t\t// none of above conditions were met, fallback to default behaviour\n\t\treturn substr($filename, strlen($this->backPath));\n\t}\n\n\t/**\n\t * Decides whether a file comes from one of the baseDirectories\n\t *\n\t * @param string $filename Filename\n\t * @param array $baseDirectories Base directories\n\t * @return boolean File belongs to a base directory or not\n\t */\n\tprotected function checkBaseDirectory($filename, array $baseDirectories) {\n\t\tforeach ($baseDirectories as $baseDirectory) {\n\t\t\t\t// check, if $filename starts with base directory\n\t\t\tif (t3lib_div::isFirstPartOfStr($filename, $baseDirectory)) {\n\t\t\t\treturn TRUE;\n\t\t\t}\n\t\t}\n\t\treturn FALSE;\n\t}\n\n\t/**\n\t * Fixes the relative paths inside of url() references in CSS files\n\t *\n\t * @param string $contents Data to process\n\t * @param string $oldDir Directory of the original file, relative to TYPO3_mainDir\n\t * @return string Processed data\n\t */\n\tprotected function cssFixRelativeUrlPaths($contents, $oldDir) {\n\t\t$mainDir = (TYPO3_MODE === 'BE' ? TYPO3_mainDir : '');\n\t\t$newDir = '../../' . $mainDir . $oldDir;\n\n\t\t\t// Replace \"url()\" paths\n\t\tif (stripos($contents, 'url') !== FALSE) {\n\t\t\t$regex = '/url(\\(\\s*[\"\\']?(?!\\/)([^\"\\']+)[\"\\']?\\s*\\))/iU';\n\t\t\t$contents = $this->findAndReplaceUrlPathsByRegex($contents, $regex, $newDir, '(\\'|\\')');\n\t\t}\n\n\t\t\t// Replace \"@import\" paths\n\t\tif (stripos($contents, '@import') !== FALSE) {\n\t\t\t$regex = '/@import\\s*([\"\\']?(?!\\/)([^\"\\']+)[\"\\']?)/i';\n\t\t\t$contents = $this->findAndReplaceUrlPathsByRegex($contents, $regex, $newDir, '\"|\"');\n\t\t}\n\n\t\treturn $contents;\n\t}\n\n\t/**\n\t * Finds and replaces all URLs by using a given regex\n\t *\n\t * @param string $contents Data to process\n\t * @param string $regex Regex used to find URLs in content\n\t * @param string $newDir Path to prepend to the original file\n\t * @param string $wrap Wrap around replaced values\n\t * @return string Processed data\n\t */\n\tprotected function findAndReplaceUrlPathsByRegex($contents, $regex, $newDir, $wrap = '|') {\n\t\t$matches = array();\n\t\t$replacements = array();\n\t\t$wrap = explode('|', $wrap);\n\n\t\tpreg_match_all($regex, $contents, $matches);\n\t\tforeach ($matches[2] as $matchCount => $match) {\n\t\t\t\t// remove ',\" or white-spaces around\n\t\t\t$match = trim($match, '\\'\" ');\n\n\t\t\t\t// we must not rewrite paths containing \":\" or \"url(\", e.g. data URIs (see RFC 2397)\n\t\t\tif (strpos($match, ':') === FALSE && !preg_match('/url\\s*\\(/i', $match)) {\n\t\t\t\t$newPath = t3lib_div::resolveBackPath($newDir . $match);\n\t\t\t\t$replacements[$matches[1][$matchCount]] = $wrap[0] . $newPath . $wrap[1];\n\t\t\t}\n\t\t}\n\n\t\t\t// replace URL paths in content\n\t\tif (!empty($replacements)) {\n\t\t\t$contents = str_replace(array_keys($replacements), array_values($replacements), $contents);\n\t\t}\n\n\t\treturn $contents;\n\t}\n\n\t/**\n\t * Moves @charset, @import and @namespace statements to the top of\n\t * the content, because they must occur before all other CSS rules\n\t *\n\t * @param string $contents Data to process\n\t * @return string Processed data\n\t */\n\tprotected function cssFixStatements($contents) {\n\t\t$matches = array();\n\t\t$comment = LF . '/* moved by compressor */' . LF;\n\n\t\t\t// nothing to do, so just return contents\n\t\tif (stripos($contents, '@charset') === FALSE && stripos($contents, '@import') === FALSE\n\t\t\t&& stripos($contents, '@namespace') === FALSE) {\n\n\t\t\treturn $contents;\n\t\t}\n\n\t\t$regex = '/@(charset|import|namespace)\\s*(url)?\\s*\\(?\\s*[\"\\']?[^\"\\']+[\"\\']?\\s*\\)?.*;/i';\n\t\tpreg_match_all($regex, $contents, $matches);\n\t\tif (!empty($matches[0])) {\n\t\t\t\t// remove existing statements\n\t\t\t$contents = str_replace($matches[0], '', $contents);\n\t\t\t\t// add statements to the top of contents in the order they occur in original file\n\t\t\t$contents = $comment . implode($comment, $matches[0]) . LF . $contents;\n\t\t}\n\n\t\treturn $contents;\n\t}\n\n\t/**\n\t * Writes $contents into file $filename together with a gzipped version into $filename.gz\n\t *\n\t * @param string $filename Target filename\n\t * @param string $contents File contents\n\t * @return void\n\t */\n\tprotected function writeFileAndCompressed($filename, $contents) {\n\t\t\t// write uncompressed file\n\t\tt3lib_div::writeFile(PATH_site . $filename, $contents);\n\n\t\tif ($this->createGzipped) {\n\t\t\t\t// create compressed version\n\t\t\tt3lib_div::writeFile(PATH_site . $filename . '.gzip', gzencode($contents, $this->gzipCompressionLevel));\n\t\t}\n\t}\n\n\t/**\n\t * Decides whether a client can deal with gzipped content or not and returns the according file name,\n\t * based on HTTP_ACCEPT_ENCODING\n\t *\n\t * @param string $filename File name\n\t * @return string $filename suffixed with '.gzip' or not - dependent on HTTP_ACCEPT_ENCODING\n\t */\n\tprotected function returnFileReference($filename) {\n\t\t\t// if the client accepts gzip and we can create gzipped files, we give him compressed versions\n\t\tif ($this->createGzipped && strpos(t3lib_div::getIndpEnv('HTTP_ACCEPT_ENCODING'), 'gzip') !== FALSE) {\n\t\t\treturn $filename . '.gzip';\n\t\t} else {\n\t\t\treturn $filename;\n\t\t}\n\t}\n\n\t/**\n\t * Retrieves an external file and stores it locally.\n\t *\n\t * @param string $url\n\t * @return string Temporary local filename for the externally-retrieved file\n\t */\n\tprotected function retrieveExternalFile($url) {\n\t\t$externalContent = t3lib_div::getUrl($url);\n\t\t$filename = $this->targetDirectory . 'external-' . md5($url);\n\t\tt3lib_div::writeFile(PATH_site . $filename, $externalContent);\n\t\treturn $filename;\n\t}\n\n}\n\nif (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_compressor.php'])) {\n\tinclude_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_compressor.php']);\n}\n\n?> =================================================================== --- t3lib/class.t3lib_compressor.php (revision d683be0af376110f3085e500fbb0dfb3854d685a) +++ t3lib/class.t3lib_compressor.php (revision ) @@ -659,7 +659,11 @@ protected function retrieveExternalFile($url) { $externalContent = t3lib_div::getUrl($url); $filename = $this->targetDirectory . 'external-' . md5($url); + + //write only if file not exists and md5 of the content is not the same with fetched one + if (!file_exists(PATH_site . $filename) && (md5($externalContent) != md5(t3lib_div::getUrl(PATH_site . $filename)))) { - t3lib_div::writeFile(PATH_site . $filename, $externalContent); + t3lib_div::writeFile(PATH_site . $filename, $externalContent); + } return $filename; } \ No newline at end of file