0% found this document useful (0 votes)
89 views110 pages

Introducing Assetic: Asset Management For PHP 5.3

This document introduces Assetic, an asset management library for PHP. It allows managing assets like CSS, JS and image files. Key features include: - Defining asset collections and applying filters to assets - Merging and minifying assets to reduce file sizes and HTTP requests - Integrating with Symfony and Twig for template asset definitions - Lazy loading and caching of assets for improved performance
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
89 views110 pages

Introducing Assetic: Asset Management For PHP 5.3

This document introduces Assetic, an asset management library for PHP. It allows managing assets like CSS, JS and image files. Key features include: - Defining asset collections and applying filters to assets - Merging and minifying assets to reduce file sizes and HTTP requests - Integrating with Symfony and Twig for template asset definitions - Lazy loading and caching of assets for improved performance
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 110

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 that encourage best practices

Best practices like



Dependency injection (DI) Proper caching, edge side includes (ESI) Test-driven development (TDD) Don't repeat yourself (DRY) Keep it simple, SVP (KISS) Performance

If you havent optimized your frontend, you havent optimized

Get your assets in line.

A poorly optimized frontend can destroy UX

and SEO!

http://googlewebmastercentral.blogspot.com/2010/04/using-site-speed-in-web-search-ranking.html

Asset Management

Lots of awesome tools



CoffeeScript Compass Framework CSSEmbed Google Closure Compiler JSMin

LESS Packer SASS Sprockets YUI Compressor

The ones written in PHP

This is a difcult problem

Assetic makes it easy

Are you ready to kick some Assetic!?!

# /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 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>

Assetic is Assets & Filters

Inspired by Pythons webassets

https://github.com/miracle2k/webassets

Assets have lazy, mutable content

Filters act on asset contents during load and dump

Assets can be gathered in collections

A collection is an asset

Load

Filter Filter Asset

Dump

Asset Collection Filter Filter Filter Filter Asset Asset

Asset Collection Asset Collection Filter Filter Filter Filter Asset Asset

Filter Filter Asset

Filter Filter 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();

Basic Asset Classes



AssetCollection AssetReference FileAsset GlobAsset StringAsset

Core Filter Classes



CallablesFilter CoffeeScriptFilter CssRewriteFilter GoogleClosure\CompilerApiFilter GoogleClosure\CompilerJarFilter LessFilter

Sass\SassFilter Sass\ScssFilter SprocketsFilter Yui\CssCompressorFilter Yui\JsCompressorFilter More to come

Asset Manager

$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 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

Debugging compressed Javascript sucks

Mark lters for omission in 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

Everything passes through the workers hands

$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, ));

Not Lazy Enough?

Asset Formulae and the Lazy Asset Manager

$asset = $factory->createAsset( array('js/src/*.coffee'), array('coffee', '?closure'), array('output' => 'js') );

$formula = array( array('js/src/*.coffee'), array('coffee', '?closure'), array('output' => 'js') );

$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 lters once and cache the content echo $styles->dump();

Better: HTTP Caching

# /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();

Best: Static Assets

# /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

Twig Integration

$twig->addExtension(new AsseticExtension($factory));

{% assetic 'js/*.coffee', filter='coffee' %} <script src="{{ asset_url }}"></script> {% endassetic %}

<script src="assets/92429d8"></script>

{% assetic 'js/*.coffee', filter='coffee' %} <script src="{{ asset_url }}"></script> {% endassetic %}

{% assetic 'js/*.coffee', filter='coffee', output='js' %} <script src="{{ asset_url }}"></script> {% endassetic %}

<script src="js/92429d8.js"></script>

{% assetic 'js/*.coffee', filter='coffee', output='js' %} <script src="{{ asset_url }}"></script> {% endassetic %}

{% 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));

$am = new LazyAssetManager($factory); $am->addFormulae(require $cache);

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 %}

<link href="css/all.css" rel="stylesheet" />

{% 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 %}

Each "leaf" asset is referenced individually


<link href="css/all_part1.css" rel="stylesheet" /> <link href="css/all_part2.css" rel="stylesheet" />

Conguration

assetic.config: debug: %kernel.debug% use_controller: %kernel.debug% document_root: %kernel.root_dir%/../web

{# when use_controller=true #} <script src="{{ path('route', { 'name': 'core_js' }) }}"...

# routing_dev.yml _assetic: resource: . type: assetic

{# when use_controller=false #} <script src="{{ asset('js/core.js') }}"></script> Lots for free

The Symfony2 Assets Helper

Multiple asset domains Cache buster

app.config: templating: assets_version: 1.2.3 assets_base_urls: - http://assets1.domain.com - http://assets2.domain.com - http://assets3.domain.com - http://assets4.domain.com

{% 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 %}

<link href="http://assets3.domain.com/css/all.css?1.2.3" ...

assetic:dump

$ php app/console assetic:dump web/

$ php app/console assetic:dump s3://my-bucket


Register a stream wrapper in boot()

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

Assetic is a killer feature of Symfony2

but is only one month old, so be nice :)

Questions?

Assetic
http://github.com/kriswallsmith/assetic

You might also like