Як застосувати метод bindValue у пункті LIMIT?


117

Ось знімок мого коду:

$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 додає єдині лапки до моїх змінних у частині LIMIT коду SQL. Я подивився, я знайшов цю помилку, на яку, на мою думку, пов’язаний: http://bugs.php.net/bug.php?id=44639

Це те, на що я дивлюсь? Ця помилка відкрита з квітня 2008 року! Що ми повинні робити тим часом?

Мені потрібно створити деяку сторінку, і перед тим, як надсилати заяву sql, переконайтесь, що дані чисті, безпечні для введення sql.



Заслуговує на увагу відповідь у двох примірниках Питання: Параметризований запит PDO та пункт `LIMIT` - не працює [дублікат] (серп. 2013; Білл Карвін)
hakre

Відповіді:


165

Я пам’ятаю, як мав цю проблему раніше. Киньте значення на ціле число, перш ніж передати його функції зв’язування. Я думаю, це вирішує це.

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

37
Дякую! Але в PHP 5.3 вищевказаний код видав помилку із записом "Фатальна помилка. Неможливо передати параметр 2 за посиланням". Це не любить кидати туди int. Замість цього (int) trim($_GET['skip'])спробуйте intval(trim($_GET['skip'])).
Буде Мартін

5
було б здорово, якби хтось надав пояснення, чому це так ... з точки зору дизайну / безпеки (чи іншого).
Росс

6
Це працюватиме лише у тому випадку, якщо включені підготовлені оператори . Він вийде з ладу, якщо його відключити (і його слід відключити!)
Привид

4
@Ross я не можу конкретно відповісти на це - але можу зазначити, що LIMIT та OFFSET - це функції, які були склеєні ПІСЛЯ всього цього PHP / MYSQL / PDO божевілля потрапило у схему розробників ... Насправді я вважаю, що саме Лердорф наглядав Обмеження впровадження через кілька років. Ні, це не відповідає на питання, але це вказує на те, що це надбудова надбудови, і ви знаєте, наскільки добре вони можуть спрацювати іноді ....
FredTheWebGuy

2
PDO @Ross не дозволяє прив'язувати до значень - скоріше до змінних. Якщо ви спробуєте bindParam (': something', 2), ви отримаєте помилку, оскільки PDO використовує вказівник на змінну, яку число не може мати (якщо $ i є 2, ви можете мати вказівник на $ i, але не на число 2).
Крістіян

44

Найпростішим рішенням було б вимкнути режим емуляції. Це можна зробити, просто додавши наступний рядок

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

Також цей режим можна встановити як параметр конструктора при створенні з'єднання PDO . Це може бути кращим рішенням, оскільки деякі звіти їх драйвер не підтримує цю setAttribute()функцію.

Це не тільки вирішить вашу проблему з прив’язкою, але і дозволить вам надсилати значення безпосередньо execute(), що зробить ваш код різко коротшим. Якщо припустити, що режим емуляції вже встановлений, вся справа займе цілих півдесятка рядків коду

$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);

SQLSTATE[IM001]: Driver does not support this function: This driver doesn't support setting attributes... Чому це ніколи не так просто для мене :) Хоча я впевнений, що там потрапить більшість людей, у моєму випадку я змусився використати щось подібне до прийнятої відповіді. Просто голова до майбутніх читачів!
Меттью Джонсон

@MatthewJohnson що це за драйвер?
Твій здоровий глузд

Я не впевнений, але в посібнику написано 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. Для мене це нове, але знову ж таки я тільки починаю з PDO. Зазвичай використовую mysqli, але подумав, що спробую розширити свій кругозір.
Меттью Джонсон

@MatthewJohnson, якщо ви використовуєте PDO для mysql, драйвер все одно підтримує цю функцію. Отже, ви отримуєте це повідомлення через якусь помилку
Ваше здорове почуття

1
Якщо у вас з'явилося повідомлення про проблему підтримки драйверів, перевірте ще раз, чи вимагаєте виклику setAttribute($ stm, $ stmt) не для об'єкта pdo.
Джеонг Ан

17

Переглядаючи звіт про помилку, може працювати наступне:

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

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

але ви впевнені, що ваші вхідні дані є правильними? Оскільки в повідомленні про помилку, здається, що після номера є лише одна цитата (на відміну від усього числа, що вкладається в лапки). Це також може бути помилка з вашими вхідними даними. Ви можете це зробити, print_r($_GET);щоб дізнатися?


1
'' 15 ', 15'. Перше число повністю укладається в лапки. Друге число взагалі не має лапок. Так що так, дані хороші.
Натан Н

8

Це так само, як резюме.
Існує чотири варіанти параметризації 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. Ще ні.


1
Я б використав sprintf () з% d протягом 3, я б сказав, що це трохи стабільніше, ніж зі змінною.
хакре

Так, варфунк-лиття + інтерполяція - не самий практичний приклад. Я часто використовував своїх ледачих {$_GET->int["limit"]}для таких випадків.
mario

7

для LIMIT :init, :end

Вам потрібно зв’язати таким чином. якщо у вас щось на кшталт $req->execute(Array());цього не буде працювати, оскільки воно буде передаватися PDO::PARAM_STRвсім варам в масиві, і LIMITвам абсолютно потрібен цілий комплекс. bindValue або BindParam, як ви хочете.

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

2

Оскільки ніхто не пояснив, чому це відбувається, я додаю відповідь. Причина, по якій так поводиться, - це те, що ви використовуєте trim(). Якщо ви подивитесь на посібник з PHP trim, тип повернення є string. Потім ви намагаєтесь передати це як PDO::PARAM_INT. Кілька способів обійти це:

  1. Використовуйте, filter_var($integer, FILTER_VALIDATE_NUMBER_INT)щоб переконатися, що ви проходите ціле число.
  2. Як казали інші, використовуючи intval()
  3. Кастинг с (int)
  4. Перевірка, чи це ціле число is_int()

Існує набагато більше способів, але це в основному першопричина.


3
Це трапляється навіть тоді, коли змінна завжди була цілим числом.
сентябрь

1

bindValue зміщення та обмеження за допомогою PDO :: PARAM_INT, і воно буде працювати


-1

// ДО ПЕРЕД (Поточна помилка) $ query = ".... LIMIT: p1, 30;"; ... $ stmt-> bindParam (': p1', $ limiteInferior);

// ПІСЛЯ (виправлена ​​помилка) $ query = ".... LIMIT: p1, 30;"; ... $ limiteInferior = (int) $ limiteInferior; $ stmt-> bindParam (': p1', $ limiteInferior, PDO:: PARAM_INT);


-1

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 ) {
    ...
}

-1

Існує багато між різними версіями PHP та диваком PDO. Я спробував 3 або 4 методу тут, але не зміг працювати LIMIT.
Моя пропозиція полягає у використанні форматування / конкретизації рядків З фільтром intval () :

$sql = 'SELECT * FROM `table` LIMIT ' . intval($limitstart) . ' , ' . intval($num).';';

Дуже важливо використовувати intval () для запобігання ін'єкції SQL, особливо якщо ви отримуєте свій ліміт від $ _GET або подібного. Якщо ви це зробите, це найпростіший спосіб роботи LIMIT.

Існує багато розмов про "Проблема з LIMIT в PDO", але я вважаю, що параметри PDO ніколи не використовувались для LIMIT, оскільки вони завжди будуть цілими числами, і швидкий фільтр працює. Тим не менш, це трохи оману, оскільки філософія завжди полягала в тому, щоб не робити фільтрування інжекцій SQL самостійно, а навпаки: «Здійснити PDO».

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.