# ioCombinerPlugin (for symfony 1.3/1.4) # The `ioCombinerPlugin` is a symfony plugin that allows you to: * Effortlessly combine and minify all css and js files into one file each, which are cached. * Dynamically add to or filter your css and js inside your project * Quickly use the plugin as-is or completely customize it For example: BEFORE <link rel="stylesheet" type="text/css" media="screen" href="/css/jquery.fancybox.css" /> <link rel="stylesheet" type="text/css" media="screen" href="/css/structure.css" /> <link rel="stylesheet" type="text/css" media="screen" href="/css/main.css" /> <script type="text/javascript" src="/js/jquery.min.js"></script> <script type="text/javascript" src="/js/jquery.fancybox.js"></script> AFTER <link rel="stylesheet" type="text/css" media="screen" href="/css/combine.css" /> <script type="text/javascript" src="/js/combine.js"></script> To learn more about combining and minifying assets, read Yahoo's [Best Practices for Speeding Up Your Web Site](http://developer.yahoo.com/performance/rules.html). ## Installation ## * Install the plugin (via a package) symfony plugin:install ioCombinerPlugin --stability=beta * Install the plugin (via a Subversion checkout) svn co http//svn.symfony-project.com/plugins/ioCombinerPlugin/trunk plugins/ioCombinerPlugin * Activate the plugin in the `config/ProjectConfiguration.class.php` class ProjectConfiguration extends sfProjectConfiguration { public function setup() { $this->enablePlugins(array( 'ioCombinerPlugin', '...' )); } } * Enable the `ioCombiner` module in settings.yml for your application all: settings: enabled_modules: [default, ioCombiner] * Add the following to your `web/.htaccess` file. Don't include the first and last lines below - they're just there so you can see where the combiner code needs to be added. An example `.htaccess` file is available in the `data` directory of the plugin. ... #RewriteRule .* - [L] # start combiner code RewriteRule ^css\/combine[_*](.*)\.css$ $1.php/css/combine.css [L] RewriteRule ^js\/combine[_*](.*)\.js$ $1.php/js/combine.js [L] # end combiner code # we check if the .html version is here (caching) ... ## Configuration ## An example of all the configuration options is in the config/app.yml file in the plugin. all: io_combiner_plugin: enabled: true # enable or disable the combiner stylesheets: [] # an array of the stylesheets to combine javascripts: [] # an array of the javascript to combine # the class that adds the combine.css/js file to the response and removes # all of the css/js file that are essentially replaced by the combine file response_class: ioCombinerResponse # the class that actually combines a group of css/js files and returns # the minified, combined string combiner_class: ioCombiner # the HTTP header Expires length, in days, to set on the combined # css/js file. Set to false to not automatically set this header expires_days: 30 ## How to Use ## * Start by configuring your `view.yml` with all of the stylesheets and javascripts that you will need: # apps/%APPNAME%/config/view.yml all: stylesheets: [grid_960.css, main.css, print.css: { media: print }] javascripts: [jquery.js, main.js] * Choose which stylesheets and javascripts you'd like to combine and add them to the `app.yml` file for your application. Be sure to use the same filename in both files (don't say "jquery" in `view.yml` and "jquery.js" in `app.yml`): # apps/%APPNAME%/config/app.yml all: io_combiner_plugin: stylesheets: [grid_960.css, main.css] javascripts: [jquery.js, main.js] That's it! The plugin will automatically remove `grid_960.css` and `main.css` and replace them with `combine.css`, which will contain the minified contents of both of those css files. Similarly, `jquery.js` and `main.js` will be removed and replaced with `combine.js`, which will contain the minified contents of both of those js files. BEFORE: <link rel="stylesheet" type="text/css" media="screen" href="/css/grid_960.css" /> <link rel="stylesheet" type="text/css" media="screen" href="/css/main.css" /> <link rel="stylesheet" type="text/css" media="print" href="/css/print.css" /> <script type="text/javascript" src="/js/jquery.min.js"></script> <script type="text/javascript" src="/js/main.js"></script> AFTER: <link rel="stylesheet" type="text/css" media="screen" href="/css/combine.css" /> <link rel="stylesheet" type="text/css" media="print" href="/css/print.css" /> <script type="text/javascript" src="/js/combine.js"></script> Notice that we chose not to include the `print.css` file in the combiner. As such, it's not included in the combiner and is output normally. Since the `combine.css` file will always have a `media="screen"`, print css cannot be included in it. ## Dynamically Modifying your stylesheets and javascripts ## An additional feature of this plugin is the ability to dynamically add or modify the contents that are output in the `combine.css` or `combine.js` files. This means that you can easily add dynamic css or js to your application! This plugin exposes two filter events that can be used to make changes to the `combine.css` or `combine.js` files just before they are output. * iocombiner.filter_stylesheets * iocombiner.filter_javascripts For information on how to use symfony events, please refer to [chapter 17 of the Definitive Guide to symfony](http://www.symfony-project.org/book/1_2/17-Extending-Symfony#chapter_17_events). Here's a quick example of how it might be used: // config/ProjectConfiguration.class.php class ProjectConfiguration extends sfProjectConfiguration { public function setup() { // ... $this->getEventDispatcher()->connect('iocombiner.filter_stylesheets', array($this, 'filterStylesheets')); } public function filterStylesheets(sfEvent $event, $result) { // add some css to the beginning of the combiner $result = 'body { background: #fff; }' . $result; // add some css to the end of the combiner $result .= 'a.footer { color: #333; }'; // map all "url(/some/path.jpg)" to a different domain (e.g. a media server) $pattern = "#url\('*\/(.+?)'*\)#s"; $replacement = "url('http://media.yourdomain.com/$1')"; $result = preg_replace($pattern, $replacement, $result); return $result; } } ## Things to watch out for ## ### Clear your cache in production As the `combine.css` and `combine.js` files are cached, you'll need to clear your symfony cache after making changes to combined css or js files in your production environment. php symfony cc ### One combiner file per site A combiner file is meant to house the css/js that you need to include on every single page. This means that the combiner files themselves don't change from page to page - it's always the same block of css/js. On many pages, you may need to include additional stylesheets or javascripts. This should be done, as normally, with the `use_stylesheet()` and `use_javascript()` helper functions. Anything specified in this way won't be included in the combiner, but will be output like normal - as another stylesheet/javascript that is included next to the combiner. ### The ordering of files is important When using the combiner, all of your css and js files are combined into one big file. The order in which files are combined follows the order in which you specify the files in `app.yml`. However, when using the `use_stylesheet()` or `use_javascript()` methods, the ordering with respect to your combined files may be slightly different as the included file will be output either completely before or completely after your combined files. As you'll usually want these files to be included after some core, combined files, you may need to add special instructions to include these files last. <?php use_stylesheet('specific.css', 'last')[ ?> <?php use_javascript('specific.js', 'last')[ ?> ### List stylesheets & javascripts in two places This plugin is meant perform as little "magic" as possible so that the developer is always aware and can explicitly control which files are and are not combined. As such, this plugin is setup so that stylesheets and javascripts that should be combined are listed both in `view.yml` for normal functionality and in `app.yml` for the combiner. Keep this in mind while developing: * If you add a file to `view.yml` but not `app.yml`, that file will be included, but won't be combined. This probably won't cause problems, but will change the order in which the files are included (since the uncombined file will be included outside of the combiner file). * If you add a file to `app.yml` but not `view.yml`, that file will be included in the combiner but not when the combiner is turned off. So, if the combiner is enabled for your `prod` environment but disabled for your `dev` environment, `prod` will include the file while `dev` will not. ## Extending the Plugin ## The majority of the work in this plugin is done by two different classes: `ioCombiner` and `ioCombinerResponse`. Either of these classes can be replaced by a class of your own via the `combiner_class` and `response_class` keys in the `app.yml` configuration seen above. For example: all: io_combiner_plugin: response_class: myCombinerResponse class myCombinerResponse extends ioCombinerResponse { // do something here } ## Future ## A web debug panel to monitor which files are being combined, the number of requests being saved, and the file size difference with the combiner will be added.