From fad271f98fd1004c5898b5259b983cb21845c3a7 Mon Sep 17 00:00:00 2001 From: Steven Fritzsche Date: Sun, 15 Mar 2015 23:56:25 +0100 Subject: [PATCH] [Init] --- .gitignore | 1 + README.md | 76 ++++++++++++++++ .../community/Pagespeed/Css/Helper/Data.php | 13 +++ .../Pagespeed/Css/Model/Observer.php | 90 +++++++++++++++++++ .../community/Pagespeed/Css/etc/config.xml | 51 +++++++++++ .../community/Pagespeed/Js/Helper/Data.php | 13 +++ .../community/Pagespeed/Js/Model/Observer.php | 77 ++++++++++++++++ .../community/Pagespeed/Js/etc/config.xml | 51 +++++++++++ app/etc/modules/Pagespeed_Css.xml | 17 ++++ app/etc/modules/Pagespeed_Js.xml | 17 ++++ 10 files changed, 406 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 app/code/community/Pagespeed/Css/Helper/Data.php create mode 100644 app/code/community/Pagespeed/Css/Model/Observer.php create mode 100644 app/code/community/Pagespeed/Css/etc/config.xml create mode 100644 app/code/community/Pagespeed/Js/Helper/Data.php create mode 100644 app/code/community/Pagespeed/Js/Model/Observer.php create mode 100644 app/code/community/Pagespeed/Js/etc/config.xml create mode 100644 app/etc/modules/Pagespeed_Css.xml create mode 100644 app/etc/modules/Pagespeed_Js.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a09c56d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/.idea diff --git a/README.md b/README.md new file mode 100644 index 0000000..5ba627a --- /dev/null +++ b/README.md @@ -0,0 +1,76 @@ +Magento Google Pagespeed Optimization Extension +=============================================== + +This extension should help, to fulfill the requirements of the tool [Google PageSpeed Insights](https://developers.google.com/speed/pagespeed/insights/). + +Current features +---------------- + +1. Move every Javascript tag (head & inline) to the bottom. ({stripped_html}{js}) + * including conditional js units () + * including external js tags + * including "inline" js tags +2. Move every CSS (head & inline) to the bottom. ({stripped_html}{css}) + * including conditional css units () + * including external css tags + * including inline css tags + +How it works ? +------------ + +Simple parse the final html stream on the event "controller_front_send_response_before". + +What about performance/paring time ? +------------------------------ + +On our local hardware the html parsing requires a maximum of 8 milliseconds. + + +Requirements from PageSpeed Insights and planned Features +--------------------------------------------------------- + +1. ~~[Eliminate render-blocking JavaScript and CSS in above-the-fold content](https://developers.google.com/speed/docs/insights/BlockingJS)~~ (feature 1 & 2) +2. [Prioritize visible content](https://developers.google.com/speed/docs/insights/PrioritizeVisibleContent) + +Requirements from PageSpeed Insights which are covered by 3rd party extensions +------------------------------------------------------------------------------ + +1. [Minify CSS](https://developers.google.com/speed/docs/insights/MinifyResources) +2. [Minify JavaScript](https://developers.google.com/speed/docs/insights/MinifyResources) + * Both are covered by [Speedster Advanced by Fooman](http://www.magentocommerce.com/magento-connect/speedster-advanced-by-fooman.html) (note: that we have no experience with this extension, but Fooman seems to be a good guy.) +3. [Optimize images](https://developers.google.com/speed/docs/insights/OptimizeImages) + * [Image Optimization](http://www.magentocommerce.com/magento-connect/image-optimization.html)(note: no experience too.) +4. [Minify HTML](https://developers.google.com/speed/docs/insights/MinifyResources)~~ + * Based primary on [Minify CSS](https://developers.google.com/speed/docs/insights/MinifyResources) and [Minify JavaScript](https://developers.google.com/speed/docs/insights/MinifyResources). + +Requirements from PageSpeed Insights which are covered by your server admin :) +------------------------------------------------------------------------------ + +1. [Enable compression](https://developers.google.com/speed/docs/insights/EnableCompression) +2. [Avoid landing page redirects](https://developers.google.com/speed/docs/insights/AvoidRedirects) +3. [Leverage browser caching](https://developers.google.com/speed/docs/insights/LeverageBrowserCaching) +4. [Reduce server response time](https://developers.google.com/speed/docs/insights/Server) + +[Goal]: http://www.mediarox.de/goal.png + +Notices +------ +1. There is also a great tool called [PageSpeed Module](https://developers.google.com/speed/pagespeed/module) +for common webservers like apache and nginx. If you have the opportunity: Use it, but read the manual. +2. Test before use. There are also "great" things like multiple "" tags, that will crash the party. +3. Front Page Cache: Test it. Look that our event "controller_front_send_response_before" is called before +your FPC-Extension starts to observe. + + +Developer +--------- +Steven Fritzsche [@de_mediarox](https://twitter.com/de_mediarox) + +Licence +------- +[OSL - Open Software Licence 3.0](http://opensource.org/licenses/osl-3.0.php) + +Copyright +--------- +(c) 2015 Steven Fritzsche + diff --git a/app/code/community/Pagespeed/Css/Helper/Data.php b/app/code/community/Pagespeed/Css/Helper/Data.php new file mode 100644 index 0000000..b3e2870 --- /dev/null +++ b/app/code/community/Pagespeed/Css/Helper/Data.php @@ -0,0 +1,13 @@ + + */ + +/** + * Standard helper + */ +class Pagespeed_Css_Helper_Data extends Mage_Core_Helper_Abstract +{ +} \ No newline at end of file diff --git a/app/code/community/Pagespeed/Css/Model/Observer.php b/app/code/community/Pagespeed/Css/Model/Observer.php new file mode 100644 index 0000000..7353cfd --- /dev/null +++ b/app/code/community/Pagespeed/Css/Model/Observer.php @@ -0,0 +1,90 @@ + + */ + +/** + * Standard observer class + */ +class Pagespeed_Css_Model_Observer +{ + /** + * Move every Css (head & inline) to the bottom. ({stripped_html}{css}) + * + * Step 1: Load needed data. + * Step 2: Return if no is found in html. + * Step 3: Search and replace conditional css units. (example: ) + * Step 4: Search and replace external css tags. (link-tags must xhtml-compliant closed by "/>") + * Step 5: Search and replace inline css tags. + * Step 6: Return if no css is found. + * Step 7: Remove blank lines from html. + * Step 8: Recalculating position, insert css groups right before body ends and set response. + * Final order: + * 1. stripped html + * 2. conditional css tags + * 3. external css tags + * 3. inline css tags + * 4. + * + * @param Varien_Event_Observer $observer + */ + public function parseCssToBottom(Varien_Event_Observer $observer) + { + //$timeStart = microtime(true); + + // Step 1 + $response = $observer->getFront()->getResponse(); + $html = $response->getBody(); + + // Step 2 + $closedBodyPosition = strpos($html, ''); + if (false === $closedBodyPosition) return; + + // Step 3 + $conditionalCssTags = ''; + $conditionalCssPattern = '#\<\!--\[if[^\>]*>\s*]*type\="text\/css"[^>]*/>\s*<\!\[endif\]-->#isUm'; + $conditionalCssHits = preg_match_all($conditionalCssPattern, $html, $conditionalMatches); + + if((bool)$conditionalCssHits) { + $conditionalCssTags = implode('', $conditionalMatches[0]); + $html = preg_replace($conditionalCssPattern, '' , $html); + } + + // Step 4 + $externalCssTags = ''; + $externalCssPattern = '#]*type\=["\']text\/css["\'][^>]*/>#isUm'; + $externalCssHits = preg_match_all($externalCssPattern, $html, $externalMatches); + + if((bool)$externalCssHits) { + $externalCssTags = implode('', $externalMatches[0]); + $html = preg_replace($externalCssPattern, '' , $html); + } + + // Step 5 + $inlineCssTags = ''; + $inlineCssPattern = '##isUm'; + $inlineCssHits = preg_match_all($inlineCssPattern, $html, $inlineMatches); + + if((bool)$inlineCssHits) { + $inlineCssTags = implode('', $inlineMatches[0]); + $html = preg_replace($inlineCssPattern, '' , $html); + } + + // Step 6 + $overallCssHit = ((bool)$conditionalCssHits || (bool)$externalCssHits || (bool)$inlineCssHits); + if(!$overallCssHit) return; + + // Step 7 + $html = preg_replace('/^\h*\v+/m', '', $html); + + // Step 8 + $closedBodyPosition = strpos($html, ''); + $css = $conditionalCssTags . $externalCssTags . $inlineCssTags; + $html = substr_replace($html, $css, $closedBodyPosition, 0); + $response->setBody($html); + + //Mage::log('parseCssToBottom ' . round(((microtime(true) - $timeStart) * 1000)) . ' ms'); + } +} \ No newline at end of file diff --git a/app/code/community/Pagespeed/Css/etc/config.xml b/app/code/community/Pagespeed/Css/etc/config.xml new file mode 100644 index 0000000..873c1cf --- /dev/null +++ b/app/code/community/Pagespeed/Css/etc/config.xml @@ -0,0 +1,51 @@ + + + + + + 0.0.0.1 + + + + + + + + + + singleton + pagespeed_css/observer + parseCssToBottom + + + + + + + + + + + + Pagespeed_Css_Model + + + + + Pagespeed_Css_Helper + + + + + + \ No newline at end of file diff --git a/app/code/community/Pagespeed/Js/Helper/Data.php b/app/code/community/Pagespeed/Js/Helper/Data.php new file mode 100644 index 0000000..c26f03e --- /dev/null +++ b/app/code/community/Pagespeed/Js/Helper/Data.php @@ -0,0 +1,13 @@ + + */ + +/** + * Standard helper + */ +class Pagespeed_Js_Helper_Data extends Mage_Core_Helper_Abstract +{ +} \ No newline at end of file diff --git a/app/code/community/Pagespeed/Js/Model/Observer.php b/app/code/community/Pagespeed/Js/Model/Observer.php new file mode 100644 index 0000000..34150a0 --- /dev/null +++ b/app/code/community/Pagespeed/Js/Model/Observer.php @@ -0,0 +1,77 @@ + + */ + +/** + * Standard observer class + */ +class Pagespeed_Js_Model_Observer +{ + /** + * Move every JS (head & inline) to the bottom. ({stripped_html}{js}) + * + * Step 1: Load needed data. + * Step 2: Return if no is found in html. + * Step 3: Search and replace conditional js units. (example: ) + * Step 4: Search and replace normal js tags. + * Step 5: Return if no js is found. + * Step 6: Remove blank lines from html. + * Step 7: Recalculating position, insert js groups right before body ends and set response. + * Final order: + * 1. stripped html + * 2. conditional js tags + * 3. normal js tags + * 4. + * + * @param Varien_Event_Observer $observer + */ + public function parseJsToBottom(Varien_Event_Observer $observer) + { + //$timeStart = microtime(true); + + // Step 1 + $response = $observer->getFront()->getResponse(); + $html = $response->getBody(); + + // Step 2 + $closedBodyPosition = strpos($html, ''); + if($closedBodyPosition === false) return; + + // Step 3 + $conditionalJsTags = ''; + $conditionalJsPattern = '#\<\!--\[if[^\>]*>\s*\s*<\!\[endif\]-->#isUm'; + $conditionalJsHits = preg_match_all($conditionalJsPattern, $html, $conditionalMatches); + + if((bool)$conditionalJsHits) { + $conditionalJsTags = implode('', $conditionalMatches[0]); + $html = preg_replace($conditionalJsPattern, '' , $html); + } + + // Step 4 + $jsTags = ''; + $jsPattern = '##isUm'; + $jsHits = preg_match_all($jsPattern, $html, $matches); + + if((bool)$jsHits) { + $jsTags = implode('', $matches[0]); + $html = preg_replace($jsPattern, '' , $html); + } + + // Step 5 + $overallJsHit = ((bool)$conditionalJsHits || (bool)$jsHits); + if(!$overallJsHit) return; + + // Step 6 + $html = preg_replace('/^\h*\v+/m', '', $html); + + // Step 7 + $closedBodyPosition = strpos($html, ''); + $html = substr_replace($html, $conditionalJsTags . $jsTags, $closedBodyPosition, 0); + $response->setBody($html); + + //Mage::log('parseJsToBottom ' . round(((microtime(true) - $timeStart) * 1000)) . ' ms'); + } +} \ No newline at end of file diff --git a/app/code/community/Pagespeed/Js/etc/config.xml b/app/code/community/Pagespeed/Js/etc/config.xml new file mode 100644 index 0000000..496949e --- /dev/null +++ b/app/code/community/Pagespeed/Js/etc/config.xml @@ -0,0 +1,51 @@ + + + + + + 0.0.0.1 + + + + + + + + + + singleton + pagespeed_js/observer + parseJsToBottom + + + + + + + + + + + + Pagespeed_Js_Model + + + + + Pagespeed_Js_Helper + + + + + + \ No newline at end of file diff --git a/app/etc/modules/Pagespeed_Css.xml b/app/etc/modules/Pagespeed_Css.xml new file mode 100644 index 0000000..3c76e61 --- /dev/null +++ b/app/etc/modules/Pagespeed_Css.xml @@ -0,0 +1,17 @@ + + + + + + true + community + + + diff --git a/app/etc/modules/Pagespeed_Js.xml b/app/etc/modules/Pagespeed_Js.xml new file mode 100644 index 0000000..4d1e80b --- /dev/null +++ b/app/etc/modules/Pagespeed_Js.xml @@ -0,0 +1,17 @@ + + + + + + true + community + + +