symfony/polyfill-intl-normalizer
Provides a fallback implementation of PHP’s Intl Normalizer class for environments without the intl extension. Part of Symfony’s polyfill suite, enabling Unicode normalization support across platforms with consistent behavior.
Installation: Add the package via Composer in your Laravel project:
composer require symfony/polyfill-intl-normalizer
No additional configuration is required—it auto-loads when the intl extension is missing.
First Use Case:
Normalize Unicode strings in a Laravel application where the intl extension is unavailable. For example, in a controller or service:
use Symfony\Component\Polyfill\Intl\Normalizer;
$text = "Café";
$normalized = Normalizer::normalize($text, Normalizer::FORM_C);
// Outputs: "Cafe\u00E9" (composed form)
Where to Look First:
Normalizer.php in the package for implementation details.Str helper for built-in Unicode handling (e.g., Str::ascii()), which may already use this polyfill internally.Basic Normalization: Use the package for standard Unicode normalization forms (NFC, NFD, etc.) in Laravel applications:
// Normalize to composed form (NFC)
$normalized = Normalizer::normalize($string, Normalizer::FORM_C);
// Normalize to decomposed form (NFD)
$decomposed = Normalizer::normalize($string, Normalizer::FORM_D);
Integration with Laravel Helpers:
Leverage Laravel’s Str helper, which may internally use this polyfill for Unicode handling:
use Illuminate\Support\Str;
$slug = Str::slug("Café", '-', Normalizer::FORM_C);
// Outputs: "cafe" (normalized before slug generation)
Custom Text Processing:
Use normalizer_get_raw_decomposition() for advanced use cases like:
$decomposed = Normalizer::normalize("Café", Normalizer::FORM_D);
$rawDecomposition = Normalizer::getRawDecomposition($decomposed);
Service Layer Integration: Create a dedicated service for Unicode normalization to centralize logic:
namespace App\Services;
use Symfony\Component\Polyfill\Intl\Normalizer;
class UnicodeNormalizer
{
public function normalizeToNFC(string $text): string
{
return Normalizer::normalize($text, Normalizer::FORM_C);
}
public function getDecomposition(string $text): array
{
return Normalizer::getRawDecomposition($text);
}
}
Register the service in Laravel’s service container:
$this->app->singleton(UnicodeNormalizer::class, function ($app) {
return new UnicodeNormalizer();
});
Validation Rules: Use the polyfill to enforce Unicode normalization in Laravel validation rules:
use Illuminate\Validation\Rule;
$validator = Validator::make($request->all(), [
'username' => [
'string',
Rule::custom(function ($attribute, $value) {
return Normalizer::normalize($value, Normalizer::FORM_C) === $value;
})->message('Username must use composed Unicode characters.'),
],
]);
Legacy System Integration:
$legacyData = "Caf\u00E9"; // Inconsistent encoding
$cleanData = Normalizer::normalize($legacyData, Normalizer::FORM_C);
Multilingual Applications:
$userInput = "Naïve";
$normalizedInput = Normalizer::normalize($userInput, Normalizer::FORM_C);
User::create(['name' => $normalizedInput]);
Search and Indexing:
$searchableText = Normalizer::normalize($post->title, Normalizer::FORM_C);
$post->searchableData = ['title' => $searchableText];
Debugging and Auditing:
normalizer_get_raw_decomposition() to inspect Unicode strings for debugging or compliance:$text = "Café";
$decomposed = Normalizer::normalize($text, Normalizer::FORM_D);
$rawDecomposition = Normalizer::getRawDecomposition($decomposed);
Log::debug("Unicode decomposition:", ['text' => $text, 'decomposition' => $rawDecomposition]);
Leverage Laravel’s Str Helper:
The Str helper may already use this polyfill internally. Check its source code for existing Unicode handling.
Cache Normalized Results: For performance-critical applications, cache normalized strings to avoid repeated processing:
$normalized = Cache::remember("normalized_{$string}", now()->addHours(1), function () use ($string) {
return Normalizer::normalize($string, Normalizer::FORM_C);
});
Fallback to Native intl:
If performance is critical and the intl extension is available, bypass the polyfill:
if (extension_loaded('intl')) {
$normalized = Normalizer::normalize($string, Normalizer::FORM_C);
} else {
// Use polyfill logic
}
Testing: Write tests to ensure Unicode normalization behaves as expected across environments:
use Symfony\Component\Polyfill\Intl\Normalizer;
public function testUnicodeNormalization()
{
$this->assertEquals("Cafe\u00E9", Normalizer::normalize("Café", Normalizer::FORM_C));
$this->assertEquals("Cafe\u0301", Normalizer::normalize("Café", Normalizer::FORM_D));
}
CI/CD Pipeline:
Ensure your CI pipeline tests the polyfill by simulating environments without the intl extension. Use Docker or PHP’s disable_functions to test fallback behavior.
Performance Overhead:
intl extension (5–20x for decomposition operations). Avoid using normalizer_get_raw_decomposition() in high-frequency loops without caching.intl extension in production.mbstring Dependency:
The polyfill requires the mbstring extension. If it’s disabled, the package will fail silently or throw errors.
if (!extension_loaded('mbstring')) {
throw new RuntimeException('The mbstring extension is required for Unicode polyfills.');
}
Edge Cases in Unicode Handling:
$edgeCases = [
'🇺🇸', // Emoji sequence
'Ź', // Combining character
'😊', // Single emoji
'A\u030A', // Decomposed character
];
foreach ($edgeCases as $text) {
$decomposed = Normalizer::getRawDecomposition($text);
// Validate expected behavior
}
Inconsistent Behavior Across PHP Versions: The polyfill may behave differently across PHP versions, especially for newer Unicode standards.
composer.json to avoid unexpected changes:
"symfony/polyfill-intl-normalizer": "^1.38.0"
Laravel-Specific Quirks:
Str helper or other components rely on the intl extension internally, the polyfill might not fully replicate their behavior.Str::slug(), Str::ascii()) to ensure consistency.intl extension:
if (!extension_loaded('intl')) {
$reflection = new ReflectionClass(\Symfony\Component\Polyfill\Intl\Normalizer::class);
Log::info('Using polyfill for Normal
How can I help you explore Laravel packages today?