The performance of your application depends heavily on the number and size of assets on each page. Even your blazingly fastest Symfony2 application can be bogged down by bloated Javascript and CSS ...
The performance of your application depends heavily on the number and size of assets on each page. Even your blazingly fastest Symfony2 application can be bogged down by bloated Javascript and CSS files. This session will give you a basic introduction to PHP's new asset management framework, Assetic, and explore how it integrates with Symfony2 for a pleasant, common sense developer experience.
Introducing Assetic: Asset Management for PHP 5.3Presentation Transcript
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
But you can still f*** that up
We build tools thatencourage best practices
Best practices like…• Dependency injection (DI)• Proper caching, edge side includes (ESI)• Test-driven development (TDD)• Dont repeat yourself (DRY)• Keep it simple, SVP (KISS)• Performance
If you haven’t optimized yourfrontend, you haven’t optimized
# /path/to/web/js/core.php$core = new FileAsset(/path/to/jquery.js);$core->load();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),));$core->load(); many files into one: fewer HTTP requests Mergeheader(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 theheader(Content-Type: text/javascript);echo $core->dump();
<script src="js/core.php"></script>
Assetic isAssets & Filters
Inspired by Python’s webassets https://github.com/miracle2k/webassets
Assets have lazy, mutable content
Filters act on asset contents during “load” and “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 filesystem isnt touched until nowheader(Content-Type: text/css);echo $styles->dump();
$am = new AssetManager();$am->set(jquery, new FileAsset(/path/to/jquery.js));
$plugin = new AssetCollection(array( new AssetReference($am, jquery), new FileAsset(/path/to/jquery.plugin.js),));
jQuery will only be included once $core = new AssetCollection(array( $jquery, $plugin1, $plugin2, )); header(text/javascript); echo $core->dump();
Filter Manager
$yui = new YuiCompressorJs();$yui->setNomunge(true);$fm = new FilterManager();$fm->set(yui_js, $yui);
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 GoogleClosureCompilerApi());$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();
$am = new LazyAssetManager($factory);$am->setFormula(core_js, $formula);header(Content-Type: text/javascript);echo $am->get(core_js)->dump();
Good: Basic Caching
# /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 filters once and cache the contentecho $styles->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));}
Best-est:Content Distribution Network
new AssetWriter(s3://my-bucket) A CloudFront S3 bucket
Formula LoaderUses 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));}$am = new LazyAssetManager($factory);$am->addFormulae(require $cache);