PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 4.7.0
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v4.7.0
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
← All changes | includes/class-wp-optimizer.php +398 -57 3.2.5 → 4.7.0 View file →
@@ -1,13 +1,59 @@
1 1 <?php
2 2
3 -if (!defined('WPO_VERSION')) die('No direct access allowed');
3 +if (!defined('ABSPATH')) die('No direct access allowed');
4 4
5 5 /**
6 - * This class invokes optimiazations. The optimizations themselves live in the 'optimizations' sub-directory of the plugin. The proper way to obtain access to the instance is via WP_Optimize()->get_optimizer()
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 7 */
8 8 class WP_Optimizer {
9 +
10 + /**
11 + * Table count limit to consider as too many tables to load in a single request
12 + *
13 + * @var int
14 + */
15 + const HIGH_TABLE_COUNT_THRESHOLD = 150;
16 +
17 + /**
18 + * Option name to store all the status information for the tables
19 + *
20 + * @var string
21 + */
22 + const TABLES_LIST_OPTION_NAME = 'all-tables-status';
23 +
24 + /**
25 + * Option name to save the state of the table scan
26 + *
27 + * @var string
28 + */
29 + const TABLES_LIST_STATE_OPTION_NAME = 'all-tables-names-scan-status';
30 +
31 + /**
32 + * Option name to save the total table count
33 + *
34 + * @var string
35 + */
36 + const TABLES_LIST_COUNT_OPTION_NAME = 'all-tables-names-count';
37 +
38 + /**
39 + * Returns singleton instance object
40 + *
41 + * @return WP_Optimizer Returns `WP_Optimizer` object
42 + */
43 + public static function instance() {
44 + static $_instance = null;
45 + if (null === $_instance) {
46 + $_instance = new self();
47 + }
48 + return $_instance;
49 + }
9 50
51 + /**
52 + * Gets retention information
53 + *
54 + * @return array
55 + */
10 56 public function get_retain_info() {
11 57
12 58 $options = WP_Optimize()->get_options();
13 59
@@ -30,8 +76,13 @@
30 76
31 77 return array($revisions_retention_enabled, $revisions_retention_count);
32 78 }
33 79
80 + /**
81 + * Gets a list of optimizations
82 + *
83 + * @return array
84 + */
34 85 public function get_optimizations_list() {
35 86
36 87 $optimizations = array();
37 88
@@ -38,16 +89,16 @@
38 89 $optimizations_dir = WPO_PLUGIN_MAIN_PATH.'optimizations';
39 90
40 91 if ($dh = opendir($optimizations_dir)) {
41 92 while (($file = readdir($dh)) !== false) {
42 - if ('.' == $file || '..' == $file || '.php' != substr($file, -4, 4) || !is_file($optimizations_dir.'/'.$file) || 'inactive-' == substr($file, 0, 9)) continue;
93 + if ('.' === $file || '..' === $file || '.php' !== substr($file, -4, 4) || !is_file($optimizations_dir.'/'.$file) || 'inactive-' === substr($file, 0, 9)) continue;
43 94 $optimizations[] = substr($file, 0, (strlen($file) - 4));
44 95 }
45 96 closedir($dh);
46 97 }
47 98
48 - return apply_filters('wp_optimize_get_optimizations_list', $optimizations);
49 -
99 + $filtered_optimizations = apply_filters('wp_optimize_get_optimizations_list', $optimizations);
100 + return is_array($filtered_optimizations) ? $filtered_optimizations : $optimizations;
50 101 }
51 102
52 103 /**
53 104 * Currently, there is only one sort rule (so, the parameter's value is ignored)
@@ -53,13 +104,13 @@
53 104 * Currently, there is only one sort rule (so, the parameter's value is ignored)
54 105 *
55 106 * @param array $optimizations An array of optimizations (i.e. WP_Optimization instances).
56 107 * @param string $sort_on Specify sort.
57 - * @param string $sort_rule Sort Rule.
108 + *
58 109 * @return array
59 110 */
60 - public function sort_optimizations($optimizations, $sort_on = 'ui_sort_order', $sort_rule = 'traditional') {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
61 - if ('run_sort_order' == $sort_on) {
111 + public function sort_optimizations($optimizations, $sort_on = 'ui_sort_order') {
112 + if ('run_sort_order' === $sort_on) {
62 113 uasort($optimizations, array($this, 'sort_optimizations_run_traditional'));
63 114 } else {
64 115 uasort($optimizations, array($this, 'sort_optimizations_ui_traditional'));
65 116 }
@@ -65,28 +116,45 @@
65 116 }
66 117 return $optimizations;
67 118 }
68 119
120 + /**
121 + * Sorts optimizations based on UI order
122 + *
123 + * @param WP_Optimization $a
124 + * @param WP_Optimization $b
125 + * @return int
126 + */
69 127 public function sort_optimizations_ui_traditional($a, $b) {
70 - return $this->sort_optimizations_traditional($a, $b, 'ui_sort_order');
128 + return $this->sort_optimizations_traditional($a, $b);
71 129 }
72 130
131 + /**
132 + * Sorts optimizations based on Run order
133 + *
134 + * @param WP_Optimization $a
135 + * @param WP_Optimization $b
136 + * @return int
137 + */
73 138 public function sort_optimizations_run_traditional($a, $b) {
74 139 return $this->sort_optimizations_traditional($a, $b, 'run_sort_order');
75 140 }
76 141
142 + /**
143 + * Sorting list of optimization based on given order
144 + *
145 + * @param WP_Optimization $a
146 + * @param WP_Optimization $b
147 + * @param string $sort_on
148 + * @return int
149 + */
77 150 public function sort_optimizations_traditional($a, $b, $sort_on = 'ui_sort_order') {
78 -
79 - if (!is_a($a, 'WP_Optimization')) return (!is_a($b, 'WP_Optimization')) ? 0 : 1;
80 - if (!is_a($b, 'WP_Optimization')) return -1;
81 -
82 151 $sort_order_a = empty($a->$sort_on) ? 0 : $a->$sort_on;
83 152 $sort_order_b = empty($b->$sort_on) ? 0 : $b->$sort_on;
84 153
85 - if ($sort_order_a == $sort_order_b) return 0;
154 + if ($sort_order_a === $sort_order_b) return 0;
86 155
87 - return ($sort_order_a < $sort_order_b) ? (-1) : 1;
88 -
156 + return ($sort_order_a < $sort_order_b) ? -1 : 1;
89 157 }
90 158
91 159 /**
92 160 * This method returns an array of available optimizations.
@@ -92,9 +160,9 @@
92 160 * This method returns an array of available optimizations.
93 161 * Each array key is an optimization ID, and the value is an object,
94 162 * as returned by get_optimization()
95 163 *
96 - * @return [array] array of optimizations or WP_Error objects
164 + * @return array array of optimizations
97 165 */
98 166 public function get_optimizations() {
99 167
100 168 $optimizations = $this->get_optimizations_list();
@@ -109,9 +177,10 @@
109 177 $optimization_objects[$optimization] = $optimization_object;
110 178 }
111 179 }
112 180
113 - return apply_filters('wp_optimize_get_optimizations', $optimization_objects);
181 + $filtered_optimizations = apply_filters('wp_optimize_get_optimizations', $optimization_objects);
182 + return is_array($filtered_optimizations) ? $filtered_optimizations : $optimization_objects;
114 183
115 184 }
116 185
117 186 /**
@@ -116,11 +185,11 @@
116 185
117 186 /**
118 187 * This method returns an object for a specific optimization.
119 188 *
120 - * @param string $which_optimization An optimization ID.
121 - * @param array $data An array of anny options $data.
122 - * @return array WP_Error Will return the optimization, or a WP_Error object if it was not found.
189 + * @param string|object $which_optimization An optimization ID.
190 + * @param array $data An array of anny options $data.
191 + * @return array|WP_Error WP_Error Will return the optimization, or a WP_Error object if it was not found.
123 192 */
124 193 public function get_optimization($which_optimization, $data = array()) {
125 194
126 195 $optimization_class = apply_filters('wp_optimize_optimization_class', 'WP_Optimization_'.$which_optimization);
@@ -156,9 +225,9 @@
156 225 /**
157 226 * The method to call to perform an optimization.
158 227 *
159 228 * @param string|object $which_optimization An optimization ID, or a WP_Optimization object.
160 - * @return array Array of results from the optimization.
229 + * @return object Array of results from the optimization.
161 230 */
162 231 public function do_optimization($which_optimization) {
163 232
164 233 $optimization = (is_object($which_optimization) && is_a($which_optimization, 'WP_Optimization')) ? $which_optimization : $this->get_optimization($which_optimization);
@@ -201,9 +270,9 @@
201 270 * The method to call to get information about an optimization.
202 271 * As with do_optimization, it is somewhat modelled after the template interface
203 272 *
204 273 * @param string|object $which_optimization An optimization ID, or a WP_Optimization object.
205 - * @return array returns the optimization information
274 + * @return object returns the optimization information
206 275 */
207 276 public function get_optimization_info($which_optimization) {
208 277
209 278 $optimization = (is_object($which_optimization) && is_a($which_optimization, 'WP_Optimization')) ? $which_optimization : $this->get_optimization($which_optimization);
@@ -224,14 +293,13 @@
224 293 $optimization->get_info();
225 294 }
226 295
227 296 $optimization->after_get_info();
228 -
229 297 return $optimization->get_results();
230 298 }
231 299
232 300 /**
233 - * THis runs the list of optimizations.
301 + * This runs the list of optimizations.
234 302 *
235 303 * @param array $optimization_options Whether to do an optimization depends on what keys are set (legacy - can be changed hopefully).
236 304 * @param string $which_option Specify which option.
237 305 * @return array Returns an array of result objects.
@@ -240,9 +308,11 @@
240 308
241 309 $results = array();
242 310
243 311 if (empty($optimization_options)) return $results;
244 -
312 +
313 + $this->snapshot_cleaned_before_run();
314 +
245 315 $optimizations = $this->sort_optimizations($this->get_optimizations(), 'run_sort_order');
246 316
247 317 foreach ($optimizations as $optimization_id => $optimization) {
248 318 $option_id = call_user_func(array($optimization, 'get_'.$which_option.'_id'));
@@ -248,11 +318,11 @@
248 318 $option_id = call_user_func(array($optimization, 'get_'.$which_option.'_id'));
249 319
250 320 if (isset($optimization_options[$option_id])) {
251 321 // if options saved as a string then compare with string (for support different versions)
252 - if (is_string($optimization_options[$option_id]) && 'false' === $optimization_options[$option_id]) continue;
322 + if ('false' === $optimization_options[$option_id]) continue;
253 323
254 - if ('auto' == $which_option && empty($optimization->available_for_auto)) continue;
324 + if ('auto' === $which_option && empty($optimization->available_for_auto)) continue;
255 325
256 326 WP_Optimize()->change_time_limit();
257 327
258 328 $results[$optimization_id] = $this->do_optimization($optimization);
@@ -258,8 +328,9 @@
258 328 $results[$optimization_id] = $this->do_optimization($optimization);
259 329 }
260 330 }
261 331
332 + $this->save_last_run_cleaned();
262 333 // Run action after all optimizations completed.
263 334 do_action('wp_optimize_after_optimizations');
264 335
265 336 return $results;
@@ -264,13 +335,33 @@
264 335
265 336 return $results;
266 337
267 338 }
339 +
340 + /**
341 + * Saves a snapshot of total-cleaned before a run so the per-run diff can be computed afterwards.
342 + */
343 + public function snapshot_cleaned_before_run() {
344 + $options = WP_Optimize()->get_options();
345 + $options->update_option('wpo_last_run_cleaned_snapshot', $options->get_option('total-cleaned', '0'));
346 + }
347 +
348 + /**
349 + * Computes bytes cleaned in the last run (current total minus pre-run snapshot)
350 + * and persists it as last-run-cleaned-bytes.
351 + */
352 + public function save_last_run_cleaned() {
353 + $options = WP_Optimize()->get_options();
354 + $snapshot = floatval($options->get_option('wpo_last_run_cleaned_snapshot', '0'));
355 + $now = floatval($options->get_option('total-cleaned', '0'));
356 + $last_run_bytes = strval(max(0, $now - $snapshot));
357 + $options->update_option('last-run-cleaned-bytes', $last_run_bytes);
358 + }
268 359
269 360 public function get_table_prefix($allow_override = false) {
270 361 $wpdb = $GLOBALS['wpdb'];
271 362 if (is_multisite() && !defined('MULTISITE')) {
272 - // In this case (which should only be possible on installs 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.
363 + // 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.
273 364 $prefix = $wpdb->base_prefix;
274 365 } else {
275 366 $prefix = $wpdb->get_blog_prefix(0);
276 367 }
@@ -277,8 +368,60 @@
277 368 return ($allow_override) ? apply_filters('wp_optimize_get_table_prefix', $prefix) : $prefix;
278 369 }
279 370
280 371 /**
372 + * Verify if the number of tables in the database is too large
373 + *
374 + * @param int $total_table_count Passed by reference. Will carry the actual count value
375 + * @return bool
376 + */
377 + public function is_table_count_too_high(&$total_table_count = null) {
378 + $wpo_db_info = WP_Optimize()->get_db_info();
379 + $table_prefix = $this->get_table_prefix();
380 +
381 + $total_table_count = $wpo_db_info->get_table_count($table_prefix);
382 + return self::HIGH_TABLE_COUNT_THRESHOLD < $total_table_count;
383 + }
384 +
385 + /**
386 + * Check the scan job state in options
387 + *
388 + * @return bool
389 + */
390 + public function is_large_table_count_scan_complete() {
391 + $options = WP_Optimize()->get_options();
392 +
393 + $state = $options->get_option(self::TABLES_LIST_STATE_OPTION_NAME);
394 +
395 + if (is_array($state) && isset($state['state'])) {
396 + return 'done' == $state['state'];
397 + }
398 +
399 + return false;
400 + }
401 +
402 + /**
403 + * Get the status for all scanned tables
404 + *
405 + * @return array
406 + */
407 + private function get_table_status_from_options() {
408 + $options = WP_Optimize()->get_options();
409 + $tables = $options->get_option(WP_Optimizer::TABLES_LIST_OPTION_NAME);
410 + return is_array($tables) ? $tables : array();
411 + }
412 +
413 + /**
414 + * Get the count of the already scanned tables
415 + *
416 + * @return int
417 + */
418 + public function get_table_status_count() {
419 + $statuses = $this->get_table_status_from_options();
420 + return is_array($statuses) ? count($statuses) : 0;
421 + }
422 +
423 + /**
281 424 * Returns information about database tables.
282 425 *
283 426 * @param bool $update refresh or no cached data
284 427 *
@@ -290,10 +433,18 @@
290 433 if (false === $update && null !== $tables_info) return $tables_info;
291 434
292 435 $wpo_db_info = WP_Optimize()->get_db_info();
293 436
294 - $table_status = WP_Optimize()->get_db_info()->get_show_table_status($update);
437 + $too_many_tables = $this->is_table_count_too_high();
438 + $scan_complete = $this->is_large_table_count_scan_complete();
295 439
440 + $table_status = array();
441 + if (!$too_many_tables) {
442 + $table_status = $wpo_db_info->get_show_table_status($update);
443 + } elseif ($scan_complete) {
444 + $table_status = $this->get_table_status_from_options();
445 + }
446 +
296 447 // Filter on the site's DB prefix (was not done in releases up to 1.9.1).
297 448 $table_prefix = $this->get_table_prefix();
298 449
299 450 if (is_array($table_status)) {
@@ -298,8 +449,9 @@
298 449
299 450 if (is_array($table_status)) {
300 451
301 452 $corrupted_tables_count = 0;
453 + $array_index_was_unset = false;
302 454
303 455 foreach ($table_status as $index => $table) {
304 456 $table_name = $table->Name;
305 457
@@ -308,17 +460,23 @@
308 460 $include_table = apply_filters('wp_optimize_get_tables_include_table', $include_table, $table_name, $table_prefix);
309 461
310 462 if (!$include_table && '' !== $table_prefix) {
311 463 unset($table_status[$index]);
464 + $array_index_was_unset = true;
312 465 continue;
313 466 }
314 467
315 - $table_status[$index]->Engine = $wpo_db_info->get_table_type($table_name);
468 + // Pass the already-fetched $table object through so these lookups don't have to
469 + // re-search for it by name again (that search is O(n), and doing it here for every
470 + // table turns the whole loop into O(n^2) on sites with a large table count).
471 + $table_type = $wpo_db_info->get_table_type($table_name, $table);
316 472
317 - $table_status[$index]->is_optimizable = $wpo_db_info->is_table_optimizable($table_name);
318 - $table_status[$index]->is_type_supported = $wpo_db_info->is_table_type_optimize_supported($table_name);
473 + $table_status[$index]->Engine = $table_type;
474 +
475 + $table_status[$index]->is_optimizable = $wpo_db_info->is_table_optimizable($table_name, $table_type);
476 + $table_status[$index]->is_type_supported = $wpo_db_info->is_table_type_optimize_supported($table_name, $table_type);
319 477 // add information about corrupted tables.
320 - $is_needing_repair = $wpo_db_info->is_table_needing_repair($table_name);
478 + $is_needing_repair = $wpo_db_info->is_table_needing_repair($table_name, $table_type);
321 479 $table_status[$index]->is_needing_repair = $is_needing_repair;
322 480 if ($is_needing_repair) $corrupted_tables_count++;
323 481
324 482 $table_status[$index] = $this->join_plugin_information($table_name, $table_status[$index]);
@@ -324,8 +482,12 @@
324 482 $table_status[$index] = $this->join_plugin_information($table_name, $table_status[$index]);
325 483
326 484 $table_status[$index]->blog_id = $wpo_db_info->get_table_blog_id($table_name);
327 485 }
486 + if ($array_index_was_unset) {
487 + // Reset array keys after removing elements
488 + $table_status = array_values($table_status);
489 + }
328 490
329 491 WP_Optimize()->get_options()->update_option('corrupted-tables-count', $corrupted_tables_count);
330 492 }
331 493
@@ -335,10 +497,10 @@
335 497
336 498 /**
337 499 * Returns information about single table by table name.
338 500 *
339 - * @param String $table_name table name
340 - * @return Object|Boolean table information object.
501 + * @param string $table_name table name
502 + * @return object|boolean table information object.
341 503 */
342 504 public function get_table($table_name) {
343 505
344 506 $db_info = WP_Optimize()->get_db_info();
@@ -360,14 +522,14 @@
360 522
361 523 /**
362 524 * Add information about relationship database tables with plugins.
363 525 *
364 - * @param {string} $table_name
365 - * @param {object} $table_obj
526 + * @param string $table_name
527 + * @param object $table_obj
366 528 *
367 - * @return {object}
529 + * @return object
368 530 */
369 - public function join_plugin_information($table_name, $table_obj) {
531 + private function join_plugin_information($table_name, $table_obj) {
370 532 // set can be removed flag.
371 533 $can_be_removed = false;
372 534 // set WP core table flag.
373 535 $wp_core_table = false;
@@ -372,22 +534,29 @@
372 534 // set WP core table flag.
373 535 $wp_core_table = false;
374 536 // set WP actionscheduler table flag.
375 537 $wp_actionscheduler_table = (false !== stripos($table_name, 'actionscheduler_'));
376 - // add information about using table by any of installed plugins.
377 - $table_obj->is_using = WP_Optimize()->get_db_info()->is_table_using_by_plugin($table_name);
378 538 // if table belongs to any plugin then add plugins status.
379 539 $plugins = WP_Optimize()->get_db_info()->get_table_plugin($table_name);
380 540
541 + if (false === $plugins) {
542 + // if we can't determine which plugin use $table then mark table as used.
543 + $table_obj->is_using = true;
544 + } else {
545 + // if any plugin is installed which use table - $table_name then mark table as used.
546 + $table_obj->is_using = WP_Optimize()->get_db_info()->is_any_plugin_installed($plugins);
547 + }
548 +
549 + $plugin_status = array();
550 +
381 551 if (false !== $plugins) {
382 552 // if belongs to any of plugin then we can remove table if plugin not active.
383 553 $can_be_removed = true;
384 554
385 - $plugin_status = array();
386 555 foreach ($plugins as $plugin) {
387 556 $status = WP_Optimize()->get_db_info()->get_plugin_status($plugin);
388 557
389 - if (__('WordPress core', 'wp-optimize') == $plugin) $wp_core_table = true;
558 + if (__('WordPress core', 'wp-optimize') === $plugin) $wp_core_table = true;
390 559 // if plugin is active then we can't remove.
391 560 if ($wp_core_table || $status['active'] || $wp_actionscheduler_table) $can_be_removed = false;
392 561
393 562 if ($status['installed'] || $status['active'] || !$table_obj->is_using) {
@@ -396,12 +565,11 @@
396 565 'status' => $status,
397 566 );
398 567 }
399 568 }
400 -
401 - $table_obj->plugin_status = $plugin_status;
402 569 }
403 570
571 + $table_obj->plugin_status = $plugin_status;
404 572 $table_obj->wp_core_table = $wp_core_table;
405 573 $table_obj->can_be_removed = $can_be_removed;
406 574
407 575 return $table_obj;
@@ -413,9 +581,9 @@
413 581 * the results to optimizations-table.php and optimizationstable.php
414 582 *
415 583 * @param int $blog_id filter tables by prefix
416 584 *
417 - * @return Array - an array of data such as table list, innodb info and data free
585 + * @return array - an array of data such as table list, innodb info and data free
418 586 */
419 587 public function get_table_information($blog_id = 0) {
420 588 // Get table information.
421 589 $tablesstatus = $this->get_tables();
@@ -427,12 +595,20 @@
427 595 $table_information['non_inno_db_tables'] = 0;
428 596 $table_information['table_list'] = '';
429 597 $table_information['is_optimizable'] = true;
430 598
599 + // When too many tables exist and the batch scan hasn't completed yet, get_tables() returns an empty array.
600 + // Fall back to a lightweight COUNT query so the UI shows the real table count instead of zero.
601 + if (empty($tablesstatus) && $this->is_table_count_too_high() && !$this->is_large_table_count_scan_complete()) {
602 + $prefix = $this->get_table_prefix();
603 + $table_information['non_inno_db_tables'] = (int) WP_Optimize()->get_db_info()->get_table_count($prefix);
604 + return $table_information;
605 + }
606 +
431 607 // Make a list of tables to optimize.
432 608 foreach ($tablesstatus as $each_table) {
433 609 // if $blog_id is set then filter tables
434 - if ($blog_id > 0 && $blog_id != $each_table->blog_id) continue;
610 + if ($blog_id > 0 && $blog_id !== $each_table->blog_id) continue;
435 611
436 612 $table_information['table_list'] .= $each_table->Name.'|';
437 613
438 614 // check if table type supported.
@@ -446,9 +622,9 @@
446 622 // calculate total gain value.
447 623 $table_information['total_gain'] += $each_table->Data_free;
448 624
449 625 // count InnoDB tables.
450 - if ('InnoDB' == $each_table->Engine) {
626 + if ('InnoDB' === $each_table->Engine) {
451 627 $table_information['inno_db_tables']++;
452 628 } else {
453 629 $table_information['non_inno_db_tables']++;
454 630 }
@@ -471,15 +647,27 @@
471 647 $new_status = $enable ? 'open' : 'closed';
472 648
473 649 switch ($type) {
474 650 case "trackbacks":
475 - $thissql = "UPDATE `".$wpdb->posts."` SET ping_status='".$new_status."' WHERE post_status = 'publish' AND post_type = 'post';";
476 - $wpdb->query($thissql);
651 + $wpdb->query(
652 + $wpdb->prepare(
653 + "UPDATE `{$wpdb->posts}` SET ping_status = %s WHERE post_status = %s AND post_type = %s",
654 + $new_status,
655 + 'publish',
656 + 'post'
657 + )
658 + );
477 659 break;
478 660
479 661 case "comments":
480 - $thissql = "UPDATE `".$wpdb->posts."` SET comment_status='".$new_status."' WHERE post_status = 'publish' AND post_type = 'post';";
481 - $wpdb->query($thissql);
662 + $wpdb->query(
663 + $wpdb->prepare(
664 + "UPDATE `{$wpdb->posts}` SET comment_status = %s WHERE post_status = %s AND post_type = %s",
665 + $new_status,
666 + 'publish',
667 + 'post'
668 + )
669 + );
482 670 break;
483 671
484 672 default:
485 673 break;
@@ -490,10 +678,10 @@
490 678
491 679 /**
492 680 * This function will return total database size and a possible gain of db in KB.
493 681 *
494 - * @param boolean $update - Wether to update the values or not
495 - * @return string total db size gained.
682 + * @param boolean $update - Whether to update the values or not
683 + * @return array total db size gained.
496 684 */
497 685 public function get_current_db_size($update = false) {
498 686
499 687 if (!$update && $db_size = get_transient('wpo_get_current_db_size')) {
@@ -516,9 +704,9 @@
516 704 $total_gain += $tablestatus->Data_free;
517 705 $data_usage += $tablestatus->Data_length;
518 706 $index_usage += $tablestatus->Index_length;
519 707
520 - if ('InnoDB' != $tablestatus->Engine) {
708 + if ('InnoDB' !== $tablestatus->Engine) {
521 709 $overhead_usage += $tablestatus->Data_free;// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $overhead_usage declared up above
522 710 $total_gain += $tablestatus->Data_free;
523 711 }
524 712 }
@@ -545,12 +733,27 @@
545 733 $total_now = strval($previously_saved + $converted_current);
546 734
547 735 $options->update_option('total-cleaned', $total_now);
548 736
737 + $previously_saved = floatval($options->get_option('total-cleaned-current-month', '0'));
738 +
739 + $total_now = strval($previously_saved + $converted_current);
740 +
741 + $options->update_option('total-cleaned-current-month', $total_now);
742 +
743 + $previously_saved = floatval($options->get_option('db_bytes_saved', '0'));
744 + $options->update_option('db_bytes_saved', strval($previously_saved + $converted_current));
745 +
549 746 return $total_now;
550 747 }
551 748
552 749
750 + /**
751 + * Trackback, Comments actions
752 + *
753 + * @param array $options
754 + * @return array
755 + */
553 756 public function trackback_comment_actions($options) {
554 757
555 758 $output = array();
556 759 $messages = array();
@@ -558,12 +761,14 @@
558 761 if (isset($options['comments'])) {
559 762 if (!$options['comments']) {
560 763 $this->enable_linkbacks('comments', false);
561 764 $output[] = __('Comments have now been disabled on all current and previously published posts.', 'wp-optimize');
765 + // translators: %s is a date and time
562 766 $messages[] = sprintf(__('All comments on existing posts were disabled at %s.', 'wp-optimize'), WP_Optimize()->format_date_time(time()));
563 767 } else {
564 768 $this->enable_linkbacks('comments');
565 769 $output[] = __('Comments have now been enabled on all current and previously published posts.', 'wp-optimize');
770 + // translators: %s is a date and time
566 771 $messages[] = sprintf(__('All comments on existing posts were enabled at %s.', 'wp-optimize'), WP_Optimize()->format_date_time(time()));
567 772 }
568 773 }
569 774
@@ -570,12 +775,14 @@
570 775 if (isset($options['trackbacks'])) {
571 776 if (!$options['trackbacks']) {
572 777 $this->enable_linkbacks('trackbacks', false);
573 778 $output[] = __('Trackbacks have now been disabled on all current and previously published posts.', 'wp-optimize');
779 + // translators: %s is a date and time
574 780 $messages[] = sprintf(__('All trackbacks on existing posts were disabled at %s.', 'wp-optimize'), WP_Optimize()->format_date_time(time()));
575 781 } else {
576 782 $this->enable_linkbacks('trackbacks');
577 783 $output[] = __('Trackbacks have now been enabled on all current and previously published posts.', 'wp-optimize');
784 + // translators: %s is a date and time
578 785 $messages[] = sprintf(__('All trackbacks on existing posts were enabled at %s.', 'wp-optimize'), WP_Optimize()->format_date_time(time()));
579 786 }
580 787 }
581 788
@@ -582,9 +789,9 @@
582 789 return array('output' => $output,'messages' => $messages);
583 790 }
584 791
585 792 /**
586 - * Wether InnoDB tables require confirmation to be optimized
793 + * Whether InnoDB tables require confirmation to be optimized
587 794 *
588 795 * @return boolean
589 796 */
590 797 public function show_innodb_force_optimize() {
@@ -589,6 +796,140 @@
589 796 */
590 797 public function show_innodb_force_optimize() {
591 798 $tablesstatus = $this->get_table_information();
592 799 return false === $tablesstatus['is_optimizable'] && $tablesstatus['inno_db_tables'] > 0;
800 + }
801 +
802 + /**
803 + * Queue handler for this class
804 + *
805 + * @return WP_Optimize_Tasks_Queue
806 + */
807 + private function _tasks_queue() {
808 + return WP_Optimize_Tasks_Queue::this('optimizer');
809 + }
810 +
811 + /**
812 + * Add a batch scan job to the queue
813 + *
814 + * @param int $offset Batch offset
815 + * @return void
816 + */
817 + public function add_load_tables_task($offset) {
818 + $this->_tasks_queue()->add_task(new WP_Optimize_Queue_Task(array(get_class($this), 'load_tables_in_batches'), array('offset' => $offset), '', 1));
819 + }
820 +
821 + /**
822 + * Persist queue tasks in DB
823 + *
824 + * @return void
825 + */
826 + public function flush_tasks() {
827 + $this->_tasks_queue()->flush();
828 + }
829 +
830 + /**
831 + * Run next available task for this queue
832 + *
833 + * @return void
834 + */
835 + public function do_next_load_table_task() {
836 + $this->_tasks_queue()->do_next_task();
837 + }
838 +
839 + /**
840 + * Check if the queue ran all jobs
841 + *
842 + * @return bool
843 + */
844 + public function is_task_queue_empty() {
845 + return $this->_tasks_queue()->is_empty();
846 + }
847 +
848 + /**
849 + * Find tables names and load their status, in sequenced requests so it does not timeout
850 + *
851 + * @param int $offset The pagination offset
852 + * @return void
853 + */
854 + public function load_tables_in_batches($offset) {
855 + global $wpdb;
856 +
857 + $prefix = $wpdb->base_prefix;
858 +
859 + $options = WP_Optimize()->get_options();
860 +
861 + $db_info = WP_Optimize_Database_Information::instance();
862 +
863 + $tables = $options->get_option(WP_Optimizer::TABLES_LIST_OPTION_NAME);
864 + $tables = is_array($tables) ? $tables : array();
865 + $tables_count = $options->get_option(WP_Optimizer::TABLES_LIST_COUNT_OPTION_NAME);
866 + $tables_count = false !== $tables_count ? $tables_count : 0;
867 +
868 + $tables_batch = $db_info->get_tables_names($prefix, $offset, WP_Optimizer::HIGH_TABLE_COUNT_THRESHOLD);
869 +
870 +
871 + foreach ($tables_batch as $table_name) {
872 + $tables[$table_name] = $db_info->get_table_status($table_name);
873 + }
874 +
875 + $options->update_option(WP_Optimizer::TABLES_LIST_OPTION_NAME, $tables, false);
876 +
877 + $done = 0 < count($tables_batch) && count($tables) >= (int) $tables_count;
878 + if (true === $done) {
879 + $options->update_option(WP_Optimizer::TABLES_LIST_STATE_OPTION_NAME, array('state' => 'done'));
880 + }
881 + }
882 +
883 + /**
884 + * Initialize the table scan job
885 + *
886 + * @return int
887 + */
888 + public function scan_job_init() {
889 + global $wpdb;
890 +
891 + $options = WP_Optimize()->get_options();
892 + $db_info = WP_Optimize_Database_Information::instance();
893 +
894 + $this->_tasks_queue()->unlock();
895 + $this->_tasks_queue()->delete_queue();
896 +
897 + $prefix = $wpdb->base_prefix;
898 + $tables_count = $db_info->get_table_count($prefix);
899 + $options->update_option(self::TABLES_LIST_COUNT_OPTION_NAME, $tables_count);
900 + $options->update_option(self::TABLES_LIST_OPTION_NAME, array(), false);
901 + $options->update_option(self::TABLES_LIST_STATE_OPTION_NAME, array(
902 + 'state' => 'working',
903 + 'start' => time()
904 + ));
905 +
906 + return $tables_count;
907 + }
908 +
909 + /**
910 + * Check if all scan is done, or it has been idle for too long
911 + *
912 + * @return bool
913 + */
914 + public function maybe_restart_scan_job() {
915 + if ($this->is_large_table_count_scan_complete()) {
916 + return false;
917 + }
918 +
919 + if ($this->_tasks_queue()->is_empty()) {
920 + return true;
921 + }
922 +
923 + $options = WP_Optimize()->get_options();
924 +
925 + $idle_max_time = HOUR_IN_SECONDS;
926 +
927 + $scan = $options->get_option(self::TABLES_LIST_STATE_OPTION_NAME);
928 +
929 + if (is_array($scan) && isset($scan['start'])) {
930 + return (time() - $scan['start']) > $idle_max_time;
931 + }
932 +
933 + return false;
593 934 }
594 935 }