Create Modules and Middleware with Command-Line Tooling Support

Create Modules and Middleware with Command-Line Tooling Support

For the longest time, Zend Framework hasn't had the strongest command-line tooling and scaffolding support. In stark contrast, other frameworks — especially Laravel, and its excellent Artisan command — have had far stronger tooling support and as a result have been far easier to build projects with.

However, that's all changed!

Since the release of Zend Expressive Skeleton 2.0.2, Zend Expressive's command-line tooling support has been rapidly developing. It's provided through a package called, aptly, Zend Expressive Tooling. In a nutshell, it supports three things:

  1. Middleware creation
  2. Module creation
  3. Migration from version 1 to version 2 of Zend Expressive

Today, I'm going to talk through points one and two, and cover point three in a separate post (likely next week).

Short on time?

If you don’t have a lot of time spare to read through the post, get the essentials in episode 26 of the podcast.


Like any, modern, PHP package Zend Expressive Tooling can be installed using Composer — would you have expected anything less? To do so, run the following command:

composer require --dev "zendframework/zend-expressive-tooling:^0.4.4"

Alternatively, ensure that your project's composer.json file at least looks like the following, and run composer update afterward:

"require-dev": {
    "zendframework/zend-expressive-tooling": "^0.4.4"

With the package installed, you can run it, from the terminal, by calling ./vendor/bin/expressive. If you do so, you'll see the following as part of the output:

  middleware:create                 Create an http-interop middleware class file.
  migrate:error-middleware-scanner  Scan for legacy error middleware or error middleware invocation.
  migrate:original-messages         Migrate getOriginal*() calls to request attributes.
  migrate:pipeline                  Generate a programmatic pipeline and routes from configuration.
  module:create                     Create and register a middleware module with the application
  module:deregister                 Deregister a middleware module from the application
  module:register                   Register a middleware module with the application

I've only included this section of the output, as the rest is the standard output for a command-line tool built using Symfony's Console Commands package; the output which you're likely already used to seeing. As I mentioned earlier, the tool has three areas of support; one for middleware, one for migration, and one for modules. Let's start with middleware.

Middleware Creation

As you can see in the command output above, you can create an http-interop compliant middleware class. This class utilizes the new approach to middleware in Zend Expressive 2.0, one based on the upcoming PSR-15 standard.

Let's assume that I have a greenfield Zend Expressive project and want to create my first piece of middleware for it, called Footer, inside the existing App namespace. To do so, we would run the following command:

./vendor/bin/expressive middleware:create "App\Middleware\Footer"

This one command will create the directory Middleware under src/App/, if it doesn't already exist, and in there create a new middleware class, called Footer.php, with the following contents:


namespace App\Middleware;

use Interop\Http\ServerMiddleware\DelegateInterface;
use Interop\Http\ServerMiddleware\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;

class Footer implements MiddlewareInterface
     * {@inheritDoc}
    public function process(
      ServerRequestInterface $request,
      DelegateInterface $delegate
    ) {
        // $response = $delegate->process($request);

Here, you can see a basic template for a new middleware class ready to be fleshed out. There's no need to create either it, or the directory structure, by hand anymore.

But two things are worth noting, however:

  1. The tool doesn't update Composer's autoloader; so you'll have to do that manually, by running composer dump-autoload.
  2. If the namespace you've provided doesn't exist you'll see the following error:
Unable to match Api\Middles\Footer to an autoloadable PSR-4 namespace

So make sure that you’re using an existing namespace before you run the command.

Want to Learn Expressive’s Essentials?

If you want to get a jump-start on building applications using Zend Expressive, then don’t miss my upcoming book and course! In it, you’ll learn all about Expressive’s core concepts and components, as well as how to create an application manually and by using the skeleton installer. Share your email address to find out when it’s ready.

Module Creation

Now let's see how to manage modules. Specifically, let's see how to create, register, and deregister a module.

What is a Module?

If this is your first time hearing about modules in Zend Expressive, and you're already familiar with Zend Framework 2, they're not technically the same thing.

Whereas Zend Framework 2 supported modules by using the Module Manager, Zend Expressive's modules are based only on configuration and directory structure.

Have a look at the following directory structure from a rudimentary expressive application which I've created for this tutorial.

├── App
    ├── Action
    │   ├── HomePageAction.php
    │   ├── HomePageFactory.php
    │   └── PingAction.php
    ├── ConfigProvider.php
    └── Middleware
        └── Footer.php

You can see, under src, that there is one directory: App; which you will be familiar with if you use the Skeleton Installer to create your Expressive projects. It has three action classes and a ConfigProvider class "How To Simplify Zend Expressive Configuration with ConfigProviders" — it also now has the Footer middleware which we created earlier.

That directory, thanks to the ConfigProvider class, is a module in Zend Expressive terms. The code inside that directory isn't (or shouldn't be) specific to this application, and could be packaged up into an independent Composer package to be used in any Expressive application.

I hope that this clarifies what a module is.

Create a Module

With that explanation out of the way, let's see how to build a module called Generic. As the name implies, it won't do much. From the command-line, run the following command:

./vendor/bin/expressive module:create Generic

After the command completes, if you look at the directory structure of src, you'll now see that it looks like the following:

├── App
│   ├── Action
│   │   ├── HomePageAction.php
│   │   ├── HomePageFactory.php
│   │   └── PingAction.php
│   ├── ConfigProvider.php
    └── Middleware
        └── Footer.php
└── Generic
    ├── src
    │   └── ConfigProvider.php
    └── templates

We have a new directory (module) called Generic, which has a ConfigProvider class and a templates directory. This might not seem too surprising, but have a look at composer.json where you'll see that the PSR-4 autoload configuration's been updated to look like the following:

"autoload": {
    "psr-4": {
        "App\\": "src/App/",
        "Generic\\": "src/Generic/src/"

What's more, if you look in config/config.php, you'll see that the module's ConfigProvider has been included so that it will be active in your application. Here's a (trimmed) snippet:


use Zend\ConfigAggregator\ArrayProvider;
use Zend\ConfigAggregator\ConfigAggregator;
use Zend\ConfigAggregator\PhpFileProvider;

// To enable or disable caching, set the `ConfigAggregator::ENABLE_CACHE` boolean in
// `config/autoload/local.php`.
$cacheConfig = [
    'config_cache_path' => 'data/config-cache.php',

$aggregator = new ConfigAggregator([
    // ...trimmed for readability
], $cacheConfig['config_cache_path']);

return $aggregator->getMergedConfig();

If you run your application at this point, you won't see a lot. However, you can quickly get started creating middleware, templates, and so on to flesh it out, without having to look after the boiler plate work.

Register an Existing Module

Now, let's say that you already have a module which you were working on by hand before you integrated Expressive Tooling into your project and want to enable it. Assuming that your module was called Authentication, to do so you would run the following command:

./vendor/bin/expressive module:register Authentication

This command updates composer.json and config/config.php making your module active.

De-register an Existing Module

If you want to disable an existing module, say because there's a bug you are having trouble fixing, you're planning to deprecate the module, do testing when it's not present, and so on, here’s how to do it. Assuming that your module was, again, called Authentication, to disable it you would run the following command:

./vendor/bin/expressive module:deregister Authentication

This would update composer.json, removing it from the PSR-4 autoload configuration, and update config/config.php, removing it from the ConfigAggregator array, de-registering your module, making it inactive. It's worth noting that the command does not remove any source files! So don’t get worried if you think it’s going to do anything like that.

In Conclusion

And that's how to use Zend Expressive's new tooling support to create middleware, as well as to create, register, and deregister modules. I hope that you found it helpful and that you can see that Expressive is growing by leaps and bounds.

I hope that you also see that Expressive is catching up to other frameworks, such as Laravel, who have had extensive tooling support for some time.

Have a go and experiment with it. I hope that it will help you spend less time on the setup aspects of your apps, and more on the development. Next week, we’ll see how to use the tool to migrate an Expressive application from version 1 to version 2.

Many thanks to Rob Allen (@akrabat) for being the technical reviewer on this tutorial.

About Matthew

Matthew Setter Matthew Setter is a PHP & Zend Framework specialist. If you're in need of a custom software application, need to migrate an existing legacy application, or want to know your current application's GPA - get in touch.