Completed
Pull Request — master (#175)
by Alexander
02:53 queued 02:53
created

AttributeDataSet::getRules()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 5
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator\DataSet;
6
7
use InvalidArgumentException;
8
use ReflectionClass;
9
use ReflectionException;
10
use Yiisoft\Validator\Attribute\HasMany;
11
use Yiisoft\Validator\Attribute\HasOne;
12
use Yiisoft\Validator\Rule\Each;
13
use Yiisoft\Validator\Rule\Nested;
14
use Yiisoft\Validator\RuleInterface;
15
use Yiisoft\Validator\RulesProviderInterface;
16
17
use function in_array;
18
19
/**
20
 * This data set makes use of attributes introduced in PHP 8. It simplifies rules configuration process, especially for
21
 * nested data and relations. Please refer to the guide for example.
22
 *
23
 * @link https://www.php.net/manual/en/language.attributes.overview.php
24
 */
25
final class AttributeDataSet implements RulesProviderInterface
26
{
27
    use ArrayDataTrait;
28
29
    private object $baseAnnotatedObject;
30
31 1
    public function __construct(object $baseAnnotatedObject, array $data = [])
32
    {
33 1
        $this->baseAnnotatedObject = $baseAnnotatedObject;
34 1
        $this->data = $data;
35
    }
36
37 1
    public function getRules(): iterable
38
    {
39 1
        $classMeta = new ReflectionClass($this->baseAnnotatedObject);
40
41 1
        return $this->handleAttributes($classMeta);
42
    }
43
44 1
    private function handleAttributes(ReflectionClass $classMeta): array
45
    {
46 1
        $rules = [];
47 1
        foreach ($classMeta->getProperties() as $property) {
48 1
            if ($property->isStatic()) {
49
                continue;
50
            }
51
52 1
            foreach ([HasMany::class, HasOne::class] as $className) {
53 1
                $attributes = $property->getAttributes($className);
54 1
                if (!$attributes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $attributes of type ReflectionAttribute[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
55 1
                    continue;
56
                }
57
58 1
                $relatedClassName = $attributes[0]->getArguments()[0];
59
60
                try {
61 1
                    $relatedClassMeta = new ReflectionClass(new $relatedClassName());
62
                } catch (ReflectionException) {
63
                    throw new InvalidArgumentException("Class $relatedClassName not found.");
64
                }
65
66 1
                $nestedRule = new Nested(
67 1
                    $this->handleAttributes($relatedClassMeta),
68 1
                    ...(($property->getAttributes(Nested::class)[0] ?? null)?->getArguments() ?? [])
0 ignored issues
show
Bug introduced by
$property->getAttributes...tArguments() ?? array() is expanded, but the parameter $errorWhenPropertyPathIsNotFound of Yiisoft\Validator\Rule\Nested::__construct() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

68
                    /** @scrutinizer ignore-type */ ...(($property->getAttributes(Nested::class)[0] ?? null)?->getArguments() ?? [])
Loading history...
69
                );
70
71 1
                if ($className !== HasMany::class) {
72 1
                    $rules[$property->getName()] = $nestedRule;
73
                } else {
74
                    /** @psalm-suppress UndefinedMethod */
75 1
                    $rules[$property->getName()][] = new Each(
76 1
                        [$nestedRule],
77 1
                        ...(($property->getAttributes(Each::class)[0] ?? null)?->getArguments() ?? [])
0 ignored issues
show
Bug introduced by
$property->getAttributes...tArguments() ?? array() is expanded, but the parameter $incorrectInputMessage of Yiisoft\Validator\Rule\Each::__construct() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

77
                        /** @scrutinizer ignore-type */ ...(($property->getAttributes(Each::class)[0] ?? null)?->getArguments() ?? [])
Loading history...
78
                    );
79
                }
80
            }
81
82 1
            $flatRules = [];
83 1
            $attributes = $property->getAttributes();
84 1
            foreach ($attributes as $attribute) {
85 1
                if (!is_subclass_of($attribute->getName(), RuleInterface::class)) {
86 1
                    continue;
87
                }
88
89 1
                if (in_array($attribute->getName(), [Each::class, Nested::class])) {
90 1
                    continue;
91
                }
92
93 1
                $flatRules[] = $attribute->newInstance();
94
            }
95
96 1
            if (!$flatRules) {
97 1
                continue;
98
            }
99
100 1
            if ((string) $property->getType() !== 'array') {
101 1
                $rules[$property->getName()] = $flatRules;
102
103 1
                continue;
104
            }
105
106
            /** @psalm-suppress UndefinedMethod */
107 1
            $rules[$property->getName()][] = new Each(
108
                $flatRules,
109 1
                ...(($property->getAttributes(Each::class)[0] ?? null)?->getArguments() ?? [])
110
            );
111
        }
112
113 1
        return $rules;
114
    }
115
}
116