bibhamrokhanpin/wp-content/__plugins/wp-optimize/includes/wp-optimize-database-information.php
2024-04-10 17:46:06 +05:45

628 lines
16 KiB
PHP

<?php
if (!defined('ABSPATH')) die('No direct access allowed');
class WP_Optimize_Database_Information {
const UNKNOWN_DB = 'unknown';
const MARIA_DB = 'MariaDB';
const PERCONA_DB = 'Percona';
// for some reason coding standard parser give error here WordPress.DB.RestrictedFunctions.mysql_mysql_db
const MYSQL_DB = 'MysqlDB';
const MYISAM_ENGINE = 'MyISAM';
const MEMORY_ENGINE = 'Memory';
const INNODB_ENGINE = 'InnoDB';
const ARCHIVE_ENGINE = 'ARCHIVE';
const CSV_ENGINE = 'CSV';
const NDB_ENGINE = 'NDB';
const ARIA_ENGINE = 'Aria'; // MariaDB
const VIEW = 'VIEW';
/**
* Returns server type MySQL or MariaDB if mysql database or Unknown if not mysql.
*
* @return string
*/
public function get_server_type() {
global $wpdb;
static $server_type = null;
if (!$wpdb->is_mysql) return self::UNKNOWN_DB;
if (null !== $server_type) return $server_type;
$server_type = self::MYSQL_DB;
$variables = $wpdb->get_results('SHOW SESSION VARIABLES LIKE "version%"');
if (!empty($variables)) {
foreach ($variables as $variable) {
if (preg_match('/mariadb/i', $variable->Value)) {
$server_type = self::MARIA_DB;
}
if (preg_match('/percona/i', $variable->Value)) {
$server_type = self::PERCONA_DB;
}
}
}
return $server_type;
}
/**
* Returns database server version
*
* @return string|bool
*/
public function get_version() {
$version = $this->get_option_value('version');
if (!empty($version)) {
if (preg_match('/^(\d+)(\.\d+)+/', $version, $match)) {
return $match[0];
}
}
return false;
}
/**
* Return table type by $table_name.
*
* @param String $table_name Database table name.
* @return String|Boolean - returns false upon failure
*/
public function get_table_type($table_name) {
$table_info = $this->get_table_status($table_name);
if ($table_info) {
if (!$table_info->Engine && $this->is_view($table_name)) return self::VIEW;
return $table_info->Engine;
}
return false;
}
/**
* Returns information about database table.
*
* @param string $table_name
* @param bool $update if true, then force request to database and don't use cached values.
* @return bool|mixed
*/
public function get_table_status($table_name, $update = false) {
$tables_info = $this->get_show_table_status($update, $table_name);
foreach ($tables_info as $table_info) {
if ($table_name == $table_info->Name) return $table_info;
}
return false;
}
/**
* Returns result for query SHOW TABLE STATUS.
*
* @param bool $update refresh or no cached data
* @return array
*/
public function get_show_table_status($update = false, $table_name = '') {
global $wpdb;
static $tables_info = array();
static $fetched_all_tables = false;
// If a table name is provided, and the whole record hasn't been fetched yet, only fetch the information for the current table.
// This allows for a big preformance gain when using WP-CLI or doing single optimizations.
if ($table_name && !$fetched_all_tables) {
$sql = $wpdb->prepare("SHOW TABLE STATUS LIKE '%s'", $table_name);
$tables_info = $wpdb->get_results($sql);
} else {
if ($update || empty($tables_info) || !is_array($tables_info) || !$fetched_all_tables) {
$tables_info = $wpdb->get_results('SHOW TABLE STATUS');
$fetched_all_tables = true;
}
}
// If option innodb_file_per_table is disabled then Data_free column will have summary overhead value for all table.
if (!empty($tables_info)) {
foreach ($tables_info as $i => $table) {
if (self::INNODB_ENGINE == $table->Engine && false == $this->is_option_enabled('innodb_file_per_table')) {
$tables_info[$i]->Data_free = 0;
}
}
}
return $tables_info;
}
/**
* Whether a table exists
*
* @return boolean
*/
public function table_exists($table_name, $use_default_prefix = true) {
global $wpdb;
return null !== $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $wpdb->esc_like($use_default_prefix ? $wpdb->prefix.$table_name : $table_name)));
}
/**
* Returns result for query SHOW FULL TABLES as associative array [table_name] => table_type.
*
* @return array
*/
public function get_show_full_tables() {
global $wpdb;
static $tables_info = array();
if (empty($tables_info) || !is_array($tables_info)) {
$_tables_info = $wpdb->get_results('SHOW FULL TABLES', ARRAY_N);
if (!empty($_tables_info)) {
foreach ($_tables_info as $row) {
$tables_info[$row[0]] = $row[1];
}
}
}
return $tables_info;
}
/**
* Checks if table is a VIEW.
*
* @param string $table_name
* @return bool
*/
public function is_view($table_name) {
$tables_info = $this->get_show_full_tables();
if (!array_key_exists($table_name, $tables_info)) return false;
return ('VIEW' == $tables_info[$table_name]);
}
/**
* Returns true if DDL supported.
*
* @return bool
*/
public function has_online_ddl() {
if (self::MYSQL_DB == $this->get_server_type()) {
if (version_compare($this->get_version(), '5.7', '>=')) {
return true;
} else {
return false;
}
} elseif (self::MARIA_DB == $this->get_server_type()) {
if (version_compare($this->get_version(), '10.0.0', '>=')) {
return true;
} else {
return false;
}
}
return false;
}
/**
* Returns database option variable
*
* @param string $option_name Name of database option.
* @return mixed|null
*/
public function get_option_value($option_name) {
global $wpdb;
static $options = array();
if (array_key_exists($option_name, $options)) return $options[$option_name];
$option = $wpdb->get_row(
$wpdb->prepare('SHOW SESSION VARIABLES LIKE %s', $option_name)
);
if (!empty($option)) {
$options[$option_name] = $option->Value;
return $option->Value;
}
return null;
}
/**
* Returns true if database option $option_name
*
* @param string $option_name Name of database option name.
* @return bool
*/
public function is_option_enabled($option_name) {
$option_value = $this->get_option_value($option_name);
if ('ON' == strtoupper($option_value)) return true;
return false;
}
/**
* Returns true if table $table_name is optimizable
*
* @param string $table_name Name of database table
* @return bool
*/
public function is_table_optimizable($table_name) {
$server_type = $this->get_server_type();
$server_version = $this->get_version();
$table_type = $this->get_table_type($table_name);
// return true if table is MyISAM.
if (self::MYISAM_ENGINE == $table_type) return true;
// return true if table is Archive or Aria.
if (self::ARCHIVE_ENGINE == $table_type || self::ARIA_ENGINE == $table_type) return true;
// if InnoDB then check if we can optimize.
if (self::INNODB_ENGINE == $table_type) {
// check for MysqlDB.
if (self::MYSQL_DB == $server_type && $this->has_online_ddl()) {
return true;
}
// check for MariaDB.
if (self::MARIA_DB == $server_type) {
// if innodb_file_per_table enabled or version not older than 10.1.1 and innodb_defragment enabled.
if ($this->is_option_enabled('innodb_file_per_table') || (version_compare($server_version, '10.1.1', '>=') && $this->is_option_enabled('innodb_defragment'))) {
return true;
}
}
}
// otherwise return false.
return false;
}
/**
* Returns true if table type is supported for optimization.
*
* @param string $table_name Name of database table
* @return bool
*/
public function is_table_type_optimize_supported($table_name) {
$table_type = $this->get_table_type($table_name);
$supported_table_types = array(
self::MYISAM_ENGINE,
self::INNODB_ENGINE,
self::ARCHIVE_ENGINE,
self::ARIA_ENGINE,
);
return in_array($table_type, $supported_table_types);
}
/**
* Returns true if table type is supported for repair.
*
* @param string $table_name
* @return bool
*/
public function is_table_type_repair_supported($table_name) {
$table_type = $this->get_table_type($table_name);
$supported_table_types = array(
self::MYISAM_ENGINE,
self::ARCHIVE_ENGINE,
self::CSV_ENGINE,
);
return in_array($table_type, $supported_table_types);
}
/**
* Run CHECK TABLE query and returns statuses for single or list of tables.
*
* @param array|string $table
*/
public function check_table($table) {
global $wpdb;
if (is_array($table)) {
$table = join('`,`', $table);
}
$result = array();
if (empty($table)) return $result;
$query_result = $wpdb->get_results('CHECK TABLE `'.$table.'`;');
if (empty($query_result)) return $result;
foreach ($query_result as $row) {
$table_name_parts = explode('.', rtrim($row->Table, ' .'));
$table_name = array_pop($table_name_parts);
if (!array_key_exists($table_name, $result)) {
$result[$table_name] = array(
'status' => '',
'corrupted' => false,
);
}
if ('error' == $row->Msg_type) {
$result[$table_name]['status'] = $row->Msg_type;
if (preg_match('/corrupt/i', $row->Msg_text)) {
$result[$table_name]['corrupted'] = true;
} else {
$result[$table_name]['message'] = $row->Msg_text;
}
}
if ('status' == $row->Msg_type) {
$result[$table_name]['status'] = $row->Msg_text;
}
}
return $result;
}
/**
* Check all supported for repair tables and return statuses for them.
*
* @return array
*/
public function check_all_tables() {
static $result = null;
if (null !== $result) return $result;
$tables = $this->get_show_table_status();
$supported_tables = array();
foreach ($tables as $table) {
if ('' == $table->Engine || $this->is_table_type_repair_supported($table->Name)) {
$supported_tables[] = $table->Name;
}
}
$result = $this->check_table($supported_tables);
return $result;
}
/**
* Returns true if table needing repair.
*
* @param string $table_name Database table name.
*/
public function is_table_needing_repair($table_name) {
$table_statuses = $this->check_all_tables();
if (!$this->is_table_type_repair_supported($table_name)) return false;
if (is_array($table_statuses) && array_key_exists($table_name, $table_statuses) && $table_statuses[$table_name]['corrupted']) {
return true;
} else {
return false;
}
}
/**
* Check if $table using by any of installed plugins.
*
* @param string $table
* @return bool
*/
public function is_table_using_by_plugin($table) {
$plugin_names = $this->get_table_plugin($table);
// if we can't determine which plugin use $table then return true.
if (false == $plugin_names) {
return true;
}
// is WordPress core table or using by any of installed plugins then return true.
foreach ($plugin_names as $plugin_name) {
if (__('WordPress core', 'wp-optimize') == $plugin_name || in_array($plugin_name, $this->get_all_installed_plugins())) {
return true;
}
}
return false;
}
/**
* Get blog_id
*
* @param string $table_name
* @return int
*/
public function get_table_blog_id($table_name) {
global $wpdb;
if (is_multisite()) {
$blogs_ids = wp_list_pluck(WP_Optimize()->get_sites(), 'blog_id');
// if match with base_prefix_(number)_
if (preg_match('/^'.$wpdb->base_prefix.'(\d+)_/', $table_name, $match)) {
// check if matched number in available sites.
if (false !== array_search($match[1], $blogs_ids)) return $match[1];
}
}
return 1;
}
/**
* Get information about relations between tables and plugins. [ 'table' => ['plugin1', 'plugin2', ...], ... ].
*
* @return array
*/
private function get_all_plugin_tables_relationship() {
static $plugin_tables = array();
if (!empty($plugin_tables)) return $plugin_tables;
$wp_core_tables = array(
'blogs',
'blog_versions',
'commentmeta',
'comments',
'links',
'options',
'postmeta',
'posts',
'registration_log',
'signups',
'term_relationships',
'term_taxonomy',
'termmeta',
'terms',
'usermeta',
'users',
'site',
'sitemeta',
);
$plugin_tables_json_file = $this->get_plugin_json_file_path();
$fallback_plugin_tables_json_file = WPO_PLUGIN_MAIN_PATH.'plugin.json';
if (is_file($plugin_tables_json_file) && is_readable($plugin_tables_json_file)) {
// get data from plugin.json file.
$plugin_tables = json_decode(file_get_contents($plugin_tables_json_file), true);
}
// Fallback to the bundled version if the list is empty
if (empty($plugin_tables)) {
if (is_file($fallback_plugin_tables_json_file) && is_readable($fallback_plugin_tables_json_file)) {
// get data from the bundled plugin.json file.
$plugin_tables = json_decode(file_get_contents($fallback_plugin_tables_json_file), true);
}
}
foreach ($wp_core_tables as $table) {
$plugin_tables[$table][] = __('WordPress core', 'wp-optimize');
}
// add WP-Optimize tables.
$plugin_tables['tm_taskmeta'][] = 'wp-optimize';
$plugin_tables['tm_tasks'][] = 'wp-optimize';
return $plugin_tables;
}
/**
* Try to get plugin name by table name and return it or return false if plugin is not defined.
*
* @param string $table
* @return array|bool - array with plugin slugs or false.
*/
public function get_table_plugin($table) {
global $wpdb;
// delete table prefix.
$table = preg_replace('/^'.$wpdb->prefix.'([0-9]+_)?/', '', $table);
$plugins_tables = $this->get_all_plugin_tables_relationship();
if (array_key_exists($table, $plugins_tables)) {
return $plugins_tables[$table];
}
return false;
}
/**
* Get the path where the updated plugin.json is stored
*
* @return string
*/
private function get_plugin_json_file_path() {
$uploads_dir = wp_upload_dir(null, false);
return apply_filters('wpo_get_plugin_json_file_path', trailingslashit($uploads_dir['basedir']).'wpo-plugins-tables-list.json');
}
/**
* Get all installed plugin slugs.
*
* @return array
*/
public function get_all_installed_plugins() {
static $installed_plugins;
if (is_array($installed_plugins)) return $installed_plugins;
$installed_plugins = array();
if (!function_exists('get_plugins')) include_once(ABSPATH.'wp-admin/includes/plugin.php');
$plugins = get_plugins();
foreach ($plugins as $plugin_file => $plugin_data) {
if ('' != $plugin_data['TextDomain']) {
$installed_plugins[] = $plugin_data['TextDomain'];
} else {
$plugin_file_parts = explode('/', $plugin_file);
$installed_plugins[]= $plugin_file_parts[0];
}
}
return $installed_plugins;
}
/**
* Check current plugin status installed/not installed and active/inactive.
*
* @param string $plugin
* @return array - ['installed' => true|false, 'active' => true|false]
*/
public function get_plugin_status($plugin) {
if (!function_exists('get_plugins')) include_once(ABSPATH.'wp-admin/includes/plugin.php');
$plugins = get_plugins();
// return true for wp-optimize without checking.
if ('wp-optimize' == $plugin) {
return array(
'installed' => true,
'active' => true,
);
}
$installed = false;
$active = false;
foreach ($plugins as $plugin_file => $plugin_data) {
$plugin_file_parts = explode('/', $plugin_file);
$plugin_slug = $plugin_file_parts[0];
if ($plugin == $plugin_slug) {
$installed = true;
$active = is_plugin_active($plugin_file);
}
}
return array(
'installed' => $installed,
'active' => $active,
);
}
/**
* Update list in plugin.json, if necessary
*
* @return void
*/
public function update_plugin_json() {
// Add the possibility to turn this off.
if (!apply_filters('wpo_update_plugin_json', true)) return;
$update_request = wp_remote_get('https://plugins.svn.wordpress.org/wp-optimize/trunk/plugin.json', array('timeout' => 3000));
if (200 !== wp_remote_retrieve_response_code($update_request)) return;
$json_content = wp_remote_retrieve_body($update_request);
if (json_decode($json_content)) {
file_put_contents($this->get_plugin_json_file_path(), $json_content);
}
}
}