Assetic (OSCON)

127
Introducing Assetic Asset Management for PHP 5.3 Kris Wallsmith (OpenSky) July 29, 2011

description

 

Transcript of Assetic (OSCON)

Page 1: Assetic (OSCON)

Introducing AsseticAsset Management for PHP 5.3

Kris Wallsmith (OpenSky)July 29, 2011

Page 2: Assetic (OSCON)

@kriswallsmith

• Symfony Guru at

• Symfony core team member

• Doctrine contributor

• Author of Assetic

• 10+ years experience with PHP and web development

• Open source evangelist and international speaker

Page 3: Assetic (OSCON)

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

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

opensky.com

• PHP 5.3 + Symfony2

• MongoDB + Doctrine MongoDB ODM

• MySQL + Doctrine2 ORM

• Less CSS

• jQuery

Page 6: Assetic (OSCON)

We all want to be FAST

Page 7: Assetic (OSCON)

We use good open source tools that encourage best practices

Page 8: Assetic (OSCON)

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 9: Assetic (OSCON)

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

Page 10: Assetic (OSCON)

Get your assets in line.

Page 11: Assetic (OSCON)

A poorly optimized frontendcan destroy UX

Page 12: Assetic (OSCON)

…and SEO!

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

Page 13: Assetic (OSCON)

Asset Management

Page 14: Assetic (OSCON)

Lots of awesome tools:

Page 15: Assetic (OSCON)

Lots of awesome tools:• CoffeeScript

• Compass Framework

• CSSEmbed

• Google Closure Compiler

• jpegoptim

• JSMin

• LESS

• OptiPNG

• Packager

• SASS & SCSS

• Sprockets

• Stylus

• YUI Compressor

Page 16: Assetic (OSCON)

Integrating these tools cleanlyis a difficult problem

Page 17: Assetic (OSCON)

Assetic makes it easy

Page 18: Assetic (OSCON)

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 19: Assetic (OSCON)

No B.S.

Page 20: Assetic (OSCON)

Enough talk

Page 21: Assetic (OSCON)

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

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

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

Page 22: Assetic (OSCON)

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

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

# /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 25: Assetic (OSCON)

# /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 26: Assetic (OSCON)

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

Page 27: Assetic (OSCON)

Assetic isAssets & Filters

Page 28: Assetic (OSCON)

Inspired by Python’s webassets

https://github.com/miracle2k/webassets

Page 29: Assetic (OSCON)

Assets have lazy, mutable content

Page 30: Assetic (OSCON)

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

Page 31: Assetic (OSCON)

Assets can be gathered in collections

Page 32: Assetic (OSCON)

A collection is an asset

Page 33: Assetic (OSCON)
Page 34: Assetic (OSCON)

Asset

Page 35: Assetic (OSCON)

Filter

Asset

Page 36: Assetic (OSCON)

FilterFilter

Asset

Page 37: Assetic (OSCON)

FilterFilter

Asset

Load

Page 38: Assetic (OSCON)

FilterFilter

Asset

Dum

p

Page 39: Assetic (OSCON)

FilterFilter

Asset

Page 40: Assetic (OSCON)

Asset Collection

FilterFilter

Asset

FilterFilter

Asset

Page 41: Assetic (OSCON)

Asset Collection

Asset Collection

FilterFilter

Asset

FilterFilter

Asset

FilterFilter

Asset

FilterFilter

Asset

Page 42: Assetic (OSCON)

# /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 43: Assetic (OSCON)

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

# /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 45: Assetic (OSCON)

# /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 46: Assetic (OSCON)

Basic Asset Classes

• AssetCollection

• AssetReference

• FileAsset

• GlobAsset

• HttpAsset

• StringAsset

Page 47: Assetic (OSCON)

Core Filter Classes• CoffeeScriptFilter

• CompassFilter

• CssEmbedFilter

• CssImportFilter

• CssMinFilter

• CssRewriteFilter

• GoogleClosure\CompilerApiFilter

• GoogleClosure\CompilerJarFilter

• JpegoptimFilter

• JpegtranFilter

• LessFilter

• LessphpFilter

• OptiPngFilter

• PackagerFilter

Page 48: Assetic (OSCON)

Core Filter Classes• PngoutFilter

• Sass\SassFilter

• Sass\ScssFilter

• SprocketsFilter

• StylusFilter

• Yui\CssCompressorFilter

• Yui\JsCompressorFilter

• More to come…

Page 49: Assetic (OSCON)

Asset Manager

Page 50: Assetic (OSCON)

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

Page 51: Assetic (OSCON)

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

Page 52: Assetic (OSCON)

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

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

Page 53: Assetic (OSCON)

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

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

jQuery will only be included once

Page 54: Assetic (OSCON)

Filter Manager

Page 55: Assetic (OSCON)

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

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

Page 56: Assetic (OSCON)

$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 57: Assetic (OSCON)

$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 58: Assetic (OSCON)

Asset Factory

Page 59: Assetic (OSCON)

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

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

Page 60: Assetic (OSCON)

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

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

Page 61: Assetic (OSCON)

Debug Mode

Page 62: Assetic (OSCON)

Debugging compressedJavascript sucks

Page 63: Assetic (OSCON)

Mark filters for omissionin debug mode using a “?”

Page 64: Assetic (OSCON)

// 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 65: Assetic (OSCON)

// 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 66: Assetic (OSCON)

// 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 67: Assetic (OSCON)

Good: Basic Caching

Page 68: Assetic (OSCON)

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

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

echo $styles->dump();

Page 69: Assetic (OSCON)

# /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: Assetic (OSCON)

# /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: Assetic (OSCON)

Better: HTTP Caching

Page 72: Assetic (OSCON)

// $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: Assetic (OSCON)

Best: Static Assets

Page 74: Assetic (OSCON)

# /path/to/deploy/scripts/dump_assets.php

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

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

Page 75: Assetic (OSCON)

Best-est:Content Distribution Network

Page 76: Assetic (OSCON)

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

Page 77: Assetic (OSCON)

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

A CloudFront S3 bucket

Page 78: Assetic (OSCON)

Custom Stream Wrappers

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

Page 79: Assetic (OSCON)

Not Lazy Enough?

Page 80: Assetic (OSCON)

Asset Formulae and theLazy Asset Manager

Page 81: Assetic (OSCON)

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

Page 82: Assetic (OSCON)

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

Page 83: Assetic (OSCON)

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

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

Page 84: Assetic (OSCON)

A ThoughtAssets are a part of the view layer

and should be defined there.

Page 85: Assetic (OSCON)

<!-- 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: Assetic (OSCON)

An IssueAssets defined in the view layer must actually exist somewhere

Page 87: Assetic (OSCON)

Option Number BadLazily dump assets to the

web directory

Page 88: Assetic (OSCON)

Option Number GoodEagerly dump assets to the

web directory

Page 89: Assetic (OSCON)

A template is a configuration file

Page 90: Assetic (OSCON)

Formula Loadersextract asset formulae from templates

Page 91: Assetic (OSCON)

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

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

Page 92: Assetic (OSCON)

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

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

Page 93: Assetic (OSCON)

$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: Assetic (OSCON)

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

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

Page 95: Assetic (OSCON)

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

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

Whether to stat each file for changes

Page 96: Assetic (OSCON)

Twig Integration

Page 97: Assetic (OSCON)

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

Page 98: Assetic (OSCON)

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

Page 99: Assetic (OSCON)

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

Page 100: Assetic (OSCON)

{% javascripts 'js/*.coffee' filter='coffee' output='js/all.js' %}<script src="{{ asset_url }}"></script>{% endjavascripts %}

Page 101: Assetic (OSCON)

<script src="js/all.js"></script>

Page 102: Assetic (OSCON)

{% javascripts 'js/*.coffee' filter='coffee' output='js/all.js' %}<script src="{{ asset_url }}"></script>{% endjavascripts %}

Page 103: Assetic (OSCON)

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

Page 104: Assetic (OSCON)

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

Page 105: Assetic (OSCON)

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

Page 106: Assetic (OSCON)

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

Page 107: Assetic (OSCON)

<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 108: Assetic (OSCON)

AsseticBundleSymfony2 integration

Page 109: Assetic (OSCON)

Configuration

Page 110: Assetic (OSCON)

# config.yml

assetic: debug: %kernel.debug% use_controller: false filters: coffee: ~ yui_js: jar: /path/to/yuicompressor.jar

Page 111: Assetic (OSCON)

{# when use_controller=true (config_dev.yml) #}

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

Page 112: Assetic (OSCON)

# routing_dev.yml_assetic: resource: . type: assetic

Page 113: Assetic (OSCON)

{# when use_controller=false (config_prod.yml) #}

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

Page 114: Assetic (OSCON)

{# when use_controller=false (config_prod.yml) #}

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

Lots for free

Page 115: Assetic (OSCON)

The Symfony2 Assets Helper

• Multiple asset domains

• Cache buster

Page 116: Assetic (OSCON)

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 117: Assetic (OSCON)

{% stylesheets filter='scss,?yui_css' output='css/all.css' 'css/src/main.scss' 'css/src/more.scss' %}<link href="{{ asset_url }}" rel="stylesheet">{% endstylesheets %}

Page 118: Assetic (OSCON)

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

Page 119: Assetic (OSCON)

assetic:dump

Page 120: Assetic (OSCON)

$ php app/console assetic:dump web/

Page 121: Assetic (OSCON)

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

Page 122: Assetic (OSCON)

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

Page 123: Assetic (OSCON)

How can you help?

Page 124: Assetic (OSCON)

How can you help?

• Join the team!

Page 125: Assetic (OSCON)

How can you help?

• Join the team!

• Documentation

Page 126: Assetic (OSCON)

How can you help?

• Join the team!

• Documentation

• Assetic needs a website

Page 127: Assetic (OSCON)

Questions?http://github.com/kriswallsmith/assetic