PHP のトレイトに気を付ける というエントリを読みました。Scala では self-type annotation により依存関係を明示できるが、PHP ではそれができないので暗黙の依存関係が生まれやすいと書かれていて、まぁその通りなのですが、それで PHP の trait は使いものになりませんねという結論になってしまうと残念でもあります。この記事では、それでも trait を使いたい場合にどう解決を図ればよいか?についての解決策の一つを提示してみたいと思います。

私の関わっているチームでは、PHP の trait で依存関係を明示するために abstract メソッドを宣言しています。上記エントリのコードを書き換えると以下のようになります。

<?php

trait Greeting
{
    public function say()
    {
        if ($this->location() === 'ja') {
            echo 'こんにちは' . PHP_EOL;
        } else {
            echo 'Hello' . PHP_EOL;
        }
    }

    abstract protected function location();
}

class Location
{
}

class US extends Location
{
    use Greeting;

    protected function location()
    {
        return 'us';
    }
}

class Japan extends Location
{
    use Greeting;

    protected function location()
    {
        return 'ja';
    }
}

$us = new US();
$us->say();

$ja = new Japan();
$ja->say();

違いとしては、Greeting trait が $location プロパティから location() メソッドに依存するようになったことです。こうすれば Greeting trait を使用するクラスは location メソッドを実装しない限り抽象メソッドの実装漏れとなり、実行時にエラーにできます。PHP の言語仕様の制約上、実行時にしかエラーにできないので微妙なことは変わりませんが、それでもプロパティを使用するよりは随分マシです。

以上、PHP の trait の使い方についての記事でした。