it-swarm.com.de

Warum sollte ich keine mysql_ * -Funktionen in PHP verwenden?

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

2308
Madara Uchiha

Die MySQL-Erweiterung:

  • Ist nicht in aktiver Entwicklung
  • Ist offiziell veraltet ab PHP 5.5 (veröffentlicht im Juni 2013).
  • Wurde entfernt vollständig ab PHP 7.0 (veröffentlicht Dezember 2015)
    • Dies bedeutet, dass es ab 31 Dec 2018 in keiner unterstützten Version von PHP vorhanden ist. Derzeit erhält es nur Sicherheit Updates.
  • Es fehlt eine OO - Schnittstelle
  • Unterstützt nicht:
    • Nicht blockierende asynchrone Abfragen
    • Vorbereitete Anweisungen oder parametrisierte Abfragen
    • Gespeicherte Prozeduren
    • Mehrere Anweisungen
    • Transaktionen
    • Die "neue" Kennwortauthentifizierungsmethode (standardmäßig aktiviert in MySQL 5.6; erforderlich in 5.7)
    • Alle Funktionen in MySQL 5.1

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.

Siehe den Vergleich von SQL-Erweiterungen .

1934
Quentin

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/mysqlwar 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.

Warum


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 :

  • Nicht in aktiver Entwicklung
  • Entfernt ab PHP 7
  • Fehlt eine OO Schnittstelle
  • Unterstützt keine nicht blockierenden, asynchronen Abfragen
  • Unterstützt keine vorbereiteten Anweisungen oder parametrisierte Abfragen
  • Unterstützt keine gespeicherten Prozeduren
  • Unterstützt nicht mehrere Anweisungen
  • Unterstützt nicht Transaktionen
  • Unterstützt nicht alle Funktionen in MySQL 5.1

Ü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.


Ein einfaches und kurzes PDO-Tutorial


F. Die erste Frage, die ich mir stellte, war: Was ist "gU"?

A. " PDO - PHP Data Objects - ist eine Datenbankzugriffsebene, die eine einheitliche Methode für den Zugriff auf mehrere Datenbanken bietet."

alt text


Verbindung zu MySQL herstellen

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

Fehlerbehandlung


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:


Daten auswählen

PDO select image

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.

Abrufmodi

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:

Jetzt komme ich zum Abrufmodus:

  • PDO::FETCH_ASSOC: Gibt ein Array zurück, das nach Spaltennamen indiziert ist und in Ihrer Ergebnismenge zurückgegeben wird
  • PDO::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 angegeben

Es 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();

Anweisungen einfügen und aktualisieren oder löschen

Insert and update PDO image

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.


Vorbereitete Aussagen

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 ):

  1. 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 (?, ?)

  2. Das DBMS analysiert, kompiliert und führt eine Abfrageoptimierung für die Anweisungsvorlage durch und speichert das Ergebnis, ohne es auszuführen.

  3. Execute : Zu einem späteren Zeitpunkt liefert (oder bindet) die Anwendung Werte für die Parameter, und der DBMS führt die Anweisung aus (möglicherweise gibt er ein Ergebnis zurück). Die Anwendung kann die Anweisung beliebig oft mit unterschiedlichen Werten ausführen. In diesem Beispiel wird möglicherweise 'Brot' für den ersten Parameter und 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 Abfragen

  1. SELECT:

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

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

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

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

HINWEIS:

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 /*"));
1243
NullPoiиteя

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!

    Neuere! ext/mysql wurde in PHP 7 entfernt.

  • 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.

292
Madara Uchiha

Benutzerfreundlichkeit

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:

Äquivalente pdo_ * -Funktionen anstelle von mysql_ *

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.

  1. Einfach _include_once(_ "pdo_mysql.php" _);_ in jedem Aufrufskript, das mit der Datenbank interagieren muss.

  2. Entferne das _mysql__ Führe überall das Präfix ein und ersetze es durch 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()
    • und so weiter ...

  3. 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 .

Gebundene Parameter können einfach zu bedienen sein

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.

  • Fügen Sie sie als durch Kommas getrennte Funktionsparameter zu pdo_query() hinzu.
  • Platzieren Sie Fragezeichen _?_ als Platzhalter, an denen sich die Variablen zuvor befanden.
  • Entfernen Sie _'_ 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.

  • Die Platzhalterfunktion wird vom eigentlichen PDO dahinter bereitgestellt.
  • Somit sind später auch _: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 _mysql__ und entkommen.

Korrigieren oder entfernen Sie alle oldschool sanitize() -Funktionen

Sobald Sie alle konvertiert haben _mysql__ Anrufe an _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.

Wie sich vorbereitete Aussagen unterscheiden

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 _mysql__. Aufgrund dieser sichtbaren und technischen Trennung von Daten und Code gibt es gelegentlich auch einen Leistungsvorteil (wiederholte INSERTs mit nur unterschiedlichen Werten).

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.

Hybrid PDO verwenden

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.

Andere Optionen

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.

212
mario

Die mysql_-Funktionen:

  1. sind veraltet - sie werden nicht mehr gewartet
  2. erlauben Sie nicht, leicht zu einem anderen Datenbank-Backend zu wechseln
  3. unterstützen Sie daher keine vorbereiteten Anweisungen
  4. programmierer zur Verwendung von Verkettung zum Erstellen von Abfragen ermutigen, was zu SQL-Injection-Schwachstellen führt
136
Alnitak

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

  • nicht blockierende, asynchrone Abfragen
  • gespeicherte Prozeduren, die mehrere Ergebnismengen zurückgeben
  • Verschlüsselung (SSL)
  • Kompression

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

  • die weitere Verwendung dieser Funktionen mit modernen PHP -Versionen wird Hinweise auf veraltete Ebenen auslösen. Sie können einfach ausgeschaltet werden.
  • in ferner Zukunft können sie möglicherweise aus dem Standardbuild PHP entfernt werden. Auch keine große Sache, da mydsql ext in PECL verschoben wird und jeder Hoster gerne PHP damit kompiliert, da er keine Kunden verlieren möchte, deren Websites jahrzehntelang funktionierten.
  • starker Widerstand der Stackoverflow-Community. Immer wenn Sie diese ehrlichen Funktionen erwähnen, wird Ihnen gesagt, dass sie unter einem strengen Tabu stehen.
  • da Sie ein durchschnittlicher PHP Benutzer sind, ist Ihre Vorstellung von der Verwendung dieser Funktionen höchstwahrscheinlich fehleranfällig und falsch. Gerade wegen all dieser zahlreichen Tutorials und Handbücher, die Sie den falschen Weg lehren. Nicht die Funktionen selbst - ich muss es betonen - sondern die Art und Weise, wie sie verwendet werden.

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

  • Unterstützt keine gespeicherten Prozeduren (wir haben mysql_query("CALL my_proc"); für Ewigkeiten verwendet)
  • Unterstützt keine Transaktionen (wie oben)
  • Unterstützt keine Multiple Statements (wer braucht sie?)
  • Nicht unter aktiver Entwicklung (also, was beeinflusst Sie in irgendeiner praktischen Weise?)
  • Fehlt eine OO Schnittstelle (die Erstellung einer ist eine Frage von mehreren Stunden)
  • Unterstützt keine vorbereiteten Anweisungen oder parametrisierten Abfragen

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 verstehen, dass eine Datenbankabstraktionsschicht verwendet werden muss und Sie nach einer API suchen, um eine zu erstellen, mysqli ist eine sehr gute Wahl, da es in der Tat viele mysql-spezifische Funktionen unterstützt.
  • 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:

    • im Gegensatz zu Mysqli kann PDO Platzhalter nach Wert binden, wodurch dynamisch erstellte Abfragen ohne mehrere Bildschirme mit recht unordentlichem Code möglich werden.
    • im Gegensatz zu mysqli kann PDO Abfrageergebnisse immer in einem einfachen, üblichen Array zurückgeben, während mysqli dies nur bei mysqlnd-Installationen ausführen kann.

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:

  1. 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.

  2. 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.

104

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.

88
Trott

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 .

71
enhzflep

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.

62
Fluffeh

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.

30
Alexander

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).

  • API unterstützt serverseitige Prepared Statements: Unterstützt von MYSQLi und PDO
  • API unterstützt clientseitige Prepared Statements: Nur von PDO unterstützt
  • Die API unterstützt gespeicherte Prozeduren: Sowohl MySQLi als auch PDO
  • Die API unterstützt Multiple Statements und alle Funktionen von MySQL 4.1+ - Unterstützt von MySQLi und meist auch von PDO

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. 

19
Ani Menon

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;
}
2
Pavel Tzonkov

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.

0
Killer

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 -->
0
Ray Paseur