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
2 changes: 1 addition & 1 deletion phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ parameters:
path: tests/Unit/Metadata/AttributeMetadataFactoryTest.php

-
message: '#^Parameter \#1 \$class of method Patchlevel\\Hydrator\\MetadataHydrator\:\:hydrate\(\) expects class\-string\<Unknown\>, string given\.$#'
message: '#^Parameter \#1 \$class of method Patchlevel\\Hydrator\\Hydrator\:\:hydrate\(\) expects class\-string\<Unknown\>, string given\.$#'
identifier: argument.type
count: 1
path: tests/Unit/MetadataHydratorTest.php
Expand Down
17 changes: 17 additions & 0 deletions src/CoreExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace Patchlevel\Hydrator;

use Patchlevel\Hydrator\Guesser\BuiltInGuesser;
use Patchlevel\Hydrator\Middleware\TransformMiddleware;

final class CoreExtension implements Extension
{
public function configure(HydratorBuilder $builder): void
{
$builder->addMiddleware(new TransformMiddleware(), -64);
$builder->addGuesser(new BuiltInGuesser(), -64);
}
}
22 changes: 22 additions & 0 deletions src/Cryptography/CryptographyExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Patchlevel\Hydrator\Cryptography;

use Patchlevel\Hydrator\Extension;
use Patchlevel\Hydrator\HydratorBuilder;

final class CryptographyExtension implements Extension
{
public function __construct(
private readonly PayloadCryptographer $cryptography,
) {
}

public function configure(HydratorBuilder $builder): void
{
$builder->addMetadataEnricher(new CryptographyMetadataEnricher(), 64);
$builder->addMiddleware(new CryptographyMiddleware($this->cryptography), 64);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,18 @@
use Patchlevel\Hydrator\Attribute\DataSubjectId;
use Patchlevel\Hydrator\Attribute\SensitiveData;
use Patchlevel\Hydrator\Metadata\ClassMetadata;
use Patchlevel\Hydrator\Metadata\MetadataFactory;
use Patchlevel\Hydrator\Metadata\MetadataEnricher;
use ReflectionProperty;

use function array_key_exists;

final class CryptographyMetadataFactory implements MetadataFactory
final class CryptographyMetadataEnricher implements MetadataEnricher
{
public function __construct(
private readonly MetadataFactory $metadataFactory,
) {
}

public function metadata(string $class): ClassMetadata
public function enrich(ClassMetadata $classMetadata): void
{
$metadata = $this->metadataFactory->metadata($class);

$subjectIdMapping = [];

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

Expand All @@ -34,8 +27,8 @@ public function metadata(string $class): ClassMetadata

if (array_key_exists($subjectIdIdentifier, $subjectIdMapping)) {
throw new DuplicateSubjectIdIdentifier(
$metadata->className,
$metadata->propertyForField($subjectIdMapping[$subjectIdIdentifier])->propertyName,
$classMetadata->className,
$classMetadata->propertyForField($subjectIdMapping[$subjectIdIdentifier])->propertyName,
$property->propertyName,
$subjectIdIdentifier,
);
Expand All @@ -53,17 +46,17 @@ public function metadata(string $class): ClassMetadata
}

if ($isSubjectId) {
throw new SubjectIdAndSensitiveDataConflict($metadata->className, $property->propertyName);
throw new SubjectIdAndSensitiveDataConflict($classMetadata->className, $property->propertyName);
}

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

if ($subjectIdMapping !== []) {
$metadata->extras[SubjectIdFieldMapping::class] = new SubjectIdFieldMapping($subjectIdMapping);
if ($subjectIdMapping === []) {
return;
}

return $metadata;
$classMetadata->extras[SubjectIdFieldMapping::class] = new SubjectIdFieldMapping($subjectIdMapping);
}

private function sensitiveDataInfo(ReflectionProperty $reflectionProperty): SensitiveDataInfo|null
Expand Down
10 changes: 10 additions & 0 deletions src/Extension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace Patchlevel\Hydrator;

interface Extension
{
public function configure(HydratorBuilder $builder): void;
}
82 changes: 82 additions & 0 deletions src/HydratorBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

declare(strict_types=1);

namespace Patchlevel\Hydrator;

use Patchlevel\Hydrator\Guesser\ChainGuesser;
use Patchlevel\Hydrator\Guesser\Guesser;
use Patchlevel\Hydrator\Metadata\AttributeMetadataFactory;
use Patchlevel\Hydrator\Metadata\MetadataEnricher;
use Patchlevel\Hydrator\Middleware\Middleware;

use function array_merge;
use function krsort;

final class HydratorBuilder
{
private bool $defaultLazy = false;

/** @var array<int, list<Middleware>> */
private array $middlewares = [];

/** @var array<int, list<MetadataEnricher>> */
private array $metadataEnrichers = [];

/** @var array<int, list<Guesser>> */
private array $guessers = [];

/** @return $this */
public function addMiddleware(Middleware $middleware, int $priority = 0): static

Check warning on line 30 in src/HydratorBuilder.php

View workflow job for this annotation

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

Escaped Mutant for Mutator "IncrementInteger": @@ @@ /** @var array<int, list<Guesser>> */ private array $guessers = []; /** @return $this */ - public function addMiddleware(Middleware $middleware, int $priority = 0): static + public function addMiddleware(Middleware $middleware, int $priority = 1): static { $this->middlewares[$priority][] = $middleware; return $this;

Check warning on line 30 in src/HydratorBuilder.php

View workflow job for this annotation

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

Escaped Mutant for Mutator "DecrementInteger": @@ @@ /** @var array<int, list<Guesser>> */ private array $guessers = []; /** @return $this */ - public function addMiddleware(Middleware $middleware, int $priority = 0): static + public function addMiddleware(Middleware $middleware, int $priority = -1): static { $this->middlewares[$priority][] = $middleware; return $this;
{
$this->middlewares[$priority][] = $middleware;

return $this;
}

/** @return $this */
public function addMetadataEnricher(MetadataEnricher $enricher, int $priority = 0): static

Check warning on line 38 in src/HydratorBuilder.php

View workflow job for this annotation

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

Escaped Mutant for Mutator "IncrementInteger": @@ @@ return $this; } /** @return $this */ - public function addMetadataEnricher(MetadataEnricher $enricher, int $priority = 0): static + public function addMetadataEnricher(MetadataEnricher $enricher, int $priority = 1): static { $this->metadataEnrichers[$priority][] = $enricher; return $this;

Check warning on line 38 in src/HydratorBuilder.php

View workflow job for this annotation

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

Escaped Mutant for Mutator "DecrementInteger": @@ @@ return $this; } /** @return $this */ - public function addMetadataEnricher(MetadataEnricher $enricher, int $priority = 0): static + public function addMetadataEnricher(MetadataEnricher $enricher, int $priority = -1): static { $this->metadataEnrichers[$priority][] = $enricher; return $this;
{
$this->metadataEnrichers[$priority][] = $enricher;

return $this;
}

/** @return $this */
public function addGuesser(Guesser $guesser, int $priority = 0): static

Check warning on line 46 in src/HydratorBuilder.php

View workflow job for this annotation

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

Escaped Mutant for Mutator "IncrementInteger": @@ @@ return $this; } /** @return $this */ - public function addGuesser(Guesser $guesser, int $priority = 0): static + public function addGuesser(Guesser $guesser, int $priority = 1): static { $this->guessers[$priority][] = $guesser; return $this;

Check warning on line 46 in src/HydratorBuilder.php

View workflow job for this annotation

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

Escaped Mutant for Mutator "DecrementInteger": @@ @@ return $this; } /** @return $this */ - public function addGuesser(Guesser $guesser, int $priority = 0): static + public function addGuesser(Guesser $guesser, int $priority = -1): static { $this->guessers[$priority][] = $guesser; return $this;
{
$this->guessers[$priority][] = $guesser;

return $this;
}

public function enableDefaultLazy(bool $lazy = true): static
{
$this->defaultLazy = $lazy;

return $this;
}

public function useExtension(Extension $extension): static
{
$extension->configure($this);

return $this;
}

public function build(): Hydrator
{
krsort($this->middlewares);
krsort($this->metadataEnrichers);
krsort($this->guessers);

return new MetadataHydrator(
new AttributeMetadataFactory(
guesser: new ChainGuesser(array_merge(...$this->guessers)),
),
array_merge(...$this->middlewares),
array_merge(...$this->metadataEnrichers),
$this->defaultLazy,
);
}
}
2 changes: 1 addition & 1 deletion src/Metadata/AttributeMetadataFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public function __construct(
Guesser|null $guesser = null,
) {
$this->typeResolver = $typeResolver ?: TypeResolver::create();
$this->guesser = $guesser ?: new BuiltInGuesser(false);
$this->guesser = $guesser ?: new BuiltInGuesser();
}

/**
Expand Down
10 changes: 10 additions & 0 deletions src/Metadata/MetadataEnricher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace Patchlevel\Hydrator\Metadata;

interface MetadataEnricher
{
public function enrich(ClassMetadata $classMetadata): void;
}
40 changes: 9 additions & 31 deletions src/MetadataHydrator.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,13 @@

namespace Patchlevel\Hydrator;

use Patchlevel\Hydrator\Guesser\BuiltInGuesser;
use Patchlevel\Hydrator\Guesser\ChainGuesser;
use Patchlevel\Hydrator\Guesser\Guesser;
use Patchlevel\Hydrator\Metadata\AttributeMetadataFactory;
use Patchlevel\Hydrator\Metadata\ClassMetadata;
use Patchlevel\Hydrator\Metadata\ClassNotFound;
use Patchlevel\Hydrator\Metadata\MetadataEnricher;
use Patchlevel\Hydrator\Metadata\MetadataFactory;
use Patchlevel\Hydrator\Middleware\Middleware;
use Patchlevel\Hydrator\Middleware\Stack;
use Patchlevel\Hydrator\Middleware\TransformMiddleware;
use Patchlevel\Hydrator\Normalizer\HydratorAwareNormalizer;
use ReflectionClass;

Expand All @@ -26,10 +23,14 @@
/** @var array<class-string, ClassMetadata> */
private array $classMetadata = [];

/** @param list<Middleware> $middlewares */
/**
* @param list<Middleware> $middlewares
* @param list<MetadataEnricher> $metadataEnrichers
*/
public function __construct(
private readonly MetadataFactory $metadataFactory = new AttributeMetadataFactory(),
private readonly array $middlewares = [],
private readonly array $metadataEnrichers = [],
private readonly bool $defaultLazy = false,
) {
}
Expand Down Expand Up @@ -110,33 +111,10 @@
$property->normalizer->setHydrator($this);
}

return $metadata;
}

/**
* @param list<Middleware> $additionalMiddleware
* @param iterable<Guesser> $guessers
*/
public static function create(
array $additionalMiddleware = [],
iterable $guessers = [],
bool $defaultLazy = false,
): self {
$guesser = new BuiltInGuesser();

if ($guessers !== []) {
$guesser = new ChainGuesser([
...$guessers,
$guesser,
]);
foreach ($this->metadataEnrichers as $enricher) {
$enricher->enrich($metadata);

Check warning on line 115 in src/MetadataHydrator.php

View workflow job for this annotation

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

Escaped Mutant for Mutator "MethodCallRemoval": @@ @@ $property->normalizer->setHydrator($this); } foreach ($this->metadataEnrichers as $enricher) { - $enricher->enrich($metadata); + } return $metadata; } }
}

return new self(
new AttributeMetadataFactory(
guesser: $guesser,
),
[...$additionalMiddleware, new TransformMiddleware()],
$defaultLazy,
);
return $metadata;
}
}
7 changes: 5 additions & 2 deletions tests/Benchmark/HydratorBench.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

namespace Patchlevel\Hydrator\Tests\Benchmark;

use Patchlevel\Hydrator\CoreExtension;
use Patchlevel\Hydrator\Hydrator;
use Patchlevel\Hydrator\MetadataHydrator;
use Patchlevel\Hydrator\HydratorBuilder;
use Patchlevel\Hydrator\Tests\Benchmark\Fixture\ProfileCreated;
use Patchlevel\Hydrator\Tests\Benchmark\Fixture\ProfileId;
use Patchlevel\Hydrator\Tests\Benchmark\Fixture\Skill;
Expand All @@ -18,7 +19,9 @@ final class HydratorBench

public function __construct()
{
$this->hydrator = MetadataHydrator::create();
$this->hydrator = (new HydratorBuilder())
->useExtension(new CoreExtension())
->build();
}

public function setUp(): void
Expand Down
19 changes: 7 additions & 12 deletions tests/Benchmark/HydratorWithCryptographyBench.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@

namespace Patchlevel\Hydrator\Tests\Benchmark;

use Patchlevel\Hydrator\Cryptography\CryptographyMetadataFactory;
use Patchlevel\Hydrator\Cryptography\CryptographyMiddleware;
use Patchlevel\Hydrator\CoreExtension;
use Patchlevel\Hydrator\Cryptography\CryptographyExtension;
use Patchlevel\Hydrator\Cryptography\SensitiveDataPayloadCryptographer;
use Patchlevel\Hydrator\Cryptography\Store\InMemoryCipherKeyStore;
use Patchlevel\Hydrator\Hydrator;
use Patchlevel\Hydrator\Metadata\AttributeMetadataFactory;
use Patchlevel\Hydrator\MetadataHydrator;
use Patchlevel\Hydrator\Middleware\TransformMiddleware;
use Patchlevel\Hydrator\HydratorBuilder;
use Patchlevel\Hydrator\Tests\Benchmark\Fixture\ProfileCreated;
use Patchlevel\Hydrator\Tests\Benchmark\Fixture\ProfileId;
use Patchlevel\Hydrator\Tests\Benchmark\Fixture\Skill;
Expand All @@ -28,13 +26,10 @@ public function __construct()
{
$this->store = new InMemoryCipherKeyStore();

$this->hydrator = new MetadataHydrator(
new CryptographyMetadataFactory(new AttributeMetadataFactory()),
[
new CryptographyMiddleware(SensitiveDataPayloadCryptographer::createWithDefaultSettings($this->store)),
new TransformMiddleware(),
],
);
$this->hydrator = (new HydratorBuilder())
->useExtension(new CoreExtension())
->useExtension(new CryptographyExtension(SensitiveDataPayloadCryptographer::createWithDefaultSettings($this->store)))
->build();
}

public function setUp(): void
Expand Down
8 changes: 6 additions & 2 deletions tests/Benchmark/HydratorWithLazyBench.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

namespace Patchlevel\Hydrator\Tests\Benchmark;

use Patchlevel\Hydrator\CoreExtension;
use Patchlevel\Hydrator\Hydrator;
use Patchlevel\Hydrator\MetadataHydrator;
use Patchlevel\Hydrator\HydratorBuilder;
use Patchlevel\Hydrator\Tests\Benchmark\Fixture\ProfileCreated;
use PhpBench\Attributes as Bench;

Expand All @@ -16,7 +17,10 @@ final class HydratorWithLazyBench

public function __construct()
{
$this->hydrator = MetadataHydrator::create(defaultLazy: true);
$this->hydrator = (new HydratorBuilder())
->useExtension(new CoreExtension())
->enableDefaultLazy()
->build();
}

public function setUp(): void
Expand Down
Loading
Loading