diff --git a/src/Validators/CountryCode.php b/src/Validators/CountryCode.php index e4a1fd10d..dd9dacbc6 100644 --- a/src/Validators/CountryCode.php +++ b/src/Validators/CountryCode.php @@ -18,6 +18,8 @@ namespace Respect\Validation\Validators; use Attribute; +use Psr\Container\NotFoundExceptionInterface; +use Respect\Validation\ContainerRegistry; use Respect\Validation\Exceptions\InvalidValidatorException; use Respect\Validation\Exceptions\MissingComposerDependencyException; use Respect\Validation\Message\Template; @@ -25,7 +27,6 @@ use Respect\Validation\Validator; use Sokil\IsoCodes\Database\Countries; -use function class_exists; use function in_array; use function is_string; @@ -43,14 +44,6 @@ public function __construct( private string $set = 'alpha-2', Countries|null $countries = null, ) { - if (!class_exists(Countries::class)) { - throw new MissingComposerDependencyException( - 'SubdivisionCode rule requires PHP ISO Codes', - 'sokil/php-isocodes', - 'sokil/php-isocodes-db-only', - ); - } - $availableOptions = ['alpha-2', 'alpha-3', 'numeric']; if (!in_array($set, $availableOptions, true)) { throw new InvalidValidatorException( @@ -60,7 +53,15 @@ public function __construct( ); } - $this->countries = $countries ?? new Countries(); + try { + $this->countries = $countries ?? ContainerRegistry::getContainer()->get(Countries::class); + } catch (NotFoundExceptionInterface) { + throw new MissingComposerDependencyException( + 'CountryCode rule requires PHP ISO Codes', + 'sokil/php-isocodes', + 'sokil/php-isocodes-db-only', + ); + } } public function evaluate(mixed $input): Result diff --git a/src/Validators/CurrencyCode.php b/src/Validators/CurrencyCode.php index 53ad1d5c7..52e6b3ce2 100644 --- a/src/Validators/CurrencyCode.php +++ b/src/Validators/CurrencyCode.php @@ -15,6 +15,8 @@ namespace Respect\Validation\Validators; use Attribute; +use Psr\Container\NotFoundExceptionInterface; +use Respect\Validation\ContainerRegistry; use Respect\Validation\Exceptions\InvalidValidatorException; use Respect\Validation\Exceptions\MissingComposerDependencyException; use Respect\Validation\Message\Template; @@ -22,7 +24,6 @@ use Respect\Validation\Validator; use Sokil\IsoCodes\Database\Currencies; -use function class_exists; use function in_array; #[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)] @@ -39,14 +40,6 @@ public function __construct( private string $set = 'alpha-3', Currencies|null $currencies = null, ) { - if (!class_exists(Currencies::class)) { - throw new MissingComposerDependencyException( - 'CurrencyCode rule requires PHP ISO Codes', - 'sokil/php-isocodes', - 'sokil/php-isocodes-db-only', - ); - } - $availableSets = ['alpha-3', 'numeric']; if (!in_array($set, $availableSets, true)) { throw new InvalidValidatorException( @@ -56,7 +49,15 @@ public function __construct( ); } - $this->currencies = $currencies ?? new Currencies(); + try { + $this->currencies = $currencies ?? ContainerRegistry::getContainer()->get(Currencies::class); + } catch (NotFoundExceptionInterface) { + throw new MissingComposerDependencyException( + 'CurrencyCode rule requires PHP ISO Codes', + 'sokil/php-isocodes', + 'sokil/php-isocodes-db-only', + ); + } } public function evaluate(mixed $input): Result diff --git a/src/Validators/LanguageCode.php b/src/Validators/LanguageCode.php index fe7d28a47..fdaf753f6 100644 --- a/src/Validators/LanguageCode.php +++ b/src/Validators/LanguageCode.php @@ -15,15 +15,15 @@ namespace Respect\Validation\Validators; use Attribute; +use Psr\Container\NotFoundExceptionInterface; +use Respect\Validation\ContainerRegistry; use Respect\Validation\Exceptions\InvalidValidatorException; use Respect\Validation\Exceptions\MissingComposerDependencyException; use Respect\Validation\Message\Template; use Respect\Validation\Result; use Respect\Validation\Validator; -use Sokil\IsoCodes\Database\Countries; use Sokil\IsoCodes\Database\Languages; -use function class_exists; use function in_array; use function is_string; @@ -41,14 +41,6 @@ public function __construct( private readonly string $set = 'alpha-2', Languages|null $languages = null, ) { - if (!class_exists(Countries::class)) { - throw new MissingComposerDependencyException( - 'LanguageCode rule requires PHP ISO Codes', - 'sokil/php-isocodes', - 'sokil/php-isocodes-db-only', - ); - } - $availableSets = ['alpha-2', 'alpha-3']; if (!in_array($set, $availableSets, true)) { throw new InvalidValidatorException( @@ -58,7 +50,15 @@ public function __construct( ); } - $this->languages = $languages ?? new Languages(); + try { + $this->languages = $languages ?? ContainerRegistry::getContainer()->get(Languages::class); + } catch (NotFoundExceptionInterface) { + throw new MissingComposerDependencyException( + 'LanguageCode rule requires PHP ISO Codes', + 'sokil/php-isocodes', + 'sokil/php-isocodes-db-only', + ); + } } public function evaluate(mixed $input): Result diff --git a/src/Validators/Phone.php b/src/Validators/Phone.php index f7289711e..203031487 100644 --- a/src/Validators/Phone.php +++ b/src/Validators/Phone.php @@ -21,6 +21,8 @@ use Attribute; use libphonenumber\NumberParseException; use libphonenumber\PhoneNumberUtil; +use Psr\Container\NotFoundExceptionInterface; +use Respect\Validation\ContainerRegistry; use Respect\Validation\Exceptions\InvalidValidatorException; use Respect\Validation\Exceptions\MissingComposerDependencyException; use Respect\Validation\Message\Template; @@ -64,7 +66,9 @@ public function __construct(string|null $countryCode = null, Countries|null $cou return; } - if (!class_exists(Countries::class)) { + try { + $countries ??= ContainerRegistry::getContainer()->get(Countries::class); + } catch (NotFoundExceptionInterface) { throw new MissingComposerDependencyException( 'Phone rule with country code requires PHP ISO Codes', 'sokil/php-isocodes', @@ -72,7 +76,6 @@ public function __construct(string|null $countryCode = null, Countries|null $cou ); } - $countries ??= new Countries(); $this->country = $countries->getByAlpha2($countryCode); if ($this->country === null) { throw new InvalidValidatorException('Invalid country code %s', $countryCode); diff --git a/src/Validators/SubdivisionCode.php b/src/Validators/SubdivisionCode.php index 36d073715..da3c721f1 100644 --- a/src/Validators/SubdivisionCode.php +++ b/src/Validators/SubdivisionCode.php @@ -12,6 +12,8 @@ namespace Respect\Validation\Validators; use Attribute; +use Psr\Container\NotFoundExceptionInterface; +use Respect\Validation\ContainerRegistry; use Respect\Validation\Exceptions\InvalidValidatorException; use Respect\Validation\Exceptions\MissingComposerDependencyException; use Respect\Validation\Helpers\CanValidateUndefined; @@ -21,8 +23,6 @@ use Sokil\IsoCodes\Database\Countries; use Sokil\IsoCodes\Database\Subdivisions; -use function class_exists; - #[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)] #[Template( '{{subject}} must be a subdivision code of {{countryName|trans}}', @@ -41,7 +41,11 @@ public function __construct( Countries|null $countries = null, Subdivisions|null $subdivisions = null, ) { - if (!class_exists(Countries::class) || !class_exists(Subdivisions::class)) { + try { + $container = ContainerRegistry::getContainer(); + $countries ??= $container->get(Countries::class); + $this->subdivisions = $subdivisions ?? $container->get(Subdivisions::class); + } catch (NotFoundExceptionInterface) { throw new MissingComposerDependencyException( 'SubdivisionCode rule requires PHP ISO Codes', 'sokil/php-isocodes', @@ -49,14 +53,12 @@ public function __construct( ); } - $countries ??= new Countries(); $country = $countries->getByAlpha2($countryCode); if ($country === null) { throw new InvalidValidatorException('"%s" is not a supported country code', $countryCode); } $this->country = $country; - $this->subdivisions = $subdivisions ?? new Subdivisions(); } public function evaluate(mixed $input): Result diff --git a/tests/benchmark/IsoCodesBench.php b/tests/benchmark/IsoCodesBench.php new file mode 100644 index 000000000..f01a9ee20 --- /dev/null +++ b/tests/benchmark/IsoCodesBench.php @@ -0,0 +1,69 @@ +evaluate('CA'); + } + + #[Bench\Iterations(10)] + #[Bench\RetryThreshold(5)] + #[Bench\Revs(5)] + #[Bench\Warmup(1)] + #[Bench\Subject] + public function countryCode(): void + { + ValidatorBuilder::countryCode()->evaluate('US'); + } + + #[Bench\Iterations(10)] + #[Bench\RetryThreshold(5)] + #[Bench\Revs(5)] + #[Bench\Warmup(1)] + #[Bench\Subject] + public function currencyCode(): void + { + ValidatorBuilder::currencyCode()->evaluate('USD'); + } + + #[Bench\Iterations(10)] + #[Bench\RetryThreshold(5)] + #[Bench\Revs(5)] + #[Bench\Warmup(1)] + #[Bench\Subject] + public function languageCode(): void + { + ValidatorBuilder::languageCode()->evaluate('en'); + } + + #[Bench\Iterations(10)] + #[Bench\RetryThreshold(5)] + #[Bench\Revs(5)] + #[Bench\Warmup(1)] + #[Bench\Subject] + public function phone(): void + { + ValidatorBuilder::phone('US')->evaluate('+1 202-555-0125'); + } +} diff --git a/tests/unit/Validators/CountryCodeTest.php b/tests/unit/Validators/CountryCodeTest.php index 47037a5ad..ea3c6bcb7 100644 --- a/tests/unit/Validators/CountryCodeTest.php +++ b/tests/unit/Validators/CountryCodeTest.php @@ -14,10 +14,13 @@ namespace Respect\Validation\Validators; +use DI; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\Test; +use Respect\Validation\ContainerRegistry; use Respect\Validation\Exceptions\InvalidValidatorException; +use Respect\Validation\Exceptions\MissingComposerDependencyException; use Respect\Validation\Test\RuleTestCase; #[Group('validator')] @@ -36,6 +39,24 @@ public function itShouldThrowsExceptionWhenInvalidFormat(): void new CountryCode('whatever'); } + #[Test] + public function shouldThrowWhenMissingComponent(): void + { + $mainContainer = ContainerRegistry::getContainer(); + ContainerRegistry::setContainer((new DI\ContainerBuilder())->useAutowiring(false)->build()); + try { + new CountryCode('alpha-3'); + $this->fail('Expected MissingComposerDependencyException was not thrown.'); + } catch (MissingComposerDependencyException $e) { + $this->assertStringContainsString( + 'CountryCode rule requires PHP ISO Codes', + $e->getMessage(), + ); + } finally { + ContainerRegistry::setContainer($mainContainer); + } + } + /** @return iterable */ public static function providerForValidInput(): iterable { diff --git a/tests/unit/Validators/CurrencyCodeTest.php b/tests/unit/Validators/CurrencyCodeTest.php index a026d5bca..f43cfff83 100644 --- a/tests/unit/Validators/CurrencyCodeTest.php +++ b/tests/unit/Validators/CurrencyCodeTest.php @@ -14,10 +14,13 @@ namespace Respect\Validation\Validators; +use DI; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\Test; +use Respect\Validation\ContainerRegistry; use Respect\Validation\Exceptions\InvalidValidatorException; +use Respect\Validation\Exceptions\MissingComposerDependencyException; use Respect\Validation\Test\RuleTestCase; #[Group('validator')] @@ -36,6 +39,24 @@ public function itShouldThrowsExceptionWhenInvalidFormat(): void new CurrencyCode('whatever'); } + #[Test] + public function shouldThrowWhenMissingComponent(): void + { + $mainContainer = ContainerRegistry::getContainer(); + ContainerRegistry::setContainer((new DI\ContainerBuilder())->useAutowiring(false)->build()); + try { + new CurrencyCode('alpha-3'); + $this->fail('Expected MissingComposerDependencyException was not thrown.'); + } catch (MissingComposerDependencyException $e) { + $this->assertStringContainsString( + 'CurrencyCode rule requires PHP ISO Codes', + $e->getMessage(), + ); + } finally { + ContainerRegistry::setContainer($mainContainer); + } + } + /** @return iterable */ public static function providerForValidInput(): iterable { diff --git a/tests/unit/Validators/LanguageCodeTest.php b/tests/unit/Validators/LanguageCodeTest.php index af46e5fcc..bb6aa63a1 100644 --- a/tests/unit/Validators/LanguageCodeTest.php +++ b/tests/unit/Validators/LanguageCodeTest.php @@ -13,10 +13,13 @@ namespace Respect\Validation\Validators; +use DI; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\Test; +use Respect\Validation\ContainerRegistry; use Respect\Validation\Exceptions\InvalidValidatorException; +use Respect\Validation\Exceptions\MissingComposerDependencyException; use Respect\Validation\Test\RuleTestCase; #[Group('validator')] @@ -35,6 +38,24 @@ public function itShouldThrowAnExceptionWhenSetIsInvalid(): void new LanguageCode('whatever'); } + #[Test] + public function shouldThrowWhenMissingComponent(): void + { + $mainContainer = ContainerRegistry::getContainer(); + ContainerRegistry::setContainer((new DI\ContainerBuilder())->useAutowiring(false)->build()); + try { + new LanguageCode('alpha-3'); + $this->fail('Expected MissingComposerDependencyException was not thrown.'); + } catch (MissingComposerDependencyException $e) { + $this->assertStringContainsString( + 'LanguageCode rule requires PHP ISO Codes', + $e->getMessage(), + ); + } finally { + ContainerRegistry::setContainer($mainContainer); + } + } + /** @return iterable */ public static function providerForValidInput(): iterable { diff --git a/tests/unit/Validators/PhoneTest.php b/tests/unit/Validators/PhoneTest.php index 8e992c847..f17792731 100644 --- a/tests/unit/Validators/PhoneTest.php +++ b/tests/unit/Validators/PhoneTest.php @@ -17,11 +17,14 @@ namespace Respect\Validation\Validators; +use DI; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\Test; +use Respect\Validation\ContainerRegistry; use Respect\Validation\Exceptions\InvalidValidatorException; +use Respect\Validation\Exceptions\MissingComposerDependencyException; use Respect\Validation\Test\TestCase; use stdClass; @@ -66,6 +69,24 @@ public function itShouldThrowsExceptionWhenCountryCodeIsNotValid(): void new Phone('BRR'); } + #[Test] + public function shouldThrowWhenMissingComponent(): void + { + $mainContainer = ContainerRegistry::getContainer(); + ContainerRegistry::setContainer((new DI\ContainerBuilder())->useAutowiring(false)->build()); + try { + new Phone('US'); + $this->fail('Expected MissingComposerDependencyException was not thrown.'); + } catch (MissingComposerDependencyException $e) { + $this->assertStringContainsString( + 'Phone rule with country code requires PHP ISO Codes', + $e->getMessage(), + ); + } finally { + ContainerRegistry::setContainer($mainContainer); + } + } + /** @return array */ public static function providerForValidInputWithoutCountryCode(): array { diff --git a/tests/unit/Validators/SubdivisionCodeTest.php b/tests/unit/Validators/SubdivisionCodeTest.php index a2b457dd0..27c0f48e6 100644 --- a/tests/unit/Validators/SubdivisionCodeTest.php +++ b/tests/unit/Validators/SubdivisionCodeTest.php @@ -12,10 +12,13 @@ namespace Respect\Validation\Validators; +use DI; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\Test; +use Respect\Validation\ContainerRegistry; use Respect\Validation\Exceptions\InvalidValidatorException; +use Respect\Validation\Exceptions\MissingComposerDependencyException; use Respect\Validation\Test\RuleTestCase; #[Group('validator')] @@ -31,6 +34,24 @@ public function shouldNotAcceptWrongNamesOnConstructor(): void new SubdivisionCode('whatever'); } + #[Test] + public function shouldThrowWhenMissingComponent(): void + { + $mainContainer = ContainerRegistry::getContainer(); + ContainerRegistry::setContainer((new DI\ContainerBuilder())->useAutowiring(false)->build()); + try { + new SubdivisionCode('US'); + $this->fail('Expected MissingComposerDependencyException was not thrown.'); + } catch (MissingComposerDependencyException $e) { + $this->assertStringContainsString( + 'SubdivisionCode rule requires PHP ISO Codes', + $e->getMessage(), + ); + } finally { + ContainerRegistry::setContainer($mainContainer); + } + } + /** @return iterable */ public static function providerForValidInput(): iterable {