Skip to content

Commit

Permalink
Merge branch 'master' into fix/properly-handle-class-sharing-class-na…
Browse files Browse the repository at this point in the history
…me-and-namespace-group-name
  • Loading branch information
romm authored Sep 24, 2024
2 parents f82591c + e695b29 commit e3f49f3
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 30 deletions.
49 changes: 28 additions & 21 deletions src/Cache/KeySanitizerCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

namespace CuyZ\Valinor\Cache;

use Closure;
use CuyZ\Valinor\Utility\Package;
use Psr\SimpleCache\CacheInterface;
use Traversable;

use function sha1;
use function hash;
use function strstr;

/**
* @internal
Expand All @@ -21,21 +21,28 @@ final class KeySanitizerCache implements WarmupCache
{
private static string $version;

/** @var Closure(string): string */
private Closure $sanitize;

public function __construct(
/** @var CacheInterface<EntryType> */
private CacheInterface $delegate
) {
// Two things:
// 1. We append the current version of the package to the cache key in
// order to avoid collisions between entries from different versions
// of the library.
// 2. The key is sha1'd so that it does not contain illegal characters.
// @see https://www.php-fig.org/psr/psr-16/#12-definitions
// @infection-ignore-all
$this->sanitize = static fn (string $key) => $key . sha1(self::$version ??= PHP_VERSION . '/' . Package::version());
) {}

/**
* Two things:
* 1. We append the current version of the package to the cache key in order
* to avoid collisions between entries from different versions of the
* library.
* 2. The key is hashed so that it does not contain illegal characters.
* @see https://www.php-fig.org/psr/psr-16/#12-definitions
*
* @infection-ignore-all
*/
private function sanitize(string $key): string
{
self::$version ??= PHP_VERSION . '/' . Package::version();

$firstPart = strstr($key, "\0", before_needle: true);

return $firstPart . hash('xxh128', $key . self::$version);
}

public function warmup(): void
Expand All @@ -47,17 +54,17 @@ public function warmup(): void

public function get($key, $default = null): mixed
{
return $this->delegate->get(($this->sanitize)($key), $default);
return $this->delegate->get($this->sanitize($key), $default);
}

public function set($key, $value, $ttl = null): bool
{
return $this->delegate->set(($this->sanitize)($key), $value, $ttl);
return $this->delegate->set($this->sanitize($key), $value, $ttl);
}

public function delete($key): bool
{
return $this->delegate->delete(($this->sanitize)($key));
return $this->delegate->delete($this->sanitize($key));
}

public function clear(): bool
Expand All @@ -67,7 +74,7 @@ public function clear(): bool

public function has($key): bool
{
return $this->delegate->has(($this->sanitize)($key));
return $this->delegate->has($this->sanitize($key));
}

/**
Expand All @@ -76,7 +83,7 @@ public function has($key): bool
public function getMultiple($keys, $default = null): Traversable
{
foreach ($keys as $key) {
yield $key => $this->delegate->get(($this->sanitize)($key), $default);
yield $key => $this->delegate->get($this->sanitize($key), $default);
}
}

Expand All @@ -85,7 +92,7 @@ public function setMultiple($values, $ttl = null): bool
$versionedValues = [];

foreach ($values as $key => $value) {
$versionedValues[($this->sanitize)($key)] = $value;
$versionedValues[$this->sanitize($key)] = $value;
}

return $this->delegate->setMultiple($versionedValues, $ttl);
Expand All @@ -96,7 +103,7 @@ public function deleteMultiple($keys): bool
$transformedKeys = [];

foreach ($keys as $key) {
$transformedKeys[] = ($this->sanitize)($key);
$transformedKeys[] = $this->sanitize($key);
}

return $this->delegate->deleteMultiple($transformedKeys);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
use CuyZ\Valinor\Type\ObjectType;
use Psr\SimpleCache\CacheInterface;

use function sha1;

/** @internal */
final class CacheClassDefinitionRepository implements ClassDefinitionRepository
{
Expand All @@ -23,7 +21,7 @@ public function __construct(
public function for(ObjectType $type): ClassDefinition
{
// @infection-ignore-all
$key = 'class-definition' . sha1($type->toString());
$key = "class-definition-\0" . $type->toString();

$entry = $this->cache->get($key);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
use CuyZ\Valinor\Utility\Reflection\Reflection;
use Psr\SimpleCache\CacheInterface;

use function sha1;

/** @internal */
final class CacheFunctionDefinitionRepository implements FunctionDefinitionRepository
{
Expand All @@ -25,7 +23,7 @@ public function for(callable $function): FunctionDefinition
$reflection = Reflection::function($function);

// @infection-ignore-all
$key = 'function-definition-' . sha1($reflection->getFileName() . $reflection->getStartLine() . $reflection->getEndLine());
$key = "function-definition-\0" . $reflection->getFileName() . ':' . $reflection->getStartLine() . '-' . $reflection->getEndLine();

$entry = $this->cache->get($key);

Expand Down
9 changes: 6 additions & 3 deletions src/Normalizer/Transformer/RecursiveTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use CuyZ\Valinor\Normalizer\AsTransformer;
use CuyZ\Valinor\Normalizer\Exception\CircularReferenceFoundDuringNormalization;
use CuyZ\Valinor\Normalizer\Exception\TypeUnhandledByNormalizer;
use CuyZ\Valinor\Type\Types\EnumType;
use CuyZ\Valinor\Type\Types\NativeClassType;
use DateTimeInterface;
use DateTimeZone;
Expand Down Expand Up @@ -63,10 +64,12 @@ private function doTransform(mixed $value, WeakMap $references, array $attribute

// @infection-ignore-all
$references[$value] = true;
}

if (is_object($value)) {
$classAttributes = $this->classDefinitionRepository->for(new NativeClassType($value::class))->attributes;
$type = $value instanceof UnitEnum
? EnumType::native($value::class)
: new NativeClassType($value::class);

$classAttributes = $this->classDefinitionRepository->for($type)->attributes;
$classAttributes = $this->filterAttributes($classAttributes);

$attributes = [...$attributes, ...$classAttributes];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace CuyZ\Valinor\Tests\Integration\NonRegression;

use CuyZ\Valinor\Normalizer\Format;
use CuyZ\Valinor\Tests\Fixture\Enum\BackedStringEnum;
use CuyZ\Valinor\Tests\Integration\IntegrationTestCase;

final class NormalizeEnumDoesNotBreakMapperTest extends IntegrationTestCase
{
/**
* The normalizer will at some point fetch the class definition of the enum,
* we need to ensure an `EnumType` is used and not a `NativeClassType`,
* otherwise the cache would be corrupted for further usage.
*
* @see https://github.com/CuyZ/Valinor/issues/562
*/
public function test_normalizing_enum_and_then_map_value_on_same_enum_class_does_not_break(): void
{
$mapperBuilder = $this->mapperBuilder();

$mapperBuilder->normalizer(Format::array())->normalize(BackedStringEnum::FOO);

$result = $mapperBuilder->mapper()->map(BackedStringEnum::class, 'foo');

self::assertSame(BackedStringEnum::FOO, $result);
}
}
26 changes: 26 additions & 0 deletions tests/Integration/Normalizer/NormalizerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,16 @@ public static function normalize_basic_values_yields_expected_output_data_provid
'expected json' => '42',
];

yield 'enum with transformer attribute' => [
'input' => SomeEnumWithTransformerAttribute::FOO,
'expected array' => 'normalizedValue-foo',
'expected json' => '"normalizedValue-foo"',
'transformers' => [],
'transformerAttributes' => [
TransformEnumToString::class,
],
];

yield 'class with public properties' => [
'input' => new class () {
public string $string = 'foo';
Expand Down Expand Up @@ -1381,3 +1391,19 @@ public function __construct(

#[TransformObjectToString]
final class SomeClassWithAttributeToTransformObjectToString {}

#[Attribute(Attribute::TARGET_CLASS)]
final class TransformEnumToString
{
public function normalize(SomeEnumWithTransformerAttribute $enum): string
{
return 'normalizedValue-' . $enum->value;
}
}

#[TransformEnumToString]
enum SomeEnumWithTransformerAttribute: string
{
case FOO = 'foo';
case BAR = 'bar';
}

0 comments on commit e3f49f3

Please sign in to comment.