Introducing Assetic (NYPHP)

125
Introducing Assetic Asset Management for PHP 5.3 March 1, 2011

description

 

Transcript of Introducing Assetic (NYPHP)

Page 1: Introducing Assetic (NYPHP)

Introducing AsseticAsset Management for PHP 5.3

March 1, 2011

Page 2: Introducing Assetic (NYPHP)

@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: Introducing Assetic (NYPHP)

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 (NYPHP)

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: Introducing Assetic (NYPHP)

ShopOpenSky.com

• PHP 5.3 + Symfony2

• MongoDB + Doctrine MongoDB ODM

• MySQL + Doctrine2 ORM

• Less CSS

• jQuery

Page 6: Introducing Assetic (NYPHP)

Agenda

• !(straw man)

• Assetic

• Twig, Symfony2 integration

Page 7: Introducing Assetic (NYPHP)

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

Page 8: Introducing Assetic (NYPHP)

A poorly optimized frontendcan destroy UX

Page 9: Introducing Assetic (NYPHP)

…and SEO

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

Page 10: Introducing Assetic (NYPHP)

Get your assets in line.

Page 11: Introducing Assetic (NYPHP)

Asset Management

Page 12: Introducing Assetic (NYPHP)

Lots of awesome tools:

Page 13: Introducing Assetic (NYPHP)

Lots of awesome tools:• CoffeeScript

• Compass Framework

• CSSEmbed

• Google Closure Compiler

• JSMin

• LESS

• Packer

• SASS

• Sprockets

• Stylus

• YUI Compressor

Page 14: Introducing Assetic (NYPHP)

The ones written in PHP…

Page 15: Introducing Assetic (NYPHP)

The ones written in PHP…

Page 16: Introducing Assetic (NYPHP)

This is a difficult problem

Page 17: Introducing Assetic (NYPHP)

Assetic makes it easy

Page 18: Introducing Assetic (NYPHP)

Enough talk

Page 19: Introducing Assetic (NYPHP)

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

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

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

Page 20: Introducing Assetic (NYPHP)

# /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 21: Introducing Assetic (NYPHP)

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

# /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 23: Introducing Assetic (NYPHP)

# /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 24: Introducing Assetic (NYPHP)

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

Page 25: Introducing Assetic (NYPHP)

Assetic isAssets & Filters

Page 26: Introducing Assetic (NYPHP)

Inspired by Python’s webassets

https://github.com/miracle2k/webassets

Page 27: Introducing Assetic (NYPHP)

Assets have lazy, mutable content

Page 28: Introducing Assetic (NYPHP)

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

Page 29: Introducing Assetic (NYPHP)

Assets can be gathered in collections

Page 30: Introducing Assetic (NYPHP)

A collection is an asset

Page 31: Introducing Assetic (NYPHP)
Page 32: Introducing Assetic (NYPHP)

Asset

Page 33: Introducing Assetic (NYPHP)

Filter

Asset

Page 34: Introducing Assetic (NYPHP)

FilterFilter

Asset

Page 35: Introducing Assetic (NYPHP)

FilterFilter

Asset

Load

Page 36: Introducing Assetic (NYPHP)

FilterFilter

Asset

Dum

p

Page 37: Introducing Assetic (NYPHP)

FilterFilter

Asset

Page 38: Introducing Assetic (NYPHP)

Asset Collection

FilterFilter

Asset

FilterFilter

Asset

Page 39: Introducing Assetic (NYPHP)

Asset Collection

Asset Collection

FilterFilter

Asset

FilterFilter

Asset

FilterFilter

Asset

FilterFilter

Asset

Page 40: Introducing Assetic (NYPHP)

# /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 41: Introducing Assetic (NYPHP)

# /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 42: Introducing Assetic (NYPHP)

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

Page 43: Introducing Assetic (NYPHP)

# /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 44: Introducing Assetic (NYPHP)

Basic Asset Classes

• AssetCollection

• AssetReference

• FileAsset

• GlobAsset

• StringAsset

Page 45: Introducing Assetic (NYPHP)

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 46: Introducing Assetic (NYPHP)

Asset Manager

Page 47: Introducing Assetic (NYPHP)

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

Page 48: Introducing Assetic (NYPHP)

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

Page 49: Introducing Assetic (NYPHP)

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

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

Page 50: Introducing Assetic (NYPHP)

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

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

jQuery will only be included once

Page 51: Introducing Assetic (NYPHP)

Filter Manager

Page 52: Introducing Assetic (NYPHP)

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

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

Page 53: Introducing Assetic (NYPHP)

$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 54: Introducing Assetic (NYPHP)

$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 55: Introducing Assetic (NYPHP)

Asset Factory

Page 56: Introducing Assetic (NYPHP)

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

Page 57: Introducing Assetic (NYPHP)

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 58: Introducing Assetic (NYPHP)

Debug Mode

Page 59: Introducing Assetic (NYPHP)

Debugging compressedJavascript sucks

Page 60: Introducing Assetic (NYPHP)

Mark filters for omissionin debug mode using a “?”

Page 61: Introducing Assetic (NYPHP)

// 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 62: Introducing Assetic (NYPHP)

// 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 63: Introducing Assetic (NYPHP)

// 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 64: Introducing Assetic (NYPHP)

Factory Workers

Page 65: Introducing Assetic (NYPHP)

Everything passes through the workers’ hands

Page 66: Introducing Assetic (NYPHP)

$worker = new EnsureFilterWorker( '/\.css$/', // the output pattern $fm->get('yui_css') // the filter);

$factory = new AssetFactory('/path/to/web');$factory->addWorker($worker);

// compressed$factory->createAsset('css/sass/*', 'sass', array( 'output' => 'css/*.css',));

Page 67: Introducing Assetic (NYPHP)

Good: Basic Caching

Page 68: Introducing Assetic (NYPHP)

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

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

echo $styles->dump();

Page 69: Introducing Assetic (NYPHP)

# /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 70: Introducing Assetic (NYPHP)

# /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 71: Introducing Assetic (NYPHP)

Better: HTTP Caching

Page 72: Introducing Assetic (NYPHP)

// $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 73: Introducing Assetic (NYPHP)

Best: Static Assets

Page 74: Introducing Assetic (NYPHP)

# /path/to/scripts/dump_assets.php

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

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

Page 75: Introducing Assetic (NYPHP)

Best-est:Content Distribution Network

Page 76: Introducing Assetic (NYPHP)

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

Page 77: Introducing Assetic (NYPHP)

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

A CloudFront S3 bucket

Page 78: Introducing Assetic (NYPHP)

Not Lazy Enough?

Page 79: Introducing Assetic (NYPHP)

Asset Formulae and theLazy Asset Manager

Page 80: Introducing Assetic (NYPHP)

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

Page 81: Introducing Assetic (NYPHP)

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

Page 82: Introducing Assetic (NYPHP)

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

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

Page 83: Introducing Assetic (NYPHP)

This will make more sensein a few minutes…

Page 84: Introducing Assetic (NYPHP)

Thought…Assets are a part of the view layer

and should be defined there.

Page 85: Introducing Assetic (NYPHP)

<!-- 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 86: Introducing Assetic (NYPHP)

# config/assetic.php

require_once '/path/to/assetic/functions.php';assetic_init($factory);

Page 87: Introducing Assetic (NYPHP)

Issue…Assets defined in the view layer must actually exist somewhere

Page 88: Introducing Assetic (NYPHP)

Option Bad...Lazily dump assets to the

web directory

Page 89: Introducing Assetic (NYPHP)

Option Good…Eagerly dump assets to the

web directory

Page 90: Introducing Assetic (NYPHP)

Formula Loaders

Page 91: Introducing Assetic (NYPHP)

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

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

Page 92: Introducing Assetic (NYPHP)

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

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

Page 93: Introducing Assetic (NYPHP)

$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 94: Introducing Assetic (NYPHP)

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

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

Page 95: Introducing Assetic (NYPHP)

Twig Integration

Page 96: Introducing Assetic (NYPHP)

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

Page 97: Introducing Assetic (NYPHP)

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

Page 98: Introducing Assetic (NYPHP)

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

Page 99: Introducing Assetic (NYPHP)

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

Page 100: Introducing Assetic (NYPHP)

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

Page 101: Introducing Assetic (NYPHP)

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

Page 102: Introducing Assetic (NYPHP)

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

Page 103: Introducing Assetic (NYPHP)

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

Page 104: Introducing Assetic (NYPHP)

AsseticBundleSymfony2 integration

Page 105: Introducing Assetic (NYPHP)

{% 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 106: Introducing Assetic (NYPHP)

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

Page 107: Introducing Assetic (NYPHP)

{% 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 108: Introducing Assetic (NYPHP)

{% 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 109: Introducing Assetic (NYPHP)

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

Page 110: Introducing Assetic (NYPHP)

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

Each "leaf" asset is referenced individually

Page 111: Introducing Assetic (NYPHP)

Configuration

Page 112: Introducing Assetic (NYPHP)

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

Page 113: Introducing Assetic (NYPHP)

{# when use_controller=true #}

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

Page 114: Introducing Assetic (NYPHP)

# routing_dev.yml_assetic: resource: . type: assetic

Page 115: Introducing Assetic (NYPHP)

{# when use_controller=false #}

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

Page 116: Introducing Assetic (NYPHP)

{# when use_controller=false #}

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

Lots for free

Page 117: Introducing Assetic (NYPHP)

The Symfony2 Assets Helper

• Multiple asset domains

• Cache buster

Page 118: Introducing Assetic (NYPHP)

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 119: Introducing Assetic (NYPHP)

{% 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 120: Introducing Assetic (NYPHP)

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

Page 121: Introducing Assetic (NYPHP)

assetic:dump

Page 122: Introducing Assetic (NYPHP)

$ php app/console assetic:dump web/

Page 123: Introducing Assetic (NYPHP)

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

Page 124: Introducing Assetic (NYPHP)

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

Page 125: Introducing Assetic (NYPHP)

Questions?

http://github.com/kriswallsmith/assetic