PageRenderTime 123ms CodeModel.GetById 60ms app.highlight 9ms RepoModel.GetById 51ms app.codeStats 0ms

/plugins/system/jch_optimize/cache/HTML.php

https://bitbucket.org/organicdevelopment/joomla-2.5
PHP | 245 lines | 134 code | 35 blank | 76 comment | 6 complexity | 68548d178e00bc344847cf88faac2d37 MD5 | raw file
  1<?php
  2/**
  3 * Class Minify_HTML
  4 * @package Minify
  5 */
  6
  7/**
  8 * Compress HTML
  9 *
 10 * This is a heavy regex-based removal of whitespace, unnecessary comments and
 11 * tokens. IE conditional comments are preserved. There are also options to have
 12 * STYLE and SCRIPT blocks compressed by callback functions.
 13 *
 14 * A test suite is available.
 15 *
 16 * @package Minify
 17 * @author Stephen Clay <steve@mrclay.org>
 18 */
 19class Minify_HTML {
 20
 21    /**
 22     * "Minify" an HTML page
 23     *
 24     * @param string $html
 25     *
 26     * @param array $options
 27     *
 28     * 'cssMinifier' : (optional) callback function to process content of STYLE
 29     * elements.
 30     *
 31     * 'jsMinifier' : (optional) callback function to process content of SCRIPT
 32     * elements. Note: the type attribute is ignored.
 33     *
 34     * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
 35     * unset, minify will sniff for an XHTML doctype.
 36     *
 37     * @return string
 38     */
 39    public static function minify($html, $options = array()) {
 40        $min = new Minify_HTML($html, $options);
 41        return $min->process();
 42    }
 43
 44
 45    /**
 46     * Create a minifier object
 47     *
 48     * @param string $html
 49     *
 50     * @param array $options
 51     *
 52     * 'cssMinifier' : (optional) callback function to process content of STYLE
 53     * elements.
 54     *
 55     * 'jsMinifier' : (optional) callback function to process content of SCRIPT
 56     * elements. Note: the type attribute is ignored.
 57     *
 58     * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
 59     * unset, minify will sniff for an XHTML doctype.
 60     *
 61     * @return null
 62     */
 63    public function __construct($html, $options = array())
 64    {
 65        $this->_html = str_replace("\r\n", "\n", trim($html));
 66        if (isset($options['xhtml'])) {
 67            $this->_isXhtml = (bool)$options['xhtml'];
 68        }
 69        if (isset($options['cssMinifier'])) {
 70            $this->_cssMinifier = $options['cssMinifier'];
 71        }
 72        if (isset($options['jsMinifier'])) {
 73            $this->_jsMinifier = $options['jsMinifier'];
 74        }
 75    }
 76
 77
 78    /**
 79     * Minify the markeup given in the constructor
 80     *
 81     * @return string
 82     */
 83    public function process()
 84    {
 85        if ($this->_isXhtml === null) {
 86            $this->_isXhtml = (false !== strpos($this->_html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML'));
 87        }
 88
 89        $this->_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']);
 90        $this->_placeholders = array();
 91
 92        // replace SCRIPTs (and minify) with placeholders
 93        $this->_html = preg_replace_callback(
 94            '/(\\s*)(<script\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i'
 95            ,array($this, '_removeScriptCB')
 96            ,$this->_html);
 97
 98        // replace STYLEs (and minify) with placeholders
 99        $this->_html = preg_replace_callback(
100            '/\\s*(<style\\b[^>]*?>)([\\s\\S]*?)<\\/style>\\s*/i'
101            ,array($this, '_removeStyleCB')
102            ,$this->_html);
103
104        // remove HTML comments (not containing IE conditional comments).
105        $this->_html = preg_replace_callback(
106            '/<!--([\\s\\S]*?)-->/'
107            ,array($this, '_commentCB')
108            ,$this->_html);
109
110        // replace PREs with placeholders
111        $this->_html = preg_replace_callback('/\\s*(<pre\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i'
112            ,array($this, '_removePreCB')
113            ,$this->_html);
114
115        // replace TEXTAREAs with placeholders
116        $this->_html = preg_replace_callback(
117            '/\\s*(<textarea\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i'
118            ,array($this, '_removeTextareaCB')
119            ,$this->_html);
120
121        // trim each line.
122        // @todo take into account attribute values that span multiple lines.
123        $this->_html = preg_replace('/^\\s+|\\s+$/m', '', $this->_html);
124
125        // remove ws around block/undisplayed elements
126        $this->_html = preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body'
127            .'|caption|center|cite|col(?:group)?|dd|dir|div|dl|dt|fieldset|form'
128            .'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta'
129            .'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)'
130            .'|ul)\\b[^>]*>)/i', '$1', $this->_html);
131
132        // remove ws outside of all elements
133        $this->_html = preg_replace_callback(
134            '/>([^<]+)</'
135            ,array($this, '_outsideTagCB')
136            ,$this->_html);
137
138        // use newlines before 1st attribute in open tags (to limit line lengths)
139        //$this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html);
140
141        // fill placeholders
142        $this->_html = str_replace(
143            array_keys($this->_placeholders)
144            ,array_values($this->_placeholders)
145            ,$this->_html
146        );
147        return $this->_html;
148    }
149
150    protected function _commentCB($m)
151    {
152        return (0 === strpos($m[1], '[') || false !== strpos($m[1], '<!['))
153            ? $m[0]
154            : '';
155    }
156
157    protected function _reservePlace($content)
158    {
159        $placeholder = '%' . $this->_replacementHash . count($this->_placeholders) . '%';
160        $this->_placeholders[$placeholder] = $content;
161        return $placeholder;
162    }
163
164    protected $_isXhtml = null;
165    protected $_replacementHash = null;
166    protected $_placeholders = array();
167    protected $_cssMinifier = null;
168    protected $_jsMinifier = null;
169
170    protected function _outsideTagCB($m)
171    {
172        return '>' . preg_replace('/^\\s+|\\s+$/', ' ', $m[1]) . '<';
173    }
174
175    protected function _removePreCB($m)
176    {
177        return $this->_reservePlace($m[1]);
178    }
179
180    protected function _removeTextareaCB($m)
181    {
182        return $this->_reservePlace($m[1]);
183    }
184
185    protected function _removeStyleCB($m)
186    {
187        $openStyle = $m[1];
188        $css = $m[2];
189        // remove HTML comments
190        $css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/', '', $css);
191
192        // remove CDATA section markers
193        $css = $this->_removeCdata($css);
194
195        // minify
196        $minifier = $this->_cssMinifier
197            ? $this->_cssMinifier
198            : 'trim';
199        $css = call_user_func($minifier, $css);
200
201        return $this->_reservePlace($this->_needsCdata($css)
202            ? "{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>"
203            : "{$openStyle}{$css}</style>"
204        );
205    }
206
207    protected function _removeScriptCB($m)
208    {
209        $openScript = $m[2];
210        $js = $m[3];
211
212        // whitespace surrounding? preserve at least one space
213        $ws1 = ($m[1] === '') ? '' : ' ';
214        $ws2 = ($m[4] === '') ? '' : ' ';
215
216        // remove HTML comments (and ending "//" if present)
217        $js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js);
218
219        // remove CDATA section markers
220        $js = $this->_removeCdata($js);
221
222        // minify
223        $minifier = $this->_jsMinifier
224            ? $this->_jsMinifier
225            : 'trim';
226        $js = call_user_func($minifier, $js);
227
228        return $this->_reservePlace($this->_needsCdata($js)
229            ? "{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}"
230            : "{$ws1}{$openScript}{$js}</script>{$ws2}"
231        );
232    }
233
234    protected function _removeCdata($str)
235    {
236        return (false !== strpos($str, '<![CDATA['))
237            ? str_replace(array('<![CDATA[', ']]>'), '', $str)
238            : $str;
239    }
240
241    protected function _needsCdata($str)
242    {
243        return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str));
244    }
245}