sql >> Base de Datos >  >> RDS >> Mysql

PDO MySQL:¿Usar PDO::ATTR_EMULATE_PREPARES o no?

Para responder a sus inquietudes:

  1. MySQL>=5.1.17 (o>=5.1.21 para PREPARE y EXECUTE declaraciones) puede usar declaraciones preparadas en la caché de consultas . Entonces, su versión de MySQL+PHP puede usar declaraciones preparadas con el caché de consultas. Sin embargo, tome nota de las advertencias sobre el almacenamiento en caché de los resultados de las consultas en la documentación de MySQL. Hay muchos tipos de consultas que no se pueden almacenar en caché o que son inútiles aunque se almacenen en caché. En mi experiencia, el caché de consultas no suele ser una gran victoria de todos modos. Las consultas y los esquemas necesitan una construcción especial para aprovechar al máximo la memoria caché. A menudo, el almacenamiento en caché a nivel de aplicación acaba siendo necesario a largo plazo.

  2. Las preparaciones nativas no hacen ninguna diferencia por seguridad. Las declaraciones pseudo-preparadas aún escaparán de los valores de los parámetros de consulta, solo se realizarán en la biblioteca PDO con cadenas en lugar de en el servidor MySQL usando el protocolo binario. En otras palabras, el mismo código PDO será igualmente vulnerable (o no vulnerable) a los ataques de inyección independientemente de su EMULATE_PREPARES entorno. La única diferencia es dónde ocurre el reemplazo del parámetro:con EMULATE_PREPARES , ocurre en la biblioteca PDO; sin EMULATE_PREPARES , ocurre en el servidor MySQL.

  3. Sin EMULATE_PREPARES puede obtener errores de sintaxis en tiempo de preparación en lugar de en tiempo de ejecución; con EMULATE_PREPARES solo obtendrá errores de sintaxis en el momento de la ejecución porque PDO no tiene una consulta para dar a MySQL hasta el momento de la ejecución. Tenga en cuenta que esto afecta el código que escribirá ! Especialmente si está utilizando PDO::ERRMODE_EXCEPTION !

Una consideración adicional:

  • Hay un costo fijo para un prepare() (usando declaraciones preparadas nativas), por lo que prepare();execute() con declaraciones preparadas nativas puede ser un poco más lento que emitir una consulta textual simple usando declaraciones preparadas emuladas. En muchos sistemas de bases de datos, el plan de consulta para un prepare() también se almacena en caché y se puede compartir con varias conexiones, pero no creo que MySQL haga esto. Entonces, si no reutiliza su objeto de declaración preparado para múltiples consultas, su ejecución general puede ser más lenta.

Como recomendación final , creo que con versiones anteriores de MySQL+PHP, debe emular declaraciones preparadas, pero con sus versiones más recientes debe desactivar la emulación.

Después de escribir algunas aplicaciones que usan PDO, hice una función de conexión de PDO que tiene lo que creo que es la mejor configuración. Probablemente debería usar algo como esto o modificar su configuración preferida:

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