objects namespaces and application architecture

Traits

A trait is a way to reuse methods inside classes without inheritance. A class imports a trait with use, and the trait's methods become part of the class.

Traits are useful for small, focused behaviour shared by unrelated classes. They become risky when they hide dependencies, add lots of state, or turn into a substitute for proper composition.

Use A Trait For Small Shared Behaviour

PHP example
<?php

declare(strict_types=1);

trait FormatsMoney
{
    public function formatPennies(int $pennies): string
    {
        return '£' . number_format($pennies / 100, 2);
    }
}

class InvoicePresenter
{
    use FormatsMoney;
}

$presenter = new InvoicePresenter();

echo $presenter->formatPennies(2499) . PHP_EOL;

// Prints:
// £24.99

The trait is small and has no hidden state, so it is easy to understand.

Traits Are Copied Into The Class Shape

When a class uses a trait, calling code just sees a method on the class.

PHP example
<?php

declare(strict_types=1);

trait HasReference
{
    public function referenceFromId(int $id): string
    {
        return 'REF-' . str_pad((string) $id, 6, '0', STR_PAD_LEFT);
    }
}

class OrderPresenter
{
    use HasReference;
}

echo (new OrderPresenter())->referenceFromId(42) . PHP_EOL;

// Prints:
// REF-000042

The object does not expose where the method came from. That is convenient, but it can make large classes harder to trace.

Traits Can Require Methods

A trait can call methods that the using class must provide.

PHP example
<?php

declare(strict_types=1);

trait HasPublicLabel
{
    abstract public function name(): string;

    public function publicLabel(): string
    {
        return strtoupper($this->name());
    }
}

class Product
{
    use HasPublicLabel;

    public function name(): string
    {
        return 'Keyboard';
    }
}

echo (new Product())->publicLabel() . PHP_EOL;

// Prints:
// KEYBOARD

This can be useful, but it creates a hidden contract. Keep it obvious and documented through names.

Resolve Method Conflicts Explicitly

If two traits provide the same method, PHP needs to know which one to use.

PHP example
<?php

declare(strict_types=1);

trait ShortLabel
{
    public function label(): string
    {
        return 'Short';
    }
}

trait LongLabel
{
    public function label(): string
    {
        return 'Long label';
    }
}

class LabelPresenter
{
    use ShortLabel;
    use LongLabel {
        LongLabel::label insteadof ShortLabel;
    }
}

echo (new LabelPresenter())->label() . PHP_EOL;

// Prints:
// Long label

Conflicts are a sign to check whether the class is gathering too much unrelated behaviour.

Prefer Composition For Real Services

If the shared behaviour needs configuration, a database, a logger, a clock, or an API client, prefer a normal object dependency.

PHP example
<?php

declare(strict_types=1);

final class MoneyFormatter
{
    public function formatPennies(int $pennies): string
    {
        return '£' . number_format($pennies / 100, 2);
    }
}

final class ReceiptPresenter
{
    public function __construct(private MoneyFormatter $formatter)
    {
    }

    public function total(int $pennies): string
    {
        return $this->formatter->formatPennies($pennies);
    }
}

echo (new ReceiptPresenter(new MoneyFormatter()))->total(1299) . PHP_EOL;

// Prints:
// £12.99

Composition is easier to test and replace when behaviour has real dependencies.

What To Remember

Traits reuse code horizontally across classes. Keep them small, focused, and low-state. Avoid hiding service dependencies inside traits. When behaviour is important enough to configure, test, or replace, use composition instead.

Practice

Task: Share Formatting Behaviour

Create a small trait for formatting money.

Requirements

  • Use declare(strict_types=1);.
  • Create a FormatsMoney trait.
  • Add a method that formats integer pennies as pounds.
  • Use the trait in an InvoicePresenter class.
  • Use the same trait in a ReceiptPresenter class.
  • Print one formatted value from each presenter.
  • Include the expected output as comments in the same PHP code block.

The trait should stay small and stateless.

Show solution
PHP example
<?php

declare(strict_types=1);

trait FormatsMoney
{
    public function formatPennies(int $pennies): string
    {
        return '£' . number_format($pennies / 100, 2);
    }
}

class InvoicePresenter
{
    use FormatsMoney;
}

class ReceiptPresenter
{
    use FormatsMoney;
}

echo (new InvoicePresenter())->formatPennies(129950) . PHP_EOL;
echo (new ReceiptPresenter())->formatPennies(2499) . PHP_EOL;

// Prints:
// £1,299.50
// £24.99

The trait is small, focused, and stateless. If formatting needed locale configuration or a currency service, a normal injected object would be a better design.