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.

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