Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Во время выполнения программы могут возникать ситуации, когда состояние внешних данных, устройств ввода-вывода или компьютерной системы в целом делает дальнейшие вычисления в соответствии с базовым алгоритмом невозможными или бессмысленными.
Я утверждаю, что обрабатывать штатные исключительные ситуации необходимо с помощью кодов возврата.
«Don't use Exceptions for flow control» — основной гайдлайн почти везде.
Ситуация «Юзер ввел неправильные данные» — не является исключительной
не очень понимаю, почему при вводе неверных креденшалов у вас «дальнейшее выполнение программы невозможно»
вы в ответ АЛЯРМ ААА ПАНИКА НЕПРАВИЛЬНЫЙ ПАРОЛЬ ВСЕ В ИСКЛЮЧЕНИЕ
Странно, что и вы не хотите услышать собеседников, хоть сами к этому призываете
Правильно говорят выше – исключение это когда вам ломают ноги и вы становитесь нетрудоспособным (коннекта к БД, допустим, нет – это исключение).
Throwing exceptions is one of the most expensive operations in .NET.
However, some languages (notably Python) use exceptions as flow-control constructs. For example, iterators raise a StopIteration exception if there are no further items.
I don't think there is anything wrong with using Exceptions for flow-control. Exceptions are somewhat similar to continuations and in statically typed languages, Exceptions are more powerful than continuations, so, if you need continuations but your language doesn't have them, you can use Exceptions to implement them.
$user = new User;
$user->load($_POST);
if ($user->save()) {
redirect('hello.php');
} else {
$this->view->assign('errors', $user->getErrors());
}$model->password = '' и получаю исключение RequiredException?catch (FormMultiException $e) {
if ($passwordErrors = $e->extractByColumn('password') {
// что-то сделали с ошибками про пароль, в лог записали о том, что была попытка подбора, например
// и все остальные ошибки бросили дальше
throw $e;
}
}$post = new Post();
$e = null;
try {
$post->load($_POST);
$post->save();
} catch (FormMultiException $e) {
$errors = $e;
} finally {
return $this->render('view', ['post' => $post, 'errors' => $e]);
}$post = new Post();
$post->load($_POST);
$post->save();
return $this->render('view', ['post' => $post, 'errors' => $post->getErrors()]);if ($post->load($_POST) && $post->save()) {
// success!
}
$this->render...$post = new Post();
$errors = [];
$res = $post->load($_POST);
if (false === $res) {
$errors = array_merge($errors, $post->getErrors());
}
if (empty($errors) {
$res = $post->save();
if (false === $res) {
$errors = array_merge($errors, $post->getErrors());
}
}
$this->view->post = $post;
$this->view->errors = $e;try {
$this->view->post = (new Post())->fill($_POST)->save();
} catch (MultiException $e) {
$this->view->errors = $e;
}$this->view->post = $post; у вас никогда не выполнится. Исключение прерывает исполнение блока try. Без finally вы работать не сможете.$post = new Post();
$post->load($_POST);
$errors = $post->validate();
if (count($errors) === 0) {
$post->save();
}
$this->render('post', ['post' => $post, 'errors' => $errors]);$post = new Post();
$post->load($_POST);
$errors = new ValidationErrors(); // или null, в зависимости от того, с чем может работать view
try {
$post->validate();
$post->save();
} catch (ValidationErrors $errors) {
// do nothing
} finally {
$this->render('post', ['post' => $post, 'errors' => $errors])
}// условный контроллер
$data = (условно)$_POST;
try {
$post = $data->id ? Post::findById($id) : new Post;
$post->fill($data)->save();
redirect();
} catch (MultiException $e) {
$this->view->errors = $e;
}
$this->view->data = $data;
// условно шаблон
{% for error in errors %}
<div class="alert">{{ error }}</div>
{% endfor %}
<form>...</form>{% for error in errors.getByColumn('password') %}«Есть форма, это модель»
Мне не ясно, почему вы валидируете два раза.
class RegisterErrors extends MultiException$errors = $user->getErrors();
$passwordErrors = $errors['password']; // можно isset добавитьЗачем вы присваиваете модели данных пустое значения поля ради того, чтобы не показывать во view это значение?
Как именно вы используете эту иерархию с UserShortPassword, UserSimplePassword, если у вас всегда нужно ловить MultiException?
foreach ($multiException->errors as $error) {
echo $error->getMessage();
}foreach ($multiException->errors… )
foreach ($errors as $error)Другой пример — поле было необязательным, в базе куча записей с пустым значением, потом решили сделать обязательным, при загрузке из БД вызывается __set(), который бросает исключение.
findOne($id). Он получает данные из БД, делает $model = new User(), и устанавливает свойство $model->address = $dbData['address'], при этом $dbData['address'] = null. Вызывается метод __set().$model->scenario = "LOAD_FROM_DB", а в сеттере или валидаторе ее проверять?Суть концепции мультиисключения как раз и состоит в том, что код НЕ ПРЕРЫВАЕТСЯ одним исключением.
validatePassword. Вместо того, чтобы проверить все правила сразу, она прерывается после первого. О том, что данные не подходят еще по какому-то критерию, пользователь должен узнавать, каждый раз переотправляя форму.Просто передайте в конструктор исключения ссылку на модель.
try и в процессе создания вылетает исключение, объектом больше пользоваться нельзя, поскольку его целостность не гарантируется. Выносить ссылку на него в блок catch — очень плохая идея.Формы на сервере есть, они передаются хттп-запросами
Модель не нужна, если она невалидна.
И да. В модели не может быть поля подтверждения пароля.
Мой код отличается от вашего тем, что исключение, в отличие от того, что вы вернули из метода getErrors() является объектом и, в силу этого факта, имеет тип.
3. Исключения управляют потоком.
$rules = [
'login' => [ 'required', 'login', 'min_len' => 3, 'max_len' => 15 ],
'email' => [ 'required', 'email' ],
'pass' => [ 'required', 'min_len' => 4 ],
'phone' => [ 'required', 'phone' ],
];
$model->setRules( $rules );
...
$model->save();
...
$errors = $model->getErrors ();[
'login' => [ 'required' => 'Login is empty', 'login' => 'Invalid login', ... ],
...
]try {
$model->fill($DATA); // $DATA - некие внешние данные, либо array, либо IArray
$model->save();
} catch (MultiException $e) {
foreach ($e->group('column') as $column => $errors) {
foreach ($errors as $error) {
/** @var Exception $error */
echo $column . '=>' . $error->getMessage();
}
}
}protected function validateEmail($value) {
if (empty($value) {
yield new Exception('Пустой email');
}
if (strlen($value) < 3) {
yield new Exception('Короткий email');
}
return true;
}$model->setValidator(string $column, callable $validator);
Мультиисключение или Хочу поделиться одним интересным архитектурным приемом