[php] LIMIT 절에서 bindValue 메서드를 적용하는 방법은 무엇입니까?

다음은 내 코드의 스냅 샷입니다.

$fetchPictures = $PDO->prepare("SELECT *
    FROM pictures
    WHERE album = :albumId
    ORDER BY id ASC
    LIMIT :skip, :max");

$fetchPictures->bindValue(':albumId', $_GET['albumid'], PDO::PARAM_INT);

if(isset($_GET['skip'])) {
    $fetchPictures->bindValue(':skip', trim($_GET['skip']), PDO::PARAM_INT);
} else {
    $fetchPictures->bindValue(':skip', 0, PDO::PARAM_INT);
}

$fetchPictures->bindValue(':max', $max, PDO::PARAM_INT);
$fetchPictures->execute() or die(print_r($fetchPictures->errorInfo()));
$pictures = $fetchPictures->fetchAll(PDO::FETCH_ASSOC);

나는 얻다

SQL 구문에 오류가 있습니다. MySQL 서버 버전에 해당하는 설명서에서 1 행에서 ”15 ‘, 15’근처에서 사용할 올바른 구문을 확인하십시오.

PDO가 SQL 코드의 LIMIT 부분에서 내 변수에 작은 따옴표를 추가하는 것 같습니다. 나는 그것을 찾아 보았다.이 버그를 발견했다.
http://bugs.php.net/bug.php?id=44639

그게 내가보고있는 건가요? 이 버그는 2008 년 4 월부터 시작되었습니다! 그동안 우리는 무엇을해야합니까?

페이지 매김을 작성하고 SQL 문을 보내기 전에 데이터가 깨끗하고 SQL 주입에 안전한지 확인해야합니다.



답변

이전에이 문제가 있었던 것을 기억합니다. bind 함수에 전달하기 전에 값을 정수로 캐스트하십시오. 나는 이것이 그것을 해결한다고 생각합니다.

$fetchPictures->bindValue(':skip', (int) trim($_GET['skip']), PDO::PARAM_INT);


답변

가장 간단한 해결책은 에뮬레이션 모드를 끄는 것입니다. 다음 줄을 추가하기 만하면됩니다.

$PDO->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );

또한이 모드는 PDO 연결을 생성 할 때 생성자 매개 변수로 설정할 수 있습니다 . 일부 드라이버가 해당 setAttribute()기능을 지원하지 않는다고보고하므로 더 나은 솔루션이 될 수 있습니다 .

바인딩 문제를 해결할뿐만 아니라 값을로 직접 보낼 execute()수 있으므로 코드가 크게 단축됩니다. 에뮬레이션 모드가 이미 설정되었다고 가정하면 전체 작업에는 6 줄의 코드가 필요합니다.

$skip = isset($_GET['skip']) ? (int)trim($_GET['skip']) : 0;
$sql  = "SELECT * FROM pictures WHERE album = ? ORDER BY id LIMIT ?, ?";
$stmt  = $PDO->prepare($sql);
$stmt->execute([$_GET['albumid'], $skip, $max]);
$pictures = $stmt->fetchAll(PDO::FETCH_ASSOC);


답변

버그 보고서를 보면 다음이 작동 할 수 있습니다.

$fetchPictures->bindValue(':albumId', (int)$_GET['albumid'], PDO::PARAM_INT);

$fetchPictures->bindValue(':skip', (int)trim($_GET['skip']), PDO::PARAM_INT);  

하지만 들어오는 데이터가 정확하다고 확신합니까? 오류 메시지 만있을 것 같습니다 때문에 하나 (인용 부호되는 정수로 반대) 수 후 견적. 수신 데이터에 오류가있을 수도 있습니다. 당신은 print_r($_GET);알아낼 수 있습니까?


답변

이것은 요약과 같습니다.
LIMIT / OFFSET 값을 매개 변수화하는 네 가지 옵션이 있습니다.

  1. 위에서PDO::ATTR_EMULATE_PREPARES 언급 한대로 비활성화합니다 .

    전달 된 값 ->execute([...])이 항상 문자열로 표시 되는 것을 방지합니다 .

  2. 수동 ->bindValue(..., ..., PDO::PARAM_INT)매개 변수 채우기로 전환합니다 .

    그러나-> 목록 실행 []보다 덜 편리합니다.

  3. 여기서 예외를 만들고 SQL 쿼리를 준비 할 때 일반 정수를 보간하면됩니다.

     $limit = intval($limit);
     $s = $pdo->prepare("SELECT * FROM tbl LIMIT {$limit}");

    캐스팅이 중요합니다. 더 일반적으로 ->prepare(sprintf("SELECT ... LIMIT %d", $num))이러한 목적으로 사용됩니다.

  4. MySQL을 사용하지 않고 SQLite 또는 Postgres를 사용하는 경우; SQL에서 직접 바인딩 된 매개 변수를 캐스트 할 수도 있습니다.

     SELECT * FROM tbl LIMIT (1 * :limit)

    다시 말하지만, MySQL / MariaDB는 LIMIT 절에서 표현식을 지원하지 않습니다. 아직.


답변

…에 대한 LIMIT :init, :end

그렇게 묶어야합니다. 배열의 모든 변수로 $req->execute(Array());캐스트되므로 작동하지 않는 경우 PDO::PARAM_STRLIMIT절대적으로 정수가 필요합니다. 원하는대로 bindValue 또는 BindParam.

$fetchPictures->bindValue(':albumId', (int)$_GET['albumid'], PDO::PARAM_INT);


답변

아무도 왜 이런 일이 발생하는지 설명하지 않았으므로 대답을 추가하고 있습니다. 이것이 작동하는 이유는 trim(). 에 대한 PHP 매뉴얼을 trim보면 반환 유형은 string. 그런 다음 이것을 PDO::PARAM_INT. 이 문제를 해결하는 몇 가지 방법은 다음과 같습니다.

  1. filter_var($integer, FILTER_VALIDATE_NUMBER_INT)정수를 전달하고 있는지 확인하는 데 사용 합니다.
  2. 다른 사람들이 말했듯이 intval()
  3. 캐스팅 (int)
  4. 정수인지 확인하기 is_int()

더 많은 방법이 있지만 이것이 기본적으로 근본 원인입니다.


답변

bindValue 오프셋 및 제한은 PDO :: PARAM_INT를 사용하여 작동합니다.