Tiny PHP Micro-Framework in 40 Lines of Code

Sep 2, 2016 · By Darren Mothersele

Web Dev

I’m working on a project that needs a very simple backend. The app will only have a handful of users, and the frontend interface has been built in Angular 2. The backend is mainly a proxy, with a bit of added configuration.

Drupal 8, with it’s built in support for REST(ish) services, could be an option for modern web apps built with Angular 2. But, in this case, Drupal would have definitely been overkill. Perhaps Express (Node.js) could have been an option, or any of the multitude of PHP Micro-frameworks. But this seemed like a good excuse to play with some modern PHP, and build something minimal.

As this project just needed a simple proxy to a third-party API to add in some configuration and cache some things, it could have been done in a single PHP script. But, this is modern PHP - so we’re going to try and do things a bit neater than that. We know that easy is not always simple. I want to keep this simple, which is not necessarily the easiest option. The most important rule to follow here is the single responsibility principle. Each file will contain just one class, each class with encapuslate one thing, and each block of code will have a single responsibility. I may blog about other interesting parts of this app in the future, but for now let’s just look at the core of the app.

Here’s the code in it’s entirety. The astute among you may recognise this as a middleware dispatcher, like Slim, Stack, or Relay

use DI\InvokerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

class StackRunner
{
    /** @var InvokerInterface */
    protected $invoker;

    protected $stack = [];
    protected $current = 0;

    public function __construct(InvokerInterface $invoker, $stack)
    {
        $this->invoker = $invoker;
        $this->stack = $stack;
    }

    public function __invoke(ServerRequestInterface $request, ResponseInterface $response)
    {
        if (!isset($this->stack[$this->current])) {
            return $response;
        }

        $middleware = $this->stack[$this->current];
        $this->current++;

        return $this->invoker->call($middleware, [
          'request' => $request,
          'response' => $response,
          'next' => $this,
        ]);
    }
}

That’s it. Less than 40 lines of code.

There’s another no-framework implementation called Harmony that takes a similar approach. It’s also very small at 200 lines. I think this minimal version above, less than a quarter of the size, is useful because it’s so simple. But, it might not be obvious at first glance what it’s doing. Let’s break it down, look at how it works, and how to use it.

The website for StackPHP has a diagram that shows how the middleware dispatcher idea works. They refer to this as an onion.

Middleware Onion (source: StackPHP)

NB: Ignore the session layer. I’m building a restful API, and restful APIs should not have state. One of the six constraints for a REST architecture is statelessness. I wont go into this now, but save this for a future blog post.

In this model you build up a stack of middleware, then pass the request through each one, then pass the response back out through each layer. Each layer in the stack gets to process the request and response as they travel into the core of the app, and then back out again. In my actual app I have layers that deal with CORS, JWT Authentication, Exception Handling, Firewall, Logging, Routing to the correct controller, and JSON formatting. Everything is nicely separated into layers that deal with a single step of the process.

The core of the app is your business logic. Everything is separated into layers, and each can be tested and reasoned about in isolation.

The stack of middleware is injected into the StackRunner as a dependency via the constructor, along with an instance of an implementation of the InvokerInterface. In my case I’m using PHP-DI as a dependency injection container and it’s container implements the required interface. The interface provides a call() method that given a PHP callable (or string registered in the container) instantiates the callable (if required) and uses the container to provide any arguments.

This StackRunner class produces an invokable object. When invoked the stack of middleware is traversed, executing each one (via the InvokerInterface object) and passing itself along, so that execution can continue. The implementation of the __invoke() method does this and has just 3 simple sections:

1) First it checks to see if it has reached the end of the stack of middlware. In which case it just returns the current $response so that it can travel back up the stack.

2) If not at the end of the stack, the current middleware is retrieved from the stack and the stack pointer ($this->current) is moved along to the next middleware.

3) The InvokerInterface is used to call the middleware, passing in the $request, $response, and the stack runner as $next so that it can be called from within the middleware to continue execution along the rest of the stack.

So, how do you make use of this?

Well, you need a few things to make it work. As you can see in the code above, it’s programmed against interfaces from PSR-7. This is the PHP-FIG (Framework Interoperability Group) effort to standardise the Request and Response object interfaces. Any implementation of PSR-7 will do, for example the Diactoros package from Zend.

You also need to register your dependencies with a PHP-DI container and provide that to the constructor. Here’s an example of how it might be used:

$stack = [ExampleMiddleware::class];
$builder = new ContainerBuilder;
$builder->addDefinitions($config);
$container = $builder->build();
$runner = new StackRunner($container, $stack);
$response = $runner($request, $response);

So what does a middleware in this stack look like?

class ExampleMiddleware
{
    public function __invoke($request, $response, $next)
    {
        // 1. optionally do something with $request

        // 2. continue execution down the stack
        $response = $next($request, $response);

        // 3. optionally do something with $response

        // 4. return $response
        return $response;
    }
}

You can add type hints to the __invoke method. I left them off here for brevity. When invoked, the middleware receives the request and response. It can process these, call the next middleware (via the $next callable provided), then it must return a response object.

Thanks to the InvokerInterface it’s possible to use the DI container to instantiate the middleware to provide dependencies.

By using a middleware stack runner like this you can easily get separation of concerns and make your code simpler. For simple tasks a PHP framework can be overkill. You can get a long way with a clean design by using a dependency injection container to instantiate your objects, and a layered calling mechanism like this. Your code will be more testable, maintainable, and reusable. And, you can get to working on the core domain of the problem without worrying about framework or CMS issues. Rather than trying to fit your solution into the constraints of a framework, you can build your solution in the most appropriate way, deal with any complexities in the domain logic up front, and then wrap it up as required.

Darren's Photo

Darren Mothersele

Darren is a software developer who builds simple, creative, and independent technology. Read more »