Php 多次使用绑定参数
我正在尝试为我的数据库实现一个非常基本的搜索引擎,用户可以在其中包含不同类型的信息。搜索本身由两个联合选择组成,其中结果始终合并为3列 但是,返回的数据是从不同的表中提取的 每个查询都使用$term进行匹配,我已将其绑定到“:term”作为准备好的参数 现在,手册上说: 调用PDOStatement::execute()时,必须为要传递到语句中的每个值包含一个唯一的参数标记。在准备好的语句中,不能两次使用同名的命名参数标记 我认为应该有更好的解决方案,而不是用:termX(x代表term=n++)替换每个:term参数 或者我只需要绑定X个:termX 编辑将我的解决方案发布到此:Php 多次使用绑定参数,php,mysql,sql,pdo,Php,Mysql,Sql,Pdo,我正在尝试为我的数据库实现一个非常基本的搜索引擎,用户可以在其中包含不同类型的信息。搜索本身由两个联合选择组成,其中结果始终合并为3列 但是,返回的数据是从不同的表中提取的 每个查询都使用$term进行匹配,我已将其绑定到“:term”作为准备好的参数 现在,手册上说: 调用PDOStatement::execute()时,必须为要传递到语句中的每个值包含一个唯一的参数标记。在准备好的语句中,不能两次使用同名的命名参数标记 我认为应该有更好的解决方案,而不是用:termX(x代表term=n++
$query = "SELECT ... FROM table WHERE name LIKE :term OR number LIKE :term";
$term = "hello world";
$termX = 0;
$query = preg_replace_callback("/\:term/", function ($matches) use (&$termX) { $termX++; return $matches[0] . ($termX - 1); }, $query);
$pdo->prepare($query);
for ($i = 0; $i < $termX; $i++)
$pdo->bindValue(":term$i", "%$term%", PDO::PARAM_STR);
一个有效的解决办法:
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, TRUE);
$query = "SELECT * FROM table WHERE name LIKE :term OR number LIKE :term";
$term = "hello world";
$stmt = $pdo->prepare($query);
$stmt->execute(array('term' => "%$term%"));
$data = $stmt->fetchAll();
我创建了两个函数,通过重命名双用术语来解决这个问题。一个用于重命名SQL,另一个用于重命名绑定
/**
* Changes double bindings to seperate ones appended with numbers in bindings array
* example: :term will become :term_1, :term_2, .. when used multiple times.
*
* @param string $pstrSql
* @param array $paBindings
* @return array
*/
private function prepareParamtersForMultipleBindings($pstrSql, array $paBindings = array())
{
foreach($paBindings as $lstrBinding => $lmValue)
{
// $lnTermCount= substr_count($pstrSql, ':'.$lstrBinding);
preg_match_all("/:".$lstrBinding."\b/", $pstrSql, $laMatches);
$lnTermCount= (isset($laMatches[0])) ? count($laMatches[0]) : 0;
if($lnTermCount > 1)
{
for($lnIndex = 1; $lnIndex <= $lnTermCount; $lnIndex++)
{
$paBindings[$lstrBinding.'_'.$lnIndex] = $lmValue;
}
unset($paBindings[$lstrBinding]);
}
}
return $paBindings;
}
/**
* Changes double bindings to seperate ones appended with numbers in SQL string
* example: :term will become :term_1, :term_2, .. when used multiple times.
*
* @param string $pstrSql
* @param array $paBindings
* @return string
*/
private function prepareSqlForMultipleBindings($pstrSql, array $paBindings = array())
{
foreach($paBindings as $lstrBinding => $lmValue)
{
// $lnTermCount= substr_count($pstrSql, ':'.$lstrBinding);
preg_match_all("/:".$lstrBinding."\b/", $pstrSql, $laMatches);
$lnTermCount= (isset($laMatches[0])) ? count($laMatches[0]) : 0;
if($lnTermCount > 1)
{
$lnCount= 0;
$pstrSql= preg_replace_callback('(:'.$lstrBinding.'\b)', function($paMatches) use (&$lnCount) {
$lnCount++;
return sprintf("%s_%d", $paMatches[0], $lnCount);
} , $pstrSql, $lnLimit = -1, $lnCount);
}
}
return $pstrSql;
}
变量命名说明:p:参数,l:函数中的局部
str:string,n:numeric,a:array,m:mixed我已经反复讨论过同一个问题好几次了,我认为我已经找到了一个非常简单而好的解决方案。如果我想多次使用参数,我只需将它们存储到MySQL
用户定义变量中
这使代码更具可读性,您不需要PHP中的任何附加函数:
$sql = "SET @term = :term";
try
{
$stmt = $dbh->prepare($sql);
$stmt->bindValue(":term", "%$term%", PDO::PARAM_STR);
$stmt->execute();
}
catch(PDOException $e)
{
// error handling
}
$sql = "SELECT ... FROM table WHERE name LIKE @term OR number LIKE @term";
try
{
$stmt = $dbh->prepare($sql);
$stmt->execute();
$stmt->fetchAll();
}
catch(PDOException $e)
{
//error handling
}
唯一的缺点可能是您需要进行额外的MySQL查询,但我认为这完全是值得的。由于
用户定义变量
在MySQL中是会话绑定的,因此也无需担心变量@term
在多用户环境中会产生副作用 用户定义变量是一种方法,可以多次使用同一个变量将值绑定到查询,是的,效果很好
//Setting this doesn't work at all, I tested it myself
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, TRUE);
//Example values
var $query = "select * from test_table where param_name_1 = :parameter and param_name_2 = :parameter";
var param_name = ":parameter";
var param_value = "value";
//Wrap these lines of codes in a function as needed sending 3 params $query, $param_name and $param_value.
//You can also use an array as I do!
//Lets check if the param is defined in the query
if (strpos($query, $param_name) !== false)
{
//Get the number of times the param appears in the query
$ocurrences = substr_count($query, $param_name);
//Loop the number of times the param is defined and bind the param value as many times needed
for ($i = 0; $i < $ocurrences; $i++)
{
//Let's bind the value to the param
$statement->bindValue($param_name, $param_value);
}
}
我根本不想像这里发布的解决方案那样使用用户定义的变量我不想像这里发布的另一个解决方案那样进行参数重命名。因此,我的解决方案不需要使用用户定义的变量,也不需要用更少的代码重命名查询中的任何内容,而且它不关心查询中使用参数的次数。我在我所有的项目中都使用它,而且效果很好
//Setting this doesn't work at all, I tested it myself
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, TRUE);
//Example values
var $query = "select * from test_table where param_name_1 = :parameter and param_name_2 = :parameter";
var param_name = ":parameter";
var param_value = "value";
//Wrap these lines of codes in a function as needed sending 3 params $query, $param_name and $param_value.
//You can also use an array as I do!
//Lets check if the param is defined in the query
if (strpos($query, $param_name) !== false)
{
//Get the number of times the param appears in the query
$ocurrences = substr_count($query, $param_name);
//Loop the number of times the param is defined and bind the param value as many times needed
for ($i = 0; $i < $ocurrences; $i++)
{
//Let's bind the value to the param
$statement->bindValue($param_name, $param_value);
}
}
//示例值
var$query=“从test_表中选择*,其中param_name_1=:parameter和param_name_2=:parameter”;
var param_name=“:parameter”;
var param_value=“value”;
//根据需要将这些代码行包装到函数中,并发送3个参数$query、$param_name和$param_value。
//你也可以像我一样使用数组!
//让我们检查查询中是否定义了参数
if(strpos($query,$param_name)!==false)
{
//获取参数在查询中出现的次数
$ocurrences=substr\u count($query,$param\u name);
//循环定义参数的次数,并根据需要多次绑定参数值
对于($i=0;$i<$ocurrences;$i++)
{
//让我们将该值绑定到参数
$statement->bindValue($param_name,$param_value);
}
}
这里有一个简单的工作解决方案
希望这能在不久的将来对某人有所帮助。我不知道自问题发布以来它是否有所改变,但现在查看手册时,它说: 除非启用了模拟模式,否则在准备好的语句中不能多次使用同名的命名参数标记 ——(我的重点。) 因此,从技术上讲,允许使用
$PDO_obj->setAttribute(PDO::ATTR_EMULATE_prepares,true)进行模拟准备代码>也会起作用;尽管这可能不是一个好主意(如中所讨论的,关闭模拟的prepared语句是防止某些注入攻击的一种方法;尽管不管是否模拟prepared语句,都不会影响安全性。(我不知道,但我不认为后者考虑到了前面提到的攻击。)
为了完整起见,我添加了这个答案;当我在我正在工作的站点上关闭“模拟”时,它导致搜索中断,因为它使用了一个类似的查询(SELECT…FROM tbl WHERE(Field1 LIKE:term或Field2 LIKE:term).
),它工作正常,直到我显式地将PDO::ATTR_EMULATE_PREPARES
设置为false
,然后它开始失败
(PHP 5.4.38,MySQL 5.1.73 FWIW)
这个问题告诉我不能在同一个查询中使用命名参数两次(这对我来说似乎是违反直觉的,但很好)。(尽管我在手册中多次查看了该页面,但不知何故,我在手册中遗漏了这一点。)首先,为什么一定要有更好的解决方案?(顺便说一句,您忘记了详细说明在您的情况下更好的含义)其次,为什么未命名的参数不适合您?(参见示例#2准备一个带有问号参数的SQL语句)@hakre未命名参数带来了相同的问题,因为绑定值的数量必须与?
相同。在我的情况下,更好的解决方案是->bindValue(':term',$term)
和use:term多次使用,而不是首先构建查询,然后对其进行解析,以便最终能够准备查询。我想?
只会使解析最终查询变得更困难,因为还有其他参数类型。那么,为什么一定要有更好的解决方案呢?因为你想要它?我感觉你的追求ion在寻找非站点资源或库时是离题的。我看不到潜在的编程问题。对不起。@hakre你是真的还是怎么的?潜在的编程问题是API可能缺少一个关键功能。这就是我试图弄明白的。我对扩展PDO没有问题,但我不觉得有问题