PluginProbe
Autoptimize / trunk
Autoptimize vtrunk
3.1.16 2.2.2 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 All 108 releases
← All changes | classes/autoptimizeUtils.php +264 -93 2.4.0 → trunk View file →
@@ -31,30 +31,8 @@
31 31 return $available;
32 32 }
33 33
34 34 /**
35 - * Returns true when iconv is available.
36 - *
37 - * @param bool|null $override Allows overriding the decision.
38 - *
39 - * @return bool
40 - */
41 - public static function iconv_available( $override = null )
42 - {
43 - static $available = null;
44 -
45 - if ( null === $available ) {
46 - $available = \extension_loaded( 'iconv' );
47 - }
48 -
49 - if ( null !== $override ) {
50 - $available = $override;
51 - }
52 -
53 - return $available;
54 - }
55 -
56 - /**
57 35 * Multibyte-capable strpos() if support is available on the server.
58 36 * If not, it falls back to using \strpos().
59 37 *
60 38 * @param string $haystack Haystack.
@@ -66,11 +44,9 @@
66 44 */
67 45 public static function strpos( $haystack, $needle, $offset = 0, $encoding = null )
68 46 {
69 47 if ( self::mbstring_available() ) {
70 - return ( null === $encoding ) ? \mb_strpos( $haystack, $needle, $offset ) : \mb_strlen( $haystack, $needle, $offset, $encoding );
71 - } elseif ( self::iconv_available() ) {
72 - return ( null === $encoding ) ? \iconv_strpos( $haystack, $needle, $offset ) : \iconv_strpos( $haystack, $needle, $offset, $encoding );
48 + return ( null === $encoding ) ? \mb_strpos( $haystack, $needle, $offset ) : \mb_strpos( $haystack, $needle, $offset, $encoding );
73 49 } else {
74 50 return \strpos( $haystack, $needle, $offset );
75 51 }
76 52 }
@@ -76,15 +52,15 @@
76 52 }
77 53
78 54 /**
79 55 * Attempts to return the number of characters in the given $string if
80 - * mbstring or iconv is available. Returns the number of bytes
56 + * mbstring is available. Returns the number of bytes
81 57 * (instead of characters) as fallback.
82 58 *
83 59 * @param string $string String.
84 60 * @param string|null $encoding Encoding.
85 61 *
86 - * @return int Number of charcters or bytes in given $string
62 + * @return int Number of characters or bytes in given $string
87 63 * (characters if/when supported, bytes otherwise).
88 64 */
89 65 public static function strlen( $string, $encoding = null )
90 66 {
@@ -89,10 +65,8 @@
89 65 public static function strlen( $string, $encoding = null )
90 66 {
91 67 if ( self::mbstring_available() ) {
92 68 return ( null === $encoding ) ? \mb_strlen( $string ) : \mb_strlen( $string, $encoding );
93 - } elseif ( self::iconv_available() ) {
94 - return ( null === $encoding ) ? @iconv_strlen( $string ) : @iconv_strlen( $string, $encoding );
95 69 } else {
96 70 return \strlen( $string );
97 71 }
98 72 }
@@ -99,9 +73,9 @@
99 73
100 74 /**
101 75 * Our wrapper around implementations of \substr_replace()
102 76 * that attempts to not break things horribly if at all possible.
103 - * Uses mbstring and/or iconv if available, before falling back to regular
77 + * Uses mbstring if available, before falling back to regular
104 78 * substr_replace() (which works just fine in the majority of cases).
105 79 *
106 80 * @param string $string String.
107 81 * @param string $replacement Replacement.
@@ -145,36 +119,8 @@
145 119 $trailer = ( $start2 < $strlen ) ? \mb_substr( $string, $start2, null, $encoding ) : '';
146 120 }
147 121
148 122 return "{$leader}{$replacement}{$trailer}";
149 - } elseif ( self::iconv_available() ) {
150 - $strlen = self::strlen( $string, $encoding );
151 -
152 - if ( $start < 0 ) {
153 - $start = \max( 0, $strlen + $start );
154 - $start = $strlen + $start;
155 - if ( $start < 0 ) {
156 - $start = 0;
157 - }
158 - } elseif ( $start > $strlen ) {
159 - $start = $strlen;
160 - }
161 -
162 - if ( $length < 0 ) {
163 - $length = \max( 0, $strlen - $start + $length );
164 - } elseif ( null === $length || ( $length > $strlen ) ) {
165 - $length = $strlen;
166 - }
167 -
168 - if ( ( $start + $length ) > $strlen ) {
169 - $length = $strlen - $start;
170 - }
171 -
172 - if ( null === $encoding ) {
173 - return self::iconv_substr( $string, 0, $start ) . $replacement . self::iconv_substr( $string, $start + $length, $strlen - $start - $length );
174 - }
175 -
176 - return self::iconv_substr( $string, 0, $start, $encoding ) . $replacement . self::iconv_substr( $string, $start + $length, $strlen - $start - $length, $encoding );
177 123 }
178 124
179 125 return ( null === $length ) ? \substr_replace( $string, $replacement, $start ) : \substr_replace( $string, $replacement, $start, $length );
180 126 }
@@ -179,39 +125,8 @@
179 125 return ( null === $length ) ? \substr_replace( $string, $replacement, $start ) : \substr_replace( $string, $replacement, $start, $length );
180 126 }
181 127
182 128 /**
183 - * Wrapper around iconv_substr().
184 - *
185 - * @param string $s String.
186 - * @param int $start Start offset.
187 - * @param int|null $length Length.
188 - * @param string|null $encoding Encoding.
189 - *
190 - * @return string
191 - */
192 - protected static function iconv_substr( $s, $start, $length = null, $encoding = null )
193 - {
194 - if ( $start < 0 ) {
195 - $start = self::strlen( $s, $encoding ) + $start;
196 - if ( $start < 0 ) {
197 - $start = 0;
198 - }
199 - }
200 -
201 - if ( null === $length ) {
202 - $length = 2147483647;
203 - } elseif ( $length < 0 ) {
204 - $length = self::strlen( $s, $encoding ) + ( $length - $start );
205 - if ( $length < 0 ) {
206 - return '';
207 - }
208 - }
209 -
210 - return (string) ( null === $encoding ) ? \iconv_substr( $s, $start, $length ) : \iconv_substr( $s, $start, $length, $encoding );
211 - }
212 -
213 - /**
214 129 * Decides whether this is a "subdirectory site" or not.
215 130 *
216 131 * @param bool $override Allows overriding the decision when needed.
217 132 *
@@ -288,9 +203,9 @@
288 203 return $results[ $cdn_url ];
289 204 }
290 205
291 206 /**
292 - * When siteurl contans a path other than '/' and the CDN URL does not have
207 + * When siteurl contains a path other than '/' and the CDN URL does not have
293 208 * a path or it's path is '/', this will modify the CDN URL's path component
294 209 * to match that of the siteurl.
295 210 * This is to support "magic" CDN urls that worked that way before v2.4...
296 211 *
@@ -351,9 +266,9 @@
351 266 {
352 267 $result = false;
353 268
354 269 if ( ! empty( $url ) ) {
355 - $result = ( '/' === $url{1} ); // second char is `/`.
270 + $result = ( 0 === strpos( $url, '//' ) );
356 271 }
357 272
358 273 return $result;
359 274 }
@@ -385,11 +300,13 @@
385 300
386 301 /**
387 302 * Checks to see if 3rd party services are available and stores result in option
388 303 *
304 + * TODO This should be two separate methods.
305 + *
389 306 * @param string $return_result should we return resulting service status array (default no).
390 307 *
391 - * @return none if $return_result is false (default), array if $return_result is true.
308 + * @return null|array Service status or null.
392 309 */
393 310 public static function check_service_availability( $return_result = false )
394 311 {
395 312 $service_availability_resp = wp_remote_get( 'https://misc.optimizingmatters.com/api/autoptimize_service_availablity.json?from=aomain&ver=' . AUTOPTIMIZE_PLUGIN_VERSION );
@@ -396,9 +313,9 @@
396 313 if ( ! is_wp_error( $service_availability_resp ) ) {
397 314 if ( '200' == wp_remote_retrieve_response_code( $service_availability_resp ) ) {
398 315 $availabilities = json_decode( wp_remote_retrieve_body( $service_availability_resp ), true );
399 316 if ( is_array( $availabilities ) ) {
400 - update_option( 'autoptimize_service_availablity', $availabilities );
317 + autoptimizeOptionWrapper::update_option( 'autoptimize_service_availablity', $availabilities );
401 318 if ( $return_result ) {
402 319 return $availabilities;
403 320 }
404 321 }
@@ -403,8 +320,9 @@
403 320 }
404 321 }
405 322 }
406 323 }
324 + return null;
407 325 }
408 326
409 327 /**
410 328 * Returns true if the string is a valid regex.
@@ -419,6 +337,259 @@
419 337 $is_regex = ( false !== preg_match( $string, '' ) );
420 338 restore_error_handler();
421 339
422 340 return $is_regex;
341 + }
342 +
343 + /**
344 + * Returns true if a certain WP plugin is active/loaded.
345 + *
346 + * @param string $plugin_file Main plugin file.
347 + *
348 + * @return bool
349 + */
350 + public static function is_plugin_active( $plugin_file )
351 + {
352 + static $ipa_exists = null;
353 + if ( null === $ipa_exists ) {
354 + if ( ! function_exists( '\is_plugin_active' ) ) {
355 + require_once ABSPATH . 'wp-admin/includes/plugin.php';
356 + }
357 + $ipa_exists = function_exists( '\is_plugin_active' );
358 + }
359 +
360 + return $ipa_exists && \is_plugin_active( $plugin_file );
361 + }
362 +
363 + /**
364 + * Returns a node without ID attrib for use in noscript tags
365 + *
366 + * @param string $node an html tag.
367 + *
368 + * @return string
369 + */
370 + public static function remove_id_from_node( $node ) {
371 + if ( strpos( $node, 'id=' ) === false || apply_filters( 'autoptimize_filter_utils_keep_ids', false ) ) {
372 + return $node;
373 + } else {
374 + return preg_replace( '#(.*) id=[\'|"].*[\'|"] (.*)#Um', '$1 $2', $node );
375 + }
376 + }
377 +
378 + /**
379 + * Returns true if given $str ends with given $test.
380 + *
381 + * @param string $str String to check.
382 + * @param string $test Ending to match.
383 + *
384 + * @return bool
385 + */
386 + public static function str_ends_in( $str, $test )
387 + {
388 + // @codingStandardsIgnoreStart
389 + // substr_compare() is bugged on 5.5.11: https://3v4l.org/qGYBH
390 + // return ( 0 === substr_compare( $str, $test, -strlen( $test ) ) );
391 + // @codingStandardsIgnoreEnd
392 +
393 + $length = strlen( $test );
394 +
395 + return ( substr( $str, -$length, $length ) === $test );
396 + }
397 +
398 + /**
399 + * Returns true if a pagecache is found, false if not.
400 + * Now used to show notice, might be used later on to (un)hide page caching in AO if no page cache found.
401 + *
402 + * @param bool $disregard_transient False by default, but can be used to ignore the transient and retest.
403 + *
404 + * @return bool
405 + */
406 + public static function find_pagecache( $disregard_transient = false ) {
407 + static $_found_pagecache = null;
408 +
409 + if ( null === $_found_pagecache ) {
410 + $_page_cache_constants = array(
411 + 'NgInx' => 'NGINX_HELPER_BASENAME',
412 + 'Kinsta' => 'KINSTAMU_VERSION',
413 + 'Presslabs' => 'PL_INSTANCE_REF',
414 + 'Cache Enabler' => 'CACHE_ENABLER_VERSION',
415 + 'Speed Booster Pack' => 'SBP_PLUGIN_NAME',
416 + 'Servebolt' => 'SERVEBOLT_PLUGIN_FILE',
417 + 'WP CloudFlare Super Page Cache' => 'SWCFPC_PLUGIN_PATH',
418 + 'Cachify' => 'CACHIFY_CACHE_DIR',
419 + 'WP Rocket' => 'WP_ROCKET_CACHE_PATH',
420 + 'WP Optimize' => 'WPO_VERSION',
421 + 'Autoptimize Pro' => 'AO_PRO_PAGECACHE_CACHE_DIR',
422 + );
423 + $_page_cache_classes = array(
424 + 'Pressidium' => 'Ninukis_Plugin',
425 + 'Swift Performance' => 'Swift_Performance_Cache',
426 + 'WP Fastest Cache' => 'WpFastestCache',
427 + 'Quick Cache' => 'c_ws_plugin__qcache_purging_routines',
428 + 'ZenCache' => 'zencache',
429 + 'Comet Cache' => 'comet_cache',
430 + 'WP Engine' => 'WpeCommon',
431 + 'Flywheel' => 'FlywheelNginxCompat',
432 + 'Pagely' => 'PagelyCachePurge',
433 + );
434 + $_page_cache_functions = array(
435 + 'WP Super Cache' => 'wp_cache_clear_cache',
436 + 'W3 Total Cache' => 'w3tc_pgcache_flush',
437 + 'WP Fast Cache' => 'wp_fast_cache_bulk_delete_all',
438 + 'Rapidcache' => 'rapidcache_clear_cache',
439 + 'Siteground' => 'sg_cachepress_purge_cache',
440 + 'WP Super Cache' => 'prune_super_cache',
441 + );
442 +
443 + $_found_pagecache = false;
444 + if ( true !== $disregard_transient ) {
445 + $_ao_pagecache_transient = 'autoptimize_pagecache_check';
446 + $_found_pagecache = get_transient( $_ao_pagecache_transient );
447 + }
448 +
449 + if ( current_user_can( 'manage_options' ) && false === $_found_pagecache ) {
450 + // loop through known pagecache constants.
451 + foreach ( $_page_cache_constants as $_name => $_constant ) {
452 + if ( defined( $_constant ) ) {
453 + $_found_pagecache = $_name;
454 + break;
455 + }
456 + }
457 + // and loop through known pagecache classes.
458 + if ( false === $_found_pagecache ) {
459 + foreach ( $_page_cache_classes as $_name => $_class ) {
460 + if ( class_exists( $_class ) ) {
461 + $_found_pagecache = $_name;
462 + break;
463 + }
464 + }
465 + }
466 + // and loop through known pagecache functions.
467 + if ( false === $_found_pagecache ) {
468 + foreach ( $_page_cache_functions as $_name => $_function ) {
469 + if ( function_exists( $_function ) ) {
470 + $_found_pagecache = $_name;
471 + break;
472 + }
473 + }
474 + }
475 +
476 + // store in transient for 1 week if pagecache found.
477 + if ( true === $_found_pagecache && true !== $disregard_transient ) {
478 + set_transient( $_ao_pagecache_transient, true, WEEK_IN_SECONDS );
479 + }
480 + }
481 + }
482 +
483 + return $_found_pagecache;
484 + }
485 +
486 + /**
487 + * Returns true if on one of the AO settings tabs, false if not.
488 + * Used to limit notifications to AO settings pages.
489 + *
490 + * @return bool
491 + */
492 + public static function is_ao_settings() {
493 + $_is_ao_settings = ( str_replace( array( 'autoptimize', 'autoptimize_imgopt', 'ao_critcss', 'autoptimize_extra', 'ao_partners', 'ao_pro_boosters', 'ao_pro_pagecache', 'ao_protab' ), '', $_SERVER['REQUEST_URI'] ) !== $_SERVER['REQUEST_URI'] ? true : false );
494 + return $_is_ao_settings;
495 + }
496 +
497 + /**
498 + * Returns false if no conflicting plugins are found, the name if the plugin if found.
499 + *
500 + * @return bool|string
501 + */
502 + public static function find_potential_conflicts() {
503 + if ( defined( 'WPFC_WP_CONTENT_BASENAME' ) ) {
504 + $_wpfc_options = json_decode( get_option( 'WpFastestCache' ) );
505 + foreach ( array( 'wpFastestCacheMinifyCss', 'wpFastestCacheCombineCss', 'wpFastestCacheCombineJs' ) as $_wpfc_conflicting ) {
506 + if ( isset( $_wpfc_options->$_wpfc_conflicting ) && 'on' === $_wpfc_options->$_wpfc_conflicting ) {
507 + return 'WP Fastest Cache';
508 + }
509 + }
510 + } elseif ( defined( 'W3TC_VERSION' ) ) {
511 + $w3tc_config = file_get_contents( WP_CONTENT_DIR . '/w3tc-config/master.php' );
512 + $w3tc_minify_on = strpos( $w3tc_config, '"minify.enabled": true' );
513 + if ( $w3tc_minify_on ) {
514 + return 'W3 Total Cache';
515 + }
516 + } elseif ( defined( 'SiteGround_Optimizer\VERSION' ) ) {
517 + if ( get_option( 'siteground_optimizer_optimize_css' ) == 1 || get_option( 'siteground_optimizer_optimize_javascript' ) == 1 || get_option( 'siteground_optimizer_combine_javascript' ) == 1 || get_option( 'siteground_optimizer_combine_css' ) == 1 ) {
518 + return 'Siteground Optimizer';
519 + }
520 + } elseif ( defined( 'WPO_VERSION' ) ) {
521 + $_wpo_options = get_site_option( 'wpo_minify_config' );
522 + if ( is_array( $_wpo_options ) && 1 == $_wpo_options['enabled'] && ( 1 == $_wpo_options['enable_css'] || 1 == $_wpo_options['enable_js'] ) ) {
523 + return 'WP Optimize';
524 + }
525 + } elseif ( defined( 'WPACU_PLUGIN_VERSION' ) || defined( 'WPACU_PRO_PLUGIN_VERSION' ) ) {
526 + $wpacu_settings_class = new \WpAssetCleanUp\Settings();
527 + $wpacu_settings = $wpacu_settings_class->getAll();
528 +
529 + if ( $wpacu_settings['minify_loaded_css'] || $wpacu_settings['minify_loaded_js'] || $wpacu_settings['combine_loaded_js'] || $wpacu_settings['combine_loaded_css'] ) {
530 + return 'Asset Cleanup';
531 + }
532 + } elseif ( defined( 'WP_ROCKET_VERSION' ) && function_exists( 'get_rocket_option' ) ) {
533 + if ( get_rocket_option( 'minify_js' ) || get_rocket_option( 'minify_concatenate_js' ) || get_rocket_option( 'minify_css' ) || get_rocket_option( 'minify_concatenate_css' ) || get_rocket_option( 'async_css' ) ) {
534 + return 'WP Rocket';
535 + }
536 + } elseif ( function_exists( 'fvm_get_settings' ) ) {
537 + return 'Fast Velocity Minify';
538 + }
539 +
540 + return false;
541 + }
542 +
543 + /**
544 + * Returns true if false if on a local dev environment, true if not.
545 + * Used to disallow image opt/ critcss for local dev environments.
546 + *
547 + * @return bool
548 + */
549 + public static function is_local_server( $_domain = AUTOPTIMIZE_SITE_DOMAIN ) {
550 + static $_is_local_server = null;
551 +
552 + if ( null === $_is_local_server ) {
553 + if ( false === strpos( $_domain, '.' ) && false === strpos( $_domain, ':' ) ) {
554 + // no dots in domain or colon (ipv6 address), so impossible to reach, this also matches 'localhost' or any other single-word domain.
555 + $_is_local_server = true;
556 + } elseif ( in_array( $_domain, array( '127.0.0.1', '0000:0000:0000:0000:0000:0000:0000:0001', '0:0:0:0:0:0:0:1', '::1' ) ) ) {
557 + // localhost IPv4/ IPv6.
558 + $_is_local_server = true;
559 + } elseif ( 0 === strpos( $_domain, '127.' ) || 0 === strpos( $_domain, '192.168.' ) || 0 === strpos( $_domain, '10.' ) || 0 === strpos( $_domain, 'fd' ) ) {
560 + // private ranges so unreachable for imgopt/ CCSS.
561 + // fixme; 172.16.0.0–172.31.255.255 also private.
562 + $_is_local_server = true;
563 + } elseif ( autoptimizeUtils::str_ends_in( $_domain, '.local') ) {
564 + // matches 'whatever.local'.
565 + $_is_local_server = true;
566 + } else {
567 + // likely OK.
568 + $_is_local_server = false;
569 + }
570 + }
571 +
572 + // filter to override result for testing purposes.
573 + return apply_filters( 'autoptimize_filter_utils_is_local_server', $_is_local_server );
574 + }
575 +
576 + public static function strip_tags_array( $array ) {
577 + // strip all tags in an array (use case: avoid XSS in CCSS rules both when importing and when outputting).
578 + // based on https://stackoverflow.com/a/44732196/237449 but heavily tweaked.
579 + if ( is_array( $array ) ) {
580 + $result = array();
581 + foreach ( $array as $key => $value ){
582 + if ( is_array( $value ) ) {
583 + $result[$key] = autoptimizeUtils::strip_tags_array( $value );
584 + } else if ( is_string( $value ) ) {
585 + $result[$key] = wp_strip_all_tags( $value );
586 + } else {
587 + $result[$key] = $value;
588 + }
589 + }
590 + } else {
591 + $result = wp_strip_all_tags( $array );
592 + }
593 + return $result;
423 594 }
424 595 }