| 1 |
<?php |
| 2 |
/** |
| 3 |
* Wraps base plugin logic/hooks and handles activation/deactivation/uninstall. |
| 4 |
*/ |
| 5 |
|
| 6 |
if ( ! defined( 'ABSPATH' ) ) { |
| 7 |
exit; |
| 8 |
} |
| 9 |
|
| 10 |
class autoptimizeMain |
| 11 |
{ |
| 12 |
const INIT_EARLIER_PRIORITY = -1; |
| 13 |
const DEFAULT_HOOK_PRIORITY = 2; |
| 14 |
|
| 15 |
/** |
| 16 |
* Version string. |
| 17 |
* |
| 18 |
* @var string |
| 19 |
*/ |
| 20 |
protected $version = null; |
| 21 |
|
| 22 |
/** |
| 23 |
* Main plugin filepath. |
| 24 |
* Used for activation/deactivation/uninstall hooks. |
| 25 |
* |
| 26 |
* @var string |
| 27 |
*/ |
| 28 |
protected $filepath = null; |
| 29 |
|
| 30 |
/** |
| 31 |
* Critical CSS base object |
| 32 |
* |
| 33 |
* @var object |
| 34 |
*/ |
| 35 |
protected $_criticalcss = null; |
| 36 |
|
| 37 |
/** |
| 38 |
* Constructor. |
| 39 |
* |
| 40 |
* @param string $version Version. |
| 41 |
* @param string $filepath Filepath. Needed for activation/deactivation/uninstall hooks. |
| 42 |
*/ |
| 43 |
public function __construct( $version, $filepath ) |
| 44 |
{ |
| 45 |
$this->version = $version; |
| 46 |
$this->filepath = $filepath; |
| 47 |
} |
| 48 |
|
| 49 |
public function run() |
| 50 |
{ |
| 51 |
$this->add_hooks(); |
| 52 |
|
| 53 |
// Runs cache size checker. |
| 54 |
$checker = new autoptimizeCacheChecker(); |
| 55 |
$checker->run(); |
| 56 |
} |
| 57 |
|
| 58 |
protected function add_hooks() |
| 59 |
{ |
| 60 |
if ( ! defined( 'AUTOPTIMIZE_SETUP_INITHOOK' ) ) { |
| 61 |
define( 'AUTOPTIMIZE_SETUP_INITHOOK', 'plugins_loaded' ); |
| 62 |
} |
| 63 |
|
| 64 |
add_action( AUTOPTIMIZE_SETUP_INITHOOK, array( $this, 'setup' ) ); |
| 65 |
add_action( AUTOPTIMIZE_SETUP_INITHOOK, array( $this, 'hook_page_cache_purge' ) ); |
| 66 |
|
| 67 |
add_action( 'autoptimize_setup_done', array( $this, 'version_upgrades_check' ) ); |
| 68 |
add_action( 'autoptimize_setup_done', array( $this, 'check_cache_and_run' ) ); |
| 69 |
add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_ao_compat' ), 10 ); |
| 70 |
add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_ao_extra' ), 15 ); |
| 71 |
add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_admin_only_trinkets' ), 20 ); |
| 72 |
add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_criticalcss' ), 11 ); |
| 73 |
add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_notfound_fallback' ), 10 ); |
| 74 |
|
| 75 |
add_action( 'init', array( $this, 'load_textdomain' ) ); |
| 76 |
|
| 77 |
if ( is_multisite() && is_admin() ) { |
| 78 |
// Only if multisite and if in admin we want to check if we need to save options on network level. |
| 79 |
add_action( 'init', 'autoptimizeOptionWrapper::check_multisite_on_saving_options' ); |
| 80 |
} |
| 81 |
|
| 82 |
// register uninstall & deactivation hooks. |
| 83 |
register_uninstall_hook( $this->filepath, 'autoptimizeMain::on_uninstall' ); |
| 84 |
register_deactivation_hook( $this->filepath, 'autoptimizeMain::on_deactivation' ); |
| 85 |
} |
| 86 |
|
| 87 |
public function load_textdomain() |
| 88 |
{ |
| 89 |
load_plugin_textdomain( 'autoptimize' ); |
| 90 |
} |
| 91 |
|
| 92 |
public function setup() |
| 93 |
{ |
| 94 |
// Do we gzip in php when caching or is the webserver doing it? |
| 95 |
define( 'AUTOPTIMIZE_CACHE_NOGZIP', (bool) autoptimizeOptionWrapper::get_option( 'autoptimize_cache_nogzip' ) ); |
| 96 |
|
| 97 |
// These can be overridden by specifying them in wp-config.php or such. |
| 98 |
if ( ! defined( 'AUTOPTIMIZE_WP_CONTENT_NAME' ) ) { |
| 99 |
define( 'AUTOPTIMIZE_WP_CONTENT_NAME', '/' . wp_basename( WP_CONTENT_DIR ) ); |
| 100 |
} |
| 101 |
if ( ! defined( 'AUTOPTIMIZE_CACHE_CHILD_DIR' ) ) { |
| 102 |
define( 'AUTOPTIMIZE_CACHE_CHILD_DIR', '/cache/autoptimize/' ); |
| 103 |
} |
| 104 |
if ( ! defined( 'AUTOPTIMIZE_CACHEFILE_PREFIX' ) ) { |
| 105 |
define( 'AUTOPTIMIZE_CACHEFILE_PREFIX', 'autoptimize_' ); |
| 106 |
} |
| 107 |
// Note: trailing slash is not optional! |
| 108 |
if ( ! defined( 'AUTOPTIMIZE_CACHE_DIR' ) ) { |
| 109 |
define( 'AUTOPTIMIZE_CACHE_DIR', autoptimizeCache::get_pathname() ); |
| 110 |
} |
| 111 |
|
| 112 |
define( 'WP_ROOT_DIR', substr( WP_CONTENT_DIR, 0, strlen( WP_CONTENT_DIR ) - strlen( AUTOPTIMIZE_WP_CONTENT_NAME ) ) ); |
| 113 |
|
| 114 |
if ( ! defined( 'AUTOPTIMIZE_WP_SITE_URL' ) ) { |
| 115 |
if ( function_exists( 'domain_mapping_siteurl' ) ) { |
| 116 |
define( 'AUTOPTIMIZE_WP_SITE_URL', domain_mapping_siteurl( get_current_blog_id() ) ); |
| 117 |
} else { |
| 118 |
define( 'AUTOPTIMIZE_WP_SITE_URL', site_url() ); |
| 119 |
} |
| 120 |
} |
| 121 |
if ( ! defined( 'AUTOPTIMIZE_WP_CONTENT_URL' ) ) { |
| 122 |
if ( function_exists( 'get_original_url' ) ) { |
| 123 |
define( 'AUTOPTIMIZE_WP_CONTENT_URL', str_replace( get_original_url( AUTOPTIMIZE_WP_SITE_URL ), AUTOPTIMIZE_WP_SITE_URL, content_url() ) ); |
| 124 |
} else { |
| 125 |
define( 'AUTOPTIMIZE_WP_CONTENT_URL', content_url() ); |
| 126 |
} |
| 127 |
} |
| 128 |
if ( ! defined( 'AUTOPTIMIZE_CACHE_URL' ) ) { |
| 129 |
if ( is_multisite() && apply_filters( 'autoptimize_separate_blog_caches', true ) ) { |
| 130 |
$blog_id = get_current_blog_id(); |
| 131 |
define( 'AUTOPTIMIZE_CACHE_URL', AUTOPTIMIZE_WP_CONTENT_URL . AUTOPTIMIZE_CACHE_CHILD_DIR . $blog_id . '/' ); |
| 132 |
} else { |
| 133 |
define( 'AUTOPTIMIZE_CACHE_URL', AUTOPTIMIZE_WP_CONTENT_URL . AUTOPTIMIZE_CACHE_CHILD_DIR ); |
| 134 |
} |
| 135 |
} |
| 136 |
if ( ! defined( 'AUTOPTIMIZE_WP_ROOT_URL' ) ) { |
| 137 |
define( 'AUTOPTIMIZE_WP_ROOT_URL', str_replace( AUTOPTIMIZE_WP_CONTENT_NAME, '', AUTOPTIMIZE_WP_CONTENT_URL ) ); |
| 138 |
} |
| 139 |
if ( ! defined( 'AUTOPTIMIZE_HASH' ) ) { |
| 140 |
define( 'AUTOPTIMIZE_HASH', wp_hash( AUTOPTIMIZE_CACHE_URL ) ); |
| 141 |
} |
| 142 |
if ( ! defined( 'AUTOPTIMIZE_SITE_DOMAIN' ) ) { |
| 143 |
define( 'AUTOPTIMIZE_SITE_DOMAIN', parse_url( AUTOPTIMIZE_WP_SITE_URL, PHP_URL_HOST ) ); |
| 144 |
} |
| 145 |
|
| 146 |
// Multibyte-capable string replacements are available with a filter. |
| 147 |
// Also requires 'mbstring' extension. |
| 148 |
$with_mbstring = apply_filters( 'autoptimize_filter_main_use_mbstring', false ); |
| 149 |
if ( $with_mbstring ) { |
| 150 |
autoptimizeUtils::mbstring_available( \extension_loaded( 'mbstring' ) ); |
| 151 |
} else { |
| 152 |
autoptimizeUtils::mbstring_available( false ); |
| 153 |
} |
| 154 |
|
| 155 |
do_action( 'autoptimize_setup_done' ); |
| 156 |
} |
| 157 |
|
| 158 |
/** |
| 159 |
* Checks if there's a need to upgrade/update options and whatnot, |
| 160 |
* in which case we might need to do stuff and flush the cache |
| 161 |
* to avoid old versions of aggregated files lingering around. |
| 162 |
*/ |
| 163 |
public function version_upgrades_check() |
| 164 |
{ |
| 165 |
autoptimizeVersionUpdatesHandler::check_installed_and_update( $this->version ); |
| 166 |
} |
| 167 |
|
| 168 |
public function check_cache_and_run() |
| 169 |
{ |
| 170 |
if ( autoptimizeCache::cacheavail() ) { |
| 171 |
$conf = autoptimizeConfig::instance(); |
| 172 |
if ( $conf->get( 'autoptimize_html' ) || $conf->get( 'autoptimize_js' ) || $conf->get( 'autoptimize_css' ) || autoptimizeImages::imgopt_active() || autoptimizeImages::should_lazyload_wrapper() ) { |
| 173 |
if ( ! defined( 'AUTOPTIMIZE_NOBUFFER_OPTIMIZE' ) ) { |
| 174 |
// Hook into WordPress frontend. |
| 175 |
if ( defined( 'AUTOPTIMIZE_INIT_EARLIER' ) ) { |
| 176 |
add_action( |
| 177 |
'init', |
| 178 |
array( $this, 'start_buffering' ), |
| 179 |
self::INIT_EARLIER_PRIORITY |
| 180 |
); |
| 181 |
} else { |
| 182 |
if ( ! defined( 'AUTOPTIMIZE_HOOK_INTO' ) ) { |
| 183 |
define( 'AUTOPTIMIZE_HOOK_INTO', 'template_redirect' ); |
| 184 |
} |
| 185 |
add_action( |
| 186 |
constant( 'AUTOPTIMIZE_HOOK_INTO' ), |
| 187 |
array( $this, 'start_buffering' ), |
| 188 |
self::DEFAULT_HOOK_PRIORITY |
| 189 |
); |
| 190 |
} |
| 191 |
} |
| 192 |
|
| 193 |
// And disable Jetpack's site accelerator if JS or CSS opt. are active. |
| 194 |
if ( class_exists( 'Jetpack' ) && apply_filters( 'autoptimize_filter_main_disable_jetpack_cdn', true ) && ( $conf->get( 'autoptimize_js' ) || $conf->get( 'autoptimize_css' ) ) ) { |
| 195 |
add_filter( 'jetpack_force_disable_site_accelerator', '__return_true' ); |
| 196 |
} |
| 197 |
|
| 198 |
// Add "no cache found" notice. |
| 199 |
add_action( 'admin_notices', 'autoptimizeMain::notice_nopagecache', 99 ); |
| 200 |
add_action( 'admin_notices', 'autoptimizeMain::notice_potential_conflict', 99 ); |
| 201 |
} |
| 202 |
} else { |
| 203 |
add_action( 'admin_notices', 'autoptimizeMain::notice_cache_unavailable' ); |
| 204 |
} |
| 205 |
} |
| 206 |
|
| 207 |
public function maybe_run_ao_extra() |
| 208 |
{ |
| 209 |
if ( apply_filters( 'autoptimize_filter_extra_activate', true ) ) { |
| 210 |
$ao_imgopt = new autoptimizeImages(); |
| 211 |
$ao_imgopt->run(); |
| 212 |
$ao_extra = new autoptimizeExtra(); |
| 213 |
$ao_extra->run(); |
| 214 |
|
| 215 |
// And show the imgopt notice. |
| 216 |
add_action( 'admin_notices', 'autoptimizeMain::notice_plug_imgopt' ); |
| 217 |
} |
| 218 |
} |
| 219 |
|
| 220 |
public function maybe_run_admin_only_trinkets() |
| 221 |
{ |
| 222 |
// Loads partners tab and exit survey code if in admin (and not in admin-ajax.php)! |
| 223 |
if ( autoptimizeConfig::is_admin_and_not_ajax() ) { |
| 224 |
new autoptimizePartners(); |
| 225 |
new autoptimizeExitSurvey(); |
| 226 |
} |
| 227 |
} |
| 228 |
|
| 229 |
public function criticalcss() |
| 230 |
{ |
| 231 |
if ( apply_filters( 'autoptimize_filter_criticalcss_active', true ) && ! autoptimizeUtils::is_plugin_active( 'autoptimize-criticalcss/ao_criticss_aas.php' ) ) { |
| 232 |
return $this->_criticalcss; |
| 233 |
} else { |
| 234 |
return false; |
| 235 |
} |
| 236 |
} |
| 237 |
|
| 238 |
public function maybe_run_criticalcss() |
| 239 |
{ |
| 240 |
// Loads criticalcss if the filter returns true & old power-up is not active. |
| 241 |
if ( apply_filters( 'autoptimize_filter_criticalcss_active', true ) && ! autoptimizeUtils::is_plugin_active( 'autoptimize-criticalcss/ao_criticss_aas.php' ) ) { |
| 242 |
$this->_criticalcss = new autoptimizeCriticalCSSBase(); |
| 243 |
$this->_criticalcss->setup(); |
| 244 |
$this->_criticalcss->load_requires(); |
| 245 |
} |
| 246 |
} |
| 247 |
|
| 248 |
public function maybe_run_notfound_fallback() |
| 249 |
{ |
| 250 |
if ( autoptimizeCache::do_fallback() ) { |
| 251 |
add_action( 'template_redirect', array( 'autoptimizeCache', 'wordpress_notfound_fallback' ) ); |
| 252 |
} |
| 253 |
} |
| 254 |
|
| 255 |
public function maybe_run_ao_compat() |
| 256 |
{ |
| 257 |
// Condtionally loads the compatibility-class to ensure more out-of-the-box compatibility with big players. |
| 258 |
$_run_compat = true; |
| 259 |
|
| 260 |
if ( autoptimizeOptionWrapper::get_option( 'autoptimize_installed_before_compatibility', false ) ) { |
| 261 |
// If AO was already running before Compatibility logic was added, don't run compat by default |
| 262 |
// because it can be assumed everything works and we want to avoid (perf) regressions that |
| 263 |
// could occur due to compatibility code. |
| 264 |
$_run_compat = false; |
| 265 |
} |
| 266 |
|
| 267 |
if ( apply_filters( 'autoptimize_filter_init_compatibility', $_run_compat ) ) { |
| 268 |
new autoptimizeCompatibility(); |
| 269 |
} |
| 270 |
} |
| 271 |
|
| 272 |
public function hook_page_cache_purge() |
| 273 |
{ |
| 274 |
// hook into a collection of page cache purge actions if filter allows. |
| 275 |
if ( apply_filters( 'autoptimize_filter_main_hookpagecachepurge', true ) ) { |
| 276 |
$page_cache_purge_actions = array( |
| 277 |
'after_rocket_clean_domain', // exists. |
| 278 |
'hyper_cache_purged', // Stefano confirmed this will be added. |
| 279 |
'w3tc_flush_posts', // exits. |
| 280 |
'w3tc_flush_all', // exists. |
| 281 |
'ce_action_cache_cleared', // Sven confirmed this will be added. |
| 282 |
'aoce_action_cache_cleared', // Some other cache enabler. |
| 283 |
'comet_cache_wipe_cache', // still to be confirmed by Raam. |
| 284 |
'wp_cache_cleared', // cfr. https://github.com/Automattic/wp-super-cache/pull/537. |
| 285 |
'wpfc_delete_cache', // Emre confirmed this will be added this. |
| 286 |
'swift_performance_after_clear_all_cache', // swift perf. yeah! |
| 287 |
'wpo_cache_flush', // wp-optimize. |
| 288 |
'rt_nginx_helper_after_fastcgi_purge_all', // nginx helper. |
| 289 |
); |
| 290 |
$page_cache_purge_actions = apply_filters( 'autoptimize_filter_main_pagecachepurgeactions', $page_cache_purge_actions ); |
| 291 |
foreach ( $page_cache_purge_actions as $purge_action ) { |
| 292 |
add_action( $purge_action, 'autoptimizeCache::clearall_actionless' ); |
| 293 |
} |
| 294 |
} |
| 295 |
} |
| 296 |
|
| 297 |
/** |
| 298 |
* Setup output buffering if needed. |
| 299 |
* |
| 300 |
* @return void |
| 301 |
*/ |
| 302 |
public function start_buffering() |
| 303 |
{ |
| 304 |
if ( $this->should_buffer() ) { |
| 305 |
|
| 306 |
// Load speedupper conditionally (true by default). |
| 307 |
if ( apply_filters( 'autoptimize_filter_speedupper', true ) ) { |
| 308 |
$ao_speedupper = new autoptimizeSpeedupper(); |
| 309 |
} |
| 310 |
|
| 311 |
$conf = autoptimizeConfig::instance(); |
| 312 |
|
| 313 |
if ( $conf->get( 'autoptimize_js' ) ) { |
| 314 |
if ( ! defined( 'CONCATENATE_SCRIPTS' ) ) { |
| 315 |
define( 'CONCATENATE_SCRIPTS', false ); |
| 316 |
} |
| 317 |
if ( ! defined( 'COMPRESS_SCRIPTS' ) ) { |
| 318 |
define( 'COMPRESS_SCRIPTS', false ); |
| 319 |
} |
| 320 |
} |
| 321 |
|
| 322 |
if ( $conf->get( 'autoptimize_css' ) ) { |
| 323 |
if ( ! defined( 'COMPRESS_CSS' ) ) { |
| 324 |
define( 'COMPRESS_CSS', false ); |
| 325 |
} |
| 326 |
} |
| 327 |
|
| 328 |
if ( apply_filters( 'autoptimize_filter_obkiller', false ) ) { |
| 329 |
while ( ob_get_level() > 0 ) { |
| 330 |
ob_end_clean(); |
| 331 |
} |
| 332 |
} |
| 333 |
|
| 334 |
// Now, start the real thing! |
| 335 |
ob_start( array( $this, 'end_buffering' ) ); |
| 336 |
} |
| 337 |
} |
| 338 |
|
| 339 |
/** |
| 340 |
* Returns true if all the conditions to start output buffering are satisfied. |
| 341 |
* |
| 342 |
* @param bool $doing_tests Allows overriding the optimization of only |
| 343 |
* deciding once per request (for use in tests). |
| 344 |
* @return bool |
| 345 |
*/ |
| 346 |
public static function should_buffer( $doing_tests = false ) |
| 347 |
{ |
| 348 |
static $do_buffering = null; |
| 349 |
|
| 350 |
// Only check once in case we're called multiple times by others but |
| 351 |
// still allows multiple calls when doing tests. |
| 352 |
if ( null === $do_buffering || $doing_tests ) { |
| 353 |
|
| 354 |
$ao_noptimize = false; |
| 355 |
|
| 356 |
// Checking for DONOTMINIFY constant as used by e.g. WooCommerce POS. |
| 357 |
if ( defined( 'DONOTMINIFY' ) && ( constant( 'DONOTMINIFY' ) === true || constant( 'DONOTMINIFY' ) === 'true' ) ) { |
| 358 |
$ao_noptimize = true; |
| 359 |
} |
| 360 |
|
| 361 |
// Skip checking query strings if they're disabled. |
| 362 |
if ( apply_filters( 'autoptimize_filter_honor_qs_noptimize', true ) ) { |
| 363 |
// Check for `ao_noptimize` (and other) keys in the query string |
| 364 |
// to get non-optimized page for debugging. |
| 365 |
$keys = array( |
| 366 |
'ao_noptimize', |
| 367 |
'ao_noptirocket', |
| 368 |
); |
| 369 |
foreach ( $keys as $key ) { |
| 370 |
if ( array_key_exists( $key, $_GET ) && '1' === $_GET[ $key ] ) { |
| 371 |
$ao_noptimize = true; |
| 372 |
break; |
| 373 |
} |
| 374 |
} |
| 375 |
} |
| 376 |
|
| 377 |
// If setting says not to optimize logged in user and user is logged in... |
| 378 |
if ( false === $ao_noptimize && 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_optimize_logged', 'on' ) && is_user_logged_in() && current_user_can( 'edit_posts' ) ) { |
| 379 |
$ao_noptimize = true; |
| 380 |
} |
| 381 |
|
| 382 |
// If setting says not to optimize cart/checkout. |
| 383 |
if ( false === $ao_noptimize && 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_optimize_checkout', 'off' ) ) { |
| 384 |
// Checking for woocommerce, easy digital downloads and wp ecommerce... |
| 385 |
foreach ( array( 'is_checkout', 'is_cart', 'is_account_page', 'edd_is_checkout', 'wpsc_is_cart', 'wpsc_is_checkout' ) as $func ) { |
| 386 |
if ( function_exists( $func ) && $func() ) { |
| 387 |
$ao_noptimize = true; |
| 388 |
break; |
| 389 |
} |
| 390 |
} |
| 391 |
} |
| 392 |
|
| 393 |
// Misc. querystring paramaters that will stop AO from doing optimizations (pagebuilders + |
| 394 |
// 2 generic parameters that could/ should become standard between optimization plugins?). |
| 395 |
if ( false === $ao_noptimize ) { |
| 396 |
$_qs_showstoppers = array( 'no_cache', 'no_optimize', 'tve', 'elementor-preview', 'fl_builder', 'vc_action', 'et_fb', 'bt-beaverbuildertheme', 'ct_builder', 'fb-edit', 'siteorigin_panels_live_editor', 'preview', 'td_action' ); |
| 397 |
|
| 398 |
// doing Jonathan a quick favor to allow correct unused CSS generation ;-) . |
| 399 |
if ( apply_filters( 'autoptimize_filter_main_showstoppers_do_wp_rocket_a_favor', true ) ) { |
| 400 |
$_qs_showstoppers[] = 'nowprocket'; |
| 401 |
} |
| 402 |
|
| 403 |
foreach ( $_qs_showstoppers as $_showstopper ) { |
| 404 |
if ( array_key_exists( $_showstopper, $_GET ) ) { |
| 405 |
$ao_noptimize = true; |
| 406 |
break; |
| 407 |
} |
| 408 |
} |
| 409 |
} |
| 410 |
|
| 411 |
// Also honor PageSpeed=off parameter as used by mod_pagespeed, in use by some pagebuilders, |
| 412 |
// see https://www.modpagespeed.com/doc/experiment#ModPagespeed for info on that. |
| 413 |
if ( false === $ao_noptimize && array_key_exists( 'PageSpeed', $_GET ) && 'off' === $_GET['PageSpeed'] ) { |
| 414 |
$ao_noptimize = true; |
| 415 |
} |
| 416 |
|
| 417 |
// If page/ post check post_meta to see if optimize is off. |
| 418 |
if ( false === autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_optimize' ) ) { |
| 419 |
$ao_noptimize = true; |
| 420 |
} |
| 421 |
|
| 422 |
// And finally allows blocking of autoptimization on your own terms regardless of above decisions. |
| 423 |
$ao_noptimize = (bool) apply_filters( 'autoptimize_filter_noptimize', $ao_noptimize ); |
| 424 |
|
| 425 |
// Check for site being previewed in the Customizer (available since WP 4.0). |
| 426 |
$is_customize_preview = false; |
| 427 |
if ( function_exists( 'is_customize_preview' ) && is_customize_preview() ) { |
| 428 |
$is_customize_preview = is_customize_preview(); |
| 429 |
} |
| 430 |
|
| 431 |
/** |
| 432 |
* We only buffer the frontend requests (and then only if not a feed |
| 433 |
* and not turned off explicitly and not when being previewed in Customizer)! |
| 434 |
* NOTE: Tests throw a notice here due to is_feed() being called |
| 435 |
* while the main query hasn't been ran yet. Thats why we use |
| 436 |
* AUTOPTIMIZE_INIT_EARLIER in tests. |
| 437 |
*/ |
| 438 |
$do_buffering = ( ! is_admin() && ! is_feed() && ! is_embed() && ! $ao_noptimize && ! $is_customize_preview ); |
| 439 |
} |
| 440 |
|
| 441 |
return $do_buffering; |
| 442 |
} |
| 443 |
|
| 444 |
/** |
| 445 |
* Returns true if given markup is considered valid/processable/optimizable. |
| 446 |
* |
| 447 |
* @param string $content Markup. |
| 448 |
* |
| 449 |
* @return bool |
| 450 |
*/ |
| 451 |
public function is_valid_buffer( $content ) |
| 452 |
{ |
| 453 |
// Defaults to true. |
| 454 |
$valid = true; |
| 455 |
|
| 456 |
$has_no_html_tag = ( false === stripos( $content, '<html' ) ); |
| 457 |
$has_xsl_stylesheet = ( false !== stripos( $content, '<xsl:stylesheet' ) || false !== stripos( $content, '<?xml-stylesheet' ) ); |
| 458 |
$has_html5_doctype = ( preg_match( '/^<!DOCTYPE.+html>/i', ltrim( $content ) ) > 0 ); |
| 459 |
$has_noptimize_page = ( false !== stripos( $content, '<!-- noptimize-page -->' ) ); |
| 460 |
|
| 461 |
if ( $has_no_html_tag ) { |
| 462 |
// Can't be valid amp markup without an html tag preceding it. |
| 463 |
$is_amp_markup = false; |
| 464 |
} else { |
| 465 |
$is_amp_markup = self::is_amp_markup( $content ); |
| 466 |
} |
| 467 |
|
| 468 |
// If it's not html, or if it's amp or contains xsl stylesheets we don't touch it. |
| 469 |
if ( $has_no_html_tag && ! $has_html5_doctype || $is_amp_markup || $has_xsl_stylesheet || $has_noptimize_page ) { |
| 470 |
$valid = false; |
| 471 |
} |
| 472 |
|
| 473 |
return $valid; |
| 474 |
} |
| 475 |
|
| 476 |
/** |
| 477 |
* Returns true if given $content is considered to be AMP markup. |
| 478 |
* This is far from actual validation against AMP spec, but it'll do for now. |
| 479 |
* |
| 480 |
* @param string $content Markup to check. |
| 481 |
* |
| 482 |
* @return bool |
| 483 |
*/ |
| 484 |
public static function is_amp_markup( $content ) |
| 485 |
{ |
| 486 |
// Short-circuit if the page is already AMP from the start. |
| 487 |
if ( |
| 488 |
preg_match( |
| 489 |
sprintf( |
| 490 |
'#^(?:<!.*?>|\s+)*+<html(?=\s)[^>]*?\s(%1$s|%2$s|%3$s)(\s|=|>)#is', |
| 491 |
'amp', |
| 492 |
"\xE2\x9A\xA1", // From \AmpProject\Attribute::AMP_EMOJI. |
| 493 |
"\xE2\x9A\xA1\xEF\xB8\x8F" // From \AmpProject\Attribute::AMP_EMOJI_ALT, per https://github.com/ampproject/amphtml/issues/25990. |
| 494 |
), |
| 495 |
$content |
| 496 |
) |
| 497 |
) { |
| 498 |
return true; |
| 499 |
} |
| 500 |
|
| 501 |
// Or else short-circuit if the AMP plugin will be processing the output to be an AMP page. |
| 502 |
if ( function_exists( 'amp_is_request' ) ) { |
| 503 |
return amp_is_request(); // For AMP plugin v2.0+. |
| 504 |
} elseif ( function_exists( 'is_amp_endpoint' ) ) { |
| 505 |
return is_amp_endpoint(); // For older/other AMP plugins (still supported in 2.0 as an alias). |
| 506 |
} |
| 507 |
|
| 508 |
return false; |
| 509 |
} |
| 510 |
|
| 511 |
/** |
| 512 |
* Processes/optimizes the output-buffered content and returns it. |
| 513 |
* If the content is not processable, it is returned unmodified. |
| 514 |
* |
| 515 |
* @param string $content Buffered content. |
| 516 |
* |
| 517 |
* @return string |
| 518 |
*/ |
| 519 |
public function end_buffering( $content ) |
| 520 |
{ |
| 521 |
// Bail early without modifying anything if we can't handle the content. |
| 522 |
if ( ! $this->is_valid_buffer( $content ) ) { |
| 523 |
return $content; |
| 524 |
} |
| 525 |
|
| 526 |
$conf = autoptimizeConfig::instance(); |
| 527 |
|
| 528 |
// Determine what needs to be ran. |
| 529 |
$classes = array(); |
| 530 |
if ( $conf->get( 'autoptimize_js' ) ) { |
| 531 |
$classes[] = 'autoptimizeScripts'; |
| 532 |
} |
| 533 |
if ( $conf->get( 'autoptimize_css' ) ) { |
| 534 |
$classes[] = 'autoptimizeStyles'; |
| 535 |
} |
| 536 |
if ( $conf->get( 'autoptimize_html' ) ) { |
| 537 |
$classes[] = 'autoptimizeHTML'; |
| 538 |
} |
| 539 |
|
| 540 |
$classoptions = array( |
| 541 |
'autoptimizeScripts' => array( |
| 542 |
'aggregate' => $conf->get( 'autoptimize_js_aggregate' ), |
| 543 |
'defer_not_aggregate' => $conf->get( 'autoptimize_js_defer_not_aggregate' ), |
| 544 |
'defer_inline' => $conf->get( 'autoptimize_js_defer_inline' ), |
| 545 |
'justhead' => $conf->get( 'autoptimize_js_justhead' ), |
| 546 |
'forcehead' => $conf->get( 'autoptimize_js_forcehead' ), |
| 547 |
'trycatch' => $conf->get( 'autoptimize_js_trycatch' ), |
| 548 |
'js_exclude' => $conf->get( 'autoptimize_js_exclude' ), |
| 549 |
'cdn_url' => $conf->get( 'autoptimize_cdn_url' ), |
| 550 |
'include_inline' => $conf->get( 'autoptimize_js_include_inline' ), |
| 551 |
'minify_excluded' => $conf->get( 'autoptimize_minify_excluded' ), |
| 552 |
), |
| 553 |
'autoptimizeStyles' => array( |
| 554 |
'aggregate' => $conf->get( 'autoptimize_css_aggregate' ), |
| 555 |
'justhead' => $conf->get( 'autoptimize_css_justhead' ), |
| 556 |
'datauris' => $conf->get( 'autoptimize_css_datauris' ), |
| 557 |
'defer' => $conf->get( 'autoptimize_css_defer' ), |
| 558 |
'defer_inline' => $conf->get( 'autoptimize_css_defer_inline' ), |
| 559 |
'inline' => $conf->get( 'autoptimize_css_inline' ), |
| 560 |
'css_exclude' => $conf->get( 'autoptimize_css_exclude' ), |
| 561 |
'cdn_url' => $conf->get( 'autoptimize_cdn_url' ), |
| 562 |
'include_inline' => $conf->get( 'autoptimize_css_include_inline' ), |
| 563 |
'nogooglefont' => $conf->get( 'autoptimize_css_nogooglefont' ), |
| 564 |
'minify_excluded' => $conf->get( 'autoptimize_minify_excluded' ), |
| 565 |
), |
| 566 |
'autoptimizeHTML' => array( |
| 567 |
'keepcomments' => $conf->get( 'autoptimize_html_keepcomments' ), |
| 568 |
'minify_inline' => $conf->get( 'autoptimize_html_minify_inline' ), |
| 569 |
), |
| 570 |
); |
| 571 |
|
| 572 |
$content = apply_filters( 'autoptimize_filter_html_before_minify', $content ); |
| 573 |
|
| 574 |
// Run the classes! |
| 575 |
foreach ( $classes as $name ) { |
| 576 |
$instance = new $name( $content ); |
| 577 |
if ( $instance->read( $classoptions[ $name ] ) ) { |
| 578 |
$instance->minify(); |
| 579 |
$instance->cache(); |
| 580 |
$content = $instance->getcontent(); |
| 581 |
} |
| 582 |
unset( $instance ); |
| 583 |
} |
| 584 |
|
| 585 |
$content = apply_filters( 'autoptimize_html_after_minify', $content ); |
| 586 |
|
| 587 |
return $content; |
| 588 |
} |
| 589 |
|
| 590 |
public static function autoptimize_nobuffer_optimize( $html_in ) { |
| 591 |
$html_out = $html_in; |
| 592 |
|
| 593 |
if ( apply_filters( 'autoptimize_filter_speedupper', true ) ) { |
| 594 |
$ao_speedupper = new autoptimizeSpeedupper(); |
| 595 |
} |
| 596 |
|
| 597 |
$self = new self( AUTOPTIMIZE_PLUGIN_VERSION, AUTOPTIMIZE_PLUGIN_FILE ); |
| 598 |
if ( $self->should_buffer() ) { |
| 599 |
$html_out = $self->end_buffering( $html_in ); |
| 600 |
} |
| 601 |
return $html_out; |
| 602 |
} |
| 603 |
|
| 604 |
public static function on_uninstall() |
| 605 |
{ |
| 606 |
// clear the cache. |
| 607 |
autoptimizeCache::clearall(); |
| 608 |
|
| 609 |
// remove postmeta if active. |
| 610 |
if ( autoptimizeConfig::is_ao_meta_settings_active() ) { |
| 611 |
delete_post_meta_by_key( 'ao_post_optimize' ); |
| 612 |
} |
| 613 |
|
| 614 |
// remove all options. |
| 615 |
$delete_options = array( |
| 616 |
'autoptimize_cache_clean', |
| 617 |
'autoptimize_cache_nogzip', |
| 618 |
'autoptimize_css', |
| 619 |
'autoptimize_css_aggregate', |
| 620 |
'autoptimize_css_datauris', |
| 621 |
'autoptimize_css_justhead', |
| 622 |
'autoptimize_css_defer', |
| 623 |
'autoptimize_css_defer_inline', |
| 624 |
'autoptimize_css_inline', |
| 625 |
'autoptimize_css_exclude', |
| 626 |
'autoptimize_html', |
| 627 |
'autoptimize_html_keepcomments', |
| 628 |
'autoptimize_html_minify_inline', |
| 629 |
'autoptimize_enable_site_config', |
| 630 |
'autoptimize_enable_meta_ao_settings', |
| 631 |
'autoptimize_js', |
| 632 |
'autoptimize_js_aggregate', |
| 633 |
'autoptimize_js_defer_not_aggregate', |
| 634 |
'autoptimize_js_defer_inline', |
| 635 |
'autoptimize_js_exclude', |
| 636 |
'autoptimize_js_forcehead', |
| 637 |
'autoptimize_js_justhead', |
| 638 |
'autoptimize_js_trycatch', |
| 639 |
'autoptimize_version', |
| 640 |
'autoptimize_show_adv', |
| 641 |
'autoptimize_cdn_url', |
| 642 |
'autoptimize_cachesize_notice', |
| 643 |
'autoptimize_css_include_inline', |
| 644 |
'autoptimize_js_include_inline', |
| 645 |
'autoptimize_optimize_logged', |
| 646 |
'autoptimize_optimize_checkout', |
| 647 |
'autoptimize_extra_settings', |
| 648 |
'autoptimize_service_availablity', |
| 649 |
'autoptimize_imgopt_provider_stat', |
| 650 |
'autoptimize_imgopt_launched', |
| 651 |
'autoptimize_imgopt_settings', |
| 652 |
'autoptimize_minify_excluded', |
| 653 |
'autoptimize_cache_fallback', |
| 654 |
'autoptimize_ccss_rules', |
| 655 |
'autoptimize_ccss_additional', |
| 656 |
'autoptimize_ccss_queue', |
| 657 |
'autoptimize_ccss_viewport', |
| 658 |
'autoptimize_ccss_finclude', |
| 659 |
'autoptimize_ccss_rlimit', |
| 660 |
'autoptimize_ccss_rtimelimit', |
| 661 |
'autoptimize_ccss_noptimize', |
| 662 |
'autoptimize_ccss_debug', |
| 663 |
'autoptimize_ccss_key', |
| 664 |
'autoptimize_ccss_keyst', |
| 665 |
'autoptimize_ccss_version', |
| 666 |
'autoptimize_ccss_loggedin', |
| 667 |
'autoptimize_ccss_forcepath', |
| 668 |
'autoptimize_ccss_deferjquery', |
| 669 |
'autoptimize_ccss_domain', |
| 670 |
'autoptimize_ccss_unloadccss', |
| 671 |
'autoptimize_installed_before_compatibility', |
| 672 |
); |
| 673 |
|
| 674 |
if ( ! is_multisite() ) { |
| 675 |
foreach ( $delete_options as $del_opt ) { |
| 676 |
delete_option( $del_opt ); |
| 677 |
} |
| 678 |
autoptimizeMain::remove_cronjobs(); |
| 679 |
} else { |
| 680 |
global $wpdb; |
| 681 |
$blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" ); |
| 682 |
$original_blog_id = get_current_blog_id(); |
| 683 |
foreach ( $blog_ids as $blog_id ) { |
| 684 |
switch_to_blog( $blog_id ); |
| 685 |
foreach ( $delete_options as $del_opt ) { |
| 686 |
delete_option( $del_opt ); |
| 687 |
} |
| 688 |
autoptimizeMain::remove_cronjobs(); |
| 689 |
} |
| 690 |
switch_to_blog( $original_blog_id ); |
| 691 |
} |
| 692 |
|
| 693 |
// Remove AO CCSS cached files and directory. |
| 694 |
$ao_ccss_dir = WP_CONTENT_DIR . '/uploads/ao_ccss/'; |
| 695 |
if ( file_exists( $ao_ccss_dir ) && is_dir( $ao_ccss_dir ) ) { |
| 696 |
// fixme: should check for subdirs when in multisite and remove contents of those as well. |
| 697 |
array_map( 'unlink', glob( $ao_ccss_dir . '*.{css,html,json,log,zip,lock}', GLOB_BRACE ) ); |
| 698 |
rmdir( $ao_ccss_dir ); |
| 699 |
} |
| 700 |
|
| 701 |
// Remove 404-handler (although that should have been removed in clearall already). |
| 702 |
$_fallback_php = trailingslashit( WP_CONTENT_DIR ) . 'autoptimize_404_handler.php'; |
| 703 |
if ( file_exists( $_fallback_php ) ) { |
| 704 |
unlink( $_fallback_php ); |
| 705 |
} |
| 706 |
} |
| 707 |
|
| 708 |
public static function on_deactivation() |
| 709 |
{ |
| 710 |
if ( is_multisite() && is_network_admin() ) { |
| 711 |
global $wpdb; |
| 712 |
$blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" ); |
| 713 |
$original_blog_id = get_current_blog_id(); |
| 714 |
foreach ( $blog_ids as $blog_id ) { |
| 715 |
switch_to_blog( $blog_id ); |
| 716 |
autoptimizeMain::remove_cronjobs(); |
| 717 |
} |
| 718 |
switch_to_blog( $original_blog_id ); |
| 719 |
} else { |
| 720 |
autoptimizeMain::remove_cronjobs(); |
| 721 |
} |
| 722 |
autoptimizeCache::clearall(); |
| 723 |
} |
| 724 |
|
| 725 |
public static function remove_cronjobs() { |
| 726 |
// Remove scheduled events. |
| 727 |
foreach ( array( 'ao_cachechecker', 'ao_ccss_queue', 'ao_ccss_maintenance', 'ao_ccss_keychecker' ) as $_event ) { |
| 728 |
if ( wp_get_schedule( $_event ) ) { |
| 729 |
wp_clear_scheduled_hook( $_event ); |
| 730 |
} |
| 731 |
} |
| 732 |
} |
| 733 |
|
| 734 |
public static function notice_cache_unavailable() |
| 735 |
{ |
| 736 |
echo '<div class="error"><p>'; |
| 737 |
// Translators: %s is the cache directory location. |
| 738 |
printf( __( 'Autoptimize cannot write to the cache directory (%s), please fix to enable CSS/ JS optimization!', 'autoptimize' ), AUTOPTIMIZE_CACHE_DIR ); |
| 739 |
echo '</p></div>'; |
| 740 |
} |
| 741 |
|
| 742 |
public static function notice_installed() |
| 743 |
{ |
| 744 |
echo '<div class="updated"><p>'; |
| 745 |
printf( __( 'Thank you for installing and activating Autoptimize. Please configure it under %1$sSettings -> Autoptimize%2$s to start improving your site\'s performance.', 'autoptimize' ), '<a href="options-general.php?page=autoptimize">', '</a>' ); |
| 746 |
echo '</p></div>'; |
| 747 |
} |
| 748 |
|
| 749 |
public static function notice_updated() |
| 750 |
{ |
| 751 |
echo '<div class="updated"><p>'; |
| 752 |
_e( 'Autoptimize has just been updated. Please <strong>test your site now</strong> and adapt Autoptimize config if needed.', 'autoptimize' ); |
| 753 |
echo '</p></div>'; |
| 754 |
} |
| 755 |
|
| 756 |
public static function notice_plug_imgopt() |
| 757 |
{ |
| 758 |
// Translators: the URL added points to the Autopmize Extra settings. |
| 759 |
$_ao_imgopt_plug_notice = sprintf( __( 'Did you know Autoptimize that Autoptimize offers on-the-fly image optimization (with support for WebP and AVIF) and CDN via ShortPixel? Check out the %1$sAutoptimize Image settings%2$s to enable this option.', 'autoptimize' ), '<a href="options-general.php?page=autoptimize_imgopt">', '</a>' ); |
| 760 |
$_ao_imgopt_plug_notice = apply_filters( 'autoptimize_filter_main_imgopt_plug_notice', $_ao_imgopt_plug_notice ); |
| 761 |
$_ao_imgopt_launch_ok = autoptimizeImages::launch_ok_wrapper(); |
| 762 |
$_ao_imgopt_plug_dismissible = 'ao-img-opt-plug-123'; |
| 763 |
$_ao_imgopt_active = autoptimizeImages::imgopt_active(); |
| 764 |
$_is_ao_settings_page = autoptimizeUtils::is_ao_settings(); |
| 765 |
|
| 766 |
if ( current_user_can( 'manage_options' ) && ! defined( 'AO_PRO_VERSION' ) && $_is_ao_settings_page && '' !== $_ao_imgopt_plug_notice && ! $_ao_imgopt_active && $_ao_imgopt_launch_ok && PAnD::is_admin_notice_active( $_ao_imgopt_plug_dismissible ) ) { |
| 767 |
echo '<div class="notice notice-info is-dismissible" data-dismissible="' . $_ao_imgopt_plug_dismissible . '"><p>'; |
| 768 |
echo $_ao_imgopt_plug_notice; |
| 769 |
echo '</p></div>'; |
| 770 |
} |
| 771 |
} |
| 772 |
|
| 773 |
public static function notice_nopagecache() |
| 774 |
{ |
| 775 |
/* |
| 776 |
* Autoptimize does not do page caching (yet) but not everyone knows, so below logic tries to find out if page caching is available and if not show a notice on the AO Settings pages. |
| 777 |
* |
| 778 |
* uses helper function in autoptimizeUtils.php |
| 779 |
*/ |
| 780 |
$_ao_nopagecache_notice = __( 'It looks like your site might not have <strong>page caching</strong> which is a <strong>must-have for performance</strong>. If you are sure you have a page cache, you can close this notice, but when in doubt check with your host if they offer this or install a page caching plugin like for example', 'autoptimize' ); |
| 781 |
$_ao_pagecache_install_url = network_admin_url() . 'plugin-install.php?tab=search&type=term&s='; |
| 782 |
$_ao_nopagecache_notice .= ' <a href="' . $_ao_pagecache_install_url . 'wp+super+cache' . '">WP Super Cache</a>, <a href="' . $_ao_pagecache_install_url . 'keycdn+cache+enabler' . '">KeyCDN Cache Enabler</a>, ...'; |
| 783 |
$_ao_nopagecache_dismissible = 'ao-nopagecache-forever'; // the notice is only shown once and will not re-appear when dismissed. |
| 784 |
$_is_ao_settings_page = autoptimizeUtils::is_ao_settings(); |
| 785 |
|
| 786 |
if ( current_user_can( 'manage_options' ) && $_is_ao_settings_page && PAnD::is_admin_notice_active( $_ao_nopagecache_dismissible ) && true === apply_filters( 'autoptimize_filter_main_show_pagecache_notice', true ) ) { |
| 787 |
if ( false === autoptimizeUtils::find_pagecache() ) { |
| 788 |
echo '<div class="notice notice-info is-dismissible" data-dismissible="' . $_ao_nopagecache_dismissible . '"><p>'; |
| 789 |
echo $_ao_nopagecache_notice; |
| 790 |
echo '</p></div>'; |
| 791 |
} |
| 792 |
} |
| 793 |
} |
| 794 |
|
| 795 |
public static function notice_potential_conflict() |
| 796 |
{ |
| 797 |
/* |
| 798 |
* Using other plugins to do CSS/ JS optimization can cause unexpected and hard to troubleshoot issues, warn users who seem to be in that situation. |
| 799 |
*/ |
| 800 |
// Translators: the sentence will be finished with the name of the offending plugin and a final stop. |
| 801 |
$_ao_potential_conflict_notice = __( 'It looks like you have <strong>another plugin also doing CSS and/ or JS optimization</strong>, which can result in hard to troubleshoot <strong>conflicts</strong>. For this reason it is recommended to disable this functionality in', 'autoptimize' ) . ' '; |
| 802 |
$_ao_potential_conflict_dismissible = 'ao-potential-conflict-forever'; // the notice is only shown once and will not re-appear when dismissed. |
| 803 |
$_is_ao_settings_page = autoptimizeUtils::is_ao_settings(); |
| 804 |
|
| 805 |
if ( current_user_can( 'manage_options' ) && $_is_ao_settings_page && PAnD::is_admin_notice_active( $_ao_potential_conflict_dismissible ) && true === apply_filters( 'autoptimize_filter_main_show_potential_conclict_notice', true ) ) { |
| 806 |
$_potential_conflicts = autoptimizeUtils::find_potential_conflicts(); |
| 807 |
if ( false !== $_potential_conflicts ) { |
| 808 |
$_ao_potential_conflict_notice .= '<strong>' . $_potential_conflicts . '</strong>.'; |
| 809 |
echo '<div class="notice notice-info is-dismissible" data-dismissible="' . $_ao_potential_conflict_dismissible . '"><p>'; |
| 810 |
echo $_ao_potential_conflict_notice; |
| 811 |
echo '</p></div>'; |
| 812 |
} |
| 813 |
} |
| 814 |
} |
| 815 |
} |
| 816 |
|