多次使用绑定参数

2021/01/14 06:21 · php ·  · 0评论

我正在尝试为我的数据库实现一个非常基本的搜索引擎,其中用户可能包含各种信息。搜索本身包含几个联合选择,其中结果始终合并为3列。

但是,返回的数据是从不同的表中获取的。

每个查询都使用$ term进行匹配,并将其绑定到“:term”作为预备参数。

现在,该手册说:

调用PDOStatement :: execute()时,对于要传递给语句的每个值,必须包含一个唯一的参数标记。您不能在准备好的语句中两次使用相同名称的命名参数标记。

我认为,不是用:termX替换每个:term参数(x表示term = n ++),必须有一个更好的解决方案?

还是我只需要绑定X个:termX?

编辑发布我对此的解决方案:

$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);

好了,这是一个示例。我没有时间使用sqlfiddle,但是如有必要,我会在后面添加一个。

(
    SELECT
        t1.`name` AS resultText
    FROM table1 AS t1
    WHERE
        t1.parent = :userID
        AND
        (
            t1.`name` LIKE :term
            OR
            t1.`number` LIKE :term
            AND
            t1.`status` = :flagStatus
        )
)
UNION
(
    SELECT
        t2.`name` AS resultText
    FROM table2 AS t2
    WHERE
        t2.parent = :userParentID
        AND
        (
            t2.`name` LIKE :term
            OR
            t2.`ticket` LIKE :term
            AND
            t1.`state` = :flagTicket
        )
)

我已经遇到过相同的问题好几次了,我想我已经找到了一个非常简单且不错的解决方案。万一我想多次使用参数,我只是将它们存储到MySQL中User-Defined Variable

这使代码更具可读性,并且您在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查询-但恕我直言,这是完全值得的。

由于
User-Defined Variables在MySQL中是会话绑定的,因此也不必担心变量会@term在多用户环境中产生副作用。

我创建了两个函数来通过重命名重复使用的术语来解决该问题。一种用于重命名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;
    }

用法示例:

$lstrSqlQuery= $this->prepareSqlForMultipleBindings($pstrSqlQuery, $paParameters);
$laParameters= $this->prepareParamtersForMultipleBindings($pstrSqlQuery, $paParameters);
$this->prepare($lstrSqlQuery)->execute($laParameters);

有关变量命名的说明:
p:参数,l:函数

str:字符串中的
局部,n:数字,a:数组,m:混合

自问题发布以来,我不知道它是否已更改,但是现在查看手册,它说:

在准备好的语句中,您不能多次使用相同名称的命名参数标记,除非仿真模式为on

http://php.net/manual/en/pdo.prepare.php-(强调我的)

因此,从技术上讲,允许通过使用模拟的准备$PDO_obj->setAttribute( PDO::ATTR_EMULATE_PREPARES, true );工作也将起作用;尽管这可能不是一个好主意(如本答案中所述,关闭模拟的准备好的语句是防止某些注入攻击的一种方法;尽管有些人相反地写道,无论是否模拟准备,安全性都没有区别(我不知道,但是我不认为后者会想到前面提到的攻击。)

为了完整起见,我添加此答案。当我在正在使用的网站上关闭emulate_prepares时,由于使用类似的查询(SELECT ... FROM tbl WHERE (Field1 LIKE :term OR Field2 LIKE :term) ...),导致搜索中断,并且运行良好,直到我明确设置PDO::ATTR_EMULATE_PREPARESfalse,然后开始失败。

(PHP 5.4.38,MySQL 5.1.73 FWIW)

这个问题是什么提示我不能在同一查询中两次使用命名参数(这对我来说似乎是违反直觉的,但是很好)。(即使我多次浏览该页面,我仍然以某种方式错过了它。)

一个可行的解决方案:

$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();

用户定义变量是将值绑定到查询上的多次使用相同变量的一种方法,是的,效果很好。

//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);
    }
}

这是一个简单的工作解决方案!

希望这会在不久的将来对某人有所帮助。

本文地址:http://php.askforanswer.com/duocishiyongbangdingcanshu.html
文章标签: ,   ,   ,  
版权声明:本文为原创文章,版权归 admin 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!