php
55 lines · 8 steps
Normalizing human names in PHP
A small class that cleans, keys, and title-cases personal names while respecting Unicode and naming particles.
Explained by
highlit
1<?php
2
3declare(strict_types=1);
4
5namespace App\Support;
6
7use Normalizer;
8
9final class NameNormalizer
10{
11 private const PARTICLES = ['de', 'la', 'van', 'von', 'der', 'den', 'del', 'di', 'da', 'du', 'le'];
12
13 public function normalize(string $name): string
14 {
15 $name = Normalizer::normalize($name, Normalizer::FORM_C) ?: $name;
16 $name = $this->transliterate($name);
17
18 $name = mb_strtolower(trim($name), 'UTF-8');
19 $name = preg_replace('/[\x{2018}\x{2019}\x{201C}\x{201D}]/u', "'", $name);
20 $name = preg_replace('/[^\p{L}\p{N}\s\'-]/u', ' ', $name);
21 $name = preg_replace('/\s+/u', ' ', trim($name));
22
23 return $name;
24 }
25
26 public function lookupKey(string $name): string
27 {
28 return str_replace([' ', '-', "'"], '', $this->normalize($name));
29 }
30
31 public function canonicalDisplay(string $name): string
32 {
33 $parts = explode(' ', $this->normalize($name));
34
35 $parts = array_map(function (string $part): string {
36 if (in_array($part, self::PARTICLES, true)) {
37 return $part;
38 }
39
40 return implode('-', array_map(
41 static fn (string $seg): string => mb_convert_case($seg, MB_CASE_TITLE, 'UTF-8'),
42 explode('-', $part)
43 ));
44 }, $parts);
45
46 return implode(' ', $parts);
47 }
48
49 private function transliterate(string $value): string
50 {
51 $ascii = transliterator_transliterate('Any-Latin; Latin-ASCII; [\u0080-\u7fff] remove', $value);
52
53 return $ascii !== false ? $ascii : $value;
54 }
55}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Unicode normalization plus transliteration turns visually identical but differently-encoded names into one comparable form.
- 2Separating a lookup key from a display form lets you match loosely while still presenting names nicely.
- 3Name capitalization needs domain rules — lowercase particles like 'van' and 'de' shouldn't be title-cased.
Related explainers
php
<?php namespace App\Http\Controllers;
Handling Stripe webhooks in Laravel
webhooks
signature-verification
dependency-injection
Intermediate
7 steps
rust
use once_cell::sync::Lazy; use regex::Regex; static CREDIT_CARD: Lazy<Regex> = Lazy::new(|| {
Redacting sensitive data from logs in Rust
regex
lazy-initialization
checksum-validation
Intermediate
9 steps
php
<?php namespace App\FeatureFlags;
How a feature flag evaluator decides
feature-flags
rollout
hashing
Intermediate
8 steps
php
namespace App\Providers; use Illuminate\Support\Facades\Blade; use Illuminate\Support\ServiceProvider;
Building a @money Blade directive in Laravel
blade-directive
service-provider
localization
Intermediate
5 steps
php
<?php namespace App\Queue;
Deduplicating a job queue with Redis
deduplication
redis
atomicity
Advanced
8 steps
rust
use once_cell::sync::Lazy; use regex::Regex; static NON_ALPHANUMERIC: Lazy<Regex> = Lazy::new(|| Regex::new(r"[^a-z0-9]+").unwrap());
Building URL slugs in Rust
string-processing
regex
transliteration
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/normalizing-human-names-in-php-explained-php-9d84/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.