AttributesRulesProvider   A
last analyzed

Complexity

Total Complexity 2

Size/Duplication

Total Lines 44
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 4
Bugs 0 Features 0
Metric Value
eloc 4
dl 0
loc 44
ccs 0
cts 0
cp 0
rs 10
c 4
b 0
f 0
wmc 2

2 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A getRules() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator\RulesProvider;
6
7
use ReflectionProperty;
8
use Yiisoft\Validator\DataSet\ObjectDataSet;
9
use Yiisoft\Validator\Helper\ObjectParser;
10
use Yiisoft\Validator\RulesProviderInterface;
11
use Yiisoft\Validator\ValidatorInterface;
12
13
/**
14
 * A rules provider that extracts rules from PHP attributes (attached to class properties and class itself). The
15
 * attributes introduced in PHP 8 simplify rules' configuration process, especially for nested data and relations. This
16
 * way the validated structures can be presented as DTO classes with references to each other.
17
 *
18
 * An example of object with both one-to-one (requires PHP > 8.0) and one-to-many (requires PHP > 8.1) relations:
19
 *
20
 * ```php
21 16
 * final class Post
22
 * {
23
 *     #[Length(max: 255)]
24
 *     public string $title = '';
25
 *
26
 *     #[Nested]
27
 *     public Author|null $author = null;
28
 *
29
 *     // Passing instances is available only since PHP 8.1.
30
 *     #[Each(new Nested(File::class))]
31
 *     public array $files = [];
32
 *
33
 *     public function __construct()
34
 *     {
35 16
 *         $this->author = new Author();
36
 *     }
37 16
 * }
38 16
 *
39
 * final class Author
40
 * {
41 16
 *     #[Length(min: 1)]
42
 *     public string $name = '';
43
 * }
44
 *
45
 * // Some rules, like "Nested" can be also configured through the class attribute.
46
 *
47 16
 * #[Nested(['url' => new Url()])]
48
 * final class File
49 16
 * {
50 7
 *     public string $url = '';
51 9
 * }
52
 *
53 16
 * $post = new Post(title: 'Yii3 Overview 3', author: 'Dmitriy');
54 16
 * $parser = new ObjectParser($post);
55 1
 * $rules = $parser->getRules();
56
 * ```
57 15
 *
58 15
 * The `$rules` will contain:
59 15
 *
60 1
 * ```php
61
 * [
62
 *     new Nested([
63 15
 *         'title' => [new Length(max: 255)],
64
 *         'author' => new Nested([
65
 *             'name' => [new Length(min: 1)],
66
 *         ]),
67
 *         'files' => new Each(new Nested([
68
 *             'url' => [new Url()],
69
 *         ])),
70 15
 *     ]);
71
 * ];
72 15
 * ```
73 15
 *
74
 * A class name string is valid as a source too:
75
 *
76
 * ```php
77
 * $parser = new ObjectParser(Post::class);
78
 * $rules = $parser->getRules(); // The result is the same as in previous example.
79
 * ```
80
 *
81
 * Please refer to the guide for more examples.
82
 *
83
 * Note that the rule attributes can be combined with others without affecting parsing. Which properties to parse can be
84
 * configured via `$propertyVisibility` and `$skipStaticProperties` constructor arguments.
85
 *
86
 * Uses {@see ObjectParser} and supports caching.
87
 *
88
 * If you want to additionally extract data or to be able to disable cache, use {@see ObjectDataSet} or
89
 * {@see ObjectParser} directly instead.
90
 *
91
 * @link https://www.php.net/manual/en/language.attributes.overview.php
92
 *
93
 * @psalm-import-type RawRulesMap from ValidatorInterface
94
 */
95
final class AttributesRulesProvider implements RulesProviderInterface
96
{
97
    /**
98
     * @var ObjectParser An object parser instance used to parse rules from attributes.
99
     */
100
    private ObjectParser $parser;
101
102
    /**
103
     * @param class-string|object $source A source for parsing rules. Can be either a class name string or an instance.
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string|object at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string|object.
Loading history...
104
     * @param int $propertyVisibility Visibility levels the properties with rules must have. For example: public and
105
     * protected only, this means that the rest (private ones) will be skipped. Defaults to all visibility levels
106
     * (public, protected and private).
107
     * @param bool $skipStaticProperties Whether the properties with "static" modifier must be skipped.
108
     *
109
     * @psalm-param int-mask-of<ReflectionProperty::IS_*> $propertyVisibility
110
     */
111
    public function __construct(
112
        string|object $source,
113
        int $propertyVisibility = ReflectionProperty::IS_PRIVATE
114
        | ReflectionProperty::IS_PROTECTED
115
        | ReflectionProperty::IS_PUBLIC,
116
        bool $skipStaticProperties = false,
117
    ) {
118
        $this->parser = new ObjectParser($source, $propertyVisibility, $skipStaticProperties);
119
    }
120
121
    /**
122
     * Returns rules parsed from attributes attached to class properties and class itself. Repetitive calls utilize
123
     * cache.
124
     *
125
     * @return iterable The resulting rules is an array with the following structure:
126
     *
127
     * ```php
128
     * [
129
     *     [new AtLeast(['name', 'author'])], // Rules not bound to a specific attribute.
130
     *     'files' => [new Count(max: 3)], // Attribute specific rules.
131
     * ],
132
     * ```
133
     *
134
     * @psalm-return RawRulesMap
135
     */
136
    public function getRules(): iterable
137
    {
138
        return $this->parser->getRules();
139
    }
140
}
141