The definitive guide to object-oriented programming in PHP

Learn the ins and outs of object-oriented programming in PHP, including classes, inheritance, abstract classes, traits, and more. With this guide, you'll be able to write efficient and scalable object-oriented code that's easy to maintain and reuse.

Organizing your code properly is a time-saver when building large applications and working across teams as a programmer. You should always strive to write organized, reusable, and, most importantly, maintainable code so that you can work efficiently across teams with fewer conflicts during development. In this article, you will learn everything you need to know about object-oriented programming and its benefits in your PHP code.

Prerequisites

To follow along with this article, you only need to know the basics of PHP. You should be familiar with some concepts, such as variables, functions, conditional statements, and loops.

Object-oriented programming in PHP

Object-oriented programming (OOP) is a programming paradigm that enables developers to structure and treat their applications like real-world objects by using classes to bundle data and functions in a single unit and using it throughout the application, thereby improving readability, reusability, maintainability, and more.

Aside from being one of the most popular programming paradigms, it is also the most used in PHP. Most PHP frameworks and CMSs, such as Laravel, Drupal, Symphony, October, CodeIgniter, and many others use OOP at their core. Even parts of the world's most popular website builder, WordPress, uses OOP.

Next, let's explore how to use classes in PHP.

Classes

As mentioned in the previous section, object-oriented programming is based on classes, and we'll explore them in this section. For example, if you want to create a User class, you can use the class keyword:

<?php
class User {

}
?>

That's it! You've just created your first class in PHP.

Class properties

PHP allows you to add properties to classes. Class properties are variables you define within the class curly brackets { } and can be used inside and outside the class. You can define a property inside a class:

class User {
    public $name = 'John Sanders';
    public $age = 25;
    protected $email = 'jsanders@ml.com'
    private $ssn = '123-45-6789';
}

The code above defines two public properties ($name and $age), one protected property ($email), and one private property ($ssn) inside the User class. All class properties are defined by prefixing the property with public, protected, or private keywords.

There are three levels of access in PHP. Public properties can be accessed inside and outside the class, and protected properties can be used inside the class that defines them and the classes that inherit them. In contrast, private properties can only be accessed within the class that defines them.

Class methods

You can add functions inside classes by defining them within the class' curly braces { }:

class User {
    // same as above...
    function greet() {
        echo 'Hello,  ' . $this->name . ' and welcome to the site!';
    }
}

The code above defines a greet function that prints a message to the user. Functions can also be either public or private, and you can define public, protected, and private functions inside the class by prefixing them with either public, protected, or private keywords:

class User {
    // same as above...

    public function getSSN() {
        return $this->ssn;
    }

    private function setSSN($ssn) {
        $this->ssn = $ssn;
    }
}    

The code above declares one public function, getSSN, and one private function, setSSN. The $this keyword is used to access the class properties and methods inside the class, which means you have to use $this->age instead of $age to access the public age property.

Creating objects

The User class you created in the previous sections doesn't do anything yet; to use the class, you need to create an object from the class. For example, to create an object based on the User class, you use the new keyword:

$user = new User;

The code above created an object, user1, using the User class as a blueprint. You can now use the properties and functions of the user class like this:

echo $user->name . ' is ' . $user->age . ' years old.';
echo "<br>";
echo $user->greet();
echo "<br>";
echo $user->getSSN();
echo "<br>";

The code above uses the single arrow (->) syntax to access the properties and functions of the user object and prints out the following in the browser:

first object result

The class you created in this section is not scalable because every object created with it will always have the same name, age, email, and SSN values. Let's explore how to make it dynamic in the following sections.

The __construct method

PHP provides a magic method, __construct, also known as the constructor method. It is used inside classes to initialize properties of new objects. Let's rewrite the User class to use a constructor method:

class User {
    public $name;
    public $age;
    public $email;
    private $ssn;

    function __construct($name, $age, $email, $ssn) {
        $this->name = $name;
        $this->age = $age;
        $this->email = $email;
        $this->ssn = $ssn;
    }

    function greet() {
        echo 'Hello,  ' . $this->name . ' and welcome to the site!';
    }

    public function getSSN() {
        return $this->ssn;
    }

    private function setSSN($ssn) {
        $this->ssn = $ssn;
    }
}

The code above creates a dynamic User class by requiring every object to provide its name, age, email, and SSN properties upon creation. You can now create an object on the User class:

$user1 = new User('Akanni William', 35, "williamak@ml.com", '123-45-6789');

$user2 = new User('Alabi Brandon', 25, "brandonal@cl.com", '987-65-4321');

echo $user1->name . ' is ' . $user1->age . ' years old.';
echo "<br>";
echo $user1->greet();
echo "<br>";
echo $user1->getSSN();

echo "<br>"; 
echo "<br>";

echo $user2->name . ' is ' . $user2->age . ' years old.';
echo "<br>";
echo $user2->greet();
echo "<br>";
echo $user2->getSSN();

The code above will print the following result using the name, age, email, and SSN properties upon object creation:

constructor method result

With this syntax, you can now create as many objects as you want with different details. Additionally, you will get an error if you do not provide all the properties listed inside the constructor for every object you create.

Note: Class names are case-insensitive, which means you can create a user object like this: $user2 = new user(... or like this: $user1 = new USer(.... However, you should always try to be consistent with naming by using the conventional camel-case, which starts with an uppercase letter.

Chaining class methods

PHP helps developers to write concise code by allowing chain class methods. For example, if you have a class that handles login and logout, you might also want to change the user's status accordingly. Here is how you can do that in PHP:

class Twita {
    function __construct(public $username, public $offlineStatus)
    {
        $this->username = $username;
        $this->offlineStatus = $offlineStatus;
    }

    function login(){
        echo "Welcome, " . $this->username . " to your account ";
        echo  "<br>";
        return $this;
    }

    function logout(){
        echo $this->username . "has logged out";
        echo  "<br>";
        return $this;
    }

    function setOnlineStatus(){
        $this->offlineStatus = false;
        echo $this->username. "is now online";
        echo  "<br>";
        return $this;
    }

    function setOfflineStatus(){
        $this->offlineStatus = true;
        echo $this->username. "is now offline";
        echo  "<br>";
        return $this;
    }
}

The code above declares a Twita class with username and offlineStatus properties and login, logout, setOnlineStatus, and setOfflineStatus methods. To chain the methods, you can use the single arrow (->) syntax:

$twit = new Twita('Abolade William', true);
$twit->login()->setOnlineStatus()->logout()->setOfflineStatus();

You should get a response that looks like this:

method chain

Note: For method chaining to work, all the functions need to return $this at the end of each function.

Common class constants

The following magic constants in PHP are especially useful when working with classes:

  • The CLASS constant returns the name of the class where it is used.
  • The FILE constant returns the full path of the file where it is used.
  • The LINE constant returns the line where it is used.
  • The METHOD constant returns the name of the method where it is used.

Here's an example of the constants inside a class:

Class Vehicle {
    public $name;
    public $color;
    public $price;
    public $brand;

    function __construct($name, $color, $price, $brand) {
        $this->name = $name;
        $this->color = $color;
        $this->price = $price;
        $this->brand = $brand;
    }

    public function getClassConstant(){
        echo "Name of class is: " .__CLASS__ . "<br>";
    }

    public function getFileConstant(){
        echo "Full path of file is: " . __FILE__ . "<br>";
    }

    public function getLineConstant(){
        echo "Line of code is: " . __LINE__ . "<br>";
    }

    public function getMethodConstant(){
        echo "Name of method is: " . __METHOD__ . "<br>";
    }
}

The code above defines a class Vehicle with four properties ($name,$color,$price, and $brand) and four methods, (getClassConstant, getFileConstant, getLineConstant, and getMethodConstant) that returns the respective values of the constants. You can now use the Vehicle class like this:

$vehicle = new Vehicle('Lexus', 'Black', 100000, 'Lexus');
$vehicle->getClassConstant();
$vehicle->getFileConstant();
$vehicle->getLineConstant();
$vehicle->getMethodConstant();

The code above should return the following:

class constants

The constants declared in the block of code above are particularly useful, especially when building developer tools and you need to print out the line of code, file, or function preventing the code from running correctly.

Comparing objects

If, for any reason, you want to compare objects of a class in PHP, you can do so with the equals operator (==) and the strict equals operator (===):

class Numbers {
    public $num1;
    public $num2;
    public $num3;

    function __construct($num1, $num2, $num3) {
        $this->num1 = $num1;
        $this->num2 = $num2;
        $this->num3 = $num3;
    }

    function add() {
        return $this->num1 + $this->num2 + $this->num3;
    }

    function multiply() {
        return $this->num1 * $this->num2 * $this->num3;
    }

    function divide() {
        return $this->num1 / $this->num2 / $this->num3;
    }

    function subtract() {
        return $this->num1 - $this->num2 - $this->num3;
    }
}

$numbers1 = new Numbers(10, 5, 2);
$numbers2 = new Numbers(10, 5, 2);

echo ($numbers1 == $numbers2) ? 'True' : 'False'; // True
echo ($numbers1 === $numbers2) ? 'True' : 'False'; // False

The code above defines a Numbers class that takes in three numbers and functions that perform basic arithmetic operations on them. Then, it creates two instances of the class and compares them with the equals and strict equals operators. The first comparison will return true because both instances hold the same values; however, the second check is strict and will return false because the objects are of different instances.

Encapsulation in PHP

Encapsulation in programming refers to bundling related data and functionalities in a single unit. This approach improves reusability, readability, and maintainability.

Encapsulation can be achieved in PHP by using classes to organize your code. For example, if you are building an e-commerce application, you can have a class that handles user authentication and another that handles the checkout process. This allows multiple developers to collaborate and work on a project with minimal or no conflict while simultaneously focusing on different parts of the code.

Class inheritance

When developing applications using an object-oriented paradigm, you can write even lesser code using class inheritance in PHP. For example, when building an e-commerce application, you might have a class called Product with properties like name, price, description, and image and methods like formatPrice and addToCart. You can create the class like this:

class Product {
    public $name;
    public $price;
    public $desc;
    public $image;

    function __construct($name, $price, $desc, $image) {
        $this->name = $name;
        $this->price = $price;
        $this->desc = $desc;
        $this->image = $image;
}
    final function formatPrice(){
        return "$". $this->price . "<br>";
    }
    public function addToCart(){
        return $this->name . " has been added to cart  <br> <br>";
    }
}

The code above defines a class Product with four properties ($name, $price, $desc, and $image) and two methods (formatPrice and addToCart).

Note: The final keyword that precedes the formatPrice functions means that the function cannot be overridden inside the classes that inherit the Product class.

You can now create multiple classes based on the Product class:

class Laptop extends Product {
    public $cpu;
    public $ram;
    public $storage;

    function __construct($name, $price, $desc, $image, $cpu, $ram, $storage) {
        parent::__construct($name, $price, $desc, $image);
        $this->cpu = $cpu;
        $this->ram = $ram;
        $this->storage = $storage;
    }

    public function addToCart(){
        return $this->name . " has been overidden  <br> <br>";
    }

    public function formatLaptopSpecs(){
        return $this->name . "<br>" . "i" . $this->cpu . " Processor <br>" . $this->ram . "GB RAM <br>" . $this->storage . "GB SSD <br> <br>";

    }
}

class Phone extends Product {
    public $os;
    public $ram;
    public $storage;

    function __construct($name, $price, $desc, $image, $os, $ram, $storage) {
        parent::__construct($name, $price, $desc, $image);
        $this->os = $os;
        $this->ram = $ram;
        $this->storage = $storage;
    }

    public function formatPhoneSpecs(){
        return $this->name . "<br>" . $this->os . "<br>" . $this->ram . "GB RAM <br>" . $this->storage . "GB <br> <br>";

    }
}

class TV extends Product {
    public $screenSize;
    public $resolution;
    public $refreshRate;

    function __construct($name, $price, $desc, $image, $screenSize, $resolution, $refreshRate) {
        parent::__construct($name, $price, $desc, $image);
        $this->screenSize = $screenSize;
        $this->resolution = $resolution;
        $this->refreshRate = $refreshRate;
    }

    public function formatTVSpecs(){
        return $this->name . "<br>" . "Screen Size: " . $this->screenSize . "<br>" . "Resolution: " .$this->resolution . "<br>" . "Refresh Rate: " .$this->refreshRate . "<br> <br>";
    }
}

The code above defines three different products with additional properties and methods by extending the Product class. The extends keyword is used to extend the Product class, also known as the parent class in this case.

Note: The Laptop class overrode the Product class' addToCart function by defining another addToCart function of its own.

You can now use the classes like this:

$tv = new TV('LG TV', 1000, 'A TV', 'image.jpg', '55"', '4K', '60Hz');
echo $tv->formatTVSpecs();
echo $tv->addToCart();

$phone = new Phone('iPhone 12', 1000, 'A phone', 'image.jpg', 'iOS', 8, 256);
echo $phone->formatPhoneSpecs();
echo $phone->addToCart();

$laptop = new Laptop('Macbook Pro', 1000, 'A laptop', 'image.jpg', '5', 16, 512);
echo $laptop->formatLaptopSpecs();
echo $laptop->addToCart();  

The code above defines one instance of the classes that extends the Product class and will return the following result:

inheritance result

Note: A class that extends another class is called a child class, while a class that is being extended is called a parent class.

Benefits of inheritance

Inheritance allows you to reuse code by creating new classes that inherit the attributes and methods of existing classes. This can save a lot of time since you don't have to write the same code repeatedly. It can make code easier to maintain because if a bug is found in the parent class, it only needs to be fixed once, and all the child classes will automatically inherit the fix.

Inheritance creates a hierarchical class structure, making the code easier to understand and organize. You can use the parent-child relationship to group classes into logical categories, making navigating and maintaining the codebase easier. Finally, it enables you to add new features or functionality to an application without modifying the existing code.

Static members

PHP allows you to define properties and methods bound to the class and not instances using the static keyword. We'll explore how to do that in this section.

Static properties

Static properties are only available to use on the class itself and not the instances. For example, if you want to keep track of how many times the User class has been used, you can do so like this:

class User {
    public $name;
    public $email;
    public $password;

    function __construct($name, $email, $password) {
        $this->name = $name;
        $this->email = $email;
        $this->password = $password;
    }
    static $usersCount = 0;
}

The code above defines a static property, usersCount, inside the User class. You can now access the property by prefixing it with the self:: syntax:

$user = new User('Abolade William', 'willabolade@ml.com', 'password');
echo User::$usersCount; // 0

The code above will return the value of usersCount, 0.

Note: You must add the $ to the property.

Let's take a look at static methods next.

Static methods

If you want to update the usersCount every time an instance of User is created, you can use the static method:

class User {
    // same as above...

    static function updateUsersCount(){
        self::$usersCount++;
        return self::$usersCount . " users registered";
    }
}

The code above defines an updateUsersCount method that updates the usersCount property. You can now call the updateUsersCount function on the class each time an instance of the User class is created:

$user = new User('Abolade William', 'willabolade@ml.com', 'password');
echo User::updateUsersCount(); // 1 users registered
echo User::$usersCount; // 0

Note: You can also access static members inside the class with the class name instead of self:: like so: User::$usersCount.

Abstract classes and methods

PHP allows you to define classes with functions that are not defined yet but forces classes that extend them to define their implementation. Abstract classes and methods are created by prefixing the class and method names with the abstract keyword.

For example, if you want to force every child class that extends the Product class to implement a formatSpec function, you can do so with an abstract class:

abstract class Product {
    public $name;
    public $price;
    public $desc;
    public $image;

    function __construct($name, $price, $desc, $image) {
        $this->name = $name;
        $this->price = $price;
        $this->desc = $desc;
        $this->image = $image;
    }

    public function addToCart(){
        return $this->name . " has been added to cart  <br> <br>";
    }

    abstract function formatSpec();
}

The code above defines an abstract Product class with an abstract formatSpec method. You cannot create an object based on an abstract class. You need to create classes that extend it.

Note: An abstract class must have at least one abstract method.

You can now create a child class that extends the abstract class:

class Laptop extends Product {
    public $cpu;
    public $ram;
    public $storage;

    function __construct($name, $price, $desc, $image, $cpu, $ram, $storage) {
        parent::__construct($name, $price, $desc, $image);
        $this->cpu = $cpu;
        $this->ram = $ram;
        $this->storage = $storage;
    }

    public function formatSpec(){
        return $this->name . "<br>" . "i" . $this->cpu . " Processor <br>" . $this->ram . "GB RAM <br>" . $this->storage . "GB SSD <br> <br>";
    }
}

The code above defines a Laptop class that extends the Product class and implements the formatSpec function inside it.

Note: You will get an error if you don't implement the abstract methods inside the classes that extend abstract classes.

Interfaces

PHP takes abstraction to the next level by allowing you to define functions that classes must implement inside an interface. An interface is similar to an abstract class but can only have public functions and cannot contain properties and defined methods. Here's an example of an interface:

interface ProductInterface {
    public function addToCart();
    public function formatSpec();
}

The code above defines a ProductInterface with two methods that the child class has to implement. A child class that implements the ProductInterface will look like this:

class Phone implements ProductInterface {
    public $name;
    public $price;
    public $desc;
    public $image;
    public $os;
    public $ram;
    public $storage;

    function __construct($name, $price, $desc, $image, $os, $ram, $storage) {
        $this->name = $name;
        $this->price = $price;
        $this->desc = $desc;
        $this->image = $image;
        $this->os = $os;
        $this->ram = $ram;
        $this->storage = $storage;
    }

    public function addToCart(){
        return $this->name . " has been added to cart  <br> <br>";
    }

    public function formatSpec(){
        return $this->name . "<br>" . $this->os . "<br>" . $this->ram . "GB RAM <br>" . $this->storage . "GB <br> <br>";
    }
}

The code above defines a Phone class based on the ProductInterface using the implements keyword.

Note: The child class must implement all the methods inside the interface that it implements, or the code will throw an error.

Polymorphism

The word polymorphism originated from two Greek words, poly, which means different or many, and morph, which means form or shape. Polymorphism is a fundamental concept in OOP that allows objects of different classes to be used interchangeably while retaining their unique behavior. It enables you to write more flexible, modular, and easier-to-maintain code.

Polymorphism in PHP is achieved by using abstract classes or interfaces to write code that can be easily extended and modified without modifying the existing code. It also allows you to write more generic code that can work with objects of different classes rather than writing separate code for each class. This reduces code duplication and increases code reusability, making your code more efficient and maintainable.

Traits

PHP allows you to define and use functions that don't fit in a class with traits. A trait is a collection of functions that can be used inside classes without inheriting, extending, or implementing a class or interface.

For example, you can define a trait that contains an addBrandLogo function, which can be used in all other classes to add a brand logo to product images:

trait AllProductsFunctions {
    public function addBrandLogo(){
        return "Brand logo added";
    }
}

The code above defines a trait (AllProductsFunctions) with a method (addBrandLogo) inside it. This trait can now be used inside other classes using the use keyword:

  class Phone implements ProductInterface {
// same as before...
    use AllProductsFunctions;
// same as before...
}

The function inside the AllProductsFunctions trait can now be used on all objects based on the Phone class. You can use multiple traits in a class by separating the traits' names with a comma.

Note: You can override a trait method by defining a method with the same name inside the class, and a trait method with the same name will override the method that the class inherited.

Benefits of object-oriented programming

Now that you have learned all the basics and advanced concepts you need to know to begin object-oriented programming in PHP and how it helps you to design and create scalable, maintainable, and reusable applications, let's explore some of the significant benefits of OOP in the following sections.

Code reusability

One of the primary benefits of OOP in PHP is code reusability. This means that you can reuse code blocks and functions throughout applications without having to rewrite the entire code from scratch. With OOP, you can create objects that encapsulate a specific set of functionalities and reuse these objects across multiple applications or even within the same application. This helps to reduce the development time and effort required to create complex applications while improving the overall quality of the code.

Scalability

OOP in PHP also makes it easier to scale applications as they grow. By using objects to encapsulate different parts of the application, you can modify or add new functionalities without affecting other parts of the codebase. This makes it easier to maintain and update applications while reducing the risk of introducing bugs or errors. Additionally, OOP makes it easier to modularize code, allowing multiple developers to work on specific parts of the application without needing to understand the entire codebase.

Encapsulation

Encapsulation is a crucial principle of OOP in PHP that helps improve an application’s security and maintainability. With encapsulation, you can hide the internal workings of an object from the rest of the application. This means that the object can only be accessed through its public interface, which helps to prevent unauthorized access to critical parts of the application. Encapsulation also makes it easier to modify or update the internal workings of an object without affecting other areas of the codebase.

Inheritance

Inheritance is another important concept in OOP that allows developers to create new classes based on existing classes. With inheritance, you can inherit the properties and methods of a parent class and then modify or extend them to create new functionality. This makes reusing code easier, reduces redundancy, and creates more efficient applications. Additionally, inheritance helps to promote consistency across different parts of an application, which makes it easier to maintain and update the codebase.

Testability

Finally, OOP in PHP makes it easier to test and debug applications. By encapsulating code into objects, you can isolate specific parts of the application and test them in isolation from other areas of the codebase. This helps to identify bugs and errors more quickly and makes it easier to fix them without affecting the rest of the application. Additionally, OOP makes it easier to write automated tests that can be used to test the application at scale, which helps to improve the overall quality of the code.

Using OOP in PHP offers a range of benefits that can help you create more scalable, maintainable, and reusable applications. As such, OOP has become a popular programming technique for building modern web applications and will likely remain a vital part of the developer toolkit for years.

Conclusion

And that's it! I hope this article achieved its aim of teaching you object-oriented programming in PHP. You learned about classes in PHP, including class properties, class methods, chaining class methods in PHP, common class constants in PHP, and class inheritance. We also explored how to use static members, abstract classes and methods, interfaces, polymorphism, and traits.

Finally, you learned some of the significant benefits of object-oriented programming in PHP.

What to do next:
  1. Try Honeybadger for FREE
    Honeybadger helps you find and fix errors before your users can even report them. Get set up in minutes and check monitoring off your to-do list.
    Start free trial
    Easy 5-minute setup — No credit card required
  2. Get the Honeybadger newsletter
    Each month we share news, best practices, and stories from the DevOps & monitoring community—exclusively for developers like you.
    author photo

    Adebayo Adams

    Being a self taught developer himself, Adams understands that consistent learning and doing is the way to become self sufficient as a software developer. He loves learning new things and firmly believes that learning never stops.

    More articles by Adebayo Adams
    Stop wasting time manually checking logs for errors!

    Try the only application health monitoring tool that allows you to track application errors, uptime, and cron jobs in one simple platform.

    • Know when critical errors occur, and which customers are affected.
    • Respond instantly when your systems go down.
    • Improve the health of your systems over time.
    • Fix problems before your customers can report them!

    As developers ourselves, we hated wasting time tracking down errors—so we built the system we always wanted.

    Honeybadger tracks everything you need and nothing you don't, creating one simple solution to keep your application running and error free so you can do what you do best—release new code. Try it free and see for yourself.

    Start free trial
    Simple 5-minute setup — No credit card required

    Learn more

    "We've looked at a lot of error management systems. Honeybadger is head and shoulders above the rest and somehow gets better with every new release."
    — Michael Smith, Cofounder & CTO of YvesBlue

    Honeybadger is trusted by top companies like:

    “Everyone is in love with Honeybadger ... the UI is spot on.”
    Molly Struve, Sr. Site Reliability Engineer, Netflix
    Start free trial
    Are you using Sentry, Rollbar, Bugsnag, or Airbrake for your monitoring? Honeybadger includes error tracking with a whole suite of amazing monitoring tools — all for probably less than you're paying now. Discover why so many companies are switching to Honeybadger here.
    Start free trial
    Stop digging through chat logs to find the bug-fix someone mentioned last month. Honeybadger's built-in issue tracker keeps discussion central to each error, so that if it pops up again you'll be able to pick up right where you left off.
    Start free trial
    “Wow — Customers are blown away that I email them so quickly after an error.”
    Chris Patton, Founder of Punchpass.com
    Start free trial