| @@ -44,9 +44,9 @@ | ||
| 44 | 44 | */ |
| 45 | 45 | public static function strpos( $haystack, $needle, $offset = 0, $encoding = null ) |
| 46 | 46 | { |
| 47 | 47 | if ( self::mbstring_available() ) { |
| 48 | - return ( null === $encoding ) ? \mb_strpos( $haystack, $needle, $offset ) : \mb_strlen( $haystack, $needle, $offset, $encoding ); | |
| 48 | + return ( null === $encoding ) ? \mb_strpos( $haystack, $needle, $offset ) : \mb_strpos( $haystack, $needle, $offset, $encoding ); | |
| 49 | 49 | } else { |
| 50 | 50 | return \strpos( $haystack, $needle, $offset ); |
| 51 | 51 | } |
| 52 | 52 | } |
| @@ -58,9 +58,9 @@ | ||
| 58 | 58 | * |
| 59 | 59 | * @param string $string String. |
| 60 | 60 | * @param string|null $encoding Encoding. |
| 61 | 61 | * |
| 62 | - * @return int Number of charcters or bytes in given $string | |
| 62 | + * @return int Number of characters or bytes in given $string | |
| 63 | 63 | * (characters if/when supported, bytes otherwise). |
| 64 | 64 | */ |
| 65 | 65 | public static function strlen( $string, $encoding = null ) |
| 66 | 66 | { |
| @@ -203,9 +203,9 @@ | ||
| 203 | 203 | return $results[ $cdn_url ]; |
| 204 | 204 | } |
| 205 | 205 | |
| 206 | 206 | /** |
| 207 | - * 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 | |
| 208 | 208 | * a path or it's path is '/', this will modify the CDN URL's path component |
| 209 | 209 | * to match that of the siteurl. |
| 210 | 210 | * This is to support "magic" CDN urls that worked that way before v2.4... |
| 211 | 211 | * |
| @@ -266,9 +266,9 @@ | ||
| 266 | 266 | { |
| 267 | 267 | $result = false; |
| 268 | 268 | |
| 269 | 269 | if ( ! empty( $url ) ) { |
| 270 | - $result = ( '/' === $url{1} ); // second char is `/`. | |
| 270 | + $result = ( 0 === strpos( $url, '//' ) ); | |
| 271 | 271 | } |
| 272 | 272 | |
| 273 | 273 | return $result; |
| 274 | 274 | } |
| @@ -300,11 +300,13 @@ | ||
| 300 | 300 | |
| 301 | 301 | /** |
| 302 | 302 | * Checks to see if 3rd party services are available and stores result in option |
| 303 | 303 | * |
| 304 | + * TODO This should be two separate methods. | |
| 305 | + * | |
| 304 | 306 | * @param string $return_result should we return resulting service status array (default no). |
| 305 | 307 | * |
| 306 | - * @return none if $return_result is false (default), array if $return_result is true. | |
| 308 | + * @return null|array Service status or null. | |
| 307 | 309 | */ |
| 308 | 310 | public static function check_service_availability( $return_result = false ) |
| 309 | 311 | { |
| 310 | 312 | $service_availability_resp = wp_remote_get( 'https://misc.optimizingmatters.com/api/autoptimize_service_availablity.json?from=aomain&ver=' . AUTOPTIMIZE_PLUGIN_VERSION ); |
| @@ -311,9 +313,9 @@ | ||
| 311 | 313 | if ( ! is_wp_error( $service_availability_resp ) ) { |
| 312 | 314 | if ( '200' == wp_remote_retrieve_response_code( $service_availability_resp ) ) { |
| 313 | 315 | $availabilities = json_decode( wp_remote_retrieve_body( $service_availability_resp ), true ); |
| 314 | 316 | if ( is_array( $availabilities ) ) { |
| 315 | - update_option( 'autoptimize_service_availablity', $availabilities ); | |
| 317 | + autoptimizeOptionWrapper::update_option( 'autoptimize_service_availablity', $availabilities ); | |
| 316 | 318 | if ( $return_result ) { |
| 317 | 319 | return $availabilities; |
| 318 | 320 | } |
| 319 | 321 | } |
| @@ -318,8 +320,9 @@ | ||
| 318 | 320 | } |
| 319 | 321 | } |
| 320 | 322 | } |
| 321 | 323 | } |
| 324 | + return null; | |
| 322 | 325 | } |
| 323 | 326 | |
| 324 | 327 | /** |
| 325 | 328 | * Returns true if the string is a valid regex. |
| @@ -334,6 +337,238 @@ | ||
| 334 | 337 | $is_regex = ( false !== preg_match( $string, '' ) ); |
| 335 | 338 | restore_error_handler(); |
| 336 | 339 | |
| 337 | 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' => 'KINSTA_CACHE_ZONE', | |
| 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' ), '', $_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, 'fd' ) ) { | |
| 560 | + // private ranges so unreachable for imgopt/ CCSS. | |
| 561 | + $_is_local_server = true; | |
| 562 | + } elseif ( autoptimizeUtils::str_ends_in( $_domain, '.local') ) { | |
| 563 | + // matches 'whatever.local'. | |
| 564 | + $_is_local_server = true; | |
| 565 | + } else { | |
| 566 | + // likely OK. | |
| 567 | + $_is_local_server = false; | |
| 568 | + } | |
| 569 | + } | |
| 570 | + | |
| 571 | + // filter to override result for testing purposes. | |
| 572 | + return apply_filters( 'autoptimize_filter_utils_is_local_server', $_is_local_server ); | |
| 338 | 573 | } |
| 339 | 574 | } |