初心者向け記事。
PHPには、それを実装することでクラスに言語構造レベルの機能を持たせることができるインタフェースがいくつかある。
[定義済みのインターフェイスとクラス] (https://www.php.net/manual/ja/reserved.interfaces.php)
Laravelなどのフレームワークの中でも使われるケースがあり、ソースコードを追ったりする際は知っておく必要が出てくる。今回はArrayAccessインタフェースを例にとって、どのように使われるのかをみていく。
(間違いなどあればご指摘ください...)
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
インタフェースを継承している。
以下は該当のテストコードを一部変更して引用したものになる。
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インタフェースを実装することで、このテストがパスすることを確認する。
実装
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
にアクセスして必要な操作を行うことになるため、isset
やunset
などをその中で使用することが普通。
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の知識として押さえておきましょう。。