tags:

views:

562

answers:

6

I am working through some code done by a previous developer. I'm pretty new to PHP so I am wondering if there is any well known pattern or solution to this problem.

Basically the original author does not check any array indexes before he tries to use them. I know I can use isset() to check each before it is used, but right now there are hundreds of lines where these errors are appearing. Before I put on some music and start slamming my head into my keyboard I want to make sure there is not some nice shortcut for handling this. Here is a typical section of code I'm looking at:

    /* snip */
"text" => $link . $top_pick_marker . $output['author'] . " " .  " " . 
                              $output['new_icon'] . $output['rec_labels'] . "   " 
                    . $output['admin_link']
                    . $output['alternate_title'] 
                    . $output['access_info'] 
                    . $output['description'] 
                    . $output['url']
                    . $output['subject_terms'] 
                    . $output['form_subdivisions'] 
                    . $output['dates_of_coverage']  
                    . $output['update_frequency']  
                    . $output['place_terms'],
    /* snip */

So I know I can use isset() here for each item. I would have to rearrange things a bit and remove all the concatenation as it is now. Is there any other easy way to do this or am I just stuck with it?

+1  A: 

Set each index in the array at the beginning (or before the $output array is used) would probably be the easiest solution for your case.

Example

$output['admin_link'] = ""
$output['alternate_title'] = ""
$output['access_info'] = ""
$output['description'] = ""
$output['url'] = ""


Also not really relevant for your case but where you said you were new to PHP and this is not really immediately obvious isset() can take multiple arguments. So in stead of this:

if(isset($var1) && isset($var2) && isset($var3) ...){
    // all are set
}

You can do:

if(isset($var1, $var2, $var3)){
   // all are set 
}
MitMaro
+3  A: 

Figure out what keys are in the $output array, and fill the missing ones in with empty strings.

$keys = array_keys($output);
$desired_keys = array('author', 'new_icon', 'admin_link', 'etc.');

foreach($desired_keys as $desired_key){
   if(in_array($desired_key, $keys)) continue;  // already set
   $output[$desired_key] = '';
}
SquareRootOf2
This is a good solution for a large list of index keys. I would use something like this for the poster's problem.
zombat
+1  A: 

You can use isset() without losing the concatenation:

//snip
$str = 'something'
 . ( isset($output['alternate_title']) ? $output['alternate_title'] : '' )
 . ( isset($output['access_info']) ? $output['access_info'] : '' )
 . //etc.

You could also write a function to return the string if it is set - this probably isn't very efficient:

function getIfSet(& $var) {
    if (isset($var)) {
        return $var;
    }
    return null;
}

$str = getIfSet($output['alternate_title']) . getIfSet($output['access_info']) //etc

You won't get a notice because the variable is passed by reference.

Tom Haigh
He did say he didn't want to use `isset`, but this is still the "neatest" way to do it with `isset`.
MitMaro
Also this way wouldn't be hard to implement since you could probably use a regex search and replace to do it automatically.
MitMaro
@MitMaro - agreed. I find the ternary operator very handy for handling this problem for string output or concatenation. For a small number of array keys, this method works well.
zombat
A: 

You could try using a nice little function that will return the value if it exists or an empty string if not. This is what I use:

function arrayValueForKey($arrayName, $key) {
   if (isset($GLOBALS[$arrayName]) && isset($GLOBALS[$arrayName][$key])) {
      return $GLOBALS[$variable][$key];
   } else {
      return '';
   }
}

Then you can use it like this:

echo ' Values: ' . arrayValueForKey('output', 'admin_link') 
                 . arrayValueForKey('output', 'update_frequency');

And it won't throw up any errors!

Hope this helps!

Michael Waterfall
Why the use of `GLOBALS` instead of just using the arguments?
grantwparks
Using globals would be a good way to do this as it avoids a 3rd argument in the function, keeping things clean and simple to use again and again. Admittedly it won't work when using OO PHP, but for simple things it'll work great with not much more code than would normally be needed to access the array directly.
Michael Waterfall
+1  A: 

If you are maintaining old code, you probably cannot aim for "the best possible code ever"... That's one case when, in my opinion, you could lower the error_reporting level.

These "undefined index" should only be Notices ; so, you could set the error_reporting level to exclude notices.

One solution is with the error_reporting function, like this :

// Report all errors except E_NOTICE
error_reporting(E_ALL ^ E_NOTICE);

The good thing with this solution is you can set it to exclude notices only when it's necessary (say, for instance, if there is only one or two files with that kind of code)

One other solution would be to set this in php.ini (might not be such a good idea if you are working on several applications, though, as it could mask useful notices ) ; see error_reporting in php.ini.

But I insist : this is acceptable only because you are maintaining an old application -- you should not do that when developping new code !

Pascal MARTIN
Personally I don't think you should ever hide any 'errors' even in old code. More then likely the errors will be hidden in the old code and there will be no incentive to fix them.
MitMaro
I kind of agree, and I don't like doing that either ; but, sometimes, there is really no other way, if you want to be able to work (at least if you don't have enough time to correct those) ; especially when the application has been developped by someone who didn't know what notices are and to both enable and avoid them...
Pascal MARTIN
+1  A: 

A variation on SquareRootOf2's answer, but this should be placed before the first use of the $output variable:

$keys = array('key1', 'key2', 'etc');
$output = array_fill_keys($keys, '');
Sterling Hirsh