In PHP you can use square brackets on an element to access attributes:
$node = /* SimpleXMLElement */
$id = $node['id'];
What's weird is that $id
isn't a string, It's another SimpleXMLElement
. Why isn't it a string? I find myself using strval()
all over the place on this.
How are the square brackets working? Can I do that with my own classes? I Haven't seen anything in the docs about this.
You can provide Array like access to your object by implementing the ArrayAccess interface, which is part of the PHP Standard Library. This interface is one of those "even if you don't have the full PHP Standard Library extension installed, you still have this interface available in PHP 5" things.
By implementing this interface, and defining four methods for your class
public boolean offsetExists ( string $offset )
public mixed offsetGet ( string $offset )
public void offsetSet ( string $offset , string $value )
public void offsetUnset ( string $offset )
you should be able to use square brackets with your instantiated objects.
As for SimpleXML itself, I'm not sure if it actually implements the ArrayAccess interface, or if there's something else going on behind the scenes in the PHP source that gives it these super powers.
I believe you can extend the SimpleXML class, and implement the ArrayAccess in that.
As for SimpleXML itself, it is an internal class. The only interface it implements for PHP userspace is Traversable
.
Internally when accessed with square brackets, SimpleXML looks for things depending on the value and type of the index/key.
If the type is integer (long), tries to find the zero-indexed, numbered element and offer its value. If you have a concrete node element like $root
for the root element, $root[0]
will represent the value of that element:
$root = new SimpleXMLElement('<root a="vala"></root>');
$root[0] = 'hello';
# <root a="vala">hello</root>
# ^^^^^- changed value
The root element is a bit uninteresting because there only exists one. It is more interesting with elements that have parents.
$root = new SimpleXMLElement('<root a="vala"><child /></root>');
$root->child[0] = 'hello';
# <root a="vala"><child>hello</child></root>
# ^^^^^- changed value
Sets the node-value of the first child
element. An index up-one will add a new child:
$root = new SimpleXMLElement('<root a="vala"><child /></root>');
$root->child[1] = 'hello';
# <root a="vala"><child/><child>hello</child></root>
# ^-- added child --^
This pretty much works like with arrays. Strings that are an integer number, work like an integer:
$root->child['1'] = 'hello';
# <root a="vala"><child/><child>hello</child></root>
# ^-- added child --^
And leaving the brackets empty, will add a new element at the end:
$root->child[] = 'hello';
$root->child[] = 'world';
# <root a="vala"><child/><child>hello</child><child>world</child></root>
So far for integers and "no" offset. Like with a standard PHP array, this must not be confused with passing NULL
. It would be converted to an empty string ""
.
With any string, SimpleXML will look for an attribute node instead of a child element node:
$root = new SimpleXMLElement('<root a="vala"></root>');
echo $root['a'], "\n"; # vala
This also works for adding attributes:
$root = new SimpleXMLElement('<root a="vala"></root>');
$root['b'] = 'hello'; # <root a="vala" b="hello"/>
An edge-case is using an empty string (""
) or NULL
because simplexml then errors out saying that an attribute with no name is invalid. Makes sense as attributes must have a name:
Warning: main(): Cannot write or create unnamed attribute in ...
Another more special case is the support of the __toString
magic method. If you pass an object as offset, SimpleXML will try to convert it to string via the magic method. It will then use the returned string as described above.
To summarize: The SimpleXMLElement
class does not implement the ArrayAccess
interface but as it is an internal class, it can add an array-similar behavior. And SimpleXML does exactly that.
The utility function in PHP sources is called sxe_prop_dim_read
.
But what about when you want to do something similar with your classes?
That is what the ArrayAccess
interface is for. Implement it in your own classes. PHP already takes some of the work for you internally to make offsets more array-like: integer stay integer; string like an integer are converted to integer and booleans are converted to integer.
However ArrayAccess
allows more than a standard array: valid offsets are floats, NULL, arrays and objects.
Especially with NULL
, you are not able to differ between setting the offset NULL
or setting a new element - both cases will provide NULL
as offset.
An example implementation of the interface is available with another question: PHP, SPL, ArrayAccess Interface.
I guess it's the magic method __get()
Edit: I think I guessed wrong. Didn't know of the Array Access interface yet.