Repository: CuyZ/Valinor (php)

Issue:
Parsing enum array not working
Hey there, I am trying to parse a array of integers to a array of integer backed enums like so:

```php
enum SomeEnum: int
{
    case SOME_VALUE  = 1;
    case OTHER_VALUE = 2;
}

class ExampleObject
{
    public function __construct(
        /** @var \SomeEnum[] */
        public array $enumArray,
    ) {}
}

(new \CuyZ\Valinor\MapperBuilder())
    ->mapper()
    ->map(
        ExampleObject::class,
        ['enumArray' => [1, 2]],
    );
```

In this test I get:

```
Fatal error: Uncaught TypeError: CuyZ\Valinor\Type\Parser\GenericCheckerParser::parse(): Argument #1 ($raw) must be of type string, null given, called in vendor/cuyz/valinor/src/Definition/Repository/Reflection/TypeResolver/ClassParentTypeResolver.php on line 47 and defined in vendor/cuyz/valinor/src/Type/Parser/GenericCheckerParser.php:30
```

However in my application it fails differently:

```
Attempt to read property "name" on false {"exception":"[object] (ErrorException(code: 0): Attempt to read property \"name\" on false at vendor/cuyz/valinor/src/Definition/Repository/Reflection/TypeResolver/ClassParentTypeResolver.php:39)
```

I was unable to get a minimal case for this error mode in a minimal way but showing it here in case it's helpful.

From a little debugging (on the second error) I ended up in `ReflectionClassDefinitionRepository@typeResolver` where there is a while loop looping while `$type->className() !== $target->name` however these are for me something like `SomeEnum` and `\SomeEnum` respectively (notice the leading `\`) which seemed odd to me. I did quadruple check it was not a type on my side 👀

Maybe I am doing something wrong or incorrect here, but from my perspective something is not right 😅 I have been trying to dig around but it's a pretty complex codebase so I was unable to devise some fix or better understanding at this time.

Pull request:
fix: properly handle full namespaced enum type in docblock
Fixes #585

Patch:
diff --git a/src/Type/Parser/Exception/Enum/EnumCaseNotFound.php b/src/Type/Parser/Exception/Enum/EnumCaseNotFound.php
index 3db53b5..97f6c20 100644
--- a/src/Type/Parser/Exception/Enum/EnumCaseNotFound.php
+++ b/src/Type/Parser/Exception/Enum/EnumCaseNotFound.php
@@ -6,13 +6,12 @@ namespace CuyZ\Valinor\Type\Parser\Exception\Enum;
 
 use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
 use RuntimeException;
-use UnitEnum;
 
 /** @internal */
 final class EnumCaseNotFound extends RuntimeException implements InvalidType
 {
     /**
-     * @param class-string<UnitEnum> $enumName
+     * @param class-string $enumName
      */
     public function __construct(string $enumName, string $pattern)
     {
diff --git a/src/Type/Types/EnumType.php b/src/Type/Types/EnumType.php
index fb320e9..38f4402 100644
--- a/src/Type/Types/EnumType.php
+++ b/src/Type/Types/EnumType.php
@@ -32,7 +32,8 @@ final class EnumType implements ClassType
      */
     public function __construct(string $enumName, string $pattern, array $cases)
     {
-        $this->enumName = $enumName;
+        // @phpstan-ignore assign.propertyType (it is still an enum class-string)
+        $this->enumName = ltrim($enumName, '\\');
         $this->pattern = $pattern;
 
         if (empty($cases)) {


Test patch:
diff --git a/tests/Integration/Mapping/Object/EnumValuesMappingTest.php b/tests/Integration/Mapping/Object/EnumValuesMappingTest.php
index 8a61cd9..d33136c 100644
--- a/tests/Integration/Mapping/Object/EnumValuesMappingTest.php
+++ b/tests/Integration/Mapping/Object/EnumValuesMappingTest.php
@@ -18,6 +18,7 @@ final class EnumValuesMappingTest extends IntegrationTestCase
         $source = [
             'pureEnumWithFirstValue' => 'FOO',
             'pureEnumWithSecondValue' => 'BAR',
+            'pureEnumWithFullNamespace' => 'FOO',
             'pureEnumWithPattern' => 'BAZ',
             'backedStringEnum' => 'foo',
             'backedStringEnumWithPattern' => 'baz',
@@ -34,6 +35,7 @@ final class EnumValuesMappingTest extends IntegrationTestCase
 
             self::assertSame(PureEnum::FOO, $result->pureEnumWithFirstValue);
             self::assertSame(PureEnum::BAR, $result->pureEnumWithSecondValue);
+            self::assertSame(PureEnum::FOO, $result->pureEnumWithFullNamespace);
             self::assertSame(PureEnum::BAZ, $result->pureEnumWithPattern);
             self::assertSame(BackedStringEnum::FOO, $result->backedStringEnum);
             self::assertSame(BackedStringEnum::BAZ, $result->backedStringEnumWithPattern);
@@ -115,6 +117,9 @@ class EnumValues
 
     public PureEnum $pureEnumWithSecondValue;
 
+    /** @var \CuyZ\Valinor\Tests\Fixture\Enum\PureEnum */
+    public mixed $pureEnumWithFullNamespace;
+
     /** @var PureEnum::BA* */
     public PureEnum $pureEnumWithPattern;
 
@@ -132,6 +137,7 @@ class EnumValues
 class EnumValuesWithConstructor extends EnumValues
 {
     /**
+     * @param \CuyZ\Valinor\Tests\Fixture\Enum\PureEnum $pureEnumWithFullNamespace
      * @param PureEnum::BA* $pureEnumWithPattern
      * @param BackedStringEnum::BA* $backedStringEnumWithPattern
      * @param BackedIntegerEnum::BA* $backedIntegerEnumWithPattern
@@ -139,6 +145,7 @@ class EnumValuesWithConstructor extends EnumValues
     public function __construct(
         PureEnum $pureEnumWithFirstValue,
         PureEnum $pureEnumWithSecondValue,
+        mixed $pureEnumWithFullNamespace,
         PureEnum $pureEnumWithPattern,
         BackedStringEnum $backedStringEnum,
         BackedStringEnum $backedStringEnumWithPattern,
@@ -147,6 +154,7 @@ class EnumValuesWithConstructor extends EnumValues
     ) {
         $this->pureEnumWithFirstValue = $pureEnumWithFirstValue;
         $this->pureEnumWithSecondValue = $pureEnumWithSecondValue;
+        $this->pureEnumWithFullNamespace = $pureEnumWithFullNamespace;
         $this->pureEnumWithPattern = $pureEnumWithPattern;
         $this->backedStringEnum = $backedStringEnum;
         $this->backedIntegerEnum = $backedIntegerEnum;