I have written the following script which simply:
- Loops through JavaScript files (either defined in a particular package, or separately with the libraries array)
- Reads the files into the script and combines them into one string
- Minifies the string and saves the contents to a cached file on the server and returns the contents.
Some key features:
- The cached file is only updated when the script detects that one of the loaded JavaScript libraries has been modified
- The script only gathers the files if a cached version does not exist
I am sure most of us programmers will notice that the array of libraries could very easily be built dynamically by scanning the _js/_source/
directory and therefore I would not have to add each library to the $js_libraries
array individually. The reason I have not done this is simple, it is much slower when there are a lot of files and the absolute key of this script is speed!
I simply want to know whether this could be written better and whether it has any areas I could improve on.
<?php
/**
* Sine Macula Package API
* The Sine Macula Package API loads and packs documents ready to
* be returned to the browser
* @name jsapi.php
* @author Ben Carey
* @version 1.0
* @date 24/10/2012
* @copyright (c) 2012 Sine Macula Limited (sinemaculammviii.com)
*/
// Retrieve the supplied variables
$package = $_REQUEST['package'] ? strtolower($_REQUEST['package']) : 'all';
$packageURL = $_REQUEST['packageURL'];
$libraries = strtolower($_REQUEST['libraries']);
// Define all the javascript libraries
$js_libraries = array(
'md5' => '/_js/_source/_md5/sinemacula.md5.js',
'cookies' => '/_js/_source/_cookies/sinemacula.cookies.js',
'preloading' => '/_js/_source/_preloading/sinemacula.preloading.js',
'browsers' => '/_js/_source/_browsers/sinemacula.browsers.js',
'overlay' => '/_js/_source/_overlay/sinemacula.overlay.js',
'forms' => '/_js/_source/_forms/sinemacula.forms.js',
'fancybox' => '/_js/_source/_fancybox/jQuery.lightbox-0.5.js',
'indexof' => '/_js/_source/_indexOf/init.indexOf.js'
// This array will be much larger
);
// Only define the javascript packages if the
// package is not coming from an external source
if($package){
// Define all the javascript packages
$js_packages = array(
'all' => array_keys($js_libraries)
// This array will be much larger
);
// Make sure the supplied package exists
if(array_key_exists($package,$js_packages)){
// Gather all the libraries for the particular package
$package = array_intersect_key($js_libraries,array_flip($js_packages[$package]));
}else{
$errors[] = 'The supplied package does not exist';
}
}elseif($packageURL){
// Attempt to retrieve the contents of the external package
$package = @file_get_contents($packageURL);
if($package){
// Decode the JSON object into an array
$package = json_decode($package,true);
}else{
$errors[] = 'Error retrieving package from remote source';
}
}
// Add the libraries to the package array
// if they have been supplied
if($libraries){
// Explode the libraries string into an array
$libraries = explode(',',$libraries);
// Gather the unrecognised libraries
$unrecognised_libraries = array_diff($libraries,array_keys($js_libraries));
// If there are unrecognised libraries then
// return relevant errors
if($unrecognised_libraries){
foreach($unrecognised_libraries as $value){
$errors[] = '\''.$value.'\' library does not exist';
}
}
// Insert all the sources for the library keys
$libraries = array_intersect_key($js_libraries,array_flip($libraries));
// Make sure package is an array
$package = is_array($package) ? $package : array();
// Merge the libraries with the package array
$package = array_merge($package,$libraries);
}
// Create a hash of the package
$package_hash = md5(implode('',array_keys($package)));
// Loop through the package and check their modified dates
$modified_timestamps = array_map(function($a){
// Get the modified time of each individual library
return filemtime(dirname(__FILE__).$a);
},$package);
// Hash the modified time, this will determine whether the,
// library has been updated
$modified_hash = md5(implode('',$modified_timestamps));
// Check to see if the package directory exists in the cache
if(@is_dir(dirname(__FILE__).'/_js/_cache/_' . $package_hash )){
// If the modified hash matches a file in the folder then it
// does not need updating
if(file_exists(dirname(__FILE__).'/_js/_cache/_'.$package_hash.'/'.$modified_hash.'.js')){
// Get the contents of the file
$packed_script = file_get_contents(dirname(__FILE__).'/_js/_cache/_'.$package_hash.'/'.$modified_hash.'.js');
}else{
// Set pack to true to ensure that the script
// is created
$pack = true;
}
}else{
// Create the directory for the package
@mkdir(dirname(__FILE__).'/_js/_cache/_'.$package_hash);
// Set pack to true to ensure that the script
// is created
$pack = true;
}
// If pack is set then gather all the files, combine
// into one and pack. This will create the file as
// well, and store it in the folder '_cache'
if($pack){
// Loop through the package and load all of the libraries
// into the script ready to be packed
foreach($package as $key => $value){
// Get the script
$current_script = @file_get_contents(dirname(__FILE__).$value);
// Return an error on fail to load
if($current_script){
$combined_script .= $current_script.";\n";
}else{
$errors[] = 'Failed to load \''.$key.'\' library';
}
}
// Include the javascript packer
require_once(dirname(__FILE__).'/_php/_classes/class.packer.php');
// Initiate the packer and pack the javascript
$js_packer = new JavaScriptPacker($combined_script);
$packed_script = $js_packer->pack();
// Retrieve the cached scripts (should only be one)
$cached_scripts = @glob(dirname(__FILE__).'/_js/_cache/_'.$package_hash.'/*.js');
// Delete all the files in the package directory
if(is_array($cached_scripts)){
array_map('unlink',$cached_scripts);
}
// Create the new javascript package file if there
// have been no errors
if(empty($errors)){
if(!@file_put_contents(dirname(__FILE__).'/_js/_cache/_'.$package_hash.'/'.$modified_hash.'.js',$packed_script)){
$errors[] = 'There was an error loading the javascript libraries';
}
}
}
// Set the content header to javascript
// and write the packed script or errors
header("Content-Type: text/javascript");
if(empty($errors)){
echo $packed_script;
}else{
// Loop through the errors and write them to the
// javascript console
foreach($errors as $value){
echo 'console.log("'.$value.'");';
}
}
?>