diff --git a/src/Definition/ParameterDefinition.php b/src/Definition/ParameterDefinition.php index 78b74f82..7d4e1629 100644 --- a/src/Definition/ParameterDefinition.php +++ b/src/Definition/ParameterDefinition.php @@ -15,6 +15,7 @@ public function __construct( /** @var non-empty-string */ public readonly string $signature, public readonly Type $type, + public readonly Type $nativeType, public readonly bool $isOptional, public readonly bool $isVariadic, public readonly mixed $defaultValue, diff --git a/src/Definition/PropertyDefinition.php b/src/Definition/PropertyDefinition.php index eb4b40a8..0e249319 100644 --- a/src/Definition/PropertyDefinition.php +++ b/src/Definition/PropertyDefinition.php @@ -15,6 +15,7 @@ public function __construct( /** @var non-empty-string */ public readonly string $signature, public readonly Type $type, + public readonly Type $nativeType, public readonly bool $hasDefaultValue, public readonly mixed $defaultValue, public readonly bool $isPublic, diff --git a/src/Definition/Repository/Cache/Compiler/ParameterDefinitionCompiler.php b/src/Definition/Repository/Cache/Compiler/ParameterDefinitionCompiler.php index 909ab02a..e0b84ab6 100644 --- a/src/Definition/Repository/Cache/Compiler/ParameterDefinitionCompiler.php +++ b/src/Definition/Repository/Cache/Compiler/ParameterDefinitionCompiler.php @@ -20,6 +20,7 @@ public function compile(ParameterDefinition $parameter): string $isVariadic = var_export($parameter->isVariadic, true); $defaultValue = $this->defaultValue($parameter); $type = $this->typeCompiler->compile($parameter->type); + $nativeType = $this->typeCompiler->compile($parameter->nativeType); $attributes = $this->attributesCompiler->compile($parameter->attributes); return <<name}', '{$parameter->signature}', $type, + $nativeType, $isOptional, $isVariadic, $defaultValue, diff --git a/src/Definition/Repository/Cache/Compiler/PropertyDefinitionCompiler.php b/src/Definition/Repository/Cache/Compiler/PropertyDefinitionCompiler.php index 371df3be..070c8b20 100644 --- a/src/Definition/Repository/Cache/Compiler/PropertyDefinitionCompiler.php +++ b/src/Definition/Repository/Cache/Compiler/PropertyDefinitionCompiler.php @@ -17,6 +17,7 @@ public function __construct( public function compile(PropertyDefinition $property): string { $type = $this->typeCompiler->compile($property->type); + $nativeType = $this->typeCompiler->compile($property->nativeType); $hasDefaultValue = var_export($property->hasDefaultValue, true); $defaultValue = var_export($property->defaultValue, true); $isPublic = var_export($property->isPublic, true); @@ -27,6 +28,7 @@ public function compile(PropertyDefinition $property): string '{$property->name}', '{$property->signature}', $type, + $nativeType, $hasDefaultValue, $defaultValue, $isPublic, diff --git a/src/Definition/Repository/Reflection/ReflectionParameterDefinitionBuilder.php b/src/Definition/Repository/Reflection/ReflectionParameterDefinitionBuilder.php index 93425901..37f75282 100644 --- a/src/Definition/Repository/Reflection/ReflectionParameterDefinitionBuilder.php +++ b/src/Definition/Repository/Reflection/ReflectionParameterDefinitionBuilder.php @@ -26,6 +26,7 @@ public function for(ReflectionParameter $reflection, ReflectionTypeResolver $typ $name = $reflection->name; $signature = Reflection::signature($reflection); $type = $typeResolver->resolveType($reflection); + $nativeType = $typeResolver->resolveNativeType($reflection); $isOptional = $reflection->isOptional(); $isVariadic = $reflection->isVariadic(); @@ -48,6 +49,7 @@ public function for(ReflectionParameter $reflection, ReflectionTypeResolver $typ $name, $signature, $type, + $nativeType, $isOptional, $isVariadic, $defaultValue, diff --git a/src/Definition/Repository/Reflection/ReflectionPropertyDefinitionBuilder.php b/src/Definition/Repository/Reflection/ReflectionPropertyDefinitionBuilder.php index f60fa537..64936f33 100644 --- a/src/Definition/Repository/Reflection/ReflectionPropertyDefinitionBuilder.php +++ b/src/Definition/Repository/Reflection/ReflectionPropertyDefinitionBuilder.php @@ -28,6 +28,7 @@ public function for(ReflectionProperty $reflection, ReflectionTypeResolver $type $name = $reflection->name; $signature = Reflection::signature($reflection); $type = $typeResolver->resolveType($reflection); + $nativeType = $typeResolver->resolveNativeType($reflection); $hasDefaultValue = $this->hasDefaultValue($reflection, $type); $defaultValue = $reflection->getDefaultValue(); $isPublic = $reflection->isPublic(); @@ -43,6 +44,7 @@ public function for(ReflectionProperty $reflection, ReflectionTypeResolver $type $name, $signature, $type, + $nativeType, $hasDefaultValue, $defaultValue, $isPublic, diff --git a/src/Definition/Repository/Reflection/ReflectionTypeResolver.php b/src/Definition/Repository/Reflection/ReflectionTypeResolver.php index f548aa08..9b6e01fd 100644 --- a/src/Definition/Repository/Reflection/ReflectionTypeResolver.php +++ b/src/Definition/Repository/Reflection/ReflectionTypeResolver.php @@ -19,28 +19,21 @@ use ReflectionParameter; use ReflectionProperty; +use function trim; + /** @internal */ final class ReflectionTypeResolver { public function __construct( private TypeParser $nativeParser, - private TypeParser $advancedParser + private TypeParser $advancedParser, ) {} public function resolveType(ReflectionProperty|ReflectionParameter|ReflectionFunctionAbstract $reflection): Type { - $nativeType = $this->nativeType($reflection); + $nativeType = $this->resolveNativeType($reflection); $typeFromDocBlock = $this->typeFromDocBlock($reflection); - if (! $nativeType && ! $typeFromDocBlock) { - return MixedType::get(); - } - - if (! $nativeType) { - /** @var Type $typeFromDocBlock */ - return $typeFromDocBlock; - } - if (! $typeFromDocBlock) { // When the type is a class, it may declare templates that must be // filled with generics. PHP does not handle generics natively, so @@ -53,16 +46,33 @@ public function resolveType(ReflectionProperty|ReflectionParameter|ReflectionFun return $nativeType; } - if (! $typeFromDocBlock instanceof UnresolvableType - && ! $nativeType instanceof UnresolvableType - && ! $typeFromDocBlock->matches($nativeType) - ) { + if ($typeFromDocBlock instanceof UnresolvableType) { + return $typeFromDocBlock; + } + + if (! $typeFromDocBlock->matches($nativeType)) { throw new TypesDoNotMatch($reflection, $typeFromDocBlock, $nativeType); } return $typeFromDocBlock; } + public function resolveNativeType(ReflectionProperty|ReflectionParameter|ReflectionFunctionAbstract $reflection): Type + { + $reflectionType = $reflection instanceof ReflectionFunctionAbstract + ? $reflection->getReturnType() + : $reflection->getType(); + + if (! $reflectionType) { + return MixedType::get(); + } + + $type = Reflection::flattenType($reflectionType); + $type = $this->parseType($type, $reflection, $this->nativeParser); + + return $this->handleVariadicType($reflection, $type); + } + private function typeFromDocBlock(ReflectionProperty|ReflectionParameter|ReflectionFunctionAbstract $reflection): ?Type { if ($reflection instanceof ReflectionFunctionAbstract) { @@ -91,22 +101,6 @@ private function typeFromDocBlock(ReflectionProperty|ReflectionParameter|Reflect return $this->handleVariadicType($reflection, $type); } - private function nativeType(ReflectionProperty|ReflectionParameter|ReflectionFunctionAbstract $reflection): ?Type - { - $reflectionType = $reflection instanceof ReflectionFunctionAbstract - ? $reflection->getReturnType() - : $reflection->getType(); - - if (! $reflectionType) { - return null; - } - - $type = Reflection::flattenType($reflectionType); - $type = $this->parseType($type, $reflection, $this->nativeParser); - - return $this->handleVariadicType($reflection, $type); - } - private function parseType(string $raw, ReflectionProperty|ReflectionParameter|ReflectionFunctionAbstract $reflection, TypeParser $parser): Type { try { diff --git a/tests/Fake/Definition/FakeFunctionDefinition.php b/tests/Fake/Definition/FakeFunctionDefinition.php index a6afabe8..e7771e03 100644 --- a/tests/Fake/Definition/FakeFunctionDefinition.php +++ b/tests/Fake/Definition/FakeFunctionDefinition.php @@ -31,6 +31,7 @@ public static function new(string $fileName = null): FunctionDefinition 'bar', 'foo::bar', NativeStringType::get(), + NativeStringType::get(), false, false, 'foo', diff --git a/tests/Fake/Definition/FakeParameterDefinition.php b/tests/Fake/Definition/FakeParameterDefinition.php index 876b5245..375fd59d 100644 --- a/tests/Fake/Definition/FakeParameterDefinition.php +++ b/tests/Fake/Definition/FakeParameterDefinition.php @@ -23,6 +23,7 @@ public static function new(string $name = 'someParameter', Type $type = null): P $name, $name, $type ?? new FakeType(), + $type ?? new FakeType(), false, false, null, @@ -39,6 +40,7 @@ public static function optional(string $name, Type $type, mixed $defaultValue): $name, $name, $type, + $type, true, false, $defaultValue, @@ -60,6 +62,7 @@ public static function fromReflection(ReflectionParameter $reflection): Paramete $name, 'Signature::' . $reflection->name, $type, + $type, $reflection->isOptional(), $reflection->isVariadic(), $reflection->isDefaultValueAvailable() ? $reflection->getDefaultValue() : null, diff --git a/tests/Fake/Definition/FakePropertyDefinition.php b/tests/Fake/Definition/FakePropertyDefinition.php index 060dfcf7..65c28a34 100644 --- a/tests/Fake/Definition/FakePropertyDefinition.php +++ b/tests/Fake/Definition/FakePropertyDefinition.php @@ -23,6 +23,7 @@ public static function new(string $name = 'someProperty'): PropertyDefinition $name, $name, new MixedType(), + new MixedType(), false, null, false, @@ -45,6 +46,7 @@ public static function fromReflection(ReflectionProperty $reflection): PropertyD $name, 'Signature::' . $reflection->name, $type, + new MixedType(), isset($defaultProperties[$reflection->name]), $defaultProperties[$reflection->name] ?? null, $reflection->isPublic(), diff --git a/tests/Functional/Definition/Repository/Cache/Compiler/FunctionDefinitionCompilerTest.php b/tests/Functional/Definition/Repository/Cache/Compiler/FunctionDefinitionCompilerTest.php index fc5d3d29..ab848375 100644 --- a/tests/Functional/Definition/Repository/Cache/Compiler/FunctionDefinitionCompilerTest.php +++ b/tests/Functional/Definition/Repository/Cache/Compiler/FunctionDefinitionCompilerTest.php @@ -40,6 +40,7 @@ public function test_function_is_compiled_correctly(): void 'bar', 'foo::bar', NativeStringType::get(), + NativeStringType::get(), false, false, 'foo', diff --git a/tests/Unit/Definition/ParameterDefinitionTest.php b/tests/Unit/Definition/ParameterDefinitionTest.php index 70d5e443..76d62000 100644 --- a/tests/Unit/Definition/ParameterDefinitionTest.php +++ b/tests/Unit/Definition/ParameterDefinitionTest.php @@ -25,6 +25,7 @@ public function test_parameter_data_can_be_retrieved(): void $name, $signature, $type, + $type, $isOptional, $isVariadic, $defaultValue, diff --git a/tests/Unit/Definition/PropertyDefinitionTest.php b/tests/Unit/Definition/PropertyDefinitionTest.php index eae94171..79691037 100644 --- a/tests/Unit/Definition/PropertyDefinitionTest.php +++ b/tests/Unit/Definition/PropertyDefinitionTest.php @@ -16,6 +16,7 @@ public function test_property_data_can_be_retrieved(): void $name = 'someProperty'; $signature = 'somePropertySignature'; $type = new FakeType(); + $nativeType = new FakeType(); $hasDefaultValue = true; $defaultValue = 'Some property default value'; $isPublic = true; @@ -25,6 +26,7 @@ public function test_property_data_can_be_retrieved(): void $name, $signature, $type, + $nativeType, $hasDefaultValue, $defaultValue, $isPublic, @@ -34,6 +36,7 @@ public function test_property_data_can_be_retrieved(): void self::assertSame($name, $property->name); self::assertSame($signature, $property->signature); self::assertSame($type, $property->type); + self::assertSame($nativeType, $property->nativeType); self::assertSame($hasDefaultValue, $property->hasDefaultValue); self::assertSame($defaultValue, $property->defaultValue); self::assertSame($isPublic, $property->isPublic);