Enhancing polymorphism in PHP applications

Docler
Byborg Engineering
Published in
7 min readOct 12, 2020

How to use the concept of generics to improve code design and make your PHP application more flexible, testable and reusable

by João Lopes

Introduction

When using PHP to develop a software you might end up facing a situation where you need generics — a concept well known by C# developers — to keep your design at best.

This is how Generics is defined by Microsoft (check it here):

Generics introduce the concept of type parameters to .NET, which make it possible to design classes and methods that defer the specification of one or more types until the class or method is declared and instantiated by client code.

There are two great advantages when it comes to generics. The first and most obvious one is to handle some data structures like lists, arrays and others. There are lots of articles about how to work with these structures using PHP, therefore I won’t discuss it further here.

This is how generics for data structures look like in C#:

public class Person
{
public IList<Skill> Skills { get; private set; }
}

As you can observe there is a generic type called Ilist<> which makes easier to create lists of objects, scalar types and even a list of lists.

The second advantage is to use generics as a way to improve your software design, enhance its polymorphism, making you able to add code to one or many implementations without changing them or breaking any tests. Interesting, isn’t it?

Let’s check out how to make it work in C# and then we’ll see how to use the same strategy in PHP. Actually, the same solution for PHP works for other languages, but that’s another subject.

Context

John is a software developer at Fake Registrations Inc. Among other features, there are login and registration. The IT Security team has been monitoring the large amount of requests coming from two IP addresses and they think it might be an attack. Therefore, they ask John to block the requests from those IP addresses on login and registration forms.

How can John do the necessary changes to his code without changing his services (login and registration) and breaking his unit tests?

Enhancing Polymorphism in C#

Imagining John’s code was written in C#, how could he do it?

Disclaimer: middleware is an option in this case, but it should not be considered the only answer to a problem like John’s. It’s important to consider the trade offs of your options and evaluate them before making your move. Therefore you shall see the implementation by design here instead of using framework’s middleware.

John’s first thought is to create a service to block requests from IP addresses and inject this new service into login and registration services like the following listing:

public interface IBlockRequestsService
{
void Block();
}
public class BlockRequestsService : IBlockRequestsService
{
public void Block()
{
// Blocking requests here
}
}
public interface ICommandService<in Tcommand>
{
void Execute(TCommand command);
}
public class LoginService : ICommandService<LoginCommand>
{
private readonly IBlockRequestsService _blockRequests;
public LoginService(IBlockRequestsService blockRequests)
{
this._blockRequests = blockRequests;
}
public void Execute(LoginCommand command)
{
this._blockRequests.Block();
// Login service execution
}
}
public class RegistrationService : IcommandService<RegistrationCommand>
{
private readonly IBlockRequestsService _blockRequests;
public RegistrationService(IBlockRequestsService blockRequests)
{
this._blockRequests = blockRequests;
}
public void Execute(RegistrationCommand command)
{
this._blockRequests.Block();
// Registration service execution
}
}

John’s idea is interesting, but it brings some problems:

  • By injecting this new dependency John breaks his unit tests for the involved services (mockists believe this is an advantage);
  • In case login service (or registration service) doesn’t need the IP blocker anymore, John will need to change his service and his unit tests;
  • In case IT Security asks John to put the IP blocker on another service he’ll need to change his service code and its unit tests;

At the same time John’s services have an interesting approach. They implement a generic abstraction:

public interface ICommandService<in TCommand>
{
void Execute(TCommand command);
}

What are the benefits in using a generic abstraction for multiple services? The main benefit is polymorphism. By using a generic abstraction John is able to extend his services without changing your services or even breaking your unit tests.

His first step then is to change his BlockRequestsService class to implement the same abstraction as login and registration services. Let’s check how it looks now:

public class BlockRequestsService<TCommand> : ICommandService<TCommand>
{
public void Execute(TCommand command)
{
// Blocking requests from IP addresses
}
}

Ok, good, but does this change anything? John still need to inject an interface on both login and registration services even though it’s a different one, right?

Wrong! John can make use of Decorator Pattern and compose his objects (his services in this case) in a way that he extends his services without changing none of them, as following:

// Blocking requests service
public class BlockRequestsService<TCommand> : ICommandService<TCommand>
{
private readonly ICommandService<TCommand> _service;
public BlockRequestsService(ICommandService<TCommand> service)
{
this._service = service;
}
public void Execute(TCommand command)
{
// Blocking requests from IP addresses
this._service.Execute(command);
}
}
// Startup (Composition Root)
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<ICommandService<LoginCommand>>(service => new BlockRequestsService(new LoginService()));
services.AddScoped<ICommandService<RegistrationCommand>>(service => new BlockRequestsService(new RegistrationService()));
}

Done! Login and registration services have now a decorator which blocks the requests and then forwards the execution the following service. Both services remain intact and no unit tests are changed because of that.

However John’s code is written in PHP. How can he implement this behavior in PHP?

Enhancing Polymorphism in PHP

What does a generic type such as BlockRequestsService do? John needs to declare the command type in order to use the service and after that the service itself forwards the execution to another service (login or registration) based on the command object passed to it.

Based on that John needs 2 things:

  1. To have a generic abstraction which is implemented by login, registration and the decorator BlockRequestsService;
  2. To have a way on the decorator to forward the execution to the main service (login or registration) based on the command object;

The first thing is super easy for any PHP developer:

interface CommandServiceInterface
{
function execute(CommandInterface $command): void;
}
class LoginService implements CommandServiceInterface
{
public function execute(CommandInterface $command): void
{
// Login
}
}
class RegistrationService implements CommandServiceInterface
{
public function execute(CommandInterface $command): void
{
// Registration
}
}
class BlockRequestsService implements CommandServiceInterface
{
public function execute(CommandInterface $command): void
{
// Blocking IP Addresses
}
}

Second step is to transform BlockRequestsService into a decorator:

class BlockRequestsService implements CommandServiceInterface
{
private $service;
public function BlockRequestsService(CommandServiceInterface $service)
{
$this->service = $service;
}
public function execute(CommandInterface $command): void
{
// Blocking IP Addresses
$this->service->execute($command);
}
}

Ok, John has now a generic abstraction and the decorator, but when he finds an interesting problem on his Composition Root:

// Composition Root similar to Laravel$this->app->bind(CommandServiceInterface::class, function ($app) {
return new BlockRequestsService(new LoginService);
});
$this->app->bind(CommandServiceInterface::class, function ($app) {
return new BlockRequestsService(new RegistrationService);
});

What’s the problem? John is binding the CommandServiceInterface twice. It won’t work that way. How can he do it then?

John thinks about the second thing he needs: “To have a way on the decorator to forward the execution to the main service (login or registration) based on the command object”. He quickly realizes that he needs an intermediary object which can identify the command object and forward to the correct service.

Sounds familiar? Let’s take a look into the definition of Mediator Design Pattern:

Define an object that encapsulates how a set of objects interact

Yes, a mediator object in PHP can promote the same behavior as generics in C#. How to implement the mediator class in PHP then?

The mediator class will implement the same generic interface (CommandServiceInterface) and it’ll be composed by an array of CommandServiceInterface objects.

Beyond that, the mediator needs a key — a way to identify the command — to forward the request (command) to the correct object within the expected parameters. The command’s object name might be a good option for array key. John implements it as following:

class CommandServiceMediator implements CommandServiceInterface
{
// CommandServiceInterface[]
private $services;
public function __construct(array $services)
{
$this->services = $services;
}
public function execute(CommandInterface $command)
{
$service = $this->services[get_class($command)];
$service->execute($command);
}
}

The mediator object is created, but John’s not finished. He needs to change the composition root:

// Composition Root similar to Laravel Framework$this->app->bind(CommandServiceInterface::class, function ($app) {
return new BlockRequestsService(
new CommandServiceMediator([
LoginCommand::class => new LoginService(),
RegistrationCommand::class => new RegistrationService(),
])
);
});

Done! John has extended his code without changing his main services and not breaking his unit tests.

Now that John has finished his implementation, let’s play a bit with PHP generics.

  • What happens if John needs more decorators? He can simple create another decorator just as the blocking requests one.
// Composition Root
$this->app->bind(CommandServiceInterface::class, function ($app) {
return new AnotherDecorator(
new BlockRequestsService(
new CommandServiceMediator([
LoginCommand::class => new LoginService(),
RegistrationCommand::class => new RegistrationService(),
])
)
);
});
  • How will John compose his objects if some services don’t have that decorator he created? Simple, he can move the decorator inside the mediator.
// Composition Root similar to Laravel Framework$this->app-bind(CommandServiceInterface::class, function ($app) {
return new CommandServiceMediator([
LoginCommand::class => new LoginService(),
RegistrationCommand::class => new BlockRequestsService(new RegistrationService()),
]);
});

Nice! John has an interesting design. His decorators are now testable, flexible and reusable. What are the downsides of this solution by design?

By using this approach the software complexity is moved to its composition root which can be considered a big advantage, but it requires a lot of training and great developers.

Another downside to consider is the fact that as your software grows the composition root might become so complex that even great developers could face difficulties to understand, but it’s up to the team to improve the design and make the composition root flexible and readable enough across the years.

The advantages otherwise are countless. The software flexibility is huge and the code becomes easily testable, isolated and decoupled.

There’s a lot to discuss about software design yet. Computer science is yet new and software engineering is completely different from others. Let’s keep our discussions on the comments and see you in the next article.

--

--

Docler
Byborg Engineering

Curious about the technologies powering the 30th most visited website in the world, with 45 million users, 2,000 servers, 4 data centers?