Mastering PHP Object-Oriented Programming: A Comprehensive Guide

Mehedi Hasan
12 min readNov 20, 2024

--

Photo by Ben Griffiths on Unsplash

PHP has long been one of the most popular server-side scripting languages. While early PHP development relied heavily on procedural programming, modern web applications demand a more structured, reusable, and maintainable approach. That’s where Object-Oriented Programming (OOP) comes in.

OOP allows developers to create modular and scalable code, making it easier to maintain and expand over time. In this comprehensive guide, we’ll dive deep into PHP OOP, covering everything from the basics to advanced topics with practical examples. By the end of this guide, you’ll be equipped to implement OOP principles in your PHP projects confidently.

1. What is Object-Oriented Programming (OOP)?

OOP is a programming paradigm based on the concept of “objects.” These objects represent real-world entities and are used to encapsulate data and functionality into reusable structures.

Key characteristics of OOP:

  • Encapsulation: Combines data (properties) and behavior (methods) into a single unit, restricting direct access to internal data and enforcing controlled interaction through access modifiers.
  • Inheritance: Allows a class (child) to inherit properties and methods from another class (parent), enabling code reuse and the extension of functionality.
  • Polymorphism: Enables methods to be defined in multiple forms, allowing objects of different classes to be treated as objects of a common parent class.
  • Abstraction: Simplifies complex systems by focusing on essential features and modeling classes appropriate to the problem, while hiding implementation details.
  • Static Binding and Late Static Binding: Provides mechanisms to resolve methods or properties at compile-time (static binding) or runtime (late static binding), enabling flexibility in inheritance hierarchies.
  • Traits: Facilitates code reuse across unrelated classes by allowing classes to include methods from one or more traits without using inheritance.
  • Magic Methods: Introduces special methods that enable dynamic behavior in a class, such as property overloading, dynamic method handling, and object invocation.
  • Interfaces: Establishes a contract or blueprint for classes, enforcing the implementation of specific methods while allowing multiple inheritance.
  • Constants: Defines immutable values within a class, ensuring consistent reference to fixed values.
  • Static Members: Declares properties and methods that belong to the class itself rather than any instance, shared across all instances.
  • Final Classes and Methods: Restricts inheritance for classes and prevents overriding for methods, ensuring immutability of certain behaviors or structures.
  • Overloading: Handles undefined or dynamically called methods and properties, enabling flexibility in method and property management.

OOP vs. Procedural Programming
In procedural programming, you write a sequence of instructions to perform a task. In OOP, you model the problem using objects.

Example of Procedural vs. OOP Approach:

Procedural:

$carBrand = "Toyota";  
$carColor = "Red";
echo "The $carColor $carBrand is driving!";

OOP:

class Car {  
public $brand;
public $color;

public function drive() {
return "The $this->color $this->brand is driving!";
}
}

$myCar = new Car();
$myCar->brand = "Toyota";
$myCar->color = "Red";
echo $myCar->drive();

As you can see how OOP groups related data and behavior into a reusable structure.

Abstract Class vs Interface

Inheritance:

  • Abstract Class: Supports single inheritance. A class can extend only one abstract class.
  • Interface: Supports multiple inheritance. A class can implement multiple interfaces.

Method Implementation:

  • Abstract Class: Can have both abstract methods (without implementation) and fully implemented methods.
  • Interface: Can only have method declarations (no implementation).

Properties:

  • Abstract Class: Can have properties (variables).
  • Interface: Cannot have properties.

Access Modifiers:

  • Abstract Class: Can have methods with any visibility (public, protected, or private).
  • Interface: All methods are implicitly public.

Use Case:

  • Abstract Class: Best suited when classes share common behavior or state.
  • Interface: Best for defining a contract or blueprint for unrelated classes.

Constructors:

  • Abstract Class: Can have a constructor.
  • Interface: Cannot have a constructor.

Constants:

  • Abstract Class: Can define constants.
  • Interface: Can also define constants (as of PHP 5.3).

Keyword Used:

  • Abstract Class: Declared using the abstract keyword.
  • Interface: Declared using the interface keyword.

Static Members:

  • Abstract Class: Can have static methods and properties.
  • Interface: Cannot have static methods or properties.

Multiple Use:

  • Abstract Class: Can be used with interfaces (a class can extend an abstract class and implement interfaces).
  • Interface: A class can implement multiple interfaces, enabling more flexible design.

When to Use:

  • Use Abstract Classes when you have a base class with shared functionality that other classes should inherit.
  • Use Interfaces when you need to ensure that unrelated classes adhere to the same contract or behavior.

2. Key Concepts of PHP OOP

Static Methods and Properties

Static properties and methods belong to the class rather than an instance of the class. They can be accessed without creating an object.

Example:

class MathHelper {  
public static $pi = 3.14159; // Static property

// Static method to calculate the area of a circle
public static function calculateCircleArea($radius) {
return self::$pi * $radius * $radius;
}
}

// Accessing static property and method without creating an object
echo "Value of PI: " . MathHelper::$pi . "<br>"; // Output: 3.14159
echo "Circle Area: " . MathHelper::calculateCircleArea(5) . "<br>"; // Output: 78.53975

Explanation:

  • static keyword is used to define static properties and methods.
  • self:: is used within the class to refer to static properties or methods.
  • Static members are shared across all instances of the class and do not require object instantiation.

Constants in Classes

Constants are values that do not change during the execution of a script. They are declared using the const keyword.

Example:

class AppConfig {  
const APP_NAME = "My PHP Application"; // Defining a class constant
const VERSION = "1.0.0";

public function getAppInfo() {
return self::APP_NAME . " - Version: " . self::VERSION;
}
}

echo AppConfig::APP_NAME . "<br>"; // Access constant directly
$config = new AppConfig();
echo $config->getAppInfo(); // Output: My PHP Application - Version: 1.0.0

Explanation:

  • Constants are defined using the const keyword and are always accessible using ClassName::CONSTANT.
  • They are immutable and can only hold scalar data types (string, number, etc.).

Final Keyword

The final keyword is used to prevent a class from being inherited or to prevent a method from being overridden in a subclass.

Example:

class BaseClass {  
final public function greet() {
return "Hello from BaseClass!";
}
}

class DerivedClass extends BaseClass {
// This would cause an error because the greet method is final
// public function greet() {
// return "Hello from DerivedClass!";
// }
}

$base = new BaseClass();
echo $base->greet(); // Output: Hello from BaseClass!

Explanation:

  • final on the greet method prevents it from being overridden in any subclass.
  • If final is used with a class, the class itself cannot be extended.

Magic Methods

Magic methods in PHP are special methods that start with __ and are invoked automatically during certain events in a class. Common magic methods include:

Example:

class MagicDemo {  
private $data = [];

// Called when setting a property that doesn't exist
public function __set($name, $value) {
$this->data[$name] = $value;
}

// Called when accessing a property that doesn't exist
public function __get($name) {
return isset($this->data[$name]) ? $this->data[$name] : "Property does not exist.";
}

// Called when invoking the object as a function
public function __invoke($message) {
return "You invoked this object with: $message";
}
}

$magic = new MagicDemo();
$magic->name = "John"; // Calls __set
echo $magic->name . "<br>"; // Calls __get; Output: John
echo $magic("Hello!"); // Calls __invoke; Output: You invoked this object with: Hello!

Explanation:

  • __set and __get handle dynamic property assignment and retrieval, useful for flexible and dynamic property management.
  • __invoke allows the object to be used as a function.

Interfaces

Interfaces define the structure of a class by specifying methods that a class must implement, without providing the implementation. They ensure consistency across different classes.

Example:

interface LoggerInterface {  
public function log($message);
}

class FileLogger implements LoggerInterface {
public function log($message) {
echo "Logging to a file: $message<br>";
}
}

class DatabaseLogger implements LoggerInterface {
public function log($message) {
echo "Logging to a database: $message<br>";
}
}

$fileLogger = new FileLogger();
$fileLogger->log("File log message."); // Output: Logging to a file: File log message.

$dbLogger = new DatabaseLogger();
$dbLogger->log("Database log message."); // Output: Logging to a database: Database log message.

Explanation:

  • The LoggerInterface defines a log method.
  • Both FileLogger and DatabaseLogger implement the interface, providing their own versions of the log method.

Classes and Objects

A class is a blueprint for creating objects, and an object is an instance of a class. You can think of a class as a template and an object as the actual product created from that template.

Example:

class Car {  
public $brand; // Property to store the brand of the car
public $color; // Property to store the color of the car

// Constructor to initialize the car object when created
public function __construct($brand, $color) {
$this->brand = $brand;
$this->color = $color;
}

// Method that defines behavior for the car
public function drive() {
return "The $this->color $this->brand is driving!";
}
}

// Creating an object of the Car class
$car1 = new Car("Toyota", "Red");

// Accessing the method of the object
echo $car1->drive(); // Output: The Red Toyota is driving!

Explanation:

  • The Car class has two properties: $brand and $color.
  • The __construct method initializes these properties when a new object is created.
  • The drive method returns a message using the object's properties.
  • $car1 is an object of the Car class with the brand Toyota and color Red. When we call $car1->drive(), the method returns a string describing the action of the car.

Properties and Methods

Properties are variables within a class that hold data, while methods are functions that define what the object can do.

Example:

class Bike {  
public $brand = "Hero"; // Property with a default value

// Method to describe the bike's behavior
public function ride() {
return "The $this->brand bike is being ridden.";
}
}

// Creating an object of the Bike class
$bike = new Bike();

// Accessing the method
echo $bike->ride(); // Output: The Hero bike is being ridden.

Explanation:

  • The $brand property is initialized with a default value, "Hero".
  • The ride method uses $this->brand to dynamically include the brand name in the output.
  • When we create an object ($bike) and call its ride method, it outputs the sentence describing the action.

Constructors and Destructors

Constructors (__construct) are special methods called automatically when an object is created. Destructors (__destruct) are called when the object is no longer in use or the script ends.

Example:

class User {  
// Constructor runs when a User object is created
public function __construct() {
echo "A user object is created.<br>";
}

// Destructor runs when the object is destroyed
public function __destruct() {
echo "The user object is destroyed.";
}
}

// Creating a User object
$user = new User();

// When the script ends, __destruct will be called automatically

Explanation:

  • The constructor prints a message when the object is created.
  • The destructor prints a message when the object is destroyed, which usually happens when the script ends or the object is explicitly unset.
  • This is useful for tasks like opening and closing database connections.

Access Modifiers

Access modifiers control the visibility of properties and methods.

Example:

class Employee {  
private $salary; // Private property, accessible only within this class

// Public method to set the salary
public function setSalary($amount) {
$this->salary = $amount;
}

// Public method to get the salary
public function getSalary() {
return $this->salary;
}
}

$employee = new Employee();
$employee->setSalary(5000); // Set the salary using the public method
echo $employee->getSalary(); // Output: 5000

Explanation:

  • The $salary property is marked as private, so it cannot be accessed directly outside the class.
  • Public methods setSalary and getSalary are used to modify and retrieve the value of $salary.
  • This encapsulation ensures that the property is protected from unintended modification.

Inheritance

Inheritance allows a child class to reuse properties and methods of a parent class, avoiding code duplication.

Example:

class Animal {  
public function eat() {
return "This animal eats food.";
}
}

class Dog extends Animal {
public function bark() {
return "This dog barks.";
}
}

$dog = new Dog();
echo $dog->eat(); // Output: This animal eats food.
echo $dog->bark(); // Output: This dog barks.

Explanation:

  • The Dog class extends the Animal class, inheriting its eat method.
  • The Dog class also defines its own method, bark.
  • This demonstrates how a subclass can extend functionality while reusing the parent class’s features.

Polymorphism

Polymorphism allows child classes to provide different implementations of methods defined in a parent class.

Example:

class Animal {  
public function sound() {
return "This animal makes a sound.";
}
}

class Cat extends Animal {
public function sound() {
return "Meow!";
}
}

$cat = new Cat();
echo $cat->sound(); // Output: Meow!

Explanation:

  • The Animal class defines a generic sound method.
  • The Cat class overrides this method with its own implementation.
  • When you call sound on a Cat object, it uses the overridden version in the child class.

Encapsulation

Encapsulation is the process of bundling data (properties) and methods that operate on that data within a single unit (class) while restricting direct access to some components (e.g., private properties). This ensures controlled access through getter and setter methods.

Example:

class BankAccount {  
private $balance; // Private property to store the account balance

// Constructor to initialize the balance
public function __construct($initialBalance) {
$this->balance = $initialBalance;
}

// Public method to get the balance
public function getBalance() {
return $this->balance;
}

// Public method to deposit money
public function deposit($amount) {
if ($amount > 0) {
$this->balance += $amount;
} else {
echo "Deposit amount must be positive.<br>";
}
}

// Public method to withdraw money
public function withdraw($amount) {
if ($amount > 0 && $amount <= $this->balance) {
$this->balance -= $amount;
} else {
echo "Invalid withdrawal amount.<br>";
}
}
}

// Creating a BankAccount object
$account = new BankAccount(1000);

// Accessing balance via method
echo "Initial Balance: " . $account->getBalance() . "<br>"; // Output: 1000

// Depositing money
$account->deposit(500);
echo "After Deposit: " . $account->getBalance() . "<br>"; // Output: 1500

// Attempting withdrawal
$account->withdraw(200);
echo "After Withdrawal: " . $account->getBalance() . "<br>"; // Output: 1300

Explanation:

  • The $balance property is private, meaning it cannot be accessed directly from outside the class.
  • Public methods (getBalance, deposit, withdraw) provide controlled access to modify or retrieve the balance.
  • Encapsulation ensures that the balance can only be manipulated in ways defined by the class, preventing invalid operations (e.g., withdrawing more than the balance).

Abstraction

Abstraction involves hiding the implementation details of methods and exposing only the essential features of a class. Abstract classes and interfaces are tools for abstraction in PHP.

Example:

abstract class Shape {  
// Abstract method with no implementation
abstract public function area();
}

class Circle extends Shape {
private $radius;

public function __construct($radius) {
$this->radius = $radius;
}

// Implementing the abstract method
public function area() {
return pi() * $this->radius * $this->radius;
}
}

class Rectangle extends Shape {
private $length;
private $width;

public function __construct($length, $width) {
$this->length = $length;
$this->width = $width;
}

// Implementing the abstract method
public function area() {
return $this->length * $this->width;
}
}

$circle = new Circle(5);
echo "Circle Area: " . $circle->area() . "<br>"; // Output: 78.539816339745

$rectangle = new Rectangle(10, 20);
echo "Rectangle Area: " . $rectangle->area() . "<br>"; // Output: 200

Explanation:

  • The Shape abstract class defines a common structure with an abstract area method.
  • Both Circle and Rectangle classes inherit from Shape and provide their specific implementation for calculating the area.
  • Abstract classes ensure that all child classes implement certain behaviors (area in this case) while leaving the details to the subclasses.

Traits

Traits allow the reuse of methods across multiple classes, avoiding duplication of code. They are used when multiple classes need the same functionality but don’t share a parent-child relationship.

Example:

trait Logger {  
public function log($message) {
echo "[LOG]: $message<br>";
}
}

class User {
use Logger; // Including the Logger trait

public function createUser($name) {
$this->log("Creating user: $name");
}
}

class Product {
use Logger; // Including the Logger trait

public function createProduct($productName) {
$this->log("Creating product: $productName");
}
}

$user = new User();
$user->createUser("Alice");
// Output: [LOG]: Creating user: Alice

$product = new Product();
$product->createProduct("Laptop");
// Output: [LOG]: Creating product: Laptop

Explanation:

  • The Logger trait defines a log method that can be reused in any class that includes it.
  • Both User and Product classes use the Logger trait to add logging functionality without duplicating the code.
  • Traits promote code reuse while keeping classes independent.

Overloading

PHP does not support traditional method overloading (methods with the same name but different parameters). However, it provides the __call and __callStatic magic methods for handling undefined methods.

Example:

class OverloadingDemo {  
// Handle undefined instance methods
public function __call($name, $arguments) {
return "Instance method '$name' called with arguments: " . implode(", ", $arguments);
}

// Handle undefined static methods
public static function __callStatic($name, $arguments) {
return "Static method '$name' called with arguments: " . implode(", ", $arguments);
}
}

$overload = new OverloadingDemo();
echo $overload->sayHello("John", "Doe") . "<br>";
// Output: Instance method 'sayHello' called with arguments: John, Doe

echo OverloadingDemo::sayGoodbye("Jane") . "<br>";
// Output: Static method 'sayGoodbye' called with arguments: Jane

Explanation:

  • __call handles calls to undefined instance methods.
  • __callStatic handles calls to undefined static methods.

--

--

No responses yet