php - PDO 准备好的语句 - 参数名称中的冒号是做什么用的?

标签 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();
    }
}

与大多数开发人员使用它相反,它也有效:
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();
    }
}

注意 execute 中的冒号语句参数。

我想了解冒号的用途。

最佳答案

TL;博士 不,你没有错过任何东西。您必须在 SQL 字符串中使用带有命名占位符的冒号 ( : ),但 执行语句或绑定(bind)参数时不需要它们。 PHP 将推断出 :如果您在该上下文中不使用它(有关 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 本身的语言源代码。为了保持最新状态,我使用了来自 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 .但事实并非如此,原因如下。

在绑定(bind)参数或执行语句时不存在歧义的风险,因为 SQL 占位符必须只有一个冒号。这意味着 PHP 解释器可以做出关键假设并且安全地进行。如果你看 pdo_sql_parser.c in the PHP source code, particularly at line 90 ,您可以在占位符中看到有效的字符列表,即字母数字(数字和字母)、下划线和冒号。遵循该文件中代码的逻辑在这里有点棘手且难以解释——我很遗憾地说它涉及很多 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 应用程序。

关于php - PDO 准备好的语句 - 参数名称中的冒号是做什么用的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17386469/

相关文章:

php - PDO 是否处理 sql 函数?

php - mysqli_fetch_assoc()需要参数/调用成员函数bind_param()错误。如何获取并修复实际的mysql错误?

java - 准备语句错误

php - json post 请求大小限制(现在是一个经过验证的 php-mysqli 错误)

javascript - 将变量传递到 Bootstrap 模态中

php - 错误 : number of bound variables does not match number of tokens

mysql - mysql中所有准备好的语句(所有连接)的计数

php - 有关远程测试服务器的问题

php - 无法识别的情况

php - 将错误存储在数据库中的错误函数