tags:

views:

157

answers:

2

Using PHP 5.3 I'm experiencing weird / non-intuitive behavior when applying empty() to dynamic object properties fetched via the __get() overload function. Consider the following code snippet:

<?php

class Test {

  protected $_data= array(
   'id'=> 23,
   'name'=> 'my string'
  );

  function __get($k) {
    return $this->_data[$k];
  }

}

$test= new Test();
var_dump("Accessing directly:");
var_dump($test->name);
var_dump($test->id);
var_dump(empty($test->name));
var_dump(empty($test->id));

var_dump("Accessing after variable assignment:");
$name= $test->name;
$id= $test->id;
var_dump($name);
var_dump($id);
var_dump(empty($name));
var_dump(empty($id));

?>

The output of this function is as follow. Compare the results of the empty() checks on the first and second result sets:

Set #1, unexpected result:

string(19) "Accessing directly:"
string(9) "my string"
int(23)
bool(true)
bool(true)

Expecting Set #1 to return the same as Set #2:

string(36) "Accessing after variable assignment:"
string(9) "my string"
int(23)
bool(false)
bool(false)

This is really baffling and non-intuitive. The object properties output non-empty strings but empty() considers them to be empty strings. What's going on here?

+5  A: 

Based on a reading of the empty's manual page and comments (Ctrl-F for isset and/or double underscores), it looks like this is known behavior, and if you want your __set and __get methods and empty to play nice together, there's an implicit assumption that you implement a __isset magic method as well.

It is a little bit unintuitive and confusing, but that tends to happen with most meta-programming, particularly in a system like PHP.

Alan Storm
You beat me by sixty seconds :) I think this solves the question, though it is hugely counter-intuitive that the __isset magic method returns false by default.
Pekka
so `__isset` is invoked when you call `empty()` but not when you call `isset()` ??
nickf
Per the manual, <a href="http://us2.php.net/__isset">__isset</a> should be invoked when use either function. THe implication of what I've read is there's no defined behavior for calling isset or empty with magic methods when there's no __isset function.
Alan Storm
A: 

In this example, empty() calls the __isset() overload function, not the __get() overload function. Try this:

class Test {

  protected $_data= array(
   'id'=> 23,
   'name'=> 'my string'
  );

  function __get($k) {
    return $this->_data[$k];
  }

  function __isset($k) {
    return isset($this->_data[$k]);
  }

}
Travis