Domanda Perché non dovrei usare le funzioni mysql_ * in PHP?


Quali sono le ragioni tecniche per cui non si dovrebbe usare mysql_* funzioni? (per esempio. mysql_query(), mysql_connect() o mysql_real_escape_string())?

Perché dovrei usare qualcos'altro anche se funzionano sul mio sito?

Se non funzionano sul mio sito, perché ottengo errori come

Avviso: mysql_connect (): nessun file o directory di questo tipo


2194
2017-10-12 13:18


origine


risposte:


L'estensione MySQL:

  • Non è in sviluppo attivo
  • È ufficialmente deprecato da PHP 5.5 (pubblicato a giugno 2013).
  • È stato rimosso interamente da PHP 7.0 (rilasciato a dicembre 2015)
    • Ciò significa che a partire da 31 dic 2018 non esisterà in nessuna versione supportata di PHP. Attualmente, ottiene solo sicurezza aggiornamenti.
  • Manca un'interfaccia OO
  • Non supporta:
    • Query asincrone non bloccanti
    • Dichiarazioni preparate o query parametrizzate
    • Procedura di archiviazione
    • Più dichiarazioni
    • Le transazioni
    • Il "nuovo" metodo di autenticazione della password (attivo per impostazione predefinita in MySQL 5.6; richiesto in 5.7)
    • Tutte le funzionalità di MySQL 5.1

Dato che è deprecato, l'uso di questo rende il tuo codice meno a prova di futuro.

La mancanza di supporto per le istruzioni preparate è particolarmente importante in quanto forniscono un metodo più chiaro e meno soggetto a errori di escaping e di citazione dei dati esterni rispetto all'esclusione manuale con una chiamata di funzione separata.

Vedere il confronto delle estensioni SQL.


1808
2018-01-01 11:52



PHP offre tre API diverse per connettersi a MySQL. Queste sono le mysql(rimosso da PHP 7), mysqli, e PDO estensioni.

Il mysql_* le funzioni erano molto popolari, ma il loro uso non è più incoraggiato. Il team di documentazione sta discutendo la situazione della sicurezza del database e l'educazione degli utenti ad allontanarsi dall'estensione ext / mysql comunemente usata fa parte di questo php.internals: deprecating ext / mysql).

E il successivo team di sviluppatori PHP ha preso la decisione di generare E_DEPRECATED errori quando gli utenti si connettono a MySQL, attraverso mysql_connect(), mysql_pconnect() o la funzionalità di connessione implicita incorporata ext/mysql.

ext/mysql era ufficialmente deprecato da PHP 5.5 ed è stato rimosso da PHP 7.

Vedi la scatola rossa?

Quando vai su qualsiasi mysql_* funzione manuale della pagina, si vede una casella rossa, spiegando che non dovrebbe più essere utilizzata.

Perché


Allontanarsi da ext/mysql non riguarda solo la sicurezza, ma anche l'accesso a tutte le funzionalità del database MySQL.

ext/mysql è stato costruito per MySQL 3.23 e da allora ha ottenuto solo poche aggiunte, mentre per lo più mantiene la compatibilità con questa vecchia versione che rende il codice un po 'più difficile da mantenere. Funzioni mancanti non supportate da ext/mysql includere: (dal manuale PHP).

Motivo per non usare mysql_* funzione:

  • Non in sviluppo attivo
  • Rimosso da PHP 7
  • Manca un'interfaccia OO
  • Non supporta query asincrone non bloccanti
  • Non supporta dichiarazioni preparate o query parametrizzate
  • Non supporta le stored procedure
  • Non supporta più istruzioni
  • Non supporta transazioni
  • Non supporta tutte le funzionalità di MySQL 5.1

Sopra il punto citato dalla risposta di Quentin

La mancanza di supporto per le istruzioni preparate è particolarmente importante in quanto forniscono un metodo più chiaro e meno soggetto a errori di escaping e di citazione dei dati esterni rispetto all'esclusione manuale con una chiamata di funzione separata.

Vedere il confronto di estensioni SQL.


Soppressione degli avvisi di deprecazione

Mentre il codice viene convertito in MySQLi/PDO, E_DEPRECATED gli errori possono essere soppressi impostando error_reporting in php.ini escludere E_DEPRECATED:

error_reporting = E_ALL ^ E_DEPRECATED

Si noti che questo si nasconderà anche altri avvisi di deprecazione, che, tuttavia, potrebbe essere per cose diverse da MySQL. (dal manuale PHP)

L'articolo PDO vs. MySQLi: quale dovresti usare? di Dejan Marjanovic ti aiuterà a scegliere.

E un modo migliore è PDOe ora sto scrivendo un semplice PDO tutorial.


Un tutorial PDO semplice e breve


D. La prima domanda nella mia mente è stata: che cosa è "DOP"?

A. "PDO - Oggetti dati PHP - è un livello di accesso al database che fornisce un metodo uniforme di accesso a più database. "

alt text


Connessione a MySQL

Con mysql_* funzione o possiamo dirlo alla vecchia maniera (deprecato in PHP 5.5 e sopra)

$link = mysql_connect('localhost', 'user', 'pass');
mysql_select_db('testdb', $link);
mysql_set_charset('UTF-8', $link);

Con PDO: Tutto ciò che devi fare è creare un nuovo PDO oggetto. Il costruttore accetta i parametri per specificare l'origine del database PDOLa funzione di costruzione richiede principalmente quattro parametri DSN (nome dell'origine dati) e facoltativamente username, password.

Qui penso che tu abbia familiarità con tutti tranne DSN; questo è nuovo dentro PDO. UN DSN è fondamentalmente una serie di opzioni che dicono PDO quale driver utilizzare e dettagli sulla connessione. Per ulteriori riferimenti, controllare DSN MySQL DSN.

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');

Nota: puoi anche usare charset=UTF-8, ma a volte causa un errore, quindi è meglio usarlo utf8.

Se c'è un errore di connessione, verrà lanciata a PDOException oggetto che può essere catturato per gestire Exception ulteriore.

Buona lettura: Connessioni e gestione delle connessioni ¶ 

È anche possibile passare diverse opzioni del driver come array al quarto parametro. Raccomando di passare il parametro che mette PDO in modalità eccezione. Perché alcuni PDO i driver non supportano le istruzioni preparate in modo nativo, quindi PDO esegue l'emulazione della preparazione. Permette anche di abilitare manualmente questa emulazione. Per utilizzare le istruzioni preparate sul lato server nativo, è necessario impostarlo esplicitamente false.

L'altro è di disattivare l'emulazione di preparazione che è abilitata nel MySQLdriver di default, ma preparare l'emulazione dovrebbe essere disattivata per l'uso PDO in modo sicuro.

In seguito spiegherò perché preparare l'emulazione dovrebbe essere disattivata. Per trovare il motivo, per favore controlla questo post.

È utilizzabile solo se si utilizza una vecchia versione di MySQL che non ho raccomandato

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 dei PDO?

, possiamo anche impostare alcuni attributi dopo la costruzione di PDO con 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ù facile in PDO di mysql_*.

Una pratica comune quando si utilizza 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 siamo in grado di gestire la cosa die. Interrompe bruscamente lo script e quindi fa eco all'errore che di solito NON vuoi mostrare ai tuoi utenti finali e lascia che i malintenzionati scoprano il tuo schema. In alternativa, i valori di ritorno di mysql_* le funzioni possono essere spesso utilizzate insieme a mysql_error () per gestire gli errori.

PDO offre una soluzione migliore: eccezioni. Qualsiasi cosa facciamo con PDO dovrebbe essere avvolto in a try-catch bloccare. Possiamo forzare PDO in una delle tre modalità di errore impostando l'attributo della modalità errore. Di seguito sono riportate tre modalità di gestione degli errori.

  • PDO::ERRMODE_SILENT. Sta solo impostando i codici di errore e si comporta più o meno come mysql_* dove devi controllare ogni risultato e poi guardare $db->errorInfo(); per ottenere i dettagli dell'errore.
  • PDO::ERRMODE_WARNING Aumentare E_WARNING. (Avvisi in fase di esecuzione (errori non fatali). L'esecuzione dello script non viene interrotta.)
  • PDO::ERRMODE_EXCEPTION: Lanciare eccezioni. Rappresenta un errore generato da PDO. Non dovresti lanciare un PDOException dal tuo codice. Vedere eccezioni per ulteriori informazioni sulle eccezioni in PHP. Funziona molto simile or die(mysql_error());, quando non viene catturato. Ma a differenza or die(), il PDOException può essere catturato e gestito con garbo 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 puoi avvolgerlo 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 gestire con try-catch proprio adesso. Puoi prenderlo in qualsiasi momento, ma ti consiglio vivamente di usarlo try-catch. Inoltre potrebbe essere più sensato catturarlo all'esterno della funzione che chiama il PDO cose:

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 gestire da 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 basta leggere il tuo log degli errori.

Ora, dopo aver letto tutte le cose di cui sopra, probabilmente stai pensando: cosa diavolo è che quando voglio iniziare semplicemente appoggiandosi SELECT, INSERT, UPDATE, o DELETE affermazioni? Non preoccuparti, eccoci qui:


Selezione dei dati

PDO select image

Quindi cosa 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 fare questo 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 stai usando il metodo come di seguito (query()), questo metodo restituisce a PDOStatement oggetto. Quindi se vuoi recuperare il risultato, usalo come sopra.

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

Nei dati PDO, è ottenuto tramite il ->fetch(), un metodo per gestire la tua dichiarazione. Prima di chiamare fetch, l'approccio migliore sarebbe dire a DOP come desideri che i dati vengano recuperati. Nella sezione seguente lo sto spiegando.

Modalità di recupero

Notare l'uso di PDO::FETCH_ASSOC nel fetch() e fetchAll() codice sopra. Questo dice PDO per restituire le righe come 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 per nome colonna come restituito nel set di risultati
  • PDO::FETCH_BOTH (impostazione predefinita): restituisce un array indicizzato dal nome della colonna e dal numero di colonna con indice 0 restituito nel set di risultati

Ci sono ancora più scelte! Leggi di loro tutti dentro PDOStatement Scarica la documentazione..

Ottenere il conteggio delle righe:

Invece di usare mysql_num_rows per ottenere il numero di righe restituite, puoi ottenere un PDOStatement e fai rowCount(), piace:

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

Inserisci e aggiorna o cancella le istruzioni

Insert and update PDO image

Cosa stiamo facendo in mysql_* la 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 restituisce il numero di righe interessate.

Inserimento ed eliminazione saranno trattati in seguito.

Il metodo precedente è 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 dichiarazione preparata o dichiarazione parametrizzata è.


Dichiarazioni preparate

Q. Che cos'è una dichiarazione preparata e perché ne ho bisogno?
UN. Una dichiarazione preparata è un'istruzione SQL precompilata che può essere eseguita più volte inviando solo i dati al server.

Il tipico flusso di lavoro dell'utilizzo di una dichiarazione preparata è il seguente (citato da Wikipedia tre 3 punti):

  1. Preparare: Il modello dell'istruzione viene creato dall'applicazione e inviato al sistema di gestione del database (DBMS). Alcuni valori non sono specificati, chiamati parametri, segnaposto o variabili di binding (etichettati ? sotto):

    INSERT INTO PRODUCT (name, price) VALUES (?, ?)

  2. Il DBMS analizza, compila ed esegue l'ottimizzazione della query sul modello dell'istruzione e memorizza il risultato senza eseguirlo.

  3. Eseguire: In un secondo momento, l'applicazione fornisce (o collega) 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 vuole con valori diversi. In questo esempio, potrebbe fornire "Pane" per il primo parametro e 1.00per il secondo parametro.

È possibile utilizzare una dichiarazione preparata includendo segnaposti nel proprio SQL. Ci sono fondamentalmente tre senza segnaposto (non provatelo con la variabile sopra), uno con segnaposto senza nome e uno con segnaposto con nome.

Q. Quindi ora, quali sono i segnaposto nominati e come li uso?
UN. Segnaposto nominati. Usa nomi descrittivi preceduti da due punti, invece di punti interrogativi. Non ci interessa la posizione / l'ordine di valore nel titolare del nome:

 $stmt->bindParam(':bla', $bla);

bindParam(parameter,variable,data_type,length,driver_options)

Puoi anche eseguire il binding utilizzando un array execute:

<?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 OOP amici è che i segnaposto nominati hanno la possibilità di inserire oggetti direttamente nel database, assumendo che le proprietà corrispondano ai campi nominati. 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, quali sono i segnaposto senza nome e come li uso?
UN. 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 nel nome di un segnaposto. 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 la dichiarazione. Nel secondo esempio, il primo elemento dell'array passa al primo ? e il secondo al secondo ?.

NOTA: In segnaposti senza nome dobbiamo occuparci del corretto ordine degli elementi nella matrice che stiamo passando al PDOStatement::execute() metodo.


SELECT, INSERT, UPDATE, DELETE domande preparate

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

NOTA:

però PDO e / o MySQLi non sono completamente al sicuro. Controlla la risposta Le dichiarazioni preparate dalla PDO sono sufficienti per prevenire l'iniezione 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 /*"));

1160
2017-10-12 13:28



Innanzitutto, iniziamo con il commento standard che diamo a tutti:

Per favore, non usare mysql_* funzioni nel nuovo codice. Non sono più mantenuti e sono ufficialmente deprecati. Vedere il scatola rossa? Impara al riguardo dichiarazioni preparate invece, e usa DOP o MySQLi - Questo articolo ti aiuterà a decidere quale. Se scegli PDO, ecco un buon tutorial.

Passiamo a questo, frase per frase, e spieghiamo:

  • Non sono più mantenuti e sono ufficialmente deprecati

    Ciò significa che la comunità PHP sta gradualmente abbandonando il supporto per queste funzioni molto vecchie. È probabile che non esistano in una versione futura (recente) di PHP! L'uso continuato di queste funzioni potrebbe violare il codice nel futuro (non così).

    NUOVO! - ext / mysql è ora ufficialmente deprecato da PHP 5.5!

    Più nuovo! ext / mysql è stato rimosso in PHP 7.

  • Invece, dovresti imparare delle dichiarazioni preparate

    mysql_* l'estensione non supporta dichiarazioni preparate, che è (tra le altre cose) una contromisura molto efficace contro SQL Injection. Risolve una vulnerabilità molto seria nelle applicazioni dipendenti da MySQL che consente agli aggressori di accedere al proprio script ed esibirsi qualsiasi domanda possibile sul tuo database

    Per ulteriori informazioni, vedere Come posso prevenire l'SQL injection in PHP?

  • Vedi la scatola rossa?

    Quando vai a qualsiasi mysql funzione manuale della pagina, si vede una casella rossa, spiegando che non dovrebbe più essere utilizzata.

  • Usa PDO o MySQLi

    Ci sono alternative migliori, più robuste e ben costruite, PDO - Oggetto database PHP, che offre un approccio OOP completo all'interazione del database e MySQLi, che è un miglioramento specifico di MySQL.


279
2017-12-24 23:30



Facilità d'uso

Le ragioni analitiche e sintetiche sono già state citate. Per i nuovi arrivati ​​c'è un incentivo più significativo a smettere di usare le funzioni mysql_ datate.

Le API di database contemporanee sono giuste Più facile usare.

È principalmente il parametri associati che può semplificare il codice. E con tutorial eccellenti (come visto sopra) la transizione a DOP non è eccessivamente arduo

Riscrivere una base di codice più grande in una volta, tuttavia, richiede tempo. Raison d'être per questa alternativa intermedia:

Funzioni equivalenti pdo_ * al posto di mysql_ *

utilizzando <pdo_mysql.php> puoi passare dalle vecchie funzioni mysql_ con sforzo minimo. Aggiunge pdo_ involucri di funzioni che sostituiscono i loro mysql_ controparti.

  1. Semplicemente include_once("pdo_mysql.php"); in ogni script di chiamata che deve interagire con il database.

  2. Rimuovi il mysql_ prefisso di funzione ovunque e sostituirlo con pdo_.

    • mysql_connect() diventa pdo_connect()
    • mysql_query() diventa pdo_query()
    • mysql_num_rows() diventa pdo_num_rows()
    • mysql_insert_id() diventa pdo_insert_id()
    • mysql_fetch_array() diventa pdo_fetch_array()
    • mysql_fetch_assoc() diventa pdo_fetch_assoc()
    • mysql_real_escape_string() diventa pdo_real_escape_string()
    • e così via... 

       
  3. Il tuo codice funzionerà allo stesso modo e per lo più sembra sempre lo stesso:

    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à.
Il tuo codice è utilizzando DOP.
Ora è il momento di farlo utilizzare esso.

I parametri associati possono essere facili da usare

Hai solo bisogno di un'API meno ingombrante.

pdo_query() aggiunge un supporto molto facile per i parametri associati. La conversione del vecchio codice è semplice:

Sposta le tue variabili fuori dalla stringa SQL.

  • Aggiungili come parametri di funzione delimitati da virgole a pdo_query().
  • Metti dei punti interrogativi ? come segnaposto in cui le variabili erano prima.
  • Liberarsi di ' le virgolette singole che valori / variabili stringa precedentemente racchiusi.

Il vantaggio diventa più ovvio per codice più lungo.

Spesso le variabili stringa non vengono solo interpolate in SQL, ma concatenate con chiamate di escape intermedie.

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

Con ? i segnaposto applicati non devono preoccuparsi di questo:

pdo_query("SELECT id, links, html, title, user, date FROM articles
   WHERE title=? OR id=? AND user<>? ORDER BY date", $title, $id, $root)

Ricorda che pdo_ * consente ancora entrambi o.
Basta non sfuggire a una variabile e legalo nella stessa query.

  • La funzione segnaposto è fornita dal vero PDO dietro di esso.
  • Così anche permesso :named elenchi segnaposto in seguito.

Ancora più importante è possibile passare le variabili $ _REQUEST [] in modo sicuro dietro qualsiasi query. Quando inviato <form> i campi corrispondono alla struttura del database esattamente è ancora più breve:

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

Tanta semplicità Ma torniamo ad altri consigli di riscrittura e motivi tecnici sul motivo per cui potresti voler liberarti di mysql_e fuggire.

Correggere o rimuovere qualsiasi oldschool sanitize() funzione

Una volta convertito tutto mysql_ chiama a pdo_query con i parametri associati, rimuovere tutti i ridondanti pdo_real_escape_string chiamate.

In particolare dovresti correggere qualsiasi sanitize o clean o filterThis o clean_data funzioni come pubblicizzate da tutorial datati in una forma o nell'altra:

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

La maggior parte dei bug abbaglianti qui è la mancanza di documentazione. Più significativamente l'ordine di filtraggio era esattamente nell'ordine sbagliato.

  • L'ordine corretto sarebbe stato: deprecato stripslashes come la chiamata più interna, quindi trim, dopo strip_tags, htmlentities per il contesto di output, e solo per ultimo il _escape_string poiché la sua applicazione dovrebbe precedere direttamente l'interspazio SQL.

  • Ma come primo passo sbarazzarsi di _real_escape_string chiamata.

  • Potrebbe essere necessario mantenere il resto del tuo sanitize() funzione per ora se il tuo database e il flusso di applicazioni si aspettano stringhe HTML-context-safe. Aggiungi un commento che applica solo l'escape HTML in seguito.

  • La gestione di stringhe / valori è delegata a PDO e alle sue istruzioni parametrizzate.

  • Se c'era qualche menzione di stripslashes() nella tua funzione di pulizia, potrebbe indicare una supervisione di livello superiore.

    Nota storica su magic_quotes. Questa caratteristica è giustamente deprecata. Spesso viene ritratto in modo non corretto come fallito sicurezza caratteristica tuttavia. Ma magic_quotes è tanto una caratteristica di sicurezza fallita quanto le palline da tennis hanno fallito come fonte di nutrizione. Questo semplicemente non era il loro scopo.

    L'implementazione originale in PHP2 / FI l'ha introdotta esplicitamente con solo "le virgolette verranno automaticamente sfuggite rendendo più semplice il passaggio dei dati del modulo direttamente alle query msqlIn particolare è stato accidentalmente sicuro da usare mSQL, poiché supportava solo ASCII.
      Quindi PHP3 / Zend ha reintrodotto magic_quotes per MySQL e lo ha erroneamente documentato. Ma in origine era solo un caratteristica di convenienza, non intendo per sicurezza.

Quanto differiscono le dichiarazioni preparate

Quando si mischiano le variabili stringa nelle query SQL, non solo diventa più complicato da seguire. È anche uno sforzo estenuante per MySQL di segregare nuovamente codice e dati.

Iniezioni SQL sono semplicemente quando i dati sanguinano nel codice contesto. Un server di database non può successivamente individuare dove PHP ha originariamente incollato le variabili tra le clausole di query.

Con i parametri associati, si separano il codice SQL e i valori del contesto SQL nel codice PHP. Ma non viene rimescolato dietro le quinte (tranne con PDO :: EMULATE_PREPARES). Il tuo database riceve i comandi SQL invariati e i valori delle variabili 1: 1.

Mentre questa risposta sottolinea che dovresti preoccuparti dei vantaggi di leggibilità del rilascio mysql_. Occasionalmente c'è anche un vantaggio in termini di prestazioni (INSERT ripetuto con valori diversi) a causa di questa separazione visibile / tecnica dei dati / codice.

Fai attenzione che il binding dei parametri non è una soluzione one-stop magica contro tutti Iniezioni SQL. Gestisce l'uso più comune di dati / valori. Ma non è possibile inserire nella whitelist identificatori di nome / tabella, aiuto nella costruzione di clausole dinamiche o semplicemente elenchi di valori di array.

Uso ibrido DOP

Queste pdo_* le funzioni wrapper rendono un'API stop-gap adatta alla codifica. (È praticamente cosa MYSQLI avrebbe potuto essere se non fosse stato per l'idiosincratico spostamento della firma della funzione). Espongono anche la vera DOP alla maggior parte delle volte.
La riscrittura non deve smettere di usare i nuovi nomi delle funzioni pdo_. È possibile passare uno alla volta ogni pdo_query () in una semplice chiamata $ pdo-> prepare () -> execute ().

Tuttavia, è meglio ricominciare a semplificare di nuovo. Ad esempio il recupero dei risultati comuni:

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

Può essere sostituito con solo un'iterazione foreach:

foreach ($result as $row) {

O meglio ancora un recupero di array diretto e completo:

$result->fetchAll();

Nella maggior parte dei casi riceverai avvertenze più utili rispetto a PDO o mysql_ che di solito forniscono dopo query non riuscite.

Altre opzioni

Quindi questo spero ne abbia visualizzato alcuni pratico ragioni e un percorso da non perdere mysql_.

Basta passare a  non lo taglia abbastanza. pdo_query() è anche solo un frontend su di esso.

A meno che tu non introduca il binding dei parametri o che possa utilizzare qualcos'altro dall'API più carina, è un passaggio inutile. Spero che sia ritratto abbastanza semplice da non favorire lo scoraggiamento verso i nuovi arrivati. (L'istruzione di solito funziona meglio del proibizionismo.)

Anche se si qualifica per la categoria più semplice-che-potrebbe-potrebbe-lavorare, è anche un codice ancora molto sperimentale. L'ho appena scritto durante il fine settimana. C'è comunque una pletora di alternative. Solo google per Astrazione del database PHP e sfoglia un po '. Ci sono sempre state e ci saranno molte librerie eccellenti per tali compiti.

Se si desidera semplificare ulteriormente l'interazione con il database, i mappatori come Paris / Idiorm vale la pena provare Proprio come nessuno usa più il bland DOM in JavaScript, non è necessario fare da babysitter a un'interfaccia di database raw al giorno d'oggi.


200
2017-10-12 13:22



Il mysql_ funzioni:

  1. non sono aggiornati, non vengono più mantenuti
  2. non ti permettono di spostarti facilmente su un altro back-end del database
  3. non supportare dichiarazioni preparate, quindi
  4. incoraggiare i programmatori a utilizzare la concatenazione per creare query, portando a vulnerabilità di SQL injection

134
2018-01-01 17:42



Parlando di tecnico ragioni, ce ne sono solo alcune, estremamente specifiche e raramente utilizzate. Molto probabilmente non li userai mai nella tua vita.
Forse sono troppo ignorante, ma non ho mai avuto l'opportunità di usarle come

  • query asincrone non bloccanti
  • stored procedure che restituiscono più set di risultati
  • Crittografia (SSL)
  • Compressione

Se ne hai bisogno, questi sono senza dubbio motivi tecnici per allontanarsi dall'estensione di mysql verso qualcosa di più elegante e moderno.

Tuttavia, ci sono anche alcuni problemi non tecnici, che possono rendere la tua esperienza un po 'più difficile

  • l'ulteriore utilizzo di queste funzioni con le moderne versioni di PHP solleverà avvisi di livello deprecato. Semplicemente possono essere disattivati.
  • in un lontano futuro, possono essere eventualmente rimossi dal build predefinito di PHP. Non è un grosso problema, dato che mydsql ext verrà spostato in PECL e ogni hoster sarà felice di compilare PHP con esso, poiché non vogliono perdere clienti i cui siti funzionano da decenni.
  • forte resistenza della community Stackoverflow. "Ogni volta che menzioni queste funzioni oneste, ti viene detto che sono sotto stretto tabù.
  • essendo un utente medio di PHP, molto probabilmente la tua idea di usare queste funzioni è soggetta a errori e sbagliata. Solo a causa di tutti questi numerosi tutorial e manuali che ti insegnano nel modo sbagliato. Non le funzioni stesse - devo sottolinearlo - ma il modo in cui vengono utilizzate.

Quest'ultimo problema è un problema.
Ma, a mio parere, la soluzione proposta non è neanche migliore.
Mi sembra troppo idealista un sogno che tutti quegli utenti PHP impareranno come gestire le query SQL in modo corretto contemporaneamente. Molto probabilmente cambieranno solo mysql_ * in mysqli_ * meccanicamente, lasciando l'approccio lo stesso. Soprattutto perché mysqli rende le dichiarazioni preparate all'uso incredibilmente dolorose e fastidiose.
Per non parlare di quello nativo dichiarazioni preparate non sono abbastanza da proteggere dalle iniezioni SQL, e né mysqli né PDO offrono una soluzione.

Quindi, invece di combattere questa onesta estensione, preferirei combattere le pratiche sbagliate ed educare le persone nel modo giusto.

Inoltre, ci sono alcune ragioni false o non significative, come

  • Non supporta stored procedure (stavamo usando mysql_query("CALL my_proc"); Per anni)
  • Non supporta le transazioni (come sopra)
  • Non supporta più dichiarazioni (chi ne ha bisogno?)
  • Non in fase di sviluppo attivo (quindi cosa? Influenza tu in qualche modo pratico?)
  • Manca un'interfaccia OO (per crearne una è questione di diverse ore)
  • Non supporta le istruzioni preparate o le query parametrizzate

L'ultimo è un punto interessante. Sebbene mysql ext non supporti nativo dichiarazioni preparate, non sono richieste per la sicurezza. Possiamo facilmente falsificare le dichiarazioni preparate usando segnaposti gestiti manualmente (proprio come fa DOP):

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

Ecco, tutto è parametrizzato e sicuro.

Ma ok, se non ti piace il riquadro rosso nel manuale, sorge un problema di scelta: mysqli o PDO?

Bene, la risposta sarebbe la seguente:

  • Se capisci la necessità di usare a livello di astrazione del database e cercando un'API per crearne una, mysqli è una scelta molto buona, in quanto supporta molte funzionalità specifiche di MySQL.
  • Se, come la stragrande maggioranza delle persone PHP, stai usando le chiamate API giuste proprio nel codice dell'applicazione (che è essenzialmente una pratica sbagliata) - DOP è l'unica scelta, poiché questa estensione pretende di essere non solo API ma piuttosto un semi-DAL, ancora incompleto ma offre molte caratteristiche importanti, con due di questi rende il PDO distinto in modo critico da mysqli:

    • a differenza di mysqli, DOP può legare i segnaposto in base al valore, che rende le query costruite dinamicamente fattibili senza diverse schermate di codice abbastanza disordinato.
    • a differenza di mysqli, PDO può sempre restituire il risultato della query in un array normale, mentre mysqli può farlo solo su installazioni mysqlnd.

Quindi, se sei un utente medio di PHP e vuoi risparmiare un sacco di mal di testa quando usi istruzioni preparate in modo nativo, PDO - di nuovo - è l'unica scelta.
Tuttavia, DOP non è un proiettile d'argento e ha le sue difficoltà.
Così, ho scritto soluzioni per tutte le insidie ​​comuni e casi complessi nel Wiki di tag PDO

Tuttavia, tutti coloro che parlano di estensioni mancano sempre il 2 fatti importanti su Mysqli e DOP:

  1. Discorso preparato non è un proiettile d'argento. Esistono identificatori dinamici che non possono essere associati usando istruzioni preparate. Esistono query dinamiche con un numero sconosciuto di parametri che rende la query la creazione di un compito difficile.

  2. Né mysqli_ * né le funzioni PDO avrebbero dovuto apparire nel codice dell'applicazione.
    Dovrebbe esserci un strato di astrazione tra loro e il codice dell'applicazione, che eseguirà tutto il lavoro sporco di associazione, looping, gestione degli errori, ecc. all'interno, rendendo il codice dell'applicazione ASCIUTTO e pulito. Soprattutto per i casi complessi come la costruzione di query dinamiche.

Quindi, passare a PDO o mysqli non è sufficiente. Si deve usare un ORM, o un generatore di query, o qualsiasi altra classe di astrazione del database invece di chiamare funzioni API raw nel loro codice.
E viceversa - se hai un livello di astrazione tra il codice dell'applicazione e l'API mysql - in realtà non importa quale motore viene utilizzato. Puoi usare mysql ext fino a quando non viene deprecato e quindi riscrivere facilmente la tua classe di astrazione su un altro motore, avere tutto il codice dell'applicazione intatto.

Ecco alcuni esempi basati sul mio classe safemysql per mostrare come una classe di astrazione del genere dovrebbe essere:

$city_ids = array(1,2,3);
$cities   = $db->getCol("SELECT name FROM cities WHERE is IN(?a)", $city_ids);

Confronta questa singola linea con quantità di codice che ti servirà con PDO.
Quindi confrontare con pazzesca quantità di codice avrai bisogno di istruzioni preparate con Mysqli. Si noti che gestione degli errori, profilazione, registrazione delle query sono già integrati e in esecuzione.

$insert = array('name' => 'John', 'surname' => "O'Hara");
$db->query("INSERT INTO users SET ?u", $insert);

Confrontalo con i soliti inserti PDO, quando ogni singolo nome di campo viene ripetuto da sei a dieci volte - in tutti questi numerosi segnaposti, collegamenti e definizioni di query.

Un altro esempio:

$data = $db->getAll("SELECT * FROM goods ORDER BY ?n", $_GET['order']);

Difficilmente puoi trovare un esempio per DOP per gestire un caso pratico.
E sarà troppo prolisso e probabilmente non sicuro.

Quindi, ancora una volta - non è solo il raw driver dovrebbe essere la vostra preoccupazione, ma la classe di astrazione, utile non solo per gli esempi stupidi del manuale per principianti, ma per risolvere qualsiasi problema di vita reale.


98
2017-10-12 13:23



Ci sono molte ragioni, ma forse la più importante è che quelle funzioni incoraggiano pratiche di programmazione non sicure perché non supportano le istruzioni preparate. Le istruzioni preparate aiutano a prevenire attacchi di SQL injection.

Quando si usa mysql_* funzioni, è necessario ricordare di eseguire i parametri forniti dall'utente attraverso mysql_real_escape_string(). Se ti dimentichi in un solo posto o se sfuggi solo a una parte dell'input, il tuo database potrebbe essere soggetto ad attacchi.

Utilizzando le dichiarazioni preparate in PDO o mysqli farà in modo che questo tipo di errori di programmazione siano più difficili da realizzare.


87
2017-10-12 13:24