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.
This commit is contained in:
Alexandre Gomes Gaigalas 2026-01-28 18:08:19 -03:00
commit d8e31dbc3a
11 changed files with 219 additions and 38 deletions

View file

@ -12,6 +12,8 @@ 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;
@ -21,8 +23,6 @@ use Respect\Validation\Validator;
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 @@ final readonly class SubdivisionCode implements Validator
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 @@ final readonly class SubdivisionCode implements Validator
);
}
$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