Pragmatism in the real world

Filtering the PSR-7 body in middleware

Sometimes, there’s a requirement to alter the data in the Response’s body after it has been created by your controller action. For example, we may want to ensure that our brand name is consistently capitalised.

One way to do this is to create middleware that looks like this:

$brandFilter = function ($request, $response, $next) {
    // call next middleware
    $response = $next($request, $response);

    $content = (string)$response->getBody();
    $newContent = str_ireplace('nineteen feet', 'Nineteen Feet', $content);

    $response->getBody()->rewind();
    $response->getBody()->write($newContent);

    return $response;
};

This works perfectly, so if my response body contains “I consult through my company, nineteen feet.“, then the output of the $brandFilter middleware is “I consult through thought my company, Nineteen Feet.“, which is exactly what we want.

That’s great, but what happens if the new string is shorter than the old one? For instance, suppose I want to replace all uses of “nineteen feet” with “19FT”.

Using the same middleware, but changing the str_ireplace call with:

$newContent = str_ireplace('nineteen feet', '19FT', $content);

creates this output:

I consult through my company, 19FT.een feet.

That’s not quite what we wanted! This result is because the new string is shorter, so the characters from the old string are still in the PHP stream and as we haven’t change the length, they are still there. The PSR-7 StreamInterface doesn’t allow us access to the underlying stream, so we can’t call ftruncate on it.

Hence, the easiest solution is to replace the Response’s PSR-7 StreamInterface object with a new one containing what we need:

$brandFilter = function ($request, $response, $next) {
    // call next middleware
    $response = $next($request, $response);

    $content = (string)$response->getBody();
    $newContent = str_ireplace('nineteen feet', '19FT', $content);

    $newBody = new \Slim\Http\Body(fopen('php://temp', 'r+'));
    $newBody->write($newContent);

    return $response->withBody($newBody);
};

The output is now what we expect:

I consult through my company, 19FT.

6 thoughts on “Filtering the PSR-7 body in middleware

Comments are closed.