it-swarm-ja.com

PHPでmysql_ *関数を使用しない方がよいのはなぜですか?

なぜmysql_*関数を使うべきでないのかという技術的な理由は何ですか? (例:mysql_query()mysql_connect()mysql_real_escape_string())?

たとえ彼らが私のサイトで働いていてもなぜ私は他の何かを使うべきですか?

彼らが私のサイトで動作しない場合は、なぜ私はのようなエラーが表示されますか

警告:mysql_connect():そのようなファイルやディレクトリはありません

2362
Madara Uchiha

MySQLエクステンション:

  • 積極的な開発中ではありません
  • 公式に 非推奨PHP 5.5以降(2013年6月リリース)。
  • 削除済み 完全に PHP 7.0現在(2015年12月リリース) [.____。
    • これは、 31 Dec 2018 現在、サポートされているどのバージョンのPHPにも存在しないことを意味します。現時点では、security個の更新のみを取得しています。
  • OOインタフェースがありません
  • サポートしていません:
    • ノンブロッキングの非同期クエリ
    • プリペアドステートメント またはパラメータ化クエリ
    • ストアドプロシージャ
    • 複数のステートメント
    • トランザクション
    • 「新しい」パスワード認証方法(MySQL 5.6ではデフォルトでオン、5.7では必須)
    • MySQL 5.1のすべての機能

これは廃止予定であるため、それを使用するとコードの将来性が減ります。 

プリペアドステートメントのサポートがないことは、外部データを別の関数呼び出しで手動でエスケープするよりも、明確でエラーの発生しにくい外部データのエスケープおよび引用方法を提供するため、特に重要です。

SQL拡張機能の比較 を参照してください。

1934
Quentin

PHPは、MySQLに接続するための3つの異なるAPIを提供します。これらは、 mysql (PHP 7から削除されました)、 mysqli 、および PDO 拡張です。

mysql_*関数は以前は非常に人気がありましたが、その使用は推奨されていません。ドキュメントチームはデータベースのセキュリティ状況について議論しており、一般的に使用されているext/mysql拡張機能から離れるようユーザーを教育することはこの一部です(checkphp.internals:deprecating ext/mysql)。

そして、後のPHP開発者チームは、mysql_connect()mysql_pconnect()、またはE_DEPRECATEDに組み込まれた暗黙的な接続機能を介して、ユーザーがMySQLに接続するときに ext/mysql エラーを生成することを決定しました。

ext/mysqlPHP 5.5の時点で公式に非推奨 であり、 PHP 7の時点で削除されました

レッドボックスが表示されますか?

mysql_*関数のマニュアルページにアクセスすると、赤いボックスが表示され、使用しないことを説明しています。

なぜ


ext/mysqlから離れることは、セキュリティだけでなく、MySQLデータベースのすべての機能にアクセスできることでもあります。

ext/mysqlMySQL 3.2向けに構築され、それ以降ほとんど追加されていませんが、この古いバージョンとの互換性をほとんど維持しているため、コードの保守が少し難しくなります。 ext/mysqlでサポートされていない機能がありません:(from PHP manual)。

mysql_*関数を使用しない理由

  • 活発に開発されていない
  • PHP 7の時点で削除されました
  • OOインターフェイスがない
  • ノンブロッキングの非同期クエリをサポートしていません
  • 準備されたステートメントまたは パラメータ化されたクエリ をサポートしていません
  • ストアドプロシージャをサポートしていません
  • 複数のステートメントをサポートしていません
  • トランザクション をサポートしていません
  • MySQL 5.1のすべての機能をサポートしていません

クエンティンの答えから引用した上記のポイント

準備されたステートメントのサポートの欠如は、外部データを個別の関数呼び出しで手動でエスケープするよりも、明確でエラーの少ない外部データのエスケープおよび引用方法を提供するため、特に重要です。

SQL拡張機能の比較 を参照してください。


非推奨の警告の抑制

コードがMySQLiPDOに変換されている間、E_DEPRECATEDエラーは、_/php.inierror_reportingを設定してE_DEPRECATED:を除外することで抑制できます。

error_reporting = E_ALL ^ E_DEPRECATED

これにより、その他の非推奨警告も非表示になることに注意してください。ただし、これはMySQL以外のものに対するものである可能性があります。 (from PHP manual

記事PDOとMySQLi:どちらを使うべきですか?by Dejan Marjanovic が選択に役立ちます。

そして、より良い方法はPDOです。私は今、簡単なPDOチュートリアルを書いています。


シンプルで短いPDOチュートリアル


Q.私の最初の質問は、「PDO」とは何ですか?

A.「PDO – PHP Data Objects –は、複数のデータベースへの均一なアクセス方法を提供するデータベースアクセスレイヤーです。」

alt text


MySQLへの接続

mysql_*関数を使用するか、古い方法で言うことができます(PHP 5.5以降で非推奨)

$link = mysql_connect('localhost', 'user', 'pass');
mysql_select_db('testdb', $link);
mysql_set_charset('UTF-8', $link);

PDOを使用する場合:新しいPDOオブジェクトを作成するだけです。コンストラクターは、データベースソースPDOのコンストラクターを指定するパラメーターを受け入れます。ほとんどの場合、DSN(データソース名)とオプションでusernamepasswordの4つのパラメーターを使用します。

ここでは、DSN以外のすべてに精通していると思います。これはPDOで新しく追加されました。 DSNは基本的に、PDOに使用するドライバーと接続の詳細を伝えるオプションの文字列です。詳細については、 PDO MySQL DSN を確認してください。

$db = new PDO('mysql:Host=localhost;dbname=testdb;charset=utf8', 'username', 'password');

注:また、charset=UTF-8を使用できますが、場合によってはエラーが発生するため、utf8を使用することをお勧めします。

接続エラーがある場合、PDOExceptionをさらに処理するためにキャッチできるExceptionオブジェクトをスローします。

[をよく読んで: 接続と接続の管理¶

4番目のパラメーターに配列としていくつかのドライバーオプションを渡すこともできます。 PDOを例外モードにするパラメーターを渡すことをお勧めします。一部のPDOドライバーはネイティブ準備済みステートメントをサポートしないため、PDOは準備のエミュレーションを実行します。また、このエミュレーションを手動で有効にすることもできます。ネイティブのサーバー側準備済みステートメントを使用するには、falseを明示的に設定する必要があります。

もう1つは、デフォルトでMySQLドライバーで有効になっている準備エミュレーションをオフにすることですが、PDOを安全に使用するには準備エミュレーションをオフにする必要があります。

準備エミュレーションをオフにする理由を後で説明します。理由を見つけるには、 この投稿 をチェックしてください。

古いバージョンのMySQLを使用している場合にのみ使用できますが、これはお勧めしません。

以下は、その方法の例です。

$db = new PDO('mysql:Host=localhost;dbname=testdb;charset=UTF-8', 
              'username', 
              'password',
              array(PDO::ATTR_EMULATE_PREPARES => false,
              PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));

PDOの構築後に属性を設定できますか?

はい、PDO構築後にsetAttributeメソッドを使用していくつかの属性を設定することもできます。

$db = new PDO('mysql:Host=localhost;dbname=testdb;charset=UTF-8', 
              'username', 
              'password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

エラー処理


PDOのエラー処理は、mysql_*よりもはるかに簡単です。

mysql_*を使用する際の一般的な方法は次のとおりです。

//Connected to MySQL
$result = mysql_query("SELECT * FROM table", $link) or die(mysql_error($link));

dieで処理できないため、OR die()はエラーを処理するのに適した方法ではありません。スクリプトを突然終了し、通常はエンドユーザーに表示したくないエラーを画面にエコーし、血まみれのハッカーにスキーマを発見させます。または、mysql_*関数の戻り値を mysql_error() と組み合わせて使用​​して、エラーを処理することもできます。

PDOはより良い解決策を提供します:例外。 PDOで行うことはすべて、try-catchブロックでラップする必要があります。エラーモード属性を設定することで、PDOを3つのエラーモードのいずれかに強制することができます。 3つのエラー処理モードを以下に示します。

  • PDO::ERRMODE_SILENT。エラーコードを設定するだけで、mysql_*とほぼ同じように動作します。各結果を確認し、$db->errorInfo();を見てエラーの詳細を取得する必要があります。
  • PDO::ERRMODE_WARNINGを上げるE_WARNING。 (実行時警告(致命的でないエラー)。スクリプトの実行は停止しません。)
  • PDO::ERRMODE_EXCEPTION:例外をスローします。 PDOによって発生したエラーを表します。独自のコードからPDOExceptionをスローしないでください。 PHPの例外の詳細については、Exceptionsを参照してください。キャッチされない場合、or die(mysql_error());と非常によく似た動作をします。ただし、or die()とは異なり、PDOExceptionをキャッチして適切に処理することもできます。

よく読む

のような:

$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

そして、以下のようにtry-catchでラップできます:

try {
    //Connect as appropriate as above
    $db->query('hi'); //Invalid query!
} 
catch (PDOException $ex) {
    echo "An Error occured!"; //User friendly message/message you want to show to user
    some_logging_function($ex->getMessage());
}

すぐにtry-catchで処理する必要はありません。いつでもキャッチできますが、try-catchを使用することを強くお勧めします。また、PDOを呼び出す関数の外側でキャッチする方が理にかなっている場合があります。

function data_fun($db) {
    $stmt = $db->query("SELECT * FROM table");
    return $stmt->fetchAll(PDO::FETCH_ASSOC);
}

//Then later
try {
    data_fun($db);
}
catch(PDOException $ex) {
    //Here you can handle error and show message/perform action you want.
}

また、or die()で処理することも、mysql_*のように言うこともできますが、実際にはさまざまです。 display_errors offを有効にしてエラーログを読み取ることで、本番環境で危険なエラーメッセージを非表示にできます。

さて、上記のすべてを読んだ後、おそらくあなたは考えているでしょう:単純なSELECTINSERTUPDATE、またはDELETEステートメントを学習し始めたいとき、一体何なのでしょうか?心配しないで、ここに行きます:


データの選択

PDO select image

mysql_*であなたがしていることは:

<?php
$result = mysql_query('SELECT * from table') or die(mysql_error());

$num_rows = mysql_num_rows($result);

while($row = mysql_fetch_assoc($result)) {
    echo $row['field1'];
}

これでPDOでは、次のようにできます:

<?php
$stmt = $db->query('SELECT * FROM table');

while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    echo $row['field1'];
}

または

<?php
$stmt = $db->query('SELECT * FROM table');
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);

//Use $results

:以下のようなメソッド(query())を使用している場合、このメソッドはPDOStatementオブジェクトを返します。したがって、結果を取得する場合は、上記のように使用します。

<?php
foreach($db->query('SELECT * FROM table') as $row) {
    echo $row['field1'];
}

PDOデータでは、ステートメントハンドルのメソッドである->fetch()を介して取得されます。フェッチを呼び出す前の最善のアプローチは、PDOにデータをフェッチする方法を伝えることです。以下のセクションでこれを説明しています。

フェッチモード

上記のfetch()およびfetchAll()コードでPDO::FETCH_ASSOCを使用していることに注意してください。これは、PDOに、フィールド名をキーとして連想配列として行を返すように指示します。他にも多くのフェッチモードがありますが、それらについては1つずつ説明します。

まず、フェッチモードを選択する方法を説明します。

 $stmt->fetch(PDO::FETCH_ASSOC)

上記では、fetch()を使用しています。以下も使用できます。

今、私はフェッチモードになります:

  • PDO::FETCH_ASSOC:結果セットで返される列名でインデックス付けされた配列を返します
  • PDO::FETCH_BOTH(デフォルト):結果セットで返される列名と0から始まる列番号の両方でインデックス付けされた配列を返します

さらに多くの選択肢があります!それらについては、すべて PDOStatement Fetch documentation。 でお読みください。

行数の取得:

mysql_num_rowsを使用して返される行の数を取得する代わりに、次のようにPDOStatementを取得してrowCount()を実行できます。

<?php
$stmt = $db->query('SELECT * FROM table');
$row_count = $stmt->rowCount();
echo $row_count.' rows selected';

最後に挿入されたIDの取得

<?php
$result = $db->exec("INSERT INTO table(firstname, lastname) VAULES('John', 'Doe')");
$insertId = $db->lastInsertId();

ステートメントの挿入と更新または削除

Insert and update PDO image

mysql_*関数で行っていることは:

<?php
$results = mysql_query("UPDATE table SET field='value'") or die(mysql_error());
echo mysql_affected_rows($result);

そして、pdoでは、これと同じことを次のように行うことができます。

<?php
$affected_rows = $db->exec("UPDATE table SET field='value'");
echo $affected_rows;

上記のクエリ PDO::exec では、SQLステートメントを実行し、影響を受ける行の数を返します。

挿入と削除については後で説明します。

上記の方法は、クエリで変数を使用していない場合にのみ役立ちます。ただし、クエリで変数を使用する必要がある場合は、上記のように 準備されたステートメントまたはパラメーター化されたステートメント isにしようとしないでください。


準備されたステートメント

Q.準備済みステートメントとは何ですか。なぜそれらが必要なのですか?
A.準備済みステートメントは、データのみをサーバーに送信することで複数回実行できるプリコンパイル済みSQLステートメントです。

準備済みステートメントを使用する一般的なワークフローは次のとおりです( Wikipedia three 3 pointから引用 ):

  1. Prepare:ステートメントテンプレートはアプリケーションによって作成され、データベース管理システム(DBMS)に送信されます。パラメーター、プレースホルダー、またはバインド変数と呼ばれる特定の値は未指定のままです(以下の?とラベル付けされています):

    INSERT INTO PRODUCT (name, price) VALUES (?, ?)

  2. DBMSは、ステートメントテンプレートの解析、コンパイル、クエリの最適化を実行し、実行せずに結果を保存します。

  3. Execute:後で、アプリケーションがパラメーターの値を提供(またはバインド)し、DBMSがステートメントを実行します(結果を返す可能性があります)。アプリケーションは、異なる値で必要な回数だけステートメントを実行できます。この例では、最初のパラメーターに「Bread」を、2番目のパラメーターに1.00を提供します。

SQLにプレースホルダーを含めることで、準備済みステートメントを使用できます。基本的に、プレースホルダーのないもの(上記の変数でこれを試さないでください)、名前のないプレースホルダーのあるもの、名前のあるプレースホルダーのあるものがあります。

Q.では、名前付きプレースホルダーとは何ですか。どのように使用しますか?
A.名前付きプレースホルダー。疑問符の代わりに、コロンで始まる説明的な名前を使用します。名前プレースホルダーの値の位置/順序は気にしません:

 $stmt->bindParam(':bla', $bla);

bindParam(parameter,variable,data_type,length,driver_options)

実行配列を使用してバインドすることもできます。

<?php
$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
$stmt->execute(array(':name' => $name, ':id' => $id));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

OOPフレンドのもう1つの優れた機能は、名前付きプレースホルダーが、プロパティが名前付きフィールドと一致すると仮定して、データベースにオブジェクトを直接挿入できることです。例えば:

class person {
    public $name;
    public $add;
    function __construct($a,$b) {
        $this->name = $a;
        $this->add = $b;
    }

}
$demo = new person('john','29 bla district');
$stmt = $db->prepare("INSERT INTO table (name, add) value (:name, :add)");
$stmt->execute((array)$demo);

Q.では、名前のないプレースホルダーとは何ですか。また、どのように使用しますか?
A.例を見てみましょう。

<?php
$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->bindValue(1, $name, PDO::PARAM_STR);
$stmt->bindValue(2, $add, PDO::PARAM_STR);
$stmt->execute();

そして

$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->execute(array('john', '29 bla district'));

上記では、名前プレースホルダーのような名前の代わりに?を見ることができます。最初の例では、変数をさまざまなプレースホルダー($stmt->bindValue(1, $name, PDO::PARAM_STR);)に割り当てます。次に、これらのプレースホルダーに値を割り当て、ステートメントを実行します。 2番目の例では、最初の配列要素は最初の?に移動し、2番目は2番目の?に移動します。

NOTE名前のないプレースホルダーでは、PDOStatement::execute()メソッドに渡す配列内の要素の適切な順序に注意する必要があります。


SELECTINSERTUPDATEDELETE準備済みクエリ

  1. SELECT

    $stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
    $stmt->execute(array(':name' => $name, ':id' => $id));
    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
  2. INSERT

    $stmt = $db->prepare("INSERT INTO table(field1,field2) VALUES(:field1,:field2)");
    $stmt->execute(array(':field1' => $field1, ':field2' => $field2));
    $affected_rows = $stmt->rowCount();
    
  3. DELETE

    $stmt = $db->prepare("DELETE FROM table WHERE id=:id");
    $stmt->bindValue(':id', $id, PDO::PARAM_STR);
    $stmt->execute();
    $affected_rows = $stmt->rowCount();
    
  4. UPDATE

    $stmt = $db->prepare("UPDATE table SET name=? WHERE id=?");
    $stmt->execute(array($name, $id));
    $affected_rows = $stmt->rowCount();
    

注意:

ただし、PDOMySQLiは完全に安全ではありません。答えを確認してくださいPDO準備文はSQLインジェクションを防ぐのに十分ですか?by ircmaxell 。また、私は彼の答えの一部を引用しています:

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->query('SET NAMES GBK');
$stmt = $pdo->prepare("SELECT * FROM test WHERE name = ? LIMIT 1");
$stmt->execute(array(chr(0xbf) . chr(0x27) . " OR 1=1 /*"));
1243
NullPoiиteя

最初に、全員に提供する標準的なコメントから始めましょう。

新しいコード ではmysql_*関数を使用しないでください。それらはもはや保守されておらず 、公式には非推奨です赤いボックス ?代わりに prepared statement について学び、 PDO または MySQLiを使用します - この記事 はどちらを決定するのに役立ちます。 PDOを選択する場合、 こちらが良いチュートリアルです

文章ごとにこれを説明し、説明しましょう:

  • これらはもはやメンテナンスされておらず、公式に廃止されました

    これは、PHPコミュニティがこれらの非常に古い機能のサポートを徐々に落としていることを意味します。 PHPの将来(最新)バージョンには存在しない可能性があります!これらの関数を継続して使用すると、将来的に(そうではないとして)コードが破損する可能性があります。

    新しい! -ext/mysqlはPHP 5.5!の時点で公式に非推奨になりました

    新しい! ext/mysqlはPHP 7で削除されました。

  • 代わりに、準備されたステートメントを学習する必要があります

    mysql_*拡張は、 SQLインジェクションに対する(特に)非常に効果的な対策である prepared statement をサポートしません。 MySQL依存アプリケーションの非常に深刻な脆弱性を修正しました。これにより、攻撃者がスクリプトにアクセスし、データベースで any query を実行できるようになります。

    詳細については、を参照してくださいPHPでSQLインジェクションを防ぐにはどうすればよいですか?

  • 赤いボックスを参照してください/

    任意のmysql関数のマニュアルページにアクセスすると、赤いボックスが表示され、使用しないことを説明します。

  • PDOまたはMySQLi のいずれかを使用

    より優れた、より堅牢で、しっかりと構築された代替物、PDO-PHP Database Objectがあり、完全な_を提供しますOOPデータベースインタラクションへのアプローチ、およびMySQLi。これはMySQL固有の改善です。

292
Madara Uchiha

mysql_の機能:

  1. 古くなっています - もうメンテナンスされていません
  2. 他のデータベースバックエンドに簡単に移動できないようにする
  3. 準備文をサポートしない
  4. sQLインジェクションの脆弱性につながるクエリを構築するために連結を使うことをプログラマに奨励する
136
Alnitak

technicalの理由から言えば、ごく少数の、非常に具体的でめったに使用されないものがあります。たぶんあなたはあなたの生活の中でこれらを使うことは決してないでしょう。
たぶん私はあまりにも無知ですが、私はそれらのようなものを使う機会がありませんでした。 

  • ノンブロッキングの非同期クエリ
  • 複数の結果セットを返すストアドプロシージャ
  • 暗号化(SSL)
  • 圧縮

あなたがそれらを必要とするなら - これらはmysql拡張からもっとスタイリッシュで現代的に見えるものに移るための疑いのない技術的な理由です。

それにもかかわらず、技術的ではない問題もいくつかあります。これはあなたの経験を少し難しくする可能性があります

  • 最新のPHPバージョンでこれらの関数をさらに使用すると、非推奨レベルの通知が表示されます。彼らは単にオフにすることができます。
  • 遠い将来、デフォルトのPHP buildから削除される可能性があります。 mydsql extがPECLに移行され、何十年もサイトが機能していたクライアントを失いたくないので、すべてのホストがそれを使ってPHPをコンパイルできれば幸いです。 
  • stackoverflowコミュニティからの強い抵抗。あなたがこれらの正直な機能に言及するたびに、あなたはそれらが厳格なタブーの下にあると言われています。
  • 平均的なPHPユーザーであるため、これらの関数を使用するという考えは間違いが起こりやすく間違っています。間違った方法を教えているこれらの多数のチュートリアルやマニュアルがすべてあるからです。機能そのものではありません - 私はそれを強調しなければなりません - しかしそれらが使われる方法。

この後者の問題は問題です。
しかし、私の意見では、提案されている解決策もそれ以上良くない。
私には思えます あまりにも理想的です これらすべてのPHPユーザーがSQLクエリーを一度に正しく処理する方法を学ぶことを夢見ています。たぶん彼らはただmysql_ *をmysqli_ *に機械的に変更するでしょう、 アプローチは同じままにします 。特にmysqliはプリペアドステートメントの使用を信じられないほど苦痛で面倒にします。
nativeプリペアドステートメント は、SQLインジェクションから を保護するのに十分ではないことは言うまでもなく、mysqliもPDOも解決策を提供していません。 

ですから、この正直な拡張と戦うのではなく、私は間違った慣習と戦い、正しい方法で人々を教育することを望みます。 

また、 のように、いくつかの誤ったまたは重要でない理由があります。

  • ストアドプロシージャをサポートしていません(年齢のためにmysql_query("CALL my_proc");を使用していました)
  • トランザクションをサポートしません(上記と同じ)
  • Multiple Statementをサポートしていません(必要な人は誰ですか?)
  • 積極的な開発中ではありません(それで何が実用的な方法であなたに影響しますか?)
  • OOインタフェースがありません(作成するのに数時間かかります)。
  • 準備済みステートメントまたはパラメーター化照会をサポートしません

最後の点は興味深い点です。 mysql extはnative prepared statementをサポートしていませんが、安全のために必須ではありません。手動で処理されたプレースホルダーを使用して、準備済みステートメントを簡単に偽造することができます(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);

voila、すべてがパラメータ化されていて安全です。

もしマニュアルの赤いボックスが気に入らなければ、mysqliとPDOのどちらかを選択してください。

その答えは次のようになります。

  • データベース抽象化層を使用してそれを作成するためのAPIを探す必要性を理解しているなら、 mysqli はmysql特有の多くの機能をサポートしているので非常に良い選択です。
  • 大多数のPHP人のように、アプリケーションコードで生のAPI呼び出しを使用している場合(これは基本的に間違った方法です) - この拡張子のふりをするので、PDOが唯一の選択肢です APIだけではなく、セミDALでありながら、まだ不完全ですが、多くの重要な機能を提供しています。そのうちの2つが、PDOをmysqliと著しく区別しています。

    • mysqliとは異なり、PDOはプレースホルダby valueをバインドすることができます。これにより、画面が非常に面倒なコードを何度も表示しなくても動的に構築されたクエリを実行できます。
    • mysqliとは異なり、PDOは常に単純な通常の配列でクエリ結果を返すことができますが、mysqliはmysqlndインストール環境でのみ実行できます。

ですから、あなたが平均的なPHPユーザーであり、ネイティブの準備済みステートメントを使用するときに頭痛の種を自分自身で節約したいのであれば、PDOが唯一の選択肢です。
しかしながら、PDOもまた弾丸ではなく、困難を伴います。
それで、私は PDOタグwiki にすべての一般的な落とし穴と複雑なケースに対する解決策を書いた

それにもかかわらず、MysqliとPDOについて 2つの重要な事実 が欠けている拡張機能について話す人は誰でも:

  1. 準備された文 銀の弾丸ではありません 。準備済みステートメントを使用してバインドできない動的識別子があります。未知の数のパラメータを持つ動的クエリがあり、それがクエリ構築を困難なタスクにしています。

  2. アプリケーションコードにmysqli_ *もPDO関数も現れてはいけません。
    それらとアプリケーションコードの間に 抽象化レイヤ があるはずです。これは内部でバインディング、ループ、エラー処理などのすべての汚い仕事をし、アプリケーションコードをDRYそしてきれいに。特に動的クエリ構築のような複雑な場合に。

そのため、PDOまたはmysqliに切り替えるだけでは不十分です。コード内で生のAPI関数を呼び出す代わりに、ORM、クエリービルダー、または任意のデータベース抽象化クラスを使用する必要があります。
反対に、アプリケーションコードとmysql APIの間に抽象化層がある場合 - 実際にはどのエンジンを使用するかは重要ではありません。 mysql extを廃止予定になるまで使用してから、抽象クラスを別のエンジンに簡単に書き直すことができます。 すべてのアプリケーションコードをそのまま使用することができます。

以下は、そのような抽象化クラスがどうあるべきかを示すために、私の safemysql class )に基づいた例です。

$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);

すべてのこれらの多数の名前付きプレースホルダー、バインディング、およびクエリ定義で、すべての単一のフィールド名が6〜10回繰り返されている場合は、通常のPDO挿入と比較してください。

もう一つの例:

$data = $db->getAll("SELECT * FROM goods ORDER BY ?n", $_GET['order']);

このような実際的なケースを処理するためのPDOの例はほとんどありません。
そして、それはあまりにも言葉になりすぎて、おそらく安全ではないでしょう。 

だから、もう一度言いますが、それはあなたの関心事ではなく抽象クラスであるべきです。初心者向けマニュアルの愚かな例だけでなく、現実の問題を解決するのに役立ちます。 

101

多くの理由がありますが、おそらく最も重要な理由は、これらの関数はプリペアドステートメントをサポートしていないため、安全でないプログラミング手法を促進することです。準備済みステートメントは、SQLインジェクション攻撃を防ぐのに役立ちます。

mysql_*関数を使用するときは、mysql_real_escape_string()を通してユーザー提供のパラメーターを実行することを忘れないでください。 1か所だけで忘れた場合、または入力の一部だけをエスケープした場合は、データベースが攻撃を受ける可能性があります。

PDOまたはmysqliでプリペアドステートメントを使用すると、このようなプログラミングエラーが発生しにくくなります。

88
Trott

(他の理由もありますが)入力データが確実にサニタイズされるようにするのははるかに難しいからです。 PDOやmysqliと同様に、パラメータ化されたクエリを使用する場合は、完全にリスクを回避できます。

例として、誰かが"enhzflep); drop table users"をユーザー名として使うことができます。古い関数ではクエリごとに複数のステートメントを実行することができるので、その厄介なバグのようなものはテーブル全体を削除できます。

もしmysqliのPDOを使うのであれば、ユーザー名は"enhzflep); drop table users"になります。

bobby-tables.com を参照してください。

71
enhzflep

この回答は、不適切に書かれたPHPユーザー検証コードを回避すること、これらの攻撃がどのように(そして何を使って)行われるのか、そして古いMySQL関数を安全なプリペアドステートメントで置き換える方法を示すことです。そして、基本的に、StackOverflowユーザー(おそらく多くの担当者がいる)が、新しいユーザーにコードを改善するための質問をしているのに吠えています。

まず、このテスト用のmysqlデータベースを作成してください(私はmine prepと呼んでいます)。

mysql> create table users(
    -> id int(2) primary key auto_increment,
    -> userid tinytext,
    -> pass tinytext);
Query OK, 0 rows affected (0.05 sec)

mysql> insert into users values(null, 'Fluffeh', 'mypass');
Query OK, 1 row affected (0.04 sec)

mysql> create user 'prepared'@'localhost' identified by 'example';
Query OK, 0 rows affected (0.01 sec)

mysql> grant all privileges on prep.* to 'prepared'@'localhost' with grant option;
Query OK, 0 rows affected (0.00 sec)

これで、PHPコードに移動できます。

次のスクリプトがWebサイトの管理者用の検証プロセスであるとしましょう(単純化したもので、コピーしてテストに使用すれば正常に動作します)。

<?php 

    if(!empty($_POST['user']))
    {
        $user=$_POST['user'];
    }   
    else
    {
        $user='bob';
    }
    if(!empty($_POST['pass']))
    {
        $pass=$_POST['pass'];
    }
    else
    {
        $pass='bob';
    }

    $database='prep';
    $link=mysql_connect('localhost', 'prepared', 'example');
    mysql_select_db($database) or die( "Unable to select database");

    $sql="select id, userid, pass from users where userid='$user' and pass='$pass'";
    //echo $sql."<br><br>";
    $result=mysql_query($sql);
    $isAdmin=false;
    while ($row = mysql_fetch_assoc($result)) {
        echo "My id is ".$row['id']." and my username is ".$row['userid']." and lastly, my password is ".$row['pass']."<br>";
        $isAdmin=true;
        // We have correctly matched the Username and Password
        // Lets give this person full access
    }
    if($isAdmin)
    {
        echo "The check passed. We have a verified admin!<br>";
    }
    else
    {
        echo "You could not be verified. Please try again...<br>";
    }
    mysql_close($link);

?>

<form name="exploited" method='post'>
    User: <input type='text' name='user'><br>
    Pass: <input type='text' name='pass'><br>
    <input type='submit'>
</form>

一見すると十分に合法的に見えます。

ユーザーはログイン名とパスワードを入力する必要がありますね。

華麗で、以下に入力しないでください。

user: bob
pass: somePass

それを提出してください。

出力は以下のとおりです。

You could not be verified. Please try again...

スーパー!期待通りに動作しているので、今実際のユーザ名とパスワードを試してみましょう:

user: Fluffeh
pass: mypass

すごい!ハイファイブオールラウンド、コードは正しく管理者を検証しました。パーフェクトだ!

そうではありません。ユーザーは賢い小さな人だと言うことができます。その人は私だと言うことができます。

以下を入力してください。

user: bob
pass: n' or 1=1 or 'm=m

そして出力は次のとおりです。

The check passed. We have a verified admin!

おめでとうございます、あなたは私があなたのスーパー保護された管理者だけのセクションに私が偽のユーザー名と偽のパスワードを入力するのを許可するだけで入ることを許可しました。あなたが私を信じていないのであれば、真剣に、私が提供したコードでデータベースを作成し、PHPコードを実行してください - 一見するとかなりユーザー名とパスワードを確かめているようです。

それで、答えとして、ISなぜあなたは怒鳴られているのですか。

それでは、何が悪かったのか、そしてなぜ私があなたのスーパーアドミニオンリーバット洞窟に入ったのかを見てみましょう。私は推測して、あなたがあなたの入力に注意を払っていなかったと仮定し、単にそれらを直接データベースに渡しました。実際に実行していたクエリを変更するような方法で入力を作成しました。それで、それは何であるべきであり、そしてそれは何であることになったのでしょうか?

select id, userid, pass from users where userid='$user' and pass='$pass'

これがクエリですが、変数を実際に使用した入力に置き換えると、次のようになります。

select id, userid, pass from users where userid='bob' and pass='n' or 1=1 or 'm=m'

最初にパスワードを囲む一重引用符を閉じ、次にまったく新しい比較を導入するように、私がどのようにして「パスワード」を構成したかを見てください。それから安全のために、私たちが最初に持っていたコードで期待通りにシングルクォートが閉じられるように私は別の "文字列"を追加しました。

しかし、これは今あなたに叫んでいる人々についてではありません、これはあなたのコードをより安全にする方法をあなたに示すことについてです。

それで、何がうまくいかなかったのでしょうか。

これは古典的なSQLインジェクション攻撃です。その点で最も単純なものの1つです。攻撃ベクトルの規模では、これは戦車を攻撃し、そして勝つ幼児です。

それでは、どのようにして私たちはあなたの聖なる管理者セクションを保護し、それを素晴らしく安全なものにするのですか?最初にすべきことは、これらの本当に古くて廃止予定のmysql_*関数の使用をやめることです。私は知っている、あなたがオンラインで見つけたチュートリアルに従った、そしてそれが働く、しかしそれは古くて、それは時代遅れで、数分の間に、私は汗を壊すほど多くせずにそれを過ぎて壊した。

これで、 mysqli_ または _ pdo _ を使用するより良い選択肢があります。私は個人的にはPDOが大好きなので、この回答の残りの部分ではPDOを使用します。長所と短所がありますが、個人的には長所が長所よりも長所を上回ることがわかります。接続文字列を変更するだけで、MySQLまたはOracleを使用しているかどうかにかかわらず、複数のデータベースエンジン間で移植性があります。私はきれいが好きです。

では、そのコードをもう一度見てみましょう。今回はPDOオブジェクトを使用して書かれています。

<?php 

    if(!empty($_POST['user']))
    {
        $user=$_POST['user'];
    }   
    else
    {
        $user='bob';
    }
    if(!empty($_POST['pass']))
    {
        $pass=$_POST['pass'];
    }
    else
    {
        $pass='bob';
    }
    $isAdmin=false;

    $database='prep';
    $pdo=new PDO ('mysql:Host=localhost;dbname=prep', 'prepared', 'example');
    $sql="select id, userid, pass from users where userid=:user and pass=:password";
    $myPDO = $pdo->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
    if($myPDO->execute(array(':user' => $user, ':password' => $pass)))
    {
        while($row=$myPDO->fetch(PDO::FETCH_ASSOC))
        {
            echo "My id is ".$row['id']." and my username is ".$row['userid']." and lastly, my password is ".$row['pass']."<br>";
            $isAdmin=true;
            // We have correctly matched the Username and Password
            // Lets give this person full access
        }
    }

    if($isAdmin)
    {
        echo "The check passed. We have a verified admin!<br>";
    }
    else
    {
        echo "You could not be verified. Please try again...<br>";
    }

?>

<form name="exploited" method='post'>
    User: <input type='text' name='user'><br>
    Pass: <input type='text' name='pass'><br>
    <input type='submit'>
</form>

主な違いはmysql_*関数がもうないということです。それはすべてPDOオブジェクトを介して行われます。第二に、それは準備されたステートメントを使用しています。さて、あなたが求める前準備された声明は何ですか?これは、クエリを実行する前にデータベースに通知する方法です。クエリとは、実行しようとしているものです。この場合、データベースに次のように伝えます。

次に、execute文で、データベースに、現在想定しているすべての変数を含む配列を渡します。

結果は素晴らしいです。ユーザー名とパスワードの組み合わせをもう一度試してみましょう。

user: bob
pass: somePass

ユーザーは確認されませんでした。驚くばかり。

どうですか?

user: Fluffeh
pass: mypass

ああ、私はちょっと興奮しました、それはうまくいきました:チェックは通りました。確認済みの管理者がいます。

それでは、私たちの小さな検証システムを乗り越えるために、巧妙な章が入るであろうデータを試してみましょう:

user: bob
pass: n' or 1=1 or 'm=m

今回は、次のようになりました。

You could not be verified. Please try again...

質問を投稿するときに怒鳴られているのはこのためです。これは、試していなくてもコードがバイパスされる可能性があることを人々が理解できるためです。この質問と回答を使用して、コードを改善し、安全性を高め、最新の機能を使用してください。

最後に、これはこれがPERFECTコードであるということではありません。たとえば、ハッシュされたパスワードを使用して、機密情報をデータベースに格納するときに、プレーンテキストで格納しないで、複数レベルの検証を行うようにすることができます。あなたは古いインジェクションを起こしやすいコードをこれに変更するだけで、良いコードを書くことができます - そしてこれまで読んできたという事実は私があなたがこのタイプを実装するだけではないという希望の感覚あなたのウェブサイトやアプリケーションを書くときのコードの量ですが、あなたは外出して私が今言及した他のことを研究するかもしれません - そしてそれ以上。機能しない最も基本的なコードではなく、可能な限り最高のコードを作成してください。

62
Fluffeh

MySQLエクステンションは、3つのうち最も古いもので、開発者がMySQLと通信するために使用していた最初の方法でした。 PHPとMySQLの両方の新しいリリースで改善されたため、この拡張は現在、他の two代替 を支持して 非推奨 になっています。

  • MySQLi はMySQLデータベースを扱うための '改善された'拡張です。それはMySQLサーバの新しいバージョンで利用可能である機能を利用して、開発者に機能指向とオブジェクト指向の両方のインターフェースを公開し、そして他のいくつかの気の利いたことをします。

  • PDO は、これまで主要なデータベースアクセス拡張、つまりMySQL、PostgreSQL、SQLite、MSSQLなどに分散されていた機能の大部分を統合するAPIを提供します。データベース接続、クエリ、および結果セットを処理し、低レベルドライバがデータベースサーバとの通信およびリソース処理を実行します。 PDOについては多くの議論と作業が行われており、現代的でプロフェッショナルなコードでデータベースを操作するための適切な方法と見なされています。

30
Alexander

要約すると、私は上記の答えは非常に長いと思います。

Mysqliエクステンションには多くのの利点があり、 mysqlエクステンションに比べて重要な機能強化は次のとおりです。

  • オブジェクト指向インタフェース
  • 準備済みステートメントのサポート
  • 複数ステートメントのサポート
  • トランザクションのサポート
  • 強化されたデバッグ機能
  • 組み込みサーバーのサポート

ソース: MySQLiの概要


上記の回答で説明したように、mysqlの代替はmysqliとPDO(PHP Data Objects)です。

  • APIはサーバーサイドのPrepared Statementをサポートします:MYSQLiとPDOでサポートされています
  • APIはクライアントサイドのPrepared Statementをサポートしています:PDOでのみサポートされています
  • APIはストアドプロシージャをサポートします:MySQLiとPDOの両方
  • APIはMultiple StatementsとすべてのMySQL 4.1+機能をサポートします - MySQLiとほとんどの場合PDOでもサポートされています

MySQLiとPDOの両方がPHP 5.0で導入されましたが、MySQLはPHP 3.0より前に導入されました。注目すべき点は、MySQLはPHP5.xに含まれていますが、それ以降のバージョンでは推奨されていないことです。 

19
Ani Menon

MysqliまたはPDOを使用してほとんどすべてのmysql_*関数を定義することは可能です。古いPHPアプリケーションの上にそれらを含めるだけで、PHP 7で動作します。私の解決策 ここ

<?php

define('MYSQL_LINK', 'dbl');
$GLOBALS[MYSQL_LINK] = null;

function mysql_link($link=null) {
    return ($link === null) ? $GLOBALS[MYSQL_LINK] : $link;
}

function mysql_connect($Host, $user, $pass) {
    $GLOBALS[MYSQL_LINK] = mysqli_connect($Host, $user, $pass);
    return $GLOBALS[MYSQL_LINK];
}

function mysql_pconnect($Host, $user, $pass) {
    return mysql_connect($Host, $user, $pass);
}

function mysql_select_db($db, $link=null) {
    $link = mysql_link($link);
    return mysqli_select_db($link, $db);
}

function mysql_close($link=null) {
    $link = mysql_link($link);
    return mysqli_close($link);
}

function mysql_error($link=null) {
    $link = mysql_link($link);
    return mysqli_error($link);
}

function mysql_errno($link=null) {
    $link = mysql_link($link);
    return mysqli_errno($link);
}

function mysql_ping($link=null) {
    $link = mysql_link($link);
    return mysqli_ping($link);
}

function mysql_stat($link=null) {
    $link = mysql_link($link);
    return mysqli_stat($link);
}

function mysql_affected_rows($link=null) {
    $link = mysql_link($link);
    return mysqli_affected_rows($link);
}

function mysql_client_encoding($link=null) {
    $link = mysql_link($link);
    return mysqli_character_set_name($link);
}

function mysql_thread_id($link=null) {
    $link = mysql_link($link);
    return mysqli_thread_id($link);
}

function mysql_escape_string($string) {
    return mysql_real_escape_string($string);
}

function mysql_real_escape_string($string, $link=null) {
    $link = mysql_link($link);
    return mysqli_real_escape_string($link, $string);
}

function mysql_query($sql, $link=null) {
    $link = mysql_link($link);
    return mysqli_query($link, $sql);
}

function mysql_unbuffered_query($sql, $link=null) {
    $link = mysql_link($link);
    return mysqli_query($link, $sql, MYSQLI_USE_RESULT);
}

function mysql_set_charset($charset, $link=null){
    $link = mysql_link($link);
    return mysqli_set_charset($link, $charset);
}

function mysql_get_Host_info($link=null) {
    $link = mysql_link($link);
    return mysqli_get_Host_info($link);
}

function mysql_get_proto_info($link=null) {
    $link = mysql_link($link);
    return mysqli_get_proto_info($link);
}
function mysql_get_server_info($link=null) {
    $link = mysql_link($link);
    return mysqli_get_server_info($link);
}

function mysql_info($link=null) {
    $link = mysql_link($link);
    return mysqli_info($link);
}

function mysql_get_client_info() {
    $link = mysql_link();
    return mysqli_get_client_info($link);
}

function mysql_create_db($db, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    return mysqli_query($link, "CREATE DATABASE `$db`");
}

function mysql_drop_db($db, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    return mysqli_query($link, "DROP DATABASE `$db`");
}

function mysql_list_dbs($link=null) {
    $link = mysql_link($link);
    return mysqli_query($link, "SHOW DATABASES");
}

function mysql_list_fields($db, $table, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    $table = str_replace('`', '', mysqli_real_escape_string($link, $table));
    return mysqli_query($link, "SHOW COLUMNS FROM `$db`.`$table`");
}

function mysql_list_tables($db, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    return mysqli_query($link, "SHOW TABLES FROM `$db`");
}

function mysql_db_query($db, $sql, $link=null) {
    $link = mysql_link($link);
    mysqli_select_db($link, $db);
    return mysqli_query($link, $sql);
}

function mysql_fetch_row($qlink) {
    return mysqli_fetch_row($qlink);
}

function mysql_fetch_assoc($qlink) {
    return mysqli_fetch_assoc($qlink);
}

function mysql_fetch_array($qlink, $result=MYSQLI_BOTH) {
    return mysqli_fetch_array($qlink, $result);
}

function mysql_fetch_lengths($qlink) {
    return mysqli_fetch_lengths($qlink);
}

function mysql_insert_id($qlink) {
    return mysqli_insert_id($qlink);
}

function mysql_num_rows($qlink) {
    return mysqli_num_rows($qlink);
}

function mysql_num_fields($qlink) {
    return mysqli_num_fields($qlink);
}

function mysql_data_seek($qlink, $row) {
    return mysqli_data_seek($qlink, $row);
}

function mysql_field_seek($qlink, $offset) {
    return mysqli_field_seek($qlink, $offset);
}

function mysql_fetch_object($qlink, $class="stdClass", array $params=null) {
    return ($params === null)
        ? mysqli_fetch_object($qlink, $class)
        : mysqli_fetch_object($qlink, $class, $params);
}

function mysql_db_name($qlink, $row, $field='Database') {
    mysqli_data_seek($qlink, $row);
    $db = mysqli_fetch_assoc($qlink);
    return $db[$field];
}

function mysql_fetch_field($qlink, $offset=null) {
    if ($offset !== null)
        mysqli_field_seek($qlink, $offset);
    return mysqli_fetch_field($qlink);
}

function mysql_result($qlink, $offset, $field=0) {
    if ($offset !== null)
        mysqli_field_seek($qlink, $offset);
    $row = mysqli_fetch_array($qlink);
    return (!is_array($row) || !isset($row[$field]))
        ? false
        : $row[$field];
}

function mysql_field_len($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    return is_object($field) ? $field->length : false;
}

function mysql_field_name($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    if (!is_object($field))
        return false;
    return empty($field->orgname) ? $field->name : $field->orgname;
}

function mysql_field_table($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    if (!is_object($field))
        return false;
    return empty($field->orgtable) ? $field->table : $field->orgtable;
}

function mysql_field_type($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    return is_object($field) ? $field->type : false;
}

function mysql_free_result($qlink) {
    try {
        mysqli_free_result($qlink);
    } catch (Exception $e) {
        return false;
    }
    return true;
}
2
Pavel Tzonkov

このmysql_connect()mysql_query()型に似た関数は前のバージョンのPHP(PHP 4)関数で、現在は使用されていません。

最新のPHP5でも、これらはmysqli_connect()mysqli_query()に置き換えられています。

これがエラーの背後にある理由です。

0
Killer

これは今日の古い質問です(2019年1月)が、まだ役に立つかもしれません。約7年前、私はMySQL/MySQLi/PDO機能の表マッピングを作成しました。参考になるかもしれません。それは オンラインここ そして以下に再現されています。 HTMLをコピー&ペーストしてください。

実際問題として、手続き型MySQL関数をOOP MySQLiに変換することが最も抵抗の少ないパスであることがわかりました。 2つのDB接続を同時に開いても問題ありません。変換の過程である程度の柔軟性が得られました。スクリプトを1回に1つのクエリで少しずつ変換することができます。私は今日それをお勧めしないかもしれませんが、それは当時は好都合でした。

<div class="container">

<h2>Mapping Obsolete MySQL Functions to Current PHP Extensions</h2>
<table>
<tr><th>MySQL Extension</th><th>MySQL<b><i>i</i></b></th><th>PDO</th></tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-affected-rows.php">mysql_affected_rows</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.affected-rows.php">mysqli::$affected_rows</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.rowcount.php">PDOStatement::rowCount</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-client-encoding.php">mysql_client_encoding</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.character-set-name.php">mysqli::character_set_name</a></td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-close.php">mysql_close</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.close.php">mysqli::close</a></td>
    <td>Assign NULL to PDO Object</td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-connect.php">mysql_connect</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.construct.php">mysqli::__construct</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.construct.php">PDO::__construct</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-create-db.php">mysql_create_db</a></td>
    <td>Query: CREATE DATABASE</a></td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-data-seek.php">mysql_data_seek</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-stmt.data-seek.php">mysqli_stmt::data_seek</a></td>
    <td>PDO::FETCH_ORI_ABS (?)</td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-db-name.php">mysql_db_name</a></td>
    <td>Query: SELECT DATABASE()</td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-db-query.php">mysql_db_query</a></td>
    <td> </td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-drop-db.php">mysql_drop_db</a></td>
    <td>Query: DROP DATABASE</td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-errno.php">mysql_errno</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.errno.php">mysqli::$errno</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.errorcode.php">PDO::errorCode</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-error.php">mysql_error</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.error-list.php">mysqli::$error_list</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.errorinfo.php">PDO::errorInfo</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-escape-string.php">mysql_escape_string</a></td>
    <td> </td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-fetch-array.php">mysql_fetch_array</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-array.php">mysqli_result::fetch_array</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.fetch.php">PDOStatement::fetch</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-fetch-assoc.php">mysql_fetch_assoc</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-assoc.php">mysqli_result::fetch_assoc</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.fetch.php">PDOStatement::fetch</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-fetch-field.php">mysql_fetch_field</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-field.php">mysqli_result::fetch_field</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.getcolumnmeta.php">PDOStatement::getColumnMeta</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-fetch-lengths.php">mysql_fetch_lengths</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.lengths.php">mysqli_result::$lengths</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.getcolumnmeta.php">PDOStatement::getColumnMeta</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-fetch-object.php">mysql_fetch_object</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-object.php">mysqli_result::fetch_object</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.fetch.php">PDOStatement::fetch</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-fetch-row.php">mysql_fetch_row</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-row.php">mysqli_result::fetch_row</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.fetch.php">PDOStatement::fetch</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-field-flags.php">mysql_field_flags</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-fields.php">mysqli_result::fetch_fields</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.getcolumnmeta.php">PDOStatement::getColumnMeta</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-field-len.php">mysql_field_len</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-field-direct.php">mysqli_result::fetch_field_direct</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.getcolumnmeta.php">PDOStatement::getColumnMeta</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-field-name.php">mysql_field_name</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-field-direct.php">mysqli_result::fetch_field_direct</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.getcolumnmeta.php">PDOStatement::getColumnMeta</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-field-seek.php">mysql_field_seek</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.field-seek.php">mysqli_result::field_seek</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.fetch.php">PDOStatement::fetch</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-field-table.php">mysql_field_table</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-field-direct.php">mysqli_result::fetch_field_direct</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.getcolumnmeta.php">PDOStatement::getColumnMeta</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-field-type.php">mysql_field_type</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-field-direct.php">mysqli_result::fetch_field_direct</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.getcolumnmeta.php">PDOStatement::getColumnMeta</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-free-result.php">mysql_free_result</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.free.php">mysqli_result::free</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.closecursor.php">PDOStatement::closeCursor</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-get-client-info.php">mysql_get_client_info</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.get-client-info.php">mysqli::get_client_info</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.getattribute.php">PDO::getAttribute</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-get-Host-info.php">mysql_get_Host_info</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.get-Host-info.php">mysqli::$Host_info</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.getattribute.php">PDO::getAttribute</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-get-proto-info.php">mysql_get_proto_info</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.get-proto-info.php">mysqli::$protocol_version</a></td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-get-server-info.php">mysql_get_server_info</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.get-server-info.php">mysqli::$server_info</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.getattribute.php">PDO::getAttribute</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-info.php">mysql_info</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.info.php">mysqli::$info</a></td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-insert-id.php">mysql_insert_id</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.insert-id.php">mysqli::$insert_id</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.lastinsertid.php">PDO::lastInsertId</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-list-dbs.php">mysql_list_dbs</a></td>
    <td>Query: SHOW DATABASES</td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-list-fields.php">mysql_list_fields</a></td>
    <td>Query: SHOW COLUMNS</td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-list-processes.php">mysql_list_processes</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.thread-id.php">mysqli::$thread_id</a></td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-list-tables.php">mysql_list_tables</a></td>
    <td>Query: SHOW TABLES</td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-num-fields.php">mysql_num_fields</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.field-count.php">mysqli::$field_count</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.columncount.php">PDOStatement::columnCount</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-num-rows.php">mysql_num_rows</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-stmt.num-rows.php">mysqli_stmt::$num_rows</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.rowcount.php">PDOStatement::rowCount</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-pconnect.php">mysql_pconnect</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.construct.php">mysqli::__construct</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.construct.php">PDO::__construct</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-ping.php">mysql_ping</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.ping.php">mysqli::ping</a></td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-query.php">mysql_query</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.query.php">mysqli::query</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.query.php">PDO::query</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-real-escape-string.php">mysql_real_escape_string</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.real-escape-string.php">mysqli::real_escape_string</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.quote.php">PDO::quote</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-result.php">mysql_result</a></td>
    <td>Combination</td>
    <td><a href="http://www.php.net/manual/en/pdostatement.fetchcolumn.php">PDOStatement::fetchColumn</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-select-db.php">mysql_select_db</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.send-query.php">mysqli::send_query</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.construct.php">PDO::__construct</a></td></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-set-charset.php">mysql_set_charset</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.character-set-name.php">mysqli::character_set_name</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.construct.php">PDO::__construct</a></td></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-stat.php">mysql_stat</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.stat.php">mysqli::stat</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.getattribute.php">PDO::getAttribute</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-tablename.php">mysql_tablename</a></td>
    <td>Query: SHOW TABLES</td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-thread-id.php">mysql_thread_id</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.thread-id.php">mysqli::$thread_id</a></td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-unbuffered-query.php">mysql_unbuffered_query</a></td>
    <td>See <a href="http://www.php.net/manual/en/mysqlinfo.concepts.buffering.php">Buffering Concepts</a></td>
    <td> </td>
    </tr>
</table>

</div><!-- container -->
0
Ray Paseur