トレイトとは
トレイトとはなかなか聞き慣れない言葉ですが、いくつかの機能をまとめてコードを再利用するためのものです。
トレイトのメリットは、継承のようなis_a関係がなくとも機能を共有できるところにあります。
トレイトは以下のように宣言します。
トレイトの宣言
<?php
trait Hoge {
public function sayHello() {
echo 'Hello';
}
}
クラスでトレイトのメソッドを使う
クラスでトレイトのメソッドを使用したいときは、useで宣言します。複数のトレイトを使用したいときは、カンマで区切ります。
public class foo {
use Hoge;
function sayHelloWorld() {
echo $this->sayHello;
echo 'World!';
}
}
$foo = new Foo();
$foo->sayHelloWorld();
// HelloWorld!
また、トレイトはプロパティを定義したり、抽象メソッドを定義することもできます。
トレイト同士のメソッドの競合
同じメソッドを持つトレイトを複数インクルード使用しようとすると、fatal error
が発生します。
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
を使用し、どちらのメソッドを使用するか宣言することで、衝突を回避することができます。
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
を使用してエイリアスを指定して使うことができます。
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
を用いることでメソッドの可視性も変更することが可能です。
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
トレイトとクラスのメソッドの競合
こんどは、トレイトとクラス、さらにスーパクラスとの間でメソッドが競合しています。
どの結果が出力されるのでしょうか?
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()
の結果が出力されました。現在のクラスの優先度が最高です。
では、スーパークラスとトレイトの優先順位の関係はどうでしょうか?
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
今度はトレイトのメソッドが勝ちました。
したがって優先順位は、現在のクラス > トレイト > スーパークラスとなります。