PDO MySQL:使用PDO :: ATTR
这是我迄今为止读到的有关PDO::ATTR_EMULATE_PREPARES
:
我不知道这些陈述是多么的真实。 在选择MySQL接口时,我最关心的是防止SQL注入。 第二个问题是性能。
我的应用程序目前使用程序MySQLi(没有准备好的语句),并且使用查询缓存很多。 它很少会在单个请求中重复使用准备好的语句。 我开始向PDO转移已命名参数和已准备语句的安全性。
我正在使用MySQL 5.1.61
和PHP 5.3.2
我应该离开PDO::ATTR_EMULATE_PREPARES
启用? 有没有办法同时具备查询缓存的性能和预准备语句的安全性?
回答你的担忧:
对于PREPARE
和EXECUTE
语句,MySQL> = 5.1.17(或> = 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()
可能比使用模拟准备语句发出纯文本查询要慢一些。 在许多数据库系统上, 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;
}
当您的PHP pdo_mysql
未针对mysqlnd
编译时,请注意禁用PDO::ATTR_EMULATE_PREPARES
(将本机准备就绪)。
由于旧的libmysql
与某些函数不完全兼容,因此可能导致奇怪的错误,例如:
PDO::PARAM_INT
(0x12345678AB将在64位计算机上裁剪为0x345678AB)时丢失64位整数的最高有效位, LOCK TABLES
这样的简单查询(它会抛出SQLSTATE[HY000]: General error: 2030 This command is not supported in the prepared statement protocol yet
异常情况下SQLSTATE[HY000]: General error: 2030 This command is not supported in the prepared statement protocol yet
) mysqlnd
或模拟准备它会自动执行此操作并且不会与mysql服务器不同步) 当我将这些错误迁移到使用libmysql
for 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”子简化版,出现在“客户端API版本”的现场pdo_mysql
中的phpinfo部分) -你不应该把PDO::ATTR_EMULATE_PREPARES
关闭。
我会在运行5.1时关闭模拟准备,这意味着PDO将利用本地准备好的语句功能。
PDO_MYSQL将利用MySQL 4.1及更高版本中的本地准备语句支持。 如果您使用的是旧版本的mysql客户端库,PDO将为您模拟它们。
http://php.net/manual/en/ref.pdo-mysql.php
为了准备好的命名语句和更好的API,我抛弃了针对PDO的MySQLi。
但是,为了保持平衡,PDO的执行速度比MySQLi慢得多,但这是需要牢记的。 当我做出选择时,我知道这一点,并且决定使用更好的API并使用行业标准比使用将您与特定引擎联系起来的可以忽略的更快的库更重要。 FWIW我认为PHP团队也将对PDO和MySQLi的未来表示乐观。
链接地址: http://www.djcxy.com/p/93779.html