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サーバーのバージョン ''15', 15' の近くで使用する正しい構文は、 ライン1
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 );
これは、バインドのparamを使用して問題を解決するだけでなく、muchshorterあなたのコードを作るであろう、あなたは()の実行中に値を送信するようになるだけではなく、
$skip = $_GET['skip'] ?: 0;
$sql = "SELECT * FROM pictures WHERE album = ? ORDER BY id LIMIT ?, ?";
$PDO->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );
$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);
しかし、あなたは必ずあなたの着信データが正しいとしていますか?ため、エラーメッセージには、番号の後にのみ、の1 引用(引用符で囲まれている全体の数ではなく)があるように思われます。また、これはあなたの着信データと誤りである可能性があります。あなたが見つけるためにprint_r($_GET);
を行うことができますか?
これは単なる概要です。
LIMIT/OFFSET 値をパラメータ化するには 4 つのオプションがあります。
無効にする
PDO::ATTR_EMULATE_PREPARES
述べたように その上.これにより、値が渡されるのを防ぎます
->execute([...])
常に文字列として表示されます。マニュアルに切り替える
->bindValue(..., ..., PDO::PARAM_INT)
パラメータの母集団。ただし、これは ->execute list[] よりも便利ではありません。
ここで例外を作成し、SQL クエリを準備するときに単純な整数を補間するだけです。
$limit = intval($limit); $s = $pdo->prepare("SELECT * FROM tbl LIMIT {$limit}");
キャストは重要です。もっと一般的に見られるのは
->prepare(sprintf("SELECT ... LIMIT %d", $num))
そのような目的に使用されます。MySQL ではなく、たとえば SQLite や Postgres を使用している場合。バインドされたパラメータを SQL で直接キャストすることもできます。
SELECT * FROM tbl LIMIT (1 * :limit)
繰り返しになりますが、MySQL/MariaDB は LIMIT 句の式をサポートしていません。まだ。
LIMIT :init, :end
のための
あなたはその方法をバインドする必要があります。それは、配列にして$req->execute(Array());
のためにすべてのVARSにPDO::PARAM_STR
を唱えるだろうとあなたはLIMIT
それは文句を言わない仕事のようなものを持っていた場合、あなたは絶対に整数を必要としています。
あなたが望むようbindValueまたはBindParamます。
$fetchPictures->bindValue(':albumId', (int)$_GET['albumid'], PDO::PARAM_INT);
なぜこれが起こるのか誰も説明していないので、答えを追加します。これが動作している理由は、使用しているためです trim()
. 。PHPのマニュアルを見ると、 trim
, 、戻り値の型は string
. 。次に、これを次のように渡そうとしています PDO::PARAM_INT
. 。これを回避するには、次のような方法があります。
- 使用
filter_var($integer, FILTER_VALIDATE_NUMBER_INT)
整数を渡していることを確認します。 - 他の人が言ったように、使用すると
intval()
- でキャスト
(int)
- 整数かどうかをチェックする
is_int()
他にも方法はたくさんありますが、基本的にはこれが根本的な原因です。
bindValueオフセットとリミットはPDO :: PARAM_INTを使用して、それが動作します。
// BEFORE(現在エラー) $クエリ= ".... LIMIT:P1、30;"; ... $ stmt-> bindParam( ':P1'、$ limiteInferior);
// AFTER(エラー訂正) $クエリ= ".... LIMIT:P1、30;"; ... $ limiteInferior =(int型)$ limiteInferior。 $ stmt-> bindParam( ':P1'、$ limiteInferior、PDO :: PARAM_INT);
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 ) {
...
}