Skip to content

Commit 203f4cd

Browse files
committed
ai performance improvements
1 parent 8b79f21 commit 203f4cd

8 files changed

Lines changed: 202 additions & 77 deletions

File tree

src/Extension/Cryptography/CryptographyMetadataEnricher.php

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@ final class CryptographyMetadataEnricher implements MetadataEnricher
1717
public function enrich(ClassMetadata $classMetadata): void
1818
{
1919
$subjectIdMapping = [];
20+
$subjectIdProperties = [];
21+
$sensitiveProperties = [];
2022

2123
foreach ($classMetadata->properties as $property) {
2224
$isSubjectId = false;
2325
$attributeReflectionList = $property->reflection->getAttributes(DataSubjectId::class);
2426

25-
if ($attributeReflectionList) {
27+
if ($attributeReflectionList !== []) {
2628
$subjectIdIdentifier = $attributeReflectionList[0]->newInstance()->name;
2729

2830
if (array_key_exists($subjectIdIdentifier, $subjectIdMapping)) {
@@ -35,6 +37,7 @@ public function enrich(ClassMetadata $classMetadata): void
3537
}
3638

3739
$subjectIdMapping[$subjectIdIdentifier] = $property->fieldName;
40+
$subjectIdProperties[$subjectIdIdentifier] = $property;
3841

3942
$isSubjectId = true;
4043
}
@@ -50,13 +53,17 @@ public function enrich(ClassMetadata $classMetadata): void
5053
}
5154

5255
$property->extras[SensitiveDataInfo::class] = $sensitiveDataInfo;
56+
$sensitiveProperties[] = $property;
5357
}
5458

55-
if ($subjectIdMapping === []) {
56-
return;
59+
if ($sensitiveProperties !== []) {
60+
$classMetadata->extras[SensitiveDataInfo::class . '::properties'] = $sensitiveProperties;
5761
}
5862

59-
$classMetadata->extras[SubjectIdFieldMapping::class] = new SubjectIdFieldMapping($subjectIdMapping);
63+
if ($subjectIdMapping !== []) {
64+
$classMetadata->extras[SubjectIdFieldMapping::class] = new SubjectIdFieldMapping($subjectIdMapping);
65+
$classMetadata->extras[SubjectIdFieldMapping::class . '::properties'] = $subjectIdProperties;
66+
}
6067
}
6168

6269
private function sensitiveDataInfo(ReflectionProperty $reflectionProperty): SensitiveDataInfo|null

src/Extension/Cryptography/CryptographyMiddleware.php

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,39 +36,48 @@ public function __construct(
3636
*/
3737
public function hydrate(ClassMetadata $metadata, array $data, array $context, Stack $stack): object
3838
{
39+
/** @var list<PropertyMetadata>|null $properties */
40+
$properties = $metadata->extras[SensitiveDataInfo::class . '::properties'] ?? null;
41+
42+
if ($properties === null) {
43+
return $stack->next()->hydrate($metadata, $data, $context, $stack);
44+
}
45+
3946
$context[SubjectIds::class] = $subjectIds = $this->resolveSubjectIds($metadata, $data, $context);
47+
$cryptographer = $this->cryptographer;
4048

41-
foreach ($metadata->properties as $propertyMetadata) {
42-
$info = $propertyMetadata->extras[SensitiveDataInfo::class] ?? null;
49+
foreach ($properties as $propertyMetadata) {
50+
$fieldName = $propertyMetadata->fieldName;
4351

44-
if (!$info instanceof SensitiveDataInfo) {
52+
if (!isset($data[$fieldName])) {
4553
continue;
4654
}
4755

48-
$value = $data[$propertyMetadata->fieldName] ?? null;
56+
$value = $data[$fieldName];
4957

50-
if ($value === null) {
58+
if (!$cryptographer->supports($value)) {
5159
continue;
5260
}
5361

54-
if (!$this->cryptographer->supports($value)) {
55-
continue;
56-
}
62+
$info = $propertyMetadata->extras[SensitiveDataInfo::class];
63+
assert($info instanceof SensitiveDataInfo);
5764

5865
$subjectId = $subjectIds->get($info->subjectIdName);
5966

6067
try {
61-
$data[$propertyMetadata->fieldName] = $this->cryptographer->decrypt($subjectId, $value);
68+
$data[$fieldName] = $cryptographer->decrypt($subjectId, $value);
6269
} catch (DecryptionFailed | CipherKeyNotExists) {
6370
$fallback = $info->fallback instanceof Closure
6471
? ($info->fallback)($subjectId)
6572
: $info->fallback;
6673

67-
if ($propertyMetadata->normalizer) {
68-
$fallback = $propertyMetadata->normalizer->normalize($fallback, $context);
74+
$normalizer = $propertyMetadata->normalizer;
75+
76+
if ($normalizer !== null) {
77+
$fallback = $normalizer->normalize($fallback, $context);
6978
}
7079

71-
$data[$propertyMetadata->fieldName] = $fallback;
80+
$data[$fieldName] = $fallback;
7281
}
7382
}
7483

src/Metadata/ClassMetadata.php

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,13 @@ final class ClassMetadata
2222
public readonly string $className;
2323

2424
/** @var array<string, PropertyMetadata> */
25-
public readonly array $properties;
25+
public array $properties;
26+
27+
/** @var list<PropertyMetadata> */
28+
public array $propertiesWithNormalizer;
29+
30+
/** @var list<PropertyMetadata> */
31+
public array $propertiesWithoutNormalizer;
2632

2733
/** @var array<string, ReflectionParameter>|null */
2834
private array|null $promotedConstructorDefaults = null;
@@ -40,13 +46,29 @@ public function __construct(
4046
) {
4147
$this->className = $reflection->getName();
4248

49+
$this->updateProperties($properties);
50+
}
51+
52+
/** @param list<PropertyMetadata> $properties */
53+
public function updateProperties(array $properties): void
54+
{
4355
$map = [];
56+
$withNormalizer = [];
57+
$withoutNormalizer = [];
4458

4559
foreach ($properties as $property) {
4660
$map[$property->propertyName] = $property;
61+
62+
if ($property->normalizer !== null) {
63+
$withNormalizer[] = $property;
64+
} else {
65+
$withoutNormalizer[] = $property;
66+
}
4767
}
4868

4969
$this->properties = $map;
70+
$this->propertiesWithNormalizer = $withNormalizer;
71+
$this->propertiesWithoutNormalizer = $withoutNormalizer;
5072
}
5173

5274
public function propertyForField(string $name): PropertyMetadata
@@ -97,7 +119,7 @@ public function __serialize(): array
97119
{
98120
return [
99121
'className' => $this->className,
100-
'properties' => $this->properties,
122+
'properties' => array_values($this->properties),
101123
'lazy' => $this->lazy,
102124
'extras' => $this->extras,
103125
];
@@ -107,7 +129,25 @@ public function __serialize(): array
107129
public function __unserialize(array $data): void
108130
{
109131
$this->reflection = new ReflectionClass($data['className']);
110-
$this->properties = $data['properties'];
132+
133+
$map = [];
134+
$withNormalizer = [];
135+
$withoutNormalizer = [];
136+
137+
foreach ($data['properties'] as $property) {
138+
$map[$property->propertyName] = $property;
139+
140+
if ($property->normalizer !== null) {
141+
$withNormalizer[] = $property;
142+
} else {
143+
$withoutNormalizer[] = $property;
144+
}
145+
}
146+
147+
$this->className = $data['className'];
148+
$this->properties = $map;
149+
$this->propertiesWithNormalizer = $withNormalizer;
150+
$this->propertiesWithoutNormalizer = $withoutNormalizer;
111151
$this->lazy = $data['lazy'];
112152
$this->extras = $data['extras'];
113153
}

src/Metadata/EnrichingMetadataFactory.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,14 @@ public function metadata(string $class): ClassMetadata
1717
{
1818
$metadata = $this->factory->metadata($class);
1919

20+
$enriched = false;
2021
foreach ($this->enrichers as $enricher) {
2122
$enricher->enrich($metadata);
23+
$enriched = true;
24+
}
25+
26+
if ($enriched) {
27+
$metadata->updateProperties(array_values($metadata->properties));
2228
}
2329

2430
return $metadata;

src/Middleware/TransformMiddleware.php

Lines changed: 81 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -34,53 +34,88 @@ public function hydrate(ClassMetadata $metadata, array $data, array $context, St
3434
{
3535
$object = $metadata->newInstance();
3636

37-
$constructorParameters = null;
37+
foreach ($metadata->propertiesWithoutNormalizer as $propertyMetadata) {
38+
$fieldName = $propertyMetadata->fieldName;
3839

39-
foreach ($metadata->properties as $propertyMetadata) {
40-
if (!array_key_exists($propertyMetadata->fieldName, $data)) {
40+
if (!isset($data[$fieldName]) && !array_key_exists($fieldName, $data)) {
4141
if (!$propertyMetadata->reflection->isPromoted()) {
42-
continue;
42+
goto next_without_normalizer;
4343
}
4444

45-
$constructorParameters ??= $metadata->promotedConstructorDefaults();
45+
$constructorParameters = $metadata->promotedConstructorDefaults();
4646

47-
if (!array_key_exists($propertyMetadata->propertyName, $constructorParameters)) {
48-
continue;
47+
if (!isset($constructorParameters[$propertyMetadata->propertyName])) {
48+
goto next_without_normalizer;
4949
}
5050

51-
$propertyMetadata->setValue(
51+
$propertyMetadata->reflection->setValue(
5252
$object,
5353
$constructorParameters[$propertyMetadata->propertyName]->getDefaultValue(),
5454
);
5555

56-
continue;
56+
goto next_without_normalizer;
5757
}
5858

59-
if ($propertyMetadata->normalizer) {
60-
try {
61-
/** @psalm-suppress MixedAssignment */
62-
$value = $propertyMetadata->normalizer->denormalize($data[$propertyMetadata->fieldName], $context);
63-
} catch (Throwable $e) {
64-
throw new DenormalizationFailure(
65-
$metadata->className,
66-
$propertyMetadata->propertyName,
67-
$propertyMetadata->normalizer::class,
68-
$e,
69-
);
59+
try {
60+
$propertyMetadata->reflection->setValue($object, $data[$fieldName]);
61+
} catch (TypeError $e) {
62+
throw new TypeMismatch(
63+
$metadata->className,
64+
$propertyMetadata->propertyName,
65+
$e,
66+
);
67+
}
68+
69+
next_without_normalizer:
70+
}
71+
72+
foreach ($metadata->propertiesWithNormalizer as $propertyMetadata) {
73+
$fieldName = $propertyMetadata->fieldName;
74+
75+
if (!isset($data[$fieldName]) && !array_key_exists($fieldName, $data)) {
76+
if (!$propertyMetadata->reflection->isPromoted()) {
77+
goto next_with_normalizer;
78+
}
79+
80+
$constructorParameters = $metadata->promotedConstructorDefaults();
81+
82+
if (!isset($constructorParameters[$propertyMetadata->propertyName])) {
83+
goto next_with_normalizer;
7084
}
71-
} else {
72-
$value = $data[$propertyMetadata->fieldName];
85+
86+
$propertyMetadata->reflection->setValue(
87+
$object,
88+
$constructorParameters[$propertyMetadata->propertyName]->getDefaultValue(),
89+
);
90+
91+
goto next_with_normalizer;
92+
}
93+
94+
$normalizer = $propertyMetadata->normalizer;
95+
96+
try {
97+
/** @psalm-suppress MixedAssignment */
98+
$value = $normalizer->denormalize($data[$fieldName], $context);
99+
} catch (Throwable $e) {
100+
throw new DenormalizationFailure(
101+
$metadata->className,
102+
$propertyMetadata->propertyName,
103+
$normalizer::class,
104+
$e,
105+
);
73106
}
74107

75108
try {
76-
$propertyMetadata->setValue($object, $value);
109+
$propertyMetadata->reflection->setValue($object, $value);
77110
} catch (TypeError $e) {
78111
throw new TypeMismatch(
79112
$metadata->className,
80113
$propertyMetadata->propertyName,
81114
$e,
82115
);
83116
}
117+
118+
next_with_normalizer:
84119
}
85120

86121
return $object;
@@ -95,7 +130,7 @@ public function extract(ClassMetadata $metadata, object $object, array $context,
95130
{
96131
$objectId = spl_object_id($object);
97132

98-
if (array_key_exists($objectId, $this->callStack)) {
133+
if (isset($this->callStack[$objectId])) {
99134
$references = array_values($this->callStack);
100135
$references[] = $object::class;
101136

@@ -107,26 +142,30 @@ public function extract(ClassMetadata $metadata, object $object, array $context,
107142
try {
108143
$data = [];
109144

110-
foreach ($metadata->properties as $propertyMetadata) {
111-
if ($propertyMetadata->normalizer) {
112-
try {
113-
/** @psalm-suppress MixedAssignment */
114-
$data[$propertyMetadata->fieldName] = $propertyMetadata->normalizer->normalize(
115-
$propertyMetadata->getValue($object),
116-
$context,
117-
);
118-
} catch (CircularReference $e) {
145+
foreach ($metadata->propertiesWithoutNormalizer as $propertyMetadata) {
146+
$data[$propertyMetadata->fieldName] = $propertyMetadata->reflection->getValue($object);
147+
}
148+
149+
foreach ($metadata->propertiesWithNormalizer as $propertyMetadata) {
150+
$normalizer = $propertyMetadata->normalizer;
151+
152+
try {
153+
/** @psalm-suppress MixedAssignment */
154+
$data[$propertyMetadata->fieldName] = $normalizer->normalize(
155+
$propertyMetadata->reflection->getValue($object),
156+
$context,
157+
);
158+
} catch (Throwable $e) {
159+
if ($e instanceof CircularReference) {
119160
throw $e;
120-
} catch (Throwable $e) {
121-
throw new NormalizationFailure(
122-
$object::class,
123-
$propertyMetadata->propertyName,
124-
$propertyMetadata->normalizer::class,
125-
$e,
126-
);
127161
}
128-
} else {
129-
$data[$propertyMetadata->fieldName] = $propertyMetadata->getValue($object);
162+
163+
throw new NormalizationFailure(
164+
$object::class,
165+
$propertyMetadata->propertyName,
166+
$normalizer::class,
167+
$e,
168+
);
130169
}
131170
}
132171
} finally {

src/Normalizer/ArrayNormalizer.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,10 @@ public function normalize(mixed $value, array $context): array|null
3535
throw InvalidArgument::withWrongType('array|null', $value);
3636
}
3737

38+
$normalizer = $this->normalizer;
39+
3840
foreach ($value as &$item) {
39-
$item = $this->normalizer->normalize($item, $context);
41+
$item = $normalizer->normalize($item, $context);
4042
}
4143

4244
return $value;
@@ -57,8 +59,10 @@ public function denormalize(mixed $value, array $context): array|null
5759
throw InvalidArgument::withWrongType('array|null', $value);
5860
}
5961

62+
$normalizer = $this->normalizer;
63+
6064
foreach ($value as &$item) {
61-
$item = $this->normalizer->denormalize($item, $context);
65+
$item = $normalizer->denormalize($item, $context);
6266
}
6367

6468
return $value;

0 commit comments

Comments
 (0)