StackPHP Explained

Share this article

Today we are going to look at StackPHP and try to understand what this thing is all about. Although this post will have some code, this article will be rather theoretical as we are interested in learning what StackPHP actually is, where it comes from and why it is useful.

StackPHP Logo

As the front page of the StackPHP project says, Stack is a convention for composing HttpKernelInterface middlewares. But, in order to actually understand this definition, we will have to cover a few concepts first. At the end, we will also illustrate the concepts we learned in the context of StackPHP with some example code. This usually makes things much easier to understand.

What is the HttpKernelInterface?

The HttpKernelInterface can be defined as more than one thing. At its most basic level, it is a PHP interface found inside the Symfony HttpKernel component. But it is more than that. It is a modern PHP pattern that allows us to treat HTTP requests in an object oriented way instead of dealing with superglobals and output functions like echo or header.

But what does this actually mean?

The interface provides a single method called handle() that accepts a $request and is expected to return a $response. The latter are basically wrapper objects around the HTTP specification. For example, in Symfony, it is the responsibility of the HttpFoundation component to create and manage these. So as you can imagine, the $request object contains data about the current user request, while the $response object is responsible for outputting the result of processing the request. But this is already a digression.

The interface doesn’t really care about what happens inside the handle() method. This is the responsibility of the class that implements it. For example, the Symfony kernel uses the Event Dispatcher component for managing this. It dispatches a number of events, and listeners to these events handle most of the heavy lifting. This keeps things abstract and decoupled.

But to get back to the point, you can regard the HttpKernelInterface as a very simple OO pattern that helps organise the handling of an incoming request for the purpose of turning it into a response.

What is the decorator pattern?

Like many other programming patterns, the decorator pattern is an object oriented way of solving a particular problem. In this case, the problem relates to extending functionality within a class without actually modifying or actually extending the class. The solution is to rather decorate it with the additional needed functionality.

Let’s quickly see a code example as it is the easiest way to understand what this pattern is all about.

Imagine you have this Action interface and class:

interface ActionInterface {
  public function trigger();
}

class Action implements ActionInterface {

  private function performAction() {
    return 'Action performed';
  }

  public function trigger() {
    return $this->performAction();
  }
  
}

And you want to be able to provide multiple different versions of this class that contain additional functionality inside the trigger() method. You could extend the class, and this is a valid approach in some cases. However, this can also create different problems down the line if you end up needing something like multiple inheritance (inheriting from multiple classes). PHP 5.4 introduces Traits to help out with these.

What you can also do is decorate it with another class that implements the same interface, add your functionality to it and then delegate back to the original one. Here is a very simple example:

class NotifiedAction implements ActionInterface {

  private $action;

  public function __construct(ActionInterface $action) {
    $this->action = $action;
  }

  public function notifyUser() {
    // Logic for user notification
  }

  public function trigger() {
    $performed = $this->action->trigger();
    $this->notifyUser();
    return $performed;
  }

}

As you can see, we now have a NotifiedAction class that implements the same interface and that takes the same interface as a constructor parameter. This acts like a decorator around the parameter object because it wraps around it and adds to its functionality from the outside (in this case sends a notification after the action is performed).

The reason why the decorator must implement the same interface is to make sure it has the same public API as the decorated one. In other words, the objects need to be more or less interchangeable. This is actually the main drawback of the decorator pattern, as wrapping interfaces with multiple methods can become quite clunky. But let’s get back to our example.

Now you have 2 options for triggering actions with the ActionInterface:

$action = new Action();
$action->trigger();

And

$action = new NotifiedAction(new Action());
$action->trigger();

And you can continue creating decorators and wrap them into one another like Russian dolls:

$action = new EmailNotifiedAction(new NotifiedAction(new Action()));
$action->trigger();

This is a very simple example of the decorator pattern. You can have slightly more complex structures in which you do some further abstractions to reduce boilerplate code. But this will be enough for our purposes.

What is a StackPHP middleware?

Now that we have a basic understanding of what the HttpKernelInterface and the decorator pattern are, it’s time to see what StackPHP middlewares are.

As I mentioned in the begining, StackPHP is

a convention for composing HttpKernelInterface middlewares.

In other words, StackPHP is a convention for building middlewares that respect a small number of rules, one of which being their implementation of the HttpKernelInterface. But what is a middleware in this context?

A middleware can be understood as a piece of functionality that gets added to the pipeline that runs between the user and the server (between a request and a response). Rack middlewares are a close example to the StackPHP definition of middlewares. And since it provides a sane pattern for implementing this pipeline, the HttpKernelInterface is a great choice for middlewares to wrap around of.

More concretely though, a Stack middleware is a simple PHP object that follows three simple rules:

  1. Implements HttpKernelInterface
  2. Decorates another object that implements this interface
  3. Adds functionality to the handle() method and delegates to the original object

Therefore, an important thing to remember is that StackPHP is not a framework or a library. It doesn’t even have any code per se (apart from a few optional tools to help you implement the conventions).

But rather, the goal of the StackPHP project is to promote a framework agnostic way for building code at the HTTP level. It argues that as opposed to framework specific or integrated code, HTTP level code can be stacked into existing applications and this also makes it more shareable and universally compatible. Currently, there are a number of PHP frameworks that use the Symfony HttpKernel component and with which Stack already works. Symfony, Silex, Laravel 5 and Drupal 8 are just a few examples and the hope is that this number will increase in the future.

What does a Stack middleware look like?

If we apply what we learned in the first two sections, we should now fully understand how to build a Stack middleware. We need to write a class that decorates an HttpKernelInterface object and add our own functionality before or after delegating to the decorated object. So let’s create a dead simple Stack middleware that wraps a Symfony application and performs a custom action before actually returning the response to the user.

At a basic level, this is the front controller of a Symfony app:

$kernel = new AppKernel('prod', false);
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);

AppKernel implements the HTTPKernelInterface and therefore has a handle() method to which we pass a Request object and from which we expect a Response object. Without even caring about what this method does internally, we can decorate it with our own middleware and add new functionality:

class CustomAppKernel implements HttpKernelInterface {

  /**
   * @var \Symfony\Component\HttpKernel\HttpKernelInterface
   */
  private $kernel;

  public function __construct(HttpKernelInterface $kernel) {
    $this->kernel = $kernel;
  }

  /**
   * {@inheritdoc}
   */
  public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = TRUE) {
    // Perform whatever task we want before handling the request
    
    // Delegate to the decorated kernel to handle the request as it normally would
    $response = $this->kernel->handle($request, $type, $catch);
    
    // Perform some more tasks after the response has been created
    
    // Return the Response
    return $response;
  }
}

In CustomAppKernel you can see the decorator pattern at work. We are wrapping a HttpKernelInterface object, perform some logic and then delegate to the decorated object. Our Symfony app front controller can look like this now:

$kernel = new CustomAppKernel(new AppKernel('prod', false));
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();

And that’s it. We have created a Stack middleware. If you want to see some more examples, feel free to check out the middlewares page on the StackPHP website.

One thing you’ll notice is the missing call to the terminate() method. If we want to to do that as well, our middleware needs to also implement the TerminableInterface. Alternatively, we can use the StackBuilder to help us stack or middlwares together and that also provides support for kernels that implement this interface.

StackBuilder

The StackBuilder is a small library that helps you build a tree of middlewares. It basically takes away some of the pain of having to manually instantiate and decorate your middlewares by providing a simple API wizard instead. So how does it work?

Let’s turn back to our example above:

$kernel = new CustomAppKernel(new AppKernel('prod', false));
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();

With StackBuilder, we can do something like this:

$kernel = new AppKernel('prod', false);
$stack = (new Stack\Builder())
    ->push('CustomAppKernel');

$kernel = $stack->resolve($kernel);

Using the push() method on the StackBuilder object we can add middlewares onto the stack. In this example, we are wrapping our AppKernel with the CustomAppKernel middleware. This really becomes valuable when you have multiple middlewares you want to stack together. All you have to do is keep chaining on the push() method:

$stack = (new Stack\Builder())
    ->push('CustomAppKernel')
    ->push('AnotherAppKernel')
    ->push('YetAotherAppKernel);

All of these will wrap one by one the original HttpKernelInterface object. And the resolve() method processes the actual stacking. So now when you call the handle() method on stacked kernel, you are basically calling the handle() method of the CustomAppKernel which wraps AnotherAppKernel which wraps YetAnotherAppKernel which wraps the original AppKernel.

Additionally, the stacked kernel that is returned by resolve() also implements the TerminableInterface and therefore has the terminate() method available. So if we call it, it will delegate to the terminate() method of each middleware in the stack that implements this interface.

Conclusion

In this article we looked at StackPHP and tried to understand what it’s all about. We first talked about the HttpKernelInterface as the core of what Stack conventions try to achieve. We also explained the decorator pattern to better understand how these conventions can be followed in practice. Then, we created a small middleware to see all this in action. Finally, we glanced at the StackBuilder, the utility Stack creators provided that makes the process of stacking middlewares a breeze.

Have you built your own StackPHP middlewares? Let us know in the comments.


Daniel SiposDaniel Sipos
View Author

Daniel Sipos is a Drupal developer who lives in Brussels, Belgium. He works professionally with Drupal but likes to use other PHP frameworks and technologies as well. He runs webomelette.com, a Drupal blog where he writes articles and tutorials about Drupal development, theming and site building.

BrunoSdecoratorhttpkernelmiddlewareOOPHPPHPstackphpsymfony
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week
Loading form