PHP offre tre diverse API per connettersi a MySQL. Queste sono le mysql
(rimosse da PHP 7) mysqli
e le PDO
estensioni.
Le mysql_*
funzioni erano molto popolari, ma il loro uso non è più incoraggiato. Il team di documentazione sta discutendo della situazione della sicurezza del database e istruendo gli utenti ad allontanarsi dall'estensione ext / mysql comunemente usata fa parte di questo (consultare php.internals: deprecating ext / mysql ).
E il team sviluppatore PHP in seguito ha preso la decisione per generare E_DEPRECATED
errori quando gli utenti si connettono a MySQL, sia attraverso mysql_connect()
, mysql_pconnect()
o la funzionalità di connessione implicita integrato in ext/mysql
.
ext/mysql
è stato ufficialmente deprecato a partire da PHP 5.5 ed è stato rimosso a partire da PHP 7 .
Vedi la scatola rossa?
Quando vai su qualsiasi mysql_*
pagina del manuale delle funzioni, viene visualizzata una casella rossa che spiega che non dovrebbe più essere utilizzata.
Perché
Allontanarsi ext/mysql
non riguarda solo la sicurezza, ma anche l'accesso a tutte le funzionalità del database MySQL.
ext/mysql
è stato creato per MySQL 3.23 e da allora ha ottenuto pochissime aggiunte mantenendo per lo più la compatibilità con questa vecchia versione che rende il codice un po 'più difficile da mantenere. Le funzioni mancanti non supportate da ext/mysql
includono: ( dal manuale di PHP ).
Motivo per non utilizzare la mysql_*
funzione :
- Non in fase di sviluppo attivo
- Rimosso da PHP 7
- Manca un'interfaccia OO
- Non supporta query asincrone e non bloccanti
- Non supporta istruzioni preparate o query con parametri
- Non supporta le stored procedure
- Non supporta più istruzioni
- Non supporta le transazioni
- Non supporta tutte le funzionalità di MySQL 5.1
Sopra il punto citato dalla risposta di Quentin
La mancanza di supporto per le dichiarazioni preparate è particolarmente importante in quanto forniscono un metodo più chiaro e meno soggetto a errori di fuga e quotazione di dati esterni rispetto alla fuga manuale con una chiamata di funzione separata.
Vedi il confronto delle estensioni SQL .
Soppressione degli avvisi di deprecazione
Durante la conversione del codice in MySQLi
/ PDO
, è E_DEPRECATED
possibile eliminare gli errori impostando error_reporting
in php.ini per escludereE_DEPRECATED:
error_reporting = E_ALL ^ E_DEPRECATED
Si noti che questo nasconderà anche altri avvisi di deprecazione , che tuttavia potrebbero riguardare cose diverse da MySQL. ( dal manuale di PHP )
L'articolo DOP vs MySQLi: quale dovresti usare? di Dejan Marjanovic ti aiuterà a scegliere.
E un modo migliore è PDO
, e ora sto scrivendo un semplice PDO
tutorial.
Un tutorial PDO semplice e breve
D. La prima domanda che avevo in mente era: che cos'è il DOP?
R. " DOP - PHP Data Objects - è un livello di accesso al database che fornisce un metodo uniforme di accesso a più database".
Connessione a MySQL
Con la mysql_*
funzione o possiamo dirlo alla vecchia maniera (deprecato in PHP 5.5 e versioni successive)
$link = mysql_connect('localhost', 'user', 'pass');
mysql_select_db('testdb', $link);
mysql_set_charset('UTF-8', $link);
Con PDO
: Tutto quello che devi fare è creare un nuovo PDO
oggetto. Il costruttore accetta parametri per specificare l'origine di database PDO
's costruttore prende principalmente quattro parametri che sono DSN
(nome di origine dati) e opzionalmente username
, password
.
Qui penso che tu abbia familiarità con tutti tranne DSN
; questo è nuovo in PDO
. A DSN
è fondamentalmente una serie di opzioni che indicano PDO
quale driver utilizzare e i dettagli di connessione. Per ulteriori riferimenti, consultare il DSN MySQL DOP .
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');
Nota: puoi anche usare charset=UTF-8
, ma a volte provoca un errore, quindi è meglio usarlo utf8
.
Se si verifica un errore di connessione, verrà generato un PDOException
oggetto che può essere catturato per essere gestito Exception
ulteriormente.
Buona lettura : connessioni e gestione delle connessioni ¶
È inoltre possibile passare diverse opzioni del driver come array al quarto parametro. Consiglio di passare il parametro che mette PDO
in modalità eccezione. Poiché alcuni PDO
driver non supportano le istruzioni preparate native, quindi PDO
esegue l'emulazione del preparato. Inoltre, consente di abilitare manualmente questa emulazione. Per utilizzare le istruzioni preparate sul lato server nativo, è necessario impostarle in modo esplicito false
.
L'altro è disattivare preparare l'emulazione che è abilitata nel MySQL
driver per impostazione predefinita, ma preparare l'emulazione deve essere disattivata per un utilizzo PDO
sicuro.
In seguito spiegherò perché preparare l'emulazione dovrebbe essere disattivato. Per trovare la ragione, controlla questo post .
È utilizzabile solo se si utilizza una versione precedente di MySQL
cui non è consigliabile.
Di seguito è riportato un esempio di come è possibile farlo:
$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));
Possiamo impostare gli attributi dopo la costruzione DOP?
Sì , possiamo anche impostare alcuni attributi dopo la costruzione DOP con il setAttribute
metodo:
$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);
Gestione degli errori
La gestione degli errori è molto più semplice PDO
di mysql_*
.
Una pratica comune durante l'utilizzo mysql_*
è:
//Connected to MySQL
$result = mysql_query("SELECT * FROM table", $link) or die(mysql_error($link));
OR die()
non è un buon modo per gestire l'errore poiché non possiamo gestire la cosa die
. Terminerà bruscamente lo script e poi farà eco all'errore sullo schermo che di solito NON vuoi mostrare ai tuoi utenti finali, e permetterà agli hacker insanguinati di scoprire il tuo schema. In alternativa, i valori di ritorno delle mysql_*
funzioni possono spesso essere utilizzati insieme a mysql_error () per gestire gli errori.
PDO
offre una soluzione migliore: eccezioni. Qualsiasi cosa facciamo con PDO
dovrebbe essere avvolto in un try
- catch
blocco. Possiamo forzare PDO
in una delle tre modalità di errore impostando l'attributo della modalità di errore. Di seguito sono riportate tre modalità di gestione degli errori.
PDO::ERRMODE_SILENT
. Sta solo impostando i codici di errore e agisce in modo analogo a quello in mysql_*
cui è necessario controllare ogni risultato e quindi guardare $db->errorInfo();
per ottenere i dettagli dell'errore.
PDO::ERRMODE_WARNING
Alza E_WARNING
. (Avvisi di runtime (errori non fatali). L'esecuzione dello script non viene interrotta.)
PDO::ERRMODE_EXCEPTION
: Genera eccezioni. Rappresenta un errore generato da DOP. Non si dovrebbe lanciare a PDOException
dal proprio codice. Vedi Eccezioni per ulteriori informazioni sulle eccezioni in PHP. Si comporta in modo molto simile a or die(mysql_error());
quando non viene catturato. Ma a differenza di or die()
, PDOException
può essere catturato e gestito con grazia se si sceglie di farlo.
Buona lettura :
Piace:
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
E si può avvolgere in try
- catch
, come di seguito:
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());
}
Non devi occuparti di try
- in catch
questo momento. Puoi prenderlo in qualsiasi momento appropriato, ma ti consiglio vivamente di usare try
- catch
. Inoltre potrebbe avere più senso prenderlo all'esterno della funzione che chiama PDO
roba:
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.
}
Inoltre, puoi gestirlo or die()
o possiamo dire come mysql_*
, ma sarà davvero vario. È possibile nascondere i messaggi di errore pericolosi in produzione girando display_errors off
e leggendo semplicemente il registro degli errori.
Ora, dopo aver letto tutte le cose di lassù, probabilmente stai pensando: cosa diavolo è che quando voglio solo iniziare appoggiato semplici SELECT
, INSERT
, UPDATE
, o DELETE
affermazioni? Non ti preoccupare, eccoci qui:
Selezione dei dati
Quindi quello che stai facendo mysql_*
è:
<?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'];
}
Ora in PDO
, puoi farlo come:
<?php
$stmt = $db->query('SELECT * FROM table');
while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo $row['field1'];
}
O
<?php
$stmt = $db->query('SELECT * FROM table');
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
//Use $results
Nota : se si utilizza il metodo come indicato di seguito ( query()
), questo metodo restituisce un PDOStatement
oggetto. Quindi, se vuoi recuperare il risultato, usalo come sopra.
<?php
foreach($db->query('SELECT * FROM table') as $row) {
echo $row['field1'];
}
In Dati DOP, si ottiene tramite il ->fetch()
metodo di gestione dell'istruzione. Prima di chiamare il recupero, l'approccio migliore sarebbe dire a DOP come desideri che i dati vengano recuperati. Nella sezione seguente sto spiegando questo.
Modalità di recupero
Nota l'uso di PDO::FETCH_ASSOC
nel fetch()
e fetchAll()
codice sopra. Questo dice PDO
di restituire le righe come un array associativo con i nomi dei campi come chiavi. Ci sono anche molte altre modalità di recupero che spiegherò una per una.
Prima di tutto, spiego come selezionare la modalità di recupero:
$stmt->fetch(PDO::FETCH_ASSOC)
In quanto sopra, ho usato fetch()
. Puoi anche usare:
Ora vengo in modalità di recupero:
PDO::FETCH_ASSOC
: restituisce un array indicizzato dal nome della colonna come restituito nel set di risultati
PDO::FETCH_BOTH
(impostazione predefinita): restituisce un array indicizzato dal nome della colonna e dal numero della colonna con indice 0 restituito nel set di risultati
Ci sono ancora più scelte! Leggi tutto su di loro nella PDOStatement
documentazione di recupero. .
Ottenere il conteggio delle righe :
Invece di utilizzare mysql_num_rows
per ottenere il numero di righe restituite, è possibile ottenere un PDOStatement
e fare rowCount()
, come:
<?php
$stmt = $db->query('SELECT * FROM table');
$row_count = $stmt->rowCount();
echo $row_count.' rows selected';
Ottenere l'ultimo ID inserito
<?php
$result = $db->exec("INSERT INTO table(firstname, lastname) VAULES('John', 'Doe')");
$insertId = $db->lastInsertId();
Inserire e aggiornare o eliminare le istruzioni
Quello che stiamo facendo in mysql_*
funzione è:
<?php
$results = mysql_query("UPDATE table SET field='value'") or die(mysql_error());
echo mysql_affected_rows($result);
E in pdo, questa stessa cosa può essere fatta da:
<?php
$affected_rows = $db->exec("UPDATE table SET field='value'");
echo $affected_rows;
Nella query precedente PDO::exec
eseguire un'istruzione SQL e restituire il numero di righe interessate.
Inserisci ed elimina saranno trattati in seguito.
Il metodo sopra è utile solo quando non si utilizza la variabile nella query. Ma quando è necessario utilizzare una variabile in una query, non provare mai come sopra e lì è per un'istruzione preparata o un'istruzione con parametri .
Dichiarazioni preparate
D. Che cos'è un'affermazione preparata e perché ne ho bisogno?
R. Un'istruzione preparata è un'istruzione SQL precompilata che può essere eseguita più volte inviando solo i dati al server.
Il flusso di lavoro tipico dell'utilizzo di un'istruzione preparata è il seguente ( citato da Wikipedia tre punti 3 ):
Prepara : il modello di istruzione viene creato dall'applicazione e inviato al sistema di gestione del database (DBMS). Alcuni valori vengono lasciati non specificati, chiamati parametri, segnaposto o variabili di bind (etichettati di ?
seguito):
INSERT INTO PRODUCT (name, price) VALUES (?, ?)
Il DBMS analizza, compila ed esegue l'ottimizzazione delle query sul modello di istruzione e memorizza il risultato senza eseguirlo.
- Esegui : in un secondo momento, l'applicazione fornisce (o associa) i valori per i parametri e il DBMS esegue l'istruzione (eventualmente restituendo un risultato). L'applicazione può eseguire l'istruzione tutte le volte che lo desidera con valori diversi. In questo esempio, potrebbe fornire "Bread" per il primo parametro e
1.00
per il secondo parametro.
È possibile utilizzare un'istruzione preparata includendo segnaposto nel proprio SQL. Esistono sostanzialmente tre senza segnaposto (non provarlo con la variabile sopra quella), uno con segnaposto senza nome e uno con segnaposto denominato.
D. Quindi, ora, quali sono i segnaposti denominati e come li uso?
A. Segnaposti nominati. Usa nomi descrittivi preceduti da due punti, anziché da punti interrogativi. Non ci interessa la posizione / ordine di valore nel segnaposto nome:
$stmt->bindParam(':bla', $bla);
bindParam(parameter,variable,data_type,length,driver_options)
Puoi anche eseguire il bind utilizzando anche un array di esecuzione:
<?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);
Un'altra caratteristica interessante per gli OOP
amici è che i segnaposto con nome hanno la possibilità di inserire oggetti direttamente nel database, assumendo che le proprietà corrispondano ai campi con nome. Per esempio:
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. Quindi ora, cosa sono i segnaposto senza nome e come li uso?
A. Facciamo un esempio:
<?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();
e
$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->execute(array('john', '29 bla district'));
In quanto sopra, puoi vedere quelli ?
invece di un nome come in un segnaposto nome. Ora nel primo esempio, assegniamo le variabili ai vari segnaposto ( $stmt->bindValue(1, $name, PDO::PARAM_STR);
). Quindi, assegniamo valori a quei segnaposto ed eseguiamo l'istruzione. Nel secondo esempio, il primo elemento dell'array passa al primo ?
e il secondo al secondo ?
.
NOTA : nei segnaposto senza nome dobbiamo occuparci del corretto ordine degli elementi nell'array che stiamo passando al PDOStatement::execute()
metodo.
SELECT
, INSERT
, UPDATE
, DELETE
Query preparate
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();
NOTA:
Tuttavia PDO
e / o MySQLi
non sono completamente sicuri. Controlla la risposta Le istruzioni preparate per DOP sono sufficienti per prevenire l'iniezione di SQL? di ircmaxell . Inoltre, sto citando una parte della sua risposta:
$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 /*"));