Passed
Pull Request — master (#502)
by Sergei
02:32
created

StopOnError::getRulesDumper()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 7
rs 10
c 0
b 0
f 0
ccs 0
cts 0
cp 0
crap 6
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator\Rule;
6
7
use Attribute;
8
use Closure;
9
use JetBrains\PhpStorm\ArrayShape;
10
use Yiisoft\Validator\AfterInitAttributeEventInterface;
11
use Yiisoft\Validator\Rule\Trait\SkipOnEmptyTrait;
12
use Yiisoft\Validator\Rule\Trait\SkipOnErrorTrait;
13
use Yiisoft\Validator\Rule\Trait\WhenTrait;
14
use Yiisoft\Validator\RuleInterface;
15
use Yiisoft\Validator\Helper\RulesDumper;
16
use Yiisoft\Validator\RuleWithOptionsInterface;
17
use Yiisoft\Validator\SkipOnEmptyInterface;
18
use Yiisoft\Validator\SkipOnErrorInterface;
19
use Yiisoft\Validator\WhenInterface;
20
21
/**
22
 * Applies to a set of rules, runs validation for each one of them in the order they are defined and stops at the rule
23
 * where validation failed. In particular, it can be useful for preventing the heavy operations to increase performance.
24
 * It can be set like that for a group of ordered rules:
25
 *
26
 * ```php
27
 * $rule = new StopOnError([
28
 *      new HasLength(min: 3),
29
 *      // This operation executes DB query and thus heavier. It's preferable not to call it if the previous rule did
30
 *      not pass the validation.
31 3
 *      new ExistsInDatabase(),
32
 * ]);
33
 * ```
34
 *
35
 * Not to be confused with skipping, there is a separate functionality for that, see {@see SkipOnErrorInterface}.
36
 *
37
 * @see StopOnErrorHandler Corresponding handler performing the actual validation.
38
 *
39
 * @psalm-import-type WhenType from WhenInterface
40
 */
41
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]
42
final class StopOnError implements
43
    RuleWithOptionsInterface,
44
    SkipOnErrorInterface,
45
    WhenInterface,
46
    SkipOnEmptyInterface,
47
    AfterInitAttributeEventInterface
48 1
{
49
    use SkipOnEmptyTrait;
50 1
    use SkipOnErrorTrait;
51
    use WhenTrait;
52
53
    /**
54
     * @param iterable $rules A set of rules for running the validation. Note that they are not normalized.
55
     * @psalm-param iterable<RuleInterface> $rules
56 3
     *
57
     * @param bool|callable|null $skipOnEmpty Whether to skip this `StopOnError` rule with all defined {@see $rules} if
58 3
     * the validated value is empty / not passed. See {@see SkipOnEmptyInterface}.
59
     * @param bool $skipOnError Whether to skip this `StopOnError` rule with all defined {@see $rules} if any of the
60
     * previous rules gave an error. See {@see SkipOnErrorInterface}.
61 1
     * @param Closure|null $when A callable to define a condition for applying this `StopOnError` rule with all defined
62
     * {@see $rules}. See {@see WhenInterface}.
63
     * @psalm-param WhenType $when
64
     */
65
    public function __construct(
66
        private iterable $rules,
67
        private mixed $skipOnEmpty = null,
68
        private bool $skipOnError = false,
69 1
        private Closure|null $when = null,
70 1
    ) {
71 1
    }
72
73
    public function getName(): string
74
    {
75 3
        return 'stopOnError';
76
    }
77 3
78
    /**
79
     * Gets a set of rules for running the validation.
80
     *
81
     * @return iterable A set of rules.
82
     *
83
     * @psalm-return iterable<RuleInterface>
84
     */
85
    public function getRules(): iterable
86
    {
87
        return $this->rules;
88
    }
89
90
    #[ArrayShape([
91
        'skipOnEmpty' => 'bool',
92
        'skipOnError' => 'bool',
93
        'rules' => 'array|null',
94
    ])]
95
    public function getOptions(): array
96
    {
97
        return [
98
            'skipOnEmpty' => $this->getSkipOnEmptyOption(),
99
            'skipOnError' => $this->skipOnError,
100
            'rules' => $this->dumpRulesAsArray(),
101
        ];
102
    }
103
104
    public function getHandler(): string
105
    {
106
        return StopOnErrorHandler::class;
107
    }
108
109
    public function afterInitAttribute(object $object, int $target): void
110
    {
111
        foreach ($this->rules as $rule) {
112
            if ($rule instanceof AfterInitAttributeEventInterface) {
113
                $rule->afterInitAttribute($object, $target);
114
            }
115
        }
116
    }
117
118
    /**
119
     * Dumps defined {@see $rules} to array.
120
     *
121
     * @return array The array of rules with their options.
122
     */
123
    private function dumpRulesAsArray(): array
124
    {
125
        return RulesDumper::asArray($this->getRules());
126
    }
127
}
128