Generate PrefixMap constants from Mixin attributes

- Add PrefixMapGenerator to produce COMPOSABLE/COMPOSABLE_WITH_ARGUMENT
  constants from #[Mixin] attributes, replacing hand-written arrays
- Move Prefix transformer to reference generated PrefixMap constants
- Extract NamespaceScanner from MixinGenerator for shared directory scanning
- Introduce FluentBuilder subnamespace for builder-chain generators
  (MixinGenerator, PrefixMapGenerator, MethodBuilder, Mixin attribute)
- Add CodeGenerator interface and Config class as shared CodeGen contracts
This commit is contained in:
Alexandre Gomes Gaigalas 2026-03-11 12:50:10 -03:00
commit 25387853f3
No known key found for this signature in database
GPG key ID: C68060CCE0AE33F0
50 changed files with 393 additions and 152 deletions

View file

@ -0,0 +1,17 @@
<?php
/*
* SPDX-License-Identifier: MIT
* SPDX-FileCopyrightText: (c) Respect Project Contributors
* SPDX-FileContributor: Alexandre Gomes Gaigalas <alganet@gmail.com>
*/
declare(strict_types=1);
namespace Respect\Dev\CodeGen;
interface CodeGenerator
{
/** @return array<string, string> filename => content */
public function generate(): array;
}

View file

@ -0,0 +1,23 @@
<?php
/*
* SPDX-License-Identifier: MIT
* SPDX-FileCopyrightText: (c) Respect Project Contributors
* SPDX-FileContributor: Alexandre Gomes Gaigalas <alganet@gmail.com>
*/
declare(strict_types=1);
namespace Respect\Dev\CodeGen;
final readonly class Config
{
public function __construct(
public string $sourceDir,
public string $sourceNamespace,
public string $outputDir,
public string $outputNamespace,
public OutputFormatter $outputFormatter = new OutputFormatter(),
) {
}
}

View file

@ -8,7 +8,7 @@
declare(strict_types=1);
namespace Respect\Dev\CodeGen;
namespace Respect\Dev\CodeGen\FluentBuilder;
use Nette\PhpGenerator\Method;
use Nette\PhpGenerator\PhpNamespace;
@ -41,13 +41,13 @@ final class MethodBuilder
public function build(
PhpNamespace $namespace,
ReflectionClass $validatorReflection,
ReflectionClass $nodeReflection,
string $returnType,
string|null $prefix = null,
bool $static = false,
ReflectionParameter|null $prefixParameter = null,
): Method {
$originalName = $validatorReflection->getShortName();
$originalName = $nodeReflection->getShortName();
$name = $prefix ? $prefix . ucfirst($originalName) : lcfirst($originalName);
$method = new Method($name);
@ -61,7 +61,7 @@ final class MethodBuilder
$this->addPrefixParameter($method, $prefixParameter);
}
$constructor = $validatorReflection->getConstructor();
$constructor = $nodeReflection->getConstructor();
if ($constructor === null) {
return $method;
}

View file

@ -8,7 +8,7 @@
declare(strict_types=1);
namespace Respect\Dev\CodeGen\Attributes;
namespace Respect\Dev\CodeGen\FluentBuilder;
use Attribute;

View file

@ -8,42 +8,39 @@
declare(strict_types=1);
namespace Respect\Dev\CodeGen;
namespace Respect\Dev\CodeGen\FluentBuilder;
use DirectoryIterator;
use Nette\PhpGenerator\PhpNamespace;
use Nette\PhpGenerator\Printer;
use ReflectionClass;
use ReflectionParameter;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\CodeGenerator;
use Respect\Dev\CodeGen\Config;
use Respect\Dev\CodeGen\InterfaceConfig;
use Respect\Dev\CodeGen\NamespaceScanner;
use function file_get_contents;
use function in_array;
use function is_file;
use function is_readable;
use function ksort;
use function sprintf;
final class MixinGenerator
final class MixinGenerator implements CodeGenerator
{
/** @param array<InterfaceConfig> $interfaces */
public function __construct(
private readonly string $sourceDir,
private readonly string $sourceNamespace,
private readonly string $outputDir,
private readonly string $outputNamespace,
private readonly array $interfaces,
private readonly Config $config,
private readonly MethodBuilder $methodBuilder = new MethodBuilder(),
private readonly OutputFormatter $outputFormatter = new OutputFormatter(),
private readonly array $interfaces = [],
) {
}
/** @return array<string, string> filename => content */
public function generate(): array
{
$validators = $this->scanValidators();
$prefixes = $this->discoverPrefixes($validators);
$filters = $this->discoverFilters($validators);
$nodes = NamespaceScanner::scan($this->config->sourceDir, $this->config->sourceNamespace);
$prefixes = $this->discoverPrefixes($nodes);
$filters = $this->discoverFilters($nodes);
$files = [];
@ -52,12 +49,12 @@ final class MixinGenerator
foreach ($prefixes as $prefix) {
$interfaceName = $prefix['name'] . $interfaceConfig->suffix;
$prefixInterfaceNames[] = $this->outputNamespace . '\\' . $interfaceName;
$prefixInterfaceNames[] = $this->config->outputNamespace . '\\' . $interfaceName;
$this->generateInterface(
$interfaceName,
$interfaceConfig,
$validators,
$nodes,
$filters,
$prefix,
$files,
@ -67,7 +64,7 @@ final class MixinGenerator
$this->generateRootInterface(
$interfaceConfig,
$prefixInterfaceNames,
$validators,
$nodes,
$filters,
$files,
);
@ -76,41 +73,16 @@ final class MixinGenerator
return $files;
}
/** @return array<string, ReflectionClass> */
private function scanValidators(): array
{
$validators = [];
foreach (new DirectoryIterator($this->sourceDir) as $file) {
if (!$file->isFile()) {
continue;
}
$className = $this->sourceNamespace . '\\' . $file->getBasename('.php');
$reflection = new ReflectionClass($className);
if ($reflection->isAbstract()) {
continue;
}
$validators[$reflection->getShortName()] = $reflection;
}
ksort($validators);
return $validators;
}
/**
* @param array<string, ReflectionClass> $validators
* @param array<string, ReflectionClass> $nodes
*
* @return array<array{name: string, prefix: string, requireInclusion: bool, prefixParameter: ?ReflectionParameter}>
*/
private function discoverPrefixes(array $validators): array
private function discoverPrefixes(array $nodes): array
{
$prefixes = [];
foreach ($validators as $reflection) {
foreach ($nodes as $reflection) {
$attributes = $reflection->getAttributes(Mixin::class);
if ($attributes === []) {
continue;
@ -121,14 +93,13 @@ final class MixinGenerator
continue;
}
$constructor = $reflection->getConstructor();
$prefixParameter = null;
if ($mixin->prefixParameter) {
$constructor = $reflection->getConstructor();
if ($constructor !== null) {
$params = $constructor->getParameters();
if ($params !== []) {
$prefixParameter = $params[0];
}
if ($mixin->prefixParameter && $constructor !== null) {
$parameters = $constructor->getParameters();
if ($parameters !== []) {
$prefixParameter = $parameters[0];
}
}
@ -146,15 +117,15 @@ final class MixinGenerator
}
/**
* @param array<string, ReflectionClass> $validators
* @param array<string, ReflectionClass> $nodes
*
* @return array<string, Mixin>
*/
private function discoverFilters(array $validators): array
private function discoverFilters(array $nodes): array
{
$filters = [];
foreach ($validators as $name => $reflection) {
foreach ($nodes as $name => $reflection) {
$attributes = $reflection->getAttributes(Mixin::class);
if ($attributes === []) {
continue;
@ -167,7 +138,7 @@ final class MixinGenerator
}
/**
* @param array<string, ReflectionClass> $validators
* @param array<string, ReflectionClass> $nodes
* @param array<string, Mixin> $filters
* @param array{name: string, prefix: string, requireInclusion: bool, prefixParameter: ?ReflectionParameter} $prefix
* @param array<string, string> $files
@ -175,15 +146,15 @@ final class MixinGenerator
private function generateInterface(
string $interfaceName,
InterfaceConfig $config,
array $validators,
array $nodes,
array $filters,
array $prefix,
array &$files,
): void {
$namespace = new PhpNamespace($this->outputNamespace);
$namespace = new PhpNamespace($this->config->outputNamespace);
$interface = $namespace->addInterface($interfaceName);
foreach ($validators as $name => $reflection) {
foreach ($nodes as $name => $reflection) {
$mixin = $filters[$name] ?? null;
if ($prefix['requireInclusion']) {
@ -211,27 +182,29 @@ final class MixinGenerator
/**
* @param array<string> $prefixInterfaceNames
* @param array<string, ReflectionClass> $validators
* @param array<string, ReflectionClass> $nodes
* @param array<string, Mixin> $filters
* @param array<string, string> $files
*/
private function generateRootInterface(
InterfaceConfig $config,
array $prefixInterfaceNames,
array $validators,
array $nodes,
array $filters,
array &$files,
): void {
$interfaceName = $config->suffix;
$namespace = new PhpNamespace($this->outputNamespace);
$namespace = new PhpNamespace($this->config->outputNamespace);
$interface = $namespace->addInterface($interfaceName);
foreach ($config->rootExtends as $extend) {
$namespace->addUse($extend);
$interface->addExtend($extend);
}
foreach ($prefixInterfaceNames as $prefixInterface) {
$interface->addExtend($prefixInterface);
foreach ($prefixInterfaceNames as $prefixInterfaceName) {
$namespace->addUse($prefixInterfaceName);
$interface->addExtend($prefixInterfaceName);
}
if ($config->rootComment !== null) {
@ -242,7 +215,7 @@ final class MixinGenerator
$namespace->addUse($use);
}
foreach ($validators as $reflection) {
foreach ($nodes as $reflection) {
$method = $this->methodBuilder->build(
$namespace,
$reflection,
@ -260,16 +233,17 @@ final class MixinGenerator
/** @param array<string, string> $files */
private function addFile(string $interfaceName, PhpNamespace $namespace, array &$files): void
{
$filename = $this->config->outputDir . '/' . $interfaceName . '.php';
$printer = new Printer();
$printer->wrapLength = 300;
$filename = sprintf('%s/%s.php', $this->outputDir, $interfaceName);
$existingContent = '';
if (is_file($filename) && is_readable($filename)) {
$existingContent = file_get_contents($filename) ?: '';
}
$formattedContent = $this->outputFormatter->format(
$formattedContent = $this->config->outputFormatter->format(
$printer->printNamespace($namespace),
$existingContent,
);

View file

@ -0,0 +1,160 @@
<?php
/*
* SPDX-License-Identifier: MIT
* SPDX-FileCopyrightText: (c) Respect Project Contributors
* SPDX-FileContributor: Alexandre Gomes Gaigalas <alganet@gmail.com>
*/
declare(strict_types=1);
namespace Respect\Dev\CodeGen\FluentBuilder;
use Nette\PhpGenerator\PhpNamespace;
use Nette\PhpGenerator\Printer;
use ReflectionClass;
use Respect\Dev\CodeGen\CodeGenerator;
use Respect\Dev\CodeGen\Config;
use Respect\Dev\CodeGen\NamespaceScanner;
use function array_keys;
use function ctype_upper;
use function file_get_contents;
use function is_file;
use function is_readable;
use function ksort;
use function lcfirst;
use function str_starts_with;
use function strlen;
use function uksort;
final class PrefixMapGenerator implements CodeGenerator
{
public function __construct(
private readonly Config $config,
private readonly string $outputClassName,
) {
}
/** @return array<string, string> filename => content */
public function generate(): array
{
$nodes = NamespaceScanner::scan($this->config->sourceDir, $this->config->sourceNamespace);
$prefixes = $this->discoverPrefixes($nodes);
$composable = $this->buildComposable($nodes, $prefixes);
$composableWithArgument = $this->buildComposableWithArgument($prefixes);
$namespace = new PhpNamespace($this->config->outputNamespace);
$class = $namespace->addClass($this->outputClassName);
$class->setFinal();
$class->addConstant('COMPOSABLE', $composable)->setPublic()->setType('array');
$class->addConstant('COMPOSABLE_WITH_ARGUMENT', $composableWithArgument)->setPublic()->setType('array');
$printer = new Printer();
$printer->wrapLength = 300;
$outputFile = $this->config->outputDir . '/' . $this->outputClassName . '.php';
$existingContent = '';
if (is_file($outputFile) && is_readable($outputFile)) {
$existingContent = file_get_contents($outputFile) ?: '';
}
$formattedContent = $this->config->outputFormatter->format(
$printer->printNamespace($namespace),
$existingContent,
);
return [$outputFile => $formattedContent];
}
/**
* @param array<string, ReflectionClass> $nodes
*
* @return array<string, array{prefix: string, prefixParameter: bool}>
*/
private function discoverPrefixes(array $nodes): array
{
$prefixes = [];
foreach ($nodes as $reflection) {
$attributes = $reflection->getAttributes(Mixin::class);
if ($attributes === []) {
continue;
}
$mixin = $attributes[0]->newInstance();
if ($mixin->prefix === null) {
continue;
}
$prefixes[$mixin->prefix] = [
'prefix' => $mixin->prefix,
'prefixParameter' => $mixin->prefixParameter,
];
}
ksort($prefixes);
return $prefixes;
}
/**
* @param array<string, ReflectionClass> $nodes
* @param array<string, array{prefix: string, prefixParameter: bool}> $prefixes
*
* @return array<string, true>
*/
private function buildComposable(array $nodes, array $prefixes): array
{
$composable = [];
foreach (array_keys($prefixes) as $prefix) {
$composable[$prefix] = true;
foreach (array_keys($nodes) as $name) {
$lcName = lcfirst($name);
if ($lcName === $prefix) {
continue;
}
if (!str_starts_with($lcName, $prefix)) {
continue;
}
if (!ctype_upper($lcName[strlen($prefix)])) {
continue;
}
$composable[$lcName] = true;
}
}
uksort($composable, static fn(string $a, string $b): int => strlen($b) <=> strlen($a) ?: $a <=> $b);
return $composable;
}
/**
* @param array<string, array{prefix: string, prefixParameter: bool}> $prefixes
*
* @return array<string, true>
*/
private function buildComposableWithArgument(array $prefixes): array
{
$composableWithArgument = [];
foreach ($prefixes as $prefix => $info) {
if (!$info['prefixParameter']) {
continue;
}
$composableWithArgument[$prefix] = true;
}
ksort($composableWithArgument);
return $composableWithArgument;
}
}

View file

@ -0,0 +1,44 @@
<?php
/*
* SPDX-License-Identifier: MIT
* SPDX-FileCopyrightText: (c) Respect Project Contributors
* SPDX-FileContributor: Alexandre Gomes Gaigalas <alganet@gmail.com>
*/
declare(strict_types=1);
namespace Respect\Dev\CodeGen;
use DirectoryIterator;
use ReflectionClass;
use function ksort;
final class NamespaceScanner
{
/** @return array<string, ReflectionClass> */
public static function scan(string $directory, string $namespace): array
{
$nodes = [];
foreach (new DirectoryIterator($directory) as $file) {
if (!$file->isFile()) {
continue;
}
$className = $namespace . '\\' . $file->getBasename('.php');
$reflection = new ReflectionClass($className);
if ($reflection->isAbstract()) {
continue;
}
$nodes[$reflection->getShortName()] = $reflection;
}
ksort($nodes);
return $nodes;
}
}

View file

@ -27,7 +27,7 @@ final class OutputFormatter
$existingHeader = $matches[0] ?? '';
$replacements = [
'/\n\n\t(public|\/\*\*)/m' => PHP_EOL . ' $1',
'/\n\n\t(public|private|\/\*\*)/m' => PHP_EOL . ' $1',
'/\t/m' => ' ',
'/\?([a-zA-Z]+) \$/' => '$1|null $',
'/\/\*\*\n +\* (.+)\n +\*\//m' => '/** $1 */',

View file

@ -11,9 +11,11 @@ declare(strict_types=1);
namespace Respect\Dev\Commands;
use Respect\Dev\CodeGen\Config;
use Respect\Dev\CodeGen\FluentBuilder\MethodBuilder;
use Respect\Dev\CodeGen\FluentBuilder\MixinGenerator;
use Respect\Dev\CodeGen\FluentBuilder\PrefixMapGenerator;
use Respect\Dev\CodeGen\InterfaceConfig;
use Respect\Dev\CodeGen\MethodBuilder;
use Respect\Dev\CodeGen\MixinGenerator;
use Respect\Dev\Differ\ConsoleDiffer;
use Respect\Dev\Differ\Item;
use Respect\Validation\Mixins\Chain;
@ -58,11 +60,15 @@ final class LintMixinCommand extends Command
{
$srcDir = dirname(__DIR__, 2) . '/src';
$generator = new MixinGenerator(
$config = new Config(
sourceDir: $srcDir . '/Validators',
sourceNamespace: 'Respect\\Validation\\Validators',
outputDir: $srcDir . '/Mixins',
outputNamespace: 'Respect\\Validation\\Mixins',
);
$generator = new MixinGenerator(
config: $config,
methodBuilder: new MethodBuilder(
excludedTypePrefixes: ['Sokil', 'Egulias'],
excludedTypeNames: ['finfo'],
@ -83,7 +89,12 @@ final class LintMixinCommand extends Command
],
);
$files = $generator->generate();
$prefixMapGenerator = new PrefixMapGenerator(
config: $config,
outputClassName: 'PrefixMap',
);
$files = $generator->generate() + $prefixMapGenerator->generate();
$updatableFiles = [];
foreach ($files as $filename => $content) {

33
src/Mixins/PrefixMap.php generated Normal file
View file

@ -0,0 +1,33 @@
<?php
/*
* SPDX-License-Identifier: MIT
* SPDX-FileCopyrightText: (c) Respect Project Contributors
* SPDX-FileContributor: Alexandre Gomes Gaigalas <alganet@gmail.com>
*/
declare(strict_types=1);
namespace Respect\Validation\Mixins;
final class PrefixMap
{
public const array COMPOSABLE = [
'propertyOptional' => true,
'propertyExists' => true,
'keyOptional' => true,
'keyExists' => true,
'property' => true,
'undefOr' => true,
'keySet' => true,
'length' => true,
'nullOr' => true,
'allOf' => true,
'all' => true,
'key' => true,
'max' => true,
'min' => true,
'not' => true,
];
public const array COMPOSABLE_WITH_ARGUMENT = ['key' => true, 'property' => true];
}

View file

@ -11,6 +11,8 @@ declare(strict_types=1);
namespace Respect\Validation\Transformers;
use Respect\Validation\Mixins\PrefixMap;
use function array_keys;
use function array_slice;
use function implode;
@ -19,31 +21,6 @@ use function sprintf;
final class Prefix implements Transformer
{
private const array RULES_THAT_PREFIX_OR_STAND_ALONE = [
'all' => true,
'allOf' => true,
'emoji' => true,
'key' => true,
'keyExists' => true,
'keyOptional' => true,
'keySet' => true,
'length' => true,
'max' => true,
'maxAge' => true,
'min' => true,
'minAge' => true,
'not' => true,
'nullOr' => true,
'property' => true,
'propertyExists' => true,
'propertyOptional' => true,
'undefOr' => true,
];
private const array RULES_THAT_USE_SUFFIX_AS_ARGUMENT = [
'key' => true,
'property' => true,
];
private static string|null $regex = null;
public function transform(ValidatorSpec $validatorSpec): ValidatorSpec
@ -53,38 +30,42 @@ final class Prefix implements Transformer
return $validatorSpec;
}
if (!isset(self::RULES_THAT_USE_SUFFIX_AS_ARGUMENT[$matches['name']])) {
if (!isset(PrefixMap::COMPOSABLE_WITH_ARGUMENT[$matches['prefix']])) {
return new ValidatorSpec(
$matches['rest'],
$matches['suffix'],
$validatorSpec->arguments,
new ValidatorSpec($matches['name']),
new ValidatorSpec($matches['prefix']),
);
}
return new ValidatorSpec(
$matches['rest'],
$matches['suffix'],
array_slice($validatorSpec->arguments, 1),
new ValidatorSpec($matches['name'], [$validatorSpec->arguments[0]]),
new ValidatorSpec($matches['prefix'], [$validatorSpec->arguments[0]]),
);
}
/** @return array{}|array{name: string, rest: string} */
/** @return array{}|array{prefix: string, suffix: string} */
private function match(ValidatorSpec $validatorSpec): array
{
if ($validatorSpec->wrapper !== null || isset(self::RULES_THAT_PREFIX_OR_STAND_ALONE[$validatorSpec->name])) {
if ($validatorSpec->wrapper !== null || isset(PrefixMap::COMPOSABLE[$validatorSpec->name])) {
return [];
}
preg_match(self::getRegex(), $validatorSpec->name, $matches);
return $matches;
if ($matches === []) {
return [];
}
return ['prefix' => $matches['prefix'], 'suffix' => $matches['suffix']];
}
private static function getRegex(): string
{
return self::$regex ?? self::$regex = sprintf(
'/^(?<name>%s)(?<rest>.+)$/',
implode('|', array_keys(self::RULES_THAT_PREFIX_OR_STAND_ALONE)),
'/^(?<prefix>%s)(?<suffix>.+)$/',
implode('|', array_keys(PrefixMap::COMPOSABLE)),
);
}
}

View file

@ -15,7 +15,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Helpers\CanEvaluateShortCircuit;
use Respect\Validation\Message\Template;
use Respect\Validation\Path;

View file

@ -17,7 +17,7 @@ use ReflectionAttribute;
use ReflectionClass;
use ReflectionObject;
use ReflectionProperty;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Id;
use Respect\Validation\Result;
use Respect\Validation\Validator;

View file

@ -15,7 +15,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Exceptions\InvalidValidatorException;
use Respect\Validation\Helpers\CanCompareValues;
use Respect\Validation\Message\Template;

View file

@ -12,7 +12,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Exceptions\InvalidValidatorException;
use Respect\Validation\Helpers\CanCompareValues;
use Respect\Validation\Message\Template;

View file

@ -15,7 +15,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Message\Template;
use Respect\Validation\Result;
use Respect\Validation\Validator;

View file

@ -15,7 +15,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Message\Template;
use Respect\Validation\Result;
use Respect\Validation\Validator;

View file

@ -15,7 +15,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Message\Template;
use Respect\Validation\Validators\Core\Comparison;

View file

@ -18,7 +18,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Message\Template;
use Respect\Validation\Validators\Core\Simple;

View file

@ -15,7 +15,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Message\Template;
use Respect\Validation\Validators\Core\Simple;
use SplFileInfo;

View file

@ -14,7 +14,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Message\Template;
use Respect\Validation\Result;
use Respect\Validation\Validator;

View file

@ -15,7 +15,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Message\Template;
use Respect\Validation\Validators\Core\Simple;

View file

@ -11,7 +11,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\StringFormatter\Formatter;
use Respect\Validation\Result;
use Respect\Validation\Validator;

View file

@ -15,7 +15,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Message\Template;
use Respect\Validation\Validators\Core\Comparison;

View file

@ -14,7 +14,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Message\Template;
use Respect\Validation\Validators\Core\Comparison;

View file

@ -15,7 +15,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Message\Template;
use Respect\Validation\Result;
use Respect\Validation\Validator;

View file

@ -16,7 +16,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Message\Template;
use Respect\Validation\Result;
use Respect\Validation\Validator;

View file

@ -15,7 +15,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Message\Template;
use Respect\Validation\Validators\Core\Simple;

View file

@ -16,7 +16,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Path;
use Respect\Validation\Result;
use Respect\Validation\Validator;

View file

@ -13,7 +13,7 @@ namespace Respect\Validation\Validators;
use ArrayAccess;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Message\Template;
use Respect\Validation\Path;
use Respect\Validation\Result;

View file

@ -13,7 +13,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Result;
use Respect\Validation\Validator;
use Respect\Validation\Validators\Core\KeyRelated;

View file

@ -15,7 +15,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Exceptions\InvalidValidatorException;
use Respect\Validation\Helpers\CanEvaluateShortCircuit;
use Respect\Validation\Message\Template;

View file

@ -20,7 +20,7 @@ namespace Respect\Validation\Validators;
use Attribute;
use Countable as PhpCountable;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Message\Template;
use Respect\Validation\Result;
use Respect\Validation\Validator;

View file

@ -15,7 +15,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Message\Template;
use Respect\Validation\Validators\Core\Comparison;

View file

@ -14,7 +14,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Message\Template;
use Respect\Validation\Validators\Core\Comparison;

View file

@ -15,7 +15,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Message\Template;
use Respect\Validation\Result;
use Respect\Validation\Validators\Core\FilteredArray;

View file

@ -15,7 +15,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Message\Template;
use Respect\Validation\Result;
use Respect\Validation\Validators\Core\FilteredArray;

View file

@ -17,7 +17,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Message\Template;
use Respect\Validation\Result;
use Respect\Validation\Validator;

View file

@ -12,7 +12,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Name;
use Respect\Validation\Result;
use Respect\Validation\Validator;

View file

@ -18,7 +18,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Result;
use Respect\Validation\Validator;

View file

@ -14,7 +14,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Message\Template;
use Respect\Validation\Result;
use Respect\Validation\Validator;

View file

@ -17,7 +17,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Message\Template;
use Respect\Validation\Validators\Core\Simple;

View file

@ -15,7 +15,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Message\Template;
use Respect\Validation\Validators\Core\Simple;

View file

@ -19,7 +19,7 @@ namespace Respect\Validation\Validators;
use Attribute;
use ReflectionClass;
use ReflectionObject;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Path;
use Respect\Validation\Result;
use Respect\Validation\Validator;

View file

@ -14,7 +14,7 @@ namespace Respect\Validation\Validators;
use Attribute;
use ReflectionClass;
use ReflectionObject;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Message\Template;
use Respect\Validation\Path;
use Respect\Validation\Result;

View file

@ -13,7 +13,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Result;
use Respect\Validation\Validator;

View file

@ -12,7 +12,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Result;
use Respect\Validation\Validator;

View file

@ -16,7 +16,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Helpers\CanValidateUndefined;
use Respect\Validation\Message\Template;
use Respect\Validation\Result;

View file

@ -13,7 +13,7 @@ declare(strict_types=1);
namespace Respect\Validation\Validators;
use Attribute;
use Respect\Dev\CodeGen\Attributes\Mixin;
use Respect\Dev\CodeGen\FluentBuilder\Mixin;
use Respect\Validation\Helpers\CanValidateUndefined;
use Respect\Validation\Message\Template;
use Respect\Validation\Result;

View file

@ -91,9 +91,7 @@ final class PrefixTest extends TestCase
'keySet' => ['keySet'],
'length' => ['length'],
'max' => ['max'],
'maxAge' => ['maxAge'],
'min' => ['min'],
'minAge' => ['minAge'],
'not' => ['not'],
'undef' => ['undef'],
'property' => ['property'],