Middleware and DI with PHP-DI/Slim-Bridge

I had the following problem with PHP-DI/Slim-Bridge, so I thought I’d shar it and the answer that I recieved:

I am using PHP-DI/Slim-Bridge in a Slim3 app. I am using Autowiring for the Controllers, so my definitions looks like this:

use App\Interfaces\FlashInterface;
use App\Helpers\Flash;

  $definitions = [
 
    FlashInterface::class => function () {
      return new Flash;
    },
  
  ]

Then my route is:

  $app->get('/auth/register', ['\App\Controllers\Auth\Register', 'getRegister'])->setName('auth.register')->add($guest);

My controller is:

namespace App\Controllers\Auth;

use App\Auth\Auth;

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use App\Interfaces\FlashInterface as Flash;
use Slim\Views\Twig as View;

class Register
{
    public function getRegister(Request $request, Response $response, View $view, Flash $flash)
    {
        return $view->render($response, 'auth/register.twig');
    }
    

All this is fine, except that I want the guest middleware to also use Autowiring:

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use App\Interfaces\FlashInterface as Flash;
use App\Interfaces\AuthInterface as Auth;

$guest = function (Request $request, Response $response, $next, Flash $flash, Auth $auth) {
    if ($auth) {
        $flash->addMessage('warning', 'You need to be logged out to see this page');
        return $response->withRedirect($this->get('router')->pathFor('home'));
    }
    $response = $next($request, $response);
    return $response;
};

So, I was hoping that this would inject the TypeHinted $flash object into the Middleware closure, but it doesn’t. The script fails as $flash is null.
Instead I have to define an additional static flash object in my container:

use App\Interfaces\FlashInterface;
use App\Helpers\Flash;

  $definitions = [
 
    FlashInterface::class => function () {
      return new Flash;
    },
    
    'flash' => function () {
        return new Flash;
    },

  ]

Then get it from the container:

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;

$guest = function (Request $request, Response $response, $next) {
    if ($this->get('auth')) {
        $this->get('flash')->addMessage('warning', 'You need to be logged out to see this page');
        return $response->withRedirect($this->get('router')->pathFor('home'));
    }
    $response = $next($request, $response);
    return $response;
};

Questions:

  1. Is this a problem for PHP-DI to get objects direct from the container, instead of Autowiring?
  2. Is there a way to get Autowiring to work with Slim’s Middleware?

@mnapoli (the author of PHP-DI/Slim-Bridge) just replied to me in Gitter:

only controllers can do dependency injection in parameters for now
the only feature that PHP-DI brings for middlewares is the ability to do something like this:

$app->get('/auth/register', ['\App\Controllers\Auth\Register', 'getRegister'])->setName('auth.register')->add('GuestMiddleware');

i.e. resolve middlewares from the container
(that way you can do dependency injection in middleware like for a classic service: through the constructor
but that requires writing middlewares as classes. With closures there is currently no DI in parameters
FYI this is something that was added very recently in the Silex bridge: https://github.com/PHP-DI/Silex-Bridge/releases/tag/1.5.0
that would be great to have the same thing for Slim, I’m not working on it at the moment

If you want to use autowiring on a function you could use call.

Also instead of creating a second Flash instance under a different container key you should use an alias:

$definitions = [
    FlashInterface::class => DI\object(Flash::class),    
    'flash' => DI\get(FlashInterface::class),
];
2 Likes