|
10 | 10 | use IteratorIterator; |
11 | 11 | use ReflectionAttribute; |
12 | 12 | use ReflectionClass; |
| 13 | +use ReflectionMethod; |
| 14 | +use ReflectionProperty; |
13 | 15 | use Traversable; |
14 | 16 | use stdClass; |
15 | 17 |
|
@@ -156,46 +158,82 @@ function yield_groups(Traversable $traversable, callable $groupBy): Generator |
156 | 158 | */ |
157 | 159 | function resolve_attribute(string $attributeClass, object $object, mixed &...$args): void |
158 | 160 | { |
159 | | - $attrRef = new ReflectionClass($attributeClass); |
160 | | - $attrAttributes = $attrRef->getAttributes(Attribute::class); |
161 | | - if (empty($attrAttributes)) { |
162 | | - throw new InvalidArgumentException(sprintf('Class %s is not an attribute', $attributeClass)); |
163 | | - } |
| 161 | + static $cache = []; |
| 162 | + if (! isset($cache[$attributeClass])) { |
| 163 | + $attrRef = new ReflectionClass($attributeClass); |
| 164 | + $attrAttributes = $attrRef->getAttributes(Attribute::class); |
| 165 | + if (empty($attrAttributes)) { |
| 166 | + throw new InvalidArgumentException(sprintf('Class %s is not an attribute', $attributeClass)); |
| 167 | + } |
164 | 168 |
|
165 | | - $attr = $attrAttributes[0]->newInstance(); |
166 | | - $objectRef = new ReflectionClass($object); |
| 169 | + $attr = $attrAttributes[0]->newInstance(); |
| 170 | + $supportedFlags = [ |
| 171 | + $attr->flags & Attribute::TARGET_PROPERTY, |
| 172 | + $attr->flags & Attribute::TARGET_METHOD |
| 173 | + ]; |
167 | 174 |
|
168 | | - if ($attr->flags & Attribute::TARGET_PROPERTY) { |
169 | | - if (! $attrRef->implementsInterface(PropertyAttribute::class)) { |
| 175 | + if ($supportedFlags[0] && ! $attrRef->implementsInterface(PropertyAttribute::class)) { |
170 | 176 | throw new InvalidArgumentException(sprintf( |
171 | 177 | 'Class %s does not implement %s', |
172 | 178 | $attributeClass, |
173 | 179 | PropertyAttribute::class |
174 | 180 | )); |
175 | 181 | } |
176 | 182 |
|
177 | | - foreach ($objectRef->getProperties() as $property) { |
178 | | - $attributes = $property->getAttributes($attributeClass, ReflectionAttribute::IS_INSTANCEOF); |
179 | | - foreach ($attributes as $attribute) { |
180 | | - $attribute->newInstance()->applyToProperty($property, $object, ...$args); |
181 | | - } |
182 | | - } |
183 | | - } |
184 | | - |
185 | | - if ($attr->flags & Attribute::TARGET_METHOD) { |
186 | | - if (! $attrRef->implementsInterface(MethodAttribute::class)) { |
| 183 | + if ($supportedFlags[1] && ! $attrRef->implementsInterface(MethodAttribute::class)) { |
187 | 184 | throw new InvalidArgumentException(sprintf( |
188 | 185 | 'Class %s does not implement %s', |
189 | 186 | $attributeClass, |
190 | 187 | MethodAttribute::class |
191 | 188 | )); |
192 | 189 | } |
193 | 190 |
|
194 | | - foreach ($objectRef->getMethods() as $method) { |
195 | | - $attributes = $method->getAttributes($attributeClass, ReflectionAttribute::IS_INSTANCEOF); |
196 | | - foreach ($attributes as $attribute) { |
197 | | - $attribute->newInstance()->applyToMethod($method, $object, ...$args); |
| 191 | + $cache[$attributeClass] = $supportedFlags; |
| 192 | + } else { |
| 193 | + $supportedFlags = $cache[$attributeClass]; |
| 194 | + } |
| 195 | + |
| 196 | + if (! isset($cache[$object::class])) { |
| 197 | + $objectRef = new ReflectionClass($object); |
| 198 | + $annotations = [ |
| 199 | + 'properties' => [], |
| 200 | + 'methods' => [] |
| 201 | + ]; |
| 202 | + |
| 203 | + if ($supportedFlags[0]) { |
| 204 | + foreach ($objectRef->getProperties() as $property) { |
| 205 | + $attributes = $property->getAttributes($attributeClass, ReflectionAttribute::IS_INSTANCEOF); |
| 206 | + foreach ($attributes as $attribute) { |
| 207 | + $annotations['properties'][$property->getName()][] = $attribute->newInstance(); |
| 208 | + } |
| 209 | + } |
| 210 | + } |
| 211 | + |
| 212 | + if ($supportedFlags[1]) { |
| 213 | + foreach ($objectRef->getMethods() as $method) { |
| 214 | + $attributes = $method->getAttributes($attributeClass, ReflectionAttribute::IS_INSTANCEOF); |
| 215 | + foreach ($attributes as $attribute) { |
| 216 | + $annotations['methods'][$method->getName()][] = $attribute->newInstance(); |
| 217 | + } |
198 | 218 | } |
199 | 219 | } |
| 220 | + |
| 221 | + $cache[$object::class] = $annotations; |
| 222 | + } else { |
| 223 | + $annotations = $cache[$object::class]; |
| 224 | + } |
| 225 | + |
| 226 | + foreach ($annotations['properties'] as $name => $attributes) { |
| 227 | + $property = new ReflectionProperty($object, $name); |
| 228 | + foreach ($attributes as $attribute) { |
| 229 | + $attribute->applyToProperty($property, $object, ...$args); |
| 230 | + } |
| 231 | + } |
| 232 | + |
| 233 | + foreach ($annotations['methods'] as $name => $attributes) { |
| 234 | + $method = new ReflectionMethod($object, $name); |
| 235 | + foreach ($attributes as $attribute) { |
| 236 | + $attribute->applyToMethod($method, $object, ...$args); |
| 237 | + } |
200 | 238 | } |
201 | 239 | } |
0 commit comments