respect-validation/library/Rules/KeySet.php
Henrique Moody 81befe8fa1
Move core rules into the "Core" namespace
That helps organize the code better, making it easier to spot the core
rule. It also helps not allow the Factory to load those rules, as the
new namespace is not registered in it.

Note that the "AbstractAge", "AbstractRelated", and "AbstractRule" were
not moved. I want to do that only when I refactor them.

After I moved classes, I realized that "Comparison" and "FilteredString"
had no tests. I created the tests, and while I did that, I spotted two
bugs:

* The "Equals" rule was failing when comparing non-scalar wth scalar
  values;

* The "Equals" and "Identical" rules were not working correctly because
  "Comparison" was converting their values.

I fixed those bugs in this commit.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-03-03 16:12:58 +01:00

78 lines
2.2 KiB
PHP

<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Helpers\CanBindEvaluateRule;
use Respect\Validation\Helpers\CanExtractRules;
use Respect\Validation\Message\Template;
use Respect\Validation\Result;
use Respect\Validation\Rules\Core\Wrapper;
use Respect\Validation\Validatable;
use function array_diff;
use function array_keys;
use function array_map;
use function array_merge;
use function array_values;
use function count;
#[Template(
'Must have keys {{missingKeys}} in {{name}}',
'Must not have keys {{missingKeys}} in {{name}}',
self::TEMPLATE_MISSING,
)]
#[Template(
'Must not have keys {{extraKeys}} in {{name}}',
'Must have keys {{extraKeys}} in {{name}}',
self::TEMPLATE_EXTRA,
)]
final class KeySet extends Wrapper
{
use CanBindEvaluateRule;
use CanExtractRules;
public const TEMPLATE_MISSING = '__missing__';
public const TEMPLATE_EXTRA = '__extra__';
/** @var array<string|int> */
private readonly array $keys;
public function __construct(Validatable $rule, Validatable ...$rules)
{
/** @var array<Key> $keyRules */
$keyRules = $this->extractMany(array_merge([$rule], $rules), Key::class);
$this->keys = array_map(static fn(Key $keyRule) => $keyRule->getReference(), $keyRules);
parent::__construct(count($keyRules) === 1 ? $keyRules[0] : new AllOf(...$keyRules));
}
public function evaluate(mixed $input): Result
{
$result = $this->bindEvaluate(new ArrayType(), $this, $input);
if (!$result->isValid) {
return $result;
}
$inputKeys = array_keys($input);
$missingKeys = array_diff($this->keys, $inputKeys);
if (count($missingKeys) > 0) {
return Result::failed($input, $this, ['missingKeys' => array_values($missingKeys)], self::TEMPLATE_MISSING);
}
$extraKeys = array_diff($inputKeys, $this->keys);
if (count($extraKeys) > 0) {
return Result::failed($input, $this, ['extraKeys' => array_values($extraKeys)], self::TEMPLATE_EXTRA);
}
return parent::evaluate($input);
}
}