Jon*_*han 17 php pdo prepared-statement
我:在使用PDO时看到过许多在命名参数前使用冒号()的文章,以及一些不使用冒号的文章.我很快就不会使用冒号,只是因为它只是一次按键,而且更容易阅读.
它似乎对我来说很好,但我很好奇是否有一些重要的东西,我在冒号的使用时缺少?
例如,这很好用:
function insertRecord ($conn, $column1, $comumn2) {
try {
$insertRecord = $conn->prepare('INSERT INTO Table1 (column1, column2)
VALUES(:column1, :column2)');
$insertRecord->execute(array(
'column1' => $column1,
'column2' => $column2
));
}
catch(PDOException $e) {
echo $e->getMessage();
}
}
Run Code Online (Sandbox Code Playgroud)
与使用此功能的大多数开发人员相反,这也有效:
function insertRecord ($conn, $column1, $comumn2) {
try {
$insertRecord = $conn->prepare('INSERT INTO Table1 (column1, column2)
VALUES(:column1, :column2)');
$insertRecord->execute(array(
':column1' => $column1,
':column2' => $column2
));
}
catch(PDOException $e) {
echo $e->getMessage();
}
}
Run Code Online (Sandbox Code Playgroud)
注意execute语句参数中的冒号.
我想了解冒号的用途.
eli*_*ide 22
TL; DR不,你没有遗漏任何东西.您必须使用冒号(:),在SQL字符串指定的占位符,但执行该语句或绑定参数时,它们不是必需的.PHP将推断出:是否在该上下文中将其保留(请参阅下面的第二部分,以获取PHP解释器本身的源代码的解释和证明).
换句话说,这是可以接受的:
$insertRecord = $conn->prepare('INSERT INTO Table1 (column1, column2)
VALUES(:column1, :column2)');
// ^ ^ note the colons
Run Code Online (Sandbox Code Playgroud)
但事实并非如此,因为占位符名称不明确,看起来像列(或其他)名称:
$insertRecord = $conn->prepare('INSERT INTO Table1 (column1, column2)
VALUES(column1, column2)');
// ^ ^ no colons
Run Code Online (Sandbox Code Playgroud)
相比之下,使用PDOStatement::bindParam()或时,冒号是可选的PDOStatement::execute().这两项工作基本相同:*
$insertRecord->execute(array(
':column1' => $column1,
':column2' => $column2
));
// or
$insertRecord->execute(array(
'column1' => $column1,
'column2' => $column2
));
Run Code Online (Sandbox Code Playgroud)
为什么这样工作?那么,为此我们必须进入PHP本身的c语言源代码.为了保持最新,我使用了github(PHP 7)的最新源代码,但同样的基本分析适用于早期版本.
PHP语言期望命名占位符在SQL中具有冒号,如文档中所述.并且文档用于PDOStatement::bindParam()指示将参数:name绑定到占位符时参数必须是表单.但由于接下来的原因,这并不是真的.
当绑定参数或执行语句时,不存在歧义的风险,因为SQL占位符必须只有一个冒号.这意味着PHP解释器可以做出一个关键的假设并安全地这样做.如果您查看pdo_sql_parser.cPHP源代码,特别是第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);
}
}
Run Code Online (Sandbox Code Playgroud)
它的作用是略微简化的伪代码:
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)
Run Code Online (Sandbox Code Playgroud)
因此,name(在bindParam()或的上下文中execute())变得:name与我们的SQL匹配,PDO非常高兴.
从技术上讲,无论哪种方式都有效,所以你可以说这是一个偏好问题.但是,如果不是很明显,那就没有详细记录.我不得不深入研究源代码来解决这个问题,理论上它可以在任何时候改变.为了在IDE中保持一致性,可读性和更轻松的搜索,请使用冒号.
*我说他们的工作"基本上"相同,因为上面的c代码对于放弃冒号施加了极小的惩罚.它必须分配更多内存,构建一个新字符串,并替换旧字符串.也就是说,对于像这样的名字,惩罚在纳秒范围内:name.如果你很容易让你的参数很长(如64 KB)的名字和你有很多人,在这种情况下,您有其他问题......也许没有这种事情,无论如何,作为结肠增加它可能成为衡量读取和解析文件的时间极短,因此这两个极小的惩罚甚至可能会被抵消.如果你担心这个级别的表现,那么你在晚上保持清醒时会遇到比我们其他人更冷的问题.此外,您应该在纯汇编程序中构建Web应用程序.</ sarcasm>
Bar*_*mar 16
SQL语句中需要冒号,以指示哪些标识符是占位符.
execute()或bindParam()呼叫中的冒号是可选的.文档指定了它们,但实现是否足够巧妙,可以弄清楚如果你把它们排除在外是什么意思(你还有什么意思?).