LoginSignup
10
10

More than 5 years have passed since last update.

PHPのトレイトをいまさら使う

Posted at

主務(会社)が変わり、環境がPHP5.6へと変わったのでトレイトを利用してみる。

トレイトとは

PHPマニュアルでは、トレイト(trait)を下記のように説明している。

トレイトは、PHP のような単一継承言語でコードを再利用するための仕組みのひとつです。 

まぁ、簡単に言えば、使い回すことができるクラスの部品のようなもの。

実際に使ってみると

<?php

trait Run
{
    public function run()
    {
        echo 'Brrrrr...' . PHP_EOL;
    }
}

class CarA
{
    use Run;
}

class CarB
{
    use Run;
}

$carA = new CarA();
$carA->run();

$carB = new CarB();
$carB->run();

"Brrrrr..."の出力部分をトレイトで共通化して、各クラスで読み込む。
この時点で既に無限の可能性を感じ始める。

<?php

trait Run
{
    public function run()
    {
        echo 'Brrrrr...' . PHP_EOL;
    }
}

trait Sound
{
    public function sound()
    {
        echo 'honk!' . PHP_EOL;
    }
}

class CarA
{
    use Run, Sound;
}

class CarB
{
    use Run;
}

$carA = new CarA();
$carA->run();
$carA->sound();

$carB = new CarB();
$carB->run();

複数のtraitを利用する場合。
多重継承っぽい。

ただ、複数のtraitを読み込んだ場合に、同名メソッドや変数はどうなるか?といった問題がある。
もちろん、この回避方法も用意されている。

<?php

trait Run
{
    public function run()
    {
        echo 'Brrrrr...' . PHP_EOL;
    }
}

trait RunFast
{
    public function run()
    {
        echo 'Wheeeee...' . PHP_EOL;
    }
}

class CarA
{
    use Run, RunFast {
        Run::run insteadof RunFast;
        // RunFast::run insteadof Run;
        RunFast::run as runFast;
    }
}

$carA = new CarA();
$carA->run();
$carA->runFast();

insteadofでどちらのメソッドを優先して利用するかが指定できる。
またasでエイリアスを張ることでもう一方のメソッドも利用できる。

こんな感じでマニュアルに書かれている通りでも十分に便利。
でも、おそらくデザインパターン中とかで利用したりすればもっと柔軟にいろいろ書けるような気がする。

<?php

/**
 * 文字列表示トレイト
 */
trait DisplayTrait
{
    /**
     * 文字列を表示する
     *
     * @return void
     */
    public function display()
    {
        echo $this->_msg . PHP_EOL;
    }
}

/**
 * 文字列加工トレイト
 */
trait ProcessTrait
{
    /**
     * 文字列を加工する
     *
     * @return void
     */
    public function process()
    {
        $this->_msg = sprintf('===== %s =====', $this->_msg);

        return $this;   // ※もちろんselfは使えないよ
    }
}

/**
 * インターフェース
 */
interface Sample
{
    /**
     * 文字列を表示する
     *
     * @return void
     */
    public function display();
}

/**
 * HogeSample
 */
class HogeSample implements Sample //extends SampleBase
{
    use ProcessTrait, DisplayTrait; # (擬似的)多重継承が可能に

    /**
     * 表示メッセージ
     *
     * @var
     */
    protected $_msg = 'Hoge';
}

/**
 * PiyoSample
 */
class PiyoSample implements Sample //extends SampleBase
{
    use DisplayTrait;

    /**
     * 表示メッセージ
     *
     * @var
     */
    protected $_msg = 'Piyo';
}

/**
 * ファクトリ
 */
class SampleFactory
{
    /**
     * オブジェクト生成
     *
     * @param string $type タイプ
     * @return SampleInterface
     * @throws InvaliParameterException タイプの指定が正しくない場合
     */
    public static function create($type)
    {
        switch ($type) {
            case 'hoge':
                return new HogeSample();
            case 'piyo':
                return new PiyoSample();
            default:
                throw new InvalidParameterException();
        }
    }
}

// 実行結果: ===== Hoge =====
SampleFactory::create('hoge')->process()->display();

これまで抽象クラスを膨らませていたこと(抽象クラスの数を増やすこと)で耐えていたファクトリパターンもすっきり。
trait中のメソッドでインターフェースの実装を行う。

トレイト便利だなー。

10
10
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
10
10