Phonetische Suchverfahren, wie Soundex und Metaphone Code, helfen dabei, ähnlich klingende Wörter trotz unterschiedlicher Schreibweise zu finden. Das ist ein wichtiger Vorteil bei unsauberer Dateneingabe.


Warum phonetische Suche?
Schon kleine Tippfehler („Müller“ → „Mueller“) oder abweichende Rechtschreibvarianten („Meier“ / „Mayer“) bringen klassische 1-zu-1-Vergleiche schnell an ihre Grenzen. Phonetische Algorithmen erzeugen daher Klang-Schlüssel, die gleich ausgesprochenen Wörtern denselben Code zuweisen. In PHP gehört soundex() zur Standardbibliothek. Das Verfahren stammt allerdings aus dem Jahr 1918 und wurde für englische Namen entwickelt (Wikipedia). Dieser Artikel zeigt:
-
Wie soundex() intern funktioniert und korrekt eingesetzt wird.
-
Warum es für deutsche Daten häufig versagt.
-
Welche Alternativen (Metaphone, Kölner Phonetik, …) in PHP bereitstehen.
-
Praxisrezepte für „Meinten Sie …?“-Features, SQL-Integration und Performance-Tuning.
PHP soundex() im Detail
Um die Funktionsweise von soundex() zu verstehen, lohnt sich ein Blick auf seine Ursprünge und das zugrunde liegende Codierungsschema.
Historie & Algorithmus-Prinzip
Robert C. Russell und Margaret K. Odell patentierten Soundex 1918/1922 als Verfahren zur Indexierung von US-Volkszählungen; Donald Knuth popularisierte es 1973 in The Art of Computer Programming (PHP).
Kurzform der Regeln (US-Variante, die PHP implementiert): Diese Funktionen sind unabhängig von Groß- und Kleinschreibung.
-
Ersten Buchstaben merken.
-
Vokale (A, E, I, O, U, Y), H und W verwerfen.
-
Verbleibende Konsonanten in Ziffern übersetzen
(B / F / P / V → 1, C / G / J / K / Q / S / X / Z → 2, D / T → 3, L → 4, M / N → 5, R → 6).
-
Doppelte Codes streichen, wenn sie direkt aufeinanderfolgen.
-
Ergebnis auf vier Zeichen bringen: kürzen oder mit 0 auffüllen.
Beispiel:
echo soundex('Robert'); // R163
echo soundex('Rupert'); // R163
Syntax
string soundex ( string $string )
-
Parameter $string – zu kodierende Zeichenkette.
-
Rückgabe – 4-stelliger String; für leere Eingabe "0000".
-
Seit PHP 4 verfügbar; Implementierung nach Knuth (PHP).
Grundlegende Beispiele
echo soundex('Knuth');
// K530
echo soundex('Canuth');
/* C530 - anderer Code → klingt nicht „gleich“ */
echo soundex('Schmidt');
// S530
echo soundex('Smith');
/* S530 - identischer Code trotz anderer Schreibweise */
Typische Fallstricke
Trotz der Einfachheit von soundex() sollten Sie die folgenden Einschränkungen kennen, um unerwartete Ergebnisse zu vermeiden.
| Stolperfalle | Erklärung / Work-around |
| Sprachabhängigkeit | Nur englische Phonetik (kein Umlaut, kein „sch“). |
| Fixed Length = 4 | Viele Kollisionen in großen Datenbeständen. |
| Nicht-alphabetische Zeichen | Zahlen & Sonderzeichen werden ignoriert. |
| Case-Insensitivity | Groß/Klein wird ignoriert – sinnvoll, aber beachten. |
Stärken & Schwächen
soundex() bietet einige nützliche Eigenschaften, die es besonders für einfache phonetische Vergleiche attraktiv machen.
Vorteile
- Eingebaut – keine Extensions, keine Abhängigkeiten.
- O( n ) Zeitkomplexität, minimaler Speicher.
- Gute Trefferquote bei einfachen englischen Namen.
Nachteile
- Sehr grob: nur 6 Kategorien + erstes Initial.
- Viele False Positives & False Negatives.
- Nicht geeignet für Deutsch (siehe § 4).
- Nicht für Wörter > 1 Wort (Straßen, Titel) optimiert.
Soundex vs. deutsche Sprache
Umlaute („Müller“), Konsonantencluster („sch“, „ch“), Vorsilben („Pf-“) – all das kennt die englische Regelmenge nicht, da sie sich auf die Buchstaben und deren Aussprache konzentriert. Ergebnis:
echo soundex('Müller');
// M460
echo soundex('Möller');
/* M460 ✓ (ok) */
echo soundex('Schule');
// S400
echo soundex('Skala');
/* S240 ✗ (klingt völlig anders → gleiches Initial, zu grob) */
Für deutschsprachige Suchen ist Soundex daher meist unbrauchbar.
Bessere Alternativen in PHP
Für genauere phonetische Vergleiche stehen mit metaphone() und double_metaphone leistungsfähigere Alternativen zur Verfügung.
echo metaphone('Müller');
// ML
echo double_metaphone('Knuth');
// ["N0", "KN0"]
-
Feineres Regelwerk, zwei Codes pro Wort (primär/sekundär).
-
Double-Metaphone-Extension via PECL doublemetaphone installieren (pecl.php.net).
-
Englisch-fokussiert, aber besser als Soundex.
2. Kölner Phonetik: Die Lösung für Deutsch
Open-Source-Bibliothek via Composer:
composer require patrickschur/cologne-phonetic
use ColognePhoneticColognePhonetic;
$cp = new ColognePhonetic();
echo $cp->convert('Schneider'); // 8627
Algorithmus speziell für deutsche Laute (GitHub). Sehr hohe Präzision bei Namen & Alltagswörtern.
3. Levenshtein-Distanz (levenshtein())
Misst Tippfehler‐Distanz (Einfügen/Löschen/Ersetzen). Gut in Kombi mit phonetischen Filtern, um Ergebnisse zu ranken (PHP).
4. similar_text()
Prozentuale Zeichenähnlichkeit. Einfach, aber O( n² ) – bei langen Strings teuer.
5. Vergleichsübersicht
Die folgende Tabelle stellt die gängigsten Algorithmen zur Ähnlichkeitsprüfung in PHP gegenüber und zeigt, für welchen Anwendungsfall sie sich eignen.
| Funktion | Typ | Sprache | Stärken | Schwächen |
| soundex() | Phonetik (klass.) | Englisch | Eingebaut, schnell | Sehr grob, 4 Zeichen, ungeeignet für DE |
| metaphone() | Phonetik | Englisch | Feiner, längere Codes | Keine Umlaute |
| double_metaphone | Phonetik doppelt | Englisch | Zwei Varianten → mehr Treffer | PECL-Install. |
| Kölner Phonetik | Phonetik | Deutsch | Beste DE-Treffer | Userland-Lib, längere Codes |
| levenshtein() | Edit-Distanz | Spracheneutral | Findet Tippfehler | Rechenintensiv, insbesondere bei der Berechnung von Edit-Distanzen von zwei Strings. |
| similar_text() | Zeichenquote | Spracheneutral | Prozentwert simpel | O(n²), kein Klang |
Praktische Anwendungsfälle und fortgeschrittene Techniken
Nachdem wir die Grundlagen, Stärken und insbesondere die Schwächen von soundex() sowie dessen Alternativen kennengelernt haben, wollen wir uns nun konkreten Anwendungsfällen und fortgeschritteneren Techniken widmen. Diese Beispiele zeigen, wie Sie Ähnlichkeitssuchen in Ihre PHP-Anwendungen integrieren und optimieren können.
1. Aufbau einer “Meinten Sie…?” (Did you mean?) Funktion
Eine “Meinten Sie…?”-Funktion ist eine häufige und nutzerfreundliche Ergänzung für Suchformulare. Wenn ein Nutzer einen Suchbegriff eingibt, der keine oder nur wenige Ergebnisse liefert, kann das System alternative, ähnlich klingende oder geschriebene Begriffe vorschlagen.
-
Grundlegendes Konzept mit soundex() (und Warnung vor dessen Schwächen):
Die einfachste Idee wäre, den soundex()-Wert des Nutzereingabe-Strings mit den soundex()-Werten einer Liste bekannter Begriffe (z.B. Produktnamen, Artikelüberschriften, Tags) zu vergleichen.
<?php
/**
* Gibt Vorschläge für ähnlich klingende Begriffe basierend auf der Soundex-Methode zurück.
*
* @param string $sSuchbegriff Der Suchbegriff, für den Vorschläge ermittelt werden sollen.
* @param array $aBegriffsliste Liste mit verfügbaren Begriffen zur Prüfung.
* @return array Array mit alternativen Vorschlägen (phonetisch ähnlich, aber nicht exakt gleich).
*/
function getSoundexVorschlaege(string $sSuchbegriff, array $aBegriffsliste): array {
$aVorschlaege = [];
$sSuchbegriffSoundex = soundex($sSuchbegriff);
/* Abbruch bei ungültigem oder leerem Soundex */
if ($sSuchbegriffSoundex === "0000") {
return $aVorschlaege;
}
foreach ($aBegriffsliste as $sBegriff) {
if (soundex($sBegriff) === $sSuchbegriffSoundex) {
/* Nicht identische Begriffe aufnehmen */
if (strtolower($sBegriff) !== strtolower($sSuchbegriff)) {
$aVorschlaege[] = $sBegriff;
}
}
}
return array_unique($aVorschlaege);
}
/* Beispiel */
$aProdukte = [
"Apfel", "Banane", "Birne", "Orange",
"Ananas", "Erdbeere", "Himbeere", "Brombäre" /* absichtlicher Fehler */
];
$sSuchbegriff = "Brombeere";
$aSoundexVorschlaege = getSoundexVorschlaege($sSuchbegriff, $aProdukte);
if (!empty($aSoundexVorschlaege)) {
echo "Meinten Sie vielleicht: " . implode(", ", $aSoundexVorschlaege) . "?<br>";
} else {
echo "Keine direkten phonetischen Vorschläge für '" . htmlspecialchars($sSuchbegriff) . "' gefunden.<br>";
}
?>
Warnung: Wie bereits ausführlich diskutiert, ist soundex() sehr grob und stark auf die englische Phonetik ausgerichtet. Für deutsche Begriffe oder bei feineren Unterschieden liefert dieser Ansatz oft unbefriedigende oder falsche Ergebnisse. Er kann als allererste, sehr einfache Filterstufe dienen, sollte aber selten die alleinige Lösung sein.
-
Verbesserter Ansatz: Kombination aus Kölner Phonetik (für Deutsch) oder Metaphone mit Levenshtein:
Ein wesentlich robusterer Ansatz kombiniert einen besseren phonetischen Algorithmus (Kölner Phonetik für Deutsch, Metaphone für Englisch/International) mit einer Editierdistanzmessung wie Levenshtein.
-
Phonetische Vorauswahl: Filtern Sie Ihre Begriffsliste mit Kölner Phonetik oder Metaphone, um eine Menge von Kandidaten zu erhalten, die ähnlich klingen.
-
Feinabstimmung mit Levenshtein: Wenden Sie auf diese Kandidaten die levenshtein()-Funktion an, um diejenigen zu finden, die dem ursprünglichen Suchbegriff auch in der Schreibweise am ähnlichsten sind (also wenige Tippfehler aufweisen). Ein kleiner Levenshtein-Abstand (z.B. 1 oder 2) deutet auf eine hohe Ähnlichkeit hin.
-
Code-Beispiel für eine einfache Implementierung (Kölner Phonetik + Levenshtein):
<?php
/**
* Vereinfachte (nicht korrekte) Demo-Funktion zur Kölner Phonetik.
*
* @param string $sEingabe Der Eingabestring.
* @return string Phonetischer Code oder "0" bei leerem Ergebnis.
*/
function koelnerPhonetik(string $sEingabe): string {
$sEingabe = strtoupper($sEingabe);
$sEingabe = str_replace(['Ä', 'Ö', 'Ü', 'ß'], ['A', 'O', 'U', 'SS'], $sEingabe);
$sEingabe = preg_replace('/[^A-Z]/', '', $sEingabe); /* Nur Buchstaben */
if (strlen($sEingabe) > 4) {
return substr($sEingabe, 0, 1) . substr($sEingabe, -3); /* Platzhalter-Logik */
}
return $sEingabe ?: "0";
}
/**
* Liefert Vorschläge auf Basis der Kölner Phonetik kombiniert mit Levenshtein-Abstand.
*
* @param string $sSuchbegriff Der zu prüfende Begriff.
* @param array $aBegriffsliste Liste möglicher Begriffe.
* @param int $iMaxDistanz Maximal erlaubte Levenshtein-Distanz (Standard: 2).
* @return array Sortierte Liste der ähnlich klingenden Begriffe.
*/
function getErweiterteVorschlaege(string $sSuchbegriff, array $aBegriffsliste, int $iMaxDistanz = 2): array {
$aVorschlaege = [];
$sSuchPhonetik = koelnerPhonetik($sSuchbegriff);
if ($sSuchPhonetik === "0") {
return $aVorschlaege;
}
$aPhonetischeKandidaten = [];
foreach ($aBegriffsliste as $sBegriff) {
if (koelnerPhonetik($sBegriff) === $sSuchPhonetik) {
$aPhonetischeKandidaten[] = $sBegriff;
}
}
foreach ($aPhonetischeKandidaten as $sKandidat) {
if (strtolower($sKandidat) === strtolower($sSuchbegriff)) {
continue;
}
$iDistanz = levenshtein(strtolower($sSuchbegriff), strtolower($sKandidat));
if ($iDistanz !== -1 && $iDistanz <= $iMaxDistanz) {
$aVorschlaege[$sKandidat] = $iDistanz;
}
}
asort($aVorschlaege); /* Beste Übereinstimmungen zuerst */
return array_keys($aVorschlaege);
}
/* Beispielanwendung */
$aProdukte = [
"Maier", "Meyer", "Mayer", "Müller", "Schulze",
"Schmidt", "Schneider", "Apfelstrudel", "Apfelkuchen"
];
$sSuchbegriff = "Meir";
$aVorschlaege = getErweiterteVorschlaege($sSuchbegriff, $aProdukte);
if (!empty($aVorschlaege)) {
echo "Meinten Sie für '" . htmlspecialchars($sSuchbegriff) . "' vielleicht: " . implode(", ", $aVorschlaege) . "?<br>";
} else {
echo "Keine fortgeschrittenen Vorschläge für '" . htmlspecialchars($sSuchbegriff) . "' gefunden.<br>";
}
?>
Dieses kombinierte Verfahren ist rechenintensiver, liefert aber deutlich bessere Ergebnisse, da es sowohl phonetische Ähnlichkeit als auch Tippfehler berücksichtigt.
2. Duplikat-Erkennung bei Namen
In Kundendatenbanken schleichen sich schnell Duplikate ein, wenn derselbe Name unterschiedlich geschrieben wird. Mit einer Kombination aus soundex() und levenshtein() lassen sich solche Einträge automatisch aufspüren.
<?php
$aKunden = [
'Schmidt', 'Schmitt', 'Schmid',
'Müller', 'Mueller', 'Miller',
'Fischer', 'Meier', 'Meyer',
'Schulze', 'Schulz'
];
$aDuplikate = [];
$iAnzahl = count($aKunden);
for ($i = 0; $i < $iAnzahl; $i++) {
for ($j = $i + 1; $j < $iAnzahl; $j++) {
$bSoundexGleich = (
soundex($aKunden[$i]) === soundex($aKunden[$j])
);
$iDistanz = levenshtein(
strtolower($aKunden[$i]),
strtolower($aKunden[$j])
);
if ($bSoundexGleich || $iDistanz <= 2) {
$aDuplikate[] = [
'name1' => $aKunden[$i],
'name2' => $aKunden[$j],
'soundex' => $bSoundexGleich,
'distanz' => $iDistanz
];
}
}
}
foreach ($aDuplikate as $aPaar) {
echo $aPaar['name1'] . ' / '
. $aPaar['name2']
. ' (Soundex: '
. ($aPaar['soundex'] ? 'ja' : 'nein')
. ', Distanz: '
. $aPaar['distanz'] . ")" . "<br>";
}
?>
Die Ausgabe zeigt potenzielle Duplikate mit beiden Bewertungskriterien. Durch die Kombination von phonetischem Vergleich und Editierdistanz werden sowohl klangliche Treffer als auch Tippfehler erfasst. In der Praxis können Sie die erkannten Duplikate dem Benutzer zur manuellen Bestätigung vorlegen, bevor sie zusammengeführt werden.
3. soundex() und Datenbanken (z.B. MySQL, PostgreSQL)
Wenn Sie soundex() (oder einen anderen phonetischen Code) für die Suche in einer Datenbank verwenden möchten, ist die direkte Berechnung des Codes in der WHERE-Klausel für jeden Datensatz oft ineffizient.
-
Speichern von Soundex-Schlüsseln in einer separaten Spalte:
Der performantere Ansatz besteht darin, den soundex()-Wert (oder den Code eines besseren Algorithmus) für die relevanten Textspalten (z.B. Produktnamen, Kundennamen) vorab zu berechnen und in einer eigenen Spalte in der Datenbanktabelle zu speichern.
-
Beim Einfügen eines neuen Datensatzes berechnen Sie den soundex()-Wert in PHP und speichern ihn mit ab.
-
Beim Aktualisieren eines relevanten Feldes berechnen und aktualisieren Sie auch den zugehörigen soundex()-Wert.
Beispiel-Tabellenstruktur (MySQL):
CREATE TABLE produkte (
id INT AUTO_INCREMENT PRIMARY KEY,
produkt_name VARCHAR(255) NOT NULL,
produkt_soundex VARCHAR(4) -- Für Standard-Soundex
-- andere Spalten...
);
-
Indizierung der Soundex-Spalte zur Beschleunigung von Suchen:
Um Suchabfragen auf dieser soundex-Spalte schnell zu machen, sollten Sie unbedingt einen Datenbankindex darauf erstellen.
CREATE INDEX idx_produkt_soundex ON produkte (produkt_soundex);
-
SQL-Beispielabfragen:
Wenn Sie nun nach einem Begriff suchen, berechnen Sie dessen soundex()-Wert in PHP und verwenden diesen Wert in Ihrer SQL-Abfrage:
Hier ist dein PHP-Code formatiert nach deinen Konventionen:
<?php
/* Annahme: $oPdo ist ein gültiges PDO-Datenbankobjekt */
$sSuchbegriff = "Smith"; // Eingabe vom Nutzer
$sSuchbegriffSoundex = soundex($sSuchbegriff);
if ($sSuchbegriffSoundex !== "0000") {
$oStmt = $oPdo->prepare(
"SELECT produkt_name FROM produkte WHERE produkt_soundex = :sSoundexWert"
);
$oStmt->bindParam(':sSoundexWert', $sSuchbegriffSoundex);
$oStmt->execute();
$aErgebnisse = $oStmt->fetchAll(PDO::FETCH_COLUMN);
if ($aErgebnisse) {
echo "Produkte ähnlich zu '" . htmlspecialchars($sSuchbegriff) . "':<br>";
echo "<ul>";
foreach ($aErgebnisse as $sProdukt) {
echo "<li>" . htmlspecialchars($sProdukt) . "</li>";
}
echo "</ul>";
} else {
echo "Keine phonetisch ähnlichen Produkte für '" . htmlspecialchars($sSuchbegriff) . "' gefunden.<br>";
}
} else {
echo "Ungültiger Soundex-Wert für den Suchbegriff.<br>";
}
?>
MySQL bietet auch eine native SOUNDEX()-Funktion. Wenn Sie diese verwenden, können Sie den Wert direkt in der Abfrage vergleichen: WHERE SOUNDEX(produkt_name) = SOUNDEX(‘Suchbegriff’). Dies ist jedoch meist langsamer als der Vergleich mit einer vorab berechneten, indizierten Spalte, da SOUNDEX(produkt_name) für jede Zeile berechnet werden müsste (es sei denn, die Datenbank kann einen Funktionsindex verwenden, was nicht alle Systeme optimal unterstützen).
-
Diskussion: Wann ist dies sinnvoll und wann sind datenbankeigene Volltextsuche-Features oder spezielle Fuzzy-Search-Erweiterungen überlegen?
-
Sinnvoll für soundex(): In sehr einfachen Szenarien, bei ausschließlich englischsprachigen Daten, oder wenn Sie eine extrem simple phonetische Suche benötigen und keine komplexeren Tools einsetzen können/wollen. Auch hier ist die indizierte Spalte der On-the-fly-Berechnung vorzuziehen.
-
Datenbankeigene Volltextsuche (FTS): Für die meisten ernsthaften Suchanwendungen sind die FTS-Funktionen moderner Datenbanken (z.B. MySQL Full-Text Search, PostgreSQL Text Search, Elasticsearch, Solr) weitaus überlegen. Sie bieten:
- Stemming (Finden von “laufen”, “lief”, “gelaufen” bei Suche nach “lauf”)
- Stop-Word-Filterung (Ignorieren häufiger Wörter wie “der”, “die”, “das”)
- Relevanz-Ranking der Ergebnisse
- Unterstützung für Phrasensuche und boolesche Operatoren
- Oft bessere Mehrsprachigkeit.
-
Spezielle Fuzzy-Search-Erweiterungen: Einige Datenbanken bieten Erweiterungen für Ähnlichkeitssuche, die über einfache Phonetik hinausgehen (z.B. pg_trgm für PostgreSQL, das auf Trigramm-Ähnlichkeit basiert und gut für Tippfehler funktioniert).
Fazit Datenbanken: Die soundex()-Integration über eine separate, indizierte Spalte ist eine technische Möglichkeit, aber für die meisten modernen Anwendungen sind FTS oder spezialisierte Erweiterungen die robustere und leistungsfähigere Wahl.
Die Berechnung von Ähnlichkeitswerten, insbesondere komplexerer Algorithmen oder bei Anwendung auf viele Datensätze, kann die Performance Ihrer Anwendung stark beeinträchtigen.
Durch die Kombination dieser Strategien können Sie die Performance Ihrer Ähnlichkeitssuchfunktionen auch bei größeren Datenmengen und komplexen Anforderungen akzeptabel halten.
Häufige Probleme und Lösungen
Bei der Arbeit mit soundex() und verwandten Funktionen tauchen immer wieder dieselben Fragen auf. Hier die wichtigsten Antworten auf einen Blick.
Warum liefert soundex() für deutsche Wörter schlechte Ergebnisse?
Soundex wurde für die englische Sprache entwickelt und kennt weder Umlaute noch typisch deutsche Laute wie „sch“ oder „ch“. Verwenden Sie für deutschsprachige Texte die Kölner Phonetik (siehe oben).
Wie verbessere ich die Treffergenauigkeit?
Kombinieren Sie mehrere Algorithmen: Nutzen Sie zuerst einen phonetischen Filter (soundex() oder metaphone()) für die Vorauswahl und wenden Sie dann levenshtein() oder similar_text() für die Feinabstimmung an. Dieses zweistufige Verfahren liefert in der Praxis die besten Ergebnisse.
Warum gibt soundex() für leere Strings „0000“ zurück?
Das ist das definierte Verhalten der Funktion. Wenn kein Buchstabe vorhanden ist, liefert soundex() den Standardcode "0000". Prüfen Sie Eingaben daher vor dem Aufruf mit strlen() oder ctype_alpha() auf Gültigkeit.
Warum erhalten völlig verschiedene Wörter denselben Soundex-Code?
Soundex verwendet nur sechs Konsonantengruppen und den ersten Buchstaben. Bei einem Ergebnis von maximal vier Zeichen sind Kollisionen unvermeidlich. Falls zu viele False Positives auftreten, wechseln Sie zu metaphone() oder verwenden Sie zusätzlich levenshtein() zum Filtern.