The definitive guide to object-oriented programming in PHP
Tags: php
Published: 2024-04-01 07:00:00

*This article was originally written by [Adebayo Adams](https://www.honeybadger.io/blog/authors/adebayoadams/) on the [Honeybadger Developer Blog](https://www.honeybadger.io/blog/in-depth-guide-to-object-oriented-programming-in-php/).*

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
<?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:

```php
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 `{ }`:

```php
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:

```php
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:

```php
$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:

```php
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](https://www.honeybadger.io/images/blog/posts/in-depth-guide-to-object-oriented-programming-in-php/first_object_result.png)

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:

```php
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:

```php
$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](https://www.honeybadger.io/images/blog/posts/in-depth-guide-to-object-oriented-programming-in-php/constructor_method_result.png)

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:

```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:

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

You should get a response that looks like this:

![method chain](https://www.honeybadger.io/images/blog/posts/in-depth-guide-to-object-oriented-programming-in-php/method_chain.png)

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:

```php
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:

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

The code above should return the following:

![class constants](https://www.honeybadger.io/images/blog/posts/in-depth-guide-to-object-oriented-programming-in-php/class_constants.png)

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 (`===`):

```php
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:

```php
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:

```php
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 `extend`ing 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:

```php
$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](https://www.honeybadger.io/images/blog/posts/in-depth-guide-to-object-oriented-programming-in-php/inheritance_result.png)

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

```php
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:

```php
$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:

```php
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:

```php
$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:

```php
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:

```php
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:

```php
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:

```php
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:

```php
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:

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