php 45 lines · 6 steps

Building a profanity filter in PHP

A class compiles a blocklist into one regex, then uses it to detect and mask offensive words.

Explained by highlit
1<?php
2 
3namespace App\Services;
4 
5final class ProfanityFilter
6{
7 private const REPLACEMENT = '****';
8 
9 private array $blocklist = [
10 'damn', 'hell', 'crap', 'jerk', 'idiot', 'moron', 'bastard',
11 ];
12 
13 private string $pattern;
14 
15 public function __construct(array $extraWords = [])
16 {
17 $words = array_merge($this->blocklist, $extraWords);
18 $escaped = array_map(static fn (string $word): string => preg_quote($word, '/'), $words);
19 
20 $this->pattern = '/\b(?:' . implode('|', $escaped) . ')\b/iu';
21 }
22 
23 public function clean(string $comment): string
24 {
25 return preg_replace_callback(
26 $this->pattern,
27 static fn (array $match): string => self::mask($match[0]),
28 $comment,
29 ) ?? $comment;
30 }
31 
32 public function contains(string $comment): bool
33 {
34 return (bool) preg_match($this->pattern, $comment);
35 }
36 
37 private static function mask(string $word): string
38 {
39 $length = mb_strlen($word);
40 
41 return $length <= 2
42 ? str_repeat('*', $length)
43 : mb_substr($word, 0, 1) . str_repeat('*', $length - 1);
44 }
45}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Compiling a word list into a single regex once in the constructor keeps every later match fast and consistent.
  2. 2Escaping user-supplied words with preg_quote prevents them from being interpreted as regex metacharacters.
  3. 3Multibyte-aware string functions like mb_strlen and mb_substr keep masking correct for non-ASCII text.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Building a profanity filter in PHP — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code