Top Banner
Introducing Assetic Asset Management for PHP 5.3 Kris Wallsmith (OpenSky) July 29, 2011
127
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
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