Trait trong PHP là một cơ chế cho phép tái sử dụng code giữa nhiều class mà không cần dùng kế thừa (inheritance). Nó giống như một “bộ phương thức dùng chung” có thể được nhúng vào nhiều class khác nhau.

Trait được giới thiệu từ PHP 5.4 để giải quyết vấn đề PHP không hỗ trợ multiple inheritance (kế thừa nhiều lớp).

Nói đơn giản

Trait = một tập hợp method có thể import vào class bằng từ khóa use.


Vì sao PHP cần Trait

Trong OOP truyền thống của PHP

Một class chỉ được kế thừa 1 class cha

class A {}
class B extends A {}

Nhưng nếu bạn muốn dùng lại logic từ nhiều nơi khác nhau thì sẽ gặp vấn đề.

Ví dụ

  • Class Post
  • Class User
  • Class Product

Cả 3 đều cần chức năng:

  • logging
  • timestamp
  • cache

Bạn không thể viết

class Post extends Logger, Timestamp

PHP không hỗ trợ multiple inheritance.

Trait ra đời để giải quyết vấn đề này.


Cách sử dụng Trait

Khai báo Trait

trait LoggerTrait
{
    public function log($message)
    {
        echo "[LOG] " . $message;
    }
}

Trait giống class nhưng dùng từ khóa trait.


Sử dụng Trait trong Class

class User
{
    use LoggerTrait;
}$user = new User();
$user->log("User created");

Kết quả

[LOG] User created

Trait được copy method vào class tại compile time.


Trait hoạt động như thế nào

Hiểu đơn giản:

Khi PHP compile code

class User {
   use LoggerTrait;
}

PHP sẽ inject code của trait vào class.

Giống như viết thẳng trong class

class User
{
    public function log($message)
    {
        echo "[LOG] " . $message;
    }
}

Vì vậy trait không phải object riêng.


Một class có thể dùng nhiều Trait

trait LoggerTrait
{
    public function log($msg)
    {
        echo $msg;
    }
}trait TimeTrait
{
    public function now()
    {
        return date("Y-m-d H:i:s");
    }
}class Post
{
    use LoggerTrait, TimeTrait;
}

Sử dụng

$post = new Post();echo $post->now();
$post->log("created");

Trait có thể chứa

Trait có thể chứa

Method

trait A {
   public function test() {}
}

Property

trait A {
   public $name;
}

Static method

trait A {
   public static function hello() {}
}

Trait Conflict (xung đột)

Nếu 2 trait có cùng method.

trait A {
   public function hello() {}
}trait B {
   public function hello() {}
}

Class

class Test {
   use A, B;
}

PHP sẽ lỗi

Trait method hello has not been applied

Giải quyết Conflict

dùng insteadof

class Test
{
    use A, B {
        A::hello insteadof B;
    }
}

Nghĩa là

dùng hello của A thay vì B

Alias method

class Test
{
    use A, B {
        A::hello insteadof B;
        B::hello as helloB;
    }
}

Lúc này

$test->hello();   // từ A
$test->helloB();  // từ B

Trait vs Abstract class vs Interface

FeatureTraitAbstract ClassInterface
Có code
Có property
Multiple use
Kế thừa

Ví dụ thực tế (Laravel hay dùng)

Ví dụ trait SoftDeletes

trait SoftDeletes
{
    public function delete()
    {
        $this->deleted_at = now();
    }
}

Model

class Post extends Model
{
    use SoftDeletes;
}

Khi nào nên dùng Trait

Trait phù hợp khi

1 chia sẻ logic giữa nhiều class

Ví dụ

  • Logging
  • Cache
  • Upload
  • Slug

2 tránh duplicate code

Ví dụ

User
Admin
Customer

Cùng cần

generateSlug()

3 mixin behavior

Giống kiểu

class User
   + Notifiable
   + Loggable
   + Sluggable

Ví dụ thực tế dễ hiểu

trait SlugTrait
{
    public function slug($text)
    {
        return strtolower(str_replace(" ", "-", $text));
    }
}class Post
{
    use SlugTrait;
}$post = new Post();echo $post->slug("Hello World");

Output

hello-world

Tổng kết

Trait là cơ chế tái sử dụng code trong PHP cho phép một class dùng nhiều nhóm method mà không cần kế thừa nhiều class.

Hiểu ngắn gọn

Trait = reusable methods

Class chỉ cần

use TraitName;

Nếu bạn chuẩn bị phỏng vấn PHP Senior, tôi có thể giải thích thêm những thứ rất hay bị hỏi về Trait như