Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

E_NOTICE ?== E_DEBUG, avoiding isset() and @ with more sophisticated error_handler

Which better ways exist to avoid an abundance of isset() in the application logic, and retain the ability to see debug messages (E_NOTICE) when required?

Presumption first: E_NOTICE is not an error, it's a misnomer and should actually be E_DEBUG. However while this is true for unset variables (PHP is still a scripting language), some file system functions etc. throw them too. Hence it's desirable to develop with E_NOTICEs on.

Yet not all debug notices are useful, which is why it's a common (unfortunate) PHP idiom to introduce isset() and @ throughout the application logic. There are certainly many valid use cases for isset/empty, yet overall it seems syntactic salt and can actually obstruct debugging.

That's why I currently use an error_reporting bookmarklet and a dumb on/off switch:

// javascript:(function(){document.cookie=(document.cookie.match(/error_reporting=1/)?'error_reporting=0':'error_reporting=1')})()

if (($_SERVER["REMOTE_ADDR"] == "127.0.0.1")
    and $_COOKIE["error_reporting"])
{
    error_reporting(E_ALL|E_STRICT);
}
else {/* less */}

However that still leaves me with the problem of having too many notices to search through once enabled. As workaround I could utilize the @ error suppression operator. Unlike isset() it does not completely kill debugging options, because a custom error handler could still receive suppressed E_NOTICEs. So it might help to separate expected debug notices from potential issues.

Yet that's likewise unsatisfactory. Hence the question. Does anyone use or know of a more sophisticated PHP error handler. I'm imagining something that:

  • outputs unfiltered errors/warnings/notices (with CSS absolute positioning?)
  • and AJAX-whatnot to allow client-side inspection and suppression
  • but also saves away a filtering list of expected and "approved" notices or warnings.

Surely some framework must already have a user error handler like that.

  • Basically I'm interested in warning / notice management.
  • Full E_NOTICE supression is really not desired.
  • E_NOTICES are wanted. Just less of them. Per default highlight the ones I might care about, not the expected.
  • If I run without ?order= parameter, an expected NOTICE occours. Which due to be expected I do not need to informed about more than once.
  • However when in full debugging mode, I do wish to see the presence of undefined variables through the presence (or more interestingly absence) of said debug notices. -> That's what I think they are for. Avoiding isset brings language-implicit print statements.
  • Also realize this is about use cases where ordinary PHP form handling semantics are suitable, not application areas where strictness is a must.

Oh my, someone please help rewrite this. Lengthy explanation fail.

like image 580
mario Avatar asked Nov 11 '10 19:11

mario


People also ask

What is isset?

The isset() function is an inbuilt function in PHP which checks whether a variable is set and is not NULL. This function also checks if a declared variable, array or array key has null value, if it does, isset() returns false, it returns true in all other possible cases.

How to check isset value in PHP?

PHP isset() Function The isset() function checks whether a variable is set, which means that it has to be declared and is not NULL. This function returns true if the variable exists and is not NULL, otherwise it returns false.


2 Answers

It is possible to develop a large PHP application that never emits any E_NOTICEs. All you have to do is avoid all situations where a Notice can be emitted, the vast majority of which are un-initialized variables and non-existist array keys. Unfortunately, this clashes with your wish to avoid isset() - and by extension array_key_exists() - because they are designed for handling that exact problem.

At best, you can minimize their use by careful framework building. This generally means (for example) an input layer which is told what GET variables to expect and what to default missing ones to. That way the page-specific code will always have values to look at. This is, in general, a worthwhile technique that can be applied to a variety of APIs. But I question whether this should be a high-priority design goal.

Unlike some other languages, PHP distinguishes between a variable not existing and containing a generally "empty" value (usually null). It is probably a design artifact from an earlier version, but it nonetheless is still present, so you cannot really avoid it.

like image 152
staticsan Avatar answered Nov 04 '22 12:11

staticsan


I am using isset() only for $_GET and $_SERVER variables, where the data comes from outside the control of my application. And I am using it in some other situation when I don't have time to write a proper OOP solution to avoid it, but I'm sure that it can be avoided in most if not all places. For example it's better to use classes instead of associative arrays, this way you don't need to check the existence of an array key.

My advices are:

  • Avoid using the @ operator.
  • Use Xdebug. First, it prints easily readable and easily noticeable messages about every notice/warnig, and it prints a very useful stack trace on exceptions (you can configure it to print out every method parameter and every local variable (xdebug.collect_params=4 and xdebug.show_local_vars=on configuration parameters). Second, it can disable the @ operator with xdebug.scream=1 config value. You can use Xdebug for profiling and for code coverage analysis as well. It's a must have on your development machine.
  • For debugging, I am also using FirePHP, because it works with Firebug, and is able to print messages to the Firebug console, so it can be used for AJAX debugging as well.
  • With a custom error handler, you can catch and filter any error and warning, and you can log them into a file or display them with FirePHP, or you can use for example jGrowl or Gritter to nicely display them on the web page.

I am using a modified version of the example in the PHP manual:

<?php
//error_reporting(0);
set_error_handler("errorHandler");

function errorHandler($errno, $errstr, $errfile, $errline)
{
    echo "errorHandler()<br />\n";

    // filter out getImageSize() function with non existent files (because I'am avoiding using file_exists(), which is a costly operation)
    if ( mb_stripos($errstr, 'getimagesize') !== false )
        return true;

    // filter out filesize() function with non existent files
    if ( mb_stripos($errstr, 'filesize') !== false )
        return true;

    // consoleWriter is my class which sends the messages with FirePHP
    if (class_exists('consoleWriter'))
        consoleWriter::debug(array('errno'=>$errno, 'errstr'=>$errstr, 'errfile'=>$errfile, 'errline'=>$errline, 'trace'=>debug_backtrace()), "errorHandler");

    switch ($errno) {
    case E_USER_ERROR:
        $out .= "<b>FATAL_ERROR</b> <i>$errno</i> $errstr<br />\n";
        $out .= "Fatal error on line $errline in file $errfile";
        echo "</script>$out";   // if we were in a script tag, then the print is not visible without this
        //writeErrorLog($out);

        echo "<pre>";
        var_export(debug_backtrace());
        echo "</pre>";

        exit(1);
        break;

    case E_USER_WARNING:
        $out .= "<b>WARNING</b> <i>$errno</i> $errstr<br />\n";
        $out .= "On line $errline in file $errfile<br />\n";
        break;

    case E_USER_NOTICE:
        $out .= "<b>NOTICE</b> <i>$errno</i> $errstr<br />\n";
        $out .= "On line $errline in file $errfile<br />\n";
        break;

    default:
        $out .= "<b>Unknown</b> <i>$errno</i> $errstr<br />\n";
        $out .= "On line $errline in file $errfile<br />\n";
        break;
    }

    if (!class_exists('consoleWriter'))
        echo $out;

    //writeErrorLog($out);
    //addJGrowlMessage($out);

    // Don't execute PHP internal error handler
    return true;
}

function testNotice($a)
{
    echo $a;
}
testNotice();

One more advice is not to use the closing ?> tag at the end of the php-only files, because it can cause headers already sent errors on configurations where the output buffering is disabled by default.

like image 34
István Ujj-Mészáros Avatar answered Nov 04 '22 13:11

István Ujj-Mészáros