views:

193

answers:

12

Hello,

I am a C++ programmer starting with PHP. I find that I lose most of debugging time (and my selfesteem!) due to undefined variables. From what I know the only way to deal with them is to watch the output at execution time.

Are other strategies to notice earliers these faults? (something like with C++ that a single compile gives you all the clues you need)

Thanks for any insights

+2  A: 

From what I know the only way to deal with them is to watch the output at execution time.

Not really: To prevent these notices from popping up, you just need to make sure you initialize variables before accessing them the first time. We (sadly IMO) don't have variable declaration in PHP, but initializing them in the beginning of your code block is just as well:

$my_var = value;

Using phpDocumentor syntax, you can also kind of declare them to be of a certain a type, at least in a way that many IDEs are able to do code lookup with:

/** @desc optional description of what the variable does
    @var int */
$my_var = 0;

Also, you can (and sometimes need to) use isset() / empty() / array_key_exists() conditions before trying to access a variable.

I agree this sucks big time sometimes, but it's necessary. There should be no notices in finished production code - they eat up performance even if displaying them is turned off, plus they are very useful to find out typos one may have made when using a variable. (But you already know that.)

Pekka
+6  A: 

Log your E_NOTICE messages to a text file. You can then process logs with automated scripts to indicate files and lines where these are raised.

Mchl
This doesn't really fix the issue IMO. What is needed is a different working routine that prevents those notices from coming up in the first place.
Pekka
That's for sure. Browsing/parsing logs is just an 'improved' version of 'watching the output' to catch errors coming from legacy code, or those that somehow slipped into new code.
Mchl
no website should ever throw out errors, NEVER - atlest redirect the user to a static error page, imagine if you went on youtube and seen this in header `"PHP Notice: X:/server/server_mounts/primary/public/data/index.php The variable $youtube_global_credit_card_details was not found"` - just be a terrable thing especially for the general public. professionalism is always an approach you should take. :)
RobertPitt
Agree on that. Fortunately you can log errors without displaying them to the user.
Mchl
+4  A: 

No. In PHP, you can only know a variable doesn't exist when you try to access it.

Consider:

if ($data = file('my_file.txt')) {
    if (count($data) >= 0)
        $line = reset($data);
}
var_dump($line);

You have to restructure your code so that all the code paths leads to the variable defined, e.g.:

$line = "default value";
if ($data = file('my_file.txt')) {
    if (count($data) >= 0)
        $line = reset($data);
}
var_dump($line);

If there isn't any default value that makes, this is still better than isset because you'll warned if you have a typo in the variable name in the final if:

$line = null;
if ($data = file('my_file.txt')) {
    if (count($data) >= 0)
        $line = reset($data);
}
if ($line !== null) { /* ... */ }

Of course, you can use isset1 to check, at a given point, if a variable exists. However, if your code relies on that, it's probably poorly structured. My point is that, contrary to e.g. C/Java, you cannot, at compile time, determine if an access to a variable is valid. This is made worse by the nonexistence of block scope in PHP.

1 Strictly speaking, isset won't tell you whether a variable is set, it tell if it's set and is not null. Otherwise, you'll need get_defined_vars.

Artefacto
And what good does that do? He's replaced a visible runtime complaint with an access to a variable with a garbage value, now perhaps giving a silent bug rather than a diagnosed one. (0 is garbage [as is any other choice of initializing constant] if it isn't the value the variable is supposed to have at at that point in the code). What he needs is *static* analysis to determine where variables are used but not initialized. Too bad there aren't more PHP analysis tools.
Ira Baxter
@Pekka I call that accessing. I'll edit to make it more clear.
Artefacto
@Ira It was just an example of a code path that would lead to the variable always being defined. I agree with you.
Artefacto
+1: Coming from a C++ background, this should be habit already. And this will prevent tons of bugs. As for 0 being a garbage value, it's not at all since you know that if it's 0, it wasn't defined yet. I personally declare all variables prior to using them. It's a good habit and will rarely lead to bugs (so long as you don't do anything stupid like declare a int then later re-assign an object)...
ircmaxell
@ircmaxell: That's crazy. Most good compilers (PHP is not one of these) will diagnose an unassigned variable arriving by some control flow path to a use of that variable. If you *set* the variable to garbage, you simply make it impossible for the compiler to help you.
Ira Baxter
@Ira No one's saying "hey, it gave a notice – let's just initialize the var to garbage to shut it up". What I'm saying is you should restructure your flow so that the variable is always defined. Once in a while, it will mean you will have to initialize it to an otherwise invalid, special flag value. In this case, you can argue it would be the same as using `isset`, but it's not. If you do `isset($variableWithTypo)`, you'll have a bug, if you do `if ($variableWithTypo === null)` (where `null` is the special "flag value"), you'll also have a bug, but at least you get a notice.
Artefacto
Nobody is arguing that you shouldn't write code the *correctly* initializes the variables (whether you do that with explicit assignments or do it in such a way that a single assignment dominates all references). The discussion appeared to be about the case where the coder wasn't perfect (I'm not). In that case, you *want* the compiler's help, and the suggestion appeared to be "just initialized to a garbage value before you do anything else". That's my objection. *If* you can find a "garbage" value that will always makes the application complain, maybe that's OK. Zero isn't that value.
Ira Baxter
@Ira Yes, `0` doesn't have any meaning, but `(mt_rand(1,2) == 1)` doesn't have any meaning either, yet I don't see you protesting. Anyway, I'll edit the answer with a real world example...
Artefacto
+2  A: 

Just watch not to do operations that requires variable value when using it first time, like concat operator .=

If you are C++ programmer you must be used to declare all variables, Do something simillar to this in PHP by zero-ing variables or creating empty array if you want to use them.

Pay attention to user input, be sure you have register globals off and check inputs from $_GET and $_POST by isset().

You can also try to code classes against structural code, and have every variable created at begining of class declaration with correct privacy policy.

You can also separate application logic from view, by preparing all variables that have to be outputted first, and when it goes to display it, you will be know which variables you prepared.

killer_PL
A: 

I believe that various of the Code Coverage tools that are available for PHP will highlight this.

Mark Baker
+7  A: 
banzaimonkey
+1 for xdebug... great profiler/debugger.
Chris
+1, for the `error_reporting(-1);`, didn't knew about that (minus) one!
Alix Axel
A: 

Good practice is to define all variable before use i.e set a default value

$variable = default_value;

This will solve most problems. As suggested before use xdebug or built-in debugging tools in editors like Netbeans

Web Developer
A: 

Personally, I try and set variables, even if it's with an empty string, array, Boolean etc. Then use a function such as isset() before using them. For example:

$page_found = false;

if ($page_found==false) {
    // do page not found stuff here
}

if (isset($_POST['field'])) {
    $value = $_POST['field'];
    $sql = "UPDATE table SET field = '$value'";
}

And so on. And before some smart-ass says it: I know that query's unsafe. It was just an example of using isset().

Martin Bean
+2  A: 

During development stages use

error_reporting(E_ALL);

witch will show every error that has caused, all NOTICE errors and etc.

Kepp an eye on your error_log aswell, that will show you errors

use an error reporting system, example:

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

class ErrorReporter
{
    public function catch($errno, $errstr, $errfile, $errline)
    {
        if($errno == E_USER_NOTICE && !defined('DEBUG'))
        {
            //Catch all output buffer and clear states, redirect or include error page.
        }
    }
}

set_error_handler(array(new ErrorReporter,'catch'));

few other tips is always use isset for variables that you may / may not have set because of a if statement lets say.

always use if(isset($_POST['key'])) or even better just use if(!empty($_POST['key'])) as this checks if the key exists and if the value is NOT empty.

make sure you know your comparison operators aswell, languages like C# use == to check bool state where as in php to check data-types you have to use === and use == to check value states, and single = to assign a value!

RobertPitt
+1  A: 

if you want to hide error of undefined variable, then use @ . example: @$var

kasp3r
+2  A: 

Unless I'm missing something, then why is no one suggesting to structure your page properly? I've never really had an ongoing problem with undefined variable errors.

An idea on structuring your page

Define all your variables at the top, assign default values if neccessary and then use those variables from there. That's how I write web pages and I never run into undefined variable problems.

Don't get in the habbit of defining variables only when you need them, this quickly creates spaghetti code and can be very difficult to manage.

alt text

No one likes spagehtti code

If you show us some of your code we might be able to offer suggestions on how you can better structure it to resolve this sorts of errors. You might be getting confused coming from a C background, the flow may work differently to web pages.

Tom Gullen
A: 

Thank you all for your replies. I was rather concerned on locating typos but as I've digged into PHP and your answers I see that the language is quite different from C++ in it's essence. The ability to create dynamically variables is inherent to the language. Maybe on a future version some option to force variable declarations per module at the expense of that dynamism would be useful on certain projects.

Juan