Frage Warum sollte ich nicht mysql_ * -Funktionen in PHP verwenden?


Aus welchen technischen Gründen sollte man nicht verwenden? mysql_* Funktionen? (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, warum bekomme ich Fehler wie

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


2194
2017-10-12 13:18


Ursprung


Antworten:


Die MySQL-Erweiterung:

  • Ist nicht unter aktiver Entwicklung
  • Ist offiziell veraltet ab PHP 5.5 (veröffentlicht im Juni 2013).
  • Ist gewesen entfernt vollständig ab PHP 7.0 (veröffentlicht im Dezember 2015)
    • Dies bedeutet, dass ab 31. Dezember 2018 Es wird in keiner unterstützten PHP-Version existieren. Derzeit wird es nur bekommen Sicherheit Aktualisierung.
  • Fehlt eine OO-Schnittstelle
  • Unterstützt nicht:
    • Nicht blockierende asynchrone Abfragen
    • Vorbereitete Aussagen oder parametrisierte Abfragen
    • Gespeicherte Prozeduren
    • Mehrere Anweisungen
    • Transaktionen
    • Die "neue" Passwort-Authentifizierungsmethode (standardmäßig in MySQL 5.6; in 5.7 erforderlich)
    • Alle Funktionen in MySQL 5.1

Da es veraltet ist, macht die Verwendung dieses Codes Ihren Code weniger zukunftssicher.

Mangelnde Unterstützung für vorbereitete Anweisungen ist besonders wichtig, da sie eine klarere, weniger fehleranfällige Methode zum Ausschließen und Angeben externer Daten bieten, als sie manuell mit einem separaten Funktionsaufruf zu umgehen.

Sehen der Vergleich von SQL-Erweiterungen.


1808
2018-01-01 11:52



PHP bietet drei verschiedene APIs zur Verbindung mit MySQL. Dies sind die mysql(entfernt von PHP 7), mysqli, und PDO Erweiterungen.

Das mysql_* Funktionen waren früher sehr beliebt, aber ihre Verwendung wird nicht mehr empfohlen. Das Dokumentations-Team diskutiert die Situation der Datenbank-Sicherheit, und die Schulung von Benutzern, um von der häufig verwendeten ext / mysql-Erweiterung wegzukommen, ist ein Teil davon php.internals: verwerfen ext / mysql).

Und das spätere PHP-Entwicklerteam hat die Entscheidung getroffen zu generieren E_DEPRECATED Fehler, wenn Benutzer eine Verbindung zu MySQL herstellen, sei es durch mysql_connect(), mysql_pconnect() oder die eingebaute implizite Verbindungsfunktionalität ext/mysql.

ext/mysql war offiziell ab PHP 5.5 veraltet und war es ab PHP 7 entfernt.

Siehst du die Red Box?

Wenn du irgendwas machst mysql_* Handbuchseite, sehen Sie eine rote Box, die erklärt, dass sie nicht mehr benutzt werden sollte.

Warum


Weg von ext/mysql Es geht nicht nur um Sicherheit, sondern auch darum, Zugang zu allen Funktionen der MySQL-Datenbank zu haben.

ext/mysql wurde für gebaut MySQL 3.23 und seither wurden nur sehr wenige Erweiterungen hinzugefügt, während die Kompatibilität mit dieser alten Version beibehalten wurde, wodurch der Code etwas schwieriger zu pflegen ist. Fehlende Funktionen, die von nicht unterstützt werden ext/mysql enthalten: (aus PHP Handbuch).

Grund nicht zu verwenden mysql_* Funktion:

  • Nicht in aktiver Entwicklung
  • Entfernt seit PHP 7
  • Fehlt eine OO-Schnittstelle
  • Unterstützt nicht blockierende asynchrone Abfragen nicht
  • Unterstützt vorbereitete 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

Der oben zitierte Punkt aus Quentins Antwort

Mangelnde Unterstützung für vorbereitete Anweisungen ist besonders wichtig, da sie eine klarere, weniger fehleranfällige Methode zum Entkommen und Angeben externer Daten bieten, als sie manuell mit einem separaten Funktionsaufruf zu umgehen.

Siehe die Vergleich von SQL-Erweiterungen.


Unterdrückungswarnungen werden unterdrückt

Während Code in konvertiert wird MySQLi/PDO, E_DEPRECATED Fehler können durch Einstellung unterdrückt werden error_reporting im php.ini ausschließen E_DEPRECATED:

error_reporting = E_ALL ^ E_DEPRECATED

Beachten Sie, dass dies auch ausgeblendet wird andere Verwarnungswarnungen, die jedoch für andere Dinge als MySQL gedacht sein können. (aus PHP Handbuch)

Der Artikel PDO vs. MySQLi: Was sollten Sie verwenden? durch Dejan Marjanovic wird dir helfen zu wählen.

Und ein besserer Weg ist PDOund ich schreibe jetzt ein einfaches PDO Anleitung.


Ein einfaches und kurzes PDO-Tutorial


Frage: Meine erste Frage war: Was ist "PDO"?

EIN. "PDO - PHP Datenobjekte - ist eine Datenbankzugriffsebene, die eine einheitliche Zugriffsmethode für 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: Alles, was Sie tun müssen, ist ein neues erstellen PDO Objekt. Der Konstruktor akzeptiert Parameter zum Angeben der Datenbankquelle PDODer Konstruktor nimmt meistens vier Parameter an DSN (Datenquellenname) und optional username, password.

Hier denke ich, dass Sie mit allem außer vertraut sind DSN; das ist neu in PDO. EIN DSN ist im Grunde eine Reihe von Optionen, die erzählen PDO welcher Treiber zu verwenden ist, und Verbindungsdetails. Als weitere Referenz überprüfen 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, also ist es besser zu verwenden utf8.

Wenn ein Verbindungsfehler vorliegt, wird ein Fehler ausgelöst PDOException Objekt, das gefangen werden kann Exception des Weiteren.

Gut gelesen: Verbindungen und Verbindungsverwaltung ¶ 

Sie können auch mehrere Treiberoptionen als Array an den vierten Parameter übergeben. Ich empfehle, den Parameter zu übergeben, der setzt PDO in den Ausnahme-Modus. Weil einige PDO Treiber unterstützen native Prepared Statements nicht PDO führt die Emulation der Vorbereitung durch. Sie können diese Emulation auch manuell aktivieren. Um die nativen serverseitigen vorbereiteten Anweisungen zu verwenden, sollten Sie sie explizit festlegen false.

Die andere Möglichkeit besteht darin, die Vorbereitungsemulation abzuschalten, die in der MySQLTreiber standardmäßig, aber Vorbereitung Emulation sollte deaktiviert sein, um zu verwenden PDO sicher.

Ich werde später erklären, warum Vorbereitung Emulation ausgeschaltet werden sollte. Um den Grund zu finden, überprüfen Sie bitte dieser Beitrag.

Es ist nur verwendbar, wenn Sie eine alte Version von MySQL was ich nicht empfehle.

Im Folgenden finden Sie ein Beispiel dafür, wie Sie es 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, können wir auch einige Attribute nach PDO - Konstruktion mit der setAttribute Methode:

$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 viel einfacher in PDO als mysql_*.

Eine übliche 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 mit dem Fehler umzugehen, da wir mit der Sache nicht umgehen können die. Es wird nur das Skript abrupt beenden und dann den Fehler auf dem Bildschirm wiedergeben, den Sie Ihren Endbenutzern normalerweise NICHT zeigen möchten, und blutige Hacker Ihr Schema entdecken lassen. Alternativ können die Rückgabewerte von mysql_* Funktionen können oft in Verbindung mit verwendet werden MySQL-Fehler() um Fehler zu behandeln.

PDO bietet eine bessere Lösung: Ausnahmen. Alles, was wir machen PDO sollte in a eingewickelt werden try-catch Block. Wir können es erzwingen PDO in einen von drei Fehlermodi durch Setzen des Fehlermodusattributs. Drei Fehlerbehandlungsmodi sind unten.

  • PDO::ERRMODE_SILENT. Es setzt nur Fehlercodes und verhält sich genauso mysql_* wo Sie jedes Ergebnis überprüfen und dann anschauen müssen $db->errorInfo(); um die Fehlerdetails zu erhalten.
  • PDO::ERRMODE_WARNING Erziehen 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 verursacht wird. Du solltest keinen werfen PDOException von deinem eigenen Code. Sehen Ausnahmen Weitere Informationen zu Ausnahmen in PHP Es verhält sich sehr ähnlich or die(mysql_error());wenn es nicht gefangen wird. Aber nicht wie or die(), das PDOException Wenn Sie sich dafür entscheiden, können Sie elegant gefangen und behandelt werden.

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-catch, Wie 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-catch jetzt sofort. Sie können es jederzeit angemessen finden, aber ich empfehle Ihnen dringend, es zu benutzen try-catch. Es kann auch sinnvoller sein, es außerhalb der Funktion zu finden, die den Aufruf aufruft PDO Sachen:

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

Außerdem können Sie 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 durch Drehen ausblenden display_errors off und nur dein Fehlerprotokoll zu lesen.

Jetzt, nachdem du all die obigen Dinge gelesen hast, denkst du wahrscheinlich: Was zum Teufel ist das, wenn ich einfach nur anfangen will zu lehnen SELECT, INSERT, UPDATE, oder DELETE Aussagen? Mach dir keine Sorgen, hier gehen wir:


Daten auswählen

PDO select image

Also, was machst du? 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 PDODu kannst das wie folgt machen:

<?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 folgt verwenden (query()), gibt diese Methode a zurück PDOStatement Objekt. 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 erhalten ->fetch(), eine Methode Ihrer Anweisung behandeln. Bevor Sie fetch aufrufen, sollten Sie PDO am besten mitteilen, wie die Daten abgerufen werden sollen. Im folgenden Abschnitt erkläre ich dies.

Abrufmodi

Beachten Sie die Verwendung von PDO::FETCH_ASSOC in dem fetch() und fetchAll() Code oben. Das sagt PDO um die Zeilen als assoziatives Array mit den Feldnamen als Schlüssel zurückzugeben. Es gibt viele andere Abrufmodi, die ich einzeln erklären werde.

Zunächst erkläre ich, wie man den Fetch-Modus auswählt:

 $stmt->fetch(PDO::FETCH_ASSOC)

In den oben genannten 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 in der Ergebnismenge zurückgegebenen Spaltennamen indiziert ist
  • PDO::FETCH_BOTH (Standard): Gibt ein Array zurück, das sowohl nach dem Spaltennamen als auch nach der Nummer der indexierten Spalte indexiert ist, die in der Ergebnismenge zurückgegeben wurde

Es gibt noch mehr Auswahlmöglichkeiten! Lies über sie alle in PDOStatement Holen Sie sich die Dokumentation..

Die Anzahl der Zeilen ermitteln:

Anstatt zu verwenden mysql_num_rows Um die Anzahl der zurückgegebenen Zeilen zu erhalten, können Sie eine PDOStatement und TU rowCount(), mögen:

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

Einfügen und Aktualisieren oder Löschen von Anweisungen

Insert and update PDO image

Was machen wir? mysql_* Funktion ist:

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

Und in PDO kann das gleiche getan werden durch:

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

In der obigen Abfrage PDO::exec Führt eine SQL-Anweisung aus und gibt 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. Aber wenn Sie eine Variable in einer Abfrage verwenden müssen, versuchen Sie nie wie oben und dort für vorbereitete Anweisung oder parametrisierte Anweisung ist.


Vorbereitete Aussagen

Q. Was ist eine vorbereitete Aussage und warum brauche ich sie?
EIN. Eine vorbereitete Anweisung ist eine vorkompilierte SQL-Anweisung, die mehrfach 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 Punkt):

  1. Bereiten: Die Anweisungsvorlage wird von der Anwendung erstellt und an das Datenbankverwaltungssystem (DBMS) gesendet. Bestimmte Werte werden nicht angegeben, Parameter, Platzhalter oder Bind - Variablen (mit der Bezeichnung ? 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 die Anwendung Werte für die Parameter (oder bindet sie), und das DBMS führt die Anweisung aus (möglicherweise gibt es ein Ergebnis zurück). Die Anwendung kann die Anweisung beliebig oft mit unterschiedlichen Werten ausführen. In diesem Beispiel könnte es "Brot" für den ersten Parameter und liefern 1.00für den zweiten Parameter.

Sie können eine vorbereitete Anweisung verwenden, indem Sie Platzhalter in Ihre SQL einfügen. Es gibt im Wesentlichen drei ohne Platzhalter (versuchen Sie es nicht mit der Variablen oben), einen mit unbenannten Platzhaltern und einen mit benannten Platzhaltern.

Q. Also, was sind Platzhalter und wie benutze ich sie?
EIN. Benannte Platzhalter Verwenden Sie beschreibende Namen, denen anstelle von Fragezeichen ein Doppelpunkt vorangestellt ist. Die Position / Reihenfolge des Wertes im Namens-Platzhalter ist uns egal:

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

Ein weiteres nettes Feature für OOP friends besteht darin, dass benannte Platzhalter die Möglichkeit haben, Objekte direkt in Ihre Datenbank einzufügen, vorausgesetzt, die Eigenschaften stimmen mit den benannten Feldern überein. Beispielsweise:

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 nun namenlose Platzhalter und wie benutze ich sie?
EIN. Nehmen wir ein Beispiel:

<?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 können Sie diese sehen ? anstelle eines Namens wie in einem Namen Platzhalter. 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 die Sekunde zu der Sekunde ?.

HINWEIS: Im unbenannte Platzhalter wir müssen auf die richtige Reihenfolge der Elemente in der Anordnung achten, die wir an die PDOStatement::execute() Methode.


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:

jedoch PDO und / oder MySQLi sind nicht völlig sicher. Überprüfen Sie die Antwort Sind PDO-vorbereitete Anweisungen ausreichend, um eine SQL-Injektion zu verhindern? durch ircmaxell. Ich zitiere auch 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 /*"));

1160
2017-10-12 13:28



Lassen Sie uns zunächst mit dem Standardkommentar beginnen, den wir allen geben:

Bitte, nicht benutzen mysql_* Funktionen in neuem Code. Sie werden nicht mehr gepflegt und sind offiziell veraltet. Siehe die rote Box? Lernen vorbereitete Aussagen stattdessen und verwenden PDO oder MySQLi - Dieser Artikel wird Ihnen helfen zu entscheiden, welche. Wenn Sie PDO wählen, Hier ist ein gutes Tutorial.

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

  • Sie werden nicht länger gewartet und sind offiziell veraltet

    Dies bedeutet, dass die PHP-Community nach und nach die Unterstützung für diese sehr alten Funktionen einstellt. Sie werden wahrscheinlich nicht in einer zukünftigen (aktuellen) Version von PHP existieren! Die fortgesetzte Nutzung dieser Funktionen kann Ihren Code in der (nicht so) fernen Zukunft brechen.

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

    Neuere! ext / mysql wurde in PHP 7 entfernt.

  • Stattdessen sollten Sie von vorbereiteten Aussagen lernen

    mysql_* Erweiterung unterstützt nicht vorbereitete Aussagen, was (neben anderen Dingen) eine sehr wirksame Gegenmaßnahme darstellt SQL-Injektion. Es wurde eine sehr ernste Schwachstelle in MySQL-abhängigen Anwendungen behoben, die es Angreifern ermöglicht, auf Ihr Skript zuzugreifen und es auszuführen jede mögliche Abfrage auf deiner Datenbank.

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

  • Siehst du die Red Box?

    Wenn Sie zu irgendeinem gehen mysql Handbuchseite, sehen Sie eine rote Box, die erklärt, dass sie nicht mehr benutzt werden sollte.

  • Verwenden Sie entweder PDO oder MySQLi

    Es gibt bessere, robustere und gut gebaute Alternativen, PDO - PHP Datenbankobjekt, die einen vollständigen OOP - Ansatz für die Datenbankinteraktion bietet, und MySQLiDies ist eine MySQL-spezifische Verbesserung.


279
2017-12-24 23:30



Benutzerfreundlichkeit

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

Zeitgenössische Datenbank-APIs sind einfach einfacher benutzen.

Es ist meistens die gebundene Parameter das kann den Code vereinfachen. Und mit hervorragende Tutorials (wie oben zu sehen) der Übergang zu PDO ist nicht übermäßig anstrengend.

Das Umschreiben einer größeren Codebasis auf einmal erfordert jedoch Zeit. Raison d'être für diese Zwischenalternative:

Äquivalent pdo_ * funktioniert anstelle von mysql_ *

Verwenden <pdo_mysql.php> Sie können von den alten mysql_ Funktionen mit wechseln minimaler Aufwand. Es fügt hinzu pdo_ Funktionswrapper, die ihre ersetzen mysql_ Gegenstücke.

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

  2. Entferne das mysql_ Funktionspräfix überall und ersetze es mit 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 wird gleich funktionieren und immer noch größtenteils gleich aussehen:

    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à.
Dein Code ist verwenden PDO.
Jetzt ist es Zeit, tatsächlich nutzen es.

Gebundene Parameter können einfach zu verwenden sein

Sie brauchen nur eine weniger sperrige API.

pdo_query() fügt eine sehr einfache Unterstützung für gebundene Parameter hinzu. Die Konvertierung alten Codes ist einfach:

Verschieben Sie Ihre Variablen aus der SQL-Zeichenfolge.

  • Fügen Sie sie als kommagetrennte Funktionsparameter hinzu pdo_query().
  • Platziere Fragezeichen ? als Platzhalter, wo die Variablen vorher waren.
  • Beseitigen, abschütteln ' einfache Anführungszeichen, die zuvor Zeichenfolgenwerte / Variablen eingeschlossen haben.

Der Vorteil wird für längeren Code offensichtlich.

Oft werden String-Variablen nicht nur in SQL interpoliert, sondern auch mit dazwischenliegenden Escapes 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 ? Platzhalter haben sich darum gekümmert:

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_ * noch erlaubt entweder oder.
Nur nicht eine Variable entkommen und Binden Sie es in derselben Abfrage ein.

  • Das Platzhalter-Feature wird von dem echten PDO dahinter bereitgestellt.
  • Also auch erlaubt :named Platzhalterlisten später.

Noch wichtiger ist, dass Sie $ _REQUEST [] - Variablen sicher hinter jeder Abfrage übergeben können. Wenn gesendet <form> Felder passen genau zur Datenbankstruktur, es ist noch kürzer:

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

So viel Einfachheit. Aber lassen Sie uns zurück zu einigen mehr umschreiben Ratschläge und technische Gründe, warum Sie vielleicht loswerden wollen mysql_und entkommen.

Repariere oder entferne eine alte Schule sanitize() Funktion

Sobald Sie alle konvertiert haben mysql_ ruft an pdo_query mit gebundenen Params, entfernen Sie alle redundanten pdo_real_escape_string Anrufe.

Insbesondere solltest du irgendwas reparieren sanitize oder clean oder filterThis oder clean_data Funktionen, wie sie von datierten Tutorials in der einen oder anderen Form angeboten werden:

function sanitize($str) {
   return trim(strip_tags(htmlentities(pdo_real_escape_string($str))));
}

Der größte Fehler hier ist der Mangel an Dokumentation. Bedeutender war die Reihenfolge der Filterung in genau der falschen Reihenfolge.

  • Die richtige Reihenfolge wäre: veraltet stripslashes als der innerste Anruf, dann trim, danach strip_tags, htmlentities für den Ausgabekontext und nur zuletzt _escape_string da seine Anwendung direkt dem SQL-Intersparsing vorausgehen sollte.

  • Aber als erster Schritt nur loswerden _real_escape_string Anruf.

  • Möglicherweise müssen Sie den Rest Ihrer behalten sanitize() Funktion für den Moment, wenn Ihre Datenbank und der Anwendungsfluss HTML-Kontext-sichere Zeichenfolgen erwarten. Fügen Sie einen Kommentar hinzu, dass nur HTML-Code von jetzt an angewendet wird.

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

  • Wenn es eine Erwähnung von gab stripslashes() In Ihrer Sanitize-Funktion kann dies auf eine höhere Aufsicht hinweisen.

    • Das war gewöhnlich dort, um Schaden (doppeltes Entkommen) von veraltetem wieder aufzuheben magic_quotes. Was aber ist am besten zentral fixiert, nicht String für String.

    • Verwenden Sie eines der Benutzerlandumkehr Ansätze. Dann entferne die stripslashes() in dem sanitize Funktion.

    Historische Anmerkung zu magic_quotes. Diese Funktion ist zu Recht veraltet. Es wird oft fälschlicherweise als gescheitert dargestellt Sicherheit Feature jedoch. Aber magic_quotes sind ebenso ein gescheitertes Sicherheitsmerkmal wie Tennisbälle als Nahrungsquelle versagt haben. Das war einfach nicht ihre Absicht.

    Die ursprüngliche Implementierung in PHP2 / FI hat sie explizit mit nur "Anführungszeichen werden automatisch maskiert, wodurch es einfacher wird, Formulardaten direkt an MSQL-Abfragen zu übergeben". Es war besonders unbedenklich mit zu benutzen mSQL, da nur ASCII unterstützt wird.
      Dann hat PHP3 / Zend magic_quotes für MySQL wieder eingeführt und es falsch dokumentiert. Aber ursprünglich war es nur ein Bequemlichkeitsmerkmal, nicht für die Sicherheit vorhaben.

Wie vorbereitete Aussagen unterscheiden sich

Wenn Sie String-Variablen in SQL-Abfragen verschlüsseln, wird es nicht nur komplizierter für Sie zu folgen. Es ist auch ein zusätzlicher Aufwand für MySQL, Code und Daten wieder zu trennen.

SQL-Injektionen sind einfach wann Daten bluten in Code Kontext. Ein Datenbankserver kann später nicht erkennen, wo PHP ursprünglich Variablen zwischen Abfrageklauseln verklebt hat.

Mit gebundenen Parametern trennen Sie SQL-Code und SQL-Kontextwerte in Ihrem PHP-Code. Aber es wird nicht hinter den Kulissen neu gemischt (außer mit 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 Lesbarkeit Vorteile des Löschens kümmern sollten mysql_. Durch diese sichtbare und technische Daten / Code-Trennung ergibt sich gelegentlich auch ein Leistungsvorteil (wiederholte INSERTs mit nur unterschiedlichen Werten).

Pass auf, dass die Parameterbindung immer noch keine magische Lösung ist alle SQL-Injektionen. Es behandelt die häufigste Verwendung für Daten / Werte. Sie können jedoch Spaltennamen / Tabellenbezeichner nicht auflisten, Hilfe bei der Konstruktion dynamischer Klauseln oder einfach nur Array-Wertelisten.

Hybrid PDO verwenden

Diese pdo_* Wrapper-Funktionen machen eine coding-freundliche Stop-Gap-API. (Es ist so ziemlich was MYSQLI hätte sein können, wenn es nicht für die idiosynkratische Signatur der Funktionssignatur gewesen wäre). Sie legen auch das echte PDO zu den meisten Zeiten frei.
Das Umschreiben muss nicht bei der Verwendung der neuen pdo_-Funktionsnamen stehen bleiben. Sie könnten nacheinander pdo_query () in einen einfachen $ pdo-> prepare () -> execute () - Aufruf überführen.

Es ist jedoch am besten, die Vereinfachung noch einmal zu beginnen. Zum Beispiel das gemeinsame Ergebnis holen:

$result = pdo_query("SELECT * FROM tbl");
while ($row = pdo_fetch_assoc($result)) {

Kann mit nur einer foreach Iteration ersetzt werden:

foreach ($result as $row) {

Oder besser noch 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

Also das hoffentlich visualisiert manches praktisch Gründe und ein wertvoller Weg zu fallen mysql_.

Einfach zu wechseln  schneidet es nicht ganz ab. pdo_query() ist auch nur ein Frontend darauf.

Wenn Sie nicht auch die Parameterbindung einführen oder etwas anderes aus der netteren API verwenden können, ist dies ein sinnloser Wechsel. Ich hoffe, dass es einfach genug dargestellt wird, um die Entmutigung für Neuankömmlinge nicht zu fördern. (Bildung funktioniert normalerweise besser als Verbot.)

Während es sich für die Kategorie "am einfachsten-dass-könnte-möglicherweise-arbeiten" qualifiziert, ist es auch immer noch sehr experimenteller Code. Ich habe es gerade über das Wochenende geschrieben. Es gibt jedoch eine Fülle von Alternativen. Einfach googeln PHP-Datenbankabstraktion und browse ein wenig. Für solche Aufgaben gab und gibt es immer ausgezeichnete Bibliotheken.

Wenn Sie Ihre Datenbankinteraktion weiter vereinfachen möchten, mögen Mapper Paris / Idiorm sind einen Versuch wert. So wie niemand mehr das fade DOM in JavaScript verwendet, müssen Sie heutzutage keine rohe Datenbankschnittstelle babysitten.


200
2017-10-12 13:22



Das mysql_ Funktionen:

  1. sind veraltet - sie werden nicht mehr gepflegt
  2. Erlauben Sie Ihnen nicht, sich leicht in ein anderes Datenbank-Backend zu bewegen
  3. unterstützen Sie daher keine vorbereiteten Aussagen
  4. ermutigen Programmierer, Verkettungen zu verwenden, um Abfragen zu erstellen, was zu SQL-Injection-Schwachstellen führt

134
2018-01-01 17:42



Apropos technisch Gründe gibt es nur wenige, sehr spezifisch und selten genutzt. Höchstwahrscheinlich wirst du sie niemals in deinem Leben benutzen.
Vielleicht bin ich zu ignorant, aber ich hatte nie die Gelegenheit, sie zu benutzen

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

Wenn Sie sie brauchen - das sind zweifellos technische Gründe, sich von der mysql-Erweiterung weg zu etwas Stilvollem und Modernem zu bewegen.

Trotzdem gibt es auch einige nicht-technische Probleme, die Ihre Erfahrung etwas erschweren können

  • Die weitere Verwendung dieser Funktionen mit modernen PHP-Versionen führt zu Benachrichtigungen auf veralteter Ebene. Sie können einfach ausgeschaltet werden.
  • in einer fernen Zukunft können sie möglicherweise aus dem Standard-PHP-Build entfernt werden. Das ist auch keine große Sache, da mydsql ext in PECL verschoben wird und jeder Hoster gerne PHP damit kompiliert, da sie keine Kunden verlieren wollen, deren Seiten seit Jahrzehnten arbeiten.
  • starker Widerstand der Stackoverflow-Community. Wann immer Sie diese ehrlichen Funktionen erwähnen, wird Ihnen gesagt, dass sie unter strengem Tabu stehen.
  • Da es sich um einen durchschnittlichen PHP-Benutzer handelt, ist Ihre Idee, diese Funktionen zu verwenden, wahrscheinlich fehleranfällig und falsch. Gerade wegen all dieser zahlreichen Tutorials und Handbücher, die dir den falschen Weg zeigen. Nicht die Funktionen selbst - ich muss es betonen - sondern die Art, wie sie benutzt werden.

Letzteres Problem ist ein Problem.
Aber meiner Meinung nach ist die vorgeschlagene Lösung auch nicht besser.
Es kommt mir vor zu idealistisch Ein Traum, dass all diese PHP-Benutzer lernen, wie man SQL-Abfragen gleichzeitig richtig behandelt. Höchstwahrscheinlich würden sie nur mysql_ * zu mysqli_ * mechanisch ändern, den Ansatz gleich lassen. Vor allem, weil mysqli vorbereitete Aussagen unglaublich schmerzhaft und mühsam macht.
Ganz zu schweigen davon einheimisch vorbereitete Aussagen sind nicht genug zu schützen von SQL-Injektionen, und weder mysqli noch PDO bietet eine Lösung.

Anstatt diese ehrliche Erweiterung zu bekämpfen, würde ich lieber falsche Praktiken bekämpfen und die Menschen auf die richtige Art und Weise ausbilden.

Außerdem gibt es einige falsche oder nicht signifikante Gründe, wie

  • Stored Procedures werden von uns nicht unterstützt mysql_query("CALL my_proc"); seit Ewigkeiten)
  • Unterstützt keine Transaktionen (wie oben)
  • Unterstützt keine Mehrfachanweisungen (wer braucht sie?)
  • Nicht in aktiver Entwicklung (was also? Beeinflusst es? Sie in irgendeiner praktischen Weise?)
  • Fehlt eine OO-Schnittstelle (um eine zu erstellen, ist eine Angelegenheit von mehreren Stunden)
  • Vorbereitete Anweisungen oder parametrisierte Abfragen werden nicht unterstützt

Der letzte ist ein interessanter Punkt. Obwohl mysql ext nicht unterstützt einheimisch vorbereitete Aussagen, sie sind für die Sicherheit nicht erforderlich. Wir können vorbereitete Anweisungen mit manuell gehandhabten Platzhaltern fälschen (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);

voilaAlles ist parametrisiert und sicher.

Aber okay, wenn Sie die rote Box im Handbuch nicht mögen, entsteht ein Problem der Wahl: mysqli oder PDO?

Nun, die Antwort wäre wie folgt:

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

    • Im Gegensatz zu Mysqli kann PDO Platzhalter binden nach WertDies macht dynamisch aufgebaute Abfragen ohne mehrere Bildschirme mit ziemlich unordentlichem Code möglich.
    • Anders als bei mysqli kann PDO das Abfrageergebnis immer in einem einfachen gewöhnlichen Array zurückgeben, während mysqli es nur bei mysqlnd-Installationen tun kann.

Also, wenn Sie ein durchschnittlicher PHP-Benutzer sind und sich eine Menge Kopfschmerzen ersparen wollen, wenn Sie native Prepared Statements verwenden, ist PDO - wiederum - die einzige Wahl.
PDO ist jedoch auch keine Wunderwaffe und hat seine Schwierigkeiten.
Also habe ich Lösungen für alle häufigen Fallstricke und komplexen Fälle in der PDO-Tag-Wiki

Trotzdem, jeder der über Erweiterungen spricht, vermisst immer die 2 wichtige Fakten über Mysqli und PDO:

  1. Vorbereitete Aussage ist keine Wunderwaffe. Es gibt dynamische Bezeichner, die nicht mit vorbereiteten Anweisungen gebunden werden können. Es gibt dynamische Abfragen mit einer unbekannten Anzahl von Parametern, die das Erstellen von Abfragen zu einer schwierigen Aufgabe machen.

  2. Weder mysqli_ * noch PDO-Funktionen sollten im Anwendungscode enthalten sein.
    Es sollte einen geben Abstraktionsschicht zwischen ihnen und Anwendungscode, der die ganze schmutzige Arbeit des Bindens, des Schleifens, der Fehlerhandhabung, usw. innen erledigt, Anwendungscode DRY und sauber machend. Speziell für komplexe Fälle wie dynamisches Abfragen.

Es reicht also nicht einfach zu PDO oder mysqli zu wechseln. Man muss einen ORM oder einen Abfrage-Generator oder eine andere Datenbankabstraktionsklasse verwenden, anstatt rohe API-Funktionen in ihrem Code aufzurufen.
Und im Gegenteil - wenn Sie eine Abstraktionsschicht zwischen Ihrem Anwendungscode und der mysql-API haben - Es spielt eigentlich keine Rolle, welcher Motor verwendet wird. Sie können mysql ext verwenden, bis es veraltet ist und dann Ihre Abstraktionsklasse einfach in eine andere Engine umschreiben. den gesamten Anwendungscode intakt zu haben.

Hier sind einige Beispiele, die auf meiner basieren Safemysql-Klasse 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 Zeile mit Menge an Code, die Sie mit PDO benötigen.
Dann vergleiche mit verrückte Menge an Code Sie werden mit Roh-Mysqli vorbereitete Aussagen benötigen. Beachten Sie, dass die Fehlerbehandlung, das Profiling und die 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 solchen praktischen Fall zu behandeln.
Und es wird zu wortwörtlich und höchstwahrscheinlich unsicher sein.

Also, noch einmal - es sollte nicht nur der rohe Treiber sein, sondern die Abstraktionsklasse, die nicht nur für dumme Beispiele aus dem Anfängerhandbuch nützlich ist, sondern um alle Probleme des wirklichen Lebens zu lösen.


98
2017-10-12 13:23



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

Beim Benutzen mysql_* Funktionen, müssen Sie daran denken, benutzerdefinierte Parameter durch zu führen mysql_real_escape_string(). Wenn Sie an nur einer Stelle vergessen oder nur einen Teil der Eingabe verlassen, kann Ihre Datenbank angegriffen werden.

Vorbereitete Aussagen verwenden in PDO oder mysqli wird es so machen, dass diese Art von Programmierfehlern schwieriger zu machen ist.


87
2017-10-12 13:24