AccessorGuesser::getMethodAccessor()   B
last analyzed

Complexity

Conditions 8
Paths 13

Size

Total Lines 34
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 8.125

Importance

Changes 0
Metric Value
cc 8
eloc 19
c 0
b 0
f 0
nc 13
nop 5
dl 0
loc 34
ccs 14
cts 16
cp 0.875
crap 8.125
rs 8.4444
1
<?php
2
3
namespace Bdf\Serializer\Util;
4
5
use Bdf\Serializer\PropertyAccessor\ClosureAccessor;
6
use Bdf\Serializer\PropertyAccessor\PropertyAccessorInterface;
7
use Bdf\Serializer\PropertyAccessor\DelegateAccessor;
8
use Bdf\Serializer\PropertyAccessor\PublicAccessor;
9
use Bdf\Serializer\PropertyAccessor\MethodAccessor;
10
use Bdf\Serializer\PropertyAccessor\ReflectionAccessor;
11
use Bdf\Serializer\PropertyAccessor\TypedPropertyAccessor;
12
use ReflectionClass;
13
14
/**
15
 * AccessorGuesser
16
 */
17
class AccessorGuesser
18
{
19
    /**
20
     * Allows closure accessor
21
     *
22
     * @var bool
23
     */
24
    private static $useClosure = false;
25
26
    /**
27
     * Guess which property accessor suit the property
28
     *
29
     * @param ReflectionClass $reflection
30
     * @param string $property
31
     * @param array $options
32
     *
33
     * @return PropertyAccessorInterface
34
     *
35
     * @deprecated Use getMethodAccessor or getPropertyAccessor instead of
36
     */
37
    public static function guessAccessor(ReflectionClass $reflection, string $property, array $options = null): PropertyAccessorInterface
38
    {
39
        // use reflection accessor if not set. Guess if property is public to use tue public accessor
40
        if ($options === null) {
41
            return self::getPropertyAccessor($reflection, $property);
42
        }
43
44
        return self::getMethodAccessor($reflection, $property, $options['reader'] ?? null, $options['writer'] ?? null, $options['readOnly'] ?? false);
45
    }
46
47
    /**
48
     * Guess which method accessor suit the property
49
     *
50
     * @param ReflectionClass $reflection
51
     * @param string $property
52
     * @param PropertyAccessorInterface|string|null $getter
53
     * @param PropertyAccessorInterface|string|null $setter
54
     *
55
     * @return PropertyAccessorInterface
56
     */
57 16
    public static function getMethodAccessor(ReflectionClass $reflection, $property, $getter, $setter, bool $readOnly = false): PropertyAccessorInterface
58
    {
59
        // If accessor is an array, it should have reader and writer keys.
60
        // The reader and the writer are methods, the method accessor will be used
61
        // Otherwise, the delegate accessor will be built with defined accessors.
62 16
        if (is_string($getter) && is_string($setter)) {
63 6
            return new MethodAccessor($reflection->name, $property, $getter, $setter);
64
        }
65
66 10
        if ($getter !== null) {
67 6
            if ($getter instanceof PropertyAccessorInterface) {
68
                $reader = $getter;
69
            } else {
70 6
                $reader = new MethodAccessor($reflection->name, $property, $getter);
71
            }
72
        } else {
73 4
            $reader = static::getPropertyAccessor($reflection, $property);
74
        }
75
76 10
        if ($readOnly) {
77 2
            return $reader;
78
        }
79
80 8
        if ($setter !== null) {
81 4
            if ($setter instanceof PropertyAccessorInterface) {
82
                $writter = $setter;
83
            } else {
84 4
                $writter = new MethodAccessor($reflection->name, $property, null, $setter);
85
            }
86
        } else {
87 4
            $writter = static::getPropertyAccessor($reflection, $property);
88
        }
89
90 8
        return new DelegateAccessor($reader, $writter);
91
    }
92
93
    /**
94
     * Guess which property accessor suit the property
95
     *
96
     * @param ReflectionClass $reflection
97
     * @param string $property
98
     *
99
     * @return PropertyAccessorInterface
100
     *
101
     * @throws \ReflectionException
102
     */
103 232
    public static function getPropertyAccessor(ReflectionClass $reflection, string $property): PropertyAccessorInterface
104
    {
105 232
        $reflection = self::getPropertyOwner($reflection, $property);
106
107
        // use reflection accessor if not set. Guess if property is public to use tue public accessor
108 232
        if ($reflection->getProperty($property)->isPublic()) {
109 66
            $propertyAccessor = new PublicAccessor($reflection->name, $property);
110 182
        } elseif (self::$useClosure) {
111
            $propertyAccessor = new ClosureAccessor($reflection->name, $property);
112
        } else {
113 182
            $propertyAccessor = new ReflectionAccessor($reflection->name, $property);
114
        }
115
116
        // In php >= 7.4 Use typed property reflection only if the property is typed to manage undefined state of the property
117 232
        if (PHP_VERSION_ID >= 70400 && $reflection->getProperty($property)->hasType()) {
118 24
            return new TypedPropertyAccessor($propertyAccessor, $reflection->name, $property);
119
        }
120
121 208
        return $propertyAccessor;
122
    }
123
124
    /**
125
     * Try to guess the setter method
126
     *
127
     * @param class-string $class
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
128
     * @param string $property
129
     *
130
     * @return null|string
131
     *
132
     * @todo Manage magic method __set
133
     */
134 6
    public static function guessSetter(string $class, string $property): ?string
135
    {
136 6
        $method = 'set'.ucfirst($property);
137
138 6
        if (method_exists($class, $method)) {
139 4
            return $method;
140
        }
141
142 2
        return null;
143
    }
144
145
    /**
146
     * Try to guess the getter method
147
     *
148
     * @param class-string $class
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
149
     * @param string $property
150
     *
151
     * @return null|string
152
     *
153
     * @todo Manage magic method __get
154
     */
155 6
    public static function guessGetter(string $class, string $property): ?string
156
    {
157 6
        if (method_exists($class, $property)) {
158
            return $property;
159
        }
160
161 6
        $method = 'get'.ucfirst($property);
162
163 6
        if (method_exists($class, $method)) {
164 4
            return $method;
165
        }
166
167 2
        return null;
168
    }
169
170
    /**
171
     * Enable/disable the closure accessor
172
     *
173
     * @param bool $flag
174
     */
175
    public static function useClosureAccessor($flag): void
176
    {
177
        self::$useClosure = $flag;
178
    }
179
180
    /**
181
     * Get the reflection that ownes the property
182
     *
183
     * @param ReflectionClass $reflection
184
     * @param string $property
185
     *
186
     * @return ReflectionClass
187
     *
188
     * @throws \LogicException if property does not belongs to this hierarchy
189
     */
190 232
    private static function getPropertyOwner($reflection, $property): ReflectionClass
191
    {
192
        do {
193 232
            if ($reflection->hasProperty($property)) {
194 232
                return $reflection;
195
            }
196 10
        } while ($reflection = $reflection->getParentClass());
197
198
        throw new \LogicException('No reflection found for property "'.$property.'"');
199
    }
200
}
201