Introducing Assetic: Asset Management for PHP 5.3

67,826
-1

Published on

The performance of your application depends heavily on the number and size of assets on each page. Even your blazingly fastest Symfony2 application can be bogged down by bloated Javascript and CSS files. This session will give you a basic introduction to PHP's new asset management framework, Assetic, and explore how it integrates with Symfony2 for a pleasant, common sense developer experience.

Published in: Technology
2 Comments
89 Likes
Statistics
Notes
No Downloads
Views
Total Views
67,826
On Slideshare
0
From Embeds
0
Number of Embeds
40
Actions
Shares
0
Downloads
462
Comments
2
Likes
89
Embeds 0
No embeds

No notes for slide

Introducing Assetic: Asset Management for PHP 5.3

  1. 1. Introducing Assetic Asset Management for PHP 5.3 Kris Wallsmith February 9, 2011
  2. 2. @kriswallsmith• Symfony core team member• Doctrine contributor• Symfony Guru at• 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. ShopOpenSky.com• PHP 5.3 + Symfony2• MongoDB + Doctrine MongoDB ODM• MySQL + Doctrine2 ORM• Less CSS• jQuery
  5. 5. Agenda• Strawman• The code• Twig Integration• Symfony2 Integration
  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• CoffeeScript • LESS• Compass Framework • Packer• CSSEmbed • SASS• Google Closure Compiler • Sprockets• JSMin • YUI Compressor
  16. 16. The ones written in PHP…
  17. 17. This is a difficult problem
  18. 18. Assetic makes it easy
  19. 19. Are you ready tokick some Assetic!?!
  20. 20. # /path/to/web/js/core.php$core = new FileAsset(/path/to/jquery.js);$core->load();header(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),));$core->load(); many files into one: fewer HTTP requests Mergeheader(Content-Type: text/javascript);echo $core->dump();
  22. 22. # /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();merged asset: less data over the wire Compress theheader(Content-Type: text/javascript);echo $core->dump();
  23. 23. <script src="js/core.php"></script>
  24. 24. Assetic isAssets & Filters
  25. 25. Inspired by Python’s webassets https://github.com/miracle2k/webassets
  26. 26. Assets have lazy, mutable content
  27. 27. Filters act on asset contents during “load” and “dump”
  28. 28. Assets can be gathered in collections
  29. 29. A collection is an asset
  30. 30. Load Filter Filter Asset Dump
  31. 31. Asset CollectionFilter FilterFilter FilterAsset Asset
  32. 32. Asset Collection Filter Filter Asset Collection AssetFilter FilterFilter FilterAsset Asset Filter Filter Asset
  33. 33. # /path/to/web/css/styles.php$styles = new AssetCollection( array(new FileAsset(/path/to/main.sass)), array(new SassFilter()));header(Content-Type: text/css);echo $styles->dump();
  34. 34. # /path/to/web/css/styles.php$styles = new AssetCollection(array( 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();
  35. 35. # /path/to/web/css/styles.php$styles = new AssetCollection(array( 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();
  36. 36. Basic Asset Classes• AssetCollection• AssetReference• FileAsset• GlobAsset• StringAsset
  37. 37. Core Filter Classes• CallablesFilter • SassSassFilter• CoffeeScriptFilter • SassScssFilter• CssRewriteFilter • SprocketsFilter• GoogleClosureCompilerApiFilter • YuiCssCompressorFilter• GoogleClosureCompilerJarFilter • YuiJsCompressorFilter• LessFilter • More to come…
  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. # /path/to/asset_factory.php$fm = new FilterManager();$fm->set(coffee, new CoffeeScriptFilter());$fm->set(closure, new GoogleClosureCompilerApi());$factory = new AssetFactory(/path/to/web);$factory->setAssetManager($am);$factory->setFilterManager($fm);
  47. 47. include /path/to/asset_factory.php;$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 omission in debug mode
  51. 51. // new AssetFactory(/path/to/web, true);include /path/to/asset_factory.php;$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);include /path/to/asset_factory.php;$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);include /path/to/asset_factory.php;$asset = $factory->createAsset( array(js/src/*.coffee), array(coffee, ?closure), array(debug => true));header(Content-Type: text/javascript);echo $asset->dump();
  54. 54. Factory Workers
  55. 55. Everything passes through the workers’ hands
  56. 56. $worker = new EnsureFilterWorker( /.css$/, // the output pattern $fm->get(yui_css), // the filter false // the debug mode);$factory = new AssetFactory(/path/to/web);$factory->addWorker($worker);// compressed$factory->createAsset(css/sass/*, sass, array( output => css,));
  57. 57. $worker = new EnsureFilterWorker( /.css$/, // the output pattern $fm->get(yui_css), // the filter false // the debug mode);$factory = new AssetFactory(/path/to/web);$factory->addWorker($worker);// uncompressed$factory->createAsset(css/sass/*, sass, array( output => css, debug => true,));
  58. 58. Not Lazy Enough?
  59. 59. Asset Formulae and the Lazy Asset Manager
  60. 60. $asset = $factory->createAsset( array(js/src/*.coffee), array(coffee, ?closure), array(output => js));
  61. 61. $formula = array( array(js/src/*.coffee), array(coffee, ?closure), array(output => js));
  62. 62. $am = new LazyAssetManager($factory);$am->setFormula(core_js, $formula);header(Content-Type: text/javascript);echo $am->get(core_js)->dump();
  63. 63. Good: Basic Caching
  64. 64. # /path/to/web/css/styles.php$styles = new AssetCollection( array(new FileAsset(/path/to/main.sass)), array(new SassFilter()));echo $styles->dump();
  65. 65. # /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();
  66. 66. Better: HTTP Caching
  67. 67. # /path/to/web/js/core.php$mtime = gmdate($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();
  68. 68. Best: Static Assets
  69. 69. # /path/to/scripts/dump_assets.php$am = new AssetManager();$am->set(foo, $foo);// etc...$writer = new AssetWriter(/path/to/web);$writer->writeManagerAssets($am);
  70. 70. # /path/to/scripts/dump_assets.php$am = new AssetManager();$am->set(foo, $foo);// etc...$writer = new AssetWriter(/path/to/web);foreach (array_slice($argv, 1) as $name) { $writer->writeAsset($am->get($name));}
  71. 71. Best-est:Content Distribution Network
  72. 72. new AssetWriter(s3://my-bucket) A CloudFront S3 bucket
  73. 73. Twig Integration
  74. 74. $twig->addExtension(new AsseticExtension($factory));
  75. 75. {% assetic js/*.coffee, filter=coffee %}<script src="{{ asset_url }}"></script>{% endassetic %}
  76. 76. <script src="assets/92429d8"></script>
  77. 77. {% assetic js/*.coffee, filter=coffee %}<script src="{{ asset_url }}"></script>{% endassetic %}
  78. 78. {% assetic js/*.coffee, filter=coffee, output=js %}<script src="{{ asset_url }}"></script>{% endassetic %}
  79. 79. <script src="js/92429d8.js"></script>
  80. 80. {% assetic js/*.coffee, filter=coffee, output=js %}<script src="{{ asset_url }}"></script>{% endassetic %}
  81. 81. {% assetic js/*.coffee, filter=coffee,?closure, output=js, name=core_js %}<script src="{{ asset_url }}"></script>{% endassetic %}
  82. 82. Formula LoaderUses the Twig parser to extract asset formulae from templates
  83. 83. $loader = new FormulaLoader($twig);// loop through your templates$formulae = array();foreach ($templates as $template) { $formulae += $loader->load($template);}$am = new LazyAssetManager($factory);$am->addFormulae($formulae);
  84. 84. if (!file_exists($cache = /path/to/formulae.php)) { $loader = new FormulaLoader($twig); // loop through your templates $formulae = array(); foreach ($templates as $template) { $formulae += $loader->load($template); } file_put_contents($cache, <?php return .var_export($formulae, true));}$am = new LazyAssetManager($factory);$am->addFormulae(require $cache);
  85. 85. AsseticBundle
  86. 86. {% 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 %}
  87. 87. <link href="css/all.css" rel="stylesheet" />
  88. 88. {% 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 %}
  89. 89. {% assetic filter=scss,?yui_css, output=css/all.css, @MainBundle/Resources/sass/main.scss, debug=true, @AnotherBundle/Resources/sass/more.scss %}<link href="{{ asset_url }}" rel="stylesheet" />{% endassetic %}
  90. 90. Each "leaf" asset is referenced individually<link href="css/all_part1.css" rel="stylesheet" /><link href="css/all_part2.css" rel="stylesheet" />
  91. 91. Configuration
  92. 92. assetic.config: debug: %kernel.debug% use_controller: %kernel.debug% document_root: %kernel.root_dir%/../web
  93. 93. {# when use_controller=true #}<script src="{{ path(route, { name: core_js }) }}"...
  94. 94. # routing_dev.yml_assetic: resource: . type: assetic
  95. 95. {# when use_controller=false #}<script src="{{ asset(js/core.js) }}"></script> Lots for free
  96. 96. The Symfony2 Assets Helper• Multiple asset domains• Cache buster
  97. 97. app.config: 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. {% 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 %}
  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 Register a stream wrapper in boot()
  103. 103. PHP templates Coming soon…
  104. 104. <?php foreach ($view[assetic]->urls( array(@MainBundle/Resources/sass/main.scss, @AnotherBundle/Resources/sass/more.scss), array(scss, ?yui_css), array(output => css/all.css)) as $url): ?> <link href="<?php echo $url ?>" rel="stylesheet" /><?php endforeach; ?>
  105. 105. Fork me!http://github.com/kriswallsmith/symfony-sandbox
  106. 106. What’s Next?• Finish Symfony2 helpers for PHP templates• Filter configuration• Image sprites, embedded image data• --watch commands• Client-aware optimizations?• Better CDN integration
  107. 107. Assetic is a killer feature of Symfony2…
  108. 108. …but is only one month old, so be nice :)
  109. 109. Questions?
  110. 110. Assetichttp://github.com/kriswallsmith/assetic

×