Skip to content

Commit b603fb6

Browse files
mkruskal-googlecopybara-github
authored andcommitted
Expand PHP generator to support overlapping subset of proto2.
This will allow us to support editions without adding support for proto2 concepts such as closed enums, required fields, and groups. The generator will now only ban unsupported features, meaning that some types of proto2 files will be allowed. The PHP runtime does not yet support editions. PiperOrigin-RevId: 588091790
1 parent b64c6e1 commit b603fb6

4 files changed

Lines changed: 186 additions & 36 deletions

File tree

src/google/protobuf/compiler/php/BUILD.bazel

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,25 @@ cc_library(
3232
],
3333
deps = [
3434
":names",
35-
"//src/google/protobuf:descriptor_legacy",
3635
"//src/google/protobuf:protobuf_nowkt",
3736
"//src/google/protobuf/compiler:code_generator",
3837
"//src/google/protobuf/compiler:retention",
3938
"@com_google_absl//absl/strings",
4039
],
4140
)
4241

42+
cc_test(
43+
name = "generator_unittest",
44+
srcs = ["generator_unittest.cc"],
45+
deps = [
46+
":php",
47+
"//:protobuf",
48+
"//src/google/protobuf/compiler:command_line_interface_tester",
49+
"@com_google_googletest//:gtest",
50+
"@com_google_googletest//:gtest_main",
51+
],
52+
)
53+
4354
################################################################################
4455
# Distribution packaging
4556
################################################################################
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Protocol Buffers - Google's data interchange format
2+
// Copyright 2023 Google Inc. All rights reserved.
3+
//
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file or at
6+
// https://developers.google.com/open-source/licenses/bsd
7+
8+
#include <memory>
9+
10+
#include "google/protobuf/descriptor.pb.h"
11+
#include <gtest/gtest.h>
12+
#include "google/protobuf/compiler/command_line_interface_tester.h"
13+
#include "google/protobuf/compiler/php/php_generator.h"
14+
15+
namespace google {
16+
namespace protobuf {
17+
namespace compiler {
18+
namespace php {
19+
namespace {
20+
21+
class PhpGeneratorTest : public CommandLineInterfaceTester {
22+
protected:
23+
PhpGeneratorTest() {
24+
RegisterGenerator("--php_out", "--php_opt", std::make_unique<Generator>(),
25+
"PHP test generator");
26+
27+
// Generate built-in protos.
28+
CreateTempFile(
29+
"google/protobuf/descriptor.proto",
30+
google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
31+
}
32+
};
33+
34+
TEST_F(PhpGeneratorTest, Basic) {
35+
CreateTempFile("foo.proto",
36+
R"schema(
37+
syntax = "proto3";
38+
message Foo {
39+
optional int32 bar = 1;
40+
int32 baz = 2;
41+
})schema");
42+
43+
RunProtoc(
44+
"protocol_compiler --proto_path=$tmpdir --php_out=$tmpdir foo.proto");
45+
46+
ExpectNoErrors();
47+
}
48+
49+
TEST_F(PhpGeneratorTest, Proto2File) {
50+
CreateTempFile("foo.proto",
51+
R"schema(
52+
syntax = "proto2";
53+
message Foo {
54+
optional int32 bar = 1;
55+
})schema");
56+
57+
RunProtoc(
58+
"protocol_compiler --proto_path=$tmpdir --php_out=$tmpdir foo.proto");
59+
60+
ExpectNoErrors();
61+
}
62+
63+
TEST_F(PhpGeneratorTest, RequiredFieldError) {
64+
CreateTempFile("foo.proto",
65+
R"schema(
66+
syntax = "proto2";
67+
message FooBar {
68+
required int32 foo_message = 1;
69+
})schema");
70+
71+
RunProtoc(
72+
"protocol_compiler --proto_path=$tmpdir --php_out=$tmpdir foo.proto");
73+
74+
ExpectErrorSubstring(
75+
"Can't generate PHP code for required field FooBar.foo_message");
76+
}
77+
78+
TEST_F(PhpGeneratorTest, GroupFieldError) {
79+
CreateTempFile("foo.proto",
80+
R"schema(
81+
syntax = "proto2";
82+
message Foo {
83+
optional group Bar = 1 {
84+
optional int32 baz = 1;
85+
};
86+
})schema");
87+
88+
RunProtoc(
89+
"protocol_compiler --proto_path=$tmpdir --php_out=$tmpdir foo.proto");
90+
91+
ExpectErrorSubstring("Can't generate PHP code for group field Foo.bar");
92+
}
93+
94+
TEST_F(PhpGeneratorTest, ClosedEnumError) {
95+
CreateTempFile("foo.proto",
96+
R"schema(
97+
syntax = "proto2";
98+
enum Foo {
99+
BAR = 0;
100+
})schema");
101+
102+
RunProtoc(
103+
"protocol_compiler --proto_path=$tmpdir --php_out=$tmpdir foo.proto");
104+
105+
ExpectErrorSubstring("Can't generate PHP code for closed enum Foo");
106+
}
107+
108+
} // namespace
109+
} // namespace php
110+
} // namespace compiler
111+
} // namespace protobuf
112+
} // namespace google

src/google/protobuf/compiler/php/php_generator.cc

Lines changed: 59 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
#include <string>
1313
#include <vector>
1414

15-
#include "google/protobuf/compiler/code_generator.h"
1615
#include "absl/container/flat_hash_map.h"
1716
#include "absl/container/flat_hash_set.h"
1817
#include "absl/log/absl_log.h"
@@ -22,10 +21,11 @@
2221
#include "absl/strings/str_replace.h"
2322
#include "absl/strings/str_split.h"
2423
#include "absl/strings/string_view.h"
24+
#include "google/protobuf/compiler/code_generator.h"
25+
#include "google/protobuf/compiler/php/names.h"
2526
#include "google/protobuf/compiler/retention.h"
2627
#include "google/protobuf/descriptor.h"
2728
#include "google/protobuf/descriptor.pb.h"
28-
#include "google/protobuf/descriptor_legacy.h"
2929
#include "google/protobuf/io/printer.h"
3030
#include "google/protobuf/io/zero_copy_stream.h"
3131

@@ -537,16 +537,28 @@ void Outdent(io::Printer* printer) {
537537
printer->Outdent();
538538
}
539539

540-
void GenerateField(const FieldDescriptor* field, io::Printer* printer,
541-
const Options& options) {
540+
bool GenerateField(const FieldDescriptor* field, io::Printer* printer,
541+
const Options& options, std::string* error) {
542+
if (field->is_required()) {
543+
*error = absl::StrCat("Can't generate PHP code for required field ",
544+
field->full_name(), ".\n");
545+
return false;
546+
}
547+
if (field->type() == FieldDescriptor::TYPE_GROUP) {
548+
*error = absl::StrCat("Can't generate PHP code for group field ",
549+
field->full_name(),
550+
". Use regular message encoding instead.\n");
551+
return false;
552+
}
553+
542554
if (field->is_repeated()) {
543555
GenerateFieldDocComment(printer, field, options, kFieldProperty);
544556
printer->Print(
545557
"private $^name^;\n",
546558
"name", field->name());
547559
} else if (field->real_containing_oneof()) {
548560
// Oneof fields are handled by GenerateOneofField.
549-
return;
561+
return true;
550562
} else {
551563
std::string initial_value =
552564
field->has_presence() ? "null" : DefaultForField(field);
@@ -556,6 +568,7 @@ void GenerateField(const FieldDescriptor* field, io::Printer* printer,
556568
"name", field->name(),
557569
"initial_value", initial_value);
558570
}
571+
return true;
559572
}
560573

561574
void GenerateOneofField(const OneofDescriptor* oneof, io::Printer* printer) {
@@ -1270,9 +1283,17 @@ void LegacyReadOnlyGenerateClassFile(const FileDescriptor* file,
12701283
"fullname", classname);
12711284
}
12721285

1273-
void GenerateEnumFile(const FileDescriptor* file, const EnumDescriptor* en,
1286+
bool GenerateEnumFile(const FileDescriptor* file, const EnumDescriptor* en,
12741287
const Options& options,
1275-
GeneratorContext* generator_context) {
1288+
GeneratorContext* generator_context, std::string* error) {
1289+
if (en->is_closed()) {
1290+
*error = absl::StrCat("Can't generate PHP code for closed enum ",
1291+
en->full_name(),
1292+
". Please use either proto3 or editions without "
1293+
"`enum_type = CLOSED`.\n");
1294+
return false;
1295+
}
1296+
12761297
std::string filename = GeneratedClassFileName(en, options);
12771298
std::unique_ptr<io::ZeroCopyOutputStream> output(
12781299
generator_context->Open(filename));
@@ -1403,15 +1424,18 @@ void GenerateEnumFile(const FileDescriptor* file, const EnumDescriptor* en,
14031424
"old", en->name());
14041425
LegacyReadOnlyGenerateClassFile(file, en, options, generator_context);
14051426
}
1427+
1428+
return true;
14061429
}
14071430

1408-
void GenerateMessageFile(const FileDescriptor* file, const Descriptor* message,
1431+
bool GenerateMessageFile(const FileDescriptor* file, const Descriptor* message,
14091432
const Options& options,
1410-
GeneratorContext* generator_context) {
1433+
GeneratorContext* generator_context,
1434+
std::string* error) {
14111435
// Don't generate MapEntry messages -- we use the PHP extension's native
14121436
// support for map fields instead.
14131437
if (message->options().map_entry()) {
1414-
return;
1438+
return true;
14151439
}
14161440

14171441
std::string filename = GeneratedClassFileName(message, options);
@@ -1461,7 +1485,9 @@ void GenerateMessageFile(const FileDescriptor* file, const Descriptor* message,
14611485
// Field and oneof definitions.
14621486
for (int i = 0; i < message->field_count(); i++) {
14631487
const FieldDescriptor* field = message->field(i);
1464-
GenerateField(field, &printer, options);
1488+
if (!GenerateField(field, &printer, options, error)) {
1489+
return false;
1490+
}
14651491
}
14661492
for (int i = 0; i < message->real_oneof_decl_count(); i++) {
14671493
const OneofDescriptor* oneof = message->oneof_decl(i);
@@ -1533,12 +1559,18 @@ void GenerateMessageFile(const FileDescriptor* file, const Descriptor* message,
15331559

15341560
// Nested messages and enums.
15351561
for (int i = 0; i < message->nested_type_count(); i++) {
1536-
GenerateMessageFile(file, message->nested_type(i), options,
1537-
generator_context);
1562+
if (!GenerateMessageFile(file, message->nested_type(i), options,
1563+
generator_context, error)) {
1564+
return false;
1565+
}
15381566
}
15391567
for (int i = 0; i < message->enum_type_count(); i++) {
1540-
GenerateEnumFile(file, message->enum_type(i), options, generator_context);
1568+
if (!GenerateEnumFile(file, message->enum_type(i), options,
1569+
generator_context, error)) {
1570+
return false;
1571+
}
15411572
}
1573+
return true;
15421574
}
15431575

15441576
void GenerateServiceFile(
@@ -1588,22 +1620,29 @@ void GenerateServiceFile(
15881620
printer.Print("}\n\n");
15891621
}
15901622

1591-
void GenerateFile(const FileDescriptor* file, const Options& options,
1592-
GeneratorContext* generator_context) {
1623+
bool GenerateFile(const FileDescriptor* file, const Options& options,
1624+
GeneratorContext* generator_context, std::string* error) {
15931625
GenerateMetadataFile(file, options, generator_context);
15941626

15951627
for (int i = 0; i < file->message_type_count(); i++) {
1596-
GenerateMessageFile(file, file->message_type(i), options,
1597-
generator_context);
1628+
if (!GenerateMessageFile(file, file->message_type(i), options,
1629+
generator_context, error)) {
1630+
return false;
1631+
}
15981632
}
15991633
for (int i = 0; i < file->enum_type_count(); i++) {
1600-
GenerateEnumFile(file, file->enum_type(i), options, generator_context);
1634+
if (!GenerateEnumFile(file, file->enum_type(i), options, generator_context,
1635+
error)) {
1636+
return false;
1637+
}
16011638
}
16021639
if (file->options().php_generic_services()) {
16031640
for (int i = 0; i < file->service_count(); i++) {
16041641
GenerateServiceFile(file, file->service(i), options, generator_context);
16051642
}
16061643
}
1644+
1645+
return true;
16071646
}
16081647

16091648
static std::string EscapePhpdoc(absl::string_view input) {
@@ -2283,18 +2322,7 @@ bool Generator::Generate(const FileDescriptor* file, const Options& options,
22832322
return false;
22842323
}
22852324

2286-
if (!options.is_descriptor &&
2287-
FileDescriptorLegacy(file).syntax() !=
2288-
FileDescriptorLegacy::Syntax::SYNTAX_PROTO3) {
2289-
*error =
2290-
"Can only generate PHP code for proto3 .proto files.\n"
2291-
"Please add 'syntax = \"proto3\";' to the top of your .proto file.\n";
2292-
return false;
2293-
}
2294-
2295-
GenerateFile(file, options, generator_context);
2296-
2297-
return true;
2325+
return GenerateFile(file, options, generator_context, error);
22982326
}
22992327

23002328
bool Generator::GenerateAll(const std::vector<const FileDescriptor*>& files,

src/google/protobuf/compiler/php/php_generator.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,11 @@
88
#ifndef GOOGLE_PROTOBUF_COMPILER_PHP_GENERATOR_H__
99
#define GOOGLE_PROTOBUF_COMPILER_PHP_GENERATOR_H__
1010

11-
#include "google/protobuf/compiler/code_generator.h"
12-
#include "google/protobuf/compiler/php/names.h"
13-
#include "google/protobuf/descriptor.h"
14-
11+
#include <cstdint>
1512
#include <string>
1613

14+
#include "google/protobuf/compiler/code_generator.h"
15+
#include "google/protobuf/descriptor.h"
1716
#include "google/protobuf/port_def.inc"
1817

1918
namespace google {

0 commit comments

Comments
 (0)