php - mysql_connect - mysqli_query select*from
PHPでmysql_*関数を使用しないでください。なぜですか? (10)
使いやすさ
分析的および合成的理由はすでに述べられている。 初心者のために、日付の付いたmysql_関数の使用をやめさせるより大きなインセンティブがあります。
現代のデータベースAPIは使いやすいです。
ほとんどの場合、コードを単純化できるバインドされたパラメータです。 shareすると、 PDOへの移行は過度に難しくありません。
一度に大きなコードベースを書き直すには時間がかかります。 この中間的な代替案のためのRaison d'être:
mysql_ *の代わりに同等のpdo_ *関数
<pdo_mysql.php>を使うと、古いmysql_機能から最小限の労力で切り替えることができます。 pdo_
代わりにpdo_
関数ラッパーを追加しpdo_
。
単純に
include_once(
<pdo_mysql.php>);
データベースと対話しなければならない各呼び出しスクリプト内で実行されます。どこにでも
関数の接頭辞を削除し、mysql_
置き換えてmysql_
pdo_
。-
mysql_
connect()
はpdo_
connect()
なりpdo_
-
mysql_
query()
はpdo_
query()
なりquery()
-
mysql_
num_rows()
はpdo_
num_rows()
-
mysql_
insert_id()
はpdo_
insert_id()
-
mysql_
fetch_array()
はfetch_array()
なりpdo_
-
mysql_
fetch_assoc()
はfetch_assoc()
なりpdo_
-
mysql_
real_escape_string()
はreal_escape_string()
なりpdo_
- 等々...
-
あなたのコードは同じように動作し、それでもほとんど同じように見えます:
include_once("pdo_mysql.php"); pdo_connect("localhost", "usrABC", "pw1234567"); pdo_select_db("test"); $result = pdo_query("SELECT title, html FROM pages"); while ($row = pdo_fetch_assoc($result)) { print "$row[title] - $row[html]"; }
Etvoilà。
あなたのコードはPDO を使用しています。
今は実際にそれを利用する時です。
結合されたパラメータは使いやすい
あなたはあまり扱いにくいAPIが必要です。
pdo_query()
は、バインドされたパラメータを非常に容易にサポートします。 古いコードを変換するのは簡単です:
変数をSQL文字列から移動します。
- それらをカンマ区切りの関数パラメータとして
pdo_query()
追加します。 - 疑問符を置く
?
変数が以前のものだったプレースホルダとして。 - 以前は文字列値/変数を囲んでいた単一引用符を取り除きます。
より長めのコードの方が利点がより明らかになります。
多くの場合、文字列変数はSQLに補間されるだけでなく、間にエスケープされた呼び出しと連結されます。
pdo_query("SELECT id, links, html, title, user, date FROM articles
WHERE title='" . pdo_real_escape_string($title) . "' OR id='".
pdo_real_escape_string($title) . "' AND user <> '" .
pdo_real_escape_string($root) . "' ORDER BY date")
と?
プレースホルダを適用しても、それを気にする必要はありません。
pdo_query("SELECT id, links, html, title, user, date FROM articles
WHERE title=? OR id=? AND user<>? ORDER BY date", $title, $id, $root)
pdo_ *はどちらか一方でもまだ許可されていることに注意してください。
変数をエスケープして同じクエリでバインドしないでください。
- プレースホルダ機能は、その背後にある実際のPDOによって提供されます。
- したがって、後で
:named
プレースホルダリストも許可:named
ます。
もっと重要なのは、$ _REQUEST []変数を任意のクエリの後ろに安全に渡すことができることです。 送信された<form>
フィールドがデータベース構造と正確に一致する場合、それはさらに短くなります。
pdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);
とても単純です。 しかし、 てエスケープする必要がある理由について、いくつかのリライトアドバイスと技術的な理由に戻ってみましょう。 mysql_
oldschool sanitize()
関数を修正または削除する
バインドされたparamsをpdo_query
してすべての呼び出しをmysql_
pdo_query
変換したら、冗長なpdo_real_escape_string
呼び出しをすべて削除します。
特に、日付付きチュートリアルで宣伝されているように、1つの形式または別の形式でfilterThis
またはclean
またはfilterThis
またはclean_data
関数を修正する必要があります。
function sanitize($str) {
return trim(strip_tags(htmlentities(pdo_real_escape_string($str))));
}
ここで最も目障りなバグは、文書の欠如です。 より重要なのは、フィルタリングの順序が間違った順序であったことです。
正しい順序は次のとおりです:最も内側の呼び出しとして
stripslashes
し、その後にstrip_tags
、strip_tags
、出力コンテキストのhtmlentities
、最後に_escape_string
をアプリケーションがSQLの_escape_string
直接_escape_string
ようにします。しかし、最初のステップで
_real_escape_string
呼び出しを取り除くだけです 。あなたのデータベースとアプリケーションの流れがHTML文脈で安全な文字列を期待しているならば、今度は
sanitize()
関数の残りの部分をsanitize()
なければならないかもしれません。 今後リリースされるHTMLのみを適用するコメントを追加してください。文字列/値の処理は、PDOとそのパラメータ化された文に委任されます。
あなたの
stripslashes()
機能にstripslashes()
が記述されていれば、それはより高いレベルの監視を示すかもしれません。これは一般的に、廃止された
magic_quotes
からのダメージ(二重エスケープ)を元に戻すためのものでした。 しかし、 中央で固定した方が良いです。ユーザランドの反転アプローチの1つを使用します。 次に、
sanitize
関数でstripslashes()
削除します。
magic_quotesに関する歴史的なメモ。 この機能は正しく使用されなくなりました。 しかし、それは失敗したセキュリティ機能として誤って描かれることがよくあります。 しかし、magic_quotesは、テニスボールが栄養源として失敗したため、セキュリティ機能に失敗しています。 それは単にその目的ではありませんでした。
PHP2 / FIのオリジナルの実装では、 " 引用符が自動的にエスケープされ、フォームデータを直接msqlクエリに渡すのが簡単になりました "というものが明示的に導入されました。 特に、サポートされているASCIIだけであるため、 mSQLで使用することは誤って安全でした。
それから、PHP3 / ZendはMySQLのmagic_quotesを再導入し、誤って文書化しました。 しかし、もともとは便利な機能であり、セキュリティを目的としたものではありませんでした。
準備されたステートメントの違い
文字列変数をSQLクエリにスクランブルするとき、それはあなたが従うために複雑になるだけではありません。 また、MySQLがコードとデータを再度分離するのは無関心な作業です。
SQLインジェクションは、 データがコードコンテキストに流入するときだけです。 データベースサーバは、PHPがもともとクエリ節間の変数を貼り付けた場所を後で特定できません。
バインドされたパラメータを使用すると、PHPコード内でSQLコードとSQLコンテキストの値を区切ります。 しかし、PDO :: EMULATE_PREPARESを除いて、シーンの裏で再びシャッフルされることはありません。 あなたのデータベースは、無条件のSQLコマンドと1:1の変数値を受け取ります。
この回答は、 を削除するmysql_
読みやすさの利点に注意する必要があることを強調しています。 この目に見える、技術的なデータ/コードの分離のために、パフォーマンスの優位性(時には値の異なるINSERTが繰り返されることもあります。 mysql_
パラメータバインディングはまだすべての SQLインジェクションに対して魔法のワンストップソリューションではないことに注意してください。 データ/値の最も一般的な使用を処理します。 しかし、列名/表識別子をホワイトリストに登録することはできず、動的節の構築や単純な配列値のリストに役立つことはできません。
ハイブリッドPDOの使用
これらのpdo_*
ラッパー関数は、コーディングに優しいstop-gap APIを作成します。 (これは、特有の関数シグネチャのシフトでない場合、 MYSQLI
可能性があります)。 彼らはまた、ほとんどの時間で実際のPDOを公開します。
書き換えは、新しいpdo_関数名の使用を止める必要はありません。 あなたは、各pdo_query()を1つずつ、普通の$ pdo-> prepare() - > execute()呼び出しに変えることができます。
しかし、もう一度単純化することをお勧めします。 たとえば、一般的な結果フェッチ:
$result = pdo_query("SELECT * FROM tbl");
while ($row = pdo_fetch_assoc($result)) {
foreach反復で置き換えることができます:
foreach ($result as $row) {
あるいは、より直接的かつ完全なアレイ検索であることがより好ましい。
$result->fetchAll();
ほとんどの場合、PDOよりも有用な警告が表示されます。あるいは、mysql_は通常、失敗したクエリの後に提供します。
別のオプション
だから、これはうまくいけば、mysql_を落とすための実用的な理由と価値ある道筋を視覚化し 。 mysql_
ちょうどpdo切り替えるだけではそれをカットしません。 pdo_query()
は、そのフロントエンドです。
パラメータバインディングを導入したり、より良いAPIの何かを利用することができない限り、それは無意味なスイッチです。 私はそれが新人に落胆させないほど簡単に描かれていることを願っています。 (教育は通常、禁止よりも効果的です。)
最もシンプルなものである可能性がありますが、まだ実験的なコードです。 私はちょうど週末にそれを書いた。 しかし、多くの選択肢があります。 ちょうどPHPデータベースの抽象化のためのGoogleと少しを参照してください。 そのような仕事のためには、常に多くの優れた図書館があります。
データベース相互作用をさらに単純化したい場合、 Paris/Idiormようなマッパーは試してみる価値があります。 誰もJavaScriptでもっと穏やかなDOMを使用しないように、今日は生のデータベースインターフェースをベビーシットする必要はありません。
なぜ、 mysql_*
関数を使用すべきでないのかの技術的な理由は何ですか? (例えば、 mysql_query()
、 mysql_connect()
またはmysql_real_escape_string()
)?
私のサイトで働いていても何か他のものを使うべきですか?
彼らが私のサイトで動作しない場合、どうして私のようなエラーが出るのですか?
警告:mysql_connect():そのようなファイルやディレクトリはありません
MySQLの拡張機能:
- 活発に開発されていない
- PHP 5.5(2013年6月リリース)では正式にdeprecatedれました。
- PHP 7.0(2015年12月リリース)から完全にremovedされました
- つまり、 2018年12月31日現在、PHPのサポート対象バージョンには存在しません。 現在のところ、 セキュリティ更新プログラムのみを取得しています。
- OOインタフェースが欠けている
- サポートしていない:
- ノンブロッキング非同期クエリ
- プリペアドステートメントまたはパラメータ付きクエリ
- ストアドプロシージャ
- 複数のステートメント
- トランザクション
- "新しい"パスワード認証方法(MySQL 5.6ではデフォルトで5.7で必要)
- MySQL 5.1のすべての機能
これは廃止予定であるため、コードを使用することで将来の証明が少なくなります。
プリペアドステートメントのサポートの欠如は、別の関数呼び出しで手動でエスケープするよりも、エラーの起こりにくい、外部データをエスケープおよび引用する方法がより明確であるため、特に重要です。
SQL拡張の比較を参照してください。
mysql_
関数:
- 期限が切れている - それ以上は維持されていない
- 別のデータベースのバックエンドに簡単に移動できないようにする
- 準備文をサポートしていないため、
- プログラマーが連結を使用してクエリを作成し、SQLインジェクションの脆弱性を引き起こすようにする
より良い関数とコード構造が開発されたことを考えると、mysql_ *関数は(PHP 5.5以降)償却されました。機能が償却されたということは、性能とセキュリティの面でそれを改善するための努力がこれ以上行われないことを意味します。
さらに理由が必要な場合:
- mysql_ *関数はプリペアドステートメントをサポートしていません。
- mysql_ *関数はパラメータの束縛をサポートしていません。
- mysql_ *関数にはオブジェクト指向プログラミングの機能がありません。
- リストは続く...
最初に、私たちが全員に与える標準的なコメントから始めましょう。
share 。 彼らはもはや維持されておらずdeprecated 。 赤い箱を見ますか? 代わりに準備文について学び、 PDOまたはMySQLiを使用してください。 この記事では、どちらを決定するのに役立ちます。 PDOを選択した場合、 ここでは良いチュートリアルです。
これを通過して文章で文章を説明してみましょう:
彼らはもはや維持されておらず、正式に廃止されました
これは、PHPコミュニティがこれらの非常に古い機能を徐々にサポートしていないことを意味しています。 彼らはPHPの将来の(最近の)バージョンには存在しない可能性が高いです! これらの関数を引き続き使用すると、遠く離れた場所でコードが壊れる可能性があります。
新しい! - ext / mysqlはdeprecated
新しい! ext / mysql はPHP 7で削除されました 。
代わりに、準備されたステートメント
mysql_*
拡張はプリペアドステートメントをサポートしていません。これは(とりわけ) SQLインジェクションに対する非常に効果的な対策です。 これは、MySQLに依存するアプリケーションに非常に深刻な脆弱性を修正し、攻撃者があなたのスクリプトにアクセスし、データベースに対して可能な限りのクエリを実行できるようにします。詳細については、「 PHPでSQLインジェクションを防止するにはどうすればよいですか?」を参照してください。
レッドボックスを参照してください?
mysql
関数のマニュアルページに行くと、もう使用しないことを説明する赤いボックスが表示されます。PDOまたはMySQLiを使用する
私は上記の答えが本当に長かったので、要約するとわかります。
mysqli拡張には多くのメリットがあります。これは、mysql拡張よりも重要な機能拡張です。
- オブジェクト指向インタフェース
- プリペアドステートメントのサポート
- 複数のステートメントのサポート
- トランザクションのサポート
- 強化されたデバッグ機能
- 組み込みサーバーのサポート
出典:MySQLiの概要
上記の答えで説明したように、mysqlの代わりにmysqliとPDO(PHP Data Objects)があります。
- APIはサーバサイドのプリペアドステートメントをサポートしています:MYSQLiとPDOでサポート
- APIはクライアントサイドのPrepared Statementsをサポートします:PDOのみがサポートします
- APIはストアドプロシージャをサポートしています:MySQLiとPDOの両方
- APIはマルチステートメントとすべてのMySQL 4.1+機能をサポートしています - MySQLiおよび主にPDOでサポート
MySQLiとPDOの両方がPHP 5.0で導入されましたが、MySQLはPHP 3.0より前に導入されました。注目すべき点は、MySQLはPHP5.xに含まれていますが、それ以降のバージョンでは非推奨です。
技術的な理由といえば、ごくわずかなものしかなく、極めて特定的であり、めったに使用されません。あなたの人生でそれらを使用することは決してありません。
たぶん私はあまりにも無知ですが、私はそれらのようなものを使用する機会がありませんでした
- ノンブロッキング、非同期クエリ
- 複数の結果セットを返すストアドプロシージャ
- 暗号化(SSL)
- 圧縮
もしあなたがそれらを必要とするならば、mysqlの拡張からもっとスタイリッシュでモダンなものに向かう技術的な理由は間違いありません。
それにもかかわらず、あなたの経験を少し難しくすることができる、技術的ではない問題もいくつかあります
- 最新のPHPバージョンでこれらの関数をさらに使用すると、廃止予定レベルの通知が発生します。彼らは単にオフにすることができます。
- 遠い将来、デフォルトのPHPビルドから削除される可能性があります。mydsql extがPECLに移行され、すべてのホスティング業者が数十年間働いていたサイトを失いたくないので、PHPをコンパイルすることができます。
- コミュニティからの強い抵抗。これらの正直な機能について言及すると、彼らは厳格なタブー状態にあると言われています。
- PHPの平均的なユーザーであるため、これらの関数を使用する考えは、エラーが発生しやすく、間違っている可能性があります。あなたが間違った方法を教えるこれらの数多くのチュートリアルとマニュアルのためだけです。関数そのものではなく、私はそれを強調しなければなりません。
この後者の問題は問題です。
しかし、私の意見では、提案された解決策はそれほど優れていません。
それは私にはあまりにも理想的な夢のようなすべてのPHPユーザーがSQLクエリを適切に一度に処理する方法を学ぶように思える。おそらくmysql_ *を機械的にmysqli_ *に変更して、そのアプローチを同じままにします。特にmysqliが準備文を使用すると、苦痛を伴う厄介なものになります。
ことは言うまでもありませんネイティブのプリペアドステートメントを保護するのに十分ではない SQLインジェクションから、そしてどちらmysqliのやPDOは、ソリューションを提供しています。
だから、この正直な拡張を戦うのではなく、間違った慣行と戦い、正しい方法で人々を教育することを好むだろう。
また、いくつかの虚偽または非重大な理由があります。
- ストアドプロシージャをサポートしていません(
mysql_query("CALL my_proc");
年齢を問わず使用しています) - トランザクションをサポートしていない(上記と同じ)
- 複数のステートメント(それを必要とする人)をサポートしていない
- 積極的な開発の下ではありません(実際には何があなたに影響しますか?)
- オブジェクト指向インタフェースが欠けている(作成に数時間かかる)
- Prepared StatementsまたはParametrized Queriesをサポートしていない
最後の1つは興味深い点です。mysql extはネイティブのプリペアドステートメントをサポートしていませんが、安全のためには必要ありません。手動で処理されるプレースホルダを使用して準備されたステートメントを簡単に偽装することができます(PDOと同様)。
function paraQuery()
{
$args = func_get_args();
$query = array_shift($args);
$query = str_replace("%s","'%s'",$query);
foreach ($args as $key => $val)
{
$args[$key] = mysql_real_escape_string($val);
}
$query = vsprintf($query, $args);
$result = mysql_query($query);
if (!$result)
{
throw new Exception(mysql_error()." [$query]");
}
return $result;
}
$query = "SELECT * FROM table where a=%s AND b LIKE %s LIMIT %d";
$result = paraQuery($query, $a, "%$b%", $limit);
出来上がり、すべてがパラメータ化し、安全です。
しかし、大丈夫ですが、マニュアルの赤いボックスが気に入らなければ、選択問題が発生します:mysqliまたはPDO?
さて、答えは次のようになります:
- データベース抽象化レイヤーを使用する必要性を理解し、APIを作成する必要があることを理解していれば、mysqliは多くのmysql固有の機能を実際にサポートしているので、非常に良い選択です。
PHPの大部分のように、アプリケーションコードの中で生のAPI呼び出しを使用している場合(本質的に間違った方法です)、PDOだけが選択肢です。この拡張はAPIだけでなく、半DAL、まだ不完全ですが、多くの重要な機能を提供しています。そのうちの2つは、PDOをmysqliと厳密に区別しています。
- mysqliとは異なり、PDOはプレースホルダを値でバインドすることができます。これにより、動的に構築されたクエリは、いくつかの非常に乱雑なコードのスクリーンなしで実行可能になります。
- mysqliとは異なり、PDOは常に単純な通常の配列でクエリ結果を返すことができますが、mysqliはmysqlndのインストールでのみ実行できます。
したがって、平均的なPHPユーザーで、ネイティブプリペアドステートメントを使用する際に頭痛の頭を払う必要がある場合は、PDOが唯一の選択肢です。
しかし、PDOも銀色の弾丸ではなく、苦労しています。
だから、私は、PDOのタグwikiのすべての一般的な落とし穴と複雑なケースの解決策を書いた
それにもかかわらず、エクステンションについて話しているすべての人は、ミスクリとPDOに関する2つの重要な事実を常に欠いています。
準備された声明は銀色の弾丸ではありません。準備されたステートメントを使用してバインドできない動的識別子があります。パラメータの数が不明な動的クエリがあり、クエリ構築が困難な作業になります。
mysqli_ *関数もPDO関数もアプリケーションコードに記述してはいけません。 バインディング、ループ、エラー処理などの内部的な作業をすべて行い、アプリケーションコードをDRYでクリーンにするアプリケーションコードとの間に抽象化レイヤーを
置くべきです。特に、動的クエリー構築のような複雑なケースの場合。
したがって、PDOまたはmysqliに切り替えるだけでは不十分です。そのコードにRAW API関数を呼び出す代わりに、ORM、クエリビルダ、またはデータベース抽象クラスを使用する必要があります。
逆に、アプリケーションコードとmysql APIの間に抽象レイヤーがある場合、どのエンジンが使用されているかは実際問題ではありません。廃止予定になるまでmysql extを使用して、抽象クラスを別のエンジンに簡単に書き直して、すべてのアプリケーションコードをそのまま残しておくことができます。
そのような抽象クラスがどうあるべきかを示すために、私のsafemysqlクラスに基づいたいくつかの例を以下に示します:
$city_ids = array(1,2,3);
$cities = $db->getCol("SELECT name FROM cities WHERE is IN(?a)", $city_ids);
この1行と、PDOに必要なコード量を比較してください。
次に、生のMysqliが準備したステートメントで必要となるコードの狂った量と比較してください。エラー処理、プロファイリング、クエリのロギングが既に組み込まれて実行されていることに注意してください。
$insert = array('name' => 'John', 'surname' => "O'Hara");
$db->query("INSERT INTO users SET ?u", $insert);
これらの名前付きプレースホルダ、バインディング、およびクエリ定義のすべてにおいて、1つのフィールド名が6〜10回繰り返されると、通常のPDO挿入と比較してください。
もう一つの例:
$data = $db->getAll("SELECT * FROM goods ORDER BY ?n", $_GET['order']);
このような実用的なケースを処理するPDOの例はほとんどありません。
そしてそれはあまりにも言葉遣いであり、おそらく安全ではないでしょう。
だから、もう一度 - 生のドライバーだけではなく、初心者のマニュアルの愚かな例だけでなく、実際の問題を解決するのにも役立つ、抽象クラスです。
MySQLエクステンションは、3つのうち最も古いもので、開発者がMySQLとの通信に使用した元々の方法でした。この拡張機能は、PHPとMySQLの両方の新しいリリースで改善されたため、他のmysqli_PDOに有利にdeprecatedました。mysqli_ PDO
mysqli_は、MySQLデータベースを扱うための「改良された」拡張機能です。新しいバージョンのMySQLサーバで利用可能な機能を利用し、機能指向とオブジェクト指向の両方のインタフェースを開発者に公開します。
PDOは、これまで主要なデータベースアクセス拡張(MySQL、PostgreSQL、SQLite、MSSQLなど)に広まっていたほとんどの機能を統合したAPIを提供しています。このインタフェースは、データベース接続、クエリ、結果セット、および低レベル・ドライバーは、データベース・サーバーとの通信およびリソース処理を行います。多くのディスカッションや作業がPDOに入り、現代的でプロフェッショナルなコードでデータベースを操作する適切な方法と考えられています。
多くの理由がありますが、おそらく最も重要なのは、それらの機能は準備されたステートメントをサポートしていないため、安全でないプログラミング方法を奨励することです。プリペアドステートメントはSQLインジェクション攻撃を防ぐのに役立ちます。
mysql_*
関数を使用する場合は、ユーザーが指定したパラメータを実行する必要がありますmysql_real_escape_string()
。1つの場所だけを忘れた場合や、入力の一部しかエスケープしなかった場合、データベースが攻撃を受ける可能性があります。
PDO
またはで準備されたステートメントを使用mysqli
すると、これらの種類のプログラミングエラーがより困難になるようになります。
(他の理由の中でも)入力データが確実に浄化されることははるかに難しいためです。パラメトリッククエリを使用する場合は、PDOまたはmysqliで行うように、リスクを完全に回避できます。
例として、誰かが"enhzflep); drop table users"
ユーザー名として使うことができます。古い関数はクエリごとに複数のステートメントを実行できるので、その厄介なバグのようなものはテーブル全体を削除することができます。
mysqliのPDOを使用する場合、ユーザ名は最終的になり"enhzflep); drop table users"
ます。
bobby-tables.com参照してください。