Passed
Pull Request — master (#496)
by
unknown
02:41
created

StopOnError::dumpRulesAsArray()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 1
c 1
b 1
f 0
nc 1
nop 0
dl 0
loc 3
cc 1
ccs 0
cts 0
cp 0
crap 2
rs 10
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
 * A higher-order rule, applies to a set of rules, runs validation for each one of them in the order they are defined
23
 * and stops at the rule where validation failed. In particular, it can be useful for preventing the heavy operations to
24
 * increase performance. 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
     * @var RulesDumper|null A rules dumper instance used to dump {@see $rules} as array. Lazily created by
55
     * {@see getRulesDumper()} only when it's needed.
56 3
     */
57
    private ?RulesDumper $rulesDumper = null;
58 3
59
    /**
60
     * @param iterable $rules A set of rules for running the validation. Note that they are not normalized.
61 1
     * @psalm-param iterable<RuleInterface> $rules
62
     *
63
     * @param bool|callable|null $skipOnEmpty Whether to skip this `StopOnError` rule with all defined {@see $rules} if
64
     * the validated value is empty / not passed. See {@see SkipOnEmptyInterface}.
65
     * @param bool $skipOnError Whether to skip this `StopOnError` rule with all defined {@see $rules} if any of the
66
     * previous rules gave an error. See {@see SkipOnErrorInterface}.
67
     * @param Closure|null $when A callable to define a condition for applying this `StopOnError` rule with all defined
68
     * {@see $rules}. See {@see WhenInterface}.
69 1
     * @psalm-param WhenType $when
70 1
     */
71 1
    public function __construct(
72
        private iterable $rules,
73
        private mixed $skipOnEmpty = null,
74
        private bool $skipOnError = false,
75 3
        private Closure|null $when = null,
76
    ) {
77 3
    }
78
79
    public function getName(): string
80
    {
81
        return 'stopOnError';
82
    }
83
84
    /**
85
     * Gets a set of rules for running the validation.
86
     *
87
     * @return iterable A set of rules.
88
     *
89
     * @psalm-return iterable<RuleInterface>
90
     */
91
    public function getRules(): iterable
92
    {
93
        return $this->rules;
94
    }
95
96
    #[ArrayShape([
97
        'skipOnEmpty' => 'bool',
98
        'skipOnError' => 'bool',
99
        'rules' => 'array|null',
100
    ])]
101
    public function getOptions(): array
102
    {
103
        return [
104
            'skipOnEmpty' => $this->getSkipOnEmptyOption(),
105
            'skipOnError' => $this->skipOnError,
106
            'rules' => $this->dumpRulesAsArray(),
107
        ];
108
    }
109
110
    public function getHandler(): string
111
    {
112
        return StopOnErrorHandler::class;
113
    }
114
115
    public function afterInitAttribute(object $object, int $target): void
116
    {
117
        foreach ($this->rules as $rule) {
118
            if ($rule instanceof AfterInitAttributeEventInterface) {
119
                $rule->afterInitAttribute($object, $target);
120
            }
121
        }
122
    }
123
124
    /**
125
     * Dumps defined {@see $rules} to array.
126
     *
127
     * @return array The array of rules with their options.
128
     */
129
    protected function dumpRulesAsArray(): array
130
    {
131
        return $this->getRulesDumper()->asArray($this->getRules());
132
    }
133
134
    /**
135
     * Returns existing rules dumper instance for dumping defined {@see $rules} as array if it's already set. If not set
136
     * yet, creates the new instance first.
137
     *
138
     * @return RulesDumper A rules dumper instance.
139
     *
140
     * @see $rulesDumper
141
     */
142
    private function getRulesDumper(): RulesDumper
143
    {
144
        if ($this->rulesDumper === null) {
145
            $this->rulesDumper = new RulesDumper();
146
        }
147
148
        return $this->rulesDumper;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->rulesDumper could return the type null which is incompatible with the type-hinted return Yiisoft\Validator\Helper\RulesDumper. Consider adding an additional type-check to rule them out.
Loading history...
149
    }
150
}
151