PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 3.3.1
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v3.3.1
4.7.0 4.6.1 4.6.0 4.5.5 4.5.4 4.5.3 4.5.2 3.2.20 3.2.21 3.2.22 3.2.3 3.2.5 3.2.6 3.2.7 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.7.0 3.7.1 All 111 releases
wp-optimize / wp-optimize.php

wp-optimize.php in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 3.3.1, at wp-optimize.php

1,927 lines 62.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 Plugin Name: WP-Optimize - Clean, Compress, Cache
4 Plugin URI: https://getwpo.com
5 Description: WP-Optimize makes your site fast and efficient. It cleans the database, compresses images and caches pages. Fast sites attract more traffic and users.
6 Version: 3.3.1
7 Update URI: https://wordpress.org/plugins/wp-optimize/
8 Author: David Anderson, Ruhani Rabin, Team Updraft
9 Author URI: https://updraftplus.com
10 Text Domain: wp-optimize
11 Domain Path: /languages
12 License: GPLv2 or later
13 */
14
15 if (!defined('ABSPATH')) die('No direct access allowed');
16
17 // Check to make sure if WP_Optimize is already call and returns.
18 if (!class_exists('WP_Optimize')) :
19 define('WPO_VERSION', '3.3.1');
20 define('WPO_PLUGIN_URL', plugin_dir_url(__FILE__));
21 define('WPO_PLUGIN_MAIN_PATH', plugin_dir_path(__FILE__));
22 define('WPO_PLUGIN_SLUG', plugin_basename(__FILE__));
23 define('WPO_PREMIUM_NOTIFICATION', false);
24 define('WPO_MINIFY_PHP_VERSION_MET', version_compare(PHP_VERSION, '5.4', '>=') ? true : false);
25 if (!defined('WPO_USE_WEBP_CONVERSION')) define('WPO_USE_WEBP_CONVERSION', true);
26
27 class WP_Optimize {
28
29 public $premium_version_link = 'https://getwpo.com/buy/';
30
31 private $template_directories;
32
33 protected static $_instance = null;
34
35 protected $_cache_init_status = null;
36
37 /**
38 * Class constructor
39 */
40 public function __construct() {
41 spl_autoload_register(array($this, 'loader'));
42
43 // Checks if premium is installed along with plugins needed.
44 add_action('plugins_loaded', array($this, 'plugins_loaded'), 1);
45
46 register_activation_hook(__FILE__, array('WPO_Activation', 'actions'));
47 register_deactivation_hook(__FILE__, array('WPO_Deactivation', 'actions'));
48 register_uninstall_hook(__FILE__, array('WPO_Uninstall', 'actions'));
49
50 $this->load_admin();
51 add_action('admin_init', array($this, 'admin_init'));
52 add_action('admin_bar_menu', array($this, 'cache_admin_bar'), 100, 1);
53
54 add_action('init', array($this, 'schedule_plugin_cron_tasks'));
55
56 add_filter("plugin_action_links_".plugin_basename(__FILE__), array($this, 'plugin_settings_link'));
57 add_action('wpo_cron_event2', array($this, 'cron_action'));
58 add_filter('cron_schedules', array($this, 'cron_schedules'));
59
60 if (!$this->get_options()->get_option('installed-for', false)) $this->get_options()->update_option('installed-for', time());
61
62 if (!self::is_premium()) {
63 add_action('auto_option_settings', array($this->get_options(), 'auto_option_settings'));
64 }
65
66 add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts'));
67
68 add_action('wp_enqueue_scripts', array($this, 'frontend_enqueue_scripts'));
69
70 $this->load_ajax_handler();
71
72 // Show update to Premium notice for non-premium multisite.
73 add_action('wpo_additional_options', array($this, 'show_multisite_update_to_premium_notice'));
74
75 // Action column (show repair button if need).
76 add_filter('wpo_tables_list_additional_column_data', array($this, 'tables_list_additional_column_data'), 15, 2);
77
78 /**
79 * Add action for display Images > Compress images tab.
80 */
81 add_action('wp_optimize_admin_page_wpo_images_smush', array($this, 'admin_page_wpo_images_smush'));
82
83 include_once(WPO_PLUGIN_MAIN_PATH.'includes/updraftcentral.php');
84
85 include_once(WPO_PLUGIN_MAIN_PATH.'includes/backward-compatibility-functions.php');
86
87 register_shutdown_function(array($this, 'log_fatal_errors'));
88
89 add_action('wpo_admin_before_closing_wrap', array($this, 'load_modal_template'), 20);
90
91 add_action('upgrader_process_complete', array($this, 'detect_active_plugins_and_themes_updates'), 10, 2);
92
93 $import_done_hooks = array(
94 'import_end', // wordpress importer
95 'pmxi_after_xml_import', // wp all import
96 );
97
98 $db_update_hooks = apply_filters('wp_optimize_db_update_hooks', $import_done_hooks);
99
100 foreach ($db_update_hooks as $hook) {
101 add_action($hook, array($this, 'maybe_schedule_update_record_count_event'));
102 }
103 add_action('wpo_update_record_count_event', array($this->get_db_info(), 'wpo_update_record_count'));
104
105 $this->set_heartbeat_api_filters();
106 }
107
108 /**
109 * Auto-loads classes.
110 *
111 * @param string $class_name The name of the class.
112 */
113 private function loader($class_name) {
114 $dirs = $this->get_class_directories();
115
116 foreach ($dirs as $dir) {
117 $class_file = WPO_PLUGIN_MAIN_PATH . trailingslashit($dir) . 'class-' . str_replace('_', '-', strtolower($class_name)) . '.php';
118 if (file_exists($class_file)) {
119 require_once($class_file);
120 return;
121 }
122 }
123 }
124
125 /**
126 * Returns an array of class directories
127 *
128 * @return array
129 */
130 private function get_class_directories() {
131 return array(
132 'cache',
133 'compatibility',
134 'includes',
135 'minify',
136 'optimizations',
137 'webp',
138 );
139 }
140
141 /**
142 * Initialize Admin class to load admin UI
143 */
144 private function load_admin() {
145 $this->get_admin_instance();
146 }
147
148 /**
149 * Returns Admin class instance
150 *
151 * @return WP_Optimize_Admin
152 */
153 public function get_admin_instance() {
154 return WP_Optimize_Admin::instance();
155 }
156
157 /**
158 * Detect when an active plugin or theme is updated, and trigger an action
159 *
160 * @param object $upgrader_object
161 * @param array $options
162 * @return void
163 */
164 public function detect_active_plugins_and_themes_updates($upgrader_object, $options) {
165 if (empty($options) || !isset($options['type'])) return;
166
167 $should_purge_cache = false;
168 $skin = $upgrader_object->skin;
169 if ('plugin' === $options['type']) {
170 // A plugin is updated using the default update system (upgrader_overwrote_package is used for the upload method)
171 if (property_exists($skin, 'plugin_active') && $skin->plugin_active) {
172 $should_purge_cache = true;
173 }
174 } elseif ('theme' === $options['type']) {
175 $active_theme = get_stylesheet();
176 $parent_theme = get_template();
177 // A theme is updated using the upload system
178 if (isset($options['action']) && 'install' === $options['action'] && isset($skin->options['overwrite']) && 'update-theme' === $skin->options['overwrite']) {
179 $updated_theme = $upgrader_object->result['destination_name'];
180 // Check if the theme is in use
181 if ($active_theme == $updated_theme || $parent_theme == $updated_theme) {
182 $should_purge_cache = true;
183 }
184 // A theme is updated using the classic update system
185 } elseif (isset($options['action']) && 'update' === $options['action'] && isset($options['themes']) && is_array($options['themes'])) {
186 // Check if the theme is in use
187 if (in_array($active_theme, $options['themes']) || in_array($parent_theme, $options['themes'])) {
188 $should_purge_cache = true;
189 }
190 }
191 }
192
193 /**
194 * Action executed when an active theme or plugin was updated
195 */
196 if ($should_purge_cache) do_action('wpo_active_plugin_or_theme_updated');
197
198 }
199
200 /**
201 * Sets a flag to indicate an import action is done, if needed
202 */
203 public function maybe_schedule_update_record_count_event() {
204 if (!wp_next_scheduled('wpo_update_record_count_event')) {
205 wp_schedule_single_event(time() + 60, 'wpo_update_record_count_event');
206 }
207 }
208
209 public function admin_page_wpo_images_smush() {
210 $options = Updraft_Smush_Manager()->get_smush_options();
211 $custom = 90 >= $options['image_quality'] && 65 <= $options['image_quality'];
212 $sites = WP_Optimize()->get_sites();
213 $this->include_template('images/smush.php', false, array('smush_options' => $options, 'custom' => $custom, 'sites' => $sites, 'does_server_allows_local_webp_conversion' => $this->does_server_allows_local_webp_conversion()));
214 }
215
216 public static function instance() {
217 if (empty(self::$_instance)) {
218 self::$_instance = new self();
219 }
220 return self::$_instance;
221 }
222
223 public function get_optimizer() {
224 return WP_Optimizer::instance();
225 }
226
227 /**
228 * Adds 3rd party plugin compatibilities.
229 */
230 public function load_compatibilities() {
231 WPO_Polylang_Compatibility::instance();
232 WPO_Page_Builder_Compatibility::instance();
233 WPO_Custom_Permalink_Compatibility::instance();
234 WPO_TranslatePress_Compatibility::instance();
235
236 do_action('wpo_load_compatibilities');
237 }
238
239 /**
240 * Get and instantiate WP_Optimize_Minify
241 *
242 * @return WP_Optimize_Minify
243 */
244 public function get_minify() {
245 return WP_Optimize_Minify::instance();
246 }
247
248 public function get_options() {
249 return WP_Optimize_Options::instance();
250 }
251
252 public function get_notices() {
253 return WP_Optimize_Notices::instance();
254 }
255
256 /**
257 * Returns instance if WPO_Page_Cache class.
258 *
259 * @return WPO_Page_Cache
260 */
261 public function get_page_cache() {
262 return WPO_Page_Cache::instance();
263 }
264
265 /**
266 * Returns instance if WP_Optimize_WebP class.
267 *
268 * @return WP_Optimize_WebP
269 */
270 public function get_webp_instance() {
271 return WP_Optimize_WebP::get_instance();
272 }
273
274 /**
275 * Detects if the platform is Kinsta or not
276 *
277 * @return bool Returns true if it is Kinsta platform, otherwise returns false
278 */
279 private function is_kinsta() {
280 return isset($_SERVER['KINSTA_CACHE_ZONE']);
281 }
282
283 /**
284 * Detects whether the server handles cache. eg. Nginx cache
285 */
286 public function does_server_handles_cache() {
287 return $this->is_kinsta();
288 }
289
290 /**
291 * Detects whether the server supports table optimization.
292 *
293 * Some servers prevent table optimization
294 * because InnoDB engine does not optimize table
295 * instead it drops tables and recreate them
296 * which results in elevated disk write operations
297 */
298 public function does_server_allows_table_optimization() {
299 return !$this->is_kinsta();
300 }
301
302 /**
303 * Detects whether the server supports local webp conversion tools
304 */
305 private function does_server_allows_local_webp_conversion() {
306 return !$this->is_kinsta();
307 }
308
309 /**
310 * Create instance of WP_Optimize_Browser_Cache.
311 *
312 * @return WP_Optimize_Browser_Cache
313 */
314 public function get_browser_cache() {
315 return WP_Optimize_Browser_Cache::instance();
316 }
317
318 /**
319 * Returns WP_Optimize_Database_Information instance.
320 *
321 * @return WP_Optimize_Database_Information
322 */
323 public function get_db_info() {
324 return WP_Optimize_Database_Information::instance();
325 }
326
327 /**
328 * Returns instance of WP_Optimize_Gzip_Compression.
329 *
330 * @return WP_Optimize_Gzip_Compression
331 */
332 public function get_gzip_compression() {
333 return WP_Optimize_Gzip_Compression::instance();
334 }
335
336 /**
337 * Create instance of WP_Optimize_Htaccess.
338 *
339 * @param string $htaccess_file absolute path to htaccess file, by default it use .htaccess in WordPress root directory.
340 * @return WP_Optimize_Htaccess
341 */
342 public static function get_htaccess($htaccess_file = '') {
343 return new WP_Optimize_Htaccess($htaccess_file);
344 }
345
346 /**
347 * Return instance of Updraft_Logger
348 *
349 * @return Updraft_Logger
350 */
351 public function get_logger() {
352 return Updraft_Logger::instance();
353 }
354
355 /**
356 * Check if the current page belongs to WP-Optimize.
357 *
358 * @return bool
359 */
360 public function is_wpo_page() {
361 $current_screen = get_current_screen();
362
363 return (bool) preg_match('/wp\-optimize/i', $current_screen->id);
364 }
365
366 /**
367 * Enqueue scripts and styles on WP-Optimize pages.
368 */
369 public function admin_enqueue_scripts() {
370 $enqueue_version = $this->get_enqueue_version();
371 $min_or_not = $this->get_min_or_not_string();
372 $min_or_not_internal = $this->get_min_or_not_internal_string();
373
374 // Register or enqueue common scripts
375 wp_register_script('wp-optimize-send-command', WPO_PLUGIN_URL.'js/send-command'.$min_or_not_internal.'.js', array(), $enqueue_version);
376 wp_localize_script('wp-optimize-send-command', 'wp_optimize_send_command_data', array('nonce' => wp_create_nonce('wp-optimize-ajax-nonce')));
377 wp_enqueue_style('wp-optimize-global', WPO_PLUGIN_URL.'css/wp-optimize-global'.$min_or_not_internal.'.css', array(), $enqueue_version);
378
379 // load scripts and styles only on WP-Optimize pages.
380 if (!$this->is_wpo_page()) return;
381
382 wp_enqueue_script('jquery-serialize-json', WPO_PLUGIN_URL.'js/serialize-json/jquery.serializejson'.$min_or_not.'.js', array('jquery'), $enqueue_version);
383
384 wp_register_script('updraft-queue-js', WPO_PLUGIN_URL.'js/queue'.$min_or_not_internal.'.js', array(), $enqueue_version);
385
386 wp_enqueue_script('wp-optimize-heartbeat-js', WPO_PLUGIN_URL.'js/heartbeat'.$min_or_not_internal.'.js', array('jquery'), $enqueue_version);
387 wp_localize_script('wp-optimize-heartbeat-js', 'wpo_heartbeat_ajax', array(
388 'ajaxurl' => admin_url('admin-ajax.php'),
389 'nonce' => wp_create_nonce('heartbeat-nonce'),
390 'interval' => WPO_Ajax::HEARTBEAT_INTERVAL
391 ));
392
393 wp_enqueue_script('wp-optimize-modal', WPO_PLUGIN_URL.'js/modal'.$min_or_not_internal.'.js', array('jquery', 'backbone', 'wp-util'), $enqueue_version);
394 wp_enqueue_script('wp-optimize-cache-js', WPO_PLUGIN_URL.'js/cache'.$min_or_not_internal.'.js', array('wp-optimize-send-command', 'smush-js', 'wp-optimize-heartbeat-js'), $enqueue_version);
395 wp_enqueue_script('wp-optimize-admin-js', WPO_PLUGIN_URL.'js/wpoadmin'.$min_or_not_internal.'.js', array('jquery', 'updraft-queue-js', 'wp-optimize-send-command', 'smush-js', 'wp-optimize-modal', 'wp-optimize-cache-js', 'wp-optimize-heartbeat-js'), $enqueue_version);
396 wp_enqueue_style('wp-optimize-admin-css', WPO_PLUGIN_URL.'css/wp-optimize-admin'.$min_or_not_internal.'.css', array(), $enqueue_version);
397 // Using tablesorter to help with organising the DB size on Table Information
398 // https://github.com/Mottie/tablesorter
399 wp_enqueue_script('tablesorter-js', WPO_PLUGIN_URL.'js/tablesorter/jquery.tablesorter'.$min_or_not.'.js', array('jquery', 'wp-optimize-send-command'), $enqueue_version);
400
401 wp_enqueue_script('tablesorter-widgets-js', WPO_PLUGIN_URL.'js/tablesorter/jquery.tablesorter.widgets'.$min_or_not.'.js', array('jquery'), $enqueue_version);
402
403 // wp_enqueue_style('tablesorter-css', WPO_PLUGIN_URL.'css/tablesorter/theme.default.min.css', array(), $enqueue_version);
404
405 $js_variables = $this->wpo_js_translations();
406 $js_variables['loggers_classes_info'] = $this->get_loggers_classes_info();
407
408 wp_localize_script('wp-optimize-admin-js', 'wpoptimize', $js_variables);
409
410 do_action('wpo_premium_scripts_styles', $min_or_not_internal, $min_or_not, $enqueue_version);
411 }
412
413 /**
414 * Enqueue any required front-end scripts
415 *
416 * @return void
417 */
418 public function frontend_enqueue_scripts() {
419 if (!current_user_can('manage_options') || !is_admin_bar_showing()) return;
420 $enqueue_version = $this->get_enqueue_version();
421 $min_or_not_internal = WP_Optimize()->get_min_or_not_internal_string();
422
423 // Register or enqueue common scripts
424 wp_enqueue_style('wp-optimize-global', WPO_PLUGIN_URL.'css/wp-optimize-global'.$min_or_not_internal.'.css', array(), $enqueue_version);
425 }
426
427 /**
428 * Load Task Manager
429 */
430 public function get_task_manager() {
431 include_once(WPO_PLUGIN_MAIN_PATH.'vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-tasks-activation.php');
432
433 Updraft_Tasks_Activation::check_updates();
434
435 include_once(WPO_PLUGIN_MAIN_PATH . 'vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-task-meta.php');
436 include_once(WPO_PLUGIN_MAIN_PATH . 'vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-task-options.php');
437 include_once(WPO_PLUGIN_MAIN_PATH . 'vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-task.php');
438
439 include_once(WPO_PLUGIN_MAIN_PATH . 'includes/class-updraft-smush-task.php');
440 include_once(WPO_PLUGIN_MAIN_PATH . 'includes/class-updraft-smush-manager.php');
441
442 return Updraft_Smush_Manager();
443 }
444
445 /**
446 * Indicate whether we have an associated instance of WP-Optimize Premium or not.
447 *
448 * @returns Boolean
449 */
450 public static function is_premium() {
451 if (file_exists(WPO_PLUGIN_MAIN_PATH.'premium.php') && function_exists('WP_Optimize_Premium')) {
452 $wp_optimize_premium = WP_Optimize_Premium();
453 if (is_a($wp_optimize_premium, 'WP_Optimize_Premium')) return true;
454 }
455 return false;
456 }
457
458 /**
459 * Check if script running on Apache web server. $is_apache is set in wp-includes/vars.php. Also returns true if the server uses litespeed.
460 *
461 * @return bool
462 */
463 public function is_apache_server() {
464 global $is_apache;
465 return $is_apache;
466 }
467
468 /**
469 * Check if script running on IIS web server.
470 *
471 * @return bool
472 */
473 public function is_IIS_server() {
474 global $is_IIS, $is_iis7;
475 return $is_IIS || $is_iis7;
476 }
477
478 /**
479 * Check if Apache module or modules active.
480 *
481 * @param string|array $module - single Apache module name or list of Apache module names.
482 *
483 * @return bool|null - if null, the result was indeterminate
484 */
485 public function is_apache_module_loaded($module) {
486 if (!$this->is_apache_server()) return false;
487
488 if (!function_exists('apache_get_modules')) return null;
489
490 $module_loaded = true;
491
492 if (is_array($module)) {
493 foreach ($module as $single_module) {
494 if (!in_array($single_module, apache_get_modules())) {
495 $module_loaded = false;
496 break;
497 }
498 }
499 } else {
500 $module_loaded = in_array($module, apache_get_modules());
501 }
502
503 return $module_loaded;
504 }
505
506 /**
507 * Checks if this is the premium version and loads it. It also ensures that if the free version is installed then it is disabled with an appropriate error message.
508 */
509 public function plugins_loaded() {
510
511 if ($this->does_server_handles_cache()) {
512 add_filter('wp_optimize_admin_page_wpo_cache_tabs', array($this, 'filter_cache_tabs'), 99, 1);
513
514 // If newly migrated to server that handles cache, disable wpo cache
515 $cache = $this->get_page_cache();
516 if ($cache->is_enabled()) {
517 $cache->disable();
518 }
519 }
520
521 add_filter('robots_txt', array($this, 'robots_txt'), 99, 1);
522
523 // Run Premium loader if it exists
524 if (file_exists(WPO_PLUGIN_MAIN_PATH.'premium.php') && !class_exists('WP_Optimize_Premium')) {
525 include_once(WPO_PLUGIN_MAIN_PATH.'premium.php');
526 }
527
528 // load defaults
529 WP_Optimize()->get_options()->set_default_options();
530
531 // Initialize loggers.
532 $this->setup_loggers();
533
534 if ($this->is_active('premium') && false !== ($free_plugin = $this->is_active('free'))) {
535 if (!function_exists('deactivate_plugins')) include_once(ABSPATH.'wp-admin/includes/plugin.php');
536 deactivate_plugins($free_plugin);
537
538 // If WPO_ADVANCED_CACHE is defined, we empty advanced-cache.php to regenerate later. Otherwise it contains the path to free.
539 if (defined('WPO_ADVANCED_CACHE') && WPO_ADVANCED_CACHE) {
540 $advanced_cache_filename = trailingslashit(WP_CONTENT_DIR) . 'advanced-cache.php';
541
542 if (!is_file($advanced_cache_filename) && is_writable(dirname($advanced_cache_filename)) || (is_file($advanced_cache_filename) && is_writable($advanced_cache_filename))) {
543 file_put_contents($advanced_cache_filename, '');
544 }
545 }
546
547 // Registers the notice letting the user know it cannot be active if premium is active.
548 add_action('admin_notices', array($this, 'show_admin_notice_premium'));
549 return;
550 }
551
552 // Loads the task manager
553 $this->get_task_manager();
554
555 // Loads the language file.
556 load_plugin_textdomain('wp-optimize', false, dirname(plugin_basename(__FILE__)) . '/languages');
557
558 // Load 3rd party plugin compatibilities.
559 $this->load_compatibilities();
560
561 // Load page cache.
562 $this->get_page_cache();
563 $this->init_page_cache();
564
565 // Include minify
566 $this->get_minify();
567 $this->run_updates();
568
569 // We need this here because webp can be unavailable because of server moves
570 // This deletes already converted webp images and original image file when a media is deleted
571 WP_Optimize_WebP_Images::get_instance();
572
573 // Include WebP
574 if (WPO_USE_WEBP_CONVERSION) {
575 $this->get_webp_instance();
576 }
577
578 // add_filter('updraftcentral_host_plugins', array($this, 'attach_updraftcentral_host'));
579 // if (file_exists(WPO_PLUGIN_MAIN_PATH.'central/factory.php')) include_once(WPO_PLUGIN_MAIN_PATH.'central/factory.php');
580 }
581
582 /**
583 * Attach this wp-optimize plugin as host of the UpdraftCentral libraries
584 * (e.g. "central" folder)
585 *
586 * @param array $hosts List of plugins having the "central" library integrated into them
587 *
588 * @return array
589 */
590 public function attach_updraftcentral_host($hosts) {
591 $hosts[] = 'wp-optimize';
592 return $hosts;
593 }
594
595 /**
596 * Filter cache tabs (when it is Kinsta)
597 *
598 * @param array $tabs An array of tabs
599 *
600 * @return array $tabs An array of tabs
601 */
602 public function filter_cache_tabs($tabs) {
603 unset($tabs['preload']);
604 unset($tabs['advanced']);
605 unset($tabs['gzip']);
606 unset($tabs['settings']);
607 return $tabs;
608 }
609
610 /**
611 * Check whether one of free/Premium is active (whether it is this instance or not)
612 *
613 * @param String $which - 'free' or 'premium'
614 *
615 * @return String|Boolean - plugin path (if installed) or false if not
616 */
617 private function is_active($which = 'free') {
618 $active_plugins = $this->get_active_plugins();
619 foreach ($active_plugins as $file) {
620 if ('wp-optimize.php' == basename($file)) {
621 $plugin_dir = WP_PLUGIN_DIR.'/'.dirname($file);
622 if (('free' == $which && !file_exists($plugin_dir.'/premium.php')) || ('free' != $which && file_exists($plugin_dir.'/premium.php'))) return $file;
623 }
624 }
625 return false;
626 }
627
628 /**
629 * Gets an array of plugins active on either the current site, or site-wide
630 *
631 * @return Array - a list of plugin paths (relative to the plugin directory)
632 */
633 private function get_active_plugins() {
634
635 // Gets all active plugins on the current site
636 $active_plugins = get_option('active_plugins');
637
638 if (is_multisite()) {
639 $network_active_plugins = get_site_option('active_sitewide_plugins');
640 if (!empty($network_active_plugins)) {
641 $network_active_plugins = array_keys($network_active_plugins);
642 $active_plugins = array_merge($active_plugins, $network_active_plugins);
643 }
644 }
645
646 return $active_plugins;
647 }
648
649 /**
650 * This function checks whether a specific plugin is installed, and returns information about it
651 *
652 * @param string $name Specify "Plugin Name" to return details about it.
653 * @return array Returns an array of details such as if installed, the name of the plugin and if it is active.
654 */
655 public function is_installed($name) {
656
657 // Needed to have the 'get_plugins()' function
658 include_once(ABSPATH.'wp-admin/includes/plugin.php');
659
660 // Gets all plugins available
661 $get_plugins = get_plugins();
662
663 $active_plugins = $this->get_active_plugins();
664
665 $plugin_info = array();
666 $plugin_info['installed'] = false;
667 $plugin_info['active'] = false;
668
669 // Loops around each plugin available.
670 foreach ($get_plugins as $key => $value) {
671 // If the plugin name matches that of the specified name, it will gather details.
672 if ($value['Name'] != $name && $value['TextDomain'] != $name) continue;
673 $plugin_info['installed'] = true;
674 $plugin_info['name'] = $key;
675 $plugin_info['version'] = $value['Version'];
676 if (in_array($key, $active_plugins)) {
677 $plugin_info['active'] = true;
678 }
679 break;
680 }
681 return $plugin_info;
682 }
683
684 /**
685 * This is a notice to show users that premium is installed
686 */
687 public function show_admin_notice_premium() {
688 echo '<div id="wp-optimize-premium-installed-warning" class="error"><p>'.__('WP-Optimize (Free) has been de-activated, because WP-Optimize Premium is active.', 'wp-optimize').'</p></div>';
689 if (isset($_GET['activate'])) unset($_GET['activate']);
690 }
691
692 /**
693 * Show update to Premium notice for non-premium multisite.
694 */
695 public function show_multisite_update_to_premium_notice() {
696 if (!is_multisite() || self::is_premium()) return;
697
698 echo '<p><a href="'.$this->premium_version_link.'">'.__('New feature: WP-Optimize Premium can now optimize all sites within a multisite install, not just the main one.', 'wp-optimize').'</a></p>';
699 }
700
701 public function admin_init() {
702 $pagenow = $GLOBALS['pagenow'];
703
704 $this->register_template_directories();
705
706 if (('index.php' == $pagenow && current_user_can('update_plugins')) || ('index.php' == $pagenow && defined('WP_OPTIMIZE_FORCE_DASHNOTICE') && WP_OPTIMIZE_FORCE_DASHNOTICE)) {
707 $options = $this->get_options();
708
709 $dismissed_until = $options->get_option('dismiss_dash_notice_until', 0);
710
711 if (file_exists(WPO_PLUGIN_MAIN_PATH . 'index.html')) {
712 $installed = filemtime(WPO_PLUGIN_MAIN_PATH . 'index.html');
713 $installed_for = (time() - $installed);
714 }
715
716 if (($installed && time() > $dismissed_until && $installed_for > (14 * 86400) && !defined('WP_OPTIMIZE_NOADS_B')) || (defined('WP_OPTIMIZE_FORCE_DASHNOTICE') && WP_OPTIMIZE_FORCE_DASHNOTICE)) {
717 add_action('all_admin_notices', array($this, 'show_admin_notice_upgraded'));
718 }
719 }
720
721 if ($this->is_wp_smush_installed()) {
722 add_filter('transient_wp-smush-conflict_check', array($this, 'modify_wp_smush_conflict_check'), 9, 1);
723 }
724 }
725
726 /**
727 * Checks whether the WP Smush plugin is active or not
728 *
729 * @return bool
730 */
731 private function is_wp_smush_installed() {
732 return is_plugin_active('wp-smushit/wp-smush.php');
733 }
734
735 /**
736 * Remove WPO plugin name from WP Smushit transient value
737 *
738 * @return array $active_plugins
739 */
740 public function modify_wp_smush_conflict_check($active_plugins) {
741 // This can be boolean value since it is return value of get_transient
742 if (!is_array($active_plugins)) return $active_plugins;
743
744 if (false !== ($key = array_search('WP-Optimize - Clean, Compress, Cache', $active_plugins))) {
745 unset($active_plugins[$key]);
746 }
747 return $active_plugins;
748 }
749
750 /**
751 * Get the install or update notice instance
752 *
753 * @return WP_Optimize_Install_Or_Update_Notice
754 */
755 public function get_install_or_update_notice() {
756 static $instance = null;
757 if (is_a($instance, 'WP_Optimize_Install_Or_Update_Notice')) return $instance;
758 $instance = new WP_Optimize_Install_Or_Update_Notice();
759 return $instance;
760 }
761
762 /**
763 * Display an admin notice for an upgraded version.
764 */
765 public function show_admin_notice_upgraded() {
766 $this->include_template('notices/thanks-for-using-main-dash.php', false, array(
767 'is_premium' => WP_Optimize::is_premium()
768 ));
769 }
770
771 public function capability_required() {
772 return apply_filters('wp_optimize_capability_required', 'manage_options');
773 }
774
775 /**
776 * Returns array of translations used in javascript code.
777 *
778 * @return array
779 */
780 public function wpo_js_translations() {
781 $log_message = __('For more details, please check your logs configured in logging destinations settings.', 'wp-optimize');
782 return apply_filters('wpo_js_translations', array(
783 'automatic_backup_before_optimizations' => __('Automatic backup before optimizations', 'wp-optimize'),
784 'error_unexpected_response' => __('An unexpected response was received.', 'wp-optimize'),
785 'optimization_complete' => __('Optimization complete', 'wp-optimize'),
786 'with_warnings' => __('(with warnings - open the browser console for more details)', 'wp-optimize'),
787 'optimizing_table' => __('Optimizing table:', 'wp-optimize'),
788 'run_optimizations' => __('Run optimizations', 'wp-optimize'),
789 'table_optimization_timeout' => 120000,
790 'add' => __('Add', 'wp-optimize'),
791 'cancel' => __('Cancel', 'wp-optimize'),
792 'cancelling' => __('Cancelling...', 'wp-optimize'),
793 'enable' => __('Enable', 'wp-optimize'),
794 'disable' => __('Disable', 'wp-optimize'),
795 'please_select_settings_file' => __('Please, select settings file.', 'wp-optimize'),
796 'are_you_sure_you_want_to_remove_logging_destination' => __('Are you sure you want to remove this logging destination?', 'wp-optimize'),
797 'fill_all_settings_fields' => __('Before saving, you need to complete the currently incomplete settings (or remove them).', 'wp-optimize'),
798 'table_was_not_repaired' => __('%s was not repaired.', 'wp-optimize') . ' ' . $log_message,
799 'table_was_not_deleted' => __('%s was not deleted.', 'wp-optimize') . ' ' . $log_message,
800 'table_was_not_converted' => __('%s was not converted to InnoDB.', 'wp-optimize') . ' ' . $log_message,
801 'please_use_positive_integers' => __('Please use positive integers.', 'wp-optimize'),
802 'please_use_valid_values' => __('Please use valid values.', 'wp-optimize'),
803 'update' => __('Update', 'wp-optimize'),
804 'run_now' => __('Run now', 'wp-optimize'),
805 'starting_preload' => __('Started preload...', 'wp-optimize'),
806 'loading_urls' => __('Loading URLs...', 'wp-optimize'),
807 'current_cache_size' => __('Current cache size:', 'wp-optimize'),
808 'number_of_files' => __('Number of files:', 'wp-optimize'),
809 'toggle_info' => __('Show information', 'wp-optimize'),
810 'delete_file' => __('Delete', 'wp-optimize'),
811 'added_to_list' => __('Added to the list', 'wp-optimize'),
812 'added_notice' => __('The file was added to the list', 'wp-optimize'),
813 'save_notice' => __('Save the changes', 'wp-optimize'),
814 'page_refresh' => __('Refreshing the page to reflect changes...', 'wp-optimize'),
815 'cache_file_not_found' => __('Cache file was not found.', 'wp-optimize'),
816 'settings_have_been_deleted_successfully' => __('WP-Optimize settings have been deleted successfully.', 'wp-optimize'),
817 'loading_data' => __('Loading data...', 'wp-optimize'),
818 'spinner_src' => esc_attr(admin_url('images/spinner-2x.gif')),
819 'settings_page_url' => is_multisite() ? network_admin_url('admin.php?page=wpo_settings') : admin_url('admin.php?page=wpo_settings'),
820 'sites' => $this->get_sites(),
821 'user_always_ignores_table_delete_warning' => (get_user_meta(get_current_user_id(), 'wpo-ignores-table-delete-warning', true)) ? true : false,
822 'post_meta_tweak_completed' => __('The tweak has been performed.', 'wp-optimize'),
823 'no_minified_assets' => __('No minified files are present', 'wp-optimize'),
824 'network_site_url' => network_site_url(),
825 'export_settings_file_name' => 'wpoptimize-settings-'.sanitize_title(get_bloginfo('name')).'.json',
826 'import_select_file' => __('You have not yet selected a file to import.', 'wp-optimize'),
827 'import_invalid_json_file' => __('Error: The chosen file is corrupt.', 'wp-optimize') . ' ' . __('Please choose a valid WP-Optimize export file.', 'wp-optimize'),
828 'importing' => __('Importing...', 'wp-optimize'),
829 'importing_data_from' => __('This will import data from:', 'wp-optimize'),
830 'exported_on' => __('Which was exported on:', 'wp-optimize'),
831 'continue_import' => __('Do you want to carry out the import?', 'wp-optimize'),
832 'select_destination' => __('Select destination', 'wp-optimize'),
833 ));
834 }
835
836 /**
837 * Manages the admin bar menu for caching (currently page and minify)
838 */
839 public function cache_admin_bar($wp_admin_bar) {
840
841 $options = $this->get_options();
842 if (!$options->get_option('enable_cache_in_admin_bar', true)) return;
843
844 /**
845 * The "purge cache" menu items
846 *
847 * @param array $menu_items - The menu items, in the format required by $wp_admin_bar->add_menu()
848 * @param object $wp_admin_bar
849 */
850 $menu_items = apply_filters('wpo_cache_admin_bar_menu_items', array(), $wp_admin_bar);
851
852 if (empty($menu_items) || !is_array($menu_items)) return;
853
854 $wp_admin_bar->add_menu(array(
855 'id' => 'wpo_purge_cache',
856 'title' => __('Purge cache', 'wp-optimize'),
857 'href' => '#',
858 'meta' => array(
859 'title' => __('Purge cache', 'wp-optimize'),
860 ),
861 'parent' => false,
862 ));
863
864 foreach ($menu_items as $item) {
865 $wp_admin_bar->add_menu($item);
866 }
867 }
868
869 /**
870 * Add settings link on plugin page
871 *
872 * @param string $links Passing through the URL to be used within the HREF.
873 * @return string Returns the Links.
874 */
875 public function plugin_settings_link($links) {
876
877 $admin_page_url = $this->get_options()->admin_page_url();
878 $settings_page_url = $this->get_options()->admin_page_url('wpo_settings');
879
880 if (false == self::is_premium()) {
881 $premium_link = '<a href="' . esc_url($this->premium_version_link) . '" target="_blank">' . __('Premium', 'wp-optimize') . '</a>';
882 array_unshift($links, $premium_link);
883 }
884
885 $settings_link = '<a href="' . esc_url($settings_page_url) . '">' . __('Settings', 'wp-optimize') . '</a>';
886 array_unshift($links, $settings_link);
887
888 $optimize_link = '<a href="' . esc_url($admin_page_url) . '">' . __('Optimize', 'wp-optimize') . '</a>';
889 array_unshift($links, $optimize_link);
890 return $links;
891 }
892
893 /**
894 * Action wpo_tables_list_additional_column_data. Output button Optimize in the action column.
895 *
896 * @param string $content String for output to column
897 * @param object $table_info Object with table info.
898 *
899 * @return string
900 */
901 public function tables_list_additional_column_data($content, $table_info) {
902 if ($table_info->is_needing_repair) {
903 $content .= '<div class="wpo_button_wrap">'
904 . '<button class="button button-secondary run-single-table-repair" data-table="' . esc_attr($table_info->Name) . '">' . __('Repair', 'wp-optimize') . '</button>'
905 . '<img class="optimization_spinner visibility-hidden" src="' . esc_attr(admin_url('images/spinner-2x.gif')) . '" width="20" height="20" alt="...">'
906 . '<span class="optimization_done_icon dashicons dashicons-yes visibility-hidden"></span>'
907 . '</div>';
908 }
909
910 // table belongs to plugin.
911 if ($table_info->can_be_removed) {
912 $content .= '<div>'
913 . '<button class="button button-secondary run-single-table-delete" data-table="' . esc_attr($table_info->Name) . '">' . __('Remove', 'wp-optimize') . '</button>'
914 . '<img class="optimization_spinner visibility-hidden" src="' . esc_attr(admin_url('images/spinner-2x.gif')) . '" width="20" height="20" alt="...">'
915 . '<span class="optimization_done_icon dashicons dashicons-yes visibility-hidden"></span>'
916 . '</div>';
917 }
918
919 // Add option for MyISAM to InnoDB conversion.
920 if ('MyISAM' == $table_info->Engine) {
921 $content .= '<div class="wpo_button_convert wpo_button_wrap">'
922 . '<button class="button button-secondary toinnodb" data-table="' . esc_attr($table_info->Name) . '">' . __('Convert to InnoDB', 'wp-optimize') . '</button>'
923 . '<img class="optimization_spinner visibility-hidden" src="' . esc_attr(admin_url('images/spinner-2x.gif')) . '" width="20" height="20" alt="...">'
924 . '<span class="optimization_done_icon dashicons dashicons-yes visibility-hidden"></span>'
925 . '</div>';
926
927 }
928
929 return $content;
930 }
931
932 /**
933 * Initialize WP-Optimize page cache.
934 */
935 public function init_page_cache() {
936 if ($this->get_page_cache()->config->get_option('enable_page_caching', false)) {
937 if ((!(defined('WP_CLI') && WP_CLI)) && (!defined('DOING_AJAX') || !DOING_AJAX)) {
938 $this->_cache_init_status = $this->get_page_cache()->enable();
939 }
940 }
941 }
942
943 /**
944 * Get init_page_cache() status.
945 *
946 * @return bool
947 */
948 public function get_init_page_cache_status() {
949 return $this->_cache_init_status;
950 }
951
952 /**
953 * Schedules cron event based on selected schedule type
954 *
955 * @return void
956 */
957 public function cron_activate() {
958 $gmt_offset = (int) (3600 * get_option('gmt_offset'));
959
960 $options = $this->get_options();
961
962 if ($options->get_option('schedule') === false) {
963 $options->set_default_options();
964 } else {
965 if ('true' == $options->get_option('schedule')) {
966 if (!wp_next_scheduled('wpo_cron_event2')) {
967 $schedule_type = $options->get_option('schedule-type', 'wpo_weekly');
968
969 // Backward compatibility
970 if ('wpo_otherweekly' == $schedule_type) $schedule_type = 'wpo_fortnightly';
971
972 $this_time = (86400 * 7);
973
974 switch ($schedule_type) {
975 case "wpo_daily":
976 $this_time = 86400;
977 break;
978
979 case "wpo_weekly":
980 $this_time = (86400 * 7);
981 break;
982
983 case "wpo_fortnightly":
984 $this_time = (86400 * 14);
985 break;
986
987 case "wpo_monthly":
988 $this_time = (86400 * 30);
989 break;
990 }
991
992 add_action('wpo_cron_event2', array($this, 'cron_action'));
993 $result = wp_schedule_event((current_time("timestamp", 0) + $this_time - $gmt_offset), $schedule_type, 'wpo_cron_event2');
994 WP_Optimize()->log('running wp_schedule_event()');
995 if (is_wp_error($result)) {
996 $error_msg = $result->get_error_message();
997 WP_Optimize()->log($error_msg);
998 WP_Optimize()->log(print_r($result, true));
999 } else {
1000 WP_Optimize()->log($result);
1001 }
1002 }
1003 }
1004 }
1005 }
1006
1007 /**
1008 * Clears all cron events
1009 *
1010 * @return void
1011 */
1012 public function wpo_cron_deactivate() {
1013 $cron_jobs = _get_cron_array();
1014 foreach ($cron_jobs as $job) {
1015 foreach (array_keys($job) as $hook) {
1016 if (preg_match('/^wpo_/', $hook)) wp_unschedule_hook($hook);
1017 }
1018 }
1019 }
1020
1021 /**
1022 * Scheduler public functions to update schedulers
1023 *
1024 * @param array $schedules An array of schedules being passed.
1025 * @return array An array of schedules being returned.
1026 */
1027 public function cron_schedules($schedules) {
1028 $schedules['wpo_daily'] = array('interval' => 86400, 'display' => 'Once Daily');
1029 $schedules['wpo_weekly'] = array('interval' => 86400 * 7, 'display' => 'Once Weekly');
1030 $schedules['wpo_fortnightly'] = array('interval' => 86400 * 14, 'display' => 'Once Every Fortnight');
1031 $schedules['wpo_monthly'] = array('interval' => 86400 * 30, 'display' => 'Once Every Month');
1032 return $schedules;
1033 }
1034
1035 /**
1036 * Returns count of overdue cron jobs.
1037 *
1038 * @return integer
1039 */
1040 public function howmany_overdue_crons() {
1041 $how_many_overdue = 0;
1042 if (function_exists('_get_cron_array') || (is_file(ABSPATH.WPINC.'/cron.php') && include_once(ABSPATH.WPINC.'/cron.php') && function_exists('_get_cron_array'))) {
1043 $crons = _get_cron_array();
1044 if (is_array($crons)) {
1045 $timenow = time();
1046 foreach ($crons as $jt => $job) {
1047 if ($jt < $timenow) {
1048 $how_many_overdue++;
1049 }
1050 }
1051 }
1052 }
1053 return $how_many_overdue;
1054 }
1055
1056 /**
1057 * Run updates on plugin activation.
1058 */
1059 public function run_updates() {
1060 include_once(WPO_PLUGIN_MAIN_PATH.'includes/class-wp-optimize-updates.php');
1061 WP_Optimize_Updates::check_updates();
1062 }
1063
1064 /**
1065 * Returns warning about overdue crons.
1066 *
1067 * @param int $howmany count of overdue crons
1068 * @return string
1069 */
1070 public function show_admin_warning_overdue_crons($howmany) {
1071 $ret = '<div class="updated below-h2"><p>';
1072 $ret .= '<strong>'.__('Warning', 'wp-optimize').':</strong> '.sprintf(__('WordPress has a number (%d) of scheduled tasks which are overdue.', 'wp-optimize'), $howmany).' '. __('Unless this is a development site, this probably means that the scheduler in your WordPress install is not working.', 'wp-optimize').' <a href="'.apply_filters('wpoptimize_com_link', "https://getwpo.com/faqs/the-scheduler-in-my-wordpress-installation-is-not-working-what-should-i-do/").'">'.__('Read this page for a guide to possible causes and how to fix it.', 'wp-optimize').'</a>';
1073 $ret .= '</p></div>';
1074 return $ret;
1075 }
1076
1077 private function wp_normalize_path($path) {
1078 // Wp_normalize_path is not present before WP 3.9.
1079 if (function_exists('wp_normalize_path')) return wp_normalize_path($path);
1080 // Taken from WP 4.6.
1081 $path = str_replace('\\', '/', $path);
1082 $path = preg_replace('|(?<=.)/+|', '/', $path);
1083 if (':' === substr($path, 1, 1)) {
1084 $path = ucfirst($path);
1085 }
1086 return $path;
1087 }
1088
1089 public function get_templates_dir() {
1090 return apply_filters('wp_optimize_templates_dir', $this->wp_normalize_path(WPO_PLUGIN_MAIN_PATH.'templates'));
1091 }
1092
1093 public function get_templates_url() {
1094 return apply_filters('wp_optimize_templates_url', WPO_PLUGIN_URL.'templates');
1095 }
1096
1097 /**
1098 * Return or output view content
1099 *
1100 * @param String $path - path to template, usually relative to templates/ within the WP-O directory
1101 * @param Boolean $return_instead_of_echo - what to do with the results
1102 * @param Array $extract_these - key/value pairs for substitution into the scope of the template
1103 *
1104 * @return String|Void
1105 */
1106 public function include_template($path, $return_instead_of_echo = false, $extract_these = array()) {
1107 if ($return_instead_of_echo) ob_start();
1108
1109 if (preg_match('#^([^/]+)/(.*)$#', $path, $matches)) {
1110 $prefix = $matches[1];
1111 $suffix = $matches[2];
1112 if (isset($this->template_directories[$prefix])) {
1113 $template_file = $this->template_directories[$prefix].'/'.$suffix;
1114 }
1115 }
1116
1117 if (!isset($template_file)) {
1118 $template_file = WPO_PLUGIN_MAIN_PATH.'templates/'.$path;
1119 }
1120
1121 $template_file = apply_filters('wp_optimize_template', $template_file, $path);
1122
1123 do_action('wp_optimize_before_template', $path, $template_file, $return_instead_of_echo, $extract_these);
1124
1125 if (!file_exists($template_file)) {
1126 error_log("WP Optimize: template not found: ".$template_file);
1127 echo __('Error:', 'wp-optimize').' '.__('template not found', 'wp-optimize')." (".$path.")";
1128 } else {
1129 extract($extract_these);
1130 // The following are useful variables which can be used in the template.
1131 // They appear as unused, but may be used in the $template_file.
1132 $wpdb = $GLOBALS['wpdb'];// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $wpdb might be used in the included template
1133 $wp_optimize = $this;// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $wp_optimize might be used in the included template
1134 $optimizer = $this->get_optimizer();// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $optimizer might be used in the included template
1135 $options = $this->get_options();// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $options might be used in the included template
1136 $wp_optimize_notices = $this->get_notices();// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $wp_optimize_notices might be used in the included template
1137 include $template_file;
1138 }
1139
1140 do_action('wp_optimize_after_template', $path, $template_file, $return_instead_of_echo, $extract_these);
1141
1142 if ($return_instead_of_echo) return ob_get_clean();
1143 }
1144
1145 /**
1146 * Build a list of template directories (stored in self::$template_directories)
1147 */
1148 private function register_template_directories() {
1149
1150 $template_directories = array();
1151
1152 $templates_dir = $this->get_templates_dir();
1153
1154 if ($dh = opendir($templates_dir)) {
1155 while (($file = readdir($dh)) !== false) {
1156 if ('.' == $file || '..' == $file) continue;
1157 if (is_dir($templates_dir.'/'.$file)) {
1158 $template_directories[$file] = $templates_dir.'/'.$file;
1159 }
1160 }
1161 closedir($dh);
1162 }
1163
1164 // Optimal hook for most extensions to hook into.
1165 $this->template_directories = apply_filters('wp_optimize_template_directories', $template_directories);
1166
1167 }
1168
1169 /**
1170 * Message to debug
1171 *
1172 * @param string $message Message to insert into the log.
1173 * @param array $context array with variables used in $message like in template,
1174 * for ex.
1175 * $message = 'Hello {message}';
1176 * $context = ['message' => 'world']
1177 * 'Hello world' string will be saved in log.
1178 */
1179 public function log($message, $context = array()) {
1180 $this->get_logger()->debug($message, $context);
1181 }
1182
1183 /**
1184 * Format Bytes Into KB/MB
1185 *
1186 * @param mixed $bytes Number of bytes to be converted.
1187 * @param integer $decimals the number of decimal digits
1188 * @return integer return the correct format size.
1189 */
1190 public function format_size($bytes, $decimals = 2) {
1191 if (!is_numeric($bytes)) return __('N/A', 'wp-optimize');
1192
1193 if (1073741824 <= $bytes) {
1194 $bytes = number_format($bytes / 1073741824, $decimals) . ' GB';
1195 } elseif (1048576 <= $bytes) {
1196 $bytes = number_format($bytes / 1048576, $decimals) . ' MB';
1197 } elseif (1024 <= $bytes) {
1198 $bytes = number_format($bytes / 1024, $decimals) . ' KB';
1199 } elseif (1 < $bytes) {
1200 $bytes = $bytes . ' bytes';
1201 } elseif (1 == $bytes) {
1202 $bytes = $bytes . ' byte';
1203 } else {
1204 $bytes = '0 bytes';
1205 }
1206
1207 return $bytes;
1208 }
1209
1210 /**
1211 * Format a timestamp into a juman readable date time
1212 *
1213 * @param int $timestamp
1214 * @return string
1215 */
1216 public function format_date_time($timestamp) {
1217 return date_i18n(get_option('date_format').' @ '.get_option('time_format'), ($timestamp + get_option('gmt_offset') * 3600));
1218 }
1219
1220 /**
1221 * Executed this function on cron event.
1222 *
1223 * @return void
1224 */
1225 public function cron_action() {
1226
1227 $optimizer = $this->get_optimizer();
1228 $options = $this->get_options();
1229
1230 $this->log('WPO: Starting cron_action()');
1231 $options->update_option('last-optimized', time());
1232 if ('true' == $options->get_option('schedule')) {
1233 $this_options = $options->get_option('auto');
1234
1235 // Currently the output of the optimizations is not saved/used/logged.
1236 $optimizer->do_optimizations($this_options, 'auto');
1237 }
1238
1239 }
1240
1241 /**
1242 * Schedule cron tasks used by plugin.
1243 *
1244 * @return void
1245 */
1246 public function schedule_plugin_cron_tasks() {
1247 if (!wp_next_scheduled('wpo_weekly_cron_tasks')) {
1248 wp_schedule_event(current_time("timestamp", 0), 'weekly', 'wpo_weekly_cron_tasks');
1249 }
1250
1251 add_action('wpo_weekly_cron_tasks', array($this, 'do_weekly_cron_tasks'));
1252 }
1253
1254 /**
1255 * Do plugin background tasks.
1256 *
1257 * @return void
1258 */
1259 public function do_weekly_cron_tasks() {
1260 // add tasks here.
1261 $this->get_db_info()->update_plugin_json();
1262 }
1263
1264 /**
1265 * This will customize a URL with a correct Affiliate link
1266 * This function can be update to suit any URL as longs as the URL is passed
1267 *
1268 * @param String $url - URL to be check to see if it an updraftplus match.
1269 * @param String $text - Text to be entered within the href a tags.
1270 * @param String $html - Any specific HTML to be added. Supplied parameter will not be escaped in this function. Provide escaped, safe HTML
1271 * @param String|Array $attrs - Specify the HTML attributes as an array or string. Use the array format for multiple attributes (e.g., array( "class" => "lorem-ipsum", "title" => "Highlighting text" )), and use the string format for a single attribute (e.g., 'class="lorem-ipsum"').
1272 * @param Boolean $return_instead_of_echo - if set, then the result will be returned, not echo-ed.
1273 * @return String|void
1274 */
1275 public function wp_optimize_url($url, $text = '', $html = '', $attrs = '', $return_instead_of_echo = false) {
1276 // Check if the URL is UpdraftPlus.
1277 $url = $this->maybe_add_affiliate_params($url); // Return URL - check if there is HTML such as images.
1278
1279 // Check if the variable $text is empty (null value included), otherwise assign $html.
1280 $content = empty($text) ? $html : esc_html($text);
1281
1282 // Check if $attrs is an array to convert the attributes into a string line.
1283 $str_attrs = '';
1284 if (is_array($attrs)) {
1285 foreach ($attrs as $attr => $value) {
1286 $str_attrs .= $attr . '="' . esc_attr($value) . '" ';
1287 }
1288 } else {
1289 // If $attrs is empty, the explode function will only return an empty array.
1290 $attrs = explode('=', $attrs);
1291 // Check if $attrs in positions 1 and 2 are not empty and exist; otherwise, return an empty string.
1292 $str_attrs = !empty($attrs[0]) && !empty($attrs[1]) ? $attrs[0] . '="' . esc_attr(str_replace('"', '', $attrs[1])) . '"' : '';
1293 }
1294
1295 // Check if it is necessary to add a target value if the url is external
1296 $is_external_url = $this->is_external_url($url);
1297 if ($is_external_url) {
1298 $str_attrs = preg_replace('/\s+target="_blank"/i', '', $str_attrs);
1299 $str_attrs .= ' target="_blank"';
1300 }
1301
1302 $result = sprintf(
1303 '<a href="%s" %s>%s</a>',
1304 esc_url($url),
1305 $str_attrs,
1306 $content
1307 );
1308
1309 if ($return_instead_of_echo) return $result;
1310 echo $result;
1311 }
1312
1313 /**
1314 * Check if a URL is external
1315 *
1316 * @param string $url
1317 * @return string
1318 */
1319 public function is_external_url($url) {
1320 if (empty($url)) {
1321 return false;
1322 }
1323
1324 $current_domain = wp_parse_url(home_url(), PHP_URL_HOST);
1325 $url = wp_parse_url($url, PHP_URL_HOST);
1326
1327 // Compare the domains and return true if they are different
1328 return $current_domain !== $url;
1329 }
1330
1331 /**
1332 * Get an URL with an eventual affiliate ID
1333 *
1334 * @param string $url
1335 * @return string
1336 */
1337 public function maybe_add_affiliate_params($url) {
1338 // Check if the URL is UpdraftPlus.
1339 if (false !== strpos($url, '//updraftplus.com')) {
1340 // Set URL with Affiliate ID.
1341 $url = add_query_arg(array('afref' => $this->get_notices()->get_affiliate_id()), $url);
1342
1343 // Apply filters.
1344 $url = apply_filters('wpoptimize_updraftplus_com_link', $url);
1345 }
1346 return apply_filters('wpoptimize_maybe_add_affiliate_params', $url);
1347 }
1348
1349 /**
1350 * Setup WPO logger(s)
1351 */
1352 public function setup_loggers() {
1353
1354 $logger = $this->get_logger();
1355 $loggers = $this->wpo_loggers();
1356
1357 if (!empty($loggers)) {
1358 foreach ($loggers as $_logger) {
1359 $logger->add_logger($_logger);
1360 }
1361 }
1362
1363 add_action('wp_optimize_after_optimizations', array($this, 'after_optimizations_logger_action'));
1364 }
1365
1366 /**
1367 * Run logger actions after all optimizations done
1368 */
1369 public function after_optimizations_logger_action() {
1370 $loggers = $this->get_logger()->get_loggers();
1371 if (!empty($loggers)) {
1372 foreach ($loggers as $logger) {
1373 if (is_a($logger, 'Updraft_Email_Logger')) {
1374 $logger->flush_log();
1375 }
1376 }
1377 }
1378 }
1379
1380 /**
1381 * Returns list of WPO loggers instances
1382 * Apply filter wp_optimize_loggers
1383 *
1384 * @return array
1385 */
1386 public function wpo_loggers() {
1387
1388 $loggers = array();
1389 $loggers_classes_by_id = array();
1390 $options_keys = array();
1391
1392 $loggers_classes = $this->get_loggers_classes();
1393
1394 foreach ($loggers_classes as $logger_class => $source) {
1395 $loggers_classes_by_id[strtolower($logger_class)] = $logger_class;
1396 }
1397
1398 $options = $this->get_options();
1399
1400 $saved_loggers = $options->get_option('logging');
1401 $logger_additional_options = $options->get_option('logging-additional');
1402
1403 // create loggers classes instances.
1404 if (!empty($saved_loggers)) {
1405 // check for previous version options format.
1406 $keys = array_keys($saved_loggers);
1407
1408 // if options stored in old format then reformat it.
1409 if (false == is_numeric($keys[0])) {
1410 $_saved_loggers = array();
1411 foreach ($saved_loggers as $logger_id => $enabled) {
1412 if ($enabled) {
1413 $_saved_loggers[] = $logger_id;
1414 }
1415 }
1416
1417 // fill email with admin.
1418 if (array_key_exists('updraft_email_logger', $saved_loggers) && $saved_loggers['updraft_email_logger']) {
1419 $logger_additional_options['updraft_email_logger'] = array(
1420 get_option('admin_email')
1421 );
1422 }
1423
1424 $saved_loggers = $_saved_loggers;
1425 }
1426
1427 foreach ($saved_loggers as $i => $logger_id) {
1428
1429 if (!array_key_exists($logger_id, $loggers_classes_by_id)) continue;
1430
1431 $logger_class = $loggers_classes_by_id[$logger_id];
1432
1433 $logger = new $logger_class();
1434
1435 $logger_options = $logger->get_options_list();
1436
1437 if (!empty($logger_options)) {
1438 foreach (array_keys($logger_options) as $option_name) {
1439 if (array_key_exists($option_name, $options_keys)) {
1440 $options_keys[$option_name]++;
1441 } else {
1442 $options_keys[$option_name] = 0;
1443 }
1444
1445 $option_value = isset($logger_additional_options[$option_name][$options_keys[$option_name]]) ? $logger_additional_options[$option_name][$options_keys[$option_name]] : '';
1446
1447 // if options in old format then get correct value.
1448 if ('' === $option_value && array_key_exists($logger_id, $logger_additional_options)) {
1449 $option_value = array_shift($logger_additional_options[$logger_id]);
1450 }
1451
1452 $logger->set_option($option_name, $option_value);
1453 }
1454 }
1455
1456 // check if logger is active.
1457 $active = (!is_array($logger_additional_options) || (array_key_exists('active', $logger_additional_options) && empty($logger_additional_options['active'][$i]))) ? false : true;
1458
1459 if ($active) {
1460 $logger->enable();
1461 } else {
1462 $logger->disable();
1463 }
1464
1465 $loggers[] = $logger;
1466 }
1467 }
1468
1469 $loggers = apply_filters('wp_optimize_loggers', $loggers);
1470
1471 return $loggers;
1472 }
1473
1474 /**
1475 * Returns associative array with logger class name in a key and path to class file in a value.
1476 *
1477 * @return array
1478 */
1479 public function get_loggers_classes() {
1480 $loggers_classes = array(
1481 'Updraft_PHP_Logger' => WPO_PLUGIN_MAIN_PATH . 'includes/class-updraft-php-logger.php',
1482 'Updraft_Email_Logger' => WPO_PLUGIN_MAIN_PATH . 'includes/class-updraft-email-logger.php',
1483 'Updraft_Ring_Logger' => WPO_PLUGIN_MAIN_PATH . 'includes/class-updraft-ring-logger.php'
1484 );
1485
1486 $loggers_classes = apply_filters('wp_optimize_loggers_classes', $loggers_classes);
1487
1488 if (!empty($loggers_classes)) {
1489 foreach ($loggers_classes as $logger_class => $logger_file) {
1490 if (!class_exists($logger_class)) {
1491 if (is_file($logger_file)) {
1492 include_once($logger_file);
1493 }
1494 }
1495 }
1496 }
1497
1498 return $loggers_classes;
1499 }
1500
1501 /**
1502 * Returns information about all loggers classes.
1503 *
1504 * @return array
1505 */
1506 public function get_loggers_classes_info() {
1507 $loggers_classes = $this->get_loggers_classes();
1508
1509 $loggers_classes_info = array();
1510
1511 if (!empty($loggers_classes)) {
1512 foreach (array_keys($loggers_classes) as $logger_class_name) {
1513
1514 if (!class_exists($logger_class_name)) continue;
1515
1516 $logger_id = strtolower($logger_class_name);
1517 $logger_class = new $logger_class_name();
1518
1519 $loggers_classes_info[$logger_id] = array(
1520 'description' => $logger_class->get_description(),
1521 'available' => $logger_class->is_available(),
1522 'allow_multiple' => $logger_class->is_allow_multiple(),
1523 'options' => $logger_class->get_options_list()
1524 );
1525 }
1526 }
1527
1528 return $loggers_classes_info;
1529 }
1530
1531 /**
1532 * Returns true if optimization works in multisite mode
1533 *
1534 * @return boolean
1535 */
1536 public function is_multisite_mode() {
1537 return (is_multisite() && self::is_premium());
1538 }
1539
1540 /**
1541 * Returns true if current user can run optimizations.
1542 *
1543 * @return bool
1544 */
1545 public function can_run_optimizations() {
1546 // we don't check permissions for cron jobs.
1547 if (defined('DOING_CRON') && DOING_CRON) return true;
1548
1549 if (self::is_premium() && false == user_can(get_current_user_id(), 'wpo_run_optimizations')) return false;
1550 return true;
1551 }
1552
1553 /**
1554 * Returns true if current user can manage plugin options.
1555 *
1556 * @return bool
1557 */
1558 public function can_manage_options() {
1559 if (self::is_premium() && false == user_can(get_current_user_id(), 'wpo_manage_settings')) return false;
1560 return true;
1561 }
1562
1563 /**
1564 * Returns list of all sites in multisite
1565 *
1566 * @return array
1567 */
1568 public function get_sites() {
1569 $sites = array();
1570 // check if function get_sites exists (since 4.6.0) else use wp_get_sites.
1571 if (function_exists('get_sites')) {
1572 $sites = get_sites(array('network_id' => null, 'deleted' => 0, 'number' => 999999));
1573 } elseif (function_exists('wp_get_sites')) {
1574 $sites = wp_get_sites(array('network_id' => null, 'deleted' => 0, 'limit' => 999999));
1575 }
1576 return $sites;
1577 }
1578
1579 /**
1580 * Returns script memory limit in megabytes.
1581 *
1582 * @param bool $memory_limit
1583 * @return int
1584 */
1585 public function get_memory_limit($memory_limit = false) {
1586 // Returns in megabytes
1587 if (false == $memory_limit) $memory_limit = ini_get('memory_limit');
1588 $memory_limit = rtrim($memory_limit);
1589
1590 return $this->return_bytes($memory_limit);
1591 }
1592
1593 /**
1594 * Returns free memory in bytes.
1595 *
1596 * @return int
1597 */
1598 public function get_free_memory() {
1599 return $this->get_memory_limit() - memory_get_usage();
1600 }
1601
1602 /**
1603 * Checks PHP memory_limit and WP_MAX_MEMORY_LIMIT values and return minimal.
1604 *
1605 * @return int memory limit in bytes.
1606 */
1607 public function get_script_memory_limit() {
1608 $memory_limit = $this->get_memory_limit();
1609
1610 if (defined('WP_MAX_MEMORY_LIMIT')) {
1611 $wp_memory_limit = $this->get_memory_limit(WP_MAX_MEMORY_LIMIT);
1612
1613 if ($wp_memory_limit > 0 && $wp_memory_limit < $memory_limit) {
1614 $memory_limit = $wp_memory_limit;
1615 }
1616 }
1617
1618 return $memory_limit;
1619 }
1620
1621 /**
1622 * Returns max packet size for database.
1623 *
1624 * @return int|string
1625 */
1626 public function get_max_packet_size() {
1627 global $wpdb;
1628 static $mp = 0;
1629
1630 if ($mp > 0) return $mp;
1631
1632 $mp = (int) $wpdb->get_var("SELECT @@session.max_allowed_packet");
1633 // Default to 1MB
1634 $mp = (is_numeric($mp) && $mp > 0) ? $mp : 1048576;
1635 // 32MB
1636 if ($mp < 33554432) {
1637 $save = $wpdb->show_errors(false);
1638 @$wpdb->query("SET GLOBAL max_allowed_packet=33554432");// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- suppress errors from displaying
1639 $wpdb->show_errors($save);
1640
1641 $mp = (int) $wpdb->get_var("SELECT @@session.max_allowed_packet");
1642 // Default to 1MB
1643 $mp = (is_numeric($mp) && $mp > 0) ? $mp : 1048576;
1644 }
1645
1646 return $mp;
1647 }
1648
1649 /**
1650 * Converts shorthand memory notation value to bytes.
1651 * From http://php.net/manual/en/function.ini-get.php
1652 *
1653 * @param string $val shorthand memory notation value.
1654 */
1655 public function return_bytes($val) {
1656 $val = trim($val);
1657 $last = strtolower($val[strlen($val)-1]);
1658 $val = (int) $val;
1659 switch ($last) {
1660 case 'g':
1661 $val *= 1024;
1662 // no break
1663 case 'm':
1664 $val *= 1024;
1665 // no break
1666 case 'k':
1667 $val *= 1024;
1668 }
1669
1670 return $val;
1671 }
1672
1673 /**
1674 * Log fatal errors to defined log destinations.
1675 */
1676 public function log_fatal_errors() {
1677 $last_error = error_get_last();
1678
1679 if (isset($last_error['type']) && E_ERROR === $last_error['type']) {
1680 $this->get_logger()->critical($last_error['message']);
1681 }
1682 }
1683
1684 /**
1685 * Close browser connection and continue script work. - Taken from UpdraftPlus
1686 *
1687 * @param array $txt Response to browser; this must be JSON (or if not, alter the Content-Type header handling below)
1688 * @return void
1689 */
1690 public function close_browser_connection($txt = '') {
1691 if (!headers_sent()) {
1692 // Close browser connection so that it can resume AJAX polling
1693 header('Content-Length: '.(empty($txt) ? '0' : 4+strlen($txt)));
1694 header('Connection: close');
1695 header('Content-Encoding: none');
1696 }
1697
1698 if (session_id()) session_write_close();
1699 echo "\r\n\r\n";
1700 echo $txt;
1701 // These two added - 19-Feb-15 - started being required on local dev machine, for unknown reason (probably some plugin that started an output buffer).
1702 $ob_level = ob_get_level();
1703 while ($ob_level > 0) {
1704 ob_end_flush();
1705 $ob_level--;
1706 }
1707 flush();
1708 if (function_exists('fastcgi_finish_request')) fastcgi_finish_request();
1709 }
1710
1711 /**
1712 * Get the current theme's style.css headers
1713 *
1714 * @return array|WP_Error
1715 */
1716 public function get_stylesheet_headers() {
1717 static $headers;
1718 if (isset($headers)) return $headers;
1719
1720 $style = get_template_directory_uri() . '/style.css';
1721
1722 /**
1723 * Filters wp_remote_get parameters, when checking if browser cache is enabled.
1724 *
1725 * @param array $request_params Default parameters
1726 */
1727 $request_params = apply_filters('wpoptimize_get_stylesheet_headers_args', array('timeout' => 10));
1728
1729 // trying to load style.css.
1730 $response = wp_remote_get($style, $request_params);
1731
1732 if (is_a($response, 'WP_Error')) return $response;
1733
1734 $headers = wp_remote_retrieve_headers($response);
1735
1736 if (method_exists($headers, 'getAll')) {
1737 $headers = $headers->getAll();
1738 }
1739
1740 return is_array($headers) ? $headers : array();
1741 }
1742
1743 /**
1744 * Try to change PHP script time limit.
1745 */
1746 public function change_time_limit() {
1747 $time_limit = (defined('WP_OPTIMIZE_SET_TIME_LIMIT') && WP_OPTIMIZE_SET_TIME_LIMIT > 15) ? WP_OPTIMIZE_SET_TIME_LIMIT : 1800;
1748
1749 @set_time_limit($time_limit); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Try to reduce the chances of PHP self-terminating via reaching max_execution_time.
1750 }
1751
1752 /**
1753 * Does the request come from UDC
1754 *
1755 * @return boolean
1756 */
1757 public function is_updraft_central_request() {
1758 return defined('UPDRAFTCENTRAL_COMMAND') && UPDRAFTCENTRAL_COMMAND;
1759 }
1760
1761 /**
1762 * Does the data need to be included in this request. Currently only true if the request is made from UpdraftCentral.
1763 *
1764 * @return boolean
1765 */
1766 public function template_should_include_data() {
1767 /**
1768 * Filters wether data should be included in certain templates or not.
1769 */
1770 return apply_filters('wpo_template_should_include_data', $this->is_updraft_central_request());
1771 }
1772
1773 /**
1774 * Load the templates for the modal window
1775 */
1776 public function load_modal_template() {
1777 $this->include_template('modal.php');
1778 }
1779
1780 /**
1781 * Delete transients and semaphores data from options table.
1782 */
1783 public function delete_transients_and_semaphores() {
1784 global $wpdb;
1785
1786 $masks = array(
1787 'updraft_locked_wpo_%',
1788 'updraft_unlocked_wpo_%',
1789 'updraft_last_lock_time_wpo_%',
1790 'updraft_semaphore_wpo_%',
1791 'wpo_locked_%',
1792 'wpo_unlocked_%',
1793 'wpo_last_lock_time_%',
1794 'wpo_semaphore_%',
1795 '_transient_timeout_wpo_%',
1796 '_transient_wpo_%',
1797 'updraft_lock_wpo_%',
1798 'wpo_last_scheduled_%',
1799 );
1800
1801 $where_parts = array();
1802 foreach ($masks as $mask) {
1803 $where_parts[] = "(`option_name` LIKE '{$mask}')";
1804 }
1805
1806 $wpdb->query("DELETE FROM {$wpdb->options} WHERE " . join(' OR ', $where_parts));
1807 }
1808
1809 /**
1810 * Prevents bots from indexing plugins list
1811 */
1812 public function robots_txt($output) {
1813 $upload_dir = wp_upload_dir();
1814 $path = parse_url($upload_dir['baseurl']);
1815 $output .= "\nUser-agent: *";
1816 $output .= "\nDisallow: " . str_replace($path['scheme'].'://'.$path['host'], '', $upload_dir['baseurl']) . "/wpo-plugins-tables-list.json\n";
1817 return $output;
1818 }
1819
1820 /**
1821 * Returns desired enqueue version string
1822 *
1823 * @return string Enqueue version as string
1824 */
1825 public function get_enqueue_version() {
1826 return (defined('WP_DEBUG') && WP_DEBUG) ? WPO_VERSION.'.'.time() : WPO_VERSION;
1827 }
1828
1829 /**
1830 * Returns script suffix string
1831 *
1832 * @return string empty or `.min` suffix string
1833 */
1834 public function get_min_or_not_string() {
1835 return (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '' : '.min';
1836 }
1837
1838 /**
1839 * Returns script suffix string with WPO_VERSION
1840 *
1841 * @return string empty or min suffix with wpo_version string
1842 */
1843 public function get_min_or_not_internal_string() {
1844 return (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '' : '-' . str_replace('.', '-', WPO_VERSION) . '.min';
1845 }
1846
1847 /**
1848 * Instantiate Ajax handling class
1849 */
1850 private function load_ajax_handler() {
1851 WPO_Ajax::get_instance();
1852 }
1853
1854 /**
1855 * Set custom heartbeat API interval
1856 *
1857 * @param array $settings Current WP settings
1858 * @return array
1859 */
1860 public function set_heartbeat_time_interval($settings) {
1861 $settings['interval'] = WPO_Ajax::HEARTBEAT_INTERVAL;
1862 return $settings;
1863 }
1864
1865 /**
1866 * Receive Heartbeat data and respond.
1867 *
1868 * Processes data received via a Heartbeat request, and returns additional data to pass back to the front end.
1869 *
1870 * @param array $response Heartbeat response data to pass back to front end.
1871 * @param array $data Data received from the front end (unslashed).
1872 *
1873 * @return array
1874 */
1875 public function receive_heartbeat($response, $data) {
1876 foreach ($data as $uid => $command) {
1877 $is_wpo_heartbeat = strpos($uid, 'wpo-heartbeat') === 0;
1878
1879 if ($is_wpo_heartbeat) {
1880 $response['callbacks'][$uid] = apply_filters('wp_optimize_heartbeat', $command);
1881 }
1882 }
1883
1884 return $response;
1885 }
1886
1887 /**
1888 * Add heartbeat API filters if the page is enabled in the array
1889 *
1890 * @return void
1891 */
1892 public function set_heartbeat_api_filters() {
1893 $pages_enabled = array('WP-Optimize', 'wpo_images', 'wpo_cache', 'wpo_minify', 'wpo_settings', 'wpo_support', 'wpo_mayalso');
1894
1895 // Change heartbeat API frequency to 15 seconds to improve UI experience
1896 // only for the pages that we enable in `$pages_enabled`
1897 $query_page = isset($_GET['page']) ? sanitize_text_field(wp_unslash($_GET['page'])) : ''; // phpcs:ignore WordPress.Security.NonceVerification -- not processing form data
1898 if (in_array($query_page, $pages_enabled)) {
1899 add_filter('heartbeat_settings', array($this, 'set_heartbeat_time_interval'), PHP_INT_MAX);
1900 }
1901
1902 // Handle heartbeat events
1903 add_filter('heartbeat_received', array($this, 'receive_heartbeat'), 10, 2);
1904 }
1905 }
1906
1907 /**
1908 * Unschedule specific tasks:
1909 * `wpo_cron_event2` => DB optimizations
1910 * `wpo_weekly_cron_tasks` => ex: plugin.json auto update
1911 *
1912 * @return void
1913 */
1914 function wpo_cron_deactivate() {
1915 WP_Optimize()->log('running wpo_cron_deactivate()');
1916 wp_clear_scheduled_hook('wpo_cron_event2');
1917 wp_clear_scheduled_hook('wpo_weekly_cron_tasks');
1918 }
1919
1920 function WP_Optimize() {
1921 return WP_Optimize::instance();
1922 }
1923
1924 endif;
1925
1926 $GLOBALS['wp_optimize'] = WP_Optimize();
1927