Skip to content

Commit a844252

Browse files
committed
resolve_attribute(): Introduce cache
It's about 40% faster, measured using phpbench.
1 parent 929338c commit a844252

File tree

1 file changed

+61
-23
lines changed

1 file changed

+61
-23
lines changed

src/functions.php

Lines changed: 61 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
use IteratorIterator;
1111
use ReflectionAttribute;
1212
use ReflectionClass;
13+
use ReflectionMethod;
14+
use ReflectionProperty;
1315
use Traversable;
1416
use stdClass;
1517

@@ -156,46 +158,82 @@ function yield_groups(Traversable $traversable, callable $groupBy): Generator
156158
*/
157159
function resolve_attribute(string $attributeClass, object $object, mixed &...$args): void
158160
{
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+
}
164168

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+
];
167174

168-
if ($attr->flags & Attribute::TARGET_PROPERTY) {
169-
if (! $attrRef->implementsInterface(PropertyAttribute::class)) {
175+
if ($supportedFlags[0] && ! $attrRef->implementsInterface(PropertyAttribute::class)) {
170176
throw new InvalidArgumentException(sprintf(
171177
'Class %s does not implement %s',
172178
$attributeClass,
173179
PropertyAttribute::class
174180
));
175181
}
176182

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)) {
187184
throw new InvalidArgumentException(sprintf(
188185
'Class %s does not implement %s',
189186
$attributeClass,
190187
MethodAttribute::class
191188
));
192189
}
193190

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+
}
198218
}
199219
}
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+
}
200238
}
201239
}

0 commit comments

Comments
 (0)