php

[PHP] Traits

About PHP Traits

Posted by JongMin-Lee on February 17, 2022

코드의 재사용은 OOP에 있어 중요한 부분중 하나 입니다. PHP에서는 상속(inheritance) 을 통해 이를 해결할 수 있습니다. 그러나 PHP의 상속 모델은 단일 상속으로 자식클래스는 하나의 부모 클래스만 상속 받을 수 있습니다. 그러므로 여러 클래스에서 상속 받는 것과 비슷한 효과를 내기 위해서는 클래스가 아닌 인터페이스로 선언하여 이를 구현하고 부모 클래스를 상속 받아야 합니다. 이 경우 같은 레벨의 클래스 마다 인터페이스를 구현해야 하는 중복된 코드가 발생하게 됩니다. 또한 상속은 결합도가 높아 상속의 남용은 코드를 유지 보수 하는데 있어 매우 힘든 상황이 생길 수 있습니다. 이를 해결하기 위해 PHP5.4에 등장한 것이 trait 입니다.

trait은 클래스와 유사해 보이지만 함수들을 세분화되고(fine-grained) 그룹화하기 위한 것이 목적이며, 클래스와 다르게 인스턴스라는 개념이 없습니다.

Fine-Grained
하나의 작업을 작은 단위의 프로세스로 나눈 뒤, 다수의 호출을 통해 작업 결과를 생성해내는 방식
Coarse-Grained
하나의 작업을 큰 프로세스로 나눈 뒤, Single Call을 통해 작업 결과를 생성해내는 방식

trait은 함수와 추상함수(abstract method)를 가질 수 있으며 여러 클래스에서 사용가능 합니다. 또한 접근지정자(public, private, protected)를 모두 사용할 수 있습니다.


PHP Traits

Trait은 trait키워드를 사용하여 선언할 수 있습니다.

1
2
3
4
5
6
7
8
9
<?php

trait Message
{
    public function msg($msg)
    {
        echo "message: $msg";
    }
}

trait을 클래스에서 사용하기 위해서는 use키워드를 사용해야 합니다. 함수를 호출하는 방식은 인스턴스 함수 호출과 비슷합니다.

use키워드는 두가지로 사용됩니다. 클래스에서의 trait사용, namespace의 alias지정에서 사용됩니다.

1
2
3
4
5
6
7
8
9
10
11
<?php

class Welcome
{
    use Message;

    public function __construct()
    {
        $this->msg("Hello world!!");
    }
}


Multiple Traits

클래스는 여러개의 trait을 사용할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php

trait Hello
{
    public function hello()
    {
        echo "Hello ";
    }
}

trait World
{
    public function world()
    {
        echo "world!!";
    }
}

class Welcome
{
    use Hello, World;
}

$welcome = new Welcome();
$welcome->hello();
$welcome->world();
// Hello world!!


Composing Multiple Traits

traituse키워드를 사용해서 합칠 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?php

trait Hello
{
    public function hello()
    {
        echo "Hello ";
    }
}

trait World
{
    public function world()
    {
        echo "world!!";
    }
}

trait Message
{
    use Hello, World;

    public function message()
    {
        $this->hello();
        $this->world();
    }
}

class Welcome
{
    use Message;
}

$welcome = new Welcome();
$welcome->message();
// Hello world!!

HelloWorld trait을 선언한 후 use키워드를 사용하여 Message trait에서 합치게 됩니다.
Welcome 클래스에서 Message trait의 함수를 호출하게 되면 두 trait이 합쳐진 결과를 볼 수 있게 됩니다.


Trait method conlict resolution

Overriding Trait Method

클래스 내에서 여러 trait을 사용하게 될 때 이름이 같다면 fetal error를 발생시키게 됩니다. 이는 insteadof키워드를 사용하여 해결할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php

trait Hello
{
    public welcome()
    {
        echo "Hello world!!";
    }
}

trait World
{
    public welcome()
    {
        echo "Welcome!!";
    }
}

class Welcome
{
    use Hello, World{
        Hello::welcome insteadof World;
    }
}

$welcome = new Welcome();
$welcome->welcome();
// Hello world!!

HelloWorld trait모두 welcome 이름의 함수를 갖고 있습니다. 이 경우 inteadof 키워드를 사용하여 어떠한 trait의 함수를 사용할지 선언해 주어야 합니다. 위의 경우 Hello trait의 함수를 사용하여 Hello world!!가 출력되게 됩니다. 만약 Welcome의 함수도 사용하고 싶다면 alias를 지정해주면 됩니다.


Aliasing Trait Method

aliasas키워드를 사용하여 지정해 줄 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php

trait Hello
{
    public welcome()
    {
        echo "Hello world!!";
    }
}

trait World
{
    public welcome()
    {
        echo "Welcome!!";
    }
}

class Welcome
{
    use Hello, World{
        World::welcome as secondWelcome;
        Hello::welcome insteadof World;
    }
}

$welcome = new Welcome();
$welcome->welcome(); // Hello world!!
$welcome->secondWelcome(); // Welcome!!


Reference

  • https://www.lesstif.com/laravelprog/trait-26083451.html
  • https://www.php.net/manual/en/language.oop5.traits.php
  • https://www.w3schools.com/php/php_oop_traits.asp
  • https://www.phptutorial.net/php-oop/php-traits/