yiisoft /
validator
| 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
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 |