Here a new version with JSMin and CssTidy. Cleaned some parts a bit. If there was an option to read the $_scriptFiles, I could extend the render function to do all this automatically, and point the $scriptMap to the generated file.
Currently I call the function in a filter in my BaseController class:
public function filterMainSetup($filterChain)
{
$combineJS[] = '/js/ui/ui.core.js';
$combineJS[] = '/js/ui/ui.tabs.js';
$cs->registerCombinedFiles('js',$combineJS,$position=CClientScript::POS_HEAD);
$combineCSS[] = '/css/layout.css';
$combineCSS[] = '/css/main.css';
$cs->registerCombinedFiles('css',$combineCSS);
$filterChain->run();
}
The class (needs jsmin in components and csstidy in extensions) :
class ExtendedClientScript extends CClientScript
{
public $minify = true;
public $scriptPath;
public $scriptUrl;
public $basePath;
public $ttlDays = 1;
public $prefix = 'c_';
public $cssTidy = true;
public $cssTidyTemplate = "highest_compression";
public $cssTidyConfig = array(
'remove_bslash' => TRUE,
'compress_colors' => TRUE,
'compress_font-weight' => TRUE,
'lowercase_s' => FALSE,
'optimise_shorthands' => 1,
'remove_last_,' => TRUE,
'case_properties' => 1,
'sort_properties' => FALSE,
'sort_selectors' => FALSE,
'merge_selectors' => 2,
'discard_invalid_properties' => FALSE,
'css_level' => 'CSS2.1',
'preserve_css' => FALSE,
'timestamp' => FALSE
);
public function registerCombinedFiles($type, $urls, $position=self::POS_HEAD)
{
if (YII_DEBUG) $this->minify = false;
$this->scriptPath or $this->scriptPath = Yii::app()->assetManager->basePath;
$this->scriptUrl or $this->scriptUrl = Yii::app()->assetManager->baseUrl;
$this->basePath or $this->basePath = Yii::app()->assetManager->basePath;
$mtimes = array();
foreach ($urls as $asset)
$mtimes[] = filemtime($this->basePath.'/'.trim($asset,'/'));
$combineHash = substr(md5(implode('',$urls)),0,10);
$changesHash = substr(md5(serialize($mtimes)),0,10);
$optionsHash = ($type == 'js') ? substr(md5($this->basePath.$this->minify.$this->ttlDays.$this->prefix),0,10):
substr(md5($this->basePath.$this->cssTidy.$this->ttlDays.$this->prefix.serialize($this->cssTidyConfig)),0,10);
$fileName = "{$this->prefix}$combineHash$optionsHash$changesHash.$type";
$renewFile = (file_exists($this->scriptPath.'/'.$fileName)) ? false : true;
if ($renewFile)
{
$this->garbageCollect($type);
foreach ($urls as $key => $file)
$combinedFile .= file_get_contents($this->basePath.'/'.$file);
if ($type == 'js' && $this->minify)
$combinedFile = JSMin::minify($combinedFile);
if ($type == 'css' && $this->cssTidy)
{
Yii::import('application.extensions.csstidy.*');
require_once('class.csstidy.php');
$cssTidy = new csstidy();
$cssTidy->load_template($this->cssTidyTemplate);
foreach($this->cssTidyConfig as $k => $v)
$cssTidy->set_cfg($k, $v);
$cssTidy->parse($combinedFile);
$combinedFile = $cssTidy->print->plain();
}
file_put_contents($this->scriptPath.'/'.$fileName, $combinedFile);
}
($type == 'js') ? $this->registerScriptFile($this->scriptUrl.'/'.$fileName, $position):
$this->registerCssFile($this->scriptUrl.'/'.$fileName);
}
private function garbageCollect($type)
{
$files = CFileHelper::findFiles($this->scriptPath, array('fileTypes' => array($type), 'level'=> 0));
$ttl = $this->ttlDays * 60 * 60 * 24;
foreach($files as $file)
{
if (strpos($file, $this->prefix) !== false)
{
if ((fileatime($file) + $ttl) < time() )
{
@unlink($file);
}
}
}
}
}
I know there can be some speedloss bcz of the filemtime routine, but I believe the lesser amount of data send to the client is more important. One could cache the filename to lessen the burden.