트레이트
PHP는 Traits라는 코드를 재사용하는 방법을 구현합니다.
Trait는 PHP와 같은 단일 상속 언어에서 코드 재사용을 위한 메커니즘입니다. Trait는 개발자가 다른 클래스 계층 구조에 있는 여러 독립 클래스에서 메서드 집합을 자유롭게 재사용할 수 있도록 하여 단일 상속의 일부 제한을 줄이기 위한 것입니다. Traits와 클래스 조합의 의미는 복잡성을 줄이고 다중 상속 및 Mixin과 관련된 일반적인 문제를 피하는 방식으로 정의됩니다.
Trait는 클래스와 유사하지만 세분화되고 일관된 방식으로 기능을 그룹화하기 위한 용도로만 사용됩니다. Trait 자체를 인스턴스화하는 것은 불가능합니다. 이것은 전통적인 상속에 추가된 것이며 행동의 수평적 구성을 가능하게 합니다. 즉, 상속을 요구하지 않고 클래스 멤버를 적용하는 것입니다.
예제 #1 Trait 예
<?php
trait ezcReflectionReturnInfo {
function getReturnType() { /*1*/ }
function getReturnDescription() { /*2*/ }
}
class ezcReflectionMethod extends ReflectionMethod {
use ezcReflectionReturnInfo;
/* ... */
}
class ezcReflectionFunction extends ReflectionFunction {
use ezcReflectionReturnInfo;
/* ... */
}
?>
우선순위
기본 클래스에서 상속된 멤버는 Trait에 의해 삽입된 멤버에 의해 재정의됩니다. 우선 순위는 현재 클래스의 멤버가 Trait 메서드를 재정의하고 상속된 메서드를 재정의하는 것입니다.
예제 #2 우선 순위 예
기본 클래스에서 상속된 메서드는 SayWorld Trait에서 MyHelloWorld에 삽입된 메서드에 의해 재정의됩니다. 동작은 MyHelloWorld 클래스에 정의된 메서드와 동일합니다. 우선 순위는 현재 클래스의 메서드가 Trait 메서드를 재정의하고 차례로 기본 클래스의 메서드를 재정의한다는 것입니다.
<?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!
Multiple Traits
여러 Traits을 쉼표로 구분하여 use
문에 나열하여 클래스에 삽입할 수 있습니다.
예제 #4 다중 Traits 사용
<?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!
충돌 해결
두 개의 Traits가 같은 이름의 메서드를 삽입하는 경우 충돌이 명시적으로 해결되지 않으면 치명적인 오류가 생성됩니다.
동일한 클래스에서 사용되는 Traits 간의 이름 충돌을 해결하려면 insteadof
연산자로 확실하게 사용할 메서드를 골라줄 필요가 있습니다.
하나의 메서드만 제외할 수 있으므로 as
연산자를 사용하여 메서드 중 하나에 별칭을 추가할 수 있습니다. as
연산자는 메서드의 이름을 바꾸지 않으며 다른 메서드에도 영향을 주지 않습니다.
예제 #5 충돌 해결
이 예에서 Talker는 특성 A와 B를 사용합니다. A와 B는 충돌하는 방법을 사용하므로 trait B의 smallTalk 변종과 trait A의 bigTalk 변종을 사용하도록 정의합니다.
Aliased_Talker는 추가 별칭 토크에서 B의 bigTalk 구현을 사용할 수 있도록 as
연산자를 사용합니다.
<?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 Traits Composed from Traits
<?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!
추상 트레이트 멤버
트레이트는 전시 클래스에 요구 사항을 부과하기 위해 추상 메서드 사용을 지원합니다. 공개(Public), 보호(protected) 및 비공개(private) 메서드가 지원됩니다. PHP 8.0.0 이전에는 공개(public) 및 보호 추상 메소드(protected abstract methods)만 지원되었습니다.
주의 구체 클래스는 동일한 이름으로 구체 메소드를 정의하여 이 요구사항을 충족합니다. 서명이 다를 수 있습니다.
예제 #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(); // echo 1
$p = new C2(); $p->inc(); // echo 1
?>
예제 #10 정적 메서드
<?php
trait StaticExample {
public static function doSomething() {
return 'Doing something';
}
}
class Example {
use StaticExample;
}
Example::doSomething();
?>
예제 #11 정적 프로퍼티
<?php
trait StaticExample {
public static $static = 'foo';
}
class Example {
use StaticExample;
}
echo Example::$static;
?>
프로퍼티
트레이트는 프로퍼티를 정의할 수 있습니다.
예제 #12 프로퍼티 정의
<?php
trait PropertiesTrait {
public $x = 1;
}
class PropertiesExample {
use PropertiesTrait;
}
$example = new PropertiesExample;
$example->x;
?>
트레이트가 프로퍼티를 정의하는 경우 클래스는 호환되지 않는 한 동일한 이름의 속성을 정의할 수 없습니다(동일한 가시성 및 초기 값). 그렇지 않으면 치명적인 오류가 발생합니다.
예제 #13 충돌 해결
<?php
trait PropertiesTrait {
public $same = true;
public $different = false;
}
class PropertiesExample {
use PropertiesTrait;
public $same = true;
public $different = true; // Fatal error
}
?>