开发者

Any good Zend Framework + Minify implementations?

开发者 https://www.devze.com 2023-02-03 10:01 出处:网络
Are there any good implementations of Minify integration with Zend Framework? I\'m looking for examples.

Are there any good implementations of Minify integration with Zend Framework? I'm looking for examples.

I'd love to have a plugin that overrides $this->headLink() and spits out the correct minified url/content.

Edit:

It seems most examples I find aren't fully optimized in one form or fashion. I'm looking for a solution that meets the following requirements:

Reduces multiple links and script tags to one request (one for link and one for scripts) The closest I've seen is a request path that passes a comma-delimited string to /min/ like so:

<script src="/min?f=file1.js,file2,js,file3.js" type="text/javascript"></script>

Why not something that combines all scripts into one file on disk on the fly and then caches it so that you aren't doing the minification on every request?

<script src="/js/app.js?someRandomStringHere" type="text/javascript"></script>

The combining aspect should maintain order (in reference to prepend, append, etc)

While I don't care so much ab开发者_JS百科out sending correct expires headers because I force gzipping, etags, and expires headers on the server-side, having that optional would be beneficial to other users.

Lastly, having a build script that generates the minified assets isn't necessary bad - as long as it is easy to do and doesn't require a code change after every build.


This is what I use, the class is shown followed by use cases. I've commented it quickly, some of it might need changing to match your paths or define() the PUBLICPATH

class View_Helper_Minify extends Zend_View_Helper_Abstract
{
    public function minify($files, $ext, $folderName)
    {   
        // The folder of the files your about to minify
        // PUBLICPATH should be the path to your public ZF folder
        $folder = PUBLICPATH . $folderName . "/";

        // Set update needed flag to false
        $update_needed = false;

        // This is the file ext of the cached files
        $cacheFileExt = "." . $ext;

        // The list of files sent is exploded into an array
        $filesExploded = explode(',', $files);

        // The full cached file path is an md5 of the files string
        $cacheFilePath = $folder . md5($files) . $cacheFileExt;

        // The filename of the cached file 
        $cacheFileName = preg_replace("#[^a-zA-Z0-9\.]#", "", end(explode("/", $cacheFilePath)));

        // Obtains the modified time of the cache file
        $cacheFileDate = is_file($cacheFilePath) ? filemtime($cacheFilePath) : 0;

        // Create new array for storing the list of valid files
        $fileList = array();

        // For each file
        foreach($filesExploded as $f)
        {
            // determine full path of the full and append extension
            $f = $folder . $f . '.' . $ext;

            // If the determined path is a file
            if(is_file($f))
            {
                // If the file's modified time is after the cached file's modified time
                // Then an update of the cached file is needed
                if(filemtime($f) > $cacheFileDate)
                    $update_needed = true;

                // File is valid add to list 
                $fileList[] = $f;
            }
        }

        // If the cache folder's modified time is after the cached file's modified time
        // Then an update is needed
        if(filemtime($folder) > $cacheFileDate) 
            $update_needed = true;

        // If an update is needed then optmise the valid files
        if($update_needed)
            $this->optmiseFiles($fileList, $cacheFilePath, $ext);

        // Finally check if the cached file path is valid and return the absolute URL
        // for te cached file
        if(is_file($cacheFilePath))
            return "/" . $folderName . "/" . $cacheFileName;

        // Throw Exception
        throw new Exception("No minified file cached");             
    }

    private function optimise($code, $ext)
    {
        // Do not optmise JS files
        // I had problems getting JS files optmised and still function
        if($ext == "js")
            return $code;

        // Remove comments from CSS
        while(($i = strpos($code, '/*')) !== false)
        {
            $i2 = strpos($code, '*/',$i);

            if($i2 === false) 
                break;

            $code = substr($code, 0, $i).substr($code, $i2 + 2);
        }

        // Remove other elements from CSS
        $code = str_replace('/*','',$code);
        $code = str_replace("\n",' ',$code);
        $code = str_replace("\r",' ',$code);
        $code = str_replace("\t",' ',$code);
        $code = @ereg_replace('[ ]+',' ',$code);
        $code = str_replace(': ',':', $code);
        $code = str_replace('; ',';', $code);
        $code = str_replace(', ',',', $code);
        $code = str_replace(' :',':', $code);
        $code = str_replace(' ;',';', $code);
        $code = str_replace(' ,',',', $code);

        // Return optimised code
        return $code;
    }

    // Optmise the list of files
    private function optmiseFiles($fileList, $cacheFilePath, $ext)
    {
        // Empty String to start with
        $code = '';

        // Check files list in case just one file was passed
        if(is_array($fileList))
        {
            // Foreach of the valid files optmise the code if the file is valid
            foreach($fileList as $f)
                $code .= is_file($f) ? $this->optimise(implode('', file($f)), $ext) : '';
        }
        // Else if a valid file is passed optmise the code
        else
            $code = is_file($fileList) ? $this->optimise(implode('', file($fileList)), $ext) : '';

        // Open the cache file
        $f = @fopen($cacheFilePath, 'w');

        // If open successful
        if(is_resource($f))
        {
            // Write code to the cache file
            fwrite($f, $code);

            // close cache file
            fclose($f);
        }
    }   
}

You would use the helper like this in your view

// Define an array of files, note you do not define the ext of those files
// The ext is defined as a param for the helper as this effects the optmisation  
$files = array("jquery-ui-1.8.7.custom",
            "jquery.pnotify.default",
            "jquery.pnotify.default.icons",
            "tipTip",
            "prettyPhoto",
            "custom");

// Get the absolute URL of the files which are imploded also pass the directory 'css' and ext 'css' 
$cssString = $this->minify(implode("," , $files), "css", "css");

// use baseURL() to output the full URL of the cached file and use it as normal with headLink()
echo $this->headLink()
->appendStylesheet($this->baseUrl($cssString));

And here is a javascript version

$files = array("jquery-1.4.4.min",
            "jquery.pnotify.min",
            "jquery.tipTip.minified",
            "jquery.countdown.min",
            "jquery.prettyPhoto",
            "jquery.typewatch",
            "default.functions");

$jsString = $this->minify(implode("," , $files), "js", "scripts");

echo $this->headScript()->appendFile($this->baseUrl($jsString));


I am trying to do the same thing right now. I am looking at NC State University's OT Framework, based on Zend Framework. This is implemented as a view helper. It has a nice class to minify all headscripts and headlinks via the Minify on Google Code:

http://ot.ncsu.edu/2010/03/03/getting-started-with-ot-framework/

Headscripts:

<?php

/**
 * Minifies the javascript files added via the minifyHeadScript helper using 
 * minify (http://code.google.com/p/minify/)
 *
 */
class Ot_View_Helper_MinifyHeadScript extends Zend_View_Helper_HeadScript
{

    protected $_regKey = 'Ot_View_Helper_MinifyHeadScript';

    public function minifyHeadScript($mode = Zend_View_Helper_HeadScript::FILE, $spec = null, $placement = 'APPEND', array $attrs = array(), $type = 'text/javascript')
    {
        return parent::headScript($mode, $spec, $placement, $attrs, $type);
    }

    public function toString()
    {
        $items = array();
        $scripts = array();
        $baseUrl = $this->getBaseUrl();

        // we can only support files
        foreach ($this as $item) {
            if (isset($item->attributes['src']) && !empty($item->attributes['src'])) {
                $scripts[] = str_replace($baseUrl, '', $item->attributes['src']);
            }
        }

        //remove the slash at the beginning if there is one
        if (substr($baseUrl, 0, 1) == '/') {
            $baseUrl = substr($baseUrl, 1);
        }

        $item = new stdClass();
        $item->type = 'text/javascript';
        $item->attributes['src'] = $this->getMinUrl() . '?b=' . $baseUrl . '&f=' . implode(',', $scripts);
        $scriptTag = $this->itemToString($item, '', '', '');

        return $scriptTag;
    }

    public function getMinUrl() {
        return $this->getBaseUrl() . '/min/';
    }

    public function getBaseUrl(){
        return Zend_Controller_Front::getInstance()->getBaseUrl();
    }
}

And here is the code for headlinks:

<?php

/**
 * Minifies the stylesheets added via the minifyHeadLink helper using 
 * minify (http://code.google.com/p/minify/)
 *
 */
class Ot_View_Helper_MinifyHeadLink extends Zend_View_Helper_HeadLink
{

    protected $_regKey = 'Ot_View_Helper_MinifyHeadLink';

    public function minifyHeadLink(array $attributes = null, $placement = Zend_View_Helper_Placeholder_Container_Abstract::APPEND)
    {
        return parent::headlink($attributes, $placement);
    }

    public function toString()
        {
        $items = array();
        $stylesheets = array();
        $baseUrl = $this->getBaseUrl();

        foreach ($this as $item) {
            if ($item->type == 'text/css' && $item->conditionalStylesheet === false) {
                $stylesheets[$item->media][] = str_replace($baseUrl, '', $item->href);
            } else {
                $items[] = $this->itemToString($item);
            }
        }

        //remove the slash at the beginning if there is one
        if (substr($baseUrl, 0, 1) == '/') {
            $baseUrl = substr($baseUrl, 1);
        }

        foreach ($stylesheets as $media=>$styles) {
            $item = new stdClass();
            $item->rel = 'stylesheet';
            $item->type = 'text/css';
            $item->href = $this->getMinUrl() . '?b=' . $baseUrl . '&f=' . implode(',', $styles);
            $item->media = $media;
            $item->conditionalStylesheet = false;
            $items[] = $this->itemToString($item);
        }

        $link = implode($this->_escape($this->getSeparator()), $items);

        return $link;
    }

    public function getMinUrl() {
        return $this->getBaseUrl() . '/min/';
    }

    public function getBaseUrl(){
        return Zend_Controller_Front::getInstance()->getBaseUrl();
    }
}


I ran across the same problem and ended up writing two drop-in helpers to manage it for me. You can see them at http://blog.hines57.com/2011/03/13/zendframework-minify/ - thanks again. Quick overview for one of them:

 * * ** 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 minifyHeadLink
 * in the same way that you used to call headLink. Here is an example:
 * 
  echo $this->minifyHeadLink('/favicon.ico')             // Whatever was already loaded from Controller.
  ->prependStylesheet('http://example.com/js/sample.css')// 6th
  ->prependStylesheet('/js/jqModal.css')                 // 5th
  ->prependStylesheet('/js/jquery.alerts.css')           // 4th
  ->prependStylesheet('/templates/main.css')             // 3rd
  ->prependStylesheet('/css/project.css.php')            // 2nd
  ->prependStylesheet('/css/jquery.autocomplete.css')    // 1st
  ->appendStylesheet('/css/ie6.css','screen','lt IE 7'); // APPEND to make it Last
 *
 * 
 * This can be interesting because you will notice that 2nd is a php file, and we
 * have a reference to a favicon link in there as well as a reference to a css file on
 * another website. Because minify can't do anything with that php file (runtime configured 
 * css file) nor with CSS on other websites, and order is important,you would notice that 
 * the output in your browser will looks something like:
 * 
   <link href="/min/?f=/css/jquery.autocomplete.css" media="screen" rel="stylesheet" type="text/css" />
   <link href="/css/project.css.php" media="screen" rel="stylesheet" type="text/css" />
   <link href="/min/?f=/templates/main.css,/js/jquery.alerts.css,/js/jqModal.css" media="screen" 
               rel="stylesheet" type="text/css" />
   <link href="http://example.com/js/sample.css" media="screen" rel="stylesheet" type="text/css" />
   <link href="/favicon.ico" rel="shortcut icon" />
   <!--[if lt IE 7]> <link href="/css/ie6.css" media="screen" rel="stylesheet" type="text/css" /><![endif]-->


mmm , sorry i don't have examples but i can help you explaining how ,

my simple workflow would be like this :

1- as a view helper in your custom library folder , create a class that extends the static function of my_minify::minify() you may create a viewhelper that override the functionality of both headLink() and minfy class

2- as a plugin : you might create plugin that runs postdispatch to minify the whole resulting view , more explanation

0

精彩评论

暂无评论...
验证码 换一张
取 消