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
This commit is contained in:
Paul Yang 2017-08-25 08:49:34 -07:00 committed by GitHub
parent 98a3734b5a
commit c7457ef65a
10 changed files with 905 additions and 59 deletions

View File

@ -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 \

View File

@ -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;

View File

@ -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) {

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);
@ -880,6 +1016,21 @@ extern zend_class_entry* oneof_descriptor_type;
// have a number of 0.
#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__

View File

@ -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;
}
}

View File

@ -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 messages 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;
}
}

View File

@ -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
#

View File

@ -1,8 +1,16 @@
<?php
use Google\Protobuf\GPBEmpty;
require_once('test_base.php');
require_once('test_util.php');
class WellKnownTest extends PHPUnit_Framework_TestCase {
use Google\Protobuf\GPBEmpty;
use Google\Protobuf\Any;
use Foo\TestMessage;
class NotMessage {}
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();
}
}