Zendframework + Minify

by

image

I have had the opportunity over the past year to do some extensive work with Zend Framework as we have been porting our legacy code to said framework, and it has been a lot of fun. It turns out that I very much enjoy the MVC framework, and have had great fun in the implementation. Our primary goal has been supportability and maintainability in moving to the framework, but we aren’t opposed to performance and scalability gains as well.image

A key factor in the migration to the framework has been, and will continue to be, ensuring that we do not lose any of the scalability and performance that we currently enjoy in the legacy code. There have been a few instances, where porting functionality over to the Zend Framework resulted in immediate performance gains; however, the initial port over tends to perform a little slower, and at that point I roll out my handy performance toolbox and get to measuring and diagnosing to find the needed gains.

I typically discover that I have not fully leveraged the framework, took a shortcut someplace, or (re) wrote the code in an inefficient manner. But every single time I would look, the first thing that jumped out at me was the fact that I was breaking the Rules for High Performance Web Sites. If you aren’t familiar, these are a series of best practice statements that have repeatedly shown to improve web page performance. From the time I began this effort last spring, I repeatedly told myself, “I’m going to fix that.”

Particularly because the framework allows for some mighty decent control over CSS and JavaScript additions to the page, it seemed to be a no brainer that we should be able to concatenate and minify the includes, then get the JavaScript to the bottom of the page and the css in the header. It even seemed to me that such functionality would be built right into the framework, I mean, we already have the HeadLink and HeadScript helpers managing them for us, just a wee bit more effort to get the rest of the way. I tweeted a few requests for a solution, and then spent a moment researching it via Google. No good. Turns out Zend Framework doesn’t support this, and there does not appear to be a time set for when they will.

My buddy WillAtHome happened to mention there was a cool PHP5 project over at Google Code called minify, that appeared to mostly do what I desired.

 image

So I set about downloading, following the instructions, and implementing it within the public directory of my framework. It was simple enough to get working, and quite impressive. However, I continued to lack the ability to actually integrate it with Zend Framework. I really wanted HeadLink and HeadScript to get to know minify up close and personal. It turned out I wasn’t the only one! As I read through Issue 105: Integration with the Zend Framework I discovered some excellent progress had been made on introducing the three. However, I didn’t find anything that really met my own needs, and the scripts provided all seemed to require some editing in order to get them working as advertised. I really wanted a drop in solution for any Zend Framework project I might ever work on.

My first thought was to refactor the Minify interface into a ZF Controller, throw the minify libraries in my projects library folder, and then extend HeadLink and HeadScript. However, the boys contributing to Issue 105 were mostly done with the helpers, so I figured that I would just apply my hand to the lowest hanging fruit; I already had minify unzipped (installed) and working. The end result can be found on GitHub under my zf-helpers repository.

The instructions for installing and leveraging the scripts can be found in the Header Comments of each one. Here’s what MinifyHeadScript’s instructions look like:

/**

*

* Helper for setting and retrieving script elements for HTML head section

* with the added twist of minifying the javascript files.

*

* ** PREREQUISITES **

* This file expects that you have installed minify in ../ZendFramworkProject/Public/min

* and that it is working. If your location has changed, modify

* $this->$_minifyLocation to your current location.

*

* ** INSTALLATION **

* Simply drop this file into your ../ZendFramworkProject/application/views/helpers

* directory.

*

* ** USAGE **

* In your Layout or View scripts, you can simply call minifyHeadScript

* in the same way that you used to call headScript. Here is an example:

*

echo $this->minifyHeadScript()

->prependFile('http://ajax.googleapis.com/ajax/libs/someObject/2.2/object.js') // 12th

->prependFile('/js/jquery.delaytrigger.js') // 11th

->prependFile('/js/sorttable.js') // 10th

->prependFile('/js/jquery.alerts.js') // 9th

->prependFile('/js/jqModal.js') // 8th

->prependFile('/js/jquery.maskedinput.js') // 7th

->prependFile('/js/jquery.checkbox.js') // 6th

->prependFile('/js/jquery.tablesorter.min.js') // 5th

->prependFile('/js/jquery.autocomplete.js') // 4th

->prependFile('/js/jquery.color.js') // 3rd

->prependFile('/js/jquery-1.3.2.min.js') // 2nd

->prependFile('/js/main.js') // 1st

->appendScript('

$(document).ready(function() {

$(\'#ajaxWait\').ajaxStart(function() {

$(this).show();

}).ajaxStop(function() {

$(this).hide();

})

try { init(); } catch(e) {}

}); // Last

');

* Because minify can't do anything with a javascript from some other server, nor

* does it do anything with inline scripts, and order is important, it will minify

* up to the point that it meets something that can't be minified, and then output

* the minified version, then the item(s) that couldn't be minified, and then attempt

* to minify items again, repeating the process till it is completed. Here is an

* example of output from the example above.

*

<script type="text/javascript" src="/min/?f=/js/main.js,/js/jquery-1.3.2.min.js,/js/jquery.color.js,

/js/jquery.autocomplete.js,/js/jquery.tablesorter.min.js,/js/jquery.checkbox.js,

/js/jquery.maskedinput.js,/js/jqModal.js,/js/jquery.alerts.js,/js/sorttable.js,

/js/jquery.delaytrigger.js"></script>

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/someObject/2.2/object.js"></script>

<script type="text/javascript">

//<![CDATA[

 

$(document).ready(function() {

$('#ajaxWait').ajaxStart(function() {

$(this).show();

}).ajaxStop(function() {

$(this).hide();

});

try { init(); } catch(e) {}

});

//]]>

 

</script>

 

*

*

*

* @see http://code.google.com/p/minify/

* @package Zend_View

* @subpackage Helper

* @copyright Copyright (c) 2010-2011 Signature Tech Studios (http://www.stechstudio.com)

* @license http://framework.zend.com/license/new-bsd New BSD License

* @author Rob "Bubba" Hines

*

*/

One of the questions I most regularly get is “why do you use prepend in your examples as opposed to append?” There is absolutely nothing preventing you from doing just that, assuming you are certain you have not made a call in the controller to assign scripts to the queue. If you are certain of that, and you are only modifying the script queue in the layout file, do whatever tickles you pink. I, however, tend to make some decisions on which JS and CSS will be included in the final results while I’m still in the controller, and order tends to get important, so prepending makes the most sense to me. Given that prepending assumes the most complex usage, it also seemed a good idea for the examples.

share

Recommended Posts

Comments

  1. So there is no way to simply change the
    headLink(); ?>

    with
    minStyleSheets(); ?>

    like the http://www.gsdesign.ro/blog/minify-css-in-zendframework/ did ( except it doesn’t work )

    how can i get $this->headLink() elements or it’s toString form ?

  2. I’m not sure I grasp the context of your question. However:

    $this->headLink()->toString();
    $this->minStyleSheets()->toString();

    will return the string forms, and:

    $this->headLink()-> getIterator();
    $this->minStyleSheets()->getIterator();

    will return an array of the contents. See the API DOC’s for more depth http://framework.zend.com/apidoc/1.10/

  3. i had to change your function like this

    public function toString($indent = null) {

    $indent = (null !== $indent) ? $this->getWhitespace($indent) : $this->getIndent();

    $items = array();
    $stylesheets = array();
    $this->getContainer()->ksort();

    $hmm = $this->view->headLink();

    foreach ( $hmm as $item ) {
    ….

    notice the $hmm variable, otherwise your for would have nothing to iterate on(using zend ‘1.11.4’ and PHP 5.3.3)

  4. what i mean, by making that change, i don’t have to change my pages where they call $this->headScript()->prependFile(‘/scripts/jquery-ui.custom.min.js’);

    with
    $this->minifyHeadScript()->prependFile(‘/scripts/jquery-ui.custom.min.js’);

    i just leave everything as it is and change
    echo $this->headScript();
    with
    echo $this->minifyHeadScript();

    thanks for the class

  5. When you create your View Helper, if you name the last part the same as the Zend one, you don’t have to change the way you call it in code:
    class My_View_Helper_HeadScript extends Zend_View_Helper_HeadScript

    Add items in the standard way:
    $this->headScript()->prependFile(‘/scripts/jquery-ui.custom.min.js’);

    Echo it the standard way:
    echo $this->headScript();

  6. i did as you said i placed the file in /library/My/View/Helper
    and called it MinifyHeadLink.php

    <?php

    class My_View_Helper_MinifyHeadScript extends Zend_View_Helper_HeadScript {
    ….

    it won't work, it doesn't ovveride

  7. Hi Rob, thank you for your helpers.

    In some conditions I got urls like “/min/?f=a.css,b.css,a.css,b.css”.

    I added array_unique to all implode(‘,’, $styles) calls and am fine now, i.e.:

    $minStyles->href = $this->getMinUrl() . ‘?f=’ . implode(‘,’, array_unique($styles));

    Regards,
    Karsten

  8. Hi

    I appreciate much your work. I’m gonna use it in one of my projects.
    And I’m particularly happy to see that you’re a christian. Such people are going rare where I live.

    Cheers

Leave a Reply

Your email address will not be published. Required fields are marked *