FileMaster
Search
Toggle Dark Mode
Home
/
.
/
wp-content
/
plugins
/
backup-backup
/
includes
/
database
Edit File: better-backup.php
<?php /** * Author: Mikołaj `iClyde` Chodorowski * Contact: kontakt@iclyde.pl * Package: Backup Migration – WP Plugin */ // Namespace namespace BMI\Plugin\Database; // Use use BMI\Plugin\BMI_Logger AS Logger; use BMI\Plugin\Progress\BMI_ZipProgress AS Progress; use BMI\Plugin\Dashboard AS Dashboard; use BMI\Plugin\Backup_Migration_Plugin as BMP; // Exit on direct access if (!defined('ABSPATH')) exit; // echo "Memory usage at the beginning: " . (memory_get_usage() / 1024 / 1024) . " MB \n"; // function bmi_find_wordpress_base_path() { // // $dir = dirname(__FILE__); // $previous = null; // // do { // // if (file_exists($dir . '/wp-config.php')) return $dir; // if ($previous == $dir) break; // $previous = $dir; // // } while ($dir = dirname($dir)); // // return null; // // } // // define('BASE_PATH', bmi_find_wordpress_base_path() . '/'); // define('WP_USE_THEMES', false); // // // Use WP Globals and load WordPress // global $wp, $wp_query, $wp_the_query, $wp_rewrite, $wp_did_header; // require_once BASE_PATH . 'wp-load.php'; // echo "Memory usage after core load: " . (memory_get_usage() / 1024 / 1024) . " MB \n"; // ini_set('memory_limit', '2M'); /** * Database exporting * Main Class, requires $wpdb */ class BMI_Database_Exporter { /** * Private local variables */ private $total_tables = 0; private $recipes = []; private $tables_by_size = []; public $total_queries = 0; public $total_rows = 0; public $total_size = 0; public $files = []; /** * __construct - Initialization and logger resolver * * @return self */ function __construct($storage, &$logger) { /** * WP Global Database variable */ global $wpdb; $this->wpdb = &$wpdb; /** * Logger of BMI core */ $this->logger = &$logger; /** * Storage directory */ // $this->storage = trailingslashit(__DIR__) . 'data'; $this->storage = $storage; /** * Percentage escape to replace * This way we know what the randomized string is */ $this->percentage = trim($this->wpdb->prepare('%s', '%'), "'"); /** * Max rows to pass each query */ $this->max_rows = BMI_DB_MAX_ROWS_PER_QUERY; $this->table_prefix = time(); $this->init_start = microtime(true); $this->logger->log("Memory usage after initialization: " . number_format(memory_get_usage() / 1024 / 1024, 2) . " MB", 'INFO'); } /** * export - Export initializer * * @return filename/filenames */ public function export() { // Table names $this->get_table_names_and_sizes(); $this->logger->log("Scan found $this->total_tables tables ($this->total_rows rows), estimated total size: $this->total_size MB.", 'INFO'); $this->logger->log("Memory usage after getting table names: " . number_format(memory_get_usage() / 1024 / 1024, 2) . " MB ", 'INFO'); // Recipes $this->logger->log("Getting table recipes...", 'INFO'); $this->table_recipes(); $this->logger->log("Table recipes have been exported.", 'INFO'); $this->logger->log("Memory usage after loading recipes: " . number_format(memory_get_usage() / 1024 / 1024, 2) . " MB ", 'INFO'); // Save Recipes $this->logger->log("Saving recipes...", 'INFO'); $this->save_recipes(); $this->logger->log("Recipes saved.", 'INFO'); $this->logger->log("Memory usage after recipe off-load: " . number_format(memory_get_usage() / 1024 / 1024, 2) . " MB", 'INFO'); // Tables data $this->logger->log("Exporting table data...", 'INFO'); $this->get_tables_data(); $this->logger->log("Table data exported.", 'INFO'); $this->logger->log("Memory usage after data export: " . number_format(memory_get_usage() / 1024 / 1024, 2) . " MB", 'INFO'); $end = number_format(microtime(true) - $this->init_start, 4); $this->logger->log("Entire process took: $end s", 'INFO'); } /** * get_table_names_and_sizes - Gets table names and sizes * * @return {array} associative array table_name => [size => its size in MB, rows => rows count] */ private function get_table_names_and_sizes() { $tables = $this->wpdb->get_results('SHOW TABLES'); $shouldExcludeTables = Dashboard\bmi_get_config('BACKUP:DATABASE:EXCLUDE'); $excludedTables = []; if (defined('BMI_BACKUP_PRO') && BMI_BACKUP_PRO == 1) { $excludedTables = Dashboard\bmi_get_config('BACKUP:DATABASE:EXCLUDE:LIST'); if (!is_array($excludedTables) || empty($excludedTables)) $excludedTables = []; } foreach ($tables as $table_index => $table_object) { foreach ($table_object as $database_name => $table_name) { if (in_array($table_name, $excludedTables) && $shouldExcludeTables) { $str = __('Excluding %s table from backup (due to exclusion rules).', 'backup-backup'); $str = str_replace('%s', $table_name, $str); $this->logger->log($str, 'INFO'); continue; } // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Identifier is safely escaped via escapeSQLIDentifier() $results = $this->wpdb->get_results($this->wpdb->prepare(" SELECT table_name AS `table`, round(((data_length + index_length) / 1024 / 1024), 2) AS `size`, (SELECT COUNT(*) FROM " . BMP::escapeSQLIDentifier($table_name) . ") AS `rows` FROM information_schema.TABLES WHERE table_schema = %s AND table_name = %s; ", DB_NAME, $table_name)); if (!is_object($results[0])) { $this->logger->log("Could not get info about: $table_name (#01)", 'INFO'); continue; } $table_name_returned = trim($results[0]->table); if ($table_name != $table_name_returned || strlen(trim($table_name)) <= 0) { $this->logger->log("Could not get info about: $table_name (#02)", 'INFO'); continue; } $this->tables_by_size[$table_name_returned] = array( 'size' => floatval($results[0]->size), 'rows' => intval($results[0]->rows) ); $this->total_size += floatval($results[0]->size); $this->total_rows += intval($results[0]->rows); $this->total_tables++; } } return $this->tables_by_size; } /** * table_recipes - Gets CREATION recipe of each table * * @return {array} - Creation recipes for each table_name => recipe */ private function table_recipes() { foreach ($this->tables_by_size as $table_name => $table_object) { // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Identifier is safely escaped via escapeSQLIDentifier() $result = $this->wpdb->get_results("SHOW CREATE TABLE " . BMP::escapeSQLIDentifier($table_name) . ";"); foreach ($result as $index => $result_object) { foreach ($result_object as $column_name => $column_value) { if ($column_value == $table_name) continue; else { $column_value = str_replace(BMP::escapeSQLIDentifier($table_name), BMP::escapeSQLIDentifier( $this->table_prefix . '_' . $table_name), $column_value); $recipe = 'CREATE TABLE IF NOT EXISTS '; $recipe .= substr($column_value, 13); $this->recipes[$table_name] = $recipe; } } } } return $this->recipes; } /** * save_recipes - Save recipes and off-load the memory * * @return {void} */ private function save_recipes() { $time_prefix = $this->table_prefix; foreach ($this->recipes as $table_name => $table_recipe) { $this->total_queries += 4 + 3; $recipe = "/* QUERY START */\n"; $recipe .= "SET foreign_key_checks = 0;\n"; $recipe .= "/* QUERY END */\n\n"; $recipe .= "/* QUERY START */\n"; $recipe .= "SET SQL_MODE = '';\n"; $recipe .= "/* QUERY END */\n\n"; $recipe .= "/* QUERY START */\n"; $recipe .= "SET time_zone = '+00:00';\n"; $recipe .= "/* QUERY END */\n\n"; $recipe .= "/* QUERY START */\n"; $recipe .= "SET NAMES 'utf8';\n"; $recipe .= "/* QUERY END */\n\n"; $recipe .= "/* CUSTOM VARS START */\n"; $recipe .= "/* REAL_TABLE_NAME: `$table_name`; */\n"; $recipe .= "/* PRE_TABLE_NAME: `$time_prefix" . "_" . "$table_name`; */\n"; $recipe .= "/* CUSTOM VARS END */\n\n"; $recipe .= "/* QUERY START */\n"; $recipe .= $table_recipe . ";\n"; $recipe .= "/* QUERY END */\n\n"; $this->total_rows++; $location = $this->file_name($table_name); $file = fopen($location, 'w'); fwrite($file, $recipe); fclose($file); unset($file); $this->files[] = $location; unset($location); } unset($this->recipes); } /** * get_tables_data - Table data getter * * @return {int} Total rows count */ private function get_tables_data() { foreach ($this->tables_by_size as $table_name => $table_object) { $start_time = microtime(true); $this->logger->log("Getting data of table: " . $table_name . " (" . number_format ($table_object['size'], 2) . " MB)", 'STEP'); $rows = intval($table_object['rows']); $this->wpdb->query("SET foreign_key_checks = 0;"); for ($i = 0; $i < $rows; $i += $this->max_rows) { // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Identifier is safely escaped via escapeSQLIDentifier() $result = $this->wpdb->get_results($this->wpdb->prepare("SELECT * FROM " . BMP::escapeSQLIDentifier($table_name) . " LIMIT %d, %d", $i, $this->max_rows)); $this->save_data($result, $table_name); unset($result); } $this->wpdb->query("SET foreign_key_checks = 1;"); $this->logger->log("Table: " . $table_name . " cloned, operation took: " . number_format((microtime(true) - $start_time), 5) . " ms", 'INFO'); unset($start_time); } } /** * save_data - Saves table data/row as query * * @param {wpdb object} &$result Database query result * @param {string} &$table_name Table name * @return {void} */ private function save_data(&$result, &$table_name) { $columns_schema_added = false; $file = fopen($this->file_name($table_name), 'a+'); $this->total_queries++; $query = "/* QUERY START */\n"; $query .= "INSERT INTO " . BMP::escapeSQLIDentifier($this->table_prefix . "_" . $table_name) . " "; foreach ($result as $index => $result_object) { $data_in_order = array(); $format_in_order = array(); $columns_in_order = array(); foreach ($result_object as $column_name => $value) { $data_in_order[] = $value; $columns_in_order[] = BMP::escapeSQLIDentifier($column_name); if (is_numeric($value)) { if (is_float($value)) $format_in_order[] = '%f'; else $format_in_order[] = '%d'; } else $format_in_order[] = '%s'; } if ($columns_schema_added === false) { $query .= "(" . implode(', ', $columns_in_order) . ") VALUES ( \n"; $columns_schema_added = true; } else { $query = "), (\n"; } $columns = sizeof($columns_in_order); unset($columns_in_order); $query .= "/* VALUES START */\n"; for ($i = 0; $i < $columns; ++$i) { if ($format_in_order[$i] == '%f') { $query .= floatval($data_in_order[$i]); } elseif ($format_in_order[$i] == '%d') { $query .= intval($data_in_order[$i]); } else { $query .= $this->wpdb->prepare("%s", $data_in_order[$i]); $query = str_replace($this->percentage, '%', $query); } if ($i < ($columns - 1)) $query .= ",\n"; else $query .= "\n/* VALUES END */\n"; } unset($data_in_order); unset($format_in_order); unset($columns_in_order); fwrite($file, $query); } fwrite($file, ");\n/* QUERY END */\n\n"); fclose($file); unset($file); } /** * file_name - Replaces table name to file name friendly format * * @param {string} $table_name Table name * @return {string} Friendly format for file */ private function file_name($table_name) { $friendly_name = preg_replace("/[^A-Za-z0-9_-]/", '', $table_name); $friendly_name = trailingslashit($this->storage) . $friendly_name . '.sql'; return $friendly_name; } }
Save
Back