7

私は、自分の目的のために、InnoDB(現在はmysql_queryとMyISAM)を使用してPDOとトランザクションにオープンソースアプリケーションを書き直すことを考えています。

私の質問は、プリペアドステートメントを使用するのに合理的なケースはどれですか?

私が読んでいるところはどこでも(ここの多くの投稿でも)、1。セキュリティと2.パフォーマンスのために、いつでもどこでもプリペアドステートメントを使用する必要があると述べられているからです。PHPのマニュアルでさえ、エスケープシングについては言及せずに、プリペアドステートメントを使用することを推奨しています。

セキュリティメカニズムを否定することはできません。しかし、何度も考えてみると、毎回ステートメントを準備してから、一度使用する必要があることに気づきます。それは意味がありません。1つのステートメントに1000倍の変数を挿入する必要がありますが、それは理にかなっていますが、それは明らかです。しかし、これは一般的なeショップやボードが構築されているものではありません。

では、これをどのように克服するのでしょうか?アプリケーション全体でステートメントを準備し、具体的に名前を付けることはできますか?いくつかの異なるステートメントを準備して、それらを名前で使用できますか?これが私が考えている唯一の合理的な解決策だからです(1000倍のものを除く)。

単一のクエリを目的として、$pdo->quoteと呼ばれるこのmysql_real_escapeもあることがわかりました。これを使わないのはなぜですか?なぜ準備に煩わされるのですか?

そして、あなたはこの素晴らしい記事についてどう思いますか? http://blog.ulf-wendel.de/2008/pdo_mysqlnd-prepared-statements-again/

ステートメントの準備によって生じるオーバーヘッドに同意しますか?

ありがとう

4

4 に答える 4

9

これは「時期尚早の最適化」の範疇に入ると思います。

オーバーヘッドはどのくらい重要ですか?あなたはそれを測定しましたか?サーバーのパフォーマンスにまったく影響しますか?

確率はそうではありません。


プラス面としては、セキュリティの面で紛れもない利益があります(これはインターネットベースのショップにとって大きな懸念事項です)。

欠点としては、パフォーマンスに影響を与える可能性があるというリスクがあります。あなたが提供したリンクでは、PDOの準備が適切に実装されていないと、状況によっては準備されていないステートメントよりもパフォーマンスがわずかに低下することを示しています。5000回の実行でのパフォーマンスの違いは0.298秒です。

取るに足らない。「準備されていない」クエリは、実際の環境で安全にするために必要な入力サニタイズルーチンなしで実行されることに気付いた場合はさらにそうです。準備されたクエリを使用しない場合は、SQL攻撃を防ぐために何らかの形式の入力サニタイズが必要であり、その実行方法によっては、結果セットをマッサージする必要がある場合があります。

結論として、重大なパフォーマンスの問題はありませんが、重大なセキュリティ上の利点があります。したがって、プリペアドステートメントを使用することの公式推奨。

あなたの質問では、あなたは「一般的なeショップ」について話します。「一般的なeショップ」には、パフォーマンスの問題があれば、それを心配するのに十分なトラフィックがありません。もう一方の端のセキュリティ問題...

于 2012-06-02T15:48:34.130 に答える
7

私の質問は、プリペアドステートメントを使用するのに合理的なケースはどれですか?

それらのすべて。コミュニティは、関数の使用に公然と反対していmysql_*ます。

注:推奨される代替案

この拡張機能の使用はお勧めしません。代わりに、MySQLiまたはPDO_MySQL拡張機能を使用する必要があります。詳細については、MySQL:APIの選択も参照してください。

この機能の代替手段は次のとおりです。

ソース

しかし、何度も考えてみると、毎回ステートメントを準備してから、一度使用する必要があることが思い浮かびます。それは意味がありません。

あなたはジャガーのためにジオを取引していて、あなたはいつもシートヒーターを使うとは限らないのでジャガーが好きではないと不平を言っています。ライブラリのすべての関数を一貫して使用して、それが優れていることを意味する必要はありません。

単一のクエリを目的として、$pdo->quoteと呼ばれるこのmysql_real_escapeもあることがわかりました。これを使わないのはなぜですか?なぜ準備に煩わされるのですか?

この関数を使用してSQLステートメントを作成する場合は、PDO :: quote()を使用してユーザー入力をSQLステートメントに補間するのではなく、PDO :: prepare()を使用してバインドされたパラメーターを持つSQLステートメントを準備することを強くお勧めします。バインドされたパラメーターを持つプリペアドステートメントは、移植性が高く、便利で、SQLインジェクションの影響を受けないだけでなく、サーバー側とクライアント側の両方がクエリのコンパイル済み形式をキャッシュできるため、補間クエリよりも実行がはるかに高速です。ソース

于 2012-06-02T15:35:23.317 に答える
2

私の質問は次のとおりです。準備されたステートメントを使用するのに妥当なケースはどれですか?

まあ、実際には、それは言いにくいです。特に、ここでどのオープンソース アプリケーションについて話しているかさえ教えてくれなかったので。

例を挙げると、非常に不十分なゲストブック アプリの場合、プリペアド ステートメントを使用する PDO は完璧な選択であり、他のすべてのオープン ソース アプリの 99% にも適しています。しかし、一部の人にとっては、これが実際に違いを生む可能性があります。ここで重要なのは、アプリケーションについて何も話していないことです。

データベースはアプリケーションにとって重要でないわけではないので、その逆でもあります。つまり、アプリケーションはデータベースにとって重要ではありません。

そのため、あなたが尋ねた「謎の」オープンソース アプリケーションについてもっと共有するか、正確に何を知りたいかを私たちに伝える必要があります一般に、それは単純だからです: PDO を取ります。ただし、具体的には違いがあるため、具体的なアプリケーションが何であるかを教えていただく必要があります。そうでない場合、あなたの質問はすでに回答されています。

ところで、アプリケーションが mysql_* スタイルの場合は、mysqli_* インターフェイスに置き換えるだけの方がはるかに簡単です。楽しみのためだけにでも、実際に書き直したことがあれば、それを見ることができたでしょう。

したがって、ここに肉を追加するか、それほど正確ではない答えを受け入れる方がよいでしょう。

于 2012-06-03T23:54:12.997 に答える
0

この質問はかなり古いものですが、OPと同じことを研究している他の人のためにここで概説する必要があるいくつかのトピックは実際には議論されていません.

以下のすべてを要約すると:

  • はい、常に準備ステートメントを使用します
  • はい、mysql ではなく mysqli で PDO を使用します。このようにデータベース システムを切り替えた場合、クエリ、関数呼び出し、引数の代わりにクエリを更新するだけで、準備済みステートメントがサポートされます。
  • パラメータ付きの準備済みステートメントを使用している場合でも、ユーザーが提供したデータを常にサニタイズします
  • DBAL (データベース アブストラクション レイヤー) を調べて、これらすべての要素を簡単に処理し、ニーズに合わせてクエリを操作してください。

PDO::ATTR_EMULATE_PREPARES のトピックがあります。これにより、MySQL >= 5.1.21 でキャッシュされたクエリを呼び出すパフォーマンスが向上します。つまり、PHP は実行前に準備をエミュレートし、実際のデータベースに送信します。エミュレートされてからエミュレートされないまでの時間は、異常に高い ping レートを持つ可能性があるクラウドなどの外部データベース (localhost ではない) を使用しない限り、通常は無視できます。

キャッシュは my.cnf の MySQL 設定にも依存しますが、MySQL の最適化はこの投稿の範囲外です。

<?php
$pdo = new \PDO($connection_string); 
$pdo->setAttribute( \PDO::ATTR_EMULATE_PREPARES, false );
?>

mysqli_ はクライアント側のエミュレーション用の API を提供せず、ステートメントの準備に常に MySQL を使用するため、この点に注意してください。 http://www.php.net/manual/en/mysqli.quickstart.prepared-statements.php

類似した機能があっても違いがあり、ある API が提供する機能が必要で、他の API が提供しない機能が必要になる場合があります。API の選択に関する PHP のリファレンスを参照してください: http://www.php.net/manual/en/mysqlinfo.api.choosing.php

したがって、これは、キャッシュ可能なクエリが MySQL サーバーにキャッシュされ、アプリケーション全体で準備する必要がないため、アプリケーション全体でステートメントを定義する際に求めたこととほぼ一致します。もう 1 つの利点は、クエリの例外が execute() ではなく prepare() でスローされることです。これは、クエリが正しいことを確認するための開発を支援します。

準備を使用するかどうかに関係なく、実際のパフォーマンス上の利点はありません。

プリペアド ステートメントのもう 1 つの利点は、InnoDB for MySQL を使用している場合にトランザクションを操作できることです。トランザクションを開始し、レコードを挿入し、最後の挿入 ID を取得し、別のテーブルを更新し、別のテーブルから削除できます。途中で何かが失敗した場合は、トランザクションが発生する前に rollBack() できます。それ以外の場合は、変更をコミットします。たとえば、新しい注文を処理し、ユーザーの最後の注文列を新しい注文 ID に設定し、保留中の注文を削除しますが、指定された支払いの種類が order_flags テーブルから注文を出すための基準を満たしていないため、rollBack() を実行できます。ユーザーに分かりやすいエラー メッセージを表示します。

セキュリティに関しては、誰もこれに触れていないことにかなり困惑しています。ユーザーが提供したデータを PHP や MySQL を含む任意のシステムに送信する場合は、サニタイズして標準化してください。はい、準備されたステートメントは、データのエスケープに関してある程度のセキュリティを提供しますが、100% の防弾ではありません。

そのため、常にプリペアド ステートメントを使用することは、実際のパフォーマンスの低下がない場合よりもはるかに有益であり、キャッシュにはいくつかの利点がありますが、それでもユーザーが提供したデータをサニタイズする必要があります。1 つのステップは、変数を作業中の目的のデータ型に型キャストすることです。オブジェクトを使用すると、同じデータを操作するたびに記憶する必要がなく、データ型の単一のモデル内で作業するため、これがさらに容易になります。

上記に追加するには、PDO を使用するデータベース抽象化レイヤーを調べる必要があります。たとえばDoctrine DBAL: http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/query-builder.html

DBAL+PDO を使用することの追加の利点は、次のとおりです。

  • やるべき仕事の量を標準化し、短縮することができます。
  • ユーザー提供データのサニタイズを支援
  • 複雑なクエリを簡単に操作
  • ネストされたトランザクションを使用する
  • データベースを簡単に切り替える
  • コードの移植性が高まり、他のプロジェクトで使用できるようになります

たとえば、PDO を拡張し、query()、fetchAll()、および fetch() メソッドをオーバーライドして、これらのメソッドが常に準備済みステートメントを使用するようにし、SQL ステートメントを fetch() または fetchAll() 内に記述する代わりに記述できるようにしました。すべてをもう一度。例えば:

<?php
$pdo = new PDOEnhanced( $connection );
$pdo->fetchAll( "SELECT * FROM foo WHERE bar = 'hi'", PDO::FETCH_OBJ ); 

//would automatically provide
$stmt = $pdo->prepare( "SELECT * FROM foo WHERE bar=?" );
$stmt->execute( array( 'hi' ) );
$resultSet = $stmt->fetchAll( PDO::FETCH_OBJ )
?>

その mysql_* スタイルを提案する人々に関しては、mysqli_* API に置き換えるだけの方がはるかに簡単です。そうではありません。mysql_* 関数の大部分が除外されたか、mysqli_* で引数が変更されました。参照: http://php.net/manual/en/mysqli.summary.php

ただし、プロセスを容易にするために Oracle からリリースされたコンバーターを入手できます: https://wikis.oracle.com/display/mysql/Converting+to+MySQLi

これはファイル ソース テキスト パーサーであり、100% 正確ではないことに注意してください。変更をマージする前に検証してください。また、作成するグローバルにかなりの量のオーバーヘッドが追加されます。

于 2014-01-17T20:08:41.850 に答える