Introducing Assetic: Asset Management For PHP 5.3
Introducing Assetic: Asset Management For PHP 5.3
Kris Wallsmith
February 9, 2011
@kriswallsmith
Symfony core team member Doctrine contributor Symfony Guru at 10+ years experience with PHP and web development Open source evangelist and international speaker
OpenSky connects you with innovators, trendsetters and tastemakers.You choose the ones you like and each week they invite you to their private online sales.
ShopOpenSky.com
PHP 5.3 + Symfony2 MongoDB + Doctrine MongoDB ODM MySQL + Doctrine2 ORM Less CSS jQuery
Agenda
Strawman The code Twig Integration Symfony2 Integration
Symfony2 is FAST
and SEO!
http://googlewebmastercentral.blogspot.com/2010/04/using-site-speed-in-web-search-ranking.html
Asset Management
# /path/to/web/js/core.php $core = new AssetCollection(array( new FileAsset('/path/to/jquery.js'), new GlobAsset('/path/to/js/core/*.js'), )); $core->load(); many les into one: fewer HTTP requests Merge header('Content-Type: text/javascript'); echo $core->dump();
# /path/to/web/js/core.php $core = new AssetCollection(array( new FileAsset('/path/to/jquery.js'), new GlobAsset('/path/to/js/core/*.js'), ), array( new YuiCompressorJsFilter('/path/to/yui.jar'), )); $core->load();merged asset: less data over the wire Compress the header('Content-Type: text/javascript'); echo $core->dump();
<script src="js/core.php"></script>
https://github.com/miracle2k/webassets
A collection is an asset
Load
Dump
Asset Collection Asset Collection Filter Filter Filter Filter Asset Asset
# /path/to/web/css/styles.php $styles = new AssetCollection( array(new FileAsset('/path/to/main.sass')), array(new SassFilter()) ); header('Content-Type: text/css'); echo $styles->dump();
# /path/to/web/css/styles.php $styles = new AssetCollection(array( new AssetCollection( array(new FileAsset('/path/to/main.sass')), array(new SassFilter()) ), new FileAsset('/path/to/more.css'), )); header('Content-Type: text/css'); echo $styles->dump();
# /path/to/web/css/styles.php $styles = new AssetCollection(array( new AssetCollection( array(new FileAsset('/path/to/main.sass')), array(new SassFilter()) ), new FileAsset('/path/to/more.css'), ), array( new YuiCompressorCss('/path/to/yui.jar'), )); Lazy! The lesystem isn't touched until now header('Content-Type: text/css'); echo $styles->dump();
Asset Manager
jQuery will only be included once $core = new AssetCollection(array( $jquery, $plugin1, $plugin2, )); header('text/javascript'); echo $core->dump();
Filter Manager
jQuery will only be compressed once $jquery = new FileAsset('/path/to/core.js'); $jquery->ensureFilter($fm->get('yui_js')); $core = new AssetCollection(array( $jquery, new GlobAsset('/path/to/js/core/*.js'), )); $core->ensureFilter($fm->get('yui_js'));
Asset Factory
# /path/to/asset_factory.php $fm = new FilterManager(); $fm->set('coffee', new CoffeeScriptFilter()); $fm->set('closure', new GoogleClosure\CompilerApi()); $factory = new AssetFactory('/path/to/web'); $factory->setAssetManager($am); $factory->setFilterManager($fm);
include '/path/to/asset_factory.php'; $asset = $factory->createAsset( array('js/src/*.coffee'), array('coffee', 'closure') ); header('Content-Type: text/javascript'); echo $asset->dump();
Debug Mode
// new AssetFactory('/path/to/web', true); include '/path/to/asset_factory.php'; $asset = $factory->createAsset( array('js/src/*.coffee'), array('coffee', 'closure') ); header('Content-Type: text/javascript'); echo $asset->dump();
// new AssetFactory('/path/to/web', true); include '/path/to/asset_factory.php'; $asset = $factory->createAsset( array('js/src/*.coffee'), array('coffee', '?closure') ); header('Content-Type: text/javascript'); echo $asset->dump();
// new AssetFactory('/path/to/web', false); include '/path/to/asset_factory.php'; $asset = $factory->createAsset( array('js/src/*.coffee'), array('coffee', '?closure'), array('debug' => true) ); header('Content-Type: text/javascript'); echo $asset->dump();
Factory Workers
$worker = new EnsureFilterWorker( '/\.css$/', // the output pattern $fm->get('yui_css'), // the filter false // the debug mode ); $factory = new AssetFactory('/path/to/web'); $factory->addWorker($worker); // compressed $factory->createAsset('css/sass/*', 'sass', array( 'output' => 'css', ));
$worker = new EnsureFilterWorker( '/\.css$/', // the output pattern $fm->get('yui_css'), // the filter false // the debug mode ); $factory = new AssetFactory('/path/to/web'); $factory->addWorker($worker); // uncompressed $factory->createAsset('css/sass/*', 'sass', array( 'output' => 'css', 'debug' => true, ));
# /path/to/web/css/styles.php $styles = new AssetCollection( array(new FileAsset('/path/to/main.sass')), array(new SassFilter()) ); echo $styles->dump();
# /path/to/web/css/styles.php $styles = new AssetCache(new AssetCollection( array(new FileAsset('/path/to/main.sass')), array(new SassFilter()) ), new FilesystemCache('/path/to/cache')); Run the lters once and cache the content echo $styles->dump();
# /path/to/web/js/core.php $mtime = gmdate($core->getLastModified()); if ($mtime == $_SERVER['HTTP_IF_MODIFIED_SINCE']) { header('HTTP/1.0 304 Not Modified'); exit(); } header('Content-Type: text/javascript'); header('Last-Modified: '.$mtime); echo $core->dump();
# /path/to/scripts/dump_assets.php $am = new AssetManager(); $am->set('foo', $foo); // etc... $writer = new AssetWriter('/path/to/web'); $writer->writeManagerAssets($am);
# /path/to/scripts/dump_assets.php $am = new AssetManager(); $am->set('foo', $foo); // etc... $writer = new AssetWriter('/path/to/web'); foreach (array_slice($argv, 1) as $name) { $writer->writeAsset($am->get($name)); }
new AssetWriter('s3://my-bucket')
A CloudFront S3 bucket
Twig Integration
$twig->addExtension(new AsseticExtension($factory));
<script src="assets/92429d8"></script>
<script src="js/92429d8.js"></script>
{% assetic 'js/*.coffee', filter='coffee,?closure', output='js', name='core_js' %} <script src="{{ asset_url }}"></script> {% endassetic %}
Formula Loader
Uses the Twig parser to extract asset formulae from templates
$loader = new FormulaLoader($twig); // loop through your templates $formulae = array(); foreach ($templates as $template) { $formulae += $loader->load($template); } $am = new LazyAssetManager($factory); $am->addFormulae($formulae);
if (!file_exists($cache = '/path/to/formulae.php')) { $loader = new FormulaLoader($twig); // loop through your templates $formulae = array(); foreach ($templates as $template) { $formulae += $loader->load($template); } file_put_contents($cache, '<?php return '.var_export($formulae, true));
AsseticBundle
{% assetic filter='scss,?yui_css', output='css/all.css', '@MainBundle/Resources/sass/main.scss', '@AnotherBundle/Resources/sass/more.scss' %} <link href="{{ asset_url }}" rel="stylesheet" /> {% endassetic %}
{% assetic filter='scss,?yui_css', output='css/all.css', '@MainBundle/Resources/sass/main.scss', '@AnotherBundle/Resources/sass/more.scss' %} <link href="{{ asset_url }}" rel="stylesheet" /> {% endassetic %}
{% assetic filter='scss,?yui_css', output='css/all.css', '@MainBundle/Resources/sass/main.scss', debug=true, '@AnotherBundle/Resources/sass/more.scss' %} <link href="{{ asset_url }}" rel="stylesheet" /> {% endassetic %}
Conguration
{% assetic filter='scss,?yui_css', output='css/all.css', '@MainBundle/Resources/sass/main.scss', '@AnotherBundle/Resources/sass/more.scss' %} <link href="{{ asset_url }}" rel="stylesheet" /> {% endassetic %}
assetic:dump
PHP templates
Coming soon
<?php foreach ($view['assetic']->urls( array('@MainBundle/Resources/sass/main.scss', '@AnotherBundle/Resources/sass/more.scss'), array('scss', '?yui_css'), array('output' => 'css/all.css') ) as $url): ?> <link href="<?php echo $url ?>" rel="stylesheet" /> <?php endforeach; ?>
Fork me!
http://github.com/kriswallsmith/symfony-sandbox
Whats Next?
Finish Symfony2 helpers for PHP templates Filter conguration Image sprites, embedded image data --watch commands Client-aware optimizations? Better CDN integration
Questions?
Assetic
http://github.com/kriswallsmith/assetic