/** * Start a batch of RPC actions. * @param array batch Array of actions to take * @return object Object with results of all actions */ PHP_METHOD(Call, startBatch) { wrapped_grpc_call *call = Z_WRAPPED_GRPC_CALL_P(getThis()); grpc_op ops[8]; size_t op_num = 0; zval *array; zval *value; zval *inner_value; HashTable *array_hash; HashPosition array_pointer; HashTable *status_hash; HashTable *message_hash; zval *message_value; zval *message_flags; zend_string *key; zend_ulong index; grpc_metadata_array metadata; grpc_metadata_array trailing_metadata; grpc_metadata_array recv_metadata; grpc_metadata_array recv_trailing_metadata; grpc_status_code status; char *status_details = NULL; size_t status_details_capacity = 0; grpc_byte_buffer *message; int cancelled; grpc_call_error error; char *message_str; size_t message_len; zval recv_status; grpc_metadata_array_init(&metadata); grpc_metadata_array_init(&trailing_metadata); grpc_metadata_array_init(&recv_metadata); grpc_metadata_array_init(&recv_trailing_metadata); object_init(return_value); /* "a" == 1 array */ #ifndef FAST_ZPP if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &array) == FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, "start_batch expects an array", 1); goto cleanup; } #else ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ARRAY(array) ZEND_PARSE_PARAMETERS_END(); #endif array_hash = HASH_OF(array); //TODO(thinkerou): why phpunit wrong when use ZEND_HASH_FOREACH_VAL marco? //ZEND_HASH_FOREACH_VAL(array_hash, value) { for (zend_hash_internal_pointer_reset_ex(array_hash, &array_pointer); (value = zend_hash_get_current_data_ex(array_hash, &array_pointer)) != NULL; zend_hash_move_forward_ex(array_hash, &array_pointer)) { if (zend_hash_get_current_key_ex(array_hash, &key, &index, &array_pointer) != HASH_KEY_IS_LONG) { zend_throw_exception(spl_ce_InvalidArgumentException, "batch keys must be integers", 1); goto cleanup; } switch(index) { case GRPC_OP_SEND_INITIAL_METADATA: if (!create_metadata_array(value, &metadata)) { zend_throw_exception(spl_ce_InvalidArgumentException, "Bad metadata value given", 1); goto cleanup; } ops[op_num].data.send_initial_metadata.count = metadata.count; ops[op_num].data.send_initial_metadata.metadata = metadata.metadata; break; case GRPC_OP_SEND_MESSAGE: if (Z_TYPE_P(value) != IS_ARRAY) { zend_throw_exception(spl_ce_InvalidArgumentException, "Expected an array for send message", 1); goto cleanup; } message_hash = HASH_OF(value); if ((message_flags = zend_hash_str_find(message_hash, "flags", sizeof("flags") - 1)) != NULL) { if (Z_TYPE_P(message_flags) != IS_LONG) { zend_throw_exception(spl_ce_InvalidArgumentException, "Expected an int for message flags", 1); } ops[op_num].flags = Z_LVAL_P(message_flags) & GRPC_WRITE_USED_MASK; } if ((message_value = zend_hash_str_find( message_hash, "message", sizeof("message") - 1)) == NULL || Z_TYPE_P(message_value) != IS_STRING) { zend_throw_exception(spl_ce_InvalidArgumentException, "Expected a string for send message", 1); goto cleanup; } ops[op_num].data.send_message = string_to_byte_buffer(Z_STRVAL_P(message_value), Z_STRLEN_P(message_value)); break; case GRPC_OP_SEND_CLOSE_FROM_CLIENT: break; case GRPC_OP_SEND_STATUS_FROM_SERVER: status_hash = HASH_OF(value); if ((inner_value = zend_hash_str_find( status_hash, "metadata", sizeof("metadata") - 1)) != NULL) { if (!create_metadata_array(inner_value, &trailing_metadata)) { zend_throw_exception(spl_ce_InvalidArgumentException, "Bad trailing metadata value given", 1); goto cleanup; } ops[op_num].data.send_status_from_server.trailing_metadata = trailing_metadata.metadata; ops[op_num].data.send_status_from_server.trailing_metadata_count = trailing_metadata.count; } if ((inner_value = zend_hash_str_find( status_hash, "code", sizeof("code") - 1)) != NULL) { if (Z_TYPE_P(inner_value) != IS_LONG) { zend_throw_exception(spl_ce_InvalidArgumentException, "Status code must be an integer", 1); goto cleanup; } ops[op_num].data.send_status_from_server.status = Z_LVAL_P(inner_value); } else { zend_throw_exception(spl_ce_InvalidArgumentException, "Integer status code is required", 1); goto cleanup; } if ((inner_value = zend_hash_str_find( status_hash, "details", sizeof("details") - 1)) != NULL) { if (Z_TYPE_P(inner_value) != IS_STRING) { zend_throw_exception(spl_ce_InvalidArgumentException, "Status details must be a string", 1); goto cleanup; } ops[op_num].data.send_status_from_server.status_details = Z_STRVAL_P(inner_value); } else { zend_throw_exception(spl_ce_InvalidArgumentException, "String status details is required", 1); goto cleanup; } break; case GRPC_OP_RECV_INITIAL_METADATA: ops[op_num].data.recv_initial_metadata = &recv_metadata; break; case GRPC_OP_RECV_MESSAGE: ops[op_num].data.recv_message = &message; break; case GRPC_OP_RECV_STATUS_ON_CLIENT: ops[op_num].data.recv_status_on_client.trailing_metadata = &recv_trailing_metadata; ops[op_num].data.recv_status_on_client.status = &status; ops[op_num].data.recv_status_on_client.status_details = &status_details; ops[op_num].data.recv_status_on_client.status_details_capacity = &status_details_capacity; break; case GRPC_OP_RECV_CLOSE_ON_SERVER: ops[op_num].data.recv_close_on_server.cancelled = &cancelled; break; default: zend_throw_exception(spl_ce_InvalidArgumentException, "Unrecognized key in batch", 1); goto cleanup; } ops[op_num].op = (grpc_op_type)index; ops[op_num].flags = 0; ops[op_num].reserved = NULL; op_num++; } //ZEND_HASH_FOREACH_END(); error = grpc_call_start_batch(call->wrapped, ops, op_num, call->wrapped, NULL); if (error != GRPC_CALL_OK) { zend_throw_exception(spl_ce_LogicException, "start_batch was called incorrectly", (long)error); goto cleanup; } grpc_completion_queue_pluck(completion_queue, call->wrapped, gpr_inf_future(GPR_CLOCK_REALTIME), NULL); for (int i = 0; i < op_num; i++) { switch(ops[i].op) { case GRPC_OP_SEND_INITIAL_METADATA: add_property_bool(return_value, "send_metadata", true); break; case GRPC_OP_SEND_MESSAGE: add_property_bool(return_value, "send_message", true); break; case GRPC_OP_SEND_CLOSE_FROM_CLIENT: add_property_bool(return_value, "send_close", true); break; case GRPC_OP_SEND_STATUS_FROM_SERVER: add_property_bool(return_value, "send_status", true); break; case GRPC_OP_RECV_INITIAL_METADATA: grpc_parse_metadata_array(&recv_metadata, array); add_property_zval(return_value, "metadata", array); //Z_DELREF_P(array); break; case GRPC_OP_RECV_MESSAGE: byte_buffer_to_string(message, &message_str, &message_len); if (message_str == NULL) { add_property_null(return_value, "message"); } else { add_property_stringl(return_value, "message", message_str, message_len); } break; case GRPC_OP_RECV_STATUS_ON_CLIENT: object_init(&recv_status); grpc_parse_metadata_array(&recv_trailing_metadata, array); add_property_zval(&recv_status, "metadata", array); //Z_DELREF_P(array); add_property_long(&recv_status, "code", status); add_property_string(&recv_status, "details", status_details); add_property_zval(return_value, "status", &recv_status); //Z_DELREF_P(&recv_status); break; case GRPC_OP_RECV_CLOSE_ON_SERVER: add_property_bool(return_value, "cancelled", cancelled); break; default: break; } } cleanup: grpc_metadata_array_destroy(&metadata); grpc_metadata_array_destroy(&trailing_metadata); grpc_metadata_array_destroy(&recv_metadata); grpc_metadata_array_destroy(&recv_trailing_metadata); if (status_details != NULL) { gpr_free(status_details); } for (int i = 0; i < op_num; i++) { if (ops[i].op == GRPC_OP_SEND_MESSAGE) { grpc_byte_buffer_destroy(ops[i].data.send_message); } if (ops[i].op == GRPC_OP_RECV_MESSAGE) { grpc_byte_buffer_destroy(message); } } RETURN_DESTROY_ZVAL(return_value); }
static php_stream_filter *user_filter_factory_create(const char *filtername, zval *filterparams, uint8_t persistent) { struct php_user_filter_data *fdat = NULL; php_stream_filter *filter; zval obj, zfilter; zval func_name; zval retval; size_t len; /* some sanity checks */ if (persistent) { php_error_docref(NULL, E_WARNING, "cannot use a user-space filter with a persistent stream"); return NULL; } len = strlen(filtername); /* determine the classname/class entry */ if (NULL == (fdat = zend_hash_str_find_ptr(BG(user_filter_map), (char*)filtername, len))) { char *period; /* Userspace Filters using ambiguous wildcards could cause problems. i.e.: myfilter.foo.bar will always call into myfilter.foo.* never seeing myfilter.* TODO: Allow failed userfilter creations to continue scanning through the list */ if ((period = strrchr(filtername, '.'))) { char *wildcard = safe_emalloc(len, 1, 3); /* Search for wildcard matches instead */ memcpy(wildcard, filtername, len + 1); /* copy \0 */ period = wildcard + (period - filtername); while (period) { *period = '\0'; strncat(wildcard, ".*", 2); if (NULL != (fdat = zend_hash_str_find_ptr(BG(user_filter_map), wildcard, strlen(wildcard)))) { period = NULL; } else { *period = '\0'; period = strrchr(wildcard, '.'); } } efree(wildcard); } if (fdat == NULL) { php_error_docref(NULL, E_WARNING, "Err, filter \"%s\" is not in the user-filter map, but somehow the user-filter-factory was invoked for it!?", filtername); return NULL; } } /* bind the classname to the actual class */ if (fdat->ce == NULL) { if (NULL == (fdat->ce = zend_lookup_class(fdat->classname))) { php_error_docref(NULL, E_WARNING, "user-filter \"%s\" requires class \"%s\", but that class is not defined", filtername, ZSTR_VAL(fdat->classname)); return NULL; } } filter = php_stream_filter_alloc(&userfilter_ops, NULL, 0); if (filter == NULL) { return NULL; } /* create the object */ object_init_ex(&obj, fdat->ce); /* filtername */ add_property_string(&obj, "filtername", (char*)filtername); /* and the parameters, if any */ if (filterparams) { add_property_zval(&obj, "params", filterparams); } else { add_property_null(&obj, "params"); } /* invoke the constructor */ ZVAL_STRINGL(&func_name, "oncreate", sizeof("oncreate")-1); call_user_function_ex(NULL, &obj, &func_name, &retval, 0, NULL, 0, NULL); if (Z_TYPE(retval) != IS_UNDEF) { if (Z_TYPE(retval) == IS_FALSE) { /* User reported filter creation error "return false;" */ zval_ptr_dtor(&retval); /* Kill the filter (safely) */ ZVAL_UNDEF(&filter->abstract); php_stream_filter_free(filter); /* Kill the object */ zval_ptr_dtor(&obj); /* Report failure to filter_alloc */ return NULL; } zval_ptr_dtor(&retval); } zval_ptr_dtor(&func_name); /* set the filter property, this will be used during cleanup */ ZVAL_RES(&zfilter, zend_register_resource(filter, le_userfilters)); ZVAL_COPY_VALUE(&filter->abstract, &obj); add_property_zval(&obj, "filter", &zfilter); /* add_property_zval increments the refcount which is unwanted here */ zval_ptr_dtor(&zfilter); return filter; }