Assetic (Zendcon)

2,507
-1

Published on

My presentation of Assetic at Zendcon 2011.

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

No Downloads
Views
Total Views
2,507
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
31
Comments
0
Likes
4
Embeds 0
No embeds

No notes for slide

Assetic (Zendcon)

  1. 1. Introducing Assetic Asset Management for PHP 5.3 October 20, 2011
  2. 2. @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
  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.com• PHP 5.3 + Symfony2• MongoDB + Doctrine MongoDB ODM• MySQL + Doctrine2 ORM• Less CSS• jQuery
  5. 5. We all want to be FAST
  6. 6. We use good open source tools that encourage best practices
  7. 7. 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
  8. 8. If you haven’t optimized yourfrontend, you haven’t optimized
  9. 9. Get your assets in line.
  10. 10. A poorly optimized frontend can destroy UX
  11. 11. …and SEO!http://googlewebmastercentral.blogspot.com/2010/04/using-site-speed-in-web-search-ranking.html
  12. 12. Asset Management
  13. 13. Lots of awesome tools:• CoffeeScript • OptiPNG• Compass Framework • Packager• CSSEmbed • SASS & SCSS• Google Closure Compiler • Sprockets• jpegoptim • Stylus• JSMin • YUI Compressor• LESS
  14. 14. Integrating these tools cleanly is a difficult problem
  15. 15. Assetic makes it easy
  16. 16. 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
  17. 17. No B.S.
  18. 18. Enough talk
  19. 19. # /path/to/web/js/core.php$core = new FileAsset(/path/to/jquery.js);$core->load();header(Content-Type: text/javascript);echo $core->dump();
  20. 20. # /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();
  21. 21. # /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();
  22. 22. <script src="js/core.php"></script>
  23. 23. Assetic isAssets & Filters
  24. 24. Inspired by Python’s webassets https://github.com/miracle2k/webassets
  25. 25. Assets have lazy, mutable content
  26. 26. A filter acts on an asset’s contents during “load” and “dump”
  27. 27. Assets can be gathered in collections
  28. 28. A collection is an asset
  29. 29. Load Filter Filter Asset Dump
  30. 30. Asset CollectionFilter FilterFilter FilterAsset Asset
  31. 31. Asset Collection Filter Filter Asset Collection AssetFilter FilterFilter FilterAsset Asset Filter Filter Asset
  32. 32. # /path/to/web/css/styles.php$styles = new FileAsset(/path/to/main.sass, array( new SassFilter(),));header(Content-Type: text/css);echo $styles->dump();
  33. 33. # /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();
  34. 34. # /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();
  35. 35. Basic Asset Classes• AssetCollection• AssetReference• FileAsset• GlobAsset• HttpAsset• StringAsset
  36. 36. Core Filter Classes• CoffeeScriptFilter • GoogleClosureCompilerJarFilter• CompassFilter • JpegoptimFilter• CssEmbedFilter • JpegtranFilter• CssImportFilter • LessFilter• CssMinFilter • LessphpFilter• CssRewriteFilter • OptiPngFilter• GoogleClosureCompilerApiFilter • PackagerFilter
  37. 37. Core Filter Classes• PngoutFilter • More to come…• SassSassFilter• SassScssFilter• SprocketsFilter• StylusFilter• YuiCssCompressorFilter• YuiJsCompressorFilter
  38. 38. Asset Manager
  39. 39. $am = new AssetManager();$am->set(jquery, new FileAsset(/path/to/jquery.js));
  40. 40. $plugin = new AssetCollection(array( new AssetReference($am, jquery), new FileAsset(/path/to/jquery.plugin.js),));
  41. 41. jQuery will only be included once $core = new AssetCollection(array( $jquery, $plugin1, $plugin2, )); header(text/javascript); echo $core->dump();
  42. 42. Filter Manager
  43. 43. $yui = new YuiCompressorJs();$yui->setNomunge(true);$fm = new FilterManager();$fm->set(yui_js, $yui);
  44. 44. 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));
  45. 45. Asset Factory
  46. 46. $fm = new FilterManager();$fm->set(coffee, new CoffeeScriptFilter());$fm->set(closure, new ClosureFilter());$factory = new AssetFactory(/path/to/web);$factory->setFilterManager($fm);
  47. 47. $asset = $factory->createAsset( array(js/src/*.coffee), array(coffee, closure));header(Content-Type: text/javascript);echo $asset->dump();
  48. 48. Debug Mode
  49. 49. Debugging compressed Javascript sucks
  50. 50. Mark filters for omissionin debug mode using a “?”
  51. 51. // 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();
  52. 52. // new AssetFactory(/path/to/web, true);$asset = $factory->createAsset( array(js/src/*.coffee), array(coffee, ?closure));header(Content-Type: text/javascript);echo $asset->dump();
  53. 53. // 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();
  54. 54. Good: Basic Caching
  55. 55. # /path/to/web/css/styles.php$styles = new AssetCollection( array(new FileAsset(/path/to/main.sass)), array(new SassFilter()));echo $styles->dump();
  56. 56. # /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();
  57. 57. Better: HTTP Caching
  58. 58. // $core = new AssetCache(...$mtime = gmdate(D, d M y H:i:s GMT, $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();
  59. 59. Best: Static Assets
  60. 60. # /path/to/deploy/scripts/dump_assets.php$am = new AssetManager();$am->set(foo, $foo);// etc...$writer = new AssetWriter(/path/to/web);$writer->writeManagerAssets($am);
  61. 61. Best-est:Content Distribution Network
  62. 62. new AssetWriter(s3://my-bucket) A CloudFront S3 bucket
  63. 63. Custom Stream Wrappers$s3 = new Zend_Service_Amazon_S3($key, $secret);$s3->registerStreamWrapper();
  64. 64. Not Lazy Enough?
  65. 65. Asset Formulae and the Lazy Asset Manager
  66. 66. $asset = $factory->createAsset( array(js/src/*.coffee), array(coffee, ?closure), array(output => js/all.js));
  67. 67. $formula = array( array(js/src/*.coffee), array(coffee, ?closure), array(output => js/all.js));
  68. 68. $am = new LazyAssetManager($factory);$am->setFormula(all_js, $formula);header(Content-Type: text/javascript);echo $am->get(all_js)->dump();
  69. 69. A ThoughtAssets are a part of the view layer and should be defined there.
  70. 70. <!-- 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; ?>
  71. 71. An IssueAssets defined in the view layermust actually exist somewhere
  72. 72. Option Number BadLazily dump assets to the web directory
  73. 73. Option Number GoodEagerly dump assets to the web directory
  74. 74. A template is a configuration file
  75. 75. Formula Loadersextract asset formulae from templates
  76. 76. $loader = new FunctionCallsFormulaLoader();$resource = new DirectoryResource( /path/to/templates, /.php$/);$formulae = $loader->load($resource);
  77. 77. $am = new LazyAssetManager($factory);$am->setLoader(php, $loader);$am->addResource($resource, php); Expensive every time$writer = new AssetWriter(/path/to/web);$writer->writeManagerAssets($am);
  78. 78. $cache = new ConfigCache(/path/to/cache);$loader = new CachedFormulaLoader( $loader, $cache, $debug); Whether to stat each file for changes
  79. 79. Twig Integration
  80. 80. {% javascripts js/*.coffee filter=coffee %}<script src="{{ asset_url }}"></script>{% endjavascripts %}
  81. 81. <script src="js/92429d8.js"></script>
  82. 82. {% javascripts js/*.coffee filter=coffee %}<script src="{{ asset_url }}"></script>{% endjavascripts %}
  83. 83. {% javascripts js/*.coffee filter=coffee output=js/all.js %}<script src="{{ asset_url }}"></script>{% endjavascripts %}
  84. 84. <script src="js/all.js"></script>
  85. 85. {% javascripts js/*.coffee filter=coffee output=js/all.js %}<script src="{{ asset_url }}"></script>{% endjavascripts %}
  86. 86. {% javascripts js/*.coffee filter=coffee,?closure %}<script src="{{ asset_url }}"></script>{% endjavascripts %}
  87. 87. {% javascripts js/*.coffee filter=coffee,?closure debug=true %}<script src="{{ asset_url }}"></script>{% endjavascripts %}
  88. 88. {% javascripts js/*.coffee filter=coffee,?closure combine=false %}<script src="{{ asset_url }}"></script>{% endjavascripts %}
  89. 89. 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>
  90. 90. AsseticBundle Symfony2 integration
  91. 91. Configuration
  92. 92. # config.ymlassetic: debug: %kernel.debug% use_controller: false filters: coffee: ~ yui_js: jar: /path/to/yuicompressor.jar
  93. 93. {# when use_controller=true (config_dev.yml) #}<script src="{{ path(assetic_foo) }}"...
  94. 94. # routing_dev.yml_assetic: resource: . type: assetic
  95. 95. {# when use_controller=false (config_prod.yml) #}<script src="{{ asset(js/core.js) }}"></script> Lots for free
  96. 96. The Symfony2 Assets Helper• Multiple asset domains• Cache buster
  97. 97. 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
  98. 98. {% stylesheets filter=scss,?yui_css output=css/all.css css/src/main.scss css/src/more.scss %}<link href="{{ asset_url }}" rel="stylesheet">{% endstylesheets %}
  99. 99. <link href="http://assets3.domain.com/css/all.css?1.2.3" ...
  100. 100. assetic:dump
  101. 101. $ php app/console assetic:dump web/
  102. 102. $ php app/console assetic:dump s3://my-bucket
  103. 103. assetic:dump --watch Dump static assets in the background as you develop
  104. 104. Roadmap• 1.1 - Asset dependencies• 1.2 - Dynamic assets
  105. 105. How can you help?• Fork me, join the team• Documentation• Assetic needs a website
  106. 106. OpenSky is hiring• Systems• Dev-ops• Help desk• Java architect
  107. 107. Questions?http://github.com/kriswallsmith/assetic
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×