Introducing Assetic: Asset Management for PHP 5.3

110
Introducing Assetic Asset Management for PHP 5.3 Kris Wallsmith February 9, 2011

description

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.

Transcript of Introducing Assetic: Asset Management for PHP 5.3

Page 1: Introducing Assetic: Asset Management for PHP 5.3

Introducing AsseticAsset Management for PHP 5.3

Kris Wallsmith February 9, 2011

Page 2: Introducing Assetic: Asset Management for PHP 5.3

@kriswallsmith

• Symfony core team member

• Doctrine contributor

• Symfony Guru at

• 10+ years experience with PHP and web development

• Open source evangelist and international speaker

Page 3: Introducing Assetic: Asset Management for PHP 5.3

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.

Page 4: Introducing Assetic: Asset Management for PHP 5.3

ShopOpenSky.com

• PHP 5.3 + Symfony2

• MongoDB + Doctrine MongoDB ODM

• MySQL + Doctrine2 ORM

• Less CSS

• jQuery

Page 5: Introducing Assetic: Asset Management for PHP 5.3

Agenda

• Strawman

• The code

• Twig Integration

• Symfony2 Integration

Page 6: Introducing Assetic: Asset Management for PHP 5.3

Symfony2 is FAST

Page 7: Introducing Assetic: Asset Management for PHP 5.3

But you can still f*** that up

Page 8: Introducing Assetic: Asset Management for PHP 5.3

We build tools thatencourage best practices

Page 9: Introducing Assetic: Asset Management for PHP 5.3

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

Page 10: Introducing Assetic: Asset Management for PHP 5.3

If you haven’t optimized your frontend, you haven’t optimized

Page 11: Introducing Assetic: Asset Management for PHP 5.3

Get your assets in line.

Page 12: Introducing Assetic: Asset Management for PHP 5.3

A poorly optimized frontendcan destroy UX

Page 13: Introducing Assetic: Asset Management for PHP 5.3

…and SEO!

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

Page 14: Introducing Assetic: Asset Management for PHP 5.3

Asset Management

Page 15: Introducing Assetic: Asset Management for PHP 5.3

Lots of awesome tools• CoffeeScript

• Compass Framework

• CSSEmbed

• Google Closure Compiler

• JSMin

• LESS

• Packer

• SASS

• Sprockets

• YUI Compressor

Page 16: Introducing Assetic: Asset Management for PHP 5.3

The ones written in PHP…

Page 17: Introducing Assetic: Asset Management for PHP 5.3

This is a difficult problem

Page 18: Introducing Assetic: Asset Management for PHP 5.3

Assetic makes it easy

Page 19: Introducing Assetic: Asset Management for PHP 5.3

Are you ready tokick some Assetic!?!

Page 20: Introducing Assetic: Asset Management for PHP 5.3

# /path/to/web/js/core.php

$core = new FileAsset('/path/to/jquery.js');$core->load();

header('Content-Type: text/javascript');echo $core->dump();

Page 21: Introducing Assetic: Asset Management for PHP 5.3

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

header('Content-Type: text/javascript');echo $core->dump();

Merge many files into one: fewer HTTP requests

Page 22: Introducing Assetic: Asset Management for PHP 5.3

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

header('Content-Type: text/javascript');echo $core->dump();

Compress the merged asset: less data over the wire

Page 23: Introducing Assetic: Asset Management for PHP 5.3

<script src="js/core.php"></script>

Page 24: Introducing Assetic: Asset Management for PHP 5.3

Assetic isAssets & Filters

Page 25: Introducing Assetic: Asset Management for PHP 5.3

Inspired by Python’s webassets

https://github.com/miracle2k/webassets

Page 26: Introducing Assetic: Asset Management for PHP 5.3

Assets have lazy, mutable content

Page 27: Introducing Assetic: Asset Management for PHP 5.3

Filters act on asset contents during “load” and “dump”

Page 28: Introducing Assetic: Asset Management for PHP 5.3

Assets can be gathered in collections

Page 29: Introducing Assetic: Asset Management for PHP 5.3

A collection is an asset

Page 30: Introducing Assetic: Asset Management for PHP 5.3

FilterFilter

Asset

FilterFilter

Asset

Load

Dum

p

Page 31: Introducing Assetic: Asset Management for PHP 5.3

Asset Collection

FilterFilter

Asset

FilterFilter

Asset

Page 32: Introducing Assetic: Asset Management for PHP 5.3

Asset Collection

Asset Collection

FilterFilter

Asset

FilterFilter

Asset

FilterFilter

Asset

FilterFilter

Asset

Page 33: Introducing Assetic: Asset Management for PHP 5.3

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

Page 34: Introducing Assetic: Asset Management for PHP 5.3

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

Page 35: Introducing Assetic: Asset Management for PHP 5.3

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

header('Content-Type: text/css');echo $styles->dump();

Lazy! The filesystem isn't touched until now

Page 36: Introducing Assetic: Asset Management for PHP 5.3

Basic Asset Classes

• AssetCollection

• AssetReference

• FileAsset

• GlobAsset

• StringAsset

Page 37: Introducing Assetic: Asset Management for PHP 5.3

Core Filter Classes• CallablesFilter

• CoffeeScriptFilter

• CssRewriteFilter

• GoogleClosure\CompilerApiFilter

• GoogleClosure\CompilerJarFilter

• LessFilter

• Sass\SassFilter

• Sass\ScssFilter

• SprocketsFilter

• Yui\CssCompressorFilter

• Yui\JsCompressorFilter

• More to come…

Page 38: Introducing Assetic: Asset Management for PHP 5.3

Asset Manager

Page 39: Introducing Assetic: Asset Management for PHP 5.3

$am = new AssetManager();$am->set('jquery', new FileAsset('/path/to/jquery.js'));

Page 40: Introducing Assetic: Asset Management for PHP 5.3

$plugin = new AssetCollection(array( new AssetReference($am, 'jquery'), new FileAsset('/path/to/jquery.plugin.js'),));

Page 41: Introducing Assetic: Asset Management for PHP 5.3

$core = new AssetCollection(array( $jquery, $plugin1, $plugin2,));

header('text/javascript');echo $core->dump();

jQuery will only be included once

Page 42: Introducing Assetic: Asset Management for PHP 5.3

Filter Manager

Page 43: Introducing Assetic: Asset Management for PHP 5.3

$yui = new YuiCompressorJs();$yui->setNomunge(true);

$fm = new FilterManager();$fm->set('yui_js', $yui);

Page 44: Introducing Assetic: Asset Management for PHP 5.3

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

jQuery will only be compressed once

Page 45: Introducing Assetic: Asset Management for PHP 5.3

Asset Factory

Page 46: Introducing Assetic: Asset Management for PHP 5.3

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

Page 47: Introducing Assetic: Asset Management for PHP 5.3

include '/path/to/asset_factory.php';

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

header('Content-Type: text/javascript');echo $asset->dump();

Page 48: Introducing Assetic: Asset Management for PHP 5.3

Debug Mode

Page 49: Introducing Assetic: Asset Management for PHP 5.3

Debugging compressedJavascript sucks

Page 50: Introducing Assetic: Asset Management for PHP 5.3

Mark filters for omissionin debug mode

Page 51: Introducing Assetic: Asset Management for PHP 5.3

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

Page 52: Introducing Assetic: Asset Management for PHP 5.3

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

Page 53: Introducing Assetic: Asset Management for PHP 5.3

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

Page 54: Introducing Assetic: Asset Management for PHP 5.3

Factory Workers

Page 55: Introducing Assetic: Asset Management for PHP 5.3

Everything passes through the workers’ hands

Page 56: Introducing Assetic: Asset Management for PHP 5.3

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

Page 57: Introducing Assetic: Asset Management for PHP 5.3

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

Page 58: Introducing Assetic: Asset Management for PHP 5.3

Not Lazy Enough?

Page 59: Introducing Assetic: Asset Management for PHP 5.3

Asset Formulae and theLazy Asset Manager

Page 60: Introducing Assetic: Asset Management for PHP 5.3

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

Page 61: Introducing Assetic: Asset Management for PHP 5.3

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

Page 62: Introducing Assetic: Asset Management for PHP 5.3

$am = new LazyAssetManager($factory);$am->setFormula('core_js', $formula);

header('Content-Type: text/javascript');echo $am->get('core_js')->dump();

Page 63: Introducing Assetic: Asset Management for PHP 5.3

Good: Basic Caching

Page 64: Introducing Assetic: Asset Management for PHP 5.3

# /path/to/web/css/styles.php

$styles = new AssetCollection( array(new FileAsset('/path/to/main.sass')), array(new SassFilter()));

echo $styles->dump();

Page 65: Introducing Assetic: Asset Management for PHP 5.3

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

echo $styles->dump();

Run the filters once and cache the content

Page 66: Introducing Assetic: Asset Management for PHP 5.3

Better: HTTP Caching

Page 67: Introducing Assetic: Asset Management for PHP 5.3

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

Page 68: Introducing Assetic: Asset Management for PHP 5.3

Best: Static Assets

Page 69: Introducing Assetic: Asset Management for PHP 5.3

# /path/to/scripts/dump_assets.php

$am = new AssetManager();$am->set('foo', $foo);// etc...

$writer = new AssetWriter('/path/to/web');$writer->writeManagerAssets($am);

Page 70: Introducing Assetic: Asset Management for PHP 5.3

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

Page 71: Introducing Assetic: Asset Management for PHP 5.3

Best-est:Content Distribution Network

Page 72: Introducing Assetic: Asset Management for PHP 5.3

new AssetWriter('s3://my-bucket')

A CloudFront S3 bucket

Page 73: Introducing Assetic: Asset Management for PHP 5.3

Twig Integration

Page 74: Introducing Assetic: Asset Management for PHP 5.3

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

Page 75: Introducing Assetic: Asset Management for PHP 5.3

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

Page 76: Introducing Assetic: Asset Management for PHP 5.3

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

Page 77: Introducing Assetic: Asset Management for PHP 5.3

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

Page 78: Introducing Assetic: Asset Management for PHP 5.3

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

Page 79: Introducing Assetic: Asset Management for PHP 5.3

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

Page 80: Introducing Assetic: Asset Management for PHP 5.3

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

Page 81: Introducing Assetic: Asset Management for PHP 5.3

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

Page 82: Introducing Assetic: Asset Management for PHP 5.3

Formula LoaderUses the Twig parser to extract asset formulae from templates

Page 83: Introducing Assetic: Asset Management for PHP 5.3

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

Page 84: Introducing Assetic: Asset Management for PHP 5.3

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

Page 85: Introducing Assetic: Asset Management for PHP 5.3

AsseticBundle

Page 86: Introducing Assetic: Asset Management for PHP 5.3

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

Page 87: Introducing Assetic: Asset Management for PHP 5.3

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

Page 88: Introducing Assetic: Asset Management for PHP 5.3

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

Page 89: Introducing Assetic: Asset Management for PHP 5.3

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

Page 90: Introducing Assetic: Asset Management for PHP 5.3

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

Each "leaf" asset is referenced individually

Page 91: Introducing Assetic: Asset Management for PHP 5.3

Configuration

Page 92: Introducing Assetic: Asset Management for PHP 5.3

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

Page 93: Introducing Assetic: Asset Management for PHP 5.3

{# when use_controller=true #}

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

Page 94: Introducing Assetic: Asset Management for PHP 5.3

# routing_dev.yml_assetic: resource: . type: assetic

Page 95: Introducing Assetic: Asset Management for PHP 5.3

{# when use_controller=false #}

<script src="{{ asset('js/core.js') }}"></script>

Lots for free

Page 96: Introducing Assetic: Asset Management for PHP 5.3

The Symfony2 Assets Helper

• Multiple asset domains

• Cache buster

Page 97: Introducing Assetic: Asset Management for PHP 5.3

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

Page 98: Introducing Assetic: Asset Management for PHP 5.3

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

Page 99: Introducing Assetic: Asset Management for PHP 5.3

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

Page 100: Introducing Assetic: Asset Management for PHP 5.3

assetic:dump

Page 101: Introducing Assetic: Asset Management for PHP 5.3

$ php app/console assetic:dump web/

Page 102: Introducing Assetic: Asset Management for PHP 5.3

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

Register a stream wrapper in boot()

Page 103: Introducing Assetic: Asset Management for PHP 5.3

PHP templatesComing soon…

Page 104: Introducing Assetic: Asset Management for PHP 5.3

<?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; ?>

Page 105: Introducing Assetic: Asset Management for PHP 5.3

Fork me!http://github.com/kriswallsmith/symfony-sandbox

Page 106: Introducing Assetic: Asset Management for PHP 5.3

What’s Next?

• Finish Symfony2 helpers for PHP templates

• Filter configuration

• Image sprites, embedded image data

• --watch commands

• Client-aware optimizations?

• Better CDN integration

Page 107: Introducing Assetic: Asset Management for PHP 5.3

Assetic is a killer feature of Symfony2…

Page 108: Introducing Assetic: Asset Management for PHP 5.3

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

Page 109: Introducing Assetic: Asset Management for PHP 5.3

Questions?

Page 110: Introducing Assetic: Asset Management for PHP 5.3

Assetichttp://github.com/kriswallsmith/assetic