【PHP】トレイトの基礎知識

#トレイトとは
トレイトとはなかなか聞き慣れない言葉ですが、いくつかの機能をまとめてコードを再利用するためのものです。
トレイトのメリットは、継承のようなis_a関係がなくとも機能を共有できるところにあります。
トレイトは以下のように宣言します。

#トレイトの宣言
```php
<?php
trait Hoge {
	public function sayHello() {
		echo 'Hello';
	}
}
```

#クラスでトレイトのメソッドを使う
クラスでトレイトのメソッドを使用したいときは、useで宣言します。複数のトレイトを使用したいときは、カンマで区切ります。

```php
public class foo {
	use Hoge;

	function sayHelloWorld() {
		echo $this->sayHello;
		echo 'World!';
	}
}

$foo = new Foo();
$foo->sayHelloWorld();
// HelloWorld!
```

また、トレイトはプロパティを定義したり、抽象メソッドを定義することもできます。

#トレイト同士のメソッドの競合
同じメソッドを持つトレイトを複数インクルード使用しようとすると、`fatal error`が発生します。

```php
trait Hoge {
	public function sayHello() {
		echo 'Hello';
	}
}

trait Hogehoge {
	public function sayHello() {
		echo 'HELLO!!';
	}
}

class Foo {
	use Hoge, Hogehoge;

	public function sayHelloWorld() {
		echo $this->sayHello();
		echo 'World!';
	}
}

$foo = new Foo();
$foo->sayHelloWorld();

// Fatal error: Trait method sayHello has not been applied, because there are collisions with other trait methods on Foo
```

`insteadof`を使用し、どちらのメソッドを使用するか宣言することで、衝突を回避することができます。

```php
trait Hoge {
	public function sayHello() {
		echo 'Hello';
	}
}

trait Hogehoge {
	public function sayHello() {
		echo 'HELLO!!';
	}
}

class Foo {
	use Hoge, Hogehoge {
		// 先頭は使用するトレイト 後ろは使わない方のトレイト
		Hogehoge::sayHello insteadof Hoge;
	}

	public function sayHelloWorld() {
		echo $this->sayHello();
		echo 'World!';
	}
}

$foo = new Foo();
$foo->sayHelloWorld();
// HELLO!!World!
```

使われないトレイトのメソッドは、`as`を使用してエイリアスを指定して使うことができます。

```php
trait Hoge {
	public function sayHello() {
		echo 'Hello';
	}
}

trait Hogehoge {
	public function sayHello() {
		echo 'HELLO!!';
	}
}

class Foo {
	use Hoge, Hogehoge {
		Hogehoge::sayHello insteadof Hoge;
		Hoge::sayHello as hello;
	}

	public function sayHelloWorld() {
		echo $this->sayHello();
		echo 'World!';
		echo '<br>';
		echo $this->hello();
		echo 'World!';
	}
}

$foo = new Foo();
$foo->sayHelloWorld();

// HELLO!!World!
// HelloWorld!
```

また、`as`を用いることでメソッドの可視性も変更することが可能です。

```php
trait Hoge {
	public function sayHello() {
		echo 'Hello';
	}
}

class Foo {
	use Hoge {
		sayHello as private;
	}
}

$foo = new Foo();
$foo->sayHelloWorld();
// Fatal error: Uncaught Error: Call to private method Foo::sayHello() from context
```

#トレイトとクラスのメソッドの競合
こんどは、トレイトとクラス、さらにスーパクラスとの間でメソッドが競合しています。
どの結果が出力されるのでしょうか？

```php
trait Hoge {
	public function sayHello() {
		echo 'Hello';
	}
}

class Par {
	public function sayHello() {
		echo 'HELLO';
	}
}

class Foo extends Par{
	use Hoge;

	public function sayHello() {
		echo 'Hello World!';
	}
}

$foo = new Foo();
$foo->sayHello();
// Hello World
```

これは、現在のクラスのメソッドの`sayHello()`の結果が出力されました。現在のクラスの優先度が最高です。
では、スーパークラスとトレイトの優先順位の関係はどうでしょうか?

```php

trait Hoge {
	public function sayHello() {
		echo 'Hello';
	}
}

class Par {
	public function sayHello() {
		echo 'HELLO';
	}
}

class Foo extends Par{
	use Hoge;
}

$foo = new Foo();
$foo->sayHello();
// Hello
```

今度はトレイトのメソッドが勝ちました。
したがって優先順位は、現在のクラス > トレイト > スーパークラスとなります。

#参考文献#
[公式](https://www.php.net/manual/ja/language.oop5.traits.php)
[プログラミングPHP](https://www.amazon.co.jp/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0PHP-%E7%AC%AC3%E7%89%88-Kevin-Tatroe/dp/4873116686/ref=sr_1_4?adgrpid=70859578037&gclid=Cj0KCQiA0NfvBRCVARIsAO4930lbztCyTEGBAAgs7a1kpKYu2xB73---uN0kyC-romI4AiZSqbaQsZkaAhmuEALw_wcB&hvadid=351990972360&hvdev=c&hvlocphy=1009300&hvnetw=g&hvpos=1t2&hvqmt=b&hvrand=84637564675596785&hvtargid=kwd-971998972072&hydadcr=27266_11561145&jp-ad-ap=0&keywords=%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0+php+%E6%9C%AC&qid=1576460940&sr=8-4)