Using magic __get

I’ve got a class which has a lot of properties, an I wanted to define getters that would selectively access the database (ie if we’ve set the property, return it, otherwise get a bunch of data from the database and store it somewhere for other class getters to access).

I stumbled upon using __get which works pretty well for this, however it only works when the property has not been defined. I like to define my properties. I think it’s neater. Even if I don’t want to write a ream of near-identical getters for them.

Is it possible to use a similar functionality that works when properties have been defined but not set?

eg This will work as desired


class AutoGetter
{
	public function __get($name)
	{
		//do some things that justify the use of magic functions
		return $name;
	}
}

$o = new AutoGetter();
echo $o->nonExistantVariable;

This will echo:
nonExistantVariable

However, if I define the property


class AutoGetter
{
	public $nonExistantVariable;
	
	public function __get($name)
	{
		//do some things that justify the use of magic functions
		return $name;
	}
}

$o = new AutoGetter();
echo $o->nonExistantVariable;

This will output null, because the property $nonExistantVariable has been defined, even if it hasn’t been set.

What is it I’m looking for here? Instead of magic functions would I be better off using my constructor to dynamically create a bunch of getters?

class AutoGetter
{
public $nonExistantVariable;

public function __get($key)
{
             if($this->$key != null)
	      return $this->$key;
}

}

Thanks, but the problem I’m having with this approach is that the magic get would not be called for the property nonExistantVariable. The magic get is only called for non-defined properties, and I was looking for a way around it, or maybe a suggestion of a different approach.

You could work around this by creating protected or private variables in the class, like this:


class AutoGetter
{
  protected $nonExistantVariable;
  
  public function __get($name)
  {
    return $name;
  }
}

$a = new AutoGetter;
var_dump($a->nonExistantVariable); // string 'nonExistantVariable' (length=19)

This returns ‘nonExistantVariable’, because even though it does exist it’s not publicly visible so you can’t access it and __get will fire instead :slight_smile:
At the same time you can call it from the class itself, so you can use it as a magic __get and __set


class AutoGetter
{
  protected $nonExistantVariable;
  
  public function __get($name)
  {
    // this will *not* call __get, because the variable is
    // visible from the scope of this class
    // just not from outside the class
    return isset($this->$name) ? $this->$name : null;
  }

  public function __set($name, $value)
  {
    // this will *not* call __set, because the variable is
    // visible from the scope of this class
    // just not from outside the class
    $this->$name = $value;
  }
}

if you want to make sure only defined variables can be set in __set, you can use [fphp]property_exists[/fphp], i.e.,


public function __set($name, $value)
{
  if (!property_exists($this, $name))
    throw new Exception('Class "'.__CLASS__.'" does not have a property "'.$name.'"');
  $this->$name = $value;
}

and the same goes for __get of course :slight_smile:

Yes, that does seem to solve a lot of the problems. However:

What did you mean by this? From my tests, attempting to call $this->nonExistantVariable from within the class does NOT call the magic function. It also doesn’t call the magic function when called from inheriting classes, eg


class AutoGetter
{
	protected $nonExistantVariable;
  
	public function __get($name)
	{
		echo "<DIV>GETTING $name using magic function</DIV>";
		return isset($this->$name)? $this->$name : null;
	}
}

class ExtendAutoGetter extends AutoGetter
{
	public function getNonExistantVariable()
	{
		return $this->nonExistantVariable;
	}

}
$ag = new ExtendAutoGetter();
var_dump( $ag->getNonExistantVariable());

This does not call the magic get when the property is accessed via a member function. As I’m planning on using this on a much-extended class this would mean I’d have to be very careful with a lot of my code. Am I missing something here?

Hm, indeed. If you want the class itself to call the magic __set and __get from inside the class as well then it doesn’t work.
In that case you could go for underscores in the properties, and call them without the properties. Something like


class AutoGetter
{
  protected $_someVar;

  public function __get($name)
  {
    $var = '_'.$name;
    if (!property_exists($this, $var))
      throw new Exception('Class "'.__CLASS__.'" does not have a property "'.$name.'"');
    return $this->$var;
  }

  public function __set($name, $value)
  {
    $var = '_'.$name;
    if (!property_exists($this, $var))
      throw new Exception('Class "'.__CLASS__.'" does not have a property "'.$name.'"');
    $this->$var = $value;
  }
}

$a = new AutoGetter;
$a->someVar = 1;

this way $a->someVar works from outside the class as well as inside class, but internally uses $a->_someVar

Oi! This sounds like it’s going to get complicated. Which is usually a sign that I shouldn’t be doing it this way!

Perhaps if I explain what I’m trying to achieve someone can point me in the right direction.

I have a base Product class, which will be extended by, for example ShoppingCartProduct, and most likely many more.

The Product class has a lot of properties, a few core ones being name, price, description, delivery_time.

I wanted the application side not to necessarily need to deal with the explicit retrieval of product details from the database, so I wanted to smoothly let the class grab required data from the database as it needs it. What I wanted to do was, when a property is accessed, return the property if it has been set, otherwise, grab a whole bunch of fields from the database, return the one that is being accessed, but store the other fields for use in the future, so we can cut out single field database calls.

eg


class Product
{
	/*define fields that can be populated from the database
	this way a getter can query the database if something has not been set, and store the returned result for other getters
	*/
	private $haveRunQuery=false;
	private  $dbFields = array('id', 'name', 'price', 'description', 'delivery_time', 'stock', 'vat_rate');
	private $dbValues = array();

	public function __get($name)
	{
		//if property has been set in the object, return it
		if(isset($this->$name))
		{
			return $this->$name;
		}
		
		else if(!array_key_exists($name,$this->dbValues))
		{
			$this->getAllFieldsFromDB();
		}
		
		//if, after all that, the value is available, set it in the object
		if(array_key_exists($name,$this->dbValues))
		{
			$this->$name=$this->dbValues[$name];
			return $this->dbValues[$name];
		}
		//if we've got this far something's gone wrong and we've attempted to access a nonexistant property		
		trigger_error("Attempting to access non-existant product property ".$name);
		return false;
	
	}
	
	protected function getAllFieldsFromDB()
	{
		//get all applicable db fields into an array for all the getters to access db values that haven't been set
		$q = query("SELECT 'id', 'name', 'price', 'description', 'delivery_time', 'stock', 'vat_rate' FROM products WHERE id=".$this->id);
		if(!$q or mysql_num_rows($q)<1) return false;
		$row = mysql_fetch_assoc($q);
		//put the
		foreach($row as $var=>$val)
		{
			$this->dbValues[$var]=$val;
		}
		$this->haveRunQuery=true;
		return true;
	}
}


Using magic __get seemed at first like a neat way to make this work. Is it perhaps not the best or right way? Another idea was to use my constructor to dynamically create getter functions (eg getId(), getName()), and in my code just call the function. But is it even possible to dynamically create class functions? I guess I could use magic function _call()? :confused:

It looks like you’re trying to reinvent ActiveRecord. Maybe you can have a look at any of the packages that are mentioned in that article and see if anything suits your needs?
I personally love the AR implementation in Yii, but I don’t think you can easily use that independently from the rest of the framework.

If you go the way of building it yourself, I would not make the fields configurable, but request them from the database using a DESCRIBE or SHOW CREATE query. To speed things up you could cache this in memcache or apc or similar if you like. Configuring manually would be faster but is more error prone (and frankly quite annoying).

And I would not go the way of dynamically adding methods to a class using __call, all this will make the whole class too “magic” IMO. Of course others will disagree on that.

I keep doing that. I keep playing with design patterns that make database access easier, and they keep turning into ActiveRecord!

For good reason I’d say. ActiveRecord is a very sweet pattern :slight_smile:

I much prefer the Data Mapper pattern, I can’t say I’m a fan of Active Record at all.