Skip to content

Commit 3d8e6a8

Browse files
committed
feat(identifier): use a hash system to avoid double loop
1 parent 1bfbcec commit 3d8e6a8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+346
-360
lines changed

src/Attribute/MapFrom.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
* @param string[]|null $groups The groups to map the property
2121
* @param string|null $dateTimeFormat The date-time format to use when transforming this property
2222
* @param bool|null $extractTypesFromGetter If true, the types will be extracted from the getter method
23+
* @param bool|null $identifier If true, the property will be used as an identifier
2324
*/
2425
public function __construct(
2526
public string|array|null $source = null,
@@ -32,6 +33,7 @@ public function __construct(
3233
public int $priority = 0,
3334
public ?string $dateTimeFormat = null,
3435
public ?bool $extractTypesFromGetter = null,
36+
public ?bool $identifier = null,
3537
) {
3638
}
3739
}

src/Attribute/MapIdentifier.php

Lines changed: 0 additions & 10 deletions
This file was deleted.

src/Attribute/MapTo.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
* @param string[]|null $groups The groups to map the property
2121
* @param string|null $dateTimeFormat The date-time format to use when transforming this property
2222
* @param bool|null $extractTypesFromGetter If true, the types will be extracted from the getter method
23+
* @param bool|null $identifier If true, the property will be used as an identifier
2324
*/
2425
public function __construct(
2526
public string|array|null $target = null,
@@ -32,6 +33,7 @@ public function __construct(
3233
public int $priority = 0,
3334
public ?string $dateTimeFormat = null,
3435
public ?bool $extractTypesFromGetter = null,
36+
public ?bool $identifier = null,
3537
) {
3638
}
3739
}

src/EventListener/MapFromListener.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ private function addPropertyFromTarget(GenerateMapperEvent $event, MapFrom $mapF
8484
groups: $mapFrom->groups,
8585
priority: $mapFrom->priority,
8686
extractTypesFromGetter: $mapFrom->extractTypesFromGetter,
87+
identifier: $mapFrom->identifier,
8788
);
8889

8990
if (\array_key_exists($propertyMetadata->target->property, $event->properties) && $event->properties[$propertyMetadata->target->property]->priority >= $propertyMetadata->priority) {

src/EventListener/MapToListener.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ private function addPropertyFromSource(GenerateMapperEvent $event, MapTo $mapTo,
8585
groups: $mapTo->groups,
8686
priority: $mapTo->priority,
8787
extractTypesFromGetter: $mapTo->extractTypesFromGetter,
88+
identifier: $mapTo->identifier,
8889
);
8990

9091
if (\array_key_exists($propertyMetadata->target->property, $event->properties) && $event->properties[$propertyMetadata->target->property]->priority >= $propertyMetadata->priority) {

src/Extractor/MappingExtractor.php

Lines changed: 0 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@
44

55
namespace AutoMapper\Extractor;
66

7-
use AutoMapper\Attribute\MapIdentifier;
87
use AutoMapper\Configuration;
98
use AutoMapper\Event\PropertyMetadataEvent;
10-
use AutoMapper\Metadata\TargetPropertyMetadata;
119
use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface;
1210
use Symfony\Component\PropertyInfo\PropertyReadInfo;
1311
use Symfony\Component\PropertyInfo\PropertyReadInfoExtractorInterface;
@@ -189,72 +187,4 @@ public function getDateTimeFormat(PropertyMetadataEvent $propertyMetadataEvent):
189187

190188
return $this->configuration->dateTimeFormat;
191189
}
192-
193-
public function isIdentifier(?\ReflectionClass $targetReflectionClass, TargetPropertyMetadata $target): bool
194-
{
195-
if ($targetReflectionClass === null) {
196-
return false;
197-
}
198-
199-
// check on reflection property
200-
if ($targetReflectionClass->hasProperty($target->property)) {
201-
$reflectionProperty = $targetReflectionClass->getProperty($target->property);
202-
203-
if (\count($reflectionProperty->getAttributes(MapIdentifier::class)) > 0) {
204-
return true;
205-
}
206-
}
207-
208-
// check on the getter to read, it may not be defined yet so we cannot skip this everytime
209-
if ($target->readAccessor !== null) {
210-
// check with read accessor on property
211-
if ($target->readAccessor->type === ReadAccessor::TYPE_PROPERTY && $targetReflectionClass->hasProperty(
212-
$target->readAccessor->property
213-
)) {
214-
$reflectionProperty = $targetReflectionClass->getProperty(
215-
$target->readAccessor->property
216-
);
217-
218-
if (\count($reflectionProperty->getAttributes(MapIdentifier::class)) > 0) {
219-
return true;
220-
}
221-
}
222-
223-
// check with read accessor on method
224-
if ($target->readAccessor->type === ReadAccessor::TYPE_METHOD && $targetReflectionClass->hasMethod(
225-
$target->readAccessor->property
226-
)) {
227-
$reflectionMethod = $targetReflectionClass->getMethod(
228-
$target->readAccessor->property
229-
);
230-
231-
if (\count($reflectionMethod->getAttributes(MapIdentifier::class)) > 0) {
232-
return true;
233-
}
234-
}
235-
}
236-
237-
// same for the write mutator, we cannot globally skip this check as it may be defined latter
238-
if ($target->writeMutator !== null) {
239-
// check for property
240-
if ($target->writeMutator->type === WriteMutator::TYPE_PROPERTY && $targetReflectionClass->hasProperty($target->writeMutator->property)) {
241-
$reflectionProperty = $targetReflectionClass->getProperty(
242-
$target->writeMutator->property
243-
);
244-
245-
if (\count($reflectionProperty->getAttributes(MapIdentifier::class)) > 0) {
246-
return true;
247-
}
248-
}
249-
250-
// check for parameter in constructor
251-
if ($target->writeMutator->type === WriteMutator::TYPE_CONSTRUCTOR && $target->writeMutator->parameter !== null) {
252-
if (\count($target->writeMutator->parameter->getAttributes(MapIdentifier::class)) > 0) {
253-
return true;
254-
}
255-
}
256-
}
257-
258-
return false;
259-
}
260190
}

src/Extractor/MappingExtractorInterface.php

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,4 @@ public function getReadAccessor(string $class, string $property): ?ReadAccessor;
4949
* @param array<string, mixed> $context
5050
*/
5151
public function getWriteMutator(string $source, string $target, string $property, array $context = []): ?WriteMutator;
52-
53-
/**
54-
* Check if a property is an identifier.
55-
*/
56-
public function isIdentifier(?\ReflectionClass $targetReflectionClass, TargetPropertyMetadata $target): bool;
5752
}

src/Extractor/ReadAccessor.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public function __construct(
6161
*
6262
* @throws CompileException
6363
*/
64-
public function getExpression(Expr\Variable $input, bool $target = false): Expr
64+
public function getExpression(Expr $input, bool $target = false): Expr
6565
{
6666
if (self::TYPE_METHOD === $this->type) {
6767
$methodCallArguments = [];

src/GeneratedMapper.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,14 @@ public function registerMappers(AutoMapperRegistryInterface $registry): void
3636
{
3737
}
3838

39-
public function areIdentifiersEquals(mixed $source, mixed $target): bool
39+
public function getSourceHash(mixed $value): ?string
4040
{
41-
return false;
41+
return null;
42+
}
43+
44+
public function getTargetHash(mixed $value): ?string
45+
{
46+
return null;
4247
}
4348

4449
/** @var array<string, MapperInterface<object, object>|MapperInterface<object, array<mixed>>|MapperInterface<array<mixed>, object>> */

src/Generator/IdentifierEqualGenerator.php

Lines changed: 0 additions & 83 deletions
This file was deleted.
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace AutoMapper\Generator;
6+
7+
use AutoMapper\Metadata\GeneratorMetadata;
8+
use PhpParser\Node\Arg;
9+
use PhpParser\Node\Expr;
10+
use PhpParser\Node\Name;
11+
use PhpParser\Node\Scalar;
12+
use PhpParser\Node\Stmt;
13+
14+
final readonly class IdentifierHashGenerator
15+
{
16+
/**
17+
* @return list<Stmt>
18+
*/
19+
public function getStatements(GeneratorMetadata $metadata, bool $fromSource): array
20+
{
21+
$identifiers = [];
22+
23+
foreach ($metadata->propertiesMetadata as $propertyMetadata) {
24+
if (!$propertyMetadata->identifier) {
25+
continue;
26+
}
27+
28+
if (null === $propertyMetadata->target->readAccessor) {
29+
continue;
30+
}
31+
32+
if (null === $propertyMetadata->source->accessor) {
33+
continue;
34+
}
35+
36+
$identifiers[] = $propertyMetadata;
37+
}
38+
39+
if (empty($identifiers)) {
40+
return [];
41+
}
42+
43+
$hashCtxVariable = new Expr\Variable('hashCtx');
44+
45+
$statements = [
46+
new Stmt\Expression(new Expr\Assign($hashCtxVariable, new Expr\FuncCall(new Name('hash_init'), [
47+
new Arg(new Scalar\String_('sha256')),
48+
]))),
49+
];
50+
51+
$valueVariable = new Expr\Variable('value');
52+
53+
// foreach property we check
54+
foreach ($identifiers as $property) {
55+
// check if the source is defined
56+
if ($fromSource) {
57+
if ($property->source->checkExists) {
58+
$statements[] = new Stmt\If_($property->source->accessor->getIsUndefinedExpression($valueVariable), [
59+
'stmts' => [
60+
new Stmt\Return_(new Expr\ConstFetch(new Name('null'))),
61+
],
62+
]);
63+
}
64+
65+
// add identifier to hash
66+
$statements[] = new Stmt\Expression(new Expr\FuncCall(new Name('hash_update'), [
67+
new Arg($hashCtxVariable),
68+
new Arg($property->source->accessor->getExpression($valueVariable)),
69+
]));
70+
} else {
71+
$statements[] = new Stmt\If_($property->target->readAccessor->getIsUndefinedExpression($valueVariable, true), [
72+
'stmts' => [
73+
new Stmt\Return_(new Expr\ConstFetch(new Name('null'))),
74+
],
75+
]);
76+
77+
$statements[] = new Stmt\Expression(new Expr\FuncCall(new Name('hash_update'), [
78+
new Arg($hashCtxVariable),
79+
new Arg($property->target->readAccessor->getExpression($valueVariable, true)),
80+
]));
81+
}
82+
}
83+
84+
// return hash as string
85+
$statements[] = new Stmt\Return_(new Expr\FuncCall(new Name('hash_final'), [
86+
new Arg($hashCtxVariable),
87+
new Arg(new Scalar\String_('true')),
88+
]));
89+
90+
return $statements;
91+
}
92+
}

0 commit comments

Comments
 (0)