Overloading arrays in PHP 5.3

Sep 18, 2010   #php 

I recently built a simple PHP 5.3 framework to use on a new project. I know, I know, I built a framework again, even though I wrote off building frameworks earlier this year. Well, now that I am able to solely focus my efforts on PHP 5.3 development (all environments at my new job are in PHP 5.3), I decided that it would be fun to build a simple MVC framework using namespaces.

Needless to say, I came across an issue I didn’t have in the Thoom framework because of how I had handled passing arrays to the view. I usually split up my view object into two pieces: a view manager and view registry. The manager is how you interact with the view in the controller and the registry is where you store all of the variables that will be accessible to the view templates. To make it easier to access the registry, I use magic methods in the manager to store and retrieve the variables from the registry.

The relevant code looks something like this:

<?php
namespace My\Framework;
class View {
	/**
	 * Instance of the registry used by the view templates
	 * @var View\Registry
	 **/
	protected $registry;

	public function __construct(View\Registry $registry) {
		$this->registry = $registry;
	}

	public function __get($var) {
		if (isset($this->registry->$var)) {
			return $this->registry->$var;
		}
	}

	public function __isset($var) {
		return isset($this->registry->$var);
	}

	public function __set($var, $val) {
		$this->registry->$var = $val;
	}
}

<?php
namespace My\Framework\View;

class Registry {
	public function __get($var) {
		if (isset($this->$var)) {
			return $this->$var;
		}
	}

	/**
	 * Enables isset() for variables accessed through the Registry object
	 * @param string $var
	 * @return bool
	 */
	public function __isset($var) {
		return isset($this->$var);
	}

	/**
	 * Sets the variable
	 * @param string $var
	 * @param mixed $value
	 */
	public function __set($var, $value) {
		$this->$var = $value;
	}
}

Unfortunately, the __get() magic method does not pass variables by reference and only pass them as read only. So when I try to write code such as:

<?php
$this->view->myarr = array();
$this->view->myarr[] = ''content'';

I get the following warning: PHP Notice: Indirect modification of overloaded property.

Matthew Weier O”Phinney, one of the developers on Zend Framework, blogged about how they solved the problem with that framework. You’ll notice that I have already utilized the functionality that he mentions in the registry. The problem with this solution is that I don’t want to pollute the manager with the variables. Because the variables are not passed by reference, an easy solution is to check for arrays and cast them as ArrayObjects instead.

So now my Registry class looks like this:

<?php
class Registry {
	/**
	 * Sets the variable
	 * @param string $var
	 * @param mixed $value
	 */
	public function __set($var, $value) {
		if (\is_array($value)) {
			$value = new \ArrayObject($value);
		}
		$this->$var = $value;
	}
}

Unfortunately, this isn’t a perfect solution because ArrayObjects are not useable everywhere an array is. However, you can easily cast an ArrayObject as an array where necessary. Rumor has it that in some future PHP revision, we will see ArrayObjects accepted anywhere arrays are. Until then, I’ll have to live with casting to arrays when necessary unless i find some other solution.