Example #1
0
/**
 * 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);
}
Example #2
0
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;
}