feat: E.164 phone validation and storage with libphonenumber

- Add giggsey/libphonenumber-for-php, PhoneNumberNormalizer, ValidPhoneNumber rule

- Store subscribers as E.164; mutator normalizes on save; optional phone required from form block

- Migration to normalize legacy subscriber phones; Mailwizz/search/UI/tests updated

- Add run-deploy-from-local.sh and PREREGISTER_DEFAULT_PHONE_REGION in .env.example

Made-with: Cursor
This commit is contained in:
2026-04-04 14:25:52 +02:00
parent 5a67827c23
commit 17e784fee7
21 changed files with 476 additions and 18 deletions

View File

@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace App\Services;
use libphonenumber\NumberParseException;
use libphonenumber\PhoneNumberFormat;
use libphonenumber\PhoneNumberUtil;
/**
* Parses international and national phone input and normalizes to E.164 for storage (includes leading +).
*/
final class PhoneNumberNormalizer
{
public function __construct(
private readonly string $defaultRegion
) {}
/**
* Returns E.164 (e.g. +31612345678) or null if empty/invalid.
*/
public function normalizeToE164(?string $input): ?string
{
if ($input === null) {
return null;
}
$trimmed = trim($input);
if ($trimmed === '') {
return null;
}
$util = PhoneNumberUtil::getInstance();
try {
$number = $util->parse($trimmed, $this->defaultRegion);
} catch (NumberParseException) {
return null;
}
if (! $util->isValidNumber($number)) {
return null;
}
return $util->format($number, PhoneNumberFormat::E164);
}
}