Goal
- 配列の添字にアクセスしたときに特定のロジックを実行させたい
-
$array->get(0)
ではなく$array[0]
と書きたい -
foreach
もサポートしたい - 具体的な利用例としては、ActiveRecordの遅延評価みたいなことをやりたい
Code
phpの配列はオブジェクトではないため、配列操作時に特定のロジックをフックさせるようなことができないですが、ArrayAccessインタフェースを実装すると、配列操作のインタフェースを備えたオブジェクトを作成することができます.
http://php.net/manual/ja/class.arrayaccess.php
<?php
class PersonList implements ArrayAccess {
private $container = [];
public function __construct(array $values=[]) {
$this->container = $values;
}
public function offsetGet($index) {
echo "Hello!";
return isset( $this->container[$index] ) ? $this->container[$index] : null;;
}
public function offsetExists($index) {
return isset($this->container[$index]);
}
public function offsetSet($index, $value) {
if (is_null($index)) {
$this->container[] = $value;
} else {
$this->container[$index] = $value;
}
}
public function offsetUnset($index) {
unset($this->container[$offset]);
}
}
$persons = new PersonList(['Newton', 'Gauss', 'Boltzmann']);
echo $persons[0] . PHP_EOL;
// Hello!!Newton
上記の例は、PHPドキュメントのサンプルコードほぼそのままですが、$persons[0]
のように添字へのアクセスがおこなわれたときにoffsetGet
メソッドがよばれるため、そのメソッドに任意の処理を追加すれば配列操作をフックすることができます.
<?php
class PersonList implements ArrayAccess, IteratorAggregate {
private $container = [];
public function __construct(array $values=[]) {
$this->container = $values;
}
private function load() {
if ( count( $this->container ) <= 0 ) {
$this->container = ['Newton', 'Gauss', 'Boltzmann'];
}
}
public function offsetGet($index) {
$this->load();
return isset( $this->container[$index] ) ? $this->container[$index] : null;;
}
public function offsetExists($index) {
return isset($this->container[$index]);
}
public function offsetSet($index, $value) {
if (is_null($index)) {
$this->container[] = $value;
} else {
$this->container[$index] = $value;
}
}
public function offsetUnset($index) {
unset($this->container[$offset]);
}
public function getIterator() {
$this->load();
return new ArrayIterator($this->container);
}
}
$persons = new PersonList();
// 添字アクセス
echo $persons[0] . PHP_EOL;
foreach ( $persons as $person ) {
// foreachアクセス
echo $person . PHP_EOL;
}
// ■ 添字アクセス結果
// Newton
// ■ foreachアクセス結果
// Newton
// Gauss
// Boltzmann
上記の例では、IteratorAggregateを使ってforeachをサポートしています. foreachで走査開始されたタイミングと添字アクセスが発生したタイミングで動的にデータをロードしています.
http://php.net/manual/ja/class.iteratoraggregate.php
PS
これと同じことはArrayObjectクラスを使うとできますが、独自の実装を追加する場合は継承する必要があるので基本はインタフェースを使うこちらの方法を使うべきだと思います.
インタフェースを使う方法でArrayObjectのフルの機能をサポートするには、IteratorAggregate, ArrayAccess, Serializable, Countableインタフェースを実装する必要があるので、てっとりばやく配列操作のインタフェースを作りたければArrayObjectを継承する方法でもよさそうです.
ArrayObject クラス
PHPで配列の要素アクセスをフックする
Environment
$ uname -a
Linux *** 2.6.32-431.el6.x86_64 #1 SMP Fri Nov 22 03:15:09 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
$ cat /etc/issue
CentOS release 6.5 (Final)
Kernel \r on an \m
$ /usr/local/php-5.5.4/bin/php -v
PHP 5.5.4 (cli) (built: Mar 11 2014 11:06:57)
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2013 Zend Technologies
$ /usr/local/php-5.6.0/bin/php -v
PHP 5.6.0 (cli) (built: Aug 29 2014 06:35:44)
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2014 Zend Technologies