ArrayAccess,IteratorAggregateインタフェースで配列操作可能なオブジェクトを作成する


# Goal

* 配列の添字にアクセスしたときに特定のロジックを実行させたい
* `$array->get(0)`ではなく`$array[0]`と書きたい
* `foreach`もサポートしたい
* 具体的な利用例としては、ActiveRecordの遅延評価みたいなことをやりたい

# Code

phpの配列はオブジェクトではないため、配列操作時に特定のロジックをフックさせるようなことができないですが、ArrayAccessインタフェースを実装すると、配列操作のインタフェースを備えたオブジェクトを作成することができます.
http://php.net/manual/ja/class.arrayaccess.php

```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
<?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 クラス](http://php.net/manual/ja/class.arrayobject.php)
[PHPで配列の要素アクセスをフックする](http://qiita.com/metheglin/items/62d8fa527b6b08099d3b)


# 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
```