Changeset 10089
- Timestamp:
- 07/03/08 15:34:47 (5 months ago)
- Files:
-
- plugins/sfCombineFilterPlugin/branches/kraven/README (modified) (5 diffs)
- plugins/sfCombineFilterPlugin/branches/kraven/lib/JSMin.class.php (added)
- plugins/sfCombineFilterPlugin/branches/kraven/lib/sfCombineFilter.class.php (modified) (9 diffs)
- plugins/sfCombineFilterPlugin/branches/kraven/modules (added)
- plugins/sfCombineFilterPlugin/branches/kraven/modules/sfCombineFilter (added)
- plugins/sfCombineFilterPlugin/branches/kraven/modules/sfCombineFilter/actions (added)
- plugins/sfCombineFilterPlugin/branches/kraven/modules/sfCombineFilter/actions/actions.class.php (added)
- plugins/sfCombineFilterPlugin/branches/kraven/modules/sfCombineFilter/config (added)
- plugins/sfCombineFilterPlugin/branches/kraven/modules/sfCombineFilter/config/filters.yml (added)
- plugins/sfCombineFilterPlugin/branches/kraven/modules/sfCombineFilter/lib (added)
- plugins/sfCombineFilterPlugin/branches/kraven/modules/sfCombineFilter/lib/BasesfCombineFilterActions.class.php (added)
- plugins/sfCombineFilterPlugin/branches/kraven/web (deleted)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
plugins/sfCombineFilterPlugin/branches/kraven/README
r6025 r10089 3 3 The `sfCombineFilter` is an implementation of the [http://rakaz.nl/extra/code/combine CSS and Javascript Combiner] by Niels Leenheer. It makes your pages load faster by combining and compressing javascript and css files. 4 4 5 This filter provides means to automatically combine your included javascripts and stylesheets into one request each. All of the included files are combined into a single large file and compressed using gzip. The resulting file is cached and used every time that particular combination of files is used, until any of the files are changed at which point a new cache is automatically created. 5 This filter provides means to automatically combine your included javascripts and stylesheets into one request each. All of the included files are combined into a single large file and compressed using gzip. The resulting file is cached and used every time that particular combination of files is used, until any of the files are changed at which point a new cache is automatically created. 6 6 7 7 The symfony filter looks at all of the javascripts and stylesheets resources specified in the current response, and automatically creates a string that combines all of the names together. When your visitors request this special file, they will get one large file, gzipped and ready to go. … … 37 37 38 38 * In your app/config/filters.yml, add the sfCombineFilter. This must come AFTER "common": 39 39 40 40 {{{ 41 41 rendering: ~ … … 53 53 javascripts: true 54 54 stylesheets: true 55 compress_js: true 56 compress_css: true 55 57 root_js_only: false 56 58 root_css_only: true … … 59 61 }}} 60 62 61 By default all javascripts and stylesheets requested in '/js-packed' or '/css-packed' respectively are combined into one request each, whereas files requested outside of that path are ignored. You can turn off the filter all together for javascript or css by setting those parameters to "false". If you want to include files requested from any path and not just those inside /js or /css, then set the parameters "root_js_only" and "root_css_only" to false. 62 63 === Modify .htaccess === 64 Add the following to your .htaccess rewrite rules: 63 * In your app/config/routing.yml 65 64 {{{ 66 RewriteRule ^css/packed/(.*\.css) /sfCombineFilterPlugin/combine.php?type=css&files=$1 67 RewriteRule ^js/packed/(.*\.js) /sfCombineFilterPlugin/combine.php?type=javascript&files=$1 65 download_packed_files: 66 url: /packed/:type/:cachefilename/packed.* 67 param: { module: sfCombineFilter, action: download } 68 68 }}} 69 69 70 This will capture any requests made to /js/packed/* or /css/packed/* and route them through the compression script. 70 * In your app/config/settings.yml 71 72 add sfCombineFilter to enabled_modules: [] 73 74 By default all javascripts and stylesheets requested in /packed/js/ or /packed/css/ respectively are combined into one request each, whereas files requested outside of that path are ignored. You can turn off the filter all together for javascript or css by setting those parameters to "false". If you want to include files requested from any path and not just those inside /js or /css, then set the parameters "root_js_only" and "root_css_only" to false. 71 75 72 76 == Changelog == … … 82 86 - Fixes issues when included files outside of root css and js directories. 83 87 - New: Option to minify the js using JSMin. 88 89 === 2008-07-03 | 0.1.2 beta === 90 Changes by Benjamin Runnels: 91 - Moved cache generation/management into the filter 92 - Removed .htaccess modification requirements 93 - added compress_js and compress_css options to the filter 94 - added client side caching for the packed files and improved the request name so it will cache properly 95 - added routing rules to route to the sfCombineFilter module 96 - added non-harmfull css compressor as JSMin can mess up the css for some javascript libraries plugins/sfCombineFilterPlugin/branches/kraven/lib/sfCombineFilter.class.php
r6025 r10089 4 4 * 5 5 * sfCombineFilter.class.php (c) 2007 Scott Meves. 6 * Combine.php Copyright (c) 2006 by Niels Leenheer6 * sfCombineFilter.class.php modifications (c) 2008 by Benjamin Runnels * 7 7 * 8 8 * For the full copyright and license information, please view the LICENSE … … 20 20 class sfCombineFilter extends sfFilter 21 21 { 22 private $response,$sf_relative_url_root,$type,$files,$lastmodified; 22 23 23 24 public function execute ($filterChain) … … 25 26 $filterChain->execute(); 26 27 27 if ($this->getParameter('javascripts', true)) { 28 sfLoader::loadHelpers('Asset'); 29 $this->response = $this->getContext()->getResponse(); 30 $this->sf_relative_url_root = $this->getContext()->getRequest()->getRelativeUrlRoot(). $this->getContext()->getRequest()->getScriptName(); 31 32 if ($this->getParameter('javascripts', true)) 33 { 34 $this->type = 'javascript'; 35 $this->files = array(); 36 $this->lastmodified = 0; 28 37 $this->getCombinedJavascripts(); 29 38 } 30 39 31 if ($this->getParameter('stylesheets', true)) { 40 if ($this->getParameter('stylesheets', true)) 41 { 42 $this->type = 'css'; 43 $this->files = array(); 44 $this->lastmodified = 0; 32 45 $this->getCombinedStylesheets(); 33 46 } … … 36 49 protected function getCombinedJavascripts() 37 50 { 38 $response = $this->getContext()->getResponse();39 $sf_relative_url_root = $this->getContext()->getRequest()->getRelativeUrlRoot();40 51 $root_js_only = $this->getParameter('root_js_only', true); 41 52 … … 45 56 foreach (array('first', '', 'last') as $position) 46 57 { 47 foreach ($ response->getJavascripts($position) as $files => $options)58 foreach ($this->response->getJavascripts($position) as $files => $options) 48 59 { 49 60 if (!is_array($files)) … … 65 76 $path = _compute_public_path($file, 'js', 'js'); 66 77 67 if ((!$root_js_only && !strpos($path, '://')) || ($root_js_only && strpos($path, $sf_relative_url_root.'/js/') === 0)) { 68 $combined_sources[] = ($root_js_only ? preg_replace("/^".str_replace('/', '\/', $sf_relative_url_root.'/js/')."/i", '', $path) : $path); 69 $response->getParameterHolder()->remove($file, 'helper/asset/auto/javascript'.($position ? '/'.$position : '')); 78 if ((!$root_js_only && !strpos($path, '://')&& strpos($path, '.js')) || ($root_js_only && strpos($path, $this->sf_relative_url_root.'/js/') === 0)) { 79 $element = ($root_js_only ? preg_replace("/^".str_replace('/', '\/', $this->sf_relative_url_root.'/js/')."/i", '', $path) : $path); 80 if($this->checkFile($element)) 81 { 82 $combined_sources[] = $element; 83 $this->response->getParameterHolder()->remove($file, 'helper/asset/auto/javascript'.($position ? '/'.$position : '')); 84 } 85 70 86 } 71 87 } … … 74 90 75 91 if (count($combined_sources)) { 76 $combined_sources_str = $sf_relative_url_root . '/js/packed/' . implode(',', $combined_sources); 77 $response->addJavascript($combined_sources_str, ''); 78 } 79 92 $cacheFileName = $this->lastmodified . '-' . md5(implode(',', $combined_sources)); 93 if($this->cacheFile($cacheFileName)) 94 { 95 $combined_sources_str = $this->sf_relative_url_root ."/packed/js/$cacheFileName/packed.js"; 96 //TODO: keep track if there is a dynamic file in the middle of static files and create multiple packed files 97 // if required to keep the proper order 98 $this->response->addJavascript($combined_sources_str, 'first'); 99 } 100 } 101 } 102 103 protected function cacheFile($cacheFileName) 104 { 105 //get an instance of the file cache object. We grab the web root then get the name of the cache folder 106 //we don't want to use sf_cache_dir because that is application and environment specific 107 //we don't want to a path relative to sf_web_dir because the sf_root_dir can be changed, better to start from there 108 $cache = new sfFileCache(sfConfig::get('sf_root_dir').DIRECTORY_SEPARATOR.sfConfig::get('sf_cache_dir_name')); 109 110 //cached files are in the 'packed_files' name space 111 112 //Next we see if we can pull the file from the cache. 113 if($cache->has($cacheFileName, 'packed_files')) 114 { 115 //Ok! We have a cached copy of the file! 116 return true; 117 } 118 else 119 { 120 // Get contents of the files 121 $contents = ''; 122 foreach ($this->files as $path) 123 { 124 if($this->type=='css') 125 { 126 //TODO: may need more replacements in the path but this covers everything I've come across so far 127 $cssPath = str_replace(array(sfConfig::get('sf_root_dir'),'plugins/','web/'),'',$path); 128 $con = $this->fixCssPaths(file_get_contents($path),$cssPath); 129 } 130 else 131 { 132 $con = file_get_contents($path); 133 } 134 135 $contents .= "\n\n" . $con; 136 } 137 138 if ($this->type=='javascript'&& $this->getParameter('compress_js')) 139 { 140 $contents = JSMin::minify($contents); 141 } 142 elseif($this->type=='css'&& $this->getParameter('compress_css')) 143 { 144 $contents = $this->compressCss($contents); 145 } 146 147 //Write the file data to the cache 148 $cache->set($cacheFileName, 'packed_files', $contents); 149 return true; 150 } 151 152 return false; 153 } 154 155 private function compressCss($content) 156 { 157 // remove comments 158 $content = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $content); 159 // remove tabs, spaces, newlines, etc. 160 $content = str_replace(array("\r\n", "\r", "\n", "\t", ' ', ' ', ' '), '', $content); 161 return $content; 162 } 163 164 private function fixCssPaths($content,$path) 165 { 166 if (preg_match_all("/url\(\s?[\'|\"]?(.+)[\'|\"]?\s?\)/ix", $content, $urlMatches) ) 167 { 168 $urlMatches = array_unique( $urlMatches[1] ); 169 $cssPathArray = explode(DIRECTORY_SEPARATOR, $path); 170 171 // pop the css file name 172 array_pop( $cssPathArray ); 173 $cssPathCount = count( $cssPathArray ); 174 foreach( $urlMatches as $match ) 175 { 176 $match = str_replace( array('"', "'"), '', $match ); 177 $relativeCount = substr_count( $match, '../' ); 178 // replace path if it is realtive 179 if ( $match[0] !== '/' and strpos( $match, 'http:' ) === false ) 180 { 181 $cssPathSlice = $relativeCount === 0 ? $cssPathArray : array_slice( $cssPathArray , 0, $cssPathCount - $relativeCount ); 182 $newMatchPath = implode('/', $cssPathSlice) . '/' . str_replace('../', '', $match); 183 $content = str_replace( $match, $newMatchPath, $content ); 184 } 185 } 186 } 187 return $content; 188 } 189 190 private function checkFile($element) 191 { 192 $sf_symfony_data_dir = sfConfig::get('sf_symfony_data_dir'); 193 194 $cachedir = sfConfig::get('sf_cache_dir'); 195 $webdir = sfConfig::get('sf_web_dir'); 196 $cssdir = $webdir.DIRECTORY_SEPARATOR.'css'; 197 $jsdir = $webdir.DIRECTORY_SEPARATOR.'js'; 198 199 // Determine the directory and type we should use 200 switch ($this->type) 201 { 202 case 'css': 203 $dir = $cssdir; 204 break; 205 case 'javascript': 206 $dir = $jsdir; 207 break; 208 } 209 210 $path = null; 211 if (substr($element, 0, 4) == '/sf/') 212 { 213 $path = $sf_symfony_data_dir.DIRECTORY_SEPARATOR.'web'.$element; 214 } 215 else if (substr($element, 0, 3) == 'sf/') 216 { 217 $path = $sf_symfony_data_dir.DIRECTORY_SEPARATOR.'web'.DIRECTORY_SEPARATOR.$element; 218 } 219 else if (0 === strpos($element, '/')) 220 { 221 $path = realpath($webdir.$element); 222 } 223 else 224 { 225 $path = realpath($dir.DIRECTORY_SEPARATOR.$element); 226 } 227 228 if (!file_exists($path)) return false; 229 230 $this->files[] = $path; 231 $this->lastmodified = max($this->lastmodified, filemtime($path)); 232 return true; 80 233 } 81 234 82 235 protected function getCombinedStylesheets() 83 236 { 84 $response = $this->getContext()->getResponse();85 $sf_relative_url_root = $this->getContext()->getRequest()->getRelativeUrlRoot();86 237 $root_css_only = $this->getParameter('root_css_only', true); 87 88 238 $already_seen = array(); 89 239 $combined_sources = array(); … … 91 241 foreach (array('first', '', 'last') as $position) 92 242 { 93 foreach ($ response->getStylesheets($position) as $files => $options)243 foreach ($this->response->getStylesheets($position) as $files => $options) 94 244 { 95 245 if (!is_array($files)) … … 111 261 $path = _compute_public_path($file, 'css', 'css'); 112 262 113 if ((!$root_css_only && !strpos($path, '://')) || ($root_css_only && strpos($path, $sf_relative_url_root.'/css/') === 0)) { 114 $combined_sources[] = (!$root_css_only ? preg_replace("/^".str_replace('/', '\/', $sf_relative_url_root.'/css/')."/i", '', $path) : $path); 115 $response->getParameterHolder()->remove($file, 'helper/asset/auto/stylesheet'.($position ? '/'.$position : '')); 116 } 117 } 118 } 119 } 120 121 if (count($combined_sources)) { 122 $combined_sources_str = $sf_relative_url_root . '/css/packed/' . implode(',', $combined_sources); 123 $response->addStylesheet($combined_sources_str, '', array('raw_name'=>true)); 124 } 125 } 126 127 protected function isInvalidMediaType($options) { 263 if ((!$root_css_only && !strpos($path, '://')&& strpos($path, '.css')) || ($root_css_only && strpos($path, $this->sf_relative_url_root.'/css/') === 0)) { 264 $element = (!$root_css_only ? preg_replace("/^".str_replace('/', '\/', $this->sf_relative_url_root.'/css/')."/i", '', $path) : $path); 265 if($this->checkFile($element)) 266 { 267 $combined_sources[] = $element; 268 $this->response->getParameterHolder()->remove($file, 'helper/asset/auto/stylesheet'.($position ? '/'.$position : '')); 269 } 270 } 271 } 272 } 273 } 274 275 if (count($combined_sources)) 276 { 277 $cacheFileName = $this->lastmodified . '-' . md5(implode(',', $combined_sources)); 278 if($this->cacheFile($cacheFileName)) 279 { 280 $combined_sources_str = $this->sf_relative_url_root ."/packed/css/$cacheFileName/packed.css"; 281 //TODO: keep track if there is a dynamic file in the middle of static files and create multiple packed files 282 // if required to keep the proper order 283 $this->response->addStylesheet($combined_sources_str, 'first'); 284 } 285 } 286 } 287 288 protected function isInvalidMediaType($options) 289 { 128 290 return isset($options['media']) && !in_array($options['media'], array('', 'all', 'screen')); 129 291 } 130 292 131 protected function isAbsolutePath($options) { 293 protected function isAbsolutePath($options) 294 { 132 295 return isset($options['absolute']) && $options['absolute'] == true; 133 296 }