Skip to content

Commit

Permalink
[Init]
Browse files Browse the repository at this point in the history
  • Loading branch information
sfritzsche committed Mar 15, 2015
0 parents commit fad271f
Show file tree
Hide file tree
Showing 10 changed files with 406 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/.idea
76 changes: 76 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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}</body></html>)
* including conditional js units (<!--[if lt IE 7]>{multiple js tags}<![endif]-->)
* including external js tags
* including "inline" js tags
2. Move every CSS (head & inline) to the bottom. ({stripped_html}{css}</body></html>)
* including conditional css units (<!--[if lt IE 7]>{multiple css tags}<![endif]-->)
* 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 "</body>" 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 <sfritzsche@mediarox.de>

13 changes: 13 additions & 0 deletions app/code/community/Pagespeed/Css/Helper/Data.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php
/**
* @package Pagespeed_Css
* @copyright Copyright (c) 2015 mediarox UG (haftungsbeschraenkt) (http://www.mediarox.de)
* @author Steven Fritzsche <sfritzsche@mediarox.de>
*/

/**
* Standard helper
*/
class Pagespeed_Css_Helper_Data extends Mage_Core_Helper_Abstract
{
}
90 changes: 90 additions & 0 deletions app/code/community/Pagespeed/Css/Model/Observer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php
/**
* @package Pagespeed_Css
* @copyright Copyright (c) 2015 mediarox UG (haftungsbeschraenkt) (http://www.mediarox.de)
* @author Steven Fritzsche <sfritzsche@mediarox.de>
*/

/**
* Standard observer class
*/
class Pagespeed_Css_Model_Observer
{
/**
* Move every Css (head & inline) to the bottom. ({stripped_html}{css}</body></html>)
*
* Step 1: Load needed data.
* Step 2: Return if no </body> is found in html.
* Step 3: Search and replace conditional css units. (example: <!--[if lt IE 7]>{multiple css tags}<![endif]-->)
* 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 </body> 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. </body></html>
*
* @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, '</body>');
if (false === $closedBodyPosition) return;

// Step 3
$conditionalCssTags = '';
$conditionalCssPattern = '#\<\!--\[if[^\>]*>\s*<link[^>]*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 = '#<link[^>]*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 = '#<style.*</style>#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, '</body>');
$css = $conditionalCssTags . $externalCssTags . $inlineCssTags;
$html = substr_replace($html, $css, $closedBodyPosition, 0);
$response->setBody($html);

//Mage::log('parseCssToBottom ' . round(((microtime(true) - $timeStart) * 1000)) . ' ms');
}
}
51 changes: 51 additions & 0 deletions app/code/community/Pagespeed/Css/etc/config.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?xml version="1.0"?>
<!--
/**
* @package Pagespeed_Css
* @copyright Copyright (c) 2015 mediarox UG (haftungsbeschraenkt) (http://www.mediarox.de)
* @author Steven Fritzsche <sfritzsche@mediarox.de>
*/
-->
<config>
<modules>
<Pagespeed_Css>
<version>0.0.0.1</version>
</Pagespeed_Css>
</modules>
<frontend>
<events>

<!--
Event before response send to browser
-->
<controller_front_send_response_before>
<observers>
<pagespeed_css_response_before>
<type>singleton</type>
<class>pagespeed_css/observer</class>
<method>parseCssToBottom</method>
</pagespeed_css_response_before>
</observers>
</controller_front_send_response_before>

</events>
</frontend>
<global>

<!--
default class declarations
-->
<models>
<pagespeed_css>
<class>Pagespeed_Css_Model</class>
</pagespeed_css>
</models>
<helpers>
<pagespeed_css>
<class>Pagespeed_Css_Helper</class>
</pagespeed_css>
</helpers>

</global>

</config>
13 changes: 13 additions & 0 deletions app/code/community/Pagespeed/Js/Helper/Data.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php
/**
* @package Pagespeed_Js
* @copyright Copyright (c) 2015 mediarox UG (haftungsbeschraenkt) (http://www.mediarox.de)
* @author Steven Fritzsche <sfritzsche@mediarox.de>
*/

/**
* Standard helper
*/
class Pagespeed_Js_Helper_Data extends Mage_Core_Helper_Abstract
{
}
77 changes: 77 additions & 0 deletions app/code/community/Pagespeed/Js/Model/Observer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php
/**
* @package Pagespeed_Js
* @copyright Copyright (c) 2015 mediarox UG (haftungsbeschraenkt) (http://www.mediarox.de)
* @author Steven Fritzsche <sfritzsche@mediarox.de>
*/

/**
* Standard observer class
*/
class Pagespeed_Js_Model_Observer
{
/**
* Move every JS (head & inline) to the bottom. ({stripped_html}{js}</body></html>)
*
* Step 1: Load needed data.
* Step 2: Return if no </body> is found in html.
* Step 3: Search and replace conditional js units. (example: <!--[if lt IE 7]>{multiple js tags}<![endif]-->)
* 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 </body> 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. </body></html>
*
* @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, '</body>');
if($closedBodyPosition === false) return;

// Step 3
$conditionalJsTags = '';
$conditionalJsPattern = '#\<\!--\[if[^\>]*>\s*<script.*</script>\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 = '#<script.*</script>#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, '</body>');
$html = substr_replace($html, $conditionalJsTags . $jsTags, $closedBodyPosition, 0);
$response->setBody($html);

//Mage::log('parseJsToBottom ' . round(((microtime(true) - $timeStart) * 1000)) . ' ms');
}
}
51 changes: 51 additions & 0 deletions app/code/community/Pagespeed/Js/etc/config.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?xml version="1.0"?>
<!--
/**
* @package Pagespeed_Js
* @copyright Copyright (c) 2015 mediarox UG (haftungsbeschraenkt) (http://www.mediarox.de)
* @author Steven Fritzsche <sfritzsche@mediarox.de>
*/
-->
<config>
<modules>
<Pagespeed_Js>
<version>0.0.0.1</version>
</Pagespeed_Js>
</modules>
<frontend>
<events>

<!--
Event before response send to browser
-->
<controller_front_send_response_before>
<observers>
<pagespeed_js_response_before>
<type>singleton</type>
<class>pagespeed_js/observer</class>
<method>parseJsToBottom</method>
</pagespeed_js_response_before>
</observers>
</controller_front_send_response_before>

</events>
</frontend>
<global>

<!--
default class declarations
-->
<models>
<pagespeed_js>
<class>Pagespeed_Js_Model</class>
</pagespeed_js>
</models>
<helpers>
<pagespeed_js>
<class>Pagespeed_Js_Helper</class>
</pagespeed_js>
</helpers>

</global>

</config>
Loading

0 comments on commit fad271f

Please sign in to comment.