PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 4.5.3
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v4.5.3
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 / includes / class-wp-optimizer.php

class-wp-optimizer.php in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 4.5.3, at includes/class-wp-optimizer.php

670 lines 21.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('ABSPATH')) die('No direct access allowed');
4
5 /**
6 * This class invokes optimiazations. The optimizations themselves live in the 'optimizations' subdirectory of the plugin. The proper way to obtain access to the instance is via WP_Optimize()->get_optimizer()
7 */
8 class WP_Optimizer {
9
10 /**
11 * Returns singleton instance object
12 *
13 * @return WP_Optimizer Returns `WP_Optimizer` object
14 */
15 public static function instance() {
16 static $_instance = null;
17 if (null === $_instance) {
18 $_instance = new self();
19 }
20 return $_instance;
21 }
22
23 /**
24 * Gets retention information
25 *
26 * @return array
27 */
28 public function get_retain_info() {
29
30 $options = WP_Optimize()->get_options();
31
32 $retain_enabled = $options->get_option('retention-enabled', 'false');
33 $retain_period = (($retain_enabled) ? $options->get_option('retention-period', '2') : null);
34
35 return array($retain_enabled, $retain_period);
36 }
37
38 /**
39 * Get data retention options
40 *
41 * @return array
42 */
43 public function get_revisions_retain_info() {
44 $options = WP_Optimize()->get_options();
45
46 $revisions_retention_enabled = $options->get_option('revisions-retention-enabled', 'false');
47 $revisions_retention_count = $revisions_retention_enabled ? $options->get_option('revisions-retention-count', '2') : null;
48
49 return array($revisions_retention_enabled, $revisions_retention_count);
50 }
51
52 /**
53 * Gets a list of optimizations
54 *
55 * @return array
56 */
57 public function get_optimizations_list() {
58
59 $optimizations = array();
60
61 $optimizations_dir = WPO_PLUGIN_MAIN_PATH.'optimizations';
62
63 if ($dh = opendir($optimizations_dir)) {
64 while (($file = readdir($dh)) !== false) {
65 if ('.' === $file || '..' === $file || '.php' !== substr($file, -4, 4) || !is_file($optimizations_dir.'/'.$file) || 'inactive-' === substr($file, 0, 9)) continue;
66 $optimizations[] = substr($file, 0, (strlen($file) - 4));
67 }
68 closedir($dh);
69 }
70
71 $filtered_optimizations = apply_filters('wp_optimize_get_optimizations_list', $optimizations);
72 return is_array($filtered_optimizations) ? $filtered_optimizations : $optimizations;
73 }
74
75 /**
76 * Currently, there is only one sort rule (so, the parameter's value is ignored)
77 *
78 * @param array $optimizations An array of optimizations (i.e. WP_Optimization instances).
79 * @param string $sort_on Specify sort.
80 *
81 * @return array
82 */
83 public function sort_optimizations($optimizations, $sort_on = 'ui_sort_order') {
84 if ('run_sort_order' === $sort_on) {
85 uasort($optimizations, array($this, 'sort_optimizations_run_traditional'));
86 } else {
87 uasort($optimizations, array($this, 'sort_optimizations_ui_traditional'));
88 }
89 return $optimizations;
90 }
91
92 /**
93 * Sorts optimizations based on UI order
94 *
95 * @param WP_Optimization $a
96 * @param WP_Optimization $b
97 * @return int
98 */
99 public function sort_optimizations_ui_traditional($a, $b) {
100 return $this->sort_optimizations_traditional($a, $b);
101 }
102
103 /**
104 * Sorts optimizations based on Run order
105 *
106 * @param WP_Optimization $a
107 * @param WP_Optimization $b
108 * @return int
109 */
110 public function sort_optimizations_run_traditional($a, $b) {
111 return $this->sort_optimizations_traditional($a, $b, 'run_sort_order');
112 }
113
114 /**
115 * Sorting list of optimization based on given order
116 *
117 * @param WP_Optimization $a
118 * @param WP_Optimization $b
119 * @param string $sort_on
120 * @return int
121 */
122 public function sort_optimizations_traditional($a, $b, $sort_on = 'ui_sort_order') {
123 $sort_order_a = empty($a->$sort_on) ? 0 : $a->$sort_on;
124 $sort_order_b = empty($b->$sort_on) ? 0 : $b->$sort_on;
125
126 if ($sort_order_a === $sort_order_b) return 0;
127
128 return ($sort_order_a < $sort_order_b) ? -1 : 1;
129 }
130
131 /**
132 * This method returns an array of available optimizations.
133 * Each array key is an optimization ID, and the value is an object,
134 * as returned by get_optimization()
135 *
136 * @return array array of optimizations
137 */
138 public function get_optimizations() {
139
140 $optimizations = $this->get_optimizations_list();
141
142 $optimization_objects = array();
143
144 foreach ($optimizations as $optimization) {
145 $optimization_object = $this->get_optimization($optimization);
146 if (is_wp_error($optimization_object)) {
147 WP_Optimize()->log('Failed to load optimization ' . $optimization . ' - ' . $optimization_object->get_error_message());
148 } else {
149 $optimization_objects[$optimization] = $optimization_object;
150 }
151 }
152
153 $filtered_optimizations = apply_filters('wp_optimize_get_optimizations', $optimization_objects);
154 return is_array($filtered_optimizations) ? $filtered_optimizations : $optimization_objects;
155
156 }
157
158 /**
159 * This method returns an object for a specific optimization.
160 *
161 * @param string|object $which_optimization An optimization ID.
162 * @param array $data An array of anny options $data.
163 * @return array|WP_Error WP_Error Will return the optimization, or a WP_Error object if it was not found.
164 */
165 public function get_optimization($which_optimization, $data = array()) {
166
167 $optimization_class = apply_filters('wp_optimize_optimization_class', 'WP_Optimization_'.$which_optimization);
168
169 if (!class_exists('WP_Optimization')) include_once(WPO_PLUGIN_MAIN_PATH.'includes/class-wp-optimization.php');
170
171 if (!class_exists($optimization_class)) {
172 $optimization_file = WPO_PLUGIN_MAIN_PATH.'optimizations/'.$which_optimization.'.php';
173 $class_file = apply_filters('wp_optimize_optimization_class_file', $optimization_file);
174 if (!preg_match('/^[a-z]+$/', $which_optimization) || !file_exists($class_file)) {
175 return new WP_Error('no_such_optimization', __('No such optimization', 'wp-optimize'), $which_optimization);
176 }
177
178 include_once($class_file);
179
180 if (!class_exists($optimization_class)) {
181 return new WP_Error('no_such_optimization', __('No such optimization', 'wp-optimize'), $which_optimization);
182 }
183 }
184
185 // set sites option for Multisite cron job.
186 if (defined('DOING_CRON') && DOING_CRON && is_multisite()) {
187 $options = WP_Optimize()->get_options();
188 $data['optimization_sites'] = $options->get_option('wpo-sites-cron', array('all'));
189 }
190
191 $optimization = new $optimization_class($data);
192
193 return $optimization;
194
195 }
196
197 /**
198 * The method to call to perform an optimization.
199 *
200 * @param string|object $which_optimization An optimization ID, or a WP_Optimization object.
201 * @return object Array of results from the optimization.
202 */
203 public function do_optimization($which_optimization) {
204
205 $optimization = (is_object($which_optimization) && is_a($which_optimization, 'WP_Optimization')) ? $which_optimization : $this->get_optimization($which_optimization);
206
207 if (is_wp_error($optimization)) {
208 WP_Optimize()->log('Error occurred. Unknown optimization.');
209 return $optimization;
210 }
211
212 WP_Optimize()->change_time_limit();
213
214 $optimization->init();
215
216 if (apply_filters('wp_optimize_do_optimization', true, $which_optimization, $optimization)) {
217
218 $optimization->before_optimize();
219
220 if ($optimization->run_multisite) {
221 foreach ($optimization->blogs_ids as $blog_id) {
222 $optimization->switch_to_blog($blog_id);
223 $optimization->optimize();
224 $optimization->restore_current_blog();
225 }
226 } else {
227 $optimization->optimize();
228 }
229
230 $optimization->after_optimize();
231
232 }
233
234 do_action('wp_optimize_after_optimization', $which_optimization, $optimization);
235
236 $results = $optimization->get_results();
237
238 return $results;
239 }
240
241 /**
242 * The method to call to get information about an optimization.
243 * As with do_optimization, it is somewhat modelled after the template interface
244 *
245 * @param string|object $which_optimization An optimization ID, or a WP_Optimization object.
246 * @return object returns the optimization information
247 */
248 public function get_optimization_info($which_optimization) {
249
250 $optimization = (is_object($which_optimization) && is_a($which_optimization, 'WP_Optimization')) ? $which_optimization : $this->get_optimization($which_optimization);
251
252 if (is_wp_error($optimization)) return $optimization;
253
254 WP_Optimize()->change_time_limit();
255
256 $optimization->before_get_info();
257
258 if ($optimization->run_multisite) {
259 foreach ($optimization->blogs_ids as $blog_id) {
260 $optimization->switch_to_blog($blog_id);
261 $optimization->get_info();
262 $optimization->restore_current_blog();
263 }
264 } else {
265 $optimization->get_info();
266 }
267
268 $optimization->after_get_info();
269
270 return $optimization->get_results();
271 }
272
273 /**
274 * This runs the list of optimizations.
275 *
276 * @param array $optimization_options Whether to do an optimization depends on what keys are set (legacy - can be changed hopefully).
277 * @param string $which_option Specify which option.
278 * @return array Returns an array of result objects.
279 */
280 public function do_optimizations($optimization_options, $which_option = 'dom') {
281
282 $results = array();
283
284 if (empty($optimization_options)) return $results;
285
286 $optimizations = $this->sort_optimizations($this->get_optimizations(), 'run_sort_order');
287
288 foreach ($optimizations as $optimization_id => $optimization) {
289 $option_id = call_user_func(array($optimization, 'get_'.$which_option.'_id'));
290
291 if (isset($optimization_options[$option_id])) {
292 // if options saved as a string then compare with string (for support different versions)
293 if ('false' === $optimization_options[$option_id]) continue;
294
295 if ('auto' === $which_option && empty($optimization->available_for_auto)) continue;
296
297 WP_Optimize()->change_time_limit();
298
299 $results[$optimization_id] = $this->do_optimization($optimization);
300 }
301 }
302
303 // Run action after all optimizations completed.
304 do_action('wp_optimize_after_optimizations');
305
306 return $results;
307
308 }
309
310 public function get_table_prefix($allow_override = false) {
311 $wpdb = $GLOBALS['wpdb'];
312 if (is_multisite() && !defined('MULTISITE')) {
313 // In this case (which should only be possible on installations upgraded from pre WP 3.0 WPMU), $wpdb->get_blog_prefix() cannot be made to return the right thing. $wpdb->base_prefix is not explicitly marked as public, so we prefer to use get_blog_prefix if we can, for future compatibility.
314 $prefix = $wpdb->base_prefix;
315 } else {
316 $prefix = $wpdb->get_blog_prefix(0);
317 }
318 return ($allow_override) ? apply_filters('wp_optimize_get_table_prefix', $prefix) : $prefix;
319 }
320
321 /**
322 * Returns information about database tables.
323 *
324 * @param bool $update refresh or no cached data
325 *
326 * @return mixed
327 */
328 public function get_tables($update = false) {
329 static $tables_info = null;
330
331 if (false === $update && null !== $tables_info) return $tables_info;
332
333 $wpo_db_info = WP_Optimize()->get_db_info();
334
335 $table_status = $wpo_db_info->get_show_table_status($update);
336
337 // Filter on the site's DB prefix (was not done in releases up to 1.9.1).
338 $table_prefix = $this->get_table_prefix();
339
340 if (is_array($table_status)) {
341
342 $corrupted_tables_count = 0;
343 $array_index_was_unset = false;
344
345 foreach ($table_status as $index => $table) {
346 $table_name = $table->Name;
347
348 $include_table = (0 === stripos($table_name, $table_prefix));
349
350 $include_table = apply_filters('wp_optimize_get_tables_include_table', $include_table, $table_name, $table_prefix);
351
352 if (!$include_table && '' !== $table_prefix) {
353 unset($table_status[$index]);
354 $array_index_was_unset = true;
355 continue;
356 }
357
358 $table_status[$index]->Engine = $wpo_db_info->get_table_type($table_name);
359
360 $table_status[$index]->is_optimizable = $wpo_db_info->is_table_optimizable($table_name);
361 $table_status[$index]->is_type_supported = $wpo_db_info->is_table_type_optimize_supported($table_name);
362 // add information about corrupted tables.
363 $is_needing_repair = $wpo_db_info->is_table_needing_repair($table_name);
364 $table_status[$index]->is_needing_repair = $is_needing_repair;
365 if ($is_needing_repair) $corrupted_tables_count++;
366
367 $table_status[$index] = $this->join_plugin_information($table_name, $table_status[$index]);
368
369 $table_status[$index]->blog_id = $wpo_db_info->get_table_blog_id($table_name);
370 }
371 if ($array_index_was_unset) {
372 // Reset array keys after removing elements
373 $table_status = array_values($table_status);
374 }
375
376 WP_Optimize()->get_options()->update_option('corrupted-tables-count', $corrupted_tables_count);
377 }
378
379 $tables_info = apply_filters('wp_optimize_get_tables', $table_status);
380 return $tables_info;
381 }
382
383 /**
384 * Returns information about single table by table name.
385 *
386 * @param string $table_name table name
387 * @return object|boolean table information object.
388 */
389 public function get_table($table_name) {
390
391 $db_info = WP_Optimize()->get_db_info();
392
393 $table = $db_info->get_table_status($table_name);
394
395 if (false === $table) return false;
396
397 $table->is_optimizable = $db_info->is_table_optimizable($table_name);
398 $table->is_type_supported = $db_info->is_table_type_optimize_supported($table_name);
399 $table->is_needing_repair = $db_info->is_table_needing_repair($table_name);
400
401 // add information about plugins.
402 $table = $this->join_plugin_information($table_name, $table);
403
404 $table = apply_filters('wp_optimize_get_table', $table);
405 return $table;
406 }
407
408 /**
409 * Add information about relationship database tables with plugins.
410 *
411 * @param string $table_name
412 * @param object $table_obj
413 *
414 * @return object
415 */
416 public function join_plugin_information($table_name, $table_obj) {
417 // set can be removed flag.
418 $can_be_removed = false;
419 // set WP core table flag.
420 $wp_core_table = false;
421 // set WP actionscheduler table flag.
422 $wp_actionscheduler_table = (false !== stripos($table_name, 'actionscheduler_'));
423 // add information about using table by any of installed plugins.
424 $table_obj->is_using = WP_Optimize()->get_db_info()->is_table_using_by_plugin($table_name);
425 // if table belongs to any plugin then add plugins status.
426 $plugins = WP_Optimize()->get_db_info()->get_table_plugin($table_name);
427
428 $plugin_status = array();
429
430 if (false !== $plugins) {
431 // if belongs to any of plugin then we can remove table if plugin not active.
432 $can_be_removed = true;
433
434 foreach ($plugins as $plugin) {
435 $status = WP_Optimize()->get_db_info()->get_plugin_status($plugin);
436
437 if (__('WordPress core', 'wp-optimize') === $plugin) $wp_core_table = true;
438 // if plugin is active then we can't remove.
439 if ($wp_core_table || $status['active'] || $wp_actionscheduler_table) $can_be_removed = false;
440
441 if ($status['installed'] || $status['active'] || !$table_obj->is_using) {
442 $plugin_status[] = array(
443 'plugin' => $plugin,
444 'status' => $status,
445 );
446 }
447 }
448 }
449
450 $table_obj->plugin_status = $plugin_status;
451 $table_obj->wp_core_table = $wp_core_table;
452 $table_obj->can_be_removed = $can_be_removed;
453
454 return $table_obj;
455 }
456
457 /**
458 * This function grabs a list of tables
459 * and information regarding each table and returns
460 * the results to optimizations-table.php and optimizationstable.php
461 *
462 * @param int $blog_id filter tables by prefix
463 *
464 * @return array - an array of data such as table list, innodb info and data free
465 */
466 public function get_table_information($blog_id = 0) {
467 // Get table information.
468 $tablesstatus = $this->get_tables();
469
470 // Set defaults.
471 $table_information = array();
472 $table_information['total_gain'] = 0;
473 $table_information['inno_db_tables'] = 0;
474 $table_information['non_inno_db_tables'] = 0;
475 $table_information['table_list'] = '';
476 $table_information['is_optimizable'] = true;
477
478 // Make a list of tables to optimize.
479 foreach ($tablesstatus as $each_table) {
480 // if $blog_id is set then filter tables
481 if ($blog_id > 0 && $blog_id !== $each_table->blog_id) continue;
482
483 $table_information['table_list'] .= $each_table->Name.'|';
484
485 // check if table type supported.
486 if (!$each_table->is_type_supported) continue;
487
488 // check if table is optimizable.
489 if (!$each_table->is_optimizable) {
490 $table_information['is_optimizable'] = false;
491 }
492
493 // calculate total gain value.
494 $table_information['total_gain'] += $each_table->Data_free;
495
496 // count InnoDB tables.
497 if ('InnoDB' === $each_table->Engine) {
498 $table_information['inno_db_tables']++;
499 } else {
500 $table_information['non_inno_db_tables']++;
501 }
502 }
503
504 return $table_information;
505 }
506
507 /**
508 * What sort of linkback to enable or disable: valid values are 'trackbacks' or 'comments', and whether to enable or disable.
509 *
510 * @param string $type Specify the type of linkbacks.
511 * @param boolean $enable If it is enabled or disabled.
512 */
513 public function enable_linkbacks($type, $enable = true) {
514
515 $wpdb = $GLOBALS['wpdb'];
516 $wpo_options = WP_Optimize()->get_options();
517
518 $new_status = $enable ? 'open' : 'closed';
519
520 switch ($type) {
521 case "trackbacks":
522 $wpdb->query(
523 $wpdb->prepare(
524 "UPDATE `{$wpdb->posts}` SET ping_status = %s WHERE post_status = %s AND post_type = %s",
525 $new_status,
526 'publish',
527 'post'
528 )
529 );
530 break;
531
532 case "comments":
533 $wpdb->query(
534 $wpdb->prepare(
535 "UPDATE `{$wpdb->posts}` SET comment_status = %s WHERE post_status = %s AND post_type = %s",
536 $new_status,
537 'publish',
538 'post'
539 )
540 );
541 break;
542
543 default:
544 break;
545 }
546 $wpo_options->update_option($type.'_action', array('action' => $enable, 'timestamp' => time()));
547
548 }
549
550 /**
551 * This function will return total database size and a possible gain of db in KB.
552 *
553 * @param boolean $update - Whether to update the values or not
554 * @return array total db size gained.
555 */
556 public function get_current_db_size($update = false) {
557
558 if (!$update && $db_size = get_transient('wpo_get_current_db_size')) {
559 return $db_size;
560 }
561
562 $wp_optimize = WP_Optimize();
563
564 $total_gain = 0;
565 $total_size = 0;
566 $row_usage = 0;// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $row_usage Used in the foreach below
567 $data_usage = 0;
568 $index_usage = 0;
569 $overhead_usage = 0;// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $overhead_usage Used in the foreach below
570
571 $tablesstatus = $this->get_tables();
572
573 foreach ($tablesstatus as $tablestatus) {
574 $row_usage += $tablestatus->Rows;// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $row_usage declared up above
575 $total_gain += $tablestatus->Data_free;
576 $data_usage += $tablestatus->Data_length;
577 $index_usage += $tablestatus->Index_length;
578
579 if ('InnoDB' !== $tablestatus->Engine) {
580 $overhead_usage += $tablestatus->Data_free;// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $overhead_usage declared up above
581 $total_gain += $tablestatus->Data_free;
582 }
583 }
584
585 $total_size = ($data_usage + $index_usage);
586 $db_size = array($wp_optimize->format_size($total_size), $wp_optimize->format_size($total_gain));
587 set_transient('wpo_get_current_db_size', $db_size, 3600);
588 return $db_size;
589 }
590
591 /**
592 * This function will return total saved data in KB.
593 *
594 * @param string $current How big the data is.
595 * @return string Returns new total value.
596 */
597 public function update_total_cleaned($current) {
598
599 $options = WP_Optimize()->get_options();
600
601 $previously_saved = floatval($options->get_option('total-cleaned', '0'));
602 $converted_current = floatval($current);
603
604 $total_now = strval($previously_saved + $converted_current);
605
606 $options->update_option('total-cleaned', $total_now);
607
608 $previously_saved = floatval($options->get_option('total-cleaned-current-month', '0'));
609
610 $total_now = strval($previously_saved + $converted_current);
611
612 $options->update_option('total-cleaned-current-month', $total_now);
613
614 return $total_now;
615 }
616
617
618 /**
619 * Trackback, Comments actions
620 *
621 * @param array $options
622 * @return array
623 */
624 public function trackback_comment_actions($options) {
625
626 $output = array();
627 $messages = array();
628
629 if (isset($options['comments'])) {
630 if (!$options['comments']) {
631 $this->enable_linkbacks('comments', false);
632 $output[] = __('Comments have now been disabled on all current and previously published posts.', 'wp-optimize');
633 // translators: %s is a date and time
634 $messages[] = sprintf(__('All comments on existing posts were disabled at %s.', 'wp-optimize'), WP_Optimize()->format_date_time(time()));
635 } else {
636 $this->enable_linkbacks('comments');
637 $output[] = __('Comments have now been enabled on all current and previously published posts.', 'wp-optimize');
638 // translators: %s is a date and time
639 $messages[] = sprintf(__('All comments on existing posts were enabled at %s.', 'wp-optimize'), WP_Optimize()->format_date_time(time()));
640 }
641 }
642
643 if (isset($options['trackbacks'])) {
644 if (!$options['trackbacks']) {
645 $this->enable_linkbacks('trackbacks', false);
646 $output[] = __('Trackbacks have now been disabled on all current and previously published posts.', 'wp-optimize');
647 // translators: %s is a date and time
648 $messages[] = sprintf(__('All trackbacks on existing posts were disabled at %s.', 'wp-optimize'), WP_Optimize()->format_date_time(time()));
649 } else {
650 $this->enable_linkbacks('trackbacks');
651 $output[] = __('Trackbacks have now been enabled on all current and previously published posts.', 'wp-optimize');
652 // translators: %s is a date and time
653 $messages[] = sprintf(__('All trackbacks on existing posts were enabled at %s.', 'wp-optimize'), WP_Optimize()->format_date_time(time()));
654 }
655 }
656
657 return array('output' => $output,'messages' => $messages);
658 }
659
660 /**
661 * Whether InnoDB tables require confirmation to be optimized
662 *
663 * @return boolean
664 */
665 public function show_innodb_force_optimize() {
666 $tablesstatus = $this->get_table_information();
667 return false === $tablesstatus['is_optimizable'] && $tablesstatus['inno_db_tables'] > 0;
668 }
669 }
670