This week’s a quick remedial primer on PHP’s ArrayAccess
interface. We’ll be back to Laravel next time, and promise you’ll see why we needed the primer.
First, some background and context. PHP has a native language type called an array. This isn’t a C array, where each element points to a specific memory location. Instead, a PHP array is an implementation of an ordered map. You can add any variable to an array. If you want to treat an array as an ordered list, you can add items with the following syntax
$array = [];
$array[] = 'abc';
$array[] = 123;
$array[2] = 'science';
You can also treat an array as a hash-map/dictionary
$array = [];
$array['foo'] = 'bar';
$array['baz'] = 42;
PHP also has a native language type called an object. To create an object, a user programmer must first define a class with the class
keyword
class Foo
{
}
and then use the new
keyword to create an object from the class
$object = new Foo;
PHP also includes a built in class called stdClass
. A user programmer can use this class to create stand-alone objects without defining a class.
$object = new stdClass;
Finally, more modern versions of php include a shortcut for creating stdClass
objects
$object = {};
Array vs. Object
Objects and arrays are different things in PHP, although they share some functionality. You can use an array as a dictionary like data structure
$array = [];
$array['foo'] = 'bar';
$array['baz-bar'] = 'science';
echo $array['foo'];
You can also use an object as a dictionary like data structure
$object = {};
$object->foo = 'bar';
$object->{'baz-bar'} = 'science'
echo $object->foo;
The only difference is the syntax. Arrays use the bracket characters []
, while objects use the “arrow” operator (->
). If you try to use array brackets to access the values on on object, PHP will issue a fatal error
Fatal error: Cannot use object of type stdClass as array …
So far, most of this is common knowledge to working PHP programmers. What’s less commonly known is PHP allows end-user-programmers to define what should happen when a programmer tries to access object properties with array syntax. Put another way, you can give your objects the ability to work with array brackets ($o[]
)
Array Access
PHP ships with a number of built in classes and interfaces. These classes and interfaces allow a PHP developer to add features to the language that would normally be reserved for a PHP core developer. The one we’re interested in today is ArrayAccess.
If you want to give your object the ability to act like an array, all you need to do is implement the built-in ArrayAccess
interface. Give the following simple program a try
<?php
class Foo implements ArrayAccess
{
}
$object = new Foo;
$object['foo'] = 'bar';
echo $object['foo'],"\n";
In our class declaration statement we’ve included implements ArrayAccess
. If we run this program, we’ll see the following error.
PHP Fatal error: Class Foo contains 4 abstract methods and must therefore be declared abstract or implement the remaining methods (ArrayAccess::offsetExists, ArrayAccess::offsetGet, ArrayAccess::offsetSet, …) in
Simply implementing an interface isn’t enough — we actually need to write the methods that are part of the interface synopsis. For a user defined interface, this would mean looking at the interface definition — for a built-in interface, we need to rely on the PHP manual. If you look at the manual, you’ll see the interface is defined as
ArrayAccess {
/* Methods */
abstract public boolean offsetExists ( mixed $offset )
abstract public mixed offsetGet ( mixed $offset )
abstract public void offsetSet ( mixed $offset , mixed $value )
abstract public void offsetUnset ( mixed $offset )
}
That is, we need to define the 4 methods ArrayAccess::offsetExists, ArrayAccess::offsetGet, ArrayAccess::offsetSet, ArrayAccess::offsetUnset
. Unfortunately, the help stops there. Interfaces are great for telling you what method you need to define, but less great at telling you what each method is supposed to do.
Let’s do a little detective work. Create the following short program, and then run it (either in your browser or via the command line)
<?php
class Foo implements ArrayAccess
{
public function offsetExists ($offset)
{
echo __METHOD__, "\n";
}
public function offsetGet ($offset)
{
echo __METHOD__, "\n";
}
public function offsetSet ($offset, $value)
{
echo __METHOD__, "\n";
}
public function offsetUnset ($offset)
{
echo __METHOD__, "\n";
}
}
$object = new Foo;
$object['foo'] = 'bar';
This program defines the class Foo
and implements each of the ArrayAccess
interface’s abstract methods. Then, it instantiates a Foo
object, and tries to set an array property. We’ve also programed each method to output its name whenever the program calls the method.
If you run the above program, you’ll see output something like this
Foo::offsetSet
Now try getting a property.
$object = new Foo;
$object['foo'] = 'bar';
$bar = $object['foo'];
echo "The value we fetched: " . $bar,"\n";
The above will output something like the following
Foo::offsetSet
Foo::offsetGet
The value we fetched:
What did this teach us? When we tried to set a value via array access ($object['foo'] = 'bar';
), behind the scenes PHP called our class’s offsetSet
method. When we tried to get a value via array access ($bar = $object['foo'];
), behind the scenes PHP called offsetGet
. However, no value was actually set or gotten from the object (The value we fetched:
).
That’s how the PHP ArrayAccess
interface works. It has some behind the scenes magic that will call a method on your class when you perform an array operation on your object, but it’s still up to you to implement that access. If you try something like this
<?php
class Foo implements ArrayAccess
{
protected $_data=array();
public function offsetExists ($offset)
{
return array_key_exists($offset, $this->_data);
}
public function offsetGet ($offset)
{
return $this->_data[$offset];
}
public function offsetSet ($offset, $value)
{
$this->_data[$offset] = $value;
}
public function offsetUnset ($offset)
{
unset($this->_data[$offset]);
}
}
$object = new Foo;
$object['foo'] = 'bar';
$bar = $object['foo'];
echo "The value we fetched: " . $bar,"\n";
You’ll have much better results.
The value we fetched: bar
While you initial reaction may be annoyance — “Why doesn’t PHP just implement this for me” — by giving you the responsibility of implementing your own ArrayAccess
rules, the PHP core developers have given you (or the framework developers you rely on) tremendous power to create new behavior for your own objects.