aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--php/ext/google/protobuf/def.c57
-rw-r--r--php/ext/google/protobuf/encode_decode.c41
-rw-r--r--php/ext/google/protobuf/message.c277
-rw-r--r--php/ext/google/protobuf/protobuf.c40
-rw-r--r--php/ext/google/protobuf/protobuf.h165
-rw-r--r--php/src/GPBMetadata/Google/Protobuf/Any.php30
-rw-r--r--php/src/Google/Protobuf/Any.php274
-rwxr-xr-xphp/tests/gdb_test.sh2
-rw-r--r--php/tests/well_known_test.php74
10 files changed, 904 insertions, 58 deletions
diff --git a/Makefile.am b/Makefile.am
index 52d7ce9d..cfcfc14e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -600,6 +600,7 @@ php_EXTRA_DIST= \
php/ext/google/protobuf/upb.c \
php/ext/google/protobuf/protobuf.c \
php/src/phpdoc.dist.xml \
+ php/src/Google/Protobuf/Any.php \
php/src/Google/Protobuf/Descriptor.php \
php/src/Google/Protobuf/DescriptorPool.php \
php/src/Google/Protobuf/EnumDescriptor.php \
@@ -664,6 +665,7 @@ php_EXTRA_DIST= \
php/src/Google/Protobuf/Internal/SourceCodeInfo.php \
php/src/Google/Protobuf/Internal/UninterpretedOption_NamePart.php \
php/src/Google/Protobuf/Internal/UninterpretedOption.php \
+ php/src/GPBMetadata/Google/Protobuf/Any.php \
php/src/GPBMetadata/Google/Protobuf/Internal/Descriptor.php \
php/tests/array_test.php \
php/tests/autoload.php \
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 `&#64;type` which contains the type URL. Example:
+ * package google.profile;
+ * message Person {
+ * string first_name = 1;
+ * string last_name = 2;
+ * }
+ * {
+ * "&#64;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 `&#64;type`
+ * field. Example (for message [google.protobuf.Duration][]):
+ * {
+ * "&#64;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();
+ }
}