Php 准备好的插入物不';如果ATTR\u EMULATE\u PREPARES为false,则不会执行任何操作
因此,在与PDO进行了大量斗争之后,我已经缩小了问题的根源。当我将属性Php 准备好的插入物不';如果ATTR\u EMULATE\u PREPARES为false,则不会执行任何操作,php,mysql,pdo,Php,Mysql,Pdo,因此,在与PDO进行了大量斗争之后,我已经缩小了问题的根源。当我将属性ATTR\u EMULATE\u PREPARES设置为false时,我的插入查询运行时不会出错,但不会向数据库表中添加条目 但是,当我通过PhpMyAdmin或将ATTR\u EMULATE\u PREPARES设置为true运行查询时,它将成功执行 这让我有些沮丧,因为应该没有什么好的理由说明它不起作用 这是我通过PhpMyAdmin直接执行的查询 INSERT INTO account (guid, displaynam
ATTR\u EMULATE\u PREPARES
设置为false时,我的插入查询运行时不会出错,但不会向数据库表中添加条目
但是,当我通过PhpMyAdmin或将ATTR\u EMULATE\u PREPARES
设置为true运行查询时,它将成功执行
这让我有些沮丧,因为应该没有什么好的理由说明它不起作用
这是我通过PhpMyAdmin直接执行的查询
INSERT INTO account (guid, displayname, password_hash, password_salt, email, superuser) VALUES (UNHEX('fcfd7f2355f211e5acfd2174e316c493'), 'bob', 'test', 'test', 'test', 1);
以下是相关的代码片段
$db = null;
try
{
$db = new PDO($pdo_connectionstring, $pdo_username, $pdo_password);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
}
catch (PDOException $ex)
{
die('[FATAL ERROR] Could not connect to the Database: '.$ex->getMessage());
}
try
{
$stmt = $db->prepare("INSERT INTO `account` (`guid`, `displayname`, `password_hash`, `password_salt`, `email`, `superuser`) VALUES (UNHEX(:guid), :displayname, :passwordhash, :passwordsalt, :email, :superuser);");
$stmt->bindValue(':guid', $guid, PDO::PARAM_STR);
$stmt->bindValue(':displayname', $displayname, PDO::PARAM_STR);
$stmt->bindValue(':passwordhash', $hash, PDO::PARAM_STR);
$stmt->bindValue(':passwordsalt', $salt, PDO::PARAM_STR);
$stmt->bindValue(':email', $email, PDO::PARAM_STR);
$stmt->bindValue(':superuser', $superuser, PDO::PARAM_BOOL);
$stmt->execute();
}
catch (Exception $ex)
{
$jsonresult['generalerror'] = 'Failed to create user. Please contact your GM: ' . $ex->getMessage();
}
编辑:这是我的数据库架构的一个示例,以及相关系统和组件的版本信息
- Debian 6.0.10在1和1的共享主机上运行
- PHP 5.4.44
- MySQL服务器5.5.44-0+deb7u1日志
superuser
被定义为tinyint(1)
,尽管它是一种常见的布尔存储数据类型,但在绑定值时需要PDO::PARAM_INT
。第二部分是,当PDO驱动程序运行时,PDO::ATTR_EMULATE_PREPARES
设置为false
时,只有当本地PDO驱动程序遇到问题时,如果数据库返回错误消息,它不会抛出错误或异常PDOStatement::execute()
返回一个关于查询是否成功的布尔值。如果execute()
返回false,则由开发人员手动检查PDOStatement::errorCode()
和PDOStatement::errorInfo()
。这在事务期间尤其值得注意,因为如果其中一条语句失败,回滚事务非常重要。PDO的一个恼人的怪癖是,如果您设置了错误的数据类型,即PDO::PARAM_BOOL
而不是像我那样设置了PDO::PARAM_INT
,那么返回的errorInfo()
将几乎是空的,让您对到底出了什么问题感到挠头
TL:DR当将
ATTR\u EMULATE\u
设置为false
时,使用额外的错误捕获,如果某些错误不起作用,检查并仔细检查您的数据类型。问题是,当使用emulate_
asfalse
时,查询将失败,并且不会从PDO
引发异常
TL;DR
:转到:测试设置和说明
TL;DR
:转到:测试和结果
TL;DR
:转到:结论
PDO准备的查询处理概述
mysql
驱动程序:
- 它不理解
,它只使用命名占位符
标记?
绑定变量的位置
- 如果成功,每个命令都会返回有用信息,如果无法完成请求,则返回
false
PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION
时,PDO
负责将其转换为异常
通过PDO执行查询时预期发生的情况概述:
prepare
:使用命名占位符将查询发送到PDO
PDO
使用“?”对其进行格式化,并将其发送到mysql
驱动程序
mysql
驱动程序将实际的mysql
服务器发送给它,该服务器对其进行处理,并将解释其如何运行的信息返回给mysql
驱动程序
mysql
驱动程序将结果发送回PDO
PDO
根据需要检查并将其转换为有用的结果或错误代码
或异常
可以看出,有比我们希望的更多的地方会发生混乱
为了调试正在发生的事情,我想确切地知道mysql服务器
接收到了什么以及返回了什么
为此,mysql服务器提供了查询的常规日志记录,请参见:
我提供了一些实用程序来控制打开和关闭常规日志
,用于写入日志文件
和写入mysql.general\u log
表
测试代码:和
调试并解释实际O/p代码:
我已经提供了PHPUnit
测试脚本来运行O/p代码并进行检查。我将它用作“执行和报告”工具,而不是测试任何东西
这真的是在解释我发现了什么并演示了它。它花费了相当多的时间和相当多的“反复试验”。重点是“错误”。;-)
测试设置和说明
使用的代码已更改,以将结果存储在上述数组中
// these change for each test...
$this->userPDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->userPDO->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
try
{
self::$rcActual['exception'] = false;
$stmt = $this->userPDO->prepare(
"INSERT INTO `account`
(`guid`, `displayname`, `password_hash`, `password_salt`, `email`, `superuser`)
VALUES
(UNHEX(:guid), :displayname, :passwordhash, :passwordsalt, :email, :superuser);");
self::$rcActual['prepare'] = $stmt !== false; // record result
$stmt->bindValue(':guid', $this->guid, PDO::PARAM_STR);
$stmt->bindValue(':displayname', $this->displayname, PDO::PARAM_STR);
$stmt->bindValue(':passwordhash', $this->hash, PDO::PARAM_STR);
$stmt->bindValue(':passwordsalt', $this->salt, PDO::PARAM_STR);
$stmt->bindValue(':email', $this->email, PDO::PARAM_STR);
$stmt->bindValue(':superuser', $this->superuser, PDO::PARAM_BOOL);
self::$rcActual['execute'] = $stmt->execute(); // record result
$this->assertTrue(self::$rcActual['execute']);
$this->assertFalse(self::$rcActual['exception']);
}
catch (\Exception $e) {
self::$rcActual['exception'] = true;
self::$rcActual['exeptionMsg'] = $e->getCode() .' : '. $e->getMessage();
$this->assertTrue(self::$rcActual['exception']);
}
启动跟踪文件的mysql常规日志记录的代码:
$testFilename = $this->logging->newTraceFilename('Test_01_EmulatesOn_ExceptionOn'));
$this->logging->setLogFile($testFilename);
$this->logging->startLoggingToFile(); // start logging...
$this->logging->stopLogging();
并停止记录到跟踪文件:
$testFilename = $this->logging->newTraceFilename('Test_01_EmulatesOn_ExceptionOn'));
$this->logging->setLogFile($testFilename);
$this->logging->startLoggingToFile(); // start logging...
$this->logging->stopLogging();
测试和结果:
测试系列1-标准PDO(模拟):
测试1:一切正常。。。
测试代码:
结果:
预期/实际:
prepare : Expected: true, Actual: true
execute : Expected: true, Actual: true
exception : Expected: false, Actual: false
exceptionMsg : Expected: '', Actual: ''
prepare : Expected: true, Actual: true
execute : Expected: true, Actual: false
exception : Expected: false, Actual: false
exceptionMsg : Expected: '', Actual: ''
errorCode : '00000', ErrorMsg: array (
0 => '00000',
1 => NULL,
2 => NULL,
)
prepare : Expected: true, Actual: true
execute : Expected: true, Actual: true
exception : Expected: false, Actual: false
exceptionMsg : Expected: '', Actual: ''
errorCode : '00000', ErrorMsg: ''
跟踪日志:
150914 14:15:30 569 Query INSERT INTO `account`
(`guid`, `displayname`, `password_hash`, `password_salt`, `email`, `superuser`)
VALUES
(UNHEX('420D4B65565311E5958000FFD7CBE75F'), 'Example User 42', '$2y$17$12345678901234567890121234567890123456789012345678942', '$2y$17$1234567890123456789042$', 'example42@example.com', 1)
150915 11:37:12 693 Prepare INSERT INTO `account`
(`guid`, `displayname`, `password_hash`, `password_salt`, `email`, `superuser`)
VALUES
(UNHEX(?), ?, ?, ?, ?, ?)
150915 12:06:07 709 Prepare INSERT INTO `account`
(`guid`, `displayname`, `password_hash`, `password_salt`, `email`, `superuser`)
VALUES
(UNHEX(?), ?, ?, ?, ?, ?)
709 Execute INSERT INTO `account`
(`guid`, `displayname`, `password_hash`, `password_salt`, `email`, `superuser`)
VALUES
(UNHEX('420D4B65565311E5958000FFD7CBE75F'), 'Example User 42', '$2y$17$12345678901234567890121234567890123456789012345678942', '$2y$17$1234567890123456789042$', 'example42@example.com', 1)
注:
- 有一个单独的
prepare
和execute
语句发送到服务器。有趣
测试系列2-PDO(模拟关闭):
测试2A:与测试1相同的设置,除了模拟关闭
测试代码:
未更改的bind语句:
$stmt->bindValue(':superuser', $this->superuser, PDO::PARAM_BOOL);
结果:
预期/实际:
prepare : Expected: true, Actual: true
execute : Expected: true, Actual: true
exception : Expected: false, Actual: false
exceptionMsg : Expected: '', Actual: ''
prepare : Expected: true, Actual: true
execute : Expected: true, Actual: false
exception : Expected: false, Actual: false
exceptionMsg : Expected: '', Actual: ''
errorCode : '00000', ErrorMsg: array (
0 => '00000',
1 => NULL,
2 => NULL,
)
prepare : Expected: true, Actual: true
execute : Expected: true, Actual: true
exception : Expected: false, Actual: false
exceptionMsg : Expected: '', Actual: ''
errorCode : '00000', ErrorMsg: ''
跟踪日志:
150914 14:15:30 569 Query INSERT INTO `account`
(`guid`, `displayname`, `password_hash`, `password_salt`, `email`, `superuser`)
VALUES
(UNHEX('420D4B65565311E5958000FFD7CBE75F'), 'Example User 42', '$2y$17$12345678901234567890121234567890123456789012345678942', '$2y$17$1234567890123456789042$', 'example42@example.com', 1)
150915 11:37:12 693 Prepare INSERT INTO `account`
(`guid`, `displayname`, `password_hash`, `password_salt`, `email`, `superuser`)
VALUES
(UNHEX(?), ?, ?, ?, ?, ?)
150915 12:06:07 709 Prepare INSERT INTO `account`
(`guid`, `displayname`, `password_hash`, `password_salt`, `email`, `superuser`)
VALUES
(UNHEX(?), ?, ?, ?, ?, ?)
709 Execute INSERT INTO `account`
(`guid`, `displayname`, `password_hash`, `password_salt`, `email`, `superuser`)
VALUES
(UNHEX('420D4B65565311E5958000FFD7CBE75F'), 'Example User 42', '$2y$17$12345678901234567890121234567890123456789012345678942', '$2y$17$1234567890123456789042$', 'example42@example.com', 1)
注:
这些都是出乎意料的
-/
- 跟踪日志仅包含
准备语句
李>
- 没有向服务器发送
execute
命令李>
PDO execute
语句返回