web-dev-qa-db-ja.com

bindValueメソッドをLIMIT句に適用する方法は?

これが私のコードのスナップショットです。

$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構文にエラーがあります。行1で '' 15 '、15'の近くで使用する正しい構文については、MySQLサーバーのバージョンに対応するマニュアルを確認してください。

PDOはSQLコードのLIMIT部分の変数に一重引用符を追加しているようです。私はそれを調べて、関連すると思われるこのバグを見つけました: http://bugs.php.net/bug.php?id=44639

それは私が見ているものですか?このバグは2008年4月から公開されています!その間に何をすべきか?

SQL文を送信する前に、ページネーションを作成し、データがクリーンで、SQLインジェクションに対して安全であることを確認する必要があります。

108
Nathan H

以前にこの問題があったことを覚えています。値を整数にキャストしてから、バインド関数に渡します。これで解決すると思います。

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

最も簡単な解決策は、 エミュレーションモード オフに切り替えることです。接続オプションとして、または単に次の行を追加することでそれを行うことができます

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

Bind paramを使用して問題を解決するだけでなく、execute()で値を送信することもできます。これにより、コードが劇的に複雑になります。

$skip = (isset($_GET['skip'])):$_GET['skip']:0;
$sql  = "SELECT * FROM pictures WHERE album = ? ORDER BY id ASC LIMIT ?, ?";
$PDO->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );
$stm  = $PDO->prepare($sql);
$stm->execute(array($_GET['albumid'],$skip,$max));
$pictures = $stm->fetchAll(PDO::FETCH_ASSOC);
41

バグレポートを見ると、次のように機能する場合があります。

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

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

しかし、着信データは正しいと確信していますか?エラーメッセージでは、数字の後にoneだけが引用されているようです(全体を引用符で囲むのではなく)。これは、受信データのエラーである可能性もあります。 print_r($_GET);を実行して確認できますか?

15
Pekka 웃

これはちょうど要約として。
LIMIT/OFFSET値をパラメーター化する4つのオプションがあります。

  1. 前述のように_PDO::ATTR_EMULATE_PREPARES_を無効にします 上記

    ->execute([...])ごとに渡される値が常に文字列として表示されるのを防ぎます。

  2. 手動->bindValue(..., ..., PDO::PARAM_INT)パラメーターの設定に切り替えます。

    ただし、これは-> execute list []よりも便利ではありません。

  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句の式をサポートしていません。未だに。

8
mario

_LIMIT :init, :end_

そのようにバインドする必要があります。 $req->execute(Array());のようなものがあれば、_PDO::PARAM_STR_を配列内のすべての変数にキャストし、LIMITには絶対に整数が必要なので、機能しません。 bindValueまたはBindParamを必要に応じて。

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

なぜこれが起こっているのか誰も説明していないので、私は答えを追加しています。これが動作しているのは、trim()を使用しているためです。 PHP trimのマニュアルを見ると、戻り値の型はstringです。これを_PDO::PARAM_INT_として渡そうとしています。これを回避するいくつかの方法は次のとおりです。

  1. filter_var($integer, FILTER_VALIDATE_NUMBER_INT)を使用して、整数を渡していることを確認してください。
  2. 他の人が言ったように、intval()を使用して
  3. _(int)_を使用したキャスト
  4. is_int()で整数かどうかを確認する

より多くの方法がありますが、これは基本的に根本的な原因です。

2

pDO :: PARAM_INTを使用したbindValueのオフセットと制限は機能します

1
Karel

PDO::ATTR_EMULATE_PREPARESは私に与えた

ドライバーはこの機能をサポートしていません:このドライバーは属性の設定エラーをサポートしていません。

私の回避策は、$limit変数を文字列として使用し、次の例のように準備ステートメントで結合します。

$limit = ' LIMIT ' . $from . ', ' . $max_results;
$stmt = $pdo->prepare( 'SELECT * FROM users WHERE company_id = :cid ORDER BY name ASC' . $limit . ';' );
try {
    $stmt->execute( array( ':cid' => $company_id ) );
    ...
}
catch ( Exception $e ) {
    ...
}
0
Fins

// BEFORE(現在のエラー)$ query = ".... LIMIT:p1、30;"; ... $ stmt-> bindParam( ':p1'、$ limiteInferior);

// AFTER(エラー修正)$ query = ".... LIMIT:p1、30;"; ... $ limiteInferior =(int)$ limiteInferior; $ stmt-> bindParam( ':p1'、$ limiteInferior、PDO :: PARAM_INT);