Skip to content
Closed
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
15 changes: 11 additions & 4 deletions src/Extension/Cryptography/CryptographyMetadataEnricher.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ final class CryptographyMetadataEnricher implements MetadataEnricher
public function enrich(ClassMetadata $classMetadata): void
{
$subjectIdMapping = [];
$subjectIdProperties = [];
$sensitiveProperties = [];

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

if ($attributeReflectionList) {
if ($attributeReflectionList !== []) {
$subjectIdIdentifier = $attributeReflectionList[0]->newInstance()->name;

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

$subjectIdMapping[$subjectIdIdentifier] = $property->fieldName;
$subjectIdProperties[$subjectIdIdentifier] = $property;

$isSubjectId = true;
}
Expand All @@ -50,13 +53,17 @@ public function enrich(ClassMetadata $classMetadata): void
}

$property->extras[SensitiveDataInfo::class] = $sensitiveDataInfo;
$sensitiveProperties[] = $property;
}

if ($subjectIdMapping === []) {
return;
if ($sensitiveProperties !== []) {
$classMetadata->extras[SensitiveDataInfo::class . '::properties'] = $sensitiveProperties;
}

$classMetadata->extras[SubjectIdFieldMapping::class] = new SubjectIdFieldMapping($subjectIdMapping);
if ($subjectIdMapping !== []) {
$classMetadata->extras[SubjectIdFieldMapping::class] = new SubjectIdFieldMapping($subjectIdMapping);
$classMetadata->extras[SubjectIdFieldMapping::class . '::properties'] = $subjectIdProperties;
}
}

private function sensitiveDataInfo(ReflectionProperty $reflectionProperty): SensitiveDataInfo|null
Expand Down
33 changes: 21 additions & 12 deletions src/Extension/Cryptography/CryptographyMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,39 +36,48 @@
*/
public function hydrate(ClassMetadata $metadata, array $data, array $context, Stack $stack): object
{
/** @var list<PropertyMetadata>|null $properties */
$properties = $metadata->extras[SensitiveDataInfo::class . '::properties'] ?? null;

Check failure on line 40 in src/Extension/Cryptography/CryptographyMiddleware.php

View workflow job for this annotation

GitHub Actions / Static Analysis by PHPStan (locked, 8.5, ubuntu-latest)

PHPDoc tag `@var` for variable $properties contains unknown class Patchlevel\Hydrator\Extension\Cryptography\PropertyMetadata.

if ($properties === null) {
return $stack->next()->hydrate($metadata, $data, $context, $stack);
}

$context[SubjectIds::class] = $subjectIds = $this->resolveSubjectIds($metadata, $data, $context);
$cryptographer = $this->cryptographer;

foreach ($metadata->properties as $propertyMetadata) {
$info = $propertyMetadata->extras[SensitiveDataInfo::class] ?? null;
foreach ($properties as $propertyMetadata) {
$fieldName = $propertyMetadata->fieldName;

Check failure on line 50 in src/Extension/Cryptography/CryptographyMiddleware.php

View workflow job for this annotation

GitHub Actions / Static Analysis by PHPStan (locked, 8.5, ubuntu-latest)

Access to property $fieldName on an unknown class Patchlevel\Hydrator\Extension\Cryptography\PropertyMetadata.

if (!$info instanceof SensitiveDataInfo) {
if (!isset($data[$fieldName])) {

Check failure on line 52 in src/Extension/Cryptography/CryptographyMiddleware.php

View workflow job for this annotation

GitHub Actions / Static Analysis by PHPStan (locked, 8.5, ubuntu-latest)

Possibly invalid array key type mixed.
continue;
}

$value = $data[$propertyMetadata->fieldName] ?? null;
$value = $data[$fieldName];

Check failure on line 56 in src/Extension/Cryptography/CryptographyMiddleware.php

View workflow job for this annotation

GitHub Actions / Static Analysis by PHPStan (locked, 8.5, ubuntu-latest)

Possibly invalid array key type mixed.

if ($value === null) {
if (!$cryptographer->supports($value)) {
continue;
}

if (!$this->cryptographer->supports($value)) {
continue;
}
$info = $propertyMetadata->extras[SensitiveDataInfo::class];

Check failure on line 62 in src/Extension/Cryptography/CryptographyMiddleware.php

View workflow job for this annotation

GitHub Actions / Static Analysis by PHPStan (locked, 8.5, ubuntu-latest)

Cannot access offset 'Patchlevel\\Hydrator\\Extension\\Cryptography\\SensitiveDataInfo' on mixed.

Check failure on line 62 in src/Extension/Cryptography/CryptographyMiddleware.php

View workflow job for this annotation

GitHub Actions / Static Analysis by PHPStan (locked, 8.5, ubuntu-latest)

Access to property $extras on an unknown class Patchlevel\Hydrator\Extension\Cryptography\PropertyMetadata.
assert($info instanceof SensitiveDataInfo);

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

try {
$data[$propertyMetadata->fieldName] = $this->cryptographer->decrypt($subjectId, $value);
$data[$fieldName] = $cryptographer->decrypt($subjectId, $value);

Check failure on line 68 in src/Extension/Cryptography/CryptographyMiddleware.php

View workflow job for this annotation

GitHub Actions / Static Analysis by PHPStan (locked, 8.5, ubuntu-latest)

Possibly invalid array key type mixed.
} catch (DecryptionFailed | CipherKeyNotExists) {
$fallback = $info->fallback instanceof Closure
? ($info->fallback)($subjectId)
: $info->fallback;

if ($propertyMetadata->normalizer) {
$fallback = $propertyMetadata->normalizer->normalize($fallback, $context);
$normalizer = $propertyMetadata->normalizer;

Check failure on line 74 in src/Extension/Cryptography/CryptographyMiddleware.php

View workflow job for this annotation

GitHub Actions / Static Analysis by PHPStan (locked, 8.5, ubuntu-latest)

Access to property $normalizer on an unknown class Patchlevel\Hydrator\Extension\Cryptography\PropertyMetadata.

if ($normalizer !== null) {
$fallback = $normalizer->normalize($fallback, $context);

Check failure on line 77 in src/Extension/Cryptography/CryptographyMiddleware.php

View workflow job for this annotation

GitHub Actions / Static Analysis by PHPStan (locked, 8.5, ubuntu-latest)

Cannot call method normalize() on mixed.
}

$data[$propertyMetadata->fieldName] = $fallback;
$data[$fieldName] = $fallback;

Check failure on line 80 in src/Extension/Cryptography/CryptographyMiddleware.php

View workflow job for this annotation

GitHub Actions / Static Analysis by PHPStan (locked, 8.5, ubuntu-latest)

Possibly invalid array key type mixed.
}
}

Expand Down
76 changes: 73 additions & 3 deletions src/Metadata/ClassMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Patchlevel\Hydrator\Metadata;

use ReflectionClass;
use ReflectionParameter;

/**
* @phpstan-type serialized array{
Expand All @@ -21,7 +22,16 @@ final class ClassMetadata
public readonly string $className;

/** @var array<string, PropertyMetadata> */
public readonly array $properties;
public array $properties;

/** @var list<PropertyMetadata> */
public array $propertiesWithNormalizer;

/** @var list<PropertyMetadata> */
public array $propertiesWithoutNormalizer;

/** @var array<string, ReflectionParameter>|null */
private array|null $promotedConstructorDefaults = null;

/**
* @param ReflectionClass<T> $reflection
Expand All @@ -36,13 +46,29 @@ public function __construct(
) {
$this->className = $reflection->getName();

$this->updateProperties($properties);
}

/** @param list<PropertyMetadata> $properties */
public function updateProperties(array $properties): void
{
$map = [];
$withNormalizer = [];
$withoutNormalizer = [];

foreach ($properties as $property) {
$map[$property->propertyName] = $property;

if ($property->normalizer !== null) {
$withNormalizer[] = $property;
} else {
$withoutNormalizer[] = $property;
}
}

$this->properties = $map;
$this->propertiesWithNormalizer = $withNormalizer;
$this->propertiesWithoutNormalizer = $withoutNormalizer;
}

public function propertyForField(string $name): PropertyMetadata
Expand All @@ -62,12 +88,38 @@ public function newInstance(): object
return $this->reflection->newInstanceWithoutConstructor();
}

/** @return array<string, ReflectionParameter> */
public function promotedConstructorDefaults(): array
{
if ($this->promotedConstructorDefaults !== null) {
return $this->promotedConstructorDefaults;
}

$constructor = $this->reflection->getConstructor();

if (!$constructor) {
return $this->promotedConstructorDefaults = [];
}

$result = [];

foreach ($constructor->getParameters() as $parameter) {
if (!$parameter->isPromoted() || !$parameter->isDefaultValueAvailable()) {
continue;
}

$result[$parameter->getName()] = $parameter;
}

return $this->promotedConstructorDefaults = $result;
}

/** @return serialized */
public function __serialize(): array
{
return [
'className' => $this->className,
'properties' => $this->properties,
'properties' => array_values($this->properties),
'lazy' => $this->lazy,
'extras' => $this->extras,
];
Expand All @@ -77,7 +129,25 @@ public function __serialize(): array
public function __unserialize(array $data): void
{
$this->reflection = new ReflectionClass($data['className']);
$this->properties = $data['properties'];

$map = [];
$withNormalizer = [];
$withoutNormalizer = [];

foreach ($data['properties'] as $property) {
$map[$property->propertyName] = $property;

if ($property->normalizer !== null) {
$withNormalizer[] = $property;
} else {
$withoutNormalizer[] = $property;
}
}

$this->className = $data['className'];
$this->properties = $map;
$this->propertiesWithNormalizer = $withNormalizer;
$this->propertiesWithoutNormalizer = $withoutNormalizer;
$this->lazy = $data['lazy'];
$this->extras = $data['extras'];
}
Expand Down
6 changes: 6 additions & 0 deletions src/Metadata/EnrichingMetadataFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,14 @@ public function metadata(string $class): ClassMetadata
{
$metadata = $this->factory->metadata($class);

$enriched = false;
foreach ($this->enrichers as $enricher) {
$enricher->enrich($metadata);
$enriched = true;
}

if ($enriched) {
$metadata->updateProperties(array_values($metadata->properties));
}

return $metadata;
Expand Down
23 changes: 12 additions & 11 deletions src/MetadataHydrator.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,15 @@ final class MetadataHydrator implements Hydrator
/** @var array<class-string, ClassMetadata> */
private array $classMetadata = [];

private readonly Stack $stack;

/** @param list<Middleware> $middlewares */
public function __construct(
private readonly MetadataFactory $metadataFactory = new AttributeMetadataFactory(),
private readonly array $middlewares = [new TransformMiddleware()],
private readonly bool $defaultLazy = false,
) {
$this->stack = new Stack($this->middlewares);
}

/**
Expand All @@ -48,23 +51,21 @@ public function hydrate(string $class, array $data, array $context = []): object
throw new ClassNotSupported($class, $e);
}

if (PHP_VERSION_ID < 80400) {
$stack = new Stack($this->middlewares);
$stack = clone $this->stack;

if (PHP_VERSION_ID < 80400) {
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, $context, $stack);
}

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

return $stack->next()->hydrate($metadata, $data, $context, $stack);
},
Expand All @@ -79,7 +80,7 @@ function () use ($metadata, $data, $context): object {
public function extract(object $object, array $context = []): array
{
$metadata = $this->metadata($object::class);
$stack = new Stack($this->middlewares);
$stack = clone $this->stack;

return $stack->next()->extract($metadata, $object, $context, $stack);
}
Expand All @@ -93,18 +94,18 @@ public function extract(object $object, array $context = []): array
*/
public function metadata(string $class): ClassMetadata
{
if (array_key_exists($class, $this->classMetadata)) {
if (isset($this->classMetadata[$class])) {
return $this->classMetadata[$class];
}

$this->classMetadata[$class] = $metadata = $this->metadataFactory->metadata($class);

foreach ($metadata->properties as $property) {
if (!($property->normalizer instanceof HydratorAwareNormalizer)) {
continue;
}
$normalizer = $property->normalizer;

$property->normalizer->setHydrator($this);
if ($normalizer instanceof HydratorAwareNormalizer) {
$normalizer->setHydrator($this);
}
}

return $metadata;
Expand Down
10 changes: 1 addition & 9 deletions src/Middleware/Stack.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,6 @@ public function __construct(

public function next(): Middleware
{
$next = $this->middlewares[$this->index] ?? null;

if ($next === null) {
throw new NoMoreMiddleware();
}

$this->index++;

return $next;
return $this->middlewares[$this->index++] ?? throw new NoMoreMiddleware();
}
}
Loading
Loading