Вопрос по exception-handling, sql, pdo, php – Вопросы об исключениях из PDO - как их поймать

17

Я использую PDO, чтобы переписать интерфейс веб-сайта для базы данных. Раньше я использовал расширение mysql, но никогда не беспокоился об обработке ошибок, а те немногие обработчики ошибок, которые у меня были, были в основном копируемыми.

Теперь я хочу сделать это правильно. Однако у меня возникают проблемы с обнаружением ошибок, которые мне нравятся (такие ошибки, как «Duplicate Entry», «Null Value» и т. Д. В MySQL). Какая часть моего утверждения должна быть в блоке try? Должно ли все это быть там? Я используюInclude() подключиться к моей БД (которая имеет свою собственную обработку ошибок), так что это только выполнение запроса, в котором есть ошибки в этом коде. Я не могу понять, почему он не улавливает ошибку при выполнении следующего кода:

try {
  $stmt = $db->prepare("INSERT INTO tbl_user (id, name, password, question, answer)    VALUES (NULL, :name, :password, :question, :answer)");
  $stmt->bindValue(":name", $_POST['name']);
  $stmt->bindValue(":password", $_POST['password']);
  $stmt->bindValue(":question", $_POST['question']);
  $stmt->bindValue(":answer", $_POST['answer']);
  $stmt->execute();
  echo "Successfully added the new user " . $_POST['name'];
} catch (PDOException $e) {
  echo "The user could not be added.<br>".$e->getMessage();
}

Итак, мои вопросы: ВСЕ ЭТО должно быть в блоке try? Могу ли я просто поместить execute в блок try? Должно поймать ошибкуDuplicate value "John" in key "name", но вместо этого проходит через сообщение об успехе. (При попытке добавить двух пользователей "Джон"). Я проверил в PHPMyAdmin; индекс уникален и выдает ошибку, как и ожидалось, просто не используя этот код.

@StuckAtWork проверит документацию php.net, чтобы узнать, генерирует ли что-то исключение или нет, или просто иногда (зависит от ERRMODE)bindValue() сам не выдает исключение, просто возвращает логическое значение,PDO::prepare(...) будет выдавать исключение, но только если у вас естьPDO::ERRMODE_EXCEPTION установить .... Поэтому преимущество заключается в читаемости кода - вы можете иметь только одинtry/catch блок :-) jave.web
Если вы этого еще не сделали, вам нужно установить$db& APOS; sPDO::ATTR_ERRMODE вPDO::ERRMODE_EXCEPTION так что он выбрасывает исключения на ошибки. FtDRbwLXw6
Never store plain text passwords! Пожалуйста, используйте PHPbuilt-in functions обрабатывать безопасность пароля. Если вы используете версию PHP ниже 5.5, вы можете использоватьpassword_hash() compatibility pack, Убедитесь, что выdon't escape passwords или используйте любой другой очищающий механизм перед их перемешиванием. Делать этоchanges пароль и вызывает ненужное дополнительное кодирование. Jay Blanchard
Вы должны проверить документацию или исходный код и посмотреть, какие функции выдаютPDOException, Тогда вы будете знать, какие части кода поместить в блок try :) AlexMorley-Finch
Я чувствую себя глупо .. установкаATTR_ERRMODE починил это. Итак, какая часть выражения должна быть в блоке try? Код может быть использован повторно (возможно, изменится только несколько операторов bindValue (), а затем выполнить). Есть ли какое-то преимущество в том, чтобы иметь весь оператор в блоке try, или необходим только execute ()? StuckAtWork

Ваш Ответ

2   ответа
10

Как правило -

DO NOT catch them.

Например, ваш код должен быть написан таким образом

$stmt = $db->prepare("INSERT INTO tbl_user (id, name, password, question, answer) VALUES (NULL, :name, :password, :question, :answer)");
$stmt->bindValue(":name", $_POST['name']);
$stmt->bindValue(":password", $_POST['password']);
$stmt->bindValue(":question", $_POST['question']);
$stmt->bindValue(":answer", $_POST['answer']);
$stmt->execute();
echo "Successfully added the new user " . $_POST['name'];

без каких-либо попыток или ловить звонки.Because you have no particular scenario for handling an exception here (простое эхо едва ли считается сценарием обработки).

Вместо этого позвольте ему всплыть в обработчике ошибок всего приложения (не пугайтесь этого термина, в PHP уже есть встроенный).

However, I'm having issues catching the errors how I'd like (errors like "Duplicate Entry", "Null Value" etc in MySQL).

Только в том случае, если у вас естьcertain scenarioВы должны использовать оператор try-catch, но всегда должны проверять, является ли ошибка, которую вы получили, той, которую вы ожидали. В противном случае исключение должно быть переброшено:

try {
    $pdo->prepare("INSERT INTO users VALUES (NULL,?,?,?,?)")->execute($data);
} catch (PDOException $e) {
    if ($e->getCode() == 1062) {
        // Take some action if there is a key constraint violation, i.e. duplicate name
    } else {
        throw $e;
    }
}

и, конечно же (как оказалось, это самая серьезная проблема для этого вопроса), вы должны настроить PDO в режиме исключения, либо в параметре конструктора, просто добавив код

$db->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

сразу после подключения.

Вы полностью правы, Джефф, getCode вернет здесь состояние sql, как вы сказали, 23000
я верю23000 это чтоgetCode() вернется в этом конкретном примере, заполненном в$e->errorInfo[0] надеюсь для всех драйверов (проверено MySQL и MS SQL Server).1062 код будет заполнен в$e->errorInfo[1] для MySQL и MS SQL Server2627 в$e->errorInfo[1]
14

вы можете добавить еще один улов:

<?php
try {
  $stmt = $db->prepare("INSERT INTO tbl_user (id, name, password, question, answer)    VALUES (NULL, :name, :password, :question, :answer)");
  $stmt->bindValue(":name", $_POST['name']);
  $stmt->bindValue(":password", $_POST['password']);
  $stmt->bindValue(":question", $_POST['question']);
  $stmt->bindValue(":answer", $_POST['answer']);
  $stmt->execute();
  echo "Successfully added the new user " . $_POST['name'];
} catch (PDOException $e) {
  echo "DataBase Error: The user could not be added.<br>".$e->getMessage();
} catch (Exception $e) {
  echo "General Error: The user could not be added.<br>".$e->getMessage();
}
?>

Это должно работать, потому что все исключения плагинов PHP наследуются от класса PHP Exception. (С 5.0, если у меня хорошая память).

Я огляделся, но, по-моему, я ошибочно предположил, что PDO генерирует исключения по умолчанию. Оказывается, вам нужно поступить так, как сказал @drrcknlsn в первом комментарии. Ни одна из версий (Exception NOR PDOException) Кидали ошибки, пока не было изменено. Однако это неплохая идея (могут быть и другие ошибки) StuckAtWork
Как мне обернуть мой $ _POST для правильного кодирования? StuckAtWork
Прежде всего, используйте «isset» чтобы быть уверенным, что ваше "имя", "пароль" ... установлены. Затем вы можете использовать is_string (). Вам не нужны плохие вещи, такие как mysql_real_escape_string (), поскольку PDO делает это очень хорошо, но убедитесь, что ваш параметр привязки является строкой, числовым или логическим значением, в зависимости от того, что вы хотите связать. Для этого есть встроенные функции PHP, такие как is_string (), is_int (), is_numeric () или is_boolean ();)
@StuckAtWork: :: bindValue или :: bindParam могут завершиться ошибкой, если вы отправите ему объекты или массивы. Не забывайте, что использование $ _POST напрямую не препятствует внедрению XSS при отправке массива здесь. (PS: я просто пишу этот комментарий здесь, а не выше, потому что по неизвестной причине я не могу добавить комментарий к вашему посту? _?) Извините за это.
Если вы не выполните isset, вы получите уведомление, если ваши ключи в $ _POST не были там. так иссет очень важен. Третье значение здесь указывает, какой тип значения вы хотите связать, но это не защита. Immagine $ _POST [& quot; foo & quot;] = false;myTable.foo это БУЛА; и вы используете PDO :: PARAM_STR. PDO вставит вам «ложь» (строка), и может интерпретироваться как ИСТИНА (bool). Убедитесь, что ваши значения относятся к хорошему типу, и, если возможно, приведите их разумно к хорошему типу, прежде чем связывать их. Добавьте 3-й параграф лучше для простоты и тривиального кастинга :)

Похожие вопросы