From 218f0e0c412013466093d176053ba8dfaf88a08b Mon Sep 17 00:00:00 2001 From: Nanook Date: Fri, 12 Jun 2026 18:23:14 +0000 Subject: [PATCH] fix: support nested method_signature fields --- src/Generation/BuildMethodFragmentGenerator.php | 12 ++++++++---- .../Generation/BuildMethodFragmentGeneratorTest.php | 11 +++++++++-- tests/Unit/Utils/example.proto | 8 ++++++++ 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/Generation/BuildMethodFragmentGenerator.php b/src/Generation/BuildMethodFragmentGenerator.php index a0ae49c09..2800230a2 100644 --- a/src/Generation/BuildMethodFragmentGenerator.php +++ b/src/Generation/BuildMethodFragmentGenerator.php @@ -128,11 +128,15 @@ public function buildMethodSnippet(MethodDetails $methodDetails, string $methodS }; $methodSignatureArguments = array_map('trim', array_filter(explode(',', $methodSignature))); + $methodSignatureTopLevelArguments = array_values(array_unique(array_map( + fn ($arg) => explode('.', $arg, 2)[0], + $methodSignatureArguments + ))); $requiredFields = $methodDetails->allFields - ->filter(fn ($f) => in_array($f->name, $methodSignatureArguments)) - ->orderBy(fn ($f) => array_search($f->name, $methodSignatureArguments)); + ->filter(fn ($f) => in_array($f->name, $methodSignatureTopLevelArguments)) + ->orderBy(fn ($f) => array_search($f->name, $methodSignatureTopLevelArguments)); - if (count($methodSignatureArguments) !== $requiredFields->count()) { + if (count($methodSignatureTopLevelArguments) !== $requiredFields->count()) { throw new \LogicException(sprintf( 'missing method signature arguments (Expected "%s", found %s)', $methodSignature, @@ -153,7 +157,7 @@ public function buildMethodSnippet(MethodDetails $methodDetails, string $methodS $cleanSignature = preg_replace('/\s+/', '', $methodSignature); $methodName = $isFirst ? 'build' - : 'buildFrom' . Helpers::toUpperCamelCase(str_replace(',', '_', $cleanSignature)); + : 'buildFrom' . Helpers::toUpperCamelCase(str_replace([',', '.'], '_', $cleanSignature)); return AST::method($methodName) ->withAccess(Access::PUBLIC, Access::STATIC) diff --git a/tests/Unit/Generation/BuildMethodFragmentGeneratorTest.php b/tests/Unit/Generation/BuildMethodFragmentGeneratorTest.php index f37ad1b00..d369979f7 100644 --- a/tests/Unit/Generation/BuildMethodFragmentGeneratorTest.php +++ b/tests/Unit/Generation/BuildMethodFragmentGeneratorTest.php @@ -32,7 +32,7 @@ final class BuildMethodFragmentGeneratorTest extends TestCase { - public function testBuildMethodNamesWithSpacesInSignatures(): void + public function testBuildMethodNamesWithSpacesAndDotNotationSignatures(): void { $descBytes = ProtoLoader::loadDescriptorBytes('Utils/example.proto'); $descSet = new \Google\Protobuf\Internal\FileDescriptorSet(); @@ -62,12 +62,19 @@ public function testBuildMethodNamesWithSpacesInSignatures(): void $this->assertTrue(isset($fragments[$requestClassName])); $buildMethods = $fragments[$requestClassName]; - $this->assertEquals(2, $buildMethods->count()); + $this->assertEquals(3, $buildMethods->count()); // First builder method should be 'build' $this->assertEquals('build', $buildMethods[0]->name); // Second builder method should be 'buildFromNameNumber' (spaces stripped and camel-cased) $this->assertEquals('buildFromNameNumber', $buildMethods[1]->name); + + // Third builder method should be based on the clean original dot-notation signature. + $this->assertEquals('buildFromFooAFooB', $buildMethods[2]->name); + + $buildMethodCode = $buildMethods[2]->toCode(); + $this->assertStringContainsString('function buildFromFooAFooB(\Example\Foo $foo): self', $buildMethodCode); + $this->assertStringContainsString('setFoo($foo)', $buildMethodCode); } } diff --git a/tests/Unit/Utils/example.proto b/tests/Unit/Utils/example.proto index 0d1ed86c7..c23fc6e58 100644 --- a/tests/Unit/Utils/example.proto +++ b/tests/Unit/Utils/example.proto @@ -17,6 +17,7 @@ syntax = "proto3"; package example; import "google/api/client.proto"; +import "google/api/field_behavior.proto"; import "google/api/resource.proto"; import "google/longrunning/operations.proto"; @@ -40,6 +41,7 @@ service Example { rpc ExampleMethodWithSignatures(Request) returns(Response) { option (google.api.method_signature) = "name"; option (google.api.method_signature) = "name, number"; + option (google.api.method_signature) = "foo.a, foo.b"; } } @@ -55,6 +57,12 @@ message Request { // Required. Percentage of selected conversation to analyze, between // [0, 100]. float analysis_percentage = 3; + Foo foo = 4 [(google.api.field_behavior) = REQUIRED]; +} + +message Foo { + string a = 1; + string b = 2; } message Response {