Inheritance and Traits
Overview
Inheritance is one of the core concepts of object-oriented programming, allowing a class to inherit properties and methods from another class. PHP supports single inheritance and provides the Trait mechanism for code reuse. This chapter will explore class inheritance, abstract classes, interfaces, and the use of Traits in depth.
Class Inheritance Basics
Basic Inheritance Syntax
php
<?php
// Parent class (base class)
class Vehicle {
protected $brand;
protected $speed;
public function __construct($brand) {
$this->brand = $brand;
$this->speed = 0;
}
public function start() {
echo "{$this->brand} started\n";
}
public function accelerate($amount) {
$this->speed += $amount;
echo "{$this->brand} accelerated to {$this->speed} km/h\n";
}
public function getBrand() {
return $this->brand;
}
public function getSpeed() {
return $this->speed;
}
}
// Child class (derived class)
class Car extends Vehicle {
private $fuelType;
private $doors;
public function __construct($brand, $fuelType, $doors = 4) {
parent::__construct($brand); // Call parent constructor
$this->fuelType = $fuelType;
$this->doors = $doors;
}
// Override parent method
public function start() {
echo "{$this->brand} car engine started\n";
$this->checkFuel();
}
// New method
public function honk() {
echo "{$this->brand} car honks: Beep beep!\n";
}
private function checkFuel() {
echo "Checking {$this->fuelType} fuel\n";
}
public function getInfo() {
return "Brand: {$this->brand}, Fuel: {$this->fuelType}, Doors: {$this->doors}";
}
}
// Usage example
$car = new Car("Toyota", "Gasoline");
$car->start();
$car->accelerate(50);
$car->honk();
echo $car->getInfo() . "\n";
?>Method Overriding and parent Keyword
php
<?php
class Animal {
protected $name;
protected $age;
public function __construct($name, $age) {
$this->name = $name;
$this->age = $age;
}
public function makeSound() {
echo "{$this->name} makes a sound\n";
}
public function eat($food) {
echo "{$this->name} is eating {$food}\n";
}
public function getInfo() {
return "Name: {$this->name}, Age: {$this->age}";
}
}
class Dog extends Animal {
private $breed;
public function __construct($name, $age, $breed) {
parent::__construct($name, $age);
$this->breed = $breed;
}
// Override parent method
public function makeSound() {
echo "{$this->name} barks\n";
}
// Override and extend parent method
public function eat($food) {
parent::eat($food); // Call parent method
echo "{$this->name} wags tail in satisfaction\n";
}
// Override parent method
public function getInfo() {
return parent::getInfo() . ", Breed: {$this->breed}";
}
// New method
public function fetch() {
echo "{$this->name} fetches the ball\n";
}
}
class Cat extends Animal {
private $color;
public function __construct($name, $age, $color) {
parent::__construct($name, $age);
$this->color = $color;
}
public function makeSound() {
echo "{$this->name} meows\n";
}
public function climb() {
echo "{$this->name} climbs a tree\n";
}
public function getInfo() {
return parent::getInfo() . ", Color: {$this->color}";
}
}
// Usage example
$dog = new Dog("Xiao Bai", 3, "Golden Retriever");
$cat = new Cat("Xiao Hua", 2, "Orange");
$dog->makeSound();
$dog->eat("Dog food");
$dog->fetch();
echo $dog->getInfo() . "\n";
$cat->makeSound();
$cat->eat("Cat food");
$cat->climb();
echo $cat->getInfo() . "\n";
?>Abstract Classes
Abstract Class Basics
php
<?php
// Abstract classes cannot be directly instantiated
abstract class Shape {
protected $color;
public function __construct($color) {
$this->color = $color;
}
// Concrete method
public function getColor() {
return $this->color;
}
// Abstract methods must be implemented in child classes
abstract public function calculateArea();
abstract public function calculatePerimeter();
// Template method pattern
public function displayInfo() {
echo "Color: {$this->color}\n";
echo "Area: " . $this->calculateArea() . "\n";
echo "Perimeter: " . $this->calculatePerimeter() . "\n";
}
}
class Circle extends Shape {
private $radius;
public function __construct($color, $radius) {
parent::__construct($color);
$this->radius = $radius;
}
public function calculateArea() {
return pi() * $this->radius * $this->radius;
}
public function calculatePerimeter() {
return 2 * pi() * $this->radius;
}
}
class Rectangle extends Shape {
private $width;
private $height;
public function __construct($color, $width, $height) {
parent::__construct($color);
$this->width = $width;
$this->height = $height;
}
public function calculateArea() {
return $this->width * $this->height;
}
public function calculatePerimeter() {
return 2 * ($this->width + $this->height);
}
}
// Usage example
$circle = new Circle("Red", 5);
$rectangle = new Rectangle("Blue", 10, 6);
$circle->displayInfo();
echo "---\n";
$rectangle->displayInfo();
?>Practical Application of Abstract Classes
php
<?php
abstract class DatabaseConnection {
protected $host;
protected $database;
protected $username;
protected $password;
protected $connection;
public function __construct($host, $database, $username, $password) {
$this->host = $host;
$this->database = $database;
$this->username = $username;
$this->password = $password;
}
// Abstract methods
abstract protected function connect();
abstract public function query($sql);
abstract public function close();
// Concrete methods
public function isConnected() {
return $this->connection !== null;
}
public function getDatabaseName() {
return $this->database;
}
}
class MySQLConnection extends DatabaseConnection {
protected function connect() {
$this->connection = new PDO(
"mysql:host={$this->host};dbname={$this->database}",
$this->username,
$this->password
);
$this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
public function query($sql) {
if (!$this->isConnected()) {
$this->connect();
}
return $this->connection->query($sql);
}
public function close() {
$this->connection = null;
}
}
class PostgreSQLConnection extends DatabaseConnection {
protected function connect() {
$this->connection = new PDO(
"pgsql:host={$this->host};dbname={$this->database}",
$this->username,
$this->password
);
$this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
public function query($sql) {
if (!$this->isConnected()) {
$this->connect();
}
return $this->connection->query($sql);
}
public function close() {
$this->connection = null;
}
}
?>Interfaces
Interface Basics
php
<?php
// Define interface
interface Flyable {
public function fly();
public function land();
}
interface Swimmable {
public function swim();
public function dive();
}
// A class can implement multiple interfaces
class Duck implements Flyable, Swimmable {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function fly() {
echo "{$this->name} flies in the sky\n";
}
public function land() {
echo "{$this->name} lands on the ground\n";
}
public function swim() {
echo "{$this->name} swims in the water\n";
}
public function dive() {
echo "{$this->name} dives into the water\n";
}
public function quack() {
echo "{$this->name} quacks\n";
}
}
class Airplane implements Flyable {
private $model;
public function __construct($model) {
$this->model = $model;
}
public function fly() {
echo "{$this->model} airplane flies at high altitude\n";
}
public function land() {
echo "{$this->model} airplane lands on the runway\n";
}
}
// Usage example
$duck = new Duck("Donald Duck");
$plane = new Airplane("Boeing 747");
$duck->fly();
$duck->swim();
$duck->quack();
$plane->fly();
$plane->land();
?>Interface Inheritance
php
<?php
interface Readable {
public function read();
}
interface Writable {
public function write($data);
}
// Interfaces can extend other interfaces
interface ReadWritable extends Readable, Writable {
public function isReadOnly();
}
class File implements ReadWritable {
private $filename;
private $readOnly;
public function __construct($filename, $readOnly = false) {
$this->filename = $filename;
$this->readOnly = $readOnly;
}
public function read() {
if (file_exists($this->filename)) {
return file_get_contents($this->filename);
}
return false;
}
public function write($data) {
if ($this->readOnly) {
throw new Exception("File is read-only");
}
return file_put_contents($this->filename, $data);
}
public function isReadOnly() {
return $this->readOnly;
}
}
// Usage example
$file = new File("data.txt");
$file->write("Hello, World!");
echo $file->read();
echo "Read-only mode: " . ($file->isReadOnly() ? "Yes" : "No") . "\n";
?>Traits
Trait Basics
php
<?php
// Define Trait
trait Loggable {
private $logFile = 'app.log';
public function log($message) {
$timestamp = date('Y-m-d H:i:s');
$logEntry = "[$timestamp] $message\n";
file_put_contents($this->logFile, $logEntry, FILE_APPEND);
}
public function setLogFile($filename) {
$this->logFile = $filename;
}
}
trait Cacheable {
private $cache = [];
public function getFromCache($key) {
return $this->cache[$key] ?? null;
}
public function putInCache($key, $value) {
$this->cache[$key] = $value;
}
public function clearCache() {
$this->cache = [];
}
}
// Using Traits
class UserService {
use Loggable, Cacheable;
public function getUser($id) {
// Check cache first
$user = $this->getFromCache("user_$id");
if ($user !== null) {
$this->log("Retrieved user $id from cache");
return $user;
}
// Simulate database retrieval
$user = "User data_$id";
$this->putInCache("user_$id", $user);
$this->log("Retrieved user $id from database");
return $user;
}
public function updateUser($id, $data) {
// Update logic
$this->log("Updated user $id: $data");
// Clear cache
$this->clearCache();
}
}
$userService = new UserService();
$userService->setLogFile('user_service.log');
echo $userService->getUser(1) . "\n";
echo $userService->getUser(1) . "\n"; // From cache
$userService->updateUser(1, "New data");
?>Trait Conflict Resolution
php
<?php
trait TraitA {
public function hello() {
echo "Hello from TraitA\n";
}
public function goodbye() {
echo "Goodbye from TraitA\n";
}
}
trait TraitB {
public function hello() {
echo "Hello from TraitB\n";
}
public function welcome() {
echo "Welcome from TraitB\n";
}
}
class MyClass {
use TraitA, TraitB {
TraitB::hello insteadof TraitA; // Use TraitB's hello method
TraitA::hello as helloA; // Alias TraitA's hello method
TraitA::goodbye as private goodbyePrivate; // Change access modifier
}
}
$obj = new MyClass();
$obj->hello(); // Output: Hello from TraitB
$obj->helloA(); // Output: Hello from TraitA
$obj->goodbye(); // Output: Goodbye from TraitA
$obj->welcome(); // Output: Welcome from TraitB
// $obj->goodbyePrivate(); // Error, because it's private
?>Trait Composition and Inheritance
php
<?php
trait Timestampable {
private $createdAt;
private $updatedAt;
public function setCreatedAt($timestamp = null) {
$this->createdAt = $timestamp ?? time();
}
public function setUpdatedAt($timestamp = null) {
$this->updatedAt = $timestamp ?? time();
}
public function getCreatedAt() {
return $this->createdAt;
}
public function getUpdatedAt() {
return $this->updatedAt;
}
}
trait Validatable {
private $errors = [];
abstract protected function getRules();
public function validate($data) {
$this->errors = [];
$rules = $this->getRules();
foreach ($rules as $field => $rule) {
if (!isset($data[$field])) {
$this->errors[$field] = "Field $field is required";
continue;
}
if ($rule === 'required' && empty($data[$field])) {
$this->errors[$field] = "Field $field cannot be empty";
}
}
return empty($this->errors);
}
public function getErrors() {
return $this->errors;
}
}
// Traits can use other Traits
trait ActiveRecord {
use Timestampable, Validatable;
public function save() {
if (!$this->validate($this->toArray())) {
throw new Exception("Validation failed: " . implode(', ', $this->getErrors()));
}
if ($this->createdAt === null) {
$this->setCreatedAt();
}
$this->setUpdatedAt();
// Logic to save to database
echo "Record saved\n";
}
abstract public function toArray();
}
class User {
use ActiveRecord;
private $name;
private $email;
public function __construct($name, $email) {
$this->name = $name;
$this->email = $email;
}
protected function getRules() {
return [
'name' => 'required',
'email' => 'required'
];
}
public function toArray() {
return [
'name' => $this->name,
'email' => $this->email
];
}
}
// Usage example
$user = new User("Zhang San", "zhang@example.com");
$user->save();
echo "Created at: " . date('Y-m-d H:i:s', $user->getCreatedAt()) . "\n";
echo "Updated at: " . date('Y-m-d H:i:s', $user->getUpdatedAt()) . "\n";
?>Polymorphism
Polymorphism Example
php
<?php
interface PaymentProcessor {
public function processPayment($amount);
public function getProcessorName();
}
class CreditCardProcessor implements PaymentProcessor {
private $cardNumber;
public function __construct($cardNumber) {
$this->cardNumber = $cardNumber;
}
public function processPayment($amount) {
echo "Processing payment of $amount yuan using credit card\n";
echo "Card number: ****" . substr($this->cardNumber, -4) . "\n";
return true;
}
public function getProcessorName() {
return "Credit Card Processor";
}
}
class PayPalProcessor implements PaymentProcessor {
private $email;
public function __construct($email) {
$this->email = $email;
}
public function processPayment($amount) {
echo "Processing payment of $amount yuan using PayPal\n";
echo "PayPal account: {$this->email}\n";
return true;
}
public function getProcessorName() {
return "PayPal Processor";
}
}
class WeChatPayProcessor implements PaymentProcessor {
private $phoneNumber;
public function __construct($phoneNumber) {
$this->phoneNumber = $phoneNumber;
}
public function processPayment($amount) {
echo "Processing payment of $amount yuan using WeChat Pay\n";
echo "Phone number: {$this->phoneNumber}\n";
return true;
}
public function getProcessorName() {
return "WeChat Pay Processor";
}
}
// Payment manager
class PaymentManager {
public function processPayment(PaymentProcessor $processor, $amount) {
echo "Processing payment using " . $processor->getProcessorName() . "...\n";
$result = $processor->processPayment($amount);
if ($result) {
echo "Payment successful!\n";
} else {
echo "Payment failed!\n";
}
echo "---\n";
}
}
// Usage example (demonstrating polymorphism)
$paymentManager = new PaymentManager();
$processors = [
new CreditCardProcessor("1234567890123456"),
new PayPalProcessor("user@example.com"),
new WeChatPayProcessor("13800138000")
];
foreach ($processors as $processor) {
$paymentManager->processPayment($processor, 100);
}
?>Best Practices
1. Inheritance vs Composition
php
<?php
// Bad inheritance design
class Bird {
public function fly() {
echo "Flying\n";
}
}
class Penguin extends Bird {
public function fly() {
throw new Exception("Penguins cannot fly");
}
}
// Better design: Use composition and interfaces
interface Flyable {
public function fly();
}
interface Swimmable {
public function swim();
}
class FlyingBehavior implements Flyable {
public function fly() {
echo "Flying in the sky\n";
}
}
class SwimmingBehavior implements Swimmable {
public function swim() {
echo "Swimming in the water\n";
}
}
abstract class Bird {
protected $name;
public function __construct($name) {
$this->name = $name;
}
public function eat() {
echo "{$this->name} is eating\n";
}
}
class Eagle extends Bird implements Flyable {
private $flyingBehavior;
public function __construct($name) {
parent::__construct($name);
$this->flyingBehavior = new FlyingBehavior();
}
public function fly() {
echo "{$this->name} ";
$this->flyingBehavior->fly();
}
}
class Penguin extends Bird implements Swimmable {
private $swimmingBehavior;
public function __construct($name) {
parent::__construct($name);
$this->swimmingBehavior = new SwimmingBehavior();
}
public function swim() {
echo "{$this->name} ";
$this->swimmingBehavior->swim();
}
}
?>2. Interface Segregation Principle
php
<?php
// Bad interface design (interface too large)
interface Worker {
public function work();
public function eat();
public function sleep();
public function writeCode();
public function designUI();
}
// Better design: Separate interfaces
interface Workable {
public function work();
}
interface Eatable {
public function eat();
}
interface Sleepable {
public function sleep();
}
interface Programmable {
public function writeCode();
}
interface Designable {
public function designUI();
}
class Developer implements Workable, Eatable, Sleepable, Programmable {
public function work() {
echo "Developer is working\n";
}
public function eat() {
echo "Developer is eating\n";
}
public function sleep() {
echo "Developer is sleeping\n";
}
public function writeCode() {
echo "Developer is writing code\n";
}
}
class Designer implements Workable, Eatable, Sleepable, Designable {
public function work() {
echo "Designer is working\n";
}
public function eat() {
echo "Designer is eating\n";
}
public function sleep() {
echo "Designer is sleeping\n";
}
public function designUI() {
echo "Designer is designing UI\n";
}
}
?>Summary
This chapter introduced inheritance and Trait mechanisms in PHP:
- Class Inheritance: Single inheritance through the
extendskeyword, usingparent::to access parent class - Abstract Classes: Define abstract methods, providing common interface and partial implementation for child classes
- Interfaces: Define contracts, classes can implement multiple interfaces
- Traits: Implement code reuse, solving limitations of single inheritance
- Polymorphism: Achieved through interfaces, allowing different classes to be handled in a unified way
Proper use of these features can build flexible and maintainable object-oriented applications. In the next chapter, we will learn about regular expressions in PHP.