Файл: CMS/core/classes/dump.class.php
Строк: 294
<?php
if (!defined('CMS')) { die('Access Denied!'); }
//error_reporting( E_ALL );
//ini_set( 'display_errors', 1 );
//ini_set( 'html_errors', false );
//ini_set( 'error_log', 'error.log' );
// Максимальное время выполнения скрипта в секундах
// 0 - без ограничений
define('TIME_LIMIT', 0);
// Ограничение размера данных доставаемых за одно обращения к БД (в мегабайтах)
// Нужно для ограничения количества памяти пожираемой сервером при дампе очень объемных таблиц
define('LIMIT', 1);
// mysql сервер
define('DBHOST', 'localhost:3306');
// Базы данных, если сервер не разрешает просматривать список баз данных,
// и ничего не показывается после авторизации. Перечислите названия через запятую
define('DBNAMES', '');
// Кодировка соединения с MySQL
// auto - автоматический выбор (устанавливается кодировка таблицы), cp1251 - windows-1251, и т.п.
define('CHARSET', 'auto');
// Кодировка соединения с MySQL при восстановлении
// На случай переноса со старых версий MySQL (до 4.1), у которых не указана кодировка таблиц в дампе
// При добавлении 'forced->', к примеру 'forced->cp1251', кодировка таблиц при восстановлении будет принудительно заменена на cp1251
// Можно также указывать сравнение нужное к примеру 'cp1251_ukrainian_ci' или 'forced->cp1251_ukrainian_ci'
define('RESTORE_CHARSET', 'cp1251');
// Включить сохранение настроек и последних действий
// Для отключения установить значение 0
define('SC', 1);
// Типы таблиц у которых сохраняется только структура, разделенные запятой
define('ONLY_CREATE', 'MRG_MyISAM,MERGE,HEAP,MEMORY');
// Глобальная статистика
// Для отключения установить значение 0
define('GS', 1);
if (!defined('DS')) define('DS', '/');
// Дальше ничего редактировать не нужно
$is_safe_mode = ini_get('safe_mode') == '1' ? 1 : 0;
if (!$is_safe_mode && function_exists('set_time_limit')) set_time_limit(TIME_LIMIT);
header('Expires: Tue, 1 Jul 2003 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Pragma: no-cache');
class dumper {
    var $connect_id;
    var $rootpath;
    var $stat;
    function dumper($rootpath = '', $stat = true) {
        global $config;
        $this->connect_id = @mysql_connect($config['db_host'], $config['db_user'], $config['db_pass']);
        if( $rootpath == '' ) {
            $rootpath = './';
        }
        $this->rootpath = $rootpath;
        $this->SET['show_stat']       = $stat;
        $this->SET['last_action']     = 0;
        $this->SET['last_db_backup']  = '';
        $this->SET['tables']          = '';
        $this->SET['comp_method']     = 2;
        $this->SET['comp_level']      = 9;
        $this->SET['last_db_restore'] = '';
        $this->tabs    = 0;
        $this->records = 0;
        $this->size    = 0;
        $this->comp    = 0;
        // Версия MySQL вида 40101
        preg_match("/^(d+).(d+).(d+)/", mysql_get_server_info(), $m);
        $this->mysql_version = sprintf("%d%02d%02d", $m[1], $m[2], $m[3]);
        $this->only_create = explode(',', ONLY_CREATE);
        $this->forced_charset  = false;
        $this->restore_charset = $this->restore_collate = '';
        if (preg_match("/^(forced->)?(([a-z0-9]+)(_w+)?)$/", RESTORE_CHARSET, $matches)) {
            $this->forced_charset  = $matches[1] == 'forced->';
            $this->restore_charset = $matches[3];
            $this->restore_collate = !empty($matches[4]) ? ' COLLATE ' . $matches[2] : '';
        }
    }
    function backup($tbName = '', $dbName = '', $dbMethod = 1, $dbLevel = 9) {
        global $config;
        if (!$this->connect_id) {
            die('ОШИБКА! Нет соединения с базой данных!');
        }
        if (!$this->rootpath) {
            die('ОШИБКА! Путь к папке не найден!');
        }
        $this->SET['last_action']     = 0;
        $this->SET['comp_level']      = !empty($dbLevel) ? intval($dbLevel) : 0;
        $this->SET['comp_method']     = !empty($dbMethod) ? intval($dbMethod) : 0;
        $this->SET['last_db_backup']  = !empty($dbName) ? $dbName : $config['db_name'];
        $this->SET['tables_exclude']  = !empty($tbName) ? 1 : 0;
        $this->SET['tables']          = !empty($tbName) ? $tbName : '';
        $this->fn_save();
        if (!empty($this->SET['tables'])) {
            $this->SET['tables'] = explode(',', $this->SET['tables']);
            foreach($this->SET['tables'] AS $table) {
                $table = preg_replace("/[^w*?^]/", "", $table);
                $pattern = array( "/?/", "/*/");
                $replace = array( ".", ".*?");
                $tbls[] = preg_replace($pattern, $replace, $table);
            }
        }
        else $this->SET['tables_exclude'] = 1;
        if ($this->SET['comp_level'] == 0) { $this->SET['comp_method'] = 0; }
        $db = $this->SET['last_db_backup'];
        if (!$db) {
            die('ОШИБКА! Не указана база данных!');
        }
        $tables = array();
        $result = mysql_query("SHOW TABLES");
        $all    = 0;
        while($row = mysql_fetch_array($result)) {
            $status = 0;
            if (!empty($tbls)) {
                foreach($tbls as $table) {
                    $exclude = preg_match("/^^/", $table) ? true : false;
                    if (!$exclude) {
                        if (preg_match("/^{$table}$/i", $row[0])) { $status = 1; }
                        $all = 1;
                    }
                    if ($exclude && preg_match("/{$table}$/i", $row[0])) { $status = -1; }
                }
            }
            else $status = 1;
            if ($status >= $all) { $tables[] = $row[0]; }
        }
        // Определение размеров таблиц
        $tabs        = count($tables);
        $result      = mysql_query("SHOW TABLE STATUS");
        $tabinfo     = array();
        $tab_charset = array();
        $tab_type    = array();
        $tabinfo[0]  = 0;
        $info        = '';
        while($item = mysql_fetch_assoc($result)) {
            if (in_array($item['Name'], $tables)) {
                $item['Rows'] = empty($item['Rows']) ? 0 : $item['Rows'];
                $tabinfo[0] += $item['Rows'];
                $tabinfo[$item['Name']] = $item['Rows'];
                $this->size += $item['Data_length'];
                $tabsize[$item['Name']] = 1 + round(LIMIT * 1048576 / ($item['Avg_row_length'] + 1));
                if ($item['Rows']) $info .= "|" . $item['Rows'];
                if (!empty($item['Collation']) && preg_match("/^([a-z0-9]+)_/i", $item['Collation'], $m)) {
                    $tab_charset[$item['Name']] = $m[1];
                }
                $tab_type[$item['Name']] = isset($item['Engine']) ? $item['Engine'] : $item['Type'];
            }
        }
        $show = 10 + $tabinfo[0] / 50;
        $info = $tabinfo[0] . $info;
        $nms = !empty($tbName) ? $tbName : $db;
        $name = $nms . '_' . date('Y-m-d_H-i');
        $fp = $this->fn_open($name, 'w');
        $this->fn_write($fp, "#SKD101|{$db}|{$tabs}|" . date("Y.m.d H:i:s") ."|{$info}nn");
        $t = 0;
        $result = mysql_query("SET SQL_QUOTE_SHOW_CREATE = 1");
        // Кодировка соединения по умолчанию
        if ($this->mysql_version > 40101 && CHARSET != 'auto') {
            mysql_query("SET NAMES '" . CHARSET . "'") or trigger_error ('Неудается изменить кодировку соединения.');
            $last_charset = CHARSET;
        }
        else $last_charset = '';
        foreach($tables as $table) {
            // Выставляем кодировку соединения соответствующую кодировке таблицы
            if ($this->mysql_version > 40101 && $tab_charset[$table] != $last_charset) {
                if (CHARSET == 'auto') {
                    mysql_query("SET NAMES '" . $tab_charset[$table] . "'") or trigger_error ('Неудается изменить кодировку соединения.');
                    $last_charset = $tab_charset[$table];
                }
                else
                {
                    die('Кодировка соединения и таблицы не совпадает: Таблица `'. $table .'` -> ' . $tab_charset[$table] . ' (соединение '  . CHARSET . ')');
                }
            }
            // Создание таблицы
            $result = mysql_query("SHOW CREATE TABLE `{$table}`");
            $tab = mysql_fetch_array($result);
            $tab = preg_replace('/(default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP|DEFAULT CHARSET=w+|COLLATE=w+|character set w+|collate w+)/i', '/*!40101 \1 */', $tab);
            $this->fn_write($fp, "DROP TABLE IF EXISTS `{$table}`;n{$tab[1]};nn");
            // Проверяем нужно ли дампить данные
            if (in_array($tab_type[$table], $this->only_create)) { continue; }
            // Опредеделяем типы столбцов
            $NumericColumn = array();
            $result = mysql_query("SHOW COLUMNS FROM `{$table}`");
            $field = 0;
            while($col = mysql_fetch_row($result)) {
                $NumericColumn[$field++] = preg_match("/^(w*int|year)/", $col[1]) ? 1 : 0;
            }
            $fields = $field;
            $from = 0;
            $limit = $tabsize[$table];
            $limit2 = round($limit / 3);
            if ($tabinfo[$table] > 0) {
                if ($tabinfo[$table] > $limit2) {
                    //echo tpl_s(0, $t / $tabinfo[0]);
                }
                $i = 0;
                $this->fn_write($fp, "INSERT INTO `{$table}` VALUES");
                while(($result = mysql_query("SELECT * FROM `{$table}` LIMIT {$from}, {$limit}")) && ($total = mysql_num_rows($result))) {
                    while($row = mysql_fetch_row($result)) {
                        $i++; $t++;
                        for($k = 0; $k < $fields; $k++) {
                             if ($NumericColumn[$k]) {
                                 $row[$k] = isset($row[$k]) ? $row[$k] : "NULL";
                             }
                             else
                             {
                                 $row[$k] = isset($row[$k]) ? "'" . mysql_escape_string($row[$k]) . "'" : "NULL";
                             }
                        }
                        $this->fn_write($fp, ($i == 1 ? "" : ",") . "n(" . implode(", ", $row) . ")");
                    }
                    @mysql_free_result($result);
                    if ($total < $limit) { break; }
                    $from += $limit;
                }
                $this->fn_write($fp, ";nn");
            }
        }
        $this->tabs = $tabs;
        $this->records = $tabinfo[0];
        $this->comp = $this->SET['comp_method'] * 10 + $this->SET['comp_level'];
        $this->fn_close($fp);
        if ($this->SET['show_stat']) {
            $alllines = fn_int($tabinfo[0]);
            $basesize = round($this->size / 1048576, 2);
            $filesize = round(filesize($this->rootpath . $this->filename) / 1048576, 2);
            return <<<HTML
              - Резервная копия БД `{$db}` создана.<br />
              - Размер БД: {$basesize} МБ<br />
              - Размер файла: {$filesize} МБ<br />
              - Таблиц обработано: {$tabs}<br />
              - Строк обработано: {$alllines}<br />
HTML;
        }
    }
    function restore($flName = '', $dbName = '') { // Восстановление БД из резервной копии
        global $config;
        $this->SET['last_action']     = 1;
        $this->SET['last_db_restore'] = !empty($dbName) ? $dbName : $config['db_name'];
        $file                         = !empty($flName) ? $flName : '';
        $this->fn_save();
        $db = $this->SET['last_db_restore'];
        if (!$db) {
            die('ОШИБКА! Не указана база данных!');
        }
        //Подключение к БД
        mysql_select_db($db) or trigger_error('Не удается выбрать базу данных.<br />' . mysql_error(), E_USER_ERROR);
        // Определение формата файла
        if(preg_match("/^(.+?).sql(.(bz2|gz))?$/", $file, $matches)) {
            if (isset($matches[3]) && $matches[3] == 'bz2') {
                $this->SET['comp_method'] = 2;
            }
            elseif (isset($matches[2]) &&$matches[3] == 'gz'){
                $this->SET['comp_method'] = 1;
            }
            else {
                $this->SET['comp_method'] = 0;
            }
            $this->SET['comp_level'] = '';
            if (!file_exists($this->rootpath. $file)) {
                die('ОШИБКА! Файл не найден!');
            }
            $file = $matches[1];
        }
        else {
            die('ОШИБКА! Не выбран файл!');
        }
        //echo tpl_l(str_repeat("-", 60));
        $fp = $this->fn_open($file, 'r'); // Чтение файла
        $this->file_cache = $sql = $table = $insert = '';
        $is_skd = $query_len = $execute = $q =$t = $i = $aff_rows = 0;
        $limit  = 300;
        $index  = 4;
        $tabs   = 0;
        $cache  = '';
        $info   = array();
        // Установка кодировки соединения
        if ($this->mysql_version > 40101 && (CHARSET != 'auto' || $this->forced_charset)) { // Кодировка по умолчанию, если в дампе не указана кодировка
            mysql_query("SET NAMES '" . $this->restore_charset . "'") or trigger_error('Неудается изменить кодировку соединения.<br />' . mysql_error(), E_USER_ERROR);
            $cache .= '- Установлена кодировка соединения `' . $this->restore_charset . '`.';
            $last_charset = $this->restore_charset;
        }
        else $last_charset = '';
        $last_showed = '';
        while(($str = $this->fn_read_str($fp)) !== false) {
            if (empty($str) || preg_match("/^(#|--)/", $str)) {
                if (!$is_skd && preg_match("/^#SKD101|/", $str)) {
                    $info = explode('|', $str);
                    //echo tpl_s(0, $t / $info[4]);
                    $is_skd = 1;
                }
                continue;
            }
            $query_len += strlen($str);
            if (!$insert && preg_match("/^(INSERT INTO `?([^` ]+)`? .*?VALUES)(.*)$/i", $str, $m)) {
                if ($table != $m[2]) {
                    $table = $m[2];
                    $tabs++;
                    $cache .= "<br />- Таблица `{$table}`.";
                    $last_showed = $table;
                    $i = 0;
                    //if ($is_skd)
                    //echo tpl_s(100 , $t / $info[4]);
                }
                $insert = $m[1] . ' ';
                $sql .= $m[3];
                $index++;
                $info[$index] = isset($info[$index]) ? $info[$index] : 0;
                $limit = round($info[$index] / 20);
                $limit = $limit < 300 ? 300 : $limit;
                if ($info[$index] > $limit){
                    //echo $cache;
                    //$cache = '';
                    //echo tpl_s(0 / $info[$index], $t / $info[4]);
                }
            }
            else {
                $sql .= $str;
                if ($insert) {
                    $i++;
                    $t++;
                    if ($is_skd && $info[$index] > $limit && $t % $limit == 0) {
                        //echo tpl_s($i / $info[$index], $t / $info[4]);
                    }
                }
            }
            if (!$insert && preg_match("/^CREATE TABLE (IF NOT EXISTS )?`?([^` ]+)`?/i", $str, $m) && $table != $m[2]) {
                $table = $m[2];
                $insert = '';
                $tabs++;
                $is_create = true;
                $i = 0;
            }
            if ($sql) {
                if (preg_match("/;$/", $str)) {
                    $sql = rtrim($insert . $sql, ";");
                    if (empty($insert)) {
                        if ($this->mysql_version < 40101) {
                            $sql = preg_replace("/ENGINEs?=/", "TYPE=", $sql);
                        }
                        elseif (preg_match("/CREATE TABLE/i", $sql)){
                            // Выставляем кодировку соединения
                            if (preg_match("/(CHARACTER SET|CHARSET)[=s]+(w+)/i", $sql, $charset)) {
                                if (!$this->forced_charset && $charset[2] != $last_charset) {
                                    if (CHARSET == 'auto') {
                                        mysql_query("SET NAMES '" . $charset[2] . "'") or trigger_error('Неудается изменить кодировку соединения.<br />' . $sql . '<br />' . mysql_error(), E_USER_ERROR);
                                        $cache .= '- Установлена кодировка соединения `' . $charset[2] . '`.';
                                        $last_charset = $charset[2];
                                    }
                                    else {
                                        $cache .= '- Кодировка соединения и таблицы не совпадает:<br />';
                                        $cache .= '- Таблица `'. $table .'` -> ' . $charset[2] . ' (соединение '  . $this->restore_charset . ')';
                                    }
                                }
                                // Меняем кодировку если указано форсировать кодировку
                                if ($this->forced_charset) {
                                    $sql = preg_replace("/(/*!d+s)?((COLLATE)[=s]+)w+(s+*/)?/i", '', $sql);
                                    $sql = preg_replace("/((CHARACTER SET|CHARSET)[=s]+)w+/i", "\1" . $this->restore_charset . $this->restore_collate, $sql);
                                }
                            }
                            elseif(CHARSET == 'auto') { // Вставляем кодировку для таблиц, если она не указана и установлена auto кодировка
                                $sql .= ' DEFAULT CHARSET=' . $this->restore_charset . $this->restore_collate;
                                if ($this->restore_charset != $last_charset) {
                                    mysql_query("SET NAMES '" . $this->restore_charset . "'") or trigger_error('Неудается изменить кодировку соединения.<br />' . $sql . '<br />' . mysql_error(), E_USER_ERROR);
                                    $cache .= '- Установлена кодировка соединения `' . $this->restore_charset . '`.';
                                    $last_charset = $this->restore_charset;
                                }
                            }
                        }
                        if ($last_showed != $table) {
                            $cache .= "<br />- Таблица `{$table}`.";
                            $last_showed = $table;
                        }
                    }
                    elseif($this->mysql_version > 40101 && empty($last_charset)) { // Устанавливаем кодировку на случай если отсутствует CREATE TABLE
                        mysql_query("SET $this->restore_charset '" . $this->restore_charset . "'") or trigger_error('Неудается изменить кодировку соединения.<br />' . $sql . '<br />' . mysql_error(), E_USER_ERROR);
                        $cache .= '- Установлена кодировка соединения `' . $this->restore_charset . '`.';
                        $last_charset = $this->restore_charset;
                    }
                    $insert = '';
                    $execute = 1;
                }
                if ($query_len >= 65536 && preg_match("/,$/", $str)) {
                    $sql = rtrim($insert . $sql, ",");
                    $execute = 1;
                }
  
                if ($execute) {
          
                    $q++;
                    mysql_query($sql) or trigger_error('Неправильный запрос.<br />' . mysql_error(), E_USER_ERROR);
                    if (preg_match("/^insert/i", $sql)) {
                        $aff_rows += mysql_affected_rows();
                    }
                    $sql = '';
                    $query_len = 0;
                    $execute = 0;
                }
            }
        }
        if ($this->SET['show_stat']) {
            return <<<HTML
                - БД восстановлена из резервной копии.<br />
                {$cache}<br />
                - Дата создания копии: {$info[3]}<br />
                - Запросов к БД: {$q}<br />
                - Таблиц создано: {$tabs}<br />
                - Строк добавлено: {$aff_rows}<br />
HTML;
        }
        $this->fn_close($fp);
    }
    function db_select() {
        if (DBNAMES != '') {
            $items = explode(',', trim(DBNAMES));
            foreach($items as $item) {
                if (mysql_select_db($item)) {
                    $tables = mysql_query("SHOW TABLES");
                    if ($tables) {
                        $tabs = mysql_num_rows($tables);
                        $dbs[$item] = "{$item} ({$tabs})";
                    }
                }
            }
        }
        else
        {
            $result = mysql_query("SHOW DATABASES");
            $dbs = array();
            while($item = mysql_fetch_array($result)) {
                if (mysql_select_db($item[0])) {
                    $tables = mysql_query("SHOW TABLES");
                     if ($tables) {
                         $tabs = mysql_num_rows($tables);
                         $dbs[$item[0]] = "{$item[0]} ({$tabs})";
                     }
                }
            }
        }
        return $dbs;
    }
    function file_select() {
        $files = array('' => ' ');
        if (is_dir($this->rootpath) && $handle = opendir($this->rootpath)) {
            while (false !== ($file = readdir($handle))) {
                if (preg_match("/^.+?.sql(.(gz|bz2))?$/", $file)) {
                    $files[$file] = $file;
                }
            }
            closedir($handle);
        }
        ksort($files);
        return $files;
    }
    function fn_open($name, $mode) {
        if ($this->SET['comp_method'] == 2) {
            $this->filename = "{$name}.sql.bz2";
            return bzopen($this->rootpath . $this->filename, "{$mode}b{$this->SET['comp_level']}");
        }
        elseif ($this->SET['comp_method'] == 1) {
            $this->filename = "{$name}.sql.gz";
            return gzopen($this->rootpath . $this->filename, "{$mode}b{$this->SET['comp_level']}");
        }
        else
        {
            $this->filename = "{$name}.sql";
            return fopen($this->rootpath . $this->filename, "{$mode}b");
        }
    }
    function fn_write($fp, $str) {
        if ($this->SET['comp_method'] == 2) {
            bzwrite($fp, $str);
        }
        elseif ($this->SET['comp_method'] == 1) {
            gzwrite($fp, $str);
        }
        else
        {
            fwrite($fp, $str);
        }
    }
    function fn_read($fp) {
        if ($this->SET['comp_method'] == 2) {
            return bzread($fp, 4096);
        }
        elseif ($this->SET['comp_method'] == 1) {
            return gzread($fp, 4096);
        }
        else
        {
            return fread($fp, 4096);
        }
    }
    function fn_read_str($fp) {
        $string = '';
        $this->file_cache = ltrim($this->file_cache);
        $pos = strpos($this->file_cache, "n", 0);
        if ($pos < 1) {
            while (!$string && ($str = $this->fn_read($fp))) {
                $pos = strpos($str, "n", 0);
                if ($pos === false) {
                    $this->file_cache .= $str;
                }
                else
                {
                    $string = $this->file_cache . substr($str, 0, $pos);
                    $this->file_cache = substr($str, $pos + 1);
                }
            }
            if (!$str) {
                if ($this->file_cache) {
                    $string = $this->file_cache;
                    $this->file_cache = '';
                    return trim($string);
                }
                return false;
            }
        }
        else
        {
            $string = substr($this->file_cache, 0, $pos);
            $this->file_cache = substr($this->file_cache, $pos + 1);
        }
        return trim($string);
    }
    function fn_close($fp) {
        if ($this->SET['comp_method'] == 2) {
            bzclose($fp);
        }
        elseif ($this->SET['comp_method'] == 1) {
            gzclose($fp);
        }
        else
        {
            fclose($fp);
        }
        @chmod($this->rootpath . $this->filename, 0644);
        $this->fn_index();
    }
    function fn_select($items, $selected) {
        $select = '';
        foreach($items as $key => $value) {
            $select .= $key == $selected ? "<option value='{$key}' selected='selected'>{$value}" : "<option value='{$key}'>{$value}";
        }
        return $select;
    }
    function fn_save() {
/*
        if (SC) {
            $ne = !file_exists($this->rootpath . "dumper.cfg.php");
            $fp = fopen($this->rootpath . "dumper.cfg.php", "wb");
            fwrite($fp, "<?phpn$this->SET = " . fn_arr2str($this->SET) . "n?>");
            fclose($fp);
            if ($ne) @chmod($this->rootpath . "dumper.cfg.php", 0644);
            $this->fn_index();
        }
*/
    }
    function fn_index() {
/*
        if (!file_exists($this->rootpath . 'index.html')) {
            $fh = fopen($this->rootpath . 'index.html', 'wb');
            fwrite($fh, tpl_backup_index());
            fclose($fh);
            @chmod($this->rootpath . 'index.html', 0644);
        }
*/
    }
}
function fn_int($num) {
    return number_format($num, 0, ',', ' ');
}
function fn_arr2str($array) {
    $str = "array(n";
    foreach ($array as $key => $value) {
        if (is_array($value)) {
            $str .= "'$key' => " . fn_arr2str($value) . ",nn";
        }
        else
        {
            $str .= "'$key' => '" . str_replace("'", "'", $value) . "',n";
        }
    }
    return $str . ")";
}
?>