Gerard Sychay php|works 2008 Atlanta, GA, USA Optimising The Front-End Using YSlow And PHP (In A Continuous Integration Environment)
Who am I? Gerard Sychay Technology Director Zipscene.com Cincinnati, OH, USA
Who am I? IRL Cool Web 2.0 Avatar
"In sampling the top ten U.S. websites, all but one spend less than 20% of the total response time getting the HTML document." - http://developer.yahoo.com/performance/rules.html Why Optimise Front-End?
Why Optimise Front-End?
Why Optimise Front-End?
Make fewer HTTP Requests Use a Content Delivery Network Add an Expires or a Cache-Control Header Gzip Components Put Stylesheets at the Top Put Scripts at the Bottom Avoid CSS Expressions Make JavaScript and CSS External Reduce DNS Lookups Minify JavaScript and CSS Avoid Redirects Remove Duplicate Scripts Configure ETags Make Ajax Cacheable Flush the Buffer Early Use GET for Ajax Requests Post-load Components 34 YSlow Rules Preload Components Reduce the Number of DOM Elements Split Components Across Domains Minimize the Number of iframes No 404s Reduce Cookie Size Use Cookie-free Domains for Components Minimize DOM Access Develop Smart Event Handlers Choose <link> over @import Avoid Filters Optimize Images Optimize CSS Sprites Don’t Scale Images in HTML Make favicon.ico Small and Cacheable Keep Components under 25K Pack Components into a Multipart Document
Make fewer HTTP Requests Use a Content Delivery Network Add an Expires or a Cache-Control Header Gzip Components Put Stylesheets at the Top Put Scripts at the Bottom Avoid CSS Expressions Make JavaScript and CSS External Reduce DNS Lookups Minify JavaScript and CSS Avoid Redirects Remove Duplicate Scripts Configure ETags Make Ajax Cacheable Flush the Buffer Early Use GET for Ajax Requests Post-load Components 34  8 YSlow Rules Preload Components Reduce the Number of DOM Elements Split Components Across Domains Minimize the Number of iframes No 404s Reduce Cookie Size Use Cookie-free Domains for Components Minimize DOM Access Develop Smart Event Handlers Choose <link> over @import Avoid Filters Optimize Images Optimize CSS Sprites Don’t Scale Images in HTML Make favicon.ico Small and Cacheable Keep Components under 25K Pack Components into a Multipart Document
Rule 1: Make Fewer HTTP Requests Instead of: <script type=&quot;text/javascript&quot; src=&quot;/javascript/foo.js&quot;></script> <script type=&quot;text/javascript&quot; src=&quot;/javascript/bar.js&quot;></script> Can we make it: <script type=&quot;text/javascript&quot; src=&quot;/javascript/foobar.js&quot;></script> Instead of: <link rel=“stylesheet” type=“text/css” href=“/css/foo.css” /> <link rel=“stylesheet” type=“text/css” href=“/css/bar.css” /> Can we make it: <link rel=“stylesheet” type=“text/css” href=“/css/foobar.css” /> What about images?
Rule 3: Add An Expires Header
Rule 3: Add An Expires Header
Rule 3: Add An Expires Header <Directory &quot;/var/www/html/css&quot;> ExpiresActive On ExpiresByType text/css &quot;access plus 1 year” </Directory>
Rule 3: Add An Expires Header
Rule 3: Add An Expires Header
So what if the file changes? <link ref=“stylesheet” type=“text/css” href=“main.css” /> <link ref=“stylesheet” type=“text/css” href=“main.css?1207641610” /> <link ref=“stylesheet” type=“text/css” href=“main-1207641610.css” /> Rule 3: Add An Expires Header
Rules 9,20,24: Domains
Rules 9,20,24: Domains &quot;Avoiding DNS lookups cuts response times, but reducing parallel downloads may increase response times. [A] guideline is to split these components across at least  two but no more than four hostnames .&quot;
http://static0.example.com http://static1.example.com ... Rules 9,20,24: Domains BONUS!  Optimise subdomains for static content Cookies HTTP Keep-Alive AllowOverride
Rules 10,29: Minify Javascript /*  Prototype JavaScript framework, version 1.5.1.1 *  (c) 2005-2007 Sam Stephenson * *  Prototype is freely distributable under the terms of an MIT-style license. *  For details, see the Prototype web site: http://www.prototypejs.org/ * /*--------------------------------------------------------------------------*/ var Prototype = { Version: '1.5.1.1', Browser: { IE:  !!(window.attachEvent && !window.opera), Opera:  !!window.opera, WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1, Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1 },... var Prototype={Version:&quot;1.5.1.1&quot;,Browser:{IE:!!(window.attachEvent&&!window.opera),Opera:!!window.opera, WebKit:navigator.userAgent.indexOf(&quot;AppleWebKit/&quot;)>-1,Gecko:navigator.userAgent.indexOf(&quot;Gecko&quot;)>-1&& navigator.userAgent.indexOf(&quot;KHTML&quot;)==-1},BrowserFeatures:{XPath:!!document.evaluate,ElementExtensions:!! window.HTMLElement,SpecificElementExtensions:(document.createElement(&quot;div&quot;).__proto__!==document.createElement(&quot;form&quot;).__proto__)}...
Javascript JSMin Dojo Shrinksafe, YUI Compressor Packer Rules 10,29: Minify
CSS Pretty much just whitespace and comments Rules 10,29: Minify
Images jpegtran optipng Rules 10,29: Minify
Rule 12: Remove Duplicate Scripts
Let's Find Out... Can We Do All These At Once?
First Attempt: Template Function <html> <head> <?php  insert_js ('/javascript/foo.js', '/javascript/bar.js'); ?> <?php  insert_css ('/css/foo.css', '/css/bar.css'); ?> </head> <body> <?php  insert_img ('/images/foo.jpg'); ?> ...
First Attempt: Template Function function insert_js($args) { static $alreadyLoaded = array(); $files = func_get_args(); // get array of javascript files $bundlefile = str_replace('/javascript/', '', join('', $files)); $bundlefile = str_replace('.js', '', $bundlefile); $buffer = ''; $latest = 0; foreach ($files as $file) { if (isset($alreadyLoaded[$file])) continue; else $alreadyLoaded[$file] = true; $mtime = filemtime(APP_ROOT . $file); if ($mtime > $latest) $latest = $mtime; // get the latest modification time $minified = YuiCompress::compress(APP_ROOT . $file); // minify content $buffer .= &quot;$minified\n&quot;; } $handle = fopen(APP_ROOT . &quot;/cache/$bundlefile-$latest.js&quot;, 'w'); // open bundle fwrite($handle, $buffer); // write minified content to bundle file fclose($handle); $twoBitValue = 0x3 & crc32(&quot;$bundlefile.js&quot;); // generate subdomain $path = &quot;http://static{$twoBitValue}.example.com/cache/$bundlefile-$latest.js&quot;;  echo &quot;<script type=\&quot;text/javascript\&quot; src=\&quot;$path\&quot;></script>&quot;; }  Combine Expires Subdomain Minify Remove duplicate
Instead of: <script type=“text/javascript” src=“/javascript/foo.js”></script> <script type=“text/javascript” src=“/javascript/bar.js”></script> We now have: <script type=“text/javascript” src=“http://static2.example.com/cache/foobar-1207762384.js”> </script> First Attempt: Template Function
Not bad, but… Multiple  stat()  calls What about CSS (e.g.  background-image: url(‘…’) )? AND IT MAKES THIS GUY SAD First Attempt: Template Function
PHP build system similar to  make Port of  ant  to PHP Second Attempt: Phing
CSS $pattern = ‘/(<link.*href=“[^”]*”.*\/>\s*)+/’; $buffer = preg_replace_callback($pattern,  “insert_css”, $buffer); Javascript $pattern = ‘/(<script.*src=“[^”]*”.”></script>s*)+/’; $buffer = preg_replace_callback($pattern,  “insert_js”, $buffer); Second Attempt: Phing
Images $pattern = ‘/src=“([^”]*[gif|jpg|png])”’; $buffer = preg_replace_callback($pattern,  “insert_img”, $buffer); $pattern = ‘/url\(‘?.*[gif|jpg|png]’?\)/”; $buffer = preg_replace_callback($pattern,  “insert_cssimg”, $buffer); Second Attempt: Phing
Automatic front-end optimisations Automatic unit testing with coverage Automatic syntax checking (with  `php -l` ) Automatic standards checking (with PHPCodeSniffer) Automatic deployment to integration Automatic deployment to any environment History of builds Beyond Phing: Continuous Integration
Summary Why the front-end is important 34  8 YSlow rules Template functions Continuous deployment Fin
Links 34 Rules for High Perfomance Websites: http://developer.yahoo.com/performance/rules.html YSlow homepage: http://developer.yahoo.com/yslow/ Cal Henderson Flickr article: http://www.thinkvitamin.com/features/webapps/serving-javascript-fast Sitepoint article: http://www.sitepoint.com/article/web-site-optimization-steps/ Fin
Thanks! Questions! Comments! [email_address] twitter.com/hellogerard straylightrun.net Fin
© 2008. Some rights reserved.

Front End Website Optimization

  • 1.
    Gerard Sychay php|works2008 Atlanta, GA, USA Optimising The Front-End Using YSlow And PHP (In A Continuous Integration Environment)
  • 2.
    Who am I?Gerard Sychay Technology Director Zipscene.com Cincinnati, OH, USA
  • 3.
    Who am I?IRL Cool Web 2.0 Avatar
  • 4.
    &quot;In sampling thetop ten U.S. websites, all but one spend less than 20% of the total response time getting the HTML document.&quot; - http://developer.yahoo.com/performance/rules.html Why Optimise Front-End?
  • 5.
  • 6.
  • 7.
    Make fewer HTTPRequests Use a Content Delivery Network Add an Expires or a Cache-Control Header Gzip Components Put Stylesheets at the Top Put Scripts at the Bottom Avoid CSS Expressions Make JavaScript and CSS External Reduce DNS Lookups Minify JavaScript and CSS Avoid Redirects Remove Duplicate Scripts Configure ETags Make Ajax Cacheable Flush the Buffer Early Use GET for Ajax Requests Post-load Components 34 YSlow Rules Preload Components Reduce the Number of DOM Elements Split Components Across Domains Minimize the Number of iframes No 404s Reduce Cookie Size Use Cookie-free Domains for Components Minimize DOM Access Develop Smart Event Handlers Choose <link> over @import Avoid Filters Optimize Images Optimize CSS Sprites Don’t Scale Images in HTML Make favicon.ico Small and Cacheable Keep Components under 25K Pack Components into a Multipart Document
  • 8.
    Make fewer HTTPRequests Use a Content Delivery Network Add an Expires or a Cache-Control Header Gzip Components Put Stylesheets at the Top Put Scripts at the Bottom Avoid CSS Expressions Make JavaScript and CSS External Reduce DNS Lookups Minify JavaScript and CSS Avoid Redirects Remove Duplicate Scripts Configure ETags Make Ajax Cacheable Flush the Buffer Early Use GET for Ajax Requests Post-load Components 34 8 YSlow Rules Preload Components Reduce the Number of DOM Elements Split Components Across Domains Minimize the Number of iframes No 404s Reduce Cookie Size Use Cookie-free Domains for Components Minimize DOM Access Develop Smart Event Handlers Choose <link> over @import Avoid Filters Optimize Images Optimize CSS Sprites Don’t Scale Images in HTML Make favicon.ico Small and Cacheable Keep Components under 25K Pack Components into a Multipart Document
  • 9.
    Rule 1: MakeFewer HTTP Requests Instead of: <script type=&quot;text/javascript&quot; src=&quot;/javascript/foo.js&quot;></script> <script type=&quot;text/javascript&quot; src=&quot;/javascript/bar.js&quot;></script> Can we make it: <script type=&quot;text/javascript&quot; src=&quot;/javascript/foobar.js&quot;></script> Instead of: <link rel=“stylesheet” type=“text/css” href=“/css/foo.css” /> <link rel=“stylesheet” type=“text/css” href=“/css/bar.css” /> Can we make it: <link rel=“stylesheet” type=“text/css” href=“/css/foobar.css” /> What about images?
  • 10.
    Rule 3: AddAn Expires Header
  • 11.
    Rule 3: AddAn Expires Header
  • 12.
    Rule 3: AddAn Expires Header <Directory &quot;/var/www/html/css&quot;> ExpiresActive On ExpiresByType text/css &quot;access plus 1 year” </Directory>
  • 13.
    Rule 3: AddAn Expires Header
  • 14.
    Rule 3: AddAn Expires Header
  • 15.
    So what ifthe file changes? <link ref=“stylesheet” type=“text/css” href=“main.css” /> <link ref=“stylesheet” type=“text/css” href=“main.css?1207641610” /> <link ref=“stylesheet” type=“text/css” href=“main-1207641610.css” /> Rule 3: Add An Expires Header
  • 16.
  • 17.
    Rules 9,20,24: Domains&quot;Avoiding DNS lookups cuts response times, but reducing parallel downloads may increase response times. [A] guideline is to split these components across at least two but no more than four hostnames .&quot;
  • 18.
    http://static0.example.com http://static1.example.com ...Rules 9,20,24: Domains BONUS! Optimise subdomains for static content Cookies HTTP Keep-Alive AllowOverride
  • 19.
    Rules 10,29: MinifyJavascript /* Prototype JavaScript framework, version 1.5.1.1 * (c) 2005-2007 Sam Stephenson * * Prototype is freely distributable under the terms of an MIT-style license. * For details, see the Prototype web site: http://www.prototypejs.org/ * /*--------------------------------------------------------------------------*/ var Prototype = { Version: '1.5.1.1', Browser: { IE: !!(window.attachEvent && !window.opera), Opera: !!window.opera, WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1, Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1 },... var Prototype={Version:&quot;1.5.1.1&quot;,Browser:{IE:!!(window.attachEvent&&!window.opera),Opera:!!window.opera, WebKit:navigator.userAgent.indexOf(&quot;AppleWebKit/&quot;)>-1,Gecko:navigator.userAgent.indexOf(&quot;Gecko&quot;)>-1&& navigator.userAgent.indexOf(&quot;KHTML&quot;)==-1},BrowserFeatures:{XPath:!!document.evaluate,ElementExtensions:!! window.HTMLElement,SpecificElementExtensions:(document.createElement(&quot;div&quot;).__proto__!==document.createElement(&quot;form&quot;).__proto__)}...
  • 20.
    Javascript JSMin DojoShrinksafe, YUI Compressor Packer Rules 10,29: Minify
  • 21.
    CSS Pretty muchjust whitespace and comments Rules 10,29: Minify
  • 22.
    Images jpegtran optipngRules 10,29: Minify
  • 23.
    Rule 12: RemoveDuplicate Scripts
  • 24.
    Let's Find Out...Can We Do All These At Once?
  • 25.
    First Attempt: TemplateFunction <html> <head> <?php insert_js ('/javascript/foo.js', '/javascript/bar.js'); ?> <?php insert_css ('/css/foo.css', '/css/bar.css'); ?> </head> <body> <?php insert_img ('/images/foo.jpg'); ?> ...
  • 26.
    First Attempt: TemplateFunction function insert_js($args) { static $alreadyLoaded = array(); $files = func_get_args(); // get array of javascript files $bundlefile = str_replace('/javascript/', '', join('', $files)); $bundlefile = str_replace('.js', '', $bundlefile); $buffer = ''; $latest = 0; foreach ($files as $file) { if (isset($alreadyLoaded[$file])) continue; else $alreadyLoaded[$file] = true; $mtime = filemtime(APP_ROOT . $file); if ($mtime > $latest) $latest = $mtime; // get the latest modification time $minified = YuiCompress::compress(APP_ROOT . $file); // minify content $buffer .= &quot;$minified\n&quot;; } $handle = fopen(APP_ROOT . &quot;/cache/$bundlefile-$latest.js&quot;, 'w'); // open bundle fwrite($handle, $buffer); // write minified content to bundle file fclose($handle); $twoBitValue = 0x3 & crc32(&quot;$bundlefile.js&quot;); // generate subdomain $path = &quot;http://static{$twoBitValue}.example.com/cache/$bundlefile-$latest.js&quot;; echo &quot;<script type=\&quot;text/javascript\&quot; src=\&quot;$path\&quot;></script>&quot;; } Combine Expires Subdomain Minify Remove duplicate
  • 27.
    Instead of: <scripttype=“text/javascript” src=“/javascript/foo.js”></script> <script type=“text/javascript” src=“/javascript/bar.js”></script> We now have: <script type=“text/javascript” src=“http://static2.example.com/cache/foobar-1207762384.js”> </script> First Attempt: Template Function
  • 28.
    Not bad, but…Multiple stat() calls What about CSS (e.g. background-image: url(‘…’) )? AND IT MAKES THIS GUY SAD First Attempt: Template Function
  • 29.
    PHP build systemsimilar to make Port of ant to PHP Second Attempt: Phing
  • 30.
    CSS $pattern =‘/(<link.*href=“[^”]*”.*\/>\s*)+/’; $buffer = preg_replace_callback($pattern, “insert_css”, $buffer); Javascript $pattern = ‘/(<script.*src=“[^”]*”.”></script>s*)+/’; $buffer = preg_replace_callback($pattern, “insert_js”, $buffer); Second Attempt: Phing
  • 31.
    Images $pattern =‘/src=“([^”]*[gif|jpg|png])”’; $buffer = preg_replace_callback($pattern, “insert_img”, $buffer); $pattern = ‘/url\(‘?.*[gif|jpg|png]’?\)/”; $buffer = preg_replace_callback($pattern, “insert_cssimg”, $buffer); Second Attempt: Phing
  • 32.
    Automatic front-end optimisationsAutomatic unit testing with coverage Automatic syntax checking (with `php -l` ) Automatic standards checking (with PHPCodeSniffer) Automatic deployment to integration Automatic deployment to any environment History of builds Beyond Phing: Continuous Integration
  • 33.
    Summary Why thefront-end is important 34 8 YSlow rules Template functions Continuous deployment Fin
  • 34.
    Links 34 Rulesfor High Perfomance Websites: http://developer.yahoo.com/performance/rules.html YSlow homepage: http://developer.yahoo.com/yslow/ Cal Henderson Flickr article: http://www.thinkvitamin.com/features/webapps/serving-javascript-fast Sitepoint article: http://www.sitepoint.com/article/web-site-optimization-steps/ Fin
  • 35.
    Thanks! Questions! Comments![email_address] twitter.com/hellogerard straylightrun.net Fin
  • 36.
    © 2008. Somerights reserved.