PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 4.7.0
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v4.7.0
4.7.0 4.6.1 4.6.0 4.5.5 4.5.4 4.5.3 4.5.2 3.2.20 3.2.21 3.2.22 3.2.3 3.2.5 3.2.6 3.2.7 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.7.0 3.7.1 All 111 releases
← All changes | minify/class-wp-optimize-minify-functions.php +216 -124 3.2.5 → 4.7.0 View file →
@@ -1,43 +1,38 @@
1 1 <?php
2 2
3 3 if (!defined('ABSPATH')) die('No direct access allowed');
4 4
5 -// handle better utf-8 and unicode encoding
5 +// handle better utf-8 and Unicode encoding
6 6 if (function_exists('mb_internal_encoding')) {
7 7 mb_internal_encoding('UTF-8');
8 8 }
9 9
10 10 // must have
11 +// phpcs:disable
12 +// Squiz.PHP.DiscouragedFunctions.Discouraged -- Not applicable here
11 13 ini_set('pcre.backtrack_limit', 5000000);
12 14 ini_set('pcre.recursion_limit', 5000000);
13 -
14 -// Include PHP Minify [1.3.60] - https://github.com/matthiasmullie/minify
15 -if (!class_exists('\MatthiasMullie\Minify\Minify')) {
16 - require_once WPO_PLUGIN_MAIN_PATH.'/vendor/matthiasmullie/minify/src/Minify.php';
17 - require_once WPO_PLUGIN_MAIN_PATH.'/vendor/matthiasmullie/minify/src/CSS.php';
18 - require_once WPO_PLUGIN_MAIN_PATH.'/vendor/matthiasmullie/minify/src/JS.php';
19 - require_once WPO_PLUGIN_MAIN_PATH.'/vendor/matthiasmullie/minify/src/Exception.php';
20 - require_once WPO_PLUGIN_MAIN_PATH.'/vendor/matthiasmullie/minify/src/Exceptions/BasicException.php';
21 - require_once WPO_PLUGIN_MAIN_PATH.'/vendor/matthiasmullie/minify/src//Exceptions/FileImportException.php';
22 - require_once WPO_PLUGIN_MAIN_PATH.'/vendor/matthiasmullie/minify/src/Exceptions/IOException.php';
23 - require_once WPO_PLUGIN_MAIN_PATH.'/vendor/matthiasmullie/path-converter/src/ConverterInterface.php';
24 - require_once WPO_PLUGIN_MAIN_PATH.'/vendor/matthiasmullie/path-converter/src/Converter.php';
25 -}
15 +// phpcs:enable
26 16
27 -use MatthiasMullie\Minify; // phpcs:ignore PHPCompatibility.Keywords.NewKeywords.t_useFound, PHPCompatibility.LanguageConstructs.NewLanguageConstructs.t_ns_separatorFound
17 +use MatthiasMullie\Minify;
28 18
29 -// Use HTML minification
30 -if (!class_exists('Minify_HTML')) {
31 - require_once WPO_PLUGIN_MAIN_PATH.'/vendor/mrclay/minify/lib/Minify/HTML.php';
32 -}
19 +class WP_Optimize_Minify_Functions {
33 20
34 -if (!class_exists('WP_Optimize_Options')) {
35 - include_once WPO_PLUGIN_MAIN_PATH.'/includes/class-wp-optimize-options.php';
36 -}
21 + /**
22 + * Applies `strip_tags` function for given array of messages
23 + *
24 + * @param array $messages Array of messages
25 + * @param string $allowed_tags Tags to retain in message (optional)
26 + *
27 + * @return array
28 + */
29 + public static function apply_strip_tags_for_messages_array($messages, $allowed_tags = '<strong>') {
30 + return array_map(function($message) use ($allowed_tags) {
31 + return strip_tags($message, $allowed_tags);
32 + }, $messages);
33 + }
37 34
38 -class WP_Optimize_Minify_Functions {
39 -
40 35 /**
41 36 * Detect external or internal scripts
42 37 *
43 38 * @param string $src
@@ -63,9 +58,9 @@
63 58 $ret = false;
64 59 foreach ($locations as $l) {
65 60 $l = preg_replace('/^https?:\/\//i', '', trim($l));
66 61 $l = trim(trim(preg_replace('/^www./', '', $l), '/'));
67 - if (stripos($src, $l) !== false && false === $ret) {
62 + if (false !== stripos($src, $l) && false === $ret) {
68 63 $ret = true;
69 64 }
70 65 }
71 66
@@ -75,9 +70,10 @@
75 70
76 71 /**
77 72 * Functions, get hurl info
78 73 *
79 - * @param string $src
74 + * @param mixed $src
75 + *
80 76 * @return string
81 77 */
82 78 public static function get_hurl($src) {
83 79 $wp_home = site_url();
@@ -82,9 +78,9 @@
82 78 public static function get_hurl($src) {
83 79 $wp_home = site_url();
84 80 $wp_domain = trim(str_ireplace(array('http://', 'https://'), '', trim($wp_home, '/')));
85 81 // preserve empty source handles
86 - $hurl = trim($src);
82 + $hurl = null === $src ? '' : trim($src);
87 83 if (empty($hurl)) {
88 84 return $hurl;
89 85 }
90 86
@@ -100,15 +96,15 @@
100 96 // make sure wp_home doesn't have a forward slash
101 97 $wp_home = rtrim($wp_home, '/');
102 98
103 99 // apply some filters
104 - if (substr($hurl, 0, 2) === "//") {
100 + if ("//" === substr($hurl, 0, 2)) {
105 101 $hurl = $protocol.ltrim($hurl, "/");
106 102 }//end if
107 - if (substr($hurl, 0, 4) === "http" && stripos($hurl, $wp_domain) === false) {
103 + if ("http" === substr($hurl, 0, 4) && false === stripos($hurl, $wp_domain)) {
108 104 return $hurl;
109 105 }//end if
110 - if (substr($hurl, 0, 4) !== "http" && stripos($hurl, $wp_domain) !== false) {
106 + if ("http" !== substr($hurl, 0, 4) && false !== stripos($hurl, $wp_domain)) {
111 107 $hurl = $wp_home.'/'.ltrim($hurl, "/");
112 108 }//end if
113 109
114 110 // prevent double forward slashes in the middle
@@ -129,9 +125,9 @@
129 125 // protocol + home for relative paths
130 126 if ("/".WPINC === substr($hurl, 0, 12)
131 127 || "/wp-admin" === substr($hurl, 0, 9)
132 128 || "/$wp_content_folder" === substr($hurl, 0, 11)
133 - || 1 == $proceed
129 + || 1 === $proceed
134 130 ) {
135 131 $hurl = $wp_home.'/'.ltrim($hurl, "/");
136 132 }
137 133
@@ -138,13 +134,13 @@
138 134 // make sure there is a protocol prefix as required
139 135 $hurl = $protocol.preg_replace('/^https?:\/\//i', '', $hurl); // enforce protocol
140 136
141 137 // no query strings
142 - if (stripos($hurl, '.js?v') !== false) {
143 - $hurl = stristr($hurl, '.js?v', true).'.js'; // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctionParameters.stristr_before_needleFound
138 + if (false !== stripos($hurl, '.js?v')) {
139 + $hurl = stristr($hurl, '.js?v', true).'.js';
144 140 }//end if
145 - if (stripos($hurl, '.css?v') !== false) {
146 - $hurl = stristr($hurl, '.css?v', true).'.css'; // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctionParameters.stristr_before_needleFound
141 + if (false !== stripos($hurl, '.css?v')) {
142 + $hurl = stristr($hurl, '.css?v', true).'.css';
147 143 }//end if
148 144
149 145 return $hurl;
150 146 }
@@ -162,18 +158,18 @@
162 158
163 159 if (substr($hurl, 0, strlen($wp_home)) === $wp_home) {
164 160 return true;
165 161 }
166 - if (stripos($hurl, $wp_home) !== false) {
162 + if (false !== stripos($hurl, $wp_home)) {
167 163 return true;
168 164 }
169 - if (isset($_SERVER['HTTP_HOST']) && stripos($hurl, preg_replace('/:\d+$/', '', $_SERVER['HTTP_HOST'])) !== false) {
165 + if (isset($_SERVER['HTTP_HOST']) && false !== stripos($hurl, preg_replace('/:\d+$/', '', sanitize_text_field(wp_unslash($_SERVER['HTTP_HOST']))))) {
170 166 return true;
171 167 }
172 - if (isset($_SERVER['SERVER_NAME']) && stripos($hurl, preg_replace('/:\d+$/', '', $_SERVER['SERVER_NAME'])) !== false) {
168 + if (isset($_SERVER['SERVER_NAME']) && false !== stripos($hurl, preg_replace('/:\d+$/', '', sanitize_text_field(wp_unslash($_SERVER['SERVER_NAME']))))) {
173 169 return true;
174 170 }
175 - if (isset($_SERVER['SERVER_ADDR']) && '::1' != $_SERVER['SERVER_ADDR'] && stripos($hurl, preg_replace('/:\d+$/', '', $_SERVER['SERVER_ADDR'])) !== false) {
171 + if (isset($_SERVER['SERVER_ADDR']) && '::1' !== sanitize_text_field(wp_unslash($_SERVER['SERVER_ADDR'])) && false !== stripos($hurl, preg_replace('/:\d+$/', '', sanitize_text_field(wp_unslash($_SERVER['SERVER_ADDR']))))) {
176 172 return true;
177 173 }
178 174
179 175 // allow specific external urls to be merged
@@ -180,9 +176,9 @@
180 176 if (null === $noxtra) {
181 177 $merge_allowed_urls = array_map('trim', explode("\n", $wpo_minify_options['merge_allowed_urls']));
182 178 if (is_array($merge_allowed_urls) && strlen(implode($merge_allowed_urls)) > 0) {
183 179 foreach ($merge_allowed_urls as $e) {
184 - if (stripos($hurl, $e) !== false && !empty($e)) {
180 + if (false !== stripos($hurl, $e) && !empty($e)) {
185 181 return true;
186 182 }
187 183 }
188 184 }
@@ -215,8 +211,20 @@
215 211 return false;
216 212 }
217 213
218 214 /**
215 + * Check if selected url is point to already minified css/js file
216 + *
217 + * @param string $url
218 + * @return bool
219 + */
220 + public static function is_minified_css_js_filename($url) {
221 + $parts = wp_parse_url($url);
222 + if (empty($parts['path']) || !is_string(basename($parts['path']))) return false;
223 + return 1 === preg_match('/\.min\.(js|css)$/i', basename($parts['path']));
224 + }
225 +
226 + /**
219 227 * Better compatibility urls + fix w3.org NamespaceAndDTDIdentifiers
220 228 *
221 229 * @param string $code
222 230 * @return string
@@ -223,11 +231,11 @@
223 231 * */
224 232 private static function compat_urls($code) {
225 233 $wpo_minify_options = wp_optimize_minify_config()->get();
226 234 $default_protocol = $wpo_minify_options['default_protocol'];
227 - if ('dynamic' == $default_protocol) {
228 - if ((isset($_SERVER['HTTPS']) && ('on' == $_SERVER['HTTPS'] || 1 == $_SERVER['HTTPS']))
229 - || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && 'https' == $_SERVER['HTTP_X_FORWARDED_PROTO'])
235 + if ('dynamic' === $default_protocol) {
236 + if ((isset($_SERVER['HTTPS']) && ('on' === $_SERVER['HTTPS'] || 1 === (int) $_SERVER['HTTPS']))
237 + || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO'])
230 238 ) {
231 239 $default_protocol = 'https://';
232 240 } else {
233 241 $default_protocol = 'http://';
@@ -247,9 +255,9 @@
247 255 * @return string
248 256 */
249 257 public static function minify_css_string($css) {
250 258 $css = apply_filters('wpo_minify_css_string', $css);
251 - $minifier = new Minify\CSS($css); // phpcs:ignore PHPCompatibility.LanguageConstructs.NewLanguageConstructs.t_ns_separatorFound
259 + $minifier = new Minify\CSS($css);
252 260 $minifier->setMaxImportSize(15); // [css only] embed assets up to 15 Kb (default 5Kb) - processes gif, png, jpg, jpeg, svg & woff
253 261 $min = $minifier->minify();
254 262 if (false !== $min) {
255 263 return self::compat_urls($min);
@@ -270,16 +278,21 @@
270 278
271 279 // exclude minification on already minified files + jquery (because minification might break those)
272 280 $excl = array('jquery.js', '.min.js', '-min.js', '/uploads/fusion-scripts/', '/min/', '.packed.js', '/includes/builder/scripts/');
273 281 foreach ($excl as $e) {
274 - if (stripos(basename($url), $e) !== false) {
282 + if (false !== stripos(basename($url), $e)) {
275 283 $enable_js_minification = false;
276 284 break;
277 285 }
278 286 }
287 +
288 + $encoding = mb_detect_encoding($js);
289 +
279 290 // remove BOM
280 291 $js = self::remove_utf8_bom($js);
281 292
293 + self::maybe_log_error_message($url, $encoding, $js);
294 +
282 295 // minify JS
283 296 if ($enable_js_minification) {
284 297 $js = self::minify_js_string($js);
285 298 } else {
@@ -290,9 +303,9 @@
290 303 $js = preg_replace('/(\/\/\s*[#]\s*sourceMappingURL\s*[=]\s*)(.+)\s*/ui', '', $js);
291 304
292 305 // needed when merging js files
293 306 $js = trim($js);
294 - if (substr($js, -1) != ';') {
307 + if (';' !== substr($js, -1)) {
295 308 $js = $js.';';
296 309 }
297 310 if ($wpo_minify_options['debug']) {
298 311 $js = '/* info: ' . $url . ' */' . "\n" . $js;
@@ -304,9 +317,10 @@
304 317 * @param string $js - The imported JS
305 318 * @param string $url - The imported url
306 319 * @param boolean $enable_js_minification - Whether to minify or not
307 320 */
308 - return apply_filters('wpo_minify_get_js', $js . "\n", $url, $enable_js_minification);
321 + $filtered_js = apply_filters('wpo_minify_get_js', $js . "\n", $url, $enable_js_minification);
322 + return is_string($filtered_js) ? $filtered_js : $js;
309 323 }
310 324
311 325 /**
312 326 * Minify JS string with PHP Minify or YUI Compressors
@@ -316,11 +330,11 @@
316 330 */
317 331 public static function minify_js_string($js) {
318 332 $js = apply_filters('wpo_minify_js_string', $js);
319 333 // PHP Minify from https://github.com/matthiasmullie/minify
320 - $minifier = new Minify\JS($js); // phpcs:ignore PHPCompatibility.LanguageConstructs.NewLanguageConstructs.t_ns_separatorFound
334 + $minifier = new Minify\JS($js);
321 335 $min = $minifier->minify();
322 - if (false !== $min && (strlen(trim($js)) == strlen(trim($min)) || strlen(trim($min)) > 0)) {
336 + if (false !== $min && (strlen(trim($js)) === strlen(trim($min)) || strlen(trim($min)) > 0)) {
323 337 return self::compat_urls($min);
324 338 }
325 339
326 340 // if we are here, something went wrong and minification didn't work
@@ -341,8 +355,19 @@
341 355 return self::minify_js_string($js, true);
342 356 }
343 357
344 358 /**
359 + * Check if the JS code contains import statements
360 + *
361 + * @param string $js
362 + * @return boolean
363 + */
364 + public static function has_js_import_statements($js) {
365 + $js = preg_replace('/\/\/.*|\/\*[\s\S]*?\*\//', '', $js); // Remove comments to avoid false positives
366 + return 1 === preg_match('/\bimport\b/', $js);
367 + }
368 +
369 + /**
345 370 * Functions, minify html
346 371 *
347 372 * @param string $html
348 373 * @return string
@@ -375,9 +400,9 @@
375 400 *
376 401 * @return void
377 402 */
378 403 public static function html_compression_start() {
379 - if (self::exclude_contents() == true) {
404 + if (self::exclude_contents()) {
380 405 return;
381 406 }
382 407 ob_start(array(__CLASS__, 'html_compression_finish'));
383 408 }
@@ -401,12 +426,16 @@
401 426 * @return string
402 427 */
403 428 public static function get_css($url, $css, $enable_css_minification) {
404 429 $wpo_minify_options = wp_optimize_minify_config()->get();
405 -
430 +
431 + $encoding = mb_detect_encoding($css);
432 +
406 433 // remove BOM
407 434 $css = self::remove_utf8_bom($css);
408 435
436 + self::maybe_log_error_message($url, $encoding, $css);
437 +
409 438 // fix url paths
410 439 if (!empty($url)) {
411 440 $css = self::make_css_urls_absolute($css, $url);
412 441 }
@@ -450,9 +479,10 @@
450 479 * @param string $css - The imported CSS
451 480 * @param string $url - The imported url
452 481 * @param boolean $enable_css_minification - Whether to minify or not
453 482 */
454 - return apply_filters('wpo_minify_get_css', $css, $url, $enable_css_minification);
483 + $filtered_css = apply_filters('wpo_minify_get_css', $css, $url, $enable_css_minification);
484 + return is_string($filtered_css) ? $filtered_css : $css;
455 485 }
456 486
457 487 /**
458 488 * Adds full path to relative url() rules
@@ -465,13 +495,13 @@
465 495 $matches = array();
466 496 preg_match_all("/url\(\s*['\"]?(?!data:)(?!http)(?![\/'\"])(.+?)['\"]?\s*\)/ui", $css, $matches);
467 497 foreach ($matches[1] as $a) {
468 498 $b = trim($a);
469 - if ($b != $a) {
499 + if ($b !== $a) {
470 500 $css = str_replace($a, $b, $css);
471 501 }
472 502 }
473 - return preg_replace("/url\(\s*['\"]?(?!data:)(?!http)(?![\/'\"])(.+?)['\"]?\s*\)/ui", "url(".dirname($url)."/$1)", $css);
503 + return (string) preg_replace("/url\(\s*['\"]?(?!data:)(?!http)(?![\/'\"])(.+?)['\"]?\s*\)/ui", "url(".dirname($url)."/$1)", $css);
474 504 }
475 505
476 506 /**
477 507 * Include @import[ed] files - The @import statement can only be used at the top of a file, which breaks when merging everything.
@@ -482,9 +512,9 @@
482 512 */
483 513 public static function replace_css_import($css, $file_url) {
484 514 $remove_print_mediatypes = wp_optimize_minify_config()->get('remove_print_mediatypes');
485 515 $debug = wp_optimize_minify_config()->get('debug');
486 - return preg_replace_callback('/(?:@import)\s(?:url\()?\s?["\'](.*?)["\']\s?\)?(?:[^;]*);?/im', function($matches) use ($file_url, $remove_print_mediatypes, $debug) { // phpcs:ignore PHPCompatibility.FunctionDeclarations.NewClosure.Found
516 + return preg_replace_callback('/(?:@import)\s(?:url\()?\s?["\'](.*?)["\']\s?\)?(?:[^;]*);?/im', function($matches) use ($file_url, $remove_print_mediatypes, $debug) {
487 517 // @import contains url()
488 518 if (preg_match('/url\s*\((.[^\)]*)[\)*?](.*);/', $matches[0], $url_matches)) {
489 519 $url = trim(str_replace(array('"', "'"), '', $url_matches[1]));
490 520 $media_query = trim($url_matches[2]);
@@ -496,9 +526,9 @@
496 526
497 527 // If $media_query contains print, and $remove_print_mediatypes is true, return empty string
498 528 if ($remove_print_mediatypes && false !== strpos($media_query, 'print') && apply_filters('wpo_minfy_remove_print_mediatypes_import', true, $url, $media_query, $matches[0], $file_url)) return ($debug ? '/*! Info: the import of "'.$url.'" was removed because the setting remove_print_mediatypes is enabled. */' : '');
499 529
500 - $purl = parse_url($url);
530 + $purl = wp_parse_url($url);
501 531 // If there's no host, the url is relative to $file_url, so prepend with the base url.
502 532 if (!isset($purl['host'])) {
503 533 $url = dirname($file_url).'/'.$url;
504 534 }
@@ -532,18 +562,20 @@
532 562
533 563 /**
534 564 * Download and cache css and js files
535 565 *
536 - * @param string $hurl
537 - * @param string $inline
566 + * @param ?string $hurl
567 + * @param ?string $inline
538 568 * @param boolean $enable_minification
539 569 * @param string $type
540 - * @param string $handle
570 + * @param ?string $handle
571 + * @param mixed $version
572 + *
541 573 * @return boolean|string
542 574 */
543 575 public static function download_and_minify($hurl, $inline, $enable_minification, $type, $handle, $version = '') {
544 576 // must have
545 - if (is_null($hurl) || empty($hurl)) {
577 + if (empty($hurl)) {
546 578 return false;
547 579 }
548 580 if (!in_array($type, array('js', 'css'))) {
549 581 return false;
@@ -558,16 +590,16 @@
558 590 'url' => $print_url,
559 591 );
560 592
561 593 // defaults
562 - if (false != $enable_minification) {
594 + if (false !== $enable_minification) {
563 595 $enable_minification = true;
564 596 }
565 - if (is_null($inline) || empty($inline)) {
597 + if (empty($inline)) {
566 598 $inline = '';
567 599 }
568 600 $print_handle = '';
569 - if (is_null($handle) || empty($handle)) {
601 + if (empty($handle)) {
570 602 $handle = '';
571 603 } else {
572 604 $print_handle = "[$handle]";
573 605 }
@@ -591,12 +623,12 @@
591 623 if ($wpo_minify_options['debug']) {
592 624 $log['debug'] = "$print_handle failed. Tried wp_remote_get and local file_get_contents.";
593 625 }
594 626 $return = array('request' => $dreq, 'log' => $log, 'code' => '', 'status' => false);
595 - return json_encode($return);
627 + return wp_json_encode($return);
596 628 }
597 629
598 - if ('js' == $type) {
630 + if ('js' === $type) {
599 631 $code = self::get_js($hurl, $code, $enable_minification);
600 632 } else {
601 633 $code = self::get_css($hurl, $code.$inline, $enable_minification);
602 634 }
@@ -602,18 +634,18 @@
602 634 }
603 635
604 636 // log, save and return
605 637 if ($wpo_minify_options['debug']) {
606 - $version_msg = ('' != $version) ? "[Version: $version]" : "";
638 + $version_msg = ('' !== $version) ? "[Version: $version]" : "";
607 639 $log['debug'] = $print_handle . $version_msg . ' was '.('local' === $asset_content['method'] ? 'opened' : 'fetched').' from '.$hurl;
608 640 }
609 641 $log['success'] = true;
610 642 $return = array('request' => $dreq, 'log' => $log, 'code' => $code, 'status' => true);
611 - return json_encode($return);
643 + return wp_json_encode($return);
612 644 }
613 645
614 646 /**
615 - * Get the content of an asset, wether local or remote
647 + * Get the content of an asset, whether local or remote
616 648 *
617 649 * @param string $url
618 650 * @return array
619 651 */
@@ -619,14 +651,14 @@
619 651 */
620 652 public static function get_asset_content($url) {
621 653
622 654 $wp_home = site_url();
623 - $wp_domain = parse_url($wp_home, PHP_URL_HOST);
655 + $wp_domain = wp_parse_url($wp_home, PHP_URL_HOST);
624 656 // If the file is local.
625 657 if (false !== stripos($url, $wp_domain)) {
626 658 // default
627 659 $f = str_ireplace(rtrim($wp_home, '/'), rtrim(ABSPATH, '/'), $url);
628 - // failover when home_url != site_url
660 + // fail over when home_url != site_url
629 661 if (!file_exists($f)) {
630 662 $nhurl = str_ireplace(site_url(), home_url(), $url);
631 663 $f = str_ireplace(rtrim($wp_home, '/'), rtrim(ABSPATH, '/'), $nhurl);
632 664 }
@@ -633,9 +665,9 @@
633 665 clearstatcache();
634 666 if (file_exists($f)) {
635 667 $content = file_get_contents($f);
636 668 // check for php code, skip if found
637 - if ("<?php" != strtolower(substr($content, 0, 5)) && false === stripos($content, "<?php")) {
669 + if ("<?php" !== strtolower(substr($content, 0, 5)) && false === stripos($content, "<?php")) {
638 670 return array('content' => $content, 'method' => 'local');
639 671 }
640 672 }
641 673 }
@@ -644,9 +676,9 @@
644 676 // else, fallback to remote urls (or windows)
645 677 $content = self::download_remote($url);
646 678 if (false !== $content
647 679 && !empty($content)
648 - && strtolower(substr($content, 0, 9)) != "<!doctype"
680 + && "<!doctype" !== strtolower(substr($content, 0, 9))
649 681 ) {
650 682 // check if we got HTML instead of js or css code
651 683 return array('content' => $content, 'method' => 'remote');
652 684 }
@@ -652,12 +684,12 @@
652 684 }
653 685
654 686
655 687 // fallback when home_url != site_url
656 - if (stripos($url, $wp_domain) !== false && home_url() != site_url()) {
688 + if (false !== stripos($url, $wp_domain) && home_url() !== site_url()) {
657 689 $nhurl = str_ireplace(site_url(), home_url(), $url);
658 690 $content = self::download_remote($nhurl);
659 - if (false !== $content && !empty($content) && '<!doctype' != strtolower(substr($content, 0, 9))) {
691 + if (false !== $content && !empty($content) && '<!doctype' !== strtolower(substr($content, 0, 9))) {
660 692 return array('content' => $content, 'method' => 'remote');
661 693 }
662 694 }
663 695
@@ -672,9 +704,11 @@
672 704 public static function disable_wp_emojicons() {
673 705 remove_action('wp_head', 'print_emoji_detection_script', 7);
674 706 remove_action('admin_print_scripts', 'print_emoji_detection_script');
675 707 remove_action('wp_print_styles', 'print_emoji_styles');
708 + remove_action('wp_enqueue_scripts', 'wp_enqueue_emoji_styles');
676 709 remove_action('admin_print_styles', 'print_emoji_styles');
710 + remove_action('admin_enqueue_scripts', 'wp_enqueue_emoji_styles');
677 711 remove_filter('the_content_feed', 'wp_staticize_emoji');
678 712 remove_filter('comment_text_rss', 'wp_staticize_emoji');
679 713 remove_filter('wp_mail', 'wp_staticize_emoji_for_email');
680 714 }
@@ -692,17 +726,17 @@
692 726 }
693 727 }
694 728
695 729 /**
696 - * Remove UTF8 BOM
730 + * Remove UTF8 BOM.
731 + * Returns BOM removed string or null when `$string` does not have a recognised encoding
697 732 *
698 733 * @param string $string
699 - * @return string
734 + * @return string|null
700 735 */
701 736 public static function remove_utf8_bom($string) {
702 737 $bom = pack('H*', 'EFBBBF');
703 - $string = preg_replace("/^$bom/ui", '', $string);
704 - return $string;
738 + return preg_replace("/^$bom/ui", '', $string);
705 739 }
706 740
707 741 /**
708 742 * Remove query string from static css files
@@ -710,9 +744,9 @@
710 744 * @param string $src
711 745 * @return string
712 746 */
713 747 public static function remove_cssjs_ver($src) {
714 - if (stripos($src, '?ver=')) {
748 + if (stripos($src, '?ver=') && self::is_already_minified($src)) {
715 749 $src = remove_query_arg('ver', $src);
716 750 }
717 751 return $src;
718 752 }
@@ -717,8 +751,18 @@
717 751 return $src;
718 752 }
719 753
720 754 /**
755 + * Determine if the source is already minified (served from minify cache)
756 + *
757 + * @param string $src
758 + * @return boolean
759 + */
760 + public static function is_already_minified($src) {
761 + return false !== strpos($src, 'cache/wpo-minify');
762 + }
763 +
764 + /**
721 765 * Rewrite cache files to http, https or dynamic
722 766 *
723 767 * @param string $url
724 768 * @return string
@@ -734,12 +778,12 @@
734 778 $cdn_url = $wpo_minify_options['cdn_url'];
735 779 $cdn_url = trim(trim(preg_replace('/^https?:\/\//i', '', trim($cdn_url, '/'))), '/');
736 780
737 781 // process cdn rewrite
738 - if (!empty($cdn_url) && self::is_local_domain($url) !== false) {
782 + if (!empty($cdn_url) && false !== self::is_local_domain($url)) {
739 783
740 784 // for js files, we need to consider thew defer for insights option
741 - if (substr($url, -3) == '.js') {
785 + if ('.js' === substr($url, -3)) {
742 786 $async_using_js = 'all' === $wpo_minify_options['enable_defer_js'] && 'async_using_js' === $wpo_minify_options['defer_js_type'];
743 787 if (!$async_using_js
744 788 || $wpo_minify_options['cdn_force']
745 789 ) {
@@ -750,11 +794,11 @@
750 794 }
751 795 }
752 796
753 797 // enforce protocol if needed
754 - if ('dynamic' == $default_protocol) {
755 - if ((isset($_SERVER['HTTPS']) && ('on' == $_SERVER['HTTPS'] || 1 == $_SERVER['HTTPS']))
756 - || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && 'https' == $_SERVER['HTTP_X_FORWARDED_PROTO'])
798 + if ('dynamic' === $default_protocol) {
799 + if ((isset($_SERVER['HTTPS']) && ('on' === $_SERVER['HTTPS'] || 1 === (int) $_SERVER['HTTPS']))
800 + || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO'])
757 801 ) {
758 802 $default_protocol = 'https://';
759 803 } else {
760 804 $default_protocol = 'http://';
@@ -776,9 +820,9 @@
776 820 // prevent execution for specific urls
777 821 if (isset($_SERVER['REQUEST_URI']) && !empty($_SERVER['REQUEST_URI'])) {
778 822 $disable_on_url = array_filter(array_map('trim', explode("\n", get_option('wpo_min_disable_on_url', ''))));
779 823 foreach ($disable_on_url as $url) {
780 - if (parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) == $url) {
824 + if (wp_parse_url(esc_url_raw(wp_unslash($_SERVER['REQUEST_URI'])), PHP_URL_PATH) === $url) {
781 825 return true;
782 826 }
783 827 }
784 828 }
@@ -783,11 +827,16 @@
783 827 }
784 828 }
785 829
786 830 // for compatibility, let's always skip the checkout page
787 - if (function_exists('is_checkout') && is_checkout() === true) {
831 + if (function_exists('is_checkout') && true === is_checkout()) {
788 832 return true;
789 833 }
834 +
835 + if (isset($_SERVER['REQUEST_URI'])) {
836 + $is_txt_extension = '.txt' === strtolower(substr(esc_url_raw(wp_unslash($_SERVER['REQUEST_URI'])), -4));
837 + $is_xml_extension = '.xml' === strtolower(substr(esc_url_raw(wp_unslash($_SERVER['REQUEST_URI'])), -4));
838 + }
790 839
791 840 // exclude processing here
792 841 if (is_feed()
793 842 || is_admin()
@@ -804,14 +853,16 @@
804 853 || (defined('XMLRPC_REQUEST') && XMLRPC_REQUEST)
805 854 || (defined('SHORTINIT') && SHORTINIT)
806 855 || (defined('REST_REQUEST') && REST_REQUEST)
807 856 || (isset($_SERVER['REQUEST_METHOD']) && 'POST' === $_SERVER['REQUEST_METHOD'])
808 - || (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest')
809 - || (isset($_SERVER['REQUEST_URI']) && (strtolower(substr($_SERVER['REQUEST_URI'], -4)) == '.txt' || strtolower(substr($_SERVER['REQUEST_URI'], -4)) == '.xml'))
857 + || (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && 'xmlhttprequest' === strtolower(sanitize_text_field(wp_unslash($_SERVER['HTTP_X_REQUESTED_WITH']))))
858 + || (isset($_SERVER['REQUEST_URI']) && ($is_txt_extension || $is_xml_extension))
810 859 ) {
811 860 return true;
812 861 }
813 862
863 + // phpcs:disable
864 + // WordPress.Security.NonceVerification.Recommended -- Using $_GET element only to compare, returns boolean
814 865 // Thrive plugins and other post_types
815 866 $arr = array('tve_form_type', 'tve_lead_shortcode', 'tqb_splash');
816 867 foreach ($arr as $a) {
817 868 if (isset($_GET['post_type']) && $a === $_GET['post_type']) {
@@ -825,9 +876,9 @@
825 876
826 877 if (is_array($_GET)) {
827 878 foreach ($_GET as $k => $v) {
828 879 if (is_string($v) && is_string($k)) {
829 - if (stripos($k, 'elementor') !== false || stripos($v, 'elementor') !== false) {
880 + if (false !== stripos($k, 'elementor') || false !== stripos($v, 'elementor')) {
830 881 return true;
831 882 }
832 883 }
833 884 }
@@ -854,13 +905,14 @@
854 905 'trp-edit-translation',
855 906 );
856 907 return (bool) count(array_intersect($excluded_params, $get_params));
857 908 }
909 + // phpcs:enable
858 910
859 911 /**
860 - * Wether to exclude the content or not from the minifying process.
912 + * Whether to exclude the content or not from the minifying process.
861 913 */
862 - return apply_filters('wpo_minify_exclude_contents', false);
914 + return (bool) apply_filters('wpo_minify_exclude_contents', false);
863 915 }
864 916
865 917 /**
866 918 * Get the default files which are ignored / excluded from processing
@@ -867,15 +919,9 @@
867 919 *
868 920 * @return array
869 921 */
870 922 public static function get_default_ignore() {
871 - /**
872 - * Filters the default exclusions
873 - *
874 - * @param array The exclusions
875 - * @return array
876 - */
877 - return apply_filters('wp-optimize-minify-default-exclusions', array(
923 + $default_exclusions = array(
878 924 '/genericons.css',
879 925 '/Avada/assets/js/main.min.js',
880 926 '/woocommerce-product-search/js/product-search.js',
881 927 '/includes/builder/scripts/frontend-builder-scripts.js',
@@ -889,9 +935,17 @@
889 935 'elementor-admin-bar',
890 936 'pdfjs-dist',
891 937 'wordpress-popular-posts',
892 938 'uploads/bb-plugin/cache', // Beaver builder page specific pages
893 - ));
939 + );
940 + /**
941 + * Filters the default exclusions
942 + *
943 + * @param array The exclusions
944 + * @return array
945 + */
946 + $filtered_exclusions = apply_filters('wp-optimize-minify-default-exclusions', $default_exclusions);
947 + return is_array($filtered_exclusions) ? $filtered_exclusions : $default_exclusions;
894 948 }
895 949
896 950 /**
897 951 * Know files that should always be ignored
@@ -916,15 +970,9 @@
916 970 *
917 971 * @return array
918 972 */
919 973 public static function get_default_ie_blacklist() {
920 - /**
921 - * Filters the default IE specific / blacklisted items
922 - *
923 - * @param array The blacklist
924 - * @return array
925 - */
926 - return apply_filters('wp-optimize-minify-blacklist', array(
974 + $default_ie_blacklist = array(
927 975 '/html5shiv.js',
928 976 '/html5shiv-printshiv.min.js',
929 977 '/excanvas.js',
930 978 '/avada-ie9.js',
@@ -944,9 +992,17 @@
944 992 '/a.optnmstr.com/app/js/api.min.js',
945 993 '/pixelyoursite/js/public.js',
946 994 '/assets/js/wcdrip-drip.js',
947 995 '/instantpage.js',
948 - ));
996 + );
997 + /**
998 + * Filters the default IE specific / blacklisted items
999 + *
1000 + * @param array The blacklist
1001 + * @return array
1002 + */
1003 + $filtered_ie_blacklist = apply_filters('wp-optimize-minify-blacklist', $default_ie_blacklist);
1004 + return is_array($filtered_ie_blacklist) ? $filtered_ie_blacklist : $default_ie_blacklist;
949 1005 }
950 1006
951 1007 /**
952 1008 * Get the files excluded for IE compatibility
@@ -973,9 +1029,9 @@
973 1029 $blacklist[] = '/wpo_min/cache/';
974 1030
975 1031 // is the url on our list and return
976 1032 $res = self::in_arrayi($url, $blacklist);
977 - if (true == $res) {
1033 + if ($res) {
978 1034 return true;
979 1035 } else {
980 1036 return false;
981 1037 }
@@ -984,19 +1040,19 @@
984 1040 /**
985 1041 * Download function with fallback
986 1042 *
987 1043 * @param string $url
988 - * @return boolean
1044 + * @return boolean|string
989 1045 */
990 1046 public static function download_remote($url) {
991 1047
992 1048 $args = array(
993 - // info (needed for google fonts woff files + hinted fonts) as well as to bypass some security filters
994 - 'user-agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36',
1049 + // info (needed for Google fonts woff files + hinted fonts) as well as to bypass some security filters
1050 + 'user-agent' => WP_Optimize_Utils::get_user_agent('gfont'),
995 1051 'timeout' => 7
996 1052 );
997 1053
998 - // fetch via wordpress functions
1054 + // fetch via WordPress functions
999 1055 $response = wp_remote_get(
1000 1056 $url,
1001 1057 /**
1002 1058 * Filters the arguments passed to wp_remote_get when downloading the scripts.
@@ -1008,9 +1064,9 @@
1008 1064 apply_filters('wpo_minify_download_request_args', $args, $url)
1009 1065 );
1010 1066
1011 1067 $res_code = wp_remote_retrieve_response_code($response);
1012 - if (200 == $res_code) {
1068 + if (200 === $res_code) {
1013 1069 $data = wp_remote_retrieve_body($response);
1014 1070 if (strlen($data) > 1) {
1015 1071 return $data;
1016 1072 }
@@ -1034,25 +1090,25 @@
1034 1090 return $script;
1035 1091 }
1036 1092
1037 1093 /**
1038 - * Checks if an URL is a font-awesome resource (checks if it contains font-awesome or fontawesome)
1094 + * Checks if URL is a font-awesome resource (checks if it contains font-awesome or fontawesome)
1039 1095 *
1040 1096 * @param string $href
1041 1097 * @return boolean
1042 1098 */
1043 1099 public static function is_font_awesome($href) {
1044 - return (boolean) preg_match('/font[-_]?awesome/i', $href);
1100 + return (bool) preg_match('/font[-_]?awesome/i', $href);
1045 1101 }
1046 1102
1047 1103 /**
1048 - * Checks if an URL is a google font resource
1104 + * Checks if URL is a Google font resource
1049 1105 *
1050 1106 * @param string $href
1051 1107 * @return boolean
1052 1108 */
1053 1109 public static function is_google_font($href) {
1054 - return 'fonts.googleapis.com' === strtolower(parse_url($href, PHP_URL_HOST));
1110 + return 'fonts.googleapis.com' === strtolower(wp_parse_url($href, PHP_URL_HOST));
1055 1111 }
1056 1112
1057 1113 /**
1058 1114 * Get the content of an asset, whether local or remote
@@ -1081,9 +1137,9 @@
1081 1137 WP_CONTENT_URL => WP_CONTENT_DIR,
1082 1138 WP_PLUGIN_URL => WP_PLUGIN_DIR,
1083 1139 $uploads_url => $uploads_dir,
1084 1140 get_template_directory_uri() => get_template_directory(),
1085 - includes_url() => ABSPATH . WPINC,
1141 + untrailingslashit(includes_url()) => ABSPATH . WPINC,
1086 1142 );
1087 1143
1088 1144 $file = false;
1089 1145 foreach ($possible_urls as $possible_url => $path) {
@@ -1107,14 +1163,14 @@
1107 1163 * @return int|false
1108 1164 */
1109 1165 public static function get_remote_file_size($url) {
1110 1166 $args = array(
1111 - // info (needed for google fonts woff files + hinted fonts) as well as to bypass some security filters
1112 - 'user-agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36',
1167 + // info (needed for Google fonts woff files + hinted fonts) as well as to bypass some security filters
1168 + 'user-agent' => WP_Optimize_Utils::get_user_agent('gfont'),
1113 1169 'timeout' => 7
1114 1170 );
1115 1171
1116 - // fetch via wordpress functions
1172 + // fetch via WordPress functions
1117 1173 $response = wp_remote_get($url, $args);
1118 1174
1119 1175 if (is_wp_error($response)) return false;
1120 1176
@@ -1144,9 +1200,9 @@
1144 1200 * @return string Fixed google fonts url
1145 1201 */
1146 1202 public static function fix_flatsome_google_fonts_url($href) {
1147 1203 // Get query from $href
1148 - $query = parse_url($href, PHP_URL_QUERY);
1204 + $query = wp_parse_url($href, PHP_URL_QUERY);
1149 1205 $query_arr = explode('&', $query);
1150 1206
1151 1207 // Separate 'family and display' arguments in query
1152 1208 $family = str_replace('family=', '', $query_arr[0]);
@@ -1169,12 +1225,12 @@
1169 1225
1170 1226 // Remove beginning '+'
1171 1227 $font = str_replace(',+', ',', $font_variant[0]);
1172 1228
1173 - // Replace '-' with '+
1229 + // Replace '-' with '+'
1174 1230 $font = str_replace(array('-', ' '), '+', $font);
1175 1231
1176 - // Remove '"' or '%22'
1232 + // Remove `"` or '%22'
1177 1233 $font = str_replace(array('%22', '"'), '', $font);
1178 1234 $font_arr = explode(',', $font);
1179 1235 $font_arr = array_diff($font_arr, $system_fonts);
1180 1236 $variant = '';
@@ -1189,6 +1245,42 @@
1189 1245 }
1190 1246 }
1191 1247 $protocol = is_ssl() ? 'https:' : 'http:';
1192 1248 return $protocol . '//fonts.googleapis.com/css?family=' . implode('|', $google_fonts) . '&' . $display_type;
1249 + }
1250 +
1251 + /**
1252 + * Get the file modification time
1253 + *
1254 + * @param string $asset_src
1255 + * @return string
1256 + */
1257 + public static function get_modification_time($asset_src) {
1258 + $hurl = self::get_hurl($asset_src);
1259 + $abs_file_path = WP_Optimize_Utils::get_file_path($hurl);
1260 + if (empty($abs_file_path)) return '';
1261 +
1262 + $modification_time = strval(@filemtime($abs_file_path)); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Suppress E-Warning on failure
1263 + $filtered_modification_time = apply_filters('wpo_minify_file_modification_time', $modification_time, $abs_file_path);
1264 +
1265 + return is_string($filtered_modification_time) ? $filtered_modification_time : $modification_time;
1266 + }
1267 +
1268 + /**
1269 + * When BOM removed code is null (due to unrecognised character encoding), logs error message
1270 + *
1271 + * @param string $url URL of the script/stylesheet
1272 + * @param string|false $encoding Character encoding
1273 + * @param string|null $code Script/Stylesheet code
1274 + *
1275 + * @return void
1276 + */
1277 + private static function maybe_log_error_message($url, $encoding, $code) {
1278 + if (null === $code) {
1279 + $message = "Minify: Could not process {$url}, it contains invalid characters. ";
1280 + if (false === $encoding) {
1281 + $message .= "Could not determine its character encoding.";
1282 + }
1283 + error_log($message); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Using for debugging purpose
1284 + }
1193 1285 }
1194 1286 }