An Introduction to Object-Oriented PHP
Introduction: Beyond a List of Instructions
Early programming was largely procedural—a list of instructions executed from top to bottom. While effective for simple tasks, this approach becomes difficult to manage as software grows, leading to tangled, unpredictable code. Object-Oriented Programming (OOP) was created to solve this complexity.
The core idea of OOP is to model your application using "objects" that represent real-world concepts. These objects bundle their own data (properties) and the behaviours they can perform (methods) into a single, self-contained unit. The blueprint for creating these objects is called a class. We then create individual objects, which are specific instances of that class, using the new keyword. For example, a
Carclass acts as a blueprint, defining that all cars have properties likecolourand methods likestartEngine(). An actual red Ford Fiesta is an object, or a specific instance, created from that blueprint. This article explores the four foundational pillars of OOP that allow us to build robust, maintainable, and scalable applications in PHP.
Pillar 1: Encapsulation (Hiding Complexity)
Encapsulation is the practice of bundling data and the methods that operate on that data into a single unit and restricting direct access to an object's internal state. This creates a protective barrier, or capsule, around the object's core data, ensuring its integrity. PHP uses three access modifier keywords (
public,private, andprotected) when declaring properties and methods within a class. These modifiers enforce this protective barrier and control the visibility of your object's data and behaviours.
To provide controlled access to private properties, we use public "getter" and "setter" methods. A setter acts as a gatekeeper, allowing you to validate data before a property's value is changed. A getter provides safe, read-only access to a property.
Encapsulation in Practice
An object created from the following Advert class is a perfect example of the principles of encapsulation. The $status and $price properties are private, and their values can only be read or modified through the public getter and setter methods, which enforce the object's business rules.
class Advert { private string $status; private float $price; public function __construct(float $initialPrice) { $this->status = 'draft'; $this->setPrice($initialPrice); } public function setStatus(string $newStatus): void { $allowedStatuses = ['draft', 'published', 'archived']; if (in_array($newStatus, $allowedStatuses)) { $this->status = $newStatus; } } public function getStatus(): string { return $this->status; } public function setPrice(float $newPrice): void { if ($newPrice >= 0) { $this->price = $newPrice; } } public function getPrice(): float { return $this->price; } } ``` ## Pillar 2: Inheritance (Reusing Code) >Inheritance is a mechanism that allows a new class (the "child") to be based on an existing class (the "parent"), automatically receiving all of its public and protected properties and methods. This is a powerful tool for code reuse, adhering to the DRY (Don't Repeat Yourself) principle. In PHP, this relationship is established with the extends keyword. A protected property is similar to a private one, but any child class can also access it. #### Inheritance in Practice > In this example, PhysicalBook and DigitalDownload both extend the base Product class to inherit its common logic, while also adding their own specific properties. > ```PHP name = $name; $this->price = $price; } public function getDisplayText(): string { return "{$this->name} - £{$this->price}"; } } Class PhysicalBook extends Product. { private float $weightKg; public function __construct(string $name, float $price, float $weightKg) { parent::__construct($name, $price); $this->weightKg = $weightKg; } public function getShippingWeight(): string { return "{$this->weightKg} kg"; } } $book = new PhysicalBook('The PHP Blueprint', 49.99, 1.2); echo $book->getDisplayText(); // Uses method from parent class``` ``` ##Pillar 3: Polymorphism (Creating Flexibility) >Polymorphism, which means "many shapes," is the ability for objects of different classes to be treated as if they belong to a common type. It allows us to write flexible code that can work with a variety of objects, as long as they all share the same interface. In PHP, we achieve this with an interface. An interface is like a contract that defines a set of public methods that a class must implement. ###Polymorphism in Practice >Here, UserReport and SalesReport are different classes, but because they both implement the Exportable interface, they can be used by the same generateExportFile function. ```PHP exportAsCsv(); file_put_contents('export.csv', $csvData); } ``` ##Pillar 4: Abstraction (Simplifying Reality) >Abstraction is the principle of hiding complex, unnecessary details from the user of an object and only exposing the essential features they need to interact with. It is the result of applying encapsulation and polymorphism effectively. In our PHP code, interfaces and abstract classes are the primary tools for abstraction. When our generateExportFile() function accepts an Exportable object, it is relying on abstraction. It doesn't know or care how each report class generates its CSV data; it only knows that the exportAsCsv() method is guaranteed to exist. ##PHP's Full OOP Toolkit >Beyond the four main pillars, modern PHP provides a rich set of features for object-oriented programming. 1. Classes & Objects: A class is the core blueprint that defines properties and methods. An object is the instance you create from that blueprint using the new keyword (e.g., $advert = new Advert(). 2. Constructors & Destructors: These are "magic methods" that are called automatically. The __construct() method is called when a new object is created, making it the perfect place to set initial properties. 3. Abstract Classes: An abstract class is a hybrid between a class and an interface. It cannot be instantiated itself, but other classes can extend it. 4. Traits: A mechanism for code reuse in single-inheritance languages like PHP, allowing you to insert a group of methods into multiple, independent classes. 5. Static Methods & Properties: These belong to the class itself, not to an individual object instance, and are called using the scope resolution operator (::). ## Applying OOP: An Introduction to Design Patterns > Once you have a firm grasp of the pillars of OOP, the next step is to learn how to combine these principles to solve common, recurring problems in software development. This is where Design Patterns come in. > A design pattern is not a finished piece of code, but rather a reusable blueprint or a well-documented solution to a common architectural challenge. They provide a shared vocabulary for developers and are a direct application of the OOP principles we've discussed. Here are a few examples: * Singleton Pattern: Ensures that a class has only one instance and provides a global point of access to it. Our Database class is a perfect candidate for this pattern, as we only ever want one database connection object per request. * Repository Pattern: Abstracts the data layer. We used this in our RBAC system with the RoleRepository and PermissionRepository. These classes encapsulate all the logic for fetching and persisting their respective entities. * Factory Pattern: Provides an interface for creating objects in a superclass but lets subclasses alter the type of ob##jects that will be created, making the code more flexible. These patterns are the building blocks of a professional architect's toolkit. We will explore them in much greater detail in the "Advanced Topics" section. ##Conclusion: Thinking in Objects > Mastering Object-Oriented Programming is about more than just learning new syntax; it's about learning a new way to think by using the four pillars: Encapsulation, Inheritance, Polymorphism, and Abstraction. We move beyond writing simple scripts and begin architecting robust, scalable, and maintainable applications. This object-oriented foundation is the key to managing the complexity of a large, unified application, allowing us to build a system from a collection of small, independent, and reusable components that work together to create a powerful whole.