Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: do not throw deprecated warning on field getters for default values #17788

Closed
86 changes: 86 additions & 0 deletions php/tests/GeneratedClassTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,92 @@ public function testDeprecatedInt32Field()
$this->assertSame(4, $deprecationCount);
}

public function testDeprecatedFieldGetterDoesNotThrowWarning()
{
// temporarily change error handler to capture the deprecated errors
$deprecationCount = 0;
set_error_handler(function ($errno, $errstr) use (&$deprecationCount) {
if (false !== strpos($errstr, ' is deprecated.')) {
$deprecationCount++;
}
}, E_USER_DEPRECATED);

// does not throw warning
$message = new TestMessage();
$message->getDeprecatedInt32();
$message->getDeprecatedOptionalInt32();
$message->getDeprecatedInt32ValueUnwrapped(); // wrapped field
$message->getDeprecatedInt32Value(); // wrapped field
$message->getDeprecatedOneofInt32(); // oneof field
$message->getDeprecatedOneof(); // oneof field
$message->getDeprecatedRepeatedInt32(); // repeated field
$message->getDeprecatedMapInt32Int32(); // map field
$message->getDeprecatedAny(); // any field
$message->getDeprecatedMessage(); // message field
$message->getDeprecatedEnum(); // enum field

restore_error_handler();

$this->assertEquals(0, $deprecationCount);
}

public function testDeprecatedFieldGetterThrowsWarningWithValue()
{
$message = new TestMessage([
'deprecated_int32' => 1,
'deprecated_optional_int32' => 1,
'deprecated_int32_value' => new \Google\Protobuf\Int32Value(['value' => 1]),
'deprecated_oneof_int32' => 1,
'deprecated_repeated_int32' => [1],
'deprecated_map_int32_int32' => [1 => 1],
'deprecated_any' => new \Google\Protobuf\Any(['type_url' => 'foo', 'value' => 'bar']),
'deprecated_message' => new TestMessage(),
'deprecated_enum' => 1,
]);

// temporarily change error handler to capture the deprecated errors
$deprecationCount = 0;
set_error_handler(function ($errno, $errstr) use (&$deprecationCount) {
if (false !== strpos($errstr, ' is deprecated.')) {
$deprecationCount++;
}
}, E_USER_DEPRECATED);

$message->getDeprecatedInt32();
$message->getDeprecatedOptionalInt32();
$message->getDeprecatedInt32ValueUnwrapped(); // wrapped field unwrapped
$message->getDeprecatedInt32Value(); // wrapped field
$message->getDeprecatedOneofInt32(); // oneof field
$message->getDeprecatedRepeatedInt32(); // repeated field
$message->getDeprecatedMapInt32Int32(); // map field
$message->getDeprecatedAny(); // any field
$message->getDeprecatedMessage(); // message field
$message->getDeprecatedEnum(); // enum field

// oneof field (should never warn)
$message->getDeprecatedOneof();

restore_error_handler();

$this->assertEquals(10, $deprecationCount);
}

public function testDeprecatedFieldWarningsOnSerialize()
{
set_error_handler(function ($errno, $errstr) {
if (false !== strpos($errstr, ' is deprecated.')) {
throw new \Exception($errstr);
}
}, E_USER_DEPRECATED);

$message = new TestMessage();
$message->serializeToJsonString();

restore_error_handler();

$this->assertTrue(true, 'No deprecation warning on serialize');
}

#########################################################
# Test optional int32 field.
#########################################################
Expand Down
20 changes: 19 additions & 1 deletion php/tests/proto/test.proto
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,25 @@ message TestMessage {
map<string, google.protobuf.Struct> map_string_struct = 124;

// deprecated field
int32 deprecated_optional_int32 = 125 [deprecated=true];
int32 deprecated_int32 = 125 [deprecated=true];
// deprecated optional field
optional int32 deprecated_optional_int32 = 126 [deprecated=true];
// deprecated wrapped field
google.protobuf.Int32Value deprecated_int32_value = 127 [deprecated=true];
// deprecated oneof
oneof deprecated_oneof {
int32 deprecated_oneof_int32 = 128 [deprecated=true];
}
// deprecated repeated field
repeated int32 deprecated_repeated_int32 = 129 [deprecated = true];
// deprecated map
map<int32, int32> deprecated_map_int32_int32 = 130 [deprecated = true];
// deprecated any
google.protobuf.Any deprecated_any = 131 [deprecated = true];
// deprecated message
TestMessage deprecated_message = 132 [deprecated = true];
// deprecated enum
NestedEnum deprecated_enum = 133 [deprecated = true];
}

enum TestEnum {
Expand Down
49 changes: 40 additions & 9 deletions src/google/protobuf/compiler/php/php_generator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,20 @@ std::string DefaultForField(const FieldDescriptor* field) {
}
}

std::string DeprecatedConditionalForField(const FieldDescriptor* field) {
if (field->is_repeated() || field->is_map()) {
mkruskal-google marked this conversation as resolved.
Show resolved Hide resolved
return absl::StrCat("$this->", field->name(), "->count() !== 0");
}
if (field->real_containing_oneof() != nullptr) {
return absl::StrCat("$this->hasOneof(", field->number(), ")");
}
if (field->has_presence()) {
return absl::StrCat("isset($this->", field->name(), ")");
}
return absl::StrCat("$this->", field->name(), " !== ",
field->has_presence() ? "null" : DefaultForField(field));
}

std::string GeneratedMetadataFileName(const FileDescriptor* file,
const Options& options) {
absl::string_view proto_file = file->name();
Expand Down Expand Up @@ -574,6 +588,12 @@ void GenerateFieldAccessor(const FieldDescriptor* field, const Options& options,
? absl::StrCat("@trigger_error('", field->name(),
" is deprecated.', E_USER_DEPRECATED);\n ")
: "";
std::string deprecation_trigger_with_conditional =
(field->options().deprecated())
? absl::StrCat("if (" + DeprecatedConditionalForField(field),
") {\n ", deprecation_trigger,
"}\n ")
: "";

// Emit getter.
if (oneof != nullptr) {
Expand All @@ -584,7 +604,7 @@ void GenerateFieldAccessor(const FieldDescriptor* field, const Options& options,
"}\n\n",
"camel_name", UnderscoresToCamelCase(field->name(), true), "number",
IntToString(field->number()), "deprecation_trigger",
deprecation_trigger);
deprecation_trigger_with_conditional);
bshaffer marked this conversation as resolved.
Show resolved Hide resolved
} else if (field->has_presence() && !field->message_type()) {
printer->Print(
"public function get^camel_name^()\n"
Expand All @@ -594,15 +614,16 @@ void GenerateFieldAccessor(const FieldDescriptor* field, const Options& options,
"}\n\n",
"camel_name", UnderscoresToCamelCase(field->name(), true), "name",
field->name(), "default_value", DefaultForField(field),
"deprecation_trigger", deprecation_trigger);
"deprecation_trigger", deprecation_trigger_with_conditional);
} else {
printer->Print(
"public function get^camel_name^()\n"
"{\n"
" ^deprecation_trigger^return $this->^name^;\n"
"}\n\n",
"camel_name", UnderscoresToCamelCase(field->name(), true), "name",
field->name(), "deprecation_trigger", deprecation_trigger);
field->name(), "deprecation_trigger",
deprecation_trigger_with_conditional);
}

// Emit hazzers/clear.
Expand All @@ -614,20 +635,22 @@ void GenerateFieldAccessor(const FieldDescriptor* field, const Options& options,
"}\n\n",
"camel_name", UnderscoresToCamelCase(field->name(), true), "number",
IntToString(field->number()), "deprecation_trigger",
deprecation_trigger);
deprecation_trigger_with_conditional);
} else if (field->has_presence()) {
printer->Print(
"public function has^camel_name^()\n"
"{\n"
" ^deprecation_trigger^return isset($this->^name^);\n"
"}\n\n"
" ^deprecation_trigger_with_conditional^return isset($this->^name^);"
"\n}\n\n"
"public function clear^camel_name^()\n"
"{\n"
" ^deprecation_trigger^unset($this->^name^);\n"
"}\n\n",
"camel_name", UnderscoresToCamelCase(field->name(), true), "name",
field->name(), "default_value", DefaultForField(field),
"deprecation_trigger", deprecation_trigger);
"deprecation_trigger", deprecation_trigger,
"deprecation_trigger_with_conditional",
deprecation_trigger_with_conditional);
}

// For wrapper types, generate an additional getXXXUnwrapped getter
Expand All @@ -642,7 +665,8 @@ void GenerateFieldAccessor(const FieldDescriptor* field, const Options& options,
"$this->readWrapperValue(\"^field_name^\");\n"
"}\n\n",
"camel_name", UnderscoresToCamelCase(field->name(), true), "field_name",
field->name(), "deprecation_trigger", deprecation_trigger);
field->name(), "deprecation_trigger",
deprecation_trigger_with_conditional);
bshaffer marked this conversation as resolved.
Show resolved Hide resolved
}

// Generate setter.
Expand All @@ -654,7 +678,8 @@ void GenerateFieldAccessor(const FieldDescriptor* field, const Options& options,

Indent(printer);

if (field->options().deprecated()) {
if (field->options().deprecated() && !field->is_map() &&
!field->is_repeated()) {
printer->Print("^deprecation_trigger^", "deprecation_trigger",
deprecation_trigger);
}
Expand Down Expand Up @@ -712,6 +737,12 @@ void GenerateFieldAccessor(const FieldDescriptor* field, const Options& options,
UnderscoresToCamelCase(field->cpp_type_name(), true));
}

if (field->options().deprecated() && (field->is_map() ||
field->is_repeated())) {
printer->Print("if ($arr->count() !== 0) {\n ^deprecation_trigger^}\n",
"deprecation_trigger", deprecation_trigger);
}

if (oneof != nullptr) {
printer->Print("$this->writeOneof(^number^, $var);\n", "number",
IntToString(field->number()));
Expand Down
Loading