Edit

Implementing ArrayAccess for Domain Objects

Option 1

In this implementation we will make use of PHPs highly dynamic nature to dynamically access properties of a subtype in a supertype at runtime. Note that this implementation has 2 main caveats:

  • It will not work with private fields
  • It will not go through any getters/setters
1<?php abstract class DomainObject implements ArrayAccess { public function offsetExists($offset): bool { return isset($this->$offset); } public function offsetSet($offset, $value): void { $this->$offset = $value; } public function offsetGet($offset) { return $this->$offset; } public function offsetUnset($offset): void { $this->$offset = null; } }
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

Option 2

In this implementation we will dynamically invoke getters/setters. Again we use PHPs dynamic nature to invoke methods on a subtype from a supertype at runtime. This implementation has the following caveats:

  • It relies on a naming convention
  • The semantics of offsetExists can differ
  • offsetUnset will not work with typehinted setters
1<?php abstract class DomainObject implements ArrayAccess { public function offsetExists($offset): bool { // In this example we say that exists means it is not null $value = $this->{"get$offset"}(); return $value !== null; } public function offsetSet($offset, $value): void { $this->{"set$offset"}($value); } public function offsetGet($offset) { return $this->{"get$offset"}(); } public function offsetUnset($offset): void { $this->{"set$offset"}(null); } }
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

Read-only

You can slightly tweak option 1 or option 2 in order to make array access read-only. This will also circumvent some of the caveats of each option. Simply make offsetSet and offsetUnset throw an exception (i.e. BadMethodCallException).

1<?php abstract class DomainObject implements ArrayAccess { public function offsetExists($offset): bool { // option 1 or option 2 } public function offsetSet($offset, $value): void { throw new BadMethodCallException("Array access of class " . get_class($this) . " is read-only!"); } public function offsetGet($offset) { // option 1 or option 2 } public function offsetUnset($offset): void { throw new BadMethodCallException("Array access of class " . get_class($this) . " is read-only!"); } }
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24