Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
70.00% covered (warning)
70.00%
21 / 30
50.00% covered (danger)
50.00%
3 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
FieldHydrator
70.00% covered (warning)
70.00%
21 / 30
50.00% covered (danger)
50.00%
3 / 6
15.89
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hydrate
81.25% covered (warning)
81.25%
13 / 16
0.00% covered (danger)
0.00%
0 / 1
6.24
 getField
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getFields
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 schemaRegistry
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 initField
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace Cosray\Field;
6
7use Cosray\Field\Schema\Registry;
8use Cosray\Value\ValueContext;
9use ReflectionClass;
10use ReflectionProperty;
11use ReflectionUnionType;
12
13class FieldHydrator
14{
15    public function __construct(
16        private readonly Registry $schemaRegistry = new Registry(),
17    ) {}
18
19    /**
20     * Scan $target for Field-typed properties, instantiate each Field with
21     * the given FieldOwner and content data, then set them on the target.
22     *
23     * @return string[] Discovered field names
24     */
25    public function hydrate(object $target, array $content, Owner $owner): array
26    {
27        $fieldNames = [];
28        $rc = new ReflectionClass($target);
29
30        foreach ($rc->getProperties() as $property) {
31            $name = $property->getName();
32
33            if (!$property->hasType()) {
34                continue;
35            }
36
37            $type = $property->getType();
38
39            if ($type::class === ReflectionUnionType::class) {
40                continue;
41            }
42
43            $typeName = $type->getName();
44
45            if (is_subclass_of($typeName, Field::class)) {
46                if ($property->isInitialized($target)) {
47                    continue;
48                }
49
50                $property->setValue($target, $this->initField($property, $typeName, $content, $owner));
51                $fieldNames[] = $name;
52            }
53        }
54
55        return $fieldNames;
56    }
57
58    public function getField(object $target, string $name): Field
59    {
60        $rc = new ReflectionClass($target);
61
62        return $rc->getProperty($name)->getValue($target);
63    }
64
65    /**
66     * @return Field[]
67     */
68    public function getFields(object $target, array $fieldNames): array
69    {
70        $rc = new ReflectionClass($target);
71        $fields = [];
72
73        foreach ($fieldNames as $name) {
74            $fields[$name] = $rc->getProperty($name)->getValue($target);
75        }
76
77        return $fields;
78    }
79
80    public function schemaRegistry(): Registry
81    {
82        return $this->schemaRegistry;
83    }
84
85    protected function initField(
86        ReflectionProperty $property,
87        string $fieldType,
88        array $content,
89        Owner $owner,
90    ): Field {
91        $fieldName = $property->getName();
92        $data = $content[$fieldName] ?? [];
93        $field = new $fieldType($fieldName, $owner, new ValueContext($fieldName, $data));
94
95        $field->initSchema($property, $this->schemaRegistry);
96
97        return $field;
98    }
99}