Problem: I am trying to extend PHP's ArrayObject
as shown below. Unfortunately I can't get it to work properly when setting multi-dimensional objects and instead an error thrown as I have the strict settings enabled in PHP. (Error: Strict standards: Creating default object from empty value
)
Question: How can I modify my class to automatically create non-existing levels for me?
The code:
$config = new Config;
$config->lvl1_0 = true; // Works
$config->lvl1_1->lvl2 = true; // Throws error as "lvl1" isn't set already
class Config extends ArrayObject
{
function __construct() {
parent::__construct(array(), self::ARRAY_AS_PROPS);
}
public function offsetSet($k, $v) {
$v = is_array($v) ? new self($v) : $v;
return parent::offsetSet($k, $v);
}
}
Implement the
offsetGet
method. If you are accessing a non exist property, you can create one as you like.As you are extend ArrayObject, you should use the array way [] to set or get.
Taking a more oop view of your issue, you can create a class that models the concept of an multi-dimensional object.
The solution im posting doesn't extends from
ArrayObject
to archieve the goals you mention. As you tagged your question as oop, i think it´s important to reinforce the separation the way you store an object's state from how do you access it.Hope this will help you archieve what you need!
From what you said, an multi-dimensional object is one that:
$config->database->host = 'localhost'
thedatabase
andhost
levels are initialized automatically, andhost
will return'localhost'
when queried.Proposed Solution
So, how can those features be implemented?
The second one is easy: using PHP's
__get
and__set
methods. Those will get called whenever an read/write is beign done on an inaccesible property (one that's not defined in an object). The trick will be then not to declare any property and handle propertie's operations through those methods and map the property name beign accesed as a key to an assosiative array used as storage. They'll provide basically an interface for accesing information stored internally.For the third one, we need a way to create a new nesting level when a undeclared property is readed. The key point here is realizing that the returned value for the property must be an multi-dimensional object so further levels of nesting can be created from it also: whenever we´re asked for a property whose name is not present in the internal array, we´ll associate that name with a new instance of
MultiDimensionalObject
and return it. The returned object will be able to handle defined or undefined properties too.When an undeclared property is written, all we have to do is assign it's name with the value provided in the internal array.
The fourth one is easy (see it on
__construct
implementation). We just have to make sure that we create anMultiDimensionalObject
when a property's value is an array.Finally, the fist one: the way we handle the second and third features allows us to read and write properties (declared and undeclared) in any level of nesting. You can do things like
$config->foo->bar->baz = 'hello'
on an empty instance and then query for$config->foo->bar->baz
successfully.Important Notice that
MultiDimensionalObject
instead of beign itself an array is it composed with an array, letting you change the way you store the object's state as needed.Implementation
Demo
Code: var_dump(new MultiDimensionalObject());
Result:
Code:
Result:
Code:
Result:
Code:
Result:
Copied pasted your code and it works fine on my PHP test box (running PHP 5.3.6). It does mention the Strict Standards warning, but it still works as expected. Here's the output from print_r:
It is worth noting that on the PHP docs there is a comment with guidance related to what you're trying to do:
Detailed explanation is linked above but, in addition to
offsetSet
which you have andoffsetGet
which xdazz mentions, you also must implementoffsetExists
andoffsetUnset
. This shouldn't have anything to do with your current error but it is something you should be mindful of.Update: xdazz' second-half has the answer to your problem. If you access your Config object as an array, it works without any errors:
Can you do that or are you restricted to the Object syntax for some reason?