50

Is it possible to have PHP stop execution upon a notice/warning, globally?

We run a development server with a lot of sites on, but want to force our developers to fix these warnings/notices (or ask for help with them at least) instead of ignoring and moving on.

Benny Mose
  • 694
  • 1
  • 8
  • 15

7 Answers7

64

Yes, it is possible. This question speaks to the more general issue of how to handle errors in PHP. You should define and register a custom error handler using set_error_handlerdocs to customize handling for PHP errors.

IMHO it's best to throw an exception on any PHP error and use try/catch blocks to control program flow, but opinions differ on this point.

To accomplish the OP's stated goal you might do something like:

function errHandle($errNo, $errStr, $errFile, $errLine) {
    $msg = "$errStr in $errFile on line $errLine";
    if ($errNo == E_NOTICE || $errNo == E_WARNING) {
        throw new ErrorException($msg, $errNo);
    } else {
        echo $msg;
    }
}

set_error_handler('errHandle');

The above code will throw an ErrorException any time an E_NOTICE or E_WARNING is raised, effectively terminating script output (if the exception isn't caught). Throwing exceptions on PHP errors is best combined with a parallel exception handling strategy (set_exception_handler) to gracefully terminate in production environments.

Note that the above example will not respect the @ error suppression operator. If this is important to you, simply add a check with the error_reporting() function as demonstrated here:

function errHandle($errNo, $errStr, $errFile, $errLine) {
    if (error_reporting() == 0) {
        // @ suppression used, don't worry about it
        return;
    }
    // handle error here
}
  • And the error handler can be defined and registered via the `auto_prepend_file` setting. – goat May 09 '12 at 17:08
  • Good stuff, auto_prepend_file it is :-).. let's see how Magento likes this ontop of it's own exception handling. – Benny Mose May 10 '12 at 11:29
  • For what it's worth, I'd strongly discourage the use of `auto_prepend_file`. –  Aug 21 '12 at 19:42
  • 5
    @BryanH I'm a big proponent that your code should fully encapsulate what's happening. `auto_prepend_file` is way too "magical" and it makes it unclear from looking at your code exactly what's happening. Say someone else has to maintain your code. How would they know that you buried some "auto include" in php.ini? And what if that auto prepend file has errors? Talk about a **debugging sanity nightmare**. There's no good reason not to simply use `require` directly in your code. `auto_prepend_file` is a *terrible* idea IMHO. –  Aug 23 '12 at 17:27
  • 1
    @rdlowrey - your explanation would be a good thing to add to the [php.ini wiki](http://stackoverflow.com/tags/php.ini/info) – BryanH Aug 23 '12 at 18:39
  • you should use if (!(error_reporting() & $severity)) then it will honor the error_reporting level, thus be quiet when it should be, not only when its nonzero (as your code is now) – hanshenrik Mar 02 '16 at 14:32
  • This is Awesome. – Abdul Rehman May 17 '17 at 08:37
8

You can globally configure this within your php.ini file­Docs.

It can be done by specifying a file that is being included by all PHP processes with the auto_prepend_file directive­Docs:

auto_prepend_file "/full/path/to/a/prepend-file.php"

Doing that inside your global php.ini file will ensure that the code inside the prepend file will be always executed. Use it then to register a global error handler that will turn all errors into exceptions­Docs:

<?php
function exception_error_handler($errno, $errstr, $errfile, $errline ) {
    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
set_error_handler("exception_error_handler");

This little script will turn all errors/warnings/notices etc. into an ErrorException.

This code example is likely too short (will comment further below) so don't miss the examples on the set_error_handler()­Docs manual page.

Combine it with the error_reporting ini directive­Docs, the values are describe in your php.ini file (or as another option use the second parameter for set_error_handler handle errors by their severity).

Throwing an exception is a very strict thing. Your programmers will be forced to deal with warnings and errors otherwise the code won't not work (including those in code with the @ error control operator, rendering it void).

Probably this is too much and not socially acceptable within the company. Instead you can also log all errors and add them at the end of the output (or at the top so it's more visible) by making use of output buffering.

Creating (but not throwing) the error exception already captures a back-trace which can be useful in error handling.

Another alternative to display the errors and warnings is to log them to the log-file and then monitor that file with another process and aggregate the information. You could create a scoreboard that display the number of warnings per application and make this visible in the office so everybody sees how well a team performs or some fun like that.

What I try to say is: Next to the technical problem, it's a social problem you need to be creative with and actually talk the problem out of your developers. You need to make clear why it's important to fix warnings and errors and how their coding and skills will benefit from that.

hakre
  • 193,403
  • 52
  • 435
  • 836
  • 2
    For the record, I don't espouse the use of `auto_prepend_file`. I think code should be explicit about what's going on. I don't want a php.ini directive to result in magic behavior that I can't see in the actual source code. IMHO it's *much* better to make a simple `require` call. What happens a month from now when you look at a piece of code and you lose your sanity because it makes no sense why something is happening? What happens if a new developer joins the team? They would have no idea that a php.ini directive is automatically including code. –  May 10 '12 at 14:12
  • Further, what if every php execution shouldn't handle errors in the same way? `auto_prepend_file` and `auto_append_file` needlessly lock you into specific behaviors and I see it as one of PHP's "Bad Parts." Just my two cents :) –  May 10 '12 at 14:14
  • @rdlowrey: `auto_prepend_file` has weaknesses, but is easy to give a go for starters. I personally love much more central logging and hooking onto logs with shell scripts, for example by notifying errors to the desktop like: https://github.com/ktomk/Miscellaneous/tree/master/notify-phperrors – hakre May 10 '12 at 15:12
  • Agreed. The ideal situation is centralized logging ... of course we both know a system like that is well beyond the scope of this post. I just have `check-mark` envy :) –  May 10 '12 at 15:18
7

As of PHP 5.3.0:

set_error_handler(
    function(int $nSeverity, string $strMessage, string $strFilePath, int $nLineNumber){
        if(error_reporting()!==0) // Not error suppression operator @
            throw new \ErrorException($strMessage, /*nExceptionCode*/ 0, $nSeverity, $strFilePath, $nLineNumber);
    },
    /*E_ALL*/ -1
);
YakovL
  • 7,557
  • 12
  • 62
  • 102
oxygen
  • 5,891
  • 6
  • 37
  • 69
5

If you are in a development environment, you might consider installing the module Xdebug and set xdebug.halt_level=E_WARNING|E_NOTICE|E_USER_WARNING|E_USER_NOTICE somewhere in your PHP ini files. The developer can reset xdebug.halt_level , but we have the same issue with the answer based on set_error_handler, because we can restore the handler to its original value.

  • I personally prefer this approach. For us, this approach is the best in terms of scalability. Non-production environments ( development, test, stage, etc ) all run on single machines/instances. Production is balanced across many. The idea of having to have aut/preloaded files only gives an additional area for problems. With this approach, all environments are the same, except non-product stops on essentially anything, which drastically eliminates what ends up on Production. – conrad10781 Dec 12 '17 at 13:32
5

The other solutions implement a custom error handler which overrides the default error logging mechanism. That means when we set a custom error handler, the notice / warning is not printed anymore in the default way, so we change more than just "stop script execution upon notice / warning".

However, I needed a way to still print the message - in my case in PHP CLI - since the output is then parsed by another program. So I didn't want to print the message in some way but in the exact same way than PHP usually does it. I just wanted to stop the PHP CLI process with an exit code of != 0 right after a notice / warning was printed, and do not change anything else.

Unfortunately, there does not seem to be a default way of exiting the script without changing the whole error handling. So I needed to reimplement the default error handler (which is implemented in C) in PHP.

For this, I looked into the default error printing mechanism in PHP's source code and ported the relevant pieces from C to PHP to get the following error handler, which I want to share with you in case you need something similar:

set_error_handler(
    function($errNo, $errStr, $errFile, $errLine) {
        $error_type_str = 'Error';
        // Source of the switch logic: default error handler in PHP's main.c
        switch ($errNo) {
            case E_ERROR:
            case E_CORE_ERROR:
            case E_COMPILE_ERROR:
            case E_USER_ERROR:
                $error_type_str = "Fatal error";
                break;
            case E_RECOVERABLE_ERROR:
                $error_type_str = "Recoverable fatal error";
                break;
            case E_WARNING:
            case E_CORE_WARNING:
            case E_COMPILE_WARNING:
            case E_USER_WARNING:
                $error_type_str = "Warning";
                break;
            case E_PARSE:
                $error_type_str = "Parse error";
                break;
            case E_NOTICE:
            case E_USER_NOTICE:
                $error_type_str = "Notice";
                break;
            case E_STRICT:
                $error_type_str = "Strict Standards";
                break;
            case E_DEPRECATED:
            case E_USER_DEPRECATED:
                $error_type_str = "Deprecated";
                break;
            default:
                $error_type_str = "Unknown error";
                break;
        }
        fwrite(STDERR, "PHP $error_type_str:  $errStr in $errFile on line $errLine\n");
        exit(1);
    },
    E_ALL
);
leemes
  • 44,967
  • 21
  • 135
  • 183
1

Note concerning the return value of error_reporting() when the @ operator is used:

Prior to PHP 8.0.0, the error_reporting() called inside the custom error handler always returned 0 if the error was suppressed by the @ operator. As of PHP 8.0.0, it returns the value E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR | E_PARSE.

(from https://www.php.net/manual/en/language.operators.errorcontrol.php)

-4

You can turn on the error_reporting to E_ALL in PHP Configuration file of your server.

In PHP 5 a new error level E_STRICT is available. As E_STRICT is not included within E_ALL you have to explicitly enable this kind of error level. Enabling E_STRICT during development has some benefits. STRICT messages will help you to use the latest and greatest suggested method of coding, for example warn you about using deprecated functions.

Reference: PHP Configuration Manual

The following settings guide you to output all the errors in a script. Please note that these will need to be included in the start of all your script files.

<?php

// Turn off all error reporting
error_reporting(0);

// Report simple running errors
error_reporting(E_ERROR | E_WARNING | E_PARSE);

// Reporting E_NOTICE can be good too (to report uninitialized
// variables or catch variable name misspellings ...)
error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE);

// Report all errors except E_NOTICE
// This is the default value set in php.ini
error_reporting(E_ALL ^ E_NOTICE);

// Report all PHP errors (see changelog)
error_reporting(E_ALL);

// Report all PHP errors
error_reporting(-1);

// Same as error_reporting(E_ALL);
ini_set('error_reporting', E_ALL);

?>
  • 3
    Your answer is missing the "stop execution" part of the question. Setting the error reporting will only affect output (either on screen or into a log), but only errors or fatals will stop the script - warnings and notices will not. – Sven Aug 25 '16 at 16:21