respect-validation/src/Validators/SubdivisionCode.php
Alexandre Gomes Gaigalas d8e31dbc3a Containerize sokil databases
The main focus of this change is to make those optional dependencies
more testable.

Unfortunately, some phpstan-ignores had to be included, since ::set
is not a PsrContainer method. We're only using it on tests though,
so it's fine. It targets our php-di container for testing purposes
only. The real implementation only relies on ::get.

This change also has the side effect of improving the performance
of those validators by not instantiating their databases each time
a iso validator is built, achieving massive improvements in those
scenarios. A small benchmark with no assertions was added to track
that improvement.
2026-01-29 19:29:51 +00:00

74 lines
2.5 KiB
PHP

<?php
/*
* SPDX-License-Identifier: MIT
* SPDX-FileCopyrightText: (c) Respect Project Contributors
* SPDX-FileContributor: Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-FileContributor: Henrique Moody <henriquemoody@gmail.com>
*/
declare(strict_types=1);
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;
use Respect\Validation\Message\Template;
use Respect\Validation\Result;
use Respect\Validation\Validator;
use Sokil\IsoCodes\Database\Countries;
use Sokil\IsoCodes\Database\Subdivisions;
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]
#[Template(
'{{subject}} must be a subdivision code of {{countryName|trans}}',
'{{subject}} must not be a subdivision code of {{countryName|trans}}',
)]
final readonly class SubdivisionCode implements Validator
{
use CanValidateUndefined;
private Countries\Country $country;
private Subdivisions $subdivisions;
public function __construct(
string $countryCode,
Countries|null $countries = null,
Subdivisions|null $subdivisions = null,
) {
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',
'sokil/php-isocodes-db-only',
);
}
$country = $countries->getByAlpha2($countryCode);
if ($country === null) {
throw new InvalidValidatorException('"%s" is not a supported country code', $countryCode);
}
$this->country = $country;
}
public function evaluate(mixed $input): Result
{
$parameters = ['countryName' => $this->country->getName()];
$subdivision = $this->subdivisions->getByCode($this->country->getAlpha2() . '-' . $input);
if ($this->isUndefined($input) && $subdivision === null) {
return Result::passed($input, $this, $parameters);
}
return Result::of($subdivision !== null, $input, $this, $parameters);
}
}