assetic (zendcon)

107
Introducing Assetic Asset Management for PHP 5.3 October 20, 2011

Upload: kris-wallsmith

Post on 15-Jan-2015

2.166 views

Category:

Technology


0 download

DESCRIPTION

My presentation of Assetic at Zendcon 2011.

TRANSCRIPT

Page 1: Assetic (Zendcon)

Introducing AsseticAsset Management for PHP 5.3

October 20, 2011

Page 2: Assetic (Zendcon)

@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 (Zendcon)

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

opensky.com

• PHP 5.3 + Symfony2

• MongoDB + Doctrine MongoDB ODM

• MySQL + Doctrine2 ORM

• Less CSS

• jQuery

Page 5: Assetic (Zendcon)

We all want to be FAST

Page 6: Assetic (Zendcon)

We use good open source tools that encourage best practices

Page 7: Assetic (Zendcon)

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 8: Assetic (Zendcon)

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

Page 9: Assetic (Zendcon)

Get your assets in line.

Page 10: Assetic (Zendcon)

A poorly optimized frontendcan destroy UX

Page 11: Assetic (Zendcon)

…and SEO!

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

Page 12: Assetic (Zendcon)

Asset Management

Page 13: Assetic (Zendcon)

Lots of awesome tools:• CoffeeScript

• Compass Framework

• CSSEmbed

• Google Closure Compiler

• jpegoptim

• JSMin

• LESS

• OptiPNG

• Packager

• SASS & SCSS

• Sprockets

• Stylus

• YUI Compressor

Page 14: Assetic (Zendcon)

Integrating these tools cleanlyis a difficult problem

Page 15: Assetic (Zendcon)

Assetic makes it easy

Page 16: Assetic (Zendcon)

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 17: Assetic (Zendcon)

No B.S.

Page 18: Assetic (Zendcon)

Enough talk

Page 19: Assetic (Zendcon)

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

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

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

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

Page 23: Assetic (Zendcon)

Assetic isAssets & Filters

Page 24: Assetic (Zendcon)

Inspired by Python’s webassets

https://github.com/miracle2k/webassets

Page 25: Assetic (Zendcon)

Assets have lazy, mutable content

Page 26: Assetic (Zendcon)

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

Page 27: Assetic (Zendcon)

Assets can be gathered in collections

Page 28: Assetic (Zendcon)

A collection is an asset

Page 29: Assetic (Zendcon)

FilterFilter

Asset

FilterFilter

Asset

Load

Dum

p

Page 30: Assetic (Zendcon)

Asset Collection

FilterFilter

Asset

FilterFilter

Asset

Page 31: Assetic (Zendcon)

Asset Collection

Asset Collection

FilterFilter

Asset

FilterFilter

Asset

FilterFilter

Asset

FilterFilter

Asset

Page 32: Assetic (Zendcon)

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

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

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

Basic Asset Classes

• AssetCollection

• AssetReference

• FileAsset

• GlobAsset

• HttpAsset

• StringAsset

Page 36: Assetic (Zendcon)

Core Filter Classes• CoffeeScriptFilter

• CompassFilter

• CssEmbedFilter

• CssImportFilter

• CssMinFilter

• CssRewriteFilter

• GoogleClosure\CompilerApiFilter

• GoogleClosure\CompilerJarFilter

• JpegoptimFilter

• JpegtranFilter

• LessFilter

• LessphpFilter

• OptiPngFilter

• PackagerFilter

Page 37: Assetic (Zendcon)

Core Filter Classes• PngoutFilter

• Sass\SassFilter

• Sass\ScssFilter

• SprocketsFilter

• StylusFilter

• Yui\CssCompressorFilter

• Yui\JsCompressorFilter

• More to come…

Page 38: Assetic (Zendcon)

Asset Manager

Page 39: Assetic (Zendcon)

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

Page 40: Assetic (Zendcon)

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

Page 41: Assetic (Zendcon)

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

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

jQuery will only be included once

Page 42: Assetic (Zendcon)

Filter Manager

Page 43: Assetic (Zendcon)

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

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

Page 44: Assetic (Zendcon)

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

Asset Factory

Page 46: Assetic (Zendcon)

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

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

Page 47: Assetic (Zendcon)

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

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

Page 48: Assetic (Zendcon)

Debug Mode

Page 49: Assetic (Zendcon)

Debugging compressedJavascript sucks

Page 50: Assetic (Zendcon)

Mark filters for omissionin debug mode using a “?”

Page 51: Assetic (Zendcon)

// 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 52: Assetic (Zendcon)

// 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 53: Assetic (Zendcon)

// 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 54: Assetic (Zendcon)

Good: Basic Caching

Page 55: Assetic (Zendcon)

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

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

echo $styles->dump();

Page 56: Assetic (Zendcon)

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

Better: HTTP Caching

Page 58: Assetic (Zendcon)

// $core = new AssetCache(...

$mtime = gmdate('D, d M y H:i:s \\G\\M\\T', $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 59: Assetic (Zendcon)

Best: Static Assets

Page 60: Assetic (Zendcon)

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

Best-est:Content Distribution Network

Page 62: Assetic (Zendcon)

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

A CloudFront S3 bucket

Page 63: Assetic (Zendcon)

Custom Stream Wrappers

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

Page 64: Assetic (Zendcon)

Not Lazy Enough?

Page 65: Assetic (Zendcon)

Asset Formulae and theLazy Asset Manager

Page 66: Assetic (Zendcon)

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

Page 67: Assetic (Zendcon)

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

Page 68: Assetic (Zendcon)

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

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

Page 69: Assetic (Zendcon)

A ThoughtAssets are a part of the view layer

and should be defined there.

Page 70: Assetic (Zendcon)

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

An IssueAssets defined in the view layer must actually exist somewhere

Page 72: Assetic (Zendcon)

Option Number BadLazily dump assets to the

web directory

Page 73: Assetic (Zendcon)

Option Number GoodEagerly dump assets to the

web directory

Page 74: Assetic (Zendcon)

A template is a configuration file

Page 75: Assetic (Zendcon)

Formula Loadersextract asset formulae from templates

Page 76: Assetic (Zendcon)

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

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

Page 77: Assetic (Zendcon)

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

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

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

Whether to stat each file for changes

Page 79: Assetic (Zendcon)

Twig Integration

Page 80: Assetic (Zendcon)

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

Page 81: Assetic (Zendcon)

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

Page 82: Assetic (Zendcon)

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

Page 83: Assetic (Zendcon)

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

Page 84: Assetic (Zendcon)

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

Page 85: Assetic (Zendcon)

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

Page 86: Assetic (Zendcon)

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

Page 87: Assetic (Zendcon)

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

Page 88: Assetic (Zendcon)

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

Page 89: Assetic (Zendcon)

<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 90: Assetic (Zendcon)

AsseticBundleSymfony2 integration

Page 91: Assetic (Zendcon)

Configuration

Page 92: Assetic (Zendcon)

# config.yml

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

Page 93: Assetic (Zendcon)

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

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

Page 94: Assetic (Zendcon)

# routing_dev.yml_assetic: resource: . type: assetic

Page 95: Assetic (Zendcon)

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

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

Lots for free

Page 96: Assetic (Zendcon)

The Symfony2 Assets Helper

• Multiple asset domains

• Cache buster

Page 97: Assetic (Zendcon)

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 98: Assetic (Zendcon)

{% 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 99: Assetic (Zendcon)

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

Page 100: Assetic (Zendcon)

assetic:dump

Page 101: Assetic (Zendcon)

$ php app/console assetic:dump web/

Page 102: Assetic (Zendcon)

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

Page 103: Assetic (Zendcon)

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

Page 104: Assetic (Zendcon)

Roadmap

• 1.1 - Asset dependencies

• 1.2 - Dynamic assets

Page 105: Assetic (Zendcon)

How can you help?

• Fork me, join the team

• Documentation

• Assetic needs a website

Page 106: Assetic (Zendcon)

OpenSky is hiring

• Systems

• Dev-ops

• Help desk

• Java architect

Page 107: Assetic (Zendcon)

Questions?

http://github.com/kriswallsmith/assetic