21

:PDO を使用する場合、名前付きパラメーターの前にコロン () を使用する記事を多数見てきましたが、コロンを使用しない記事もいくつかあります。コロンは、キーストロークが 1 つ少なくて読みやすいという理由だけで、すぐには使用しません。

私にとってはうまくいっているようですが、コロンの使用に関して欠けている重要なものがあるかどうか知りたいですか?

たとえば、これは問題なく機能します。

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ステートメント パラメーターのコロンに注意してください。

コロンの目的を理解したいのですが。

4

6 に答える 6

28

TL;DRいいえ、何も見逃していません。SQL 文字列内の名前付きプレースホルダーにはコロン ( ) を使用する必要がありますが、ステートメントを実行するときやパラメーターをバインドするときは必要ありません。そのコンテキストで省略した場合、PHP は a を推測します (PHP インタープリター自体のソース コードからの説明と証明については、以下の 2 番目のセクションを参照してください)。::

機能するもの (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 自体の最新の状態を維持するために、github (PHP 7) の最新のソースを使用しましたが、以前のバージョンにも同じ基本的な分析が適用されます。

PHP 言語は、ドキュメントに記載されているように、名前付きプレースホルダーが SQL にコロンを持つことを想定しています。また、パラメータを placeholder にバインドするときは、パラメータが次の形式でなければならないことを示すドキュメントがPDOStatement::bindParam():nameあります。しかし、次の理由から、それは本当ではありません。

SQL プレースホルダーにはコロンを 1 つだけ含める必要があるため、パラメーターをバインドしたり、ステートメントを実行したりするときに、あいまいさが生じるリスクはありません。これは、PHP インタープリターが重要な仮定を安全に行うことができることを意味します。pdo_sql_parser.cPHP ソース コードの特に 90 行目を見ると、プレースホルダー内の有効な文字のリスト、つまり英数字 (数字と文字)、アンダースコア、およびコロンを確認できます。そのファイルのコードのロジックに従うのは少しトリッキーで、ここで説明するのは難しいです — 悲しいことに、多くgotoステートメントが含まれます — しかし、短いバージョンでは、最初の文字だけがコロンになることができます。

簡単に言えば、:nameは SQL の有効なプレースホルダーですが、nameとはそうで::nameはありません。

これは、パーサーが に到達するまでに安全に想定できること、bindParam()またはexecute()名前付きパラメーターnameが実際には である必要があることを意味します:name。つまり:、パラメーター名の残りの部分の前に a を追加するだけです。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)

したがって、 ( ornameのコンテキストで) は になり、これは SQL と一致し、PDO は完全に満足しています。bindParam()execute():name

ベストプラクティス

技術的には、どちらの方法でも機能するため、好みの問題と言えます。ただし、明らかでない場合、これは十分に文書化されていません。これを理解するには、ソース コードを非常に深く掘り下げる必要があり、理論的にはいつでも変更される可能性があります。一貫性、読みやすさ、および IDE での検索を容易にするために、コロンを使用します。


* 上記の C コードでは、コロンを省略した場合に非常に小さなペナルティが課せられるため、それらは「基本的に」同じように機能すると言います。より多くのメモリを割り当て、新しい文字列を構築し、古い文字列を置き換える必要があります。とはいえ、そのペナルティは、次のような名前のナノ秒の範囲にあります:name. パラメーターに非常に長い名前 (64 Kb など) を付ける傾向があり、それらの名前が多数ある場合は、測定可能になる可能性があります。その場合、他の問題があります...おそらく、コロンが追加されるため、おそらくこれは問題ではありませんファイルの読み取りと解析にかかる時間のペナルティは非常に小さいため、これら 2 つの非常に小さなペナルティが相殺される可能性さえあります。このレベルのパフォーマンスを心配している場合は、他の人よりも夜中に目が覚めないという問題を抱えていることになります。また、その時点で、おそらく純粋なアセンブラで Web アプリケーションを構築する必要があります。</sarcasm>

于 2016-08-12T19:24:54.983 に答える
0

公式ドキュメントには、コロンを使用した構文のみが示されています。

$insertRecord->execute(array(
    ':column1' => $column1,
    ':column2' => $column2
));

さらに、内部的に (PDO ソース コード)、先頭のコロンが欠落している場合は自動的に追加されます。
したがって、WITH コロンの構文を使用する必要があります。

于 2016-08-14T16:54:07.487 に答える