Como aplicar o método bindValue na cláusula LIMIT?
Pergunta
Aqui está um instantâneo do meu código:
$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);
eu recebo
Você tem um erro na sintaxe SQL;Verifique o manual que corresponde à sua versão do MySQL Server para obter a sintaxe certa para usar próximo '' 15 ', 15' na linha 1
Parece que o PDO está adicionando aspas simples às minhas variáveis na parte LIMIT do código SQL.Pesquisei e encontrei esse bug que acho que está relacionado:http://bugs.php.net/bug.php?id=44639
É isso que estou olhando?Este bug foi aberto desde abril de 2008!O que devemos fazer enquanto isso?
Preciso construir alguma paginação e ter certeza de que os dados estão limpos e seguros para injeção de sql, antes de enviar a instrução sql.
Solução
Lembro-me de ter tido esse problema antes.Transforme o valor em um número inteiro antes de passá-lo para a função de ligação.Acho que isso resolve.
$fetchPictures->bindValue(':skip', (int) trim($_GET['skip']), PDO::PARAM_INT);
Outras dicas
A solução mais simples seria desligar o modo de emulação.Você pode fazer isso como uma opção de conexão ou simplesmente adicionando a seguinte linha
$PDO->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );
Isso não apenas resolverá seu problema com parâmetros de ligação, mas também permitirá que você envie valores em execute(), o que tornará seu código muito mais curto
$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);
Olhando o relatório do bug, o seguinte pode funcionar:
$fetchPictures->bindValue(':albumId', (int)$_GET['albumid'], PDO::PARAM_INT);
$fetchPictures->bindValue(':skip', (int)trim($_GET['skip']), PDO::PARAM_INT);
mas você tem certeza de que seus dados recebidos estão corretos?Porque na mensagem de erro, parece haver apenas um aspas após o número (em vez de o número inteiro estar entre aspas).Isso também pode ser um erro nos dados recebidos.Você pode fazer um print_r($_GET);
descobrir?
Isso é apenas um resumo.
Existem quatro opções para parametrizar valores LIMIT/OFFSET:
Desativar
PDO::ATTR_EMULATE_PREPARES
como mencionado acima.O que impede que valores passados por
->execute([...])
para sempre aparecer como strings.Mudar para manual
->bindValue(..., ..., PDO::PARAM_INT)
população de parâmetros.O que, no entanto, é menos conveniente do que uma ->executar lista[].
Simplesmente abra uma exceção aqui e interpole números inteiros simples ao preparar a consulta SQL.
$limit = intval($limit); $s = $pdo->prepare("SELECT * FROM tbl LIMIT {$limit}");
O elenco é importante.Mais comumente você vê
->prepare(sprintf("SELECT ... LIMIT %d", $num))
usados para tais fins.Se você não estiver usando MySQL, mas por exemplo SQLite ou Postgres;você também pode converter parâmetros vinculados diretamente no SQL.
SELECT * FROM tbl LIMIT (1 * :limit)
Novamente, MySQL/MariaDB não suporta expressões na cláusula LIMIT.Ainda não.
para LIMIT :init, :end
Você precisa se vincular dessa maneira.se você tivesse algo parecido $req->execute(Array());
não funcionará, pois será lançado PDO::PARAM_STR
para todos os vars na matriz e para o LIMIT
você absolutamente precisa de um número inteiro.bindValue ou BindParam conforme desejar.
$fetchPictures->bindValue(':albumId', (int)$_GET['albumid'], PDO::PARAM_INT);
Como ninguém explicou por que isso está acontecendo, estou adicionando uma resposta.A razão pela qual está se comportando assim é porque você está usando trim()
.Se você olhar o manual do PHP para trim
, o tipo de retorno é string
.Você está então tentando passar isso como PDO::PARAM_INT
.Algumas maneiras de contornar isso são:
- Usar
filter_var($integer, FILTER_VALIDATE_NUMBER_INT)
para ter certeza de que você está passando um número inteiro. - Como outros disseram, usando
intval()
- Fundição com
(int)
- Verificando se é um número inteiro com
is_int()
Existem muitas outras maneiras, mas esta é basicamente a causa raiz.
deslocamento e limite de bindValue usando PDO::PARAM_INT e funcionará
// antes (erro presente) $ query = "....LIMITE:p1, 30;";...$stmt->bindParam(':p1', $limiteInferior);
// depois (Erro corrigido) $ query = "....LIMITE:p1, 30;";...$limiteInferior = (int)$limiteInferior;$stmt->bindParam(':p1', $limiteInferior, PDO::PARAM_INT);
PDO::ATTR_EMULATE_PREPARES
me deu o
O driver não suporta esta função:Este driver não suporta o erro dos atributos de configuração.
Minha solução alternativa foi definir um $limit
variável como uma string e combine-a na instrução prepare como no exemplo a seguir:
$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 ) {
...
}