diff options
author | Paul Yang <TeBoring@users.noreply.github.com> | 2017-08-02 07:42:27 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-08-02 07:42:27 -0700 |
commit | c15a3269f9dea81db5a45b527cd8699f11b03f0b (patch) | |
tree | 9a824786160ee256544541361d71532850c097f5 | |
parent | be73938d72c91ff0075b1ed683f07922ad5801b0 (diff) |
Expose descriptor API in php c extension (#3422)
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | php/ext/google/protobuf/def.c | 481 | ||||
-rw-r--r-- | php/ext/google/protobuf/protobuf.c | 31 | ||||
-rw-r--r-- | php/ext/google/protobuf/protobuf.h | 63 | ||||
-rw-r--r-- | php/tests/descriptors_test.php | 243 | ||||
-rw-r--r-- | php/tests/proto/test_descriptors.proto | 35 | ||||
-rwxr-xr-x | php/tests/test.sh | 2 | ||||
-rwxr-xr-x | tests.sh | 11 |
8 files changed, 841 insertions, 27 deletions
diff --git a/Makefile.am b/Makefile.am index 7ffb202e..6279b2de 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 \ diff --git a/php/ext/google/protobuf/def.c b/php/ext/google/protobuf/def.c index 332616b2..ae17455c 100644 --- a/php/ext/google/protobuf/def.c +++ b/php/ext/google/protobuf/def.c @@ -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 +} diff --git a/php/ext/google/protobuf/protobuf.c b/php/ext/google/protobuf/protobuf.c index 2b5b58c6..dc730030 100644 --- a/php/ext/google/protobuf/protobuf.c +++ b/php/ext/google/protobuf/protobuf.c @@ -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) { + 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); - gpb_type_init(TSRMLS_C); - message_init(TSRMLS_C); - descriptor_pool_init(TSRMLS_C); - descriptor_init(TSRMLS_C); - enum_descriptor_init(TSRMLS_C); util_init(TSRMLS_C); return 0; diff --git a/php/ext/google/protobuf/protobuf.h b/php/ext/google/protobuf/protobuf.h index c00cd917..1f79fe8c 100644 --- a/php/ext/google/protobuf/protobuf.h +++ b/php/ext/google/protobuf/protobuf.h @@ -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. diff --git a/php/tests/descriptors_test.php b/php/tests/descriptors_test.php new file mode 100644 index 00000000..c3833c24 --- /dev/null +++ b/php/tests/descriptors_test.php @@ -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; + } +} diff --git a/php/tests/proto/test_descriptors.proto b/php/tests/proto/test_descriptors.proto new file mode 100644 index 00000000..d42aec7c --- /dev/null +++ b/php/tests/proto/test_descriptors.proto @@ -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; +} + diff --git a/php/tests/test.sh b/php/tests/test.sh index b640c143..c35372d3 100755 --- a/php/tests/test.sh +++ b/php/tests/test.sh @@ -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 @@ -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 |