Caching middleware with Expressive

Performance is one of the key feature for web application. Using a middleware
architecture makes it very simple to implement a caching system in PHP.

The general idea is to store the response output of a URL in a file (or in
memory, using memcached) and use it for subsequent
requests. In this way we can bypass the execution of the previous middlewares
starting from the second request.

Of course, this technique can only be applied for static contents, that does not
require update for each HTTP request.

Implement a caching middleware

Imagine we want to create a simple cache system with Expressive.
We can use an implementation like that:

In this example, we used the PSR-15
proposal to implement the Middleware interface using the process() function.
This is the suggested way to implement middleware in Expressive 2.0.

The idea of this middleware is quite simple. If the caching system is enabled
and if the requested URL matches an existing cache file, we return the cache
content as HtmlResponse,
ending the execution flow.

If the requested URL path does not exist in cache, we process the delegate
middleware (basically we continue with the normal workflow) and we store the
response in the cache, if enabled.

Configure the cache system

To manage the cache, we used a configuration key cache to specify the path of
the cache files, the lifetime in seconds and the enabled value to turn on
and off the caching system.

Since we used a file to store the cache content, we can use the file
modification time to manage the lifetime of the cache. We used the filemtime
function of PHP to retrieve the modification file time.

Note: if you want to use memcached instead of file you need to replace thefile_get_contents and file_put_contents functions with Memcached::get
and Memcached::set. Moreover, you do not need to check for lifetime
because when you set a content in memcached you can specify the expiration
directly.

In order to pass the $config dependency, we can use a simple factory class.
This is an example:

Following the folder structure of Expressive, we can store this configuration in
a simple PHP file in the config/autoload directory. For instance, we can store
it in config/autoload/cache.local.php file, as follows:

We used the folder /data/cache for storing the caching file. The content of
this folder should be omitted in the version control. For instance, using git
you can omit the content putting a .gitignore file inside the cache folder
with the following content:

*
!.gitignore

Finally, in order to activate the caching system we need to add theCacheMiddleware class as service. In our example, we used zend-servicemanager
as service container. To add the cache system we can use a configuration file
(e.g. /config/autoload/cache.global.php) with the following content:

The middleware actions to be excuted for the /about URL are CacheMiddleware
and AboutAction in this order. The $app object is the instance of ZendExpressiveApplication,
the main class that manages the execution of an Expressive application.

Conclusion

In this brief article we showed how to build a caching system for a PHP
application based on PSR-7 and PSR-15. A middleware architecture facilitates the
design of a cache layer because it uses the same workflow, an HTTP request as
input and an HTTP response as output. In this way, we can manage the HTTP
request, get the HTTP response for any middlewares and store the result for
caching, all in one place.

In this article we used the zendframework/zend-expressive-skeleton application
as example. For more information about Expressive, visit the documentation.