PHP traitとは

traitとは

  • php5.4以降で導入
  • 単一継承言語(PHPなど)でコードを再利用するための仕組みの一つ
  • メソッド群を異なるクラス階層にある独立したクラスで再利用が可能
  • 多重継承などの問題を回避することができる
  • クラスと似ているがいくつかの機能をまとめるだけももの
  • 継承クラスと同様にインスタンス作成は不可
  • 継承しなくても他クラスでも使用可能
  • 優先順位
    • 現在のクラスのメソッド>トレイトのメソッド> 継承したメソッド

PHP: トレイト - Manual

使い所

  • 機能をパーツとして使いたい場合
  • Interfaceと一緒に使うことでデフォルトの実装を提供したい場合
  • 機能をグルーピングすることによって、可読性を向上させたい場合

使い方

  • 定義クラス
trait トレイト名 {
  function メソッドA() {
    // 処理
  }
  function メソッドB() {
    // 処理
  }
}
  • 利用クラス
class クラス名 {
  use トレイト名;
}
  • トレイトを複数使用する場合はuseを使ってカンマ区切りで追加していく
<?php

namespace Illuminate\\Foundation\\Auth;

use Illuminate\\Http\\Request;
use Illuminate\\Support\\Facades\\Auth;
use Illuminate\\Validation\\ValidationException;

trait AuthenticatesUsers
{
    use RedirectsUsers, ThrottlesLogins; //traitを使用

   /**
     * Show the application's login form.
     *
     * @return \\Illuminate\\Http\\Response
     */
    public function showLoginForm()
    {
        return view('auth.login');
    }

    /**
     * Handle a login request to the application.
     *
     * @param  \\Illuminate\\Http\\Request  $request
     * @return \\Illuminate\\Http\\RedirectResponse|\\Illuminate\\Http\\Response|\\Illuminate\\Http\\JsonResponse
     *
     * @throws \\Illuminate\\Validation\\ValidationException
     */
    public function login(Request $request)
    {
        /**
         * 1. バリデーション(形式のチェック)
         **/
        $this->validateLogin($request);

        /**
         * 2. ログイン試行回数を超過していればロックアウトを返す
         **/
        if (
            method_exists($this, 'hasTooManyLoginAttempts') &&
            $this->hasTooManyLoginAttempts($request)
        ) {
            $this->fireLockoutEvent($request);

            return $this->sendLockoutResponse($request);
        }

        /**
         * 3. 認証OKならログイン成功をレスポンス(トップページにリダイレクト)
         **/
        if ($this->attemptLogin($request)) {
            return $this->sendLoginResponse($request);
        }

        /**
         * 4. 認証NGであればログイン試行回数を1増やしてログイン画面をレスポンス
         **/
        $this->incrementLoginAttempts($request);

        return $this->sendFailedLoginResponse($request);
    }

メリット

  • 継承しなくても他クラスでメソッド群を利用可能
  • 共通点のあるメソッド群をグルーピングできる

デメリット

  • 複数のtraitで同名メソッドが定義され、それらを同クラスでuseしようとするとFatal error となる(insteadofやエイリアスを追加することで回避可能)
  • 複雑で暗黙的な依存関係が生まれる

まとめ

  • 意図せずクラス間の結合を生み出す危険性がある
  • 暗黙的な知識を要求するトレイトを使うときはきちんとドキュメントを書くなり知識の共有を行うなどする
  • trait が悪いというよりは書き方の問題

参考

Laravelから学ぶPHPのTraitの使い所 - Qiita
PHP: traitの基礎的なこと - Qiita
【PHP】クラスと抽象クラスとインターフェースとトレイトとDIをまとめてみた【図解】【初心者向け】