【问题标题】:PDO MySQL backups functionPDO MySQL 备份功能
【发布时间】:2013-08-19 04:47:24
【问题描述】:

这里有这个功能 http://davidwalsh.name/backup-mysql-database-php

已经在互联网上流传了一段时间并且非常有名,但它使用的是旧的 MySQL API。有没有人有相同的但在 PDO 中?如果没有,有人想做一个吗?

有没有可能,我在某处读到 PDO 不执行 SHOW CREATE TABLE - 对吗?

【问题讨论】:

    标签: php mysql pdo backup database-backups


    【解决方案1】:

    那个备份脚本很荒谬,没有人应该制作另一个版本。我以前看过那个脚本,也有类似的尝试,但他们有很多问题:

    • 不在反引号中分隔表名
    • 不处理空值
    • 不处理字符集
    • 不处理二进制数据
    • 不备份 VIEWs
    • 不备份触发器或存储过程或存储函数或事件
    • 使用过时的 mysql 扩展(但这就是您想要 PDO 版本的原因,不是吗?)
    • 使用 addlashes() 代替适当的 MySQL 转义函数。
    • 在输出全部内容之前,将 all 表的 all 数据附加到一个非常长的字符串中。这意味着您必须能够将整个数据库存储在一个字符串中,这几乎肯定会超出您的 PHP 最大内存限制。

    另请参阅我过去对不幸的 David Walsh 备份脚本的回答:


    你的评论:

    阅读您链接到的页面上的 cmets。很多人已经发现了问题,有些人已经修复或至少提出了建议。

    我认为这个脚本将所有内容附加到一个字符串中的事实是一个交易破坏者,但更改脚本以打开输出文件首先应该不难,然后输出每个循环期间行的数据,然后在循环后关闭文件。这很简单,我不确定为什么脚本不这样做。但很明显,该脚本没有经过很好的测试。

    但无论如何,我不会尝试重新发明这个轮子。 Mysqldump 或 mydumper 可以很好地完成这项工作。 FWIW,您不必在数据库所在的同一台服务器上运行 mysqldump 。 mysqldump 支持--host 的选项,因此您可以在任何地方运行 mysqldump 来备份远程数据库,只要防火墙不阻止您的客户端连接。基本上,如果您可以从某个客户端主机将 PHP 应用程序连接到数据库,则可以连接 mysqldump。

    如果这真的不是一个选项,那么我会使用 phpmyadmin 的数据库转储功能。这些都是成熟且经过充分测试的,它们可以正确地转储所有内容。这是一篇描述如何使用转储功能的文章:

    http://www.techrepublic.com/blog/smb-technologist/import-and-export-databases-using-phpmyadmin/


    [从你的答案中复制我的 cmets:]

    这是进入代码审查,这不是 StackOverflow 的目的。但简而言之:

    • 没有对 NULL 的适当支持(您将它们转换为 '');
    • 表名的分隔不一致;
    • 使用非 ANSI 双引号作为字符串分隔符;
    • 在大表上使用缓冲查询将打破 PHP 最大内存限制;
    • 为一个大表附加所有行将打破 PHP 最大内存限制;
    • 使用 addlashes() 代替 PDO::quote();
    • 仅在函数结束时检查查询错误;
    • 不检查文件创建失败;
    • gzip 扩展可能无法加载
    • 另外,可能仍然不支持 UTF8 数据。

    但它正在到达那里,不是吗?

    是的,这比原来的 David Walsh 剧本要好。 :-)

    NULL 到 '' 有什么问题?

    NULL 与 SQL 中的 '' 不同(Oracle 除外,但在这种情况下它们不符合 SQL 标准)。见MySQL, better to insert NULL or empty string?

    表结构必须非常大才能达到最大内存。每个插入行都单独写入文件,因此,对于最大内存,行必须非常大。

    我误读了有关内存限制问题的代码。您正在为每一行编写输出,所以没关系(除非该行包含 1GB 的 blob 或其他东西)。

    但是您不应该只输出一个带有逗号分隔行集的单个 INSERT 语句。即使mysqldump --extended-insert 输出有限长度的数据,然后开始一个新的INSERT 语句。标准是 INSERT 语句的长度是否适合 --net-buffer-length 的选项参数。

    "" 字符串分隔符有什么问题?我如何获得 ANSI 的?

    在 ANSI SQL 中,单引号 '' 用于分隔字符串文字或日期文字。双引号“”用于分隔标识符,如表名或列名。默认情况下,MySQL 将它们视为相同,但这是非标准的。见Do different databases use different name quote?。如果您尝试在拥有SET SQL_MODE=ANSI_QUOTES 的 MySQL 服务器上导入备份数据,则导入将失败。

    哪些表没有分隔?

    示例:query('SELECT * FROM '.$table); 以及实际上在查询中使用 $table 的其他每种情况。在脚本输出的 INSERT 语句中,您只对表进行了一次分隔。

    所有的$tables都没有分隔,他们都需要用“”吗?

    MySQL 始终将反引号识别为标识符分隔符,并将单引号识别为字符串/日期。但是双引号会根据我提到的 SQL_MODE 改变含义。您不能假设哪个 SQL_MODE 对您还原的 MySQL 实例有效,因此最好使用反引号作为标识符,使用单引号作为字符串。您在查询表时对它们进行分隔的原因是您的表名可能是 SQL 保留字或包含特殊字符等。

    你能在mysql中插入不带分隔符的浮点数,还是需要''?谢谢

    您可以插入所有没有分隔符的数字类型。只有字符串和日期需要分隔符。请参阅 dev.mysql.com/doc/refman/5.6/en/literals.html

    【讨论】:

    • 哇。这是很好的反馈。我将把它放到 PDO 中,并解决你在这里提出的一些问题:- 反引号、NULLS、字符集、addlashes、isset 和内存都可以解决和修复。我不太关心视图、触发器、事件或二进制数据。不过,你必须给大卫一些爱,他正在朝着人类的正确方向迈出一步。如果您不能使用 mysqldump/shell 命令,您建议改用什么?
    • 感谢您的回复,是的,我花了一个小时阅读每条评论,按照您说的进行了修改,首先创建文件,然后将每一行写入文件 - 这就是我会做的。 mysql远程转储很有趣,但对我没有用。我正在制作一个简单的 CRM 软件包,客户购买并上传到他们的服务器。当他们退出 crm 时(如果 x 次没有完成),它会将他们的数据备份保存到根文件夹
    • 还注意到一些评论者抱怨转储函数不处理 UTF8 数据。我建议你应该编写 David Walsh 从未做过的测试。创建一个包含各种表、空和非空数据、国际字符等的示例数据库,并确保它可以备份和恢复。您可以立即执行,也可以在客户抱怨时在紧急模式下执行。
    【解决方案2】:

    对于任何寻找类似于 mysqldump 的功能的人,这里是最新的草案,上面/下面的 cmets 中讨论的缺陷已经解决。

    require 'login.php';
    $DBH = new PDO("mysql:host=$db_hostname;dbname=$db_database; charset=utf8", $db_username, $db_password);
    
    //put table names you want backed up in this array.
    //leave empty to do all
    $tables = array();
    
    backup_tables($DBH, $tables);
    
    function backup_tables($DBH, $tables) {
        $DBH->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_NATURAL);
    
        //Script Variables
        $compression = false;
        $BACKUP_PATH = "";
        $nowtimename = time();
    
        //create/open files
        if ($compression) {
            $zp = gzopen($BACKUP_PATH . $nowtimename . '.sql.gz', "a9");
        } else {
            $handle = fopen($BACKUP_PATH . $nowtimename . '.sql', 'a+');
        }
    
        //array of all database field types which just take numbers
        $numtypes = array('tinyint', 'smallint', 'mediumint', 'int', 'bigint', 'float', 'double', 'decimal', 'real');
    
        //get all of the tables
        if (empty($tables)) {
            $pstm1 = $DBH->query('SHOW TABLES');
            while ($row = $pstm1->fetch(PDO::FETCH_NUM)) {
                $tables[] = $row[0];
            }
        } else {
            $tables = is_array($tables) ? $tables : explode(',', $tables);
        }
    
        //cycle through the table(s)
    
        foreach ($tables as $table) {
            $result = $DBH->query("SELECT * FROM $table");
            $num_fields = $result->columnCount();
            $num_rows = $result->rowCount();
    
            $return = "";
            //uncomment below if you want 'DROP TABLE IF EXISTS' displayed
            //$return.= 'DROP TABLE IF EXISTS `'.$table.'`;';
    
            //table structure
            $pstm2 = $DBH->query("SHOW CREATE TABLE $table");
            $row2 = $pstm2->fetch(PDO::FETCH_NUM);
            $ifnotexists = str_replace('CREATE TABLE', 'CREATE TABLE IF NOT EXISTS', $row2[1]);
            $return .= "\n\n" . $ifnotexists . ";\n\n";
    
            if ($compression) {
                gzwrite($zp, $return);
            } else {
                fwrite($handle, $return);
            }
            $return = "";
    
            //insert values
            if ($num_rows) {
                $return = 'INSERT INTO `' . $table . '` (';
                $pstm3 = $DBH->query("SHOW COLUMNS FROM $table");
                $count = 0;
                $type = array();
    
                while ($rows = $pstm3->fetch(PDO::FETCH_NUM)) {
                    if (stripos($rows[1], '(')) {
                        $type[$table][] = stristr($rows[1], '(', true);
                    } else {
                        $type[$table][] = $rows[1];
                    }
    
                    $return .= "`" . $rows[0] . "`";
                    $count++;
                    if ($count < ($pstm3->rowCount())) {
                        $return .= ", ";
                    }
                }
    
                $return .= ")" . ' VALUES';
    
                if ($compression) {
                    gzwrite($zp, $return);
                } else {
                    fwrite($handle, $return);
                }
                $return = "";
            }
            $count = 0;
            while ($row = $result->fetch(PDO::FETCH_NUM)) {
                $return = "\n\t(";
    
                for ($j = 0; $j < $num_fields; $j++) {
    
                    //$row[$j] = preg_replace("\n","\\n",$row[$j]);
    
                    if (isset($row[$j])) {
    
                        //if number, take away "". else leave as string
                        if ((in_array($type[$table][$j], $numtypes)) && (!empty($row[$j]))) {
                            $return .= $row[$j];
                        } else {
                            $return .= $DBH->quote($row[$j]);
                        }
                    } else {
                        $return .= 'NULL';
                    }
                    if ($j < ($num_fields - 1)) {
                        $return .= ',';
                    }
                }
                $count++;
                if ($count < ($result->rowCount())) {
                    $return .= "),";
                } else {
                    $return .= ");";
                }
                if ($compression) {
                    gzwrite($zp, $return);
                } else {
                    fwrite($handle, $return);
                }
                $return = "";
            }
            $return = "\n\n-- ------------------------------------------------ \n\n";
            if ($compression) {
                gzwrite($zp, $return);
            } else {
                fwrite($handle, $return);
            }
            $return = "";
        }
    
        $error1 = $pstm2->errorInfo();
        $error2 = $pstm3->errorInfo();
        $error3 = $result->errorInfo();
        echo $error1[2];
        echo $error2[2];
        echo $error3[2];
    
        if ($compression) {
            gzclose($zp);
        } else {
            fclose($handle);
        }
    }
    

    【讨论】:

    • 运行脚本返回以下错误:致命错误:在非对象上调用成员函数columnCount() ...也许最好将代码提交到Github,以便每个人都可以为改进做出贡献代码并修复错误。
    • 好的,会的。但是如果你有那个错误信息,听起来你还没有连接到 mysql,即密码、用户名、数据库名或主机名有问题
    • @Lan,如果他没有连接,他会在调用 columnCount() 之前得到一个错误。您在此脚本中没有很好的错误检查来捕获失败的语句。
    • @Lan,例如,如果他正在备份一个名为order 的表怎么办?这是一个保留字,所以当它运行SELECT * FROM order 时,您没有分隔表名会导致语法错误。但是您不会检查query() 的返回值指示的错误,因此您永远不会知道。
    【解决方案3】:

    按照https://stackoverflow.com/a/18281687/2259391 的建议,使用mysqldump 和exec。归结为:

    <?php
    
    function importDatabase($host, $user, $password, $database, $backupFilePath)
    {
        //returns true iff successfull
        return exec('mysqlimport --host '. $host .' --user '. $user .' --password '. $password .' '. $database .' '.targetFilePath) === 0;
    }
    
    function exportDatabase($host, $user, $password, $database, $targetFilePath)
    {
        //returns true iff successfull
        return exec('mysqldump --host '. $host .' --user '. $user .' --password '. $password .' '. $database .' --result-file='.targetFilePath) === 0;
    }
    

    【讨论】:

      【解决方案4】:

      所有PDO 和ext/mysql 都是将命令包装到底层数据库(本例中为MySQL)。也就是说,没有什么可以阻止PDO 运行SHOW CREATE TABLE 或其他命令。

      出于所有意图和目的,您几乎可以替换:

      - $link = mysql_connect($host,$user,$pass);
      - mysql_select_db($name,$link);
      + $link = new PDO("mysql:host=$host;dbname=$name", $user, $pass);
      

      而不是

      $result = mysql_query($query);
      mysql_fetch_assoc($result);
      

      使用

      $result = $link->query($query);
      $result->fetch();
      

      【讨论】:

      • 是的,我知道它可以很容易地转换为 PDO,但我真的很懒,希望其他人也能做到。你能做到吗?我敢打赌你不敢这样做
      • @Lan 要求某人在 Stackoverflow 上为您编写代码通常是不受欢迎的,大多数人(包括我自己)不会这样做。问题和答案的重点是提供概念性帮助,适用于其他人以后可以从中学习的各种情况
      • 嗨,我只是想问一下是否可以在网页中创建一个按钮,单击该按钮将创建整个数据库的备份。我想为我的数据库创建一个备份,但我不知道如何进行备份我一直在阅读一些文章,但我无法提交一个简单的应用程序,其中对于我的级别来说更复杂。我正在使用 php pdo 我的数据库是 mysql 希望尽快得到响应
      【解决方案5】:

      我刚刚完成了大卫沃尔什原始备份功能的PDO版本。
      我还对其进行了改进以解决Bill Karwin's answer 中提到的问题;处理 NULL,写入单独的行,因此没有内存问题,带有反引号等。
      给出了 mysqldump 的功能。
      可以稍微整理一下,但在这里,请提出任何改进意见

      require 'login.php';
      $DBH = new PDO("mysql:host=$db_hostname;dbname=$db_database; charset=utf8", $db_username, $db_password);
      
      
      
      
      //put table names you want backed up in this array.
      //leave empty to do all
      $tables = array();
      
      backup_tables($DBH, $tables);
      
      
      
      function backup_tables($DBH, $tables) {
      
      $DBH->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_TO_STRING );
      
      //Script Variables
      $compression = false;
      $BACKUP_PATH = "";
      $nowtimename = time();
      
      
      //create/open files
      if ($compression) {
      $zp = gzopen($BACKUP_PATH.$nowtimename.'.sql.gz', "w9");
      } else {
      $handle = fopen($BACKUP_PATH.$nowtimename.'.sql','a+');
      }
      
      
      //array of all database field types which just take numbers 
      $numtypes=array('tinyint','smallint','mediumint','int','bigint','float','double','decimal','real');
      
      //get all of the tables
      if(empty($tables)) {
      $pstm1 = $DBH->query('SHOW TABLES');
      while ($row = $pstm1->fetch(PDO::FETCH_NUM)) {
      $tables[] = $row[0];
      }
      } else {
      $tables = is_array($tables) ? $tables : explode(',',$tables);
      }
      
      //cycle through the table(s)
      
      foreach($tables as $table) {
      $result = $DBH->query('SELECT * FROM '.$table);
      $num_fields = $result->columnCount();
      $num_rows = $result->rowCount();
      
      $return="";
      //uncomment below if you want 'DROP TABLE IF EXISTS' displayed
      //$return.= 'DROP TABLE IF EXISTS `'.$table.'`;'; 
      
      
      //table structure
      $pstm2 = $DBH->query('SHOW CREATE TABLE '.$table);
      $row2 = $pstm2->fetch(PDO::FETCH_NUM);
      $ifnotexists = str_replace('CREATE TABLE', 'CREATE TABLE IF NOT EXISTS', $row2[1]);
      $return.= "\n\n".$ifnotexists.";\n\n";
      
      
      if ($compression) {
      gzwrite($zp, $return);
      } else {
      fwrite($handle,$return);
      }
      $return = "";
      
      //insert values
      if ($num_rows){
      $return= 'INSERT INTO `'.$table."` (";
      $pstm3 = $DBH->query('SHOW COLUMNS FROM '.$table);
      $count = 0;
      $type = array();
      
      while ($rows = $pstm3->fetch(PDO::FETCH_NUM)) {
      
      if (stripos($rows[1], '(')) {$type[$table][] = stristr($rows[1], '(', true);
      } else $type[$table][] = $rows[1];
      
      $return.= $rows[0];
      $count++;
      if ($count < ($pstm3->rowCount())) {
      $return.= ", ";
      }
      }
      
      $return.= ")".' VALUES';
      
      if ($compression) {
      gzwrite($zp, $return);
      } else {
      fwrite($handle,$return);
      }
      $return = "";
      }
      
      while($row = $result->fetch(PDO::FETCH_NUM)) {
      $return= "\n\t(";
      for($j=0; $j<$num_fields; $j++) {
      $row[$j] = addslashes($row[$j]);
      //$row[$j] = preg_replace("\n","\\n",$row[$j]);
      
      
      if (isset($row[$j])) {
      //if number, take away "". else leave as string
      if (in_array($type[$table][$j], $numtypes)) $return.= $row[$j] ; else $return.= '"'.$row[$j].'"' ;
      } else {
      $return.= '""';
      }
      if ($j<($num_fields-1)) {
      $return.= ',';
      }
      }
      $count++;
      if ($count < ($result->rowCount())) {
      $return.= "),";
      } else {
      $return.= ");";
      
      }
      if ($compression) {
      gzwrite($zp, $return);
      } else {
      fwrite($handle,$return);
      }
      $return = "";
      }
      $return="\n\n-- ------------------------------------------------ \n\n";
      if ($compression) {
      gzwrite($zp, $return);
      } else {
      fwrite($handle,$return);
      }
      $return = "";
      }
      
      
      
      $error1= $pstm2->errorInfo();
      $error2= $pstm3->errorInfo();
      $error3= $result->errorInfo();
      echo $error1[2];
      echo $error2[2];
      echo $error3[2];
      
      if ($compression) {
      gzclose($zp);
      } else {
      fclose($handle);
      }
      }
      

      【讨论】:

      • 这是进入代码审查,这不是 StackOverflow 的目的。但简而言之:对 NULL 没有适当的支持(您将它们转换为 '');没有一致地分隔表名;使用非 ANSI 双引号作为字符串分隔符;在大表上使用缓冲查询将打破 PHP 最大内存限制;为一个巨大的表附加所有行将打破 PHP 最大内存限制;使用 addlashes() 代替 PDO::quote();仅在函数结束时检查查询错误;不检查文件创建失败; gzip 扩展可能无法加载
      • 另外,可能还不支持 UTF8 数据。
      • 哎哟。字符集在连接中说明。 NULL 到 '' 有什么问题?表结构必须非常大才能最大内存。每个插入行都单独写入文件,因此,对于最大内存,行必须非常大。 addlashes 不是那么糟糕,但会更改为 PDO 报价。将添加对 gzip 的检查。同意,错误检查可能更漂亮一些。但它正在到达那里,不是吗?
      • 另外,"" 字符串分隔符有什么问题?我如何获得 ANSI 的?什么表没有分隔?谢谢
      • @Dave,我正在做一个。我正在实现一个模仿mysqldump 的PHP 类,并且我正在使用mysqldump 自己的测试套件对其进行测试。我希望在 2014 年 4 月在 Percona Live MySQL Conference & Expo 上展示它。我已经为它创建了一个 github 存储库:github.com/billkarwin/cats-and-dogs
      【解决方案6】:
      function backupDB()
      {
          $db_config = getDbConfigFromWordPress();
          if ($db_config === false) {
              unset($db_config);
              logMessage('Unable to get database configuration from WordPress', true, 'red');
              return false;
          }
      
          $new_backup_file = __DIR__ . DIRECTORY_SEPARATOR . 'newbackup_xxx_date.sql';
          if (is_file($new_backup_file) && is_writable($new_backup_file)) {
              @unlink($new_backup_file);
          } elseif (is_file($new_backup_file) && !is_writable($new_backup_file)) {
              logMessage('Unable to remove new backup SQL file. This is necessary to create backup SQL file.', true, 'red');
              return false;
          }
          unset($new_backup_file);
      
          $dbh = new \PDO('mysql:dbname=' . $db_config['dbname'] . ';host=' . $db_config['dbhost'] . ';charset=' . $db_config['dbcharset'], $db_config['dbuser'], $db_config['dbpassword']);
          $dbh->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
          $dbh->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_OBJ);
          $dbh->setAttribute(\PDO::ATTR_EMULATE_PREPARES, true);
      
          $sth = $dbh->prepare('SHOW TABLES');
          $sth->execute();
          $result = $sth->fetchAll(\PDO::FETCH_COLUMN);
          $tables = [];
          if (is_array($result) && !empty($result)) {
              foreach ($result as $row) {
                  if (is_string($row) && stristr($row, $db_config['tableprefix']) !== false) {
                      $tables[] = $row;
                  } elseif (is_array($row) && array_key_exists(0, $row) && stristr($row[0], $db_config['tableprefix']) !== false) {
                      $tables[] = $row[0];
                  }
              }// endforeach;
              natcasesort($tables);
          }
          $sth->closeCursor();
          unset($result, $row, $sth);
      
          // begins export string header.
          $export_sql = '-- Manual backup SQL Dump'."\n\n";
          $export_sql .= 'SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";'."\n\n\n";
          $export_sql .= '--'."\n";
          $export_sql .= '-- Database: `' . $db_config['dbname'] . '`'."\n";
          $export_sql .= '--'."\n\n";
          unset($db_config);
          writeDownBackupDB($export_sql);
          unset($export_sql);
      
          // starting to loop thru tables.
          if (isset($tables) && is_array($tables)) {
              foreach ($tables as $table) {
                  $export_sql = '-- --------------------------------------------------------'."\n\n";
                  $export_sql .= '--'."\n";
                  $export_sql .= '-- Table structure for table `' . $table . '`'."\n";
                  $export_sql .= '--'."\n\n";
                  $export_sql .= 'DROP TABLE IF EXISTS `' . $table . '`;'."\n";
                  $sth = $dbh->prepare('SHOW CREATE TABLE `' . $table . '`');
                  $sth->execute();
                  $row = $sth->fetch(\PDO::FETCH_NUM);
                  if (isset($row[1])) {
                      $create_sql_string = $row[1];
                      $create_sql_string = str_replace(['CREATE TABLE `'], ['CREATE TABLE IF NOT EXISTS `'], $create_sql_string);
                      if (substr($create_sql_string, -1) != ';') {
                          $create_sql_string .= ' ;';
                      }
                  } else {
                      $create_sql_string = '';
                  }
                  unset($row);
                  $export_sql .= $create_sql_string."\n\n";
                  $sth->closeCursor();
                  unset($sth);
                  writeDownBackupDB($export_sql);
                  unset($export_sql);
      
                  $export_sql = '--'."\n";
                  $export_sql .= '-- Dumping data for table `' . $table . '`'."\n";
                  $export_sql .= '--'."\n\n";
                  writeDownBackupDB($export_sql);
                  unset($export_sql);
      
                  // get fields
                  $sth = $dbh->prepare('SELECT * FROM `' . $table . '` LIMIT 1');
                  $sth->execute();
                  $result = $sth->fetch(\PDO::FETCH_ASSOC);
                  if (is_array($result)) {
                      $fields = array_keys($result);
                  } else {
                      $fields = [];
                  }
                  $sth->closeCursor();
                  unset($result, $sth);
      
                  // get fields type
                  $sth = $dbh->prepare('DESCRIBE `' . $table . '`');
                  $sth->execute();
                  $table_columns = $sth->fetchAll();
                  $columns = [];
                  if (is_array($table_columns)) {
                      foreach ($table_columns as $column) {
                          $columns[$column->Field] = [
                              'field' => $column->Field,
                              'type' => $column->Type,
                              'null' => $column->Null,
                              'default' => $column->Default,
                          ];
                      }// endforeach;
                      unset($column);
                  }
                  $sth->closeCursor();
                  unset($sth, $table_columns);
      
                  if (isset($fields) && is_array($fields) && !empty($fields)) {
                      $select_string = 'SELECT ';
                      $i_count_field = 1;
                      foreach ($fields as $field) {
                          $select_string .= 'IF (`' . $field . '` IS NULL, \'FIELD_VALUE_NULL\', `' . $field . '`) AS `' . $field . '`';
                          if ($i_count_field < count($fields)) {
                              $select_string .= ', ';
                          }
                          $i_count_field++;
                      }// endforeach;
                      unset($i_count_field, $field);
                      $select_string .= ' FROM `' . $table . '`';
                      $sth = $dbh->prepare($select_string);
                      unset($select_string);
                      $sth->execute();
                      $result = $sth->fetchAll();
                      $export_sql = '';
                      if (is_array($result) && !empty($result)) {
                          // generate INSERT INTO `table_name` string.
                          $export_sql .= 'INSERT INTO `' . $table . '` (';
                          $i_count = 1;
                          foreach ($fields as $field) {
                              $export_sql .= '`' . $field . '`';
                              if ($i_count < count($fields)) {
                                  $export_sql .= ', ';
                              }
                              $i_count++;
                          }// endforeach;
                          unset($field, $i_count);
                          $export_sql .= ') VALUES'."\n";
                          writeDownBackupDB($export_sql);
                          unset($export_sql);
      
                          // generate VALUES of INSERT INTO.
                          if (is_array($result)) {
                              $i_count = 1;
                              $i_count_break = 1;
                              foreach ($result as $row) {
                                  $export_sql = '(';
                                  $i_count_fields = 1;
                                  foreach ($fields as $field) {
                                      $field_value = $row->{$field};
                                      // escape slash
                                      $field_value = str_replace('\\', '\\\\', $field_value);
                                      // sanitize new line
                                      $field_value = str_replace(["\r\n", "\r", "\n"], ['\r\n', '\r', '\n'], $field_value);
                                      // escape single quote
                                      $field_value = str_replace('\'', '\'\'', $field_value);
                                      // change value to NULL if it is NULL.
                                      if ($field_value === 'FIELD_VALUE_NULL') {
                                          $field_value = 'NULL';
                                      }
      
                                      // detect field value type and cloak with single quote.
                                      if (isset($columns[$field]['type']) && 
                                          (
                                              stristr($columns[$field]['type'], 'tinyint(') !== false ||
                                              stristr($columns[$field]['type'], 'smallint(') !== false ||
                                              stristr($columns[$field]['type'], 'mediumint(') !== false ||
                                              stristr($columns[$field]['type'], 'int(') !== false ||
                                              stristr($columns[$field]['type'], 'bigint(') !== false
                                          )
                                      ) {
                                          // this field column type is int
                                          if (!is_numeric($field_value) && $field_value !== 'NULL') {
                                              $field_value = '\'' . $field_value . '\'';
                                          }
                                      } else {
                                          if ($field_value !== 'NULL') {
                                              $field_value = '\'' . $field_value . '\'';
                                          }
                                      }
      
                                      $export_sql .= $field_value;
                                      unset($field_value);
      
                                      if ($i_count_fields < count($fields)) {
                                          $export_sql .= ', ';
                                      }
                                      $i_count_fields++;
                                  }// endforeach;
                                  unset($field, $i_count_fields);
                                  $export_sql .= ')';
      
                                  if ($i_count < count($result)) {
                                      if ($i_count_break >= 30) {
                                          $export_sql .= ';'."\n";
                                          writeDownBackupDB($export_sql);
                                          unset($export_sql);
                                          $i_count_break = 0;
      
                                          $export_sql = 'INSERT INTO `' . $table . '` (';
                                          $i_count_fields = 1;
                                          foreach ($fields as $field) {
                                              $export_sql .= '`' . $field . '`';
                                              if ($i_count_fields < count($fields)) {
                                                  $export_sql .= ', ';
                                              }
                                              $i_count_fields++;
                                          }// endforeach;
                                          unset($field, $i_count_fields);
                                          $export_sql .= ') VALUES'."\n";
                                          writeDownBackupDB($export_sql);
                                          unset($export_sql);
                                          $export_sql = '';
                                      } else {
                                          $export_sql .= ','."\n";
                                      }
                                  } else {
                                      $export_sql .= ';'."\n\n";
                                  }
                                  $i_count++;
                                  $i_count_break++;
                                  writeDownBackupDB($export_sql);
                                  unset($export_sql);
                              }// endforeach;
                              unset($i_count, $i_count_break, $result, $row);
                          }
                      } else {
                          $export_sql .= "\n";
                          writeDownBackupDB($export_sql);
                          unset($export_sql);
                      }
                      unset($fields);
                      $sth->closeCursor();
                      unset($result, $sth);
                  } else {
                      $export_sql = "\n";
                      writeDownBackupDB($export_sql);
                      unset($export_sql);
                  }
                  unset($export_sql);
              }// endforeach;
              unset($table);
          }
          unset($tables);
      
          unset($dbh);
          logMessage('Backup DB completed. Max memory usage is ' . formatBytes(memory_get_peak_usage(true)) . '.', true, 'green');
          return true;
      }// backupDB
      
      
      /**
       * Write content to backup SQL file by append.
       * 
       * @param string $content
       */
      function writeDownBackupDB($content)
      {
          $new_backup_file = __DIR__ . DIRECTORY_SEPARATOR . 'newbackup_xxx_date.sql';
          $handle = fopen($new_backup_file, 'a+');
          fwrite($handle, $content);
          fclose($handle);
          unset($handle, $new_backup_file);
      }// writeDownBackupDB
      
      
      logMessage('Beginning backup DB.', true, 'light_gray');
      backupDB();
      

      请注意...

      1. 缺少一些函数,例如logMessage()、getDbConfigFromWordPress()。请在使用前移除。
      2. 必须更改 $db_config['tableprefix'] 或 $db_config[...] 之类的内容。
      3. 有很多事情不能像@Bill Karwin说的那样处理。
      4. 我不确定它是否支持 UTF-8 数据,但我认为它支持多种语言并且还支持表情符号 (???)。
      5. 使用 mysql 命令导出总是更好的主意。

      【讨论】:

        【解决方案7】:

        我通过一些修改实现了 Lan 的最新版本(见下面的代码):

        • 表格保存在名称为今天日期的文件夹中;同一天的先前版本将被覆盖
        • 它支持多种格式,包括逗号分隔的 CSV
        • 它以字节和行数显示表格大小
        • 使用我的(当然是老式的)表格

        添加 csv 选项的一个原因是,当它是多字节(亚洲脚本)时,我发现无法从 TEXT (UTF8) 格式的 sql 文件中导入数据。它确实适用于 BLOB 格式,但是我们不能将其索引为 FULLTEXT。我可能错过了格式化表格的一点......

        这里是代码:

        function backup_tables($DBH,$tables,$compression,$format) {
        $DBH->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_NATURAL );
        //Script Variables
        $BACKUP_PATH = DUMP;
        $date = date("Y-m-d");
        $olddir =  getcwd();
        chdir($BACKUP_PATH);
        if(!file_exists($date)) {
            echo "<font color=red>Created ‘".$date."’ folder</font><br />";
            $cmd = "mkdir ".$date;
            exec($cmd);
            }
        chdir($date);
        
        //array of all database field types which just take numbers 
        $numtypes = array('tinyint', 'smallint', 'mediumint', 'int', 'bigint', 'float', 'double', 'decimal', 'real');
        
        //get all of the tables
        if(empty($tables)) {
            $pstm1 = $DBH->query('SHOW TABLES');
            while($row = $pstm1->fetch(PDO::FETCH_NUM)) {
                $tables[] = $row[0];
                }
            }
        else {
            $tables = is_array($tables) ? $tables : explode(',',$tables);
            }
        
        //cycle through the table(s)
        echo "<font color=blue>Dumping tables to DB_DUMP:</font>";
        echo "<ul>";
        foreach($tables as $table) {
            //create/open files
            if($format == "csv") {
                $filename = $table.".csv";
                $handle = fopen($filename,"w");
                }
            else {
                if($compression) {
                    $filename = $table.".sql.gz";
                    $zp = gzopen($filename,"wb9");
                    }
                else {
                    $filename = $table.".sql";
                    $handle = fopen($filename,"w");
                    }
                }
            echo "<li><small><font color=blue>".$filename."</font>";
            $result = $DBH->query("SELECT * FROM $table");
            $num_fields = $result->columnCount();
            $num_rows = $result->rowCount();
            $return = "";
            $return .= 'DROP TABLE IF EXISTS `'.$table.'`;'; 
        
            //table structure
            $pstm2 = $DBH->query("SHOW CREATE TABLE $table");
            $row2 = $pstm2->fetch(PDO::FETCH_NUM);
            $ifnotexists = str_replace('CREATE TABLE', 'CREATE TABLE IF NOT EXISTS', $row2[1]);
            $return .= "\n\n".$ifnotexists.";\n\n";
        
            if($format <> "csv") {
                if($compression) gzwrite($zp, $return);
                else fwrite($handle,$return);
                }
            $return = "";
        
            //insert values
            if($num_rows) {
                $return = 'INSERT INTO `'."$table"."` (";
                $pstm3 = $DBH->query("SHOW COLUMNS FROM $table");
                $count = 0;
                $type = array();
        
                while($rows = $pstm3->fetch(PDO::FETCH_NUM)) {
                    if(stripos($rows[1], '(')) {
                        $type[$table][] = stristr($rows[1], '(', true);
                        }
                    else $type[$table][] = $rows[1];
        
                    $return .= "`".$rows[0]."`";
                    $count++;
                    if($count < ($pstm3->rowCount())) {
                        $return .= ", ";
                        }
                    }
                $return .= ")".' VALUES';
                if($format <> "csv") {
                    if($compression) gzwrite($zp, $return);
                    else fwrite($handle,$return);
                    }
                $return = "";
                }
            $count = 0;
            while($row = $result->fetch(PDO::FETCH_NUM)) {
                if($format <> "csv") $return = "\n\t(";
                for($j=0; $j < $num_fields; $j++) {
                    //$row[$j] = preg_replace("\n","\\n",$row[$j]);
                    if(isset($row[$j])) {
                        if($format == "csv") $return .= '"'.$row[$j].'"';
                        else {
                            //if number, take away "". else leave as string
                            if((in_array($type[$table][$j],$numtypes)) && (!empty($row[$j])))
                                $return .= $row[$j];
                            else
                                $return .= $DBH->quote($row[$j]);
                            }
                        }
                    else {
                        if($format == "csv") $return .= '';
                        else $return .= 'NULL';
                        }
                    if($j < ($num_fields-1)) $return .= ',';
                    }
                $count++;
                if($format == "csv") $return .= "\n";
                else {
                    if($count < ($result->rowCount()))
                        $return .= "),";
                    else $return .= ");";
                    }
                if($format == "csv") fwrite($handle,$return);
                else {
                    if($compression) gzwrite($zp, $return);
                    else fwrite($handle,$return);
                    }
                $return = "";
                }
            $return = "\n\n-- ------------------------------------------------ \n\n";
            echo " (".$count." records)";
        
            if($format <> "csv") {
                if($compression) gzwrite($zp, $return);
                else fwrite($handle,$return);
                }
            $return = "";
        
            $error1 = $pstm2->errorInfo();
            $error2 = $pstm3->errorInfo();
            $error3 = $result->errorInfo();
            echo $error1[2];
            echo $error2[2];
            echo $error3[2];
        
            if($format == "csv") fclose($handle);
            else {
                if($compression) gzclose($zp);
                else fclose($handle);
                }
            $filesize = filesize($filename);
            echo " - ".$filesize." bytes</small></li>";
            }
        echo "</ul>";
        chdir($olddir);
        return;
        }
        

        【讨论】:

          猜你喜欢
          • 2011-08-06
          • 2018-11-01
          • 1970-01-01
          • 1970-01-01
          • 2017-09-29
          • 2012-11-25
          • 2019-12-21
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多