Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions src/Cryptography/CryptographyMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,36 @@ public function __construct(
/**
* @param ClassMetadata<T> $metadata
* @param array<string, mixed> $data
* @param array<string, mixed> $context
*
* @return T
*
* @template T of object
*/
public function hydrate(ClassMetadata $metadata, array $data, Stack $stack): object
public function hydrate(ClassMetadata $metadata, array $data, array $context, Stack $stack): object
{
return $stack->next()->hydrate(
$metadata,
$this->cryptography->decrypt($metadata, $data),
$context,
$stack,
);
}

/**
* @param ClassMetadata<T> $metadata
* @param T $object
* @param ClassMetadata<T> $metadata
* @param T $object
* @param array<string, mixed> $context
*
* @return array<string, mixed>
*
* @template T of object
*/
public function extract(ClassMetadata $metadata, object $object, Stack $stack): array
public function extract(ClassMetadata $metadata, object $object, array $context, Stack $stack): array
{
return $this->cryptography->encrypt(
$metadata,
$stack->next()->extract($metadata, $object, $stack),
$stack->next()->extract($metadata, $object, $context, $stack),
);
}
}
11 changes: 8 additions & 3 deletions src/Hydrator.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,20 @@ interface Hydrator
/**
* @param class-string<T> $class
* @param array<string, mixed> $data
* @param array<string, mixed> $context
*
* @return T
*
* @throws ClassNotSupported if the class is not supported or not found.
*
* @template T of object
*/
public function hydrate(string $class, array $data): object;
public function hydrate(string $class, array $data, array $context = []): object;

/** @return array<string, mixed> */
public function extract(object $object): array;
/**
* @param array<string, mixed> $context
*
* @return array<string, mixed>
*/
public function extract(object $object, array $context = []): array;
}
21 changes: 13 additions & 8 deletions src/MetadataHydrator.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,13 @@ public function __construct(
/**
* @param class-string<T> $class
* @param array<string, mixed> $data
* @param array<string, mixed> $context
*
* @return T
*
* @template T of object
*/
public function hydrate(string $class, array $data): object
public function hydrate(string $class, array $data, array $context = []): object
{
try {
$metadata = $this->metadata($class);
Expand All @@ -53,33 +54,37 @@ public function hydrate(string $class, array $data): object
if (PHP_VERSION_ID < 80400) {
$stack = new Stack($this->middlewares);

return $stack->next()->hydrate($metadata, $data, $stack);
return $stack->next()->hydrate($metadata, $data, $context, $stack);
}

$lazy = $metadata->lazy ?? $this->defaultLazy;

if (!$lazy) {
$stack = new Stack($this->middlewares);

return $stack->next()->hydrate($metadata, $data, $stack);
return $stack->next()->hydrate($metadata, $data, $context, $stack);
}

return (new ReflectionClass($class))->newLazyProxy(
function () use ($metadata, $data): object {
function () use ($metadata, $data, $context): object {
$stack = new Stack($this->middlewares);

return $stack->next()->hydrate($metadata, $data, $stack);
return $stack->next()->hydrate($metadata, $data, $context, $stack);
},
);
}

/** @return array<string, mixed> */
public function extract(object $object): array
/**
* @param array<string, mixed> $context
*
* @return array<string, mixed>
*/
public function extract(object $object, array $context = []): array
{
$metadata = $this->metadata($object::class);
$stack = new Stack($this->middlewares);

return $stack->next()->extract($metadata, $object, $stack);
return $stack->next()->extract($metadata, $object, $context, $stack);
}

/**
Expand Down
10 changes: 6 additions & 4 deletions src/Middleware/Middleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,22 @@ interface Middleware
/**
* @param ClassMetadata<T> $metadata
* @param array<string, mixed> $data
* @param array<string, mixed> $context
*
* @return T
*
* @template T of object
*/
public function hydrate(ClassMetadata $metadata, array $data, Stack $stack): object;
public function hydrate(ClassMetadata $metadata, array $data, array $context, Stack $stack): object;

/**
* @param ClassMetadata<T> $metadata
* @param T $object
* @param ClassMetadata<T> $metadata
* @param T $object
* @param array<string, mixed> $context
*
* @return array<string, mixed>
*
* @template T of object
*/
public function extract(ClassMetadata $metadata, object $object, Stack $stack): array;
public function extract(ClassMetadata $metadata, object $object, array $context, Stack $stack): array;
}
43 changes: 30 additions & 13 deletions src/Middleware/TransformMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Patchlevel\Hydrator\DenormalizationFailure;
use Patchlevel\Hydrator\Metadata\ClassMetadata;
use Patchlevel\Hydrator\NormalizationFailure;
use Patchlevel\Hydrator\Normalizer\ContextAwareNormalizer;
use Patchlevel\Hydrator\TypeMismatch;
use ReflectionParameter;
use Throwable;
Expand All @@ -25,12 +26,13 @@
/**
* @param ClassMetadata<T> $metadata
* @param array<string, mixed> $data
* @param array<string, mixed> $context
*
* @return T
*
* @template T of object
*/
public function hydrate(ClassMetadata $metadata, array $data, Stack $stack): object
public function hydrate(ClassMetadata $metadata, array $data, array $context, Stack $stack): object
{
$object = $metadata->newInstance();

Expand Down Expand Up @@ -58,17 +60,20 @@
continue;
}

$normalizer = $propertyMetadata->normalizer;

if ($normalizer) {
if ($propertyMetadata->normalizer) {
try {
/** @psalm-suppress MixedAssignment */
$value = $normalizer->denormalize($data[$propertyMetadata->fieldName]);
if ($propertyMetadata->normalizer instanceof ContextAwareNormalizer) {
/** @psalm-suppress MixedAssignment */
$value = $propertyMetadata->normalizer->denormalize($data[$propertyMetadata->fieldName], $context);
} else {
/** @psalm-suppress MixedAssignment */
$value = $propertyMetadata->normalizer->denormalize($data[$propertyMetadata->fieldName]);
}
} catch (Throwable $e) {
throw new DenormalizationFailure(
$metadata->className,
$propertyMetadata->propertyName,
$normalizer::class,
$propertyMetadata->normalizer::class,
$e,
);
}
Expand All @@ -90,8 +95,12 @@
return $object;
}

/** @return array<string, mixed> */
public function extract(ClassMetadata $metadata, object $object, Stack $stack): array
/**
* @param array<string, mixed> $context
*
* @return array<string, mixed>
*/
public function extract(ClassMetadata $metadata, object $object, array $context, Stack $stack): array
{
$objectId = spl_object_id($object);

Expand All @@ -104,16 +113,24 @@

$this->callStack[$objectId] = $object::class;

try {

Check warning on line 116 in src/Middleware/TransformMiddleware.php

View workflow job for this annotation

GitHub Actions / Mutation tests on diff (locked, 8.4, ubuntu-latest)

Escaped Mutant for Mutator "UnwrapFinally": @@ @@ throw new CircularReference($references); } $this->callStack[$objectId] = $object::class; - try { - $data = []; - foreach ($metadata->properties as $propertyMetadata) { - if ($propertyMetadata->normalizer) { - try { - if ($propertyMetadata->normalizer instanceof ContextAwareNormalizer) { - /** @psalm-suppress MixedAssignment */ - $data[$propertyMetadata->fieldName] = $propertyMetadata->normalizer->normalize($propertyMetadata->getValue($object), $context); - } else { - /** @psalm-suppress MixedAssignment */ - $data[$propertyMetadata->fieldName] = $propertyMetadata->normalizer->normalize($propertyMetadata->getValue($object)); - } - } catch (CircularReference $e) { - throw $e; - } catch (Throwable $e) { - throw new NormalizationFailure($object::class, $propertyMetadata->propertyName, $propertyMetadata->normalizer::class, $e); + $data = []; + foreach ($metadata->properties as $propertyMetadata) { + if ($propertyMetadata->normalizer) { + try { + if ($propertyMetadata->normalizer instanceof ContextAwareNormalizer) { + /** @psalm-suppress MixedAssignment */ + $data[$propertyMetadata->fieldName] = $propertyMetadata->normalizer->normalize($propertyMetadata->getValue($object), $context); + } else { + /** @psalm-suppress MixedAssignment */ + $data[$propertyMetadata->fieldName] = $propertyMetadata->normalizer->normalize($propertyMetadata->getValue($object)); } - } else { - $data[$propertyMetadata->fieldName] = $propertyMetadata->getValue($object); + } catch (CircularReference $e) { + throw $e; + } catch (Throwable $e) { + throw new NormalizationFailure($object::class, $propertyMetadata->propertyName, $propertyMetadata->normalizer::class, $e); } + } else { + $data[$propertyMetadata->fieldName] = $propertyMetadata->getValue($object); } - } finally { - unset($this->callStack[$objectId]); } + unset($this->callStack[$objectId]); return $data; } /** @return array<string, ReflectionParameter> */
$data = [];

foreach ($metadata->properties as $propertyMetadata) {
if ($propertyMetadata->normalizer) {
try {
/** @psalm-suppress MixedAssignment */
$data[$propertyMetadata->fieldName] = $propertyMetadata->normalizer->normalize(
$propertyMetadata->getValue($object),
);
if ($propertyMetadata->normalizer instanceof ContextAwareNormalizer) {
/** @psalm-suppress MixedAssignment */
$data[$propertyMetadata->fieldName] = $propertyMetadata->normalizer->normalize(
$propertyMetadata->getValue($object),
$context,
);
} else {
/** @psalm-suppress MixedAssignment */
$data[$propertyMetadata->fieldName] = $propertyMetadata->normalizer->normalize(
$propertyMetadata->getValue($object),
);
}
} catch (CircularReference $e) {
throw $e;
} catch (Throwable $e) {
Expand Down
22 changes: 22 additions & 0 deletions src/Normalizer/ContextAwareNormalizer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Patchlevel\Hydrator\Normalizer;

interface ContextAwareNormalizer extends Normalizer
{
/**
* @param array<string, mixed> $context
*
* @throws InvalidArgument
*/
public function normalize(mixed $value, array $context = []): mixed;

/**
* @param array<string, mixed> $context
*
* @throws InvalidArgument
*/
public function denormalize(mixed $value, array $context = []): mixed;
}
17 changes: 11 additions & 6 deletions src/Normalizer/ObjectNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
use function is_array;

#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_CLASS)]
final class ObjectNormalizer implements Normalizer, TypeAwareNormalizer, HydratorAwareNormalizer
final class ObjectNormalizer implements ContextAwareNormalizer, TypeAwareNormalizer, HydratorAwareNormalizer
{
private Hydrator|null $hydrator = null;

Expand All @@ -25,8 +25,12 @@ public function __construct(
) {
}

/** @return array<string, mixed>|null */
public function normalize(mixed $value): array|null
/**
* @param array<string, mixed> $context
*
* @return array<string, mixed>|null
*/
public function normalize(mixed $value, array $context = []): array|null
{
if (!$this->hydrator) {
throw new MissingHydrator();
Expand All @@ -42,10 +46,11 @@ public function normalize(mixed $value): array|null
throw InvalidArgument::withWrongType($className . '|null', $value);
}

return $this->hydrator->extract($value);
return $this->hydrator->extract($value, $context);
}

public function denormalize(mixed $value): object|null
/** @param array<string, mixed> $context */
public function denormalize(mixed $value, array $context = []): object|null
{
if (!$this->hydrator) {
throw new MissingHydrator();
Expand All @@ -61,7 +66,7 @@ public function denormalize(mixed $value): object|null

$className = $this->getClassName();

return $this->hydrator->hydrate($className, $value);
return $this->hydrator->hydrate($className, $value, $context);
}

public function setHydrator(Hydrator $hydrator): void
Expand Down
8 changes: 4 additions & 4 deletions tests/Unit/Cryptography/CryptographyMiddlewareTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ public function testHydrate(): void

$stack = new Stack([$otherMiddleware]);

$otherMiddleware->expects($this->once())->method('hydrate')->with($metadata, ['name' => 'bar'], $stack)->willReturn($object);
$otherMiddleware->expects($this->once())->method('hydrate')->with($metadata, ['name' => 'bar'], [], $stack)->willReturn($object);

$result = $cryptographyMiddleware->hydrate($metadata, ['name' => 'foo'], $stack);
$result = $cryptographyMiddleware->hydrate($metadata, ['name' => 'foo'], [], $stack);

self::assertSame($object, $result);
}
Expand All @@ -54,9 +54,9 @@ public function testExtract(): void

$stack = new Stack([$otherMiddleware]);

$otherMiddleware->expects($this->once())->method('extract')->with($metadata, $object, $stack)->willReturn(['name' => 'foo']);
$otherMiddleware->expects($this->once())->method('extract')->with($metadata, $object, [], $stack)->willReturn(['name' => 'foo']);

$result = $cryptographyMiddleware->extract($metadata, $object, $stack);
$result = $cryptographyMiddleware->extract($metadata, $object, [], $stack);

self::assertSame(['name' => 'bar'], $result);
}
Expand Down
Loading
Loading