这就是我到目前为止读到的关于PDO::ATTR_EMULATE_PREPARES的文章
我不知道这些说法有多真实。在选择MySQL接口时,我最关心的是防止SQL注入。第二个问题是表现。
我的应用程序目前使用过程MySQLi (没有准备好的语句),并且相当多地使用查询缓存。它很少在单个请求中重用已准备好的语句。为了获得已准备语句的命名参数和安全性,我开始迁移到PDO。
我用的是MySQL 5.1.61和PHP 5.3.2
我是否应该让PDO::ATTR_EMULATE_PREPARES启用?是否有一种方法既具有查询缓存的性能,又具有准备好的语句的安全性?
发布于 2012-05-04 19:48:39
要回答你的担忧:
PREPARE和EXECUTE语句的>= 5.1.21 ) can use prepared statements in the query cache。因此,您的MySQL+PHP版本可以在查询缓存中使用准备好的语句。但是,请仔细注意MySQL文档中缓存查询结果的注意事项。有许多类型的查询不能被缓存,或者即使它们被缓存,也是无用的。根据我的经验,查询缓存通常不是一个很大的胜利。查询和架构需要特殊的构造才能最大限度地利用缓存。通常,从长远来看,应用程序级缓存是必要的。EMULATE_PREPARES设置如何,相同的PDO代码都同样容易受到注入攻击的攻击(或不受攻击)。唯一的区别是参数替换发生在哪里--使用EMULATE_PREPARES,它发生在PDO库中;没有EMULATE_PREPARES,它发生在MySQL EMULATE_PREPARES上,您可能会在准备时而不是在执行时得到语法错误;对于EMULATE_PREPARES,只有在执行时才会得到语法错误,因为在执行之前,PDO没有提供给MySQL的查询。请注意,这会影响您将要编写的代码!尤其是如果您使用的是PDO::ERRMODE_EXCEPTION!另一项考虑是:
prepare()有固定的成本(使用本机准备语句),因此带有本机准备语句的prepare();execute()可能比使用模拟准备语句发出简单文本查询要慢一些。在许多数据库系统中,prepare()的查询计划也是缓存的,并且可以与多个连接共享,但我认为MySQL不会这样做。因此,如果不将准备好的语句对象用于多个查询,则总体执行速度可能会更慢。作为最后的推荐,我认为对于较早版本的MySQL+PHP,您应该模仿准备好的语句,但是对于最近的版本,您应该关闭模拟。
在编写了一些使用PDO的应用程序之后,我制作了一个PDO连接函数,它具有我认为最好的设置。您可能应该使用类似的东西或调整到您喜欢的设置:
/**
* 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;
}发布于 2019-11-13 03:47:12
我很惊讶没有人提到关闭仿真的最大原因之一。在进行仿真之后,PDO将所有整数和浮点数作为字符串返回。当您关闭模拟时,MySQL中的整数和浮点数在PHP中变成整数和浮点数。
有关更多信息,请参见该问题的公认答案:PHP + PDO + MySQL: how do I return integer and numeric columns from MySQL as integers and numerics in PHP?。
发布于 2016-09-29 19:36:22
当您的PDO::ATTR_EMULATE_PREPARES pdo_mysql没有针对mysqlnd编译时,请注意禁用mysqlnd(打开本机准备)。
由于旧libmysql与某些函数不完全兼容,因此可能会导致奇怪的错误,例如:
LOCK TABLES (它抛出SQLSTATE[HY000]: General error: 2030 This command is not supported in the prepared statement protocol yet异常),mysqlnd或模拟准备)h 214G 215,这样就会自动为您工作,并且不会与mysql服务器同步。当迁移到使用libmysql作为pdo_mysql模块的其他服务器时,我在简单的项目中发现了这些bug。也许还有更多的虫子,我不知道。我还在新鲜的64位debian上进行了测试,所有列出的bug在我apt-get install php5-mysql时都会出现,当我apt-get install php5-mysqlnd时会消失。
当PDO::ATTR_EMULATE_PREPARES设置为true时(默认情况下)-这些bug无论如何都不会发生,因为PDO在这种模式下根本不使用准备好的语句。因此,如果您使用基于pdo_mysql的libmysql ("mysqlnd“子字符串不出现在phpinfo中pdo_mysql部分的"Client版本”字段中),则不应该关闭PDO::ATTR_EMULATE_PREPARES。
https://stackoverflow.com/questions/10113562
复制相似问题