Assetic (Symfony Live Paris)

14,724 views

Published on

Presentation on Assetic at Symfony Live 2011 Paris.

Published in: Technology
0 Comments
23 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
14,724
On SlideShare
0
From Embeds
0
Number of Embeds
3,215
Actions
Shares
0
Downloads
193
Comments
0
Likes
23
Embeds 0
No embeds

No notes for slide

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 thatencourage best practices
  9. 9. Best practices like…• Dependency injection (DI)• Proper caching, edge side includes (ESI)• Test-driven development (TDD)• Dont repeat yourself (DRY)• Keep it simple, SVP (KISS)• Performance
  10. 10. If you haven’t optimized yourfrontend, 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•cismdescribes 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 Mergeheader(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 wireheader(Content-Type: text/javascript);echo $core->dump();
  29. 29. <script src="js/core.php"></script>
  30. 30. Assetic isAssets & 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. FilterAsset
  38. 38. FilterFilterAsset
  39. 39. Load Filter Filter Asset
  40. 40. FilterFilterAsset Dump
  41. 41. FilterFilterAsset
  42. 42. Asset CollectionFilter FilterFilter FilterAsset Asset
  43. 43. Asset Collection Filter Filter Asset Collection AssetFilter FilterFilter FilterAsset 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 impliedheader(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 isnt touched until nowheader(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 omissionin 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 contentecho $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 ThoughtAssets 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 IssueAssets defined in the view layermust actually exist somewhere
  89. 89. Option Number BadLazily dump assets to the web directory
  90. 90. Option Number GoodEagerly dump assets to the web directory
  91. 91. A template is a configuration file
  92. 92. Formula Loadersextract 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

×