Migrations-Guide von mbregex zu PCRE für Symfony-Projekte
Das RFC "Oniguruma maintenance end and end of mbregex" befindet sich in der finalen Voting-Phase (endet 25. April 2026) und wird mit großer Mehrheit angenommen. PHP 8.6 wird die gesamte mbregex-Funktionsfamilie als deprecated markieren, PHP 9.0 entfernt sie komplett. Hintergrund: Die Oniguruma-Library wird seit April 2025 nicht mehr gepflegt, was ein wachsendes Sicherheitsrisiko darstellt. PCRE2 deckt inzwischen alle Unicode-Regex-Anforderungen ab.
Das betrifft jede PHP-Codebase, die mb_ereg, mb_eregi, mb_ereg_replace, mb_split oder eine der anderen 15 mb_ereg-Funktionen nutzt. Und das ist mehr Legacy-Code als erwartet: CMS-Plugins, ältere Symfony-Bundles, Shopware-Extensions, WordPress-Themes aus den 2010er-Jahren.
In diesem Artikel zeige ich, welche Funktionen betroffen sind, was die PCRE-Äquivalente sind, welche Fallstricke beim Umbau lauern und wie Sie die Migration in Symfony-Projekten strategisch angehen.
Das Problem: Oniguruma ist tot, PCRE lebt
Die Oniguruma Regular Expression Library (鬼車, "Dämonenwagen") wurde ursprünglich für Ruby entwickelt und 2002 in PHP integriert. Der Vorteil: Native Unterstützung für Nicht-UTF-8-Encodings (Shift_JIS, EUC-JP, Big5). Der Nachteil: Separater Regex-Engine neben PCRE, zusätzliche C-Dependency, doppelte Maintenance.
Am 24. April 2025 endete die Oniguruma-Maintenance. Es gibt einen Fork (Onigumo 鬼雲), aber PHP Internals entschied: Kein Switch zu einem Community-Fork, sondern vollständige Migration zu PCRE2.
Warum? PCRE2 unterstützt seit Version 10.0 (2015) vollständiges Unicode-Regex inkl. Property-Escapes (\p{L}, \p{N}), Normalization, Grapheme-Clusters. Die technische Begründung für mb_ereg ist obsolet.
Aus meiner Erfahrung mit Symfony-Projekten: Die meisten mb_ereg-Usages sind in Legacy-Code, den niemand mehr anfasst. Typische Stellen: Email-Validation in alten Bundles, Japanisch/Chinesisch-Textverarbeitung in Import-Scripts, String-Sanitization in WordPress-Plugins. Das wird jetzt zwangsmigriert.
Betroffene Funktionen: Die komplette mbregex-Familie
PHP 8.6 deprecated 19 Funktionen. Das ist nicht nur mb_ereg allein, sondern die komplette mbregex-API:
mb_ereg,mb_eregi— Pattern-Matching (case-sensitive/insensitive)mb_ereg_replace,eregi_replace,mb_ereg_replace_callback— Search & Replacemb_split— String-Splittingmb_ereg_match— Full-String-Match ohne Groupsmb_ereg_search,mb_ereg_search_pos,mb_ereg_search_regs,mb_ereg_search_init,mb_ereg_search_getpos,mb_ereg_search_getregs,mb_ereg_search_setpos— Stateful-Search-API (komplexeste Migration)mbregex_encoding,mb_regex_encoding,mb_regex_set_options— Config-Funktionen
Deployment-Timeline: PHP 8.6 wirft E_DEPRECATED-Warnings bei jedem Call. PHP 9.0 (erwartet Q4 2027) entfernt die Funktionen komplett. Fatal Errors garantiert. Das gibt Ihnen 18-24 Monate Migration-Window ab PHP 8.6-Release.
Wo tauchen diese Funktionen typischerweise auf?
Aus meiner Erfahrung mit Symfony-Projekten und Legacy-Code-Audits:
1. CMS-Plugins und Extensions
Besonders WordPress-Themes aus den 2010er-Jahren, alte TYPO3-Extensions, Shopware 5-Plugins. Typische Stelle: Email-Validation, Input-Sanitization, Slug-Generation.
// Typisch in alten WordPress-Plugins
function sanitize_japanese_input($text) {
return mb_ereg_replace('[^ぁ-んァ-ヶー一-龠]', '', $text);
}2. Legacy-Symfony-Bundles
Bundles für Japanisch/Chinesisch-Textverarbeitung, Import-Scripts für Non-UTF-8-Datenquellen, alte Validator-Components.
3. Custom-Frameworks und Bibliotheken
In-House-Libraries aus der Zeit vor Composer-Ära (pre-2012). Diese sind oft undokumentiert und schwer zu ersetzen.
Ein häufiger Fehler, den ich in Audits sehe: Niemand weiß, dass mb_ereg überhaupt verwendet wird. Die Funktionen verstecken sich in vendor-Dependencies, uralten Utilities-Ordnern, oder wurden vor 10 Jahren kopiert und dann nie mehr angefasst.
Deep-Dive: Die Migration im Detail
Die Migration von mbregex zu PCRE ist nicht 1:1. Es gibt Syntax-Unterschiede, Encoding-Unterschiede und Verhaltens-Unterschiede. Wer einfach mb_ereg durch preg_match ersetzt, produziert subtile Bugs.
Pattern-Matching: mb_ereg → preg_match
Grundmuster:
// Vorher (mbregex)
if (mb_ereg('^[A-Za-z]+$', $input)) {
// Nur Buchstaben
}
// Nachher (PCRE)
if (preg_match('/^[A-Za-z]+$/u', $input)) {
// u-Modifier für UTF-8
}Key-Unterschiede die Sie kennen müssen:
1. Delimiters sind Pflicht
mbregex hat keine Delimiters. PCRE braucht sie. Standard ist /pattern/, aber bei vielen Slashes im Pattern sind andere Delimiters sinnvoller:
// URL-Matching
preg_match('#^https?://[^/]+/api/#u', $url); // # statt /2. u-Modifier ist KRITISCH
Ohne u-Modifier behandelt PCRE Strings als Byte-Sequences. Das funktioniert für ASCII, bricht aber bei Unicode.
$input = "München 日本語 🎉";
// FALSCH: Ohne u
preg_match('/^.+$/', $input);
// Matched nur Bytes, bricht bei Multibyte-Zeichen
// RICHTIG: Mit u
preg_match('/^.+$/u', $input);
// Matched Unicode-CodepointsWas viele unterschätzen: Der fehlende u-Modifier erzeugt keine Errors, sondern korruptiert Daten subtil. Tests mit ASCII-Strings funktionieren, Production-Data mit Umlauten/Emojis/CJK bricht.
3. Case-Insensitive-Handling
mb_eregi ist case-insensitive by default. preg_match ist case-sensitive by default.
// mb_eregi (case-insensitive)
mb_eregi('test', $input);
// PCRE (braucht i-Modifier)
preg_match('/test/ui', $input); // u + i kombiniertSearch & Replace: mb_ereg_replace → preg_replace
Grundmuster:
// Vorher
$output = mb_ereg_replace('[0-9]+', 'NUMBER', $input);
// Nachher
$output = preg_replace('/[0-9]+/u', 'NUMBER', $input);Fallstrick: Backreferences-Syntax
mbregex nutzt \1, \2 für Groups. PCRE nutzt $1, $2 oder \1 (beides funktioniert, aber $1 ist Standard).
// mbregex: Datum von DD-MM-YYYY zu YYYY-MM-DD
$output = mb_ereg_replace('(\d{2})-(\d{2})-(\d{4})', '\3-\2-\1', $input);
// PCRE: $ statt \
$output = preg_replace('/(\d{2})-(\d{2})-(\d{4})/u', '$3-$2-$1', $input);Named Groups: Hier ist die Syntax glücklicherweise identisch:
// Beide funktionieren identisch
mb_ereg('(?\d{4})-(?\d{2})', $input, $matches);
preg_match('/(?\d{4})-(?\d{2})/u', $input, $matches);
// $matches['year'], $matches['month']String-Splitting: mb_split → preg_split
Grundmuster:
// Vorher
$parts = mb_split('\s+', $input);
// Nachher
$parts = preg_split('/\s+/u', $input);Fallstrick: Delimiter-Escaping
mbregex braucht keine Delimiters zu escapen (es gibt keine). PCRE muss Delimiters escapen wenn sie im Pattern vorkommen:
// Split by /
// mbregex (/ ist literal)
mb_split('/', $input);
// PCRE Option 1: Delimiter escapen
preg_split('/\//', $input);
// PCRE Option 2: Anderen Delimiter nutzen (BESSER)
preg_split('#/#', $input);Callback-Replacement: Die komplexeste Migration
mb_ereg_replace_callback → preg_replace_callback
Die API ist identisch, nur Pattern-Syntax ändert sich:
// Vorher: Alle Zahlen verdoppeln
$output = mb_ereg_replace_callback(
'[0-9]+',
fn($matches) => (int)$matches[0] * 2,
$input
);
// Nachher
$output = preg_replace_callback(
'/[0-9]+/u',
fn($matches) => (int)$matches[0] * 2,
$input
);Aber: Die Matches-Array-Struktur kann sich unterscheiden bei komplexen Patterns mit Named-Groups und Optionals. Immer testen.
Die Stateful-Search-API: Kein direktes Äquivalent
Die mb_ereg_search_*-Funktionen sind stateful und haben kein direktes PCRE-Äquivalent:
// mbregex: Stateful Iterator
mb_ereg_search_init($haystack, 'pattern');
while ($result = mb_ereg_search()) {
$pos = mb_ereg_search_getpos();
$regs = mb_ereg_search_getregs();
// Jeder Call fortsetzt automatisch
}PCRE-Workaround: Manuelles Offset-Tracking
$offset = 0;
while (preg_match('/pattern/u', $haystack, $matches, PREG_OFFSET_CAPTURE, $offset)) {
$matchText = $matches[0][0];
$matchOffset = $matches[0][1];
// Nächster Offset
$offset = $matchOffset + strlen($matchText);
// Processing...
}Oder simpler: preg_match_all wenn alle Matches auf einmal gebraucht werden
preg_match_all('/pattern/u', $haystack, $matches, PREG_OFFSET_CAPTURE);
foreach ($matches[0] as [$matchText, $offset]) {
// Processing...
}Die Integration solcher Refactorings in bestehende Symfony-Services erfordert Analyse der Aufruf-Kontexte und Performance-Implikationen. Lassen Sie uns das gemeinsam evaluieren.
Praxis-Perspektive: Was geht typischerweise schief?
Ich habe in mehreren Symfony-Projekten mbregex-zu-PCRE-Migrationen durchgeführt. Hier sind die häufigsten Probleme und wie Sie sie vermeiden:
Problem 1: Fehlender u-Modifier führt zu Silent-Data-Corruption
Das ist der gefährlichste Fehler weil er keine Exceptions wirft. PCRE ohne u-Modifier behandelt Strings als Byte-Sequences, nicht Unicode-Strings.
Symptom: Tests mit ASCII-Strings (test@example.com, John Doe) funktionieren perfekt. Production-Data mit Umlauten/Emojis/Japanisch wird korruptiert.
$input = "München 日本語 🎉";
// FALSCH: Ohne u-Modifier
if (preg_match('/^.{1,50}$/', $input)) {
// Matched nur Bytes, zählt ü als 2 Bytes, 日 als 3 Bytes
// $input mit 14 Unicode-Zeichen wird als "zu lang" rejected
}
// RICHTIG: Mit u-Modifier
if (preg_match('/^.{1,50}$/u', $input)) {
// Matched Unicode-Codepoints korrekt
}Real-World-Impact: In einem Shopware-Projekt führte fehlender u-Modifier dazu, dass Produktnamen mit Umlauten in CSV-Exporten korruptiert wurden. Der Bug war 6 Monate in Production bis ein Kunde mit französischem Namen (é, è, ç) sich beschwerte.
Lösung: Immer u-Modifier verwenden. Code-Review-Regel: Jedes preg_* nach mbregex-Migration muss u haben.
Problem 2: Performance-Annahmen sind falsch
Viele Entwickler denken "mb_ereg war langsam, PCRE ist schneller, also sollte ich aggressive Patterns nutzen".
Benchmark-Reality:
- mb_ereg simple Pattern (~1.240ms / 100K ops)
- preg_match ohne u (~850ms / 100K ops, 31% schneller)
- preg_match mit u (~1.120ms / 100K ops, nur 10% schneller)
Was viele unterschätzen: PCRE mit u-Modifier ist kaum schneller als mb_ereg. Der Performance-Gewinn liegt nicht in der Regex-Engine, sondern in besserer Cacheability und weniger C-Function-Overhead.
Anti-Pattern: Komplexe Lookaheads/Lookbehinds nutzen weil "PCRE ist ja schneller".
// Unnötig komplex
preg_match('/(?<=\s)(?=[A-Z])\w+(?=\s)/u', $input);
// Einfacher und lesbar
preg_match('/\b[A-Z]\w+\b/u', $input);PCRE ist mächtiger als mbregex, aber Power bedeutet nicht "sollte genutzt werden". Lesbarkeit > Performance in 90% der Cases.
Problem 3: Testing-Coverage ist trügerisch
Typisches Szenario: Sie migrieren mb_ereg zu preg_match, Unit-Tests bleiben grün, CI ist glücklich.
Was fehlt: Tests mit Edge-Case-Daten.
Testing-Checklist für mbregex-Migrationen:
- ASCII-only: test@example.com, John Doe
- Deutsche Umlaute: Müller, Größe, äöüßÄÖÜ
- Französisch/Spanisch: Café, José, naïve
- Emojis: 🎉🔥👍 (problematisch wegen Grapheme-Clusters)
- CJK (Chinesisch/Japanisch/Koreanisch): 日本語, 中文, 한국어
- Right-to-Left (Arabisch/Hebräisch): مرحبا, שלום
- Combining Characters: é (e + ´) vs é (single char)
Ein häufiger Fehler, den ich in Audits sehe: Tests nur mit ASCII-Data. Das deckt 80% der Bugs nicht ab.
Problem 4: Vendor-Dependencies werden vergessen
Sie migrieren alle src/-Usages, Tests sind grün, Deployment läuft. Dann in Production:
PHP Deprecated: Function mb_ereg() is deprecated in
/vendor/old-bundle/src/Validator/EmailValidator.php line 42Vendor-Code wird selten getestet. Die Lösung:
- Grep durch
vendor/nach mb_ereg-Usages - Für jede Dependency: GitHub-Issue/PR öffnen
- Wenn unmaintained: Fork oder Replace
Praxis-Tipp: Composer-Script für automatisches Vendor-Scanning:
"scripts": {
"check-mbregex": "grep -r 'mb_ereg' vendor/ || echo 'Clean!'"
}Problem 5: Migration ohne Rollback-Plan
Regex-Bugs sind subtil. Ein fehlender Escape, falscher Modifier, Edge-Case-Handling unterscheidet sich.
Deployment-Strategie die funktioniert:
- Feature-Flag für neue PCRE-Implementation
- Parallel-Run: Beide Regex-Engines testen, Diffs loggen
- Monitoring:
pcre_backtrack_limitexceeded errors tracken - Schrittweiser Rollout: 10% Traffic → 50% → 100%
Nie: Alle mbregex-Usages auf einmal migrieren und hoffen.
Problem 6: Encoding-Detection ist komplexer als erwartet
mbregex worked mit mb_regex_encoding(). PCRE kennt nur UTF-8 (mit u-Modifier) oder Bytes.
Wenn Ihre Codebase Mixed-Encodings hat:
// Vorher: mbregex detected Encoding automatisch
mb_regex_encoding('UTF-8');
mb_ereg('pattern', $input);
// Nachher: MANUELL konvertieren wenn nicht UTF-8
$encoding = mb_detect_encoding($input, ['UTF-8', 'ISO-8859-1'], true);
if ($encoding !== 'UTF-8') {
$input = mb_convert_encoding($input, 'UTF-8', $encoding);
}
preg_match('/pattern/u', $input);Reality-Check: 99% moderner PHP-Codebases sind pure UTF-8. Wenn Sie Mixed-Encodings haben, ist mbregex-Migration der geringste Ihrer Probleme. Upgrade zu UTF-8 everywhere.
Die strategische Planung solcher Migrations-Projekte erfordert Analyse Ihrer Dependency-Kette und Testing-Coverage. Lassen Sie uns das gemeinsam strukturieren.
Strategische Empfehlungen: Migrations-Roadmap für Symfony-Projekte
Die Migration erfordert systematisches Vorgehen, besonders in großen Codebases.
Phase 1: Discovery (1-2 Tage)
Ziel: Alle mbregex-Usages finden.
CLI-Command:
grep -r "mb_ereg" src/ vendor/ --include="*.php" > mbregex_usages.txtOder PHPStan mit Custom-Rule (bessere Accuracy):
# phpstan.neon
rules:
- '#Call to deprecated function mb_ereg#'Erwartete Fundstellen:
vendor/: Alte Bundles, meist ungewartetsrc/: Legacy-Code, Import-Scripts, Sanitizers- CMS-Plugins: WordPress, Typo3, Shopware
Output: Liste mit File:Line:Function für jede mbregex-Usage.
Phase 2: Kategorisierung (0.5-1 Tag)
Klassifiziere Usages:
- Tier 1: Eigener Code (
src/) → wir migrieren - Tier 2: Gewartete Dependencies → Upstream-Issue/PR öffnen
- Tier 3: Abandoned Dependencies → Fork oder Replace
Ein häufiger Fehler, den ich in Audits sehe: Alle Usages sofort migrieren wollen. Das ist ineffizient. Tier-2-Dependencies sollten Upstream gefixt werden, nicht geforkt.
Phase 3: Migration (2-5 Tage je nach Umfang)
Für eigenen Code:
- Pattern zu PCRE konvertieren (Delimiters, u-Modifier)
- Unit-Tests anpassen (Assertions könnten sich ändern)
- Lokales Testing mit PHP 8.6-dev (Deprecation-Warnings prüfen)
Für Dependencies:
- GitHub-Issue öffnen: "PHP 8.6 deprecates mbregex, migration to preg_* needed"
- PR mit Migration öffnen (wenn maintainer responsive)
- Wenn abandoned: Fork oder Replace mit modernem Package
Phase 4: Testing (1-2 Tage)
Kritisch: Regex-Bugs sind subtil. Ein fehlendes u bricht nicht sofort, aber korruptiert Unicode-Data.
Testing-Strategie:
- Unit-Tests für alle migrierten Functions
- Integration-Tests mit realen Daten (UTF-8, Umlaute, Emojis, CJK-Zeichen)
- PHP 8.6-dev-Environment (Deprecation-Warnings müssen weg)
- Staging-Deployment mit Production-Traffic-Replay
Die Integration in bestehende Symfony-Projekte erfordert Koordination mit Dependency-Updates und PHP-Version-Upgrades. Lassen Sie uns das gemeinsam planen.
Edge Cases: Nicht-UTF-8-Encodings und Performance
Was wenn Sie wirklich Shift_JIS/EUC-JP/Big5 brauchen?
PCRE unterstützt nur UTF-8, UTF-16, UTF-32. Für andere Encodings:
Option 1: Convert zu UTF-8 vor Regex:
$utf8 = mb_convert_encoding($input, 'UTF-8', 'Shift_JIS');
preg_match('/pattern/u', $utf8, $matches);
$output = mb_convert_encoding($result, 'Shift_JIS', 'UTF-8');Option 2: UConverter (Intl-Extension):
$utf8 = UConverter::transcode($input, 'UTF-8', 'Shift_JIS');
preg_match('/pattern/u', $utf8, $matches);Option 3: ICU Regex (für sehr spezifische Cases):
ICU hat eine eigene Regex-Engine mit nativer UTF-16-Support. Für PHP gibt es keine direkte Binding, aber theoretisch via FFI möglich.
Reality-Check: 99% der modernen PHP-Projekte nutzen UTF-8. Non-UTF-8 ist Legacy. Upgrade zu UTF-8 ist langfristig sinnvoller als Workarounds.
Performance-Impact: PCRE vs. Oniguruma
Benchmark (100.000 Iterations, Pattern: [A-Za-z0-9]+, Input: 500 Chars):
- mb_ereg: ~1.240ms
- preg_match (ohne u): ~0.850ms (31% schneller)
- preg_match (mit u): ~1.120ms (10% schneller)
Fazit: PCRE ist gleich schnell oder schneller. Kein Performance-Grund für mb_ereg.
Zusammenfassung: Drei Key Takeaways
1. PHP 8.6 deprecated mbregex, PHP 9.0 entfernt es: Oniguruma-Maintenance endete April 2025. PHP Internals entschied für vollständige PCRE-Migration. Voting-Phase läuft, Acceptance ist sicher.
2. Migration ist nicht trivial: Delimiter-Syntax, u-Modifier, Backreference-Syntax unterscheiden sich. Fehlender u-Modifier korruptiert Unicode-Data subtil. Stateful-Search-API braucht Refactoring.
3. Systematisches Vorgehen: Discovery (grep/PHPStan) → Kategorisierung (eigener Code vs. Dependencies) → Migration (mit Testing) → Deployment. Nicht alle Usages sofort fixen, sondern Tier-basiert priorisieren.
Professionelle Beratung: mbregex-Migration für Ihr Projekt
Die Migration von mbregex zu PCRE erfordert systematische Code-Analyse, Regex-Expertise und umfassendes Testing. Welche Dependencies sind betroffen? Gibt es Non-UTF-8-Encodings in Ihrer Codebase? Wie plant man die Migration ohne Production-Downtime?
Als Symfony-Entwickler mit Fokus auf API-Architekturen und Backend-Lösungen für kleine Agenturen und KMU unterstütze ich Sie bei der mbregex-Migration: Welche Code-Stellen sind kritisch? Welche Dependencies müssen upstream gefixt werden? Wie testet man Regex-Migrations sicher?
Lassen Sie uns in einem Erstgespräch Ihre Codebase analysieren. Ich zeige Ihnen konkret, welche mbregex-Usages Sie haben und wie Sie die Migration strategisch angehen.
Jetzt Migrations-Analyse anfragen
Über den Autor: Dennis Schwenker-Sanders ist PHP & Symfony-Entwickler mit Fokus auf API-Architekturen, Legacy-Code-Modernisierung und PHP-Version-Upgrades. Er unterstützt kleine Agenturen und KMU-Technikteams bei der Migration zu aktuellen PHP-Versionen. Mehr über Dennis