Muranx
@Muranx
кто понял this тот в цирке не смеётся

Как грамотно сделать обработку ислючений в php?

Здравствуйте!

Структура проекта следущая...

1. в приложении одна точка входа index.php

2. запрос попадает в класс контроллер Controller

3. в контроллере с через некоторые манипуляции получается путь к командному файлу для обработки текущего запроса , к примеру LoginUserCommand.php т.е. данный класс полностью отвечает за логирование пользователя

4. из полученного в контроллере командного класса для обработки конкретного запроса будет вызван метод LoginUserCommand::execute(Request $request) который произведёт валидацию данных (именно проверку на корректность строк с данными) пользователя, путём вызова методов соответствующих классов типа UserDataValidator, а также если данные валидны, произведёт запрос к БД чтобы проверить , есть ли такой пользователь в системе .

Реализация в псевдокоде , может кому будет нагляднее

# Controller.php
Controller::run(){
          $reg = Registry::getRequest(); # реестр приложения, грубо говоря глобальное хранилище
          $request = $reg->getRequest(); # объект содержащий всю инфу по запросу , тип Request
          $commandClass = CommandResolver( $request );  # через CommandResolver получаем класса инстанс нужной нам команды
          $commandClass->execute( $request );
};

# LoginUserCommandClass.php
LoginUserCommand::execute(Request $request){
          $userData = $request->getUserDataArray(); # массив с данными пользователя , логин и пасс
          $userDataValidator = new Validate( new LoginValidation( $userData['login'] ) , new PasswordValidation( $userData['password'] ) ); # класс валидатор
          $validationResult = $userDataValidator->checkUserData(); 

          if( !$validationResult ) throw new ErrorsInUserDataException(); # исключение , если в пользовательских данных ошибка (ну например меньше символов чем нужно)

          $userDataMapper = new LoginUserDataMapper( $userData ) # класс, который проверяет инфу в БД
          $userDataFromDb = $userDataMapper->getData();

          if( !$userDataFromDb ){
                    throw new EmptyDbUserPositionException(); # исключение, если такого юзера нету в БД и PDOStatement::execute() вернул null
          }else{
                   # делаем что-то с данными пользователя, либо возвращаем в контроллер, либо на месте вызываем класс типа View , и передаём данные для отрисовки 
          }
};

Я начинаю изучать паттерны, в том числе и скелетные шаблоны приложений типа Front Controller, Data Mapper и т. д. Но разумеется в книжках опущена обработка ислючений (просто потому-что слишком много описывать) . Где лучше обрабатывать пользовательские исключения? (в контроллере к примеру с помощью try{/*..*/}catch($e){}) И какова сама механника их обработки, т.е. допустим я поймал в контроллере исключение о том, что пользовательские данные не корректны , т.е. мне нужно иметь по мимо классов исключений типа UserDataNotValidException еще и классы, которые будут обрабатывать эти исключения, и доставлять до пользователя инфу в удобочитаемой форме, или это будет один класс, который просто будет инклюдить страничку с ошибкой (и с сообщением из ошибки). О5 же, если речь идёт о страничке с ошибкой тут вроде все понятно, но если допустим нужно отправить json на сторону клиента с ошибкой , получается там тоже должно быть что-то универсальное для обработки этих исключений? Ну и когда мы отправляем пользователю сообщение в виде странички с ошибкой или json то ему определённо не стоит читать описание ошибки в коде, т.е. нужен какой-то переводчик с яп на человеческий..
  • Вопрос задан
  • 353 просмотра
Решения вопроса 1
ipatiev
@ipatiev Куратор тега PHP
Потомок старинного рода Ипатьевых-Колотитьевых
Структура проекта какая-то странная, в нем, судя по всему, контроллером называется роутер, но на этом я останавливаться не буду, обработка исключений от этого не зависит.

Основных правил при обработке исключений три:
1. Не использовать исключения там, где они не нужны. Например, при проверке пользовательских данных.
Собственно, эмпирическое правило звучит так: функция должна бросать исключение, если она не может выполнить ту работу, для которой она предназначена.

Отсюда сразу становится понятно, что для функции, которая должна проверять введенные данные, наличие ошибок в них не является исключительной ситуацией. А совершенно штатной. И обеспечивается штатными же средствами. По результатам проверки пользователю просто отправляется ответ, что данные неверны, безо всяких исключений. В нормальной структуре проекта этим занимается контроллер.

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

2. Системные ошибки обрабатывает глобальный обработчик исключений, логируя саму ошибку, а на клиент отправляя статус 500 и какое-нибудь абстрактное сообщение о проблеме на сервере. Это самое важное в системных исключениях - текст ошибки никогда, не при каких обстоятельствах не уходит наружу.

Пример такого исключения - когда запрос в БД порождает ошибку.

3. Пользовательские исключения, там где они нужны, обычно обрабатываются по месту, но есть нюансы.
Собственно, под обработкой исключений часто понимают два разных действия:
- собственно обработку, когда программа совершает какие-то действия, чтобы нивелировать негативный эффект. Например, если не удалось подключение к какому-то сервису, то либо попробовать подключиться к другому, либо просто подождать и попробовать снова.
- простое информирование пользователя

Для информирования можно действительно ловить исключение через try..catch и писать какое-то свое сообщение.
Но можно и автоматизировать этот процесс, вот две статьи, которые показывают примеры, как это можно сделать:
https://angelovdejan.me/2022/11/24/centralized-exc...
https://habr.com/ru/articles/688202/
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы