11

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.

Community
  • 1
  • 1
mario
  • 144,265
  • 20
  • 237
  • 291
  • 12
    Personally, I feel your presumption is wrong. My position is that they are thrown by undefined array indexes or variable usage. If a variable is undefined, accessing it **is** an error. Yes, a lot of people just turn notices off and never use `isset`. Is that wrong? No. But you do lose a lot of valid undefined variable errors (such as variable mis-spellings)... So while it's not "wrong", I think not using it (`isset`, and developing notice-free code) is lazy and not a great practice (even bordering on "bad practice")... – ircmaxell Nov 11 '10 at 19:42
  • @ircmaxell My take is that it really depends. There is logic where a NULL or undef variable can bite you in the butt quite exceptionally. For the majority of PHP code, dealing with form input etc, it doesn't seem that farfetched to accept PHPs dynamic semantics where undefined vars are **not** errors. Again, it depends. Not trying to avoid isset at all costs. Also I do want to retain the notices actually, basically as NULL assertions. Just less of them. – mario Nov 11 '10 at 19:48
  • 2
    That's fair. But if you go that far, why are you even accessing raw form input at all? Why aren't you using a library which can handle validation, cleaning, etc for you? Instead of `$foo = $_GET['bar']`, `$foo = $request->get('bar');`... Then you're not littered everywhere with `isset()`, and you actually make it more readable. While I agree that it's not "farfetched to accept PHPs dynamic...", I would argue whether it's good or not to do so. Predefining variables and checking for existence not only makes it more readable, it also clarifies your intent (which makes debugging easier). – ircmaxell Nov 11 '10 at 19:53
  • @ircmaxell: Well I do already ,) this one http://sourceforge.net/p/php7framework/wiki/input/ - while I could disable the manual E_NOTICE trigger, it's infrequently desirable to have them. So my use case are less the $_REQUEST arrays/objects, but in-application undef/optional vars and parameters. But, point taken on the expliciteness of hardcoded isset() tests being an advantage for reliability. Maybe `assert(isset())` is sometimes even preferable. – mario Nov 11 '10 at 19:59
  • 2
    I could see that (the assertion)... Plus, you could take the defaulting approach to optional variables: `$array = $array + array('optional1' => '', 'optional2' => '');`... That way, it's clear from a cursory glance what you might expect (as opposed to having to read through the entire method to see what keys you use)... Again, that's just my stance on it... – ircmaxell Nov 11 '10 at 20:05
  • Agreed with ircmaxwell. I develop with all notices set to throw Exceptions using ErrorException and it has saved me a world of pain from typos and assumptions about what variables are present. Perhaps it's too long writing C, but I hate that PHP, by default, lets you away with referencing array keys and variables that don't even exist. If possible, I'd somehow force static typing too... – El Yobo Nov 15 '10 at 00:16
  • @El Yobo - I also agree with him. There is no discussion about declarative coding being more reliable. Yet, do you keep the notices/warnings throw exceptions at runtime, or do you use it exclusively as development feature? (I meanwhile realize, if this wasn't for the PHP code sample part, this question would actually belong on programmers SE.) -- Btw, static typing can be enforced for object attributes at least. – mario Nov 15 '10 at 00:27
  • 1
    @mario: I do use them in production. But I have a custom error and exception handler that logs debug information and emails a copy to my issue tracking system. That way if *any* error happens, I am immediately notified so that I can start debugging. After all, how can you debug what you don't know exists (and don't have decent backtrace / environmental information about). – ircmaxell Nov 15 '10 at 13:37
  • I really wonder which E_NOTICE's you don't wanna see? – c0rnh0li0 Nov 20 '10 at 15:34
  • @c0rnh0li0: Yes, would be a better question with examples. The only thing that irked me currently was a loop iterating over a list of optional entries. Coincidentally this would be a place where I'd normally "use lots of isset lol" too, because multiple `foreach{$opt["undef"]}` accesses might be a performance drain. Still that's what got me wondering: isset is a micro optimization. – mario Nov 20 '10 at 16:52
  • @ircmaxell " If a variable is undefined, accessing it is an error" I think PHP's behavior here is questionable. If you have an array `$foo` and you try to access an undefined index `'bar'` it would be much more helpful if PHP just returned null. Where it *would* make sense to throw an error would be when accessing `$foo['bar']['baz']`, since then the access presumes an array where there is none. This is what JS does (`foo.bar`) and IMO it's much cleaner. That being said, the null coalesce operator (in PHP 7) will remove a lot of the repetition. Using `$foo['bar'] ?? null` will be a godsend. – Chris Middleton Apr 11 '15 at 22:20

9 Answers9

9

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.

staticsan
  • 29,935
  • 4
  • 60
  • 73
  • I have to upvote *and* disagree. :) And need to update my question; Because I do **not** want to avoid and supress all E_NOTICEs. In fact I believe they are essential to be spewed. I'm just disliking the all the time part. -- When I wish to debug (=> turn on E_NOTICEs) I want to be informed of unset variables. If there are isset decorators everywhere, I won't get any. My wish is to classify E_NOTICEs as expected and desired, so the *possible* problems are highlighted by turning on the debug/e_notice mode. – mario Nov 15 '10 at 00:20
  • 4
    Ah... I could be wrong but think I see where you're going. And if I'm right, you're trying to use a development technique that isn't going to work in PHP, which is why you're having trouble. IME, a better approach is to leave `E_NOTICE` *on* all the time and fix Notices where and as you find them. Yes, this means coding a little more defensively and, yes, it means a possible forest of `isset()` calls before you rely on a variable that might not exist (but you *can* check once and set a default). Put another way, it means you're *always* looking for non-existent variables whilst developing. – staticsan Nov 15 '10 at 02:16
  • @mario, Would php's assert be helpful? http://us2.php.net/manual/en/function.assert.php . Otherwise, I'm in full agreement with staticscan (both answer and comment). – enobrev Nov 18 '10 at 05:43
  • @enobrev It's not the right answer here. And I realize I'm arguing against the common E_NOTICE cleanliness meme. But adding `isset` as syntactic warning suppression is counterproductive IMO. – mario Nov 18 '10 at 06:31
  • There's your problem: you're seeing `isset()` as a means to suppress a syntax warning. Although that is how programmers often discover it, IMO that is **not** what it is for. Instead, you should see its function as an integral step in the algorithm. (FWIW, in my own code it is used in less than 1% of lines.) – staticsan Nov 18 '10 at 23:18
  • Nah, that's not how I see it. It's the impression I got from your comment about "fix Notices". This approach assumes notices === errors, which would be true for Java or C++, but less so for PHP. -- But to recap: If you see an E_NOTICE you will (1) look into it and rewrite if it's a problem and (2) always add an isset so the E_NOTICE goes away. And that's where I'd like to digress. Assessing the problem capability (1) is why I want to see E_NOTICES in the first place. But I don't want to supress (2) said notice just after one look, or because I've supplied an expression default. I'd retain it. – mario Nov 20 '10 at 17:03
  • The trouble about seeing Notices as less than errors that should be fixed in development is that PHP emits Notices for a variety of things. If you are willing to accept Notices for un-initialized variables, you have to accept Notices for all the other reasons. I'm not willing to accept Notices in Production code at all, therefore I check in code for conditions that could emit them and prevent them happening. – staticsan Nov 21 '10 at 22:35
6

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.

István Ujj-Mészáros
  • 3,228
  • 1
  • 27
  • 46
  • +1 Interesting points. While not entirely reasoned, the @ operator post was a good overview. (However I disagree on omitting `?>`, which is more of a newcomer coding style recommendation. The mentioned error results from sloppy coding, mostly on Windows and due to weak text editors.) – mario Nov 18 '10 at 19:54
  • 1
    @mario There are many recommendations about avoiding the closing tag. For example, there is [Zend framework coding standard](http://framework.zend.com/manual/en/coding-standard.php-file-formatting.html#coding-standard.php-file-formatting.general), and a [Sitepoint article](http://blogs.sitepoint.com/2010/09/18/should-you-close-your-php-code-tags/), and an [other good one](http://www.finishjoomla.com/blog/20/the-php-closing-tag-explained/). And sometimes the errors come from [outside](http://svn.haxx.se/tsvnusers/archive-2009-01/0480.shtml) *(I have run into this once)*. – István Ujj-Mészáros Nov 18 '10 at 20:32
  • Good summary about not using `@` too much. I personally use it only where things can (and will!) emit errors when there is no reason to (`fopen()` is my favourite culprit) and in code that wasn't mind, I don't have time to fix but must keep running. – staticsan Nov 18 '10 at 23:32
  • Bounty here. While I disagree on a couple of points, this answer is the least offmeta in regards to my original quest; and more importantly it sounds like reasoned advise. – mario Nov 21 '10 at 19:01
3

Well, if you wait for PHP 7, you'll have access to the null coalesce ternary operator, which, in addition to having the coolest operator name in existence (I'm naming my next kid "Null Coalesce") will let you do this:

$var = $some_array[$some_value] ?? "default value";

Which replaces the ubiquitous (and ugly)

$var = isset( $some_array[$some_value] ) ? $some_array[$some_value] : "default_value";
mario
  • 144,265
  • 20
  • 237
  • 291
Tom Auger
  • 19,421
  • 22
  • 81
  • 104
  • 2
    (Based on the current mailing list musings, there might even be a 5.7 backporting a few things. [PHP7 deployment is unlikely to become widespread in a decade, due to all the deprecations..]) – mario Dec 18 '14 at 19:45
2

try xdebug - http://www.xdebug.org/docs/stack_trace

lots of isset checking does not harm u,

in fact, it encourage declare variables before use it

ajreal
  • 46,720
  • 11
  • 89
  • 119
2

I think following the best practice is not waste of time. That's true, a notice is not an error, but with correct variable declaration and validation your code could be more readable and secure. But it's not so complex to write a user-defined error handler with debug_backtrace sort the E_NOTICE(8) with a regexp.

PazsitZ
  • 131
  • 2
2

I had a similar desire. So I started using custom error handlers.

http://php.net/manual/en/function.set-error-handler.php

You can then create your own filters/mechanisms for displaying/logging errors/notices.

Cheers!

Sean Thayne
  • 863
  • 1
  • 7
  • 9
2

PHP is definitely broken around this making code less readible. "null" means "undefined" - simple enough.

Here is what I do when I run into this problem making code unreadable:

/**
 * Safely index a possibly incomplete array without a php "undefined index" warning.
 * @param <type> $array
 * @param <type> $index
 * @return <type> null, or the value in the index (possibly null)
 */
function safeindex($array, $index) {
  if (!is_array($array)) return null;
  return (isset($array[$index])) ? $array[$index] : null;
}

// this might generate a warning
$configMenus = $config['menus'];  

// WTF are you talking about!!  16 punctuation marks!!!
$configMenus = (isset($config['menus']) ? $config['menus'] : null;

// First-time awkward, but readible
$configMenus = safeindex($config, 'menus');

Cross posting this answer here. Does this help spam-checker?

Mike
  • 3,087
  • 3
  • 17
  • 13
  • +1 for the great suggestion Mike. I really like it...especially since you can use this method on a case-by-case basis rather than setting error_reporting() or set_error_handler() which covers a whole section of script. It only works for array indices, but those are the majority that I'm battling (usually from $_POST and $_SESSION). – Simon East Jul 18 '11 at 05:50
1

The best way to avoid isset() in my opinion is to define your variables before you use them. I dislike isset() not so much because it's ugly but because it promotes a bad programming practice.

As for error handling itself, I put all that information out to the server logs. I also use php -l on the command line to syntax check the programs before hand. I make pretty messages for the users by default.

You might look into one of the various frameworks to see if any of them would work for you. Most of them I've looked at have error handling routines to make things easier than what PHP offers out of the box.

EDIT: @mario - my response to your comment was getting too long :-). I don't advocate defining types or going to some kind of strict format like Java or C. I just advocate declaring the variable in the context that it's used. ( $foo = null; is not the same as leaving the variable blank).

I think this is more of a problem with global variables in a lot of cases, especially the super globals for getting GET and POST data. I really wish that PHP would drop super globals in favor of an class for getting input data. Something like this (super simple but hey you wanted something concrete: :) )

<?php
class PostData {
     private $data;

     public function __construct() {
          $this->data = $_POST;
          unset($_POST);
     }

     public function param($name, $value = null) {
          if( $value !== null ) {
               $this->data[$name] = $value;
          }

          if( isset( $this->data[$name] ) ) {
               return $this->data[$name];
          }
          return null;
      }
}
?>  

Include the class then you can get and set POST data from the param() method. It would also be a nice way to incorporate validation into the input data. And as a bonus, no checking everything for isset() (it already is).

Cfreak
  • 19,191
  • 6
  • 49
  • 60
  • 3
    Can you elaborate on *I dislike `isset()` not so much because it's ugly but because it promotes a bad programming practice.* ? I'm curious as to what bad practices it promotes... – ircmaxell Nov 11 '10 at 19:44
  • Because it encourages people to declare variables on the fly. Declaring variables (even in a weakly typed language) makes the code more maintainable for someone else and for yourself when you come back to a project after 6 months. It used to be encouraged, apparently being old school gets one modded down around here. – Cfreak Nov 12 '10 at 15:32
  • 1
    Upvote (back to 0). Last note fits the question. It's too inconcrete however, as I'm looking for a specific implementation of a clueful or filtering error handler. (Surely *some* framework must have one.) -- But let me also disagree on the first point. IMO you shouldn't try to bend Java or C semantics onto PHP. Explicit declarations *can* make code more reliable, yet PHP or Perl deal really well with NULL or undef in most contexts, so it's redundant. E_"NOTICE" should be taken literally, not be glorified as error. (But that's just my opinion ;) – mario Nov 12 '10 at 23:49
  • 1
    @Cfreak: I disagree with that premise. `isset()` does not encourage people to declare variables on the fly. Sure, it provides a mechanism for people to do so, but I've *never* seen it used in that context. I agree that predeclaring variables is the way to go even if just from a readability standpoint. But I really don't think `isset()` should be blamed for promoting bad practices (especially considering that most instances of the bad practice you describe that I've seen don't even use `isset` and hence throw notices. Limiting the use of a useful tool because it *can* be abused is pointless. – ircmaxell Nov 15 '10 at 13:26
  • @ircmaxwell - where did I say it should be limited? I stated I dislike it and I gave a reason. That's my opinion. Feel free to disagree if you want but don't put words in my mouth. – Cfreak Nov 15 '10 at 16:23
  • @Cfreak: I see where you are going. You raise an important point with differentiating NULL and UNDEF (as staticsan also mentioned). My point is that `$foo = $_GET["undef"];` is very equivalent to your explicit `$foo = null` example. Again, very use case dependent! But if you throw in an `isset` or predefine variables, you actually just lose a debugging notice. It doesn't change the behaviour. I agree on making it explicit where necessary though. -- But btw, I already have an input class http://sourceforge.net/p/php7framework/wiki/input/ which can alleviate the superglobals isset worries nicely – mario Nov 15 '10 at 17:50
  • There's nothing wrong with an explicit check in the code for an error condition instead of letting it emit an error. I come up against the same mentality against using exceptions. – staticsan Nov 18 '10 at 23:25
1

It's kind of an outdated answer now, but I originally used a flexible log dispatcher back then, https://github.com/grosser/errorhandler (Not exactly what I was looking for IIRC, but at least a little more sophisticated than alternating between complete and partial supression.)

Anyway, I'm meanwhile using a $_GET->int["input"] wrapper for the most common cases. Which is just a trivial ArrayAccess wrapper, implicitly catches non-existant vars, that allows easier notice recovery. (Just a by-product. Primarily meant for immediate filtering though.)

And for another project I'm even using a preproccessor macro IFSET@($var), to allow enabling/disabling or log-redirection depending on build parameters.

mario
  • 144,265
  • 20
  • 237
  • 291