Using objects as arrays (with a real world example) in PHP
Sometimes, it’s convenient when you could get to access class objects as arrays. For instance, the time when one of the class properties is of type array and you want to manipulate it just like you’d do with an array without exposing it during object creation. I’ve included a real world example of where this could be useful, at the end of the article. So, read on!
In PHP, there is a predefined interface called ArrayAccess comes in PHP 5, 7 which is used to accomplish such kind of a use case. When a class implements ArrayAccess
, they’ll need to implement a few methods in order to comply with the interface and these are the methods that make the objects behave like arrays.
The ArrayAccess
interface
The ArrayAccess
interface, as I said earlier, is a predefined interface in PHP with the following structure.
interface ArrayAccess
{
abstract public offsetExists ( mixed $offset ) : bool
abstract public offsetGet ( mixed $offset ) : mixed
abstract public offsetSet ( mixed $offset , mixed $value ) : void
abstract public offsetUnset ( mixed $offset ) : void
}
As you can see, the interface contains four abstract methods which have different purposes for them once implemented by the classes. Let’s try to understand what each of these does.
The offsetExists
method
This method can be used to check if the particular array offset exists in the object or not and return a boolean value when using isset() or empty() on objects implementing ArrayAccess
. For this, you can write your own implementation in the method.
Take following for example,
<?php
class MyObj implements ArrayAccess
{
// excluded other methods for brevity
public function offsetExists($var)
{
var_dump(__METHOD__);
if ($var == 'foobar') {
return true;
}
return false;
}
}
Now, when an offset gets called on the object like following,
$myObj = new MyObj();
var_dump(isset($myObj['foobar']));
The offsetExists
method will get called and excute the underlying implementation which will produce the following output.
string(17) "obj::offsetExists"
bool(true)
If the offset was something other than “foobar”, it would return bool(false)
instead.
However, if you use isset()
on the object without any offset like below,
$myObj = new MyObj();
var_dump(isset($myObj));
The method offsetExists
always returns true
irrespectively.
The offsetGet
method
This method returns a value for the specified offset for the object. The method also gets invoked when calling empty()
on an offset. Here’s how it works.
<?php
class MyObj implements ArrayAccess
{
private $container = [];
public function __construct()
{
$this->container = [
'apple' => 1,
'banana' => 2
];
}
// excluded other methods for brevity
public function offsetGet($offset)
{
return isset($this->container[$offset]) ? $this->container[$offset] : null;
}
}
So now, whenever am attempt made to access the designated offset on the object, offsetGet
method will get invoked and it will return the value for the $container
for that offset and null
if there doesn’t exist the specified offset.
$myObj = new MyObj();
var_dump($myObj['apple']); // 1
var_dump($myObj['kiwi']); // null
The offsetSet
method
The method assigns a value to the specified offset of the object.
abstract public ArrayAccess::offsetSet ( mixed $offset , mixed $value ) : void
The method accepts following two parameters:
offset
- The offset to assign the value to.value
- The value to be set to the offset.
Check the following for example.
<?php
class MyObj implements ArrayAccess
{
private $container = [];
public function __construct()
{
$this->container = [
'apple' => 1,
'banana' => 2
];
}
// excluded other methods for brevity
public function offsetGet($offset)
{
return isset($this->container[$offset]) ? $this->container[$offset] : null;
}
public function offsetSet($offset, $value)
{
if (is_null($offset)) {
$this->container[] = $value;
} else {
$this->container[$offset] = $value;
}
}
}
$myObj = new MyObj;
$myObj['kiwi'] = 10;
var_dump($myObj['kiwi']); // 10
$myObj['apple'] = 5
var_dump($myObj['apple']); // 5
$myObj[] = 50; // will add a "0" offset in the array and set the value
?>
The offsetUnset
method
This method gets called when an attempt been made to unset an offset on the object.
abstract public ArrayAccess::offsetUnset ( mixed $offset ) : void
The method accepts one parameter offset
which can be used to unset an offset. Below is an example of the same.
<?php
class MyObj implements ArrayAccess
{
private $container = [];
public function __construct()
{
$this->container = [
'apple' => 1,
'banana' => 2
];
}
// excluded other methods for brevity
public function offsetUnset($offset)
{
unset($this->container[$offset]);
}
}
$myObj = new MyObj;
unset($myObj['apple'])
var_dump($obj['apple']); // null
?>
Using __invoke
to access entire array
In case if you want to access the entire object as an array, you can implement __invoke()
magic method into the class and return the entire array from it like so.
<?php
class MyObj implements ArrayAccess
{
private $container = array();
public function __construct()
{
$this->container = [
'apple' => 1,
'banana' => 2
];
}
// excluded other methods for brevity
public function __invoke()
{
return $this->container;
}
}
$myObj = new MyObj;
var_dump($myObj);
/*
object(MyObj)#1 (1) {
["container":"MyObj":private]=>
array(2) {
["apple"]=>
int(1)
["banana"]=>
int(2)
}
}
*/
?>
A real-world usecase
Recently, I came across this PR #30817 in the Laravel framework by Taylor Otwell. In this, he has implemented ArrayAccess on both JsonResponse
and TestResponse
to be able to proxy directly into the response JSON without going through the original
property.
And due to this the code that used to access the JSON payload in the response like so.
$response->original['foo'];
Now, reduced to to the following because of implementing ArrayAccess
.
$response['foo'];
Pretty neat, right?
Like this article?
Buy me a coffee👋 Hi there! I'm Amit. I write articles about all things web development. You can become a sponsor on my blog to help me continue my writing journey and get your brand in front of thousands of eyes.