Skip to content

Commit b603ef7

Browse files
committed
Containerize PhoneNumberUtil instance for Phone rule
Similar to d8e31db (commit that containerized iso code dbs), but this time for PhoneNumberUtil. This makes the optional dependency testable. PhoneNumberUtil doesn't have a public constructor, so a factory was declared instead.
1 parent d8e31db commit b603ef7

3 files changed

Lines changed: 41 additions & 6 deletions

File tree

src/ContainerRegistry.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
namespace Respect\Validation;
1212

1313
use DI\Container;
14+
use libphonenumber\PhoneNumberUtil;
1415
use Psr\Container\ContainerInterface;
1516
use Respect\StringFormatter\BypassTranslator;
1617
use Respect\StringFormatter\Modifier;
@@ -52,6 +53,7 @@ final class ContainerRegistry
5253
public static function createContainer(array $definitions = []): Container
5354
{
5455
return new Container($definitions + [
56+
PhoneNumberUtil::class => factory(static fn() => PhoneNumberUtil::getInstance()),
5557
Transformer::class => create(Prefix::class),
5658
TemplateResolver::class => create(TemplateResolver::class),
5759
TranslatorInterface::class => autowire(BypassTranslator::class),

src/Validators/Phone.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
use Respect\Validation\Validator;
3131
use Sokil\IsoCodes\Database\Countries;
3232

33-
use function class_exists;
3433
use function is_scalar;
3534

3635
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]
@@ -53,9 +52,9 @@ final class Phone implements Validator
5352

5453
public function __construct(string|null $countryCode = null, Countries|null $countries = null)
5554
{
56-
if (!class_exists(PhoneNumberUtil::class)) {
55+
if (!ContainerRegistry::getContainer()->has(PhoneNumberUtil::class)) {
5756
throw new MissingComposerDependencyException(
58-
'Phone rule libphonenumber for PHP',
57+
'Phone rule requires libphonenumber for PHP',
5958
'giggsey/libphonenumber-for-php',
6059
);
6160
}
@@ -96,7 +95,7 @@ public function evaluate(mixed $input): Result
9695
private function isValidPhone(string $input): bool
9796
{
9897
try {
99-
$phoneNumberUtil = PhoneNumberUtil::getInstance();
98+
$phoneNumberUtil = ContainerRegistry::getContainer()->get(PhoneNumberUtil::class);
10099
$phoneNumberObject = $phoneNumberUtil->parse($input, $this->country?->getAlpha2());
101100
if ($this->country === null) {
102101
return $phoneNumberUtil->isValidNumber($phoneNumberObject);

tests/unit/Validators/PhoneTest.php

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
namespace Respect\Validation\Validators;
1919

2020
use DI;
21+
use libphonenumber\PhoneNumberUtil;
2122
use PHPUnit\Framework\Attributes\CoversClass;
2223
use PHPUnit\Framework\Attributes\DataProvider;
2324
use PHPUnit\Framework\Attributes\Group;
@@ -26,6 +27,7 @@
2627
use Respect\Validation\Exceptions\InvalidValidatorException;
2728
use Respect\Validation\Exceptions\MissingComposerDependencyException;
2829
use Respect\Validation\Test\TestCase;
30+
use Sokil\IsoCodes\Database\Countries;
2931
use stdClass;
3032

3133
#[Group('validator')]
@@ -70,10 +72,17 @@ public function itShouldThrowsExceptionWhenCountryCodeIsNotValid(): void
7072
}
7173

7274
#[Test]
73-
public function shouldThrowWhenMissingComponent(): void
75+
public function shouldThrowWhenMissingIsocodesComponent(): void
7476
{
7577
$mainContainer = ContainerRegistry::getContainer();
76-
ContainerRegistry::setContainer((new DI\ContainerBuilder())->useAutowiring(false)->build());
78+
ContainerRegistry::setContainer(
79+
(new DI\ContainerBuilder())
80+
->addDefinitions([
81+
PhoneNumberUtil::class => DI\factory(static fn() => PhoneNumberUtil::getInstance()),
82+
])
83+
->useAutowiring(false)
84+
->build(),
85+
);
7786
try {
7887
new Phone('US');
7988
$this->fail('Expected MissingComposerDependencyException was not thrown.');
@@ -87,6 +96,31 @@ public function shouldThrowWhenMissingComponent(): void
8796
}
8897
}
8998

99+
#[Test]
100+
public function shouldThrowWhenMissingPhonesComponent(): void
101+
{
102+
$mainContainer = ContainerRegistry::getContainer();
103+
ContainerRegistry::setContainer(
104+
(new DI\ContainerBuilder())
105+
->addDefinitions([
106+
Countries::class => DI\create(Countries::class),
107+
])
108+
->useAutowiring(false)
109+
->build(),
110+
);
111+
try {
112+
new Phone('US');
113+
$this->fail('Expected MissingComposerDependencyException was not thrown.');
114+
} catch (MissingComposerDependencyException $e) {
115+
$this->assertStringContainsString(
116+
'Phone rule requires libphonenumber for PHP.',
117+
$e->getMessage(),
118+
);
119+
} finally {
120+
ContainerRegistry::setContainer($mainContainer);
121+
}
122+
}
123+
90124
/** @return array<array{mixed}> */
91125
public static function providerForValidInputWithoutCountryCode(): array
92126
{

0 commit comments

Comments
 (0)