From 616e68ecc1c997c8b4c22a708cc9f6605a329bef Mon Sep 17 00:00:00 2001 From: Paul Yang Date: Fri, 10 Mar 2017 13:42:59 -0800 Subject: Repeated/Map field setter should accept a regular PHP array (#2817) Accept regular PHP array for repeated/map setter. Existing map/repeated field will be swapped by a clean map/repeated field. Then, elements in the array will be added to the map/repeated field. All elements will be type checked before adding. See #2686 for detail. --- php/ext/google/protobuf/array.c | 20 +++--- php/ext/google/protobuf/map.c | 27 ++++++-- php/ext/google/protobuf/protobuf.h | 12 +++- php/ext/google/protobuf/storage.c | 6 +- php/ext/google/protobuf/type_check.c | 116 ++++++++++++++++++++++++++++++++--- 5 files changed, 154 insertions(+), 27 deletions(-) (limited to 'php/ext/google') diff --git a/php/ext/google/protobuf/array.c b/php/ext/google/protobuf/array.c index 63bb6d0a..ab2eaf90 100644 --- a/php/ext/google/protobuf/array.c +++ b/php/ext/google/protobuf/array.c @@ -225,8 +225,17 @@ void repeated_field_push_native(RepeatedField *intern, void *value TSRMLS_DC) { zend_hash_next_index_insert(ht, (void **)value, size, NULL); } +void repeated_field_create_with_field(zend_class_entry *ce, + const upb_fielddef *field, + zval **repeated_field TSRMLS_DC) { + upb_fieldtype_t type = upb_fielddef_type(field); + const zend_class_entry *msg_ce = field_type_class(field TSRMLS_CC); + repeated_field_create_with_type(ce, type, msg_ce, repeated_field); +} + void repeated_field_create_with_type(zend_class_entry *ce, - const upb_fielddef *field, + upb_fieldtype_t type, + const zend_class_entry* msg_ce, zval **repeated_field TSRMLS_DC) { MAKE_STD_ZVAL(*repeated_field); Z_TYPE_PP(repeated_field) = IS_OBJECT; @@ -235,13 +244,8 @@ void repeated_field_create_with_type(zend_class_entry *ce, RepeatedField *intern = zend_object_store_get_object(*repeated_field TSRMLS_CC); - intern->type = upb_fielddef_type(field); - if (intern->type == UPB_TYPE_MESSAGE) { - const upb_msgdef *msg = upb_fielddef_msgsubdef(field); - zval *desc_php = get_def_obj(msg); - Descriptor *desc = zend_object_store_get_object(desc_php TSRMLS_CC); - intern->msg_ce = desc->klass; - } + intern->type = type; + intern->msg_ce = msg_ce; MAKE_STD_ZVAL(intern->array); repeated_field_array_init(intern->array, intern->type, 0 ZEND_FILE_LINE_CC); diff --git a/php/ext/google/protobuf/map.c b/php/ext/google/protobuf/map.c index ab98879d..4a3829d6 100644 --- a/php/ext/google/protobuf/map.c +++ b/php/ext/google/protobuf/map.c @@ -146,6 +146,11 @@ static zend_function_entry map_field_methods[] = { ZEND_FE_END }; +// Forward declare static functions. + +static bool map_field_write_dimension(zval *object, zval *key, + zval *value TSRMLS_DC); + // ----------------------------------------------------------------------------- // MapField creation/desctruction // ----------------------------------------------------------------------------- @@ -183,6 +188,7 @@ void map_field_init(TSRMLS_D) { map_field_handlers = PEMALLOC(zend_object_handlers); memcpy(map_field_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + map_field_handlers->write_dimension = map_field_write_dimension; map_field_handlers->get_gc = map_field_get_gc; } @@ -235,7 +241,18 @@ void map_field_free(void *object TSRMLS_DC) { efree(object); } -void map_field_create_with_type(zend_class_entry *ce, const upb_fielddef *field, +void map_field_create_with_field(zend_class_entry *ce, const upb_fielddef *field, + zval **map_field TSRMLS_DC) { + const upb_fielddef *key_field = map_field_key(field); + const upb_fielddef *value_field = map_field_value(field); + map_field_create_with_type( + ce, upb_fielddef_type(key_field), upb_fielddef_type(value_field), + field_type_class(value_field TSRMLS_CC), map_field); +} + +void map_field_create_with_type(zend_class_entry *ce, upb_fieldtype_t key_type, + upb_fieldtype_t value_type, + const zend_class_entry *msg_ce, zval **map_field TSRMLS_DC) { MAKE_STD_ZVAL(*map_field); Z_TYPE_PP(map_field) = IS_OBJECT; @@ -245,11 +262,9 @@ void map_field_create_with_type(zend_class_entry *ce, const upb_fielddef *field, Map* intern = (Map*)zend_object_store_get_object(*map_field TSRMLS_CC); - const upb_fielddef *key_field = map_field_key(field); - const upb_fielddef *value_field = map_field_value(field); - intern->key_type = upb_fielddef_type(key_field); - intern->value_type = upb_fielddef_type(value_field); - intern->msg_ce = field_type_class(value_field TSRMLS_CC); + intern->key_type = key_type; + intern->value_type = value_type; + intern->msg_ce = msg_ce; } static void map_field_free_element(void *object) { diff --git a/php/ext/google/protobuf/protobuf.h b/php/ext/google/protobuf/protobuf.h index 28cea95c..2287f7e6 100644 --- a/php/ext/google/protobuf/protobuf.h +++ b/php/ext/google/protobuf/protobuf.h @@ -296,6 +296,7 @@ PHP_METHOD(Util, checkBool); PHP_METHOD(Util, checkString); PHP_METHOD(Util, checkBytes); PHP_METHOD(Util, checkMessage); +PHP_METHOD(Util, checkMapField); PHP_METHOD(Util, checkRepeatedField); // ----------------------------------------------------------------------------- @@ -349,7 +350,11 @@ const upb_fielddef* map_entry_key(const upb_msgdef* msgdef); const upb_fielddef* map_entry_value(const upb_msgdef* msgdef); zend_object_value map_field_create(zend_class_entry *ce TSRMLS_DC); -void map_field_create_with_type(zend_class_entry *ce, const upb_fielddef *field, +void map_field_create_with_field(zend_class_entry *ce, const upb_fielddef *field, + zval **map_field TSRMLS_DC); +void map_field_create_with_type(zend_class_entry *ce, upb_fieldtype_t key_type, + upb_fieldtype_t value_type, + const zend_class_entry *msg_ce, zval **map_field TSRMLS_DC); void map_field_free(void* object TSRMLS_DC); void* upb_value_memory(upb_value* v); @@ -392,9 +397,12 @@ struct RepeatedFieldIter { long position; }; -void repeated_field_create_with_type(zend_class_entry* ce, +void repeated_field_create_with_field(zend_class_entry* ce, const upb_fielddef* field, zval** repeated_field TSRMLS_DC); +void repeated_field_create_with_type(zend_class_entry* ce, upb_fieldtype_t type, + const zend_class_entry* msg_ce, + zval** repeated_field TSRMLS_DC); // Return the element at the index position from the repeated field. There is // not restriction on the type of stored elements. void *repeated_field_index_native(RepeatedField *intern, int index TSRMLS_DC); diff --git a/php/ext/google/protobuf/storage.c b/php/ext/google/protobuf/storage.c index 1b239ee3..af7c292f 100644 --- a/php/ext/google/protobuf/storage.c +++ b/php/ext/google/protobuf/storage.c @@ -517,12 +517,12 @@ void layout_init(MessageLayout* layout, void* storage, *oneof_case = ONEOF_CASE_NONE; } else if (is_map_field(field)) { zval_ptr_dtor(property_ptr); - map_field_create_with_type(map_field_type, field, property_ptr TSRMLS_CC); + map_field_create_with_field(map_field_type, field, property_ptr TSRMLS_CC); DEREF(memory, zval**) = property_ptr; } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { zval_ptr_dtor(property_ptr); - repeated_field_create_with_type(repeated_field_type, field, - property_ptr TSRMLS_CC); + repeated_field_create_with_field(repeated_field_type, field, + property_ptr TSRMLS_CC); DEREF(memory, zval**) = property_ptr; } else { native_slot_init(upb_fielddef_type(field), memory, property_ptr); diff --git a/php/ext/google/protobuf/type_check.c b/php/ext/google/protobuf/type_check.c index d12d0025..718682ac 100644 --- a/php/ext/google/protobuf/type_check.c +++ b/php/ext/google/protobuf/type_check.c @@ -51,6 +51,13 @@ ZEND_BEGIN_ARG_INFO_EX(arg_check_repeated, 0, 0, 2) ZEND_ARG_INFO(0, klass) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arg_check_map, 0, 0, 3) + ZEND_ARG_INFO(1, val) + ZEND_ARG_INFO(0, key_type) + ZEND_ARG_INFO(0, value_type) + ZEND_ARG_INFO(0, klass) +ZEND_END_ARG_INFO() + static zend_function_entry util_methods[] = { PHP_ME(Util, checkInt32, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) PHP_ME(Util, checkUint32, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) @@ -63,6 +70,7 @@ static zend_function_entry util_methods[] = { PHP_ME(Util, checkString, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) PHP_ME(Util, checkBytes, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) PHP_ME(Util, checkMessage, arg_check_message, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + PHP_ME(Util, checkMapField, arg_check_map, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) PHP_ME(Util, checkRepeatedField, arg_check_repeated, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) ZEND_FE_END @@ -411,20 +419,112 @@ PHP_METHOD(Util, checkRepeatedField) { zval* val; long type; const zend_class_entry* klass = NULL; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Ol|C", &val, - repeated_field_type, &type, &klass) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zl|C", &val, &type, + &klass) == FAILURE) { return; } - RepeatedField *intern = - (RepeatedField *)zend_object_store_get_object(val TSRMLS_CC); - if (to_fieldtype(type) != intern->type) { + if (Z_TYPE_P(val) == IS_ARRAY) { + HashTable* table = Z_ARRVAL_P(val); + HashPosition pointer; + void* memory; + zval* repeated_field; + + repeated_field_create_with_type(repeated_field_type, to_fieldtype(type), + klass, &repeated_field TSRMLS_CC); + RepeatedField* intern = + (RepeatedField*)zend_object_store_get_object(repeated_field TSRMLS_CC); + + for (zend_hash_internal_pointer_reset_ex(table, &pointer); + zend_hash_get_current_data_ex(table, (void**)&memory, &pointer) == + SUCCESS; + zend_hash_move_forward_ex(table, &pointer)) { + repeated_field_handlers->write_dimension(repeated_field, NULL, *(zval**)memory); + } + + Z_DELREF_P(repeated_field); + RETURN_ZVAL(repeated_field, 1, 0); + + } else if (Z_TYPE_P(val) == IS_OBJECT) { + if (!instanceof_function(Z_OBJCE_P(val), repeated_field_type TSRMLS_CC)) { + zend_error(E_USER_ERROR, "Given value is not an instance of %s.", + repeated_field_type->name); + return; + } + RepeatedField* intern = + (RepeatedField*)zend_object_store_get_object(val TSRMLS_CC); + if (to_fieldtype(type) != intern->type) { + zend_error(E_USER_ERROR, "Incorrect repeated field type."); + return; + } + if (klass != NULL && intern->msg_ce != klass) { + zend_error(E_USER_ERROR, + "Expect a repeated field of %s, but %s is given.", klass->name, + intern->msg_ce->name); + return; + } + RETURN_ZVAL(val, 1, 0); + } else { zend_error(E_USER_ERROR, "Incorrect repeated field type."); return; } - if (klass != NULL && intern->msg_ce != klass) { - zend_error(E_USER_ERROR, "Expect a repeated field of %s, but %s is given.", - klass->name, intern->msg_ce->name); + +} + +PHP_METHOD(Util, checkMapField) { + zval* val; + long key_type, value_type; + const zend_class_entry* klass = NULL; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zll|C", &val, &key_type, + &value_type, &klass) == FAILURE) { + return; + } + + if (Z_TYPE_P(val) == IS_ARRAY) { + HashTable* table = Z_ARRVAL_P(val); + HashPosition pointer; + zval key, *map_field; + void* value; + + map_field_create_with_type(map_field_type, to_fieldtype(key_type), + to_fieldtype(value_type), klass, + &map_field TSRMLS_CC); + Map* intern = + (Map*)zend_object_store_get_object(map_field TSRMLS_CC); + + for (zend_hash_internal_pointer_reset_ex(table, &pointer); + zend_hash_get_current_data_ex(table, (void**)&value, &pointer) == + SUCCESS; + zend_hash_move_forward_ex(table, &pointer)) { + zend_hash_get_current_key_zval_ex(table, &key, &pointer); + map_field_handlers->write_dimension(map_field, &key, *(zval**)value); + } + + Z_DELREF_P(map_field); + RETURN_ZVAL(map_field, 1, 0); + } else if (Z_TYPE_P(val) == IS_OBJECT) { + if (!instanceof_function(Z_OBJCE_P(val), map_field_type TSRMLS_CC)) { + zend_error(E_USER_ERROR, "Given value is not an instance of %s.", + map_field_type->name); + return; + } + Map* intern = (Map*)zend_object_store_get_object(val TSRMLS_CC); + if (to_fieldtype(key_type) != intern->key_type) { + zend_error(E_USER_ERROR, "Incorrect map field key type."); + return; + } + if (to_fieldtype(value_type) != intern->value_type) { + zend_error(E_USER_ERROR, "Incorrect map field value type."); + return; + } + if (klass != NULL && intern->msg_ce != klass) { + zend_error(E_USER_ERROR, "Expect a map field of %s, but %s is given.", + klass->name, intern->msg_ce->name); + return; + } + RETURN_ZVAL(val, 1, 0); + } else { + zend_error(E_USER_ERROR, "Incorrect map field type."); return; } } -- cgit v1.2.3