HeadScript.php #1

  • //
  • guest/
  • perforce_software/
  • chronicle/
  • main/
  • library/
  • P4Cms/
  • View/
  • Helper/
  • HeadScript.php
  • View
  • Commits
  • Open Download .zip Download (7 KB)
<?php
/**
 * Aggregating version of HeadScript helper (combines JS files)
 * 
 * @copyright   2011 Perforce Software. All rights reserved.
 * @license     Please see LICENSE.txt in top-level folder of this distribution.
 * @version     <release>/<patch>
 * @todo        absolutize @import statements
 */
class P4Cms_View_Helper_HeadScript extends Zend_View_Helper_HeadScript
{
    protected   $_aggregate     = false;
    protected   $_aggregated    = false;
    protected   $_assetHandler  = null;
    protected   $_documentRoot  = null;

    /**
     * Enable or disable JS aggregation.
     *
     * @param   bool    $aggregate              set to true to enable, false to disable.
     * @return  P4Cms_View_Helper_HeadScript    provides fluent interface.
     */
    public function setAggregateJs($aggregate)
    {
        $this->_aggregate = (bool) $aggregate;
        return $this;
    }

    /**
     * Set the file-system path to the document root.
     * 
     * @param   string  $path                   the location of the public folder.
     * @return  P4Cms_View_Helper_HeadScript    provides fluent interface.
     */
    public function setDocumentRoot($path)
    {
        $this->_documentRoot = rtrim($path, '/\\');
        return $this;
    }

    /**
     * Get the file-system path to the document root.
     * 
     * @return  string  the location of the public folder.
     */
    public function getDocumentRoot()
    {
        return $this->_documentRoot;
    }

    /**
     * Set the asset handler used to store the aggregated JS file(s).
     *
     * @param   P4Cms_AssetHandlerInterface|null    $handler    The handler to use or null
     * @return  P4Cms_View_Helper_HeadScript        provides fluent interface.
     */
    public function setAssetHandler(P4Cms_AssetHandlerInterface $handler = null)
    {
        $this->_assetHandler = $handler;

        return $this;
    }

    /**
     * Get the asset handler used to store the aggregated JS file(s).
     *
     * @return  P4Cms_AssetHandlerInterface|null    the handler to use or null
     */
    public function getAssetHandler()
    {
        return $this->_assetHandler;
    }

    /**
     * Extend toString to aggregate JS files where possible.
     *
     * @param   string|int  $indent  Zend provides no documentation for this param.
     * @return  string
     */
    public function toString($indent = null)
    {
        // aggregate JS files (but only once).
        if ($this->_aggregate && !$this->_aggregated) {
            $this->_aggregateJs();
        }

        return parent::toString();
    }

    /**
     * Aggregate local unconditional JS files.
     * Rebuilds whenever a file changes.
     */
    protected function _aggregateJs()
    {
        // bail out if no asset handler is configured
        if (!$this->getAssetHandler()) {
            P4Cms_Log::log(
                "Failed to aggregate JS. Asset Handler is unset.",
                P4Cms_Log::ERR
            );
            return;
        }

        // bail out if document root is unset.
        if (!$this->getDocumentRoot()) {
            P4Cms_Log::log(
                "Failed to aggregate JS. Document root is unset.",
                P4Cms_Log::ERR
            );
            return;
        }

        // identify JS we can aggregate.
        $time   = 0;
        $files  = array();
        $ignore = array();
        $this->getContainer()->ksort();        
        foreach ($this as $item) {
            
            if (!$this->_isValid($item)) {
                continue;
            }

            // normalize the attributes.
            $attributes = $item->attributes + array(
                'src'           => null,
                'conditional'   => null,
                'charset'       => null,
                'defer'         => null
            );
            
            // only aggregate scripts that:
            //  - are type text/javascript
            //  - have a 'src' attribute
            //  - are local
            //  - are not conditional
            //  - have no explicit charset
            //  - are not deferred
            //  - exist in the document root
            $file = $this->getDocumentRoot() . $attributes['src'];
            if (($item->type !== 'text/javascript' 
                || !$attributes['src']
                || P4Cms_Uri::hasScheme($attributes['src']))
                || $attributes['conditional']
                || $attributes['charset']
                || $attributes['defer']
                || !file_exists($file)
            ) {
                $ignore[] = $item;
                continue;
            }
            
            $files[] = $file;
            $time    = max($time, filemtime($file));
        }
        
        // nothing to do if there are no files to aggregate.
        if (!$files) {
            return;
        }

        // determine if compression should be enabled
        $compressed = $this->_canGzipCompress() && $this->_clientAcceptsGzip();

        // generate build filename.
        $buildFile = md5(implode(',', $files) . $time) 
                   . ($compressed ? '.jsgz' : '.js');

        // rebuild if file does not exist.
        if (!$this->getAssetHandler()->exists($buildFile)) {
            $js = "";
            foreach ($files as $file) {
                $js .= file_get_contents($file) . "\n";
            }

            // also compress if possible.
            if ($compressed) {
                $js = gzencode($js, 9);
            }

            // write out aggregate file - if it fails, abort aggregation.
            if (!$this->getAssetHandler()->put($buildFile, $js)) {
                return;
            }
        }
        
        // if we made it this far aggregation worked; update the
        // list to only contain ignored plus the aggregated scripts.
        $this->getContainer()->exchangeArray($ignore);
        $this->appendFile($this->getAssetHandler()->uri($buildFile));
        
        $this->_aggregated = true;
    }

    /**
     * Check if this PHP can generate gzip compressed data.
     *
     * @return  bool    true if this PHP has gzip support.
     */
    protected function _canGzipCompress()
    {
        return function_exists('gzencode');
    }

    /**
     * Check if the client can accept gzip encoded content.
     *
     * @return  bool    true if the client supports gzip; false otherwise.
     */
    protected function _clientAcceptsGzip()
    {
        $front   = Zend_Controller_Front::getInstance();
        $request = $front->getRequest();
        $accepts = isset($request)
            ? $request->getHeader('Accept-Encoding')
            : '';

        return strpos($accepts, 'gzip') !== false;
    }
}
# Change User Description Committed
#1 16170 perforce_software Move Chronicle files to follow new path scheme for branching.
//guest/perforce_software/chronicle/library/P4Cms/View/Helper/HeadScript.php
#1 8972 Matt Attaway Initial add of the Chronicle source code