respect-validation/library/Rules/Phone.php
Henrique Moody ae369c4791
Improve Phone validation
This commit will improve the Phone rule in the following ways:

* Upgrade its validation engine;

* Increase the number of tests;

* Do not validate phone numbers from other regions.

The last item is a possible bug with "libphonenumber-for-php", which I
have already reported.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-03-25 08:48:31 +01:00

98 lines
3.1 KiB
PHP

<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use libphonenumber\NumberParseException;
use libphonenumber\PhoneNumberUtil;
use Respect\Validation\Exceptions\InvalidRuleConstructorException;
use Respect\Validation\Exceptions\MissingComposerDependencyException;
use Respect\Validation\Message\Template;
use Respect\Validation\Result;
use Respect\Validation\Rules\Core\Standard;
use Sokil\IsoCodes\Database\Countries;
use function class_exists;
use function is_scalar;
#[Template(
'{{name}} must be a valid telephone number',
'{{name}} must not be a valid telephone number',
self::TEMPLATE_INTERNATIONAL,
)]
#[Template(
'{{name}} must be a valid telephone number for country {{countryName|trans}}',
'{{name}} must not be a valid telephone number for country {{countryName|trans}}',
self::TEMPLATE_FOR_COUNTRY,
)]
final class Phone extends Standard
{
public const TEMPLATE_FOR_COUNTRY = '__for_country__';
public const TEMPLATE_INTERNATIONAL = '__international__';
private readonly ?Countries\Country $country;
public function __construct(?string $countryCode = null, ?Countries $countries = null)
{
if (!class_exists(PhoneNumberUtil::class)) {
throw new MissingComposerDependencyException(
'Phone rule libphonenumber for PHP',
'giggsey/libphonenumber-for-php'
);
}
if ($countryCode == null) {
$this->country = null;
return;
}
if (!class_exists(Countries::class)) {
throw new MissingComposerDependencyException(
'Phone rule with country code requires PHP ISO Codes',
'sokil/php-isocodes',
'sokil/php-isocodes-db-only'
);
}
$countries ??= new Countries();
$this->country = $countries->getByAlpha2($countryCode);
if ($this->country === null) {
throw new InvalidRuleConstructorException('Invalid country code %s', $countryCode);
}
}
public function evaluate(mixed $input): Result
{
$parameters = ['countryName' => $this->country?->getName()];
$template = $this->country === null ? self::TEMPLATE_INTERNATIONAL : self::TEMPLATE_FOR_COUNTRY;
if (!is_scalar($input)) {
return Result::failed($input, $this, $parameters, $template);
}
return new Result($this->isValidPhone((string) $input), $input, $this, $parameters, $template);
}
private function isValidPhone(string $input): bool
{
try {
$phoneNumberUtil = PhoneNumberUtil::getInstance();
$phoneNumberObject = $phoneNumberUtil->parse($input, $this->country?->getAlpha2());
if ($this->country === null) {
return $phoneNumberUtil->isValidNumber($phoneNumberObject);
}
return $phoneNumberUtil->getRegionCodeForNumber($phoneNumberObject) === $this->country->getAlpha2();
} catch (NumberParseException) {
}
return false;
}
}