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 自体のc言語のソース コードに取り掛かる必要があります。最新の状態を維持するために、github (PHP 7) の最新のソースを使用しましたが、以前のバージョンにも同じ基本的な分析が適用されます。
PHP 言語は、ドキュメントに記載されているように、名前付きプレースホルダーが SQL にコロンを持つことを想定しています。また、パラメータを placeholder にバインドするときは、パラメータが次の形式でなければならないことを示すドキュメントがPDOStatement::bindParam()
:name
あります。しかし、次の理由から、それは本当ではありません。
SQL プレースホルダーにはコロンを 1 つだけ含める必要があるため、パラメーターをバインドしたり、ステートメントを実行したりするときに、あいまいさが生じるリスクはありません。これは、PHP インタープリターが重要な仮定を安全に行うことができることを意味します。pdo_sql_parser.c
PHP ソース コードの特に 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>