PHPのお勉強!

PHP TOP

トレイト

PHP 5.4.0 以降では、コードを再利用するための「トレイト」という仕組みが導入されました。

トレイトは、PHP のような単一継承言語でコードを再利用するための仕組みのひとつです。 トレイトは、単一継承の制約を減らすために作られたもので、 いくつかのメソッド群を異なるクラス階層にある独立したクラスで再利用できるようにします。 トレイトとクラスを組み合わせた構文は複雑さを軽減させてくれ、 多重継承や Mixin に関連するありがちな問題を回避することもできます。

トレイトはクラスと似ていますが、トレイトは単にいくつかの機能をまとめるためだけのものです。 トレイト自身のインスタンスを作成することはできません。 昔ながらの継承に機能を加えて、振る舞いを水平方向で構成できるようになります。 つまり、継承しなくてもクラスのメンバーに追加できるようになります。

例1 トレイトの例

<?php
trait ezcReflectionReturnInfo {
    function 
getReturnType() { /*1*/ }
    function 
getReturnDescription() { /*2*/ }
}

class 
ezcReflectionMethod extends ReflectionMethod {
    use 
ezcReflectionReturnInfo;
    
/* ... */
}

class 
ezcReflectionFunction extends ReflectionFunction {
    use 
ezcReflectionReturnInfo;
    
/* ... */
}
?>

優先順位

基底クラスから継承したメンバーよりも、トレイトで追加したメンバーのほうが優先されます。 優先順位は現在のクラスのメンバーが最高で、その次がトレイトのメソッド、 そしてその次にくるのが継承したメソッドとなります。

例2 優先順位の例

基底クラスから継承したメソッドは、MyHelloWorld に SayWorld トレイトから追加されたメソッドで上書きされます。 この挙動は、MyHelloWorld クラスで定義したメソッドでも同じです。 優先順位は現在のクラスのメンバーが最高で、その次がトレイトのメソッド、 そしてその次にくるのが継承したメソッドとなります。

<?php
class Base {
    public function 
sayHello() {
        echo 
'Hello ';
    }
}

trait 
SayWorld {
    public function 
sayHello() {
        
parent::sayHello();
        echo 
'World!';
    }
}

class 
MyHelloWorld extends Base {
    use 
SayWorld;
}

$o = new MyHelloWorld();
$o->sayHello();
?>

上の例の出力は以下となります。

Hello World!

例3 もうひとつの優先順位の例

<?php
trait HelloWorld {
    public function 
sayHello() {
        echo 
'Hello World!';
    }
}

class 
TheWorldIsNotEnough {
    use 
HelloWorld;
    public function 
sayHello() {
        echo 
'Hello Universe!';
    }
}

$o = new TheWorldIsNotEnough();
$o->sayHello();
?>

上の例の出力は以下となります。

Hello Universe!

複数のトレイト

複数のトレイトをひとつのクラスに追加するには、use 文でカンマ区切りで指定します。

例4 複数のトレイトの使用例

<?php
trait Hello {
    public function 
sayHello() {
        echo 
'Hello ';
    }
}

trait 
World {
    public function 
sayWorld() {
        echo 
'World';
    }
}

class 
MyHelloWorld {
    use 
HelloWorld;
    public function 
sayExclamationMark() {
        echo 
'!';
    }
}

$o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
$o->sayExclamationMark();
?>

上の例の出力は以下となります。

Hello World!

衝突の解決

同じ名前のメンバーを含む複数のトレイトを追加するときには、 衝突を明示的に解決しておかないと fatal エラーが発生します。

同一クラス内での複数のトレイト間の名前の衝突を解決するには、 insteadof 演算子を使って そのうちのひとつを選ばなければなりません。

この方法はひとつのメソッドだけしか使えませんが、 as 演算子を使うと、 メソッドのいずれかにエイリアスを追加できます。 as 演算子はメソッドをリネームするわけではないので、 その他のメソッドにも何も影響を及ぼさないことに注意しましょう。

例5 衝突の解決

この例では、Talker がトレイト A と B を使います。 A と B には同じ名前のメソッドがあるので、 smallTalk はトレイト B を使って bigTalk はトレイト A を使うように定義します。

Aliased_Talker は、as 演算子を使って B の bigTalk の実装に talk というエイリアスを指定して使います。

<?php
trait {
    public function 
smallTalk() {
        echo 
'a';
    }
    public function 
bigTalk() {
        echo 
'A';
    }
}

trait 
{
    public function 
smallTalk() {
        echo 
'b';
    }
    public function 
bigTalk() {
        echo 
'B';
    }
}

class 
Talker {
    use 
A{
        
B::smallTalk insteadof A;
        
A::bigTalk insteadof B;
    }
}

class 
Aliased_Talker {
    use 
A{
        
B::smallTalk insteadof A;
        
A::bigTalk insteadof B;
        
B::bigTalk as talk;
    }
}
?>

注意:

PHP 7.0 より前のバージョンでは、 トレイトに存在する名前と同じ名前をクラスのプロパティに定義すると、 クラス定義に互換性(公開範囲と初期値が同じ)があっても、 E_STRICT レベルの警告が発生していました。

メソッドの可視性の変更

as 構文を使うと、 クラス内でのメソッドの可視性も変更することができます。

例6 メソッドの可視性の変更

<?php
trait HelloWorld {
    public function 
sayHello() {
        echo 
'Hello World!';
    }
}

// sayHello の可視性を変更します
class MyClass1 {
    use 
HelloWorld sayHello as protected; }
}

// 可視性を変更したエイリアスメソッドを作ります
// sayHello 自体の可視性は変わりません
class MyClass2 {
    use 
HelloWorld sayHello as private myPrivateHello; }
}
?>

トレイトを組み合わせたトレイト

クラスからトレイトを使えるのと同様に、トレイトからもトレイトを使えます。 トレイトの定義の中でトレイトを使うと、 定義したトレイトのメンバーの全体あるいは一部を組み合わせることができます。

例7 トレイトを組み合わせたトレイト

<?php
trait Hello {
    public function 
sayHello() {
        echo 
'Hello ';
    }
}

trait 
World {
    public function 
sayWorld() {
        echo 
'World!';
    }
}

trait 
HelloWorld {
    use 
HelloWorld;
}

class 
MyHelloWorld {
    use 
HelloWorld;
}

$o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
?>

上の例の出力は以下となります。

Hello World!

トレイトのメンバーの抽象化

トレイトでは、抽象メソッドを使ってクラスの要件を指定できます。

警告

具象クラスでは、 具象メソッドを同じ名前で定義することによって、要件を満たせます。 つまり、シグナチャは異なっていても構いません。

例8 抽象メソッドによる、要件の明示

<?php
trait Hello {
    public function 
sayHelloWorld() {
        echo 
'Hello'.$this->getWorld();
    }
    abstract public function 
getWorld();
}

class 
MyHelloWorld {
    private 
$world;
    use 
Hello;
    public function 
getWorld() {
        return 
$this->world;
    }
    public function 
setWorld($val) {
        
$this->world $val;
    }
}
?>

トレイトの静的なメンバー

トレイトでは、静的なメンバーやメソッドを定義できます。

例9 静的な変数

<?php
trait Counter {
    public function 
inc() {
        static 
$c 0;
        
$c $c 1;
        echo 
"$c\n";
    }
}

class 
C1 {
    use 
Counter;
}

class 
C2 {
    use 
Counter;
}

$o = new C1(); $o->inc(); // 1 と表示
$p = new C2(); $p->inc(); // 1 と表示
?>

例10 静的なメソッド

<?php
trait StaticExample {
    public static function 
doSomething() {
        return 
'Doing something';
    }
}

class 
Example {
    use 
StaticExample;
}

Example::doSomething();
?>

プロパティ

トレイトにはプロパティも定義できます。

例11 プロパティの定義

<?php
trait PropertiesTrait {
    public 
$x 1;
}

class 
PropertiesExample {
    use 
PropertiesTrait;
}

$example = new PropertiesExample;
$example->x;
?>

トレイトでプロパティを定義したときは、 クラスではそれと互換性(公開範囲と初期値が同じ)がない同じ名前のプロパティを定義できません。 互換性がない名前を定義すると、致命的なエラーが発生します。 PHP 7.0.0 より前のバージョンでは、 トレイトに存在する名前と同じ名前をクラスのプロパティに定義すると、 クラス定義に互換性(公開範囲と初期値が同じ)があっても、 E_STRICT レベルの警告が発生していました。

例12 衝突の解決

<?php
trait PropertiesTrait {
    public 
$same true;
    public 
$different false;
}

class 
PropertiesExample {
    use 
PropertiesTrait;
    public 
$same true// Allowed as of PHP 7.0.0; E_STRICT notice formerly
    
public $different true// Fatal error
}
?>
add a note add a note

User Contributed Notes 37 notes

up
532
Safak Ozpinar / safakozpinar at gmail
8 years ago
Unlike inheritance; if a trait has static properties, each class using that trait has independent instances of those properties.

Example using parent class:
<?php
class TestClass {
    public static
$_bar;
}
class
Foo1 extends TestClass { }
class
Foo2 extends TestClass { }
Foo1::$_bar = 'Hello';
Foo2::$_bar = 'World';
echo
Foo1::$_bar . ' ' . Foo2::$_bar; // Prints: World World
?>

Example using trait:
<?php
trait TestTrait {
    public static
$_bar;
}
class
Foo1 {
    use
TestTrait;
}
class
Foo2 {
    use
TestTrait;
}
Foo1::$_bar = 'Hello';
Foo2::$_bar = 'World';
echo
Foo1::$_bar . ' ' . Foo2::$_bar; // Prints: Hello World
?>
up
361
greywire at gmail dot com
8 years ago
The best way to understand what traits are and how to use them is to look at them for what they essentially are:  language assisted copy and paste.

If you can copy and paste the code from one class to another (and we've all done this, even though we try not to because its code duplication) then you have a candidate for a trait.
up
205
Stefan W
6 years ago
Note that the "use" operator for traits (inside a class) and the "use" operator for namespaces (outside the class) resolve names differently. "use" for namespaces always sees its arguments as absolute (starting at the global namespace):

<?php
namespace Foo\Bar;
use
Foo\Test// means \Foo\Test - the initial \ is optional
?>

On the other hand, "use" for traits respects the current namespace:

<?php
namespace Foo\Bar;
class
SomeClass {
    use
Foo\Test;   // means \Foo\Bar\Foo\Test
}
?>

Together with "use" for closures, there are now three different "use" operators. They all mean different things and behave differently.
up
76
chris dot rutledge at gmail dot com
8 years ago
It may be worth noting here that the magic constant __CLASS__ becomes even more magical - __CLASS__ will return the name of the class in which the trait is being used.

for example

<?php
trait sayWhere {
    public function
whereAmI() {
        echo
__CLASS__;
    }
}

class
Hello {
    use
sayWHere;
}

class
World {
    use
sayWHere;
}

$a = new Hello;
$a->whereAmI(); //Hello

$b = new World;
$b->whereAmI(); //World
?>

The magic constant __TRAIT__ will giev you the name of the trait
up
68
atorich at gmail dot com
6 years ago
add to "chris dot rutledge at gmail dot com":
__CLASS__ will return the name of the class in which the trait is being used (!) not the class in which trait method is being called:

<?php
trait TestTrait {
    public function
testMethod() {
        echo
"Class: " . __CLASS__ . PHP_EOL;
        echo
"Trait: " . __TRAIT__ . PHP_EOL;
    }
}

class
BaseClass {
    use
TestTrait;
}

class
TestClass extends BaseClass {

}

$t = new TestClass();
$t->testMethod();

//Class: BaseClass
//Trait: TestTrait
up
44
qeremy (!) gmail
5 years ago
Keep in mind; "final" keyword is useless in traits when directly using them, unlike extending classes / abstract classes.

<?php
trait Foo {
    final public function
hello($s) { print "$s, hello!"; }
}
class
Bar {
    use
Foo;
   
// Overwrite, no error
   
final public function hello($s) { print "hello, $s!"; }
}

abstract class
Foo {
    final public function
hello($s) { print "$s, hello!"; }
}
class
Bar extends Foo {
   
// Fatal error: Cannot override final method Foo::hello() in ..
   
final public function hello($s) { print "hello, $s!"; }
}
?>

But this way will finalize trait methods as expected;

<?php
trait FooTrait {
    final public function
hello($s) { print "$s, hello!"; }
}
abstract class
Foo {
    use
FooTrait;
}
class
Bar extends Foo {
   
// Fatal error: Cannot override final method Foo::hello() in ..
   
final public function hello($s) { print "hello, $s!"; }
}
?>
up
67
t8 at AT pobox dot com
7 years ago
Another difference with traits vs inheritance is that methods defined in traits can access methods and properties of the class they're used in, including private ones.

For example:
<?php
trait MyTrait
{
  protected function
accessVar()
  {
    return
$this->var;
  }

}

class
TraitUser
{
  use
MyTrait;

  private
$var = 'var';

  public function
getVar()
  {
    return
$this->accessVar();
  }
}

$t = new TraitUser();
echo
$t->getVar(); // -> 'var'                                                                                                                                                                                                                         

?>
up
14
marko at newvibrations dot net
3 years ago
As already noted, static properties and methods in trait could be accessed directly using trait. Since trait is language assisted c/p, you should be aware that static property from trait will be initialized to the value trait property had in the time of class declaration.

Example:

<?php

trait Beer {
    protected static
$type = 'Light';
    public static function
printed(){
        echo static::
$type.PHP_EOL;
    }
    public static function
setType($type){
        static::
$type = $type;
    }
}

class
Ale {
    use
Beer;
}

Beer::setType("Dark");

class
Lager {
    use
Beer;
}

Beer::setType("Amber");

header("Content-type: text/plain");

Beer::printed();  // Prints: Amber
Ale::printed();   // Prints: Light
Lager::printed(); // Prints: Dark

?>
up
9
rawsrc
2 years ago
About the (Safak Ozpinar / safakozpinar at gmail)'s great note, you can still have the same behavior than inheritance using trait with this approach :
<?php

trait TestTrait {
    public static
$_bar;
}

class
FooBar {
    use
TestTrait;
}

class
Foo1 extends FooBar {

}
class
Foo2 extends FooBar {

}
Foo1::$_bar = 'Hello';
Foo2::$_bar = 'World';
echo
Foo1::$_bar . ' ' . Foo2::$_bar; // Prints: World World
up
21
canufrank
3 years ago
A number of the notes make incorrect assertions about trait behaviour because they do not extend the class.

So, while "Unlike inheritance; if a trait has static properties, each class using that trait has independent instances of those properties.

Example using parent class:
<?php
class TestClass {
    public static
$_bar;
}
class
Foo1 extends TestClass { }
class
Foo2 extends TestClass { }
Foo1::$_bar = 'Hello';
Foo2::$_bar = 'World';
echo
Foo1::$_bar . ' ' . Foo2::$_bar; // Prints: World World
?>

Example using trait:
<?php
trait TestTrait {
    public static
$_bar;
}
class
Foo1 {
    use
TestTrait;
}
class
Foo2 {
    use
TestTrait;
}
Foo1::$_bar = 'Hello';
Foo2::$_bar = 'World';
echo
Foo1::$_bar . ' ' . Foo2::$_bar; // Prints: Hello World
?>"

shows a correct example, simply adding
<?php
require_once('above');
class
Foo3 extends Foo2 {
}
Foo3::$_bar = 'news';
echo
Foo1::$_bar . ' ' . Foo2::$_bar . ' ' . Foo3::$_bar;

// Prints: Hello news news

I think the best conceptual model of an incorporated trait is an advanced insertion of text, or as someone put it "language assisted copy and paste." If Foo1 and Foo2 were defined with $_bar, you would not expect them to share the instance. Similarly, you would expect Foo3 to share with Foo2, and it does.

Viewing this way explains away a lot of  the 'quirks' that are observed above with final, or subsequently declared private vars,
up
43
karolis at iwsolutions dot ie
7 years ago
Not very obvious but trait methods can be called as if they were defined as static methods in a regular class

<?php
trait Foo {
    function
bar() {
        return
'baz';
    }
}

echo
Foo::bar(),"\\n";
?>
up
7
balbuf
4 years ago
(It's already been said, but for the sake of searching on the word "relative"...)

The "use" keyword to import a trait into a class will resolve relative to the current namespace and therefore should include a leading slash to represent a full path, whereas "use" at the namespace level is always absolute.
up
46
Anonymous
8 years ago
Traits can not implement interfaces.
(should be obvious, but tested is tested)
up
13
Vasyl Sovyak
4 years ago
<?php
trait A
{
    public function
bar()
    {
        echo
'A::bar';
    }
}

trait
B
{
    public function
bar()
    {
        echo
'B::bar';
    }
}

trait
C
{
    public function
bar()
    {
        echo
'C::bar';
    }
}

class
Foo
{
    use
A, B, C {
       
C::bar insteadof A, B;
    }
}

$foo = new Foo();
$foo->bar(); //C::bar
up
22
anthony bishopric
8 years ago
The magic method __call works as expected using traits.

<?php
trait Call_Helper{
   
    public function
__call($name, $args){
        return
count($args);
    }
}

class
Foo{
    use
Call_Helper;
}

$foo = new Foo();
echo
$foo->go(1,2,3,4); // echoes 4
up
42
ryan at derokorian dot com
8 years ago
Simple singleton trait.

<?php

trait singleton {   
   
/**
     * private construct, generally defined by using class
     */
    //private function __construct() {}
   
   
public static function getInstance() {
        static
$_instance = NULL;
       
$class = __CLASS__;
        return
$_instance ?: $_instance = new $class;
    }
   
    public function
__clone() {
       
trigger_error('Cloning '.__CLASS__.' is not allowed.',E_USER_ERROR);
    }
   
    public function
__wakeup() {
       
trigger_error('Unserializing '.__CLASS__.' is not allowed.',E_USER_ERROR);
    }
}

/**
* Example Usage
*/

class foo {
    use
singleton;
   
    private function
__construct() {
       
$this->name = 'foo';
    }
}

class
bar {
    use
singleton;
   
    private function
__construct() {
       
$this->name = 'bar';
    }
}

$foo = foo::getInstance();
echo
$foo->name;

$bar = bar::getInstance();
echo
$bar->name;
up
16
Edward
8 years ago
The difference between Traits and multiple inheritance is in the inheritance part.   A trait is not inherited from, but rather included or mixed-in, thus becoming part of "this class".   Traits also provide a more controlled means of resolving conflicts that inevitably arise when using multiple inheritance in the few languages that support them (C++).  Most modern languages are going the approach of a "traits" or "mixin" style system as opposed to multiple-inheritance, largely due to the ability to control ambiguities if a method is declared in multiple "mixed-in" classes.

Also, one can not "inherit" static member functions in multiple-inheritance.
up
10
qschuler at neosyne dot com
6 years ago
Note that you can omit a method's inclusion by excluding it from one trait in favor of the other and doing the exact same thing in the reverse way.

<?php

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

    public function
sayWorld()
    {
        echo
'World from A';
    }
}

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

    public function
sayWorld()
    {
        echo
'World from B';
    }
}

class
Talker {
    use
A, B {
       
A::sayHello insteadof B;
       
A::sayWorld insteadof B;
       
B::sayWorld insteadof A;
    }
}

$talker = new Talker();
$talker->sayHello();
$talker->sayWorld();

?>

The method sayHello is imported, but the method sayWorld is simply excluded.
up