TL; DR不,您没有遗漏任何东西。您必须:
在 SQL 字符串中使用带有命名占位符的冒号 ( ),但在执行语句或绑定参数时不需要它们。如果您在该上下文中将其关闭, PHP 将推断 a :
(请参阅下面的第二部分,以获取 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()
or时冒号是可选的PDOStatement::execute()
。这两者的工作方式基本相同:*
$insertRecord->execute(array(
':column1' => $column1,
':column2' => $column2
));
// or
$insertRecord->execute(array(
'column1' => $column1,
'column2' => $column2
));
为什么有效(探索 PHP 源代码)
为什么它会这样工作?好吧,为此我们必须了解 PHP 本身的c语言源代码。为了保持最新,我使用了来自 github (PHP 7) 的最新源代码,但同样的基本分析适用于早期版本。
如文档中所述,PHP 语言希望命名占位符在 SQL 中有一个冒号。当您将参数绑定到 placeholder 时,用于指示参数的文档PDOStatement::bindParam()
必须是表单:name
。但这并不是真的,原因如下。
在绑定参数或执行语句时没有歧义的风险,因为 SQL 占位符必须有一个且只有一个冒号。这意味着 PHP 解释器可以做出一个关键的假设并且安全地这样做。如果您查看pdo_sql_parser.c
PHP 源代码,尤其是第 90 行,您可以看到占位符中的有效字符列表,即字母数字(数字和字母)、下划线和冒号。遵循该文件中代码的逻辑有点棘手,在这里很难解释——我很遗憾地说它涉及到很多语句——但简短的goto
版本是只有第一个字符可以是冒号。
简而言之,:name
是 SQL 中的有效占位符,但name
不是::name
。
这意味着解析器可以安全地假设你到达的时候bindParam()
或者execute()
一个名为的参数name
真的应该是:name
. 也就是说,它可以:
在参数名称的其余部分之前添加一个。事实上,这正是它所做的pdo_stmt.c
,从第 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()
or的上下文中execute()
)变为:name
,这与我们的 SQL 匹配,并且 PDO 非常满意。
最佳实践
从技术上讲,任何一种方式都有效,所以你可以说这是一个偏好问题。但是,如果它不明显,则没有很好的记录。我必须深入研究源代码才能弄清楚这一点,理论上它可以随时改变。为了在 IDE 中保持一致性、可读性和更轻松的搜索,请使用冒号。
* 我说它们“基本上”工作相同,因为上面的 c 代码对省略冒号施加了极小的惩罚。它必须分配更多的内存,构建一个新的字符串,并替换旧的字符串。也就是说,对于像这样的名称,惩罚在纳秒范围内:name
. 如果您倾向于给参数提供很长(如 64 Kb)的名称并且您有很多参数,那么它可能会变得可测量,在这种情况下您还有其他问题......无论如何,这可能都不重要,因为冒号添加读取和解析文件的时间惩罚非常小,因此这两个超微小的惩罚甚至可能抵消。如果您担心这个级别的性能,那么您在晚上保持清醒的问题比我们其他人要酷得多。此外,此时,您可能应该在纯汇编程序中构建您的 web 应用程序。</sarcasm>