From 1ab0b04bc92f97980b3cf2c90efd5bf8de0b17ce Mon Sep 17 00:00:00 2001 From: Aayush Pandey <108435719+AayushP123@users.noreply.github.com> Date: Sat, 1 Aug 2026 02:10:39 -0700 Subject: [PATCH] Fix PHP numeric deserializer keys --- CHANGELOG.md | 1 + .../Writers/Php/CodeMethodWriter.cs | 21 +++++++++++++------ .../Writers/Php/CodeMethodWriterTests.cs | 8 +++++++ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a24e5cc50e..c386172160 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- PHP: generated deserializers with numeric property names now satisfy static return-type analysis. [#7830](https://github.com/microsoft/kiota/issues/7830) - golang: generated code now always uses LF line endings, so `gofmt` no longer reports formatting differences when generating on Windows. - golang: make sure all generated code adheres to golangs coding standards - Fixed non-deterministic model class descriptions when a component schema is referenced from multiple properties with differing reference-level descriptions. [#7927](https://github.com/microsoft/kiota/issues/7927) diff --git a/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs index a49d9787f8..e5f1491803 100644 --- a/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using Kiota.Builder.CodeDOM; using Kiota.Builder.Extensions; @@ -680,23 +681,31 @@ private void WriteDeserializerBody(CodeClass parentClass, LanguageWriter writer, } private void WriteDeserializerBodyForInheritedModel(CodeMethod method, CodeClass parentClass, LanguageWriter writer, bool extendsModelClass = false) { - var codeProperties = parentClass.GetPropertiesOfKind(CodePropertyKind.Custom).ToArray(); + var codeProperties = parentClass.GetPropertiesOfKind(CodePropertyKind.Custom) + .Where(static x => !x.ExistsInBaseType && x.Setter != null) + .OrderBy(static x => x.Name) + .ToArray(); + var hasNumericPropertyName = codeProperties.Any(static x => IsNumericArrayKey(x.WireName)); writer.WriteLine("$o = $this;"); + if (hasNumericPropertyName) + writer.WriteLine("/** @var array $deserializers */"); writer.WriteLines( - $"return {((extendsModelClass) ? $"array_merge(parent::{method.Name.ToFirstCharacterLowerCase()}(), [" : " [")}"); + $"{(hasNumericPropertyName ? "$deserializers =" : "return")} {((extendsModelClass) ? $"array_merge(parent::{method.Name.ToFirstCharacterLowerCase()}(), [" : " [")}"); writer.IncreaseIndent(); if (codeProperties.Length != 0) - { codeProperties - .Where(static x => !x.ExistsInBaseType && x.Setter != null) - .OrderBy(static x => x.Name) .ToList() .ForEach(x => WriteDeserializerPropertyCallback(x, method, writer)); - } writer.DecreaseIndent(); writer.WriteLine(extendsModelClass ? "]);" : "];"); + if (hasNumericPropertyName) + writer.WriteLine("return $deserializers;"); } + private static bool IsNumericArrayKey(string key) => + long.TryParse(key, NumberStyles.Integer, CultureInfo.InvariantCulture, out var value) && + value.ToString(CultureInfo.InvariantCulture).Equals(key, StringComparison.Ordinal); + private void WriteDeserializerPropertyCallback(CodeProperty property, CodeMethod method, LanguageWriter writer) { if (property.Type.CollectionKind != CodeTypeBase.CodeTypeCollectionKind.None diff --git a/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs index 2229faa142..75021b1d91 100644 --- a/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs @@ -962,6 +962,14 @@ public async Task EscapesIndexerPathParameterNameAsync() "'dOB' => fn(ParseNode $n) => $o->setDOB($n->getDateTimeValue())," }, new object[] + { + new CodeProperty { Name = "fiveHundred", Type = new CodeType { Name = "int32" }, Access = AccessModifier.Private, Kind = CodePropertyKind.Custom, SerializationName = "500" }, + "/** @var array $deserializers */", + "$deserializers =", + "'500' => fn(ParseNode $n) => $o->setFiveHundred($n->getIntegerValue()),", + "return $deserializers;" + }, + new object[] { new CodeProperty { Name = "story", Type = new CodeType { Name = "binary" }, Access = AccessModifier.Private, Kind = CodePropertyKind.Custom }, "'story' => fn(ParseNode $n) => $o->setStory($n->getBinaryContent()),"