Update: I ran into issues with the ArrayObject solution, as there was
a bug in PHP 5.2.0 (now fixed) with its interaction with empty() and isset()
when used with the ARRAY_AS_PROPS flag. I tried a number of fixes, but
eventually my friend Mike pointed
out something I'd missed: as of PHP 5.1, setting undefined public properties
no longer raises an E_STRICT notice. Knowing this, you can now do the
following without raising any errors:
class Foo
{ public
function __set
($key,
$value) { $this->
$key =
$value;
}}$foo =
new Foo
();
$foo->
bar =
array();
$foo->
bar[] =
42;
This is a much simpler solution, performs better, and solves all the issues
I was presented. Thanks, Mike!
Several weeks back, a bug was reported against
Zend_View
that had me initially stumped. Basically, the following was now failing in
PHP 5.2.0:
$view->
foo =
array();
$view->
foo[] =
42;
A notice was raised stating, "Notice: Indirect modification of overloaded
property Zend_View::$foo has no effect."
I'd read about this some months back on the php internals list, but at the
time hadn't understood the consequences. Basically, __get() no longer
returns a reference and returns values in read mode, which makes modifying
arrays using overloading impossible using traditional methods.
Derick Rethans blogged about the issue
in August. His solution was to use a switch() statement in __get() to cast
the returned value explicitly as an array:
public
function __get
($key){ if (is_array($this->_vars
[$key])) { return (array) $this->_vars
[$key];
} return $this->_vars
[$key];
}
The problem with this approach is that you then have issues with other array
functionalities, such as assigning by reference.
After some work, I found the best solution was to have the class extend
ArrayObject, but with a slight twist:
class My_Class extends ArrayObject
{ public
function __construct
($config =
array()) { // ... some setup // Allow accessing properties as either array keys or object properties: parent::__construct
(array(), ArrayObject::
ARRAY_AS_PROPS);
}}
This combination allows some very flexible access to properties in the
object:
// from the original example:$view->
foo =
array();
$view->
foo[] =
42;
echo $view['foo'][0];
// '42'echo $view->
foo[0];
// same
One issue that was always difficult to work with in Zend_View was keeping
'public' properties -- template variables -- separate from private/protected
properties (things like the helper, filter, and script paths). Since those
properties are pre-declared in the class, the
ArrayObject::ARRAY_AS_PROPS setting prevented any such collision
from happening -- and helped simplify the code.
Moral of the story? If you need to be able to modify overloaded arrays in
your class, and support PHP 5.2.0, extend ArrayObject.
Note: other projects have also come across this problem and have fixed and suggested to move up to 5.2.1. 5.2.0 is simply broken when it comes to __get() behavior, and forcing a referenced return does not work for that version.. This should be taken...
Tracked: Aug 02, 17:56