Completed
Push — master ( 407897...338945 )
by Jesse
05:08
created

ObjectHydrator::default()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
eloc 2
nc 1
nop 0
cc 1
1
<?php
2
declare(strict_types=1);
3
4
namespace Stratadox\Hydrator;
5
6
use Closure;
7
use ReflectionObject;
8
use Throwable;
9
10
/**
11
 * Hydrates an object from array input.
12
 *
13
 * @package Stratadox\Hydrate
14
 * @author  Stratadox
15
 */
16
final class ObjectHydrator implements Hydrates
17
{
18
    private $setter;
19
20
    private function __construct(
21
        ?Closure $setter
22
    ) {
23
        $this->setter = $setter ?: function (string $attribute, $value): void {
24
            $this->$attribute = $value;
25
        };
26
    }
27
28
    /**
29
     * Produces an object hydrator with a default setter.
30
     *
31
     * Faster than a reflective object hydrator, but limited to properties that
32
     * can be accessed by the instance itself. That means this method cannot be
33
     * used in the context of inheritance when the parent class has private
34
     * properties.
35
     *
36
     * @return Hydrates A hydrator that uses closure binding to write properties.
37
     */
38
    public static function default(): Hydrates
39
    {
40
        return new self(null);
41
    }
42
43
    /**
44
     * Produces an object hydrator with a reflection setter.
45
     *
46
     * Slower than the default setter, but useful in the context of inheritance,
47
     * when some of the properties are private to the parent class and therefore
48
     * inaccessible through simple closure binding.
49
     *
50
     * @return Hydrates A hydrator that uses reflection to write properties.
51
     */
52
    public static function reflective(): Hydrates
53
    {
54
        return new self((new class {
55
            public function setter(): Closure
56
            {
57
                return function (string $name, $value): void {
58
                    $object = new ReflectionObject($this);
59
                    while ($object && !$object->hasProperty($name)) {
60
                        $object = $object->getParentClass();
61
                    }
62
                    // @todo if !object, write as public?
63
                    $property = $object->getProperty($name);
64
                    $property->setAccessible(true);
65
                    $property->setValue($this, $value);
66
                };
67
            }
68
        })->setter());
69
    }
70
71
    /**
72
     * Produces an object hydrator with a custom setter.
73
     *
74
     * @param Closure   $setter The closure that writes the values.
75
     * @return Hydrates         A hydrator that uses a custom closure to write
76
     *                          properties.
77
     */
78
    public static function using(
79
        Closure $setter
80
    ): Hydrates {
81
        return new self($setter);
82
    }
83
84
    /** @inheritdoc */
85
    public function writeTo(object $target, array $data): object
86
    {
87
        foreach ($data as $attribute => $value) {
88
            try {
89
                $this->setter->call($target, $attribute, $value);
90
            } catch (Throwable $exception) {
91
                throw HydrationFailed::encountered($exception, $target);
92
            }
93
        }
94
        return $target;
95
    }
96
}
97