Methods from two sibling classes: traits in PHP
| Introduction | |
| The sibling problem | |
| Traits: horizontal reuse | |
| Same method in both traits | |
| Alternative: composition |
Introduction
A base class with two children that are siblings — neither is the parent of the other — and now you need one class with methods from both branches. PHP forbids it directly; traits are the language answer (inheritance and OOP basics first if needed).
The sibling problem
<?php
namespace Aredel\Demo;
class Reporter
{
/* base: shared logging */
}
class PdfReporter extends Reporter
{
public function pdf(): void
{
}
}
class CsvReporter extends Reporter
{
public function csv(): void
{
}
}
// need pdf() AND csv() in one class:
// class ComboReporter extends PdfReporter, CsvReporter {} // FATAL
class X extends A, B is a fatal error: PHP has single inheritance only (it avoids the diamond problem by refusing to have it). The branches are siblings — restructuring them into a parent/child chain just to share code falsifies the model.
Traits: horizontal reuse
A trait is a bundle of methods meant to be pasted into classes with use — reuse without any parent/child relation:
<?php
namespace Aredel\Demo;
trait PdfExport
{
public function pdf(): void
{
}
}
trait CsvExport
{
public function csv(): void
{
}
}
class ComboReporter extends Reporter
{
use PdfExport;
use CsvExport;
}
Keep the genuine is-a relation in extends (ComboReporter still is a Reporter) and put the cross-cutting abilities in traits. Trait methods behave as if defined in the class itself, so they see $this and can use its properties.
Same method in both traits
If both traits define save(), PHP refuses to guess — resolve explicitly: insteadof picks the winner, as keeps the loser under an alias:
<?php
namespace Aredel\Demo;
class ComboReporter extends Reporter
{
use PdfExport, CsvExport {
PdfExport::save insteadof CsvExport;
CsvExport::save as saveCsv;
}
}
A method defined directly in the class always wins over trait methods — the precedence is: class beats trait, trait beats inherited.
Alternative: composition
When the siblings carry state, not just behavior, prefer holding them as properties and delegating:
<?php
namespace Aredel\Demo;
class ComboReporter extends Reporter
{
public function __construct(
private PdfReporter $pdf,
private CsvReporter $csv,
) {
}
}
Rule of thumb: shared behavior without state — traits; collaborators with their own state and lifecycle — composition.
Article author: Arthur Isaev
| Development with PHP | |
| OOP in PHP | |
| PHP 8 Classes | |
| Inheritance in PHP | |
| Methods from two sibling classes: traits |