The 5 Most Common Design Patterns in PHP Applications

Author profile picture

@arslanArslan

Full Stack developer /Lead Developer/Project Manager/Software Engineer

If you think that the number one pattern is Singleton then you are fired! The Singleton pattern is already deprecated, and not wanted and even hated.
Let’s take a look at the 5 most commonly used designed patterns in the PHP world these days.

Factory

You should use factories when you want to build an object. That’s right — build and not create. You don’t want to have a factory just to create a new object. When you build the object you first create it and then initialize it. Usually, it requires to perform multiple steps and apply certain logic. With that, it makes sense to have all that in one place and re-use it whenever you need to have a new object built in the same way. Basically, that’s the point of the factory pattern.
It’s a good idea to have an interface for your factory and have your code depended on it and not on a concrete factory. With that, you can easily replace one factory with another whenever you need it.
Next, we implement our factory interface with the following class:
interface FriendFactoryInterface {
    public function create() : Friend
}
That’s pretty simple and yet powerful design pattern!

Strategy

It is used to hide implementation details of algorithms needed to perform an operation. Having strategies, the client can choose needed algorithm without knowing actual implementation and apply it to perform the operation.
Let’s say we need to create a library that transfers the data from one data source to another. For example, we need to transfer the data from the database to the csv file, or from the spreadsheet to the json file. How would you do that?
First, we need to create respective strategies to read the data from the storage. Let’s call them Readers. Next, we need to create respective strategies to write the data to the storage. Let’s call them Writers.
Therefore, we will have 2 Readers to read the data either from the database or from the spreadsheet. Accordingly, we will have 2 Writers to write the data either into the csv file or into the json file.
Important: the client that will work with our strategies should not care about their implementations. Therefore, we should also define interfaces for our strategies. That way, the client will know only about the methods defined by the strategy interfaces and work only with them, and what happens behind the scene is not its problem.
Finally, we need to create the client that will select needed strategies based on from where and to where it needs to transfer the data.
Let’s see all that in action:
interface ReaderInterface { 
    public function start() : void;
    public function read() : array;
    public function stop() : void;
}
interface WriterInterface {
   public function start() : void;
   public function write(array $data) : void;
   public function stop() : void;
}
class DatabaseReader implements ReaderInterface {
    ...
}
class SpreadsheetReader implements ReaderInterface {
    ...
}
class CsvWriter implements WriterInterface {
    ...
}
class JsonWriter implements WriterInterface {
    ...
}
class Transformer {
    
    ...
    public function transform(string $from, string $to) : void {
        $reader = $this->findReader($from);
        $writer = $this->findWriter($to);
        
        $reader->start();
        $writer->start();
        try {
            foreach ($reader->read() as $row) {
                $writer->write($row);
            }
         } finally {
             $writer->stop();
             $reader->stop();
         }
     }
     ...
}
As you can see, the transformer which is the client of our strategies doesn’t really care about the implementations with which it works. All it cares about is the methods defined by our strategy interfaces.

Adapter

It is used to turn a foreign interface into a common interface. Let’s assume that in the project you get the data from some storage using the following class.
class Storage {
    private $source;
    
    public function __constructor(AdapterInterface $source) {
        $this->source = $source;
    }
    public function getOne(int $id) : ?object {
        return $this->source->find($id);
    }
    
    public function getAll(array $criteria = []) : Collection {
        return $this->source->findAll($criteria);
    }
}
Note that the storage doesn’t work directly with the source but instead it works with the adapter of the source.
Moreover, the storage doesn’t know anything about concrete adapters. It refers to the adapter interface only. Thus, the concrete implementation of the provided adapter is a complete black-box for it.
Here’s an example of the adapter interface
interface AdapterInterface {
    public function find(int $id) : ?object;
    public function findAll(array $criteria = []) : Collection;
}
Now, let’s assume that we use some library to access the MySQL database. The library dictates its own interface and it looks like the following:
$row = $mysql->fetchRow(...);
$data = $mysql->fetchAll(...);
As you can see, we cannot integrate this library just like that into our storage. We need to create an adapter for it like below:
class MySqlAdapter implements AdapterInterface {
    
     ...
     public function find(int $id) : ?object {
         
         $data = $this->mysql->fetchRow(['id' => $id]);
         // some data transformation
     }
     public function findAll(array $criteria = []) : Collection {
              
         $data = $this->mysql->fetchAll($criteria);
         // some data transformation
     }
   
     ...
}
After that, we can inject it into the Storage just like this:
$storage = new Storage(new MySqlAdapter($mysql));
If later we decide to use another library instead of that one we will only have to create another adapter for that library just like we did above, and then, inject the new adapter into the Storage. As you can see, in order to use a different library to get the data from the database we don’t need to touch a thing within the Storage class. That’s the power of the Adapter design pattern!

Observer

It is used to notify the rest of the system about certain events in certain place. To get better understanding of the benefits of this pattern let’s review two solutions of the same problem.
Let’s say we need to create Theater to show movies to the critics. We define the class Theater with the method present. Before presenting the movie we want to send messages to the critics’ cell phones. Then, in the middle of the movie we want to stop the movie for a 5 minutes to let the critics have a break. Finally, after the movie ends we want to ask the critics to leave their feedback.
Let’s see how this would look like in the code:
class Theater {
   
    public function present(Movie $movie) : void {
       
        $critics = $movie->getCritics();
        $this->messenger->send($critics, '...');

        $movie->play();

        $movie->pause(5);
        $this->progress->break($critics)
        $movie->finish();

        $this->feedback->request($critics);
    }
}
It looks clean and promising.
Now, after some time, the boss told us that before starting the movie we also want to turn the lights off. Additionally, in the middle of the movie when it pauses we want to show the advertisement. Finally, when the movie ends we want to start auto-cleaning of the room.
Well, one of the issues here is that in order to achieve that we need to modify our Theater class, and that breaks SOLID principles. Particularly, it breaks the open/closed principleMoreover, this approach will make the Theater class to be depended on several additional services which is not good either.
What if we turn the things upside down. Instead of adding more and more complexity and dependencies to the Theater class we will spread the complexity across the system, and with that, reduce the dependencies of the Theater class as a bonus.
Here’s how this will look like in action:
class Theater {
    
    public function present(Movie $movie) : void {
        
        $this->getEventManager()
            ->notify(new Event(Event::START, $movie));
        $movie->play();

        $movie->pause(5);
        $this->getEventManager()
            ->notify(new Event(Event::PAUSE, $movie));
        $movie->finish();

        $this->getEventManager()
            ->notify(new Event(Event::END, $movie));
    }
}
$theater = new Theater();
$theater
    ->getEventManager()
    ->listen(Event::START, new MessagesListener())
    ->listen(Event::START, new LightsListener())
    ->listen(Event::PAUSE, new BreakListener())    
    ->listen(Event::PAUSE, new AdvertisementListener())
    ->listen(Event::END, new FeedbackListener())
    ->listen(Event::END, new CleaningListener());
$theater->present($movie);
As you can see, the present method becomes extremely straightforward. It doesn’t care about what happens outside of the class. It just does what it is supposed to do and notifies the rest of the system about the facts. Whatever is interested in those facts can listen to the respective events and be notified about them and do what it has to do.
With this approach, it also becomes pretty easy to add additional complexity. All you have to do is to create a new listener and put the needed logic there.
Hope you found the Observer pattern useful.

Decorator

It is used when you want to adjust the behavior of an object at run-time, and with that, reduce redundant inheritances and the number of classes. You might ask why do I need that at all? Well, it could be better explained with examples.
Let’s say we have classes Window and Door, and they both implement OpenerInterface.
interface OpenerInterface {
    public function open() : void;
}
class Door implements OpenerInterface {
    public function open() : void {
        // opens the door
    }
}
class Window implements OpenerInterface {
    public function open() : void {
        // opens the window
    }
}
Both the windows and the doors have the same behavior to open. Now, we need other doors and windows with additional functionality that will tell the users the temperature outside when they open the doors or windows. We can solve this problem with inheritance just like this:
class SmartDoor extends Door {
    public function open() : void {
        parent::open();
        $this->temperature();
    }
}
class SmartWindow extends Window {
    public function open() : void {
        parent::open();
        $this->temperature();
    }
}
All in all, we have 4 classes in total by now. However, with the Decorator pattern we could solve this problem with 3 classes only. Here’s how:
class SmartOpener implements OpenerInterface  {
    
    private $opener;
    public function __construct(OpenerInterface $opener) {
        $this->opener = $opener;
    }
    
    public function open() : void {
        $this->opener->open();
        $this->temperature();
    }
}
$door = new Door();
$window = new Window();
$smartDoor = new SmartOpener($door);
$smartWindow = new SmartOpener($window);
We have introduced a new type of an opener that acts like a proxy but with an additional functionality on top of it. That’s what does the trick.
If you need help in any kind of development project & I can also provide you consultancy about your project. I am top rated freelancer. You can hire me directly on UpworkYou can also hire me on Freelancer. If you have any comment, question, or recommendation, feel free to post them in the comment section below!

Comments

Tags

The Noonification banner

Subscribe to get your daily round-up of top tech stories!