有没有办法从PDOException获取sql语句?

时间:2017-11-08 18:31:17

标签: php pdo

我会发现在获取导致PDOException的sql语句文本时会很有用。 据我所知,该例外没有这方面的信息。 例如(在阅读PDOException类的文档后),我使用了Exception::__toString()并获得了类似的内容:

SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '14' for key 'PRIMARY'
exception 'PDOException' with message 'SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '14' for key 'PRIMARY'' in xx.php:64
Stack trace:
#0 xx.php(64): PDOStatement->execute(Array)
#1 xx.php(108): insertKeplerian(Object(MyConn), '14', Object(stdClass))
#2 Command line code(1): include('/srv/www/htdocs...')
#3 {main}

问题是我有从不同函数执行的语句,我想在单个catch块中捕获所有异常。 如果确实无法从异常中恢复语句,那么我可以考虑两种可能的解决方案:

  1. 将sql语句文本存储在某种“全局”变量中 可以在catch部分恢复。
  2. 捕获并管理执行SQL语句的每个函数中的PDOException
  3. 我想有更好的方法可以做到这一点。

1 个答案:

答案 0 :(得分:0)

我根据答案https://stackoverflow.com/a/7716896/4044001找到了做到这一点的方法。  代码如下,其中包括对支持问号(?)的改进,而不是参数标记的命名占位符(:name):

<?php
///\brief Class that extends PDOStatement to add exception handling
class MyPDOStatement extends PDOStatement
{
   protected $_debugValues = null;
   protected $_ValuePos = 0;

   protected function __construct()
   {
      // need this empty construct()!
   }

   ///\brief overrides execute saving array of values and catching exception with error logging
   public function execute($values = array())
   {
      $this->_debugValues = $values;
      $this->_ValuePos    = 0;

      try {
         $t = parent::execute($values);
      }
      catch (PDOException $e) {
         // Do some logging here
         print $this->_debugQuery() . PHP_EOL;
         throw $e;
      }

      return $t;
   }

   ///\brief Retrieves query text with values for placeholders
   public function _debugQuery($replaced = true)
   {
      $q = $this->queryString;

      if (!$replaced) {
         return $q;
      }

      return preg_replace_callback('/(:([0-9a-z_]+)|(\?))/i', array(
         $this,
         '_debugReplace'
      ), $q);
   }

   ///\brief Replaces a placeholder with the corresponding value
   //$m is the name of a placeholder
   protected function _debugReplace($m)
   {
      if ($m[1] == '?') {
         $v = $this->_debugValues[$this->_ValuePos++];
      }
      else {
         $v = $this->_debugValues[$m[1]];
      }
      if ($v === null) {
         return "NULL";
      }
      if (!is_numeric($v)) {
         $v = str_replace("'", "''", $v);
      }

      return "'" . $v . "'";
   }
}

///\brief Class that encapsulates DB connection parameters and configuration
class MyConn extends PDO
{
   function __construct()
   {
      $servername = "localhost";
      $username   = "root";
      $password   = "xxx";
      $dbname     = "new_att";

      parent::__construct("mysql:host=$servername;dbname=$dbname", $username, $password);

      //Set connection to raise exception when error
      $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
      //Set connection to use the derived class MyPDOStatement instead of PDOStatement for statements
      $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array(
         'MyPDOStatement',
         array()
      ));
   }
}
?>