如何调试PDO数据库查询?

发布于 2024-08-24 06:42:46 字数 311 浏览 3 评论 0原文

在转向 PDO 之前,我通过连接字符串在 PHP 中创建了 SQL 查询。如果我遇到数据库语法错误,我可以只回显最终的 SQL 查询字符串,自己在数据库上尝试,并调整它直到修复错误,然后将其放回代码中。

准备好的 PDO 语句更快、更好、更安全,但有一件事困扰着我:当最终查询发送到数据库时,我从未看到它。当我在 Apache 日志或自定义日志文件中收到有关语法的错误(我将错误记录在 catch 块内)时,我看不到导致这些错误的查询。

有没有办法捕获 PDO 发送到数据库的完整 SQL 查询并将其记录到文件中?

Before moving to PDO, I created SQL queries in PHP by concatenating strings. If I got database syntax error, I could just echo the final SQL query string, try it myself on the database, and tweak it until I fixed the error, then put that back into the code.

Prepared PDO statements are faster and better and safer, but one thing bothers me: I never see the final query as it's sent to the database. When I get errors about the syntax in my Apache log or my custom log file (I log errors inside a catch block), I can't see the query that caused them.

Is there a way capture the complete SQL query sent by PDO to the database and log it to a file?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(19

一瞬间的火花 2024-08-31 06:42:46

你这样说:

我从来没有看到最终的查询,因为它是
发送到数据库

嗯,实际上,当使用准备好的语句时,不存在“最终查询这样的东西:

  • 首先,一条语句被发送到数据库,然后在那里准备
    • 数据库解析查询,并构建其内部表示
  • 并且,当您绑定变量并执行语句时,只有变量被发送到数据库
    • 数据库将值“注入”到语句的内部表示中

所以,回答你的问题:

有没有办法捕获完整的
PDO 向数据库发送 SQL 查询
并将其记录到文件中?

否:因为任何地方都没有“完整的 SQL 查询”,因此无法捕获它。

出于调试目的,您可以做的最好的事情是通过将值注入到语句的 SQL 字符串中来“重新构造”“真实”SQL 查询。

在这种情况下,我通常做的是:

  • 回显与该语句相对应的 SQL 代码,并使用占位符
  • 并在其后使用 var_dump (或等效项),显示参数的值
  • 这通常足以看到可能的错误,即使您没有任何可以执行的“真实”查询。

当涉及到调试时,这并不是很好——但这就是准备好的语句的代价及其带来的优势。

You say this :

I never see the final query as it's
sent to the database

Well, actually, when using prepared statements, there is no such thing as a "final query" :

  • First, a statement is sent to the DB, and prepared there
    • The database parses the query, and builds an internal representation of it
  • And, when you bind variables and execute the statement, only the variables are sent to the database
    • And the database "injects" the values into its internal representation of the statement

So, to answer your question :

Is there a way capture the complete
SQL query sent by PDO to the database
and log it to a file?

No : as there is no "complete SQL query" anywhere, there is no way to capture it.

The best thing you can do, for debugging purposes, is "re-construct" an "real" SQL query, by injecting the values into the SQL string of the statement.

What I usually do, in this kind of situations, is :

  • echo the SQL code that corresponds to the statement, with placeholders
  • and use var_dump (or an equivalent) just after, to display the values of the parameters
  • This is generally enough to see a possible error, even if you don't have any "real" query that you can execute.

This is not great, when it comes to debugging -- but that's the price of prepared statements and the advantages they bring.

哭了丶谁疼 2024-08-31 06:42:46

查看数据库日志

虽然Pascal MARTIN是正确的,PDO不会一次将完整的查询发送到数据库,但是ryeguy建议使用数据库的日志记录功能实际上让我可以看到数据库组装和执行的完整查询。

方法如下:
(这些说明适用于 Windows 计算机上的 MySQL - 您的情况可能会有所不同)

  • my.ini 中的 [mysqld] 部分下,添加 log code> 命令,如 log="C:\Program Files\MySQL\MySQL Server 5.1\data\mysql.log"
  • 重新启动 MySQL。
  • 它将开始记录该文件中的每个查询。

该文件会快速增长,因此请务必在完成测试后将其删除并关闭日志记录。

Looking in the database log

Although Pascal MARTIN is correct that PDO doesn't send the complete query to the database all at once, ryeguy's suggestion to use the DB's logging function actually allowed me to see the complete query as assembled and executed by the database.

Here's how:
(These instructions are for MySQL on a Windows machine - your mileage may vary)

  • In my.ini, under the [mysqld] section, add a log command, like log="C:\Program Files\MySQL\MySQL Server 5.1\data\mysql.log"
  • Restart MySQL.
  • It will start logging every query in that file.

That file will grow quickly, so be sure to delete it and turn off logging when you're done testing.

萝莉病 2024-08-31 06:42:46

当然您可以使用此模式进行调试 {{ PDO::ATTR_ERRMODE }}
只需在查询之前添加新行即可显示调试行。

$db->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$db->query('SELECT *******');  

Sure you can debug using this mode {{ PDO::ATTR_ERRMODE }}
Just add new line before your query then you will show the debug lines.

$db->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$db->query('SELECT *******');  
栀梦 2024-08-31 06:42:46

也许您想要做的是在语句句柄上使用 debugDumpParams() 。您可以在将值绑定到准备好的查询后随时运行该命令(无需 execute() 语句)。

它不会为您构建准备好的语句,但会显示您的参数。

Probably what you want to do is use debugDumpParams() on the statement handle. You can run that any time after binding values to the prepared query (no need to execute() the statement).

It doesn't build the prepared statement for you, but it will show your parameters.

或十年 2024-08-31 06:42:46

一篇旧文章,但也许​​有人会发现这很有用;

function pdo_sql_debug($sql,$placeholders){
    foreach($placeholders as $k => $v){
        $sql = preg_replace('/:'.$k.'/',"'".$v."'",$sql);
    }
    return $sql;
}

An old post but perhaps someone will find this useful;

function pdo_sql_debug($sql,$placeholders){
    foreach($placeholders as $k => $v){
        $sql = preg_replace('/:'.$k.'/',"'".$v."'",$sql);
    }
    return $sql;
}
方圜几里 2024-08-31 06:42:46

下面是一个函数,用于查看有效的 SQL 是什么,改编自“Mark”在 的评论php.net

function sql_debug($sql_string, array $params = null) {
    if (!empty($params)) {
        $indexed = $params == array_values($params);
        foreach($params as $k=>$v) {
            if (is_object($v)) {
                if ($v instanceof \DateTime) $v = $v->format('Y-m-d H:i:s');
                else continue;
            }
            elseif (is_string($v)) $v="'$v'";
            elseif ($v === null) $v='NULL';
            elseif (is_array($v)) $v = implode(',', $v);

            if ($indexed) {
                $sql_string = preg_replace('/\?/', $v, $sql_string, 1);
            }
            else {
                if ($k[0] != ':') $k = ':'.$k; //add leading colon if it was left out
                $sql_string = str_replace($k,$v,$sql_string);
            }
        }
    }
    return $sql_string;
}

Here's a function to see what the effective SQL will be, adpated from a comment by "Mark" at php.net:

function sql_debug($sql_string, array $params = null) {
    if (!empty($params)) {
        $indexed = $params == array_values($params);
        foreach($params as $k=>$v) {
            if (is_object($v)) {
                if ($v instanceof \DateTime) $v = $v->format('Y-m-d H:i:s');
                else continue;
            }
            elseif (is_string($v)) $v="'$v'";
            elseif ($v === null) $v='NULL';
            elseif (is_array($v)) $v = implode(',', $v);

            if ($indexed) {
                $sql_string = preg_replace('/\?/', $v, $sql_string, 1);
            }
            else {
                if ($k[0] != ':') $k = ':'.$k; //add leading colon if it was left out
                $sql_string = str_replace($k,$v,$sql_string);
            }
        }
    }
    return $sql_string;
}
唔猫 2024-08-31 06:42:46

不会。PDO 查询不是在客户端准备的。 PDO 只是将 SQL 查询和参数发送到数据库服务器。 数据库是(的)替换的内容。您有两个选择:

  • 使用数据库的日志记录功能(但即使如此,至少对于 Postgres,它通常显示为两个单独的语句(即“不是最终的”))
  • 输出 SQL 查询和
    参数并将其拼凑在一起
    你自己

No. PDO queries are not prepared on the client side. PDO simply sends the SQL query and the parameters to the database server. The database is what does the substitution (of the ?'s). You have two options:

  • Use your DB's logging function (but even then it's normally shown as two separate statements (ie, "not final") at least with Postgres)
  • Output the SQL query and the
    paramaters and piece it together
    yourself
心碎无痕… 2024-08-31 06:42:46

除了检查错误日志之外,几乎没有提到错误显示,
但有一个相当有用的功能:(

<?php
/* Provoke an error -- bogus SQL syntax */
$stmt = $dbh->prepare('bogus sql');
if (!$stmt) {
    echo "\PDO::errorInfo():\n";
    print_r($dbh->errorInfo());
}
?>

源链接

很明显,可以将此代码修改为用作异常消息
或任何其他类型的错误处理

almost nothing was said about error displaying except check error logs,
but there's a rather helpful functionality:

<?php
/* Provoke an error -- bogus SQL syntax */
$stmt = $dbh->prepare('bogus sql');
if (!$stmt) {
    echo "\PDO::errorInfo():\n";
    print_r($dbh->errorInfo());
}
?>

(source link)

it is clear that this code can be modified to be used as exception message
or any other kind of error handling

蓝眼睛不忧郁 2024-08-31 06:42:46

例如,您有这个 pdo 语句:

$query="insert into tblTest (field1, field2, field3)
values (:val1, :val2, :val3)";
$res=$db->prepare($query);
$res->execute(array(
  ':val1'=>$val1,
  ':val2'=>$val2,
  ':val3'=>$val3,
));

现在您可以通过定义如下数组来获取执行的查询:

$assoc=array(
  ':val1'=>$val1,
  ':val2'=>$val2,
  ':val3'=>$val3,
);
$exQuery=str_replace(array_keys($assoc), array_values($assoc), $query);
echo $exQuery;

for example you have this pdo statement :

$query="insert into tblTest (field1, field2, field3)
values (:val1, :val2, :val3)";
$res=$db->prepare($query);
$res->execute(array(
  ':val1'=>$val1,
  ':val2'=>$val2,
  ':val3'=>$val3,
));

now you can get the executed query by defining an array like this :

$assoc=array(
  ':val1'=>$val1,
  ':val2'=>$val2,
  ':val3'=>$val3,
);
$exQuery=str_replace(array_keys($assoc), array_values($assoc), $query);
echo $exQuery;
醉南桥 2024-08-31 06:42:46

在互联网上搜索我发现这是一个可以接受的解决方案。使用不同的类代替 PDO,并且通过魔术函数调用来调用 PDO 函数。我不确定这会造成严重的性能问题。但在 PDO 中添加合理的日志记录功能之前,它可以使用。

因此,根据这个 线程 ,您可以为 PDO 连接编写一个包装器,它可以在出现错误时记录并抛出异常。

这是一个简单的示例:

class LoggedPDOSTatement extends PDOStatement    {

function execute ($array)    {
    parent::execute ($array);
    $errors = parent::errorInfo();
    if ($errors[0] != '00000'):
        throw new Exception ($errors[2]);
    endif;
  }

}

因此您可以使用该类而不是 PDOStatement:

$this->db->setAttribute (PDO::ATTR_STATEMENT_CLASS, array ('LoggedPDOStatement', array()));

这里提到了 PDO 装饰器实现:

class LoggedPDOStatement    {

function __construct ($stmt)    {
    $this->stmt = $stmt;
}

function execute ($params = null)    {
    $result = $this->stmt->execute ($params); 
    if ($this->stmt->errorCode() != PDO::ERR_NONE):
        $errors = $this->stmt->errorInfo();
        $this->paint ($errors[2]);
    endif;
    return $result;
}

function bindValue ($key, $value)    {
    $this->values[$key] = $value;    
    return $this->stmt->bindValue ($key, $value);
}

function paint ($message = false)    {
    echo '<pre>';
    echo '<table cellpadding="5px">';
    echo '<tr><td colspan="2">Message: ' . $message . '</td></tr>';
    echo '<tr><td colspan="2">Query: ' . $this->stmt->queryString . '</td></tr>';
    if (count ($this->values) > 0):
    foreach ($this->values as $key => $value):
    echo '<tr><th align="left" style="background-color: #ccc;">' . $key . '</th><td>' . $value . '</td></tr>';
    endforeach;
    endif;
    echo '</table>';
    echo '</pre>';
}

function __call ($method, $params)    {
    return call_user_func_array (array ($this->stmt, $method), $params); 
}

}

Searching internet I found this as an acceptable solution. A different class is used instead of PDO and PDO functions are called through magic function calls. I am not sure this creates serious performance problems. But it can be used until a sensible logging feature is added to PDO.

So as per this thread, you can write a wrapper for your PDO connection which can log and throws an exception when you get a error.

Here is simple example:

class LoggedPDOSTatement extends PDOStatement    {

function execute ($array)    {
    parent::execute ($array);
    $errors = parent::errorInfo();
    if ($errors[0] != '00000'):
        throw new Exception ($errors[2]);
    endif;
  }

}

so you can use that class instead of PDOStatement:

$this->db->setAttribute (PDO::ATTR_STATEMENT_CLASS, array ('LoggedPDOStatement', array()));

Here a mentioned PDO decorator implementation:

class LoggedPDOStatement    {

function __construct ($stmt)    {
    $this->stmt = $stmt;
}

function execute ($params = null)    {
    $result = $this->stmt->execute ($params); 
    if ($this->stmt->errorCode() != PDO::ERR_NONE):
        $errors = $this->stmt->errorInfo();
        $this->paint ($errors[2]);
    endif;
    return $result;
}

function bindValue ($key, $value)    {
    $this->values[$key] = $value;    
    return $this->stmt->bindValue ($key, $value);
}

function paint ($message = false)    {
    echo '<pre>';
    echo '<table cellpadding="5px">';
    echo '<tr><td colspan="2">Message: ' . $message . '</td></tr>';
    echo '<tr><td colspan="2">Query: ' . $this->stmt->queryString . '</td></tr>';
    if (count ($this->values) > 0):
    foreach ($this->values as $key => $value):
    echo '<tr><th align="left" style="background-color: #ccc;">' . $key . '</th><td>' . $value . '</td></tr>';
    endforeach;
    endif;
    echo '</table>';
    echo '</pre>';
}

function __call ($method, $params)    {
    return call_user_func_array (array ($this->stmt, $method), $params); 
}

}
心舞飞扬 2024-08-31 06:42:46

要在WAMP中记录MySQL,您需要编辑my.ini(例如在wamp\bin\mysql\mysql5.6.17\my.ini下)

并添加到[mysqld]:

general_log = 1
general_log_file="c:\\tmp\\mysql.log"

To log MySQL in WAMP, you will need to edit the my.ini (e.g. under wamp\bin\mysql\mysql5.6.17\my.ini)

and add to [mysqld]:

general_log = 1
general_log_file="c:\\tmp\\mysql.log"
一紙繁鸢 2024-08-31 06:42:46

这是我创建的一个函数,用于返回带有“已解析”参数的 SQL 查询。

function paramToString($query, $parameters) {
    if(!empty($parameters)) {
        foreach($parameters as $key => $value) {
            preg_match('/(\?(?!=))/i', $query, $match, PREG_OFFSET_CAPTURE);
            $query = substr_replace($query, $value, $match[0][1], 1);
        }
    }
    return $query;
    $query = "SELECT email FROM table WHERE id = ? AND username = ?";
    $values = [1, 'Super'];

    echo paramToString($query, $values);

假设您像这样执行

$values = array(1, 'SomeUsername');
$smth->execute($values);

此函数不会向查询添加引号,但会为我完成这项工作。

Here is a function I made to return a SQL query with "resolved" parameters.

function paramToString($query, $parameters) {
    if(!empty($parameters)) {
        foreach($parameters as $key => $value) {
            preg_match('/(\?(?!=))/i', $query, $match, PREG_OFFSET_CAPTURE);
            $query = substr_replace($query, $value, $match[0][1], 1);
        }
    }
    return $query;
    $query = "SELECT email FROM table WHERE id = ? AND username = ?";
    $values = [1, 'Super'];

    echo paramToString($query, $values);

Assuming you execute like this

$values = array(1, 'SomeUsername');
$smth->execute($values);

This function DOES NOT add quotes to queries but does the job for me.

新一帅帅 2024-08-31 06:42:46

我在这里创建了一个现代 Composer 加载的项目/存储库:

pdo-debug

找到项目的 GitHub回到这里,请参阅此处解释的博客文章。在您的composer.json中添加一行,然后您可以像这样使用它:

echo debugPDO($sql, $parameters);

$sql是原始SQL语句,$parameters是参数数组:关键是占位符名称(“:user_id”)或未命名参数的编号(“?”),该值是..好吧,该值。

背后的逻辑:该脚本将简单地对参数进行分级,并将它们替换为提供的 SQL 字符串。超级简单,但对于 99% 的用例来说超级有效。注意:这只是一个基本的模拟,而不是真正的 PDO 调试(因为这是不可能的,因为 PHP 将原始 SQL 和参数单独发送到 MySQL 服务器)。

非常感谢来自 StackOverflow 线程 从 PDO 获取原始 SQL 查询字符串,用于编写此脚本背后的整个主要函数。大起来!

I've created a modern Composer-loaded project / repository for exactly this here:

pdo-debug

Find the project's GitHub home here, see a blog post explaining it here. One line to add in your composer.json, and then you can use it like this:

echo debugPDO($sql, $parameters);

$sql is the raw SQL statement, $parameters is an array of your parameters: The key is the placeholder name (":user_id") or the number of the unnamed parameter ("?"), the value is .. well, the value.

The logic behind: This script will simply grad the parameters and replace them into the SQL string provided. Super-simple, but super-effective for 99% of your use-cases. Note: This is just a basic emulation, not a real PDO debugging (as this is not possible as PHP sends raw SQL and parameters to the MySQL server seperated).

A big thanks to bigwebguy and Mike from the StackOverflow thread Getting raw SQL query string from PDO for writing basically the entire main function behind this script. Big up!

滿滿的愛 2024-08-31 06:42:46

如何在 Ubuntu 中调试 PDO mysql 数据库查询

TL;DR 记录所有查询并跟踪 mysql 日志。

这些说明适用于我安装的 Ubuntu 14.04。发出命令 lsb_release -a 来获取您的版本。您的安装可能会有所不同。

打开 mysql 中的日志记录

  1. 转到您的开发服务器 cmd 行
  2. 更改目录 cd /etc/mysql。您应该会看到一个名为 my.cnf 的文件。这就是我们要更改的文件。
  3. 输入 cat my.cnf | 验证您是否位于正确的位置。 grep General_log。这将为您过滤 my.cnf 文件。您应该看到两个条目:#general_log_file = /var/log/mysql/mysql.log && #general_log = 1
  4. 取消注释这两行并通过您选择的编辑器保存。
  5. 重启mysql:sudo service mysql restart
  6. 您可能还需要重新启动网络服务器。 (我不记得我使用的顺序)。对于我的安装,这是 nginx:sudo service nginx restart

干得好!你已经准备好了。现在您所要做的就是跟踪日志文件,以便您可以实时查看应用程序进行的 PDO 查询。

跟踪日志以查看您的查询

输入此 cmd tail -f /var/log/mysql/mysql.log

您的输出将如下所示:

73 Connect  xyz@localhost on your_db
73 Query    SET NAMES utf8mb4
74 Connect  xyz@localhost on your_db
75 Connect  xyz@localhost on your_db
74 Quit 
75 Prepare  SELECT email FROM customer WHERE email=? LIMIT ?
75 Execute  SELECT email FROM customer WHERE email='[email protected]' LIMIT 5
75 Close stmt   
75 Quit 
73 Quit 

只要您继续跟踪日志,您的应用发出的任何新查询都会自动弹出到视图中。要退出尾部,请点击 cmd/ctrl c。

注意

  1. 注意:此日志文件可能会变得很大。我只在我的开发服务器上运行它。
  2. 日志文件太大?截断它。这意味着文件会保留,但内容会被删除。 截断--size 0 mysql.log
  3. 很酷的是日志文件列出了 mysql 连接。我知道其中之一来自我正在转换的遗留 mysqli 代码。第三个来自我的新 PDO 连接。但是,不确定第二个来自哪里。如果您知道快速找到它的方法,请告诉我。

信用与信用感谢

上面 Nathan Long 的回答,让我们在 Ubuntu 上解决这个问题。还要感谢 dikirill 对 Nathan 帖子的评论,这使我找到了这个解决方案。

爱你堆栈溢出!

How to debug PDO mysql database queries in Ubuntu

TL;DR Log all your queries and tail the mysql log.

These directions are for my install of Ubuntu 14.04. Issue command lsb_release -a to get your version. Your install might be different.

Turn on logging in mysql

  1. Go to your dev server cmd line
  2. Change directories cd /etc/mysql. You should see a file called my.cnf. That’s the file we’re gonna change.
  3. Verify you’re in the right place by typing cat my.cnf | grep general_log. This filters the my.cnf file for you. You should see two entries: #general_log_file = /var/log/mysql/mysql.log && #general_log = 1.
  4. Uncomment those two lines and save via your editor of choice.
  5. Restart mysql: sudo service mysql restart.
  6. You might need to restart your webserver too. (I can’t recall the sequence I used). For my install, that’s nginx: sudo service nginx restart.

Nice work! You’re all set. Now all you have to do is tail the log file so you can see the PDO queries your app makes in real time.

Tail the log to see your queries

Enter this cmd tail -f /var/log/mysql/mysql.log.

Your output will look something like this:

73 Connect  xyz@localhost on your_db
73 Query    SET NAMES utf8mb4
74 Connect  xyz@localhost on your_db
75 Connect  xyz@localhost on your_db
74 Quit 
75 Prepare  SELECT email FROM customer WHERE email=? LIMIT ?
75 Execute  SELECT email FROM customer WHERE email='[email protected]' LIMIT 5
75 Close stmt   
75 Quit 
73 Quit 

Any new queries your app makes will automatically pop into view, as long as you continue tailing the log. To exit the tail, hit cmd/ctrl c.

Notes

  1. Careful: this log file can get huge. I’m only running this on my dev server.
  2. Log file getting too big? Truncate it. That means the file stays, but the contents are deleted. truncate --size 0 mysql.log.
  3. Cool that the log file lists the mysql connections. I know one of those is from my legacy mysqli code from which I'm transitioning. The third is from my new PDO connection. However, not sure where the second is coming from. If you know a quick way to find it, let me know.

Credit & thanks

Huge shout out to Nathan Long’s answer above for the inspo to figure this out on Ubuntu. Also to dikirill for his comment on Nathan’s post which lead me to this solution.

Love you stackoverflow!

寂寞笑我太脆弱 2024-08-31 06:42:46

我在捕获 PDO 豁免以进行调试的解决方案中遇到的问题是,它只捕获了 PDO 豁免(废话),但没有捕获被注册为 php 错误的语法错误(我不确定这是为什么,但是“为什么”与解决方案无关)。我所有的 PDO 调用都来自一个表模型类,我为与所有表的所有交互扩展了该模型类...当我尝试调试代码时,这很复杂,因为错误会注册我的执行调用所在的 php 代码行打来电话,但没有告诉我电话实际上是从哪里打来的。我使用以下代码来解决这个问题:

/**
 * Executes a line of sql with PDO.
 * 
 * @param string $sql
 * @param array $params
 */
class TableModel{
    var $_db; //PDO connection
    var $_query; //PDO query

    function execute($sql, $params) { 
        //we're saving this as a global, so it's available to the error handler
        global $_tm;
        //setting these so they're available to the error handler as well
        $this->_sql = $sql;
        $this->_paramArray = $params;            

        $this->_db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $this->_query = $this->_db->prepare($sql);

        try {
            //set a custom error handler for pdo to catch any php errors
            set_error_handler('pdoErrorHandler');

            //save the table model object to make it available to the pdoErrorHandler
            $_tm = $this;
            $this->_query->execute($params);

            //now we restore the normal error handler
            restore_error_handler();
        } catch (Exception $ex) {
            pdoErrorHandler();
            return false;
        }            
    }
}

因此,上面的代码捕获 PDO 异常和 php 语法错误,并以相同的方式处理它们。我的错误处理程序看起来像这样:

function pdoErrorHandler() {
    //get all the stuff that we set in the table model
    global $_tm;
    $sql = $_tm->_sql;
    $params = $_tm->_params;
    $query = $tm->_query;

    $message = 'PDO error: ' . $sql . ' (' . implode(', ', $params) . ") \n";

    //get trace info, so we can know where the sql call originated from
    ob_start();
    debug_backtrace(); //I have a custom method here that parses debug backtrace, but this will work as well
    $trace = ob_get_clean();

    //log the error in a civilized manner
    error_log($message);

    if(admin(){
        //print error to screen based on your environment, logged in credentials, etc.
        print_r($message);
    }
}

如果有人对如何获取与我的错误处理程序相关的信息比将表模型设置为全局变量有更好的想法,我会很高兴听到它并编辑我的代码。

The problem I had with the solution to catch PDO exemptions for debuging purposes is that it only caught PDO exemptions (duh), but didn't catch syntax errors which were registered as php errors (I'm not sure why this is, but "why" is irrelevant to the solution). All my PDO calls come from a single table model class that I extended for all my interactions with all tables... this complicated things when I was trying to debug code, because the error would register the line of php code where my execute call was called, but didn't tell me where the call was, actually, being made from. I used the following code to solve this problem:

/**
 * Executes a line of sql with PDO.
 * 
 * @param string $sql
 * @param array $params
 */
class TableModel{
    var $_db; //PDO connection
    var $_query; //PDO query

    function execute($sql, $params) { 
        //we're saving this as a global, so it's available to the error handler
        global $_tm;
        //setting these so they're available to the error handler as well
        $this->_sql = $sql;
        $this->_paramArray = $params;            

        $this->_db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $this->_query = $this->_db->prepare($sql);

        try {
            //set a custom error handler for pdo to catch any php errors
            set_error_handler('pdoErrorHandler');

            //save the table model object to make it available to the pdoErrorHandler
            $_tm = $this;
            $this->_query->execute($params);

            //now we restore the normal error handler
            restore_error_handler();
        } catch (Exception $ex) {
            pdoErrorHandler();
            return false;
        }            
    }
}

So, the above code catches BOTH PDO exceptions AND php syntax errors and treats them the same way. My error handler looks something like this:

function pdoErrorHandler() {
    //get all the stuff that we set in the table model
    global $_tm;
    $sql = $_tm->_sql;
    $params = $_tm->_params;
    $query = $tm->_query;

    $message = 'PDO error: ' . $sql . ' (' . implode(', ', $params) . ") \n";

    //get trace info, so we can know where the sql call originated from
    ob_start();
    debug_backtrace(); //I have a custom method here that parses debug backtrace, but this will work as well
    $trace = ob_get_clean();

    //log the error in a civilized manner
    error_log($message);

    if(admin(){
        //print error to screen based on your environment, logged in credentials, etc.
        print_r($message);
    }
}

If anyone has any better ideas on how to get relevant info to my error handler than setting the table model as a global variable, I would be happy to hear it and edit my code.

任谁 2024-08-31 06:42:46

这段代码对我来说非常有用:

echo str_replace(array_keys($data), array_values($data), $query->queryString);

不要忘记用你的名字替换 $data 和 $query

this code works great for me :

echo str_replace(array_keys($data), array_values($data), $query->queryString);

Don't forget to replace $data and $query by your names

请别遗忘我 2024-08-31 06:42:46

我使用此类来调试 PDO(使用 Log4PHP

<?php

/**
 * Extends PDO and logs all queries that are executed and how long
 * they take, including queries issued via prepared statements
 */
class LoggedPDO extends PDO
{

    public static $log = array();

    public function __construct($dsn, $username = null, $password = null, $options = null)
    {
        parent::__construct($dsn, $username, $password, $options);
    }

    public function query($query)
    {
        $result = parent::query($query);
        return $result;
    }

    /**
     * @return LoggedPDOStatement
     */
    public function prepare($statement, $options = NULL)
    {
        if (!$options) {
            $options = array();
        }
        return new \LoggedPDOStatement(parent::prepare($statement, $options));
    }
}

/**
 * PDOStatement decorator that logs when a PDOStatement is
 * executed, and the time it took to run
 * @see LoggedPDO
 */
class LoggedPDOStatement
{

    /**
     * The PDOStatement we decorate
     */
    private $statement;
    protected $_debugValues = null;

    public function __construct(PDOStatement $statement)
    {
        $this->statement = $statement;
    }

    public function getLogger()
    {
        return \Logger::getLogger('PDO sql');
    }

    /**
     * When execute is called record the time it takes and
     * then log the query
     * @return PDO result set
     */
    public function execute(array $params = array())
    {
        $start = microtime(true);
        if (empty($params)) {
            $result = $this->statement->execute();
        } else {
            foreach ($params as $key => $value) {
                $this->_debugValues[$key] = $value;
            }
            $result = $this->statement->execute($params);
        }

        $this->getLogger()->debug($this->_debugQuery());

        $time = microtime(true) - $start;
        $ar = (int) $this->statement->rowCount();
        $this->getLogger()->debug('Affected rows: ' . $ar . ' Query took: ' . round($time * 1000, 3) . ' ms');
        return $result;
    }

    public function bindValue($parameter, $value, $data_type = false)
    {
        $this->_debugValues[$parameter] = $value;
        return $this->statement->bindValue($parameter, $value, $data_type);
    }

    public function _debugQuery($replaced = true)
    {
        $q = $this->statement->queryString;

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

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

    protected function _debugReplace($m)
    {
        $v = $this->_debugValues[$m[0]];

        if ($v === null) {
            return "NULL";
        }
        if (!is_numeric($v)) {
            $v = str_replace("'", "''", $v);
        }

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

    /**
     * Other than execute pass all other calls to the PDOStatement object
     * @param string $function_name
     * @param array $parameters arguments
     */
    public function __call($function_name, $parameters)
    {
        return call_user_func_array(array($this->statement, $function_name), $parameters);
    }
}

i use this class to debug PDO (with Log4PHP)

<?php

/**
 * Extends PDO and logs all queries that are executed and how long
 * they take, including queries issued via prepared statements
 */
class LoggedPDO extends PDO
{

    public static $log = array();

    public function __construct($dsn, $username = null, $password = null, $options = null)
    {
        parent::__construct($dsn, $username, $password, $options);
    }

    public function query($query)
    {
        $result = parent::query($query);
        return $result;
    }

    /**
     * @return LoggedPDOStatement
     */
    public function prepare($statement, $options = NULL)
    {
        if (!$options) {
            $options = array();
        }
        return new \LoggedPDOStatement(parent::prepare($statement, $options));
    }
}

/**
 * PDOStatement decorator that logs when a PDOStatement is
 * executed, and the time it took to run
 * @see LoggedPDO
 */
class LoggedPDOStatement
{

    /**
     * The PDOStatement we decorate
     */
    private $statement;
    protected $_debugValues = null;

    public function __construct(PDOStatement $statement)
    {
        $this->statement = $statement;
    }

    public function getLogger()
    {
        return \Logger::getLogger('PDO sql');
    }

    /**
     * When execute is called record the time it takes and
     * then log the query
     * @return PDO result set
     */
    public function execute(array $params = array())
    {
        $start = microtime(true);
        if (empty($params)) {
            $result = $this->statement->execute();
        } else {
            foreach ($params as $key => $value) {
                $this->_debugValues[$key] = $value;
            }
            $result = $this->statement->execute($params);
        }

        $this->getLogger()->debug($this->_debugQuery());

        $time = microtime(true) - $start;
        $ar = (int) $this->statement->rowCount();
        $this->getLogger()->debug('Affected rows: ' . $ar . ' Query took: ' . round($time * 1000, 3) . ' ms');
        return $result;
    }

    public function bindValue($parameter, $value, $data_type = false)
    {
        $this->_debugValues[$parameter] = $value;
        return $this->statement->bindValue($parameter, $value, $data_type);
    }

    public function _debugQuery($replaced = true)
    {
        $q = $this->statement->queryString;

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

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

    protected function _debugReplace($m)
    {
        $v = $this->_debugValues[$m[0]];

        if ($v === null) {
            return "NULL";
        }
        if (!is_numeric($v)) {
            $v = str_replace("'", "''", $v);
        }

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

    /**
     * Other than execute pass all other calls to the PDOStatement object
     * @param string $function_name
     * @param array $parameters arguments
     */
    public function __call($function_name, $parameters)
    {
        return call_user_func_array(array($this->statement, $function_name), $parameters);
    }
}
流云如水 2024-08-31 06:42:46

在 Debian NGINX 环境中我做了以下操作。

如果发现 log-error = /var/log/mysql/error.log<,请转到 /etc/mysql/mysql.conf.d 编辑 mysqld.cnf /code> 在其下方添加以下两行。

general_log_file        = /var/log/mysql/mysql.log
general_log             = 1

要查看日志,请转到 /var/log/mysqltail -f mysql.log

如果您在生产环境中,请记住在完成调试后将这些行注释掉删除mysql.log,因为该日志文件会快速增长并且可能会很大。

In Debian NGINX environment i did the following.

Goto /etc/mysql/mysql.conf.d edit mysqld.cnf if you find log-error = /var/log/mysql/error.log add the following 2 lines bellow it.

general_log_file        = /var/log/mysql/mysql.log
general_log             = 1

To see the logs goto /var/log/mysql and tail -f mysql.log

Remember to comment these lines out once you are done with debugging if you are in production environment delete mysql.log as this log file will grow quickly and can be huge.

你怎么这么可爱啊 2024-08-31 06:42:46

为了这些目的,我多年前编写了一个 模块 mysqli 的包装器。现在我写这样的查询:

// Getting a result object
$result = $db->query("SELECT * FROM `users` WHERE `name` = '?s' AND `age` = ?i", "d'Artagnan", 41);

// We receive data (in the form of an associative array, for example)
$data = $result->fetchAssoc();

// SQL query not working as expected?
// Not a problem - print it and see the generated SQL query,
// which will already be with the parameters substituted into its body:
echo $db->getQueryString(); // SELECT * FROM `users` WHERE `name` = 'd\'Artagnan' AND `age` = 41

For these purposes, I wrote a wrapper for module mysqli many years ago. Now I write queries like this:

// Getting a result object
$result = $db->query("SELECT * FROM `users` WHERE `name` = '?s' AND `age` = ?i", "d'Artagnan", 41);

// We receive data (in the form of an associative array, for example)
$data = $result->fetchAssoc();

// SQL query not working as expected?
// Not a problem - print it and see the generated SQL query,
// which will already be with the parameters substituted into its body:
echo $db->getQueryString(); // SELECT * FROM `users` WHERE `name` = 'd\'Artagnan' AND `age` = 41
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文