PluginProbe
Autoptimize / 2.4.3
Autoptimize v2.4.3
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 2.4.3, at classes/autoptimizeMain.php

563 lines 21.9 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 * Constructor.
32 *
33 * @param string $version Version.
34 * @param string $filepath Filepath. Needed for activation/deactivation/uninstall hooks.
35 */
36 public function __construct( $version, $filepath )
37 {
38 $this->version = $version;
39 $this->filepath = $filepath;
40 }
41
42 public function run()
43 {
44 $this->add_hooks();
45
46 // Runs cache size checker.
47 $checker = new autoptimizeCacheChecker();
48 $checker->run();
49 }
50
51 protected function add_hooks()
52 {
53 add_action( 'plugins_loaded', array( $this, 'setup' ) );
54
55 add_action( 'autoptimize_setup_done', array( $this, 'version_upgrades_check' ) );
56 add_action( 'autoptimize_setup_done', array( $this, 'check_cache_and_run' ) );
57 add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_ao_extra' ) );
58 add_action( 'autoptimize_setup_done', array( $this, 'maybe_run_partners_tab' ) );
59
60 add_action( 'init', array( $this, 'load_textdomain' ) );
61 add_action( 'plugins_loaded', array( $this, 'hook_page_cache_purge' ) );
62 add_action( 'admin_init', array( 'PAnD', 'init' ) );
63
64 register_activation_hook( $this->filepath, array( $this, 'on_activate' ) );
65 }
66
67 public function on_activate()
68 {
69 register_uninstall_hook( $this->filepath, 'autoptimizeMain::on_uninstall' );
70 }
71
72 public function load_textdomain()
73 {
74 load_plugin_textdomain( 'autoptimize' );
75 }
76
77 public function setup()
78 {
79 // Do we gzip in php when caching or is the webserver doing it?
80 define( 'AUTOPTIMIZE_CACHE_NOGZIP', (bool) get_option( 'autoptimize_cache_nogzip' ) );
81
82 // These can be overridden by specifying them in wp-config.php or such.
83 if ( ! defined( 'AUTOPTIMIZE_WP_CONTENT_NAME' ) ) {
84 define( 'AUTOPTIMIZE_WP_CONTENT_NAME', '/' . wp_basename( WP_CONTENT_DIR ) );
85 }
86 if ( ! defined( 'AUTOPTIMIZE_CACHE_CHILD_DIR' ) ) {
87 define( 'AUTOPTIMIZE_CACHE_CHILD_DIR', '/cache/autoptimize/' );
88 }
89 if ( ! defined( 'AUTOPTIMIZE_CACHEFILE_PREFIX' ) ) {
90 define( 'AUTOPTIMIZE_CACHEFILE_PREFIX', 'autoptimize_' );
91 }
92 // Note: trailing slash is not optional!
93 if ( ! defined( 'AUTOPTIMIZE_CACHE_DIR' ) ) {
94 define( 'AUTOPTIMIZE_CACHE_DIR', autoptimizeCache::get_pathname() );
95 }
96
97 define( 'WP_ROOT_DIR', substr( WP_CONTENT_DIR, 0, strlen( WP_CONTENT_DIR ) - strlen( AUTOPTIMIZE_WP_CONTENT_NAME ) ) );
98
99 if ( ! defined( 'AUTOPTIMIZE_WP_SITE_URL' ) ) {
100 if ( function_exists( 'domain_mapping_siteurl' ) ) {
101 define( 'AUTOPTIMIZE_WP_SITE_URL', domain_mapping_siteurl( get_current_blog_id() ) );
102 } else {
103 define( 'AUTOPTIMIZE_WP_SITE_URL', site_url() );
104 }
105 }
106 if ( ! defined( 'AUTOPTIMIZE_WP_CONTENT_URL' ) ) {
107 if ( function_exists( 'domain_mapping_siteurl' ) ) {
108 define( 'AUTOPTIMIZE_WP_CONTENT_URL', str_replace( get_original_url( AUTOPTIMIZE_WP_SITE_URL ), AUTOPTIMIZE_WP_SITE_URL, content_url() ) );
109 } else {
110 define( 'AUTOPTIMIZE_WP_CONTENT_URL', content_url() );
111 }
112 }
113 if ( ! defined( 'AUTOPTIMIZE_CACHE_URL' ) ) {
114 if ( is_multisite() && apply_filters( 'autoptimize_separate_blog_caches', true ) ) {
115 $blog_id = get_current_blog_id();
116 define( 'AUTOPTIMIZE_CACHE_URL', AUTOPTIMIZE_WP_CONTENT_URL . AUTOPTIMIZE_CACHE_CHILD_DIR . $blog_id . '/' );
117 } else {
118 define( 'AUTOPTIMIZE_CACHE_URL', AUTOPTIMIZE_WP_CONTENT_URL . AUTOPTIMIZE_CACHE_CHILD_DIR );
119 }
120 }
121 if ( ! defined( 'AUTOPTIMIZE_WP_ROOT_URL' ) ) {
122 define( 'AUTOPTIMIZE_WP_ROOT_URL', str_replace( AUTOPTIMIZE_WP_CONTENT_NAME, '', AUTOPTIMIZE_WP_CONTENT_URL ) );
123 }
124 if ( ! defined( 'AUTOPTIMIZE_HASH' ) ) {
125 define( 'AUTOPTIMIZE_HASH', wp_hash( AUTOPTIMIZE_CACHE_URL ) );
126 }
127 if ( ! defined( 'AUTOPTIMIZE_SITE_DOMAIN' ) ) {
128 define( 'AUTOPTIMIZE_SITE_DOMAIN', parse_url( AUTOPTIMIZE_WP_SITE_URL, PHP_URL_HOST ) );
129 }
130
131 // Multibyte-capable string replacements are available with a filter.
132 // Also requires 'mbstring' extension.
133 $with_mbstring = apply_filters( 'autoptimize_filter_main_use_mbstring', false );
134 if ( $with_mbstring ) {
135 autoptimizeUtils::mbstring_available( \extensions_loaded( 'mbstring' ) );
136 } else {
137 autoptimizeUtils::mbstring_available( false );
138 }
139
140 do_action( 'autoptimize_setup_done' );
141 }
142
143 /**
144 * Checks if there's a need to upgrade/update options and whatnot,
145 * in which case we might need to do stuff and flush the cache
146 * to avoid old versions of aggregated files lingering around.
147 */
148 public function version_upgrades_check()
149 {
150 autoptimizeVersionUpdatesHandler::check_installed_and_update( $this->version );
151 }
152
153 public function check_cache_and_run()
154 {
155 if ( autoptimizeCache::cacheavail() ) {
156 $conf = autoptimizeConfig::instance();
157 if ( $conf->get( 'autoptimize_html' ) || $conf->get( 'autoptimize_js' ) || $conf->get( 'autoptimize_css' ) ) {
158 // Hook into WordPress frontend.
159 if ( defined( 'AUTOPTIMIZE_INIT_EARLIER' ) ) {
160 add_action(
161 'init',
162 array( $this, 'start_buffering' ),
163 self::INIT_EARLIER_PRIORITY
164 );
165 } else {
166 if ( ! defined( 'AUTOPTIMIZE_HOOK_INTO' ) ) {
167 define( 'AUTOPTIMIZE_HOOK_INTO', 'template_redirect' );
168 }
169 add_action(
170 constant( 'AUTOPTIMIZE_HOOK_INTO' ),
171 array( $this, 'start_buffering' ),
172 self::DEFAULT_HOOK_PRIORITY
173 );
174 }
175 }
176 } else {
177 add_action( 'admin_notices', 'autoptimizeMain::notice_cache_unavailable' );
178 }
179 }
180
181 public function maybe_run_ao_extra()
182 {
183 if ( apply_filters( 'autoptimize_filter_extra_activate', true ) ) {
184 $ao_extra = new autoptimizeExtra();
185 $ao_extra->run();
186
187 // And show the imgopt notice.
188 add_action( 'admin_notices', 'autoptimizeMain::notice_plug_imgopt' );
189 }
190 }
191
192 public function maybe_run_partners_tab()
193 {
194 // Loads partners tab code if in admin (and not in admin-ajax.php)!
195 if ( autoptimizeConfig::is_admin_and_not_ajax() ) {
196 new autoptimizePartners();
197 }
198 }
199
200 public function hook_page_cache_purge()
201 {
202 // hook into a collection of page cache purge actions if filter allows.
203 if ( apply_filters( 'autoptimize_filter_main_hookpagecachepurge', true ) ) {
204 $page_cache_purge_actions = array(
205 'after_rocket_clean_domain', // exists.
206 'hyper_cache_purged', // Stefano confirmed this will be added.
207 'w3tc_flush_posts', // exits.
208 'w3tc_flush_all', // exists.
209 'ce_action_cache_cleared', // Sven confirmed this will be added.
210 'comet_cache_wipe_cache', // still to be confirmed by Raam.
211 'wp_cache_cleared', // cfr. https://github.com/Automattic/wp-super-cache/pull/537.
212 'wpfc_delete_cache', // Emre confirmed this will be added this.
213 'swift_performance_after_clear_all_cache', // swift perf. yeah!
214 );
215 $page_cache_purge_actions = apply_filters( 'autoptimize_filter_main_pagecachepurgeactions', $page_cache_purge_actions );
216 foreach ( $page_cache_purge_actions as $purge_action ) {
217 add_action( $purge_action, 'autoptimizeCache::clearall_actionless' );
218 }
219 }
220 }
221
222 /**
223 * Setup output buffering if needed.
224 *
225 * @return void
226 */
227 public function start_buffering()
228 {
229 if ( $this->should_buffer() ) {
230
231 // Load speedupper conditionally (true by default).
232 if ( apply_filters( 'autoptimize_filter_speedupper', true ) ) {
233 $ao_speedupper = new autoptimizeSpeedupper();
234 }
235
236 $conf = autoptimizeConfig::instance();
237
238 if ( $conf->get( 'autoptimize_js' ) ) {
239 if ( ! defined( 'CONCATENATE_SCRIPTS' ) ) {
240 define( 'CONCATENATE_SCRIPTS', false );
241 }
242 if ( ! defined( 'COMPRESS_SCRIPTS' ) ) {
243 define( 'COMPRESS_SCRIPTS', false );
244 }
245 }
246
247 if ( $conf->get( 'autoptimize_css' ) ) {
248 if ( ! defined( 'COMPRESS_CSS' ) ) {
249 define( 'COMPRESS_CSS', false );
250 }
251 }
252
253 if ( apply_filters( 'autoptimize_filter_obkiller', false ) ) {
254 while ( ob_get_level() > 0 ) {
255 ob_end_clean();
256 }
257 }
258
259 // Now, start the real thing!
260 ob_start( array( $this, 'end_buffering' ) );
261 }
262 }
263
264 /**
265 * Returns true if all the conditions to start output buffering are satisfied.
266 *
267 * @param bool $doing_tests Allows overriding the optimization of only
268 * deciding once per request (for use in tests).
269 * @return bool
270 */
271 public function should_buffer( $doing_tests = false )
272 {
273 static $do_buffering = null;
274
275 // Only check once in case we're called multiple times by others but
276 // still allows multiple calls when doing tests.
277 if ( null === $do_buffering || $doing_tests ) {
278
279 $ao_noptimize = false;
280
281 // Checking for DONOTMINIFY constant as used by e.g. WooCommerce POS.
282 if ( defined( 'DONOTMINIFY' ) && ( constant( 'DONOTMINIFY' ) === true || constant( 'DONOTMINIFY' ) === 'true' ) ) {
283 $ao_noptimize = true;
284 }
285
286 // Skip checking query strings if they're disabled.
287 if ( apply_filters( 'autoptimize_filter_honor_qs_noptimize', true ) ) {
288 // Check for `ao_noptimize` (and other) keys in the query string
289 // to get non-optimized page for debugging.
290 $keys = array(
291 'ao_noptimize',
292 'ao_noptirocket',
293 );
294 foreach ( $keys as $key ) {
295 if ( array_key_exists( $key, $_GET ) && '1' === $_GET[ $key ] ) {
296 $ao_noptimize = true;
297 break;
298 }
299 }
300 }
301
302 // If setting says not to optimize logged in user and user is logged in...
303 if ( 'on' !== get_option( 'autoptimize_optimize_logged', 'on' ) && is_user_logged_in() && current_user_can( 'edit_posts' ) ) {
304 $ao_noptimize = true;
305 }
306
307 // If setting says not to optimize cart/checkout.
308 if ( 'on' !== get_option( 'autoptimize_optimize_checkout', 'on' ) ) {
309 // Checking for woocommerce, easy digital downloads and wp ecommerce...
310 foreach ( array( 'is_checkout', 'is_cart', 'edd_is_checkout', 'wpsc_is_cart', 'wpsc_is_checkout' ) as $func ) {
311 if ( function_exists( $func ) && $func() ) {
312 $ao_noptimize = true;
313 break;
314 }
315 }
316 }
317
318 // Allows blocking of autoptimization on your own terms regardless of above decisions.
319 $ao_noptimize = (bool) apply_filters( 'autoptimize_filter_noptimize', $ao_noptimize );
320
321 // Check for site being previewed in the Customizer (available since WP 4.0).
322 $is_customize_preview = false;
323 if ( function_exists( 'is_customize_preview' ) && is_customize_preview() ) {
324 $is_customize_preview = is_customize_preview();
325 }
326
327 /**
328 * We only buffer the frontend requests (and then only if not a feed
329 * and not turned off explicitly and not when being previewed in Customizer)!
330 * NOTE: Tests throw a notice here due to is_feed() being called
331 * while the main query hasn't been ran yet. Thats why we use
332 * AUTOPTIMIZE_INIT_EARLIER in tests.
333 */
334 $do_buffering = ( ! is_admin() && ! is_feed() && ! $ao_noptimize && ! $is_customize_preview );
335 }
336
337 return $do_buffering;
338 }
339
340 /**
341 * Returns true if given markup is considered valid/processable/optimizable.
342 *
343 * @param string $content Markup.
344 *
345 * @return bool
346 */
347 public function is_valid_buffer( $content )
348 {
349 // Defaults to true.
350 $valid = true;
351
352 $has_no_html_tag = ( false === stripos( $content, '<html' ) );
353 $has_xsl_stylesheet = ( false !== stripos( $content, '<xsl:stylesheet' ) );
354 $has_html5_doctype = ( preg_match( '/^<!DOCTYPE.+html>/i', ltrim( $content ) ) > 0 );
355
356 if ( $has_no_html_tag ) {
357 // Can't be valid amp markup without an html tag preceding it.
358 $is_amp_markup = false;
359 } else {
360 $is_amp_markup = self::is_amp_markup( $content );
361 }
362
363 // If it's not html, or if it's amp or contains xsl stylesheets we don't touch it.
364 if ( $has_no_html_tag && ! $has_html5_doctype || $is_amp_markup || $has_xsl_stylesheet ) {
365 $valid = false;
366 }
367
368 return $valid;
369 }
370
371 /**
372 * Returns true if given $content is considered to be AMP markup.
373 * This is far from actual validation against AMP spec, but it'll do for now.
374 *
375 * @param string $content Markup to check.
376 *
377 * @return bool
378 */
379 public static function is_amp_markup( $content )
380 {
381 $is_amp_markup = preg_match( '/<html[^>]*(?:amp|âš¡)/i', $content );
382
383 return (bool) $is_amp_markup;
384 }
385
386 /**
387 * Processes/optimizes the output-buffered content and returns it.
388 * If the content is not processable, it is returned unmodified.
389 *
390 * @param string $content Buffered content.
391 *
392 * @return string
393 */
394 public function end_buffering( $content )
395 {
396 // Bail early without modifying anything if we can't handle the content.
397 if ( ! $this->is_valid_buffer( $content ) ) {
398 return $content;
399 }
400
401 $conf = autoptimizeConfig::instance();
402
403 // Determine what needs to be ran.
404 $classes = array();
405 if ( $conf->get( 'autoptimize_js' ) ) {
406 $classes[] = 'autoptimizeScripts';
407 }
408 if ( $conf->get( 'autoptimize_css' ) ) {
409 $classes[] = 'autoptimizeStyles';
410 }
411 if ( $conf->get( 'autoptimize_html' ) ) {
412 $classes[] = 'autoptimizeHTML';
413 }
414
415 $classoptions = array(
416 'autoptimizeScripts' => array(
417 'aggregate' => $conf->get( 'autoptimize_js_aggregate' ),
418 'justhead' => $conf->get( 'autoptimize_js_justhead' ),
419 'forcehead' => $conf->get( 'autoptimize_js_forcehead' ),
420 'trycatch' => $conf->get( 'autoptimize_js_trycatch' ),
421 'js_exclude' => $conf->get( 'autoptimize_js_exclude' ),
422 'cdn_url' => $conf->get( 'autoptimize_cdn_url' ),
423 'include_inline' => $conf->get( 'autoptimize_js_include_inline' ),
424 ),
425 'autoptimizeStyles' => array(
426 'aggregate' => $conf->get( 'autoptimize_css_aggregate' ),
427 'justhead' => $conf->get( 'autoptimize_css_justhead' ),
428 'datauris' => $conf->get( 'autoptimize_css_datauris' ),
429 'defer' => $conf->get( 'autoptimize_css_defer' ),
430 'defer_inline' => $conf->get( 'autoptimize_css_defer_inline' ),
431 'inline' => $conf->get( 'autoptimize_css_inline' ),
432 'css_exclude' => $conf->get( 'autoptimize_css_exclude' ),
433 'cdn_url' => $conf->get( 'autoptimize_cdn_url' ),
434 'include_inline' => $conf->get( 'autoptimize_css_include_inline' ),
435 'nogooglefont' => $conf->get( 'autoptimize_css_nogooglefont' ),
436 ),
437 'autoptimizeHTML' => array(
438 'keepcomments' => $conf->get( 'autoptimize_html_keepcomments' ),
439 ),
440 );
441
442 $content = apply_filters( 'autoptimize_filter_html_before_minify', $content );
443
444 // Run the classes!
445 foreach ( $classes as $name ) {
446 $instance = new $name( $content );
447 if ( $instance->read( $classoptions[ $name ] ) ) {
448 $instance->minify();
449 $instance->cache();
450 $content = $instance->getcontent();
451 }
452 unset( $instance );
453 }
454
455 $content = apply_filters( 'autoptimize_html_after_minify', $content );
456
457 return $content;
458 }
459
460 public static function on_uninstall()
461 {
462 autoptimizeCache::clearall();
463
464 $delete_options = array(
465 'autoptimize_cache_clean',
466 'autoptimize_cache_nogzip',
467 'autoptimize_css',
468 'autoptimize_css_aggregate',
469 'autoptimize_css_datauris',
470 'autoptimize_css_justhead',
471 'autoptimize_css_defer',
472 'autoptimize_css_defer_inline',
473 'autoptimize_css_inline',
474 'autoptimize_css_exclude',
475 'autoptimize_html',
476 'autoptimize_html_keepcomments',
477 'autoptimize_js',
478 'autoptimize_js_aggregate',
479 'autoptimize_js_exclude',
480 'autoptimize_js_forcehead',
481 'autoptimize_js_justhead',
482 'autoptimize_js_trycatch',
483 'autoptimize_version',
484 'autoptimize_show_adv',
485 'autoptimize_cdn_url',
486 'autoptimize_cachesize_notice',
487 'autoptimize_css_include_inline',
488 'autoptimize_js_include_inline',
489 'autoptimize_optimize_logged',
490 'autoptimize_optimize_checkout',
491 'autoptimize_extra_settings',
492 'autoptimize_service_availablity',
493 'autoptimize_imgopt_provider_stat',
494 'autoptimize_imgopt_launched',
495 );
496
497 if ( ! is_multisite() ) {
498 foreach ( $delete_options as $del_opt ) {
499 delete_option( $del_opt );
500 }
501 } else {
502 global $wpdb;
503 $blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
504 $original_blog_id = get_current_blog_id();
505 foreach ( $blog_ids as $blog_id ) {
506 switch_to_blog( $blog_id );
507 foreach ( $delete_options as $del_opt ) {
508 delete_option( $del_opt );
509 }
510 }
511 switch_to_blog( $original_blog_id );
512 }
513
514 if ( wp_get_schedule( 'ao_cachechecker' ) ) {
515 wp_clear_scheduled_hook( 'ao_cachechecker' );
516 }
517 }
518
519 public static function notice_cache_unavailable()
520 {
521 echo '<div class="error"><p>';
522 // Translators: %s is the cache directory location.
523 printf( __( 'Autoptimize cannot write to the cache directory (%s), please fix to enable CSS/ JS optimization!', 'autoptimize' ), AUTOPTIMIZE_CACHE_DIR );
524 echo '</p></div>';
525 }
526
527 public static function notice_installed()
528 {
529 echo '<div class="updated"><p>';
530 _e( 'Thank you for installing and activating Autoptimize. Please configure it under "Settings" -> "Autoptimize" to start improving your site\'s performance.', 'autoptimize' );
531 echo '</p></div>';
532 }
533
534 public static function notice_updated()
535 {
536 echo '<div class="updated"><p>';
537 _e( 'Autoptimize has just been updated. Please <strong>test your site now</strong> and adapt Autoptimize config if needed.', 'autoptimize' );
538 echo '</p></div>';
539 }
540
541 public static function notice_plug_imgopt()
542 {
543 // Translators: the URL added points to the Autopmize Extra settings.
544 $_ao_imgopt_plug_notice = sprintf( __( 'Did you know Autoptimize includes on-the-fly image optimization and CDN via ShortPixel? Check out the %1$sAutoptimize Extra settings%2$s to activate this option.', 'autoptimize' ), '<a href="options-general.php?page=autoptimize_extra">', '</a>' );
545 $_ao_imgopt_plug_notice = apply_filters( 'autoptimize_filter_main_imgopt_plug_notice', $_ao_imgopt_plug_notice );
546 $_ao_imgopt_launch_ok = autoptimizeExtra::imgopt_launch_ok_wrapper();
547 $_ao_imgopt_plug_dismissible = 'ao-img-opt-plug-123';
548
549 // check if AO is optimizing images already.
550 $_ao_imgopt_active = false;
551 $_ao_extra_options = get_option( 'autoptimize_extra_settings', '' );
552 if ( is_array( $_ao_extra_options ) && array_key_exists( 'autoptimize_extra_checkbox_field_5', $_ao_extra_options ) && ! empty( $_ao_extra_options['autoptimize_extra_checkbox_field_5'] ) ) {
553 $_ao_imgopt_active = true;
554 }
555
556 if ( current_user_can( 'manage_options' ) && '' !== $_ao_imgopt_plug_notice && ! $_ao_imgopt_active && $_ao_imgopt_launch_ok && PAnD::is_admin_notice_active( $_ao_imgopt_plug_dismissible ) ) {
557 echo '<div class="notice notice-info is-dismissible" data-dismissible="' . $_ao_imgopt_plug_dismissible . '"><p>';
558 echo $_ao_imgopt_plug_notice;
559 echo '</p></div>';
560 }
561 }
562 }
563