PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 4.5.5
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v4.5.5
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 4.5.5, at wp-optimize.php

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