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 +276 -10 4.5.3 → 4.7.0 View file →
@@ -7,8 +7,36 @@
7 7 */
8 8 class WP_Optimizer {
9 9
10 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 + /**
11 39 * Returns singleton instance object
12 40 *
13 41 * @return WP_Optimizer Returns `WP_Optimizer` object
14 42 */
@@ -265,9 +293,8 @@
265 293 $optimization->get_info();
266 294 }
267 295
268 296 $optimization->after_get_info();
269 -
270 297 return $optimization->get_results();
271 298 }
272 299
273 300 /**
@@ -281,9 +308,11 @@
281 308
282 309 $results = array();
283 310
284 311 if (empty($optimization_options)) return $results;
285 -
312 +
313 + $this->snapshot_cleaned_before_run();
314 +
286 315 $optimizations = $this->sort_optimizations($this->get_optimizations(), 'run_sort_order');
287 316
288 317 foreach ($optimizations as $optimization_id => $optimization) {
289 318 $option_id = call_user_func(array($optimization, 'get_'.$which_option.'_id'));
@@ -299,8 +328,9 @@
299 328 $results[$optimization_id] = $this->do_optimization($optimization);
300 329 }
301 330 }
302 331
332 + $this->save_last_run_cleaned();
303 333 // Run action after all optimizations completed.
304 334 do_action('wp_optimize_after_optimizations');
305 335
306 336 return $results;
@@ -305,8 +335,28 @@
305 335
306 336 return $results;
307 337
308 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 + }
309 359
310 360 public function get_table_prefix($allow_override = false) {
311 361 $wpdb = $GLOBALS['wpdb'];
312 362 if (is_multisite() && !defined('MULTISITE')) {
@@ -318,8 +368,60 @@
318 368 return ($allow_override) ? apply_filters('wp_optimize_get_table_prefix', $prefix) : $prefix;
319 369 }
320 370
321 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 + /**
322 424 * Returns information about database tables.
323 425 *
324 426 * @param bool $update refresh or no cached data
325 427 *
@@ -331,10 +433,18 @@
331 433 if (false === $update && null !== $tables_info) return $tables_info;
332 434
333 435 $wpo_db_info = WP_Optimize()->get_db_info();
334 436
335 - $table_status = $wpo_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();
336 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 +
337 447 // Filter on the site's DB prefix (was not done in releases up to 1.9.1).
338 448 $table_prefix = $this->get_table_prefix();
339 449
340 450 if (is_array($table_status)) {
@@ -354,14 +464,19 @@
354 464 $array_index_was_unset = true;
355 465 continue;
356 466 }
357 467
358 - $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);
359 472
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);
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);
362 477 // add information about corrupted tables.
363 - $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);
364 479 $table_status[$index]->is_needing_repair = $is_needing_repair;
365 480 if ($is_needing_repair) $corrupted_tables_count++;
366 481
367 482 $table_status[$index] = $this->join_plugin_information($table_name, $table_status[$index]);
@@ -412,9 +527,9 @@
412 527 * @param object $table_obj
413 528 *
414 529 * @return object
415 530 */
416 - public function join_plugin_information($table_name, $table_obj) {
531 + private function join_plugin_information($table_name, $table_obj) {
417 532 // set can be removed flag.
418 533 $can_be_removed = false;
419 534 // set WP core table flag.
420 535 $wp_core_table = false;
@@ -419,13 +534,19 @@
419 534 // set WP core table flag.
420 535 $wp_core_table = false;
421 536 // set WP actionscheduler table flag.
422 537 $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 538 // if table belongs to any plugin then add plugins status.
426 539 $plugins = WP_Optimize()->get_db_info()->get_table_plugin($table_name);
427 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 +
428 549 $plugin_status = array();
429 550
430 551 if (false !== $plugins) {
431 552 // if belongs to any of plugin then we can remove table if plugin not active.
@@ -474,8 +595,16 @@
474 595 $table_information['non_inno_db_tables'] = 0;
475 596 $table_information['table_list'] = '';
476 597 $table_information['is_optimizable'] = true;
477 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 +
478 607 // Make a list of tables to optimize.
479 608 foreach ($tablesstatus as $each_table) {
480 609 // if $blog_id is set then filter tables
481 610 if ($blog_id > 0 && $blog_id !== $each_table->blog_id) continue;
@@ -610,8 +739,11 @@
610 739 $total_now = strval($previously_saved + $converted_current);
611 740
612 741 $options->update_option('total-cleaned-current-month', $total_now);
613 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 +
614 746 return $total_now;
615 747 }
616 748
617 749
@@ -664,6 +796,140 @@
664 796 */
665 797 public function show_innodb_force_optimize() {
666 798 $tablesstatus = $this->get_table_information();
667 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;
668 934 }
669 935 }