Question

Override a PHP Trait Method and Call the Original Method

phptraits

Question

Given this PHP trait and class, how can MyClass override the trait's calc() method while still calling the original implementation supplied by the trait?

trait A
{
    public function calc($v)
    {
        return $v + 1;
    }
}

class MyClass
{
    use A;

    public function calc($v)
    {
        $v++;
        return A::calc($v);
    }
}

print (new MyClass())->calc(2); // Expected output: 4

Calls such as self::calc($v), static::calc($v), parent::calc($v), and A::calc($v) do not call the original trait method. Is there a way to reuse the trait implementation instead of completely rewriting a more complex method?

Short Answer

PHP traits are copied into the classes that use them; they are not parent classes that can be called with parent::. To override a trait method and still reuse it, give the trait method an alias during use, then call that alias through $this.

Concept

A PHP trait is a mechanism for sharing methods among multiple classes. When a class uses a trait, PHP composes the trait's methods into that class.

This is different from class inheritance:

  • A child class can call an inherited parent method with parent::method().
  • A trait does not become a parent object or parent class.
  • Therefore, A::calc() is not a valid way to invoke the implementation of trait A from an object.

When a class defines a method with the same name as a trait method, the class method takes precedence. This lets you customize trait behavior, but it also hides the trait method under its original name.

PHP solves this with trait adaptations. You can create an alias for the trait method in the use statement. The class can then define its own calc() method and call the aliased trait method, such as $this->traitCalc($v).

This is useful when a trait contains reusable default behavior, while one class needs to add validation, logging, normalization, or a small adjustment before or after that behavior.

Mental Model

Think of a trait as a recipe copied into a class's cookbook.

When MyClass uses trait A, the calc() recipe is copied into MyClass. If MyClass writes its own recipe also named calc(), that new recipe is the one people use.

An alias is like saving the copied recipe under a second label:

  • Public class recipe: calc()
  • Saved original recipe: traitCalc()

Your custom calc() recipe can now perform extra work and then call the saved original recipe with $this->traitCalc(...).

Take Quiz

Syntax and Examples

Use a trait adaptation block to alias the trait method:

use TraitName {
    methodName as aliasName;
}

Then call the alias as an instance method:

$this->aliasName($value);

Here is a complete example:

<?php

trait A
{
    public function calc($v)
    {
        return $v + 1;
    }
}

class MyClass
{
    use A {
        calc as protected traitCalc;
    }

    public function calc($v)
    {
        $v++;

        return $this->traitCalc($v);
    }
}

$object = new MyClass();
echo $object->calc(2); // 4

calc as protected traitCalc; creates another name for the trait's calc() implementation.

The alias is marked protected because it is only an implementation detail for MyClass and its subclasses. The public API remains calc(). You may omit protected if the alias should be public, but that is usually unnecessary.

Step by Step Execution

Consider this code:

$result = (new MyClass())->calc(2);

Execution proceeds as follows:

  1. new MyClass() creates a MyClass object.
  2. PHP calls MyClass::calc(2), because a class method takes precedence over a trait method with the same name.
  3. Inside MyClass::calc(), $v starts as 2.
  4. $v++ changes $v from 2 to 3.
  5. $this->traitCalc(3) calls the alias for A::calc() that was imported from the trait.
  6. The trait implementation returns 3 + 1, which is 4.
  7. MyClass::calc() returns 4.

So the final output is:

4

Real World Use Cases

Trait method aliases are useful when a shared implementation is mostly correct but one class needs a small variation.

  • Logging: Add a log entry before delegating to the trait's save, send, or calculate method.
  • Input normalization: Trim text, convert units, or standardize a date before invoking shared behavior.
  • Validation: Check class-specific rules before performing the trait's default action.
  • Metrics: Measure execution time around the original trait method.
  • Decorating output: Call the trait method first, then format or enrich its returned result.
  • Framework extensions: A trait may implement common behavior for models, controllers, commands, or services, while a particular class adds a narrow customization.
Take Quiz

Real Codebase Usage

In real codebases, prefer a descriptive alias that explains why the method exists:

use CalculatesPrice {
    calculate as protected calculateBasePrice;
}

Then make the public override easy to read:

public function calculate(float $amount): float
{
    if ($amount < 0) {
        throw new InvalidArgumentException('Amount cannot be negative.');
    }

    $basePrice = $this->calculateBasePrice($amount);

    return $basePrice + $this->handlingFee;
}

Common project patterns include:

  • Guard clauses: Reject invalid input before calling the aliased trait method.
  • Before/after hooks: Do setup first, call the base implementation, then process its result.
  • Visibility control: Keep the alias protected or private so callers only see the intended public method.
  • Clear naming: Use names such as saveFromTrait, baseValidate, or performDefaultSync rather than vague names like method2.

If many classes need different variations of the same behavior, consider whether a separate service or strategy object would be clearer than repeated overrides.

Common Mistakes

Calling the trait as though it were a parent class

return A::calc($v); // Incorrect

A trait is not a parent class and is not used as an object instance in this situation. Alias the trait method and call it on the current object instead.

Calling the overriding method recursively

public function calc($v)
{
    return $this->calc($v); // Incorrect: calls itself forever
}

This causes infinite recursion until PHP throws an error. Call the alias instead:

return $this->traitCalc($v);

Using self::calc() or static::calc()

return self::calc($v);   // Incorrect here
return static::calc($v); // Incorrect here

Both resolve to the overridden calc() method and can recurse. They do not mean “the trait version.”

Calling an alias without $this->

return traitCalc($v); // Incorrect

This looks for a global function named traitCalc. The alias is a method on the current object:

return $this->traitCalc($v);

Expecting parent::calc() to work

return parent::calc($v); // Incorrect unless an actual parent class defines calc()

parent:: only refers to the class named in extends, not to a trait.

Forgetting the adaptation block

use A;

This imports only calc() under its original name. Once the class overrides calc(), there is no second name available to call. Add the alias during import.

Comparisons

FeatureTrait method aliasparent::method()Method override only
Main purposePreserve access to a trait implementation under another nameCall an inherited parent-class implementationReplace behavior completely
Requires a parent classNoYesNo
Typical call$this->traitCalc()parent::calc()$this->calc() is usually recursion
Can change alias visibilityYesNoNot applicable
Best forExtending a trait method without copying itExtending inherited behaviorFully custom behavior

Trait aliases and method renaming are related but different:

use A {
    calc as traitCalc;
}

This keeps calc() available from the trait too, unless the class supplies its own calc(). It additionally creates traitCalc().

use A {
    calc as protected traitCalc;
}

This does the same thing while reducing the alias visibility. In most override scenarios, this is the preferred form.

Take Quiz

Cheat Sheet

trait ExampleTrait
{
    public function work($value)
    {
        return $value;
    }
}

class Example
{
    use ExampleTrait {
        work as protected traitWork;
    }

    public function work($value)
    {
        // Extra behavior here.
        return $this->traitWork($value);
    }
}
  • Traits are composed into a class; they are not parent classes.
  • A class method overrides a trait method with the same name.
  • Create an alias with use TraitName { method as alias; }.
  • Call the alias with $this->alias().
  • Use protected for an internal alias in most cases.
  • Do not use parent:: unless a real parent class defines the method.
  • Do not use self::sameMethod() inside an override unless recursion is intended.
  • The aliased method receives arguments and returns values normally.

FAQ

Can I call a PHP trait method directly with TraitName::method()?

No. Traits are not instantiated or called like ordinary classes in this use case. Import the trait method into a class and call it through that class instance.

How do I call the original trait method after overriding it?

Alias the method in the trait use statement, then call the alias with $this->aliasName().

Why does parent::calc() not call the trait method?

parent:: refers only to the class extended by the current class. A trait is not a parent class.

Does aliasing remove the original trait method?

No. An alias adds another name for the trait method. If the class defines the original name, the class method takes precedence for that name.

Should a trait method alias be public or protected?

Usually protected. The alias is commonly an internal implementation detail, while the overriding method is the public API.

Can I change the visibility of a trait method with an alias?

Yes. For example, calc as protected traitCalc; creates a protected alias.

Can I use this with static trait methods?

Yes, traits can contain static methods, but the call and design should match static-method rules. For ordinary object behavior, instance methods and $this->alias() are clearer.

When should I avoid overriding a trait method?

If many classes need substantially different behavior, a service object, interface, or strategy pattern may be easier to maintain than many trait overrides.

Take Quiz

Mini Project

Description

Build a small order calculator that uses a trait for a default subtotal calculation. A special order class will override the calculation to add a handling fee while still reusing the trait's original logic.

Goal

Create an ExpressOrder class whose overridden method adds a handling fee and then delegates the subtotal calculation to a trait alias.

Requirements

  • Create a trait with a public calculateTotal(float $subtotal): float method.
  • Make the trait apply a 10% tax to the subtotal.
  • Create an ExpressOrder class that uses the trait and aliases its method.
  • Override calculateTotal() to add a 5.00 handling fee before applying tax.
  • Keep the aliased trait method non-public.
  • Print the total for a subtotal of 100.00.
Take Quiz

Keep learning

Related questions

View more