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
EasyLet'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 calledDiscountablethat provides discount functionality. The trait should have:- An abstract method
getBasePrice(): floatthat 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
- An abstract method
Product.php— Create aProductclass that uses theDiscountabletrait. Include the trait file. Use constructor promotion to define a private$name(string) and a private$price(float). Implement the requiredgetBasePrice()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 aProductinstance and print two lines:- The base price formatted as
"Base: $[price]"with 2 decimal places - The result of calling
getFinalPrice()with the discount percentage
- The base price formatted as
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
?>This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesIntroduction to OOPClasses vs ObjectsThe $this KeywordMethodsPropertiesConstructor (__construct)Destructor (__destruct)Recap - Simple Calculator4Inheritance
Basic InheritanceThe parent:: KeywordMethod OverridingThe final KeywordAbstract ClassesRecap - Employee Hierarchy7Encapsulation
Public, Protected, PrivateAccess Modifiers in DepthGetters and SettersInformation HidingConstructor Promotion (8.0)Recap - Student Records System2Namespaces & Autoloading
Introduction to NamespacesThe use KeywordPSR-4 Autoloading StandardComposer AutoloaderRecap - Organized Project5Interfaces & Contracts
Introduction to InterfacesImplementing InterfacesMultiple Interface ImplementInterface vs Abstract ClassType Hinting with InterfacesRecap - Shape Calculator8Magic Methods
Magic Methods Introduction__toString & __debugInfo__get, __set, __isset, __unset__call & __callStatic__clone & Object Cloning__serialize & __unserializeRecap - Custom Collection11Type System & Error Handling
Type DeclarationsNullable TypesUnion & Intersection TypesException ClassesCustom Exception HierarchyTry, Catch, FinallyRecap - Form Validator14Project: Library Management
Project OverviewBook and User Classes3Class Properties
Instance vs Static PropertiesConstants in ClassesStatic Methods & PropertiesPrivate & Protected PropertiesReadonly Properties (PHP 8.1)Recap - Bank Account Manager6Polymorphism
Method Overriding RevisitedPolymorphism via InterfacesType Hinting & Union TypesLate Static BindingRecap - Payment Processor9Traits
Introduction to TraitsUsing Multiple TraitsTrait Conflict ResolutionAbstract Methods in TraitsTraits vs Inheritance12Design Patterns Part 1
Intro to Design PatternsSingleton PatternFactory PatternObserver PatternStrategy Pattern