Getting Started with Assetic

There was a time when asset management meant little more than inserting a <link> tag or two and a couple of <script> tags into your HTML.

Nowadays, though, that approach just won’t cut it. There’s performance, for one thing. Assets are a significant performance drain both in terms of file size and the number of HTTP requests – optimizing both has become an essential part of the development process. This has been exacerbated by the proliferation of mobile devices.

Also, as client-side applications have become more and more sophisticated, managing dependencies amongst scripts and libraries has become increasingly complex.

Furthermore, technologies such as Less, Compass and Coffeescript require assets to be compiled, adding yet another step to the process of managing assets.

In this article I’m going to look at a PHP package called Assetic which helps manage, compile and optimize assets such as scripts, stylesheets and images.

What is asset management?

Let’s start by looking at what asset management actually is.

At its simplest, it’s a way of managing what resources are required by a website or a web application – or a specific page – and their dependencies. This might involve ensuring a particular library is available to your JavaScript, or making sure a CSS reset is applied before your other styles.

It sometimes helps to look at asset management in terms of several distinct stages. The first is compilation, a necessary step if you’re using a CSS framework such as Less, SASS, SCSS, Stylus, Compass etc, or something like Coffeescript or Dart. There are also a few additional compilation steps involved in some JavaScript-based tools; for example compiling Handlebars, Twig or Mustache templates.

The next stage is optimization. For CSS, typically you’ll minify your stylesheet files when you go into production mode. For JavaScript, you’d probably minify or pack your source files. Images can often be reduced quite significantly in size using various compression algorithms, and in some cases you can also reduce the number of HTTP requests by BASE-64 encoding them and embedding the data in your stylesheets.

Finally, concatenation. Reducing the number of HTTP requests can have significant performance gains, so CSS and JavaScript files are often concatenated together into a single, large file for each type. The technique of building “sprites” applies the same principle to images, though that’s outside the scope of this article.

Introducing Assetic

Assetic is a PHP package designed to help with the asset management process.

There are a couple of important concepts to understand when getting started with Assetic – collections and filters.

A collection groups together a bunch of assets. A single file is an asset. ‘Nix users in particular will be familar with glob, which is essentially a way of collecting together a bunch of files whose filenames match a particular pattern; Assetic treats these as an asset as well. For example, the glob patterm /assets/js/*.js refers to all files in /assets/js with the .js extension.

A filter is applied to a collection to modify it in some way. If you refer back to the idea of stages in the asset management process, you can think of each stage as being implemented by one or more filters. So for example, one filter might be responsible for compilation, and one for minification.

Different filters are usually applied to different collections. For example, you might have a project with the following:

  1. A collection of .less files which are compiled with the Less filter, minified using a CSS minification filter and concatenated into one CSS file.
  2. A collection of .css files which are simply minified
  3. A collection of Coffeescript files are compiled using the Coffeescript filter, minified using the Google Closure Compiler filter, then concatenated into one .js file.

The exact combinations of file types and filters will depend on what technologies you’re using and your personal preference, such as your preferred minifier.

Assetic provides support for numerous modules, giving you the flexibility to choose whichever approach you prefer. For example, it’s up to you whether you minify your JavaScript using JSMin, the YUI Compressor or the Google Closure Compiler.

Installing Assetic

You can install Assetic by downloading or cloning from Github or better still, via Composer:

"kriswallsmith/assetic": "1.2.*@dev"

There are also Assetic packages available on Packagist for frameworks such as Zend Framework, Symfony, Laravel and Silex.

Usage

Let’s start with a simple example – creating an asset collection to concatenate a bunch of JavaScript files:

$js = new AssetCollection(array(
    new GlobAsset('/assets/js/libs/*'),
    new FileAsset('/assets/js/app.js'),
));

print $js->dump();

The code above takes all .js files from /assets/js/libs and then the file /assets/js/app.js, and concatenates them when dump() is called – we’ll look at what you do with the output shortly.

You can extend the above example by adding a filter – in this case, it uses JSMin to minify the resulting JavaScript:

$scripts = new AssetCollection(array(
    new GlobAsset('/assets/js/libs/*'),
    new FileAsset('/assets/js/app.js'),
), array(
    new JSMinFilter(),
));

print $scripts->dump();

Note: If you wish to use JSMin, you can use the following PHP implementation:
"lmammino/jsmin4assetic": "1.0.*@dev" (view it on Github here).

Your stylesheets will need a separate asset collection, given that the filters behave differently. Suppose you have a bunch of CSS files, and you’re using Less to define your grid:

$styles = new AssetCollection(array(
    new FileAsset('/assets/less/grid.less', array(new LessFilter())),
    new GlobAsset('/assets/css/*'),
), array(
    new Yui\CssCompressorFilter('/path/to/yuicompressor.jar'),
));

print $styles->dump();

This uses the LessFilter to compile grid.less, concatenates it along with all the .css files in /assets/css, then compresses the whole thing using the YUI compressor.

Note that in order to get the example above working, you’ll need to ensure you have the YUI Compressor installed correctly, ensure that the path to its .jar files is correct, and add the following to your composer.json: "leafo/lessphp": "0.4.*@dev"

If you’d rather use CssMin than the YUI Compressor, you’ll need to install it, and your code becomes:

$styles = new AssetCollection(array(
    new FileAsset('/assets/less/grid.less', array(new LessFilter())),
    new GlobAsset('/assets/css/*'),
), array(
    new CssMinFilter(),
));

print $styles->dump();

If you’re using SASS instead of Less, install SCSSphp by adding the following to your composer.json:

"leafo/scssphp": "dev-master"

You can then use ScssphpFilter to compile your SASS assets.

You can optimize images, too. In this example, we use glob to grab all images files from assets/img and compress them with OptiPng, then do the same with Jpegs but use Jpegoptim:

$images = new AssetCollection(array(
    new GlobAsset('/assets/img/*.png', array(new OptiPngFilter())),
    new GlobAsset('/assets/img/*.jpg', array(new JpegoptimFilter()),
));

Manager Classes and the AssetFactory

There are other ways to configure your assets. The AssetManager class is essentially a keyed store of assets – for example, you can collect the asset collections we defined in the previous section together like this:

$am = new AssetManager();
$am->set('jquery', new FileAsset('/path/to/jquery.js'));
$am->set('scripts', $scripts);
$am->set('styles', $styles);

The FilterManager class works in much the same way:

$fm = new FilterManager();
$fm->set('sass', new SassFilter('/path/to/parser/sass'));
$fm->set('yui_css', new Yui\CssCompressorFilter('/path/to/yuicompressor.jar'));

Then you can use the AssetFactory class to simplify the whole process:

use Assetic\Factory\AssetFactory;

$factory = new AssetFactory('/path/to/asset/directory/');
$factory->setAssetManager($am);
$factory->setFilterManager($fm);
$factory->setDebug(true);

$styles = $factory->createAsset(array(
    '@reset',         // load the asset manager's "reset" asset
    'css/src/*.scss', // load every scss files from "/path/to/asset/directory/css/src/"
), array(
    'scss',           // filter through the filter manager's "scss" filter
    '?yui_css',       // don't use this filter in debug mode
));

echo $styles->dump();

Output

I’ve shown how to select what you want to include and how to apply filters; but I haven’t yet looked at where you do this or how you reference it. There are a number of approaches you can take.

One possibility is to create a PHP file in a publicly accessible directory, e.g. JavaScripts.php and reference it from your HTML as if it were an asset:

<script src="/assets/JavaScripts.php"></script>

Or, you could create a file called stylesheets.php and embed it inline:

<style>
<?php include('/assets/stylesheets.php');
</style>

Alternatively you could generate .css and .js files and just reference those as normal. You can use the AssetWriter for this:

use Assetic\AssetWriter;

$scripts.js = new AssetCollection(array(
    new GlobAsset('/assets/js/libs/*'),
    new FileAsset('/assets/js/app.js'),
), array(
    new JSMinFilter(),
));

// Set target path, relative to the path passed to the
// AssetWriter constructor as an argument shortly
$scripts->setTargetPath('scripts.js');
$am->set('scripts.js', $scripts.js);

// see above for instantiation of $styles
$styles->setTargetPath('stylesheets.css');
$am->set('styles', $styles);

$writer = new AssetWriter('/assets/build');
$writer->writeManagerAssets($am);

You could create a command-line script to do this as part of your workflow, or use a tool such as Guard to “watch” the filesystem and re-run it whenever one of the relevant files has changed.

Caching

Assetic ships with a simple file-based caching mechanism to ensure that filters aren’t run unnecessarily. Here’s an example of caching the output of the YUI Compressor:

use Assetic\Asset\AssetCache;
use Assetic\Asset\FileAsset;
use Assetic\Cache\FilesystemCache;
use Assetic\Filter\Yui;

$yui = new Yui\JsCompressorFilter('/path/to/yuicompressor.jar');
$js = new AssetCache(
    new FileAsset('/path/to/some.js', array($yui)),
    new FilesystemCache('/path/to/cache')
);

// the YUI compressor will only run on the first call
$js->dump();
$js->dump();
$js->dump();

Summary

In this article I’ve introduced Assetic – a PHP package for managing assets. I’ve shown how you can use it to manage dependencies, run compilation processes, minify / pack / compress / optimise assets, and concatenate files to minimise the number of HTTP requests. Be sure to check out the documentation for details of all the available filters; or, you could even look at implementing FilterInterface / extending BaseFilter with a view to defining your own. For packages which complement it, refer to the suggested packages either when you first install it, or by inspecting the suggests section of its composer.json file.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://loige.com/ Luciano Mammino

    Great article @Lukas.

    I really love Assetic and I admit I am a big fan of Kris Wallsmith. I have used assetic within several Symfony2 in the latest 2 years. Anyway I’m starting to consider to leave Php for this kind of tasks and have a nodejs/grunt process to do the hard work. It seems to be a lot faster (and anyway it’s decoupled from the Php codebase).

    What do you think about it?

  • Taylor Ren

    Nice one @lwhite

    I have been using Symfony 2 for quite some time but have not touched on that Assetic thing yet. Useful in my future works.

  • mihaeu

    @loige:disqus I’m with you, but not so much for reasons of speed. I think it’s more a matter of separating concerns. There are so many great pre-processors nowadays (mostly node though) which don’t force me to hardcode these things (another question for this code is always: where does it go? it’s not templating, it’s not controller business either), but instead use simple markup in the templates and put the rest into meta documents.

  • Wesam Alalem

    Great article, first when I read the title I thought it was about Symfony :) I never knew there was standalone library with such amazing features.
    Thank you for sharing.

  • Roy

    What’s the argument for using server side asset management when developers are trending more and more towards client side templating and general front end development?

  • http://webmastah.pl/ nrm

    “You can then use ScssphpFilter to compile your SASS assets.” No, you can’t.

    “scssphp is a compiler for SCSS written in PHP. It implements SCSS 3.2.12. It does not implement the SASS syntax, only the SCSS syntax.”