Skip to content

Commit 58b7180

Browse files
authored
Merge pull request #162 from patchlevel/backport-context-feature
backport context feature
2 parents d997a05 + 5c68f0d commit 58b7180

15 files changed

+483
-44
lines changed

phpstan-baseline.neon

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,6 @@ parameters:
7272
count: 1
7373
path: src/Metadata/ClassMetadata.php
7474

75-
-
76-
message: '#^Dead catch \- Patchlevel\\Hydrator\\CircularReference is never thrown in the try block\.$#'
77-
identifier: catch.neverThrown
78-
count: 1
79-
path: src/MetadataHydrator.php
80-
8175
-
8276
message: '#^Property Patchlevel\\Hydrator\\Normalizer\\EnumNormalizer\:\:\$enum \(class\-string\<BackedEnum\>\|null\) does not accept string\.$#'
8377
identifier: assign.propertyType
@@ -90,12 +84,24 @@ parameters:
9084
count: 1
9185
path: src/Normalizer/ObjectMapNormalizer.php
9286

87+
-
88+
message: '#^Parameter \#2 \$data of method Patchlevel\\Hydrator\\HydratorWithContext\:\:hydrate\(\) expects array\<string, mixed\>, array given\.$#'
89+
identifier: argument.type
90+
count: 1
91+
path: src/Normalizer/ObjectMapNormalizer.php
92+
9393
-
9494
message: '#^Parameter \#2 \$data of method Patchlevel\\Hydrator\\Hydrator\:\:hydrate\(\) expects array\<string, mixed\>, array\<mixed, mixed\> given\.$#'
9595
identifier: argument.type
9696
count: 1
9797
path: src/Normalizer/ObjectNormalizer.php
9898

99+
-
100+
message: '#^Parameter \#2 \$data of method Patchlevel\\Hydrator\\HydratorWithContext\:\:hydrate\(\) expects array\<string, mixed\>, array\<mixed, mixed\> given\.$#'
101+
identifier: argument.type
102+
count: 1
103+
path: src/Normalizer/ObjectNormalizer.php
104+
99105
-
100106
message: '#^Property Patchlevel\\Hydrator\\Normalizer\\ObjectNormalizer\:\:\$className \(class\-string\|null\) does not accept string\.$#'
101107
identifier: assign.propertyType

src/HydratorWithContext.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Patchlevel\Hydrator;
6+
7+
interface HydratorWithContext extends Hydrator
8+
{
9+
/**
10+
* @param class-string<T> $class
11+
* @param array<string, mixed> $data
12+
* @param array<string, mixed> $context
13+
*
14+
* @return T
15+
*
16+
* @throws ClassNotSupported if the class is not supported or not found.
17+
*
18+
* @template T of object
19+
*/
20+
public function hydrate(string $class, array $data, array $context = []): object;
21+
22+
/**
23+
* @param array<string, mixed> $context
24+
*
25+
* @return array<string, mixed>
26+
*/
27+
public function extract(object $object, array $context = []): array;
28+
}

src/MetadataHydrator.php

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Patchlevel\Hydrator\Metadata\ClassNotFound;
1717
use Patchlevel\Hydrator\Metadata\MetadataFactory;
1818
use Patchlevel\Hydrator\Normalizer\HydratorAwareNormalizer;
19+
use Patchlevel\Hydrator\Normalizer\NormalizerWithContext;
1920
use ReflectionClass;
2021
use ReflectionParameter;
2122
use Symfony\Component\EventDispatcher\EventDispatcher;
@@ -30,7 +31,7 @@
3031

3132
use const PHP_VERSION_ID;
3233

33-
final class MetadataHydrator implements Hydrator
34+
final class MetadataHydrator implements HydratorWithContext
3435
{
3536
/** @var array<int, class-string> */
3637
private array $stack = [];
@@ -57,12 +58,13 @@ public function __construct(
5758
/**
5859
* @param class-string<T> $class
5960
* @param array<string, mixed> $data
61+
* @param array<string, mixed> $context
6062
*
6163
* @return T
6264
*
6365
* @template T of object
6466
*/
65-
public function hydrate(string $class, array $data): object
67+
public function hydrate(string $class, array $data, array $context = []): object
6668
{
6769
try {
6870
$metadata = $this->metadataFactory->metadata($class);
@@ -71,31 +73,32 @@ public function hydrate(string $class, array $data): object
7173
}
7274

7375
if (PHP_VERSION_ID < 80400) {
74-
return $this->doHydrate($metadata, $data);
76+
return $this->doHydrate($metadata, $data, $context);
7577
}
7678

7779
$lazy = $metadata->lazy() ?? $this->defaultLazy;
7880

7981
if (!$lazy) {
80-
return $this->doHydrate($metadata, $data);
82+
return $this->doHydrate($metadata, $data, $context);
8183
}
8284

8385
return (new ReflectionClass($class))->newLazyProxy(
84-
function () use ($metadata, $data): object {
85-
return $this->doHydrate($metadata, $data);
86+
function () use ($metadata, $data, $context): object {
87+
return $this->doHydrate($metadata, $data, $context);
8688
},
8789
);
8890
}
8991

9092
/**
9193
* @param ClassMetadata<T> $metadata
9294
* @param array<string, mixed> $data
95+
* @param array<string, mixed> $context
9396
*
9497
* @return T
9598
*
9699
* @template T of object
97100
*/
98-
private function doHydrate(ClassMetadata $metadata, array $data): object
101+
private function doHydrate(ClassMetadata $metadata, array $data, array $context = []): object
99102
{
100103
if ($this->eventDispatcher) {
101104
$data = $this->eventDispatcher->dispatch(new PreHydrate($data, $metadata))->data;
@@ -138,7 +141,11 @@ private function doHydrate(ClassMetadata $metadata, array $data): object
138141

139142
try {
140143
/** @psalm-suppress MixedAssignment */
141-
$value = $normalizer->denormalize($value);
144+
if ($normalizer instanceof NormalizerWithContext) {
145+
$value = $normalizer->denormalize($value, $context);
146+
} else {
147+
$value = $normalizer->denormalize($value);
148+
}
142149
} catch (Throwable $e) {
143150
throw new DenormalizationFailure(
144151
$metadata->className(),
@@ -167,8 +174,12 @@ private function doHydrate(ClassMetadata $metadata, array $data): object
167174
return $object;
168175
}
169176

170-
/** @return array<string, mixed> */
171-
public function extract(object $object): array
177+
/**
178+
* @param array<string, mixed> $context
179+
*
180+
* @return array<string, mixed>
181+
*/
182+
public function extract(object $object, array $context = []): array
172183
{
173184
$objectId = spl_object_id($object);
174185

@@ -202,11 +213,18 @@ public function extract(object $object): array
202213
}
203214

204215
try {
205-
/** @psalm-suppress MixedAssignment */
206-
$value = $normalizer->normalize($value);
207-
} catch (CircularReference $e) {
208-
throw $e;
216+
if ($normalizer instanceof NormalizerWithContext) {
217+
/** @psalm-suppress MixedAssignment */
218+
$value = $normalizer->normalize($value, $context);
219+
} else {
220+
/** @psalm-suppress MixedAssignment */
221+
$value = $normalizer->normalize($value);
222+
}
209223
} catch (Throwable $e) {
224+
if ($e instanceof CircularReference) {
225+
throw $e;
226+
}
227+
210228
throw new NormalizationFailure(
211229
$object::class,
212230
$propertyMetadata->propertyName(),

src/Normalizer/ArrayNormalizer.php

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,19 @@
1313
use function is_array;
1414

1515
#[Attribute(Attribute::TARGET_PROPERTY)]
16-
final readonly class ArrayNormalizer implements Normalizer, TypeAwareNormalizer, HydratorAwareNormalizer
16+
final readonly class ArrayNormalizer implements NormalizerWithContext, TypeAwareNormalizer, HydratorAwareNormalizer
1717
{
1818
public function __construct(
1919
private Normalizer $normalizer,
2020
) {
2121
}
2222

23-
/** @return array<array-key, mixed>|null */
24-
public function normalize(mixed $value): array|null
23+
/**
24+
* @param array<string, mixed> $context
25+
*
26+
* @return array<array-key, mixed>|null
27+
*/
28+
public function normalize(mixed $value, array $context = []): array|null
2529
{
2630
if ($value === null) {
2731
return null;
@@ -31,15 +35,25 @@ public function normalize(mixed $value): array|null
3135
throw InvalidArgument::withWrongType('array|null', $value);
3236
}
3337

34-
foreach ($value as &$item) {
35-
$item = $this->normalizer->normalize($item);
38+
if ($this->normalizer instanceof NormalizerWithContext) {
39+
foreach ($value as &$item) {
40+
$item = $this->normalizer->normalize($item, $context);
41+
}
42+
} else {
43+
foreach ($value as &$item) {
44+
$item = $this->normalizer->normalize($item);
45+
}
3646
}
3747

3848
return $value;
3949
}
4050

41-
/** @return array<array-key, mixed>|null */
42-
public function denormalize(mixed $value): array|null
51+
/**
52+
* @param array<string, mixed> $context
53+
*
54+
* @return array<array-key, mixed>|null
55+
*/
56+
public function denormalize(mixed $value, array $context = []): array|null
4357
{
4458
if ($value === null) {
4559
return null;
@@ -49,8 +63,14 @@ public function denormalize(mixed $value): array|null
4963
throw InvalidArgument::withWrongType('array|null', $value);
5064
}
5165

52-
foreach ($value as &$item) {
53-
$item = $this->normalizer->denormalize($item);
66+
if ($this->normalizer instanceof NormalizerWithContext) {
67+
foreach ($value as &$item) {
68+
$item = $this->normalizer->denormalize($item, $context);
69+
}
70+
} else {
71+
foreach ($value as &$item) {
72+
$item = $this->normalizer->denormalize($item);
73+
}
5474
}
5575

5676
return $value;

src/Normalizer/ArrayShapeNormalizer.php

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,20 @@
1313
use function is_array;
1414

1515
#[Attribute(Attribute::TARGET_PROPERTY)]
16-
final readonly class ArrayShapeNormalizer implements Normalizer, TypeAwareNormalizer, HydratorAwareNormalizer
16+
final readonly class ArrayShapeNormalizer implements NormalizerWithContext, TypeAwareNormalizer, HydratorAwareNormalizer
1717
{
1818
/** @param array<array-key, Normalizer> $normalizerMap */
1919
public function __construct(
2020
private array $normalizerMap,
2121
) {
2222
}
2323

24-
/** @return array<array-key, mixed>|null */
25-
public function normalize(mixed $value): array|null
24+
/**
25+
* @param array<string, mixed> $context
26+
*
27+
* @return array<array-key, mixed>|null
28+
*/
29+
public function normalize(mixed $value, array $context = []): array|null
2630
{
2731
if ($value === null) {
2832
return null;
@@ -39,14 +43,22 @@ public function normalize(mixed $value): array|null
3943
continue;
4044
}
4145

42-
$result[$field] = $normalizer->normalize($value[$field]);
46+
if ($normalizer instanceof NormalizerWithContext) {
47+
$result[$field] = $normalizer->normalize($value[$field], $context);
48+
} else {
49+
$result[$field] = $normalizer->normalize($value[$field]);
50+
}
4351
}
4452

4553
return $result;
4654
}
4755

48-
/** @return array<array-key, mixed>|null */
49-
public function denormalize(mixed $value): array|null
56+
/**
57+
* @param array<string, mixed> $context
58+
*
59+
* @return array<array-key, mixed>|null
60+
*/
61+
public function denormalize(mixed $value, array $context = []): array|null
5062
{
5163
if ($value === null) {
5264
return null;
@@ -63,7 +75,11 @@ public function denormalize(mixed $value): array|null
6375
continue;
6476
}
6577

66-
$result[$field] = $normalizer->denormalize($value[$field]);
78+
if ($normalizer instanceof NormalizerWithContext) {
79+
$result[$field] = $normalizer->denormalize($value[$field], $context);
80+
} else {
81+
$result[$field] = $normalizer->denormalize($value[$field]);
82+
}
6783
}
6884

6985
return $result;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Patchlevel\Hydrator\Normalizer;
6+
7+
interface NormalizerWithContext extends Normalizer
8+
{
9+
/**
10+
* @param array<string, mixed> $context
11+
*
12+
* @throws InvalidArgument
13+
*/
14+
public function normalize(mixed $value, array $context = []): mixed;
15+
16+
/**
17+
* @param array<string, mixed> $context
18+
*
19+
* @throws InvalidArgument
20+
*/
21+
public function denormalize(mixed $value, array $context = []): mixed;
22+
}

0 commit comments

Comments
 (0)