Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

PDO MySQL:Use PDO::ATTR_EMULATE_PREPARES ou não?


Para responder às suas preocupações:

  1. MySQL>=5.1.17 (ou>=5.1.21 para o PREPARE e EXECUTE declarações) pode usar declarações preparadas no cache de consulta . Portanto, sua versão do MySQL+PHP pode usar instruções preparadas com o cache de consulta. No entanto, anote cuidadosamente as advertências para armazenar em cache os resultados da consulta na documentação do MySQL. Existem muitos tipos de consultas que não podem ser armazenadas em cache ou que são inúteis, embora sejam armazenadas em cache. Na minha experiência, o cache de consulta não costuma ser uma grande vitória de qualquer maneira. Consultas e esquemas precisam de construção especial para aproveitar ao máximo o cache. Muitas vezes, o cache em nível de aplicativo acaba sendo necessário de qualquer maneira a longo prazo.

  2. A preparação nativa não faz diferença para a segurança. As instruções pseudo-preparadas ainda escaparão dos valores dos parâmetros de consulta, isso será feito apenas na biblioteca PDO com strings em vez de no servidor MySQL usando o protocolo binário. Em outras palavras, o mesmo código PDO será igualmente vulnerável (ou não vulnerável) a ataques de injeção, independentemente do seu EMULATE_PREPARES contexto. A única diferença é onde ocorre a substituição do parâmetro - com EMULATE_PREPARES , ocorre na biblioteca PDO; sem EMULATE_PREPARES , ocorre no servidor MySQL.

  3. Sem EMULATE_PREPARES você pode obter erros de sintaxe em tempo de preparação em vez de em tempo de execução; com EMULATE_PREPARES você só obterá erros de sintaxe no tempo de execução porque o PDO não tem uma consulta para dar ao MySQL até o tempo de execução. Observe que isso afeta o código que você escreverá ! Especialmente se você estiver usando PDO::ERRMODE_EXCEPTION !

Uma consideração adicional:
  • Há um custo fixo para um prepare() (usando instruções preparadas nativas), então um prepare();execute() com instruções preparadas nativas pode ser um pouco mais lento do que emitir uma consulta textual simples usando instruções preparadas emuladas. Em muitos sistemas de banco de dados, o plano de consulta para um prepare() também é armazenado em cache e pode ser compartilhado com várias conexões, mas não acho que o MySQL faça isso. Portanto, se você não reutilizar seu objeto de instrução preparado para várias consultas, sua execução geral poderá ser mais lenta.

Como recomendação final , acho que com versões mais antigas do MySQL+PHP, você deve emular instruções preparadas, mas com suas versões muito recentes você deve desativar a emulação.

Depois de escrever alguns aplicativos que usam PDO, criei uma função de conexão PDO que tem o que acho que são as melhores configurações. Você provavelmente deve usar algo assim ou ajustar suas configurações preferidas:
/**
 * 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;
}