1

I am revising a PHP project that is necessary because of a massive redesign in the database. Therefore, the project should use the latest PHP version, which at the moment of writing is version 8. I am also using an OOP approach with namespace, so the project can benefit from features like autoloading in the future.

In the new classes I wrote, I need to throw errors. But in PHP 8, there are so many ways to do this. I could use trigger_error or exceptions or the error class and its specific variants like value error. Now I am unsure which to choose. Currently I have a mixture of exceptions and trigger_error in the code.

I have a custom handler defined for both exception and "old" errors, which writes the errors to a log file so I can fix them (if possible).

So what should I use to write a future-proof code? Unfortunately I couldn't find anything in the PHP docs about this. Also my web research didn't bring any useful result unfortunately. Is there a best approach rule?

1 Answers1

1

I'm not a PHP expert.

However my little observation is that latest PHP versions tend to be more OOP. PHP now supports many OOP features you will find in any other fully OOP languages like C# or Java. We can see that many PHP functions now throw exceptions, and with the introduction of Exception and Error in PHP 7 and ValueError in PHP 8 you are likely to encounter and have to deal with exceptions than the infamous false and null.

A very small list of PHP features that now throw excptions (with versions next to them):

  • Constructors of internal classes will now always throw an exception (PHP 7)
  • Calling a function with less arguments than mandatory declared ones throws exception (PHP 7.1)
  • 1 + "a" throws TypeError (PHP 8)
  • explode throws ValueError when separator parameter is given an empty string (PHP 8)
  • mb_ord throws ValueError when $string argument is given an empty string (PHP 8)
  • MySQLi the default error handling mode has been changed from "silent" to "exceptions" (PHP 8.1)

And many more will throw an instance of Error or exception instead of resulting in a fatal error.

Conclusion

Throw exceptions instead of using trigger_error.

Rain
  • 3,416
  • 3
  • 24
  • 40
  • That's what I was thinking, too. The only drawback I found is that you can't use OR for the throwing of an error/exception. E.g. mysqli_query return false if the query fails so I could do something like this: `$mysqli->query($sql) OR trigger_error('Error in '.__CLASS__.'-'.__FUNCTION__.':'.__LINE__.$mysqli->error.PHP_EOL.$sql, E_USER_ERROR);` When using Error / Exception I have to modify this code so it looks like this: `if(!mysqli->query($sql)){ throw new Error('Error in '.__CLASS__.'-'.__FUNCTION__.':'.__LINE__.$mysqli->error.PHP_EOL.$sql, E_USER_ERROR); } else{ ... }` – Alexander Behling Aug 30 '21 at 06:28
  • There also is a bug in the doc of php (https://www.php.net/manual/en/errorfunc.constants.php), but actually you can't correct it. I think it is because, the dev team is reviewing it. For each E_USER they mentioned that this is generated by using trigger_error. That isn't true, because it could also be a parameter in the constructor of a Error/ Exception. I would the sentence of at by. – Alexander Behling Aug 30 '21 at 06:33
  • @AlexanderBehling In OOP code you don't have to use Short-circuit evaluation (or). If you are using PHP 8.1 then just wrap your code with a `try` statement and then catch `mysqli_sql_exception`. Or if your PHP version < PHP 8.1 then turn your query errors to exceptions by `mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT)` or by `mysqli_driver::$report_mode` – Rain Aug 30 '21 at 07:11
  • @AlexanderBehling As for the documentation, it's correct. The only way to generate `E_USER_*` errors is through `trigger_error()`. When you pass it to an exception constructor then what will be thrown is an exception not a `E_ERROR`. – Rain Aug 30 '21 at 07:27
  • Seems that I'm not the only one who is struggling with this. see https://stackoverflow.com/questions/1626119/are-php-exceptions-really-more-useful-than-errors-adv?rq=1 or https://stackoverflow.com/questions/7063053/php-error-handling-die-vs-trigger-error-vs-throw-exception?rq=1 – Alexander Behling Aug 30 '21 at 11:50
  • You're right about trigger_error. The main difference is trigger_error use the error reporting of the interpreter aka Zend Engine. Exception / Error are the OOP way to deal with errors in PHP. Althought it seems that the Error class is a descendant of Exception it isn't. It is defined as an independent class and is supposed to serve as an alias for Exception. Thus, Exception and Error can be interchanged without causing problems in program execution. See also \Zend\zend_exceptions.stup.php of the php source code. – Alexander Behling Aug 31 '21 at 11:20
  • Be aware when setting an error_handler with set_error_handler and it always returns true the program execution will continue regardless which error level constant is given. Therefore it is mandatory that you have to check it and if it is an unrecoverable error return false or use die /exit to stop code execution. – Alexander Behling Aug 31 '21 at 11:29
  • @AlexanderBehling i see you ave been told to use mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT) almost a year ago, but you are still using this enormously elaborate approach with multiple manually written conditions that bloat your code tenfold. And again I am bound to ask: *what kind of logic* can be behind that decision? – Your Common Sense Mar 18 '22 at 07:10
  • @YourCommonSense To what code you are referencing? Could you please provide a better way to do it. This will help me and other looking at this as well. – Alexander Behling Mar 18 '22 at 07:19
  • @AlexanderBehling I am referencing to all that mess mentioned in the very first comment here. All that `OR trigger_error('Error in` stuff. That is made obsoleted by configuring mysqli to throw exceptions by itself. by means of adding a single line to your mysqli connection code, mentioned in the 3rd comment: `mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT)` – Your Common Sense Mar 18 '22 at 07:25
  • Ah, I see. Thanks for clearifying this. – Alexander Behling Mar 18 '22 at 07:31