| @@ -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 | } |