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