PluginProbe
Autoptimize / 3.1.7
Autoptimize v3.1.7
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 2.7.8 All 107 releases
autoptimize / classes / autoptimizeMain.php

autoptimizeMain.php in Autoptimize 3.1.7, at classes/autoptimizeMain.php

846 lines 37.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 add_action( 'admin_notices', 'autoptimizeMain::notice_imgopt_issue' );
218 }
219 }
220
221 public function maybe_run_admin_only_trinkets()
222 {
223 // Loads partners tab and exit survey code if in admin (and not in admin-ajax.php)!
224 if ( autoptimizeConfig::is_admin_and_not_ajax() ) {
225 new autoptimizePartners();
226 new autoptimizeExitSurvey();
227 }
228 }
229
230 public function criticalcss()
231 {
232 if ( apply_filters( 'autoptimize_filter_criticalcss_active', true ) && ! autoptimizeUtils::is_plugin_active( 'autoptimize-criticalcss/ao_criticss_aas.php' ) ) {
233 return $this->_criticalcss;
234 } else {
235 return false;
236 }
237 }
238
239 public function maybe_run_criticalcss()
240 {
241 // Loads criticalcss if the filter returns true & old power-up is not active.
242 if ( apply_filters( 'autoptimize_filter_criticalcss_active', true ) && ! autoptimizeUtils::is_plugin_active( 'autoptimize-criticalcss/ao_criticss_aas.php' ) ) {
243 $this->_criticalcss = new autoptimizeCriticalCSSBase();
244 $this->_criticalcss->setup();
245 $this->_criticalcss->load_requires();
246 }
247 }
248
249 public function maybe_run_notfound_fallback()
250 {
251 if ( autoptimizeCache::do_fallback() ) {
252 add_action( 'template_redirect', array( 'autoptimizeCache', 'wordpress_notfound_fallback' ) );
253 }
254 }
255
256 public function maybe_run_ao_compat()
257 {
258 // Condtionally loads the compatibility-class to ensure more out-of-the-box compatibility with big players.
259 $_run_compat = true;
260
261 if ( autoptimizeOptionWrapper::get_option( 'autoptimize_installed_before_compatibility', false ) ) {
262 // If AO was already running before Compatibility logic was added, don't run compat by default
263 // because it can be assumed everything works and we want to avoid (perf) regressions that
264 // could occur due to compatibility code.
265 $_run_compat = false;
266 }
267
268 if ( apply_filters( 'autoptimize_filter_init_compatibility', $_run_compat ) ) {
269 new autoptimizeCompatibility();
270 }
271 }
272
273 public function hook_page_cache_purge()
274 {
275 // hook into a collection of page cache purge actions if filter allows.
276 if ( apply_filters( 'autoptimize_filter_main_hookpagecachepurge', true ) ) {
277 $page_cache_purge_actions = array(
278 'after_rocket_clean_domain', // exists.
279 'hyper_cache_purged', // Stefano confirmed this will be added.
280 'w3tc_flush_posts', // exits.
281 'w3tc_flush_all', // exists.
282 'ce_action_cache_cleared', // Sven confirmed this will be added.
283 'aoce_action_cache_cleared', // Some other cache enabler.
284 'comet_cache_wipe_cache', // still to be confirmed by Raam.
285 'wp_cache_cleared', // cfr. https://github.com/Automattic/wp-super-cache/pull/537.
286 'wpfc_delete_cache', // Emre confirmed this will be added this.
287 'swift_performance_after_clear_all_cache', // swift perf. yeah!
288 'wpo_cache_flush', // wp-optimize.
289 'rt_nginx_helper_after_fastcgi_purge_all', // nginx helper.
290 );
291 $page_cache_purge_actions = apply_filters( 'autoptimize_filter_main_pagecachepurgeactions', $page_cache_purge_actions );
292 foreach ( $page_cache_purge_actions as $purge_action ) {
293 add_action( $purge_action, 'autoptimizeCache::clearall_actionless' );
294 }
295 }
296 }
297
298 /**
299 * Setup output buffering if needed.
300 *
301 * @return void
302 */
303 public function start_buffering()
304 {
305 if ( $this->should_buffer() ) {
306
307 // Load speedupper conditionally (true by default).
308 if ( apply_filters( 'autoptimize_filter_speedupper', true ) ) {
309 $ao_speedupper = new autoptimizeSpeedupper();
310 }
311
312 $conf = autoptimizeConfig::instance();
313
314 if ( $conf->get( 'autoptimize_js' ) ) {
315 if ( ! defined( 'CONCATENATE_SCRIPTS' ) ) {
316 define( 'CONCATENATE_SCRIPTS', false );
317 }
318 if ( ! defined( 'COMPRESS_SCRIPTS' ) ) {
319 define( 'COMPRESS_SCRIPTS', false );
320 }
321 }
322
323 if ( $conf->get( 'autoptimize_css' ) ) {
324 if ( ! defined( 'COMPRESS_CSS' ) ) {
325 define( 'COMPRESS_CSS', false );
326 }
327 }
328
329 if ( apply_filters( 'autoptimize_filter_obkiller', false ) ) {
330 while ( ob_get_level() > 0 ) {
331 ob_end_clean();
332 }
333 }
334
335 // Now, start the real thing!
336 ob_start( array( $this, 'end_buffering' ) );
337 }
338 }
339
340 /**
341 * Returns true if all the conditions to start output buffering are satisfied.
342 *
343 * @param bool $doing_tests Allows overriding the optimization of only
344 * deciding once per request (for use in tests).
345 * @return bool
346 */
347 public static function should_buffer( $doing_tests = false )
348 {
349 static $do_buffering = null;
350
351 // Only check once in case we're called multiple times by others but
352 // still allows multiple calls when doing tests.
353 if ( null === $do_buffering || $doing_tests ) {
354
355 $ao_noptimize = false;
356
357 // Checking for DONOTMINIFY constant as used by e.g. WooCommerce POS.
358 if ( defined( 'DONOTMINIFY' ) && ( constant( 'DONOTMINIFY' ) === true || constant( 'DONOTMINIFY' ) === 'true' ) ) {
359 $ao_noptimize = true;
360 }
361
362 // Skip checking query strings if they're disabled.
363 if ( apply_filters( 'autoptimize_filter_honor_qs_noptimize', true ) ) {
364 // Check for `ao_noptimize` (and other) keys in the query string
365 // to get non-optimized page for debugging.
366 $keys = array(
367 'ao_noptimize',
368 'ao_noptirocket',
369 );
370 foreach ( $keys as $key ) {
371 if ( array_key_exists( $key, $_GET ) && '1' === $_GET[ $key ] ) {
372 $ao_noptimize = true;
373 break;
374 }
375 }
376 }
377
378 // If setting says not to optimize logged in user and user is logged in...
379 if ( false === $ao_noptimize && 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_optimize_logged', 'on' ) && is_user_logged_in() && current_user_can( 'edit_posts' ) ) {
380 $ao_noptimize = true;
381 }
382
383 // If setting says not to optimize cart/checkout.
384 if ( false === $ao_noptimize && 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_optimize_checkout', 'off' ) ) {
385 // Checking for woocommerce, easy digital downloads and wp ecommerce...
386 foreach ( array( 'is_checkout', 'is_cart', 'is_account_page', 'edd_is_checkout', 'wpsc_is_cart', 'wpsc_is_checkout' ) as $func ) {
387 if ( function_exists( $func ) && $func() ) {
388 $ao_noptimize = true;
389 break;
390 }
391 }
392 }
393
394 // Misc. querystring paramaters that will stop AO from doing optimizations (pagebuilders +
395 // 2 generic parameters that could/ should become standard between optimization plugins?).
396 if ( false === $ao_noptimize ) {
397 $_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' );
398
399 // doing Jonathan a quick favor to allow correct unused CSS generation ;-) .
400 if ( apply_filters( 'autoptimize_filter_main_showstoppers_do_wp_rocket_a_favor', true ) ) {
401 $_qs_showstoppers[] = 'nowprocket';
402 }
403
404 foreach ( $_qs_showstoppers as $_showstopper ) {
405 if ( array_key_exists( $_showstopper, $_GET ) ) {
406 $ao_noptimize = true;
407 break;
408 }
409 }
410 }
411
412 // Also honor PageSpeed=off parameter as used by mod_pagespeed, in use by some pagebuilders,
413 // see https://www.modpagespeed.com/doc/experiment#ModPagespeed for info on that.
414 if ( false === $ao_noptimize && array_key_exists( 'PageSpeed', $_GET ) && 'off' === $_GET['PageSpeed'] ) {
415 $ao_noptimize = true;
416 }
417
418 // If page/ post check post_meta to see if optimize is off.
419 if ( false === autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_optimize' ) ) {
420 $ao_noptimize = true;
421 }
422
423 // And finally allows blocking of autoptimization on your own terms regardless of above decisions.
424 $ao_noptimize = (bool) apply_filters( 'autoptimize_filter_noptimize', $ao_noptimize );
425
426 // Check for site being previewed in the Customizer (available since WP 4.0).
427 $is_customize_preview = false;
428 if ( function_exists( 'is_customize_preview' ) && is_customize_preview() ) {
429 $is_customize_preview = is_customize_preview();
430 }
431
432 // explicitly disable when is_login exists and is true but don't use it direclty because older versions of WordPress don't have that yet.
433 $is_login = false;
434 if ( function_exists( 'is_login' ) && true === is_login() ) {
435 $is_login = true;
436 }
437
438 /**
439 * We only buffer the frontend requests (and then only if not a feed
440 * and not turned off explicitly and not when being previewed in Customizer)!
441 * NOTE: Tests throw a notice here due to is_feed() being called
442 * while the main query hasn't been ran yet. Thats why we use
443 * AUTOPTIMIZE_INIT_EARLIER in tests.
444 */
445 $do_buffering = ( ! is_admin() && ! is_feed() && ! is_embed() && ! $is_login && ! $is_customize_preview && ! $ao_noptimize );
446 }
447
448 return $do_buffering;
449 }
450
451 /**
452 * Returns true if given markup is considered valid/processable/optimizable.
453 *
454 * @param string $content Markup.
455 *
456 * @return bool
457 */
458 public function is_valid_buffer( $content )
459 {
460 // Defaults to true.
461 $valid = true;
462
463 $has_no_html_tag = ( false === stripos( $content, '<html' ) );
464 $has_xsl_stylesheet = ( false !== stripos( $content, '<xsl:stylesheet' ) || false !== stripos( $content, '<?xml-stylesheet' ) );
465 $has_html5_doctype = ( preg_match( '/^<!DOCTYPE.+html>/i', ltrim( $content ) ) > 0 );
466 $has_noptimize_page = ( false !== stripos( $content, '<!-- noptimize-page -->' ) );
467
468 if ( $has_no_html_tag ) {
469 // Can't be valid amp markup without an html tag preceding it.
470 $is_amp_markup = false;
471 } else {
472 $is_amp_markup = self::is_amp_markup( $content );
473 }
474
475 // If it's not html, or if it's amp or contains xsl stylesheets we don't touch it.
476 if ( $has_no_html_tag && ! $has_html5_doctype || $is_amp_markup || $has_xsl_stylesheet || $has_noptimize_page ) {
477 $valid = false;
478 }
479
480 return $valid;
481 }
482
483 /**
484 * Returns true if given $content is considered to be AMP markup.
485 * This is far from actual validation against AMP spec, but it'll do for now.
486 *
487 * @param string $content Markup to check.
488 *
489 * @return bool
490 */
491 public static function is_amp_markup( $content )
492 {
493 // Short-circuit if the page is already AMP from the start.
494 if (
495 preg_match(
496 sprintf(
497 '#^(?:<!.*?>|\s+)*+<html(?=\s)[^>]*?\s(%1$s|%2$s|%3$s)(\s|=|>)#is',
498 'amp',
499 "\xE2\x9A\xA1", // From \AmpProject\Attribute::AMP_EMOJI.
500 "\xE2\x9A\xA1\xEF\xB8\x8F" // From \AmpProject\Attribute::AMP_EMOJI_ALT, per https://github.com/ampproject/amphtml/issues/25990.
501 ),
502 $content
503 )
504 ) {
505 return true;
506 }
507
508 // Or else short-circuit if the AMP plugin will be processing the output to be an AMP page.
509 if ( function_exists( 'amp_is_request' ) ) {
510 return amp_is_request(); // For AMP plugin v2.0+.
511 } elseif ( function_exists( 'is_amp_endpoint' ) ) {
512 return is_amp_endpoint(); // For older/other AMP plugins (still supported in 2.0 as an alias).
513 }
514
515 return false;
516 }
517
518 /**
519 * Processes/optimizes the output-buffered content and returns it.
520 * If the content is not processable, it is returned unmodified.
521 *
522 * @param string $content Buffered content.
523 *
524 * @return string
525 */
526 public function end_buffering( $content )
527 {
528 // Bail early without modifying anything if we can't handle the content.
529 if ( ! $this->is_valid_buffer( $content ) ) {
530 return $content;
531 }
532
533 $conf = autoptimizeConfig::instance();
534
535 // Determine what needs to be ran.
536 $classes = array();
537 if ( $conf->get( 'autoptimize_js' ) ) {
538 $classes[] = 'autoptimizeScripts';
539 }
540 if ( $conf->get( 'autoptimize_css' ) ) {
541 $classes[] = 'autoptimizeStyles';
542 }
543 if ( $conf->get( 'autoptimize_html' ) ) {
544 $classes[] = 'autoptimizeHTML';
545 }
546
547 $classoptions = array(
548 'autoptimizeScripts' => array(
549 'aggregate' => $conf->get( 'autoptimize_js_aggregate' ),
550 'defer_not_aggregate' => $conf->get( 'autoptimize_js_defer_not_aggregate' ),
551 'defer_inline' => $conf->get( 'autoptimize_js_defer_inline' ),
552 'justhead' => $conf->get( 'autoptimize_js_justhead' ),
553 'forcehead' => $conf->get( 'autoptimize_js_forcehead' ),
554 'trycatch' => $conf->get( 'autoptimize_js_trycatch' ),
555 'js_exclude' => $conf->get( 'autoptimize_js_exclude' ),
556 'cdn_url' => $conf->get( 'autoptimize_cdn_url' ),
557 'include_inline' => $conf->get( 'autoptimize_js_include_inline' ),
558 'minify_excluded' => $conf->get( 'autoptimize_minify_excluded' ),
559 ),
560 'autoptimizeStyles' => array(
561 'aggregate' => $conf->get( 'autoptimize_css_aggregate' ),
562 'justhead' => $conf->get( 'autoptimize_css_justhead' ),
563 'datauris' => $conf->get( 'autoptimize_css_datauris' ),
564 'defer' => $conf->get( 'autoptimize_css_defer' ),
565 'defer_inline' => $conf->get( 'autoptimize_css_defer_inline' ),
566 'inline' => $conf->get( 'autoptimize_css_inline' ),
567 'css_exclude' => $conf->get( 'autoptimize_css_exclude' ),
568 'cdn_url' => $conf->get( 'autoptimize_cdn_url' ),
569 'include_inline' => $conf->get( 'autoptimize_css_include_inline' ),
570 'nogooglefont' => $conf->get( 'autoptimize_css_nogooglefont' ),
571 'minify_excluded' => $conf->get( 'autoptimize_minify_excluded' ),
572 ),
573 'autoptimizeHTML' => array(
574 'keepcomments' => $conf->get( 'autoptimize_html_keepcomments' ),
575 'minify_inline' => $conf->get( 'autoptimize_html_minify_inline' ),
576 ),
577 );
578
579 $content = apply_filters( 'autoptimize_filter_html_before_minify', $content );
580
581 // Run the classes!
582 foreach ( $classes as $name ) {
583 $instance = new $name( $content );
584 if ( $instance->read( $classoptions[ $name ] ) ) {
585 $instance->minify();
586 $instance->cache();
587 $content = $instance->getcontent();
588 }
589 unset( $instance );
590 }
591
592 $content = apply_filters( 'autoptimize_html_after_minify', $content );
593
594 return $content;
595 }
596
597 public static function autoptimize_nobuffer_optimize( $html_in ) {
598 $html_out = $html_in;
599
600 if ( apply_filters( 'autoptimize_filter_speedupper', true ) ) {
601 $ao_speedupper = new autoptimizeSpeedupper();
602 }
603
604 $self = new self( AUTOPTIMIZE_PLUGIN_VERSION, AUTOPTIMIZE_PLUGIN_FILE );
605 if ( $self->should_buffer() ) {
606 $html_out = $self->end_buffering( $html_in );
607 }
608 return $html_out;
609 }
610
611 public static function on_uninstall()
612 {
613 // clear the cache.
614 autoptimizeCache::clearall();
615
616 // remove postmeta if active.
617 if ( autoptimizeConfig::is_ao_meta_settings_active() ) {
618 delete_post_meta_by_key( 'ao_post_optimize' );
619 }
620
621 // remove all options.
622 $delete_options = array(
623 'autoptimize_cache_clean',
624 'autoptimize_cache_nogzip',
625 'autoptimize_css',
626 'autoptimize_css_aggregate',
627 'autoptimize_css_datauris',
628 'autoptimize_css_justhead',
629 'autoptimize_css_defer',
630 'autoptimize_css_defer_inline',
631 'autoptimize_css_inline',
632 'autoptimize_css_exclude',
633 'autoptimize_html',
634 'autoptimize_html_keepcomments',
635 'autoptimize_html_minify_inline',
636 'autoptimize_enable_site_config',
637 'autoptimize_enable_meta_ao_settings',
638 'autoptimize_js',
639 'autoptimize_js_aggregate',
640 'autoptimize_js_defer_not_aggregate',
641 'autoptimize_js_defer_inline',
642 'autoptimize_js_exclude',
643 'autoptimize_js_forcehead',
644 'autoptimize_js_justhead',
645 'autoptimize_js_trycatch',
646 'autoptimize_version',
647 'autoptimize_show_adv',
648 'autoptimize_cdn_url',
649 'autoptimize_cachesize_notice',
650 'autoptimize_css_include_inline',
651 'autoptimize_js_include_inline',
652 'autoptimize_optimize_logged',
653 'autoptimize_optimize_checkout',
654 'autoptimize_extra_settings',
655 'autoptimize_service_availablity',
656 'autoptimize_imgopt_provider_stat',
657 'autoptimize_imgopt_launched',
658 'autoptimize_imgopt_settings',
659 'autoptimize_minify_excluded',
660 'autoptimize_cache_fallback',
661 'autoptimize_ccss_rules',
662 'autoptimize_ccss_additional',
663 'autoptimize_ccss_queue',
664 'autoptimize_ccss_viewport',
665 'autoptimize_ccss_finclude',
666 'autoptimize_ccss_rlimit',
667 'autoptimize_ccss_rtimelimit',
668 'autoptimize_ccss_noptimize',
669 'autoptimize_ccss_debug',
670 'autoptimize_ccss_key',
671 'autoptimize_ccss_keyst',
672 'autoptimize_ccss_version',
673 'autoptimize_ccss_loggedin',
674 'autoptimize_ccss_forcepath',
675 'autoptimize_ccss_deferjquery',
676 'autoptimize_ccss_domain',
677 'autoptimize_ccss_unloadccss',
678 'autoptimize_installed_before_compatibility',
679 );
680
681 if ( ! is_multisite() ) {
682 foreach ( $delete_options as $del_opt ) {
683 delete_option( $del_opt );
684 }
685 autoptimizeMain::remove_cronjobs();
686 } else {
687 global $wpdb;
688 $blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
689 $original_blog_id = get_current_blog_id();
690 foreach ( $blog_ids as $blog_id ) {
691 switch_to_blog( $blog_id );
692 foreach ( $delete_options as $del_opt ) {
693 delete_option( $del_opt );
694 }
695 autoptimizeMain::remove_cronjobs();
696 }
697 switch_to_blog( $original_blog_id );
698 }
699
700 // Remove AO CCSS cached files and directory.
701 $ao_ccss_dir = WP_CONTENT_DIR . '/uploads/ao_ccss/';
702 if ( file_exists( $ao_ccss_dir ) && is_dir( $ao_ccss_dir ) ) {
703 // fixme: should check for subdirs when in multisite and remove contents of those as well.
704 array_map( 'unlink', glob( $ao_ccss_dir . '*.{css,html,json,log,zip,lock}', GLOB_BRACE ) );
705 rmdir( $ao_ccss_dir );
706 }
707
708 // Remove 404-handler (although that should have been removed in clearall already).
709 $_fallback_php = trailingslashit( WP_CONTENT_DIR ) . 'autoptimize_404_handler.php';
710 if ( file_exists( $_fallback_php ) ) {
711 unlink( $_fallback_php );
712 }
713 }
714
715 public static function on_deactivation()
716 {
717 if ( is_multisite() && is_network_admin() ) {
718 global $wpdb;
719 $blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
720 $original_blog_id = get_current_blog_id();
721 foreach ( $blog_ids as $blog_id ) {
722 switch_to_blog( $blog_id );
723 autoptimizeMain::remove_cronjobs();
724 }
725 switch_to_blog( $original_blog_id );
726 } else {
727 autoptimizeMain::remove_cronjobs();
728 }
729 autoptimizeCache::clearall();
730 }
731
732 public static function remove_cronjobs() {
733 // Remove scheduled events.
734 foreach ( array( 'ao_cachechecker', 'ao_ccss_queue', 'ao_ccss_maintenance', 'ao_ccss_keychecker' ) as $_event ) {
735 if ( wp_get_schedule( $_event ) ) {
736 wp_clear_scheduled_hook( $_event );
737 }
738 }
739 }
740
741 public static function notice_cache_unavailable()
742 {
743 echo '<div class="error"><p>';
744 // Translators: %s is the cache directory location.
745 printf( __( 'Autoptimize cannot write to the cache directory (%s), please fix to enable CSS/ JS optimization!', 'autoptimize' ), AUTOPTIMIZE_CACHE_DIR );
746 echo '</p></div>';
747 }
748
749 public static function notice_installed()
750 {
751 echo '<div class="updated"><p>';
752 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>' );
753 echo '</p></div>';
754 }
755
756 public static function notice_updated()
757 {
758 echo '<div class="updated"><p>';
759 _e( 'Autoptimize has just been updated. Please <strong>test your site now</strong> and adapt Autoptimize config if needed.', 'autoptimize' );
760 echo '</p></div>';
761 }
762
763 public static function notice_plug_imgopt()
764 {
765 // Translators: the URL added points to the Autopmize Extra settings.
766 $_ao_imgopt_plug_notice = sprintf( __( 'Did you know 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>' );
767 $_ao_imgopt_plug_notice = apply_filters( 'autoptimize_filter_main_imgopt_plug_notice', $_ao_imgopt_plug_notice );
768 $_ao_imgopt_launch_ok = autoptimizeImages::launch_ok_wrapper();
769 $_ao_imgopt_plug_dismissible = 'ao-img-opt-plug-123';
770 $_ao_imgopt_active = autoptimizeImages::imgopt_active();
771 $_is_ao_settings_page = autoptimizeUtils::is_ao_settings();
772
773 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 ) ) {
774 echo '<div class="notice notice-info is-dismissible" data-dismissible="' . $_ao_imgopt_plug_dismissible . '"><p>';
775 echo $_ao_imgopt_plug_notice;
776 echo '</p></div>';
777 }
778 }
779
780 public static function notice_imgopt_issue()
781 {
782 // Translators: the URL added points to the Autopmize Extra settings.
783 $_ao_imgopt_issue_notice = sprintf( __( 'Shortpixel reports it cannot always reach your site, which might mean some images are not optimized. You can %1$sread more about why this happens and how you can fix that problem here%2$s.', 'autoptimize' ), '<a href="https://shortpixel.com/knowledge-base/article/469-i-received-an-e-mail-that-says-some-of-my-images-are-not-accessible-what-should-i-do#fullarticle" target="_blank">', '</a>' );
784 $_ao_imgopt_issue_notice = apply_filters( 'autoptimize_filter_main_imgopt_issue_notice', $_ao_imgopt_issue_notice );
785 $_ao_imgopt_issue_dismissible = 'ao-img-opt-issue-14';
786 $_ao_imgopt_active = autoptimizeImages::imgopt_active();
787 $_ao_imgopt_status = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_provider_stat', '' );
788
789 if ( is_array( $_ao_imgopt_status ) && array_key_exists( 'TemporaryRedirectOrigin', $_ao_imgopt_status ) && ( $_ao_imgopt_status['TemporaryRedirectOrigin'] === "true" || $_ao_imgopt_status['TemporaryRedirectOrigin'] === true ) ) {
790 $_ao_imgopt_status_redirect_warning = true;
791 } else {
792 $_ao_imgopt_status_redirect_warning = false;
793 }
794
795 if ( current_user_can( 'manage_options' ) && $_ao_imgopt_active && $_ao_imgopt_status_redirect_warning && '' !== $_ao_imgopt_issue_notice && PAnD::is_admin_notice_active( $_ao_imgopt_issue_dismissible ) ) {
796 echo '<div class="notice notice-info is-dismissible" data-dismissible="' . $_ao_imgopt_issue_dismissible . '"><p>';
797 echo $_ao_imgopt_issue_notice;
798 echo '</p></div>';
799 }
800 }
801
802
803 public static function notice_nopagecache()
804 {
805 /*
806 * 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.
807 *
808 * uses helper function in autoptimizeUtils.php
809 */
810 $_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' );
811 $_ao_pagecache_install_url = network_admin_url() . 'plugin-install.php?tab=search&type=term&s=';
812 $_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>, ...';
813 $_ao_nopagecache_dismissible = 'ao-nopagecache-forever'; // the notice is only shown once and will not re-appear when dismissed.
814 $_is_ao_settings_page = autoptimizeUtils::is_ao_settings();
815
816 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 ) ) {
817 if ( false === autoptimizeUtils::find_pagecache() ) {
818 echo '<div class="notice notice-info is-dismissible" data-dismissible="' . $_ao_nopagecache_dismissible . '"><p>';
819 echo $_ao_nopagecache_notice;
820 echo '</p></div>';
821 }
822 }
823 }
824
825 public static function notice_potential_conflict()
826 {
827 /*
828 * 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.
829 */
830 // Translators: the sentence will be finished with the name of the offending plugin and a final stop.
831 $_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' ) . ' ';
832 $_ao_potential_conflict_dismissible = 'ao-potential-conflict-forever'; // the notice is only shown once and will not re-appear when dismissed.
833 $_is_ao_settings_page = autoptimizeUtils::is_ao_settings();
834
835 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 ) ) {
836 $_potential_conflicts = autoptimizeUtils::find_potential_conflicts();
837 if ( false !== $_potential_conflicts ) {
838 $_ao_potential_conflict_notice .= '<strong>' . $_potential_conflicts . '</strong>.';
839 echo '<div class="notice notice-info is-dismissible" data-dismissible="' . $_ao_potential_conflict_dismissible . '"><p>';
840 echo $_ao_potential_conflict_notice;
841 echo '</p></div>';
842 }
843 }
844 }
845 }
846