PluginProbe ʕ •ᴥ•ʔ
LiteSpeed Cache / 7.8.1
LiteSpeed Cache v7.8.1
trunk 1.0.15 1.9.1.1 2.9.9.2 3.6.4 4.6 5.7.0.1 6.5.4 7.0.0.1 7.0.1 7.1 7.2 7.3 7.3.0.1 7.4 7.5 7.5.0.1 7.6 7.6.1 7.6.2 7.7 7.8 7.8.0.1 7.8.1
litespeed-cache / lib / html-min.cls.php
litespeed-cache / lib Last commit date
css_js_min 2 months ago guest.cls.php 2 months ago html-min.cls.php 2 months ago object-cache.php 2 months ago php-compatibility.func.php 2 months ago urirewriter.cls.php 2 months ago
html-min.cls.php
292 lines
1 <?php
2 // phpcs:ignoreFile
3 /**
4 * Compress HTML
5 *
6 * This is a heavy regex-based removal of whitespace, unnecessary comments and
7 * tokens. IE conditional comments are preserved. There are also options to have
8 * STYLE and SCRIPT blocks compressed by callback functions.
9 *
10 * A test suite is available.
11 *
12 * @package Minify
13 * @author Stephen Clay <steve@mrclay.org>
14 */
15 namespace LiteSpeed\Lib;
16
17 defined( 'WPINC' ) || exit;
18
19 class HTML_MIN {
20
21 /**
22 * @var string
23 */
24 protected $_html = '';
25
26 /**
27 * @var boolean
28 */
29 protected $_jsCleanComments = true;
30 protected $_skipComments = array();
31
32 /**
33 * "Minify" an HTML page
34 *
35 * @param string $html
36 *
37 * @param array $options
38 *
39 * 'cssMinifier' : (optional) callback function to process content of STYLE
40 * elements.
41 *
42 * 'jsMinifier' : (optional) callback function to process content of SCRIPT
43 * elements. Note: the type attribute is ignored.
44 *
45 * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
46 * unset, minify will sniff for an XHTML doctype.
47 *
48 * @return string
49 */
50 public static function minify( $html, $options = array() ) {
51 $min = new self( $html, $options );
52
53 return $min->process();
54 }
55
56 /**
57 * Create a minifier object
58 *
59 * @param string $html
60 *
61 * @param array $options
62 *
63 * 'cssMinifier' : (optional) callback function to process content of STYLE
64 * elements.
65 *
66 * 'jsMinifier' : (optional) callback function to process content of SCRIPT
67 * elements. Note: the type attribute is ignored.
68 *
69 * 'jsCleanComments' : (optional) whether to remove HTML comments beginning and end of script block
70 *
71 * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
72 * unset, minify will sniff for an XHTML doctype.
73 */
74 public function __construct( $html, $options = array() ) {
75 $this->_html = str_replace( "\r\n", "\n", trim( $html ) );
76 if ( isset( $options['xhtml'] ) ) {
77 $this->_isXhtml = (bool) $options['xhtml'];
78 }
79 if ( isset( $options['cssMinifier'] ) ) {
80 $this->_cssMinifier = $options['cssMinifier'];
81 }
82 if ( isset( $options['jsMinifier'] ) ) {
83 $this->_jsMinifier = $options['jsMinifier'];
84 }
85 if ( isset( $options['jsCleanComments'] ) ) {
86 $this->_jsCleanComments = (bool) $options['jsCleanComments'];
87 }
88 if ( isset( $options['skipComments'] ) ) {
89 $this->_skipComments = $options['skipComments'];
90 }
91 }
92
93 /**
94 * Minify the markeup given in the constructor
95 *
96 * @return string
97 */
98 public function process() {
99 if ( $this->_isXhtml === null ) {
100 $this->_isXhtml = ( false !== strpos( $this->_html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML' ) );
101 }
102
103 $this->_replacementHash = 'MINIFYHTML' . md5( $_SERVER['REQUEST_TIME'] );
104 $this->_placeholders = array();
105
106 // replace SCRIPTs (and minify) with placeholders
107 $this->_html = preg_replace_callback(
108 '/(\\s*)<script(\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i',
109 array( $this, '_removeScriptCB' ),
110 $this->_html
111 );
112
113 // replace STYLEs (and minify) with placeholders
114 $this->_html = preg_replace_callback(
115 '/\\s*<style(\\b[^>]*>)([\\s\\S]*?)<\\/style>\\s*/i',
116 array( $this, '_removeStyleCB' ),
117 $this->_html
118 );
119
120 // remove HTML comments (not containing IE conditional comments).
121 $this->_html = preg_replace_callback(
122 '/<!--([\\s\\S]*?)-->/',
123 array( $this, '_commentCB' ),
124 $this->_html
125 );
126
127 // replace PREs with placeholders
128 $this->_html = preg_replace_callback(
129 '/\\s*<pre(\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i',
130 array( $this, '_removePreCB' ),
131 $this->_html
132 );
133
134 // replace TEXTAREAs with placeholders
135 $this->_html = preg_replace_callback(
136 '/\\s*<textarea(\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i',
137 array( $this, '_removeTextareaCB' ),
138 $this->_html
139 );
140
141 // trim each line.
142 // @todo take into account attribute values that span multiple lines.
143 $this->_html = preg_replace( '/^\\s+|\\s+$/m', '', $this->_html );
144
145 // remove ws around block/undisplayed elements
146 $this->_html = preg_replace(
147 '/\\s+(<\\/?(?:area|article|aside|base(?:font)?|blockquote|body'
148 . '|canvas|caption|center|col(?:group)?|dd|dir|div|dl|dt|fieldset|figcaption|figure|footer|form'
149 . '|frame(?:set)?|h[1-6]|head|header|hgroup|hr|html|legend|li|link|main|map|menu|meta|nav'
150 . '|ol|opt(?:group|ion)|output|p|param|section|t(?:able|body|head|d|h||r|foot|itle)'
151 . '|ul|video)\\b[^>]*>)/i',
152 '$1',
153 $this->_html
154 );
155
156 // remove ws outside of all elements
157 $this->_html = preg_replace(
158 '/>(\\s(?:\\s*))?([^<]+)(\\s(?:\s*))?</',
159 '>$1$2$3<',
160 $this->_html
161 );
162
163 // use newlines before 1st attribute in open tags (to limit line lengths)
164 // $this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html);
165
166 // fill placeholders
167 $this->_html = str_replace(
168 array_keys( $this->_placeholders ),
169 array_values( $this->_placeholders ),
170 $this->_html
171 );
172 // issue 229: multi-pass to catch scripts that didn't get replaced in textareas
173 $this->_html = str_replace(
174 array_keys( $this->_placeholders ),
175 array_values( $this->_placeholders ),
176 $this->_html
177 );
178
179 return $this->_html;
180 }
181
182 /**
183 * From LSCWP 6.2: Changed the function to test for special comments that will be skipped. See: https://github.com/litespeedtech/lscache_wp/pull/622
184 */
185 protected function _commentCB( $m ) {
186 // If is IE conditional comment return it.
187 if ( 0 === strpos( $m[1], '[' ) || false !== strpos( $m[1], '<![' ) ) {
188 return $m[0];
189 }
190
191 // Check if comment text is present in Page Optimization -> HTML Settings -> HTML Keep comments
192 if ( count( $this->_skipComments ) > 0 ) {
193 foreach ( $this->_skipComments as $comment ) {
194 if ( $comment && strpos( $m[1], $comment ) !== false ) {
195 return $m[0];
196 }
197 }
198 }
199
200 // Comment can be removed.
201 return '';
202 }
203
204 protected function _reservePlace( $content ) {
205 $placeholder = '%' . $this->_replacementHash . count( $this->_placeholders ) . '%';
206 $this->_placeholders[ $placeholder ] = $content;
207
208 return $placeholder;
209 }
210
211 protected $_isXhtml = null;
212 protected $_replacementHash = null;
213 protected $_placeholders = array();
214 protected $_cssMinifier = null;
215 protected $_jsMinifier = null;
216
217 protected function _removePreCB( $m ) {
218 return $this->_reservePlace( "<pre{$m[1]}" );
219 }
220
221 protected function _removeTextareaCB( $m ) {
222 return $this->_reservePlace( "<textarea{$m[1]}" );
223 }
224
225 protected function _removeStyleCB( $m ) {
226 $openStyle = "<style{$m[1]}";
227 $css = $m[2];
228 // remove HTML comments
229 $css = preg_replace( '/(?:^\\s*<!--|-->\\s*$)/', '', $css );
230
231 // remove CDATA section markers
232 $css = $this->_removeCdata( $css );
233
234 // minify
235 $minifier = $this->_cssMinifier
236 ? $this->_cssMinifier
237 : 'trim';
238 $css = call_user_func( $minifier, $css );
239
240 return $this->_reservePlace(
241 $this->_needsCdata( $css )
242 ? "{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>"
243 : "{$openStyle}{$css}</style>"
244 );
245 }
246
247 protected function _removeScriptCB( $m ) {
248 $openScript = "<script{$m[2]}";
249 $js = $m[3];
250
251 // whitespace surrounding? preserve at least one space
252 $ws1 = ( $m[1] === '' ) ? '' : ' ';
253 $ws2 = ( $m[4] === '' ) ? '' : ' ';
254
255 // remove HTML comments (and ending "//" if present)
256 if ( $this->_jsCleanComments ) {
257 $js = preg_replace( '/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js );
258 }
259
260 // remove CDATA section markers
261 $js = $this->_removeCdata( $js );
262
263 // minify
264 /**
265 * Added 2nd param by LiteSpeed
266 *
267 * @since 2.2.3
268 */
269 if ( $this->_jsMinifier ) {
270 $js = call_user_func( $this->_jsMinifier, $js, trim( $m[2] ) );
271 } else {
272 $js = trim( $js );
273 }
274
275 return $this->_reservePlace(
276 $this->_needsCdata( $js )
277 ? "{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}"
278 : "{$ws1}{$openScript}{$js}</script>{$ws2}"
279 );
280 }
281
282 protected function _removeCdata( $str ) {
283 return ( false !== strpos( $str, '<![CDATA[' ) )
284 ? str_replace( array( '<![CDATA[', ']]>' ), '', $str )
285 : $str;
286 }
287
288 protected function _needsCdata( $str ) {
289 return ( $this->_isXhtml && preg_match( '/(?:[<&]|\\-\\-|\\]\\]>)/', $str ) );
290 }
291 }
292