Best way to test for a variable's existence in PHP; isset() is clearly broken

148,736

Solution 1

If the variable you are checking would be in the global scope you could do:

array_key_exists('v', $GLOBALS) 

Solution 2

Attempting to give an overview of the various discussions and answers:

There is no single answer to the question which can replace all the ways isset can be used. Some use cases are addressed by other functions, while others do not stand up to scrutiny, or have dubious value beyond code golf. Far from being "broken" or "inconsistent", other use cases demonstrate why isset's reaction to null is the logical behaviour.

Real use cases (with solutions)

1. Array keys

Arrays can be treated like collections of variables, with unset and isset treating them as though they were. However, since they can be iterated, counted, etc, a missing value is not the same as one whose value is null.

The answer in this case, is to use array_key_exists() instead of isset().

Since this is takes the array to check as a function argument, PHP will still raise "notices" if the array itself doesn't exist. In some cases, it can validly be argued that each dimension should have been initialised first, so the notice is doing its job. For other cases, a "recursive" array_key_exists function, which checked each dimension of the array in turn, would avoid this, but would basically be the same as @array_key_exists. It is also somewhat tangential to the handling of null values.

2. Object properties

In the traditional theory of "Object-Oriented Programming", encapsulation and polymorphism are key properties of objects; in a class-based OOP implementation like PHP's, the encapsulated properties are declared as part of the class definition, and given access levels (public, protected, or private).

However, PHP also allows you to dynamically add properties to an object, like you would keys to an array, and some people use class-less objects (technically, instances of the built in stdClass, which has no methods or private functionality) in a similar way to associative arrays. This leads to situations where a function may want to know if a particular property has been added to the object given to it.

As with array keys, a solution for checking object properties is included in the language, called, reasonably enough, property_exists.

Non-justifiable use cases, with discussion

3. register_globals, and other pollution of the global namespace

The register_globals feature added variables to the global scope whose names were determined by aspects of the HTTP request (GET and POST parameters, and cookies). This can lead to buggy and insecure code, which is why it has been disabled by default since PHP 4.2, released Aug 2000 and removed completely in PHP 5.4, released Mar 2012. However, it's possible that some systems are still running with this feature enabled or emulated. It's also possible to "pollute" the global namespace in other ways, using the global keyword, or $GLOBALS array.

Firstly, register_globals itself is unlikely to unexpectedly produce a null variable, since the GET, POST, and cookie values will always be strings (with '' still returning true from isset), and variables in the session should be entirely under the programmer's control.

Secondly, pollution of a variable with the value null is only an issue if this over-writes some previous initialization. "Over-writing" an uninitialized variable with null would only be problematic if code somewhere else was distinguishing between the two states, so on its own this possibility is an argument against making such a distinction.

4. get_defined_vars and compact

A few rarely-used functions in PHP, such as get_defined_vars and compact, allow you to treat variable names as though they were keys in an array. For global variables, the super-global array $GLOBALS allows similar access, and is more common. These methods of access will behave differently if a variable is not defined in the relevant scope.

Once you've decided to treat a set of variables as an array using one of these mechanisms, you can do all the same operations on it as on any normal array. Consequently, see 1.

Functionality that existed only to predict how these functions are about to behave (e.g. "will there be a key 'foo' in the array returned by get_defined_vars?") is superfluous, since you can simply run the function and find out with no ill effects.

4a. Variable variables ($$foo)

While not quite the same as functions which turn a set of variables into an associative array, most cases using "variable variables" ("assign to a variable named based on this other variable") can and should be changed to use an associative array instead.

A variable name, fundamentally, is the label given to a value by the programmer; if you're determining it at run-time, it's not really a label but a key in some key-value store. More practically, by not using an array, you are losing the ability to count, iterate, etc; it can also become impossible to have a variable "outside" the key-value store, since it might be over-written by $$foo.

Once changed to use an associative array, the code will be amenable to solution 1. Indirect object property access (e.g. $foo->$property_name) can be addressed with solution 2.

5. isset is so much easier to type than array_key_exists

I'm not sure this is really relevant, but yes, PHP's function names can be pretty long-winded and inconsistent sometimes. Apparently, pre-historic versions of PHP used a function name's length as a hash key, so Rasmus deliberately made up function names like htmlspecialchars so they would have an unusual number of characters...

Still, at least we're not writing Java, eh? ;)

6. Uninitialized variables have a type

The manual page on variable basics includes this statement:

Uninitialized variables have a default value of their type depending on the context in which they are used

I'm not sure whether there is some notion in the Zend Engine of "uninitialized but known type" or whether this is reading too much into the statement.

What is clear is that it makes no practical difference to their behaviour, since the behaviours described on that page for uninitialized variables are identical to the behaviour of a variable whose value is null. To pick one example, both $a and $b in this code will end up as the integer 42:

unset($a);
$a += 42;

$b = null;
$b += 42;

(The first will raise a notice about an undeclared variable, in an attempt to make you write better code, but it won't make any difference to how the code actually runs.)

99. Detecting if a function has run

(Keeping this one last, as it's much longer than the others. Maybe I'll edit it down later...)

Consider the following code:

$test_value = 'hello';
foreach ( $list_of_things as $thing ) {
    if ( some_test($thing, $test_value) ) {
        $result = some_function($thing);
    }
}
if ( isset($result) ) {
    echo 'The test passed at least once!';
}

If some_function can return null, there's a possibility that the echo won't be reached even though some_test returned true. The programmer's intention was to detect when $result had never been set, but PHP does not allow them to do so.

However, there are other problems with this approach, which become clear if you add an outer loop:

foreach ( $list_of_tests as $test_value ) {
    // something's missing here...
    foreach ( $list_of_things as $thing ) {
        if ( some_test($thing, $test_value) ) {
            $result = some_function($thing);
        }
    }
    if ( isset($result) ) {
        echo 'The test passed at least once!';
    }
}

Because $result is never initialized explicitly, it will take on a value when the very first test passes, making it impossible to tell whether subsequent tests passed or not. This is actually an extremely common bug when variables aren't initialised properly.

To fix this, we need to do something on the line where I've commented that something's missing. The most obvious solution is to set $result to a "terminal value" that some_function can never return; if this is null, then the rest of the code will work fine. If there is no natural candidate for a terminal value because some_function has an extremely unpredictable return type (which is probably a bad sign in itself), then an additional boolean value, e.g. $found, could be used instead.

Thought experiment one: the very_null constant

PHP could theoretically provide a special constant - as well as null - for use as a terminal value here; presumably, it would be illegal to return this from a function, or it would be coerced to null, and the same would probably apply to passing it in as a function argument. That would make this very specific case slightly simpler, but as soon as you decided to re-factor the code - for instance, to put the inner loop into a separate function - it would become useless. If the constant could be passed between functions, you could not guarantee that some_function would not return it, so it would no longer be useful as a universal terminal value.

The argument for detecting uninitialised variables in this case boils down to the argument for that special constant: if you replace the comment with unset($result), and treat that differently from $result = null, you are introducing a "value" for $result that cannot be passed around, and can only be detected by specific built-in functions.

Thought experiment two: assignment counter

Another way of thinking about what the last if is asking is "has anything made an assignment to $result?" Rather than considering it to be a special value of $result, you could maybe think of this as "metadata" about the variable, a bit like Perl's "variable tainting". So rather than isset you might call it has_been_assigned_to, and rather than unset, reset_assignment_state.

But if so, why stop at a boolean? What if you want to know how many times the test passed; you could simply extend your metadata to an integer and have get_assignment_count and reset_assignment_count...

Obviously, adding such a feature would have a trade-off in complexity and performance of the language, so it would need to be carefully weighed against its expected usefulness. As with a very_null constant, it would be useful only in very narrow circumstances, and would be similarly resistant to re-factoring.

The hopefully-obvious question is why the PHP runtime engine should assume in advance that you want to keep track of such things, rather than leaving you to do it explicitly, using normal code.

Solution 3

Sometimes I get a little lost trying to figure out which comparison operation to use in a given situation. isset() only applies to uninitialized or explicitly null values. Passing/assigning null is a great way to ensure a logical comparison works as expected.

Still, it's a little difficult to think about so here's a simple matrix comparing how different values will be evaluated by different operations:

|           | ===null | is_null | isset | empty | if/else | ternary | count>0 |
| -----     | -----   | -----   | ----- | ----- | -----   | -----   | -----   |
| $a;       | true    | true    |       | true  |         |         |         |
| null      | true    | true    |       | true  |         |         |         |
| []        |         |         | true  | true  |         |         |         |
| 0         |         |         | true  | true  |         |         | true    |
| ""        |         |         | true  | true  |         |         | true    |
| 1         |         |         | true  |       | true    | true    | true    |
| -1        |         |         | true  |       | true    | true    | true    |
| " "       |         |         | true  |       | true    | true    | true    |
| "str"     |         |         | true  |       | true    | true    | true    |
| [0,1]     |         |         | true  |       | true    | true    | true    |
| new Class |         |         | true  |       | true    | true    | true    |

To fit the table I compressed the labels a bit:

  • $a; refers to a declared but unassigned variable
  • everything else in the first column refers to an assigned value, like:
    • $a = null;
    • $a = [];
    • $a = 0;
  • the columns refer to comparison operations, like:
    • $a === null
    • isset($a)
    • empty($a)
    • $a ? true : false

All results are boolean, true is printed and false is omitted.

You can run the tests yourself, check this gist:
https://gist.github.com/mfdj/8165967

Solution 4

Explaining NULL, logically thinking

I guess the obvious answer to all of this is... Don't initialise your variables as NULL, initalise them as something relevant to what they are intended to become.

Treat NULL properly

NULL should be treated as "non-existant value", which is the meaning of NULL. The variable can't be classed as existing to PHP because it hasn't been told what type of entity it is trying to be. It may aswell not exist, so PHP just says "Fine, it doesn't because there's no point to it anyway and NULL is my way of saying this".

An argument

Let's argue now. "But NULL is like saying 0 or FALSE or ''.

Wrong, 0-FALSE-'' are all still classed as empty values, but they ARE specified as some type of value or pre-determined answer to a question. FALSE is the answer to yes or no,'' is the answer to the title someone submitted, and 0 is the answer to quantity or time etc. They ARE set as some type of answer/result which makes them valid as being set.

NULL is just no answer what so ever, it doesn't tell us yes or no and it doesn't tell us the time and it doesn't tell us a blank string got submitted. That's the basic logic in understanding NULL.

Summary

It's not about creating wacky functions to get around the problem, it's just changing the way your brain looks at NULL. If it's NULL, assume it's not set as anything. If you are pre-defining variables then pre-define them as 0, FALSE or "" depending on the type of use you intend for them.

Feel free to quote this. It's off the top of my logical head :)

Solution 5

You can use the compact language construct to test for the existence of a null variable. Variables that do not exist will not turn up in the result, while null values will show.

$x = null;
$y = 'y';

$r = compact('x', 'y', 'z');
print_r($r);

// Output:
// Array ( 
//  [x] => 
//  [y] => y 
// ) 

In the case of your example:

if (compact('v')) {
   // True if $v exists, even when null. 
   // False on var $v; without assignment and when $v does not exist.
}

Of course for variables in global scope you can also use array_key_exists().

B.t.w. personally I would avoid situations like the plague where there is a semantic difference between a variable not existing and the variable having a null value. PHP and most other languages just does not think there is.

Share:
148,736

Related videos on Youtube

chazomaticus
Author by

chazomaticus

Wooooooooooooo!

Updated on October 10, 2020

Comments

  • chazomaticus
    chazomaticus over 3 years

    From the isset() docs:

    isset() will return FALSE if testing a variable that has been set to NULL.
    

    Basically, isset() doesn't check for whether the variable is set at all, but whether it's set to anything but NULL.

    Given that, what's the best way to actually check for the existence of a variable? I tried something like:

    if(isset($v) || @is_null($v))
    

    (the @ is necessary to avoid the warning when $v is not set) but is_null() has a similar problem to isset(): it returns TRUE on unset variables! It also appears that:

    @($v === NULL)
    

    works exactly like @is_null($v), so that's out, too.

    How are we supposed to reliably check for the existence of a variable in PHP?


    Edit: there is clearly a difference in PHP between variables that are not set, and variables that are set to NULL:

    <?php
    $a = array('b' => NULL);
    var_dump($a);
    

    PHP shows that $a['b'] exists, and has a NULL value. If you add:

    var_dump(isset($a['b']));
    var_dump(isset($a['c']));
    

    you can see the ambiguity I'm talking about with the isset() function. Here's the output of all three of these var_dump()s:

    array(1) {
      ["b"]=>
      NULL
    }
    bool(false)
    bool(false)
    

    Further edit: two things.

    One, a use case. An array being turned into the data of an SQL UPDATE statement, where the array's keys are the table's columns, and the array's values are the values to be applied to each column. Any of the table's columns can hold a NULL value, signified by passing a NULL value in the array. You need a way to differentiate between an array key not existing, and an array's value being set to NULL; that's the difference between not updating the column's value and updating the column's value to NULL.

    Second, Zoredache's answer, array_key_exists() works correctly, for my above use case and for any global variables:

    <?php
    $a = NULL;
    var_dump(array_key_exists('a', $GLOBALS));
    var_dump(array_key_exists('b', $GLOBALS));
    

    outputs:

    bool(true)
    bool(false)
    

    Since that properly handles just about everywhere I can see there being any ambiguity between variables that don't exist and variables that are set to NULL, I'm calling array_key_exists() the official easiest way in PHP to truly check for the existence of a variable.

    (Only other case I can think of is for class properties, for which there's property_exists(), which, according to its docs, works similarly to array_key_exists() in that it properly distinguishes between not being set and being set to NULL.)

    • Theo Orphanos
      Theo Orphanos over 15 years
      You can't check - but why do you need to?
    • Zoredache
      Zoredache over 15 years
      Yes, a good example for why you really need this would be helpful.
    • Ed S.
      Ed S. over 15 years
      I agree. As far as you need to know, neither are set because they are both null. You can't use them for anything.
    • Josh Smeaton
      Josh Smeaton over 15 years
      Interesting observation, I wasn't aware of this. An answer has been found so this is redundant and incorrect - but I'd of probably used a placeholder value or an object for each row to make finding an unset property clearer.
    • Nathan Long
      Nathan Long about 14 years
      There ARE reasons to differentiate between null and nonexistent. For example, you're building up an object to represent a row in a database table. For every column in the row, you create a private variable, accessible only via the object's getter method. Suppose a column value is null. Now how does that getter method know whether there's no such column in the table, or whether this object just has a null value there? Fortunately, in my case, the private variable is actually an entry in a private array, so I can use array_key_exists, but this is a real problem.
    • Halil Özgür
      Halil Özgür over 13 years
      The very same issue here for the same usage (db nulls). Having to use array_key_exists() is unintuitive. One more drop to my PHP cup...
    • IMSoP
      IMSoP almost 11 years
      I see no use cases in the edited question, or any of the answers and their comments, which actually justify having a variable which might or might not exist at runtime. Array keys and object properties can be treated as variables, and tested with isset, but their existence can also be verified with array_key_exists() and property_exists(), so use cases involving them should be considered separately.
    • chazomaticus
      chazomaticus almost 11 years
      Especially in legacy setups, register_globals leads to global variables that might or might not exist at runtime.
    • IMSoP
      IMSoP almost 11 years
      @chazomaticus Firstly, if they're NULL, it still doesn't matter. Secondly, if it does, we can chalk that up to one of the many things which were wrong with that feature, which has been removed from PHP completely. Justifying one bad idea with another doesn't convince me of a sensible use for non-existent variables.
    • chazomaticus
      chazomaticus almost 11 years
      It's been removed from new versions of PHP, yes. Unfortunately, it's not gone from every deployment of PHP. Also, it seems like a pointless semantic detail to quibble about whether we're talking about array elements or variables. Regardless of what standards you think code should adhere to, it's useful to know how to work around an inconsistency in the PHP language.
    • IMSoP
      IMSoP almost 11 years
      @chazomaticus But variables and array elements are fundamentally different things; just because you can do some of the same things with them doesn't mean they are or should be 100% interchangeable. There is no "inconsistency in the PHP language" here, just something you don't happen to like/understand. As for register_globals, I'm still struggling to think of a situation where even that would require such a distinction, since anything registered from the HTTP request would always be a string, not null.
    • DimeCadmium
      DimeCadmium almost 6 years
      The real problem here is that PHP's database APIs treat database-NULL equivalently to PHP-NULL despite their being different things. (PHP-NULL is "uninitialized"; database-NULL is "initialized to empty")
  • chazomaticus
    chazomaticus over 15 years
    This isn't a bad practice for simple scripts, but in complex (e.g. large OO) projects, it becomes infeasible. Also, as I said above, is_null() returns TRUE for variables that are not set, so there's really no reason to do what you're saying except to avoid a PHP warning.
  • Beau Simensen
    Beau Simensen over 15 years
    For well designed "large OO" projects, why would this be a problem at all? Why would you ever have $foo in a method body that may have not been set before its first usage?
  • chazomaticus
    chazomaticus over 15 years
    Ah ha! NOW you're talkin'! How would you do that for, say, class properties?
  • chazomaticus
    chazomaticus over 15 years
    I'm not sure what you mean. If you have an array, with one element 'a', you don't have to unset() element 'b' for element 'b' not to exist in PHP, it just doesn't exist. Same thing with e.g. global variables, which you can think of as elements of the $GLOBALS array.
  • chazomaticus
    chazomaticus over 15 years
    But I agree that a variable with a NULL value does in fact exist.
  • Henrik Opel
    Henrik Opel over 13 years
    As a variation, if the check needs to work for local scope variables as well, on can do a $defined_vars = get_defined_vars(); and then test via array_key_exists('v', $defined_vars);.
  • curtisdf
    curtisdf about 12 years
    Great answer. So many times I see people ranting about how they hate this or that feature of a language. But they seem to be assuming that "if it doesn't do it MY way, then it's broken." Yes, there are bad design decisions. But there are also very close-minded developers!
  • Halil Özgür
    Halil Özgür almost 12 years
    empty() does not check if the variable is null, it checks if it's false-y, e.g. not one of the "" (an empty string), 0 (0 as an integer), 0.0 (0 as a float), "0" (0 as a string), NULL, FALSE, array() (an empty array) and $var; (a variable declared, but without a value). Say you have a required radio field in a form with two inputs with the values 0 and 1. If you use empty() for validation and the user selects the 0 one, you'd inadvertently error out "required field cannot be empty". See the manual php.net/manual/en/function.empty.php
  • enrey
    enrey almost 11 years
    There IS a HUGE difference between unset variable and variable===null. One doesn't exist, the other has value null. Arguments that null means no value simply aren't true. Null IS A VALUE of type null. It's perfectly valid value and there's no reason for php to treat it as non-existing value, which it sadly does. It would be OK, if non-existing variables were null and every existing variable was not-null and assigning null into variable would unset it. But there are MANY situations, where functions return null as actual value. Then we're screwed up, because there's no bloody way to test for it.
  • IMSoP
    IMSoP almost 11 years
    @enrey If a function returns NULL, and you're testing that return value, then you've put it somewhere, so you know that the container exists. Other than debugging, there should never be a case where a variable - as opposed to an array key or object property - "might or might not have been set yet".
  • IMSoP
    IMSoP almost 11 years
    Your example is not checking the existence of a variable, but of an array key. A solution to that exists, in the form of array_key_exists. You should never be in a situation where you don't know at runtime if an actual variable exists.
  • enrey
    enrey almost 11 years
    @IMSoP what brings you to that idea? Didn't you ever run into a situation, where a function is in the middle of complex loops and conditions and it might or might not have been run, and it's return value was/wasn't assigned to a variable, and testing on variable existence is the only thing to help you... Well, not really, since you can't test for set/not-set variable in php, you had to add that $my_function_has_run = true; right next to your function call. Well, that doesn't make the language less turing-complete. But it surely does make it more anoying.
  • IMSoP
    IMSoP almost 11 years
    @enrey That sounds a lot like debugging to me. If that check is necessary in the final code, you've got some pretty wacky logic. Remember that in many languages, a variable either exists or not based on declaration in a particular scope, so such a test would be meaningless.
  • IMSoP
    IMSoP almost 11 years
    @chazomaticus Well, you should never be in a situation where register_globals is turned on, so I stand by that statement.
  • chazomaticus
    chazomaticus almost 11 years
    Oh, I agree. Still, not everyone can control where their code is deployed. It's useful to have information for every situation, whether it's how things "should" be or not.
  • chazomaticus
    chazomaticus almost 11 years
    Very good points, though not exactly an answer to the question.
  • IMSoP
    IMSoP almost 11 years
    To the explicit question "How are we supposed to reliably check for the existence of a variable in PHP?" my answer is "you're not, and here's why". Both this answer and greatbigmassive's also answer the implicit question "why does isset() behave that way?".
  • IMSoP
    IMSoP almost 11 years
    @chazomaticus If your problem is register_globals, then your answer is not a change to isset(). The PHP manual mentions "it really is generally a good programming practice to initialize variables first", which solves register_globals at design-time rather than run-time. There is also an FAQ entry giving an unregister_globals() function to deal with it at run-time.
  • enrey
    enrey almost 11 years
    @IMSoP true, but php doesn't follow logic of other languages. It has its own world, and in it's own world, variable is created when it's assigned to, there's no declaration for it. Besides, even in other languages, you can test variable existence. I'm not saying null being equivalent to unset variable is somehow big problem, it's just a small anoyance in a language, that has been built purely from small anoyances. So, whatever. Let's get back to business.
  • IMSoP
    IMSoP almost 11 years
    @enrey See my answer for the reason an uninitialized variable could still be considered to "exist". You can even assign to another variable from it, and you get null. It would be far more annoying to have a second constant representing not-null-but-read-from-an-unitialized-variable, which would be the consistent result of isset() making such a distinction.
  • enrey
    enrey almost 11 years
    I know we're "not supposed" to check variable existence in php, hell, there's not even any real way to check it. I'm not going to write code which deppends on it, because it's not possible in php. That's a limitation of php. There clearly is a difference between non-set and null variable, yet php provides no ways to distinguish those. Yet lot of meta functionality depends on it internaly: reading non-existing var produces notice, isset($a['x']) will tell you false if x is null, yet it will show up in count($a).. compact will work on all set variables, including nulls, and so on.
  • IMSoP
    IMSoP almost 11 years
    @enrey Careful, once again some of your examples are using arrays, for which we have array_key_exists. The vast majority of functionality treats an uninitialized variable as equal to null, so it's perfectly reasonable not to distinguish between the two states. The notice is a hint that you should change your code to initialize it explicitly for better code, not because it makes any difference in itself. As for compact, that's more like code reflection than anything you'd see in every day code.
  • chazomaticus
    chazomaticus almost 11 years
    This answer is flawed in one major way: in OO programming, null is the logical choice to mean "no object". For example, in unexceptional circumstances when a function can return an object or no object, null is the obvious choice. Technically in PHP, false or any other value considered false in boolean context could be used, but then you're losing some semantic purity. Thus, null is a perfectly reasonable value to initialize a variable to that should hold an object eventually, because it is relevant to what it's intended to become.
  • chazomaticus
    chazomaticus almost 11 years
    IMSoP, the PHP manual actually makes it clear that uninitialized variables can have any number of types, depending on the context, so it's not correct to say "the vast majority of functionality..." without knowing what the code is doing. Also, nobody's arguing that you shouldn't initialize variables before using them. The question is simply wondering, given a situation where there's some ambiguity about whether a variable was declared, how can that be resolved.
  • enrey
    enrey almost 11 years
    @IMSoP key is 3 characters long, isset has 5 characters, array_key_exists has 16. Think about it. Plus, array_key_exists complains if the array itself does not exist. Therefore checking index that's at the bottom of huge tree structure the proper way is going to introduce massive amount of boilerplate, all just for checking one key existence. Again, i'm not saying it's unusable, it's just introducing unnecessary pain. But I don't care, I'm running away from php anyway.
  • IMSoP
    IMSoP almost 11 years
    @chazomaticus 1) I have never said that there aren't valid uses for null; I have said that there aren't valid uses for "uninitialized" as a value/state distinct from null. 2) I hadn't seen that description of uninitialized variables before, but note that in practice they consistently behave the same as a null value being coerced in the same situation. I stand by the claim that except in very esoteric situations, there is not and should not be a meaningful distinction between the two states.
  • IMSoP
    IMSoP almost 11 years
    @enrey I'm not really sure what the length of function names has to do with anything being discussed here. The argument for a recursive array_key_exists (i.e. one that performs multiple existence checks rather than checking a normal variable against a particular test) is an interesting one; I guess you can use the @ "shut up operator", but that does always seem like a hack. It's still not a use case for a function that detects uninitialized bare variables, rather than array keys, though.
  • chazomaticus
    chazomaticus almost 11 years
    IMSoP, I think you're confused. My comment about null was in response to this answer, not you. And I'll repeat myself in regard to your claim: nobody's denying that! This conversation has gone far beyond being useful.
  • IMSoP
    IMSoP almost 11 years
    @chazomaticus Apologies, I did misunderstand that point. It is a valid clarification of the answer that null might be a valid initialization state. Of course, that would then be a constant in the code, so there would be no need to check if it had happened or not at runtime.
  • Piet Bijl
    Piet Bijl over 10 years
    NULL is perfectly valid for setting artibrary variables, if the value has yet to be determined that is. Argument for not setting a $var as NULL is thus broken. So is ISSET()
  • Denis Pshenov
    Denis Pshenov over 10 years
    This answer is fundamentally flawed as demonstrated by enrey and others.
  • M Miller
    M Miller over 10 years
    PHP doesn't, but I wouldn't say most other languages don't. Most any language that declares variables will throw an error if a variable hasn't been declared, but you can set them to NULL. Semantically, NULL should mean "no resource," but not defining a variable is a programmer error.
  • IMSoP
    IMSoP about 10 years
    @MMiller Sure, but writing code that follows one path in the case of "no resource" and a different path in the case of "programmer error" is fairly nonsensical. If you want to detect undeclared variables during debugging, use a static analysis tool, like you would to find potential errors in any language.
  • IMSoP
    IMSoP about 10 years
    If the intention is to avoid a warning that your variables haven't been declared, then the solution is to fix your code to declare them properly. Your inst function is basically like the @ error-suppression operator: "I know I'm doing something wrong here, but I just want that message to go away, without actually changing the operation of my code in any way".
  • IMSoP
    IMSoP about 10 years
    The detection methods, on the other hand, are ingenious, but I am still of the firm belief that you should never have any use for them other than to echo the very warning messages that they're catching. (You should probably clarify that your output buffering version requires error_reporting set high and display_errors to be turned on.)
  • Arild
    Arild almost 10 years
    This looks a bit ugly to me, but in the case where you're actually checking an array element, it makes much more sense: isset($foo[$bar]) becomes array_key_exists($bar, $foo)
  • Chris Middleton
    Chris Middleton over 9 years
    As long as PHP throws errors for undefined variables, but not for null, then there is a difference. If null and undefined were really the same concept, then PHP should assume default undefined/undeclared variables to null and never throw an error, but no one wants that because it's a development nightmare. Null and undefined may not really be different in the context of value semantics, but they are very different when it comes to writing clear and debuggable code.
  • Pacerier
    Pacerier over 9 years
    @MMiller, Cool, how did you even thought of this.
  • M Miller
    M Miller over 9 years
    I end up looking at it a little differently now that I use JavaScript because there are more states. If you say alert(x); when x was never declared, it throws a reference error. If you do var x; alert(x); you get undefined. undefined is a different state from null. For example, many programs use an options hash, e.g. the options for a lightbox script might be: var options = { onClose: function () { ... }, centerHorizontally: true }. If onClose were set to null, that might mean do nothing, whereas undefined (or absent) implies use the default setting.
  • Beejor
    Beejor about 9 years
    @ChrisMiddleton Exactly! In most languages, there's a big difference between null/nil and undefined. The former is a data type, meaning you can assign it to variables (it basically means "empty, but not an empty string or zero or false"). The latter is a read-only keyword that evaluates to true in the case of an identifier that isn't found. The difference is why JavaScript has two different keywords, and why stricter languages like Basic don't let you set Integers/Booleans/Strings to Null (only Variants or Objects).
  • IMSoP
    IMSoP about 9 years
    @MMiller As with so many examples here, your JS options example is of a key in some particular object/hash, not a bare variable. Thus the equivalent code in PHP would use array_key_exists or property_exists, not isset.
  • IMSoP
    IMSoP about 9 years
    The warning on undefined variables is a hint to the programmer that they have done something wrong in the code. Outside debugging (for which there are tools outside the language), there should never be a need for a program to detect such a state, because the programmer should always know what variables they are declaring.
  • M Miller
    M Miller about 9 years
    @IMSoP Yes, I know. This comment was simply in response to the answer to point out that there is a purpose to having a case when something doesn't exist versus when it is null. I didn't intend my comment to be a response to the OP's question, but rather a rebuttal against this particular answer.
  • IMSoP
    IMSoP about 9 years
    @MMiller But it doesn't work as a rebuttal, because the statement in the answer is explicitly about "a variable not existing", and your counter-example is about an object property / hash key not existing. The distinction between these cases is not just incidental.
  • M Miller
    M Miller about 9 years
    Yup, you're right, my mistake. Although the same would still apply if I did var onClose = options.onClose; if (onClose === undefined) { ... } else if (onClose === null) { ... }
  • Andrew
    Andrew about 9 years
    Regarding classes and properties, sadly property_exists() doesn't work when the property is array, for example: Class{ public $property = array() }. Throws an error.
  • IMSoP
    IMSoP about 9 years
    @Andrew Seems to work fine for me: 3v4l.org/TnAY5 Care to provide a complete example?
  • Andrew
    Andrew about 9 years
    yeah it seems to work fine, something wrong was with my setup. Sorry for the false alarm :)
  • Hugo Zink
    Hugo Zink over 8 years
    There are plenty of reasons to have variables set to null. What if I'm getting data from a database and I want to see if the column exists but is simply null (which happens all the time)?
  • Hugo Zink
    Hugo Zink over 8 years
    "If you read from a variable that doesn't currently have an entry, PHP considers that variable to have the value NULL." This is false. An undefined variable is simply undefined. It may return null when you try to access it, but that's irrelevant.
  • IMSoP
    IMSoP over 8 years
    @HugoZink Irrelevant to what? Any test you do of the value of an undefined variable will tell you that the value is null. Whether that value exists before you look at it is a question for philosophers, but as far as any observable behaviour is concerned the value is consistently null.
  • alexw
    alexw over 8 years
    property_exists seems promising, except for this: > The property_exists() function cannot detect properties that are magically accessible using the __get magic method.
  • ToolmakerSteve
    ToolmakerSteve over 7 years
    @HugoZink - IMHO you've misunderstood the discussion. No one suggested that variables should not be set to null. The only question is whether/when there need to be TWO different concepts: null vs. has never been set to anything, not even to null. This lengthy answer intends to demonstrate that when you need to make that distinction, there is some way to do so in php [except in limited cases, that there is disagreement about how important those are]. Though the code needed is different, depending on what entity you are examining.
  • ToolmakerSteve
    ToolmakerSteve over 7 years
    @MMiller - indeed that is a better example. Still, after 20+ years programming in strict languages, the situations where I needed a distinctions between undefined and null are so rare that I don't miss it. IMHO, the main use for undefined is "programmer error in a non-strict language". In a strict language, if I need a distinct state for client did not state a value, then I declare a value appropriate to the situation, and test for it. Worst case, have to add a separate flag variable. But doing that rarely is better than having to ALWAYS cope with TWO different non-value states!!
  • Stephan Vierkant
    Stephan Vierkant over 7 years
    I think that's what many other people said before, or am I wrong?
  • Brilliand
    Brilliand about 7 years
    @alexw Variables "created" via __get indeed don't exist. __get is arbitrary code used as a fallback for nonexistent variables, that can return whatever it wants regardless of whether any relevant data was ever stored.
  • Hugo Zink
    Hugo Zink almost 7 years
    If you have a magic __get method, you will probably want to have a magic __isset method as well, which you could use.
  • jave.web
    jave.web almost 7 years
    This is not probably relevant in reality, but ... get_defined_vars() + array_key_exists WILL NOT test if variable was really declared or not, it's just a way how to detect that variable was "initialized" with null... Just a simple declaration - $a; is a valid code, though this method would result in false :) But as I said, this is more of a "fun fact", that may concern a few random C people :-)
  • Rik Schaaf
    Rik Schaaf over 6 years
    Maybe out of the scope of this question, but you might want to add "0" to the table, for completeness and clarity of the empty operation