Skip to content

Commit eb27c20

Browse files
authored
fix: reserve "ReadOnly" keyword for PHP 8.1 and add compatibility (protocolbuffers#9633)
* fix: reserve "ReadOnly" keyword for PHP 8.1 and add compatibility * typo fix - readme > readonly
1 parent 8077ac2 commit eb27c20

10 files changed

Lines changed: 117 additions & 30 deletions

File tree

php/ext/google/protobuf/names.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,12 @@ const char *const kReservedNames[] = {
8282
"global", "goto", "insteadof", "interface", "isset",
8383
"list", "match", "namespace", "new", "object",
8484
"or", "parent", "print", "private", "protected",
85-
"public", "require", "require_once", "return", "self",
86-
"static", "switch", "throw", "trait", "try",
87-
"unset", "use", "var", "while", "xor",
88-
"yield", "int", "float", "bool", "string",
89-
"true", "false", "null", "void", "iterable",
90-
NULL};
85+
"public", "readonly", "require", "require_once", "return",
86+
"self", "static", "switch", "throw", "trait",
87+
"try", "unset", "use", "var", "while",
88+
"xor", "yield", "int", "float", "bool",
89+
"string", "true", "false", "null", "void",
90+
"iterable", NULL};
9191

9292
bool is_reserved_name(const char* name) {
9393
int i;

php/src/Google/Protobuf/Internal/GPBUtil.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -285,11 +285,12 @@ public static function getClassNamePrefix(
285285
"include"=>0, "include_once"=>0, "instanceof"=>0, "insteadof"=>0,
286286
"interface"=>0, "isset"=>0, "list"=>0, "match"=>0, "namespace"=>0,
287287
"new"=>0, "or"=>0, "parent"=>0, "print"=>0, "private"=>0,
288-
"protected"=>0,"public"=>0, "require"=>0, "require_once"=>0,
289-
"return"=>0, "self"=>0, "static"=>0, "switch"=>0, "throw"=>0,
290-
"trait"=>0, "try"=>0,"unset"=>0, "use"=>0, "var"=>0, "while"=>0,
291-
"xor"=>0, "yield"=>0, "int"=>0, "float"=>0, "bool"=>0, "string"=>0,
292-
"true"=>0, "false"=>0, "null"=>0, "void"=>0, "iterable"=>0
288+
"protected"=>0,"public"=>0, "readonly" => 0,"require"=>0,
289+
"require_once"=>0,"return"=>0, "self"=>0, "static"=>0, "switch"=>0,
290+
"throw"=>0,"trait"=>0, "try"=>0,"unset"=>0, "use"=>0, "var"=>0,
291+
"while"=>0,"xor"=>0, "yield"=>0, "int"=>0, "float"=>0, "bool"=>0,
292+
"string"=>0,"true"=>0, "false"=>0, "null"=>0, "void"=>0,
293+
"iterable"=>0
293294
);
294295

295296
if (array_key_exists(strtolower($classname), $reserved_words)) {

php/tests/GeneratedClassTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,18 @@ public function testLegacyTypehintWithNestedEnums()
330330
$this->legacyEnum(new TestLegacyMessage\NestedEnum);
331331
}
332332

333+
public function testLegacyReadOnlyMessage()
334+
{
335+
$this->assertTrue(class_exists('\Upper\READONLY'));
336+
$this->assertTrue(class_exists('\Lower\readonly'));
337+
}
338+
339+
public function testLegacyReadOnlyEnum()
340+
{
341+
$this->assertTrue(class_exists('\Upper_enum\READONLY'));
342+
$this->assertTrue(class_exists('\Lower_enum\readonly'));
343+
}
344+
333345
private function legacyEnum(TestLegacyMessage_NestedEnum $enum)
334346
{
335347
// If we made it here without a PHP Fatal error, the typehint worked
@@ -939,6 +951,7 @@ public function testPrefixForReservedWords()
939951
$m = new \Lower\PBprivate();
940952
$m = new \Lower\PBprotected();
941953
$m = new \Lower\PBpublic();
954+
$m = new \Lower\PBreadonly();
942955
$m = new \Lower\PBrequire();
943956
$m = new \Lower\PBrequire_once();
944957
$m = new \Lower\PBreturn();
@@ -1019,6 +1032,7 @@ public function testPrefixForReservedWords()
10191032
$m = new \Upper\PBPRIVATE();
10201033
$m = new \Upper\PBPROTECTED();
10211034
$m = new \Upper\PBPUBLIC();
1035+
$m = new \Upper\PBREADONLY();
10221036
$m = new \Upper\PBREQUIRE();
10231037
$m = new \Upper\PBREQUIRE_ONCE();
10241038
$m = new \Upper\PBRETURN();
@@ -1100,6 +1114,7 @@ public function testPrefixForReservedWords()
11001114
$m = new \Lower_enum\PBprotected();
11011115
$m = new \Lower_enum\PBpublic();
11021116
$m = new \Lower_enum\PBrequire();
1117+
$m = new \Lower_enum\PBreadonly();
11031118
$m = new \Lower_enum\PBrequire_once();
11041119
$m = new \Lower_enum\PBreturn();
11051120
$m = new \Lower_enum\PBself();
@@ -1179,6 +1194,7 @@ public function testPrefixForReservedWords()
11791194
$m = new \Upper_enum\PBPRIVATE();
11801195
$m = new \Upper_enum\PBPROTECTED();
11811196
$m = new \Upper_enum\PBPUBLIC();
1197+
$m = new \Upper_enum\PBREADONLY();
11821198
$m = new \Upper_enum\PBREQUIRE();
11831199
$m = new \Upper_enum\PBREQUIRE_ONCE();
11841200
$m = new \Upper_enum\PBRETURN();
@@ -1283,6 +1299,7 @@ public function testPrefixForReservedWords()
12831299
$m = \Lower_enum_value\NotAllowed::iterable;
12841300
$m = \Lower_enum_value\NotAllowed::parent;
12851301
$m = \Lower_enum_value\NotAllowed::self;
1302+
$m = \Lower_enum_value\NotAllowed::readonly;
12861303

12871304
$m = \Upper_enum_value\NotAllowed::PBABSTRACT;
12881305
$m = \Upper_enum_value\NotAllowed::PBAND;
@@ -1363,6 +1380,7 @@ public function testPrefixForReservedWords()
13631380
$m = \Upper_enum_value\NotAllowed::ITERABLE;
13641381
$m = \Upper_enum_value\NotAllowed::PARENT;
13651382
$m = \Upper_enum_value\NotAllowed::SELF;
1383+
$m = \Upper_enum_value\NotAllowed::READONLY;
13661384

13671385
$this->assertTrue(true);
13681386
}

php/tests/proto/test_reserved_enum_lower.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ enum print { ZERO51 = 0; }
5757
enum private { ZERO52 = 0; }
5858
enum protected { ZERO53 = 0; }
5959
enum public { ZERO54 = 0; }
60+
enum readonly { ZERO80 = 0; }
6061
enum require { ZERO55 = 0; }
6162
enum require_once { ZERO56 = 0; }
6263
enum return { ZERO57 = 0; }

php/tests/proto/test_reserved_enum_upper.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ enum PRINT { ZERO51 = 0; }
5757
enum PRIVATE { ZERO52 = 0; }
5858
enum PROTECTED { ZERO53 = 0; }
5959
enum PUBLIC { ZERO54 = 0; }
60+
enum READONLY { ZERO80 = 0; }
6061
enum REQUIRE { ZERO55 = 0; }
6162
enum REQUIRE_ONCE { ZERO56 = 0; }
6263
enum RETURN { ZERO57 = 0; }

php/tests/proto/test_reserved_enum_value_lower.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ enum NotAllowed {
5858
private = 51;
5959
protected = 52;
6060
public = 53;
61+
readonly = 79;
6162
require = 54;
6263
require_once = 55;
6364
return = 56;

php/tests/proto/test_reserved_enum_value_upper.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ enum NotAllowed {
5858
PRIVATE = 51;
5959
PROTECTED = 52;
6060
PUBLIC = 53;
61+
READONLY = 79;
6162
REQUIRE = 54;
6263
REQUIRE_ONCE = 55;
6364
RETURN = 56;

php/tests/proto/test_reserved_message_lower.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ message print {}
5757
message private {}
5858
message protected {}
5959
message public {}
60+
message readonly {}
6061
message require {}
6162
message require_once {}
6263
message return {}

php/tests/proto/test_reserved_message_upper.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ message PRINT {}
5757
message PRIVATE {}
5858
message PROTECTED {}
5959
message PUBLIC {}
60+
message READONLY {}
6061
message REQUIRE {}
6162
message REQUIRE_ONCE {}
6263
message RETURN {}

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

Lines changed: 81 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -48,29 +48,29 @@ const std::string kDescriptorMetadataFile =
4848
const std::string kDescriptorDirName = "Google/Protobuf/Internal";
4949
const std::string kDescriptorPackageName = "Google\\Protobuf\\Internal";
5050
const char* const kReservedNames[] = {
51-
"abstract", "and", "array", "as", "break",
52-
"callable", "case", "catch", "class", "clone",
53-
"const", "continue", "declare", "default", "die",
54-
"do", "echo", "else", "elseif", "empty",
55-
"enddeclare", "endfor", "endforeach", "endif", "endswitch",
56-
"endwhile", "eval", "exit", "extends", "final",
57-
"finally", "fn", "for", "foreach", "function",
58-
"global", "goto", "if", "implements", "include",
59-
"include_once", "instanceof", "insteadof", "interface", "isset",
60-
"list", "match", "namespace", "new", "or",
61-
"parent", "print", "private", "protected", "public",
62-
"require", "require_once", "return", "self", "static",
63-
"switch", "throw", "trait", "try", "unset",
64-
"use", "var", "while", "xor", "yield",
65-
"int", "float", "bool", "string", "true",
66-
"false", "null", "void", "iterable"};
51+
"abstract", "and", "array", "as", "break",
52+
"callable", "case", "catch", "class", "clone",
53+
"const", "continue", "declare", "default", "die",
54+
"do", "echo", "else", "elseif", "empty",
55+
"enddeclare", "endfor", "endforeach", "endif", "endswitch",
56+
"endwhile", "eval", "exit", "extends", "final",
57+
"finally", "fn", "for", "foreach", "function",
58+
"global", "goto", "if", "implements", "include",
59+
"include_once", "instanceof", "insteadof", "interface", "isset",
60+
"list", "match", "namespace", "new", "or",
61+
"parent", "print", "private", "protected", "public",
62+
"readonly", "require", "require_once", "return", "self",
63+
"static", "switch", "throw", "trait", "try",
64+
"unset", "use", "var", "while", "xor",
65+
"yield", "int", "float", "bool", "string",
66+
"true", "false", "null", "void", "iterable"};
6767
const char* const kValidConstantNames[] = {
6868
"int", "float", "bool", "string", "true",
6969
"false", "null", "void", "iterable", "parent",
70-
"self"
70+
"self", "readonly"
7171
};
72-
const int kReservedNamesSize = 79;
73-
const int kValidConstantNamesSize = 11;
72+
const int kReservedNamesSize = 80;
73+
const int kValidConstantNamesSize = 12;
7474
const int kFieldSetter = 1;
7575
const int kFieldGetter = 2;
7676
const int kFieldProperty = 3;
@@ -420,6 +420,16 @@ std::string LegacyGeneratedClassFileName(const DescriptorType* desc,
420420
return result + ".php";
421421
}
422422

423+
template <typename DescriptorType>
424+
std::string LegacyReadOnlyGeneratedClassFileName(const DescriptorType* desc,
425+
const Options& options) {
426+
std::string php_namespace = RootPhpNamespace(desc, options);
427+
if (!php_namespace.empty()) {
428+
return php_namespace + "/" + desc->name() + ".php";
429+
}
430+
return desc->name() + ".php";
431+
}
432+
423433
std::string GeneratedServiceFileName(const ServiceDescriptor* service,
424434
const Options& options) {
425435
std::string result = FullClassName(service, options) + "Interface";
@@ -1302,6 +1312,32 @@ void LegacyGenerateClassFile(const FileDescriptor* file,
13021312
"fullname", newname);
13031313
}
13041314

1315+
template <typename DescriptorType>
1316+
void LegacyReadOnlyGenerateClassFile(const FileDescriptor* file,
1317+
const DescriptorType* desc, const Options& options,
1318+
GeneratorContext* generator_context) {
1319+
std::string filename = LegacyReadOnlyGeneratedClassFileName(desc, options);
1320+
std::unique_ptr<io::ZeroCopyOutputStream> output(
1321+
generator_context->Open(filename));
1322+
io::Printer printer(output.get(), '^');
1323+
1324+
GenerateHead(file, &printer);
1325+
1326+
std::string php_namespace = RootPhpNamespace(desc, options);
1327+
if (!php_namespace.empty()) {
1328+
printer.Print(
1329+
"namespace ^name^;\n\n",
1330+
"name", php_namespace);
1331+
}
1332+
std::string newname = FullClassName(desc, options);
1333+
printer.Print("class_exists(^new^::class);\n",
1334+
"new", GeneratedClassNameImpl(desc));
1335+
printer.Print("@trigger_error(__NAMESPACE__ . '\\^old^ is deprecated and will be removed in "
1336+
"the next major release. Use ^fullname^ instead', E_USER_DEPRECATED);\n\n",
1337+
"old", desc->name(),
1338+
"fullname", newname);
1339+
}
1340+
13051341
void GenerateEnumFile(const FileDescriptor* file, const EnumDescriptor* en,
13061342
const Options& options,
13071343
GeneratorContext* generator_context) {
@@ -1405,6 +1441,19 @@ void GenerateEnumFile(const FileDescriptor* file, const EnumDescriptor* en,
14051441
"old", LegacyFullClassName(en, options));
14061442
LegacyGenerateClassFile(file, en, options, generator_context);
14071443
}
1444+
1445+
// Write legacy file for backwards compatibility with "readonly" keywword
1446+
std::string lower = en->name();
1447+
std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
1448+
if (lower == "readonly") {
1449+
printer.Print(
1450+
"// Adding a class alias for backwards compatibility with the \"readonly\" keyword.\n");
1451+
printer.Print(
1452+
"class_alias(^new^::class, __NAMESPACE__ . '\\^old^');\n\n",
1453+
"new", fullname,
1454+
"old", en->name());
1455+
LegacyReadOnlyGenerateClassFile(file, en, options, generator_context);
1456+
}
14081457
}
14091458

14101459
void GenerateMessageFile(const FileDescriptor* file, const Descriptor* message,
@@ -1521,6 +1570,19 @@ void GenerateMessageFile(const FileDescriptor* file, const Descriptor* message,
15211570
LegacyGenerateClassFile(file, message, options, generator_context);
15221571
}
15231572

1573+
// Write legacy file for backwards compatibility with "readonly" keywword
1574+
std::string lower = message->name();
1575+
std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
1576+
if (lower == "readonly") {
1577+
printer.Print(
1578+
"// Adding a class alias for backwards compatibility with the \"readonly\" keyword.\n");
1579+
printer.Print(
1580+
"class_alias(^new^::class, __NAMESPACE__ . '\\^old^');\n\n",
1581+
"new", fullname,
1582+
"old", message->name());
1583+
LegacyReadOnlyGenerateClassFile(file, message, options, generator_context);
1584+
}
1585+
15241586
// Nested messages and enums.
15251587
for (int i = 0; i < message->nested_type_count(); i++) {
15261588
GenerateMessageFile(file, message->nested_type(i), options,

0 commit comments

Comments
 (0)