Added feature #14247: Add a concatenator for CSS and JS (thanks to Steffen Gebert)
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_compressor.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2010 Steffen Gebert (steffen@steffen-gebert.de)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27
28 /**
29 * Compressor
30 * This class can currently merge CSS files of the TYPO3 Backend.
31 *
32 * @author Steffen Gebert <steffen@steffen-gebert.de>
33 * @package TYPO3
34 * @subpackage t3lib
35 * $Id$
36 */
37 class t3lib_compressor {
38
39 protected $skinStylesheetDirectories = array();
40 protected $targetDirectory = 'typo3temp/compressor/';
41
42 /**
43 * Constructor
44 */
45 public function __construct() {
46
47 // we check for existance of our targetDirectory
48 if (!is_dir(PATH_site . $this->targetDirectory)) {
49 t3lib_div::mkdir(PATH_site . $this->targetDirectory);
50 }
51 }
52
53 /**
54 * Concatenates the cssFiles
55 *
56 * Options:
57 * baseDirectories If set, only include files below one of the base directories
58 *
59 * @param array $cssFiles CSS files added to the PageRenderer
60 * @param array $options Additional options
61 * @return array CSS files
62 */
63 public function concatenateCssFiles(array $cssFiles, $options = array()) {
64 $filesToInclude = array();
65 foreach ($cssFiles as $filename => $fileOptions) {
66 // we remove BACK_PATH from $filename, so make it relative to TYPO3_mainDir
67 $filenameFromMainDir = substr($filename, strlen($GLOBALS['BACK_PATH']));
68 // if $options['baseDirectories'] set, we only include files below these directroies
69 if (!isset($options['baseDirectories']) || $this->checkBaseDirectory($filenameFromMainDir, $options['baseDirectories'])) {
70 $filesToInclude[] = $filenameFromMainDir;
71 // remove the file from the incoming file array
72 unset($cssFiles[$filename]);
73 }
74 }
75
76 if (count($filesToInclude)) {
77 $targetFile = $this->createMergedCssFile($filesToInclude);
78 $concatenatedOptions = array(
79 'rel' => 'stylesheet',
80 'media' => 'all',
81 'title' => 'Merged TYPO3 Backend Stylesheets',
82 'compress' => TRUE,
83 );
84 $targetFileRelative = $GLOBALS['BACK_PATH'] . '../' . $targetFile;
85 // place the merged stylesheet on top of the stylesheets
86 $cssFiles = array_merge(array($targetFileRelative => $concatenatedOptions), $cssFiles);
87 }
88 return $cssFiles;
89 }
90
91 /**
92 * Creates a merged CSS file
93 *
94 * @param array $filesToInclude Files which should be merged, paths relative to TYPO3_mainDir
95 * @return mixed Filename of the merged file
96 */
97 protected function createMergedCssFile(array $filesToInclude) {
98 // we add up the filenames, filemtimes and filsizes to later build a checksum over
99 // it and include it in the temporary file name
100 $unique = '';
101
102 foreach ($filesToInclude as $filename) {
103 $unique .= $filename . filemtime($GLOBALS['BACK_PATH'] . $filename) . filesize($GLOBALS['BACK_PATH'] . $filename);
104 }
105 $targetFile = $this->targetDirectory . TYPO3_MODE . '-'. md5($unique) . '.css';
106
107 // if the file doesn't already exist, we create it
108 if (!file_exists($targetFile)) {
109 $concatenated = '';
110 // concatenate all the files together
111 foreach ($filesToInclude as $filename) {
112 $contents = file_get_contents($GLOBALS['BACK_PATH'] . $filename);
113 $concatenated .= $this->cssFixRelativeUrlPaths($contents, dirname($filename) . '/', $this->targetDirectory);
114 }
115
116 if (strlen($concatenated)) {
117 t3lib_div::writeFile(PATH_site . $targetFile, $concatenated);
118 }
119 }
120 return $targetFile;
121 }
122
123 /**
124 * Decides whether a CSS file comes from one of the baseDirectories
125 *
126 * @param string $filename Filename
127 * @return boolean File belongs to a skin or not
128 */
129 protected function checkBaseDirectory($filename, array $baseDirectories) {
130 foreach ($baseDirectories as $baseDirectory) {
131 // check, if $filename starts with $skinStylesheetDirectory (it's position 0, not FALSE!)
132 if (strpos($filename, $baseDirectory) === 0) {
133 return TRUE;
134 }
135 }
136 return FALSE;
137 }
138
139 /**
140 * Fixes the relative paths inside of url() references in CSS files
141 *
142 * @param string $contents Data to process
143 * @param string $oldDir Directory of the originial file, relative to TYPO3_mainDir
144 * @param string $newDir Directory of the resulting file
145 * @return string Processed data
146 */
147 protected function cssFixRelativeUrlPaths($contents, $oldDir, $newDir) {
148 $matches = array();
149 preg_match_all('/url[\s]*\([\'\"]?(.*)[\'\"]?\)/iU', $contents, $matches);
150 foreach ($matches[1] as $match) {
151 // remove '," or white-spaces around
152 $match = preg_replace('/[\"\'\s]/', '', $match);
153
154 $newPath = t3lib_div::resolveBackPath('../../' . TYPO3_mainDir . $oldDir . $match);
155 $contents = str_replace($match, $newPath, $contents);
156 }
157 return $contents;
158 }
159 }
160
161 ?>