update : 2015.11.03
php.shukuma.com

검색:
 
 
트레이트

트레이트

PHP 5.4.0 부터, 트레이트 라는 코드 재사용 방법을 구현했습니다.

트레이트는 PHP와 같은 단일상속 언어를 위한 코드 재사용 기법입니다. 트레이트는 단일상속의 몇가지 한계를 줄이기 위해 고안되었습니다. 개발자들은 서로 다른 클래스 계층구조를 가지는 독립 클래스들에서 자유롭게 메서드셋을 재사용할수 있습니다. 트레이트와 클래스의 조합에 대한 의의는 복잡성을 줄이고, 다중상속과 믹신(Mixin)과 연관된 통상적인 문제를 줄이는데 있습니다.

트레이트는 클래스와 비슷하지만, 매끄럽고 일관된 방법으로 기능을 그룹화하기 위해 고안되었습니다. 트레이트는 자신을 인스턴스화할 수 없습니다. 트레이트는 기존의 상속을 확장하고, 수평적으로 동작을 내재화 할수 있도록 합니다. 이말은, 상속없이 클래스 멤버에 추가할수 있음을 의미합니다.

Example #1 트레이트 예제

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

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

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

우선순위

기본 클래스로부터 상속된 멤버는 트레이트의 의해 추가된 멤버에 의한 방법이 우선 됩니다. 우선순위는 현재 클래스의 멤버를 트레이트의 메서드로 재정의하고, 그 다음이 상속된 메서드를 재정의하는 순서입니다.

Example #2 우선순위 예제

기본 클래스로부터 상속된 메서드는 MyHelloWorld 에 추가된 SayWorld 트레이트의 메서드의해 재정의됩니다. MyHelloWord 클래스에 메서드가 정의된것과 같이 동작합니다. 우선순위는 현재클래스의 메서드를 트레이트 메서드로 재정의하고, 그 다음으로 기본 클래스의 메서드를 재정의하는 순서입니다.

<?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!

Example #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!

다중 트레이트

다중 트레이트는 콤마로 구분하여 클래스에 삽입될수 있습니다.

Example #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 연산자를 사용하면 충돌되는 메서드를 다른 이름을 사용하여 포함할수 있습니다.

Example #5 충돌 해결 예제

이 예제에서는, Talker 는 틀레이트 A와 B를 사용합니다. A와 B의 메서드가 충돌하지만, 트레이트 B의 smallTalk와 트레이트 A의 bigTalk의 사용하도록 정의 합니다.

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;
    }
}
?>

메서드 가시성 변경

as 구문을 사용하여, 가시성또한 조정할 수 있습니다.

Example #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; }
}
?>

트레이트로 조합된 트레이트

클래스들이 트레이트를 사용할수 있는것처럼 트레이트도 트레이트를 사용할 수 있습니다. 하나또는 여러 트레이트를 하나의 트레이트로 정의하기 위해서, 부분적 혹은 전체적으로 조합할수 있습니다.

Example #7 Traits Composed from Traits

<?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!

추상 트레이트 멤버

트레이트는 클래스의 요구사항을 강요하기위해 추상 메서드의 사용을 지원합니다.

Example #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;
    }
}
?>

정적 트레이트 멤버

트레이트는 정적 멤버와 정적 메서드로 정의 가능합니다.

Example #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(); // echo 1
$p = new C2(); $p->inc(); // echo 1
?>

Example #10 정적 메서드

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

class 
Example {
    use 
StaticExample;
}

Example::doSomething();
?>

프로퍼티

트레이트는 프로퍼티를 정의할 수 있습니다.

Example #11 프로퍼티 정의

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

class 
PropertiesExample {
    use 
PropertiesTrait;
}

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

트레이트가 프로퍼티를 정의하면 클래스는 같은 이름으로 프로퍼티를 정의할수 없습니다. 그렇지 않을 경우 에러가 발생합니다. E_STRICT 는 클래스 정의가 동일할때(같은 가시성과 초기값을 가진경우) 발생하며, 그렇지 않을경우에는 치명적인 에러가 발생합니다.

Example #12 충돌 해결

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

class 
PropertiesExample {
    use 
PropertiesTrait;
    public 
$same true// Strict Standards
    
public $different true// Fatal error
}
?>