PHP Traits

Traits are a mechanism for code reuse in PHP that solves a specific problem: PHP only allows a class to inherit from one parent class, but sometimes unrelated classes need to share the same piece of functionality. Traits allow methods to be "mixed into" any class, regardless of the class hierarchy. Think of a trait as a reusable set of methods that can be dropped into any class that needs them.

Defining and Using a Trait

<?php
  trait Timestampable {
    private ?string $createdAt = null;
    private ?string $updatedAt = null;

    public function setCreatedAt(): void {
      $this->createdAt = date("Y-m-d H:i:s");
    }

    public function setUpdatedAt(): void {
      $this->updatedAt = date("Y-m-d H:i:s");
    }

    public function getCreatedAt(): ?string {
      return $this->createdAt;
    }

    public function getUpdatedAt(): ?string {
      return $this->updatedAt;
    }
  }
?>
<?php
  class Article {
    use Timestampable;   // Include the trait

    public string $title;

    public function __construct(string $title) {
      $this->title = $title;
      $this->setCreatedAt();
    }
  }

  class Comment {
    use Timestampable;   // Same trait, different class

    public string $body;

    public function __construct(string $body) {
      $this->body = $body;
      $this->setCreatedAt();
    }
  }

  $article = new Article("PHP Traits Explained");
  $comment = new Comment("Great article!");

  echo $article->getCreatedAt();   // 2026-03-17 14:30:00
  echo $comment->getCreatedAt();   // 2026-03-17 14:30:00
?>

Article and Comment are unrelated classes, but both gain the timestamp functionality from the shared trait without any code duplication.

Using Multiple Traits

A class can use multiple traits by listing them separated by commas inside the use statement.

<?php
  trait Loggable {
    private array $logs = [];

    public function log(string $message): void {
      $this->logs[] = date("H:i:s") . " - " . $message;
    }

    public function getLogs(): array {
      return $this->logs;
    }
  }

  trait Serializable {
    public function toArray(): array {
      return get_object_vars($this);
    }

    public function toJson(): string {
      return json_encode($this->toArray());
    }
  }

  class UserActivity {
    use Loggable, Serializable;   // Using two traits

    public string $username;
    public int $loginCount = 0;

    public function __construct(string $username) {
      $this->username = $username;
    }

    public function login(): void {
      $this->loginCount++;
      $this->log($this->username . " logged in.");
    }
  }

  $activity = new UserActivity("alice");
  $activity->login();
  $activity->login();

  print_r($activity->getLogs());
  // Array ( [0] => 14:30:00 - alice logged in. [1] => 14:30:01 - alice logged in. )

  echo $activity->toJson();
?>

Resolving Trait Conflicts

When two traits define a method with the same name, a conflict arises. PHP requires resolving this explicitly using the insteadof and as operators inside the use block.

<?php
  trait TextFormatter {
    public function format(string $text): string {
      return strtoupper($text);
    }
  }

  trait HtmlFormatter {
    public function format(string $text): string {
      return "<strong>" . $text . "</strong>";
    }
  }

  class Document {
    use TextFormatter, HtmlFormatter {
      TextFormatter::format insteadof HtmlFormatter;   // Use TextFormatter's format()
      HtmlFormatter::format as htmlFormat;              // Also keep HtmlFormatter's as htmlFormat()
    }
  }

  $doc = new Document();
  echo $doc->format("hello");       // HELLO (from TextFormatter)
  echo $doc->htmlFormat("hello");   // <strong>hello</strong> (from HtmlFormatter)
?>

Changing Method Visibility in Traits

The as keyword can also change the visibility of a trait method in the class using it.

<?php
  trait Debuggable {
    public function debugInfo(): string {
      return "Debug: " . get_class($this);
    }
  }

  class App {
    use Debuggable {
      debugInfo as private;   // Make the method private in this class
    }

    public function run(): void {
      echo $this->debugInfo();   // Still accessible inside the class
    }
  }

  $app = new App();
  $app->run();        // Debug: App
  // $app->debugInfo();  // Fatal error - method is private
?>

Abstract Methods in Traits

A trait can declare abstract methods, forcing any class that uses the trait to implement them.

<?php
  trait Validatable {
    abstract protected function validate(): bool;

    public function save(): void {
      if ($this->validate()) {
        echo "Data is valid. Saving...";
      } else {
        echo "Validation failed. Not saved.";
      }
    }
  }

  class RegistrationForm {
    use Validatable;

    public string $email;
    public string $password;

    public function __construct(string $email, string $password) {
      $this->email = $email;
      $this->password = $password;
    }

    protected function validate(): bool {
      return filter_var($this->email, FILTER_VALIDATE_EMAIL)
             && strlen($this->password) >= 8;
    }
  }

  $form = new RegistrationForm("user@example.com", "password123");
  $form->save();   // Data is valid. Saving...
?>

Key Points

  • Traits are reusable groups of methods that can be mixed into any class using the use keyword.
  • A class can use multiple traits simultaneously.
  • Traits solve the limitation of single inheritance by allowing code sharing between unrelated class hierarchies.
  • When two used traits define the same method, resolve the conflict with insteadof and keep the other with as.
  • The as operator can also change a trait method's visibility inside the using class.
  • Traits can declare abstract methods, requiring the using class to provide implementations.
  • Traits are not classes — they cannot be instantiated directly.

Leave a Comment

Your email address will not be published. Required fields are marked *