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
![]() |
|||
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
|
|||
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 |