我知道这是一个老问题,但我想深入研究一下,因为我在绑定值/参数时总是忘记数据类型参数的内部工作原理。此外,我对 MySQL 有更多的经验,所以这个答案将更多地与 MySQL 相关。
MySQL 会根据需要自动将数字转换为字符串,反之亦然,所以如果您这样做:
SELECT * FROM tablename WHERE columnname = 42
或
SELECT * FROM tablename WHERE columnname = '42'
MySQL 知道 columnname 应该是什么列类型,如果提供了错误的类型作为值,它将自动进行类型转换。
现在,看看the source code for PDOStatement:
if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_STR && param->max_value_len <= 0 && !Z_ISNULL_P(parameter)) {
if (Z_TYPE_P(parameter) == IS_DOUBLE) {
char *p;
int len = zend_spprintf_unchecked(&p, 0, "%.*H", (int) EG(precision), Z_DVAL_P(parameter));
ZVAL_STRINGL(parameter, p, len);
efree(p);
} else {
convert_to_string(parameter);
}
} else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_INT && (Z_TYPE_P(parameter) == IS_FALSE || Z_TYPE_P(parameter) == IS_TRUE)) {
convert_to_long(parameter);
} else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_BOOL && Z_TYPE_P(parameter) == IS_LONG) {
convert_to_boolean(parameter);
}
您会从上面的代码中注意到,当绑定语句的第三个参数被省略或PDO_PARAM_STR(默认值)时,PHP 会将值类型转换为字符串。当它以PDO_PARAM_INT 提供时,与您实际期望的不同,它确实不 将类型转换为整数,而是 PHP 将其转换为 long!所以如果你这样做:
$stmt->bindValue("integer_column", "41.9", PDO_PARAM_INT);
数据库实际上会收到长值41.9 以用于查询,然后将其四舍五入(至少由 MySQL)到最接近的整数,因此在上面对INTEGER 类型的列的查询将是42。
mysql> SELECT CAST(41.9 AS UNSIGNED);
+------------------------+
| CAST(41.9 AS UNSIGNED) |
+------------------------+
| 42 |
+------------------------+
如果你有一个非数字字符串也是如此,例如"41.9asdf":
$sth->bindValue("integer_column_value", "41.9asdf", PDO_PARAM_INT);
它将首先由 PHP (41.9) 类型转换为 long,然后该值将由 MySQL (42) 四舍五入为整数。
就像我之前提到的,MySQL 会自动将字符串转换为数字,所以如果你给 MySQL 字符串"123",那么将它转换为整数或任何其他数字类型都没有问题。但是,请记住,当 MySQL 从字符串转换为整数时,它会截断而不是舍入,因此这可能是绑定时提供类型的实际区别。
mysql> SELECT CAST('123.6' AS UNSIGNED);
+---------------------------+
| CAST('123.6' AS UNSIGNED) |
+---------------------------+
| 123 |
+---------------------------+
mysql> SELECT CAST(123.6 AS UNSIGNED);
+-------------------------+
| CAST(123.6 AS UNSIGNED) |
+-------------------------+
| 124 |
+-------------------------+
以下是 PHP 中整数列将包含的值:
$stmt->bindValue("integer_column", "123.6", PDO_PARAM_INT); // 124
$stmt->bindValue("integer_column", "123.6"); // 123
$stmt->bindValue("integer_column", (int) "123.6"); // 123
$stmt->bindValue("integer_column", round("123.6")); // 124
请注意,您可能会从上面注意到,PHP 将字符串转换为整数与 MySQL 不同。 MySQL 舍入浮点数,但 PHP 将使用 floor 值:
var_dump((int) "123.6"); // int(123)
var_dump((int) 123.6); // int(123)
现在,要实际回答您问题的安全方面,将第三个参数提供给准备好的语句没有任何安全优势。准备好的语句本身足以在正确完成时减轻 SQL 注入(请参阅this link for some obscure edge-cases)。