Repository: microsoft/kiota (csharp)

Issue:
Php integration failing due to wrong parameter types.
Generated clients for php during integration testing contain files with wrong import statements of the string ``type``. Instead of string array an object array is being passed. See image below.
![Image](https://github.com/user-attachments/assets/f4fe3cfa-1f8b-49ed-9969-7e93d003d2e3).

The below log info extracted from one of the test runs further gives a good view of the above scenario.
![Image](https://github.com/user-attachments/assets/5f398b88-dbbc-473b-8046-d884496b876f)

Pull request:
Fixes #5779

Patch:
diff --git a/it/config.json b/it/config.json
index 777f0a36fd..78cab6a5fe 100644
--- a/it/config.json
+++ b/it/config.json
@@ -32,10 +32,6 @@
       {
         "Language": "ruby",
         "Rationale": "https://github.com/microsoft/kiota/issues/1816"
-      },
-      {
-        "Language": "php",
-        "Rationale": "https://github.com/microsoft/kiota/issues/5779"
       }
     ],
     "ExcludePatterns": [
diff --git a/it/php/phpstan.neon b/it/php/phpstan.neon
index 11ffe2a09e..8af9a44026 100644
--- a/it/php/phpstan.neon
+++ b/it/php/phpstan.neon
@@ -7,4 +7,4 @@ parameters:
     - src
   ignoreErrors:
     - '#Parameter [\w\W]+ \$errorMappings of method [\w\\]+RequestAdapter::send[\w]+\(\) expects [\w\W\s]+ given\.#'
-    - '#getFieldDeserializers\(\) should return array\<string, callable\(Microsoft\\Kiota\\Abstractions\\Serialization\\ParseNode\)\: void\> but returns array\{[\d]+\: Closure\(Microsoft\\Kiota\\Abstractions\\Serialization\\ParseNode\)\: void.+#'
+    - '#getFieldDeserializers\(\) should return array\<string, callable\(Microsoft\\Kiota\\Abstractions\\Serialization\\ParseNode\)\: void\> but returns array\{.+\: Closure\(Microsoft\\Kiota\\Abstractions\\Serialization\\ParseNode\)\: void.+#'
diff --git a/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs
index a8601c382a..c1b25d996f 100644
--- a/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs
+++ b/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs
@@ -475,35 +475,42 @@ _ when conventions.PrimitiveTypes.Contains(lowerCaseProp) => $"write{lowerCasePr
     }
 
     private const string ParseNodeVarName = "$parseNode";
-    private string GetDeserializationMethodName(CodeTypeBase propType, CodeMethod method)
+    private (string, string) GetDeserializationMethodName(CodeTypeBase propType, CodeMethod method)
     {
         var isCollection = propType.CollectionKind != CodeTypeBase.CodeTypeCollectionKind.None;
         var propertyType = conventions.GetTypeString(propType, method, false);
-        var parseNodeMethod = string.Empty;
+        var parseNodeMethod = (string.Empty, string.Empty);
         if (propType is CodeType currentType)
         {
             if (isCollection)
                 parseNodeMethod = currentType.TypeDefinition switch
                 {
-                    CodeEnum enumType => $"getCollectionOfEnumValues({enumType.Name.ToFirstCharacterUpperCase()}::class)",
-                    _ => $"getCollectionOfObjectValues([{conventions.TranslateType(propType)}::class, '{CreateDiscriminatorMethodName}'])"
+                    CodeEnum enumType => (string.Empty, $"getCollectionOfEnumValues({enumType.Name.ToFirstCharacterUpperCase()}::class)"),
+                    CodeClass => (string.Empty, $"getCollectionOfObjectValues([{conventions.TranslateType(propType)}::class, '{CreateDiscriminatorMethodName}'])"),
+                    _ => (conventions.TranslateType(propType), $"getCollectionOfPrimitiveValues('{conventions.TranslateType(propType)}')")
                 };
             else if (currentType.TypeDefinition is CodeEnum)
-                parseNodeMethod = $"getEnumValue({propertyType.ToFirstCharacterUpperCase()}::class)";
+                parseNodeMethod = (string.Empty, $"getEnumValue({propertyType.ToFirstCharacterUpperCase()}::class)");
         }
 
         var lowerCaseType = propertyType.ToLowerInvariant();
-        return string.IsNullOrEmpty(parseNodeMethod) ? lowerCaseType switch
-        {
-            "int" => "getIntegerValue()",
-            "bool" => "getBooleanValue()",
-            "number" => "getIntegerValue()",
-            "decimal" or "double" => "getFloatValue()",
-            "streaminterface" => "getBinaryContent()",
-            "byte" => "getByteValue()",
-            _ when conventions.PrimitiveTypes.Contains(lowerCaseType) => $"get{propertyType.ToFirstCharacterUpperCase()}Value()",
-            _ => $"getObjectValue([{propertyType.ToFirstCharacterUpperCase()}::class, '{CreateDiscriminatorMethodName}'])",
-        } : parseNodeMethod;
+        var res = parseNodeMethod;
+        if (string.IsNullOrEmpty(parseNodeMethod.Item2))
+            res = (string.Empty, lowerCaseType switch
+            {
+                "int" => "getIntegerValue()",
+                "bool" => "getBooleanValue()",
+                "number" => "getIntegerValue()",
+                "decimal" or "double" => "getFloatValue()",
+                "streaminterface" => "getBinaryContent()",
+                "byte" => "getByteValue()",
+                _ when conventions.PrimitiveTypes.Contains(lowerCaseType) =>
+                    $"get{propertyType.ToFirstCharacterUpperCase()}Value()",
+                _ =>
+                    $"getObjectValue([{propertyType.ToFirstCharacterUpperCase()}::class, '{CreateDiscriminatorMethodName}'])",
+            });
+
+        return res;
     }
 
     private void WriteSetterBody(LanguageWriter writer, CodeMethod codeElement, CodeClass parentClass)
@@ -672,7 +679,7 @@ private void WriteDeserializerPropertyCallback(CodeProperty property, CodeMethod
             writer.WriteLine("},");
             return;
         }
-        writer.WriteLine($"'{property.WireName}' => fn(ParseNode $n) => $o->{property.Setter!.Name.ToFirstCharacterLowerCase()}($n->{GetDeserializationMethodName(property.Type, method)}),");
+        writer.WriteLine($"'{property.WireName}' => fn(ParseNode $n) => $o->{property.Setter!.Name.ToFirstCharacterLowerCase()}($n->{GetDeserializationMethodName(property.Type, method).Item2}),");
     }
 
     private static void WriteDeserializerBodyForIntersectionModel(CodeClass parentClass, LanguageWriter writer)
@@ -903,7 +910,8 @@ private void WriteFactoryMethodBodyForIntersectionModel(CodeMethod codeElement,
         {
             if (property.Type is CodeType propertyType)
             {
-                var deserializationMethodName = $"{ParseNodeVarName}->{GetDeserializationMethodName(propertyType, codeElement)}";
+                var methodName = GetDeserializationMethodName(propertyType, codeElement);
+                var deserializationMethodName = $"{ParseNodeVarName}->{methodName.Item2}";
                 writer.StartBlock($"{(includeElse ? "} else " : string.Empty)}if ({deserializationMethodName} !== null) {{");
                 writer.WriteLine($"{ResultVarName}->{property.Setter!.Name.ToFirstCharacterLowerCase()}({deserializationMethodName});");
                 writer.DecreaseIndent();
@@ -973,9 +981,16 @@ private void WriteFactoryMethodBodyForUnionModelForUnDiscriminatedTypes(CodeMeth
             .ToArray();
         foreach (var property in otherProps)
         {
-            var serializationMethodName = $"{ParseNodeVarName}->{GetDeserializationMethodName(property.Type, currentElement)}";
+            var methodName = GetDeserializationMethodName(property.Type, currentElement);
+            var serializationMethodName = $"{ParseNodeVarName}->{methodName.Item2}";
+            const string finalValueName = "$finalValue";
             writer.StartBlock($"{(includeElse ? "} else " : string.Empty)}if ({serializationMethodName} !== null) {{");
-            writer.WriteLine($"{ResultVarName}->{property.Setter!.Name.ToFirstCharacterLowerCase()}({serializationMethodName});");
+            if (!string.IsNullOrEmpty(methodName.Item1))
+            {
+                writer.WriteLine($"/** @var array<{methodName.Item1}> {finalValueName} */");
+            }
+            writer.WriteLine($"{finalValueName} = {serializationMethodName};");
+            writer.WriteLine($"{ResultVarName}->{property.Setter!.Name.ToFirstCharacterLowerCase()}({finalValueName});");
             writer.DecreaseIndent();
             if (!includeElse)
                 includeElse = true;


Test patch:
diff --git a/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs
index fe01e34e07..ad5c54b056 100644
--- a/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs
+++ b/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs
@@ -1474,9 +1474,11 @@ public void WritesModelFactoryBodyForUnionModels()
         Assert.Contains("if ('#kiota.complexType1' === $mappingValue) {", result);
         Assert.Contains("$result->setComplexType1Value(new ComplexType1())", result);
         Assert.Contains("if ($parseNode->getStringValue() !== null) {", result);
-        Assert.Contains("$result->setStringValue($parseNode->getStringValue())", result);
+        Assert.Contains("$finalValue = $parseNode->getStringValue()", result);
+        Assert.Contains("$result->setStringValue($finalValue)", result);
         Assert.Contains("else if ($parseNode->getCollectionOfObjectValues([ComplexType2::class, 'createFromDiscriminatorValue']) !== null) {", result);
-        Assert.Contains("$result->setComplexType2Value($parseNode->getCollectionOfObjectValues([ComplexType2::class, 'createFromDiscriminatorValue']))", result);
+        Assert.Contains("$finalValue = $parseNode->getCollectionOfObjectValues([ComplexType2::class, 'createFromDiscriminatorValue'])", result);
+        Assert.Contains("$result->setComplexType2Value($finalValue)", result);
         Assert.Contains("return $result", result);
         Assert.DoesNotContain("return new UnionTypeWrapper()", result);
         AssertExtensions.Before("$parseNode->getStringValue()", "getCollectionOfObjectValues([ComplexType2::class, 'createFromDiscriminatorValue'])", result);