为啥 PDO 将我的 bool(false) 参数转换为 string('')?
Posted
技术标签:
【中文标题】为啥 PDO 将我的 bool(false) 参数转换为 string(\'\')?【英文标题】:Why is PDO converting my bool(false) param to string('')?为什么 PDO 将我的 bool(false) 参数转换为 string('')? 【发布时间】:2021-02-28 14:56:35 【问题描述】:我在安装新的 MariaDB 10.5.8 时遇到问题。 STRICT_TRANS_TABLES
已设置,当我尝试使用 $sql
时:
'INSERT INTO test (flag) VALUES (?)'
(其中flag
定义为tinyint(1)
),var_dump($params)
显示为:
array(1)
[0]=>
bool(false)
我收到这条消息:
Incorrect integer value: '' for column `mydb`.`test`.`flag` at row 1
如果我这样做:
'INSERT INTO test (flag) VALUES (false)'
没有参数,它按预期工作。
这是我连接数据库的方式:
$this->PDO = new PDO('mysql:host=' . DB_SERVER . ';dbname=' . DB_NAME . ';charset=utf8mb4', DB_USER, DB_PASSWORD, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_STRINGIFY_FETCHES => false,
]);
$this->PDO->query("SET NAMES 'utf8mb4' COLLATE 'utf8mb4_unicode_ci'");
这就是我将查询/参数发送到 MariaDB 的方式:
$stmt = self::$_instance->PDO->prepare($sql);
$stmt->execute($params);
如果我尝试插入 true
而不是 false
,一切正常。 true
正在转换为 1
,false
正在转换为 ''
。我做错了什么?
【问题讨论】:
【参考方案1】:所有值都被视为PDO::PARAM_STR。
https://www.php.net/manual/en/pdostatement.execute.php
execute()
方法不允许您指定变量的类型。它总是转换成一个字符串,这就是为什么你会得到奇怪的结果:
php > var_dump((string) true);
string(1) "1"
php > var_dump((string) false);
string(0) ""
您可以使用PDOStatement::bindValue() 指定值的类型:
$statement->bindValue('some_bool', true, PDO::PARAM_INT);
请注意,MySQL 缺少“真正的”布尔类型,这就是我推荐 PDO::PARAM_INT
而不是 PDO::PARAM_BOOL
的原因。
【讨论】:
完美,谢谢。我多年来一直在使用 PDO,但是在安装了sql_mode=''
的 MySQL 上(我没有记录这样做,也不记得我的原因)。使用默认 sql_mode
的新安装引发了这个问题。以前我假设所有内容都作为预期类型发送,因为这就是我认为我正在做的事情并且它似乎正在工作......我假设没有 PDO 选项/常量来改变这种行为(类似于我如何设置 PDO::ATTR_STRINGIFY_FETCHES => false
用于获取非字符串数据)?
@Codemonkey 据我所知,不,确实没有。如果您正在编写/运行大量数据库查询,您可能希望查看一个 ORM 库(Doctrine、Eloquent 等),该库在将变量类型映射到适当的 SQL 行/类型方面完成了大部分繁重的工作。但对于简单的应用程序,在 foreach
循环中调用 bindValue()
可能就足够了。
关于你的最后一句话,据我所知PDO::PARAM_BOOL
和PDO::PARAM_INT
在使用PDO_MySQL时是一样的。
这似乎不适用于 PHP 8+。即使通过了bindValue("some_bool", true, PDO::PARAM_INT)
,它也会抱怨“列的值超出范围”。【参考方案2】:
一个更简单的解决方案是预先将一个布尔值转换为 int
$stmt = $PDO->prepare('INSERT INTO test (flag) VALUES (?)');
$stmt->execute([(int)$flag]);
【讨论】:
没错,MySQL 并不特别关心您插入的是数字字符串而不是整数。但其他数据库可能。另请注意,强制转换基本上会忽略任何错误处理,因为(int) 'whatever' === 0
.
一发现“问题”,我就已经在我的数据库包装函数中做了类似的事情,作为我弄清楚发生了什么的权宜之计。只需遍历所有参数并将它们转换为 1 或 0,如果它们是 === true 或 false。
@Duroth 据我所知,您的方法也可以转换为整数,但只能转换一次。 YCS 方法先转换为整数,然后转换为字符串,然后再转换为整数,对吗?
@Dharman 没有;这种方法将转换为整数,然后转换为字符串。就这样。然后将使用字符串参数执行该语句。这可能导致错误,尽管它很可能不会,至少对于 MySQL 连接。我的方法应该稍微可靠一些。虽然这里有很多猜想。
@Duroth 我认为这是关于完整的转换链,PHP 转换为 int,然后 PDO 转换为字符串,最后 mysql 转换为 int【参考方案3】:
这是一个快速的 sn-p,它可以解决在 pdo 参数中将布尔值转换为空字符串的问题
$params = array_map(fn($param) => is_bool($param) ? intval($param) : $param, $params);
【讨论】:
@YourCommonSense 我知道这在理论上可能会导致问题,但这是我在使用布尔参数时想要的行为。理想情况下,$statement->execute($params)
会推断类型,而不是将所有参数视为PDO::PARAM_STR
(或者有一种机制可以轻松地做到这一点)。话虽如此,我看不出您所说的示例查询如何应用 DELETE FROM users WHERE email='0'
不会引起任何我能想象的问题,除非您的电子邮件列中有 0
伙计,你是对的。我将您的自动化与另一个案例混淆了。以上是关于为啥 PDO 将我的 bool(false) 参数转换为 string('')?的主要内容,如果未能解决你的问题,请参考以下文章
Mysql BOOL 字段应该使用哪些值:TRUE/FALSE 或 1/0,为啥?
为啥两个向量的大小<bool> bVec = true,false,true,false,true;向量<char> cVec = 'a', 'b', 'c', 'd',
php switch 为啥 bool TRUE 被判断为了 int 1,NULL 成了 string '',而 bool FALSE 正常?