assetic (symfony live paris)

129
Introducing Assetic Asset Management for PHP 5.3 March 4, 2011

Upload: kris-wallsmith

Post on 15-Jan-2015

13.118 views

Category:

Technology


4 download

DESCRIPTION

Presentation on Assetic at Symfony Live 2011 Paris.

TRANSCRIPT

Page 1: Assetic (Symfony Live Paris)

Introducing AsseticAsset Management for PHP 5.3

March 4, 2011

Page 2: Assetic (Symfony Live Paris)

@kriswallsmith

• Symfony Guru at

• Symfony core team member

• Doctrine contributor

• 10+ years experience with PHP and web development

• Open source evangelist and international speaker

Page 3: Assetic (Symfony Live Paris)

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: Assetic (Symfony Live Paris)

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 5: Assetic (Symfony Live Paris)

ShopOpenSky.com

• PHP 5.3 + Symfony2

• MongoDB + Doctrine MongoDB ODM

• MySQL + Doctrine2 ORM

• Less CSS

• jQuery

Page 6: Assetic (Symfony Live Paris)

Symfony2 is FAST

Page 7: Assetic (Symfony Live Paris)

But you can still f*** that up

Page 8: Assetic (Symfony Live Paris)

We build tools thatencourage best practices

Page 9: Assetic (Symfony Live Paris)

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: Assetic (Symfony Live Paris)

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

Page 11: Assetic (Symfony Live Paris)

Get your assets in line.

Page 12: Assetic (Symfony Live Paris)

A poorly optimized frontendcan destroy UX

Page 13: Assetic (Symfony Live Paris)

…and SEO!

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

Page 14: Assetic (Symfony Live Paris)

Asset Management

Page 15: Assetic (Symfony Live Paris)

Lots of awesome tools:

Page 16: Assetic (Symfony Live Paris)

Lots of awesome tools:• CoffeeScript

• Compass Framework

• CSSEmbed

• Google Closure Compiler

• JSMin

• LESS

• Packer

• SASS

• Sprockets

• Stylus

• YUI Compressor

Page 17: Assetic (Symfony Live Paris)

The ones written in PHP…

Page 18: Assetic (Symfony Live Paris)

The ones written in PHP…

Page 19: Assetic (Symfony Live Paris)

This is a difficult problem

Page 20: Assetic (Symfony Live Paris)

Assetic makes it easy

Page 21: Assetic (Symfony Live Paris)

as•cet•i•cismdescribes a lifestyle characterized by abstinence from various sorts of worldly

pleasures often with the aim of pursuing religious and spiritual goals

Page 22: Assetic (Symfony Live Paris)

No B.S.

Page 23: Assetic (Symfony Live Paris)

Enough talk

Page 24: Assetic (Symfony Live Paris)

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

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

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

Page 25: Assetic (Symfony Live Paris)

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

Page 26: Assetic (Symfony Live Paris)

# /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 27: Assetic (Symfony Live Paris)

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

Page 28: Assetic (Symfony Live Paris)

# /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 29: Assetic (Symfony Live Paris)

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

Page 30: Assetic (Symfony Live Paris)

Assetic isAssets & Filters

Page 31: Assetic (Symfony Live Paris)

Inspired by Python’s webassets

https://github.com/miracle2k/webassets

Page 32: Assetic (Symfony Live Paris)

Assets have lazy, mutable content

Page 33: Assetic (Symfony Live Paris)

A filter acts on an asset’s contents during “load” and “dump”

Page 34: Assetic (Symfony Live Paris)

Assets can be gathered in collections

Page 35: Assetic (Symfony Live Paris)

A collection is an asset

Page 36: Assetic (Symfony Live Paris)
Page 37: Assetic (Symfony Live Paris)

Asset

Page 38: Assetic (Symfony Live Paris)

Filter

Asset

Page 39: Assetic (Symfony Live Paris)

FilterFilter

Asset

Page 40: Assetic (Symfony Live Paris)

FilterFilter

Asset

Load

Page 41: Assetic (Symfony Live Paris)

FilterFilter

Asset

Dum

p

Page 42: Assetic (Symfony Live Paris)

FilterFilter

Asset

Page 43: Assetic (Symfony Live Paris)

Asset Collection

FilterFilter

Asset

FilterFilter

Asset

Page 44: Assetic (Symfony Live Paris)

Asset Collection

Asset Collection

FilterFilter

Asset

FilterFilter

Asset

FilterFilter

Asset

FilterFilter

Asset

Page 45: Assetic (Symfony Live Paris)

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

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

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

Page 46: Assetic (Symfony Live Paris)

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

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

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

Load is implied

Page 47: Assetic (Symfony Live Paris)

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

$styles = 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 48: Assetic (Symfony Live Paris)

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

$styles = 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();

Page 49: Assetic (Symfony Live Paris)

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

$styles = 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 50: Assetic (Symfony Live Paris)

Basic Asset Classes

• AssetCollection

• AssetReference

• FileAsset

• GlobAsset

• StringAsset

Page 51: Assetic (Symfony Live Paris)

Core Filter Classes• CallablesFilter

• CoffeeScriptFilter

• CssRewriteFilter

• GoogleClosure\CompilerApiFilter

• GoogleClosure\CompilerJarFilter

• LessFilter

• Sass\SassFilter

• Sass\ScssFilter

• SprocketsFilter

• StylusFilter

• Yui\CssCompressorFilter

• Yui\JsCompressorFilter

• More to come…

Page 52: Assetic (Symfony Live Paris)

Asset Manager

Page 53: Assetic (Symfony Live Paris)

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

Page 54: Assetic (Symfony Live Paris)

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

Page 55: Assetic (Symfony Live Paris)

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

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

Page 56: Assetic (Symfony Live Paris)

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

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

jQuery will only be included once

Page 57: Assetic (Symfony Live Paris)

Filter Manager

Page 58: Assetic (Symfony Live Paris)

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

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

Page 59: Assetic (Symfony Live Paris)

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

Page 60: Assetic (Symfony Live Paris)

$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 61: Assetic (Symfony Live Paris)

Asset Factory

Page 62: Assetic (Symfony Live Paris)

$fm = new FilterManager();$fm->set('coffee', new CoffeeScriptFilter());$fm->set('closure', new ClosureFilter());

$factory = new AssetFactory('/path/to/web');$factory->setFilterManager($fm);

Page 63: Assetic (Symfony Live Paris)

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

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

Page 64: Assetic (Symfony Live Paris)

Debug Mode

Page 65: Assetic (Symfony Live Paris)

Debugging compressedJavascript sucks

Page 66: Assetic (Symfony Live Paris)

Mark filters for omissionin debug mode using a “?”

Page 67: Assetic (Symfony Live Paris)

// new AssetFactory('/path/to/web', $debug = true);

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

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

Page 68: Assetic (Symfony Live Paris)

// new AssetFactory('/path/to/web', true);

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

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

Page 69: Assetic (Symfony Live Paris)

// new AssetFactory('/path/to/web', false);

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

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

Page 70: Assetic (Symfony Live Paris)

Good: Basic Caching

Page 71: Assetic (Symfony Live Paris)

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

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

echo $styles->dump();

Page 72: Assetic (Symfony Live Paris)

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

Page 73: Assetic (Symfony Live Paris)

# /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 74: Assetic (Symfony Live Paris)

Better: HTTP Caching

Page 75: Assetic (Symfony Live Paris)

// $core = new AssetCache(...

$mtime = gmdate('D, d M y H:i:s', $core->getLastModified()).' GMT';

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 76: Assetic (Symfony Live Paris)

Best: Static Assets

Page 77: Assetic (Symfony Live Paris)

# /path/to/scripts/dump_assets.php

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

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

Page 78: Assetic (Symfony Live Paris)

Best-est:Content Distribution Network

Page 79: Assetic (Symfony Live Paris)

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

Page 80: Assetic (Symfony Live Paris)

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

A CloudFront S3 bucket

Page 81: Assetic (Symfony Live Paris)

Custom Stream Wrappers

$s3 = new Zend_Service_Amazon_S3($key, $secret);$s3->registerStreamWrapper();

Page 82: Assetic (Symfony Live Paris)

Not Lazy Enough?

Page 83: Assetic (Symfony Live Paris)

Asset Formulae and theLazy Asset Manager

Page 84: Assetic (Symfony Live Paris)

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

Page 85: Assetic (Symfony Live Paris)

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

Page 86: Assetic (Symfony Live Paris)

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

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

Page 87: Assetic (Symfony Live Paris)

A ThoughtAssets are a part of the view layer

and should be defined there.

Page 88: Assetic (Symfony Live Paris)

<!-- header.php -->

<?php foreach (assetic_javascripts( array('js/core.js', 'js/more.js'), array('?yui_js')) as $url): ?>

<script src="<?php echo $url ?>"></script>

<?php endforeach; ?>

Page 89: Assetic (Symfony Live Paris)

An IssueAssets defined in the view layer must actually exist somewhere

Page 90: Assetic (Symfony Live Paris)

Option Number BadLazily dump assets to the

web directory

Page 91: Assetic (Symfony Live Paris)

Option Number GoodEagerly dump assets to the

web directory

Page 92: Assetic (Symfony Live Paris)

A template is a configuration file

Page 93: Assetic (Symfony Live Paris)

Formula Loadersextract asset formulae from templates

Page 94: Assetic (Symfony Live Paris)

$loader = new FunctionCallsFormulaLoader();$resource = new DirectoryResource( '/path/to/templates', '/\.php$/');

$formulae = $loader->load($resource);

Page 95: Assetic (Symfony Live Paris)

$am = new LazyAssetManager($factory);$am->setLoader('php', $loader);$am->addResource($resource, 'php');

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

Page 96: Assetic (Symfony Live Paris)

$am = new LazyAssetManager($factory);$am->setLoader('php', $loader);$am->addResource($resource, 'php');

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

Expensive every time

Page 97: Assetic (Symfony Live Paris)

$cache = new ConfigCache('/path/to/cache');

$loader = new CachedFormulaLoader( $loader, $cache, $debug);

Page 98: Assetic (Symfony Live Paris)

$cache = new ConfigCache('/path/to/cache');

$loader = new CachedFormulaLoader( $loader, $cache, $debug);

Whether to stat each file for changes

Page 99: Assetic (Symfony Live Paris)

Twig Integration

Page 100: Assetic (Symfony Live Paris)

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

Page 101: Assetic (Symfony Live Paris)

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

Page 102: Assetic (Symfony Live Paris)

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

Page 103: Assetic (Symfony Live Paris)

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

Page 104: Assetic (Symfony Live Paris)

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

Page 105: Assetic (Symfony Live Paris)

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

Page 106: Assetic (Symfony Live Paris)

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

Page 107: Assetic (Symfony Live Paris)

{% javascripts 'js/*.coffee' filter='coffee,?closure' %}<script src="{{ asset_url }}"></script>{% endjavascripts %}

Page 108: Assetic (Symfony Live Paris)

{% javascripts 'js/*.coffee' filter='coffee,?closure' %}<script src="{{ asset_url }}"></script>{% endjavascripts %}

Adds a default output string

Page 109: Assetic (Symfony Live Paris)

{% javascripts 'js/*.coffee' filter='coffee,?closure' debug=true %}<script src="{{ asset_url }}"></script>{% endjavascripts %}

Page 110: Assetic (Symfony Live Paris)

<script src="js/92429d8_1.js"></script><script src="js/92429d8_2.js"></script><script src="js/92429d8_3.js"></script>

Page 111: Assetic (Symfony Live Paris)

<script src="js/92429d8_1.js"></script><script src="js/92429d8_2.js"></script><script src="js/92429d8_3.js"></script>

Each "leaf" asset is referenced individually

Page 112: Assetic (Symfony Live Paris)

AsseticBundleSymfony2 integration

Page 113: Assetic (Symfony Live Paris)

{% 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 114: Assetic (Symfony Live Paris)

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

Page 115: Assetic (Symfony Live Paris)

Configuration

Page 116: Assetic (Symfony Live Paris)

assetic: debug: %kernel.debug% use_controller: %kernel.debug% read_from: %kernel.root_dir%/../web write_to: s3://mybucket

Page 117: Assetic (Symfony Live Paris)

{# when use_controller=true #}

<script src="{{ path('assetic_foo') }}"...

Page 118: Assetic (Symfony Live Paris)

# routing_dev.yml_assetic: resource: . type: assetic

Page 119: Assetic (Symfony Live Paris)

{# when use_controller=false #}

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

Page 120: Assetic (Symfony Live Paris)

{# when use_controller=false #}

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

Lots for free

Page 121: Assetic (Symfony Live Paris)

The Symfony2 Assets Helper

• Multiple asset domains

• Cache buster

Page 122: Assetic (Symfony Live Paris)

framework: 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 123: Assetic (Symfony Live Paris)

{% 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 124: Assetic (Symfony Live Paris)

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

Page 125: Assetic (Symfony Live Paris)

assetic:dump

Page 126: Assetic (Symfony Live Paris)

$ php app/console assetic:dump web/

Page 127: Assetic (Symfony Live Paris)

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

Page 128: Assetic (Symfony Live Paris)

assetic:dump --watchDump static assets in the background as you develop

Page 129: Assetic (Symfony Live Paris)

Questions?

http://github.com/kriswallsmith/assetic