Skip to content

Commit daab337

Browse files
committed
test deep clone
1 parent c5e1037 commit daab337

5 files changed

Lines changed: 243 additions & 3 deletions

File tree

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"psr/cache": "^2.0.0 || ^3.0.0",
2525
"psr/simple-cache": "^2.0.0 || ^3.0.0",
2626
"symfony/event-dispatcher": "^5.4.29 || ^6.4.0 || ^7.0.0 || ^8.0.0",
27+
"symfony/polyfill-deepclone": "^1.37",
2728
"symfony/type-info": "^7.3.0 || ^8.0.0"
2829
},
2930
"require-dev": {

composer.lock

Lines changed: 88 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/CoreExtension.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@
55
namespace Patchlevel\Hydrator;
66

77
use Patchlevel\Hydrator\Guesser\BuiltInGuesser;
8+
use Patchlevel\Hydrator\Middleware\SymfonyTransformMiddleware;
89
use Patchlevel\Hydrator\Middleware\TransformMiddleware;
910

1011
/** @experimental */
1112
final class CoreExtension implements Extension
1213
{
1314
public function configure(StackHydratorBuilder $builder): void
1415
{
15-
$builder->addMiddleware(new TransformMiddleware(), -64);
16+
$builder->addMiddleware(new SymfonyTransformMiddleware(), -64);
1617
$builder->addGuesser(new BuiltInGuesser(), -64);
1718
}
1819
}
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Patchlevel\Hydrator\Middleware;
6+
7+
use Patchlevel\Hydrator\CircularReference;
8+
use Patchlevel\Hydrator\DenormalizationFailure;
9+
use Patchlevel\Hydrator\HydratorWithContext;
10+
use Patchlevel\Hydrator\Metadata\ClassMetadata;
11+
use Patchlevel\Hydrator\NormalizationFailure;
12+
use Patchlevel\Hydrator\Normalizer\NormalizerWithContext;
13+
use Throwable;
14+
15+
use function array_key_exists;
16+
use function array_values;
17+
use function spl_object_id;
18+
19+
/** @experimental */
20+
final class SymfonyTransformMiddleware implements Middleware
21+
{
22+
/** @var array<int, class-string> */
23+
private array $callStack = [];
24+
25+
/**
26+
* @param ClassMetadata<T> $metadata
27+
* @param array<string, mixed> $data
28+
* @param array<string, mixed> $context
29+
*
30+
* @return T
31+
*
32+
* @template T of object
33+
*/
34+
public function hydrate(ClassMetadata $metadata, array $data, array $context, Stack $stack): object
35+
{
36+
$constructorParameters = null;
37+
38+
$vars = [];
39+
40+
dump('ja');
41+
42+
foreach ($metadata->properties() as $propertyMetadata) {
43+
/*
44+
if (!array_key_exists($propertyMetadata->fieldName(), $data)) {
45+
if (!$propertyMetadata->reflection->isPromoted()) {
46+
continue;
47+
}
48+
49+
$constructorParameters ??= $metadata->promotedConstructorDefaults();
50+
51+
if (!array_key_exists($propertyMetadata->propertyName, $constructorParameters)) {
52+
continue;
53+
}
54+
55+
$vars[$propertyMetadata->propertyName] = $constructorParameters[$propertyMetadata->propertyName]->getDefaultValue();
56+
57+
$propertyMetadata->setValue(
58+
$object,
59+
$constructorParameters[$propertyMetadata->propertyName]->getDefaultValue(),
60+
);
61+
62+
continue;
63+
}
64+
*/
65+
66+
if ($propertyMetadata->normalizer) {
67+
try {
68+
if ($propertyMetadata->normalizer instanceof NormalizerWithContext) {
69+
/** @psalm-suppress MixedAssignment */
70+
$vars[$propertyMetadata->propertyName] = $propertyMetadata->normalizer->denormalize($data[$propertyMetadata->fieldName], $context);
71+
} else {
72+
/** @psalm-suppress MixedAssignment */
73+
$vars[$propertyMetadata->propertyName] = $propertyMetadata->normalizer->denormalize($data[$propertyMetadata->fieldName]);
74+
}
75+
} catch (Throwable $e) {
76+
throw new DenormalizationFailure(
77+
$metadata->className,
78+
$propertyMetadata->propertyName,
79+
$propertyMetadata->normalizer::class,
80+
$e,
81+
);
82+
}
83+
} else {
84+
$vars[$propertyMetadata->propertyName] = $data[$propertyMetadata->fieldName];
85+
}
86+
}
87+
88+
return deepclone_hydrate(
89+
$context[HydratorWithContext::OBJECT_TO_POPULATE] ?? $metadata->className(),
90+
$vars,
91+
);
92+
}
93+
94+
/**
95+
* @param array<string, mixed> $context
96+
*
97+
* @return array<string, mixed>
98+
*/
99+
public function extract(ClassMetadata $metadata, object $object, array $context, Stack $stack): array
100+
{
101+
$objectId = spl_object_id($object);
102+
103+
if (array_key_exists($objectId, $this->callStack)) {
104+
$references = array_values($this->callStack);
105+
$references[] = $object::class;
106+
107+
throw new CircularReference($references);
108+
}
109+
110+
$this->callStack[$objectId] = $object::class;
111+
112+
try {
113+
$data = [];
114+
115+
foreach ($metadata->properties as $propertyMetadata) {
116+
if ($propertyMetadata->normalizer) {
117+
try {
118+
if ($propertyMetadata->normalizer instanceof NormalizerWithContext) {
119+
/** @psalm-suppress MixedAssignment */
120+
$data[$propertyMetadata->fieldName] = $propertyMetadata->normalizer->normalize(
121+
$propertyMetadata->getValue($object),
122+
$context,
123+
);
124+
} else {
125+
/** @psalm-suppress MixedAssignment */
126+
$data[$propertyMetadata->fieldName] = $propertyMetadata->normalizer->normalize(
127+
$propertyMetadata->getValue($object),
128+
);
129+
}
130+
} catch (CircularReference $e) {
131+
throw $e;
132+
} catch (Throwable $e) {
133+
throw new NormalizationFailure(
134+
$object::class,
135+
$propertyMetadata->propertyName,
136+
$propertyMetadata->normalizer::class,
137+
$e,
138+
);
139+
}
140+
} else {
141+
$data[$propertyMetadata->fieldName] = $propertyMetadata->getValue($object);
142+
}
143+
}
144+
} finally {
145+
unset($this->callStack[$objectId]);
146+
}
147+
148+
return $data;
149+
}
150+
}

src/StackHydrator.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Patchlevel\Hydrator\Metadata\MetadataFactory;
1111
use Patchlevel\Hydrator\Middleware\Middleware;
1212
use Patchlevel\Hydrator\Middleware\Stack;
13+
use Patchlevel\Hydrator\Middleware\SymfonyTransformMiddleware;
1314
use Patchlevel\Hydrator\Middleware\TransformMiddleware;
1415
use Patchlevel\Hydrator\Normalizer\HydratorAwareNormalizer;
1516
use ReflectionClass;
@@ -27,7 +28,7 @@ final class StackHydrator implements HydratorWithContext
2728
/** @param list<Middleware> $middlewares */
2829
public function __construct(
2930
private readonly MetadataFactory $metadataFactory = new AttributeMetadataFactory(),
30-
private readonly array $middlewares = [new TransformMiddleware()],
31+
private readonly array $middlewares = [new SymfonyTransformMiddleware()],
3132
private readonly bool $defaultLazy = false,
3233
) {
3334
if ($middlewares === []) {

0 commit comments

Comments
 (0)