1# Assetic [![Build Status](https://travis-ci.org/kriswallsmith/assetic.png?branch=master)](https://travis-ci.org/kriswallsmith/assetic) ![project status](http://stillmaintained.com/kriswallsmith/assetic.png) #
2
3Assetic is an asset management framework for PHP.
4
5``` php
6<?php
7
8use Assetic\Asset\AssetCollection;
9use Assetic\Asset\FileAsset;
10use Assetic\Asset\GlobAsset;
11
12$js = new AssetCollection(array(
13    new GlobAsset('/path/to/js/*'),
14    new FileAsset('/path/to/another.js'),
15));
16
17// the code is merged when the asset is dumped
18echo $js->dump();
19```
20
21Assets
22------
23
24An Assetic asset is something with filterable content that can be loaded and
25dumped. An asset also includes metadata, some of which can be manipulated and
26some of which is immutable.
27
28| **Property** | **Accessor**    | **Mutator**   |
29|--------------|-----------------|---------------|
30| content      | getContent      | setContent    |
31| mtime        | getLastModified | n/a           |
32| source root  | getSourceRoot   | n/a           |
33| source path  | getSourcePath   | n/a           |
34| target path  | getTargetPath   | setTargetPath |
35
36The "target path" property denotes where an asset (or an collection of assets) should be dumped.
37
38Filters
39-------
40
41Filters can be applied to manipulate assets.
42
43``` php
44<?php
45
46use Assetic\Asset\AssetCollection;
47use Assetic\Asset\FileAsset;
48use Assetic\Asset\GlobAsset;
49use Assetic\Filter\LessFilter;
50use Assetic\Filter\Yui;
51
52$css = new AssetCollection(array(
53    new FileAsset('/path/to/src/styles.less', array(new LessFilter())),
54    new GlobAsset('/path/to/css/*'),
55), array(
56    new Yui\CssCompressorFilter('/path/to/yuicompressor.jar'),
57));
58
59// this will echo CSS compiled by LESS and compressed by YUI
60echo $css->dump();
61```
62
63The filters applied to the collection will cascade to each asset leaf if you
64iterate over it.
65
66``` php
67<?php
68
69foreach ($css as $leaf) {
70    // each leaf is compressed by YUI
71    echo $leaf->dump();
72}
73```
74
75The core provides the following filters in the `Assetic\Filter` namespace:
76
77 * `AutoprefixerFilter`: Parse and update vendor-specific properties using autoprefixer
78 * `CoffeeScriptFilter`: compiles CoffeeScript into Javascript
79 * `CompassFilter`: Compass CSS authoring framework
80 * `CssEmbedFilter`: embeds image data in your stylesheets
81 * `CssImportFilter`: inlines imported stylesheets
82 * `CssMinFilter`: minifies CSS
83 * `CleanCssFilter`: minifies CSS
84 * `CssRewriteFilter`: fixes relative URLs in CSS assets when moving to a new URL
85 * `DartFilter`: compiles Javascript using dart2js
86 * `EmberPrecompileFilter`: precompiles Handlebars templates into Javascript for use in the Ember.js framework
87 * `GoogleClosure\CompilerApiFilter`: compiles Javascript using the Google Closure Compiler API
88 * `GoogleClosure\CompilerJarFilter`: compiles Javascript using the Google Closure Compiler JAR
89 * `GssFilter`: compliles CSS using the Google Closure Stylesheets Compiler
90 * `HandlebarsFilter`: compiles Handlebars templates into Javascript
91 * `JpegoptimFilter`: optimize your JPEGs
92 * `JpegtranFilter`: optimize your JPEGs
93 * `JSMinFilter`: minifies Javascript
94 * `JSMinPlusFilter`: minifies Javascript
95 * `JSqueezeFilter`: compresses Javascript
96 * `LessFilter`: parses LESS into CSS (using less.js with node.js)
97 * `LessphpFilter`: parses LESS into CSS (using lessphp)
98 * `OptiPngFilter`: optimize your PNGs
99 * `PackagerFilter`: parses Javascript for packager tags
100 * `PackerFilter`: compresses Javascript using Dean Edwards's Packer
101 * `PhpCssEmbedFilter`: embeds image data in your stylesheet
102 * `PngoutFilter`: optimize your PNGs
103 * `ReactJsxFilter`: compiles React JSX into JavaScript
104 * `Sass\SassFilter`: parses SASS into CSS
105 * `Sass\ScssFilter`: parses SCSS into CSS
106 * `SassphpFilter`: parses Sass into CSS using the sassphp bindings for Libsass
107 * `ScssphpFilter`: parses SCSS using scssphp
108 * `SeparatorFilter`: inserts a separator between assets to prevent merge failures
109 * `SprocketsFilter`: Sprockets Javascript dependency management
110 * `StylusFilter`: parses STYL into CSS
111 * `TypeScriptFilter`: parses TypeScript into Javascript
112 * `UglifyCssFilter`: minifies CSS
113 * `UglifyJs2Filter`: minifies Javascript
114 * `UglifyJsFilter`: minifies Javascript
115 * `Yui\CssCompressorFilter`: compresses CSS using the YUI compressor
116 * `Yui\JsCompressorFilter`: compresses Javascript using the YUI compressor
117
118Asset Manager
119-------------
120
121An asset manager is provided for organizing assets.
122
123``` php
124<?php
125
126use Assetic\AssetManager;
127use Assetic\Asset\FileAsset;
128use Assetic\Asset\GlobAsset;
129
130$am = new AssetManager();
131$am->set('jquery', new FileAsset('/path/to/jquery.js'));
132$am->set('base_css', new GlobAsset('/path/to/css/*'));
133```
134
135The asset manager can also be used to reference assets to avoid duplication.
136
137``` php
138<?php
139
140use Assetic\Asset\AssetCollection;
141use Assetic\Asset\AssetReference;
142use Assetic\Asset\FileAsset;
143
144$am->set('my_plugin', new AssetCollection(array(
145    new AssetReference($am, 'jquery'),
146    new FileAsset('/path/to/jquery.plugin.js'),
147)));
148```
149
150Filter Manager
151--------------
152
153A filter manager is also provided for organizing filters.
154
155``` php
156<?php
157
158use Assetic\FilterManager;
159use Assetic\Filter\Sass\SassFilter;
160use Assetic\Filter\Yui;
161
162$fm = new FilterManager();
163$fm->set('sass', new SassFilter('/path/to/parser/sass'));
164$fm->set('yui_css', new Yui\CssCompressorFilter('/path/to/yuicompressor.jar'));
165```
166
167Asset Factory
168-------------
169
170If you'd rather not create all these objects by hand, you can use the asset
171factory, which will do most of the work for you.
172
173``` php
174<?php
175
176use Assetic\Factory\AssetFactory;
177
178$factory = new AssetFactory('/path/to/asset/directory/');
179$factory->setAssetManager($am);
180$factory->setFilterManager($fm);
181$factory->setDebug(true);
182
183$css = $factory->createAsset(array(
184    '@reset',         // load the asset manager's "reset" asset
185    'css/src/*.scss', // load every scss files from "/path/to/asset/directory/css/src/"
186), array(
187    'scss',           // filter through the filter manager's "scss" filter
188    '?yui_css',       // don't use this filter in debug mode
189));
190
191echo $css->dump();
192```
193
194The `AssetFactory` is constructed with a root directory which is used as the base directory for relative asset paths.
195
196Prefixing a filter name with a question mark, as `yui_css` is here, will cause
197that filter to be omitted when the factory is in debug mode.
198
199You can also register [Workers](src/Assetic/Factory/Worker/WorkerInterface.php) on the factory and all assets created
200by it will be passed to the worker's `process()` method before being returned. See _Cache Busting_ below for an example.
201
202Dumping Assets to static files
203------------------------------
204
205You can dump all the assets an AssetManager holds to files in a directory. This will probably be below your webserver's document root
206so the files can be served statically.
207
208``` php
209<?php
210
211use Assetic\AssetWriter;
212
213$writer = new AssetWriter('/path/to/web');
214$writer->writeManagerAssets($am);
215```
216
217This will make use of the assets' target path.
218
219Cache Busting
220-------------
221
222If you serve your assets from static files as just described, you can use the CacheBustingWorker to rewrite the target
223paths for assets. It will insert an identifier before the filename extension that is unique for a particular version
224of the asset.
225
226This identifier is based on the modification time of the asset and will also take depended-on assets into
227consideration if the applied filters support it.
228
229``` php
230<?php
231
232use Assetic\Factory\AssetFactory;
233use Assetic\Factory\Worker\CacheBustingWorker;
234
235$factory = new AssetFactory('/path/to/asset/directory/');
236$factory->setAssetManager($am);
237$factory->setFilterManager($fm);
238$factory->setDebug(true);
239$factory->addWorker(new CacheBustingWorker());
240
241$css = $factory->createAsset(array(
242    '@reset',         // load the asset manager's "reset" asset
243    'css/src/*.scss', // load every scss files from "/path/to/asset/directory/css/src/"
244), array(
245    'scss',           // filter through the filter manager's "scss" filter
246    '?yui_css',       // don't use this filter in debug mode
247));
248
249echo $css->dump();
250```
251
252Internal caching
253-------
254
255A simple caching mechanism is provided to avoid unnecessary work.
256
257``` php
258<?php
259
260use Assetic\Asset\AssetCache;
261use Assetic\Asset\FileAsset;
262use Assetic\Cache\FilesystemCache;
263use Assetic\Filter\Yui;
264
265$yui = new Yui\JsCompressorFilter('/path/to/yuicompressor.jar');
266$js = new AssetCache(
267    new FileAsset('/path/to/some.js', array($yui)),
268    new FilesystemCache('/path/to/cache')
269);
270
271// the YUI compressor will only run on the first call
272$js->dump();
273$js->dump();
274$js->dump();
275```
276
277Twig
278----
279
280To use the Assetic [Twig][3] extension you must register it to your Twig
281environment:
282
283``` php
284<?php
285
286$twig->addExtension(new AsseticExtension($factory));
287```
288
289Once in place, the extension exposes a stylesheets and a javascripts tag with a syntax similar
290to what the asset factory uses:
291
292``` html+jinja
293{% stylesheets '/path/to/sass/main.sass' filter='sass,?yui_css' output='css/all.css' %}
294    <link href="{{ asset_url }}" type="text/css" rel="stylesheet" />
295{% endstylesheets %}
296```
297
298This example will render one `link` element on the page that includes a URL
299where the filtered asset can be found.
300
301When the extension is in debug mode, this same tag will render multiple `link`
302elements, one for each asset referenced by the `css/src/*.sass` glob. The
303specified filters will still be applied, unless they are marked as optional
304using the `?` prefix.
305
306This behavior can also be triggered by setting a `debug` attribute on the tag:
307
308``` html+jinja
309{% stylesheets 'css/*' debug=true %} ... {% stylesheets %}
310```
311
312These assets need to be written to the web directory so these URLs don't
313return 404 errors.
314
315``` php
316<?php
317
318use Assetic\AssetWriter;
319use Assetic\Extension\Twig\TwigFormulaLoader;
320use Assetic\Extension\Twig\TwigResource;
321use Assetic\Factory\LazyAssetManager;
322
323$am = new LazyAssetManager($factory);
324
325// enable loading assets from twig templates
326$am->setLoader('twig', new TwigFormulaLoader($twig));
327
328// loop through all your templates
329foreach ($templates as $template) {
330    $resource = new TwigResource($twigLoader, $template);
331    $am->addResource($resource, 'twig');
332}
333
334$writer = new AssetWriter('/path/to/web');
335$writer->writeManagerAssets($am);
336```
337
338---
339
340Assetic is based on the Python [webassets][1] library (available on
341[GitHub][2]).
342
343[1]: http://elsdoerfer.name/docs/webassets
344[2]: https://github.com/miracle2k/webassets
345[3]: http://twig.sensiolabs.org
346