Mysql
 sql >> Database >  >> RDS >> Mysql

PDO MySQL:usa PDO::ATTR_EMULATE_PREPARES o no?

Per rispondere alle tue preoccupazioni:

  1. MySQL>=5.1.17 (o>=5.1.21 per PREPARE e EXECUTE istruzioni) può utilizzare le istruzioni preparate nella cache delle query . Quindi la tua versione di MySQL+PHP può utilizzare istruzioni preparate con la cache delle query. Tuttavia, prendi nota delle avvertenze per la memorizzazione nella cache dei risultati delle query nella documentazione di MySQL. Esistono molti tipi di query che non possono essere memorizzate nella cache o che sono inutili anche se sono memorizzate nella cache. Nella mia esperienza, la cache delle query non è spesso comunque una grande vittoria. Le query e gli schemi richiedono una costruzione speciale per sfruttare al massimo la cache. Spesso la memorizzazione nella cache a livello di applicazione finisce per essere comunque necessaria a lungo termine.

  2. La preparazione nativa non fa alcuna differenza per la sicurezza. Le istruzioni pseudo-preparate sfuggiranno comunque ai valori dei parametri della query, verrà semplicemente eseguita nella libreria PDO con le stringhe anziché sul server MySQL utilizzando il protocollo binario. In altre parole, lo stesso codice PDO sarà ugualmente vulnerabile (o non vulnerabile) agli attacchi injection indipendentemente dal tuo EMULATE_PREPARES collocamento. L'unica differenza è dove avviene la sostituzione del parametro, con EMULATE_PREPARES , si trova nella libreria PDO; senza EMULATE_PREPARES , si verifica sul server MySQL.

  3. Senza EMULATE_PREPARES potresti ricevere errori di sintassi in fase di preparazione piuttosto che in fase di esecuzione; con EMULATE_PREPARES otterrai solo errori di sintassi al momento dell'esecuzione perché PDO non ha una query da fornire a MySQL fino al momento dell'esecuzione. Tieni presente che questo influisce sul codice che scriverai ! Soprattutto se stai usando PDO::ERRMODE_EXCEPTION !

Una considerazione aggiuntiva:

  • C'è un costo fisso per un prepare() (usando istruzioni preparate native), quindi un prepare();execute() con istruzioni preparate native potrebbe essere un po' più lento rispetto all'emissione di una query testuale semplice utilizzando istruzioni preparate emulate. Su molti sistemi di database il piano di query per un prepare() è anche memorizzato nella cache e può essere condiviso con più connessioni, ma non credo che MySQL lo faccia. Quindi, se non riutilizzi l'oggetto istruzione preparato per più query, l'esecuzione complessiva potrebbe essere più lenta.

Come consiglio finale , penso che con le versioni precedenti di MySQL+PHP dovresti emulare le istruzioni preparate, ma con le versioni più recenti dovresti disattivare l'emulazione.

Dopo aver scritto alcune app che utilizzano PDO, ho creato una funzione di connessione PDO che ha quelle che penso siano le migliori impostazioni. Probabilmente dovresti usare qualcosa di simile o modificare le tue impostazioni preferite:

/**
 * Return PDO handle for a MySQL connection using supplied settings
 *
 * Tries to do the right thing with different php and mysql versions.
 *
 * @param array $settings with keys: host, port, unix_socket, dbname, charset, user, pass. Some may be omitted or NULL.
 * @return PDO
 * @author Francis Avila
 */
function connect_PDO($settings)
{
    $emulate_prepares_below_version = '5.1.17';

    $dsndefaults = array_fill_keys(array('host', 'port', 'unix_socket', 'dbname', 'charset'), null);
    $dsnarr = array_intersect_key($settings, $dsndefaults);
    $dsnarr += $dsndefaults;

    // connection options I like
    $options = array(
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    );

    // connection charset handling for old php versions
    if ($dsnarr['charset'] and version_compare(PHP_VERSION, '5.3.6', '<')) {
        $options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES '.$dsnarr['charset'];
    }
    $dsnpairs = array();
    foreach ($dsnarr as $k => $v) {
        if ($v===null) continue;
        $dsnpairs[] = "{$k}={$v}";
    }

    $dsn = 'mysql:'.implode(';', $dsnpairs);
    $dbh = new PDO($dsn, $settings['user'], $settings['pass'], $options);

    // Set prepared statement emulation depending on server version
    $serverversion = $dbh->getAttribute(PDO::ATTR_SERVER_VERSION);
    $emulate_prepares = (version_compare($serverversion, $emulate_prepares_below_version, '<'));
    $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, $emulate_prepares);

    return $dbh;
}