回答:
MySQL拡張:
これは非推奨であるため、使用するとコードの将来性が失われます。
準備されたステートメントのサポートの欠如は、外部データをエスケープして引用する方法が、個別の関数呼び出しで手動でエスケープするよりも明確でエラーが発生しにくいため、特に重要です。
SQL拡張機能の比較をご覧ください。
PHPは、MySQLに接続するための3つの異なるAPIを提供します。これらはmysql
(PHP 7で削除されました)、、mysqli
およびPDO
拡張機能です。
mysql_*
以前は非常に人気のあった機能ですが、その使用は推奨されなくなりました。ドキュメントチームはデータベースのセキュリティ状況について議論しており、一般的に使用されるext / mysql拡張から離れるようにユーザーを教育することはこれの一部です(php.internals:ext / mysqlの非推奨を確認してください)。
以降PHPの開発者チームは、生成することを決定とったE_DEPRECATED
ているかどうか、ユーザーは、MySQLに接続する際のエラーをmysql_connect()
、mysql_pconnect()
またはに組み込まれた暗黙的な接続機能ext/mysql
。
ext/mysql
た公式にPHP 5.5の時点で非推奨とされているPHP 7のように除去。
赤い箱を見る?
あなたはどのに行くときmysql_*
の機能のマニュアルページ、あなたはそれはもう使用すべきではない説明、赤いボックスを参照してください。
離れることext/mysql
は、セキュリティだけでなく、MySQLデータベースのすべての機能にアクセスできることも重要です。
ext/mysql
MySQL 3.23用に構築されましたおり、追加されたものはごくわずかで、コードの保守が少し難しくなるこの古いバージョンとの互換性はほとんど維持されています。ext/mysql
includeでサポートされていない不足している機能:(PHPマニュアルから)。
使用しない理由 mysql_*
関数を:
準備されたステートメントのサポートの欠如は、外部データをエスケープして引用する方法が、個別の関数呼び出しで手動でエスケープするよりも明確でエラーが発生しにくいため、特に重要です。
非推奨の警告の抑制
コードがMySQLi
/ に変換されている間、php.iniで除外するように設定することPDO
でE_DEPRECATED
エラーを抑制できますerror_reporting
E_DEPRECATED:
error_reporting = E_ALL ^ E_DEPRECATED
これにより、他の非推奨の警告も非表示になることに注意してください。ただし、MySQL以外のものである可能性があります。(PHPマニュアルから)
記事PDOとMySQLi:どちらを使用すればよいですか?Dejan Marjanovicによってあなたが選択するのに役立ちます。
そしてより良い方法はPDO
であり、私は今簡単なPDO
チュートリアルを書いています。
A.「PDO – PHPデータオブジェクト –は、複数のデータベースへの統一的なアクセス方法を提供するデータベースアクセスレイヤーです。」
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
場合username
、(データソース名)とオプションでの4つのパラメーターを取りますpassword
。
ここで私はあなたが以外のすべてに精通していると思いますDSN
。これはの新機能ですPDO
。A 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の構築後に属性を設定できますか?
はい、setAttribute
メソッドを使用してPDOを構築した後にいくつかの属性を設定することもできます:
$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));
OR die()
で処理できないため、エラーを処理するのに適した方法ではありません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の例外の詳細については、例外を参照してください。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
エラーログを回して読むだけで、本番環境の危険なエラーメッセージを非表示にできます。
さて、上記のすべてのものを読んだ後、あなたはおそらく考えている:一体何であるかという、私は単純な傾い開始したい時にSELECT
、INSERT
、UPDATE
、またはDELETE
ステートメントを?心配しないで、ここに行きます:
だからあなたがやっていること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()
は、ステートメントハンドルのメソッドであるを介して取得されます。fetchを呼び出す前に、最善の方法は、データをどのようにフェッチするかをPDOに指示することです。以下のセクションでは、これについて説明します。
上記PDO::FETCH_ASSOC
のfetch()
およびfetchAll()
コードでの使用に注意してください。これはPDO
、フィールド名をキーとする連想配列として行を返すように指示します。他にも多くのフェッチモードがありますが、これらについては1つずつ説明します。
まず、フェッチモードの選択方法を説明します。
$stmt->fetch(PDO::FETCH_ASSOC)
上記では、を使用していfetch()
ます。次のものも使用できます。
PDOStatement::fetchAll()
-すべての結果セット行を含む配列を返しますPDOStatement::fetchColumn()
-結果セットの次の行から単一の列を返しますPDOStatement::fetchObject()
-次の行をフェッチしてオブジェクトとして返します。PDOStatement::setFetchMode()
-このステートメントのデフォルトのフェッチモードを設定します今私はフェッチモードに入ります:
PDO::FETCH_ASSOC
:結果セットで返された列名でインデックス付けされた配列を返しますPDO::FETCH_BOTH
(デフォルト):結果セットで返されるように、列名と0でインデックス付けされた列番号の両方でインデックス付けされた配列を返しますさらに選択肢があります!PDOStatement
Fetchのドキュメントでそれらすべてについて読んでください。。
行数の取得:
を使用mysql_num_rows
して、返された行数を取得する代わりに、次のようにa PDOStatement
およびdoを取得できます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();
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ステートメントを実行し、影響を受けた行の数を返します。
挿入と削除については後で説明します。
上記の方法は、クエリで変数を使用していない場合にのみ役立ちます。ただし、クエリで変数を使用する必要がある場合は、これまでに説明したようなことを行わないでください。準備されたステートメントまたはパラメーター化されたステートメントが存在し ます。
Q.準備済みステートメントとは何ですか。なぜ必要なのですか。
A.準備されたステートメントは、データのみをサーバーに送信することで複数回実行できるプリコンパイルされたSQLステートメントです。
準備済みステートメントを使用する一般的なワークフローは次のとおりです(Wikipediaの3つの3ポイントから引用)。
準備:ステートメントテンプレートはアプリケーションによって作成され、データベース管理システム(DBMS)に送信されます。特定の値は、パラメーター、プレースホルダー、またはバインド変数(?
以下にラベルが付けられています)と呼ばれる、未指定のままになります。
INSERT INTO PRODUCT (name, price) VALUES (?, ?)
DBMSは、ステートメントテンプレートでクエリの最適化を解析、コンパイル、および実行し、実行せずに結果を保存します。
1.00
2番目のパラメーターに「パン」を提供する場合があります。SQLにプレースホルダーを含めることにより、準備済みステートメントを使用できます。基本的に、プレースホルダーのないもの(変数の上にある変数を使用しないでください)、名前のないプレースホルダーがあるもの、名前のあるプレースホルダーがあるものの3つがあります。
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番目の配列要素に移動し?
ます。
注:名前のないプレースホルダーでは、PDOStatement::execute()
メソッドに渡す配列内の要素の適切な順序に注意する必要があります。
SELECT
、INSERT
、UPDATE
、DELETE
クエリを準備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);
INSERT
:
$stmt = $db->prepare("INSERT INTO table(field1,field2) VALUES(:field1,:field2)");
$stmt->execute(array(':field1' => $field1, ':field2' => $field2));
$affected_rows = $stmt->rowCount();
DELETE
:
$stmt = $db->prepare("DELETE FROM table WHERE id=:id");
$stmt->bindValue(':id', $id, PDO::PARAM_STR);
$stmt->execute();
$affected_rows = $stmt->rowCount();
UPDATE
:
$stmt = $db->prepare("UPDATE table SET name=? WHERE id=?");
$stmt->execute(array($name, $id));
$affected_rows = $stmt->rowCount();
ただしPDO
、MySQLi
完全に安全ではありません。回答を確認してくださいPDOの準備済みステートメントはSQLインジェクションを防ぐのに十分ですか?によって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 /*"));
IN (...) construct
。
function throwEx() { throw new Exception("You did selected not existng db"); } mysql_select_db("nonexistdb") or throwEx();
をスローするために機能します。
Doesn't support non-blocking, asynchronous queries
mysql_を使用しない理由としてリストする-PDOもサポートしていないため、PDOを使用しない理由としてもリストする必要があります。(ただし、MySQLiがサポート)
まず、全員に与える標準的なコメントから始めましょう。
mysql_*
新しいコードでは関数を使用しないでください。それらはもはや保守されておらず、正式に非推奨になっています。赤い箱が見えますか?学ぶプリペアドステートメントの代わりに、および使用 PDOまたは MySQLiをする -この記事は、あなたがどれを決めるのに役立ちます。PDOを選択した場合は、こちらが良いチュートリアルです。
文章ごとに説明し、説明しましょう。
それらはもはや保守されておらず、正式に非推奨になっています
これは、PHPコミュニティがこれらの非常に古い関数のサポートを段階的に廃止していることを意味します。PHPの将来の(最近の)バージョンには存在しない可能性があります!これらの関数を継続して使用すると、(そうではない)遠い将来にコードが壊れる可能性があります。
新着!-ext / mysqlがPHP 5.5で正式に非推奨になりました!
代わりに、準備されたステートメントについて学ぶ必要があります
mysql_*
拡張機能は、準備されたステートメントをサポートしていません。これは、(とりわけ)SQLインジェクションに対する非常に効果的な対策です。MySQLに依存するアプリケーションの非常に深刻な脆弱性を修正しました。これにより、攻撃者がスクリプトにアクセスし、データベースで可能なクエリを実行する可能性があります。
詳細については、「PHPでSQLインジェクションを防ぐにはどうすればよいですか?」を参照してください。
赤い箱を見る?
あなたがいずれかに行くときmysql
の機能のマニュアルページ、あなたはそれはもう使用すべきではない説明、赤いボックスを参照してください。
PDOまたはMySQLiを使用する
より優れた、より堅牢でよく構築された代替手段、PDO-PHPデータベースオブジェクト(データベースの相互作用に対する完全なOOPアプローチを提供)、およびMySQLi(MySQL固有の改善)があります。
IN (...) construct
。
分析的および合成的な理由はすでに述べられています。初心者にとっては、日付が付けられたmysql_関数の使用をやめるより大きなインセンティブがあります。
現代のデータベースAPIは使いやすいだけです。
コードを簡略化できるのは、主にバインドされたパラメーターです。そして、で優れたチュートリアル(上で見たように)への移行PDOは過度に困難ではありません。
ただし、大きなコードベースを一度に書き換えるには時間がかかります。この中間的な代替案の存在理由:
< pdo_mysql.php >を使用すると、最小限の労力で古いmysql_関数から切り替えることができます。対応するpdo_
関数ラッパーを追加しますmysql_
。
単に、データベースと対話する必要のある各呼び出しスクリプトで。
include_once(
"pdo_mysql.php"
);
あらゆる場所で関数の接頭辞を削除し、それをに置き換えます。mysql_
pdo_
mysql_
connect()
なる pdo_
connect()
mysql_
query()
なる pdo_
query()
mysql_
num_rows()
なる pdo_
num_rows()
mysql_
insert_id()
なる pdo_
insert_id()
mysql_
fetch_array()
なる pdo_
fetch_array()
mysql_
fetch_assoc()
なる pdo_
fetch_assoc()
mysql_
real_escape_string()
なる pdo_
real_escape_string()
コードは同じように動作しますが、ほとんど同じように見えます。
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_ *でも、またはのいずれかを使用できることに注意してください。
変数をエスケープせず、同じクエリにバインドしないでください。
:named
後でプレースホルダリストも使用できます。さらに重要なこととして、$ _ REQUEST []変数をクエリの背後で安全に渡すことができます。送信された<form>
フィールドがデータベース構造と完全に一致する場合、さらに短くなります。
pdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);
とてもシンプルです。しかし、なぜあなたが脱出して脱出したいのかについて、いくつかの書き直しのアドバイスと技術的な理由に戻りましょう。mysql_
sanitize()
機能を修正または削除するバインドされたパラメータを使用mysql_
pdo_query
してすべての呼び出しをに変換したら、すべての冗長なpdo_real_escape_string
呼び出しを削除します。
特に、日付付きのチュートリアルで宣伝されているsanitize
or clean
またはfilterThis
or clean_data
機能をいずれかの形式で修正する必要があります。
function sanitize($str) {
return trim(strip_tags(htmlentities(pdo_real_escape_string($str))));
}
ここで最も目立つバグは、ドキュメントの欠如です。さらに重要なことに、フィルタリングの順序は正確に間違った順序でした。
正しい順序は次のとおりです。非推奨stripslashes
に、最も内側の呼び出しとしてtrim
、その後strip_tags
、htmlentities
出力コンテキストに対して、そして最後に、_escape_string
アプリケーションがSQLインターパースの直前にあるべきです。
しかし、最初のステップとして、_real_escape_string
呼び出しを取り除くだけです。
sanitize()
データベースとアプリケーションフローがHTMLコンテキストセーフな文字列を想定している場合は、今のところ残りの関数を保持する必要があるかもしれません。今後はHTMLエスケープのみが適用されるというコメントを追加します。
文字列/値の処理は、PDOとそのパラメーター化されたステートメントに委任されます。
stripslashes()
サニタイズ機能で言及があった場合は、より高いレベルの監視を示している可能性があります。
これは一般的に、廃止されmagic_quotes
たからのダメージ(ダブルエスケープ)を元に戻すためにありました。ただし、文字列ごとではなく、中央で修正するのが最適です。
ユーザーランドの反転アプローチのいずれかを使用します。次にstripslashes()
、sanitize
関数内のを削除します。
magic_quotesに関する歴史的なメモ。その機能は当然非推奨です。ただし、セキュリティ機能が失敗したと誤って表現されることがよくあります。しかし、magic_quotesは、栄養源として失敗したテニスボールと同じくらい、失敗したセキュリティ機能です。それは単に彼らの目的ではありませんでした。
PHP2 / FIの元の実装では、「引用符が自動的にエスケープされ、フォームデータをmsqlクエリに直接渡すのが簡単になる」というだけで明示的に導入されました。特に、mSQLはASCIIのみをサポートしていたため、誤って使用しても安全でした。
次に、PHP3 / ZendはMySQLのmagic_quotesを再導入し、それを誤って文書化しました。しかし、もともとは便利な機能であり、セキュリティを目的としたものではありませんでした。
文字列変数をSQLクエリにスクランブルすると、従うのが複雑になるだけではありません。また、MySQLがコードとデータを再度分離することは、余分な労力です。
SQLインジェクションは、単にデータがコードコンテキストに流れ込んだときです。データベースサーバーは、PHPが最初にクエリ句の間に変数を接着した場所を後で見つけることはできません。
バインドされたパラメーターを使用して、SQLコードとSQLコンテキスト値をPHPコードで分離します。しかし、それは舞台裏で再びシャッフルされません(PDO :: EMULATE_PREPARESを除く)。データベースは、変化しないSQLコマンドと1:1の変数値を受け取ります。
この回答は、ドロップの読みやすさの利点に注意する必要があることを強調しています。この可視的で技術的なデータ/コードの分離により、パフォーマンス上の利点(値が異なるだけのINSERTの繰り返し)も時々あります。mysql_
パラメータのバインドは、すべての SQLインジェクションに対する魔法のワンストップソリューションではないことに注意してください。データ/値の最も一般的な用途を処理します。ただし、列名/テーブル識別子をホワイトリストに登録したり、動的な句の作成に役立てたり、単純な配列値リストを作成したりすることはできません。
これらのpdo_*
ラッパー関数は、コーディングに適したストップギャップ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_
に切り替えるだけ pdo完全にそれをカットしていません。pdo_query()
それは単なるフロントエンドでもあります。
パラメータバインディングを導入したり、より優れたAPIから何かを利用したりできる場合を除いて、それは無意味なスイッチです。初心者を落胆させないように、シンプルに描かれているといいのですが。(教育は通常禁止よりもうまく機能します。)
それは、最も単純なものである可能性のある作業カテゴリの対象ですが、それでも非常に実験的なコードです。週末に書いただけです。ただし、選択肢はたくさんあります。PHPデータベースの抽象化のためにグーグルし、少し閲覧します。そのようなタスクのための優れたライブラリーはたくさんあります。
データベースの相互作用をさらに単純化したい場合は、Paris / Idiormのようなマッパーが試してみる価値があります。JavaScriptで平凡なDOMを使用する人がいないのと同じように、現在、生のデータベースインターフェースをベビーシットする必要はありません。
pdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);
機能に注意してくださいpdo_query("INSERT INTO users VALUES (?, ?, ?), $_POST); $_POST = array( 'username' => 'lawl', 'password' => '123', 'is_admin' => 'true');
pdo_real_escape_string()
<-これは実際の機能でもありますが、ドキュメントは見つかりませんか?このソースを投稿してください。
mysql_
機能:
mysqli_
mysql_*
関数は、新しいPHPバージョンのmysqlnd関数へのシェルです。したがって、古いクライアントライブラリが維持されなくなっても、mysqlndは維持されます:)
技術的な理由といえば、ごく少数であり、非常に具体的でほとんど使用されていません。ほとんどの場合、あなたは自分の人生でそれらを決して使用しないでしょう。
たぶん私は無知かもしれませんが、私はそれらのようなものを使用する機会がありませんでした
あなたがそれらを必要とする場合-これらは間違いなくmysql拡張からよりスタイリッシュでモダンな外観に移行する技術的な理由です。
それにもかかわらず、いくつかの非技術的な問題もあり、経験を少し難しくする可能性があります
この後者の問題は問題です。
しかし、私の意見では、提案されたソリューションも優れています。これらのすべてのPHPユーザーがSQLクエリを一度に適切に処理する方法を学ぶ
ことは、あまりにも理想的な夢のようです。ほとんどの場合、彼らはmysql_ *をmysqli_ *に機械的に変更するだけで、アプローチは同じままです。特に、mysqliは準備されたステートメントの使用を信じられないほど苦痛で面倒にするためです。
言うまでもなく、ネイティブの準備されたステートメントは SQLインジェクションから保護するのに十分ではなく、mysqliもPDOもソリューションを提供しません。
したがって、この正直な拡張と戦うのではなく、間違った慣習と戦い、人々を正しい方法で教育したいと思います。
また、次のようないくつかの誤ったまたは重要ではない理由があります
mysql_query("CALL my_proc");
古くから使用していました)最後は面白い点です。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の選択の問題が発生します。
さて、答えは次のようになります:
ほとんどのPHP関係者のように、アプリケーションコードで直接API呼び出しを使用している場合(これは基本的に間違った方法です)- この拡張機能はAPIだけでなくセミDALのふりをしているため、PDOが唯一の選択肢です。まだ不完全ですが、多くの重要な機能を提供します。そのうちの2つは、PDOをmysqliと明確に区別します。
したがって、平均的なPHPユーザーであり、ネイティブの準備済みステートメントを使用するときに頭痛の種を節約したい場合は、PDOが唯一の選択肢です。
ただし、PDOも特効薬ではなく、困難を伴います。
だから、私はすべての一般的な落とし穴と複雑なケースの解決策をPDOタグwikiに書いた
それにもかかわらず、拡張機能について話している誰もが常にMysqliとPDOに関する2つの重要な事実を見逃しています。
準備された声明は特効薬ではありません。準備されたステートメントを使用してバインドできない動的識別子があります。不明な数のパラメーターを持つ動的クエリがあり、クエリの作成が困難なタスクになります。
mysqli_ *もPDO関数もアプリケーションコードに表示されるべきではありませんでした。それらとアプリケーションコードの間に抽象化レイヤー
が存在する必要があります。これにより、バインディング、ループ、エラー処理などのすべてのダーティジョブが内部で実行され、アプリケーションコードがDRYでクリーンになります。特に、動的クエリの作成などの複雑なケースでは。
したがって、PDOまたはmysqliに切り替えるだけでは不十分です。コードで生の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);
これを通常のPDO挿入と比較します。すべての単一のフィールド名が6〜10回繰り返される場合-これらの多数の名前付きプレースホルダー、バインディング、およびクエリ定義すべてで。
もう一つの例:
$data = $db->getAll("SELECT * FROM goods ORDER BY ?n", $_GET['order']);
このような実用的なケースを処理するPDOの例はほとんど見つかりません。
そして、それは言い過ぎで、おそらく安全ではありません。
だから、もう一度-それは単なる生のドライバである必要があるだけでなく、抽象クラスでもあり、初心者用マニュアルの愚かな例だけでなく、実際の問題を解決するのにも役立ちます。
mysql_*
脆弱性が発生しやすくなります。PHPは多くの初心者ユーザーによって使用されるためmysql_*
、理論上は問題なく使用できるとしても、実際には有害です。
everything is parameterized and safe
-パラメータ化されている可能性がありますが、関数は実際の準備済みステートメントを使用していません。
Not under active development
その構成された「0.01%」だけはどうですか?この静止関数を使用して何かを構築し、1年以内にmysql-versionを更新し、稼働していないシステムを使用すると、その「0.01%」に突然多くの人がいるはずです。それと密接に関連しているdeprecated
と思いますnot under active development
。それには「[価値のある]理由がない」と言えますが、オプションの選択肢を提示するとno active development
、deprecated
私が言うのとほとんど同じくらい悪いのです。
理由はたくさんありますが、おそらく最も重要な理由は、これらの関数は準備されたステートメントをサポートしていないため、安全でないプログラミング手法を奨励することです。準備済みステートメントは、SQLインジェクション攻撃の防止に役立ちます。
mysql_*
関数を使用するときは、ユーザーが指定したパラメーターをで実行することを忘れないでくださいmysql_real_escape_string()
。1か所だけで忘れたり、入力の一部だけをエスケープしたりすると、データベースが攻撃を受ける可能性があります。
PDO
またはで準備されたステートメントを使用mysqli
すると、この種のプログラミングエラーが発生しにくくなります。
(他の理由の中で)入力データが確実にサニタイズされるようにするのははるかに難しいためです。PDOやmysqliのようにパラメーター化されたクエリを使用すると、リスクを完全に回避できます。
例として、誰かが"enhzflep); drop table users"
ユーザー名として使用できます。古い関数では、クエリごとに複数のステートメントを実行できるため、その厄介なバグのようなものがテーブル全体を削除する可能性があります。
mysqliのPDOを使用する場合、ユーザー名はになり"enhzflep); drop table users"
ます。
bobby-tables.comを参照してください。
The old functions will allow executing of multiple statements per query
-いいえ、彼らはしません。そのようなインジェクションはext / mysqlでは不可能です。この種のインジェクションがPHPとMySQLで可能になる唯一の方法は、MySQLiとmysqli_multi_query()
関数を使用する場合です。ext / mysqlおよびエスケープされていない文字列で可能な種類の注入は' OR '1' = '1
、データベースからアクセスできるように意図されていないデータを抽出するようなものです。特定の状況では、サブクエリを挿入することが可能ですが、この方法でデータベースを変更することはまだ不可能です。
この回答は、不十分に記述された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コードを実行してください。
だから、答えは、それがあなたが怒られている理由です。
では、何が問題だったか、なぜ私があなたのsuper-admin-only-bat-caveに入ったのかを見てみましょう。私は推測して、あなたはあなたの入力に注意していないと思い、単純にそれらを直接データベースに渡しました。実際に実行していたクエリを変更する方法で入力を作成しました。それで、それは何であるはずでしたか、そしてそれは結局何でしたか?
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オブジェクトを介して行われます。次に、準備されたステートメントを使用します。さて、あなたが尋ねる準備された声明は何ですか?これは、クエリを実行する前に、実行するクエリをデータベースに通知する方法です。この場合、データベースに「こんにちは、ID、ユーザーID、およびユーザーIDが変数でパスが変数でもあるテーブルユーザーからのパスを要求するSELECTステートメントを実行します。」と伝えます。
次に、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...
これが、あなたが質問を投稿するときに怒鳴られる理由です。それは、あなたのコードが試みられてもバイパスされる可能性があることが人々にわかるためです。この質問と回答を使用してコードを改善し、コードをより安全にし、最新の機能を使用してください。
最後に、これはこれが完璧なコードであると言っているのではありません。それを改善するためにできることはもっとたくさんあります。たとえば、ハッシュ化されたパスワードを使用します。データベースに機密情報を保存するときは、プレーンテキストでは保存せず、複数のレベルの検証を行ってください。古いインジェクションが発生しやすいコードをこれに変更するだけで、優れたコードを作成する過程で順調に進みます。これまでに達成し、現在も読んでいるという事実は、このタイプを実装するだけではないという希望を感じさせます。あなたのウェブサイトやアプリケーションを書くときのコードの数、しかしあなたが外に出て先ほど述べた他のことを研究するかもしれません-そしてもっと。かろうじて機能する最も基本的なコードではなく、可能な限り最高のコードを記述してください。
mysql_*
自体は安全ではないことは注目に値しますが、不適切なチュートリアルや適切なステートメント準備APIの欠如により、安全でないコードを促進します。
MySQL拡張機能は、3つのうち最も古いものであり、開発者がMySQLと通信するために使用した最初の方法でした。PHPとMySQLの両方の新しいリリースで行われた改善のため、この拡張機能は現在、他の2つの代替案のために廃止されています。
MySQLiは、MySQLデータベースを操作するための「改善された」拡張機能です。これは、MySQLサーバーの新しいバージョンで利用可能な機能を利用し、関数指向とオブジェクト指向の両方のインターフェイスを開発者に公開し、その他いくつかの気の利いたことを行います。
PDOは、以前は主要なデータベースアクセス拡張機能(MySQL、PostgreSQL、SQLite、MSSQLなど)に分散していた機能のほとんどを統合するAPIを提供します。インターフェイスは、プログラマーがデータベース接続、クエリ、および結果セット、および低レベルのドライバーは、データベースサーバーとの通信およびリソース処理を実行します。PDOについては多くの議論と作業が行われており、最新のプロフェッショナルなコードでデータベースを操作する適切な方法と見なされています。
上記の回答は非常に長いため、要約すると次のようになります。
mysqli拡張には多くの利点があり、mysql拡張に対する主な機能強化は次のとおりです。
- オブジェクト指向のインターフェース
- 準備済みステートメントのサポート
- 複数のステートメントのサポート
- トランザクションのサポート
- 強化されたデバッグ機能
- 組み込みサーバーのサポート
出典:MySQLiの概要
上記の回答で説明したように、mysqlの代替はmysqliおよびPDO(PHP Data Objects)です。
MySQLiとPDOはどちらもPHP 5.0で導入されましたが、MySQLはPHP 3.0より前に導入されました。注意すべき点は、MySQLはPHP5.xに含まれていますが、それ以降のバージョンでは非推奨です。
mysql_*
mysqliまたはPDOを使用して、ほぼすべての関数を定義できます。古いPHPアプリケーションの上にそれらを含めるだけで、PHP7で動作します。私の解決策はこちら。
<?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;
}
より優れた関数とコード構造が開発されたという事実を踏まえて、mysql_ *関数は廃止されました(PHP 5.5以降)。この機能が廃止されたという事実は、パフォーマンスとセキュリティの観点から機能の改善にこれ以上の努力が払われなくなることを意味します。つまり、将来の保証は少なくなります。
さらに理由が必要な場合: