source

바운드 매개변수를 여러 번 사용

ittop 2023. 10. 14. 10:35
반응형

바운드 매개변수를 여러 번 사용

저는 사용자가 다양한 정보를 포함할 수 있는 데이터베이스에 아주 기본적인 검색 엔진을 구현하려고 합니다.검색 자체는 결과가 항상 3개의 열로 병합되는 몇 개의 조합 선택으로 구성됩니다.

그러나 반환 데이터를 다른 테이블에서 가져오는 중입니다.

각 쿼리는 매치메이킹에 $term을 사용하며, 준비된 매개변수로 ":term"에 묶었습니다.

자, 설명서에 이렇게 나와 있습니다.

PDOStatement::execute()를 호출할 때 문에 전달할 각 값에 대해 고유한 매개 변수 마커를 포함해야 합니다.준비된 문에는 동일한 이름의 명명된 파라미터 마커를 두 번 사용할 수 없습니다.

각 :term 매개변수를 :termX(term = n++의 경우 x)로 대체하는 대신 더 나은 해결책이 있어야 한다고 생각했습니다.

아니면 :termX 의 X 번호를 바인딩하면 됩니까?

편집 이에 대한 내 솔루션 게시:

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

저는 지금 같은 문제를 몇 번 검토해 보았는데 꽤 간단하고 좋은 해결책을 찾았다고 생각합니다. 번 우 MySQLUser-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다 도 걱정할 .@term다중 사용자 환경에서 측면 effects을 야기합니다.

저는 이중 사용 용어를 개명하여 문제를 해결하기 위해 두 가지 기능을 만들었습니다.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);

변수 명명에 대한 설명:
function : , l: local in function
혼합 :열, n:자, a:열, m:열

질문이 올라와서 바뀐건지는 모르겠지만 지금 매뉴얼을 확인해보니 다음과 같이 적혀있습니다.

에뮬레이션 모드가 켜져 있지 않은 한, 준비된 문에서 동일한 이름의 명명된 매개 변수 마커를 두 번 이상 사용할 수 없습니다.

http://php.net/manual/en/pdo.prepare.php -- (내 것을 강조합니다.)

그래서 기술적으로, 에뮬레이트를 허용하는 것은$PDO_obj->setAttribute( PDO::ATTR_EMULATE_PREPARES, true );그것도 효과가 있을 것입니다. 비록 좋은 생각은 아닐 것입니다. ( 대답에서 논의한 바와 같이, 에뮬레이트된 준비문을 끄는 것은 특정 주사 공격으로부터 보호하는 하나의 방법입니다.) 비록 어떤 사람들은 준비문이 에뮬레이트되었는지 아닌지는 보안에 아무런 차이가 없다고 반대로 글을 쓰기도 합니다. (모르겠습니다.그러나 후자가 앞에서 언급한 공격을 염두에 둔 것은 아니라고 생각합니다.)

완성도를 위해 이 답변을 추가합니다; 작업 중인 사이트에서 에뮬레이트_prepares를 끄면 비슷한 쿼리를 사용하고 있어 검색이 중단되었습니다 (SELECT ... FROM tbl WHERE (Field1 LIKE :term OR Field2 LIKE :term) ...), 그리고 내가 명시적으로 설정하기 전까지는 잘 작동했습니다.PDO::ATTR_EMULATE_PREPARES로.false, 그리고 실패하기 시작했습니다

(PHP 5.4.38, MySQL 5.1.73 FWIW)

이 질문은 동일한 쿼리에서 이름이 지정된 매개 변수를 두 번 사용할 수 없다는 것을 귀띔해 준 것입니다(저에게는 직관에 어긋나는 것 같지만, 오 잘됐네요).(그 페이지를 여러 번 봤는데도 설명서에서 그걸 놓쳤어요.)

준비된 문 에뮬레이션을 활성화하는 경우에만 가능합니다.설정으로 가능합니다.PDO::ATTR_EMULATE_PREPARES로.true.

효과적인 솔루션:

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

그리고 여기 간단한 작업 솔루션이 있습니다!

이것이 가까운 미래에 누군가에게 도움이 되기를 바랍니다.

언급URL : https://stackoverflow.com/questions/18511645/use-bound-parameter-multiple-times

반응형