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


2502

Was sind die technischen Gründe, warum man keine mysql_*Funktionen verwenden sollte? (zB mysql_query(), mysql_connect()oder mysql_real_escape_string())?

Warum sollte ich etwas anderes verwenden, auch wenn es auf meiner Website funktioniert?

Wenn sie auf meiner Website nicht funktionieren, warum erhalte ich dann Fehler wie

Warnung: mysql_connect (): Keine solche Datei oder kein solches Verzeichnis


Fehler zu sein wie: Schwerwiegender Fehler: Nicht erfasst Fehler: Aufruf der undefinierten Funktion mysql_connect () ...
Bimal Poudel

21
Allein veraltet ist Grund genug, sie zu meiden
Sasa1234

Antworten:


2088

Die MySQL-Erweiterung:

  • Ist nicht in aktiver Entwicklung
  • Ist ab PHP 5.5 (veröffentlicht im Juni 2013) offiziell veraltet .
  • Wurde ab PHP 7.0 vollständig entfernt (veröffentlicht im Dezember 2015)
    • Dies bedeutet, dass es zum 31. Dezember 2018 in keiner unterstützten Version von PHP vorhanden ist. Wenn Sie eine Version von PHP verwenden, die dies unterstützt, verwenden Sie eine Version, bei der keine Sicherheitsprobleme behoben werden.
  • 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 in MySQL 5.6 aktiviert; in 5.7 erforderlich)
    • Alle neuen Funktionen in MySQL 5.1 oder höher

Da es veraltet ist, ist Ihr Code durch seine Verwendung weniger zukunftssicher.

Die mangelnde Unterstützung für vorbereitete Anweisungen ist besonders wichtig, da sie eine klarere und weniger fehleranfällige Methode zum Escape- und Zitieren externer Daten bieten als das manuelle Escape-Verfahren mit einem separaten Funktionsaufruf.

Siehe den Vergleich von SQL-Erweiterungen .


287
Allein veraltet ist Grund genug, sie zu vermeiden. Sie werden eines Tages nicht da sein, und Sie werden nicht glücklich sein, wenn Sie sich auf sie verlassen. Der Rest ist nur eine Liste von Dingen, die die Verwendung der alten Erweiterungen vom Lernen abgehalten haben.
Tim Post

111
Abwertung ist nicht das Wundermittel, das jeder zu glauben scheint. PHP selbst wird eines Tages nicht da sein, aber wir verlassen uns auf die Tools, die uns heute zur Verfügung stehen. Wenn wir die Werkzeuge wechseln müssen, werden wir es tun.
Leichtigkeitsrennen im Orbit

133
@LightnessRacesinOrbit - Verachtung ist kein Wundermittel, sondern eine Flagge mit der Aufschrift "Wir erkennen, dass dies scheiße ist, also werden wir es nicht mehr lange unterstützen". Eine bessere Zukunftssicherheit von Code ist zwar ein guter Grund, sich von den veralteten Funktionen zu entfernen, aber nicht die einzige (oder sogar die wichtigste). Ändern Sie Werkzeuge, weil es bessere Werkzeuge gibt, nicht weil Sie dazu gezwungen sind. (Und das Ändern von Tools, bevor Sie dazu gezwungen werden, bedeutet, dass Sie die neuen Tools nicht lernen, nur weil Ihr Code nicht mehr funktioniert und gestern repariert werden muss. Dies ist der schlechteste Zeitpunkt, um neue Tools zu erlernen.)
Quentin

18
Eine Sache, die ich über das Fehlen vorbereiteter Aussagen nicht erwähnt habe, ist das Leistungsproblem. Jedes Mal , wenn Sie eine Erklärung abgeben, was hat es zu kompilieren , so dass der MySQL - Daemon kann es verstehen. Wenn Sie mit dieser API 200.000 derselben Abfrage in einer Schleife ausgeben, muss die Abfrage 200.000 Mal kompiliert werden, damit MySQL sie versteht. Mit vorbereiteten Anweisungen wird es einmal kompiliert, und dann werden Werte in das kompilierte SQL parametrisiert.
Goldentoa11

20
@symcbean, Es ist sicherlich nicht unterstützt vorbereitete Anweisungen. Das ist in der Tat der Hauptgrund, warum es veraltet ist. Ohne (einfach zu verwendende) vorbereitete Anweisungen fällt die MySQL-Erweiterung häufig SQL-Injection-Angriffen zum Opfer.
Rustyx

1287

PHP bietet drei verschiedene APIs für die Verbindung mit MySQL. Dies sind die mysql(ab PHP 7 entfernten) mysqliund PDOErweiterungen.

Die mysql_*Funktionen waren früher sehr beliebt, aber ihre Verwendung wird nicht mehr empfohlen. Das Dokumentationsteam erörtert die Datenbanksicherheitssituation, und die Schulung der Benutzer zur Abkehr von der häufig verwendeten ext / mysql-Erweiterung ist Teil davon ( siehe php.internals: ext / mysql veraltet ).

Und je später PHP - Entwickler - Team hat die Entscheidung getroffen erzeugen E_DEPRECATEDFehler , wenn Benutzer zu MySQL verbinden, sei es durch mysql_connect(), mysql_pconnect()oder die implizite Verbindung Funktionalität eingebaut in ext/mysql.

ext/mysqlwurde ab PHP 5.5 offiziell veraltet und ab PHP 7 entfernt .

Sehen Sie die Red Box?

Wenn Sie eine mysql_*Funktionshandbuchseite aufrufen, wird ein rotes Kästchen angezeigt, in dem erklärt wird, dass es nicht mehr verwendet werden sollte.

Warum


Bei der Abkehr von ext/mysqlgeht es nicht nur um Sicherheit, sondern auch darum, Zugriff auf alle Funktionen der MySQL-Datenbank zu haben.

ext/mysqlwurde 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 blieb, wodurch die Wartung des Codes etwas schwieriger wird. Zu den fehlenden Funktionen, die nicht unterstützt werden, ext/mysqlgehören: ( aus dem PHP-Handbuch ).

Grund, die mysql_*Funktion nicht zu verwenden :

  • Nicht in aktiver Entwicklung
  • Ab PHP 7 entfernt
  • Fehlt eine OO-Schnittstelle
  • Unterstützt keine nicht blockierenden asynchronen Abfragen
  • Unterstützt keine vorbereiteten Anweisungen oder parametrisierten Abfragen
  • Unterstützt keine gespeicherten Prozeduren
  • Unterstützt nicht mehrere Anweisungen
  • Unterstützt keine Transaktionen
  • Unterstützt nicht alle Funktionen in MySQL 5.1

Oben zitierter Punkt aus Quentins Antwort

Die mangelnde Unterstützung für vorbereitete Anweisungen ist besonders wichtig, da sie eine klarere, weniger fehleranfällige Methode zum Escape- und Zitieren externer Daten bieten als das manuelle Escapezeichen mit einem separaten Funktionsaufruf.

Siehe den Vergleich von SQL-Erweiterungen .


Unterdrückungswarnungen unterdrücken

Während der Konvertierung von Code in MySQLi/ PDOkönnen E_DEPRECATEDFehler unterdrückt werden, indem error_reportingin php.ini festgelegt wird , dass sie ausgeschlossen werden sollenE_DEPRECATED:

error_reporting = E_ALL ^ E_DEPRECATED

Beachten Sie, dass dadurch auch andere Verfallswarnungen ausgeblendet werden , die jedoch möglicherweise für andere Zwecke als MySQL gelten. ( aus dem PHP-Handbuch )

Der Artikel PDO vs. MySQLi: Welches sollten Sie verwenden? von Dejan Marjanovic hilft Ihnen bei der Auswahl.

Und ein besserer Weg ist PDO, und ich schreibe jetzt ein einfaches PDOTutorial.


Ein einfaches und kurzes PDO-Tutorial


Frage: Die erste Frage in meinem Kopf war: Was ist "gU"?

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

Alt-Text


Verbindung zu MySQL herstellen

Mit mysql_*Funktion oder wir können es auf die alte Art sagen (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 PDOObjekt erstellen . Der Konstruktor übernimmt Parameter zum Spezifizieren der Datenbankquelle PDO‚s Konstruktor meist vier Parameter, die DSN(Datenquellennamen) und gegebenenfalls username, password.

Hier denke ich, dass Sie mit allen außer vertraut sind DSN; das ist neu in PDO. A DSNist im Grunde eine Reihe von Optionen, die angeben, PDOwelcher Treiber verwendet werden soll, und Verbindungsdetails. 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 verwenden charset=UTF-8, aber manchmal verursacht es einen Fehler, daher ist es besser zu verwenden utf8.

Wenn ein Verbindungsfehler auftritt, wird ein PDOExceptionObjekt ausgelöst, das abgefangen werden kann, um es Exceptionweiter zu verarbeiten .

Gut gelesen : Verbindungen und Verbindungsmanagement ¶

Sie können auch mehrere Treiberoptionen als Array an den vierten Parameter übergeben. Ich empfehle, den Parameter zu übergeben, der PDOin den Ausnahmemodus versetzt wird. Da einige PDOTreiber native vorbereitete Anweisungen nicht unterstützen, wird PDOdie Vorbereitung emuliert. Sie können diese Emulation auch manuell aktivieren. Um die nativen serverseitig vorbereiteten Anweisungen zu verwenden, sollten Sie sie explizit festlegen false.

Die andere MySQLMöglichkeit besteht darin, die Vorbereitungsemulation zu deaktivieren, die standardmäßig im Treiber aktiviert ist. Die Vorbereitungsemulation sollte jedoch deaktiviert sein, um eine PDOsichere Verwendung zu gewährleisten.

Ich werde später erklären, warum die Vorbereitungsemulation deaktiviert werden sollte. Um einen Grund zu finden, überprüfen Sie bitte diesen Beitrag .

Es ist nur verwendbar, wenn Sie eine alte Version verwenden, MySQLdie ich nicht empfehle.

Unten finden Sie ein Beispiel dafür, 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-Erstellung festlegen?

Ja , wir können auch einige Attribute nach der PDO-Erstellung mit der setAttributeMethode festlegen :

$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


Fehlerbehandlung ist in viel einfacher PDOals mysql_*.

Eine gängige Praxis bei der Verwendung 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 die Sache nicht in behandeln können die. Das Skript wird nur abrupt beendet und der Fehler wird dann auf dem Bildschirm angezeigt, den Sie Ihren Endbenutzern normalerweise NICHT anzeigen möchten, und blutige Hacker können 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.

PDObietet eine bessere Lösung: Ausnahmen. Alles , was wir mit tun PDOsollte in einem gewickelt werden try- catchBlock. Wir können PDOin einen von drei Fehlermodi wechseln, indem wir das Fehlermodusattribut festlegen. Im Folgenden sind drei Fehlerbehandlungsmodi aufgeführt.

  • PDO::ERRMODE_SILENT. Es setzt nur Fehlercodes und verhält sich fast so, als mysql_*müssten Sie jedes Ergebnis überprüfen und dann nachsehen $db->errorInfo();, um die Fehlerdetails zu erhalten.
  • PDO::ERRMODE_WARNINGErhöhen E_WARNING. (Laufzeitwarnungen (nicht schwerwiegende Fehler). Die Ausführung des Skripts wird nicht angehalten.)
  • PDO::ERRMODE_EXCEPTION: Ausnahmen auslösen. Es stellt einen Fehler dar, der von PDO ausgelöst wurde. Sie sollten kein PDOExceptionaus Ihrem eigenen Code werfen . Siehe Ausnahmen für weitere Informationen über Ausnahmen in PHP. Es verhält sich sehr ähnlich or die(mysql_error());, wenn es nicht gefangen wird. Aber im Gegensatz zu or die(), die PDOExceptionkönnen anmutig abgefangen und behandelt werden , wenn Sie sich dazu entscheiden , dies zu tun.

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 einwickeln try- catchwie unten:

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 nicht damit umgehen try- im catchMoment. Sie können es jederzeit angemessen fangen, aber ich empfehle Ihnen dringend, try- zu verwenden catch. Es kann auch sinnvoller sein, es außerhalb der Funktion abzufangen, die das PDOZeug 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 damit umgehen or die()oder wir können sagen wie mysql_*, aber es wird wirklich abwechslungsreich sein. Sie können die gefährlichen Fehlermeldungen in der Produktion ausblenden, display_errors offindem Sie Ihr Fehlerprotokoll drehen und einfach lesen.

Jetzt, nachdem vor allem die Dinge zu lesen, sind Sie wahrscheinlich denken: was zum Teufel das ist , wenn ich will nur starten einfach gelehnt SELECT, INSERT, UPDATE, oder DELETEAussagen? Keine Sorge, los geht's:


Daten auswählen

PDO Bild auswählen

Was Sie also tun, mysql_*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 PDOkö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 Methode wie unten ( query()) verwenden, gibt diese Methode ein PDOStatementObjekt zurück. Wenn Sie das Ergebnis abrufen möchten, verwenden Sie es wie oben beschrieben.

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

In PDO-Daten wird es über die ->fetch()Methode Ihres Anweisungshandles abgerufen . Bevor Sie fetch aufrufen, teilen Sie PDO am besten mit, wie die Daten abgerufen werden sollen. Im folgenden Abschnitt erkläre ich dies.

Modi abrufen

Beachten Sie die Verwendung von PDO::FETCH_ASSOCim fetch()und fetchAll()Code oben. Dies weist PDOan, die Zeilen als assoziatives Array mit den Feldnamen als Schlüssel zurückzugeben. Es gibt auch viele andere Abrufmodi, die ich nacheinander erläutern werde.

Zunächst erkläre ich, wie der Abrufmodus ausgewählt wird:

 $stmt->fetch(PDO::FETCH_ASSOC)

Oben habe ich verwendet fetch(). Sie können auch verwenden:

Jetzt komme ich zum Abrufmodus:

  • PDO::FETCH_ASSOC: Gibt ein Array zurück, das nach dem 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 zurückgegeben

Es gibt noch mehr Möglichkeiten! Lesen Sie mehr darüber in der PDOStatementFetch-Dokumentation. .

Abrufen der Zeilenanzahl :

Anstatt mysql_num_rowsdie Anzahl der zurückgegebenen Zeilen abzurufen, können Sie a PDOStatementand do rowCount()wie folgt abrufen:

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

Abrufen der zuletzt eingefügten ID

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

Anweisungen einfügen und aktualisieren oder löschen

PDO-Image einfügen und aktualisieren

Was wir in der mysql_*Funktion 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 getan werden durch:

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

Führen Sie in der obigen Abfrage PDO::execeine SQL-Anweisung aus und geben Sie die Anzahl der betroffenen Zeilen zurück.

Einfügen und Löschen wird später behandelt.

Die obige Methode ist nur nützlich, wenn Sie keine Variable in der Abfrage verwenden. Wenn Sie jedoch eine Variable in einer Abfrage verwenden müssen, versuchen Sie niemals, wie oben beschrieben, eine vorbereitete Anweisung oder eine parametrisierte Anweisung zu verwenden .


Vorbereitete Aussagen

F. Was ist eine vorbereitete Erklärung 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 für die Verwendung einer vorbereiteten Anweisung lautet wie folgt ( zitiert aus 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 Bindungsvariablen bezeichnet (siehe ?unten):

    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. Ausführen : Zu einem späteren Zeitpunkt liefert (oder bindet) die Anwendung Werte für die Parameter, und das DBMS führt die Anweisung aus (möglicherweise wird ein Ergebnis zurückgegeben). 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.00für den zweiten Parameter angegeben.

Sie können eine vorbereitete Anweisung verwenden, indem Sie Platzhalter in Ihre SQL aufnehmen. Grundsätzlich gibt es drei ohne Platzhalter (versuchen Sie dies nicht mit einer Variablen über dem einen), einen mit unbenannten Platzhaltern und einen mit benannten Platzhaltern.

F : Nun, was Platzhalter genannt und wie kann ich sie nutzen?
A. Benannte Platzhalter. Verwenden Sie beschreibende Namen, denen ein Doppelpunkt vorangestellt ist, 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 mit einem Execute-Array binden:

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

F : Nun, was sind unbenannte Platzhalter und wie kann ich sie nutzen?
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 diese ?anstelle eines Namens wie in einem Namensplatzhalter. Im ersten Beispiel weisen wir den verschiedenen Platzhaltern Variablen zu ( $stmt->bindValue(1, $name, PDO::PARAM_STR);). Dann weisen wir diesen Platzhaltern Werte zu und führen die Anweisung aus. Im zweiten Beispiel geht das erste Array-Element zum ersten ?und das zweite zum zweiten ?.

HINWEIS : Bei unbenannten Platzhaltern müssen wir auf die richtige Reihenfolge der Elemente im Array achten, die wir an die PDOStatement::execute()Methode übergeben.


SELECT, INSERT, UPDATE, DELETEVorbereitet 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:

Jedoch PDOund / oder MySQLisind nicht ganz sicher. Überprüfen Sie die Antwort. Sind PDO-vorbereitete Anweisungen ausreichend, um eine SQL-Injection zu verhindern? von ircmaxell . Außerdem zitiere ich einen Teil 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 /*"));

15
Was die gute Lektüre oben wohl erwähnen sollte: Eine vorbereitete Aussage nimmt jede sinnvolle Verwendung der IN (...) construct.
Eugen Rieck

24
Die Frage war "Warum sollte ich nicht mysql_ * Funktionen in PHP verwenden". Diese Antwort ist zwar beeindruckend und voller hilfreicher Informationen, geht jedoch aus dem Rahmen und wie @trejder sagt - 8 von 10 Personen werden diese Informationen verpassen, nur weil sie nicht 4 Stunden Zeit haben, um sich durchzuarbeiten es. Dies wäre weitaus wertvoller, wenn es aufgeschlüsselt und als Antwort auf mehrere präzisere Fragen verwendet würde.
Alex McMillan

Persoanlly bevorzuge ich Mysqli und PDO. Aber für die Handhabung habe ich eine Ausnahmealternative ausprobiert. function throwEx() { throw new Exception("You did selected not existng db"); } mysql_select_db("nonexistdb") or throwEx();Es funktioniert, um Ausnahmen auszulösen.
Kuldeep.kamboj

Sie listen Doesn't support non-blocking, asynchronous queriesals Grund auf, mysql_ nicht zu verwenden - Sie sollten dies auch als Grund angeben, PDO nicht zu verwenden, da PDO dies auch nicht unterstützt. (aber MySQLi unterstützt es)
Hanshenrik

Ist es möglich, Charset utf8mb4_unicode_ci zu verwenden, da ich eine Datenbank habe, die dies verwendet?
Ryan Stone

301

Beginnen wir zunächst mit dem Standardkommentar, den wir allen geben:

Bitte verwenden Sie keine mysql_*Funktionen in neuem Code . Sie werden nicht mehr gewartet und sind offiziell veraltet . Sehen Sie die rote Box ? Erfahrenmehr über vorbereitete Anweisungen statt, und verwenden Sie PDO oder MySQLi - dieser Artikel wird Ihnen helfen, entscheidenwelche. Wenn Sie sich für PDO entscheiden, finden Sie hier ein gutes Tutorial .

Lassen Sie uns dies Satz für Satz durchgehen und erklären:

  • Sie werden nicht mehr gewartet und sind offiziell veraltet

    Dies bedeutet, dass die PHP-Community die Unterstützung für diese sehr alten Funktionen allmählich einstellt. Sie werden wahrscheinlich in einer zukünftigen (aktuellen) Version von PHP nicht existieren! Die fortgesetzte Verwendung dieser Funktionen kann Ihren Code in (nicht so) ferner Zukunft beschädigen.

    NEU! - ext / mysql ist ab PHP 5.5 offiziell veraltet!

    Neuer! ext / mysql wurde in PHP 7 entfernt .

  • Stattdessen sollten Sie von vorbereiteten Aussagen erfahren

    mysql_*Die Erweiterung unterstützt keine vorbereiteten Anweisungen , was (unter anderem) eine sehr effektive Gegenmaßnahme gegen SQL Injection ist . Es wurde eine sehr schwerwiegende Sicherheitsanfälligkeit in MySQL-abhängigen Anwendungen behoben, die es Angreifern ermöglicht, auf Ihr Skript zuzugreifen und mögliche Abfragen in Ihrer Datenbank durchzuführen .

    Weitere Informationen finden Sie unter Wie kann ich die SQL-Injection in PHP verhindern?

  • Sehen Sie die Red Box?

    Wenn Sie zu einer mysqlFunktionshandbuchseite gehen , sehen Sie ein rotes Kästchen, in dem erklärt wird, dass es nicht mehr verwendet werden sollte.

  • Verwenden Sie entweder PDO oder MySQLi

    Es gibt bessere, robustere und gut ausgebaute Alternativen, PDO - PHP Database Object , das einen vollständigen OOP-Ansatz für die Datenbankinteraktion bietet, und MySQLi , eine MySQL-spezifische Verbesserung.


6
Es gibt noch eine Sache: Ich denke, dass die Funktion in PHP nur aus einem Grund noch vorhanden ist - Kompatibilität mit alten, veralteten, aber immer noch laufenden CMS-, E-Commerce-, Bulletin-Board-Systemen usw. Schließlich wird sie entfernt und Sie müssen Ihre neu schreiben Anwendung ...
Kamil

4
@Kamil: Das stimmt, aber es ist nicht wirklich ein Grund, warum Sie es nicht verwenden sollten. Der Grund, es nicht zu benutzen, ist, weil es uralt, unsicher usw. ist :)
Madaras Geist

4
@Mario - Die PHP-Entwickler haben einen Prozess und haben gerade dafür gestimmt, ext / mysql ab 5.5 offiziell abzulehnen. Es ist kein hypothetisches Problem mehr.
DEZA

2
Das Hinzufügen einiger zusätzlicher Zeilen mit einer bewährten Technik wie PDO oder MySQLi bietet immer noch die Benutzerfreundlichkeit, die PHP immer geboten hat. Ich hoffe für den Entwickler, dass er / sie weiß, dass das Sehen dieser schrecklichen mysql_ * -Funktionen in einem Tutorial tatsächlich die Lektion beeinträchtigt, und dem OP mitteilen sollte, dass diese Art von Code vor 10 Jahren soooo ist - und das in Frage stellen sollte Relevanz des Tutorials auch!
FredTheWebGuy

1
Was die Antwort wahrscheinlich erwähnen sollte: Eine vorbereitete Erklärung nimmt jede sinnvolle Verwendung der IN (...) construct.
Eugen Rieck

217

Benutzerfreundlichkeit

Die analytischen und synthetischen Gründe wurden bereits erwähnt. Für Neulinge gibt es einen größeren Anreiz, die datierten mysql_-Funktionen nicht mehr zu verwenden.

Zeitgemäße Datenbank-APIs sind einfach einfacher zu verwenden.

Es sind hauptsächlich die gebundenen Parameter, die den Code vereinfachen können. Und mit hervorragenden Tutorials (wie oben gezeigt) ist der Übergang zu PDO nicht allzu mühsam.

Das sofortige Umschreiben einer größeren Codebasis erfordert jedoch Zeit. Existenzberechtigung 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_Funktions-Wrapper hinzugefügt, die ihre mysql_Gegenstücke ersetzen .

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

  2. Entfernen Sie das mysql_Funktionspräfix überall und ersetzen Sie 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 verwendet PDO.
Jetzt ist es Zeit, es tatsächlich zu nutzen .

Gebundene Parameter können einfach zu verwenden sein

Sie benötigen lediglich eine weniger unhandliche API.

pdo_query()Fügt eine sehr einfache Unterstützung für gebundene Parameter hinzu. Das Konvertieren von altem Code ist unkompliziert:

Verschieben Sie Ihre Variablen aus der SQL-Zeichenfolge.

  • Fügen Sie sie als durch Kommas getrennte Funktionsparameter hinzu pdo_query().
  • Platzieren Sie Fragezeichen ?als Platzhalter, an denen sich die Variablen zuvor befanden.
  • Entfernen Sie 'einfache Anführungszeichen, die zuvor Zeichenfolgenwerte / -variablen enthalten haben.

Der Vorteil wird bei längerem Code offensichtlicher.

Oft werden String-Variablen 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 ?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 einer Variablen einfach nicht und binde sie in dieselbe Abfrage.

  • Die Platzhalterfunktion wird von der tatsächlichen gU dahinter bereitgestellt.
  • Somit :namedsind später auch Platzhalterlisten erlaubt .

Noch wichtiger ist, dass Sie $ _REQUEST [] -Variablen sicher hinter jeder Abfrage übergeben können. Wenn übermittelte <form>Felder genau mit der Datenbankstruktur übereinstimmen, ist sie noch kürzer:

pdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);

So viel Einfachheit. Aber lassen Sie uns noch einmal auf einige Ratschläge zum Umschreiben und technische Gründe zurückkommen, warum Sie vielleicht loswerden mysql_und entkommen möchten .

Korrigieren oder entfernen Sie alle Oldschool- sanitize()Funktionen

Wenn Sie alle mysql_Aufrufe pdo_querymit gebundenen Parametern in konvertiert haben , entfernen Sie alle redundanten pdo_real_escape_stringAufrufe.

Insbesondere sollten Sie jede reparieren sanitizeoder cleanoder filterThisoder clean_dataFunktionen wie durch datiert Tutorials in dem einem oder dem anderen beworben:

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 gewesen wäre: deprecatedly stripslashesals innerste Anruf, dann trim, danach strip_tags, htmlentitiesfür Ausgabekontext, und nur schließlich die _escape_stringseine Anwendung direkt auf die SQL intersparsing vorausgehen sollte.

  • Aber als ersten Schritt einfach den_real_escape_string Anruf loswerden .

  • Möglicherweise müssen Sie den Rest Ihrer sanitize()Funktion vorerst beibehalten, wenn Ihr Datenbank- und Anwendungsfluss HTML-kontextsichere Zeichenfolgen erwartet. Fügen Sie einen Kommentar hinzu, der nur HTML anwendet, das fortan maskiert wird.

  • Die Behandlung von Zeichenfolgen / Werten wird an PDO und seine parametrisierten Anweisungen delegiert.

  • Wenn stripslashes()in Ihrer Desinfektionsfunktion etwas erwähnt wurde, kann dies auf eine übergeordnete Aufsicht hinweisen.

    Historischer Hinweis zu magic_quotes. Diese Funktion ist zu Recht veraltet. Es wird jedoch häufig fälschlicherweise als fehlgeschlagene Sicherheitsfunktion 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 maskiert, wodurch es einfacher wird, 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 eine praktische Funktion , die nicht der Sicherheit dient.

Wie sich vorbereitete Aussagen unterscheiden

Wenn Sie Zeichenfolgenvariablen in die SQL-Abfragen verschlüsseln, wird dies nicht nur für Sie komplizierter. Es ist für MySQL auch eine enorme Anstrengung, Code und Daten wieder zu trennen.

SQL-Injections sind einfach dann, wenn Daten in den Codekontext übergehen. Ein Datenbankserver kann später nicht erkennen, wo PHP ursprünglich Variablen zwischen Abfrageklauseln geklebt hat.

Mit gebundenen Parametern trennen Sie SQL-Code und SQL-Kontextwerte in Ihrem PHP-Code. Aber es wird hinter den Kulissen nicht wieder gemischt (außer bei PDO :: EMULATE_PREPARES). Ihre Datenbank empfängt die unveränderten SQL-Befehle und 1: 1-Variablenwerte.

Während diese Antwort betont, dass Sie sich um die Lesbarkeitsvorteile des Fallens kümmern sollten mysql_. Gelegentlich gibt es aufgrund dieser sichtbaren und technischen Daten- / Codetrennung auch einen Leistungsvorteil (wiederholte INSERTs mit nur unterschiedlichen Werten).

Beachten Sie, dass die Parameterbindung immer noch keine magische Lösung aus einer Hand für alle SQL-Injektionen ist. Es behandelt die häufigste Verwendung für Daten / Werte. Es können jedoch keine Spaltennamen- / Tabellenkennungen in die Whitelist aufgenommen, keine Hilfe bei der Erstellung dynamischer Klauseln oder nur einfache Array-Wertelisten verwendet werden.

Verwendung von Hybrid-PDOs

Diese pdo_*Wrapper-Funktionen bilden eine codierungsfreundliche Stop-Gap-API. (Es ist so ziemlich das, was MYSQLIhätte sein können, wenn es nicht die eigenwillige Funktionssignaturverschiebung gegeben hätte). Sie legen auch meistens die echte gU offen.
Das Umschreiben muss nicht bei der Verwendung der neuen Funktionsnamen pdo_ aufhören. Sie können jede pdo_query () einzeln in einen einfachen Aufruf von $ pdo-> prepare () -> execute () umwandeln.

Es ist jedoch am besten, wieder mit der Vereinfachung zu beginnen. Zum Beispiel das gemeinsame Abrufen von Ergebnissen:

$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 ein direkter und vollständiger Array-Abruf:

$result->fetchAll();

In den meisten Fällen erhalten Sie hilfreichere Warnungen als PDO oder mysql_ normalerweise nach fehlgeschlagenen Abfragen.

Andere Optionen

Dies visualisierte hoffentlich einige praktische Gründe und einen lohnenden Weg, um fallen zu lassen mysql_.

Einfach umschalten auf schneidet es nicht ganz. pdo_query()ist auch nur ein Frontend drauf.

Sofern Sie nicht auch die Parameterbindung einführen oder etwas anderes aus der schöneren API verwenden können, ist dies ein sinnloser Wechsel. 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 sich für die einfachste Kategorie qualifiziert, die möglicherweise funktionieren könnte, ist es auch immer noch sehr experimenteller Code. Ich habe es gerade über das Wochenende geschrieben. Es gibt jedoch eine Vielzahl von Alternativen. Google einfach nach PHP-Datenbankabstraktion und stöbere ein wenig. Es gab und wird immer viele hervorragende Bibliotheken für solche Aufgaben geben.

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.


8
Seien Sie vorsichtig mit der pdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);Funktion - dh:pdo_query("INSERT INTO users VALUES (?, ?, ?), $_POST); $_POST = array( 'username' => 'lawl', 'password' => '123', 'is_admin' => 'true');
Rickyduck

@ Tom Sicher, obwohl es nicht viel gepflegt wird (0.9.2 war das letzte), können Sie ein fossiles Konto erstellen , dem Wiki hinzufügen oder einen Fehlerbericht einreichen (ohne Registrierung IIRC).
Mario

pdo_real_escape_string() <- Ist das überhaupt eine echte Funktion, ich kann keine Dokumentation dafür finden? Bitte posten Sie eine Quelle dafür.
Ryan Stone

144

Die mysql_Funktionen:

  1. sind veraltet - sie werden nicht mehr gewartet
  2. Sie können nicht einfach in ein anderes Datenbank-Backend wechseln
  3. Unterstützen Sie daher keine vorbereiteten Aussagen
  4. Ermutigen Sie Programmierer, Verkettungen zum Erstellen von Abfragen zu verwenden, was zu Sicherheitslücken bei der SQL-Injection führt

18
# 2 gilt auch fürmysqli_
eggyal

16
Um fair zu sein, angesichts der Unterschiede im SQL-Dialekt gibt Ihnen selbst PDO nicht mit Sicherheit die Nummer 2. Dafür benötigen Sie einen geeigneten ORM-Wrapper.
DEZA

Die mysql_*Funktion ist eine Shell in MySQL-Funktionen für neuere PHP-Versionen. Also, auch wenn die alte Client-Bibliothek nicht mehr gepflegt wird, wird mysqlnd gepflegt :)
hakre

Das Problem ist, dass nicht viele Webhosting-Anbieter aufgrund der veralteten PHP-Version einen solchen objektorientierten Designstil unterstützen können
Raju yourPepe

@ RajuGujarati so finden Sie einen Web-Host, der kann. Wenn Ihr Webhost dies nicht tut, ist die Wahrscheinlichkeit sehr hoch, dass er anfällig für Angriffe auf seine Server ist.
Alnitak

106

Apropos technische Gründe, es gibt nur wenige, äußerst spezifische und selten verwendete. Höchstwahrscheinlich werden Sie sie niemals in Ihrem Leben verwenden.
Vielleicht bin ich zu unwissend, aber ich hatte nie die Gelegenheit, sie zu benutzen

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

Wenn Sie sie brauchen - dies sind zweifellos technische Gründe, um von der MySQL-Erweiterung zu etwas Stilvollerem und Modernem überzugehen.

Es gibt jedoch auch einige nichttechnische Probleme, die Ihre Erfahrung etwas erschweren können

  • Die weitere Verwendung dieser Funktionen mit modernen PHP-Versionen führt zu veralteten Benachrichtigungen. Sie können einfach ausgeschaltet werden.
  • In ferner Zukunft können sie möglicherweise aus dem Standard-PHP-Build entfernt werden. 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.
  • Als durchschnittlicher PHP-Benutzer ist Ihre Vorstellung, diese Funktionen zu verwenden, höchstwahrscheinlich fehleranfällig und falsch. Nur wegen all dieser zahlreichen Tutorials und Handbücher, die Ihnen den falschen Weg zeigen. Nicht die Funktionen selbst - ich muss es betonen - sondern die Art und Weise, wie sie verwendet werden.

Dieses letztere Problem 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 SQL-Abfragen gleichzeitig richtig behandelt. Höchstwahrscheinlich würden sie mysql_ * nur mechanisch in mysqli_ * ändern, wobei der Ansatz unverändert bleibt . Vor allem, weil mysqli die Verwendung vorbereiteter Aussagen unglaublich schmerzhaft und mühsam macht.
Ganz zu schweigen davon, dass native vorbereitete Anweisungen nicht ausreichen, um vor SQL-Injektionen zu schützen , und weder mysqli noch PDO bieten eine Lösung.

Anstatt diese ehrliche Erweiterung zu bekämpfen, würde ich es vorziehen, falsche Praktiken zu bekämpfen und die Menschen auf die richtige Weise zu erziehen.

Es gibt auch einige falsche oder nicht signifikante Gründe, wie z

  • Unterstützt keine gespeicherten Prozeduren (wir haben sie mysql_query("CALL my_proc");seit Ewigkeiten verwendet)
  • Unterstützt keine Transaktionen (wie oben)
  • Unterstützt nicht mehrere Anweisungen (wer braucht sie?)
  • Nicht in aktiver Entwicklung (na und? Betrifft es Sie in irgendeiner praktischen Weise?)
  • Fehlt eine OO-Schnittstelle (das Erstellen 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 aus Sicherheitsgründen nicht erforderlich. Wir können vorbereitete Anweisungen leicht mit manuell behandelten Platzhaltern fälschen (genau wie bei 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 parametrisiert und sicher.

Aber okay, wenn Ihnen das rote Kästchen im Handbuch nicht gefällt, tritt ein Problem der Wahl auf: mysqli oder PDO?

Nun, die Antwort wäre wie folgt:

  • Wenn Sie die Notwendigkeit verstehen, eine Datenbankabstraktionsschicht zu verwenden und nach einer API zu suchen, um eine zu erstellen, ist mysqli eine sehr gute Wahl, da es tatsächlich viele mysql-spezifische Funktionen unterstützt.
  • Wenn Sie, wie die große Mehrheit der PHP-Leute, unformatierte API-Aufrufe direkt im Anwendungscode verwenden (was im Wesentlichen eine falsche Praxis ist), ist PDO die einzige Wahl , da diese Erweiterung vorgibt, nicht nur API, sondern eine Semi-DAL zu sein. immer noch unvollständig, bietet aber viele wichtige Funktionen, von denen zwei PDO kritisch von mysqli unterscheiden:

    • Im Gegensatz zu mysqli kann PDO Platzhalter nach Wert binden , wodurch dynamisch erstellte Abfragen ohne mehrere Bildschirme mit recht unübersichtlichem Code möglich werden.
    • Im Gegensatz zu mysqli kann PDO das Abfrageergebnis immer in einem einfachen, üblichen Array zurückgeben, während mysqli dies nur bei mysqlnd-Installationen tun kann.

Wenn Sie also ein durchschnittlicher PHP-Benutzer sind und sich eine Menge Kopfschmerzen ersparen möchten, wenn Sie native vorbereitete Anweisungen verwenden, ist PDO - wieder - die einzige Wahl.
PDO ist jedoch auch keine Silberkugel und hat seine Schwierigkeiten.
Also schrieb ich Lösungen für alle gängigen Fallstricke und komplexen Fälle im PDO-Tag-Wiki

Trotzdem fehlen allen, die über Erweiterungen sprechen, immer die 2 wichtigen Fakten über Mysqli und PDO:

  1. Vorbereitete Aussage 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 den ganzen schmutzigen Job des Bindens, Schleifens, der Fehlerbehandlung usw. im Inneren erledigt und den Anwendungscode trocken und sauber macht. Speziell für komplexe Fälle wie das dynamische Erstellen von Abfragen.

Es reicht also nicht aus, nur auf PDO oder mysqli umzusteigen. Man muss einen ORM oder einen Abfrage-Generator oder eine beliebige Datenbankabstraktionsklasse verwenden, anstatt unformatierte API-Funktionen in ihrem Code 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 sind einige Beispiele, die auf meiner safemysql-Klasse basieren, um zu zeigen, wie eine solche Abstraktionsklasse sein 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 der Menge an Code, die Sie mit PDO benötigen .
Vergleichen Sie dann mit der verrückten Menge an Code, die Sie benötigen, mit rohen, von Mysqli vorbereiteten Anweisungen. Beachten Sie, dass die 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']);

Sie können kaum ein Beispiel für PDO finden, um einen solchen praktischen Fall zu behandeln.
Und es wird zu wortreich und höchstwahrscheinlich unsicher sein.

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 aller realen Probleme.


20
mysql_*macht es sehr einfach, Schwachstellen zu finden. Da PHP von vielen Anfängern verwendet wird, mysql_*ist es in der Praxis aktiv schädlich, auch wenn es theoretisch problemlos verwendet werden kann.
Madaras Geist

4
everything is parameterized and safe- Es kann parametrisiert werden, aber Ihre Funktion verwendet keine wirklich vorbereiteten Anweisungen.
uınbɐɥs

6
Wie ist Not under active developmentnur für diese Zusammensetzung "0,01%"? Wenn Sie etwas mit dieser Stand-Still-Funktion erstellen, Ihre MySQL-Version in einem Jahr aktualisieren und mit einem nicht funktionierenden System enden, sind sicher plötzlich sehr viele Leute in diesen '0,01%'. Ich würde das sagen deprecatedund bin not under active developmenteng verwandt. Sie können sagen, dass es "keinen [würdigen] Grund" dafür gibt, aber Tatsache ist, dass die Wahl zwischen den Optionen no active developmentfast genauso schlecht ist, wie deprecatedich sagen würde?
Nanne

1
@ MadaraUchiha: Können Sie erklären, wie leicht Schwachstellen zu finden sind? Besonders in den Fällen, in denen dieselben Sicherheitslücken PDO oder MySQLi nicht betreffen ... Weil mir keine einzige bekannt ist, von der Sie sprechen.
Ircmaxell

4
@ShaquinTrifonoff: Sicher, es werden keine vorbereiteten Anweisungen verwendet. Aber auch PDO , das die meisten Leute über MySQLi empfehlen. Ich bin mir also nicht sicher, ob dies hier einen signifikanten Einfluss hat. Der obige Code (mit etwas mehr Analyse) ist das, was PDO tut, wenn Sie standardmäßig eine Anweisung vorbereiten ...
ircmaxell

97

Es gibt viele Gründe, aber der vielleicht wichtigste ist, dass diese Funktionen unsichere Programmierpraktiken fördern, da sie vorbereitete Anweisungen nicht unterstützen. Vorbereitete Anweisungen verhindern SQL-Injection-Angriffe.

Wenn Sie mysql_*Funktionen verwenden, müssen Sie daran denken, vom Benutzer bereitgestellte Parameter auszuführen mysql_real_escape_string(). Wenn Sie nur an einer Stelle vergessen oder nur einen Teil der Eingabe verlassen, ist Ihre Datenbank möglicherweise einem Angriff ausgesetzt.

Die Verwendung vorbereiteter Anweisungen in PDOoder mysqliwird es so machen, dass diese Art von Programmierfehlern schwieriger zu machen sind.


3
Leider fördert die schlechte Unterstützung in MySQLi_ * für die Übergabe einer variablen Anzahl von Parametern (z. B. wenn Sie eine Liste von Werten übergeben möchten, gegen die in einer IN-Klausel geprüft werden soll) die Nichtverwendung von Parametern und die Verwendung genau derselben verketteten Abfragen wie Lassen Sie MySQL_ * -Anrufe anfällig.
Kickstart

5
Aber auch hier ist Unsicherheit kein inhärentes Problem der mysql_ * -Funktionen, sondern ein Problem der falschen Verwendung.
Agamemnus

2
@Agamemnus Das Problem ist, dass mysql_ * es einfach macht, diese "falsche Verwendung" zu implementieren, insbesondere für unerfahrene Programmierer. Bibliotheken, die vorbereitete Anweisungen implementieren, erschweren es, diese Art von Fehler zu machen.
Trott

75

Weil es (unter anderem) viel schwieriger ist, sicherzustellen, dass die Eingabedaten bereinigt sind. Wenn Sie parametrisierte Abfragen verwenden, wie dies bei PDO oder mysqli der Fall ist, können Sie das Risiko vollständig vermeiden.

Als Beispiel könnte jemand "enhzflep); drop table users"einen Benutzernamen verwenden. Mit den alten Funktionen können mehrere Anweisungen pro Abfrage ausgeführt werden, sodass so etwas wie dieser böse Mistkerl eine ganze Tabelle löschen kann.

Wenn man PDO von mysqli verwenden würde, wäre der Benutzername am Ende "enhzflep); drop table users".

Siehe bobby-tables.com .


10
The old functions will allow executing of multiple statements per query- Nein, das werden sie nicht. Diese Art der Injektion ist mit ext / mysql nicht möglich - diese Art der Injektion ist mit PHP und MySQL nur möglich, wenn MySQLi und die mysqli_multi_query()Funktion verwendet werden. Die Art der Injektion, die mit ext / mysql und nicht entkappten Zeichenfolgen möglich ist ' OR '1' = '1, besteht darin, Daten aus der Datenbank zu extrahieren, auf die nicht zugegriffen werden sollte. In bestimmten Situationen ist es möglich, Unterabfragen einzufügen, es ist jedoch immer noch nicht möglich, die Datenbank auf diese Weise zu ändern.
Dave Random

64

Diese Antwort soll zeigen, wie trivial es ist, schlecht geschriebenen PHP-Benutzervalidierungscode zu umgehen, wie (und was) diese Angriffe funktionieren und wie die alten MySQL-Funktionen durch eine sicher vorbereitete Anweisung ersetzt werden - und im Grunde, warum StackOverflow-Benutzer (wahrscheinlich mit viel Wiederholung) bellen neue Benutzer an, die Fragen stellen, um ihren Code zu verbessern.

Zunächst können Sie diese Test-MySQL-Datenbank erstellen (ich habe meine Vorbereitung aufgerufen):

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)

Nachdem dies erledigt ist, 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 aber, wenn Sie es 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.

Der Benutzer muss ein Login und ein Passwort eingeben, oder?

Genial, geben Sie nicht Folgendes ein:

user: bob
pass: somePass

und einreichen.

Die Ausgabe ist wie folgt:

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

Super! Versuchen wir nun, den tatsächlichen Benutzernamen und das Passwort wie erwartet zu verwenden:

user: Fluffeh
pass: mypass

Tolle! Hi-Fives rundum, der Code hat einen Administrator korrekt verifiziert. Es ist perfekt!

Nicht wirklich. Nehmen wir an, der Benutzer ist eine kluge kleine Person. Nehmen wir an, die Person bin 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 nur erlaubt, Ihren Abschnitt nur für Super-geschützte Administratoren einzugeben, wobei ich einen falschen Benutzernamen und ein falsches Passwort eingegeben habe. Im Ernst, wenn Sie mir nicht glauben, erstellen Sie die Datenbank mit dem von mir bereitgestellten Code und führen Sie diesen PHP-Code aus - der auf den ersten Blick WIRKLICH den Benutzernamen und das Passwort ziemlich gut zu überprüfen scheint.

Als Antwort ist das der Grund, warum Sie angeschrien werden.

Schauen wir uns also an, was schief gelaufen ist und warum ich gerade in Ihre Super-Admin-Only-Bat-Höhle gekommen 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 die Abfrage, die Sie tatsächlich ausgeführt haben, geändert wird. Also, was sollte es sein und was war es am Ende?

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

Das ist die Abfrage, aber wenn wir die Variablen durch die tatsächlich verwendeten Eingaben ersetzen, 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 konstruiert habe, dass es zuerst das einfache Anführungszeichen um das Passwort schließt und dann einen völlig neuen Vergleich einführt? Aus Sicherheitsgründen habe ich dann eine weitere "Zeichenfolge" hinzugefügt, damit das einfache Anführungszeichen wie erwartet in dem Code geschlossen wird, den wir ursprünglich hatten.

Hier geht es jedoch nicht darum, dass Leute Sie jetzt anschreien, sondern darum, Ihnen zu zeigen, wie Sie Ihren Code sicherer machen können.

Okay, was ist schief gelaufen und wie können wir das beheben?

Dies ist ein klassischer SQL-Injection-Angriff. Eine 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 Ihren heiligen Verwaltungsbereich und machen ihn schön und sicher? Das erste, was Sie tun müssen, ist, die Verwendung dieser wirklich alten und veralteten mysql_*Funktionen einzustellen . Ich weiß, Sie sind einem Tutorial gefolgt, das Sie online gefunden haben, und es funktioniert, aber es ist alt, veraltet und innerhalb weniger Minuten habe ich es einfach hinter mich gebracht, ohne auch nur ins Schwitzen zu geraten.

Jetzt haben Sie die besseren Möglichkeiten, mysqli_ oder PDO zu verwenden . Ich persönlich bin ein großer Fan von PDO, daher werde ich PDO im Rest dieser Antwort verwenden. Es gibt Vor- und Nachteile, aber ich persönlich finde, dass die Vor- und Nachteile die Nachteile bei weitem überwiegen. Es ist portabel über mehrere Datenbank-Engines hinweg - egal ob Sie MySQL oder Oracle verwenden oder fast alles - 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.

Schauen wir uns diesen Code noch einmal an, diesmal mit einem PDO-Objekt geschrieben:

<?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 mysql_*Funktionen mehr gibt . Dies geschieht alles über ein PDO-Objekt, zweitens wird eine vorbereitete Anweisung verwendet. Was ist eine vorgefertigte Aussage, die Sie fragen? Auf diese Weise können Sie der Datenbank vor dem Ausführen einer Abfrage mitteilen, welche Abfrage ausgeführt werden soll. In diesem Fall teilen wir der Datenbank mit: "Hallo, ich werde eine select-Anweisung ausführen, die id, userid und pass von den Tabellenbenutzern wünscht, wobei die userid eine Variable und der pass auch eine Variable ist."

Dann übergeben wir in der execute-Anweisung der Datenbank ein Array mit allen Variablen, die sie jetzt erwartet.

Die Ergebnisse sind fantastisch. Versuchen wir noch einmal die Kombinationen aus Benutzername und Passwort:

user: bob
pass: somePass

Benutzer wurde nicht verifiziert. Genial.

Wie wäre es mit:

user: Fluffeh
pass: mypass

Oh, ich war nur ein bisschen aufgeregt, es hat funktioniert: Der Scheck hat bestanden. Wir haben einen verifizierten Administrator!

Versuchen wir nun die Daten, die ein kluger Kerl eingeben würde, um an unserem kleinen Verifizierungssystem vorbeizukommen:

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

Dieses Mal erhalten wir Folgendes:

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

Dies ist der Grund, warum Sie beim Posten von Fragen angeschrien werden - weil die Leute sehen können, dass Ihr Code umgangen werden kann, ohne es zu versuchen. Verwenden Sie diese Frage und Antwort, um Ihren Code zu verbessern, ihn sicherer zu machen und aktuelle Funktionen zu verwenden.

Schließlich soll dies nicht heißen, dass dies PERFEKTER Code ist. Es gibt noch viel mehr Dinge, die Sie tun können, um es zu verbessern. Verwenden Sie beispielsweise Hash-Passwörter. Stellen Sie beispielsweise sicher, dass Sie beim Speichern sensibler Informationen in der Datenbank diese nicht im Klartext speichern und über mehrere Überprüfungsebenen verfügen - aber wirklich, wenn Wenn Sie nur Ihren alten Code ändern, der zu Injektionen neigt, werden Sie auf dem Weg zum Schreiben von gutem Code GUT sein - und die Tatsache, dass Sie so weit gekommen sind und noch lesen, gibt mir ein Gefühl der Hoffnung, dass Sie diesen Typ nicht nur implementieren werden von Code beim Schreiben Ihrer Websites und Anwendungen, aber dass Sie die anderen Dinge, die ich gerade erwähnt habe, recherchieren könnten - und mehr. Schreiben Sie den bestmöglichen Code, nicht den grundlegendsten Code, der kaum funktioniert.


2
Vielen Dank für Ihre Antwort! Habe meine +1! Es ist erwähnenswert, dass mysql_*an sich nicht unsicher ist, aber unsicheren Code durch schlechte Tutorials und das Fehlen einer geeigneten API zur Vorbereitung von Anweisungen fördert.
Madaras Geist

2
ungehackte Passwörter, oh der Schrecken! = oP Andernfalls +1 für eine detaillierte Erklärung.
kryptisch ツ

33

Die MySQL-Erweiterung ist die älteste der drei und war die ursprüngliche Art und Weise, wie Entwickler mit MySQL kommunizierten. Diese Erweiterung wird jetzt zugunsten der beiden anderen Alternativen aufgrund von Verbesserungen in neueren Versionen von PHP und MySQL abgelehnt .

  • MySQLi ist die 'verbesserte' Erweiterung für die Arbeit mit MySQL-Datenbanken. Es nutzt die 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 einige andere nützliche Dinge.

  • PDO bietet eine API, die die meisten Funktionen konsolidiert, die zuvor auf die wichtigsten Datenbankzugriffserweiterungen verteilt waren, z. B. MySQL, PostgreSQL, SQLite, MSSQL usw. Die Schnittstelle stellt dem Programmierer Objekte auf hoher Ebene zur Verfügung, um mit Datenbankverbindungen, Abfragen und zu arbeiten Ergebnismengen und Treiber auf niedriger Ebene führen die Kommunikation und das Ressourcenhandling mit dem Datenbankserver durch. In PDO wird viel diskutiert und gearbeitet, und es wird als geeignete Methode für die Arbeit mit Datenbanken in modernem, professionellem Code angesehen.


21

Ich finde die obigen Antworten sehr lang, um es zusammenzufassen:

Die mysqli-Erweiterung bietet eine Reihe von Vorteilen. Die wichtigsten Verbesserungen gegenüber der mysql-Erweiterung sind:

  • Objektorientierte Schnittstelle
  • Unterstützung für vorbereitete Anweisungen
  • Unterstützung für mehrere Anweisungen
  • Unterstützung für Transaktionen
  • Erweiterte Debugging-Funktionen
  • Unterstützung für eingebettete Server

Quelle: MySQLi-Übersicht


Wie in den obigen Antworten erläutert, sind die Alternativen zu mysql mysqli und PDO (PHP Data Objects).

  • API unterstützt serverseitige vorbereitete Anweisungen: Unterstützt von MYSQLi und PDO
  • API unterstützt clientseitige vorbereitete Anweisungen: Wird nur von PDO unterstützt
  • API unterstützt gespeicherte Prozeduren: Sowohl MySQLi als auch PDO
  • API unterstützt mehrere Anweisungen und alle MySQL 4.1+ -Funktionen - Unterstützt von MySQLi und meistens auch von PDO

Sowohl MySQLi als auch 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 veraltet ist.


2
Ihre Antwort ist zu lang, während die eigentliche Zusammenfassung "mysql ext is no more" lautet. Das ist alles
Ihr gesunder Menschenverstand

1
@YourCommonSense Meine Antwort lautet, warum mysqli mysql ersetzt hat. Es geht nicht darum zu sagen, dass Mysqli heute existiert, also benutze es. Jeder weiß das!
Ani Menon

1
Abgesehen von der Tatsache, dass niemand gefragt hat, warum mysqli mysql ersetzt hat, beantwortet es diese Frage auch nicht. Es antwortet, warum mysqli eingeführt wurde. Aber es erklärt nicht, warum MySQL und Mysqli nicht parallel leben durften
Ihr

@YourCommonSense Auch die Frage des OP lautet: "Warum sollte ich etwas anderes verwenden, auch wenn es auf meiner Website funktioniert?" und das ist der Grund, warum ich auf die Änderungen und Verbesserungen hingewiesen habe. Sie können sich alle anderen Antworten ansehen, die lang sind, also dachte ich, ich sollte es zusammenfassen.
Ani Menon

6

Es ist möglich, fast alle mysql_*Funktionen mit mysqli oder PDO zu definieren . Fügen Sie sie einfach über Ihre alte PHP-Anwendung ein, und es funktioniert unter PHP7. 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;
}

Anstatt den Link für Ihre Lösung anzuzeigen, fügen Sie ihn bitte hier als Antwort hinzu.
Amarnath

1

Die Funktionen, die diesem Typ ähnlich sind mysql_connect(), mysql_query()sind die PHP-Funktionen der Vorgängerversion (dh PHP 4) und werden jetzt nicht verwendet.

Diese werden ersetzt durch mysqli_connect(), mysqli_query()ähnlich in der neuesten PHP5.

Dies ist der Grund für den Fehler.


2
PHP 5 ist seit über 2 Jahren nicht mehr aktuell.
Madaras Geist

1

MySQL ist in PHP 5.5.0 veraltet und in PHP 7.0.0 entfernt worden. Für eine große und alte Anwendung ist es schwierig, jede Funktion zu suchen und zu ersetzen.

Wir können MySQL-Funktionen verwenden, indem wir eine Wrapper-Funktion für jeden der folgenden Code erstellen. Klick hier


-9

mysql_ * -Funktionen waren (ab PHP 5.5 ) veraltet , da bessere Funktionen und Codestrukturen entwickelt wurden. Die Tatsache, dass die Funktion veraltet war, bedeutet, dass keine weiteren Anstrengungen unternommen werden, um sie in Bezug auf Leistung und Sicherheit zu verbessern, was bedeutet, dass sie weniger zukunftssicher ist .

Wenn Sie weitere Gründe benötigen:

  • mysql_ * -Funktionen unterstützen keine vorbereiteten Anweisungen.
  • mysql_ * -Funktionen unterstützen die Bindung von Parametern nicht.
  • mysql_ * -Funktionen haben keine Funktionalität für die objektorientierte Programmierung.
  • Die Liste geht weiter ...

18
Diese Antwort ist veraltet. Außerdem fügt es den bereits vorhandenen Antworten nichts Nützliches hinzu.
Ihr gesunder Menschenverstand
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.