Methods from two sibling classes: traits in PHP

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).

Author: Arthur Isaev

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.

Автор статьи: Андрей Олегович

Изображение баннера