diff options
author | mlumish <mlumish@google.com> | 2014-12-09 16:28:23 -0800 |
---|---|---|
committer | Michael Lumish <mlumish@google.com> | 2014-12-09 17:50:41 -0800 |
commit | b892a27e67e534eccf011ab8c68d455c5be77e6c (patch) | |
tree | 7d1475a645c6ac60ff0ee28f26425734048480cb /src/php/ext/grpc/call.c | |
parent | 275b3ac04dbe5654a38de47e7f4ee6b9b7ca7b8e (diff) |
Added PHP to the global gRPC moe config
Change on 2014/12/09 by mlumish <mlumish@google.com>
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=81727766
Diffstat (limited to 'src/php/ext/grpc/call.c')
-rwxr-xr-x | src/php/ext/grpc/call.c | 454 |
1 files changed, 454 insertions, 0 deletions
diff --git a/src/php/ext/grpc/call.c b/src/php/ext/grpc/call.c new file mode 100755 index 0000000000..be7969f927 --- /dev/null +++ b/src/php/ext/grpc/call.c @@ -0,0 +1,454 @@ +#include "call.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "ext/spl/spl_exceptions.h" +#include "php_grpc.h" + +#include "zend_exceptions.h" +#include "zend_hash.h" + +#include <stdbool.h> + +#include "grpc/support/log.h" +#include "grpc/grpc.h" + +#include "timeval.h" +#include "channel.h" +#include "completion_queue.h" +#include "byte_buffer.h" + +/* Frees and destroys an instance of wrapped_grpc_call */ +void free_wrapped_grpc_call(void *object TSRMLS_DC){ + wrapped_grpc_call *call = (wrapped_grpc_call*)object; + if(call->wrapped != NULL){ + grpc_call_destroy(call->wrapped); + } + efree(call); +} + +/* Initializes an instance of wrapped_grpc_call to be associated with an object + * of a class specified by class_type */ +zend_object_value create_wrapped_grpc_call( + zend_class_entry *class_type TSRMLS_DC){ + zend_object_value retval; + wrapped_grpc_call *intern; + + intern = (wrapped_grpc_call*)emalloc(sizeof(wrapped_grpc_call)); + memset(intern, 0, sizeof(wrapped_grpc_call)); + + zend_object_std_init(&intern->std, class_type TSRMLS_CC); + object_properties_init(&intern->std, class_type); + retval.handle = zend_objects_store_put( + intern, + (zend_objects_store_dtor_t) zend_objects_destroy_object, + free_wrapped_grpc_call, + NULL TSRMLS_CC); + retval.handlers = zend_get_std_object_handlers(); + return retval; +} + +zval *grpc_php_wrap_call(grpc_call *wrapped){ + zval *call_object; + MAKE_STD_ZVAL(call_object); + object_init_ex(call_object, grpc_ce_call); + wrapped_grpc_call *call = (wrapped_grpc_call*)zend_object_store_get_object( + call_object TSRMLS_CC); + call->wrapped = wrapped; + return call_object; +} + +zval *grpc_call_create_metadata_array(int count, grpc_metadata *elements){ + int i; + zval *array; + zval **data = NULL; + HashTable *array_hash; + zval *inner_array; + char *str_key; + char *str_val; + size_t key_len; + MAKE_STD_ZVAL(array); + array_init(array); + array_hash = Z_ARRVAL_P(array); + grpc_metadata *elem; + for(i=0; i<count; i++){ + elem = &elements[i]; + key_len = strlen(elem->key); + str_key = ecalloc(key_len+1, sizeof(char)); + memcpy(str_key, elem->key, key_len); + str_val = ecalloc(elem->value_length+1, sizeof(char)); + memcpy(str_val, elem->value, elem->value_length); + if(zend_hash_find(array_hash, + str_key, + key_len, + (void**)data) == SUCCESS){ + switch(Z_TYPE_P(*data)){ + case IS_STRING: + MAKE_STD_ZVAL(inner_array); + array_init(inner_array); + add_next_index_zval(inner_array, *data); + add_assoc_zval(array, str_key, inner_array); + break; + case IS_ARRAY: + inner_array = *data; + break; + default: + zend_throw_exception(zend_exception_get_default(), + "Metadata hash somehow contains wrong types.", + 1 TSRMLS_CC); + efree(str_key); + efree(str_val); + return NULL; + } + add_next_index_stringl(inner_array, + str_val, + elem->value_length, + false); + } else { + add_assoc_stringl(array, + str_key, + str_val, + elem->value_length, + false); + } + } + return array; +} + +int php_grpc_call_add_metadata_array_walk(void *elem TSRMLS_DC, + int num_args, + va_list args, + zend_hash_key *hash_key){ + zval **data = (zval**)elem; + grpc_metadata metadata; + grpc_call *call = va_arg(args, grpc_call*); + gpr_uint32 flags = va_arg(args, gpr_uint32); + const char *key; + HashTable *inner_hash; + /* We assume that either two args were passed, and we are in the recursive + case (and the second argument is the key), or one arg was passed and + hash_key is the string key. */ + if(num_args > 2){ + key = va_arg(args, const char*); + } else { + /* TODO(mlumish): If possible, check that hash_key is a string */ + key = hash_key->arKey; + } + switch(Z_TYPE_P(*data)){ + case IS_STRING: + metadata.key = (char*)key; + metadata.value = Z_STRVAL_P(*data); + metadata.value_length = Z_STRLEN_P(*data); + grpc_call_add_metadata(call, &metadata, 0u); + break; + case IS_ARRAY: + inner_hash = Z_ARRVAL_P(*data); + zend_hash_apply_with_arguments(inner_hash TSRMLS_CC, + php_grpc_call_add_metadata_array_walk, + 3, + call, + flags, + key); + break; + default: + zend_throw_exception(zend_exception_get_default(), + "Metadata hash somehow contains wrong types.", + 1 TSRMLS_CC); + } + return ZEND_HASH_APPLY_KEEP; +} + +/** + * Constructs a new instance of the Call class. + * @param Channel $channel The channel to associate the call with. Must not be + * closed. + * @param string $method The method to call + * @param Timeval $absolute_deadline The deadline for completing the call + */ +PHP_METHOD(Call, __construct){ + wrapped_grpc_call *call = (wrapped_grpc_call*)zend_object_store_get_object( + getThis() TSRMLS_CC); + zval *channel_obj; + char *method; + int method_len; + zval *deadline_obj; + /* "OsO" == 1 Object, 1 string, 1 Object */ + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "OsO", + &channel_obj, grpc_ce_channel, + &method, &method_len, + &deadline_obj, grpc_ce_timeval) == FAILURE){ + zend_throw_exception(spl_ce_InvalidArgumentException, + "Call expects a Channel, a String, and a Timeval", + 1 TSRMLS_CC); + return; + } + wrapped_grpc_channel *channel = + (wrapped_grpc_channel*)zend_object_store_get_object(channel_obj TSRMLS_CC); + if(channel->wrapped == NULL) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Call cannot be constructed from a closed Channel", + 1 TSRMLS_CC); + return; + } + add_property_zval(getThis(), "channel", channel_obj); + wrapped_grpc_timeval *deadline = + (wrapped_grpc_timeval*)zend_object_store_get_object(deadline_obj TSRMLS_CC); + call->wrapped = grpc_channel_create_call(channel->wrapped, + method, + channel->target, + deadline->wrapped); +} + +/** + * Add metadata to the call. All array keys must be strings. If the value is a + * string, it is added as a key/value pair. If it is an array, each value is + * added paired with the same string + * @param array $metadata The metadata to add + * @param long $flags A bitwise combination of the Grpc\WRITE_* constants + * (optional) + * @return Void + */ +PHP_METHOD(Call, add_metadata){ + wrapped_grpc_call *call = (wrapped_grpc_call*)zend_object_store_get_object( + getThis() TSRMLS_CC); + zval *array; + HashTable *array_hash; + long flags = 0; + /* "a|l" == 1 array, 1 optional long */ + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "a|l", + &array, + &flags) == FAILURE){ + zend_throw_exception(spl_ce_InvalidArgumentException, + "add_metadata expects an array and an optional long", + 1 TSRMLS_CC); + return; + } + array_hash = Z_ARRVAL_P(array); + zend_hash_apply_with_arguments(array_hash TSRMLS_CC, + php_grpc_call_add_metadata_array_walk, + 2, + call->wrapped, + (gpr_uint32)flags); +} + +/** + * Invoke the RPC. Starts sending metadata and request headers over the wire + * @param CompletionQueue $queue The completion queue to use with this call + * @param long $invoke_accepted_tag The tag to associate with this invocation + * @param long $metadata_tag The tag to associate with returned metadata + * @param long $finished_tag The tag to associate with the finished event + * @param long $flags A bitwise combination of the Grpc\WRITE_* constants + * (optional) + * @return long Error code + */ +PHP_METHOD(Call, start_invoke){ + long tag1; + long tag2; + long tag3; + zval *queue_obj; + long flags = 0; + /* "Olll|l" == 1 Object, 3 mandatory longs, 1 optional long */ + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "Olll|l", + &queue_obj, grpc_ce_completion_queue, + &tag1, + &tag2, + &tag3, + &flags) == FAILURE){ + zend_throw_exception( + spl_ce_InvalidArgumentException, + "start_invoke needs a CompletionQueue, 3 longs, and an optional long", + 1 TSRMLS_CC); + return; + } + add_property_zval(getThis(), "completion_queue", queue_obj); + wrapped_grpc_call *call = (wrapped_grpc_call*)zend_object_store_get_object( + getThis() TSRMLS_CC); + wrapped_grpc_completion_queue *queue = + (wrapped_grpc_completion_queue*)zend_object_store_get_object( + queue_obj TSRMLS_CC); + RETURN_LONG(grpc_call_start_invoke(call->wrapped, + queue->wrapped, + (void*)tag1, + (void*)tag2, + (void*)tag3, + (gpr_uint32)flags)); +} + +/** + * Accept an incoming RPC, binding a completion queue to it. To be called after + * adding metadata to the call, but before sending messages. Can only be called + * on the server + * @param CompletionQueue $queue The completion queue to use with this call + * @param long $finished_tag The tag to associate with the finished event + * @param long $flags A bitwise combination of the Grpc\WRITE_* constants + * (optional) + * @return long Error code + */ +PHP_METHOD(Call, accept){ + long tag; + zval *queue_obj; + long flags = 0; + /* "Ol|l" == 1 Object, 1 mandatory long, 1 optional long */ + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "Ol|l", + &queue_obj, grpc_ce_completion_queue, + &tag, + &flags) == FAILURE){ + zend_throw_exception( + spl_ce_InvalidArgumentException, + "accept expects a CompletionQueue, a long, and an optional long", + 1 TSRMLS_CC); + return; + } + add_property_zval(getThis(), "completion_queue", queue_obj); + wrapped_grpc_call *call = (wrapped_grpc_call*)zend_object_store_get_object( + getThis() TSRMLS_CC); + wrapped_grpc_completion_queue *queue = + (wrapped_grpc_completion_queue*)zend_object_store_get_object( + queue_obj TSRMLS_CC); + RETURN_LONG(grpc_call_accept(call->wrapped, + queue->wrapped, + (void*)tag, + (gpr_uint32)flags)); +} + +/** + * Called by clients to cancel an RPC on the server. + * @return long Error code + */ +PHP_METHOD(Call, cancel){ + wrapped_grpc_call *call = (wrapped_grpc_call*)zend_object_store_get_object( + getThis() TSRMLS_CC); + RETURN_LONG(grpc_call_cancel(call->wrapped)); +} + +/** + * Queue a byte buffer for writing + * @param string $buffer The buffer to queue for writing + * @param long $tag The tag to associate with this write + * @param long $flags A bitwise combination of the Grpc\WRITE_* constants + * (optional) + * @return long Error code + */ +PHP_METHOD(Call, start_write){ + wrapped_grpc_call *call = (wrapped_grpc_call*)zend_object_store_get_object( + getThis() TSRMLS_CC); + char *buffer; + int buffer_len; + long tag; + long flags = 0; + /* "Ol|l" == 1 Object, 1 mandatory long, 1 optional long */ + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "sl|l", + &buffer, &buffer_len, + &tag, + &flags) == FAILURE){ + zend_throw_exception( + spl_ce_InvalidArgumentException, + "start_write expects a string and an optional long", + 1 TSRMLS_CC); + return; + } + RETURN_LONG(grpc_call_start_write(call->wrapped, + string_to_byte_buffer(buffer, buffer_len), + (void*)tag, + (gpr_uint32)flags)); +} + +/** + * Queue a status for writing + * @param long $status_code The status code to send + * @param string $status_details The status details to send + * @param long $tag The tag to associate with this status + * @return long Error code + */ +PHP_METHOD(Call, start_write_status){ + wrapped_grpc_call *call = (wrapped_grpc_call*)zend_object_store_get_object( + getThis() TSRMLS_CC); + long status_code; + int status_details_length; + long tag; + grpc_status status; + /* "lsl" == 1 long, 1 string, 1 long */ + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "lsl", + &status_code, + &status.details, &status_details_length, + &tag) == FAILURE){ + zend_throw_exception( + spl_ce_InvalidArgumentException, + "start_write_status expects a long, a string, and a long", + 1 TSRMLS_CC); + return; + } + status.code = (gpr_uint32)status_code; + RETURN_LONG(grpc_call_start_write_status(call->wrapped, + status, + (void*)tag)); +} + +/** + * Indicate that there are no more messages to send + * @return long Error code + */ +PHP_METHOD(Call, writes_done){ + wrapped_grpc_call *call = (wrapped_grpc_call*)zend_object_store_get_object( + getThis() TSRMLS_CC); + long tag; + /* "l" == 1 long */ + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &tag) == FAILURE){ + zend_throw_exception(spl_ce_InvalidArgumentException, + "writes_done expects a long", + 1 TSRMLS_CC); + return; + } + RETURN_LONG(grpc_call_writes_done(call->wrapped, (void*)tag)); +} + +/** + * Initiate a read on a call. Output event contains a byte buffer with the + * result of the read + * @param long $tag The tag to associate with this read + * @return long Error code + */ +PHP_METHOD(Call, start_read){ + wrapped_grpc_call *call = (wrapped_grpc_call*)zend_object_store_get_object( + getThis() TSRMLS_CC); + long tag; + /* "l" == 1 long */ + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &tag) == FAILURE){ + zend_throw_exception(spl_ce_InvalidArgumentException, + "start_read expects a long", + 1 TSRMLS_CC); + return; + } + RETURN_LONG(grpc_call_start_read(call->wrapped, (void*)tag)); +} + +static zend_function_entry call_methods[] = { + PHP_ME(Call, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) + PHP_ME(Call, accept, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Call, add_metadata, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Call, cancel, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Call, start_invoke, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Call, start_read, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Call, start_write, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Call, start_write_status, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Call, writes_done, NULL, ZEND_ACC_PUBLIC) + PHP_FE_END +}; + +void grpc_init_call(TSRMLS_D){ + zend_class_entry ce; + INIT_CLASS_ENTRY(ce, "Grpc\\Call", call_methods); + ce.create_object = create_wrapped_grpc_call; + grpc_ce_call = zend_register_internal_class(&ce TSRMLS_CC); +} |