Using AS on a __construct method (and maybe other magic methods) is really, really bad. The problem is that is doesn't throw any errors, at least in 5.4.0. It just sporadically resets the connection. And when I say "sporadically," I mean that arbitrary changes in the preceding code can cause the browser connection to reset or not reset *consistently*, so that subsequent page refreshes will continue to hang, crash, or display perfectly in the same fashion as the first load of the page after a change in the preceding code, but the slightest change in the code can change this state. (I believe it is related to precise memory usage.)
I've spent a good part of the day chasing down this one, and weeping every time commenting or even moving a completely arbitrary section of code would cause the connection to reset. It was just by luck that I decided to comment the
"__construct as primitiveObjectConstruct"
line and then the crashes went away entirely.
My parent trait constructor was very simple, so my fix this time was to copy the functionality into the child __construct. I'm not sure how I'll approach a more complicated parent trait constructor.
トレイト
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 Hello, World;
public function sayExclamationMark() {
echo '!';
}
}
$o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
$o->sayExclamationMark();
?>
上の例の出力は以下となります。
Hello World!
衝突の解決
同じ名前のメンバーを含む複数のトレイトを追加するときには、 衝突を明示的に解決しておかないと fatal エラーが発生します。
同一クラス内での複数のトレイト間の名前の衝突を解決するには、 insteadof 演算子を使って そのうちのひとつを選ばなければなりません。
この方法はひとつのメソッドだけしか使えませんが、 as 演算子を使うと、 衝突するメソッドのいずれかを別の名前で含めることができます。
例5 衝突の解決
この例では、Talker がトレイト A と B を使います。 A と B には同じ名前のメソッドがあるので、 smallTalk はトレイト B を使って bigTalk はトレイト A を使うように定義します。
Aliased_Talker は、as 演算子を使って B の bigTalk の実装に talk というエイリアスを指定して使います。
<?php
trait A {
public function smallTalk() {
echo 'a';
}
public function bigTalk() {
echo 'A';
}
}
trait B {
public function smallTalk() {
echo 'b';
}
public function bigTalk() {
echo 'B';
}
}
class Talker {
use A, B {
B::smallTalk insteadof A;
A::bigTalk insteadof B;
}
}
class Aliased_Talker {
use A, B {
B::smallTalk insteadof A;
A::bigTalk insteadof B;
B::bigTalk as talk;
}
}
?>
メソッドの可視性の変更
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 Hello, World;
}
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;
?>
トレイトでプロパティを定義したときは、クラスでは同じ名前のプロパティを定義できません。
定義しようとすると、エラーが発生します。クラス側での定義がトレイトでの定義と互換性がある
(可視性も初期値も同じ) 場合は E_STRICT、
それ以外の場合は fatal error となります。
例12 衝突の解決
<?php
trait PropertiesTrait {
public $same = true;
public $different = false;
}
class PropertiesExample {
use PropertiesTrait;
public $same = true; // Strict Standards
public $different = true; // Fatal error
}
?>
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;
Traits can not implement interfaces.
(should be obvious, but tested is tested)
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
?>
A (somewhat) practical example of trait usage.
Without traits:
<?php
class Controller {
/* Controller-specific methods defined here. */
}
class AdminController extends Controller {
/* Controller-specific methods inherited from Controller. */
/* Admin-specific methods defined here. */
}
class CrudController extends Controller {
/* Controller-specific methods inherited from Controller. */
/* CRUD-specific methods defined here. */
}
class AdminCrudController extends CrudController {
/* Controller-specific methods inherited from Controller. */
/* CRUD-specific methods inherited from CrudController. */
/* (!!!) Admin-specific methods copied and pasted from AdminController. */
}
?>
With traits:
<?php
class Controller {
/* Controller-specific methods defined here. */
}
class AdminController extends Controller {
/* Controller-specific methods inherited from Controller. */
/* Admin-specific methods defined here. */
}
trait CrudControllerTrait {
/* CRUD-specific methods defined here. */
}
class AdminCrudController extends AdminController {
use CrudControllerTrait;
/* Controller-specific methods inherited from Controller. */
/* Admin-specific methods inherited from AdminController. */
/* CRUD-specific methods defined by CrudControllerTrait. */
}
?>
<?php
trait first_trait
{
function first_function()
{
echo "From First Trait";
}
}
trait second_trait
{
function first_function()
{
echo "From Second Trait";
}
}
class first_class
{
use first_trait, second_trait
{
// This class will now call the method
// first function from first_trait only
first_trait::first_function insteadof second_trait;
// first_function of second_traits can be
// accessed with second_function
second_trait::first_function as second_function;
}
}
$obj = new first_class();
// Output: From First Trait
$obj->first_function();
// Output: From Second Trait
$obj->second_function();
?>
the iranian php programmer
writer: farhad zand
farhad.peb@gmail.com
php_engineer_bk@yahoo.com
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
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.
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.
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
