Update 10/27/2011: This method is outdated. I recommend using the very awesome Carabiner for your minification/concatenation needs.
If you still want to stick to this method, I would recommend implementing some sort of caching for your minified/concatenated files. Carabiner does this for your automatically, however!
——–
As any good UI developer will tell you, one of the best practices for speeding up your website is to minimize HTTP requests. Since most of the user’s loading time is tied up in things like loading scripts, images, flash, stylesheets, etc., reducing the number of these requests can drastically help improve loading time. It also helps to Gzip the components as well, which we’ll also cover in this tutorial.
This first tutorial will cover how to concatenate and minify your javascript and CSS files into a single HTTP request, using the CodeIgniter framework. For sites that load lots of scripts like jQuery plugins, this can be very beneficial for performance. Additionally, minification of scripts and stylesheets can help reduce the size of files, saving you bandwidth and shortening loading times for user.
So, let’s get started!
NOTE: I am assuming in this tutorial that you are already using an .htaccess file to eliminate index.php from your URIs.
Create an assets directory
You’ll want to manage your static assets (js, css, images, etc) by placing them in a directory called /assets in your web root. Here’s what your CI folder structure should look like:
/css
/images
/js
/system
/application
...
index.php
You may have your application/ folder elsewhere, but for this tutorial we’ll have it in the default location, inside the /system folder.
You’ll also need the below two libraries, which are essential to this tutorial:
Let’s grab copies of the following two libraries:
JSMin-PHP, Ryan Grove’s PHP port of Douglas Crockford’s JSMin.
Joe Scylla’s CssMin
Name these two files JSMin.php and CssMin.php, respectively, and place them in your application/libraries folder.
Create an asset_url() helper function
Having a helper function to access your assets URL can be helpful in the event you decide to rename or change the location of your application. It works like the base_url() function, only it will link to your assets rather than your base URL (obviously). In config.php, add the following code:
|--------------------------------------------------------------------------
| Assets Path
|--------------------------------------------------------------------------
|
| URL Path to static assets library
|
*/
$config['asset_path'] = 'assets/';
$config['css_path'] = 'assets/css/';
$config['js_path'] = 'assets/js/';
We’ll be using css_path and js_path a little later in this tutorial, so keep those in mind.
Next, add this function in /system/helpers/path_helper.php
* Get asset URL
*
* @access public
* @return string
*/
if (!function_exists('asset_url'))
{
function asset_url()
{
//get an instance of CI so we can access our configuration
$CI =& get_instance();
//return the full asset path
return base_url() . $CI->config->item('asset_path');
}
}
Add the following to your config/autoload.php file, to helpers:
Now you’re set! You can now call asset_url() anywhere to access your asset URL path. So instead of writing:
You would write instead:
Now that that’s out of the way, let’s get to the good stuff:
Create asset loader helper functions
In order to reduce our HTTP requests for our scripts to a single call, we’ll need a script tag that does the call for all files in one <script>tag. In your application/helpers folder, create a new file called asset_helper.php and insert the following code:
/**
* Load JS
* Creates the <script> tag that links all requested js files
* @access public
* @param array
* @return string
*/
if (!function_exists('load_js'))
{
function load_js(array $files, $version = '')
{
//check if base url has a trailing slash, if not append it
$base_url = substr(base_url(), -1) == '/' ? base_url() : base_url() . '/';
foreach ($files as $index => $file)
{
//replace all backslash occurrences with tilde (~) to avoid URI confusion (gets converted back later in controller)
$files[$index] = str_replace('/', '~', $file);
}
return '<script type="text/javascript" src="' . $base_url . 'loader/js/' . implode('|', $files) . '/' . $version . '"></script>';
}
}
/**
* Load CSS
* Creates the <link> tag that links all requested css files
* @access public
* @param array
* @return string
*/
if (!function_exists('load_css'))
{
function load_css(array $files, $version = '')
{
//check if base url has a trailing slash, if not append it
$base_url = substr(base_url(), -1) == '/' ? base_url() : base_url() . '/';
foreach ($files as $index => $file)
{
//replace all backslash occurrences with tilde (~) to avoid URI confusion (gets converted back later in controller)
$files[$index] = str_replace('/', '~', $file);
}
return '<link type="text/css" rel="stylesheet" href="' . $base_url . 'loader/css/' . implode('|', $files) . '/' . $version . '" />';
}
}
/* End of file asset_helper.php */
/* Location: ./system/application/helpers/asset_helper.php */
These helpers will be used on pages that you need to load specific JS or CSS files. Usage would be:
echo load_js($files_to_load);
You might want to also add this helper to your autoload.php file as well, just like the path helper above. Just add it to the array.
Create a loader controller
In order to actually load our asset files, we’ll need a controller that handles the concatentation and minification of the scripts and files. Create a new file in your application/controllers directory called loader.php with the following code:
/**
* loader.php
*/
load_class('CSSMin', false);
load_class('JSMin', false);
class Loader extends Controller {
var $asset_output;
var $type;
var $files;
var $asset_path;
var $ext;
var $content_type;
var $modified_time;
function Loader()
{
parent::Controller();
$this->modified_time = 0;
}
function js($files)
{
$this->type = 'js';
$this->files = $files;
$this->asset_path = $this->config->item('js_path');
$this->ext = '.js';
$this->content_type = 'text/javascript';
$this->_output();
}
function css($files)
{
$this->type = 'css';
$this->files = $files;
$this->asset_path = $this->config->item('css_path');
$this->ext = '.css';
$this->content_type = 'text/css';
$this->_output();
}
function _output()
{
$files_array = explode("|", $this->files);
foreach ($files_array as $key => $file)
{
//replace chars for folder separation, replace ~ with /
$file = str_replace('~', '/', $file);
if (file_exists($this->asset_path . $file . $this->ext))
{
$this->asset_output .= file_get_contents($this->asset_path . $file . $this->ext)."\n";
$this->modified_time = max(filemtime($this->asset_path . $file . $this->ext), $this->modified_time);
}
}
//replace occurrences of /assets/ with asset_url()
//$this->asset_output = str_replace("/assets/", asset_url(), $this->asset_output);
switch ($this->type)
{
case 'js':
$this->asset_output = JSMin::minify($this->asset_output);
break;
case 'css':
$this->asset_output = CSSMin::minify($this->asset_output);
break;
default:
throw new LoaderException("Unknown file type.");
break;
}
//gzip
if (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) {
if (stristr($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false) {
$this->asset_output = gzencode($this->asset_output);
header('Content-encoding: gzip');
} else if (stristr($_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate') !== false) {
$this->asset_output = gzdeflate($this->asset_output);
header('Content-encoding: deflate');
}
}
//headers
header('Content-type: ' . $this->content_type);
header('Last-modified: ' . date('r', $this->modified_time));
header('Expires: ' . date('r', time() + 2592000));
header('Content-length: ' . strlen($this->asset_output));
echo $this->asset_output;
}
}
/* End of file loader.php */
/* Location: ./system/application/controllers/loader.php */
And that’s it!
Usage examples:
Javascript:
CSS:
To load files inside a directory, for example, assets/js/plugins/example.js, you would include 'plugins/example' inside your the array as shown above.
If you update your css/js, you can use the option $version parameter. Simply use an incrementing integer value of your choice and the browser will read the file as a new file rather than loading the cached file.
Happy coding!
13 Comments
Hi,
Thanks for this it’s awesome
.
I’ve followed it but there is one small problem, none of my images will load :S I think it’s because the paths to the background images are no longer relative to the css file thats being created.
If I put the full url in the background:url( etc in my css it works fine. But can’t figure out how to get it working otherwise, any help would be much appreciated.
Hi Luke,
If you see the commented out line in the code above:
$this->asset_output = str_replace("/assets/", asset_url(), $this->asset_output);
Uncomment that and the script will replace your relative URLs with full URLs using your asset_url() path. Otherwise, your image files should point to your asset directory from index.php, which is where all CI requested are routed through. If your asset directory is above index.php your CSS might look like: background:url(‘assets/images/whatever.jpg’).
Hi, thanks for the quick reply.
Yeah that sorted it thanks, it’ll teach me to read through tutorials better next time!
Glad that helped. Keep in mind that doing this does increase your overhead since it needs to search and replace on the entire string of text. That’s why I had it commented out. The better method is to use relative paths that will reach your assets directory.
sir i got this error ” Call to undefined function load_js()”
Hello Sir,
I have one website in codeigniter 1.73 version and in which i have put all your code and i have some done some custmization in code like i have remove the asset path, css_path and put directly like this in load_css and load_js function
return ”;
return ”;
and download the two library for jsmin and cssmin and put inside the application/library folder and create controller of loader class but it is not working.
my loader.php file put code like
class Loader extends Controller {
var $asset_output;
var $type;
var $files;
var $asset_path;
var $ext;
var $content_type;
var $modified_time;
function Loader()
{
parent::Controller();
$this->load->library(‘jsmin’);
$this->load->library(‘cssmin’);
$this->modified_time = 0;
}
function js($files)
{
$this->type = ‘js’;
$this->files = $files;
//$this->asset_path = $this->config->item(‘js_path’);
$this->ext = ‘.js’;
$this->content_type = ‘text/javascript’;
$this->_output();
}
function css($files)
{
$this->type = ‘css’;
$this->files = $files;
// $this->asset_path = $this->config->item(‘css_path’);
$this->ext = ‘.css’;
$this->content_type = ‘text/css’;
$this->_output();
}
function _output()
{
$files_array = explode(“|”, $this->files);
foreach ($files_array as $key => $file)
{
//replace chars for folder separation, replace ~ with /
$file = str_replace(‘~’, ‘/’, $file);
if (file_exists($file . $this->ext))
{
$this->asset_output .= file_get_contents($file . $this->ext).”\n”;
$this->modified_time = max(filemtime($file . $this->ext), $this->modified_time);
}
}
Please help me.
Thank You
Hi kashyap,
Sorry for the (really) late reply. It looks like you are missing some code that echoes the output. Check the above, you need to echo
$this->asset_outputat the end of your_outputmethod.Hello Sir,
Main confusion is when the loader controller function js and css will call?
sir my page source is making one http request but can not load css and js
Hello calvin i and kashyap we both have same problem please help me as soon as possible.
Question is how my loader controller js and css function will call? and how to replace my url from js~jquery.js|js~jqury.1.3.min.js same for css both are not loaded but load_js and load_css function which are in loader helper file are execute. i have also download jsmin and cssmin class file but no working at all. waiting for your response.
Hello Sir,
my js and css are not loaded because how to execute the loader controller because the helper will convert the all array in which all files name where make it jquery.js|~jquary.min.js but all this are not loaded because of loader class in controller is not executed. sir please help me i will wait for your kindly response i will reach upto 75% if you help me then i will get success.
THX
create script.
this is really nice and helpful plugin.
2 Trackbacks
[...] I made some code tonight based on this article at RobotSlacker. In the end, it ended up a whole lot different and it still needs some fine tuning. I was planning [...]
[...] I made some code tonight based on this article at RobotSlacker. In the end, it ended up a whole lot different and it still needs some fine tuning. I was planning [...]