PHP Use Autowiring with Slim3

In this post; I will show you how to use a middleware to create an object inside of PHP-DI for your Action classes to use. One of the most annoying things I have ever found is that Security Code pops-up everywhere. Adding context to a request is hard; but Auto-wiring makes it less hard!

Security is hard

Any web-dev will tell you compliance and security is a huge PITA. Every project starts out nice and easy and productive! But then Mr. Compliance/Whitebeard/Security Guru comes along and asks how you are going to deal with X,Y,Z... Suddenly your HTTP Actions are cluttered with stuff like this.

$user = $this->getUserFromToken($request->getHeader('Authorization')[0]);

Ugh; what a pain; what happens when we change that header? Well you use FInd and Replace to swap them all. This sort of code is horrible and for those with OCD; you likely cringe everytime you open a Http class b/c it's likely in there somewhere. Sure it might be hidden through your 4 levels of class structure; but it's somewhere lurking and eating at your brain.

Autowiring is the solution

What if I were to tell you you could use Auto-Wiring to fix this problem. You would probably think I am crazy. I am but that's a separate issue. PHP-DI with Slim is pretty much the best Rapid Development Framework Stack I have ever come across and if you are unfamilar with PHP-DI you should totally go check it out.

This function needs to happen on every call of "SOME" routes

Then by definition it's a middleware. You might ask; well then dear sir how do you modify the DI Container after execution is started. HA. Well #1 don't use pimple; use PHP-DI. #2 You make Rob Allen cringe; and you do something that is a bit unconvential and might make some purists really angry .... You inject the container into your middleware.

Let's take a look.

use DI\Container;
use Slim\Http\Request;
use Slim\Http\Response;
use Domain\Account\User;
use Domain\Account\UserSecurityService;

class SecurityCheckMiddleware
{
    /**
     * @var Container
     */
    private $container;

    /**
     * SecurityCheckMiddleware constructor.
     *
     * @param Container $container
     */
    public function __construct(Container $container) //Inject Container
    {
        $this->container = $container;
    }

    public function __invoke(Request $request, Response $response, $next)
    {

        if ($request->hasHeader('Authorization')) { //Our request has the header
            $token = $request->getHeader('Authorization')[0];
            
            //Fetch the User
            $securityTokenRepo = $this->container->get(UserSecurityService::class);
            $token = $securityTokenRepo->findToken($token);

            //We then tell the container that the Class should use this instance!
            $this->container->set(User::class, $token->getUser());

            //Continue executing this request
            return $next($request, $response);
        }

        return $response->withStatus(401, 'Unauthorized');
    }
}

Very straight-forward. You can use more OOP to reduce the code complexity of this section; Ultimately you are just turning a String into a User class.

Ok ... But how is that useful?

My friends ... Now we can Type-Hint against User::class and get the authenticated user .... ANYWHERE IN OUR APPLICATION! Magic you say? Yes. Auto-Wiring is amazing.

Observe!

use Slim\Http\Request;
use Slim\Http\Response;
use Core\Identity\Uuid;
use Domain\Account\User;
use Domain\Game\Entities\Game;
use Domain\Game\Repositories\GameRepository;

class CreateGame
{
    /**
     * @var GameRepository
     */
    private $gameRepository;
    /**
     * @var User
     */
    private $user;

    /**
     * CreateGame constructor.
     *
     * @param GameRepository $gameRepository
     */
    public function __construct(GameRepository $gameRepository, User $user)
    {
        $this->gameRepository = $gameRepository;
        $this->user = $user;
    }

    public function __invoke(Request $request, Response $response)
    {
        $body = $request->getParsedBody();

        try {
            $game = new Game(new Uuid(), $body['name']);

            $this->gameRepository->store($game);

            return $response->withJson(['message' => 'Created, ' . $this->user->getId()]);

        } catch (\Exception $exception) {
            return $response->withJson(['message' => 'Something went wrong, A game with that name probably exists']);
        }
    }
}

Mwhahahahahaha. Now your HTTP Classes no longer need that security garbage.

You can thank me by following me on Twitter!

Written by Glenn Eggleton on Saturday August 19, 2017
Permalink - Chapter: Slim