diff options
author | Brent Shaffer <betterbrent@google.com> | 2018-05-23 16:43:30 -0700 |
---|---|---|
committer | Bo Yang <teboring@google.com> | 2018-05-25 13:04:19 -0700 |
commit | 2774e5441d8508b7d61cc3786b69149dc43d9f42 (patch) | |
tree | 5415e227b9f454a402caf23d71bafadef2627dc4 /php/ext | |
parent | 8b336f8c5a77ca4932b00203e485f9266f6f0c30 (diff) | |
download | protobuf-2774e5441d8508b7d61cc3786b69149dc43d9f42.tar.gz protobuf-2774e5441d8508b7d61cc3786b69149dc43d9f42.tar.bz2 protobuf-2774e5441d8508b7d61cc3786b69149dc43d9f42.zip |
PHP namespaces for nested messages and enums (#4536)
* uses namespaces for nested messages and enums
* fixes namespaces for PHP dist
* fixes namespace for Descriptors, adds Cardinality and Kind
* fixes nested namespaces for reserved words and adds tests
* adds tests and generator fix for php class prefixes
* fixes escaping of protobuf packages, enum comments, misc others
* nice refactor of generated code
* adds class files for backwards compatibility
* simplifies code with templates
* adds compatibility files to makefile
* cleanup of generator and fixes nested namespace bug
* regenerates proto types
* remove internal BC classes
* adds deprecated warning, adds methods back
* simplifies if statement
* fixes dist files
* addresses review comments
* adds back TYPE_URL_PREFIX constant
* adds @deprecated to old nested class files
* skips tests which require a separate process when protobuf.so is enabled
* Adds tests for legacy nested classes that do not require separate processes to test
* uses legacy names for GPBUtil message check
* adds block for IDE @deprecated message
* Namespace for nested message/enum in c extension
* Remove unused code
Diffstat (limited to 'php/ext')
-rw-r--r-- | php/ext/google/protobuf/def.c | 253 | ||||
-rw-r--r-- | php/ext/google/protobuf/message.c | 8 |
2 files changed, 170 insertions, 91 deletions
diff --git a/php/ext/google/protobuf/def.c b/php/ext/google/protobuf/def.c index 8140fe47..cb2c7201 100644 --- a/php/ext/google/protobuf/def.c +++ b/php/ext/google/protobuf/def.c @@ -718,105 +718,186 @@ PHP_METHOD(InternalDescriptorPool, getGeneratedPool) { #endif } -static void classname_no_prefix(const char *fullname, const char *package_name, - char *class_name) { - size_t i = 0, j; - bool first_char = true, is_reserved = false; - size_t pkg_name_len = package_name == NULL ? 0 : strlen(package_name); - size_t message_name_start = package_name == NULL ? 0 : pkg_name_len + 1; - size_t message_len = (strlen(fullname) - message_name_start); +static size_t classname_len_max(const char *fullname, + const char *package, + const char *php_namespace, + const char *prefix) { + size_t fullname_len = strlen(fullname); + size_t package_len = 0; + size_t prefix_len = 0; + size_t namespace_len = 0; + size_t length = fullname_len; + int i, segment, classname_start = 0; + + if (package != NULL) { + package_len = strlen(package); + } + if (prefix != NULL) { + prefix_len = strlen(prefix); + } + if (php_namespace != NULL) { + namespace_len = strlen(php_namespace); + } - // Submessage is concatenated with its containing messages by '_'. - for (j = message_name_start; j < message_name_start + message_len; j++) { - if (fullname[j] == '.') { - class_name[i++] = '_'; - } else { - class_name[i++] = fullname[j]; + // Process package + if (package_len > 0) { + segment = 1; + for (i = 0; i < package_len; i++) { + if (package[i] == '.') { + segment++; + } } - } -} + // In case of reserved name in package. + length += 3 * segment; -static const char *classname_prefix(const char *classname, - const char *prefix_given, - const char *package_name) { - size_t i; - bool is_reserved = false; + classname_start = package_len + 1; + } - if (prefix_given != NULL && strcmp(prefix_given, "") != 0) { - return prefix_given; + // Process class name + segment = 1; + for (i = classname_start; i < fullname_len; i++) { + if (fullname[i] == '.') { + segment++; + } + } + if (prefix_len == 0) { + length += 3 * segment; + } else { + length += prefix_len * segment; } - char* lower = ALLOC_N(char, strlen(classname) + 1); - i = 0; - while(classname[i]) { - lower[i] = (char)tolower(classname[i]); + // The additional 2, one is for preceding '.' and the other is for trailing 0. + return length + namespace_len + 2; +} + +static bool is_reserved(const char *segment, int length) { + bool result; + char* lower = ALLOC_N(char, length + 1); + memcpy(lower, segment, length); + int i = 0; + while(lower[i]) { + lower[i] = (char)tolower(lower[i]); i++; } - lower[i] = 0; - - is_reserved = is_reserved_name(lower); + lower[length] = 0; + result = is_reserved_name(lower); FREE(lower); + return result; +} - if (is_reserved) { - if (package_name != NULL && strcmp("google.protobuf", package_name) == 0) { - return "GPB"; - } else { - return "PB"; +static char* fill_prefix(const char *segment, int length, + const char *prefix_given, + const char *package_name, char *classname) { + size_t i; + + if (prefix_given != NULL && strcmp(prefix_given, "") != 0) { + size_t prefix_len = strlen(prefix_given); + memcpy(classname, prefix_given, strlen(prefix_given)); + classname += prefix_len; + } else { + if (is_reserved(segment, length)) { + if (package_name != NULL && + strcmp("google.protobuf", package_name) == 0) { + memcpy(classname, "GPB", 3); + classname += 3; + } else { + memcpy(classname, "PB", 2); + classname += 2; + } } } + return classname; +} - return ""; +static char* fill_segment(const char *segment, int length, + char *classname, bool use_camel) { + memcpy(classname, segment, length); + if (use_camel && (segment[0] < 'A' || segment[0] > 'Z')) { + classname[0] += 'A' - 'a'; + } + return classname + length; } -static void convert_to_class_name_inplace(const char *package, - const char *namespace_given, - const char *prefix, char *classname) { - size_t prefix_len = prefix == NULL ? 0 : strlen(prefix); - size_t classname_len = strlen(classname); - int i = 0, j; - bool first_char = true; +static char* fill_namespace(const char *package, const char *namespace_given, + char *classname) { + if (namespace_given != NULL) { + size_t namespace_len = strlen(namespace_given); + memcpy(classname, namespace_given, namespace_len); + classname += namespace_len; + *classname = '\\'; + classname++; + } else if (package != NULL) { + int i = 0, j, offset = 0; + size_t package_len = strlen(package); + while (i < package_len) { + j = i; + while (j < package_len && package[j] != '.') { + j++; + } + classname = fill_prefix(package + i, j - i, "", package, classname); + classname = fill_segment(package + i, j - i, classname, true); + classname[0] = '\\'; + classname++; + i = j + 1; + } + } + return classname; +} - size_t package_len = package == NULL ? 0 : strlen(package); - size_t namespace_given_len = - namespace_given == NULL ? 0 : strlen(namespace_given); - bool use_namespace_given = namespace_given != NULL; - size_t namespace_len = - use_namespace_given ? namespace_given_len : package_len; +static char* fill_classname(const char *fullname, + const char *package, + const char *namespace_given, + const char *prefix, char *classname) { + int classname_start = 0; + if (package != NULL) { + size_t package_len = strlen(package); + classname_start = package_len == 0 ? 0 : package_len + 1; + } + size_t fullname_len = strlen(fullname); + classname = fill_prefix(fullname + classname_start, + fullname_len - classname_start, + prefix, package, classname); + + int i = classname_start, j; + while (i < fullname_len) { + j = i; + while (j < fullname_len && fullname[j] != '.') { + j++; + } + classname = fill_segment(fullname + i, j - i, classname, false); + if (j != fullname_len) { + *classname = '_'; + classname++; + } + i = j + 1; + } + return classname; +} - int offset = namespace_len != 0 ? 2 : 0; +static char* fill_qualified_classname(const char *fullname, + const char *package, + const char *namespace_given, + const char *prefix, char *classname) { + classname = fill_namespace(package, namespace_given, classname); + return fill_classname(fullname, package, namespace_given, prefix, classname); +} - for (j = 0; j < classname_len; j++) { - classname[namespace_len + prefix_len + classname_len + offset - 1 - j] = - classname[classname_len - j - 1]; - } +static void classname_no_prefix(const char *fullname, const char *package_name, + char *class_name) { + size_t i = 0, j; + bool first_char = true, is_reserved = false; + size_t pkg_name_len = package_name == NULL ? 0 : strlen(package_name); + size_t message_name_start = package_name == NULL ? 0 : pkg_name_len + 1; + size_t message_len = (strlen(fullname) - message_name_start); - if (namespace_len != 0) { - classname[i++] = '\\'; - for (j = 0; j < namespace_len; j++) { - if (use_namespace_given) { - classname[i++] = namespace_given[j]; - continue; - } - // php packages are divided by '\'. - if (package[j] == '.') { - classname[i++] = '\\'; - first_char = true; - } else if (first_char) { - // PHP package uses camel case. - if (package[j] < 'A' || package[j] > 'Z') { - classname[i++] = package[j] + 'A' - 'a'; - } else { - classname[i++] = package[j]; - } - first_char = false; - } else { - classname[i++] = package[j]; - } + // Submessage is concatenated with its containing messages by '_'. + for (j = message_name_start; j < message_name_start + message_len; j++) { + if (fullname[j] == '.') { + class_name[i++] = '_'; + } else { + class_name[i++] = fullname[j]; } - classname[i++] = '\\'; } - - memcpy(classname + i, prefix, prefix_len); } void internal_add_generated_file(const char *data, PHP_PROTO_SIZE data_len, @@ -858,20 +939,14 @@ void internal_add_generated_file(const char *data, PHP_PROTO_SIZE data_len, * bytes allocated, one for '.', one for trailing 0, and 3 for 'GPB' if \ * given message is google.protobuf.Empty.*/ \ const char *fullname = upb_##def_type_lower##_fullname(def_type_lower); \ + const char *package = upb_filedef_package(files[0]); \ const char *php_namespace = upb_filedef_phpnamespace(files[0]); \ const char *prefix_given = upb_filedef_phpprefix(files[0]); \ - size_t classname_len = strlen(fullname) + 5; \ - if (prefix_given != NULL) { \ - classname_len += strlen(prefix_given); \ - } \ - if (php_namespace != NULL) { \ - classname_len += strlen(php_namespace); \ - } \ + size_t classname_len = classname_len_max(fullname, package, \ + php_namespace, prefix_given); \ char *classname = ecalloc(sizeof(char), classname_len); \ - const char *package = upb_filedef_package(files[0]); \ - classname_no_prefix(fullname, package, classname); \ - const char *prefix = classname_prefix(classname, prefix_given, package); \ - convert_to_class_name_inplace(package, php_namespace, prefix, classname); \ + fill_qualified_classname(fullname, package, php_namespace, \ + prefix_given, classname); \ PHP_PROTO_CE_DECLARE pce; \ if (php_proto_zend_lookup_class(classname, strlen(classname), &pce) == \ FAILURE) { \ diff --git a/php/ext/google/protobuf/message.c b/php/ext/google/protobuf/message.c index 5a8734bc..69012871 100644 --- a/php/ext/google/protobuf/message.c +++ b/php/ext/google/protobuf/message.c @@ -858,7 +858,7 @@ static zend_function_entry field_cardinality_methods[] = { zend_class_entry* field_cardinality_type; // Init class entry. -PHP_PROTO_INIT_ENUMCLASS_START("Google\\Protobuf\\Field_Cardinality", +PHP_PROTO_INIT_ENUMCLASS_START("Google\\Protobuf\\Field\\Cardinality", Field_Cardinality, field_cardinality) zend_declare_class_constant_long(field_cardinality_type, "CARDINALITY_UNKNOWN", 19, 0 TSRMLS_CC); @@ -868,6 +868,8 @@ PHP_PROTO_INIT_ENUMCLASS_START("Google\\Protobuf\\Field_Cardinality", "CARDINALITY_REQUIRED", 20, 2 TSRMLS_CC); zend_declare_class_constant_long(field_cardinality_type, "CARDINALITY_REPEATED", 20, 3 TSRMLS_CC); + const char *alias = "Google\\Protobuf\\Field_Cardinality"; + zend_register_class_alias_ex(alias, strlen(alias), field_cardinality_type TSRMLS_CC); PHP_PROTO_INIT_ENUMCLASS_END // ----------------------------------------------------------------------------- @@ -881,7 +883,7 @@ static zend_function_entry field_kind_methods[] = { zend_class_entry* field_kind_type; // Init class entry. -PHP_PROTO_INIT_ENUMCLASS_START("Google\\Protobuf\\Field_Kind", +PHP_PROTO_INIT_ENUMCLASS_START("Google\\Protobuf\\Field\\Kind", Field_Kind, field_kind) zend_declare_class_constant_long(field_kind_type, "TYPE_UNKNOWN", 12, 0 TSRMLS_CC); @@ -921,6 +923,8 @@ PHP_PROTO_INIT_ENUMCLASS_START("Google\\Protobuf\\Field_Kind", "TYPE_SINT32", 11, 17 TSRMLS_CC); zend_declare_class_constant_long(field_kind_type, "TYPE_SINT64", 11, 18 TSRMLS_CC); + const char *alias = "Google\\Protobuf\\Field_Kind"; + zend_register_class_alias_ex(alias, strlen(alias), field_kind_type TSRMLS_CC); PHP_PROTO_INIT_ENUMCLASS_END // ----------------------------------------------------------------------------- |