Menu
Coddy logo textTech

Abstract Methods in Traits

Part of the Object Oriented Programming section of Coddy's PHP journey — lesson 54 of 91.

Traits can provide ready-to-use methods, but sometimes you need the class using the trait to implement specific functionality. This is where abstract methods in traits become useful - they define a contract that the using class must fulfill.

When a trait declares an abstract method, any class using that trait must implement it:

<?php
trait Notifiable {
    abstract public function getEmail(): string;
    
    public function sendNotification(string $message): void {
        echo "Sending to " . $this->getEmail() . ": $message";
    }
}

class User {
    use Notifiable;
    
    public function __construct(private string $email) {}
    
    public function getEmail(): string {
        return $this->email;
    }
}

$user = new User("john@example.com");
$user->sendNotification("Welcome!");

Output:

Sending to john@example.com: Welcome!

The trait provides the sendNotification() method but relies on getEmail() to be implemented by the class. This creates a powerful pattern where the trait handles common logic while delegating class-specific details to the implementing class.

If a class uses the trait without implementing the abstract method, PHP throws a fatal error. This ensures that traits can safely depend on certain methods existing, making your code more predictable and maintainable.

challenge icon

Challenge

Easy

Let's build a pricing system where different product types calculate their final prices differently, but they all share common discount logic through a trait with an abstract method.

You'll organize your code across three files:

  • Discountable.php — Create a trait called Discountable that provides discount functionality. The trait should have:
    • An abstract method getBasePrice(): float that classes using this trait must implement
    • A method applyDiscount(int $percent) that calculates and returns the price after applying the discount percentage to the base price
    • A method getFinalPrice(int $percent) that returns a formatted string: "Final price: $[discounted_price]" with 2 decimal places
    The trait handles all the discount math, but relies on each class to tell it what the base price is.
  • Product.php — Create a Product class that uses the Discountable trait. Include the trait file. Use constructor promotion to define a private $name (string) and a private $price (float). Implement the required getBasePrice() method to return the product's price.
  • main.php — Include the Product file. You'll receive three inputs: a product name, a price, and a discount percentage. Create a Product instance and print two lines:
    • The base price formatted as "Base: $[price]" with 2 decimal places
    • The result of calling getFinalPrice() with the discount percentage

This pattern is powerful because the Discountable trait can be used by any class — products, services, subscriptions — as long as they implement getBasePrice(). The trait provides the shared discount logic while each class defines where its base price comes from.

Cheat sheet

Traits can declare abstract methods that must be implemented by any class using the trait:

<?php
trait Notifiable {
    abstract public function getEmail(): string;
    
    public function sendNotification(string $message): void {
        echo "Sending to " . $this->getEmail() . ": $message";
    }
}

class User {
    use Notifiable;
    
    public function __construct(private string $email) {}
    
    public function getEmail(): string {
        return $this->email;
    }
}

The trait provides ready-to-use methods while requiring the class to implement specific functionality. If a class uses the trait without implementing the abstract method, PHP throws a fatal error.

This pattern allows traits to handle common logic while delegating class-specific details to the implementing class.

Try it yourself

<?php

require_once 'Product.php';

// Read inputs
$name = trim(fgets(STDIN));
$price = floatval(trim(fgets(STDIN)));
$discountPercent = intval(trim(fgets(STDIN)));

// TODO: Create a Product instance with the name and price

// TODO: Print the base price formatted as "Base: $[price]" with 2 decimal places

// TODO: Print the result of calling getFinalPrice() with the discount percentage

?>
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming