 [ ← blog / drupal ](/) Jan 10, 2023  ·  \#drupal  ·  3 min read 

# Trimming whitespace from Drupal form submissions using an HTTP middleware



Leading and trailing whitespaces. The bane of normalized data everywhere. It seems like a constant task that needs to be performed on incoming user data. When working with Laravel, I use the [`TrimStrings` middleware](https://laravel.com/docs/9.x/requests#input-trimming-and-normalization). Calls PHP's built-in `trim` function on all string values for the request query and input values – including JSON! While working on my project [WhiskeyDex](https://whiskeydex.com/), I noticed how mobile device keyboards always add trailing whitespace when using the autocomplete functionality. This, in turn, meant user-created data will end up with trailing whitespaces. I started to just quick-fix in the single instance and realized it should be solved once and for all.

You can read the [Middleware API documentation](https://www.drupal.org/docs/8/api/middleware-api/overview) if you are unfamiliar with HTTP middleware in Drupal or as a concept. Drupal uses [Symfony's HttpKernel component](https://symfony.com/doc/current/components/http_kernel.html) to handle incoming requests and outgoing responses. HTTP middleware services implement the `HttpKernelInterface` interface and register themselves as services tagged with `http_middleware`. When Drupal handles a request, it invokes all registered `http_middleware` to process the request. For instance, Drupal's `PageCache` is an HTTP middleware to immediately return responses from the cache if available.

Drupal does not provide as many developer-friendly tools as Laravel, so I needed to recreate my version of the `TrimStrings` middleware. It's relatively simple, requiring one class and its service registration. Note: the following code is for PHP 8.1.

In your module, create a `src/StackMiddleware/TrimMiddleware.php` file with the following:

```php
<?php

declare(strict_types=1);

namespace Drupal\whiskeydex\StackMiddleware;

use Symfony\Component\HttpFoundation\InputBag;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;

final class TrimMiddleware implements HttpKernelInterface {

  public function __construct(
    private readonly HttpKernelInterface $app
  ) {
  }

  public function handle(
    Request $request,
    int $type = self::MAIN_REQUEST,
    bool $catch = TRUE): Response {
    $this->trimBag($request->query);
    $this->trimBag($request->request);
    return $this->app->handle($request);
  }

  private function trimBag(InputBag $bag): void {
    $bag->replace($this->trimArray($bag->all()));
  }

  private function trimArray(array $array): array {
    return array_map(
      function ($value) {
        if (is_array($value)) {
          return $this->trimArray($value);
        }
        return is_string($value) ? trim($value) : $value;
      },
      $array
    );
  }

}

```

The real work is in `trimArray`, which iterates through the array, and any nested ones, returning trimmed strings.

Then register the service in your module's `services.yml`:

```
services:
  http_middleware.trim:
    class: Drupal\whiskeydex\StackMiddleware\TrimMiddleware
    arguments: ['@kernel']
    tags:
      - { name: http_middleware }
```

Here is a link to a gist of the code as well: <https://gist.github.com/mglaman/4533051f0ec50ebd698bbde93c1ac79b>

 

  ![](/themes/custom/mglaman/images/headshot.webp)Thanks for reading

I write about Drupal, PHP, and the quiet infrastructure behind large sites — caching, config, and testing. New posts land every week or two.

 [Subscribe via RSS →](/rss.xml) [About me →](/about-me) 

 



 

  [← Older

Drupal: cache tags for all, regardless your backend

 ](/blog/drupal-cache-tags-all-regardless-your-backend) [Newer →

Launching my wife's cookie shop web store with Drupal 10 and Square

 ](/blog/launching-my-wifes-cookie-shop-web-store-drupal-10-and-square)