PHPSpeedy helper

This article is also available in the following languages:
By Marcelius
So I needed a nice package that minifies/gzips all assets like js/css/html and combines them into as less http requests possible. CakePHP1.2 does have an option called 'asset.filter.css' for example in the core.php, but all it does is minify the files, not combining them. PHPSpeedy does, and I've created a helper to automagicly do the work.
PHPSpeedy reads the HTML, and replaces all < link src="" /> and < script src="" /> with only one call to a cached, minified version for javascript, and one for css. This reduces the numer of HTTP requests the browser has to do for each page request. Also, it can gzip the html so download times of html will also decrease. And the best part: it notices changes in the javascript and css files! So cache is automaticly rebuilded when needed.

Step 1, download PHPSpeedy
It can be found here: http://aciddrop.com/php-speedy/ Open the zip, and extract the contents of the libs folder to your vendors folder. You should now have a php_speedy folder there.

Step 2, Configuring PHPSpeedy
We need to tell PHPSpeedy where the cached versions of the javascript and css should be stored. This can be done in the app/vendors/php_speedy/config.php
You can skip the username and password fields, they won't be used.
This is what I filled in for the path info:

## Path info
$compress_options['document_root'] = WWW_ROOT;
$compress_options['javascript_cachedir'] = WWW_ROOT . "js" . DS . "cache";
$compress_options['css_cachedir'] = WWW_ROOT . "css" . DS . "cache";

Please note that the document_root setting isn't there by default! So please add it to the config.php


As you can see, I set the cache folders as a subfolder of the js and css folders, so they can be accessed by the browser.

Remember to set the correct permissions on the cache folders else, the won't be writeable by PHPSpeedy.


Step 3, making small changes to the PHPSpeedy core files. This will make PHPSpeedy compatible with CakePHP
Open app/vendors/php_speedy/php_speedy.php

FIND

$jsmin = new JSMin($contents);

REPLACE WITH

$jsmin = new JSMin(isset($contents) ? $contents : null);

FIND

$compressor = new compressor(array('view'=>$view,

REPLACE WITH

global $compressor;
$compressor = new compressor(array('view'=>$view,

Step 4, download the code of the PHPSpeedy helper found below and put it in the helpers folder.
Then, add the helper to the $helpers array of the controller. You can also add it to the 'app_controller' so it works for all your controllers.

Controller Class:

<?php 
    
class AppController extends Controller{
        var 
$helpers = array('PhpSpeedy');
    }
?>



That's it, you're all set! The PHPSpeedy helper is only active when debug mode is set to 0.

Problems I encounterd during the proces:
  • It could be that your javascript doesn't work anymore. The problem will most likely be a forgotten ':' at the end of the script.
  • Your first call will cause PHPSpeedy to read all javascript and css files and do its magic. This will take a while, but do another refresh and all is good again.
  • If you do not have javascript or css, set all related options in the config.php of PHPSpeedy to zero. If you don't, you will see PHP warnings regarding invalid arguments in a foreach loop
  • If you have short_open_tag disabled in your PHP configuration, change

Final notes:
  • This article is based on PHPSpeedy v0.5.2
  • Please refer to the readme.txt of PHPSPeedy to so pending bugs / inabilities

Helper Class:

<?php 
    
/**
    * Helper for PHPSpeedy class (PHP5 only!)
    *
    * @author      Marcel Raaijmakers (Marcelius)
    * @copyright   Copyright 2009, Marcel Raaijmakers
    * @license     http://www.opensource.org/licenses/mit-license.php The MIT License
    */
    
class PhpSpeedyHelper extends AppHelper {
        
/**
        * Storage of the view object
        */
        
private $view;
    
        
/**
        * Constructor
        */
        
public function __construct(){    
            
$this->view ClassRegistry::getObject('view');
        }
        
        
/**
        * Trigger
        */
        
public function afterLayout(){

            if (
Configure::read('debug') == 0){
                
$r App::import('vendor''php_speedy');
    
                global 
$compressor;
    
                if (
$compressor instanceof compressor){
                    
$compressor->return_content true;
                    
$this->view->output $compressor->finish($this->view->output);
                }
            
                return 
parent::afterLayout();
            }
        }
    }
?>

Enjoy!

Comments

  • Posted 07/21/10 05:32:12 PM
    i found a small bug
    when i use session->setFlash(..) the output of the flash message was cryptic. i found out that session output is renderd first and gzipped first thiem. then the layout is rendered with the gzipped session text and then the whole code is gzipped again. so i check if

    public function afterLayout()
            {
                if (Configure::read('debug') == 0)
                {
                    if (strpos($this->view->output, "<html") !== false)
                    {
                        $r = App::import('Vendor', 'php_speedy');
            
                        global $compressor;
        
                        if ($compressor instanceof compressor){
                            $compressor->return_content = true;
                            
                            $this->view->output = $compressor->finish($this->view->output);
                        }
                    }
                    
                    return parent::afterLayout();
                }
            }
  • Posted 10/27/09 05:30:42 PM
    It didn't have anything to do with PHPSpeedy. Here's the solution if you're interested: http://stackoverflow.com/questions/1603575/mamp-and-cakephp-call-to-model-issue
    However, PHPSpeedy was still giving me problems on my MAMP setup, as well as on our live server (on MediaTemple) - but not on our dev server (Hostmonster).

    The problem was with how the document_root variable was being set in the php_speedy/libs/php/view.php file. It doesn't get it from the config file, but from $_SERVER['DOCUMENT_ROOT'] and this reads it as trunk/ instead of trunk/app/webroot/.

    So I made the follow change in function set_paths in php_speedy/libs/php/view.php

    //Save doc root
    // Old line:
    /* $this->paths['full']['document_root'] = $this->ensure_trailing_slash($_SERVER['DOCUMENT_ROOT']); */
    // New line:
    $this->paths['full']['document_root'] = ($document_root) ? $document_root : WWW_ROOT;

    I'm now getting this error: Undefined index: rel [APP/vendors/php_speedy/controller/compressor.php, line 511]
    But it doesn't seem to effect the function of php_speedy (I'd still like it gone though)
  • Posted 10/13/09 12:41:55 AM
    Now this might not be directly caused by PHPspeedy, but I was trying to setup PHPSpeedy on my live site and it wouldn't work (whereas the dev site hosted on another provider was fine). Which is really annoying as it would be amazingly helpful for this to work on the LIVE site!

    The error I got was:

    Warning (512): Cache not configured properly. Please check Cache::config(); in APP/config/core.php [CORE/cake/libs/configure.php, line 628]
    Code | Context

    $boot = true
    $vendorPaths = null
    $pluginPaths = null
    $helperPaths = null
    $viewPaths = null
    $componentPaths = null
    $controllerPaths = null
    $behaviorPaths = null
    $modelPaths = null
    $cache = false


    if (empty($cache['settings'])) {
    trigger_error('Cache not configured properly. Please check Cache::config(); in APP/config/core.php', E_USER_WARNING);

    Configure::__loadBootstrap() - CORE/cake/libs/configure.php, line 628
    Configure::getInstance() - CORE/cake/libs/configure.php, line 125
    include - CORE/cake/bootstrap.php, line 45
    [main] - APP/webroot/index.php, line 85


    ----

    Just using the default "FILE" cache engine. Any ideas?
  • Posted 09/26/09 07:28:16 PM
    I just implemented this as described above and it worked great. I can clearly see the improvement. I am going to apply this to my other cake sites.

    Thanks for the nice work.
  • Posted 03/24/09 06:44:25 AM
    I've carried out the amends to the php_speedy code as outlined above everything seems to work ok except for when the links to the js and css get replaced in the layout the links look like this

    'http://local.site.com/Users/jim/Sites/staak/htdocs/app/webroot/css/cache/_cmp_cssstylesheet_157dcc1a4dd55680c061bac33552431a.php'

    when they need to be

    'http://local.site.com/css/cache/_cmp_cssstylesheet_157dcc1a4dd55680c061bac33552431a.php'

    Any ideas where I'm going wrong here?

    Thanks
    • Posted 06/11/09 09:48:30 AM
      I've carried out the amends to the php_speedy code as outlined above everything seems to work ok except for when the links to the js and css get replaced in the layout the links look like this

      'http://local.site.com/Users/jim/Sites/staak/htdocs/app/webroot/css/cache/_cmp_cssstylesheet_157dcc1a4dd55680c061bac33552431a.php'

      when they need to be

      'http://local.site.com/css/cache/_cmp_cssstylesheet_157dcc1a4dd55680c061bac33552431a.php'

      Any ideas where I'm going wrong here?

      Thanks

      I've just found a solution. I've been faced to the same problem but found a really easy solution.

      You only have to replace the "str_replace" fonction at line 551 of the file "app/vendor/php_speedy/controller/compressor.php" with the "str_ireplace".

      so the updates line 551 of "compressor.php" should look like this:

      $relative_cachedir = str_ireplace($this->view->prevent_trailing_slash($this->unify_dir_separator($this->view->paths['full']['document_root'])),"",$this->view->prevent_trailing_slash($this->unify_dir_separator($options['cachedir'])));

      I think it's a problem with the server drive letter case. Maybe cause i'm on IIS.

      Expect this gonna work for you too.

      And a great thanks to Marcel for this great tutorial.
    • Posted 03/28/09 01:50:41 PM
      I've carried out the amends to the php_speedy code as outlined above everything seems to work ok except for when the links to the js and css get replaced in the layout the links look like this

      'http://local.site.com/Users/jim/Sites/staak/htdocs/app/webroot/css/cache/_cmp_cssstylesheet_157dcc1a4dd55680c061bac33552431a.php'

      when they need to be

      'http://local.site.com/css/cache/_cmp_cssstylesheet_157dcc1a4dd55680c061bac33552431a.php'

      Any ideas where I'm going wrong here?

      Thanks

      I'm having the same problem... Any hints guys?
  • Posted 02/26/09 11:38:43 PM
    I have fixed the return_array problem by modifying the below functions in compressor.php

    partial code
    /**
    * Gets the path locations of the scripts being compressed
    *
    **/
    function get_file_locations($script_array,$options) {

    if(!empty($script_array)){
    foreach($script_array as $key => $value){
    if(strpos($value, "/js/")){
    $script_array1[] = str_replace("/js/", "/app/webroot/js/", $value);
    } else if(strpos($value, "/css/")){
    $script_array1[] = str_replace("/css/", "/app/webroot/css/", $value);
    }
    }
    }

    $script_array = $script_array1;

    /**
    * Replaces the script or css links in the source with a marker
    *
    */
    function _remove_scripts($script_array,$source) {
    $maxKey = array_pop(array_keys($script_array));
    foreach($script_array AS $key=>$value) {
    if(strpos($value['location'], "/app/webroot/js/")){
    $value['location'] = str_replace("/app/webroot/js/", "/js/", $value['location']);
    } else if(strpos($value['location'], "/app/webroot/css/")){
    $value['location'] = str_replace("/app/webroot/css/", "/css/", $value['location']);
    }



    thanks for nice helper file.



  • Posted 02/24/09 06:37:05 AM
    Hallo Marcel,

    Ik ben op dit moment bezig om jouw helper te implementeren.
    Echter werkt mijn javascript niet meer, zoals je schreef is het waarschijnlijk doordat er een : mist in het script.

    Maar in welk script mis dit? in mijn code? of in de helper?

    Wellicht dat je mij op weg kan helpen.

    Alvast bedankt.

    groeten,
    Wilco
  • Posted 02/04/09 05:00:42 PM
    I'm getting two different errors when trying to implement this. If I have the debug set to 0 then none of my javascript or css is affected. If I alter the helper to allow for debug to be 1 then I get the following errors:

    Undefined variable: contents [APP/vendors/php_speedy/php_speedy.php, line 71]
    Undefined variable: return_array [APP/vendors/php_speedy/controller/compressor.php, line 731]

    Any thoughts?
    • Posted 02/05/09 03:03:52 AM
      I'm getting two different errors when trying to implement this. If I have the debug set to 0 then none of my javascript or css is affected. If I alter the helper to allow for debug to be 1 then I get the following errors:

      Undefined variable: contents [APP/vendors/php_speedy/php_speedy.php, line 71]
      Undefined variable: return_array [APP/vendors/php_speedy/controller/compressor.php, line 731]

      Any thoughts?

      I've found the problem: I've used v0.4.6, and you have probably v0.5.2. I'll update the artice since there are many changes made between these two versions.

Comments are closed for articles over a year old