Oto migawka mojego kodu:
$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);
dostaję
Masz błąd w składni SQL; sprawdź podręcznik, który odpowiada Twojej wersji serwera MySQL, aby uzyskać właściwą składnię, której należy użyć w pobliżu „15”, 15 ”w wierszu 1
Wygląda na to, że PDO dodaje pojedyncze cudzysłowy do moich zmiennych w części LIMIT kodu SQL. Sprawdziłem to i znalazłem ten błąd, który moim zdaniem jest powiązany: http://bugs.php.net/bug.php?id=44639
Czy na to właśnie patrzę? Ten błąd jest otwarty od kwietnia 2008! Co mamy robić w międzyczasie?
Muszę zbudować paginację i upewnić się, że dane są czyste, bezpieczne dla iniekcji sql, przed wysłaniem instrukcji sql.
Odpowiedzi:
Pamiętam, że miałem wcześniej ten problem. Rzutuj wartość na liczbę całkowitą przed przekazaniem jej do funkcji bind. Myślę, że to rozwiązuje problem.
źródło
(int) trim($_GET['skip'])
spróbujintval(trim($_GET['skip']))
.Najprostszym rozwiązaniem byłoby wyłączenie trybu emulacji. Możesz to zrobić, po prostu dodając następujący wiersz
Ten tryb można również ustawić jako parametr konstruktora podczas tworzenia połączenia PDO . To mogłoby być lepsze rozwiązanie, ponieważ niektórzy zgłaszają, że ich sterownik nie obsługuje tej
setAttribute()
funkcji.Nie tylko rozwiąże problem z wiązaniem, ale także umożliwi wysyłanie wartości bezpośrednio do
execute()
, co znacznie skróci kod. Zakładając, że tryb emulacji został już ustawiony, cała sprawa zajmie aż pół tuzina linii koduźródło
SQLSTATE[IM001]: Driver does not support this function: This driver doesn't support setting attributes
... Dlaczego to nigdy nie jest dla mnie takie proste :) Chociaż jestem pewien, że to przyciągnie większość ludzi, w moim przypadku musiałem użyć czegoś podobnego do zaakceptowanej odpowiedzi. Tylko uwaga dla przyszłych czytelników!PDO::ATTR_EMULATE_PREPARES Enables or disables emulation of prepared statements. Some drivers do not support native prepared statements or have limited support for them
. To dla mnie nowość, ale z drugiej strony dopiero zaczynam z PDO. Zwykle używam mysqli, ale pomyślałem, że spróbuję poszerzyć swoje horyzonty.setAttribute
instrukcję ($ stm, $ stmt), a nie obiekt pdo.Patrząc na raport o błędzie, może zadziałać:
ale czy na pewno Twoje dane przychodzące są prawidłowe? Ponieważ w komunikacie o błędzie wydaje się, że po liczbie znajduje się tylko jeden cudzysłów (a nie cała liczba ujęta w cudzysłów). Może to być również błąd w przychodzących danych. Czy możesz się
print_r($_GET);
dowiedzieć?źródło
To tylko podsumowanie.
Istnieją cztery opcje parametryzacji wartości LIMIT / OFFSET:
Wyłącz,
PDO::ATTR_EMULATE_PREPARES
jak wspomniano powyżej .Zapobiega to, aby wartości przekazywane
->execute([...])
przez zawsze pojawiały się jako ciągi.Przełącz na ręczne wprowadzanie
->bindValue(..., ..., PDO::PARAM_INT)
parametrów.Co jednak jest mniej wygodne niż -> lista wykonywania [].
Po prostu zrób wyjątek i po prostu interpoluj zwykłe liczby całkowite podczas przygotowywania zapytania SQL.
Odlew jest ważny. Częściej widzisz
->prepare(sprintf("SELECT ... LIMIT %d", $num))
używane do takich celów.Jeśli nie używasz MySQL, ale na przykład SQLite lub Postgres; można również rzutować powiązane parametry bezpośrednio w języku SQL.
Ponownie, MySQL / MariaDB nie obsługuje wyrażeń w klauzuli LIMIT. Jeszcze nie.
źródło
{$_GET->int["limit"]}
w takich przypadkach.dla
LIMIT :init, :end
Musisz związać się w ten sposób. jeśli coś takiego
$req->execute(Array());
nie zadziała, ponieważ będzie rzutowaćPDO::PARAM_STR
na wszystkie zmienne w tablicy, a dla tegoLIMIT
absolutnie potrzebujesz liczby całkowitej. bindValue lub BindParam, jak chcesz.źródło
Ponieważ nikt nie wyjaśnił, dlaczego tak się dzieje, dodaję odpowiedź. Powodem, dla którego tak się zachowuje, jest to, że używasz
trim()
. Jeśli spojrzysz na podręcznik PHP dlatrim
, zwracanym typem jeststring
. Następnie próbujesz przekazać to jakoPDO::PARAM_INT
. Oto kilka sposobów obejścia tego problemu:filter_var($integer, FILTER_VALIDATE_NUMBER_INT)
aby upewnić się, że przekazujesz liczbę całkowitą.intval()
(int)
is_int()
Sposobów jest o wiele więcej, ale jest to w zasadzie główna przyczyna.
źródło
bindValue offset i limit przy użyciu PDO :: PARAM_INT i będzie działać
źródło
// PRZED (obecny błąd) $ query = ".... LIMIT: p1, 30;"; ... $ stmt-> bindParam (': p1', $ limiteInferior);
// PO (poprawiono błąd) $ query = ".... LIMIT: p1, 30;"; ... $ limiteInferior = (int) $ limiteInferior; $ stmt-> bindParam (': p1', $ limiteInferior, PDO :: PARAM_INT);
źródło
PDO::ATTR_EMULATE_PREPARES
dał miMoje obejście polegało na ustawieniu
$limit
zmiennej jako ciągu, a następnie połączeniu jej w instrukcji przygotowania, jak w poniższym przykładzie:źródło
Wiele się dzieje między różnymi wersjami PHP i osobliwościami PDO. Wypróbowałem tutaj 3 lub 4 metody, ale nie udało mi się uzyskać działania LIMIT.
Moją sugestią jest użycie formatowania / łączenia ciągów z filtrem intval () :
Bardzo ważne jest, aby użyć funkcji intval (), aby zapobiec iniekcji SQL, szczególnie jeśli otrzymujesz swój limit z $ _GET lub podobnego. Jeśli to zrobisz, jest to najłatwiejszy sposób, aby LIMIT działał.
Dużo się mówi o „problemie z LIMITem w PDO”, ale myślę tutaj, że parametry PDO nigdy nie były używane do LIMIT, ponieważ zawsze będą one liczbami całkowitymi, działa szybki filtr. Mimo to jest to trochę mylące, ponieważ filozofia zawsze polegała na tym, aby nie wykonywać samodzielnie filtrowania iniekcji SQL, ale raczej „niech PDO sobie z tym poradzi”.
źródło