#phonenumber

Советы с тегом "phonenumber"

1 совет

Парсер телефонов и страна по умолчанию в #[Phone]

Парсер телефонов и страна по умолчанию в #[Phone]

Проблема/контекст

Атрибут #[Phone] из Bitrix\Main\Validation\Rule выглядит как проверка формата строки. На деле он собирает единственный валидатор PhoneValidator, а тот вызывает Parser::getInstance()->parse($value)->isValid() — без второго аргумента. Внутри parse() пустая страна заменяется на Parser::getDefaultCountry(), поэтому один и тот же номер без + на двух порталах даст разный результат валидации.

Решение с кодом

Порядок источников в Parser::getDefaultCountry():

  1. Option::get('main', 'phone_number_default_country') — в опции хранится ID страны, а не двухбуквенный код. Значение задаётся в настройках главного модуля полем «Страна по умолчанию».
  2. Если опция пуста, вызывается detectCountry(): сначала REG_COUNTRY модуля bitrix24, затем портальная зона из списка br, cn, de, in, ru, ua, by, kz, fr, pl, uz, затем Context::getCurrent()->getLanguage() из списка br, cn, de, in, ru, ua, by, kz, fr, pl, и только в конце GeoIp\Manager::getCountryCode().
  3. Найденный код конвертируется в ID и записывается в опцию — первый же вызов фиксирует страну на всё время жизни портала.
  4. Наружу возвращается GetCountryCodeById($id), при неудаче — пустая строка.

Пустая строка означает, что MetadataProvider::getCountryMetadata('') вернёт false, и parse() отдаст невалидный PhoneNumber для любого номера без +. Номер с + разбирается по коду страны из самой строки, и $defaultCountry игнорируется — это единственный формат, устойчивый к настройкам портала.

В коде модуля страну задавайте явно вторым аргументом:

        <?php declare(strict_types=1);

use Bitrix\Main\PhoneNumber\Format;
use Bitrix\Main\PhoneNumber\Parser;

$number = Parser::getInstance()->parse('8 (916) 123-45-67', 'RU');

$number->isValid();            // true
$number->getCountry();         // RU
$number->getCountryCode();     // 7
$number->format(Format::E164); // +79161234567

    

Сигнатура — PhoneNumber::format($formatType = '', $forceNationalPrefix = false). Константы класса Bitrix\Main\PhoneNumber\Format: E164 (значение 'E.164'), INTERNATIONAL, NATIONAL. При пустом $formatType вызывается Formatter::formatOriginal() — он сохраняет исходное написание и откатывается к сырой строке, если форматирование изменило состав цифр. Для невалидного номера format() всегда возвращает getRawNumber(), поэтому isValid() проверяем до форматирования, а не после.

В базе храните Format::E164 — это '+' . countryCode . nationalNumber без разделителей, единственный вид, пригодный для поиска дублей и сравнения:

        <?php declare(strict_types=1);

namespace Vendor\Module\Application\Service;

use Bitrix\Main\PhoneNumber\Format;
use Bitrix\Main\PhoneNumber\Parser;

final class PhoneNormalizer
{
    public function __construct(private readonly string $defaultCountry = 'RU') {}

    public function normalize(string $raw): ?string
    {
        $number = Parser::getInstance()->parse($raw, $this->defaultCountry);

        return $number->isValid() ? $number->format(Format::E164) : null;
    }
}

    

Если валидация должна быть привязана к стране, напишите свой валидатор — интерфейс состоит из одного метода:

        <?php declare(strict_types=1);

namespace Vendor\Module\Validation\Validator;

use Bitrix\Main\Localization\LocalizableMessage;
use Bitrix\Main\PhoneNumber\Parser;
use Bitrix\Main\Validation\ValidationError;
use Bitrix\Main\Validation\ValidationResult;
use Bitrix\Main\Validation\Validator\ValidatorInterface;

final class CountryPhoneValidator implements ValidatorInterface
{
    public function __construct(private readonly string $country) {}

    public function validate(mixed $value): ValidationResult
    {
        $result = new ValidationResult();

        if (!is_string($value) || !Parser::getInstance()->parse($value, $this->country)->isValid())
        {
            $result->addError(new ValidationError(
                new LocalizableMessage('MAIN_VALIDATION_PHONE_INVALID'),
                failedValidator: $this,
            ));
        }

        return $result;
    }
}

    

Подключается он через собственный атрибут, унаследованный от AbstractPropertyValidationAttribute, с возвратом валидатора из getValidators().

Итог

#[Phone] проверяет номер относительно страны портала, а не относительно вашей предметной области, и эта страна может прийти из зоны Битрикс24, языка или GeoIP. Задавайте страну явно в Parser::parse() и храните результат в Format::E164.

Мы используем файлы cookie для улучшения работы сайта. Продолжая использовать сайт, вы соглашаетесь с нашей политикой конфиденциальности.
AI Домовой

AI Домовой История

на связи

пишет…
Нет истории чатов
AI Домовой

Нужна авторизация

Войдите, чтобы задавать вопросы AI Домовому.

Войти