mirror of https://github.com/avecms/AVE.cms.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
643 lines
19 KiB
643 lines
19 KiB
<?php |
|
|
|
/** |
|
* AVE.cms |
|
* |
|
* Класс, предназначенный для создания и восстановления дампов БД через Панель управления |
|
* |
|
* @package AVE.cms |
|
* @version 3.x |
|
* @filesource |
|
* @copyright © 2007-2014 AVE.cms, http://www.ave-cms.ru |
|
* |
|
*/ |
|
|
|
class AVE_DB_Service |
|
{ |
|
/** |
|
* Метка-разделитель SQL-запросов |
|
* |
|
* @public string |
|
*/ |
|
public $_delimiter = '#####systemdump#####'; |
|
|
|
/** |
|
* Дамп базы данных |
|
* |
|
* @public string |
|
*/ |
|
public $_database_dump = ''; |
|
|
|
|
|
/** |
|
* Метод, предназначенный для формирования файла дампа базы данных |
|
* |
|
* @return boolean |
|
*/ |
|
function _databaseDumpCreate() |
|
{ |
|
global $AVE_DB; |
|
|
|
if (! (! empty($_REQUEST['ta']) && is_array($_REQUEST['ta']))) |
|
return false; |
|
|
|
$search = array("\x00", "\x0a", "\x0d", "\x1a"); |
|
$replace = array('\0', '\n', '\r', '\Z'); |
|
|
|
$this->_database_dump = ''; |
|
|
|
// Циклически обрабатываем каждую таблицу |
|
foreach ($_REQUEST['ta'] as $table) |
|
{ |
|
if (! DB_EXPORT_PREFIX) |
|
$table_export = preg_replace('/^' . PREFIX . '/', '%%PRFX%%', $table); |
|
|
|
// Если таблица имеет корректный префикс |
|
if (preg_match('/^' . preg_quote(PREFIX) . '_/', $table)) |
|
{ |
|
$row = $AVE_DB->Query("SHOW CREATE TABLE " . $table)->FetchArray(); |
|
// Сохраняем CREATE и DROP запросы |
|
$this->_database_dump .= "DROP TABLE IF EXISTS `" . (! DB_EXPORT_PREFIX ? $table_export : $table) . "`;" . $this->_delimiter . "\n"; |
|
|
|
if (! DB_EXPORT_PREFIX) |
|
$this->_database_dump .= str_replace('CREATE TABLE `' . PREFIX . '_', 'CREATE TABLE `%%PRFX%%_', $row[1]) . ";" . $this->_delimiter . "\n\n"; |
|
else |
|
$this->_database_dump .= $row[1] . ";" . $this->_delimiter . "\n\n"; |
|
|
|
$nums = 0; |
|
|
|
// Получаем данные, которые в дальнейшем будут вставлены в INSERT запросы. |
|
$sql = $AVE_DB->Query('SELECT * FROM `' . $table . '`'); |
|
|
|
while ($row = $sql->FetchArray()) |
|
{ |
|
if ($nums == 0) |
|
{ |
|
$nums = $sql->NumFields(); |
|
|
|
$temp_array = array(); |
|
|
|
for ($i = 0; $i < $nums; $i++) |
|
{ |
|
$temp_array[] = $sql->FieldName($i); |
|
} |
|
|
|
$table_list = '(`' . implode('`, `', $temp_array) . '`)'; |
|
} |
|
|
|
$temp_array = array(); |
|
|
|
for ($i=0; $i<$nums; $i++) |
|
{ |
|
if (! isset($row[$i])) |
|
{ |
|
$temp_array[] = 'NULL'; |
|
} |
|
elseif ($row[$i] != '') |
|
{ |
|
$temp_array[] = "'" . str_replace($search, $replace, addslashes($row[$i])) . "'"; |
|
} |
|
else |
|
{ |
|
$temp_array[] = "''"; |
|
} |
|
} |
|
|
|
// Сохряняем INSERT запросы |
|
$this->_database_dump .= 'INSERT INTO `' . (! DB_EXPORT_PREFIX ? $table_export : $table) . '` ' . $table_list . ' VALUES (' . implode(', ', $temp_array) . ");" . $this->_delimiter . "\n"; |
|
} |
|
|
|
$this->_database_dump .= "\n"; |
|
|
|
$sql->Close(); |
|
} |
|
} |
|
|
|
return ! empty($this->_database_dump); |
|
} |
|
|
|
|
|
/** |
|
* Метод, предназначенный для формирования файла дампа базы данных |
|
* |
|
* @return boolean |
|
*/ |
|
function _databaseTopDumpCreate() |
|
{ |
|
global $AVE_DB; |
|
|
|
$dbtables = array(); |
|
|
|
$sql = $AVE_DB->Query("SHOW TABLES LIKE '" . PREFIX . "_%'"); |
|
|
|
while ($row = $sql->FetchArray()) |
|
{ |
|
array_push($dbtables, $row[0]); |
|
} |
|
|
|
$search = array("\x00", "\x0a", "\x0d", "\x1a"); |
|
$replace = array('\0', '\n', '\r', '\Z'); |
|
|
|
$this->_database_dump = ''; |
|
|
|
// Циклически обрабатываем каждую таблицу |
|
foreach ($dbtables as $table) |
|
{ |
|
if (! DB_EXPORT_PREFIX) |
|
$table_export = preg_replace('/^' . PREFIX . '/', '%%PRFX%%', $table); |
|
|
|
// Если таблица имеет корректный префикс |
|
if (preg_match('/^' . preg_quote(PREFIX) . '_/', $table)) |
|
{ |
|
$row = $AVE_DB->Query("SHOW CREATE TABLE " . $table)->FetchArray(); |
|
|
|
// Сохраняем CREATE и DROP запросы |
|
$this->_database_dump .= "DROP TABLE IF EXISTS `" . (! DB_EXPORT_PREFIX ? $table_export : $table) . "`;" . $this->_delimiter . "\n"; |
|
|
|
if (! DB_EXPORT_PREFIX) |
|
$this->_database_dump .= str_replace('CREATE TABLE `' . PREFIX . '_', 'CREATE TABLE `%%PRFX%%_', $row[1]) . ";" . $this->_delimiter . "\n\n"; |
|
else |
|
$this->_database_dump .= $row[1] . ";" . $this->_delimiter . "\n\n"; |
|
|
|
$nums = 0; |
|
|
|
// Получаем данные, которые в дальнейшем будут вставлены в INSERT запросы. |
|
$sql = $AVE_DB->Query('SELECT * FROM `' . $table . '`'); |
|
|
|
while ($row = $sql->FetchArray()) |
|
{ |
|
if ($nums==0) |
|
{ |
|
$nums = $sql->NumFields(); |
|
|
|
$temp_array = array(); |
|
for ($i=0; $i<$nums; $i++) |
|
{ |
|
$temp_array[] = $sql->FieldName($i); |
|
} |
|
$table_list = '(`' . implode('`, `', $temp_array) . '`)'; |
|
} |
|
|
|
$temp_array = array(); |
|
|
|
for ($i=0; $i<$nums; $i++) |
|
{ |
|
if (!isset($row[$i])) |
|
{ |
|
$temp_array[] = 'NULL'; |
|
} |
|
elseif ($row[$i] != '') |
|
{ |
|
$temp_array[] = "'" . str_replace($search, $replace, addslashes($row[$i])) . "'"; |
|
} |
|
else |
|
{ |
|
$temp_array[] = "''"; |
|
} |
|
} |
|
|
|
// Сохряняем INSERT запросы |
|
$this->_database_dump .= 'INSERT INTO `' . (! DB_EXPORT_PREFIX ? $table_export : $table) . '` ' . $table_list . ' VALUES (' . implode(', ', $temp_array) . ");" . $this->_delimiter . "\n"; |
|
} |
|
|
|
$this->_database_dump .= "\n"; |
|
|
|
$sql->Close(); |
|
} |
|
} |
|
|
|
return ! empty($this->_database_dump); |
|
} |
|
|
|
|
|
/** |
|
* Внешние методы класса |
|
*/ |
|
|
|
/** |
|
* Метод, предназначенный для сохранения файла дампа базы данных на жеский диск |
|
* |
|
*/ |
|
function databaseDumpExport($top = 0, $exit = 0) |
|
{ |
|
global $AVE_Template; |
|
|
|
// Если дамп не удалось создать, тогда завершаем работу |
|
if ($top) |
|
{ |
|
if (! $this->_databaseTopDumpCreate()) |
|
exit; |
|
} |
|
else |
|
{ |
|
if (! $this->_databaseDumpCreate()) |
|
exit; |
|
} |
|
|
|
// Готовим шаблон имени файла |
|
if (isset($_REQUEST['file_name']) AND $_REQUEST['file_name'] != '') |
|
$file_name = prepare_fname($_REQUEST['file_name']); |
|
else |
|
$file_name = preg_replace_ru(array("/%SERVER%/", "/%DATE%/", "/%TIME%/"), array($_SERVER['SERVER_NAME'], date('d.m.y'), date('H.i.s')), DB_EXPORT_TPL); |
|
|
|
$dump = (defined('DB_EXPORT_GZ') && DB_EXPORT_GZ |
|
? gzencode($this->_database_dump) |
|
: $this->_database_dump); |
|
|
|
if (isset($_REQUEST['server']) && $_REQUEST['server'] == 1) |
|
{ |
|
if (! is_dir(BASE_DIR . '/tmp/backup/')) |
|
{ |
|
@mkdir(BASE_DIR . '/tmp/backup/', 0777, true); |
|
write_htaccess_deny(BASE_DIR . '/tmp/backup/'); |
|
} |
|
|
|
@file_put_contents(BASE_DIR . '/tmp/backup/'. $file_name . '.sql'. (defined('DB_EXPORT_GZ') && DB_EXPORT_GZ ? '.gz' : ''), $dump); |
|
|
|
@chmod(BASE_DIR . '/tmp/backup/'. $file_name . '.sql', 0777); |
|
|
|
if (! $exit) |
|
header('Location:index.php?do=dbsettings&cp=' . SESSION); |
|
else |
|
return BASE_DIR . '/tmp/backup/'. $file_name . '.sql'. (defined('DB_EXPORT_GZ') && DB_EXPORT_GZ ? '.gz' : ''); |
|
} |
|
else |
|
{ |
|
// Формируем заголовок |
|
header('Content-Type: text/plain'); |
|
header('Expires: ' . gmdate('D, d M Y H:i:s') . ' GMT'); |
|
header('Content-Disposition: attachment; filename=' . $file_name . '.sql'. (defined('DB_EXPORT_GZ') && DB_EXPORT_GZ ? '.gz' : '')); |
|
header('Content-Length: ' . strlen($dump)); |
|
header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); |
|
header('Pragma: public'); |
|
|
|
// Выводим данные |
|
echo $dump; |
|
|
|
$this->_database_dump = ''; |
|
} |
|
|
|
// Выполняем запись системного сообщения в журнал |
|
reportLog($AVE_Template->get_config_vars('DB_REPORT_DUMP')); |
|
exit; |
|
} |
|
|
|
|
|
/** |
|
* Метод, предназначенный для сохранения файла дампа базы данных на жеский диск |
|
* |
|
*/ |
|
function databaseDumpFileSave($file = '') |
|
{ |
|
global $AVE_Template; |
|
|
|
$file = BASE_DIR . '/tmp/backup/'. $file; |
|
|
|
// Если дамп не удалось создать, тогда завершаем работу |
|
if (! is_file($file)) |
|
return false; |
|
|
|
header('Content-Description: File Transfer'); |
|
header('Content-Type: application/octet-stream'); |
|
header('Content-Disposition: attachment; filename=' . basename($file)); |
|
header('Content-Transfer-Encoding: binary'); |
|
header('Expires: 0'); |
|
header('Cache-Control: must-revalidate'); |
|
header('Pragma: public'); |
|
header('Content-Length: ' . filesize($file)); |
|
|
|
ob_clean(); |
|
|
|
flush(); |
|
|
|
readfile($file); |
|
|
|
exit; |
|
} |
|
|
|
|
|
/** |
|
* Метод, предназначенный для восстановления базы данных из дампа |
|
* |
|
* @param string $tempdir путь к папке в которую загружается файл дампа |
|
*/ |
|
function databaseDumpImport($tempdir) |
|
{ |
|
global $AVE_DB, $AVE_Template; |
|
|
|
$insert = false; |
|
|
|
// Если файл не пустой |
|
if ($_FILES['file']['size'] != 0) |
|
{ |
|
// Получаем имя файла и его расширение (должно быть sql) |
|
$fupload_name = $_FILES['file']['name']; |
|
$gz = substr($fupload_name, -3) == '.gz'; |
|
$end = substr($fupload_name, -3); |
|
|
|
// Если расширение sql, тогда |
|
if ($gz || $end == 'sql') |
|
{ |
|
// Если файл не удалось загрузить, формируем сообщение с ошибкой |
|
if (! @move_uploaded_file($_FILES['file']['tmp_name'], $tempdir . $fupload_name)) |
|
die('Ошибка при загрузке файла!'); |
|
|
|
// Устанавливаем права чтения, записи, выполнения на файл |
|
@chmod($fupload_name, 0777); |
|
|
|
// Определяем флаг готовности к записи данных в БД |
|
$insert = true; |
|
} |
|
else |
|
{ |
|
// В противном случае, если расширение файла НЕ sql, формируем сообщение с ошибкой |
|
$AVE_Template->assign('msg', '<li class="highlight red"><strong>Ошибка:</strong> ' . $AVE_Template->get_config_vars('MAIN_SQL_FILE_ERROR') . '</li>'); |
|
} |
|
} |
|
|
|
// Если флаг готовности записи установлен, тогда |
|
if ($insert) |
|
{ |
|
// Еще раз провреяем наличие загруженного файла |
|
if ($fupload_name != '' && file_exists($tempdir . $fupload_name)) |
|
{ |
|
// Читаем данные из файла |
|
$handle = @fopen($tempdir . $fupload_name, 'r'); |
|
|
|
$db_q = @fread($handle, filesize($tempdir . $fupload_name)); |
|
|
|
fclose($handle); |
|
|
|
if ($gz) |
|
$db_q = gzdecode($db_q); |
|
|
|
$m_ok = 0; |
|
|
|
$m_fail = 0; |
|
|
|
// Формируем массив запросов ориентируясь по разделителю указанному в свойстве _delimiter |
|
$querys = @explode($this->_delimiter, $db_q); |
|
|
|
// Циклически обрабатываем массив, выполняя каждый запрос |
|
foreach ($querys as $val) |
|
{ |
|
if (chop($val) != '') |
|
{ |
|
$q = str_replace("\n",'',$val); |
|
|
|
$q = $q . ';'; |
|
|
|
if ($AVE_DB->Query($q)) |
|
{ |
|
$m_ok++; |
|
} |
|
else |
|
{ |
|
$m_fail++; |
|
} |
|
} |
|
} |
|
|
|
// Удаляем файл дампа |
|
@unlink($tempdir . $fupload_name); |
|
|
|
// Формируем сопроводительные сообщения |
|
$msg = '<li class="highlight green"><strong>' . $AVE_Template->get_config_vars('MAIN_RESTORE_OK') . '</strong><br /><br />' |
|
. $AVE_Template->get_config_vars('MAIN_TABLE_SUCC') |
|
. '<strong>' . $m_ok . '</strong><br/> ' |
|
. $AVE_Template->get_config_vars('MAIN_TABLE_ERROR') |
|
. '<strong><span style="color:red">' . $m_fail . '</span></strong></li>'; |
|
|
|
$AVE_Template->assign('msg', $msg); |
|
} |
|
else // В противном случае, если файл не найден, формируем сообщение с ошибкой |
|
{ |
|
$AVE_Template->assign('msg', '<li class="highlight red">'.$AVE_Template->get_config_vars('DB_REPORT_DUMP_ER').'</li>'); |
|
} |
|
} |
|
|
|
// Выполняем запись системного сообщения в журнал |
|
reportLog($AVE_Template->get_config_vars('DB_REPORT_DUMP_RECOVER')); |
|
} |
|
|
|
/** |
|
* Метод, предназначенный для удаления файла дампа на сервере |
|
* |
|
* @param string $file путь к файлу дампа |
|
*/ |
|
function databaseDumpFileDelete($file = '') |
|
{ |
|
global $AVE_DB, $AVE_Template; |
|
|
|
$file = BASE_DIR . '/tmp/backup/'. $file; |
|
|
|
if (! is_file($file)) |
|
return false; |
|
|
|
if (@unlink($file)) |
|
{ |
|
reportLog($AVE_Template->get_config_vars('DB_REPORT_DUMP_DEL_OK') . ' ('.basename($file).')'); |
|
} |
|
else |
|
{ |
|
reportLog($AVE_Template->get_config_vars('DB_REPORT_DUMP_DEL_ER') . ' ('.basename($file).')'); |
|
} |
|
|
|
header('Location:index.php?do=dbsettings&cp=' . SESSION); |
|
} |
|
|
|
/** |
|
* Метод, предназначенный для восстановления базы данных из дампа на сервере |
|
* |
|
* @param string $file путь к файлу дампа |
|
*/ |
|
function databaseDumpFileImport($file = '') |
|
{ |
|
global $AVE_DB, $AVE_Template; |
|
|
|
$insert = false; |
|
|
|
$file = BASE_DIR . '/tmp/backup/'. $file; |
|
|
|
// Если дамп не удалось создать, тогда завершаем работу |
|
if (! is_file($file)) $insert = false; |
|
|
|
// Если файл не пустой |
|
if (filesize($file) != 0) |
|
{ |
|
// Получаем имя файла и его расширение (должно быть sql) |
|
$file_name = basename($file); |
|
$gz = substr($file_name, -3)=='.gz'; |
|
$end = substr($file_name, -3); |
|
|
|
// Если расширение sql, тогда |
|
if ($gz || $end == 'sql') |
|
{ |
|
// Определяем флаг готовности к записи данных в БД |
|
$insert = true; |
|
} |
|
else |
|
{ |
|
// В противном случае, если расширение файла НЕ sql, формируем сообщение с ошибкой |
|
$AVE_Template->assign('msg', '<li class="highlight red"><strong>Ошибка:</strong> ' . $AVE_Template->get_config_vars('MAIN_SQL_FILE_ERROR') . '</li>'); |
|
} |
|
} |
|
|
|
// Если флаг готовности записи установлен, тогда |
|
if ($insert) |
|
{ |
|
// Еще раз провреяем наличие загруженного файла |
|
if ($file_name != '' && file_exists($file)) |
|
{ |
|
// Читаем данные из файла |
|
$handle = @fopen($file, 'r'); |
|
|
|
$db_q = @fread($handle, filesize($file)); |
|
|
|
fclose($handle); |
|
|
|
if($gz)$db_q=gzdecode($db_q); |
|
|
|
$m_ok = 0; |
|
|
|
$m_fail = 0; |
|
|
|
// Формируем массив запросов ориентируясь по разделителю указанному в свойстве _delimiter |
|
$querys = @explode($this->_delimiter, $db_q); |
|
|
|
// Циклически обрабатываем массив, выполняя каждый запрос |
|
foreach ($querys as $val) |
|
{ |
|
if (chop($val) != '') |
|
{ |
|
$q = str_replace("\n",'',$val); |
|
|
|
$q = $q . ';'; |
|
|
|
@$q = str_replace('%%PRFX%%', PREFIX, $q); |
|
|
|
if ($AVE_DB->Query($q)) |
|
{ |
|
$m_ok++; |
|
} |
|
else |
|
{ |
|
$m_fail++; |
|
} |
|
} |
|
} |
|
|
|
// Формируем сопроводительные сообщения |
|
$msg = '<li class="highlight green"><strong>' . $AVE_Template->get_config_vars('MAIN_RESTORE_OK') . '</strong><br /><br />' |
|
. $AVE_Template->get_config_vars('MAIN_TABLE_SUCC') |
|
. '<strong>' . $m_ok . '</strong><br/> ' |
|
. $AVE_Template->get_config_vars('MAIN_TABLE_ERROR') |
|
. '<strong><span style="color:red">' . $m_fail . '</span></strong></li>'; |
|
|
|
$AVE_Template->assign('msg', $msg); |
|
} |
|
else // В противном случае, если файл не найден, формируем сообщение с ошибкой |
|
{ |
|
$AVE_Template->assign('msg', '<li class="highlight red">'.$AVE_Template->get_config_vars('DB_REPORT_DUMP_ER').'</li>'); |
|
} |
|
} |
|
|
|
// Выполняем запись системного сообщения в журнал |
|
reportLog($AVE_Template->get_config_vars('DB_REPORT_DUMP_RECOVER') . ' ('.$file_name.')'); |
|
} |
|
|
|
/** |
|
* Метод, предназначенный для оптимизации таблиц базы данных |
|
* |
|
*/ |
|
function databaseTableOptimize() |
|
{ |
|
global $AVE_DB, $AVE_Template; |
|
|
|
if (! empty($_POST['ta']) && is_array($_POST['ta'])) |
|
{ |
|
// Выполняем запрос на оптимизацию |
|
$AVE_DB->Query("OPTIMIZE TABLE `" . implode("`, `", $_POST['ta']) . "`"); |
|
|
|
// Выполняем запись системного сообщения в журнал |
|
reportLog($AVE_Template->get_config_vars('DB_REPORT_DUMP_OPTIM')); |
|
} |
|
} |
|
|
|
/** |
|
* Метод, предназначенный для восстановления повреждённых таблиц базы данных |
|
* |
|
*/ |
|
function databaseTableRepair() |
|
{ |
|
global $AVE_DB, $AVE_Template; |
|
|
|
if (! empty($_POST['ta']) && is_array($_POST['ta'])) |
|
{ |
|
// Выполняем запрос на восстановление |
|
$AVE_DB->Query("REPAIR TABLE `" . implode("`, `", $_POST['ta']) . "`"); |
|
|
|
// Выполняем запись системного сообщения в журнал |
|
reportLog($AVE_Template->get_config_vars('DB_REPORT_DUMP_TABLE')); |
|
} |
|
} |
|
|
|
/** |
|
* Метод, предназначенный для формирования списка всех таблиц в БД |
|
* |
|
* @return string |
|
*/ |
|
function databaseTableGet() |
|
{ |
|
global $AVE_DB; |
|
|
|
$tables = ''; |
|
|
|
// Получаем список всех таблиц, которые имею префикс, указанный в конфигурации системы |
|
$sql = $AVE_DB->Query("SHOW TABLES LIKE '" . PREFIX . "_%'"); |
|
|
|
while ($row = $sql->FetchArray()) |
|
{ |
|
$tables .= '<option value="' . $row[0] . '" selected="selected">' . substr($row[0], 1+strlen(PREFIX)) . '</option>'; |
|
} |
|
|
|
$sql->Close(); |
|
|
|
// Возвращаем полученный список |
|
return $tables; |
|
} |
|
|
|
/** |
|
* Метод, предназначенный для вывода всех sql файлов в папке backup |
|
* |
|
* @return string |
|
*/ |
|
function databaseFilesGet() |
|
{ |
|
$dir = BASE_DIR . '/tmp/backup/'; |
|
|
|
if($handle = opendir($dir)) |
|
{ |
|
$files = array(); |
|
|
|
while (false !== ($file = readdir($handle))) |
|
{ |
|
if ($file != "." && $file != ".." && (substr($file, -3) == 'sql' || substr($file, -2) == 'gz')) |
|
{ |
|
if(is_file($dir . '/' . $file)) |
|
{ |
|
$files[] = array( |
|
'name' => $file, |
|
'data' => (filectime($dir . '/' . $file)), |
|
'size' => (filesize($dir . '/' . $file)) |
|
); |
|
} |
|
} |
|
} |
|
closedir($handle); |
|
} |
|
|
|
return msort($files, 'data', null, SORT_DESC); |
|
} |
|
} |
|
?>
|