Expose descriptor API in php c extension (#3422)

This commit is contained in:
Paul Yang 2017-08-02 07:42:27 -07:00 committed by GitHub
parent be73938d72
commit c15a3269f9
8 changed files with 842 additions and 28 deletions

View File

@ -658,6 +658,7 @@ php_EXTRA_DIST= \
php/tests/array_test.php \
php/tests/autoload.php \
php/tests/compatibility_test.sh \
php/tests/descriptors_test.php \
php/tests/encode_decode_test.php \
php/tests/gdb_test.sh \
php/tests/generated_class_test.php \
@ -666,6 +667,7 @@ php_EXTRA_DIST= \
php/tests/map_field_test.php \
php/tests/memory_leak_test.php \
php/tests/php_implementation_test.php \
php/tests/proto/test_descriptors.proto \
php/tests/proto/test_empty_php_namespace.proto \
php/tests/proto/test_import_descriptor_proto.proto \
php/tests/proto/test_include.proto \

View File

@ -37,12 +37,27 @@ const int kReservedNamesSize = 3;
static void descriptor_init_c_instance(Descriptor* intern TSRMLS_DC);
static void descriptor_free_c(Descriptor* object TSRMLS_DC);
static void field_descriptor_init_c_instance(FieldDescriptor* intern TSRMLS_DC);
static void field_descriptor_free_c(FieldDescriptor* object TSRMLS_DC);
static void enum_descriptor_init_c_instance(EnumDescriptor* intern TSRMLS_DC);
static void enum_descriptor_free_c(EnumDescriptor* object TSRMLS_DC);
static void enum_value_descriptor_init_c_instance(
EnumValueDescriptor *intern TSRMLS_DC);
static void enum_value_descriptor_free_c(EnumValueDescriptor *object TSRMLS_DC);
static void descriptor_pool_free_c(DescriptorPool* object TSRMLS_DC);
static void descriptor_pool_init_c_instance(DescriptorPool* pool TSRMLS_DC);
static void internal_descriptor_pool_free_c(
InternalDescriptorPool *object TSRMLS_DC);
static void internal_descriptor_pool_init_c_instance(
InternalDescriptorPool *pool TSRMLS_DC);
static void oneof_descriptor_free_c(Oneof* object TSRMLS_DC);
static void oneof_descriptor_init_c_instance(Oneof* pool TSRMLS_DC);
// -----------------------------------------------------------------------------
// Common Utilities
// -----------------------------------------------------------------------------
@ -169,10 +184,15 @@ void gpb_type_init(TSRMLS_D) {
// -----------------------------------------------------------------------------
static zend_function_entry descriptor_methods[] = {
PHP_ME(Descriptor, getFullName, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Descriptor, getField, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Descriptor, getFieldCount, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Descriptor, getOneofDecl, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Descriptor, getOneofDeclCount, NULL, ZEND_ACC_PUBLIC)
ZEND_FE_END
};
DEFINE_CLASS(Descriptor, descriptor, "Google\\Protobuf\\Internal\\Descriptor");
DEFINE_CLASS(Descriptor, descriptor, "Google\\Protobuf\\Descriptor");
static void descriptor_free_c(Descriptor *self TSRMLS_DC) {
if (self->layout) {
@ -203,7 +223,6 @@ static void descriptor_free_c(Descriptor *self TSRMLS_DC) {
}
static void descriptor_init_c_instance(Descriptor *desc TSRMLS_DC) {
// zend_object_std_init(&desc->std, descriptor_type TSRMLS_CC);
desc->msgdef = NULL;
desc->layout = NULL;
desc->klass = NULL;
@ -215,30 +234,207 @@ static void descriptor_init_c_instance(Descriptor *desc TSRMLS_DC) {
desc->json_serialize_handlers_preserve = NULL;
}
PHP_METHOD(Descriptor, getFullName) {
Descriptor *intern = UNBOX(Descriptor, getThis());
const char* fullname = upb_msgdef_fullname(intern->msgdef);
PHP_PROTO_RETVAL_STRINGL(fullname, strlen(fullname), 1);
}
PHP_METHOD(Descriptor, getField) {
long index;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
FAILURE) {
zend_error(E_USER_ERROR, "Expect integer for index.\n");
return;
}
Descriptor *intern = UNBOX(Descriptor, getThis());
int field_num = upb_msgdef_numfields(intern->msgdef);
if (index < 0 || index >= field_num) {
zend_error(E_USER_ERROR, "Cannot get element at %ld.\n", index);
return;
}
upb_msg_field_iter iter;
int i;
for(upb_msg_field_begin(&iter, intern->msgdef), i = 0;
!upb_msg_field_done(&iter) && i < index;
upb_msg_field_next(&iter), i++);
const upb_fielddef *field = upb_msg_iter_field(&iter);
PHP_PROTO_HASHTABLE_VALUE field_hashtable_value = get_def_obj(field);
if (field_hashtable_value == NULL) {
#if PHP_MAJOR_VERSION < 7
MAKE_STD_ZVAL(field_hashtable_value);
ZVAL_OBJ(field_hashtable_value, field_descriptor_type->create_object(
field_descriptor_type TSRMLS_CC));
#else
field_hashtable_value =
field_descriptor_type->create_object(field_descriptor_type TSRMLS_CC);
#endif
FieldDescriptor *field_php =
UNBOX_HASHTABLE_VALUE(FieldDescriptor, field_hashtable_value);
field_php->fielddef = field;
add_def_obj(field, field_hashtable_value);
}
#if PHP_MAJOR_VERSION < 7
RETURN_ZVAL(field_hashtable_value, 1, 0);
#else
++GC_REFCOUNT(field_hashtable_value);
RETURN_OBJ(field_hashtable_value);
#endif
}
PHP_METHOD(Descriptor, getFieldCount) {
Descriptor *intern = UNBOX(Descriptor, getThis());
RETURN_LONG(upb_msgdef_numfields(intern->msgdef));
}
PHP_METHOD(Descriptor, getOneofDecl) {
long index;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
FAILURE) {
zend_error(E_USER_ERROR, "Expect integer for index.\n");
return;
}
Descriptor *intern = UNBOX(Descriptor, getThis());
int field_num = upb_msgdef_numoneofs(intern->msgdef);
if (index < 0 || index >= field_num) {
zend_error(E_USER_ERROR, "Cannot get element at %ld.\n", index);
return;
}
upb_msg_oneof_iter iter;
int i;
for(upb_msg_oneof_begin(&iter, intern->msgdef), i = 0;
!upb_msg_oneof_done(&iter) && i < index;
upb_msg_oneof_next(&iter), i++);
upb_oneofdef *oneof = upb_msg_iter_oneof(&iter);
ZVAL_OBJ(return_value, oneof_descriptor_type->create_object(
oneof_descriptor_type TSRMLS_CC));
Oneof *oneof_php = UNBOX(Oneof, return_value);
oneof_php->oneofdef = oneof;
}
PHP_METHOD(Descriptor, getOneofDeclCount) {
Descriptor *intern = UNBOX(Descriptor, getThis());
RETURN_LONG(upb_msgdef_numoneofs(intern->msgdef));
}
// -----------------------------------------------------------------------------
// EnumDescriptor
// -----------------------------------------------------------------------------
static zend_function_entry enum_descriptor_methods[] = {
PHP_ME(EnumDescriptor, getValue, NULL, ZEND_ACC_PUBLIC)
PHP_ME(EnumDescriptor, getValueCount, NULL, ZEND_ACC_PUBLIC)
ZEND_FE_END
};
DEFINE_CLASS(EnumDescriptor, enum_descriptor,
"Google\\Protobuf\\Internal\\EnumDescriptor");
"Google\\Protobuf\\EnumDescriptor");
static void enum_descriptor_free_c(EnumDescriptor *self TSRMLS_DC) {
}
static void enum_descriptor_init_c_instance(EnumDescriptor *self TSRMLS_DC) {
// zend_object_std_init(&self->std, enum_descriptor_type TSRMLS_CC);
self->enumdef = NULL;
self->klass = NULL;
}
PHP_METHOD(EnumDescriptor, getValue) {
long index;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
FAILURE) {
zend_error(E_USER_ERROR, "Expect integer for index.\n");
return;
}
EnumDescriptor *intern = UNBOX(EnumDescriptor, getThis());
int field_num = upb_enumdef_numvals(intern->enumdef);
if (index < 0 || index >= field_num) {
zend_error(E_USER_ERROR, "Cannot get element at %ld.\n", index);
return;
}
upb_enum_iter iter;
int i;
for(upb_enum_begin(&iter, intern->enumdef), i = 0;
!upb_enum_done(&iter) && i < index;
upb_enum_next(&iter), i++);
ZVAL_OBJ(return_value, enum_value_descriptor_type->create_object(
enum_value_descriptor_type TSRMLS_CC));
EnumValueDescriptor *enum_value_php =
UNBOX(EnumValueDescriptor, return_value);
enum_value_php->name = upb_enum_iter_name(&iter);
enum_value_php->number = upb_enum_iter_number(&iter);
}
PHP_METHOD(EnumDescriptor, getValueCount) {
EnumDescriptor *intern = UNBOX(EnumDescriptor, getThis());
RETURN_LONG(upb_enumdef_numvals(intern->enumdef));
}
// -----------------------------------------------------------------------------
// EnumValueDescriptor
// -----------------------------------------------------------------------------
static zend_function_entry enum_value_descriptor_methods[] = {
PHP_ME(EnumValueDescriptor, getName, NULL, ZEND_ACC_PUBLIC)
PHP_ME(EnumValueDescriptor, getNumber, NULL, ZEND_ACC_PUBLIC)
ZEND_FE_END
};
DEFINE_CLASS(EnumValueDescriptor, enum_value_descriptor,
"Google\\Protobuf\\EnumValueDescriptor");
static void enum_value_descriptor_free_c(EnumValueDescriptor *self TSRMLS_DC) {
}
static void enum_value_descriptor_init_c_instance(EnumValueDescriptor *self TSRMLS_DC) {
self->name = NULL;
self->number = 0;
}
PHP_METHOD(EnumValueDescriptor, getName) {
EnumValueDescriptor *intern = UNBOX(EnumValueDescriptor, getThis());
PHP_PROTO_RETVAL_STRINGL(intern->name, strlen(intern->name), 1);
}
PHP_METHOD(EnumValueDescriptor, getNumber) {
EnumValueDescriptor *intern = UNBOX(EnumValueDescriptor, getThis());
RETURN_LONG(intern->number);
}
// -----------------------------------------------------------------------------
// FieldDescriptor
// -----------------------------------------------------------------------------
static zend_function_entry field_descriptor_methods[] = {
PHP_ME(FieldDescriptor, getName, NULL, ZEND_ACC_PUBLIC)
PHP_ME(FieldDescriptor, getNumber, NULL, ZEND_ACC_PUBLIC)
PHP_ME(FieldDescriptor, getLabel, NULL, ZEND_ACC_PUBLIC)
PHP_ME(FieldDescriptor, getType, NULL, ZEND_ACC_PUBLIC)
PHP_ME(FieldDescriptor, isMap, NULL, ZEND_ACC_PUBLIC)
PHP_ME(FieldDescriptor, getEnumType, NULL, ZEND_ACC_PUBLIC)
PHP_ME(FieldDescriptor, getMessageType, NULL, ZEND_ACC_PUBLIC)
ZEND_FE_END
};
DEFINE_CLASS(FieldDescriptor, field_descriptor,
"Google\\Protobuf\\FieldDescriptor");
static void field_descriptor_free_c(FieldDescriptor *self TSRMLS_DC) {
}
static void field_descriptor_init_c_instance(FieldDescriptor *self TSRMLS_DC) {
self->fielddef = NULL;
}
upb_fieldtype_t to_fieldtype(upb_descriptortype_t type) {
switch (type) {
#define CASE(descriptor_type, type) \
@ -272,6 +468,150 @@ upb_fieldtype_t to_fieldtype(upb_descriptortype_t type) {
return 0;
}
PHP_METHOD(FieldDescriptor, getName) {
FieldDescriptor *intern = UNBOX(FieldDescriptor, getThis());
const char* name = upb_fielddef_name(intern->fielddef);
PHP_PROTO_RETVAL_STRINGL(name, strlen(name), 1);
}
PHP_METHOD(FieldDescriptor, getNumber) {
FieldDescriptor *intern = UNBOX(FieldDescriptor, getThis());
RETURN_LONG(upb_fielddef_number(intern->fielddef));
}
PHP_METHOD(FieldDescriptor, getLabel) {
FieldDescriptor *intern = UNBOX(FieldDescriptor, getThis());
RETURN_LONG(upb_fielddef_label(intern->fielddef));
}
PHP_METHOD(FieldDescriptor, getType) {
FieldDescriptor *intern = UNBOX(FieldDescriptor, getThis());
RETURN_LONG(upb_fielddef_descriptortype(intern->fielddef));
}
PHP_METHOD(FieldDescriptor, isMap) {
FieldDescriptor *intern = UNBOX(FieldDescriptor, getThis());
RETURN_BOOL(upb_fielddef_ismap(intern->fielddef));
}
PHP_METHOD(FieldDescriptor, getEnumType) {
FieldDescriptor *intern = UNBOX(FieldDescriptor, getThis());
const upb_enumdef *enumdef = upb_fielddef_enumsubdef(intern->fielddef);
if (enumdef == NULL) {
char error_msg[100];
sprintf(error_msg, "Cannot get enum type for non-enum field '%s'",
upb_fielddef_name(intern->fielddef));
zend_throw_exception(NULL, error_msg, 0 TSRMLS_CC);
return;
}
PHP_PROTO_HASHTABLE_VALUE desc = get_def_obj(enumdef);
#if PHP_MAJOR_VERSION < 7
RETURN_ZVAL(desc, 1, 0);
#else
++GC_REFCOUNT(desc);
RETURN_OBJ(desc);
#endif
}
PHP_METHOD(FieldDescriptor, getMessageType) {
FieldDescriptor *intern = UNBOX(FieldDescriptor, getThis());
const upb_msgdef *msgdef = upb_fielddef_msgsubdef(intern->fielddef);
if (msgdef == NULL) {
char error_msg[100];
sprintf(error_msg, "Cannot get message type for non-message field '%s'",
upb_fielddef_name(intern->fielddef));
zend_throw_exception(NULL, error_msg, 0 TSRMLS_CC);
return;
}
PHP_PROTO_HASHTABLE_VALUE desc = get_def_obj(msgdef);
#if PHP_MAJOR_VERSION < 7
RETURN_ZVAL(desc, 1, 0);
#else
++GC_REFCOUNT(desc);
RETURN_OBJ(desc);
#endif
}
// -----------------------------------------------------------------------------
// Oneof
// -----------------------------------------------------------------------------
static zend_function_entry oneof_descriptor_methods[] = {
PHP_ME(Oneof, getName, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Oneof, getField, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Oneof, getFieldCount, NULL, ZEND_ACC_PUBLIC)
ZEND_FE_END
};
DEFINE_CLASS(Oneof, oneof_descriptor,
"Google\\Protobuf\\OneofDescriptor");
static void oneof_descriptor_free_c(Oneof *self TSRMLS_DC) {
}
static void oneof_descriptor_init_c_instance(Oneof *self TSRMLS_DC) {
self->oneofdef = NULL;
}
PHP_METHOD(Oneof, getName) {
Oneof *intern = UNBOX(Oneof, getThis());
const char *name = upb_oneofdef_name(intern->oneofdef);
PHP_PROTO_RETVAL_STRINGL(name, strlen(name), 1);
}
PHP_METHOD(Oneof, getField) {
long index;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
FAILURE) {
zend_error(E_USER_ERROR, "Expect integer for index.\n");
return;
}
Oneof *intern = UNBOX(Oneof, getThis());
int field_num = upb_oneofdef_numfields(intern->oneofdef);
if (index < 0 || index >= field_num) {
zend_error(E_USER_ERROR, "Cannot get element at %ld.\n", index);
return;
}
upb_oneof_iter iter;
int i;
for(upb_oneof_begin(&iter, intern->oneofdef), i = 0;
!upb_oneof_done(&iter) && i < index;
upb_oneof_next(&iter), i++);
const upb_fielddef *field = upb_oneof_iter_field(&iter);
PHP_PROTO_HASHTABLE_VALUE field_hashtable_value = get_def_obj(field);
if (field_hashtable_value == NULL) {
#if PHP_MAJOR_VERSION < 7
MAKE_STD_ZVAL(field_hashtable_value);
ZVAL_OBJ(field_hashtable_value, field_descriptor_type->create_object(
field_descriptor_type TSRMLS_CC));
#else
field_hashtable_value =
field_descriptor_type->create_object(field_descriptor_type TSRMLS_CC);
#endif
FieldDescriptor *field_php =
UNBOX_HASHTABLE_VALUE(FieldDescriptor, field_hashtable_value);
field_php->fielddef = field;
add_def_obj(field, field_hashtable_value);
}
#if PHP_MAJOR_VERSION < 7
RETURN_ZVAL(field_hashtable_value, 1, 0);
#else
++GC_REFCOUNT(field_hashtable_value);
RETURN_OBJ(field_hashtable_value);
#endif
}
PHP_METHOD(Oneof, getFieldCount) {
Oneof *intern = UNBOX(Oneof, getThis());
RETURN_LONG(upb_oneofdef_numfields(intern->oneofdef));
}
// -----------------------------------------------------------------------------
// DescriptorPool
// -----------------------------------------------------------------------------
@ -279,52 +619,79 @@ upb_fieldtype_t to_fieldtype(upb_descriptortype_t type) {
static zend_function_entry descriptor_pool_methods[] = {
PHP_ME(DescriptorPool, getGeneratedPool, NULL,
ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
PHP_ME(DescriptorPool, internalAddGeneratedFile, NULL, ZEND_ACC_PUBLIC)
PHP_ME(DescriptorPool, getDescriptorByClassName, NULL, ZEND_ACC_PUBLIC)
PHP_ME(DescriptorPool, getEnumDescriptorByClassName, NULL, ZEND_ACC_PUBLIC)
ZEND_FE_END
};
static zend_function_entry internal_descriptor_pool_methods[] = {
PHP_ME(InternalDescriptorPool, getGeneratedPool, NULL,
ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
PHP_ME(InternalDescriptorPool, internalAddGeneratedFile, NULL, ZEND_ACC_PUBLIC)
ZEND_FE_END
};
DEFINE_CLASS(DescriptorPool, descriptor_pool,
"Google\\Protobuf\\DescriptorPool");
DEFINE_CLASS(InternalDescriptorPool, internal_descriptor_pool,
"Google\\Protobuf\\Internal\\DescriptorPool");
// wrapper of generated pool
#if PHP_MAJOR_VERSION < 7
zval* generated_pool_php;
zval* internal_generated_pool_php;
#else
zend_object *generated_pool_php;
zend_object *internal_generated_pool_php;
#endif
DescriptorPool *generated_pool; // The actual generated pool
InternalDescriptorPool *generated_pool; // The actual generated pool
static void init_generated_pool_once(TSRMLS_D) {
if (generated_pool_php == NULL) {
if (generated_pool == NULL) {
#if PHP_MAJOR_VERSION < 7
MAKE_STD_ZVAL(generated_pool_php);
MAKE_STD_ZVAL(internal_generated_pool_php);
ZVAL_OBJ(internal_generated_pool_php,
internal_descriptor_pool_type->create_object(
internal_descriptor_pool_type TSRMLS_CC));
generated_pool = UNBOX(InternalDescriptorPool, internal_generated_pool_php);
ZVAL_OBJ(generated_pool_php, descriptor_pool_type->create_object(
descriptor_pool_type TSRMLS_CC));
generated_pool = UNBOX(DescriptorPool, generated_pool_php);
#else
internal_generated_pool_php = internal_descriptor_pool_type->create_object(
internal_descriptor_pool_type TSRMLS_CC);
generated_pool = (InternalDescriptorPool *)((char *)internal_generated_pool_php -
XtOffsetOf(InternalDescriptorPool, std));
generated_pool_php =
descriptor_pool_type->create_object(descriptor_pool_type TSRMLS_CC);
generated_pool = (DescriptorPool *)((char *)generated_pool_php -
XtOffsetOf(DescriptorPool, std));
#endif
}
}
static void descriptor_pool_init_c_instance(DescriptorPool *pool TSRMLS_DC) {
// zend_object_std_init(&pool->std, descriptor_pool_type TSRMLS_CC);
static void internal_descriptor_pool_init_c_instance(
InternalDescriptorPool *pool TSRMLS_DC) {
pool->symtab = upb_symtab_new();
ALLOC_HASHTABLE(pool->pending_list);
zend_hash_init(pool->pending_list, 1, NULL, ZVAL_PTR_DTOR, 0);
}
static void descriptor_pool_free_c(DescriptorPool *pool TSRMLS_DC) {
static void internal_descriptor_pool_free_c(
InternalDescriptorPool *pool TSRMLS_DC) {
upb_symtab_free(pool->symtab);
zend_hash_destroy(pool->pending_list);
FREE_HASHTABLE(pool->pending_list);
}
static void descriptor_pool_init_c_instance(DescriptorPool *pool TSRMLS_DC) {
assert(generated_pool != NULL);
pool->intern = generated_pool;
}
static void descriptor_pool_free_c(DescriptorPool *pool TSRMLS_DC) {
}
static void validate_enumdef(const upb_enumdef *enumdef) {
// Verify that an entry exists with integer value 0. (This is the default
// value.)
@ -358,6 +725,16 @@ PHP_METHOD(DescriptorPool, getGeneratedPool) {
#endif
}
PHP_METHOD(InternalDescriptorPool, getGeneratedPool) {
init_generated_pool_once(TSRMLS_C);
#if PHP_MAJOR_VERSION < 7
RETURN_ZVAL(internal_generated_pool_php, 1, 0);
#else
++GC_REFCOUNT(internal_generated_pool_php);
RETURN_OBJ(internal_generated_pool_php);
#endif
}
static void classname_no_prefix(const char *fullname, const char *package_name,
char *class_name) {
size_t i = 0, j;
@ -455,7 +832,7 @@ static void convert_to_class_name_inplace(const char *package,
memcpy(classname + i, prefix, prefix_len);
}
PHP_METHOD(DescriptorPool, internalAddGeneratedFile) {
PHP_METHOD(InternalDescriptorPool, internalAddGeneratedFile) {
char *data = NULL;
PHP_PROTO_SIZE data_len;
upb_filedef **files;
@ -466,7 +843,7 @@ PHP_METHOD(DescriptorPool, internalAddGeneratedFile) {
return;
}
DescriptorPool *pool = UNBOX(DescriptorPool, getThis());
InternalDescriptorPool *pool = UNBOX(InternalDescriptorPool, getThis());
CHECK_UPB(files = upb_loaddescriptor(data, data_len, &pool, &status),
"Parse binary descriptors to internal descriptors failed");
@ -550,3 +927,77 @@ PHP_METHOD(DescriptorPool, internalAddGeneratedFile) {
upb_filedef_unref(files[0], &pool);
upb_gfree(files);
}
PHP_METHOD(DescriptorPool, getDescriptorByClassName) {
DescriptorPool *public_pool = UNBOX(DescriptorPool, getThis());
InternalDescriptorPool *pool = public_pool->intern;
char *classname = NULL;
PHP_PROTO_SIZE classname_len;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &classname,
&classname_len) == FAILURE) {
return;
}
PHP_PROTO_CE_DECLARE pce;
if (php_proto_zend_lookup_class(classname, classname_len, &pce) ==
FAILURE) {
RETURN_NULL();
}
PHP_PROTO_HASHTABLE_VALUE desc = get_ce_obj(PHP_PROTO_CE_UNREF(pce));
if (desc == NULL) {
RETURN_NULL();
}
zend_class_entry* instance_ce = HASHTABLE_VALUE_CE(desc);
if (!instanceof_function(instance_ce, descriptor_type TSRMLS_CC)) {
RETURN_NULL();
}
#if PHP_MAJOR_VERSION < 7
RETURN_ZVAL(desc, 1, 0);
#else
++GC_REFCOUNT(desc);
RETURN_OBJ(desc);
#endif
}
PHP_METHOD(DescriptorPool, getEnumDescriptorByClassName) {
DescriptorPool *public_pool = UNBOX(DescriptorPool, getThis());
InternalDescriptorPool *pool = public_pool->intern;
char *classname = NULL;
PHP_PROTO_SIZE classname_len;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &classname,
&classname_len) == FAILURE) {
return;
}
PHP_PROTO_CE_DECLARE pce;
if (php_proto_zend_lookup_class(classname, classname_len, &pce) ==
FAILURE) {
RETURN_NULL();
}
PHP_PROTO_HASHTABLE_VALUE desc = get_ce_obj(PHP_PROTO_CE_UNREF(pce));
if (desc == NULL) {
RETURN_NULL();
}
zend_class_entry* instance_ce = HASHTABLE_VALUE_CE(desc);
if (!instanceof_function(instance_ce, enum_descriptor_type TSRMLS_CC)) {
RETURN_NULL();
}
#if PHP_MAJOR_VERSION < 7
RETURN_ZVAL(desc, 1, 0);
#else
++GC_REFCOUNT(desc);
RETURN_OBJ(desc);
#endif
}

View File

@ -63,7 +63,6 @@ static void* get_from_table(const HashTable* t, const void* def) {
void** value;
if (php_proto_zend_hash_index_find_mem(t, (zend_ulong)def, (void**)&value) ==
FAILURE) {
zend_error(E_ERROR, "PHP object not found for given definition.\n");
return NULL;
}
return *value;
@ -166,6 +165,7 @@ static PHP_RINIT_FUNCTION(protobuf) {
generated_pool = NULL;
generated_pool_php = NULL;
internal_generated_pool_php = NULL;
return 0;
}
@ -182,21 +182,40 @@ static PHP_RSHUTDOWN_FUNCTION(protobuf) {
zval_dtor(generated_pool_php);
FREE_ZVAL(generated_pool_php);
}
if (internal_generated_pool_php != NULL) {
zval_dtor(internal_generated_pool_php);
FREE_ZVAL(internal_generated_pool_php);
}
#else
if (generated_pool_php != NULL) {
zval tmp;
ZVAL_OBJ(&tmp, generated_pool_php);
zval_dtor(&tmp);
}
if (internal_generated_pool_php != NULL) {
zval tmp;
ZVAL_OBJ(&tmp, internal_generated_pool_php);
zval_dtor(&tmp);
}
#endif
return 0;
}
static PHP_MINIT_FUNCTION(protobuf) {
map_field_init(TSRMLS_C);
map_field_iter_init(TSRMLS_C);
repeated_field_init(TSRMLS_C);
repeated_field_iter_init(TSRMLS_C);
gpb_type_init(TSRMLS_C);
message_init(TSRMLS_C);
descriptor_pool_init(TSRMLS_C);
descriptor_init(TSRMLS_C);
enum_descriptor_init(TSRMLS_C);
enum_value_descriptor_init(TSRMLS_C);
field_descriptor_init(TSRMLS_C);
gpb_type_init(TSRMLS_C);
internal_descriptor_pool_init(TSRMLS_C);
map_field_init(TSRMLS_C);
map_field_iter_init(TSRMLS_C);
message_init(TSRMLS_C);
oneof_descriptor_init(TSRMLS_C);
repeated_field_init(TSRMLS_C);
repeated_field_iter_init(TSRMLS_C);
util_init(TSRMLS_C);
return 0;

View File

@ -185,6 +185,7 @@
#define HASHTABLE_VALUE_DTOR ZVAL_PTR_DTOR
#define PHP_PROTO_HASHTABLE_VALUE zval*
#define HASHTABLE_VALUE_CE(val) Z_OBJCE_P(val)
#define CREATE_HASHTABLE_VALUE(OBJ, WRAPPED_OBJ, OBJ_TYPE, OBJ_CLASS_ENTRY) \
OBJ_TYPE* OBJ; \
@ -369,6 +370,7 @@ static inline int php_proto_zend_hash_get_current_data_ex(HashTable* ht,
#define HASHTABLE_VALUE_DTOR php_proto_hashtable_descriptor_release
#define PHP_PROTO_HASHTABLE_VALUE zend_object*
#define HASHTABLE_VALUE_CE(val) val->ce
#define CREATE_HASHTABLE_VALUE(OBJ, WRAPPED_OBJ, OBJ_TYPE, OBJ_CLASS_ENTRY) \
OBJ_TYPE* OBJ; \
@ -397,7 +399,9 @@ static inline int php_proto_zend_lookup_class(
struct DescriptorPool;
struct Descriptor;
struct EnumDescriptor;
struct EnumValueDescriptor;
struct FieldDescriptor;
struct InternalDescriptorPool;
struct MessageField;
struct MessageHeader;
struct MessageLayout;
@ -410,7 +414,9 @@ struct Oneof;
typedef struct DescriptorPool DescriptorPool;
typedef struct Descriptor Descriptor;
typedef struct EnumDescriptor EnumDescriptor;
typedef struct EnumValueDescriptor EnumValueDescriptor;
typedef struct FieldDescriptor FieldDescriptor;
typedef struct InternalDescriptorPool InternalDescriptorPool;
typedef struct MessageField MessageField;
typedef struct MessageHeader MessageHeader;
typedef struct MessageLayout MessageLayout;
@ -431,9 +437,12 @@ ZEND_END_MODULE_GLOBALS(protobuf)
void descriptor_init(TSRMLS_D);
void enum_descriptor_init(TSRMLS_D);
void descriptor_pool_init(TSRMLS_D);
void internal_descriptor_pool_init(TSRMLS_D);
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 oneof_descriptor_init(TSRMLS_D);
void repeated_field_init(TSRMLS_D);
void repeated_field_iter_init(TSRMLS_D);
void util_init(TSRMLS_D);
@ -458,22 +467,34 @@ extern zend_class_entry* repeated_field_type;
// -----------------------------------------------------------------------------
PHP_PROTO_WRAP_OBJECT_START(DescriptorPool)
InternalDescriptorPool* intern;
PHP_PROTO_WRAP_OBJECT_END
PHP_METHOD(DescriptorPool, getGeneratedPool);
PHP_METHOD(DescriptorPool, getDescriptorByClassName);
PHP_METHOD(DescriptorPool, getEnumDescriptorByClassName);
PHP_PROTO_WRAP_OBJECT_START(InternalDescriptorPool)
upb_symtab* symtab;
HashTable* pending_list;
PHP_PROTO_WRAP_OBJECT_END
PHP_METHOD(DescriptorPool, getGeneratedPool);
PHP_METHOD(DescriptorPool, internalAddGeneratedFile);
PHP_METHOD(InternalDescriptorPool, getGeneratedPool);
PHP_METHOD(InternalDescriptorPool, internalAddGeneratedFile);
// wrapper of generated pool
#if PHP_MAJOR_VERSION < 7
extern zval* generated_pool_php;
extern zval* internal_generated_pool_php;
void descriptor_pool_free(void* object TSRMLS_DC);
void internal_descriptor_pool_free(void* object TSRMLS_DC);
#else
extern zend_object *generated_pool_php;
extern zend_object *internal_generated_pool_php;
void descriptor_pool_free(zend_object* object);
void internal_descriptor_pool_free(zend_object* object);
#endif
extern DescriptorPool* generated_pool; // The actual generated pool
extern InternalDescriptorPool* generated_pool; // The actual generated pool
PHP_PROTO_WRAP_OBJECT_START(Descriptor)
const upb_msgdef* msgdef;
@ -487,6 +508,12 @@ PHP_PROTO_WRAP_OBJECT_START(Descriptor)
const upb_handlers* json_serialize_handlers_preserve;
PHP_PROTO_WRAP_OBJECT_END
PHP_METHOD(Descriptor, getFullName);
PHP_METHOD(Descriptor, getField);
PHP_METHOD(Descriptor, getFieldCount);
PHP_METHOD(Descriptor, getOneofDecl);
PHP_METHOD(Descriptor, getOneofDeclCount);
extern zend_class_entry* descriptor_type;
void descriptor_name_set(Descriptor *desc, const char *name);
@ -495,14 +522,36 @@ PHP_PROTO_WRAP_OBJECT_START(FieldDescriptor)
const upb_fielddef* fielddef;
PHP_PROTO_WRAP_OBJECT_END
PHP_METHOD(FieldDescriptor, getName);
PHP_METHOD(FieldDescriptor, getNumber);
PHP_METHOD(FieldDescriptor, getLabel);
PHP_METHOD(FieldDescriptor, getType);
PHP_METHOD(FieldDescriptor, isMap);
PHP_METHOD(FieldDescriptor, getEnumType);
PHP_METHOD(FieldDescriptor, getMessageType);
extern zend_class_entry* field_descriptor_type;
PHP_PROTO_WRAP_OBJECT_START(EnumDescriptor)
const upb_enumdef* enumdef;
zend_class_entry* klass; // begins as NULL
// VALUE module; // begins as nil
PHP_PROTO_WRAP_OBJECT_END
PHP_METHOD(EnumDescriptor, getValue);
PHP_METHOD(EnumDescriptor, getValueCount);
extern zend_class_entry* enum_descriptor_type;
PHP_PROTO_WRAP_OBJECT_START(EnumValueDescriptor)
const char* name;
int32_t number;
PHP_PROTO_WRAP_OBJECT_END
PHP_METHOD(EnumValueDescriptor, getName);
PHP_METHOD(EnumValueDescriptor, getNumber);
extern zend_class_entry* enum_value_descriptor_type;
// -----------------------------------------------------------------------------
// Message class creation.
// -----------------------------------------------------------------------------
@ -819,6 +868,12 @@ PHP_PROTO_WRAP_OBJECT_START(Oneof)
char value[NATIVE_SLOT_MAX_SIZE];
PHP_PROTO_WRAP_OBJECT_END
PHP_METHOD(Oneof, getName);
PHP_METHOD(Oneof, getField);
PHP_METHOD(Oneof, getFieldCount);
extern zend_class_entry* oneof_descriptor_type;
// Oneof case slot value to indicate that no oneof case is set. The value `0` is
// safe because field numbers are used as case identifiers, and no field can
// have a number of 0.

View File

@ -0,0 +1,243 @@
<?php
require_once('generated/Descriptors/TestDescriptorsEnum.php');
require_once('generated/Descriptors/TestDescriptorsMessage.php');
require_once('test_base.php');
require_once('test_util.php');
use Google\Protobuf\DescriptorPool;
use Google\Protobuf\Internal\RepeatedField;
use Google\Protobuf\Internal\MapField;
use Descriptors\TestDescriptorsEnum;
use Descriptors\TestDescriptorsMessage;
use Descriptors\TestDescriptorsMessage_Sub;
class DescriptorsTest extends TestBase
{
// Redefine these here for compatibility with c extension
const GPBLABEL_OPTIONAL = 1;
const GPBLABEL_REQUIRED = 2;
const GPBLABEL_REPEATED = 3;
const GPBTYPE_DOUBLE = 1;
const GPBTYPE_FLOAT = 2;
const GPBTYPE_INT64 = 3;
const GPBTYPE_UINT64 = 4;
const GPBTYPE_INT32 = 5;
const GPBTYPE_FIXED64 = 6;
const GPBTYPE_FIXED32 = 7;
const GPBTYPE_BOOL = 8;
const GPBTYPE_STRING = 9;
const GPBTYPE_GROUP = 10;
const GPBTYPE_MESSAGE = 11;
const GPBTYPE_BYTES = 12;
const GPBTYPE_UINT32 = 13;
const GPBTYPE_ENUM = 14;
const GPBTYPE_SFIXED32 = 15;
const GPBTYPE_SFIXED64 = 16;
const GPBTYPE_SINT32 = 17;
const GPBTYPE_SINT64 = 18;
#########################################################
# Test descriptor pool.
#########################################################
public function testDescriptorPool()
{
$pool = DescriptorPool::getGeneratedPool();
$desc = $pool->getDescriptorByClassName(get_class(new TestDescriptorsMessage()));
$this->assertInstanceOf('\Google\Protobuf\Descriptor', $desc);
$enumDesc = $pool->getEnumDescriptorByClassName(get_class(new TestDescriptorsEnum()));
$this->assertInstanceOf('\Google\Protobuf\EnumDescriptor', $enumDesc);
}
public function testDescriptorPoolIncorrectArgs()
{
$pool = DescriptorPool::getGeneratedPool();
$desc = $pool->getDescriptorByClassName('NotAClass');
$this->assertNull($desc);
$desc = $pool->getDescriptorByClassName(get_class(new TestDescriptorsEnum()));
$this->assertNull($desc);
$enumDesc = $pool->getEnumDescriptorByClassName(get_class(new TestDescriptorsMessage()));
$this->assertNull($enumDesc);
}
#########################################################
# Test descriptor.
#########################################################
public function testDescriptor()
{
$pool = DescriptorPool::getGeneratedPool();
$desc = $pool->getDescriptorByClassName(get_class(new TestDescriptorsMessage()));
$this->assertSame('descriptors.TestDescriptorsMessage', $desc->getFullName());
$this->assertInstanceOf('\Google\Protobuf\FieldDescriptor', $desc->getField(0));
$this->assertSame(7, $desc->getFieldCount());
$this->assertInstanceOf('\Google\Protobuf\OneofDescriptor', $desc->getOneofDecl(0));
$this->assertSame(1, $desc->getOneofDeclCount());
}
#########################################################
# Test enum descriptor.
#########################################################
public function testEnumDescriptor()
{
// WARNINIG - we need to do this so that TestDescriptorsEnum is registered!!?
new TestDescriptorsMessage();
$pool = DescriptorPool::getGeneratedPool();
$enumDesc = $pool->getEnumDescriptorByClassName(get_class(new TestDescriptorsEnum()));
// Build map of enum values
$enumDescMap = [];
for ($i = 0; $i < $enumDesc->getValueCount(); $i++) {
$enumValueDesc = $enumDesc->getValue($i);
$this->assertInstanceOf('\Google\Protobuf\EnumValueDescriptor', $enumValueDesc);
$enumDescMap[$enumValueDesc->getNumber()] = $enumValueDesc->getName();
}
$this->assertSame('ZERO', $enumDescMap[0]);
$this->assertSame('ONE', $enumDescMap[1]);
$this->assertSame(2, $enumDesc->getValueCount());
}
#########################################################
# Test field descriptor.
#########################################################
public function testFieldDescriptor()
{
$pool = DescriptorPool::getGeneratedPool();
$desc = $pool->getDescriptorByClassName(get_class(new TestDescriptorsMessage()));
$fieldDescMap = $this->buildFieldMap($desc);
// Optional int field
$fieldDesc = $fieldDescMap[1];
$this->assertSame('optional_int32', $fieldDesc->getName());
$this->assertSame(1, $fieldDesc->getNumber());
$this->assertSame(self::GPBLABEL_OPTIONAL, $fieldDesc->getLabel());
$this->assertSame(self::GPBTYPE_INT32, $fieldDesc->getType());
$this->assertFalse($fieldDesc->isMap());
// Optional enum field
$fieldDesc = $fieldDescMap[16];
$this->assertSame('optional_enum', $fieldDesc->getName());
$this->assertSame(16, $fieldDesc->getNumber());
$this->assertSame(self::GPBLABEL_OPTIONAL, $fieldDesc->getLabel());
$this->assertSame(self::GPBTYPE_ENUM, $fieldDesc->getType());
$this->assertInstanceOf('\Google\Protobuf\EnumDescriptor', $fieldDesc->getEnumType());
$this->assertFalse($fieldDesc->isMap());
// Optional message field
$fieldDesc = $fieldDescMap[17];
$this->assertSame('optional_message', $fieldDesc->getName());
$this->assertSame(17, $fieldDesc->getNumber());
$this->assertSame(self::GPBLABEL_OPTIONAL, $fieldDesc->getLabel());
$this->assertSame(self::GPBTYPE_MESSAGE, $fieldDesc->getType());
$this->assertInstanceOf('\Google\Protobuf\Descriptor', $fieldDesc->getMessageType());
$this->assertFalse($fieldDesc->isMap());
// Repeated int field
$fieldDesc = $fieldDescMap[31];
$this->assertSame('repeated_int32', $fieldDesc->getName());
$this->assertSame(31, $fieldDesc->getNumber());
$this->assertSame(self::GPBLABEL_REPEATED, $fieldDesc->getLabel());
$this->assertSame(self::GPBTYPE_INT32, $fieldDesc->getType());
$this->assertFalse($fieldDesc->isMap());
// Repeated message field
$fieldDesc = $fieldDescMap[47];
$this->assertSame('repeated_message', $fieldDesc->getName());
$this->assertSame(47, $fieldDesc->getNumber());
$this->assertSame(self::GPBLABEL_REPEATED, $fieldDesc->getLabel());
$this->assertSame(self::GPBTYPE_MESSAGE, $fieldDesc->getType());
$this->assertInstanceOf('\Google\Protobuf\Descriptor', $fieldDesc->getMessageType());
$this->assertFalse($fieldDesc->isMap());
// Oneof int field
// Tested further in testOneofDescriptor()
$fieldDesc = $fieldDescMap[51];
$this->assertSame('oneof_int32', $fieldDesc->getName());
$this->assertSame(51, $fieldDesc->getNumber());
$this->assertSame(self::GPBLABEL_OPTIONAL, $fieldDesc->getLabel());
$this->assertSame(self::GPBTYPE_INT32, $fieldDesc->getType());
$this->assertFalse($fieldDesc->isMap());
// Map int-enum field
$fieldDesc = $fieldDescMap[71];
$this->assertSame('map_int32_enum', $fieldDesc->getName());
$this->assertSame(71, $fieldDesc->getNumber());
$this->assertSame(self::GPBLABEL_REPEATED, $fieldDesc->getLabel());
$this->assertSame(self::GPBTYPE_MESSAGE, $fieldDesc->getType());
$this->assertTrue($fieldDesc->isMap());
$mapDesc = $fieldDesc->getMessageType();
$this->assertSame('descriptors.TestDescriptorsMessage.MapInt32EnumEntry', $mapDesc->getFullName());
$this->assertSame(self::GPBTYPE_INT32, $mapDesc->getField(0)->getType());
$this->assertSame(self::GPBTYPE_ENUM, $mapDesc->getField(1)->getType());
}
/**
* @expectedException \Exception
*/
public function testFieldDescriptorEnumException()
{
$pool = DescriptorPool::getGeneratedPool();
$desc = $pool->getDescriptorByClassName(get_class(new TestDescriptorsMessage()));
$fieldDesc = $desc->getField(0);
$fieldDesc->getEnumType();
}
/**
* @expectedException \Exception
*/
public function testFieldDescriptorMessageException()
{
$pool = DescriptorPool::getGeneratedPool();
$desc = $pool->getDescriptorByClassName(get_class(new TestDescriptorsMessage()));
$fieldDesc = $desc->getField(0);
$fieldDesc->getMessageType();
}
#########################################################
# Test oneof descriptor.
#########################################################
public function testOneofDescriptor()
{
$pool = DescriptorPool::getGeneratedPool();
$desc = $pool->getDescriptorByClassName(get_class(new TestDescriptorsMessage()));
$fieldDescMap = $this->buildFieldMap($desc);
$fieldDesc = $fieldDescMap[51];
$oneofDesc = $desc->getOneofDecl(0);
$this->assertSame('my_oneof', $oneofDesc->getName());
$fieldDescFromOneof = $oneofDesc->getField(0);
$this->assertSame($fieldDesc, $fieldDescFromOneof);
$this->assertSame(1, $oneofDesc->getFieldCount());
}
private function buildFieldMap($desc)
{
$fieldDescMap = [];
for ($i = 0; $i < $desc->getFieldCount(); $i++) {
$fieldDesc = $desc->getField($i);
$fieldDescMap[$fieldDesc->getNumber()] = $fieldDesc;
}
return $fieldDescMap;
}
}

View File

@ -0,0 +1,35 @@
syntax = "proto3";
package descriptors;
message TestDescriptorsMessage {
int32 optional_int32 = 1;
TestDescriptorsEnum optional_enum = 16;
Sub optional_message = 17;
// Repeated
repeated int32 repeated_int32 = 31;
repeated Sub repeated_message = 47;
oneof my_oneof {
int32 oneof_int32 = 51;
}
map<int32, EnumSub> map_int32_enum = 71;
message Sub {
int32 a = 1;
repeated int32 b = 2;
}
enum EnumSub {
ZERO = 0;
ONE = 1;
}
}
enum TestDescriptorsEnum {
ZERO = 0;
ONE = 1;
}

View File

@ -8,7 +8,7 @@ set -e
phpize && ./configure CFLAGS='-g -O0' && make
popd
tests=( array_test.php encode_decode_test.php generated_class_test.php generated_phpdoc_test.php map_field_test.php well_known_test.php generated_service_test.php )
tests=( array_test.php encode_decode_test.php generated_class_test.php generated_phpdoc_test.php map_field_test.php well_known_test.php generated_service_test.php descriptors_test.php )
for t in "${tests[@]}"
do

View File

@ -347,7 +347,16 @@ generate_php_test_proto() {
# Generate test file
rm -rf generated
mkdir generated
../../src/protoc --php_out=generated proto/test.proto proto/test_include.proto proto/test_no_namespace.proto proto/test_prefix.proto proto/test_php_namespace.proto proto/test_empty_php_namespace.proto proto/test_service.proto proto/test_service_namespace.proto
../../src/protoc --php_out=generated \
proto/test.proto \
proto/test_include.proto \
proto/test_no_namespace.proto \
proto/test_prefix.proto \
proto/test_php_namespace.proto \
proto/test_empty_php_namespace.proto \
proto/test_service.proto \
proto/test_service_namespace.proto \
proto/test_descriptors.proto
pushd ../../src
./protoc --php_out=../php/tests/generated google/protobuf/empty.proto
./protoc --php_out=../php/tests/generated -I../php/tests -I. ../php/tests/proto/test_import_descriptor_proto.proto