objects namespaces and application architecture
Composition
Composition means building objects out of other objects. Instead of inheriting behaviour from a parent class, a class receives or creates collaborators and asks them to do focused work.
Composition is often easier to change than inheritance because each object keeps a clear responsibility. In PHP applications, services, controllers, command handlers, validators, formatters, repositories, mailers, and clients are commonly composed from smaller objects.
Compose A Class From A Helper
One object can use another object to do part of its work.
<?php
declare(strict_types=1);
final class MoneyFormatter
{
public function formatPennies(int $pennies): string
{
return '£' . number_format($pennies / 100, 2);
}
}
final class InvoicePresenter
{
public function __construct(private MoneyFormatter $formatter)
{
}
public function total(int $pennies): string
{
return 'Invoice total: ' . $this->formatter->formatPennies($pennies);
}
}
$presenter = new InvoicePresenter(new MoneyFormatter());
echo $presenter->total(129950) . PHP_EOL;
// Prints:
// Invoice total: £1,299.50
InvoicePresenter does not inherit formatting behaviour. It collaborates with a formatter.
Composition Makes Dependencies Visible
The constructor shows what a class needs.
<?php
declare(strict_types=1);
interface Mailer
{
public function send(string $to, string $subject): void;
}
final class EchoMailer implements Mailer
{
public function send(string $to, string $subject): void
{
echo 'Sending ' . $subject . ' to ' . $to . PHP_EOL;
}
}
final class WelcomeUser
{
public function __construct(private Mailer $mailer)
{
}
public function handle(string $email): void
{
$this->mailer->send($email, 'Welcome');
}
}
(new WelcomeUser(new EchoMailer()))->handle('nia@example.com');
// Prints:
// Sending Welcome to nia@example.com
There is no hidden static mailer. The dependency is visible and replaceable.
Composition Over Inheritance
If the relationship is "uses a" rather than "is a kind of", composition is usually the better choice.
<?php
declare(strict_types=1);
final class TaxCalculator
{
public function addVat(int $netPennies): int
{
return (int) round($netPennies * 1.2);
}
}
final class CheckoutTotal
{
public function __construct(private TaxCalculator $taxCalculator)
{
}
public function totalWithTax(int $netPennies): int
{
return $this->taxCalculator->addVat($netPennies);
}
}
echo (new CheckoutTotal(new TaxCalculator()))->totalWithTax(1000) . PHP_EOL;
// Prints:
// 1200
CheckoutTotal is not a kind of TaxCalculator; it uses one.
Composition Helps Testing
You can pass a simple fake collaborator into the class under test.
<?php
declare(strict_types=1);
interface Clock
{
public function now(): DateTimeImmutable;
}
final class FixedClock implements Clock
{
public function now(): DateTimeImmutable
{
return new DateTimeImmutable('2026-05-20 09:00:00');
}
}
final class TimestampLabel
{
public function __construct(private Clock $clock)
{
}
public function label(): string
{
return $this->clock->now()->format('Y-m-d H:i');
}
}
echo (new TimestampLabel(new FixedClock()))->label() . PHP_EOL;
// Prints:
// 2026-05-20 09:00
The class can be tested without depending on the real current time.
Avoid Too Many Tiny Collaborators
Composition is not a reason to split every line into another class. A collaborator should have a real responsibility and make the caller simpler.
<?php
declare(strict_types=1);
final class DisplayName
{
public function clean(string $name): string
{
$name = trim($name);
if ($name === '') {
throw new InvalidArgumentException('Name is required.');
}
return $name;
}
}
echo (new DisplayName())->clean(' Nia ') . PHP_EOL;
// Prints:
// Nia
Small classes are good when they carry a concept, not when they only create ceremony.
What To Remember
Composition builds behaviour by connecting focused objects. Prefer it when one object uses another service or helper. Keep dependencies explicit, inject collaborators through constructors, and avoid inheritance when there is no genuine "is a kind of" relationship.
Practice
Task: Compose A Receipt Presenter
Create a receipt presenter that uses a separate money formatter.
Requirements
- Use
declare(strict_types=1);. - Create a
MoneyFormatterclass. - Create a
ReceiptPresenterclass that receivesMoneyFormatterthrough its constructor. - Add a method that returns a receipt total label.
- Keep formatting logic in
MoneyFormatter. - Print one receipt total.
- Include the expected output as comments in the same PHP code block.
The presenter should use composition, not inheritance or a trait.
Show solution
<?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 totalLabel(int $totalPennies): string
{
return 'Receipt total: ' . $this->formatter->formatPennies($totalPennies);
}
}
$presenter = new ReceiptPresenter(new MoneyFormatter());
echo $presenter->totalLabel(4695) . PHP_EOL;
// Prints:
// Receipt total: £46.95
ReceiptPresenter uses MoneyFormatter instead of inheriting from it. The dependency is visible in the constructor and each class has one clear job.