diff options
author | Paul Yang <TeBoring@users.noreply.github.com> | 2017-08-25 08:49:34 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-08-25 08:49:34 -0700 |
commit | c7457ef65a7a8584b1e3bd396c401ccf8e275ffa (patch) | |
tree | a142f1ed092807b1bd38683f57e37bcf852f665a /php | |
parent | 98a3734b5aa680f565af10a5fd4430baa4b4aa10 (diff) |
Add any support in php runtime. (#3486)
* Add any support in php runtime.
* Remove unused file in config.m4
* Fix comments
* Fix error for tsrmls build
* Add newly added file to Makefile.am
Diffstat (limited to 'php')
-rw-r--r-- | php/ext/google/protobuf/def.c | 57 | ||||
-rw-r--r-- | php/ext/google/protobuf/encode_decode.c | 41 | ||||
-rw-r--r-- | php/ext/google/protobuf/message.c | 277 | ||||
-rw-r--r-- | php/ext/google/protobuf/protobuf.c | 40 | ||||
-rw-r--r-- | php/ext/google/protobuf/protobuf.h | 165 | ||||
-rw-r--r-- | php/src/GPBMetadata/Google/Protobuf/Any.php | 30 | ||||
-rw-r--r-- | php/src/Google/Protobuf/Any.php | 274 | ||||
-rwxr-xr-x | php/tests/gdb_test.sh | 2 | ||||
-rw-r--r-- | php/tests/well_known_test.php | 74 |
9 files changed, 902 insertions, 58 deletions
diff --git a/php/ext/google/protobuf/def.c b/php/ext/google/protobuf/def.c index f885c145..421b84e5 100644 --- a/php/ext/google/protobuf/def.c +++ b/php/ext/google/protobuf/def.c @@ -115,33 +115,6 @@ static void append_map_entry_name(char *result, const char *field_name, check_upb_status(&status, msg); \ } while (0) -// Define PHP class -#define DEFINE_PROTOBUF_INIT_CLASS(CLASSNAME, CAMELNAME, LOWERNAME) \ - PHP_PROTO_INIT_CLASS_START(CLASSNAME, CAMELNAME, LOWERNAME) \ - PHP_PROTO_INIT_CLASS_END - -#define DEFINE_PROTOBUF_CREATE(NAME, LOWERNAME) \ - PHP_PROTO_OBJECT_CREATE_START(NAME, LOWERNAME) \ - LOWERNAME##_init_c_instance(intern TSRMLS_CC); \ - PHP_PROTO_OBJECT_CREATE_END(NAME, LOWERNAME) - -#define DEFINE_PROTOBUF_FREE(CAMELNAME, LOWERNAME) \ - PHP_PROTO_OBJECT_FREE_START(CAMELNAME, LOWERNAME) \ - LOWERNAME##_free_c(intern TSRMLS_CC); \ - PHP_PROTO_OBJECT_FREE_END - -#define DEFINE_PROTOBUF_DTOR(CAMELNAME, LOWERNAME) \ - PHP_PROTO_OBJECT_DTOR_START(CAMELNAME, LOWERNAME) \ - PHP_PROTO_OBJECT_DTOR_END - -#define DEFINE_CLASS(NAME, LOWERNAME, string_name) \ - zend_class_entry *LOWERNAME##_type; \ - zend_object_handlers *LOWERNAME##_handlers; \ - DEFINE_PROTOBUF_FREE(NAME, LOWERNAME) \ - DEFINE_PROTOBUF_DTOR(NAME, LOWERNAME) \ - DEFINE_PROTOBUF_CREATE(NAME, LOWERNAME) \ - DEFINE_PROTOBUF_INIT_CLASS(string_name, NAME, LOWERNAME) - // ----------------------------------------------------------------------------- // GPBType // ----------------------------------------------------------------------------- @@ -657,7 +630,7 @@ zend_object *internal_generated_pool_php; #endif InternalDescriptorPool *generated_pool; // The actual generated pool -static void init_generated_pool_once(TSRMLS_D) { +void init_generated_pool_once(TSRMLS_D) { if (generated_pool == NULL) { #if PHP_MAJOR_VERSION < 7 MAKE_STD_ZVAL(generated_pool_php); @@ -843,18 +816,11 @@ static void convert_to_class_name_inplace(const char *package, memcpy(classname + i, prefix, prefix_len); } -PHP_METHOD(InternalDescriptorPool, internalAddGeneratedFile) { - char *data = NULL; - PHP_PROTO_SIZE data_len; +void internal_add_generated_file(const char *data, PHP_PROTO_SIZE data_len, + InternalDescriptorPool *pool TSRMLS_DC) { upb_filedef **files; size_t i; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data, &data_len) == - FAILURE) { - return; - } - - InternalDescriptorPool *pool = UNBOX(InternalDescriptorPool, getThis()); CHECK_UPB(files = upb_loaddescriptor(data, data_len, &pool, &status), "Parse binary descriptors to internal descriptors failed"); @@ -913,6 +879,8 @@ PHP_METHOD(InternalDescriptorPool, internalAddGeneratedFile) { desc->klass = PHP_PROTO_CE_UNREF(pce); \ } \ add_ce_obj(desc->klass, desc_php); \ + add_proto_obj(upb_##def_type_lower##_fullname(desc->def_type_lower), \ + desc_php); \ efree(classname); \ break; \ } @@ -939,6 +907,21 @@ PHP_METHOD(InternalDescriptorPool, internalAddGeneratedFile) { upb_gfree(files); } +PHP_METHOD(InternalDescriptorPool, internalAddGeneratedFile) { + char *data = NULL; + PHP_PROTO_SIZE data_len; + upb_filedef **files; + size_t i; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data, &data_len) == + FAILURE) { + return; + } + + InternalDescriptorPool *pool = UNBOX(InternalDescriptorPool, getThis()); + internal_add_generated_file(data, data_len, pool TSRMLS_CC); +} + PHP_METHOD(DescriptorPool, getDescriptorByClassName) { DescriptorPool *public_pool = UNBOX(DescriptorPool, getThis()); InternalDescriptorPool *pool = public_pool->intern; diff --git a/php/ext/google/protobuf/encode_decode.c b/php/ext/google/protobuf/encode_decode.c index 89e75d6a..93a5e9c4 100644 --- a/php/ext/google/protobuf/encode_decode.c +++ b/php/ext/google/protobuf/encode_decode.c @@ -1445,9 +1445,9 @@ static const upb_handlers* msgdef_json_serialize_handlers( // PHP encode/decode methods // ----------------------------------------------------------------------------- -PHP_METHOD(Message, serializeToString) { +void serialize_to_string(zval* val, zval* return_value TSRMLS_DC) { Descriptor* desc = - UNBOX_HASHTABLE_VALUE(Descriptor, get_ce_obj(Z_OBJCE_P(getThis()))); + UNBOX_HASHTABLE_VALUE(Descriptor, get_ce_obj(Z_OBJCE_P(val))); stringsink sink; stringsink_init(&sink); @@ -1461,7 +1461,7 @@ PHP_METHOD(Message, serializeToString) { stackenv_init(&se, "Error occurred during encoding: %s"); encoder = upb_pb_encoder_create(&se.env, serialize_handlers, &sink.sink); - putmsg(getThis(), desc, upb_pb_encoder_input(encoder), 0 TSRMLS_CC); + putmsg(val, desc, upb_pb_encoder_input(encoder), 0 TSRMLS_CC); PHP_PROTO_RETVAL_STRINGL(sink.ptr, sink.len, 1); @@ -1470,6 +1470,26 @@ PHP_METHOD(Message, serializeToString) { } } +PHP_METHOD(Message, serializeToString) { + serialize_to_string(getThis(), return_value TSRMLS_CC); +} + +void merge_from_string(const char* data, int data_len, const Descriptor* desc, + MessageHeader* msg) { + const upb_pbdecodermethod* method = msgdef_decodermethod(desc); + const upb_handlers* h = upb_pbdecodermethod_desthandlers(method); + stackenv se; + upb_sink sink; + upb_pbdecoder* decoder; + stackenv_init(&se, "Error occurred during parsing: %s"); + + upb_sink_reset(&sink, h, msg); + decoder = upb_pbdecoder_create(&se.env, method, &sink); + upb_bufsrc_putbuf(data, data_len, upb_pbdecoder_input(decoder)); + + stackenv_uninit(&se); +} + PHP_METHOD(Message, mergeFromString) { Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, get_ce_obj(Z_OBJCE_P(getThis()))); @@ -1483,20 +1503,7 @@ PHP_METHOD(Message, mergeFromString) { return; } - { - const upb_pbdecodermethod* method = msgdef_decodermethod(desc); - const upb_handlers* h = upb_pbdecodermethod_desthandlers(method); - stackenv se; - upb_sink sink; - upb_pbdecoder* decoder; - stackenv_init(&se, "Error occurred during parsing: %s"); - - upb_sink_reset(&sink, h, msg); - decoder = upb_pbdecoder_create(&se.env, method, &sink); - upb_bufsrc_putbuf(data, data_len, upb_pbdecoder_input(decoder)); - - stackenv_uninit(&se); - } + merge_from_string(data, data_len, desc, msg); } PHP_METHOD(Message, serializeToJsonString) { diff --git a/php/ext/google/protobuf/message.c b/php/ext/google/protobuf/message.c index 519786dd..052865ad 100644 --- a/php/ext/google/protobuf/message.c +++ b/php/ext/google/protobuf/message.c @@ -32,9 +32,11 @@ #include <stdlib.h> #include "protobuf.h" +#include "utf8.h" -static zend_class_entry* message_type; +zend_class_entry* message_type; zend_object_handlers* message_handlers; +static const char TYPE_URL_PREFIX[] = "type.googleapis.com/"; static zend_function_entry message_methods[] = { PHP_ME(Message, clear, NULL, ZEND_ACC_PUBLIC) @@ -342,3 +344,276 @@ PHP_METHOD(Message, whichOneof) { msg->descriptor->layout, message_data(msg), oneof TSRMLS_CC); PHP_PROTO_RETURN_STRING(oneof_case_name, 1); } + +// ----------------------------------------------------------------------------- +// Any +// ----------------------------------------------------------------------------- + +static zend_function_entry any_methods[] = { + PHP_ME(Any, __construct, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Any, getTypeUrl, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Any, setTypeUrl, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Any, getValue, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Any, setValue, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Any, pack, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Any, unpack, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Any, is, NULL, ZEND_ACC_PUBLIC) + {NULL, NULL, NULL} +}; + +zend_class_entry* any_type; + +// Init class entry. +PHP_PROTO_INIT_SUBMSGCLASS_START("Google\\Protobuf\\Any", Any, any) + zend_class_implements(any_type TSRMLS_CC, 1, message_type); + zend_declare_property_string(any_type, "type_url", strlen("type_url"), + "" ,ZEND_ACC_PRIVATE TSRMLS_CC); + zend_declare_property_string(any_type, "value", strlen("value"), + "" ,ZEND_ACC_PRIVATE TSRMLS_CC); +PHP_PROTO_INIT_SUBMSGCLASS_END + +void hex_to_binary(const char* hex, char** binary, int* binary_len) { + int i; + int hex_len = strlen(hex); + *binary_len = hex_len / 2; + *binary = ALLOC_N(char, *binary_len); + for (i = 0; i < *binary_len; i++) { + char value = 0; + if (hex[i * 2] >= '0' && hex[i * 2] <= '9') { + value += (hex[i * 2] - '0') * 16; + } else { + value += (hex[i * 2] - 'a' + 10) * 16; + } + if (hex[i * 2 + 1] >= '0' && hex[i * 2 + 1] <= '9') { + value += hex[i * 2 + 1] - '0'; + } else { + value += hex[i * 2 + 1] - 'a' + 10; + } + (*binary)[i] = value; + } +} + +PHP_METHOD(Any, __construct) { + PHP_PROTO_HASHTABLE_VALUE desc_php = get_ce_obj(any_type); + if (desc_php == NULL) { + init_generated_pool_once(TSRMLS_C); + const char* generated_file = + "0acd010a19676f6f676c652f70726f746f6275662f616e792e70726f746f" + "120f676f6f676c652e70726f746f62756622260a03416e7912100a087479" + "70655f75726c180120012809120d0a0576616c756518022001280c426f0a" + "13636f6d2e676f6f676c652e70726f746f6275664208416e7950726f746f" + "50015a256769746875622e636f6d2f676f6c616e672f70726f746f627566" + "2f7074797065732f616e79a20203475042aa021e476f6f676c652e50726f" + "746f6275662e57656c6c4b6e6f776e5479706573620670726f746f33"; + char* binary; + int binary_len; + hex_to_binary(generated_file, &binary, &binary_len); + + internal_add_generated_file(binary, binary_len, generated_pool TSRMLS_CC); + FREE(binary); + } + + MessageHeader* intern = UNBOX(MessageHeader, getThis()); + custom_data_init(any_type, intern PHP_PROTO_TSRMLS_CC); +} + +PHP_METHOD(Any, getTypeUrl) { + zval member; + PHP_PROTO_ZVAL_STRING(&member, "type_url", 1); + + PHP_PROTO_FAKE_SCOPE_BEGIN(any_type); +#if PHP_MAJOR_VERSION < 7 + zval* value = message_handlers->read_property(getThis(), &member, BP_VAR_R, + NULL PHP_PROTO_TSRMLS_CC); +#else + zval* value = message_handlers->read_property(getThis(), &member, BP_VAR_R, + NULL, NULL PHP_PROTO_TSRMLS_CC); +#endif + PHP_PROTO_FAKE_SCOPE_END; + + PHP_PROTO_RETVAL_ZVAL(value); +} + +PHP_METHOD(Any, setTypeUrl) { + char *type_url = NULL; + PHP_PROTO_SIZE type_url_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &type_url, + &type_url_len) == FAILURE) { + return; + } + + zval member; + zval value; + PHP_PROTO_ZVAL_STRING(&member, "type_url", 1); + PHP_PROTO_ZVAL_STRINGL(&value, type_url, type_url_len, 1); + + PHP_PROTO_FAKE_SCOPE_BEGIN(any_type); + message_handlers->write_property(getThis(), &member, &value, + NULL PHP_PROTO_TSRMLS_CC); + PHP_PROTO_FAKE_SCOPE_END; + + PHP_PROTO_RETVAL_ZVAL(getThis()); +} + +PHP_METHOD(Any, getValue) { + zval member; + PHP_PROTO_ZVAL_STRING(&member, "value", 1); + + PHP_PROTO_FAKE_SCOPE_BEGIN(any_type); + zval* value = + php_proto_message_read_property(getThis(), &member PHP_PROTO_TSRMLS_CC); + PHP_PROTO_FAKE_SCOPE_END; + + PHP_PROTO_RETVAL_ZVAL(value); +} + +PHP_METHOD(Any, setValue) { + char *value = NULL; + PHP_PROTO_SIZE value_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &value, + &value_len) == FAILURE) { + return; + } + + zval member; + zval value_to_set; + PHP_PROTO_ZVAL_STRING(&member, "value", 1); + PHP_PROTO_ZVAL_STRINGL(&value_to_set, value, value_len, 1); + + PHP_PROTO_FAKE_SCOPE_BEGIN(any_type); + message_handlers->write_property(getThis(), &member, &value_to_set, + NULL PHP_PROTO_TSRMLS_CC); + PHP_PROTO_FAKE_SCOPE_END; + + PHP_PROTO_RETVAL_ZVAL(getThis()); +} + +PHP_METHOD(Any, unpack) { + // Get type url. + zval type_url_member; + PHP_PROTO_ZVAL_STRING(&type_url_member, "type_url", 1); + PHP_PROTO_FAKE_SCOPE_BEGIN(any_type); + zval* type_url_php = php_proto_message_read_property( + getThis(), &type_url_member PHP_PROTO_TSRMLS_CC); + PHP_PROTO_FAKE_SCOPE_END; + + // Get fully-qualified name from type url. + size_t url_prefix_len = strlen(TYPE_URL_PREFIX); + const char* type_url = Z_STRVAL_P(type_url_php); + size_t type_url_len = Z_STRLEN_P(type_url_php); + + if (url_prefix_len > type_url_len || + strncmp(TYPE_URL_PREFIX, type_url, url_prefix_len) != 0) { + zend_throw_exception( + NULL, "Type url needs to be type.googleapis.com/fully-qulified", + 0 TSRMLS_CC); + return; + } + + const char* fully_qualified_name = type_url + url_prefix_len; + PHP_PROTO_HASHTABLE_VALUE desc_php = get_proto_obj(fully_qualified_name); + if (desc_php == NULL) { + zend_throw_exception( + NULL, "Specified message in any hasn't been added to descriptor pool", + 0 TSRMLS_CC); + return; + } + Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, desc_php); + zend_class_entry* klass = desc->klass; + ZVAL_OBJ(return_value, klass->create_object(klass TSRMLS_CC)); + MessageHeader* msg = UNBOX(MessageHeader, return_value); + custom_data_init(klass, msg PHP_PROTO_TSRMLS_CC); + + // Get value. + zval value_member; + PHP_PROTO_ZVAL_STRING(&value_member, "value", 1); + PHP_PROTO_FAKE_SCOPE_RESTART(any_type); + zval* value = php_proto_message_read_property( + getThis(), &value_member PHP_PROTO_TSRMLS_CC); + PHP_PROTO_FAKE_SCOPE_END; + + merge_from_string(Z_STRVAL_P(value), Z_STRLEN_P(value), desc, msg); +} + +PHP_METHOD(Any, pack) { + zval* val; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &val) == + FAILURE) { + return; + } + + if (!instanceof_function(Z_OBJCE_P(val), message_type TSRMLS_CC)) { + zend_error(E_USER_ERROR, "Given value is not an instance of Message."); + return; + } + + // Set value by serialized data. + zval data; + serialize_to_string(val, &data TSRMLS_CC); + + zval member; + PHP_PROTO_ZVAL_STRING(&member, "value", 1); + + PHP_PROTO_FAKE_SCOPE_BEGIN(any_type); + message_handlers->write_property(getThis(), &member, &data, + NULL PHP_PROTO_TSRMLS_CC); + PHP_PROTO_FAKE_SCOPE_END; + + // Set type url. + Descriptor* desc = + UNBOX_HASHTABLE_VALUE(Descriptor, get_ce_obj(Z_OBJCE_P(val))); + const char* fully_qualified_name = upb_msgdef_fullname(desc->msgdef); + size_t type_url_len = + strlen(TYPE_URL_PREFIX) + strlen(fully_qualified_name) + 1; + char* type_url = ALLOC_N(char, type_url_len); + sprintf(type_url, "%s%s", TYPE_URL_PREFIX, fully_qualified_name); + zval type_url_php; + PHP_PROTO_ZVAL_STRING(&type_url_php, type_url, 1); + PHP_PROTO_ZVAL_STRING(&member, "type_url", 1); + + PHP_PROTO_FAKE_SCOPE_RESTART(any_type); + message_handlers->write_property(getThis(), &member, &type_url_php, + NULL PHP_PROTO_TSRMLS_CC); + PHP_PROTO_FAKE_SCOPE_END; + FREE(type_url); +} + +PHP_METHOD(Any, is) { + zend_class_entry *klass = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "C", &klass) == + FAILURE) { + return; + } + + PHP_PROTO_HASHTABLE_VALUE desc_php = get_ce_obj(klass); + if (desc_php == NULL) { + RETURN_BOOL(false); + } + + // Create corresponded type url. + Descriptor* desc = + UNBOX_HASHTABLE_VALUE(Descriptor, get_ce_obj(klass)); + const char* fully_qualified_name = upb_msgdef_fullname(desc->msgdef); + size_t type_url_len = + strlen(TYPE_URL_PREFIX) + strlen(fully_qualified_name) + 1; + char* type_url = ALLOC_N(char, type_url_len); + sprintf(type_url, "%s%s", TYPE_URL_PREFIX, fully_qualified_name); + + // Fetch stored type url. + zval member; + PHP_PROTO_ZVAL_STRING(&member, "type_url", 1); + PHP_PROTO_FAKE_SCOPE_BEGIN(any_type); + zval* value = + php_proto_message_read_property(getThis(), &member PHP_PROTO_TSRMLS_CC); + PHP_PROTO_FAKE_SCOPE_END; + + // Compare two type url. + bool is = strcmp(type_url, Z_STRVAL_P(value)) == 0; + FREE(type_url); + + RETURN_BOOL(is); +} diff --git a/php/ext/google/protobuf/protobuf.c b/php/ext/google/protobuf/protobuf.c index dc730030..abdf14b9 100644 --- a/php/ext/google/protobuf/protobuf.c +++ b/php/ext/google/protobuf/protobuf.c @@ -46,6 +46,9 @@ static HashTable* upb_def_to_php_obj_map; // Global map from message/enum's php class entry to corresponding wrapper // Descriptor/EnumDescriptor instances. static HashTable* ce_to_php_obj_map; +// Global map from message/enum's proto fully-qualified name to corresponding +// wrapper Descriptor/EnumDescriptor instances. +static HashTable* proto_to_php_obj_map; // ----------------------------------------------------------------------------- // Global maps. @@ -80,6 +83,22 @@ static void add_to_list(HashTable* t, void* value) { (void**)&pDest); } +static void add_to_strtable(HashTable* t, const char* key, int key_size, + void* value) { + zval* pDest = NULL; + php_proto_zend_hash_update_mem(t, key, key_size, &value, sizeof(void*), + (void**)&pDest); +} + +static void* get_from_strtable(const HashTable* t, const char* key, int key_size) { + void** value; + if (php_proto_zend_hash_find_mem(t, key, key_size, (void**)&value) == + FAILURE) { + return NULL; + } + return *value; +} + void add_def_obj(const void* def, PHP_PROTO_HASHTABLE_VALUE value) { #if PHP_MAJOR_VERSION < 7 Z_ADDREF_P(value); @@ -110,6 +129,20 @@ bool class_added(const void* ce) { return exist_in_table(ce_to_php_obj_map, ce); } +void add_proto_obj(const char* proto, PHP_PROTO_HASHTABLE_VALUE value) { +#if PHP_MAJOR_VERSION < 7 + Z_ADDREF_P(value); +#else + ++GC_REFCOUNT(value); +#endif + add_to_strtable(proto_to_php_obj_map, proto, strlen(proto), value); +} + +PHP_PROTO_HASHTABLE_VALUE get_proto_obj(const char* proto) { + return (PHP_PROTO_HASHTABLE_VALUE)get_from_strtable(proto_to_php_obj_map, + proto, strlen(proto)); +} + // ----------------------------------------------------------------------------- // Utilities. // ----------------------------------------------------------------------------- @@ -163,6 +196,9 @@ static PHP_RINIT_FUNCTION(protobuf) { ALLOC_HASHTABLE(ce_to_php_obj_map); zend_hash_init(ce_to_php_obj_map, 16, NULL, HASHTABLE_VALUE_DTOR, 0); + ALLOC_HASHTABLE(proto_to_php_obj_map); + zend_hash_init(proto_to_php_obj_map, 16, NULL, HASHTABLE_VALUE_DTOR, 0); + generated_pool = NULL; generated_pool_php = NULL; internal_generated_pool_php = NULL; @@ -177,6 +213,9 @@ static PHP_RSHUTDOWN_FUNCTION(protobuf) { zend_hash_destroy(ce_to_php_obj_map); FREE_HASHTABLE(ce_to_php_obj_map); + zend_hash_destroy(proto_to_php_obj_map); + FREE_HASHTABLE(proto_to_php_obj_map); + #if PHP_MAJOR_VERSION < 7 if (generated_pool_php != NULL) { zval_dtor(generated_pool_php); @@ -217,6 +256,7 @@ static PHP_MINIT_FUNCTION(protobuf) { repeated_field_init(TSRMLS_C); repeated_field_iter_init(TSRMLS_C); util_init(TSRMLS_C); + any_init(TSRMLS_C); return 0; } diff --git a/php/ext/google/protobuf/protobuf.h b/php/ext/google/protobuf/protobuf.h index b2838e56..105acc30 100644 --- a/php/ext/google/protobuf/protobuf.h +++ b/php/ext/google/protobuf/protobuf.h @@ -77,15 +77,28 @@ #define php_proto_zend_hash_index_update_zval(ht, h, pData) \ zend_hash_index_update(ht, h, &(pData), sizeof(void*), NULL) +#define php_proto_zend_hash_update_zval(ht, key, key_len, value) \ + zend_hash_update(ht, key, key_len, value, sizeof(void*), NULL) + #define php_proto_zend_hash_index_update_mem(ht, h, pData, nDataSize, pDest) \ zend_hash_index_update(ht, h, pData, nDataSize, pDest) +#define php_proto_zend_hash_update_mem(ht, key, key_len, pData, nDataSize, \ + pDest) \ + zend_hash_update(ht, key, key_len, pData, nDataSize, pDest) + #define php_proto_zend_hash_index_find_zval(ht, h, pDest) \ zend_hash_index_find(ht, h, pDest) #define php_proto_zend_hash_index_find_mem(ht, h, pDest) \ zend_hash_index_find(ht, h, pDest) +#define php_proto_zend_hash_find_zval(ht, key, key_len, pDest) \ + zend_hash_find(ht, key, key_len, pDest) + +#define php_proto_zend_hash_find_mem(ht, key, key_len, pDest) \ + zend_hash_find(ht, key, key_len, pDest) + #define php_proto_zend_hash_next_index_insert_zval(ht, pData) \ zend_hash_next_index_insert(ht, pData, sizeof(void*), NULL) @@ -103,6 +116,17 @@ #define PHP_PROTO_WRAP_OBJECT_END \ }; +#define PHP_PROTO_INIT_SUBMSGCLASS_START(CLASSNAME, CAMELNAME, LOWWERNAME) \ + void LOWWERNAME##_init(TSRMLS_D) { \ + zend_class_entry class_type; \ + const char* class_name = CLASSNAME; \ + INIT_CLASS_ENTRY_EX(class_type, CLASSNAME, strlen(CLASSNAME), \ + LOWWERNAME##_methods); \ + LOWWERNAME##_type = zend_register_internal_class(&class_type TSRMLS_CC); \ + LOWWERNAME##_type->create_object = message_create; +#define PHP_PROTO_INIT_SUBMSGCLASS_END \ + } + #define PHP_PROTO_INIT_CLASS_START(CLASSNAME, CAMELNAME, LOWWERNAME) \ void LOWWERNAME##_init(TSRMLS_D) { \ zend_class_entry class_type; \ @@ -202,6 +226,8 @@ #define php_proto_zend_lookup_class(name, name_length, ce) \ zend_lookup_class(name, name_length, ce TSRMLS_CC) +#define PHP_PROTO_RETVAL_ZVAL(value) ZVAL_ZVAL(return_value, value, 1, 0) + #else // PHP_MAJOR_VERSION >= 7 #define php_proto_zend_literal void** @@ -243,6 +269,23 @@ static inline int php_proto_zend_hash_index_update_mem(HashTable* ht, ulong h, return result != NULL ? SUCCESS : FAILURE; } +static inline int php_proto_zend_hash_update_zval(HashTable* ht, + const char* key, uint key_len, + zval* pData) { + zend_string* internal_key = zend_string_init(key, key_len, 0); + zend_hash_update(ht, internal_key, pData); +} + +static inline int php_proto_zend_hash_update_mem(HashTable* ht, const char* key, + uint key_len, void* pData, + uint nDataSize, void** pDest) { + zend_string* internal_key = zend_string_init(key, key_len, 0); + void* result = zend_hash_update_mem(ht, internal_key, pData, nDataSize); + zend_string_release(internal_key); + if (pDest != NULL) *pDest = result; + return result != NULL ? SUCCESS : FAILURE; +} + static inline int php_proto_zend_hash_index_find_zval(const HashTable* ht, ulong h, void** pDest) { zval* result = zend_hash_index_find(ht, h); @@ -258,6 +301,25 @@ static inline int php_proto_zend_hash_index_find_mem(const HashTable* ht, return result != NULL ? SUCCESS : FAILURE; } +static inline int php_proto_zend_hash_find_zval(const HashTable* ht, + const char* key, uint key_len, + void** pDest) { + zend_string* internal_key = zend_string_init(key, key_len, 1); + zval* result = zend_hash_find(ht, internal_key); + if (pDest != NULL) *pDest = result; + return result != NULL ? SUCCESS : FAILURE; +} + +static inline int php_proto_zend_hash_find_mem(const HashTable* ht, + const char* key, uint key_len, + void** pDest) { + zend_string* internal_key = zend_string_init(key, key_len, 1); + void* result = zend_hash_find_ptr(ht, internal_key); + zend_string_release(internal_key); + if (pDest != NULL) *pDest = result; + return result != NULL ? SUCCESS : FAILURE; +} + static inline int php_proto_zend_hash_next_index_insert_zval(HashTable* ht, void* pData) { zval tmp; @@ -292,6 +354,17 @@ static inline int php_proto_zend_hash_get_current_data_ex(HashTable* ht, zend_object std; \ }; +#define PHP_PROTO_INIT_SUBMSGCLASS_START(CLASSNAME, CAMELNAME, LOWWERNAME) \ + void LOWWERNAME##_init(TSRMLS_D) { \ + zend_class_entry class_type; \ + const char* class_name = CLASSNAME; \ + INIT_CLASS_ENTRY_EX(class_type, CLASSNAME, strlen(CLASSNAME), \ + LOWWERNAME##_methods); \ + LOWWERNAME##_type = zend_register_internal_class(&class_type TSRMLS_CC); \ + LOWWERNAME##_type->create_object = message_create; +#define PHP_PROTO_INIT_SUBMSGCLASS_END \ + } + #define PHP_PROTO_INIT_CLASS_START(CLASSNAME, CAMELNAME, LOWWERNAME) \ void LOWWERNAME##_init(TSRMLS_D) { \ zend_class_entry class_type; \ @@ -390,12 +463,60 @@ static inline int php_proto_zend_lookup_class( return *ce != NULL ? SUCCESS : FAILURE; } +#define PHP_PROTO_RETVAL_ZVAL(value) ZVAL_COPY(return_value, value) + #endif // PHP_MAJOR_VERSION >= 7 +#if PHP_MAJOR_VERSION < 7 || (PHP_MAJOR_VERSION == 7 && PHP_MINOR_VERSION == 0) +#define PHP_PROTO_FAKE_SCOPE_BEGIN(klass) \ + zend_class_entry* old_scope = EG(scope); \ + EG(scope) = klass; +#define PHP_PROTO_FAKE_SCOPE_RESTART(klass) \ + old_scope = EG(scope); \ + EG(scope) = klass; +#define PHP_PROTO_FAKE_SCOPE_END EG(scope) = old_scope; +#else +#define PHP_PROTO_FAKE_SCOPE_BEGIN(klass) \ + zend_class_entry* old_scope = EG(fake_scope); \ + EG(fake_scope) = klass; +#define PHP_PROTO_FAKE_SCOPE_RESTART(klass) \ + old_scope = EG(fake_scope); \ + EG(fake_scope) = klass; +#define PHP_PROTO_FAKE_SCOPE_END EG(fake_scope) = old_scope; +#endif + +// Define PHP class +#define DEFINE_PROTOBUF_INIT_CLASS(CLASSNAME, CAMELNAME, LOWERNAME) \ + PHP_PROTO_INIT_CLASS_START(CLASSNAME, CAMELNAME, LOWERNAME) \ + PHP_PROTO_INIT_CLASS_END + +#define DEFINE_PROTOBUF_CREATE(NAME, LOWERNAME) \ + PHP_PROTO_OBJECT_CREATE_START(NAME, LOWERNAME) \ + LOWERNAME##_init_c_instance(intern TSRMLS_CC); \ + PHP_PROTO_OBJECT_CREATE_END(NAME, LOWERNAME) + +#define DEFINE_PROTOBUF_FREE(CAMELNAME, LOWERNAME) \ + PHP_PROTO_OBJECT_FREE_START(CAMELNAME, LOWERNAME) \ + LOWERNAME##_free_c(intern TSRMLS_CC); \ + PHP_PROTO_OBJECT_FREE_END + +#define DEFINE_PROTOBUF_DTOR(CAMELNAME, LOWERNAME) \ + PHP_PROTO_OBJECT_DTOR_START(CAMELNAME, LOWERNAME) \ + PHP_PROTO_OBJECT_DTOR_END + +#define DEFINE_CLASS(NAME, LOWERNAME, string_name) \ + zend_class_entry *LOWERNAME##_type; \ + zend_object_handlers *LOWERNAME##_handlers; \ + DEFINE_PROTOBUF_FREE(NAME, LOWERNAME) \ + DEFINE_PROTOBUF_DTOR(NAME, LOWERNAME) \ + DEFINE_PROTOBUF_CREATE(NAME, LOWERNAME) \ + DEFINE_PROTOBUF_INIT_CLASS(string_name, NAME, LOWERNAME) + // ----------------------------------------------------------------------------- // Forward Declaration // ---------------------------------------------------------------------------- +struct Any; struct DescriptorPool; struct Descriptor; struct EnumDescriptor; @@ -411,6 +532,7 @@ struct Map; struct MapIter; struct Oneof; +typedef struct Any Any; typedef struct DescriptorPool DescriptorPool; typedef struct Descriptor Descriptor; typedef struct EnumDescriptor EnumDescriptor; @@ -434,6 +556,7 @@ ZEND_BEGIN_MODULE_GLOBALS(protobuf) ZEND_END_MODULE_GLOBALS(protobuf) // Init module and PHP classes. +void any_init(TSRMLS_D); void descriptor_init(TSRMLS_D); void enum_descriptor_init(TSRMLS_D); void descriptor_pool_init(TSRMLS_D); @@ -442,11 +565,11 @@ void field_descriptor_init(TSRMLS_D); void gpb_type_init(TSRMLS_D); void map_field_init(TSRMLS_D); void map_field_iter_init(TSRMLS_D); +void message_init(TSRMLS_D); void oneof_descriptor_init(TSRMLS_D); void repeated_field_init(TSRMLS_D); void repeated_field_iter_init(TSRMLS_D); void util_init(TSRMLS_D); -void message_init(TSRMLS_D); // Global map from upb {msg,enum}defs to wrapper Descriptor/EnumDescriptor // instances. @@ -459,6 +582,11 @@ void add_ce_obj(const void* ce, PHP_PROTO_HASHTABLE_VALUE value); PHP_PROTO_HASHTABLE_VALUE get_ce_obj(const void* ce); bool class_added(const void* ce); +// Global map from message/enum's proto fully-qualified name to corresponding +// wrapper Descriptor/EnumDescriptor instances. +void add_proto_obj(const char* proto, PHP_PROTO_HASHTABLE_VALUE value); +PHP_PROTO_HASHTABLE_VALUE get_proto_obj(const char* proto); + extern zend_class_entry* map_field_type; extern zend_class_entry* repeated_field_type; @@ -482,6 +610,10 @@ PHP_PROTO_WRAP_OBJECT_END PHP_METHOD(InternalDescriptorPool, getGeneratedPool); PHP_METHOD(InternalDescriptorPool, internalAddGeneratedFile); +void internal_add_generated_file(const char* data, PHP_PROTO_SIZE data_len, + InternalDescriptorPool* pool TSRMLS_DC); +void init_generated_pool_once(TSRMLS_D); + // wrapper of generated pool #if PHP_MAJOR_VERSION < 7 extern zval* generated_pool_php; @@ -567,6 +699,7 @@ void custom_data_init(const zend_class_entry* ce, void build_class_from_descriptor( PHP_PROTO_HASHTABLE_VALUE php_descriptor TSRMLS_DC); +extern zend_class_entry* message_type; extern zend_object_handlers* message_handlers; // ----------------------------------------------------------------------------- @@ -674,6 +807,9 @@ PHP_METHOD(Message, __construct); // This is called from the message class creation code. const upb_pbdecodermethod *new_fillmsg_decodermethod(Descriptor *desc, const void *owner); +void serialize_to_string(zval* val, zval* return_value TSRMLS_DC); +void merge_from_string(const char* data, int data_len, const Descriptor* desc, + MessageHeader* msg); PHP_METHOD(Message, serializeToString); PHP_METHOD(Message, mergeFromString); @@ -881,6 +1017,21 @@ extern zend_class_entry* oneof_descriptor_type; #define ONEOF_CASE_NONE 0 // ----------------------------------------------------------------------------- +// Well Known Type. +// ----------------------------------------------------------------------------- + +PHP_METHOD(Any, __construct); +PHP_METHOD(Any, getTypeUrl); +PHP_METHOD(Any, setTypeUrl); +PHP_METHOD(Any, getValue); +PHP_METHOD(Any, setValue); +PHP_METHOD(Any, unpack); +PHP_METHOD(Any, pack); +PHP_METHOD(Any, is); + +extern zend_class_entry* any_type; + +// ----------------------------------------------------------------------------- // Upb. // ----------------------------------------------------------------------------- @@ -910,4 +1061,16 @@ const zend_class_entry* field_type_class( .bucket.obj.object)) #endif +// Message handler +static inline zval* php_proto_message_read_property( + zval* msg, zval* member PHP_PROTO_TSRMLS_DC) { +#if PHP_MAJOR_VERSION < 7 + return message_handlers->read_property(msg, member, BP_VAR_R, + NULL PHP_PROTO_TSRMLS_CC); +#else + return message_handlers->read_property(msg, member, BP_VAR_R, NULL, + NULL PHP_PROTO_TSRMLS_CC); +#endif +} + #endif // __GOOGLE_PROTOBUF_PHP_PROTOBUF_H__ diff --git a/php/src/GPBMetadata/Google/Protobuf/Any.php b/php/src/GPBMetadata/Google/Protobuf/Any.php new file mode 100644 index 00000000..22cc2500 --- /dev/null +++ b/php/src/GPBMetadata/Google/Protobuf/Any.php @@ -0,0 +1,30 @@ +<?php +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: google/protobuf/any.proto + +namespace GPBMetadata\Google\Protobuf; + +class Any +{ + public static $is_initialized = false; + + public static function initOnce() { + $pool = \Google\Protobuf\Internal\DescriptorPool::getGeneratedPool(); + + if (static::$is_initialized == true) { + return; + } + $pool->internalAddGeneratedFile(hex2bin( + "0acd010a19676f6f676c652f70726f746f6275662f616e792e70726f746f" . + "120f676f6f676c652e70726f746f62756622260a03416e7912100a087479" . + "70655f75726c180120012809120d0a0576616c756518022001280c426f0a" . + "13636f6d2e676f6f676c652e70726f746f6275664208416e7950726f746f" . + "50015a256769746875622e636f6d2f676f6c616e672f70726f746f627566" . + "2f7074797065732f616e79a20203475042aa021e476f6f676c652e50726f" . + "746f6275662e57656c6c4b6e6f776e5479706573620670726f746f33" + )); + + static::$is_initialized = true; + } +} + diff --git a/php/src/Google/Protobuf/Any.php b/php/src/Google/Protobuf/Any.php new file mode 100644 index 00000000..a8ce4316 --- /dev/null +++ b/php/src/Google/Protobuf/Any.php @@ -0,0 +1,274 @@ +<?php +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: google/protobuf/any.proto + +namespace Google\Protobuf; + +use Google\Protobuf\Internal\DescriptorPool; +use Google\Protobuf\Internal\GPBType; +use Google\Protobuf\Internal\GPBUtil; +use Google\Protobuf\Internal\Message; +use Google\Protobuf\Internal\RepeatedField; + +/** + * `Any` contains an arbitrary serialized protocol buffer message along with a + * URL that describes the type of the serialized message. + * Protobuf library provides support to pack/unpack Any values in the form + * of utility functions or additional generated methods of the Any type. + * Example 1: Pack and unpack a message in C++. + * Foo foo = ...; + * Any any; + * any.PackFrom(foo); + * ... + * if (any.UnpackTo(&foo)) { + * ... + * } + * Example 2: Pack and unpack a message in Java. + * Foo foo = ...; + * Any any = Any.pack(foo); + * ... + * if (any.is(Foo.class)) { + * foo = any.unpack(Foo.class); + * } + * Example 3: Pack and unpack a message in Python. + * foo = Foo(...) + * any = Any() + * any.Pack(foo) + * ... + * if any.Is(Foo.DESCRIPTOR): + * any.Unpack(foo) + * ... + * The pack methods provided by protobuf library will by default use + * 'type.googleapis.com/full.type.name' as the type URL and the unpack + * methods only use the fully qualified type name after the last '/' + * in the type URL, for example "foo.bar.com/x/y.z" will yield type + * name "y.z". + * JSON + * ==== + * The JSON representation of an `Any` value uses the regular + * representation of the deserialized, embedded message, with an + * additional field `@type` which contains the type URL. Example: + * package google.profile; + * message Person { + * string first_name = 1; + * string last_name = 2; + * } + * { + * "@type": "type.googleapis.com/google.profile.Person", + * "firstName": <string>, + * "lastName": <string> + * } + * If the embedded message type is well-known and has a custom JSON + * representation, that representation will be embedded adding a field + * `value` which holds the custom JSON in addition to the `@type` + * field. Example (for message [google.protobuf.Duration][]): + * { + * "@type": "type.googleapis.com/google.protobuf.Duration", + * "value": "1.212s" + * } + * + * Generated from protobuf message <code>google.protobuf.Any</code> + */ +class Any extends \Google\Protobuf\Internal\Message +{ + /** + * A URL/resource name whose content describes the type of the + * serialized protocol buffer message. + * For URLs which use the scheme `http`, `https`, or no scheme, the + * following restrictions and interpretations apply: + * * If no scheme is provided, `https` is assumed. + * * The last segment of the URL's path must represent the fully + * qualified name of the type (as in `path/google.protobuf.Duration`). + * The name should be in a canonical form (e.g., leading "." is + * not accepted). + * * An HTTP GET on the URL must yield a [google.protobuf.Type][] + * value in binary format, or produce an error. + * * Applications are allowed to cache lookup results based on the + * URL, or have them precompiled into a binary to avoid any + * lookup. Therefore, binary compatibility needs to be preserved + * on changes to types. (Use versioned type names to manage + * breaking changes.) + * Schemes other than `http`, `https` (or the empty scheme) might be + * used with implementation specific semantics. + * + * Generated from protobuf field <code>string type_url = 1;</code> + */ + private $type_url = ''; + /** + * Must be a valid serialized protocol buffer of the above specified type. + * + * Generated from protobuf field <code>bytes value = 2;</code> + */ + private $value = ''; + + const TYPE_URL_PREFIX = 'type.googleapis.com/'; + + public function __construct() { + \GPBMetadata\Google\Protobuf\Any::initOnce(); + parent::__construct(); + } + + /** + * A URL/resource name whose content describes the type of the + * serialized protocol buffer message. + * For URLs which use the scheme `http`, `https`, or no scheme, the + * following restrictions and interpretations apply: + * * If no scheme is provided, `https` is assumed. + * * The last segment of the URL's path must represent the fully + * qualified name of the type (as in `path/google.protobuf.Duration`). + * The name should be in a canonical form (e.g., leading "." is + * not accepted). + * * An HTTP GET on the URL must yield a [google.protobuf.Type][] + * value in binary format, or produce an error. + * * Applications are allowed to cache lookup results based on the + * URL, or have them precompiled into a binary to avoid any + * lookup. Therefore, binary compatibility needs to be preserved + * on changes to types. (Use versioned type names to manage + * breaking changes.) + * Schemes other than `http`, `https` (or the empty scheme) might be + * used with implementation specific semantics. + * + * Generated from protobuf field <code>string type_url = 1;</code> + * @return string + */ + public function getTypeUrl() + { + return $this->type_url; + } + + /** + * A URL/resource name whose content describes the type of the + * serialized protocol buffer message. + * For URLs which use the scheme `http`, `https`, or no scheme, the + * following restrictions and interpretations apply: + * * If no scheme is provided, `https` is assumed. + * * The last segment of the URL's path must represent the fully + * qualified name of the type (as in `path/google.protobuf.Duration`). + * The name should be in a canonical form (e.g., leading "." is + * not accepted). + * * An HTTP GET on the URL must yield a [google.protobuf.Type][] + * value in binary format, or produce an error. + * * Applications are allowed to cache lookup results based on the + * URL, or have them precompiled into a binary to avoid any + * lookup. Therefore, binary compatibility needs to be preserved + * on changes to types. (Use versioned type names to manage + * breaking changes.) + * Schemes other than `http`, `https` (or the empty scheme) might be + * used with implementation specific semantics. + * + * Generated from protobuf field <code>string type_url = 1;</code> + * @param string $var + * @return $this + */ + public function setTypeUrl($var) + { + GPBUtil::checkString($var, True); + $this->type_url = $var; + + return $this; + } + + /** + * Must be a valid serialized protocol buffer of the above specified type. + * + * Generated from protobuf field <code>bytes value = 2;</code> + * @return string + */ + public function getValue() + { + return $this->value; + } + + /** + * Must be a valid serialized protocol buffer of the above specified type. + * + * Generated from protobuf field <code>bytes value = 2;</code> + * @param string $var + * @return $this + */ + public function setValue($var) + { + GPBUtil::checkString($var, False); + $this->value = $var; + + return $this; + } + + /** + * This method will try to resolve the type_url in Any message to get the + * targeted message type. If failed, an error will be thrown. Otherwise, + * the method will create a message of the targeted type and fill it with + * the decoded value in Any. + * @return unpacked message + * @throws Exception Type url needs to be type.googleapis.com/fully-qulified. + * @throws Exception Class hasn't been added to descriptor pool. + * @throws Exception cannot decode data in value field. + */ + public function unpack() + { + // Get fully qualifed name from type url. + $type_url_len = strlen($this->type_url); + $url_prifix_len = strlen(Any::TYPE_URL_PREFIX); + if ($type_url_len < url_prifix_len || + substr($this->type_url, 0, $url_prifix_len) != + Any::TYPE_URL_PREFIX) { + throw new \Exception( + "Type url needs to be type.googleapis.com/fully-qulified"); + } + $fully_qualifed_name = + substr($this->type_url, $url_prifix_len, $type_url_len); + + // Create message according to fully qualified name. + $pool = DescriptorPool::getGeneratedPool(); + $desc = $pool->getDescriptorByProtoName( ".".$fully_qualifed_name); + if (is_null($desc)) { + throw new \Exception("Class ".$fully_qualifed_name + ." hasn't been added to descriptor pool"); + } + $klass = $desc->getClass(); + $msg = new $klass(); + + // Merge data into message. + $msg->mergeFromString($this->value); + return $msg; + } + + /** + * The type_url will be created according to the given message’s type and + * the value is encoded data from the given message.. + * @param message: A proto message. + */ + public function pack($msg) + { + if (!$msg instanceof Message) { + trigger_error("Given parameter is not a message instance.", + E_USER_ERROR); + return; + } + + // Set value using serialzed message. + $this->value = $msg->serializeToString(); + + // Set type url. + $pool = DescriptorPool::getGeneratedPool(); + $desc = $pool->getDescriptorByClassName(get_class($msg)); + $fully_qualifed_name = $desc->getFullName(); + $this->type_url = Any::TYPE_URL_PREFIX.substr( + $fully_qualifed_name, 1, strlen($fully_qualifed_name)); + } + + /** + * This method returns whether the type_url in any_message is corresponded + * to the given class. + * @param klass: The fully qualified PHP class name of a proto message type. + */ + public function is($klass) + { + $pool = DescriptorPool::getGeneratedPool(); + $desc = $pool->getDescriptorByClassName($klass); + $fully_qualifed_name = $desc->getFullName(); + $type_url = Any::TYPE_URL_PREFIX.substr( + $fully_qualifed_name, 1, strlen($fully_qualifed_name)); + return $this->type_url === $type_url; + } +} diff --git a/php/tests/gdb_test.sh b/php/tests/gdb_test.sh index 484e2edf..0809bef3 100755 --- a/php/tests/gdb_test.sh +++ b/php/tests/gdb_test.sh @@ -3,7 +3,7 @@ # gdb --args php -dextension=../ext/google/protobuf/modules/protobuf.so `which # phpunit` --bootstrap autoload.php tmp_test.php # -gdb --args php -dextension=../ext/google/protobuf/modules/protobuf.so `which phpunit` --bootstrap autoload.php encode_decode_test.php +gdb --args php -dextension=../ext/google/protobuf/modules/protobuf.so `which phpunit` --bootstrap autoload.php well_known_test.php # # gdb --args php -dextension=../ext/google/protobuf/modules/protobuf.so memory_leak_test.php # diff --git a/php/tests/well_known_test.php b/php/tests/well_known_test.php index 0c2aec13..bdf41bfb 100644 --- a/php/tests/well_known_test.php +++ b/php/tests/well_known_test.php @@ -1,8 +1,16 @@ <?php +require_once('test_base.php'); +require_once('test_util.php'); + use Google\Protobuf\GPBEmpty; +use Google\Protobuf\Any; + +use Foo\TestMessage; + +class NotMessage {} -class WellKnownTest extends PHPUnit_Framework_TestCase { +class WellKnownTest extends TestBase { public function testNone() { @@ -14,4 +22,68 @@ class WellKnownTest extends PHPUnit_Framework_TestCase { $msg = new TestImportDescriptorProto(); } + public function testAny() + { + // Create embed message + $embed = new TestMessage(); + $this->setFields($embed); + $data = $embed->serializeToString(); + + // Set any via normal setter. + $any = new Any(); + + $this->assertSame( + $any, $any->setTypeUrl("type.googleapis.com/foo.TestMessage")); + $this->assertSame("type.googleapis.com/foo.TestMessage", + $any->getTypeUrl()); + + $this->assertSame($any, $any->setValue($data)); + $this->assertSame($data, $any->getValue()); + + // Test unpack. + $msg = $any->unpack(); + $this->assertTrue($msg instanceof TestMessage); + $this->expectFields($msg); + + // Test pack. + $any = new Any(); + $any->pack($embed); + $this->assertSame($data, $any->getValue()); + $this->assertSame("type.googleapis.com/foo.TestMessage", $any->getTypeUrl()); + + // Test is. + $this->assertTrue($any->is(TestMessage::class)); + $this->assertFalse($any->is(Any::class)); + } + + /** + * @expectedException Exception + */ + public function testAnyUnpackInvalidTypeUrl() + { + $any = new Any(); + $any->setTypeUrl("invalid"); + $any->unpack(); + } + + /** + * @expectedException Exception + */ + public function testAnyUnpackMessageNotAdded() + { + $any = new Any(); + $any->setTypeUrl("type.googleapis.com/MessageNotAdded"); + $any->unpack(); + } + + /** + * @expectedException Exception + */ + public function testAnyUnpackDecodeError() + { + $any = new Any(); + $any->setTypeUrl("type.googleapis.com/foo.TestMessage"); + $any->setValue("abc"); + $any->unpack(); + } } |