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