Was sind die technischen Gründe, warum man mysql_*
-Funktionen nicht verwenden sollte? (z. B. mysql_query()
, mysql_connect()
oder mysql_real_escape_string()
)?
Warum sollte ich etwas anderes verwenden, auch wenn sie auf meiner Website funktionieren?
Wenn sie auf meiner Website nicht funktionieren, werden Fehler angezeigt
Warnung: mysql_connect (): Keine Datei oder Verzeichnis
Die MySQL-Erweiterung:
Da es veraltet ist, macht es den Code weniger zukunftssicher.
Die mangelnde Unterstützung für vorbereitete Anweisungen ist besonders wichtig, da sie eine klarere, weniger fehleranfällige Methode für das Escape-Verfahren und für die Angabe externer Daten bieten als das manuelle Escape-Verfahren mit einem separaten Funktionsaufruf.
PHP bietet drei verschiedene APIs, um eine Verbindung zu MySQL herzustellen. Dies sind die Erweiterungen mysql
(entfernt ab PHP 7), mysqli
und PDO
.
Die mysql_*
-Funktionen waren früher sehr beliebt, aber ihre Verwendung wird nicht mehr empfohlen. Das Dokumentationsteam erörtert die Sicherheitslage der Datenbank, und es gehört dazu, die Benutzer zu erziehen, sich von der häufig verwendeten ext/mysql-Erweiterung zu entfernen (check php.internals: deprecating ext/mysql).
Und das spätere PHP Entwicklerteam hat beschlossen, E_DEPRECATED
Fehler zu generieren, wenn Benutzer eine Verbindung zu MySQL herstellen, sei es über mysql_connect()
, mysql_pconnect()
oder die in ext/mysql
integrierte implizite Verbindungsfunktionalität.
ext/mysql
war offiziell veraltet ab PHP 5.5 und wurde entfernt ab PHP 7 .
Sehen Sie die Red Box?
Wenn Sie eine mysql_*
-Funktionshandbuchseite aufrufen, sehen Sie ein rotes Kästchen, das erklärt, dass es nicht mehr verwendet werden sollte.
Beim Umzug von ext/mysql
geht es nicht nur um Sicherheit, sondern auch um den Zugriff auf alle Funktionen der MySQL-Datenbank.
ext/mysql
wurde für MySQL 3.23 entwickelt und hat seitdem nur sehr wenige Ergänzungen erhalten, wobei die Kompatibilität mit dieser alten Version größtenteils erhalten bleibt, was die Pflege des Codes etwas erschwert. Zu den fehlenden Funktionen, die von ext/mysql
nicht unterstützt werden, gehören: (aus dem PHP Handbuch).
Grund, mysql_*
Funktion nicht zu verwenden :
Über Punkt zitiert aus Quentins Antwort
Die mangelnde Unterstützung für vorbereitete Anweisungen ist besonders wichtig, da sie eine klarere und weniger fehleranfällige Methode zum Entschlüsseln und Zitieren externer Daten bieten als das manuelle Entschlüsseln mit einem separaten Funktionsaufruf.
Siehe Vergleich von SQL-Erweiterungen .
Unterdrücken von Verwerfungswarnungen
Während der Konvertierung von Code in MySQLi
/PDO
können E_DEPRECATED
-Fehler unterdrückt werden, indem error_reporting
in php.ini festgelegt wird, um E_DEPRECATED:
auszuschließen.
error_reporting = E_ALL ^ E_DEPRECATED
Beachten Sie, dass dies auch andere Verfallswarnungen verbirgt, die jedoch für andere Dinge als MySQL gelten können. (aus PHP Handbuch)
Der Artikel PDO vs. MySQLi: Welche sollten Sie verwenden? von Dejan Marjanovic hilft Ihnen bei der Auswahl .
Und ein besserer Weg ist PDO
, und ich schreibe jetzt ein einfaches PDO
-Tutorial.
A. " PDO - PHP Data Objects - ist eine Datenbankzugriffsebene, die eine einheitliche Methode für den Zugriff auf mehrere Datenbanken bietet."
Mit der Funktion mysql_*
oder auf die alte Weise (veraltet in PHP 5.5 und höher)
$link = mysql_connect('localhost', 'user', 'pass');
mysql_select_db('testdb', $link);
mysql_set_charset('UTF-8', $link);
Mit PDO
: Sie müssen lediglich ein neues PDO
-Objekt erstellen. Der Konstruktor akzeptiert Parameter für die Angabe der Datenbankquelle. Der Konstruktor von PDO
verwendet meistens vier Parameter: DSN
(Datenquellenname) und optional username
, password
.
Hier denke ich, dass Sie mit allen außer DSN
vertraut sind; Dies ist neu in PDO
. Ein DSN
ist im Grunde eine Folge von Optionen, die PDO
mitteilen, welcher Treiber und welche Verbindungsdetails verwendet werden sollen. Weitere Informationen finden Sie unter PDO MySQL DSN .
$db = new PDO('mysql:Host=localhost;dbname=testdb;charset=utf8', 'username', 'password');
Hinweis: Sie können auch charset=UTF-8
verwenden, aber manchmal verursacht dies einen Fehler. Daher ist es besser, utf8
zu verwenden.
Wenn ein Verbindungsfehler auftritt, wird ein PDOException
-Objekt ausgelöst, das abgefangen werden kann, um Exception
weiterzuverarbeiten.
Good read : Verbindungen und Verbindungsmanagement ¶
Sie können auch mehrere Treiberoptionen als Array an den vierten Parameter übergeben. Ich empfehle, den Parameter zu übergeben, der PDO
in den Ausnahmemodus versetzt. Da einige PDO
-Treiber keine nativen vorbereiteten Anweisungen unterstützen, führt PDO
eine Emulation der Vorbereitung durch. Sie können diese Emulation auch manuell aktivieren. Um die systemeigenen, serverseitig vorbereiteten Anweisungen zu verwenden, müssen Sie sie explizit auf false
setzen.
Die andere Möglichkeit besteht darin, die Vorbereitungsemulation zu deaktivieren, die im Treiber MySQL
standardmäßig aktiviert ist. Die Vorbereitungsemulation sollte jedoch deaktiviert sein, um PDO
sicher zu verwenden.
Ich werde später erklären, warum die Prepare-Emulation deaktiviert werden sollte. Um einen Grund zu finden, überprüfen Sie bitte diesen Beitrag .
Es ist nur verwendbar, wenn Sie eine alte Version von MySQL
verwenden, die ich nicht empfehle.
Nachfolgend finden Sie ein Beispiel, wie Sie dies tun können:
$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));
Können wir Attribute nach der PDO-Konstruktion setzen?
Ja , wir können auch einige Attribute nach der PDO-Konstruktion mit der Methode setAttribute
setzen:
$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);
Die Fehlerbehandlung ist in PDO
viel einfacher als in mysql_*
.
Eine übliche Vorgehensweise bei der Verwendung von mysql_*
ist:
//Connected to MySQL
$result = mysql_query("SELECT * FROM table", $link) or die(mysql_error($link));
OR die()
ist kein guter Weg, um den Fehler zu behandeln, da wir das Ding in die
nicht behandeln können. Das Skript wird nur abrupt beendet, und der Fehler wird auf dem Bildschirm angezeigt, den Sie normalerweise NICHT Ihren Endbenutzern zeigen möchten. So können verdammte Hacker Ihr Schema entdecken. Alternativ können die Rückgabewerte von mysql_*
-Funktionen häufig in Verbindung mit mysql_error () verwendet werden, um Fehler zu behandeln.
PDO
bietet eine bessere Lösung: Ausnahmen. Alles, was wir mit PDO
machen, sollte in einen try
-catch
-Block eingeschlossen werden. Wir können PDO
durch Setzen des Fehlermodus-Attributs in einen von drei Fehlermodi zwingen. Es folgen drei Fehlerbehandlungsmodi.
PDO::ERRMODE_SILENT
. Es setzt nur Fehlercodes und verhält sich so ziemlich wie mysql_*
, wobei Sie jedes Ergebnis überprüfen und dann $db->errorInfo();
anschauen müssen, um die Fehlerdetails zu erhalten.PDO::ERRMODE_WARNING
Erhöhen Sie E_WARNING
. (Laufzeitwarnungen (nicht schwerwiegende Fehler). Die Ausführung des Skripts wird nicht angehalten.)PDO::ERRMODE_EXCEPTION
: Ausnahmen auslösen. Es handelt sich um einen von PDO ausgelösten Fehler. Sie sollten kein PDOException
aus Ihrem eigenen Code werfen. Weitere Informationen zu Ausnahmen in PHP finden Sie unter Ausnahmen . Es verhält sich sehr ähnlich wie or die(mysql_error());
, wenn es nicht abgefangen wird. Im Gegensatz zu or die()
kann PDOException
jedoch auf angemessene Weise abgefangen und gehandhabt werden, wenn Sie sich dazu entschließen.Gut gelesen :
Mögen:
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
Und Sie können es wie folgt in try
-catch
einwickeln:
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());
}
Sie müssen jetzt nicht mit try
-catch
umgehen. Sie können es jederzeit abfangen, aber ich empfehle Ihnen dringend, try
-catch
zu verwenden. Es kann auch sinnvoller sein, es außerhalb der Funktion abzufangen, die das Zeug PDO
aufruft:
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.
}
Sie können auch mit or die()
umgehen, oder wir können sagen wie mysql_*
, aber es wird wirklich abwechslungsreich sein. Sie können die gefährlichen Fehlermeldungen in der Produktion ausblenden, indem Sie display_errors off
drehen und einfach Ihr Fehlerprotokoll lesen.
Nun, nachdem Sie all die Dinge oben gelesen haben, denken Sie wahrscheinlich: Was zum Teufel ist das, wenn ich nur anfangen möchte, einfache SELECT
-, INSERT
-, UPDATE
- oder DELETE
-Aussagen zu machen? Mach dir keine Sorgen, hier gehen wir:
Also, was Sie in mysql_*
tun, ist:
<?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'];
}
Jetzt in PDO
können Sie dies wie folgt tun:
<?php
$stmt = $db->query('SELECT * FROM table');
while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo $row['field1'];
}
Oder
<?php
$stmt = $db->query('SELECT * FROM table');
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
//Use $results
Hinweis : Wenn Sie die folgende Methode verwenden (query()
), gibt diese Methode ein PDOStatement
-Objekt zurück. Wenn Sie also das Ergebnis abrufen möchten, verwenden Sie es wie oben.
<?php
foreach($db->query('SELECT * FROM table') as $row) {
echo $row['field1'];
}
In PDO-Daten wird es über die Funktion ->fetch()
abgerufen, eine Methode Ihres Anweisungshandles. Bevor Sie fetch aufrufen, sollten Sie PDO mitteilen, wie Sie die Daten abrufen möchten. Im folgenden Abschnitt erkläre ich dies.
Beachten Sie die Verwendung von PDO::FETCH_ASSOC
im obigen Code für fetch()
und fetchAll()
. Dies weist PDO
an, die Zeilen als assoziatives Array mit den Feldnamen als Schlüssel zurückzugeben. Es gibt auch viele andere Abrufmodi, die ich einzeln erläutern werde.
Zunächst erkläre ich, wie der Abrufmodus ausgewählt wird:
$stmt->fetch(PDO::FETCH_ASSOC)
Oben habe ich fetch()
verwendet. Sie können auch verwenden:
PDOStatement::fetchAll()
- Gibt ein Array zurück, das alle Ergebnismengenzeilen enthältPDOStatement::fetchColumn()
- Gibt eine einzelne Spalte aus der nächsten Zeile einer Ergebnismenge zurückPDOStatement::fetchObject()
- Holt die nächste Zeile und gibt sie als Objekt zurück.PDOStatement::setFetchMode()
- Legt den Standardabrufmodus für diese Anweisung festJetzt komme ich zum Abrufmodus:
PDO::FETCH_ASSOC
: Gibt ein Array zurück, das nach Spaltennamen indiziert ist und in Ihrer Ergebnismenge zurückgegeben wirdPDO::FETCH_BOTH
(Standard): Gibt ein Array zurück, das sowohl nach dem Spaltennamen als auch nach der 0-indizierten Spaltennummer indiziert ist, wie in Ihrer Ergebnismenge angegebenEs gibt noch mehr Möglichkeiten! Lesen Sie mehr darüber in PDOStatement
Fetch documentation. .
Erhalte die Zeilenzahl :
Anstatt mysql_num_rows
zu verwenden, um die Anzahl der zurückgegebenen Zeilen abzurufen, können Sie PDOStatement
abrufen und rowCount()
ausführen, z.
<?php
$stmt = $db->query('SELECT * FROM table');
$row_count = $stmt->rowCount();
echo $row_count.' rows selected';
Holen Sie sich die zuletzt eingefügte ID
<?php
$result = $db->exec("INSERT INTO table(firstname, lastname) VAULES('John', 'Doe')");
$insertId = $db->lastInsertId();
Was wir in der Funktion mysql_*
tun, ist:
<?php
$results = mysql_query("UPDATE table SET field='value'") or die(mysql_error());
echo mysql_affected_rows($result);
Und in pdo kann dasselbe gemacht werden durch:
<?php
$affected_rows = $db->exec("UPDATE table SET field='value'");
echo $affected_rows;
In der obigen Abfrage PDO::exec
eine SQL-Anweisung ausführen und die Anzahl der betroffenen Zeilen zurückgeben.
Einfügen und Löschen wird später behandelt.
Die obige Methode ist nur nützlich, wenn Sie in der Abfrage keine Variablen verwenden. Wenn Sie jedoch eine Variable in einer Abfrage verwenden müssen, versuchen Sie niemals, wie oben beschrieben vorzugehen, und zwar für vorbereitete Anweisung oder parametrisierte Anweisung is.
Q. Was ist eine vorbereitete Aussage und warum brauche ich sie?
A. Eine vorbereitete Anweisung ist eine vorkompilierte SQL-Anweisung, die mehrmals ausgeführt werden kann, indem nur die Daten an den Server gesendet werden.
Der typische Arbeitsablauf bei der Verwendung einer vorbereiteten Anweisung lautet wie folgt ( zitiert nach Wikipedia drei 3 Punkte ):
Vorbereiten : Die Anweisungsvorlage wird von der Anwendung erstellt und an das Datenbankverwaltungssystem (DBMS) gesendet. Bestimmte Werte werden nicht angegeben und als Parameter, Platzhalter oder Bindevariablen bezeichnet (im Folgenden mit ?
bezeichnet):
INSERT INTO PRODUCT (name, price) VALUES (?, ?)
Das DBMS analysiert, kompiliert und führt eine Abfrageoptimierung für die Anweisungsvorlage durch und speichert das Ergebnis, ohne es auszuführen.
1.00
für den zweiten Parameter angegeben.Sie können eine vorbereitete Anweisung verwenden, indem Sie Platzhalter in Ihre SQL einfügen. Grundsätzlich gibt es drei ohne Platzhalter (versuchen Sie es nicht mit der obigen Variablen), einen mit unbenannten Platzhaltern und einen mit benannten Platzhaltern.
Q. Also, was sind benannte Platzhalter und wie verwende ich sie?
A. Benannte Platzhalter. Verwenden Sie beschreibende Namen mit vorangestelltem Doppelpunkt anstelle von Fragezeichen. Wir kümmern uns nicht um Position/Reihenfolge des Wertes im Namen Platzhalter:
$stmt->bindParam(':bla', $bla);
bindParam(parameter,variable,data_type,length,driver_options)
Sie können auch ein Execute-Array verwenden:
<?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);
Eine weitere nette Funktion für OOP
-Freunde ist, dass benannte Platzhalter Objekte direkt in Ihre Datenbank einfügen können, vorausgesetzt, die Eigenschaften stimmen mit den benannten Feldern überein. Zum Beispiel:
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. Also, was sind unbenannte Platzhalter und wie verwende ich sie?
A. Lassen Sie uns ein Beispiel haben:
<?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();
und
$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->execute(array('john', '29 bla district'));
Oben sehen Sie diesen ?
anstelle eines Namens wie in einem Namensplatzhalter. Im ersten Beispiel weisen wir nun den verschiedenen Platzhaltern Variablen zu ($stmt->bindValue(1, $name, PDO::PARAM_STR);
). Anschließend weisen wir diesen Platzhaltern Werte zu und führen die Anweisung aus. Im zweiten Beispiel wechselt das erste Array-Element zum ersten ?
und das zweite zum zweiten ?
.
NOTE: In unbenannten Platzhaltern müssen wir uns um die richtige Reihenfolge der Elemente in dem Array kümmern, die wir an die Methode PDOStatement::execute()
übergeben.
SELECT
, INSERT
, UPDATE
, DELETE
vorbereitete AbfragenSELECT
:
$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
und/oder MySQLi
sind jedoch nicht vollständig sicher. Überprüfen Sie die Antwort Reichen PDO-vorbereitete Anweisungen aus, um SQL-Injection zu verhindern? von ircmaxell . Ich zitiere auch einen Teil aus seiner Antwort:
$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 /*"));
Beginnen wir zunächst mit dem Standardkommentar, den wir allen geben:
Bitte benutzen Sie nicht
mysql_*
Funktionen in neuem Code . Sie werden nicht mehr gepflegt nd sind offiziell veraltet . Siehe rotes Kästchen ? Erfahren Sie mehr über vorbereitete Anweisungen und verwenden Sie PDO oder MySQLi - dieser Artikel hilft Ihnen bei der Entscheidung, welche. Wenn Sie PDO wählen, hier ist ein gutes Tutorial .
Lassen Sie uns das Satz für Satz durchgehen und erklären:
Sie werden nicht mehr gepflegt und sind offiziell veraltet
Dies bedeutet, dass die PHP Community die Unterstützung für diese sehr alten Funktionen allmählich einstellt. Sie sind wahrscheinlich nicht in einer zukünftigen (aktuellen) Version von PHP vorhanden! Die fortgesetzte Verwendung dieser Funktionen kann Ihren Code in (nicht so ferner) Zukunft beschädigen.
NEU! - ext/mysql ist jetzt offiziell veraltet ab PHP 5.5!
Stattdessen solltest du von vorbereiteten Aussagen lernen
Die Erweiterung mysql_*
unterstützt vorbereitete Anweisungen nicht. Dies ist (unter anderem) eine sehr effektive Gegenmaßnahme gegen SQL-Injection. Es wurde eine sehr schwerwiegende Sicherheitslücke in MySQL-abhängigen Anwendungen behoben, die es Angreifern ermöglicht, auf Ihr Skript zuzugreifen und jede mögliche Abfrage für Ihre Datenbank auszuführen.
Weitere Informationen finden Sie unter Wie kann ich SQL-Injection in PHP verhindern?
Siehe das rote Kästchen?
Wenn Sie zu einer mysql
Funktionshandbuchseite wechseln, wird ein rotes Kästchen angezeigt, das erklärt, dass diese Seite nicht mehr verwendet werden sollte.
Verwende entweder PDO oder MySQLi
Es gibt bessere, robustere und besser aufgebaute Alternativen, PDO - PHP Database Object, die einen vollständigen OOP Ansatz bieten zur Datenbankinteraktion und MySQLi, was eine MySQL-spezifische Verbesserung ist.
Die analytischen und synthetischen Gründe wurden bereits erwähnt. Für Neulinge gibt es einen größeren Anreiz, die Verwendung der datierten mysql_ -Funktionen einzustellen.
Zeitgemäße Datenbank-APIs sind nur einfacher zu verwenden.
Meist sind es die gebundenen Parameter , die den Code vereinfachen können. Und mit ausgezeichnete Tutorials (wie oben gezeigt) ist der Übergang zu PDO nicht allzu mühsam.
Das Umschreiben einer größeren Codebasis auf einmal erfordert jedoch Zeit. Daseinsberechtigung für diese Zwischenalternative:
Mit < pdo_mysql.php > können Sie mit minimalem Aufwand von den alten mysql_-Funktionen wechseln . Es werden _pdo_
_ Funktionswrapper hinzugefügt, die ihre _mysql_
_ Gegenstücke ersetzen.
Einfach _include_once(
_ "pdo_mysql.php"
_);
_ in jedem Aufrufskript, das mit der Datenbank interagieren muss.
Entferne das _ Führe überall das Präfix ein und ersetze es durch mysql_
_pdo_
.
mysql_
_connect()
wird _pdo_
_connect()
mysql_
_query()
wird _pdo_
_query()
mysql_
_num_rows()
wird _pdo_
_num_rows()
mysql_
_insert_id()
wird _pdo_
_insert_id()
mysql_
_fetch_array()
wird _pdo_
_fetch_array()
mysql_
_fetch_assoc()
wird _pdo_
_fetch_assoc()
mysql_
_real_escape_string()
wird _pdo_
_real_escape_string()
Ihr Code funktioniert gleich und sieht meistens immer noch gleich aus:
_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]";
}
_
Et voilà.
Ihr Code ist mit PDO.
Jetzt ist es Zeit, es tatsächlich zu nutzen .
Sie benötigen nur eine weniger unhandliche API.
pdo_query()
fügt eine sehr einfache Unterstützung für gebundene Parameter hinzu. Das Konvertieren von altem Code ist einfach:
Verschieben Sie Ihre Variablen aus der SQL-Zeichenfolge.
pdo_query()
hinzu.?
_ als Platzhalter, an denen sich die Variablen zuvor befanden.'
_ einfache Anführungszeichen, die zuvor Zeichenfolgenwerte/-variablen enthielten.Der Vorteil wird bei längerem Code deutlicher.
Häufig werden Zeichenfolgenvariablen nicht nur in SQL interpoliert, sondern mit dazwischen liegenden Escape-Aufrufen verknüpft.
_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")
_
Mit _?
_ angewendeten Platzhaltern müssen Sie sich nicht darum kümmern:
_pdo_query("SELECT id, links, html, title, user, date FROM articles
WHERE title=? OR id=? AND user<>? ORDER BY date", $title, $id, $root)
_
Denken Sie daran, dass pdo_ * weiterhin entweder oder zulässt.
Entkomme einfach keiner Variablen und binde sie in derselben Abfrage.
:named
_ Platzhalterlisten erlaubt.Noch wichtiger ist, dass Sie hinter jeder Abfrage $ _REQUEST [] -Variablen sicher übergeben können. Wenn übermittelte _<form>
_ Felder genau mit der Datenbankstruktur übereinstimmen, ist diese sogar noch kürzer:
_pdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);
_
So viel Einfachheit. Kommen wir jedoch auf einige weitere Tipps zum Umschreiben und technische Gründe zurück, warum Sie diese möglicherweise entfernen möchten _ und entkommen.mysql_
_
sanitize()
-FunktionenSobald Sie alle konvertiert haben _ Anrufe an _mysql_
_pdo_query
_ mit gebundenen Parametern, entfernen Sie alle redundanten _pdo_real_escape_string
_ Anrufe.
Insbesondere sollten Sie alle Funktionen von sanitize
oder clean
oder filterThis
oder _clean_data
_ wie in den datierten Tutorials angegeben in der einen oder anderen Form korrigieren:
_function sanitize($str) {
return trim(strip_tags(htmlentities(pdo_real_escape_string($str))));
}
_
Der auffälligste Fehler hier ist der Mangel an Dokumentation. Noch wichtiger ist, dass die Reihenfolge der Filterung genau in der falschen Reihenfolge war.
Die richtige Reihenfolge wäre gewesen: veraltet stripslashes
als innerster Aufruf, dann trim
, danach _strip_tags
_, htmlentities
für den Ausgabekontext und zuletzt __escape_string
_ als Die Anwendung sollte direkt vor dem SQL-Intersparsing erfolgen.
Aber als ersten Schritt nur den __real_escape_string
_ Aufruf loswerden.
Möglicherweise müssen Sie den Rest Ihrer Funktion sanitize()
erst einmal beibehalten, wenn Ihr Datenbank- und Anwendungsfluss HTML-kontextsichere Zeichenfolgen erwartet. Fügen Sie einen Kommentar hinzu, der darauf hinweist, dass nur HTML angewendet wird, das fortan maskiert wird.
Die Behandlung von Zeichenfolgen/Werten wird an das PDO und seine parametrisierten Anweisungen delegiert.
Wenn in Ihrer Desinfektionsfunktion stripslashes()
erwähnt wurde, deutet dies möglicherweise auf eine höhere Aufsichtsebene hin.
Das war gewöhnlich da, um Schaden (doppeltes Entkommen) von dem veralteten magic_quotes
rückgängig zu machen. Was jedoch am besten zentral festgelegt ist, nicht Zeichenfolge für Zeichenfolge.
Verwenden Sie einen der Ansätze serland Reversal . Entfernen Sie dann die stripslashes()
in der sanitize
-Funktion.
Historischer Hinweis zu magic_quotes. Diese Funktion ist zu Recht veraltet. Es wird jedoch oft fälschlicherweise als ausgefallenes Sicherheitsmerkmal dargestellt. Aber magic_quotes sind ebenso ein fehlgeschlagenes Sicherheitsmerkmal wie Tennisbälle als Nahrungsquelle. Das war einfach nicht ihr Zweck.
Die ursprüngliche Implementierung in PHP2/FI führte es explizit mit nur " Anführungszeichen werden automatisch ausgeblendet, was es einfacher macht, Formulardaten direkt an msql-Abfragen zu übergeben". Insbesondere war die Verwendung mit mSQL aus Versehen sicher, da dies nur ASCII unterstützte.
Dann führte PHP3/Zend magic_quotes für MySQL wieder ein und dokumentierte es falsch. Aber ursprünglich war es nur ein Convenience-Feature , nicht für die Sicherheit gedacht.
Wenn Sie Zeichenfolgenvariablen in die SQL-Abfragen mischen, wird es für Sie nicht nur komplizierter, zu folgen. Es ist für MySQL auch ein unnötiger Aufwand, Code und Daten wieder voneinander zu trennen.
SQL-Injektionen erfolgen einfach, wenn Daten in den Code-Kontext übergehen. Ein Datenbankserver kann später nicht erkennen, wo PHP ursprünglich Variablen zwischen Abfrageklauseln eingefügt hat.
Mit gebundenen Parametern trennen Sie SQL-Code und SQL-Kontextwerte in Ihrem PHP Code. Hinter den Kulissen wird es jedoch nicht erneut gemischt (außer bei PDO :: EMULATE_PREPARES). Ihre Datenbank empfängt die unveränderten SQL-Befehle und 1: 1-Variablenwerte.
In dieser Antwort wird betont, dass Sie auf die Lesbarkeitsvorteile des Ablegens achten sollten _. Aufgrund dieser sichtbaren und technischen Trennung von Daten und Code gibt es gelegentlich auch einen Leistungsvorteil (wiederholte INSERTs mit nur unterschiedlichen Werten).mysql_
_
Beachten Sie, dass die Parameterbindung immer noch keine Lösung aus einer Hand für alle SQL-Injections ist. Es behandelt die am häufigsten verwendete Verwendung für Daten/Werte. Es ist jedoch nicht möglich, Spaltennamen/Tabellen-IDs auf die Positivliste zu setzen, bei der Erstellung dynamischer Klauseln zu helfen oder nur einfache Array-Wertelisten.
Diese _pdo_*
_ Wrapper-Funktionen bilden eine codierungsfreundliche Stop-Gap-API. (Es ist so ziemlich das, was MYSQLI
hätte sein können, wenn es nicht die eigenwillige Funktionsunterschriftenverschiebung gegeben hätte). Sie legen auch meistens die reale gU frei.
Das Umschreiben muss nicht bei der Verwendung der neuen pdo_-Funktionsnamen aufhören. Sie könnten jeden pdo_query () einzeln in einen einfachen Aufruf von $ pdo-> prepare () -> execute () überführen.
Es ist jedoch am besten, wieder mit der Vereinfachung zu beginnen. Zum Beispiel der allgemeine Ergebnisabruf:
_$result = pdo_query("SELECT * FROM tbl");
while ($row = pdo_fetch_assoc($result)) {
_
Kann durch nur eine foreach-Iteration ersetzt werden:
_foreach ($result as $row) {
_
Oder noch besser eine direkte und vollständige Array-Abfrage:
_$result->fetchAll();
_
In den meisten Fällen erhalten Sie hilfreichere Warnungen als PDO oder mysql_ normalerweise nach fehlgeschlagenen Abfragen.
Das hat also hoffentlich einige praktische Gründe und einen sinnvollen Weg zum Ablegen aufgezeigt _.mysql_
_
Nur auf pdo umzuschalten, ist noch nicht so einfach. pdo_query()
ist auch nur ein Frontend darauf.
Sofern Sie nicht auch die Parameterbindung einführen oder etwas anderes aus der schöneren API verwenden können, ist dies ein sinnloser Schalter. Ich hoffe, es ist einfach genug dargestellt, um die Entmutigung von Neuankömmlingen nicht weiter zu fördern. (Bildung funktioniert normalerweise besser als Verbot.)
Während es für die einfachste Kategorie qualifiziert ist, die möglicherweise funktionieren könnte, ist es auch noch sehr experimenteller Code. Ich habe es gerade über das Wochenende geschrieben. Es gibt jedoch eine Vielzahl von Alternativen. Suchen Sie einfach bei Google nach PHP-Datenbankabstraktion und stöbern Sie ein wenig. Es gab und gibt immer viele exzellente Bibliotheken für solche Aufgaben.
Wenn Sie Ihre Datenbankinteraktion weiter vereinfachen möchten, sind Mapper wie Paris/Idiorm einen Versuch wert. So wie niemand mehr das langweilige DOM in JavaScript verwendet, müssen Sie heutzutage keine rohe Datenbankschnittstelle mehr babysitten.
Die mysql_
-Funktionen:
Apropos technische Gründe, es gibt nur wenige, extrem spezifische und selten verwendete. Höchstwahrscheinlich wirst du sie niemals in deinem Leben benutzen.
Vielleicht bin ich zu ahnungslos, aber ich hatte nie die Gelegenheit, solche Dinge zu benutzen
Wenn Sie sie brauchen - das sind ohne Zweifel technische Gründe, um von der mysql-Erweiterung wegzukommen und etwas Stilvolleres und Moderneres zu schaffen.
Dennoch gibt es auch einige nicht-technische Probleme, die Ihre Erfahrung etwas erschweren können
Letzteres ist ein Problem.
Aber meiner Meinung nach ist die vorgeschlagene Lösung auch nicht besser.
Es scheint mir ein zu idealistischer Traum zu sein, dass all diese PHP Benutzer lernen werden, wie man mit SQL-Abfragen richtig umgeht auf einmal. Höchstwahrscheinlich würden sie mysql_ * nur mechanisch in mysqli_ * ändern , wobei der Ansatz unverändert bleibt . Vor allem, weil mysqli die Verwendung von vorbereiteten Anweisungen unglaublich schmerzhaft und mühsam macht.
Ganz zu schweigen davon, dass native vorbereitete Anweisungen nicht ausreichen, um vor SQL zu schützen Injektionen, und weder mysqli noch PDO bieten eine Lösung.
Anstatt diese ehrliche Ausweitung zu bekämpfen, würde ich es vorziehen, falsche Praktiken zu bekämpfen und die Menschen auf die richtige Art und Weise zu erziehen.
Auch gibt es einige falsche oder nicht signifikante Gründe, wie
mysql_query("CALL my_proc");
für Ewigkeiten verwendet)Der letzte ist ein interessanter Punkt. Obwohl mysql ext keine nativen vorbereiteten Anweisungen unterstützt, sind sie für die Sicherheit nicht erforderlich. Wir können leicht vorbereitete Anweisungen mit manuell verarbeiteten Platzhaltern fälschen (genau wie 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 , alles ist parametriert und sicher.
Aber okay, wenn Ihnen das rote Kästchen im Handbuch nicht gefällt, entsteht ein Problem der Wahl: mysqli oder PDO?
Nun, die Antwort wäre wie folgt:
Wenn Sie wie die überwiegende Mehrheit der PHP -Anwender Raw-API-Aufrufe direkt im Anwendungscode verwenden (was im Wesentlichen eine falsche Vorgehensweise ist), ist PDO die einzige Wahl , da diese Erweiterung vorgibt, nicht nur eine API zu sein, sondern eine Semi-DAL, die noch unvollständig ist, aber viele wichtige Funktionen bietet. Mit zwei davon unterscheidet sich PDO kritisch von mysqli:
Wenn Sie also ein durchschnittlicher PHP Benutzer sind und sich eine Menge Kopfschmerzen sparen möchten, wenn Sie native vorbereitete Anweisungen verwenden, ist PDO - wieder - die einzige Wahl.
Allerdings ist die gU auch keine Wunderwaffe und hat ihre Nöte.
Also schrieb ich Lösungen für alle gängigen Fallstricke und komplexen Fälle im PDO-Tag-Wiki
Trotzdem haben alle, die von Erweiterungen sprechen, immer die 2 wichtigen Fakten zu Mysqli und PDO übersehen:
Die vorbereitete Anweisung ist keine Silberkugel . Es gibt dynamische Bezeichner, die mit vorbereiteten Anweisungen nicht gebunden werden können. Es gibt dynamische Abfragen mit einer unbekannten Anzahl von Parametern, was das Erstellen von Abfragen zu einer schwierigen Aufgabe macht.
Weder mysqli_ * noch PDO-Funktionen sollten im Anwendungscode enthalten sein.
Es sollte eine Abstraktionsschicht zwischen ihnen und dem Anwendungscode geben, die die ganze schmutzige Arbeit des Bindens, Schleifens und der Fehlerbehandlung erledigt usw. innen, machen Anwendungscode DRY und sauber. Speziell für die komplexen Fälle wie dynamisches Querybuilding.
Es reicht also nicht aus, nur auf PDO oder MySQL umzusteigen. Man muss ein ORM, einen Query Builder oder eine beliebige Datenbankabstraktionsklasse verwenden, anstatt in ihrem Code unformatierte API-Funktionen aufzurufen.
Und im Gegenteil - wenn Sie eine Abstraktionsschicht zwischen Ihrem Anwendungscode und der mysql-API haben - spielt es keine Rolle, welche Engine verwendet wird. Sie können mysql ext verwenden, bis es veraltet ist, und dann Ihre Abstraktionsklasse einfach in eine andere Engine umschreiben , wobei der gesamte Anwendungscode intakt ist .
Hier einige Beispiele, die auf meiner safemysql-Klasse basieren, um zu zeigen, wie eine solche Abstraktionsklasse aussehen sollte:
$city_ids = array(1,2,3);
$cities = $db->getCol("SELECT name FROM cities WHERE is IN(?a)", $city_ids);
Vergleichen Sie diese eine einzelne Zeile mit Code-Menge, die Sie mit PDO benötigen .
Dann vergleiche mit verrückte Menge an Code , das du mit rohen, von Mysqli vorbereiteten Anweisungen brauchst. Beachten Sie, dass Fehlerbehandlung, Profilerstellung und Abfrageprotokollierung bereits integriert sind und ausgeführt werden.
$insert = array('name' => 'John', 'surname' => "O'Hara");
$db->query("INSERT INTO users SET ?u", $insert);
Vergleichen Sie es mit üblichen PDO-Einfügungen, wenn jeder einzelne Feldname sechs- bis zehnmal wiederholt wird - in all diesen zahlreichen benannten Platzhaltern, Bindungen und Abfragedefinitionen.
Ein anderes Beispiel:
$data = $db->getAll("SELECT * FROM goods ORDER BY ?n", $_GET['order']);
Es gibt kaum ein Beispiel dafür, wie PDO einen solchen praktischen Fall handhaben kann.
Und es wird zu wortreich und höchstwahrscheinlich unsicher sein.
Also noch einmal - es ist nicht nur ein roher Treiber, der Ihr Anliegen sein sollte, sondern eine Abstraktionsklasse, die nicht nur für alberne Beispiele aus dem Anfängerhandbuch nützlich ist, sondern auch zur Lösung von Problemen aus dem wirklichen Leben.
Es gibt viele Gründe, aber der wichtigste ist vielleicht, dass diese Funktionen unsichere Programmierpraktiken fördern, da sie keine vorbereiteten Anweisungen unterstützen. Vorbereitete Anweisungen helfen, SQL-Injection-Angriffe zu verhindern.
Wenn Sie mysql_*
-Funktionen verwenden, müssen Sie daran denken, die vom Benutzer angegebenen Parameter über mysql_real_escape_string()
auszuführen. Wenn Sie nur an einer Stelle vergessen oder nur einen Teil der Eingabe entgehen, kann Ihre Datenbank angegriffen werden.
Die Verwendung von vorbereiteten Anweisungen in PDO
oder mysqli
führt dazu, dass diese Arten von Programmierfehlern schwieriger zu machen sind.
Unter anderem ist es viel schwieriger sicherzustellen, dass die Eingabedaten desinfiziert werden. Wenn Sie parametrisierte Abfragen verwenden, wie bei PDO oder Mysqli, können Sie das Risiko vollständig vermeiden.
Als Beispiel könnte jemand "enhzflep); drop table users"
als Benutzernamen verwenden. Die alten Funktionen erlauben die Ausführung mehrerer Anweisungen pro Abfrage, so dass ein böswilliger Fehler eine ganze Tabelle löschen kann.
Wenn ein PDO von mysqli verwendet wird, würde der Benutzername "enhzflep); drop table users"
sein.
Siehe bobby-tables.com .
Diese Antwort soll zeigen, wie unbedeutend es ist, schlecht geschriebene PHP Code für die Benutzerüberprüfung zu umgehen, wie (und mit welchen Mitteln) diese Angriffe funktionieren und wie die alten MySQL-Funktionen durch eine sichere vorbereitete Anweisung ersetzt werden können. Und im Grunde, warum StackOverflow-Benutzer (wahrscheinlich mit vielen Wiederholungszahlen) neue Benutzer bellen, die Fragen stellen, um ihren Code zu verbessern.
Als Erstes erstellen Sie bitte diese Test-MySQL-Datenbank (ich habe meine Vorbereitung genannt):
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)
Danach können wir zu unserem PHP Code wechseln.
Nehmen wir an, das folgende Skript ist der Überprüfungsprozess für einen Administrator auf einer Website (vereinfacht, funktioniert jedoch, wenn Sie ihn kopieren und zum Testen verwenden):
<?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>
Scheint auf den ersten Blick legitim genug zu sein.
Der Benutzer muss ein Login und ein Passwort eingeben, oder?
Brilliant, geben Sie nicht Folgendes ein:
user: bob
pass: somePass
und reichen Sie es ein.
Die Ausgabe lautet wie folgt:
You could not be verified. Please try again...
Super! Wie erwartet, können wir jetzt den tatsächlichen Benutzernamen und das aktuelle Kennwort ausprobieren:
user: Fluffeh
pass: mypass
Tolle! Hi-Fives rundum, der Code hat einen Admin korrekt verifiziert. Es ist perfekt!
Nicht wirklich. Sagen wir, der Benutzer ist eine clevere kleine Person. Lass uns sagen, die Person ist ich.
Geben Sie Folgendes ein:
user: bob
pass: n' or 1=1 or 'm=m
Und die Ausgabe ist:
The check passed. We have a verified admin!
Herzlichen Glückwunsch, Sie haben mir gerade erlaubt, in den Bereich Ihres Super-geschützten Admins einzutreten und dabei einen falschen Benutzernamen und ein falsches Passwort einzugeben. Im Ernst, wenn Sie mir nicht glauben, erstellen Sie die Datenbank mit dem von mir bereitgestellten Code und führen Sie diesen Code aus PHP der auf den ersten Blick WIRKLICH scheint, den Benutzernamen und das Passwort ziemlich gut zu verifizieren.
Also, in Antwort, dass IS warum Sie angeschrien werden.
Also, schauen wir uns an, was schief gelaufen ist und warum ich gerade in Ihre Super-Admin-Only-Bat-Höhle geraten bin. Ich nahm eine Vermutung an und nahm an, dass Sie mit Ihren Eingaben nicht vorsichtig waren, und gab sie einfach direkt an die Datenbank weiter. Ich habe die Eingabe so konstruiert, dass sie die Abfrage ÄNDERN würde, die Sie tatsächlich ausgeführt haben. Also, was sollte es sein und was war es dann?
select id, userid, pass from users where userid='$user' and pass='$pass'
Das ist die Abfrage, aber wenn wir die Variablen durch die tatsächlichen Eingaben ersetzen, die wir verwendet haben, erhalten wir Folgendes:
select id, userid, pass from users where userid='bob' and pass='n' or 1=1 or 'm=m'
Sehen Sie, wie ich mein "Passwort" so aufgebaut habe, dass es zunächst das einfache Anführungszeichen um das Passwort schließt und dann einen völlig neuen Vergleich einleitet. Dann habe ich aus Sicherheitsgründen einen weiteren "String" hinzugefügt, damit das einfache Anführungszeichen wie erwartet in dem Code geschlossen wurde, den wir ursprünglich hatten.
Es geht hier jedoch nicht darum, dass die Leute Sie anschreien, sondern wie Sie Ihren Code sicherer machen.
Okay, was ist schief gelaufen und wie können wir das beheben?
Dies ist ein klassischer SQL-Injection-Angriff. Eines der einfachsten für diese Angelegenheit. Auf der Skala der Angriffsvektoren ist dies ein Kleinkind, das einen Panzer angreift - und gewinnt.
Wie schützen wir also Ihren heiligen Administrationsabschnitt und machen ihn schön und sicher? Als Erstes müssen Sie die wirklich alten und veralteten mysql_*
-Funktionen nicht mehr verwenden. Ich weiß, Sie haben ein Online-Tutorial befolgt, das Sie online gefunden haben, und es funktioniert, aber es ist alt, es ist veraltet, und innerhalb weniger Minuten habe ich es einfach hinter sich gelassen, ohne dabei ins Schwitzen zu geraten.
Jetzt haben Sie die besseren Möglichkeiten, mysqli_ oder PDO zu verwenden. Ich bin persönlich ein großer Fan von PDO, daher werde ich in dieser Antwort PDO verwenden. Es gibt Vor- und Nachteile, aber ich persönlich finde, dass die Profis bei weitem die Nachteile überwiegen. Es ist für mehrere Datenbank-Engines portierbar - egal, ob Sie MySQL oder Oracle verwenden oder einfach nur verdammt noch mal - nur durch Ändern der Verbindungszeichenfolge verfügt es über alle ausgefallenen Funktionen, die wir verwenden möchten, und es ist schön und sauber. Ich mag sauber.
Sehen wir uns jetzt diesen Code noch einmal an, diesmal mit einem PDO-Objekt:
<?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>
Die Hauptunterschiede sind, dass es keine weiteren mysql_*
-Funktionen gibt. Das geschieht alles über ein PDO-Objekt, zweitens verwendet es eine vorbereitete Anweisung. Was ist eine vorgefertigte Aussage, die Sie fragen? Dies ist eine Möglichkeit, der Datenbank vor dem Ausführen einer Abfrage mitzuteilen, welche Abfrage wir ausführen werden. In diesem Fall sagen wir der Datenbank: "Hallo, ich werde eine select-Anweisung ausführen, die ID, Benutzer-ID und Übergabe von den Tabellenbenutzern verlangt, bei denen die Benutzer-ID eine Variable und der Durchlauf auch eine Variable ist.".
In der Execute-Anweisung übergeben wir der Datenbank ein Array mit allen Variablen, die jetzt erwartet werden.
Die Ergebnisse sind fantastisch. Lasst uns diese Kombinationen aus Benutzername und Passwort noch einmal ausprobieren:
user: bob
pass: somePass
Benutzer wurde nicht bestätigt. Genial.
Wie wäre es mit:
user: Fluffeh
pass: mypass
Oh, ich war nur ein bisschen aufgeregt, es hat funktioniert: Der Scheck ist bestanden. Wir haben einen verifizierten Admin!
Versuchen wir nun die Daten, die ein cleverer Kerl eingeben würde, um an unserem kleinen Überprüfungssystem vorbeizukommen:
user: bob
pass: n' or 1=1 or 'm=m
Dieses Mal erhalten wir Folgendes:
You could not be verified. Please try again...
Deshalb werden Sie bei der Veröffentlichung von Fragen angeschrien. Dies liegt daran, dass die Benutzer sehen können, dass Ihr Code umgangen werden kann, ohne dass Sie es versuchen. Bitte verwenden Sie diese Frage und Antwort, um Ihren Code zu verbessern, ihn sicherer zu machen und aktuelle Funktionen zu verwenden.Dies bedeutet nicht, dass dies PERFEKT ist. Es gibt viele weitere Möglichkeiten, die Sie verbessern könnten, indem Sie beispielsweise Hash-Kennwörter verwenden. Wenn Sie sensible Informationen in der Datenbank speichern, speichern Sie sie nicht im Klartext Sie ändern einfach Ihren alten injektionsanfälligen Code, und Sie sind auf dem Weg, guten Code zu schreiben - und die Tatsache, dass Sie so weit gekommen sind und noch lesen, gibt mir die Hoffnung, dass Sie diesen Typ nicht nur implementieren werden Code, wenn Sie Ihre Websites und Anwendungen schreiben, aber dass Sie vielleicht nach den anderen Dingen suchen, die ich gerade erwähnt habe - und mehr. Schreiben Sie den besten Code, den Sie können, nicht den einfachsten Code, der kaum funktioniert.
Lastly, this isn't to say that this is PERFECT code. There are many more things that you could do to improve it, use hashed passwords for example, ensure that when you store sensetive information in the database, you don't store it in plain text, have multiple levels of verification - but really, if you just change your old injection prone code to this, you will be WELL along the way to writing good code - and the fact that you have gotten this far and are still reading gives me a sense of hope that you will not only implement this type of code when writing your websites and applications, but that you might go out and research those other things I just mentioned - and more. Write the best code you can, not the most basic code that barely functions.
Die MySQL-Erweiterung ist die älteste der drei und war die ursprüngliche Art und Weise, mit der Entwickler mit MySQL kommunizieren. Diese Erweiterung wird jetzt nicht mehr empfohlen zugunsten der anderen two- Alternativen aufgrund von Verbesserungen in neueren Versionen von PHP und MySQL.
MySQLi ist die "verbesserte" Erweiterung für die Arbeit mit MySQL-Datenbanken. Es nutzt Funktionen, die in neueren Versionen des MySQL-Servers verfügbar sind, stellt dem Entwickler sowohl eine funktionsorientierte als auch eine objektorientierte Schnittstelle zur Verfügung und erledigt ein paar andere einfache Dinge.
PDO bietet eine API, die den größten Teil der Funktionalität konsolidiert, die zuvor auf die wichtigsten Erweiterungen des Datenbankzugriffs verteilt wurde, z. B. MySQL, PostgreSQL, SQLite, MSSQL usw. Die Schnittstelle stellt dem Programmierer übergeordnete Objekte zur Verfügung Bei Datenbankverbindungen führen Abfragen und Ergebnissätze sowie Low-Level-Treiber zur Kommunikation und zum Ressourcen-Handling mit dem Datenbankserver. Es wird viel Diskussion und Arbeit in die PDO gesteckt, und es wird als die geeignete Methode für das Arbeiten mit Datenbanken in modernem, professionellem Code betrachtet.
Ich finde die obigen Antworten sehr lang, also zusammenfassend:
Die mysqli-Erweiterung hat eine Anzahl von Vorteile, die wichtigsten Verbesserungen gegenüber Die MySQL-Erweiterung ist:
- Objektorientierte Schnittstelle
- Unterstützung für vorbereitete Anweisungen
- Unterstützung für mehrere Anweisungen
- Unterstützung für Transaktionen
- Verbesserte Debugging-Funktionen
- Unterstützung für eingebettete Server
Quelle: MySQLi-Übersicht
Wie in den obigen Antworten erklärt, sind die Alternativen zu mysql mysqli und PDO (PHP Data Objects).
MySQLi und PDO wurden in PHP 5.0 eingeführt, während MySQL vor PHP 3.0 eingeführt wurde. Zu beachten ist, dass MySQL in PHP5.x enthalten ist, in späteren Versionen jedoch nicht mehr empfohlen wird.
Es ist möglich, fast alle mysql_*
-Funktionen mit mysqli oder PDO zu definieren. Fügen Sie sie einfach in Ihre alte PHP - Anwendung ein, und es wird auf PHP7 funktionieren. Meine Lösung hier .
<?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;
}
Die Funktionen, die diesem mysql_connect()
, mysql_query()
-Typ ähnlich sind, sind die Funktionen der vorherigen Version PHP, d. H. (PHP 4), und werden jetzt nicht verwendet.
Diese werden in der aktuellen Version von PHP5 auf ähnliche Weise durch mysqli_connect()
, mysqli_query()
ersetzt.
Dies ist der Grund für den Fehler.
Dies ist eine alte Frage von heute (Januar 2019), kann aber dennoch nützlich sein. Vor ungefähr 7 Jahren habe ich eine tabellarische Abbildung der MySQL/MySQLi/PDO-Funktionalität erstellt. Kann eine nützliche Referenz sein. Es ist hier online und unten abgebildet. Fühlen Sie sich frei, den HTML-Code zu kopieren und einzufügen.
Praktisch fand ich, dass die Umwandlung prozeduraler MySQL-Funktionen in OOP MySQLi der Weg des geringsten Widerstands war. Es ist vollkommen in Ordnung, zwei DB-Verbindungen gleichzeitig geöffnet zu haben. Dies gab uns etwas Flexibilität, als wir die Konvertierungen durcharbeiteten. Wir konnten die Skripts stückweise konvertieren, und zwar Abfrage für Abfrage. Obwohl ich das heute vielleicht nicht empfehlen kann, war es damals zweckmäßig.
<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 -->