[PHP] トレイト



PHPのトレイトについてのメモです。 ここでは基本のコードと、継承時メンバー名が重複した時の挙動を確認してみました。

基本コード

トレイトのコード コードを開く
トレイトのコード

<?php

trait SayHello
{
    function sayHello() {
        echo "say hello.", "\n";
    }
}

class Person
{
    use SayHello;   // 上で定義したトレイトの名前

    function say()
    {
        echo "say.", "\n";
    }
}
$person = new Person();
$person->say();
$person->sayHello();    // トレイトのメソッド

  • 5.4.0以降で使える。
  • 単一継承でできないようなコードの再利用ができるようになる。
  • トレイトからインスタンスの生成はできない
なプ

useの記述位置はクラスのブロック内であることに注意です。

継承とトレイトを使ってみる

ここでは継承したクラスにトレイトを追加するサンプルを作ってみます。

Actorは攻撃手段を持っていて、継承してHeroになると回復手段を持ち、トレイトで攻撃魔法を追加する、みたいな感じのコード。

コード

コードを開く

<?php

trait SkillFire
{
    function fire()
    {
        echo "skill fire.", "\n";
    }
}

class Actor
{
    function attack()
    {
        echo "attack.", "\n";
    }
}

class Hero extends Actor
{
    use SkillFire;

    function cure()
    {
        echo "skill cure.", "\n";
    }
}

$person = new Hero();
$person->attack();  // Actorのattackメソッド
$person->cure();    // Heroのcureメソッド
$person->fire();    // SkillFire(トレイト)のメソッド

結果

attack.
skill cure.
skill fire.

メンバーの優先順位(同じ名前がカブった時)

トレイトを追加したクラス・親クラスに同じメンバがあった場合はどうなるか?

つまりみんなが同じメソッドとか持っていた場合は、どれが優先されるのか?という場合の話です。

優先順位は以下のようになっています。

優先度 優先されるメンバ
1番目(最優先) トレイトを使うクラス
2番目 トレイト
3番目 継承するクラス

優先度が1番目を試す(クラスのメンバ)

親クラス・サブクラス・トレイトで同じメソッドを定義して1番目(トレイトを使うクラス)が優先されることを確認します。

ここではMagicianクラスのfireが優先して呼び出されることをコードで確認していきます。

サンプル コードを開く
サンプル

// トレイト
trait SkillFire
{
    function fire()
    {
        echo "skill fire.", "\n";
    }
}

// 親クラス
class Actor
{
    function fire()
    {
        echo "actor fire.", "\n";
    }
}

// 子クラス
class Magician extends Actor
{
    use SkillFire;

    function fire()
    {
        echo "magician fire", "\n";
    }
}

$magician = new Magician();
$magician->fire();

結果は以下のようになります。 Magicianクラスが持つfireメソッドが呼び出されていますね。

magician fire

優先度が2番目を試す(トレイト)

親クラス・サブクラス・トレイトで同じメソッドを定義して2番目(トレイト)が優先されることを確認します。

今回はMagicialクラスはfireメソッドを持っておらず、Actorとトレイトがfireメソッドを持っています。

このケースではトレイトのfireメソッドが優先されることを確認していきます。

サンプル コードを開く
サンプル

<?php

// トレイト
trait SkillFire
{
    function fire()
    {
        echo "skill fire.", "\n";
    }
}

// 親クラス
class Actor
{
    function fire()
    {
        echo "actor fire.", "\n";
    }
}

// 子クラス
class Magician extends Actor
{
    use SkillFire;

    // マジシャンですがまだスキルを持っていません!
}

$magician = new Magician();
$magician->fire();

結果は以下の通りになります。

skill fire.

トレイトクラスのfireメソッドが呼ばれているので、優先度2(トレイト)であることが確認できました。

優先度が3番目を試す(継承したクラス)は省略

これはトレイトのメンバ名とクラスのメンバ名が異なっているので、衝突が起こっていない状態です。

この場合は継承されたメンバが優先されるので、サンプルは省略します。



関連した記事