Feature Request: Deferred stream write

Currently the sequence of the call stack is starting from the outer most Middleware, through the layers of Middlewares, to the route Controller, then back through the Middlewares from inner most to outer most. Right now, I cannot think of a use case where Middleware can be useful when on the way out. This is because we are writing directly to the output stream along the way and there is no way to alter the output on the way out.

Therefore I want to propose deferred stream writing, i.e. only write after exiting the outer most Middleware, and allow changes to the respond object along the way. as an example, we can use DeferredRendererTrait in Respond class.

trait DeferredRendererTrait
{
    public $context;
    private $writer;

    public function setContext($writer, $context)
    {
        $this->context = $context;
        $this->writer = $writer;
        return $this;
    }

    public function render($request, $response)
    {
        // must return ResponseInterface
        return call_user_func_array($this->writer, [$request, $response, $this->context]);
    }
}

Then in MiddlewareAwareTrait

public function callMiddlewareStack(ServerRequestInterface $request, ResponseInterface $response)
{
    ...

    $start = $this->stack->top();
    $this->middlewareLock = true;
    $response = $start($request, $response);
    $this->middlewareLock = false;
    
    // add this line to render the deferred context
    $response->render($request, $response);

    return $response;
}

In this way, we can modify DeferredRendererTrait::context any where in the call stack. In the controller class, we can also call another route handler from the current one to generate the context and then modify the context in the current handler. For example:

class Controller
{
	public getEditForm($request, $response)
	{
		$context = ... // prepare context data, fetching from DB or otherwise

		return $response->setContext(function($request, $response, $context) {
			// defer twig view compiling until the end to give middleware
			// and other callers a chance to modify the context
			$this-view->render($response, 'edit.twig', $context);
		}, $context);
	}

	public postEditForm($request, $response)
	{
		... // validate inputs

		if(!empty($error))
		{
			// automatically prepare context for edit form
			$response = $this->getEditForm();

			// modify context to include errors
			$response->context['error'] = $error;

			// modify context to populate form with old inputs
			// instead of using DB data
			$response->context['form'] = $request->getParams();

			// finally, just return the response
			return $response;
		}
		else
		{
			return $response->withRedirect(...);
		}
	}
}

Welcome to the Slim community, @jakesee. Generally, we use the Issues tracker over at Github for bugs and feature requests and we use the forum here for support type questions. I see @geggleto sent you over here for support, but this is a different topic than you posted there.

Nonetheless, the middleware signature and flow will not change for Slim 3.x and I believe it has been set for Slim 4.x. You may wish to contribute your thoughts to the following topics:

As to your original question about modifying the response on the way back out, consider an example where you might want to filter the PSR-7 body.

Hi @tflight , after asking the question on github, I dug deeper into the source code and confirmed what I intended to do is not directly achievable, so I posted this feature request here to see if anyone else has considered this scenario. Thanks for pointing me to the various discussion topics, I will take some time to read through and confirm them myself.

1 Like