Files
wingedit.pl/wp-content/plugins/wpvivid-backuprestore/includes/new_backup/class-wpvivid-mysqldump2.php
2026-03-11 15:57:27 +01:00

1060 lines
32 KiB
PHP

<?php
if (!defined('WPVIVID_PLUGIN_DIR'))
{
die;
}
use Exception as Exception;
class CompressTest_2
{
private $fileHandler = null;
/**
* @param string $filename
*/
public function open($filename)
{
$this->fileHandler = fopen($filename, "wb");
if (false === $this->fileHandler) {
throw new Exception("Output file is not writable");
}
return true;
}
public function write($str)
{
if (false === ($bytesWritten = fwrite($this->fileHandler, $str))) {
throw new Exception("Writting to file failed! Probably, there is no more free space left?");
}
return $bytesWritten;
}
public function close()
{
return fclose($this->fileHandler);
}
public function get_size()
{
$fstat = fstat($this->fileHandler);
return $fstat['size'];
}
}
class WPvivid_Mysqldump2
{
// Same as mysqldump
const MAXLINESIZE = 1000000;
// Available compression methods as constants
const GZIP = 'Gzip';
const BZIP2 = 'Bzip2';
const NONE = 'None';
// Available connection strings
const UTF8 = 'utf8';
const UTF8MB4 = 'utf8mb4';
/**
* Database username
* @var string
*/
public $user;
/**
* Database password
* @var string
*/
public $pass;
/**
* Destination filename, defaults to stdout
* @var string
*/
public $fileName = 'php://output';
// Internal stuff
private $tables = array();
//private $dbHandler = null;
private $dbType;
private $compressManager;
private $typeAdapter;
private $dumpSettings = array();
private $version;
private $tableColumnTypes = array();
public $log=false;
public $task_id='';
/**
* database name, parsed from dsn
* @var string
*/
private $dbName;
/**
* host name, parsed from dsn
* @var string
*/
private $host;
public $last_query_string='';
public $task = false;
public $file_index=1;
public $tmp_file_name='';
public $current_size=0;
public $files=array();
public $backup_tables;
public $find_zero_date=false;
public function __construct($task,$dump_setting)
{
if(is_a($task, 'WPvivid_Backup_Task_2'))
{
$this->task=$task;
}
else
{
throw new Exception('not as wpvivid task type');
}
$dumpSettingsDefault = array(
'include-tables' => array(),
'exclude-tables' => array(),
'compress' => WPvivid_Mysqldump2::NONE,
'init_commands' => array(),
'no-data' => array(),
'reset-auto-increment' => false,
'add-drop-database' => false,
'add-drop-table' => true,
'add-drop-trigger' => true,
'add-locks' => true,
'complete-insert' => false,
'default-character-set' => WPvivid_Mysqldump2::UTF8,
'disable-keys' => true,
'extended-insert' => false,
'events' => false,
'hex-blob' => true, /* faster than escaped content */
'net_buffer_length' => self::MAXLINESIZE,
'no-autocommit' => false,
'no-create-info' => false,
'lock-tables' => false,
'routines' => false,
'single-transaction' => true,
'skip-triggers' => false,
'skip-tz-utc' => false,
'skip-comments' => false,
'skip-dump-date' => false,
'where' => '',
);
if(defined('DB_CHARSET'))
{
$dumpSettingsDefault['default-character-set']=DB_CHARSET;
}
$this->dumpSettings = $this->array_replace_recursive($dumpSettingsDefault, $dump_setting);
$this->dumpSettings['init_commands'][] = "SET NAMES " . WPvivid_Mysqldump2::UTF8MB4;
if (false === $this->dumpSettings['skip-tz-utc'])
{
$this->dumpSettings['init_commands'][] = "SET TIME_ZONE='+00:00'";
}
// Create a new compressManager to manage compressed output
$this->compressManager = new CompressTest_2();
$this->backup_tables=0;
}
public function connect()
{
$dbType=$this->dumpSettings['db_connect_method'];
$host=$this->dumpSettings['host'];
$user=$this->dumpSettings['user'];
$pass=$this->dumpSettings['pass'];
$database=$this->dumpSettings['database'];
$this->typeAdapter = WPvividTypeAdapterFactory::create($dbType, null);
$this->typeAdapter->connect($host,$database,$user,$pass,$this->dumpSettings['init_commands']);
}
public function write_header()
{
// Write some basic info to output file
$upload_dir = wp_upload_dir();
$site_url=$this->dumpSettings['site_url'];
$home_url=$this->dumpSettings['home_url'];
$content_url=$this->dumpSettings['content_url'];
$upload_url=$upload_dir['baseurl'];
$this->compressManager->write('/* # site_url: '.$site_url.' */;'.PHP_EOL);
$this->compressManager->write('/* # home_url: '.$home_url.' */;'.PHP_EOL);
$this->compressManager->write('/* # content_url: '.$content_url.' */;'.PHP_EOL);
$this->compressManager->write('/* # upload_url: '.$upload_url.' */;'.PHP_EOL);
if(isset($this->dumpSettings['prefix']))
{
$table_prefix=$this->dumpSettings['prefix'];
$this->compressManager->write('/* # table_prefix: '.$table_prefix.' */;'.PHP_EOL.PHP_EOL.PHP_EOL);
}
// Store server settings and use sanner defaults to dump
$this->compressManager->write(
$this->typeAdapter->backup_parameters($this->dumpSettings)
);
}
public function write_footer()
{
// Restore saved parameters
$this->compressManager->write(
$this->typeAdapter->restore_parameters($this->dumpSettings)
);
}
public function init_job()
{
$tables=$this->list_tables();
if(empty($tables))
{
return false;
}
usort($tables, function ($a, $b)
{
if ($a['size'] == $b['size'])
return 0;
if ($a['size'] > $b['size'])
return 1;
else
return -1;
});
$jobs=array();
foreach ($tables as $table)
{
$jobs[$table['name']]['index']=0;
$jobs[$table['name']]['finished']=0;
$jobs[$table['name']]['created']=0;
$jobs[$table['name']]['name']=$table['name'];
$jobs[$table['name']]['size']=$table['size'];
$jobs[$table['name']]['rows']=$table['rows'];
}
$this->task->update_current_sub_job($jobs);
return $jobs;
}
public function start_jobs()
{
$this->tables= $this->task->get_current_sub_job();
return $this->exportTables();
}
public function list_tables()
{
$tables=array();
$views=array();
global $wpdb;
$resultSet=$wpdb->get_results('SHOW TABLE STATUS', ARRAY_A);
$resultViews=$wpdb->get_results('SHOW FULL TABLES WHERE table_type = \'VIEW\'', ARRAY_A);
if(!is_null($resultViews))
{
foreach ($resultViews as $view)
{
$name = 'Tables_in_'.DB_NAME;
$views[] = $view[$name];
}
}
if (is_null($resultSet))
{
return $tables;
}
if(isset($this->dumpSettings['prefix'])&&!empty($this->dumpSettings['prefix']))
{
$exclude = array('/^(?!' . $this->dumpSettings['prefix'] . ')/i');
}
else
{
$exclude=array();
}
foreach ($resultSet as $row)
{
if(isset($row['Comment']) && $row['Comment'] === 'VIEW')
{
continue;
}
if ( $this->matches($row['Name'], $this->dumpSettings['include-tables']) )
{
$table['name']=$row['Name'];
$table['size']= ($row["Data_length"] + $row["Index_length"]);
$table['rows']=$row['Rows'];
$tables[]=$table;
continue;
}
if(!empty($exclude))
{
if ( $this->matches($row['Name'], $exclude) )
{
continue;
}
}
if ( $this->matches($row['Name'], $this->dumpSettings['exclude-tables']) )
{
continue;
}
if(!empty($views))
{
if ( $this->matches($row['Name'], $views) )
{
continue;
}
}
$table['name']=$row['Name'];
$table['size']= ($row["Data_length"] + $row["Index_length"]);
$table['rows']=$row['Rows'];
$tables[]=$table;
}
return $tables;
}
/**
* Compare if $table name matches with a definition inside $arr
* @param $table string
* @param $arr array with strings or patterns
* @return bool
*/
private function matches($table, $arr) {
$match = false;
if(empty($arr))
{
return false;
}
foreach ($arr as $pattern) {
if ( '/' != $pattern[0] ) {
continue;
}
if ( 1 == preg_match($pattern, $table) ) {
$match = true;
}
}
return in_array($table, $arr) || $match;
}
public function check_tmp_file()
{
$max_file_size=$this->dumpSettings['max_file_size'];
$max_backup_tables=5000;
if($max_file_size==0)
return;
$this->current_size=$this->compressManager->get_size();
$path=$this->dumpSettings['path'];
if( $this->current_size>$max_file_size||$this->backup_tables>=$max_backup_tables)
{
$this->current_size=0;
$this->backup_tables=0;
$this->close_tmp_file();
$name_file_name=$this->dumpSettings['file_prefix'].'.part'.sprintf('%03d',($this->file_index)).'.sql';
$this->file_index++;
rename($this->tmp_file_name,$path.DIRECTORY_SEPARATOR.$name_file_name);
$this->task->update_current_sub_job($this->tables);
$this->task->add_mysql_dump_files($name_file_name);
$this->open_tmp_file();
}
}
public function open_tmp_file($b_delete=false)
{
if($b_delete)
@wp_delete_file( $this->tmp_file_name);
$this->compressManager->open($this->tmp_file_name);
}
public function close_tmp_file()
{
$this->compressManager->close();
}
private function exportTables()
{
global $wpvivid_plugin;
//tmp_file_name
$path=$this->dumpSettings['path'];
$this->tmp_file_name=$path.DIRECTORY_SEPARATOR.$this->dumpSettings['file_prefix'].'_tmp.sql';
$this->open_tmp_file();
$this->write_header();
/*
if(file_exists($this->tmp_file_name))
{
$this->open_tmp_file();
}
else
{
$this->open_tmp_file();
$this->write_header();
}*/
// Exporting tables one by one
$this->file_index=$this->task->get_current_mysql_file_index();
$this->current_size=0;
$tables=$this->tables;
$i=0;
$i_step=0;
$this->backup_tables=0;
if($this->task->task_id!=='')
{
$size = $this->task->get_backup_jobs();
if(sizeof($size) > 0)
{
$i_step = intval(1 / (sizeof($size)) * 100);
}
}
foreach ($tables as $name=>$table)
{
if($this->task->check_cancel_backup())
{
die();
}
if($table['finished']==1)
{
continue;
}
$index=$table['index'];
$table_name=$table['name'];
$message='Preparing to dump table '.$table_name;
$wpvivid_plugin->wpvivid_log->WriteLog($message,'notice');
$this->task->update_sub_task_progress($message);
if($table['created']==0)
{
$this->getTableStructure($table_name);
$this->tables[$name]['created']=1;
//$this->task->update_current_sub_job($this->tables);
}
$this->tableColumnTypes[$table_name] = $this->getTableColumnTypes($table_name);
if($this->tableColumnTypes[$table_name]===false)
{
continue;
}
$this->listValues($table_name,$index);
$this->check_tmp_file();
$this->tables[$name]['finished']=1;
$this->backup_tables++;
//$this->task->update_current_sub_job($this->tables);
$i++;
if($this->task->task_id!=='')
{
$i_progress=intval($i/sizeof($this->tables)*$i_step);
$this->task->update_database_progress($i_progress);
}
}
$this->current_size=$this->compressManager->get_size();
if($this->current_size>0)
{
$this->close_tmp_file();
$name_file_name=$this->dumpSettings['file_prefix'].'.part'.sprintf('%03d',($this->file_index)).'.sql';
$this->file_index++;
rename($this->tmp_file_name,$path.DIRECTORY_SEPARATOR.$name_file_name);
$this->task->add_mysql_dump_files($name_file_name);
}
else
{
$this->close_tmp_file();
}
$ret['result']='success';
return $ret;
}
/**
* Table structure extractor
*
* @param string $tableName Name of table to export
* @return null
*/
private function getTableStructure($tableName)
{
if (!$this->dumpSettings['no-create-info']) {
$ret = '';
if (!$this->dumpSettings['skip-comments']) {
$ret = "--" . PHP_EOL .
"-- Table structure for table `$tableName`" . PHP_EOL .
"--" . PHP_EOL . PHP_EOL;
}
$stmt = $this->typeAdapter->show_create_table($tableName);
foreach ($this->query($stmt) as $r)
{
$this->compressManager->write($ret);
if ($this->dumpSettings['add-drop-table']) {
$this->compressManager->write(
$this->typeAdapter->drop_table($tableName)
);
}
$this->compressManager->write(
$this->typeAdapter->create_table($r, $this->dumpSettings)
);
break;
}
}
return;
}
/**
* Store column types to create data dumps and for Stand-In tables
*
* @param string $tableName Name of table to export
* @return array type column types detailed
*/
private function getTableColumnTypes($tableName) {
$columnTypes = array();
$columns = $this->query(
$this->typeAdapter->show_columns($tableName)
);
if($columns===false)
{
$error=$this->typeAdapter->errorInfo();
if(isset($error[2])){
$error = 'Error: '.$error[2];
}
else{
$error = '';
}
$columns = $this->query(
'DESCRIBE '.$tableName
);
if($columns===false)
{
$error=$this->typeAdapter->errorInfo();
if(isset($error[2])){
$error = 'Error: '.$error[2];
}
else{
$error = '';
}
return false;
}
}
foreach($columns as $key => $col) {
$types = $this->typeAdapter->parseColumnType($col);
$columnTypes[$col['Field']] = array(
'is_numeric'=> $types['is_numeric'],
'is_blob' => $types['is_blob'],
'type' => $types['type'],
'type_sql' => $col['Type'],
'is_virtual' => $types['is_virtual']
);
}
return $columnTypes;
}
/**
* Escape values with quotes when needed
*
* @param string $tableName Name of table which contains rows
* @param array $row Associative array of column names and values to be quoted
*
* @return array
*/
private function escape($tableName, $row)
{
$ret = array();
$columnTypes = $this->tableColumnTypes[$tableName];
foreach ($row as $colName => $colValue) {
if (is_null($colValue)) {
$ret[] = "NULL";
} elseif ($this->dumpSettings['hex-blob'] && $columnTypes[$colName]['is_blob']) {
if ($columnTypes[$colName]['type'] == 'bit' || !empty($colValue)) {
$ret[] = "0x{$colValue}";
} else {
$ret[] = "''";
}
} elseif ($columnTypes[$colName]['is_numeric']) {
$ret[] = $colValue;
} else {
$ret[] = $this->typeAdapter->quote($colValue);
}
}
return $ret;
}
private function listValues($tableName,$index)
{
global $wpvivid_plugin;
$this->prepareListValues($tableName);
$onlyOnce = true;
$lineSize = 0;
$colStmt = $this->getColumnStmt($tableName);
global $wpdb;
$prefix=$wpdb->base_prefix;
$dbType=$this->dumpSettings['db_connect_method'];
$start=$index;
$limit_count=5000;
//$sum =$wpdb->get_var("SELECT COUNT(1) FROM `{$tableName}`");
$sum=0;
$resultSet = $this->query("SELECT COUNT(1) FROM `{$tableName}`");
foreach ($resultSet as $row)
{
$sum=$row['COUNT(1)'];
}
$this->typeAdapter->closeCursor($resultSet);
if($dbType=='wpdb')
{
$b_options=false;
if(substr($tableName, strlen($prefix))=='options')
{
$b_options=true;
}
$stmt = "SELECT " . implode(",", $colStmt) . " FROM `$tableName`";
if ($this->dumpSettings['where']) {
$stmt .= " WHERE {$this->dumpSettings['where']}";
}
$i=0;
$i_check_cancel=0;
$count=0;
while($sum > $start)
{
$limit = " LIMIT {$limit_count} OFFSET {$start}";
$query=$stmt.$limit;
$resultSet = $this->query($query);
if($resultSet===false)
{
$error=$this->typeAdapter->errorInfo();
if(isset($error[2])){
$error = 'Error: '.$error[2];
}
else{
$error = '';
}
$this->endListValues($tableName);
return ;
}
foreach ($resultSet as $row)
{
$i++;
$skip=false;
$vals = $this->escape($tableName, $row);
foreach($vals as $key => $value)
{
if($value === '\'0000-00-00 00:00:00\'')
{
//$vals[$key] = '\'1999-01-01 00:00:00\'';
$this->find_zero_date=true;
}
if($b_options)
{
if($value=="'wpvivid_task_list'")
{
$skip=true;
}
}
}
if($skip)
continue;
if ($onlyOnce || !$this->dumpSettings['extended-insert'])
{
if ($this->dumpSettings['complete-insert'])
{
$lineSize += $this->compressManager->write(
"INSERT INTO `$tableName` (" .
implode(", ", $colStmt) .
") VALUES (" . implode(",", $vals) . ")"
);
} else {
$lineSize += $this->compressManager->write(
"INSERT INTO `$tableName` VALUES (" . implode(",", $vals) . ")"
);
}
$onlyOnce = false;
}
else {
$lineSize += $this->compressManager->write(",(" . implode(",", $vals) . ")");
}
if (($lineSize > $this->dumpSettings['net_buffer_length']) ||
!$this->dumpSettings['extended-insert']) {
$onlyOnce = true;
$lineSize = $this->compressManager->write(";" . PHP_EOL);
}
if($i>=200000)
{
$count+=$i;
$i=0;
if($this->task->task_id!=='')
{
$i_check_cancel++;
if($i_check_cancel>5)
{
$i_check_cancel=0;
$this->task->check_cancel_backup();
}
$message='Dumping table '.$tableName.', rows dumped: '.$count.' rows.';
$this->task->update_sub_task_progress($message);
}
}
}
$this->typeAdapter->closeCursor($resultSet);
$start += $limit_count;
$this->tables[$tableName]['index']=$start;
//$this->task->update_current_sub_job($this->tables);
$this->check_tmp_file();
}
if (!$onlyOnce) {
$this->compressManager->write(";" . PHP_EOL);
}
$this->endListValues($tableName);
}
else
{
$b_options=false;
if(substr($tableName, strlen($prefix))=='options')
{
$b_options=true;
}
$stmt = "SELECT " . implode(",", $colStmt) . " FROM `$tableName`";
if ($this->dumpSettings['where']) {
$stmt .= " WHERE {$this->dumpSettings['where']}";
}
$i=0;
$i_check_cancel=0;
$count=0;
while($sum > $start)
{
$limit = " LIMIT {$limit_count} OFFSET {$start}";
$query=$stmt.$limit;
$resultSet = $this->query($query);
if($resultSet===false)
{
$error=$this->typeAdapter->errorInfo();
if(isset($error[2])){
$error = 'Error: '.$error[2];
}
else{
$error = '';
}
$this->endListValues($tableName);
return ;
}
foreach ($resultSet as $row)
{
$skip=false;
$vals = $this->escape($tableName, $row);
foreach($vals as $key => $value)
{
if($value === '\'0000-00-00 00:00:00\'')
{
//$vals[$key] = '\'1999-01-01 00:00:00\'';
$this->find_zero_date=true;
}
if($b_options)
{
if($value=="'wpvivid_task_list'")
{
$skip=true;
}
}
}
if($skip)
continue;
if ($onlyOnce || !$this->dumpSettings['extended-insert'])
{
if ($this->dumpSettings['complete-insert'])
{
var_dump('test1');
$lineSize += $this->compressManager->write(
"INSERT INTO `$tableName` (" .
implode(", ", $colStmt) .
") VALUES (" . implode(",", $vals) . ")"
);
} else {
$lineSize += $this->compressManager->write(
"INSERT INTO `$tableName` VALUES (" . implode(",", $vals) . ")"
);
}
$onlyOnce = false;
}
else {
$lineSize += $this->compressManager->write(",(" . implode(",", $vals) . ")");
}
if (($lineSize > $this->dumpSettings['net_buffer_length']) ||
!$this->dumpSettings['extended-insert']) {
$onlyOnce = true;
$lineSize = $this->compressManager->write(";" . PHP_EOL);
}
if($i>=200000)
{
$count+=$i;
$i=0;
if($this->task->task_id!=='')
{
$i_check_cancel++;
if($i_check_cancel>5)
{
$i_check_cancel=0;
$this->task->check_cancel_backup();
}
$message='Dumping table '.$tableName.', rows dumped: '.$count.' rows.';
$this->task->update_sub_task_progress($message);
}
}
}
$this->typeAdapter->closeCursor($resultSet);
$start += $limit_count;
$this->tables[$tableName]['index']=$start;
//$this->task->update_current_sub_job($this->tables);
$this->check_tmp_file();
}
if (!$onlyOnce) {
$this->compressManager->write(";" . PHP_EOL);
}
$this->endListValues($tableName);
}
//$this->current_size+=$table['size'];
}
/**
* Table rows extractor, append information prior to dump
*
* @param string $tableName Name of table to export
*
* @return null
*/
function prepareListValues($tableName)
{
if (!$this->dumpSettings['skip-comments']) {
$this->compressManager->write(
"--" . PHP_EOL .
"-- Dumping data for table `$tableName`" . PHP_EOL .
"--" . PHP_EOL . PHP_EOL
);
}
if ($this->dumpSettings['single-transaction']) {
$this->exec($this->typeAdapter->setup_transaction());
$this->exec($this->typeAdapter->start_transaction());
}
if ($this->dumpSettings['lock-tables'])
{
$this->typeAdapter->lock_table($tableName);
//if($this -> privileges['LOCK TABLES'] == 0)
//{
//global $wpvivid_plugin;
// $wpvivid_plugin->wpvivid_log->WriteLog('The lack of LOCK TABLES privilege, the backup will skip lock_tables() to continue.','notice');
//}else{
// $this->typeAdapter->lock_table($tableName);
//}
}
if ($this->dumpSettings['add-locks']) {
$this->compressManager->write(
$this->typeAdapter->start_add_lock_table($tableName)
);
}
if ($this->dumpSettings['disable-keys']) {
$this->compressManager->write(
$this->typeAdapter->start_add_disable_keys($tableName)
);
}
// Disable autocommit for faster reload
if ($this->dumpSettings['no-autocommit']) {
$this->compressManager->write(
$this->typeAdapter->start_disable_autocommit()
);
}
return;
}
/**
* Table rows extractor, close locks and commits after dump
*
* @param string $tableName Name of table to export
*
* @return null
*/
function endListValues($tableName)
{
if ($this->dumpSettings['disable-keys']) {
$this->compressManager->write(
$this->typeAdapter->end_add_disable_keys($tableName)
);
}
if ($this->dumpSettings['add-locks']) {
$this->compressManager->write(
$this->typeAdapter->end_add_lock_table($tableName)
);
}
if ($this->dumpSettings['single-transaction']) {
$this->exec($this->typeAdapter->commit_transaction());
}
if ($this->dumpSettings['lock-tables']) {
$this->typeAdapter->unlock_table($tableName);
}
// Commit to enable autocommit
if ($this->dumpSettings['no-autocommit']) {
$this->compressManager->write(
$this->typeAdapter->end_disable_autocommit()
);
}
$this->compressManager->write(PHP_EOL);
return;
}
/**
* Build SQL List of all columns on current table
*
* @param string $tableName Name of table to get columns
*
* @return string SQL sentence with columns
*/
function getColumnStmt($tableName)
{
$colStmt = array();
foreach($this->tableColumnTypes[$tableName] as $colName => $colType) {
if ($colType['type'] == 'bit' && $this->dumpSettings['hex-blob']) {
$colStmt[] = "LPAD(HEX(`{$colName}`),2,'0') AS `{$colName}`";
} else if ($colType['is_blob'] && $this->dumpSettings['hex-blob']) {
$colStmt[] = "HEX(`{$colName}`) AS `{$colName}`";
} else if ($colType['is_virtual']) {
$this->dumpSettings['complete-insert'] = true;
continue;
} else {
$colStmt[] = "`{$colName}`";
}
}
return $colStmt;
}
/**
* Custom array_replace_recursive to be used if PHP < 5.3
* Replaces elements from passed arrays into the first array recursively
*
* @param array $array1 The array in which elements are replaced
* @param array $array2 The array from which elements will be extracted
*
* @return array Returns an array, or NULL if an error occurs.
*/
public function array_replace_recursive($array1, $array2)
{
if (function_exists('array_replace_recursive')) {
return array_replace_recursive($array1, $array2);
}
foreach ($array2 as $key => $value) {
if (is_array($value)) {
$array1[$key] = $this->array_replace_recursive($array1[$key], $value);
} else {
$array1[$key] = $value;
}
}
return $array1;
}
public function query($query_string)
{
$this->last_query_string=$query_string;
return $this->typeAdapter->query($query_string);
}
private function exec($query_string)
{
$this->last_query_string=$query_string;
return $this->typeAdapter->query($query_string);
}
public function is_has_zero_date()
{
if($this->find_zero_date)
{
return true;
}
else
{
return false;
}
}
}