DEV Community

spO0q
spO0q

Posted on • Edited on

Reusing PHP code

Developers can leverage OOP (Object-oriented programming) to reuse code, but, in my experience, beginners tend to have difficulties in understanding when to use a specific pattern or a built-in PHP structure.

Why do we want to reuse code?

Business teams usually cannot afford to develop the same modules from scratch every time they need a new feature.

That's why dev teams use patterns and OOP principles to reduce the TTM (Time To Market) and bugs while keeping the code maintainable and extensible.

Code reuse is a craft, though, that requires experience and accuracy. There's no magic recipe you can apply blindly, but a few design principles can prevent very bad failures.

You won't be able to run on water, but, at least, you may avoid common traps.

Inheritance is not meant for code reuse

Basic inheritance with the keyword extends is one of the first tricks you'll learn as a beginner.

It's pretty straightforward: you have a mother class and child classes, perhaps childs for the child classes, and so on.

It may look fine at first sight. You define some methods and properties in the mother class, and child classes can override or reuse them.

Cool but quite limited, as modern apps usually require more sophisticated patterns, as not everything can be represented as a parent-child relationship.

Besides, it's easy to break the encapsulation by exposing the implementation (~ code integrity), which can lead to nasty bugs. On top of that, PHP does not support multiple inheritance like some other programming languages, so you'll have to stick with single inheritance.

Fortunately, there are other ways such as polymorphism to fix the problem, but even such approaches are not meant for every situation.

Interfaces vs. PHP Traits

Traits allow reusing code, so you can share common methods between multiple classes that do not have the same purpose or behaviour. Using a PHP trait can be better than implementing an interface. But when?

Traits can help when the implementation is the same everywhere, and interfaces are meant for the exact opposite case. In other words, when the the methods share a common structure but their bodies differ, the interface makes sense.

We want to force classes to declare some methods, but the implementation is not our problem. Such polymorphic approach allows other parts of the code to use objects without knowing their implementation.

In contrast, if you have to re-implement the same interface over and over without modifying anything, you will only generate duplicates. Instead, you may use PHP Traits.

However, Traits are not perfect at all, and some PHP developers simply prefer not using them, as it's still possible to modify the visibility of the imported methods!

Setters can be dangerous

The idea with encapsulation is to protect objects and their properties by preventing unwanted modifications from external code.

That's why you only want to use the public keyword in specific cases. Most of the time, methods and properties should stay private or protected.

The problem is that devs might be tempted to introduce setters to bypass this fundamental mechanism:

class Product
{
    private int $id;

    // don't use that, use a constructor instead
    public function setId(int $id): int
    {
        $this->id = $id;
    }
}
Enter fullscreen mode Exit fullscreen mode

Here $id is declared as private, but the setter breaks its encapsulation, allowing anyone to change the ID at will.

In a nutshell, setters are not meant for code reuse.

Shallow copy with clone

You can clone objects to reuse them without affecting existing instances.

Just write clone before the object you want to clone.

You can also define the magic method clone() inside the associated class to fine tune the copy or forbid it.

Dependency Injection (PHP 8)

An object called service is injected in another object called client. Services are dependencies.

Instead of hardcoding classes inside the client's constructor and instanciate them manually, we pass them directly as arguments:

public function __construct(
    protected Service $service
) {}
Enter fullscreen mode Exit fullscreen mode

Many PHP frameworks such as Symfony use similar approaches, but they usually add some magic to instanciate dependencies, so you don't have to handle it.

Behind the scene, there are containers and interfaces that have the necessary information to set your dependencies. You may find related terms such as "resolvers" or "autowire."

You may read PHP-DI to dig further.

Wrap up

We only saw a few tricks to reuse PHP code and some traps to avoid, but these ones are quite frequent.

Top comments (0)