Passed
Pull Request — master (#332)
by Dmitriy
03:02
created

ObjectDataSet::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 5
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 2
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator\DataSet;
6
7
use ReflectionAttribute;
8
use ReflectionObject;
9
use ReflectionProperty;
10
use Yiisoft\Validator\AttributeEventInterface;
11
use Yiisoft\Validator\DataSetInterface;
12
use Yiisoft\Validator\RuleInterface;
13
use Yiisoft\Validator\RulesProviderInterface;
14
15
use function array_key_exists;
16
17
/**
18
 * This data set makes use of attributes introduced in PHP 8. It simplifies rules configuration process, especially for
19
 * nested data and relations. Please refer to the guide for example.
20
 *
21
 * @link https://www.php.net/manual/en/language.attributes.overview.php
22
 */
23
final class ObjectDataSet implements RulesProviderInterface, DataSetInterface
24
{
25
    private bool $dataSetProvided;
26
27
    /**
28
     * @var ReflectionProperty[]
29
     */
30
    private array $reflectionProperties = [];
31
32
    private iterable $rules;
33
34 43
    public function __construct(
35
        private object $object,
36
        private int $propertyVisibility = ReflectionProperty::IS_PRIVATE|ReflectionProperty::IS_PROTECTED|ReflectionProperty::IS_PUBLIC
37
    ) {
38 43
        $this->parseObject();
39
    }
40
41 4
    public function getObject(): object
42
    {
43 4
        return $this->object;
44
    }
45
46 36
    public function getRules(): iterable
47
    {
48 36
        return $this->rules;
49
    }
50
51 29
    public function getAttributeValue(string $attribute): mixed
52
    {
53 29
        if ($this->dataSetProvided) {
54 2
            return $this->object->getAttributeValue($attribute);
55
        }
56
57 27
        return $this->hasAttribute($attribute)
58 27
            ? $this->reflectionProperties[$attribute]->getValue($this->object)
59 27
            : null;
60
    }
61
62 27
    public function hasAttribute(string $attribute): bool
63
    {
64 27
        return $this->dataSetProvided
65
            ? $this->object->hasAttribute($attribute)
66 27
            : array_key_exists($attribute, $this->reflectionProperties);
67
    }
68
69 7
    public function getData(): array
70
    {
71 7
        if ($this->dataSetProvided) {
72 2
            return $this->object->getData();
73
        }
74
75 5
        $data = [];
76 5
        foreach ($this->reflectionProperties as $name => $property) {
77 5
            $data[$name] = $property->getValue($this->object);
78
        }
79 5
        return $data;
80
    }
81
82
    // TODO: use Generator to collect attributes
83 43
    private function parseObject(): void
84
    {
85 43
        $objectHasRules = $this->object instanceof RulesProviderInterface;
86 43
        $this->rules = $objectHasRules ? $this->object->getRules() : [];
87
88 43
        $this->dataSetProvided = $this->object instanceof DataSetInterface;
89
        // Providing data set assumes object has its own attributes and rules getting logic. So further parsing of
90
        // Reflection properties and rules is intentionally skipped.
91 43
        if ($this->dataSetProvided) {
92 2
            return;
93
        }
94
95 41
        $reflection = new ReflectionObject($this->object);
96 41
        foreach ($reflection->getProperties($this->propertyVisibility) as $property) {
97 40
            if (PHP_VERSION_ID < 80100) {
98 40
                $property->setAccessible(true);
99
            }
100 40
            $this->reflectionProperties[$property->getName()] = $property;
101
102 40
            if ($objectHasRules === true) {
103 6
                continue;
104
            }
105
106 35
            $attributes = $property->getAttributes(RuleInterface::class, ReflectionAttribute::IS_INSTANCEOF);
107 35
            foreach ($attributes as $attribute) {
108 32
                $rule = $attribute->newInstance();
109 32
                $this->rules[$property->getName()][] = $rule;
110
111 32
                if ($rule instanceof AttributeEventInterface) {
112 4
                    $rule->afterInitAttribute($this);
113
                }
114
            }
115
        }
116
    }
117
}
118