PHP bietet drei verschiedene APIs für die Verbindung mit MySQL. Dies sind die mysql
(ab PHP 7 entfernten) mysqli
und PDO
Erweiterungen.
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_DEPRECATED
Fehler , wenn Benutzer zu MySQL verbinden, sei es durch mysql_connect()
, mysql_pconnect()
oder die implizite Verbindung Funktionalität eingebaut in ext/mysql
.
ext/mysql
wurde 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/mysql
geht es nicht nur um Sicherheit, sondern auch darum, Zugriff auf alle Funktionen der MySQL-Datenbank zu haben.
ext/mysql
wurde für MySQL 3.23 entwickelt und hat seitdem nur sehr wenige Ergänzungen erhalten, wobei die Kompatibilität mit dieser alten Version größtenteils erhalten blieb, wodurch die Wartung des Codes etwas schwieriger wird. Zu den fehlenden Funktionen, die nicht unterstützt werden, ext/mysql
gehö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
/ PDO
können E_DEPRECATED
Fehler unterdrückt werden, indem error_reporting
in 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 PDO
Tutorial.
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."
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 PDO
Objekt 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 DSN
ist im Grunde eine Reihe von Optionen, die angeben, PDO
welcher 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 PDOException
Objekt ausgelöst, das abgefangen werden kann, um es Exception
weiter 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 PDO
in den Ausnahmemodus versetzt wird. Da einige PDO
Treiber native vorbereitete Anweisungen nicht unterstützen, wird PDO
die 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 MySQL
Möglichkeit besteht darin, die Vorbereitungsemulation zu deaktivieren, die standardmäßig im Treiber aktiviert ist. Die Vorbereitungsemulation sollte jedoch deaktiviert sein, um eine PDO
sichere 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, MySQL
die 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 setAttribute
Methode 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 PDO
als 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.
PDO
bietet eine bessere Lösung: Ausnahmen. Alles , was wir mit tun PDO
sollte in einem gewickelt werden try
- catch
Block. Wir können PDO
in 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_WARNING
Erhö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 PDOException
aus 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 PDOException
kö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
- 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
- im catch
Moment. 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 PDO
Zeug 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 off
indem 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 DELETE
Aussagen? Keine Sorge, los geht's:
Daten 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 PDO
können Sie dies wie folgt tun:
<?php
$stmt = $db->query('SELECT * FROM table');
while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo $row['field1'];
}
Oder
<?php
$stmt = $db->query('SELECT * FROM table');
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
//Use $results
Hinweis : Wenn Sie die Methode wie unten ( query()
) verwenden, gibt diese Methode ein PDOStatement
Objekt 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_ASSOC
im fetch()
und fetchAll()
Code oben. Dies weist PDO
an, die Zeilen als assoziatives Array mit den Feldnamen als Schlüssel zurückzugeben. Es gibt auch viele andere Abrufmodi, die ich 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 PDOStatement
Fetch-Dokumentation. .
Abrufen der Zeilenanzahl :
Anstatt mysql_num_rows
die Anzahl der zurückgegebenen Zeilen abzurufen, können Sie a PDOStatement
and 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
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::exec
eine 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 ):
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 (?, ?)
Das DBMS analysiert, kompiliert und führt eine Abfrageoptimierung für die Anweisungsvorlage durch und speichert das Ergebnis, ohne es auszuführen.
- 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.00
fü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 OOP
Freunde ist, dass benannte Platzhalter Objekte direkt in Ihre Datenbank einfügen können, vorausgesetzt, die Eigenschaften stimmen mit den benannten Feldern überein. Zum Beispiel:
class person {
public $name;
public $add;
function __construct($a,$b) {
$this->name = $a;
$this->add = $b;
}
}
$demo = new person('john','29 bla district');
$stmt = $db->prepare("INSERT INTO table (name, add) value (:name, :add)");
$stmt->execute((array)$demo);
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
, DELETE
Vorbereitet Abfragen
SELECT
::
$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
$stmt->execute(array(':name' => $name, ':id' => $id));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
INSERT
::
$stmt = $db->prepare("INSERT INTO table(field1,field2) VALUES(:field1,:field2)");
$stmt->execute(array(':field1' => $field1, ':field2' => $field2));
$affected_rows = $stmt->rowCount();
DELETE
::
$stmt = $db->prepare("DELETE FROM table WHERE id=:id");
$stmt->bindValue(':id', $id, PDO::PARAM_STR);
$stmt->execute();
$affected_rows = $stmt->rowCount();
UPDATE
::
$stmt = $db->prepare("UPDATE table SET name=? WHERE id=?");
$stmt->execute(array($name, $id));
$affected_rows = $stmt->rowCount();
HINWEIS:
Jedoch PDO
und / oder MySQLi
sind 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 /*"));