php - query - pdo::fetch_assoc
PDO MySQL: ¿usar PDO:: ATTR_EMULATE_PREPARES o no? (4)
Desactivaría emular los preparativos mientras ejecuta 5.1, lo que significa que PDO aprovechará la funcionalidad de declaración preparada nativa.
PDO_MYSQL aprovechará el soporte de declaraciones preparadas nativas presentes en MySQL 4.1 y versiones posteriores. Si está utilizando una versión anterior de las bibliotecas de cliente de mysql, PDO las emulará por usted.
http://php.net/manual/en/ref.pdo-mysql.php
Me deshice de MySQLi para PDO para las declaraciones nombradas preparadas y la mejor API.
Sin embargo, para ser equilibrado, el PDO se ejecuta de manera insignificante más lenta que MySQLi, pero es algo a tener en cuenta. Sabía esto cuando hice la elección, y decidí que una mejor API y el uso del estándar de la industria era más importante que usar una biblioteca a menor velocidad que lo vincule a un motor en particular. FWIW Creo que el equipo de PHP también está mirando favorablemente a PDO sobre MySQLi para el futuro también.
Esto es lo que he leído hasta ahora sobre PDO::ATTR_EMULATE_PREPARES
:
- La emulación de preparación de PDO es mejor para el rendimiento ya que la preparación nativa de MySQL omite la caché de consultas .
- La preparación nativa de MySQL es mejor para la seguridad (prevención de inyección SQL) .
- La preparación nativa de MySQL es mejor para informar errores .
No sé cuán cierta es cualquiera de estas afirmaciones. Mi mayor preocupación al elegir una interfaz MySQL es la prevención de la inyección SQL. La segunda preocupación es el rendimiento.
Mi aplicación actualmente utiliza procedimientos de MySQLi (sin declaraciones preparadas), y utiliza bastante el caché de consultas. Rara vez reutilizará las declaraciones preparadas en una sola solicitud. Inicié el cambio a PDO para los parámetros nombrados y la seguridad de las declaraciones preparadas.
Estoy usando MySQL 5.1.61
y PHP 5.3.2
¿Debo dejar PDO::ATTR_EMULATE_PREPARES
habilitado o no? ¿Hay alguna forma de tener tanto el rendimiento del caché de consultas como la seguridad de las declaraciones preparadas?
Para responder a sus inquietudes:
MySQL> = 5.1.17 (o> = 5.1.21 para las instrucciones
PREPARE
yEXECUTE
) puede usar declaraciones preparadas en el caché de consultas . Entonces, su versión de MySQL + PHP puede usar declaraciones preparadas con el caché de consultas. Sin embargo, tenga en cuenta las advertencias para el almacenamiento en caché de los resultados de la consulta en la documentación de MySQL. Hay muchos tipos de consultas que no pueden almacenarse en caché o que son inútiles aunque estén en caché. En mi experiencia, el caché de consultas a menudo no es una ganancia muy grande. Las consultas y los esquemas necesitan una construcción especial para aprovechar al máximo el caché. A menudo, el almacenamiento en caché a nivel de aplicación termina siendo necesario de todos modos en el largo plazo.Native se prepara no hace ninguna diferencia por seguridad. Las declaraciones pseudopreparadas seguirán escapándose de los valores de los parámetros de consulta, simplemente se realizará en la biblioteca PDO con cadenas en lugar de hacerlo en el servidor MySQL utilizando 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 configuración
EMULATE_PREPARES
. La única diferencia es donde ocurre la sustitución de parámetros: conEMULATE_PREPARES
, ocurre en la biblioteca de PDO; sinEMULATE_PREPARES
, ocurre en el servidor MySQL.Sin
EMULATE_PREPARES
puede obtener errores de sintaxis en tiempo de preparación en lugar de en tiempo de ejecución; conEMULATE_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 va a escribir ! ¡Especialmente si está usandoPDO::ERRMODE_EXCEPTION
!
Una consideración adicional:
- Hay un costo fijo para un
prepare()
(usando declaraciones nativas preparadas), por lo que unprepare();execute()
con declaraciones preparadas nativas puede ser un poco más lento que emitir una consulta de texto sin formato usando declaraciones preparadas emuladas. En muchos sistemas de bases de datos, el plan de consulta para unprepare()
se almacena en caché y puede compartirse con múltiples conexiones, pero no creo que MySQL lo haga. Por lo tanto, si no reutiliza su objeto de extracto preparado para múltiples consultas, su ejecución general puede ser más lenta.
Como recomendación final , creo que con las versiones anteriores de MySQL + PHP, debe emular las 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 PDO que tiene lo que creo que son las mejores configuraciones. Probablemente deberías usar algo como esto o modificar tu 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;
}
Recomiendo habilitar las llamadas a PREPARE
base de datos real ya que la emulación no capta todo ..., por ejemplo, preparará INSERT;
!
var_dump($dbh->prepare(''INSERT;''));
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
var_dump($dbh->prepare(''INSERT;''));
La salida
object(PDOStatement)#2 (1) {
["queryString"]=>
string(7) "INSERT;"
}
bool(false)
Me encantaría tomar un golpe de rendimiento para el código que realmente funciona.
FWIW
Versión PHP: PHP 5.4.9-4ubuntu2.4 (cli)
Versión de MySQL: 5.5.34-0ubuntu0
Tenga cuidado con la desactivación de PDO::ATTR_EMULATE_PREPARES
(convirtiendo nativos se prepara) cuando su PHP pdo_mysql
no se compila contra mysqlnd
.
Como el antiguo libmysql
no es totalmente compatible con algunas funciones, puede provocar errores extraños, por ejemplo:
- Perder los bits más significativos para enteros de 64 bits cuando se vincula como
PDO::PARAM_INT
(0x12345678AB se recortará en 0x345678AB en una máquina de 64 bits) - Incapacidad para realizar consultas simples como
LOCK TABLES
(arrojaSQLSTATE[HY000]: General error: 2030 This command is not supported in the prepared statement protocol yet
excepción) - Es necesario buscar todas las filas del resultado o cerrar el cursor antes de la siguiente consulta (con
mysqlnd
omysqlnd
prepara automáticamente, esto funciona para usted y no se desincroniza con el servidor mysql)
Estos errores los descubrí en mi proyecto simple cuando migré a otro servidor que usó libmysql
para el módulo pdo_mysql
. Tal vez hay muchos más errores, no sé. También probé en Debian jessie de 64 bits, todos los errores enumerados se producen cuando tengo apt-get install php5-mysql
, y desaparecen cuando apt-get install php5-mysqlnd
.
Cuando PDO::ATTR_EMULATE_PREPARES
se establece en verdadero (como valor predeterminado), estos errores no se producen de todos modos, porque PDO no utiliza declaraciones preparadas en absoluto en este modo. Entonces, si usa pdo_mysql
basado en libmysql
(la subcadena "mysqlnd" no aparece en el campo "Client API version" de la sección pdo_mysql en phpinfo) - no debe desactivar PDO::ATTR_EMULATE_PREPARES
.