Skip to main content

Traits

Chapter Overview​

In this chapter you will learn:

  • What traits are
  • Why traits exist
  • Multiple inheritance problem
  • Creating traits
  • Using traits
  • Multiple traits
  • Method conflicts
  • Aliasing methods
  • Trait visibility
  • Trait properties
  • Trait constants
  • PocketMine examples
  • Enterprise examples
  • Best practices
  • Exercises and projects

What is a Trait?

A Trait is a mechanism for:

Reusing code in multiple classes.

Traits allow developers to share methods between unrelated classes.


Definition

Traits are:

Reusable pieces of code
that can be inserted into classes.

Why Traits Exist

PHP does NOT support:

Multiple Inheritance

Invalid:

class Player
extends Human, Logger {

}

This would cause:

Fatal Error

PHP only allows:

One Parent Class

The Problem

Imagine:

Player

needs:

  • Logging
  • Configuration
  • Serialization
  • Permissions

Without traits:

You would duplicate code everywhere.


Example Without Traits

class Player {

public function log() {

}

}

class Server {

public function log() {

}

}

class Database {

public function log() {

}

}

Huge duplication.


Solution: Traits


Creating Trait

trait LoggerTrait {

public function log(
string $message
): void {

echo $message;

}

}

Using Trait

class Player {

use LoggerTrait;

}

Usage:

$player =
new Player();

$player->log(
"Hello"
);

Visualization

LoggerTrait
↓
┌────────────┐
│ Player │
│ Server │
│ Database │
└────────────┘

How Traits Work Internally

PHP basically copies trait methods into the class.

Example:

class Player {

use LoggerTrait;

}

Internally behaves like:

class Player {

public function log() {

}

}

Traits Are NOT Inheritance

Traits are:

Code Inclusion

NOT:

Parent → Child Relationship

Traits vs Inheritance


Inheritance​

IS-A Relationship

Example:

Player IS A Human

Traits​

HAS-A Behavior

Example:

Player HAS Logging Ability

Multiple Traits

PHP allows:

class Player {

use
LoggerTrait,
ConfigTrait;

}

Example:

trait LoggerTrait {

public function log() {

}

}

trait ConfigTrait {

public function saveConfig() {

}

}

Usage:

$player->log();

$player->saveConfig();

Visualization

Player
│
├── LoggerTrait
└── ConfigTrait

Method Conflicts

What happens if:

trait A {

public function hello() {

}

}

trait B {

public function hello() {

}

}

Usage:

class Test {

use A, B;

}

Error:

Trait method conflict.

Solving Conflicts

Use:

insteadof

Example:

class Test {

use A, B {

A::hello
insteadof B;

}

}

Now:

A::hello()

is used.


Aliasing Methods

You can rename methods.


Example:

class Test {

use A {

hello as sayHello;

}

}

Usage:

$test->sayHello();

Combination Example

class Test {

use A, B {

A::hello
insteadof B;

B::hello
as helloFromB;

}

}

Now both methods are available.


Trait Properties

Traits can contain properties.


Example:

trait CounterTrait {

protected int $count = 0;

}

Usage:

class Test {

use CounterTrait;

}

Trait Methods

Traits may contain:

✅ Methods

✅ Properties

✅ Static Methods

✅ Constants


Trait Constants

PHP 8.2+

Example:

trait LoggerTrait {

public const VERSION = 1;

}

Static Methods

trait HelperTrait {

public static function hello() {

}

}

Usage:

Player::hello();

Private Methods

Traits may contain:

private
protected
public

methods.


Example

trait LoggerTrait {

private function format(
string $message
) {

}

}

Trait Constructor

Traits may define constructors.

Example:

trait InitTrait {

public function __construct() {

}

}

However, be careful.

Multiple traits may cause conflicts.


Traits Inside Traits

Traits may use other traits.


Example:

trait LoggerTrait {

}

trait DatabaseTrait {

use LoggerTrait;

}

Visualization

LoggerTrait
↓
DatabaseTrait
↓
Player

Real World Example


Logger Trait

trait LoggerTrait {

public function log(
string $message
) {

echo
"[" .
date("H:i:s")
. "] "
. $message;

}

}

Timestamp Trait

trait TimestampTrait {

protected int $createdAt;

}

UUID Trait

trait UUIDTrait {

protected string $uuid;

}

User Class

class User {

use
LoggerTrait,
TimestampTrait,
UUIDTrait;

}

Enterprise Example

Laravel uses traits heavily.

Examples:

HasFactory
SoftDeletes
Notifiable
HasApiTokens

Example

class User
extends Model {

use Notifiable;

}

Instant notification support.


PocketMine Examples

PocketMine heavily uses traits.


SingletonTrait

use SingletonTrait;

Provides:

self::getInstance()

LegacyEnumShimTrait

Used internally for compatibility.


StringToItemParserTrait

Provides parser utilities.


Why?

Because traits allow:

Shared Functionality
without
deep inheritance chains.

Example Plugin

class Main
extends PluginBase {

use SingletonTrait;

}

Usage:

Main::getInstance();

Traits and SOLID

Traits help reduce:

Code Duplication

But can violate:

Single Responsibility Principle

if abused.


Bad Example

MegaTrait

containing:

Database
Networking
Logging
Permissions
API

Terrible design.


Good Example

Small traits:

LoggerTrait
UUIDTrait
ConfigTrait

Traits vs Interfaces


Traits​

Provide:

Implementation

Interfaces​

Provide:

Contracts

Comparison Table

FeatureTraitInterface
Methods✅❌
Properties✅❌
Constructors✅❌
Multiple Usage✅✅
Implementation✅❌

Traits vs Abstract Classes

FeatureTraitAbstract
Inheritance Required❌✅
Multiple Usage✅❌
State✅✅
RelationshipCode ReuseIS-A

When To Use Traits

Use traits when:

✅ Multiple classes need same code.

✅ Classes are unrelated.

✅ You want reusable behavior.


When NOT To Use Traits

Avoid traits when:

❌ Classes share true inheritance.

❌ Trait becomes too large.

❌ State becomes complicated.


Common Beginner Mistakes


Using Traits Everywhere​

Traits are useful.

But too many traits become messy.


Giant Traits​

Bad:

UtilityTrait

containing 200 methods.


Hidden Dependencies​

Traits should remain independent.


Best Practices

✅ Keep traits small.

✅ One responsibility per trait.

✅ Avoid huge traits.

✅ Use interfaces with traits.


Example Architecture

Player
│
├── LoggerTrait
├── UUIDTrait
└── ConfigTrait

Exercises


Exercise 1​

Create:

LoggerTrait

Exercise 2​

Create:

TimestampTrait

Exercise 3​

Create:

UUIDTrait

Mini Project

Create:

Zyro User System

Classes:

User
Admin
Moderator
Developer

Traits:

LoggerTrait
NotificationTrait
UUIDTrait
TimestampTrait

Interview Questions


What problem do traits solve?​

Multiple inheritance problem.


Can traits have properties?​

Yes.


Can traits have constructors?​

Yes.


Difference between trait and interface?​

Trait provides implementation.

Interface provides contracts.


Difference between trait and inheritance?​

Traits reuse code.

Inheritance models relationships.


Summary

Traits provide:

✅ Code Reuse

✅ Multiple Behaviors

✅ Cleaner Architecture

✅ Less Duplication

✅ Better Framework Design


References

https://www.php.net/manual/en/language.oop5.traits.php


Next Chapter

➡ Static Members

➡ Final Keyword

➡ Magic Methods

➡ Enums