D
D
Danbka2018-03-13 13:23:08
PHP
Danbka, 2018-03-13 13:23:08

How to deal with exceptions correctly?

Hello.
After reading several articles on Habré, there was no enlightenment on the correct use of exceptions. Rather, there are even questions about the organization of code that uses Exceptions. Below is code with comments to illustrate questions. The class and method names are random and not important in this case.
So I call a method that can throw exceptions:

try {
  User::getById(1);
} catch (Exception $exception) {
  // Какие-то действия, например, вывести сообщение и завершить работу:
  print $exception->getMessage();
  exit;
}

The User::getById() method, in turn, can call other methods that can also throw exceptions:
Class User()
{
  public function getById()
  {
    Profile::someMethod(1);
  }
}

Class Profile()
{
  public function someMethod($param)
  {
    if ($param == 1) {
      throw new Exception();
    }
  }
}

Question:
Do I need to wrap the someMethod() method call in try...catch and throw the exception further?
try {
  Profile::someMethod(1);
} catch (Exception $exception) {
  throw $exception;
}

or is it not necessary?
The second question is a continuation of the first: where should the exception be thrown, in the called method or the caller? For example, the code above could be rewritten like this:
Class Profile()
{
  public function anotherMethod($param)
  {
    if ($param == 1) {
      return false;
    }

    return true;
  }
}

Class User()
{
  public function getById()
  {
    if (Profile::anotherMethod(1) === fasle) {
      throw new Exception();
    }
  }
}

How right? Or is it a matter of taste?

Answer the question

In order to leave comments, you need to log in

9 answer(s)
S
Stalker_RED, 2018-03-14
@Stalker_RED

At the very top level of your application, there should be some kind of Global AND Grand General Purpose Exception Handler. His tasks are very simple - write down what and where broke down in the log, show the user a sign "oh, everything is gone", and optionally kick monitoring, send SMS to the admin or a letter to Santa Claus.
At a lower level, you can check, catch, and somehow handle some types of exceptions. First of all, those that you can somehow influence.
Let's consider a couple of examples:
1. Let's imagine that the "photo preview generator" module, which has been working faithfully for many months, one day encounters an insurmountable obstacle for it and throws an exception - "kapets, the disk space is over, I don't work like that anymore! "
If your code can't do anything about it - you skip this exception above, let the Global AND Grand Handler show the user a nice sign "sorry, we're on a lunch break", and send emails to the admin at 3am. And if you have cloud hosting, for example, and you can connect more disk space on the fly, or you can demolish some unnecessary cache to free up space, then you can catch this exception, show the user "sorry for the delay, your photos will be processed in a couple minutes", and send the admin not a red alert, but a yellow one.
2.The widget, which shows the most rated cats from YouTube, could not load the next portion of the cats, because. YouTube banned by Roskomnadzor. If your code can't do anything about it - the Global AND Great Exception Handler will show the user "oops". Or you will intercept this event at a lower level and show a sign "no fresh cats, but there is a rating for last Friday." Or, if the cats must be fresh, then you can provide an emergency connection via vpn or tor, or you can take the cats from vimeo instead of YouTube, for example.
As a result, the general rules are as follows:
1. skip to the top all exceptions that you cannot handle.
2. process those that are absolutely necessary and important to process, and you really have the opportunity to do this.
It is important not to overdo it, and not to base business logic on exceptions. This mechanism is for rare, exceptional situations that cannot be handled in any other way. If the photo resizing module is written by Indians, and, suddenly, there is nothing to replace it with. But if it is possible (and necessary) to move this problem from the category of an exceptional to a normal working situation, it is better to do so. If your place runs out twice a week, it is better to set up some kind of monitoring that will monitor this indicator and resolve such situations in advance . In the case of server unavailability, you can check the availability at the business logic level and connect backup channels or data sources, instead of falling with the "oh 500!" error.

C
cicatrix, 2018-03-13
@cicatrix

If there was a definitive answer to your question, it would be included in programming guides.
There are many factors that affect how you should deal with exceptions:
1) Don't catch them at all, let the caller take care of it
2) Catch and handle certain exceptions, throw the rest
3) Catch, generate your own, wrap the internal and throw your own 4) Catch
everything, process it yourself
5) Choke everything
(and this is not a complete list).
This should be determined by the architecture of the application and the place of this particular piece of code in this architecture.
In general, you should always approach code from a caller's perspective. If the caller (code, user, program, service, application, etc.) only needs the result and it doesn’t matter what happened to you, then you have to handle it yourself. If the caller needs full control, then you can not process it at all. There is always some reasonable balance between the two extremes, and each time you have to decide separately. There is no universal solution here.
In a large project, there is a manager/architect who must set either general rules or rules specifically for you. For open source, the issue is complex - exceptions are usually not handled in libraries, sometimes they are thrown.
If you control the code from the MyMethod method and further down the call hierarchy, then it's up to you to decide. As you feel more comfortable, do so. If there is another code at the bottom of the hierarchy, then usually, until the opposite is explicitly stated, you must act in the same way as the code "from below" does - if it throws / forwards, then you do the same, if not - accordingly, and you process.

A
Andrey Fedoseev, 2018-03-13
@itlen

try...catch should not be used as a control construct or for checking the state instead of if...else. It is for transaction processing. If the transaction could potentially fail to complete, an exception is thrown. For example, a database entry.

try {  
  пишем в базу 100 записей
} catch (Exception $e) {
  если на 99й записи сбой — откат
  echo $e->getMessage();
}

S
Stanislav Pugachev, 2018-03-13
@Stqs

in my opinion, the problem here is that throwing an exception will not be correct
, especially in the second example,
the exception is for handling exceptions (c) KO
you have an exception (you throw it yourself) to transfer control to another code
, that is, to organize logic
, it’s like an attack, it’s called an anti-pattern, and for this you can get in the ass at the code review
what's the point in that?
in this way you can harm yourself by spoiling the original traceback

M
Maxim Fedorov, 2018-03-13
@qonand

What is an exception? this is roughly speaking a notification that something went wrong and the program code cannot fully work out. Accordingly, it should be thrown out in the code where the problem occurred - i.e. in the called method, and the calling code must somehow react to it - catch it with try-catch and do something (for example, try to fix the situation somehow) or forward it further ...

E
egor_nullptr, 2018-03-13
@egor_nullptr

Here everything is described in detail https://isocpp.org/wiki/faq/exceptions

D
Dmitry Entelis, 2018-03-13
@DmitriyEntelis

My personal point of view:
General rule: if you have some special logic of your own in case of a fall in this particular place, then yes, you need it.
If there is no specific logic, then, accordingly, no.
Where the business logic is checked for meaning.
Moving on to more specific examples - the getUser function can throw an exception if the user is not found, the getAdminUser function using the getUser function can additionally throw an exception if the found user is not an admin.

A
Alexander Yudakov, 2018-03-13
@AlexanderYudakov

Do I need to wrap the someMethod() method call in try...catch and throw the exception further?
try {
  Profile::someMethod(1);
} catch (Exception $exception) {
  throw $exception;
}

This is equivalent to a simple method call:
The exception should be thrown as early as possible - in the place where the problem is detected:
Class Profile()
{
  public function anotherMethod($param)
  {
    if ($param == 1) {
      throw new Exception('$param не может быть 1');
    }
  }
}

This will allow you to write nothing at all in the calling code in most cases. This is the essence of exceptions: the larger the project we have, the longer the call stack, and the more program code we have in the middle of the stack and is written very simply:
Class User()
{
  public function getById()
  {
    Profile::anotherMethod(1);
  }
}

Catching exceptions through try... catch is required only in two cases:
1) at the top of the stack - where our programming language ends and we need to further inform the caller about the problem - you wrote correctly:
try {
  User::getById(1);
} catch (Exception $exception) {
  // Какие-то действия, например, вывести сообщение и завершить работу:
  print $exception->getMessage();
  exit;
}

2) in rare cases in the middle of the stack - when we know how to solve the problem that has arisen and at the same time successfully complete the execution of our own task.

N
Night, 2018-03-13
@maxtm

Don't catch exceptions.
Your code should do this only in extremely rare cases, in the rest - all exceptions are caught in one place.
This will save you from the problems of logging errors (this is done in a single place), rolling back transactions, returning a response with an error code, etc.
Make a single entry point to the apka, catch all exceptions in it, log.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question