minify
minify copied to clipboard
Asset Compilation
Caching is nice, but for Production the files never change. On a lot of sites, using something like APC cache would result in a one-time long load, and then never again. Even that is annoying, but definitely not a deal breaker. On Heroku servers go up and down completely randomly, meaning we get that first long load quite often. Static asset compilation for production seems all around better since it also benefits from not hitting PHP to get the cached version.
I went ahead and implemented this myself and it wasn't really that difficult. I grab all of the groups I have defined and minify them, and save them to a specific file. After that, I look for a group of specific html files to do replace the /min/g=? urls with the static compiled versions. I added a few config variables to make this behavior a little more flexible if do decide to include this in the main build. If you include something like this in the composer /bin/ directory then all we would have to do to add custom asset building would be to add php compileAssets.php or whatever you decide to call the file.
There are a bunch of optimizations you can do such as better file reading and being able to do wildcard replacements (ie: just put *.html instead of specifying every html file), but this should be a decent base at least.
compileAssets.php:
<?php
require_once(dirname(dirname(dirname(__FILE__))) . '/min/config.php');
// Compile all minified content down to static files
require "$min_libPath/Minify/Loader.php";
Minify_Loader::register();
Minify::setCache(null);
$min_serveOptions['minApp']['groups'] = $groups = (require(dirname(dirname(dirname(__FILE__))) . '/min/groupsConfig.php'));
$min_serveOptions['quiet'] = true;
$startTime = microtime(true);
$compiledCount = 0;
$skippedCount = 0;
$rewriteStrings = Array();
foreach($groups as $groupName => $groupDescription) {
if(in_array($groupName, $min_skipCompileGroups)) {
$skippedCount++;
continue;
}
$_GET['g'] = $groupName;
// Controller caches Content-Type, so have to recreate for every run
$minifiedData = Minify::serve(new Minify_Controller_MinApp(), $min_serveOptions);
if($minifiedData['success']) {
$folderName = 'assets';
$fileExtension = '';
if(strpos($minifiedData['headers']['Content-Type'], 'javascript') !== FALSE) {
$folderName = $fileExtension = 'js';
} else if(strpos($minifiedData['headers']['Content-Type'], 'css') !== FALSE) {
$folderName = $fileExtension = 'css';
}
$newFilePath = $folderName . '/' . $groupName . '-' . md5($minifiedData['content']) . ($fileExtension ? ('.' . $fileExtension) : '');
$staticFileName = $_SERVER["DOCUMENT_ROOT"] . '/' . $newFilePath;
file_put_contents($staticFileName, $minifiedData['content']);
$rewriteStrings[$min_rewriteBasePath . $groupName] = $newFilePath;
echo "Compiled $staticFileName\n";
$compiledCount++;
}
}
// Rewrite html files to reference these new static files
foreach($min_rewriteHtmlFiles as $htmlFile) {
$htmlFileName = $_SERVER["DOCUMENT_ROOT"] . '/' . $htmlFile;
$htmlContents = file_get_contents($htmlFileName);
if($htmlContents !== FALSE) {
$replacements = 0;
foreach($rewriteStrings as $startString => $endString) {
$replaceCount = 0;
$htmlContents = str_replace('"' . $startString . '"', '"' . $endString . '"', $htmlContents, $replaceCount);
$replacements += $replaceCount;
}
if($replacements > 0) {
file_put_contents($htmlFileName, $htmlContents);
echo "Inserted $replacements static file(s) in $htmlFileName\n";
}
} else {
echo 'Failed to update ' . $htmlFileName . "\n";
}
}
echo 'Compiled ' . $compiledCount . ' static files (' . $skippedCount . ' skipped) in ' . round(microtime(true) - $startTime) . ' seconds' . "\n";
New config variables:
$min_skipCompileGroups = Array(
'testjs',
'testcss'
);
$min_rewriteBasePath = 'min/g=';
$min_rewriteHtmlFiles = Array(
'main.php',
'extra.html'
);
composer.json:
"scripts": {
"compile": [
"php private/bin/compileAssets.php"
]
}
please make this against master branch, the 2.x branch is in maintenance mode. i guess script in bin is fine.
if i would write that script, i would but put the code in class, leave the bin script just think wrapper to initialize autoloader and run the class.
I agree that most of that logic should be directly in classes. I implemented it that way simply because I am calling it from the outside and want to keep Minify vendored. I am also fine with having it only in 3.0. I just wrote that against 2.3 since that is the latest release at this point.
Updated code sample to add content hashing to compiled files so you can use CloudFlare caching and still get updates when the file contents are changed:
$newFilePath = $folderName . '/' . $groupName . '-' . md5($minifiedData['content']) . ($fileExtension ? ('.' . $fileExtension) : '');
Please take a look at #541. This requires no compilation, but does require a manual directory delete to clear cache.