src/HandlerContainer.php
public function process(Request $request) : Response
    {
        $router = $request->attributes->get('router');
        
        $next = next($this->middlewareStack);
        if ($next instanceof Middleware) {
            $router->log("Router: Calling Middleware: %s", get_class($next));
            $response = $next->process($request, $this);
            $router->log("Router: Leaving Middleware: %s", get_class($next));
            return $response;
        } elseif (is_string($next)) {
            $router->log("Router: Calling Middleware: %s", $next);
            $response = $router->getMiddleware($next)->process($request, $this);
            $router->log("Router: Leaving Middleware: %s", $next);
            return $response;
        } else {
            $params = $request->attributes->get('controller');

            // Call controller with request and args
            $router->log("Router: Calling Controller: %s@%s", $params->className, $params->method);
            $return = (new $params->className($params->container))->{$params->method}($request, ...array_values($params->args));
            $router->log("Router: Controller Left");
            
            // Instantly return Response objects
            if ($return instanceof Response) {
                return $return;
            }
            
            // If not a string (or something easily castable to a string) assume Json -> JsonResponse
            if (is_array($return) or is_object($return)) {
                return new JsonResponse(
                    $return,
                    Response::HTTP_OK,
                    array('content-type' => 'application/json')
                );
            }
            
            // Strings, and other primitives.
            return new Response(
                $return,
                Response::HTTP_OK,
                array('content-type' => 'text/html')
            );
        }
    }
public function process(Request $request) : Response
    {
        $router = $request->attributes->get('router');
        
        $next = next($this->middlewareStack);
        if ($next instanceof Middleware) {
            $router->log("Router: Calling Middleware: %s", get_class($next));
            $response = $next->process($request, $this);
            $router->log("Router: Leaving Middleware: %s", get_class($next));
            return $response;
        } elseif (is_string($next)) {
            $router->log("Router: Calling Middleware: %s", $next);
            $response = $router->getMiddleware($next)->process($request, $this);
            $router->log("Router: Leaving Middleware: %s", $next);
            return $response;
        } else {
            $params = $request->attributes->get('controller');

            // Call controller with request and args
            $router->log("Router: Calling Controller: %s@%s", $params->className, $params->method);
            $return = (new $params->className($params->container))->{$params->method}($request, ...array_values($params->args));
            $router->log("Router: Controller Left");
            
            // Instantly return Response objects
            if ($return instanceof Response) {
                return $return;
            }
            
            // If not a string (or something easily castable to a string) assume Json -> JsonResponse
            if (is_array($return) or is_object($return)) {
                return new JsonResponse(
                    $return,
                    Response::HTTP_OK,
                    array('content-type' => 'application/json')
                );
            }
            
            // Strings, and other primitives.
            return new Response(
                $return,
                Response::HTTP_OK,
                array('content-type' => 'text/html')
            );
        }
    }
Call middleware in order, and then call the specified Controller, using a modified PSR15 Middleware pattern

@param Request $request The request for the current route
@return Response