Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
},
"require": {
"php": ">=8.5",
"php-di/php-di": "^7.1",
"psr/container": "^2.0",
"respect/config": "dev-master",
"respect/string-formatter": "^1.0",
"respect/stringifier": "^3.0",
"symfony/polyfill-mbstring": "^1.33"
Expand Down
2 changes: 1 addition & 1 deletion docs/messages/placeholder-conversion.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ See [PlaceholderFormatter][] documentation for more information on creating cust

## Container configuration

The `ContainerRegistry::createContainer()` method returns a [PHP-DI](https://php-di.org/) container. The definitions array follows the [PHP-DI definitions format](https://php-di.org/doc/php-definitions.html).
The `ContainerRegistry::createContainer()` returns a PSR-11 compatible container.

If you prefer to use a different container, `ContainerRegistry::setContainer()` accepts any [PSR-11](https://www.php-fig.org/psr/psr-11/) compatible container:

Expand Down
2 changes: 1 addition & 1 deletion docs/messages/translation.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ When using validators that display lists of values, use the `|list:or` or `|list

## Container configuration

The `ContainerRegistry::createContainer()` method returns a [PHP-DI](https://php-di.org/) container. The definitions array follows the [PHP-DI definitions format](https://php-di.org/doc/php-definitions.html).
The `ContainerRegistry::createContainer()` returns a PSR-11 compatible container.

If you prefer to use a different container, `ContainerRegistry::setContainer()` accepts any [PSR-11](https://www.php-fig.org/psr/psr-11/) compatible container:

Expand Down
2 changes: 1 addition & 1 deletion docs/migrating-from-v2-to-v3.md
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@ The `Factory` class has been replaced by a dependency injection container approa
+ ContainerRegistry::setContainer($container);
```

The `ContainerRegistry::createContainer()` returns a [PHP-DI](https://php-di.org/) container. You can also use any PSR-11 compatible container with `ContainerRegistry::setContainer()`.
The `ContainerRegistry::createContainer()` returns a PSR-11 compatible container.

### Custom validators

Expand Down
132 changes: 75 additions & 57 deletions src/ContainerRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@

namespace Respect\Validation;

use DI\Container;
use libphonenumber\PhoneNumberUtil;
use Psr\Container\ContainerInterface;
use Ramsey\Uuid\UuidFactory;
use Respect\Config\Container;
use Respect\StringFormatter\BypassTranslator;
use Respect\StringFormatter\Modifier;
use Respect\StringFormatter\Modifiers\ListModifier;
Expand All @@ -39,86 +40,103 @@
use Respect\Validation\Message\Renderer;
use Respect\Validation\Transformers\Prefix;
use Respect\Validation\Transformers\Transformer;
use Sokil\IsoCodes\Database\Countries;
use Sokil\IsoCodes\Database\Currencies;
use Sokil\IsoCodes\Database\Languages;
use Sokil\IsoCodes\Database\Subdivisions;
use Symfony\Contracts\Translation\TranslatorInterface;

use function DI\autowire;
use function DI\create;
use function DI\factory;

final class ContainerRegistry
{
private static ContainerInterface|null $container = null;
private static ContainerInterface|null $c = null;

/** @param array<string, mixed> $definitions */
public static function createContainer(array $definitions = []): Container
{
return new Container($definitions + [
PhoneNumberUtil::class => factory(static fn() => PhoneNumberUtil::getInstance()),
Transformer::class => create(Prefix::class),
TemplateResolver::class => create(TemplateResolver::class),
TranslatorInterface::class => autowire(BypassTranslator::class),
Renderer::class => autowire(InterpolationRenderer::class),
ResultFilter::class => create(OnlyFailedChildrenResultFilter::class),
'respect.validation.formatter.message' => autowire(FirstResultStringFormatter::class),
'respect.validation.formatter.full_message' => autowire(NestedListStringFormatter::class),
'respect.validation.formatter.messages' => autowire(NestedArrayFormatter::class),
$c = new Container();

$definitions += [
// By key
'respect.validation.formatter.full_message' => static fn() => new NestedListStringFormatter(),
'respect.validation.formatter.message' => static fn() => new FirstResultStringFormatter(),
'respect.validation.formatter.messages' => static fn() => new NestedArrayFormatter(),
'respect.validation.ignored_backtrace_paths' => [__DIR__ . '/ValidatorBuilder.php'],
'respect.validation.rule_factory.namespaces' => ['Respect\\Validation\\Validators'],
ValidatorFactory::class => factory(static fn(Container $container) => new NamespacedValidatorFactory(
$container->get(Transformer::class),
$container->get('respect.validation.rule_factory.namespaces'),
)),
Quoter::class => create(CodeQuoter::class)->constructor(120),
Handler::class => factory(static function (Container $container) {

// By interface
Quoter::class => static fn() => new CodeQuoter(120),
Transformer::class => static fn() => new Prefix(),
TranslatorInterface::class => static fn() => new BypassTranslator(),
Renderer::class => static fn() => new InterpolationRenderer(
$c->get(TranslatorInterface::class),
$c->get(PlaceholderFormatter::class),
$c->get(TemplateResolver::class),
),
Modifier::class => static fn() => new TransModifier(
new ListModifier(
new QuoteModifier(new RawModifier(new StringifyModifier($c->get(Stringifier::class)))),
$c->get(TranslatorInterface::class),
),
$c->get(TranslatorInterface::class),
),
ResultFilter::class => static fn() => new OnlyFailedChildrenResultFilter(),
Handler::class => static function () use ($c) {
$handler = CompositeHandler::create();
$handler->prependHandler(new PathHandler($container->get(Quoter::class)));
$handler->prependHandler(new PathHandler($c->get(Quoter::class)));
$handler->prependHandler(new NameHandler());
$handler->prependHandler(new ResultHandler($handler));

return $handler;
}),
PlaceholderFormatter::class => factory(static fn(Container $container) => new PlaceholderFormatter(
[],
$container->get(Modifier::class),
)),
Stringifier::class => factory(static fn(Container $container) => new HandlerStringifier(
$container->get(Handler::class),
new DumpStringifier(),
)),
Modifier::class => factory(static fn(Container $container) => new TransModifier(
new ListModifier(
new QuoteModifier(
new RawModifier(
new StringifyModifier($container->get(Stringifier::class)),
),
),
$container->get(TranslatorInterface::class),
),
$container->get(TranslatorInterface::class),
)),
ValidatorBuilder::class => factory(static fn(Container $container) => new ValidatorBuilder(
$container->get(ValidatorFactory::class),
$container->get(Renderer::class),
$container->get('respect.validation.formatter.message'),
$container->get('respect.validation.formatter.full_message'),
$container->get('respect.validation.formatter.messages'),
$container->get(ResultFilter::class),
$container->get('respect.validation.ignored_backtrace_paths'),
)),
]);
},
Stringifier::class => static fn() => new HandlerStringifier($c->get(Handler::class), new DumpStringifier()),

// By class
Countries::class => static fn() => new Countries(),
Currencies::class => static fn() => new Currencies(),
Languages::class => static fn() => new Languages(),
PhoneNumberUtil::class => static fn() => PhoneNumberUtil::getInstance(),
PlaceholderFormatter::class => static fn() => new PlaceholderFormatter([], $c->get(Modifier::class)),
Subdivisions::class => static fn() => new Subdivisions(),
TemplateResolver::class => static fn() => new TemplateResolver(),
UuidFactory::class => static fn() => new UuidFactory(),
ValidatorBuilder::class => static fn() => new ValidatorBuilder(
$c->get(ValidatorFactory::class),
$c->get(Renderer::class),
$c->get('respect.validation.formatter.message'),
$c->get('respect.validation.formatter.full_message'),
$c->get('respect.validation.formatter.messages'),
$c->get(ResultFilter::class),
$c->get('respect.validation.ignored_backtrace_paths'),
),
ValidatorFactory::class => static fn() => new NamespacedValidatorFactory(
$c->get(Transformer::class),
$c->get('respect.validation.rule_factory.namespaces'),
),
];

foreach ($definitions as $key => $value) {
$c->$key = $value;
}

return $c;
}

public static function getContainer(): ContainerInterface
{
if (!isset(self::$container)) {
self::$container = self::createContainer();
if (!isset(self::$c)) {
self::$c = self::createContainer();
}

return self::$container;
return self::$c;
}

public static function setContainer(ContainerInterface $instance): void
{
self::$container = $instance;
self::$c = $instance;
}

public static function resetContainer(): void
{
self::$c = null;
}
}
28 changes: 28 additions & 0 deletions tests/benchmark/ContainerBench.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

/*
* SPDX-License-Identifier: MIT
* SPDX-FileCopyrightText: (c) Respect Project Contributors
*/

declare(strict_types=1);

namespace Respect\Validation\Benchmarks;

use PhpBench\Attributes as Bench;
use Respect\Validation\ContainerRegistry;
use Respect\Validation\ValidatorBuilder;

class ContainerBench
{
#[Bench\Iterations(10)]
#[Bench\RetryThreshold(5)]
#[Bench\Revs(50)]
#[Bench\Warmup(1)]
#[Bench\Subject]
public function containerBuild(): void
{
ContainerRegistry::resetContainer();
ContainerRegistry::getContainer()->get(ValidatorBuilder::class);
}
}
2 changes: 1 addition & 1 deletion tests/feature/TranslatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
'en',
);
$container = ContainerRegistry::createContainer();
$container->set(TranslatorInterface::class, $translator);
$container[TranslatorInterface::class] = $translator;

beforeAll(fn() => ContainerRegistry::setContainer($container));

Expand Down
4 changes: 2 additions & 2 deletions tests/src/Stubs/MyValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ public static function assertIntType(mixed $input): void
$defaultContainer = ContainerRegistry::getContainer();

$container = ContainerRegistry::createContainer();
$container->set('respect.validation.ignored_backtrace_paths', [
$container['respect.validation.ignored_backtrace_paths'] = [
__FILE__,
...$defaultContainer->get('respect.validation.ignored_backtrace_paths'),
]);
];
ContainerRegistry::setContainer($container);

try {
Expand Down
11 changes: 11 additions & 0 deletions tests/unit/ContainerRegistryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,15 @@ public function itAllowsOverwritingTheContainer(): void

self::assertSame($newContainer, ContainerRegistry::getContainer());
}

#[Test]
public function itResetsTheContainerToTheDefaultState(): void
{
$newContainer = ContainerRegistry::createContainer(['foo' => 'bar']);
ContainerRegistry::setContainer($newContainer);
ContainerRegistry::resetContainer();
$defaultContainer = ContainerRegistry::getContainer();
self::assertNotSame($newContainer, $defaultContainer);
self::assertFalse($defaultContainer->has('foo'));
}
}
7 changes: 3 additions & 4 deletions tests/unit/Validators/CountryCodeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@

namespace Respect\Validation\Validators;

use DI;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\Test;
use Respect\Config\Container;
use Respect\Validation\ContainerRegistry;
use Respect\Validation\Exceptions\InvalidValidatorException;
use Respect\Validation\Exceptions\MissingComposerDependencyException;
Expand All @@ -42,8 +42,7 @@ public function itShouldThrowsExceptionWhenInvalidFormat(): void
#[Test]
public function shouldThrowWhenMissingComponent(): void
{
$mainContainer = ContainerRegistry::getContainer();
ContainerRegistry::setContainer((new DI\ContainerBuilder())->useAutowiring(false)->build());
ContainerRegistry::setContainer(new Container());
try {
new CountryCode('alpha-3');
$this->fail('Expected MissingComposerDependencyException was not thrown.');
Expand All @@ -53,7 +52,7 @@ public function shouldThrowWhenMissingComponent(): void
$e->getMessage(),
);
} finally {
ContainerRegistry::setContainer($mainContainer);
ContainerRegistry::resetContainer();
}
}

Expand Down
7 changes: 3 additions & 4 deletions tests/unit/Validators/CurrencyCodeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@

namespace Respect\Validation\Validators;

use DI;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\Test;
use Respect\Config\Container;
use Respect\Validation\ContainerRegistry;
use Respect\Validation\Exceptions\InvalidValidatorException;
use Respect\Validation\Exceptions\MissingComposerDependencyException;
Expand All @@ -42,8 +42,7 @@ public function itShouldThrowsExceptionWhenInvalidFormat(): void
#[Test]
public function shouldThrowWhenMissingComponent(): void
{
$mainContainer = ContainerRegistry::getContainer();
ContainerRegistry::setContainer((new DI\ContainerBuilder())->useAutowiring(false)->build());
ContainerRegistry::setContainer(new Container());
try {
new CurrencyCode('alpha-3');
$this->fail('Expected MissingComposerDependencyException was not thrown.');
Expand All @@ -53,7 +52,7 @@ public function shouldThrowWhenMissingComponent(): void
$e->getMessage(),
);
} finally {
ContainerRegistry::setContainer($mainContainer);
ContainerRegistry::resetContainer();
}
}

Expand Down
7 changes: 3 additions & 4 deletions tests/unit/Validators/LanguageCodeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@

namespace Respect\Validation\Validators;

use DI;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\Test;
use Respect\Config\Container;
use Respect\Validation\ContainerRegistry;
use Respect\Validation\Exceptions\InvalidValidatorException;
use Respect\Validation\Exceptions\MissingComposerDependencyException;
Expand All @@ -41,8 +41,7 @@ public function itShouldThrowAnExceptionWhenSetIsInvalid(): void
#[Test]
public function shouldThrowWhenMissingComponent(): void
{
$mainContainer = ContainerRegistry::getContainer();
ContainerRegistry::setContainer((new DI\ContainerBuilder())->useAutowiring(false)->build());
ContainerRegistry::setContainer(new Container());
try {
new LanguageCode('alpha-3');
$this->fail('Expected MissingComposerDependencyException was not thrown.');
Expand All @@ -52,7 +51,7 @@ public function shouldThrowWhenMissingComponent(): void
$e->getMessage(),
);
} finally {
ContainerRegistry::setContainer($mainContainer);
ContainerRegistry::resetContainer();
}
}

Expand Down
Loading