TL;DR 不,您没有遗漏任何东西。您必须在 SQL 字符串中使用带有命名占位符的冒号 (:),但在执行语句或绑定参数时不需要它们。 PHP 将推断出 @987654329 @ 如果您在该上下文中不使用它(有关 PHP 解释器本身源代码的解释和证明,请参见下面的第二部分)。
什么有效(你可以在 PHP 中做什么)
换句话说,这是可以接受的:
$insertRecord = $conn->prepare('INSERT INTO Table1 (column1, column2)
VALUES(:column1, :column2)');
// ^ ^ note the colons
但这不是,因为占位符名称不明确,看起来像列(或其他)名称:
$insertRecord = $conn->prepare('INSERT INTO Table1 (column1, column2)
VALUES(column1, column2)');
// ^ ^ no colons
相比之下,在使用PDOStatement::bindParam() 或PDOStatement::execute() 时,冒号是可选的。这两者的工作原理基本相同:*
$insertRecord->execute(array(
':column1' => $column1,
':column2' => $column2
));
// or
$insertRecord->execute(array(
'column1' => $column1,
'column2' => $column2
));
为什么有效(探索 PHP 源代码)
为什么会这样?好吧,为此我们必须进入 PHP 本身的 c-language 源代码。为了保持最新状态,我使用了来自 github 的最新源代码(PHP 7),但同样的基本分析也适用于早期版本。
PHP 语言 expects named placeholders to have a colon in the SQL,如文档中所述。和the documentation for PDOStatement::bindParam() indicates the parameter must be of the form :name when you bind the parameter to the placeholder。但事实并非如此,原因如下。
在绑定参数或执行语句时没有歧义的风险,因为 SQL 占位符必须有一个且只有一个冒号。这意味着 PHP 解释器可以做出一个关键的假设并且安全地这样做。如果您查看pdo_sql_parser.c in the PHP source code, particularly at line 90,您可以在占位符中看到有效的字符列表,即字母数字(数字和字母)、下划线和冒号。遵循该文件中代码的逻辑有点棘手,在这里很难解释——我很遗憾地说它涉及到 lot 的 goto 语句——但简短的版本是 只有第一个字符可以是冒号。
简单地说,:name 是 SQL 中的有效占位符,但 name 和 ::name 不是。
这意味着当您到达bindParam() 或execute() 时,解析器可以安全地假设名为name 的参数实际上应该是:name。也就是说,它可以在参数名称的其余部分之前添加一个:。事实上,这正是它所做的,在pdo_stmt.c, starting at line 362:
if (param->name) {
if (is_param && param->name[0] != ':') {
char *temp = emalloc(++param->namelen + 1);
temp[0] = ':';
memmove(temp+1, param->name, param->namelen);
param->name = temp;
} else {
param->name = estrndup(param->name, param->namelen);
}
}
这是用稍微简化的伪代码做的:
if the parameter has a name then
if the parameter name does not start with ':' then
allocate a new string, 1 character larger than the current name
add ':' at the start of that string
copy over the rest of the name to the new string
replace the old string with the new string
else
call estrndup, which basically just copies the string as-is (see https://github.com/php/php-src/blob/1c295d4a9ac78fcc2f77d6695987598bb7abcb83/Zend/zend_alloc.h#L173)
所以,name(在bindParam() 或execute() 的上下文中)变为:name,与我们的SQL 匹配,PDO 非常满意。
最佳实践
从技术上讲,任何一种方式都有效,所以你可以说这是一个偏好问题。但是,如果它不明显,则没有很好的记录。我必须深入研究源代码才能弄清楚这一点,理论上它可以随时改变。为了在 IDE 中保持一致性、可读性和更轻松的搜索,请使用冒号。
* 我说它们“基本上”工作相同,因为上面的 c 代码对省略冒号施加了极小的惩罚。它必须分配更多的内存,构建一个新的字符串,并替换旧的字符串。也就是说,对于:name 这样的名称,该惩罚在纳秒范围内。如果您倾向于给参数提供很长(如 64 Kb)的名称并且您有很多参数,那么它可能会变得可测量,在这种情况下您还有其他问题......无论如何,这可能都不重要,因为冒号添加读取和解析文件的时间惩罚非常小,因此这两个超小的惩罚甚至可能抵消。如果您担心这个级别的性能,那么您在晚上保持清醒的问题比我们其他人要酷得多。此外,此时,您可能应该在纯汇编程序中构建您的 web 应用程序。