PluginProbe
Autoptimize / 3.1.11
Autoptimize v3.1.11
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.11, at classes/autoptimizeMain.php

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