Successfully reported this slideshow.
Your SlideShare is downloading. ×

Assetic (Symfony Live Paris)

Ad

Introducing Assetic
  Asset Management for PHP 5.3




         March 4, 2011

Ad

@kriswallsmith

•   Symfony Guru at

•   Symfony core team member

•   Doctrine contributor

•   10+ years experience with...

Ad

OpenSky connects you with innovators,
trendsetters and tastemakers.You choose
   the ones you like and each week they
  in...

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Loading in …3
×

Check these out next

1 of 129 Ad
1 of 129 Ad

More Related Content

Assetic (Symfony Live Paris)

  1. 1. Introducing Assetic Asset Management for PHP 5.3 March 4, 2011
  2. 2. @kriswallsmith • Symfony Guru at • Symfony core team member • Doctrine contributor • 10+ years experience with PHP and web development • Open source evangelist and international speaker
  3. 3. 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.
  4. 4. 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.
  5. 5. ShopOpenSky.com • PHP 5.3 + Symfony2 • MongoDB + Doctrine MongoDB ODM • MySQL + Doctrine2 ORM • Less CSS • jQuery
  6. 6. Symfony2 is FAST
  7. 7. But you can still f*** that up
  8. 8. We build tools that encourage best practices
  9. 9. 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
  10. 10. If you haven’t optimized your frontend, you haven’t optimized
  11. 11. Get your assets in line.
  12. 12. A poorly optimized frontend can destroy UX
  13. 13. …and SEO! http://googlewebmastercentral.blogspot.com/2010/04/using-site-speed-in-web-search-ranking.html
  14. 14. Asset Management
  15. 15. Lots of awesome tools:
  16. 16. Lots of awesome tools: • CoffeeScript • Packer • Compass Framework • SASS • CSSEmbed • Sprockets • Google Closure Compiler • Stylus • JSMin • YUI Compressor • LESS
  17. 17. The ones written in PHP…
  18. 18. The ones written in PHP…
  19. 19. This is a difficult problem
  20. 20. Assetic makes it easy
  21. 21. as•cet•i•cism describes a lifestyle characterized by abstinence from various sorts of worldly pleasures often with the aim of pursuing religious and spiritual goals
  22. 22. No B.S.
  23. 23. Enough talk
  24. 24. # /path/to/web/js/core.php $core = new FileAsset('/path/to/jquery.js'); $core->load(); header('Content-Type: text/javascript'); echo $core->dump();
  25. 25. # /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();
  26. 26. # /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();many files into one == fewer HTTP requests Merge header('Content-Type: text/javascript'); echo $core->dump();
  27. 27. # /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();
  28. 28. # /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(); Compress the merged asset == less data over the wire header('Content-Type: text/javascript'); echo $core->dump();
  29. 29. <script src="js/core.php"></script>
  30. 30. Assetic is Assets & Filters
  31. 31. Inspired by Python’s webassets https://github.com/miracle2k/webassets
  32. 32. Assets have lazy, mutable content
  33. 33. A filter acts on an asset’s contents during “load” and “dump”
  34. 34. Assets can be gathered in collections
  35. 35. A collection is an asset
  36. 36. Asset
  37. 37. Filter Asset
  38. 38. Filter Filter Asset
  39. 39. Load Filter Filter Asset
  40. 40. Filter Filter Asset Dump
  41. 41. Filter Filter Asset
  42. 42. Asset Collection Filter Filter Filter Filter Asset Asset
  43. 43. Asset Collection Filter Filter Asset Collection Asset Filter Filter Filter Filter Asset Asset Filter Filter Asset
  44. 44. # /path/to/web/css/styles.php $styles = new FileAsset('/path/to/main.sass', array( new SassFilter(), )); header('Content-Type: text/css'); echo $styles->dump();
  45. 45. # /path/to/web/css/styles.php $styles = new FileAsset('/path/to/main.sass', array( new SassFilter(), )); Load is implied header('Content-Type: text/css'); echo $styles->dump();
  46. 46. # /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();
  47. 47. # /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();
  48. 48. # /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'), )); Lazy! The filesystem isn't touched until now header('Content-Type: text/css'); echo $styles->dump();
  49. 49. Basic Asset Classes • AssetCollection • AssetReference • FileAsset • GlobAsset • StringAsset
  50. 50. Core Filter Classes • CallablesFilter • SassScssFilter • CoffeeScriptFilter • SprocketsFilter • CssRewriteFilter • StylusFilter • GoogleClosureCompilerApiFilter • YuiCssCompressorFilter • GoogleClosureCompilerJarFilter • YuiJsCompressorFilter • LessFilter • More to come… • SassSassFilter
  51. 51. Asset Manager
  52. 52. $am = new AssetManager(); $am->set('jquery', new FileAsset('/path/to/jquery.js'));
  53. 53. $plugin = new AssetCollection(array( new AssetReference($am, 'jquery'), new FileAsset('/path/to/jquery.plugin.js'), ));
  54. 54. $core = new AssetCollection(array( $jquery, $plugin1, $plugin2, )); header('text/javascript'); echo $core->dump();
  55. 55. jQuery will only be included once $core = new AssetCollection(array( $jquery, $plugin1, $plugin2, )); header('text/javascript'); echo $core->dump();
  56. 56. Filter Manager
  57. 57. $yui = new YuiCompressorJs(); $yui->setNomunge(true); $fm = new FilterManager(); $fm->set('yui_js', $yui);
  58. 58. $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'));
  59. 59. jQuery will only be compressed once $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'));
  60. 60. Asset Factory
  61. 61. $fm = new FilterManager(); $fm->set('coffee', new CoffeeScriptFilter()); $fm->set('closure', new ClosureFilter()); $factory = new AssetFactory('/path/to/web'); $factory->setFilterManager($fm);
  62. 62. $asset = $factory->createAsset( array('js/src/*.coffee'), array('coffee', 'closure') ); header('Content-Type: text/javascript'); echo $asset->dump();
  63. 63. Debug Mode
  64. 64. Debugging compressed Javascript sucks
  65. 65. Mark filters for omission in debug mode using a “?”
  66. 66. // 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();
  67. 67. // new AssetFactory('/path/to/web', true); $asset = $factory->createAsset( array('js/src/*.coffee'), array('coffee', '?closure') ); header('Content-Type: text/javascript'); echo $asset->dump();
  68. 68. // 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();
  69. 69. Good: Basic Caching
  70. 70. # /path/to/web/css/styles.php $styles = new AssetCollection( array(new FileAsset('/path/to/main.sass')), array(new SassFilter()) ); echo $styles->dump();
  71. 71. # /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();
  72. 72. # /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')); Run the filters once and cache the content echo $styles->dump();
  73. 73. Better: HTTP Caching
  74. 74. // $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();
  75. 75. Best: Static Assets
  76. 76. # /path/to/scripts/dump_assets.php $am = new AssetManager(); $am->set('foo', $foo); // etc... $writer = new AssetWriter('/path/to/web'); $writer->writeManagerAssets($am);
  77. 77. Best-est: Content Distribution Network
  78. 78. new AssetWriter('s3://my-bucket')
  79. 79. new AssetWriter('s3://my-bucket') A CloudFront S3 bucket
  80. 80. Custom Stream Wrappers $s3 = new Zend_Service_Amazon_S3($key, $secret); $s3->registerStreamWrapper();
  81. 81. Not Lazy Enough?
  82. 82. Asset Formulae and the Lazy Asset Manager
  83. 83. $asset = $factory->createAsset( array('js/src/*.coffee'), array('coffee', '?closure'), array('output' => 'js/*.js') );
  84. 84. $formula = array( array('js/src/*.coffee'), array('coffee', '?closure'), array('output' => 'js/*.js') );
  85. 85. $am = new LazyAssetManager($factory); $am->setFormula('core_js', $formula); header('Content-Type: text/javascript'); echo $am->get('core_js')->dump();
  86. 86. A Thought Assets are a part of the view layer and should be defined there.
  87. 87. <!-- 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; ?>
  88. 88. An Issue Assets defined in the view layer must actually exist somewhere
  89. 89. Option Number Bad Lazily dump assets to the web directory
  90. 90. Option Number Good Eagerly dump assets to the web directory
  91. 91. A template is a configuration file
  92. 92. Formula Loaders extract asset formulae from templates
  93. 93. $loader = new FunctionCallsFormulaLoader(); $resource = new DirectoryResource( '/path/to/templates', '/.php$/' ); $formulae = $loader->load($resource);
  94. 94. $am = new LazyAssetManager($factory); $am->setLoader('php', $loader); $am->addResource($resource, 'php'); $writer = new AssetWriter('/path/to/web'); $writer->writeManagerAssets($am);
  95. 95. $am = new LazyAssetManager($factory); $am->setLoader('php', $loader); $am->addResource($resource, 'php'); Expensive every time $writer = new AssetWriter('/path/to/web'); $writer->writeManagerAssets($am);
  96. 96. $cache = new ConfigCache('/path/to/cache'); $loader = new CachedFormulaLoader( $loader, $cache, $debug );
  97. 97. $cache = new ConfigCache('/path/to/cache'); $loader = new CachedFormulaLoader( $loader, $cache, $debug ); Whether to stat each file for changes
  98. 98. Twig Integration
  99. 99. $twig->addExtension(new AsseticExtension($factory));
  100. 100. {% assetic 'js/*.coffee' filter='coffee' %} <script src="{{ asset_url }}"></script> {% endassetic %}
  101. 101. <script src="assets/92429d8"></script>
  102. 102. {% assetic 'js/*.coffee' filter='coffee' %} <script src="{{ asset_url }}"></script> {% endassetic %}
  103. 103. {% assetic 'js/*.coffee' filter='coffee' output='js/*.js' %} <script src="{{ asset_url }}"></script> {% endassetic %}
  104. 104. <script src="js/92429d8.js"></script>
  105. 105. {% assetic 'js/*.coffee' filter='coffee' output='js/*.js' %} <script src="{{ asset_url }}"></script> {% endassetic %}
  106. 106. {% javascripts 'js/*.coffee' filter='coffee,?closure' %} <script src="{{ asset_url }}"></script> {% endjavascripts %}
  107. 107. {% javascripts 'js/*.coffee' filter='coffee,?closure' %} <script src="{{ asset_url }}"></script> {% endjavascripts %} Adds a default output string
  108. 108. {% javascripts 'js/*.coffee' filter='coffee,?closure' debug=true %} <script src="{{ asset_url }}"></script> {% endjavascripts %}
  109. 109. <script src="js/92429d8_1.js"></script> <script src="js/92429d8_2.js"></script> <script src="js/92429d8_3.js"></script>
  110. 110. Each "leaf" asset is referenced individually <script src="js/92429d8_1.js"></script> <script src="js/92429d8_2.js"></script> <script src="js/92429d8_3.js"></script>
  111. 111. AsseticBundle Symfony2 integration
  112. 112. {% 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 %}
  113. 113. <link href="css/all.css" rel="stylesheet" />
  114. 114. Configuration
  115. 115. assetic: debug: %kernel.debug% use_controller: %kernel.debug% read_from: %kernel.root_dir%/../web write_to: s3://mybucket
  116. 116. {# when use_controller=true #} <script src="{{ path('assetic_foo') }}"...
  117. 117. # routing_dev.yml _assetic: resource: . type: assetic
  118. 118. {# when use_controller=false #} <script src="{{ asset('js/core.js') }}"></script>
  119. 119. {# when use_controller=false #} <script src="{{ asset('js/core.js') }}"></script> Lots for free
  120. 120. The Symfony2 Assets Helper • Multiple asset domains • Cache buster
  121. 121. 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
  122. 122. {% 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 %}
  123. 123. <link href="http://assets3.domain.com/css/all.css?1.2.3" ...
  124. 124. assetic:dump
  125. 125. $ php app/console assetic:dump web/
  126. 126. $ php app/console assetic:dump s3://my-bucket
  127. 127. assetic:dump --watch Dump static assets in the background as you develop
  128. 128. Questions? http://github.com/kriswallsmith/assetic

×