Skip to content
Merged
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
208 changes: 104 additions & 104 deletions benchmark.php

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion config/laranail-validation.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

'aliases' => [
'enabled' => env('LARANAIL_VALIDATION_ALIASES', false),
'prefix' => 'laranail_',
'prefix' => 'laranail_',
],

/*
Expand Down
2 changes: 1 addition & 1 deletion rector-migrate-1.0.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\Renaming\Rector\MethodCall\RenameMethodRector;
use Rector\Renaming\ValueObject\MethodCallRename;
use Simtabi\Laranail\Validation\Builder\Nodes\ArrayRule;
use Rector\Renaming\Rector\MethodCall\RenameMethodRector;

/**
* The 0.x → 1.0 migration set (UPGRADING.md, v1.0.0). Run it against an
Expand Down
24 changes: 12 additions & 12 deletions rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@

declare(strict_types=1);

use Rector\Config\RectorConfig;
use RectorPest\Set\PestSetList;
use RectorLaravel\Set\LaravelSetList;
use RectorPest\Rules\UseToMatchRector;
use Rector\Caching\ValueObject\Storage\FileCacheStorage;
use Rector\CodeQuality\Rector\If_\ExplicitBoolCompareRector;
use Rector\Carbon\Rector\FuncCall\DateFuncCallToCarbonRector;
use Rector\CodeQuality\Rector\BooleanOr\RepeatedOrEqualToInArrayRector;
use Rector\CodeQuality\Rector\ClassMethod\InlineArrayReturnAssignRector;
use Rector\CodeQuality\Rector\If_\ExplicitBoolCompareRector;
use Rector\CodingStyle\Rector\Encapsed\EncapsedStringsToSprintfRector;
use Rector\Config\RectorConfig;
use Rector\DeadCode\Rector\ClassMethod\RemoveUselessParamTagRector;
use Rector\DeadCode\Rector\ClassMethod\RemoveUselessReturnTagRector;
use Rector\Php81\Rector\FuncCall\NullToStrictStringFuncCallArgRector;
use Rector\CodingStyle\Rector\Encapsed\EncapsedStringsToSprintfRector;
use Rector\CodeQuality\Rector\BooleanOr\RepeatedOrEqualToInArrayRector;
use Rector\CodeQuality\Rector\ClassMethod\InlineArrayReturnAssignRector;
use Rector\Privatization\Rector\ClassMethod\PrivatizeFinalClassMethodRector;
use Rector\TypeDeclaration\Rector\ArrowFunction\AddArrowFunctionReturnTypeRector;
use RectorLaravel\Set\LaravelSetList;
use RectorPest\Rules\UseToMatchRector;
use RectorPest\Set\PestSetList;

return RectorConfig::configure()
->withCache(
Expand All @@ -25,10 +25,10 @@
containerCacheDirectory: './.cache/rectorContainer',
)
->withPaths([
__DIR__ . '/src',
__DIR__ . '/tests',
__DIR__.'/src',
__DIR__.'/tests',
])
->withSkipPath(__DIR__ . '/tests/Fixtures/MacroableFootgun')
->withSkipPath(__DIR__.'/tests/Fixtures/MacroableFootgun')
->withPreparedSets(
deadCode: true,
codeQuality: true,
Expand Down Expand Up @@ -78,6 +78,6 @@
// Hot-path closure allocates a literal array on every invocation
// when in_array() is used. Explicit === comparisons avoid that.
RepeatedOrEqualToInArrayRector::class => [
__DIR__ . '/src/FastCheckCompiler.php',
__DIR__.'/src/FastCheckCompiler.php',
],
]);
192 changes: 96 additions & 96 deletions resources/lang/en/validation.php

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions src/Actions/CachedDnsResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

namespace Simtabi\Laranail\Validation\Actions;

use Throwable;
use Illuminate\Support\Facades\Cache;
use Illuminate\Contracts\Cache\Repository;
use Illuminate\Support\Facades\Cache;
use Simtabi\Laranail\Validation\Contracts\Email\DnsResolver;
use Throwable;

/**
* The default {@see DnsResolver}: a cached MX lookup over PHP's resolver.
Expand Down Expand Up @@ -68,7 +68,7 @@ public function hasMailExchanger(string $domain): bool
}

/**
* @param callable(): bool $callback
* @param callable(): bool $callback
*/
private function remember(string $domain, callable $callback): bool
{
Expand All @@ -80,7 +80,7 @@ private function remember(string $domain, callable $callback): bool

try {
$value = $store->remember(
self::CACHE_PREFIX . $domain,
self::CACHE_PREFIX.$domain,
$this->ttl ?? $this->configuredTtl(),
static fn (): bool => $callback(),
);
Expand Down
89 changes: 40 additions & 49 deletions src/BatchDatabaseChecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@

namespace Simtabi\Laranail\Validation;

use Throwable;
use Stringable;
use ReflectionProperty;
use ReflectionException;
use Illuminate\Validation\Rules\Exists;
use Illuminate\Validation\Rules\Unique;
use Illuminate\Validation\DatabasePresenceVerifier;
use Illuminate\Validation\PresenceVerifierInterface;
use Simtabi\Laranail\Validation\Internal\BatchRuleShape;
use Illuminate\Validation\Rules\Exists;
use Illuminate\Validation\Rules\Unique;
use ReflectionException;
use ReflectionProperty;
use Simtabi\Laranail\Validation\Exceptions\BatchLimitExceededException;
use Simtabi\Laranail\Validation\Internal\BatchPresenceQuery;
use Simtabi\Laranail\Validation\Internal\ValueTypePredicates;
use Simtabi\Laranail\Validation\Internal\BatchRuleShape;
use Simtabi\Laranail\Validation\Internal\PresenceMatchFidelity;
use Simtabi\Laranail\Validation\Exceptions\BatchLimitExceededException;
use Simtabi\Laranail\Validation\Internal\ValueTypePredicates;
use Stringable;
use Throwable;

/**
* Batches exists/unique database validation queries for wildcard arrays.
Expand Down Expand Up @@ -80,8 +80,7 @@ public static function isBatchable(Exists|Unique $rule): bool
/**
* Batch-query which values exist in the database for an Exists rule.
*
* @param array<int, mixed> $values Deduplicated, non-null values to check
*
* @param array<int, mixed> $values Deduplicated, non-null values to check
* @return array<int, mixed> Values that exist in the DB
*/
public static function fetchExisting(array $values, Exists $rule): array
Expand All @@ -102,8 +101,7 @@ public static function fetchExisting(array $values, Exists $rule): array
/**
* Batch-query which values are already taken for a Unique rule.
*
* @param array<int, mixed> $values Deduplicated, non-null values to check
*
* @param array<int, mixed> $values Deduplicated, non-null values to check
* @return array<int, mixed> Values that already exist (taken)
*/
public static function fetchTaken(array $values, Unique $rule): array
Expand Down Expand Up @@ -168,8 +166,7 @@ public static function batchableSignature(Exists|Unique $rule, string $table, st
/**
* Find batchable Exists/Unique rules in a set of compiled rules.
*
* @param array<string, mixed> $rules
*
* @param array<string, mixed> $rules
* @return array<string, Exists|Unique> Field name → rule object
*/
public static function findBatchableRules(array $rules): array
Expand All @@ -195,10 +192,9 @@ public static function findBatchableRules(array $rules): array
/**
* Collect values from items for batchable rules and group by table:column.
*
* @param array<string, Exists|Unique> $batchableFields
* @param array<int|string, mixed> $items
* @param array<string, mixed> $fieldRules Per-field rule array used to derive type predicates.
*
* @param array<string, Exists|Unique> $batchableFields
* @param array<int|string, mixed> $items
* @param array<string, mixed> $fieldRules Per-field rule array used to derive type predicates.
* @return array<string, array{rule: Exists|Unique, values: list<mixed>}>
*/
public static function collectValues(array $batchableFields, array $items, bool $isScalar, array $fieldRules = []): array
Expand Down Expand Up @@ -267,9 +263,8 @@ public static function collectValues(array $batchableFields, array $items, bool
* Unlike collectValues() which iterates items, this iterates expanded
* rules like 'items.0.email', 'items.1.email' and uses data_get().
*
* @param array<string, mixed> $preparedRules Expanded rules with concrete paths
* @param array<string, mixed> $data
*
* @param array<string, mixed> $preparedRules Expanded rules with concrete paths
* @param array<string, mixed> $data
* @return array<string, array{rule: Exists|Unique, values: list<mixed>}>
*/
public static function collectExpandedValues(array $preparedRules, array $data): array
Expand Down Expand Up @@ -336,9 +331,8 @@ public static function collectExpandedValues(array $preparedRules, array $data):
* Object-form rules fall through unchanged; if no known type rule is
* present, values are returned as-is (matches previous behaviour).
*
* @param array<mixed> $values
* @param array<mixed>|string $itemRules Either a pipe-delimited string or an array of rules.
*
* @param array<mixed> $values
* @param array<mixed>|string $itemRules Either a pipe-delimited string or an array of rules.
* @return array<int, mixed>
*/
public static function filterValuesByType(array $values, array|string $itemRules): array
Expand All @@ -352,9 +346,9 @@ public static function filterValuesByType(array $values, array|string $itemRules
* Groups arrive already filtered (Phase 1) and deduplicated (collectors
* own the dedup). Canonical order is filter → dedup → cap check → query.
*
* @param array<string, array{rule: Exists|Unique, values: list<mixed>}> $groups
* @param array<string, true> $poisonedTableColumns Extra table:columns that must not
* register a lookup ({@see findPoisonedTableColumns})
* @param array<string, array{rule: Exists|Unique, values: list<mixed>}> $groups
* @param array<string, true> $poisonedTableColumns Extra table:columns that must not
* register a lookup ({@see findPoisonedTableColumns})
*
* @throws BatchLimitExceededException When any group's value count exceeds `$maxValuesPerGroup`.
*/
Expand Down Expand Up @@ -399,12 +393,11 @@ public static function buildVerifier(array $groups, array $poisonedTableColumns
* handles each rule correctly via per-item queries. Rare case, small
* perf hit — exists and unique on the same (table, column) is unusual.
*
* @param array<string, array{rule: Exists|Unique, values: list<mixed>}> $groups Keyed by "table:column:ruleType"
* @param array<string, true> $poisonedTableColumns Table:columns claimed by rules the
* collectors never saw (a field's second DB rule,
* non-batchable or string-form rules) — treated
* exactly like a detected group conflict.
*
* @param array<string, array{rule: Exists|Unique, values: list<mixed>}> $groups Keyed by "table:column:ruleType"
* @param array<string, true> $poisonedTableColumns Table:columns claimed by rules the
* collectors never saw (a field's second DB rule,
* non-batchable or string-form rules) — treated
* exactly like a detected group conflict.
* @return bool False if any group was skipped, so the caller knows a
* fallback verifier is required to answer it.
*/
Expand All @@ -422,7 +415,7 @@ public static function registerLookups(PrecomputedPresenceVerifier $verifier, ar

[$table, $column] = self::parseGroupKey($key);

if (isset($conflicting[$table . ':' . $column])) {
if (isset($conflicting[$table.':'.$column])) {
$complete = false;

continue;
Expand All @@ -447,8 +440,7 @@ public static function registerLookups(PrecomputedPresenceVerifier $verifier, ar
/**
* Deduplicate and cast values to strings for batch queries.
*
* @param array<mixed> $values
*
* @param array<mixed> $values
* @return list<string>
*/
public static function uniqueStringValues(array $values): array
Expand All @@ -469,17 +461,17 @@ public static function uniqueStringValues(array $values): array
*/
private static function groupKey(string $table, string $column, Exists|Unique $rule): string
{
return $table . ':' . $column . ':'
. ($rule instanceof Unique ? 'unique' : 'exists') . ':'
. BatchRuleShape::of(self::extractMeta($rule));
return $table.':'.$column.':'
.($rule instanceof Unique ? 'unique' : 'exists').':'
.BatchRuleShape::of(self::extractMeta($rule));
}

/**
* Throw `BatchLimitExceededException` if any group exceeds the hard cap.
* Called after filter/dedup so the cap sees the same normalised set the
* query will.
*
* @param array<string, array{rule: Exists|Unique, values: list<mixed>}> $groups
* @param array<string, array{rule: Exists|Unique, values: list<mixed>}> $groups
*/
private static function assertWithinCap(array $groups): void
{
Expand Down Expand Up @@ -508,8 +500,7 @@ private static function assertWithinCap(array $groups): void
* one rule type in the grouped rules (i.e. both an exists and a unique
* group exist for the same physical column).
*
* @param array<string, array{rule: Exists|Unique, values: list<mixed>}> $groups
*
* @param array<string, array{rule: Exists|Unique, values: list<mixed>}> $groups
* @return array<string, true>
*/
private static function findConflictingTableColumns(array $groups): array
Expand All @@ -523,7 +514,7 @@ private static function findConflictingTableColumns(array $groups): array
// Keyed by rule type AND query shape: the verifier looks up by
// table and column only, so anything that would have queried
// differently cannot share that lookup.
$seen[$table . ':' . $column][$ruleType . ':' . $shape] = true;
$seen[$table.':'.$column][$ruleType.':'.$shape] = true;
}

$conflicting = [];
Expand Down Expand Up @@ -593,11 +584,11 @@ private static function extractMeta(Exists|Unique $rule): ?array

return [
'connection' => $connection,
'table' => $table,
'column' => $column === 'NULL' ? '' : $column,
'wheres' => $typedWheres,
'ignore' => $ignore,
'idColumn' => $idColumn,
'table' => $table,
'column' => $column === 'NULL' ? '' : $column,
'wheres' => $typedWheres,
'ignore' => $ignore,
'idColumn' => $idColumn,
];
} catch (ReflectionException) {
return null;
Expand Down
4 changes: 2 additions & 2 deletions src/Builder/Concerns/HasEmbeddedRules.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

namespace Simtabi\Laranail\Validation\Builder\Concerns;

use Closure;
use BackedEnum;
use Illuminate\Validation\Rule;
use Closure;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Validation\Rule;

trait HasEmbeddedRules
{
Expand Down
Loading
Loading