这是我到目前为止所读到的PDO::ATTR_EMULATE_PREPARES:
我不知道这些陈述是多么真实.选择MySQL接口时我最担心的是阻止SQL注入.第二个问题是表现.
我的应用程序目前使用过程MySQLi(没有预处理语句),并且使用了很多查询缓存.它很少会在单个请求中重复使用预准备语句.我开始转向PDO以获取已准备好的语句的命名参数和安全性.
我正在使用MySQL 5.1.61和PHP 5.3.2
我应该PDO::ATTR_EMULATE_PREPARES启用还是不启用?有没有办法既具有查询缓存的性能又具有预准备语句的安全性?
Fra*_*ila 105
回答你的疑虑:
MySQL> = 5.1.17(对于PREPARE和EXECUTE语句,> = 5.1.21 )可以在查询缓存中使用预准备语句.因此,您的MySQL + PHP版本可以将预准备语句与查询缓存一起使用.但是,请仔细注意在MySQL文档中缓存查询结果的注意事项.有许多类型的查询无法缓存,或者即使它们被缓存也没用.根据我的经验,查询缓存通常不是一个非常大的胜利.查询和模式需要特殊构造才能最大限度地利用缓存.从长远来看,无论如何,应用程序级缓存通常都是必要的.
原生准备对安全性没有任何影响.伪预处理语句仍然会转义查询参数值,它只会在PDO库中使用字符串而不是使用二进制协议在MySQL服务器上完成.换句话说,无论您的EMULATE_PREPARES设置如何,相同的PDO代码都会同样容易受到攻击(或不易受攻击).唯一的区别是参数替换发生的地方 - EMULATE_PREPARES它发生在PDO库中; 没有EMULATE_PREPARES,它发生在MySQL服务器上.
没有EMULATE_PREPARES你可能会在准备时而不是在执行时获得语法错误; 与EMULATE_PREPARES您只会在执行时获得语法错误,因为PDO没有查询在执行时间之前提供给MySQL.请注意,这会影响您要编写的代码!特别是如果你正在使用PDO::ERRMODE_EXCEPTION!
另一个考虑因素:
prepare()(使用本机预处理语句)有固定成本,因此prepare();execute()使用本机预处理语句可能比使用模拟预准备语句发出纯文本查询要慢一些.在许多数据库系统上,a的查询计划也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;
}
Run Code Online (Sandbox Code Playgroud)
dal*_*lin 15
我很惊讶没有人提到关闭仿真的最大原因之一。启用仿真后,PDO 将所有整数和浮点数作为字符串返回。当您关闭仿真时,MySQL 中的整数和浮点数将变成 PHP 中的整数和浮点数。
有关更多信息,请参阅此问题的公认答案:PHP + PDO + MySQL:如何在 PHP 中将整数和数字列从 MySQL 返回为整数和数字?.
Sag*_*ter 10
PDO::ATTR_EMULATE_PREPARES当你的PHP pdo_mysql没有被编译时要小心禁用(打开本机准备)mysqlnd.
因为旧的libmysql与某些功能不完全兼容,所以它可能导致奇怪的错误,例如:
PDO::PARAM_INT(0x12345678AB将在64位机器上裁剪为0x345678AB)LOCK TABLES(它抛出SQLSTATE[HY000]: General error: 2030 This command is not supported in the prepared statement protocol yet异常)mysqlnd或模拟自动准备它,这对您有用,并且不会与mysql服务器不同步)当我迁移到libmysql用于pdo_mysql模块的其他服务器时,我在我的简单项目中发现了这些错误.也许还有更多的错误,我不知道.我还测试了新的64位debian jessie,所有列出的错误都出现在我身上apt-get install php5-mysql,并在我消失时消失apt-get install php5-mysqlnd.
当PDO::ATTR_EMULATE_PREPARES设置为true(默认值)时 - 无论如何都不会发生这些错误,因为PDO在此模式下根本不使用预准备语句.因此,如果您使用pdo_mysql基于libmysql("mysqlnd"子字符串不会出现pdo_mysql在phpinfo部分的"客户端API版本"字段中) - 您不应该PDO::ATTR_EMULATE_PREPARES关闭.
当你运行5.1时我会关闭模拟准备,这意味着PDO将利用本机预准备语句功能.
PDO_MYSQL将利用MySQL 4.1及更高版本中存在的本机预处理语句支持.如果您使用的是旧版本的mysql客户端库,PDO将为您模拟它们.
http://php.net/manual/en/ref.pdo-mysql.php
我为准备好的命名语句和更好的API抛弃了MySQLi for PDO.
然而,为了保持平衡,PDO的执行速度比MySQLi慢得多,但要记住这一点.当我做出选择时,我就知道这一点,并且认为使用更好的API并使用行业标准比使用一个可以忽略不计的更快的库将您与特定引擎联系起来更重要.FWIW我认为PHP团队也对未来的MySQLi也非常看好.
我建议启用真正的数据库PREPARE调用,因为仿真不会捕获所有内容..例如,它会准备好INSERT;!
var_dump($dbh->prepare('INSERT;'));
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
var_dump($dbh->prepare('INSERT;'));
Run Code Online (Sandbox Code Playgroud)
输出
object(PDOStatement)#2 (1) {
["queryString"]=>
string(7) "INSERT;"
}
bool(false)
Run Code Online (Sandbox Code Playgroud)
我很乐意在实际工作的代码中获得性能提升.
FWIW
PHP版本:PHP 5.4.9-4ubuntu2.4(cli)
MySQL版本:5.5.34-0ubuntu0
为什么将仿真切换为“假”?
这样做的主要原因是让数据库引擎做准备而不是 PDO 是查询和实际数据分开发送,这增加了安全性。这意味着当参数传递给查询时,试图将 SQL 注入其中的尝试将被阻止,因为 MySQL 准备好的语句仅限于单个查询。这意味着当在参数中传递第二个查询时,真正的准备好的语句将失败。
反对在准备与 PDO 中使用数据库引擎的主要论点是两次访问服务器——一次用于准备,另一次用于传递参数——但我认为增加的安全性是值得的。此外,至少在 MySQL 的情况下,查询缓存自 5.1 版以来就不再是问题。
https://tech.michaelseiler.net/2016/07/04/dont-emulate-prepared-statements-pdo-mysql/
作为记录
PDO::ATTR_EMULATE_PREPARES=true
它可能会产生令人讨厌的副作用。它可以将 int 值作为字符串返回。
PHP 7.4,带有 mysqlnd 的 pdo。
列:id
类型:整数
值:1
列:id
类型:字符串
值:“1”
在任何情况下,无论配置如何,十进制值始终返回字符串:-(
| 归档时间: |
|
| 查看次数: |
48622 次 |
| 最近记录: |