2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

PHPのArrayAccessインタフェースをざっくり解説

Last updated at Posted at 2020-03-29

初心者向け記事。
PHPには、それを実装することでクラスに言語構造レベルの機能を持たせることができるインタフェースがいくつかある。

[定義済みのインターフェイスとクラス] (https://www.php.net/manual/ja/reserved.interfaces.php)

Laravelなどのフレームワークの中でも使われるケースがあり、ソースコードを追ったりする際は知っておく必要が出てくる。今回はArrayAccessインタフェースを例にとって、どのように使われるのかをみていく。

(間違いなどあればご指摘ください...:pray:

ArrayAccessインタフェース

PHPのオブジェクトは、ArrayAccessインタフェースを実装することで配列のように操作することができるようになる。

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
}

引数の$offsetは配列のキー、$valueは格納する値に相当する。
それぞれのメソッドの役割は以下のようになる。

  • offsetExists()は指定したキーに値がセットされているかをbool値で返す。isset()による判定を可能にする。
  • offsetGet()はキーを元に値を取得する。
  • offsetSet()は指定したキーと値をペアで格納する。
  • offsetUnset()は指定したキーとそのペアの値を削除。unset()による割り当ての解除を可能にする。

引数にはmixedが指定されており、様々な型を受け入れてくれることがわかる。

Illuminate\Container\Containerクラスの例

例えばLaravelのサービスコンテナ(Illuminate\Foundation\Applicationクラス)が継承しているIlluminate\Container\ContainerクラスはArrayAccessインタフェースを継承している。
以下は該当のテストコードを一部変更して引用したものになる。

ContainerTest.php

use PHPUnit\Framework\TestCase;

class ContainerTest extends TestCase
{
    public function testArrayAccess()
    {
        $container = new Container;
        $container['something'] = 'foo'; // offsetSet()
        $this->assertTrue(isset($container['something'])); // offsetExists()
        $this->assertSame('foo', $container['something']); // offsetGet()
        
        unset($container['something']); // offsetUnset()
        $this->assertFalse(isset($container['something']));
    }
}

コードを見るとわかる通り、実装するContainerクラスは当然オブジェクトだが、配列と同じように操作していることがわかる。 ArrayAccessインタフェースを実装することで、このテストがパスすることを確認する。

実装

Container.php

class Container implements ArrayAccess
{   
    private $container = [];

    public function offsetExists($key): bool
    {
        return isset($this->container[$key]);
    }

    public function offsetGet($key)
    {
        return $this->container[$key];
    }

    public function offsetSet($key, $value): void
    {
        $this->container[$key] = $value;
    }

    public function offsetUnset($key): void
    {
        unset($this->container[$key]);
    }
}

本来のIlluminate\Container\Containerクラスでは結合処理などを行なっているのでもう少し混み入っているが、ArrayAccessインタフェースを使用してオブジェクトに配列操作を持たせるだけならこんな感じでも十分。

Containerクラスのインスタンスは自身が保持する$containerプロパティに実際に与えられるキーと値を格納する(実際のサービスコンテナの場合、ここにキーと解決処理などをバインドする)。

それぞれ4つのメソッドの中では実際に配列をもつプロパティ$containerにアクセスして必要な操作を行うことになるため、issetunsetなどをその中で使用することが普通。
offsetExistsメソッドはissetを使用し、$containerプロパティに指定されたキーをもつ値が格納されているかをboolで返す。offsetUnsetメソッドも同様にunsetを使用してキーとその値の割り当てを解除。offsetGetメソッドとoffsetSetメソッドは$containerプロパティに対し、指定されたキーでアクセスし、値を取得するか、新規に値を登録する。

これを実装したら、テストが通ることを確認する。

$ ./vendor/bin/phpunit tests/ContainerTest.php
PHPUnit 9.0.1 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 141 ms, Memory: 4.00 MB

OK (1 test, 3 assertions)

Laravelでは他にもEloquent\ModelクラスやHttp\RequestクラスなどでArrayAccessインタフェースを実装してたりする。Containerはまだしもこの辺は正直不要だと思うが、それでも実装されてしまっているので読む際には知らないと??となりがちなので、基本的なPHPの知識として押さえておきましょう。。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?