Mysql
 sql >> Base de données >  >> RDS >> Mysql

PDO MySQL :Utiliser PDO : : ATTR_EMULATE_PREPARES ou non ?

Pour répondre à vos préoccupations :

  1. MySQL>=5.1.17 (ou>=5.1.21 pour le PREPARE et EXECUTE instructions) peut utiliser des instructions préparées dans le cache de requête . Ainsi, votre version de MySQL+PHP peut utiliser des instructions préparées avec le cache de requêtes. Cependant, notez attentivement les mises en garde concernant la mise en cache des résultats de requête dans la documentation MySQL. Il existe de nombreux types de requêtes qui ne peuvent pas être mises en cache ou qui sont inutiles même si elles sont mises en cache. D'après mon expérience, le cache de requêtes n'est pas souvent une très grande victoire de toute façon. Les requêtes et les schémas nécessitent une construction spéciale pour tirer le meilleur parti du cache. Souvent, la mise en cache au niveau de l'application finit par être nécessaire de toute façon à long terme.

  2. Les préparations natives ne font aucune différence pour la sécurité. Les instructions pseudo-préparées échapperont toujours aux valeurs des paramètres de requête, cela se fera simplement dans la bibliothèque PDO avec des chaînes plutôt que sur le serveur MySQL en utilisant le protocole binaire. En d'autres termes, le même code PDO sera également vulnérable (ou non vulnérable) aux attaques par injection, quel que soit votre EMULATE_PREPARES paramètre. La seule différence est où le paramètre de remplacement se produit--avec EMULATE_PREPARES , il se produit dans la bibliothèque PDO ; sans EMULATE_PREPARES , il se produit sur le serveur MySQL.

  3. Sans EMULATE_PREPARES vous pouvez obtenir des erreurs de syntaxe au moment de la préparation plutôt qu'au moment de l'exécution ; avec EMULATE_PREPARES vous n'obtiendrez des erreurs de syntaxe qu'au moment de l'exécution car PDO n'a pas de requête à donner à MySQL jusqu'au moment de l'exécution. Notez que cela affecte le code que vous écrirez ! Surtout si vous utilisez PDO::ERRMODE_EXCEPTION !

Une considération supplémentaire :

  • Il y a un coût fixe pour une prepare() (en utilisant des instructions préparées natives), donc un prepare();execute() avec des instructions préparées natives peut être un peu plus lent que l'émission d'une requête textuelle simple à l'aide d'instructions préparées émulées. Sur de nombreux systèmes de base de données, le plan de requête pour un prepare() est également mis en cache et peut être partagé avec plusieurs connexions, mais je ne pense pas que MySQL le fasse. Ainsi, si vous ne réutilisez pas votre objet d'instruction préparé pour plusieurs requêtes, votre exécution globale risque d'être plus lente.

En tant que recommandation finale , je pense qu'avec les anciennes versions de MySQL+PHP, vous devriez émuler des instructions préparées, mais avec vos versions très récentes, vous devriez désactiver l'émulation.

Après avoir écrit quelques applications qui utilisent PDO, j'ai créé une fonction de connexion PDO qui a ce que je pense être les meilleurs paramètres. Vous devriez probablement utiliser quelque chose comme ceci ou modifier vos paramètres préférés :

/**
 * 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;
}