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
diff --git a/src/php/ext/grpc/byte_buffer.c b/src/php/ext/grpc/byte_buffer.c
new file mode 100755
index 0000000..db01831
--- /dev/null
+++ b/src/php/ext/grpc/byte_buffer.c
@@ -0,0 +1,38 @@
+#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 <string.h>
+
+#include "byte_buffer.h"
+
+#include "grpc/grpc.h"
+#include "grpc/support/slice.h"
+
+grpc_byte_buffer *string_to_byte_buffer(char *string, size_t length) {
+ gpr_slice slice = gpr_slice_malloc(length);
+ memcpy(GPR_SLICE_START_PTR(slice), string, length);
+ return grpc_byte_buffer_create(&slice, 1);
+}
+
+void byte_buffer_to_string(grpc_byte_buffer *buffer,
+ char **out_string,
+ size_t *out_length) {
+ size_t length = grpc_byte_buffer_length(buffer);
+ char *string = ecalloc(length+1, sizeof(char));
+ size_t offset = 0;
+ grpc_byte_buffer_reader *reader = grpc_byte_buffer_reader_create(buffer);
+ gpr_slice next;
+ while(grpc_byte_buffer_reader_next(reader, &next) != 0) {
+ memcpy(string+offset, GPR_SLICE_START_PTR(next), GPR_SLICE_LENGTH(next));
+ offset += GPR_SLICE_LENGTH(next);
+ }
+ *out_string = string;
+ *out_length = length;
+}
diff --git a/src/php/ext/grpc/byte_buffer.h b/src/php/ext/grpc/byte_buffer.h
new file mode 100755
index 0000000..1dd4769
--- /dev/null
+++ b/src/php/ext/grpc/byte_buffer.h
@@ -0,0 +1,12 @@
+#ifndef NET_GRPC_PHP_GRPC_BYTE_BUFFER_H_
+#define NET_GRPC_PHP_GRPC_BYTE_BUFFER_H_
+
+#include "grpc/grpc.h"
+
+grpc_byte_buffer *string_to_byte_buffer(char *string, size_t length);
+
+void byte_buffer_to_string(grpc_byte_buffer *buffer,
+ char **out_string,
+ size_t *out_length);
+
+#endif /* NET_GRPC_PHP_GRPC_BYTE_BUFFER_H_ */
diff --git a/src/php/ext/grpc/call.c b/src/php/ext/grpc/call.c
new file mode 100755
index 0000000..be7969f
--- /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);
+}
diff --git a/src/php/ext/grpc/call.h b/src/php/ext/grpc/call.h
new file mode 100755
index 0000000..c433e6f
--- /dev/null
+++ b/src/php/ext/grpc/call.h
@@ -0,0 +1,35 @@
+#ifndef NET_GRPC_PHP_GRPC_CALL_H_
+#define NET_GRPC_PHP_GRPC_CALL_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "php_grpc.h"
+
+#include "grpc/grpc.h"
+
+/* Class entry for the Call PHP class */
+zend_class_entry *grpc_ce_call;
+
+/* Wrapper struct for grpc_call that can be associated with a PHP object */
+typedef struct wrapped_grpc_call {
+ zend_object std;
+
+ grpc_call *wrapped;
+} wrapped_grpc_call;
+
+/* Initializes the Call PHP class */
+void grpc_init_call(TSRMLS_D);
+
+/* Creates a Call object that wraps the given grpc_call struct */
+zval *grpc_php_wrap_call(grpc_call *wrapped);
+
+/* Creates and returns a PHP associative array of metadata from a C array of
+ * call metadata */
+zval *grpc_call_create_metadata_array(int count, grpc_metadata *elements);
+
+#endif /* NET_GRPC_PHP_GRPC_CHANNEL_H_ */
diff --git a/src/php/ext/grpc/channel.c b/src/php/ext/grpc/channel.c
new file mode 100755
index 0000000..c2847b9
--- /dev/null
+++ b/src/php/ext/grpc/channel.c
@@ -0,0 +1,182 @@
+#include "channel.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 <stdbool.h>
+
+#include "grpc/grpc.h"
+#include "grpc/support/log.h"
+#include "grpc/grpc_security.h"
+
+#include "completion_queue.h"
+#include "server.h"
+#include "credentials.h"
+
+/* Frees and destroys an instance of wrapped_grpc_channel */
+void free_wrapped_grpc_channel(void *object TSRMLS_DC){
+ wrapped_grpc_channel *channel = (wrapped_grpc_channel*)object;
+ if(channel->wrapped != NULL){
+ grpc_channel_destroy(channel->wrapped);
+ }
+ efree(channel);
+}
+
+/* Initializes an instance of wrapped_grpc_channel to be associated with an
+ * object of a class specified by class_type */
+zend_object_value create_wrapped_grpc_channel(
+ zend_class_entry *class_type TSRMLS_DC){
+ zend_object_value retval;
+ wrapped_grpc_channel *intern;
+ intern = (wrapped_grpc_channel*)emalloc(sizeof(wrapped_grpc_channel));
+ memset(intern, 0, sizeof(wrapped_grpc_channel));
+ 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_channel,
+ NULL TSRMLS_CC);
+ retval.handlers = zend_get_std_object_handlers();
+ return retval;
+}
+
+void php_grpc_read_args_array(zval *args_array, grpc_channel_args *args){
+ HashTable *array_hash;
+ HashPosition array_pointer;
+ int args_index;
+ zval **data;
+ char *key;
+ uint key_len;
+ ulong index;
+ array_hash = Z_ARRVAL_P(args_array);
+ args->num_args = zend_hash_num_elements(array_hash);
+ args->args = ecalloc(args->num_args, sizeof(grpc_arg));
+ args_index = 0;
+ for(zend_hash_internal_pointer_reset_ex(array_hash, &array_pointer);
+ zend_hash_get_current_data_ex(array_hash,
+ (void**)&data,
+ &array_pointer) == SUCCESS;
+ zend_hash_move_forward_ex(array_hash, &array_pointer)){
+ if(zend_hash_get_current_key_ex(array_hash,
+ &key,
+ &key_len,
+ &index,
+ 0,
+ &array_pointer) != HASH_KEY_IS_STRING){
+ zend_throw_exception(spl_ce_InvalidArgumentException,
+ "args keys must be strings",
+ 1 TSRMLS_CC);
+ return;
+ }
+ args->args[args_index].key = key;
+ switch(Z_TYPE_P(*data)){
+ case IS_LONG:
+ args->args[args_index].value.integer = (int)Z_LVAL_P(*data);
+ break;
+ case IS_STRING:
+ args->args[args_index].value.string = Z_STRVAL_P(*data);
+ break;
+ default:
+ zend_throw_exception(spl_ce_InvalidArgumentException,
+ "args values must be int or string",
+ 1 TSRMLS_CC);
+ return;
+ }
+ args_index++;
+ }
+}
+
+/**
+ * Construct an instance of the Channel class. If the $args array contains a
+ * "credentials" key mapping to a Credentials object, a secure channel will be
+ * created with those credentials.
+ * @param string $target The hostname to associate with this channel
+ * @param array $args The arguments to pass to the Channel (optional)
+ */
+PHP_METHOD(Channel, __construct){
+ wrapped_grpc_channel *channel =
+ (wrapped_grpc_channel*)zend_object_store_get_object(getThis() TSRMLS_CC);
+ char *target;
+ int target_length;
+ zval *args_array = NULL;
+ grpc_channel_args args;
+ HashTable *array_hash;
+ zval **creds_obj = NULL;
+ wrapped_grpc_credentials *creds = NULL;
+ /* "s|a" == 1 string, 1 optional array */
+ if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "s|a",
+ &target, &target_length,
+ &args_array) == FAILURE){
+ zend_throw_exception(spl_ce_InvalidArgumentException,
+ "Channel expects a string and an array",
+ 1 TSRMLS_CC);
+ return;
+ }
+ if (args_array == NULL) {
+ channel->wrapped = grpc_channel_create(target, NULL);
+ } else {
+ array_hash = Z_ARRVAL_P(args_array);
+ if(zend_hash_find(array_hash,
+ "credentials",
+ sizeof("credentials"),
+ (void**)&creds_obj) == SUCCESS) {
+ if(zend_get_class_entry(*creds_obj TSRMLS_CC) != grpc_ce_credentials) {
+ zend_throw_exception(spl_ce_InvalidArgumentException,
+ "credentials must be a Credentials object",
+ 1 TSRMLS_CC);
+ return;
+ }
+ creds = (wrapped_grpc_credentials*)zend_object_store_get_object(
+ *creds_obj TSRMLS_CC);
+ zend_hash_del(array_hash, "credentials", 12);
+ }
+ php_grpc_read_args_array(args_array, &args);
+ if (creds == NULL) {
+ channel->wrapped = grpc_channel_create(target, &args);
+ } else {
+ gpr_log(GPR_DEBUG, "Initialized secure channel");
+ channel->wrapped = grpc_secure_channel_create(creds->wrapped,
+ target,
+ &args);
+ }
+ efree(args.args);
+ }
+ channel->target = ecalloc(target_length+1, sizeof(char));
+ memcpy(channel->target, target, target_length);
+}
+
+/**
+ * Close the channel
+ */
+PHP_METHOD(Channel, close){
+ wrapped_grpc_channel *channel =
+ (wrapped_grpc_channel*)zend_object_store_get_object(getThis() TSRMLS_CC);
+ if(channel->wrapped != NULL) {
+ grpc_channel_destroy(channel->wrapped);
+ channel->wrapped = NULL;
+ }
+}
+
+static zend_function_entry channel_methods[] = {
+ PHP_ME(Channel, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
+ PHP_ME(Channel, close, NULL, ZEND_ACC_PUBLIC)
+ PHP_FE_END
+};
+
+void grpc_init_channel(TSRMLS_D){
+ zend_class_entry ce;
+ INIT_CLASS_ENTRY(ce, "Grpc\\Channel", channel_methods);
+ ce.create_object = create_wrapped_grpc_channel;
+ grpc_ce_channel = zend_register_internal_class(&ce TSRMLS_CC);
+}
diff --git a/src/php/ext/grpc/channel.h b/src/php/ext/grpc/channel.h
new file mode 100755
index 0000000..e36f130
--- /dev/null
+++ b/src/php/ext/grpc/channel.h
@@ -0,0 +1,32 @@
+#ifndef NET_GRPC_PHP_GRPC_CHANNEL_H_
+#define NET_GRPC_PHP_GRPC_CHANNEL_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "php_grpc.h"
+
+#include "grpc/grpc.h"
+
+/* Class entry for the PHP Channel class */
+zend_class_entry *grpc_ce_channel;
+
+/* Wrapper struct for grpc_channel that can be associated with a PHP object */
+typedef struct wrapped_grpc_channel {
+ zend_object std;
+
+ grpc_channel *wrapped;
+ char *target;
+} wrapped_grpc_channel;
+
+/* Initializes the Channel class */
+void grpc_init_channel(TSRMLS_D);
+
+/* Iterates through a PHP array and populates args with the contents */
+void php_grpc_read_args_array(zval *args_array, grpc_channel_args *args);
+
+#endif /* NET_GRPC_PHP_GRPC_CHANNEL_H_ */
diff --git a/src/php/ext/grpc/completion_queue.c b/src/php/ext/grpc/completion_queue.c
new file mode 100755
index 0000000..5b7bcfa
--- /dev/null
+++ b/src/php/ext/grpc/completion_queue.c
@@ -0,0 +1,145 @@
+#include "completion_queue.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 <stdbool.h>
+
+#include "grpc/grpc.h"
+
+#include "event.h"
+#include "timeval.h"
+
+/* Frees and destroys a wrapped instance of grpc_completion_queue */
+void free_wrapped_grpc_completion_queue(void *object TSRMLS_DC){
+ wrapped_grpc_completion_queue *queue = NULL;
+ grpc_event *event;
+ queue = (wrapped_grpc_completion_queue*)object;
+ if(queue->wrapped != NULL){
+ grpc_completion_queue_shutdown(queue->wrapped);
+ event = grpc_completion_queue_next(queue->wrapped, gpr_inf_future);
+ while(event != NULL){
+ if(event->type == GRPC_QUEUE_SHUTDOWN){
+ break;
+ }
+ event = grpc_completion_queue_next(queue->wrapped, gpr_inf_future);
+ }
+ grpc_completion_queue_destroy(queue->wrapped);
+ }
+ efree(queue);
+}
+
+/* Initializes an instance of wrapped_grpc_channel to be associated with an
+ * object of a class specified by class_type */
+zend_object_value create_wrapped_grpc_completion_queue(
+ zend_class_entry *class_type TSRMLS_DC){
+ zend_object_value retval;
+ wrapped_grpc_completion_queue *intern;
+
+ intern = (wrapped_grpc_completion_queue*)emalloc(
+ sizeof(wrapped_grpc_completion_queue));
+ memset(intern, 0, sizeof(wrapped_grpc_completion_queue));
+
+ 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_completion_queue,
+ NULL TSRMLS_CC);
+ retval.handlers = zend_get_std_object_handlers();
+ return retval;
+}
+
+/**
+ * Construct an instance of CompletionQueue
+ */
+PHP_METHOD(CompletionQueue, __construct){
+ wrapped_grpc_completion_queue *queue =
+ (wrapped_grpc_completion_queue*)zend_object_store_get_object(
+ getThis() TSRMLS_CC);
+ queue->wrapped = grpc_completion_queue_create();
+}
+
+/**
+ * Blocks until an event is available, the completion queue is being shutdown,
+ * or timeout is reached. Returns NULL on timeout, otherwise the event that
+ * occurred. Callers should call event.finish once they have processed the
+ * event.
+ * @param Timeval $timeout The timeout for the event
+ * @return Event The event that occurred
+ */
+PHP_METHOD(CompletionQueue, next){
+ zval *timeout;
+ /* "O" == 1 Object */
+ if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "O",
+ &timeout, grpc_ce_timeval)==FAILURE){
+ zend_throw_exception(spl_ce_InvalidArgumentException,
+ "next needs a Timeval",
+ 1 TSRMLS_CC);
+ return;
+ }
+ wrapped_grpc_completion_queue *completion_queue =
+ (wrapped_grpc_completion_queue*)zend_object_store_get_object(
+ getThis() TSRMLS_CC);
+ wrapped_grpc_timeval *wrapped_timeout =
+ (wrapped_grpc_timeval*)zend_object_store_get_object(timeout TSRMLS_CC);
+ grpc_event *event = grpc_completion_queue_next(completion_queue->wrapped,
+ wrapped_timeout->wrapped);
+ if(event == NULL){
+ RETURN_NULL();
+ }
+ zval *wrapped_event = grpc_php_wrap_event(event);
+ RETURN_DESTROY_ZVAL(wrapped_event);
+}
+
+PHP_METHOD(CompletionQueue, pluck){
+ long tag;
+ zval *timeout;
+ /* "lO" == 1 long, 1 Object */
+ if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "lO",
+ &tag,
+ &timeout, grpc_ce_timeval)==FAILURE){
+ zend_throw_exception(spl_ce_InvalidArgumentException,
+ "pluck needs a long and a Timeval",
+ 1 TSRMLS_CC);
+ }
+ wrapped_grpc_completion_queue *completion_queue =
+ (wrapped_grpc_completion_queue*)zend_object_store_get_object(
+ getThis() TSRMLS_CC);
+ wrapped_grpc_timeval *wrapped_timeout =
+ (wrapped_grpc_timeval*)zend_object_store_get_object(timeout TSRMLS_CC);
+ grpc_event *event = grpc_completion_queue_pluck(completion_queue->wrapped,
+ (void*)tag,
+ wrapped_timeout->wrapped);
+ if(event == NULL){
+ RETURN_NULL();
+ }
+ zval *wrapped_event = grpc_php_wrap_event(event);
+ RETURN_DESTROY_ZVAL(wrapped_event);
+}
+
+static zend_function_entry completion_queue_methods[] = {
+ PHP_ME(CompletionQueue, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
+ PHP_ME(CompletionQueue, next, NULL, ZEND_ACC_PUBLIC)
+ PHP_ME(CompletionQueue, pluck, NULL, ZEND_ACC_PUBLIC)
+ PHP_FE_END
+};
+
+void grpc_init_completion_queue(TSRMLS_D){
+ zend_class_entry ce;
+ INIT_CLASS_ENTRY(ce, "Grpc\\CompletionQueue", completion_queue_methods);
+ ce.create_object = create_wrapped_grpc_completion_queue;
+ grpc_ce_completion_queue = zend_register_internal_class(&ce TSRMLS_CC);
+}
diff --git a/src/php/ext/grpc/completion_queue.h b/src/php/ext/grpc/completion_queue.h
new file mode 100755
index 0000000..6bf5b16
--- /dev/null
+++ b/src/php/ext/grpc/completion_queue.h
@@ -0,0 +1,29 @@
+#ifndef NET_GRPC_PHP_GRPC_COMPLETION_QUEUE_H_
+#define NET_GRPC_PHP_GRPC_COMPLETION_QUEUE_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "php_grpc.h"
+
+#include "grpc/grpc.h"
+
+/* Class entry for the PHP CompletionQueue class */
+zend_class_entry *grpc_ce_completion_queue;
+
+/* Wrapper class for grpc_completion_queue that can be associated with a
+ PHP object */
+typedef struct wrapped_grpc_completion_queue {
+ zend_object std;
+
+ grpc_completion_queue *wrapped;
+} wrapped_grpc_completion_queue;
+
+/* Initialize the CompletionQueue class */
+void grpc_init_completion_queue(TSRMLS_D);
+
+#endif /* NET_GRPC_PHP_GRPC_COMPLETION_QUEUE_H_ */
diff --git a/src/php/ext/grpc/config.m4 b/src/php/ext/grpc/config.m4
new file mode 100755
index 0000000..2d0db1a
--- /dev/null
+++ b/src/php/ext/grpc/config.m4
@@ -0,0 +1,74 @@
+PHP_ARG_ENABLE(grpc, whether to enable grpc support,
+[ --enable-grpc Enable grpc support])
+
+if test "$PHP_GRPC" != "no"; then
+ dnl Write more examples of tests here...
+
+ dnl # --with-grpc -> check with-path
+ SEARCH_PATH="/usr/local /usr $HOME/grpc_dev" # you might want to change this
+ SEARCH_FOR="include/grpc/grpc.h" # you most likely want to change this
+ if test -r $PHP_GRPC/$SEARCH_FOR; then # path given as parameter
+ GRPC_DIR=$PHP_GRPC
+ else # search default path list
+ AC_MSG_CHECKING([for grpc files in default path])
+ for i in $SEARCH_PATH ; do
+ if test -r $i/$SEARCH_FOR; then
+ GRPC_DIR=$i
+ AC_MSG_RESULT(found in $i)
+ fi
+ done
+ fi
+ if test -z "$GRPC_DIR"; then
+ AC_MSG_RESULT([not found])
+ AC_MSG_ERROR([Please reinstall the grpc distribution])
+ fi
+
+ dnl # --with-grpc -> add include path
+ PHP_ADD_INCLUDE($GRPC_DIR/include)
+
+ LIBS="-lpthread $LIBS"
+
+ dnl PHP_ADD_LIBRARY(pthread,,GRPC_SHARED_LIBADD)
+ GRPC_SHARED_LIBADD="-lpthread $GRPC_SHARED_LIBADD"
+ PHP_ADD_LIBRARY(pthread)
+
+ PHP_ADD_LIBRARY(rt,,GRPC_SHARED_LIBADD)
+ PHP_ADD_LIBRARY(rt)
+
+ PHP_ADD_LIBPATH($GRPC_DIR/lib)
+
+ PHP_CHECK_LIBRARY(gpr,gpr_now,
+ [
+ PHP_ADD_LIBRARY(gpr,,GRPC_SHARED_LIBADD)
+ PHP_ADD_LIBRARY(gpr)
+ AC_DEFINE(HAVE_GPRLIB,1,[ ])
+ ],[
+ AC_MSG_ERROR([wrong gpr lib version or lib not found])
+ ],[
+ -L$GRPC_DIR/lib
+ ])
+
+ PHP_ADD_LIBRARY(event,,GRPC_SHARED_LIBADD)
+ PHP_ADD_LIBRARY(event)
+
+ PHP_ADD_LIBRARY(event_pthreads,,GRPC_SHARED_LIBADD)
+ PHP_ADD_LIBRARY(event_pthreads)
+
+ PHP_ADD_LIBRARY(event_core,,GRPC_SHARED_LIBADD)
+ PHP_ADD_LIBRARY(event_core)
+
+ PHP_CHECK_LIBRARY(grpc,grpc_channel_destroy,
+ [
+ PHP_ADD_LIBRARY(grpc,,GRPC_SHARED_LIBADD)
+ dnl PHP_ADD_LIBRARY_WITH_PATH(grpc, $GRPC_DIR/lib, GRPC_SHARED_LIBADD)
+ AC_DEFINE(HAVE_GRPCLIB,1,[ ])
+ ],[
+ AC_MSG_ERROR([wrong grpc lib version or lib not found])
+ ],[
+ -L$GRPC_DIR/lib
+ ])
+
+ PHP_SUBST(GRPC_SHARED_LIBADD)
+
+ PHP_NEW_EXTENSION(grpc, byte_buffer.c call.c channel.c completion_queue.c credentials.c event.c timeval.c server.c server_credentials.c php_grpc.c, $ext_shared, , -Wall -Werror -pedantic -std=c99)
+fi
diff --git a/src/php/ext/grpc/credentials.c b/src/php/ext/grpc/credentials.c
new file mode 100755
index 0000000..ffafdda
--- /dev/null
+++ b/src/php/ext/grpc/credentials.c
@@ -0,0 +1,171 @@
+#include "credentials.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 "grpc/grpc.h"
+#include "grpc/grpc_security.h"
+
+/* Frees and destroys an instance of wrapped_grpc_credentials */
+void free_wrapped_grpc_credentials(void *object TSRMLS_DC){
+ wrapped_grpc_credentials *creds = (wrapped_grpc_credentials*)object;
+ if(creds->wrapped != NULL) {
+ grpc_credentials_release(creds->wrapped);
+ }
+ efree(creds);
+}
+
+/* Initializes an instance of wrapped_grpc_credentials to be associated with an
+ * object of a class specified by class_type */
+zend_object_value create_wrapped_grpc_credentials(
+ zend_class_entry *class_type TSRMLS_DC){
+ zend_object_value retval;
+ wrapped_grpc_credentials *intern;
+
+ intern = (wrapped_grpc_credentials*)emalloc(sizeof(wrapped_grpc_credentials));
+ memset(intern, 0, sizeof(wrapped_grpc_credentials));
+
+ 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_credentials,
+ NULL TSRMLS_CC);
+ retval.handlers = zend_get_std_object_handlers();
+ return retval;
+}
+
+zval *grpc_php_wrap_credentials(grpc_credentials *wrapped){
+ zval *credentials_object;
+ MAKE_STD_ZVAL(credentials_object);
+ object_init_ex(credentials_object, grpc_ce_credentials);
+ wrapped_grpc_credentials *credentials =
+ (wrapped_grpc_credentials*)zend_object_store_get_object(
+ credentials_object TSRMLS_CC);
+ credentials->wrapped = wrapped;
+ return credentials_object;
+}
+
+/**
+ * Create a default credentials object.
+ * @return Credentials The new default credentials object
+ */
+PHP_METHOD(Credentials, createDefault){
+ grpc_credentials *creds = grpc_default_credentials_create();
+ zval *creds_object = grpc_php_wrap_credentials(creds);
+ RETURN_DESTROY_ZVAL(creds_object);
+}
+
+/**
+ * Create SSL credentials.
+ * @param string pem_root_certs PEM encoding of the server root certificates
+ * @param string pem_private_key PEM encoding of the client's private key
+ * (optional)
+ * @param string pem_cert_chain PEM encoding of the client's certificate chain
+ * (optional)
+ * @return Credentials The new SSL credentials object
+ */
+PHP_METHOD(Credentials, createSsl){
+ char *pem_root_certs;
+ char *pem_private_key = NULL;
+ char *pem_cert_chain = NULL;
+
+ int root_certs_length, private_key_length = 0, cert_chain_length = 0;
+
+ /* "s|s!s! == 1 string, 2 optional nullable strings */
+ if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "s|s!s!",
+ &pem_root_certs, &root_certs_length,
+ &pem_private_key, &private_key_length,
+ &pem_cert_chain, &cert_chain_length) == FAILURE) {
+ zend_throw_exception(spl_ce_InvalidArgumentException,
+ "createSsl expects 1 to 3 strings",
+ 1 TSRMLS_CC);
+ return;
+ }
+ grpc_credentials *creds = grpc_ssl_credentials_create(
+ (unsigned char*)pem_root_certs, (size_t)root_certs_length,
+ (unsigned char*)pem_private_key, (size_t)private_key_length,
+ (unsigned char*)pem_cert_chain, (size_t)cert_chain_length);
+ zval *creds_object = grpc_php_wrap_credentials(creds);
+ RETURN_DESTROY_ZVAL(creds_object);
+}
+
+/**
+ * Create composite credentials from two existing credentials.
+ * @param Credentials cred1 The first credential
+ * @param Credentials cred2 The second credential
+ * @return Credentials The new composite credentials object
+ */
+PHP_METHOD(Credentials, createComposite){
+ zval *cred1_obj;
+ zval *cred2_obj;
+
+ /* "OO" == 3 Objects */
+ if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "OO",
+ &cred1_obj, grpc_ce_credentials,
+ &cred2_obj, grpc_ce_credentials) == FAILURE) {
+ zend_throw_exception(spl_ce_InvalidArgumentException,
+ "createComposite expects 2 Credentials",
+ 1 TSRMLS_CC);
+ return;
+ }
+ wrapped_grpc_credentials *cred1 =
+ (wrapped_grpc_credentials*)zend_object_store_get_object(
+ cred1_obj TSRMLS_CC);
+ wrapped_grpc_credentials *cred2 =
+ (wrapped_grpc_credentials*)zend_object_store_get_object(
+ cred2_obj TSRMLS_CC);
+ grpc_credentials *creds = grpc_composite_credentials_create(cred1->wrapped,
+ cred2->wrapped);
+ zval *creds_object = grpc_php_wrap_credentials(creds);
+ RETURN_DESTROY_ZVAL(creds_object);
+}
+
+/**
+ * Create Google Compute Engine credentials
+ * @return Credentials The new GCE credentials object
+ */
+PHP_METHOD(Credentials, createGce) {
+ grpc_credentials *creds = grpc_compute_engine_credentials_create();
+ zval *creds_object = grpc_php_wrap_credentials(creds);
+ RETURN_DESTROY_ZVAL(creds_object);
+}
+
+/**
+ * Create fake credentials. Only to be used for testing.
+ * @return Credentials The new fake credentials object
+ */
+PHP_METHOD(Credentials, createFake) {
+ grpc_credentials *creds = grpc_fake_transport_security_credentials_create();
+ zval *creds_object = grpc_php_wrap_credentials(creds);
+ RETURN_DESTROY_ZVAL(creds_object);
+}
+
+static zend_function_entry credentials_methods[] = {
+ PHP_ME(Credentials, createDefault, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ PHP_ME(Credentials, createSsl, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ PHP_ME(Credentials, createComposite, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ PHP_ME(Credentials, createGce, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ PHP_ME(Credentials, createFake, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ PHP_FE_END
+};
+
+void grpc_init_credentials(TSRMLS_D){
+ zend_class_entry ce;
+ INIT_CLASS_ENTRY(ce, "Grpc\\Credentials", credentials_methods);
+ ce.create_object = create_wrapped_grpc_credentials;
+ grpc_ce_credentials = zend_register_internal_class(&ce TSRMLS_CC);
+}
diff --git a/src/php/ext/grpc/credentials.h b/src/php/ext/grpc/credentials.h
new file mode 100755
index 0000000..ba2aa89
--- /dev/null
+++ b/src/php/ext/grpc/credentials.h
@@ -0,0 +1,30 @@
+#ifndef NET_GRPC_PHP_GRPC_CREDENTIALS_H_
+#define NET_GRPC_PHP_GRPC_CREDENTIALS_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "php_grpc.h"
+
+#include "grpc/grpc.h"
+#include "grpc/grpc_security.h"
+
+/* Class entry for the Credentials PHP class */
+zend_class_entry *grpc_ce_credentials;
+
+/* Wrapper struct for grpc_credentials that can be associated with a PHP
+ * object */
+typedef struct wrapped_grpc_credentials {
+ zend_object std;
+
+ grpc_credentials *wrapped;
+} wrapped_grpc_credentials;
+
+/* Initializes the Credentials PHP class */
+void grpc_init_credentials(TSRMLS_D);
+
+#endif /* NET_GRPC_PHP_GRPC_CREDENTIALS_H_ */
diff --git a/src/php/ext/grpc/event.c b/src/php/ext/grpc/event.c
new file mode 100755
index 0000000..8ec29c9
--- /dev/null
+++ b/src/php/ext/grpc/event.c
@@ -0,0 +1,191 @@
+#include "event.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "php_grpc.h"
+
+#include <stdbool.h>
+
+#include "grpc/grpc.h"
+
+#include "byte_buffer.h"
+#include "call.h"
+#include "timeval.h"
+
+/* Frees and finishes a wrapped instance of grpc_event */
+void free_wrapped_grpc_event(void *object TSRMLS_DC){
+ wrapped_grpc_event *event = (wrapped_grpc_event*)object;
+ if(event->wrapped != NULL){
+ grpc_event_finish(event->wrapped);
+ }
+ efree(event);
+}
+
+/* Initializes an instance of wrapped_grpc_channel to be associated with an
+ * object of a class specified by class_type */
+zend_object_value create_wrapped_grpc_event(
+ zend_class_entry *class_type TSRMLS_DC){
+ zend_object_value retval;
+ wrapped_grpc_event *intern;
+ intern = (wrapped_grpc_event*)emalloc(sizeof(wrapped_grpc_event));
+ memset(intern, 0, sizeof(wrapped_grpc_event));
+ 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_event,
+ NULL TSRMLS_CC);
+ retval.handlers = zend_get_std_object_handlers();
+ return retval;
+}
+
+zval *grpc_php_wrap_event(grpc_event *wrapped){
+ zval *event_object;
+ MAKE_STD_ZVAL(event_object);
+
+ object_init_ex(event_object, grpc_ce_event);
+ wrapped_grpc_event *event = (wrapped_grpc_event*)zend_object_store_get_object(
+ event_object TSRMLS_CC);
+ event->wrapped = wrapped;
+ return event_object;
+}
+
+/**
+ * Get the type of the event
+ * @return long Integer representing the type
+ */
+PHP_METHOD(Event, get_type){
+ wrapped_grpc_event *event = (wrapped_grpc_event*)zend_object_store_get_object(
+ getThis() TSRMLS_CC);
+ RETURN_LONG((long)(event->wrapped->type));
+}
+
+/**
+ * Get the tag of the event
+ * @return long The event's tag
+ */
+PHP_METHOD(Event, get_tag){
+ wrapped_grpc_event *event = (wrapped_grpc_event*)zend_object_store_get_object(
+ getThis() TSRMLS_CC);
+ RETURN_LONG((long)(event->wrapped->tag));
+}
+
+/**
+ * Get the call associated with the event
+ * @return Call The call
+ */
+PHP_METHOD(Event, get_call){
+ wrapped_grpc_event *event = (wrapped_grpc_event*)zend_object_store_get_object(
+ getThis() TSRMLS_CC);
+ zval *call_obj = grpc_php_wrap_call(event->wrapped->call);
+ RETURN_DESTROY_ZVAL(call_obj);
+}
+
+/**
+ * Get the data associated with the event
+ * @return object The data, with type depending on the type field
+ */
+PHP_METHOD(Event, get_data){
+ zval *retval;
+ wrapped_grpc_event *wrapped_event =
+ (wrapped_grpc_event*)zend_object_store_get_object(
+ getThis() TSRMLS_CC);
+ grpc_event *event = wrapped_event->wrapped;
+ char *detail_string;
+ size_t detail_len;
+ char *method_string;
+ size_t method_len;
+ char *host_string;
+ size_t host_len;
+ char *read_string;
+ size_t read_len;
+
+ switch(event->type){
+ case GRPC_QUEUE_SHUTDOWN: RETURN_NULL(); break;
+ case GRPC_READ:
+ if(event->data.read == NULL){
+ RETURN_NULL();
+ } else {
+ byte_buffer_to_string(event->data.read, &read_string, &read_len);
+ RETURN_STRINGL(read_string, read_len, true);
+ }
+ break;
+ case GRPC_INVOKE_ACCEPTED:
+ RETURN_LONG((long)event->data.invoke_accepted); break;
+ case GRPC_WRITE_ACCEPTED:
+ RETURN_LONG((long)event->data.write_accepted); break;
+ case GRPC_FINISH_ACCEPTED:
+ RETURN_LONG((long)event->data.finish_accepted); break;
+ case GRPC_CLIENT_METADATA_READ:
+ retval = grpc_call_create_metadata_array(
+ event->data.client_metadata_read.count,
+ event->data.client_metadata_read.elements);
+ break;
+ case GRPC_FINISHED:
+ MAKE_STD_ZVAL(retval);
+ object_init(retval);
+ add_property_long(retval, "code", event->data.finished.code);
+ if(event->data.finished.details == NULL){
+ add_property_null(retval, "details");
+ } else {
+ detail_len = strlen(event->data.finished.details);
+ detail_string = ecalloc(detail_len+1, sizeof(char));
+ memcpy(detail_string, event->data.finished.details, detail_len);
+ add_property_string(retval,
+ "details",
+ detail_string,
+ true);
+ }
+ break;
+ case GRPC_SERVER_RPC_NEW:
+ MAKE_STD_ZVAL(retval);
+ object_init(retval);
+ method_len = strlen(event->data.server_rpc_new.method);
+ method_string = ecalloc(method_len+1, sizeof(char));
+ memcpy(method_string, event->data.server_rpc_new.method, method_len);
+ add_property_string(retval,
+ "method",
+ method_string,
+ false);
+ host_len = strlen(event->data.server_rpc_new.host);
+ host_string = ecalloc(host_len+1, sizeof(char));
+ memcpy(host_string, event->data.server_rpc_new.host, host_len);
+ add_property_string(retval,
+ "host",
+ host_string,
+ false);
+ add_property_zval(retval,
+ "absolute_timeout",
+ grpc_php_wrap_timeval(
+ event->data.server_rpc_new.deadline));
+ add_property_zval(retval,
+ "metadata",
+ grpc_call_create_metadata_array(
+ event->data.server_rpc_new.metadata_count,
+ event->data.server_rpc_new.metadata_elements));
+ break;
+ default: RETURN_NULL(); break;
+ }
+ RETURN_DESTROY_ZVAL(retval);
+}
+
+static zend_function_entry event_methods[] = {
+ PHP_ME(Event, get_call, NULL, ZEND_ACC_PUBLIC)
+ PHP_ME(Event, get_data, NULL, ZEND_ACC_PUBLIC)
+ PHP_ME(Event, get_tag, NULL, ZEND_ACC_PUBLIC)
+ PHP_ME(Event, get_type, NULL, ZEND_ACC_PUBLIC)
+ PHP_FE_END
+};
+
+void grpc_init_event(TSRMLS_D){
+ zend_class_entry ce;
+ INIT_CLASS_ENTRY(ce, "Grpc\\Event", event_methods);
+ ce.create_object = create_wrapped_grpc_event;
+ grpc_ce_event = zend_register_internal_class(&ce TSRMLS_CC);
+}
diff --git a/src/php/ext/grpc/event.h b/src/php/ext/grpc/event.h
new file mode 100755
index 0000000..9dc164e
--- /dev/null
+++ b/src/php/ext/grpc/event.h
@@ -0,0 +1,31 @@
+#ifndef NET_GRPC_PHP_GRPC_EVENT_H_
+#define NET_GRPC_PHP_GRPC_EVENT_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "php_grpc.h"
+
+#include "grpc/grpc.h"
+
+/* Class entry for the PHP Event class */
+zend_class_entry *grpc_ce_event;
+
+/* Struct wrapping grpc_event that can be associated with a PHP object */
+typedef struct wrapped_grpc_event {
+ zend_object std;
+
+ grpc_event *wrapped;
+} wrapped_grpc_event;
+
+/* Initialize the Event class */
+void grpc_init_event(TSRMLS_D);
+
+/* Create a new Event object that wraps an existing grpc_event struct */
+zval *grpc_php_wrap_event(grpc_event *wrapped);
+
+#endif /* NET_GRPC_PHP_GRPC_COMPLETION_CHANNEL_H */
diff --git a/src/php/ext/grpc/php_grpc.c b/src/php/ext/grpc/php_grpc.c
new file mode 100755
index 0000000..c49b845
--- /dev/null
+++ b/src/php/ext/grpc/php_grpc.c
@@ -0,0 +1,247 @@
+#include "call.h"
+#include "channel.h"
+#include "server.h"
+#include "completion_queue.h"
+#include "event.h"
+#include "timeval.h"
+#include "credentials.h"
+#include "server_credentials.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "php_grpc.h"
+
+//ZEND_DECLARE_MODULE_GLOBALS(grpc)
+
+/* {{{ grpc_functions[]
+ *
+ * Every user visible function must have an entry in grpc_functions[].
+ */
+const zend_function_entry grpc_functions[] = {
+ PHP_FE_END /* Must be the last line in grpc_functions[] */
+};
+/* }}} */
+
+/* {{{ grpc_module_entry
+ */
+zend_module_entry grpc_module_entry = {
+#if ZEND_MODULE_API_NO >= 20010901
+ STANDARD_MODULE_HEADER,
+#endif
+ "grpc",
+ grpc_functions,
+ PHP_MINIT(grpc),
+ PHP_MSHUTDOWN(grpc),
+ NULL,
+ NULL,
+ PHP_MINFO(grpc),
+#if ZEND_MODULE_API_NO >= 20010901
+ PHP_GRPC_VERSION,
+#endif
+ STANDARD_MODULE_PROPERTIES
+};
+/* }}} */
+
+#ifdef COMPILE_DL_GRPC
+ZEND_GET_MODULE(grpc)
+#endif
+
+/* {{{ PHP_INI
+ */
+/* Remove comments and fill if you need to have entries in php.ini
+PHP_INI_BEGIN()
+ STD_PHP_INI_ENTRY("grpc.global_value", "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_grpc_globals, grpc_globals)
+ STD_PHP_INI_ENTRY("grpc.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_grpc_globals, grpc_globals)
+PHP_INI_END()
+*/
+/* }}} */
+
+/* {{{ php_grpc_init_globals
+ */
+/* Uncomment this function if you have INI entries
+static void php_grpc_init_globals(zend_grpc_globals *grpc_globals)
+{
+ grpc_globals->global_value = 0;
+ grpc_globals->global_string = NULL;
+}
+*/
+/* }}} */
+
+/* {{{ PHP_MINIT_FUNCTION
+ */
+PHP_MINIT_FUNCTION(grpc)
+{
+ /* If you have INI entries, uncomment these lines
+ REGISTER_INI_ENTRIES();
+ */
+ /* Register call error constants */
+ grpc_init();
+ REGISTER_LONG_CONSTANT("Grpc\\CALL_OK", GRPC_CALL_OK, CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR", GRPC_CALL_ERROR, CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_ON_SERVER",
+ GRPC_CALL_ERROR_NOT_ON_SERVER,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_ON_CLIENT",
+ GRPC_CALL_ERROR_NOT_ON_CLIENT,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_ALREADY_INVOKED",
+ GRPC_CALL_ERROR_ALREADY_INVOKED,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_INVOKED",
+ GRPC_CALL_ERROR_NOT_INVOKED,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_ALREADY_FINISHED",
+ GRPC_CALL_ERROR_ALREADY_FINISHED,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_TOO_MANY_OPERATIONS",
+ GRPC_CALL_ERROR_TOO_MANY_OPERATIONS,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_INVALID_FLAGS",
+ GRPC_CALL_ERROR_INVALID_FLAGS,
+ CONST_CS);
+
+ /* Register op error constants */
+ REGISTER_LONG_CONSTANT("Grpc\\OP_OK", GRPC_OP_OK, CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\OP_ERROR", GRPC_OP_ERROR, CONST_CS);
+
+ /* Register flag constants */
+ REGISTER_LONG_CONSTANT("Grpc\\WRITE_BUFFER_HINT",
+ GRPC_WRITE_BUFFER_HINT,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\WRITE_NO_COMPRESS",
+ GRPC_WRITE_NO_COMPRESS,
+ CONST_CS);
+
+ /* Register completion type constants */
+ REGISTER_LONG_CONSTANT("Grpc\\QUEUE_SHUTDOWN",
+ GRPC_QUEUE_SHUTDOWN,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\READ", GRPC_READ, CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\INVOKE_ACCEPTED",
+ GRPC_INVOKE_ACCEPTED,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\WRITE_ACCEPTED",
+ GRPC_WRITE_ACCEPTED,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\FINISH_ACCEPTED",
+ GRPC_FINISH_ACCEPTED,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\CLIENT_METADATA_READ",
+ GRPC_CLIENT_METADATA_READ,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\FINISHED", GRPC_FINISHED, CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\SERVER_RPC_NEW",
+ GRPC_SERVER_RPC_NEW,
+ CONST_CS);
+
+ /* Register status constants */
+ REGISTER_LONG_CONSTANT("Grpc\\STATUS_OK",
+ GRPC_STATUS_OK,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\STATUS_CANCELLED",
+ GRPC_STATUS_CANCELLED,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNKNOWN",
+ GRPC_STATUS_UNKNOWN,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\STATUS_INVALID_ARGUMENT",
+ GRPC_STATUS_INVALID_ARGUMENT,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\STATUS_DEADLINE_EXCEEDED",
+ GRPC_STATUS_DEADLINE_EXCEEDED,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\STATUS_NOT_FOUND",
+ GRPC_STATUS_NOT_FOUND,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\STATUS_ALREADY_EXISTS",
+ GRPC_STATUS_ALREADY_EXISTS,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\STATUS_PERMISSION_DENIED",
+ GRPC_STATUS_PERMISSION_DENIED,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNAUTHENTICATED",
+ GRPC_STATUS_UNAUTHENTICATED,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\STATUS_RESOURCE_EXHAUSTED",
+ GRPC_STATUS_RESOURCE_EXHAUSTED,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\STATUS_FAILED_PRECONDITION",
+ GRPC_STATUS_FAILED_PRECONDITION,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\STATUS_ABORTED",
+ GRPC_STATUS_ABORTED,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\STATUS_OUT_OF_RANGE",
+ GRPC_STATUS_OUT_OF_RANGE,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNIMPLEMENTED",
+ GRPC_STATUS_UNIMPLEMENTED,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\STATUS_INTERNAL",
+ GRPC_STATUS_INTERNAL,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNAVAILABLE",
+ GRPC_STATUS_UNAVAILABLE,
+ CONST_CS);
+ REGISTER_LONG_CONSTANT("Grpc\\STATUS_DATA_LOSS",
+ GRPC_STATUS_DATA_LOSS,
+ CONST_CS);
+
+ grpc_init_call(TSRMLS_C);
+ grpc_init_channel(TSRMLS_C);
+ grpc_init_server(TSRMLS_C);
+ grpc_init_completion_queue(TSRMLS_C);
+ grpc_init_event(TSRMLS_C);
+ grpc_init_timeval(TSRMLS_C);
+ grpc_init_credentials(TSRMLS_C);
+ grpc_init_server_credentials(TSRMLS_C);
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MSHUTDOWN_FUNCTION
+ */
+PHP_MSHUTDOWN_FUNCTION(grpc)
+{
+ /* uncomment this line if you have INI entries
+ UNREGISTER_INI_ENTRIES();
+ */
+ grpc_shutdown_timeval(TSRMLS_C);
+ grpc_shutdown();
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MINFO_FUNCTION
+ */
+PHP_MINFO_FUNCTION(grpc)
+{
+ php_info_print_table_start();
+ php_info_print_table_header(2, "grpc support", "enabled");
+ php_info_print_table_end();
+
+ /* Remove comments if you have entries in php.ini
+ DISPLAY_INI_ENTRIES();
+ */
+}
+/* }}} */
+/* The previous line is meant for vim and emacs, so it can correctly fold and
+ unfold functions in source code. See the corresponding marks just before
+ function definition, where the functions purpose is also documented. Please
+ follow this convention for the convenience of others editing your code.
+*/
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/src/php/ext/grpc/php_grpc.h b/src/php/ext/grpc/php_grpc.h
new file mode 100755
index 0000000..777e0c4
--- /dev/null
+++ b/src/php/ext/grpc/php_grpc.h
@@ -0,0 +1,66 @@
+
+#ifndef PHP_GRPC_H
+#define PHP_GRPC_H
+
+#include <stdbool.h>
+
+extern zend_module_entry grpc_module_entry;
+#define phpext_grpc_ptr &grpc_module_entry
+
+#define PHP_GRPC_VERSION "0.1.0" /* Replace with version number for your extension */
+
+#ifdef PHP_WIN32
+# define PHP_GRPC_API __declspec(dllexport)
+#elif defined(__GNUC__) && __GNUC__ >= 4
+# define PHP_GRPC_API __attribute__ ((visibility("default")))
+#else
+# define PHP_GRPC_API
+#endif
+
+#ifdef ZTS
+#include "TSRM.h"
+#endif
+
+#include "php.h"
+
+#include "grpc/grpc.h"
+
+#define RETURN_DESTROY_ZVAL(val) \
+ RETURN_ZVAL( \
+ val, \
+ false /* Don't execute copy constructor */, \
+ true /* Dealloc original before returning */)
+
+/* These are all function declarations */
+/* Code that runs at module initialization */
+PHP_MINIT_FUNCTION(grpc);
+/* Code that runs at module shutdown */
+PHP_MSHUTDOWN_FUNCTION(grpc);
+/* Displays information about the module */
+PHP_MINFO_FUNCTION(grpc);
+
+/*
+ Declare any global variables you may need between the BEGIN
+ and END macros here:
+
+ZEND_BEGIN_MODULE_GLOBALS(grpc)
+ZEND_END_MODULE_GLOBALS(grpc)
+*/
+
+/* In every utility function you add that needs to use variables
+ in php_grpc_globals, call TSRMLS_FETCH(); after declaring other
+ variables used by that function, or better yet, pass in TSRMLS_CC
+ after the last function argument and declare your utility function
+ with TSRMLS_DC after the last declared argument. Always refer to
+ the globals in your function as GRPC_G(variable). You are
+ encouraged to rename these macros something shorter, see
+ examples in any other php module directory.
+*/
+
+#ifdef ZTS
+#define GRPC_G(v) TSRMG(grpc_globals_id, zend_grpc_globals *, v)
+#else
+#define GRPC_G(v) (grpc_globals.v)
+#endif
+
+#endif /* PHP_GRPC_H */
diff --git a/src/php/ext/grpc/server.c b/src/php/ext/grpc/server.c
new file mode 100755
index 0000000..7e98713
--- /dev/null
+++ b/src/php/ext/grpc/server.c
@@ -0,0 +1,202 @@
+#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 <stdbool.h>
+
+#include "grpc/grpc.h"
+#include "grpc/support/log.h"
+#include "grpc/grpc_security.h"
+
+#include "server.h"
+#include "completion_queue.h"
+#include "channel.h"
+#include "server_credentials.h"
+
+/* Frees and destroys an instance of wrapped_grpc_server */
+void free_wrapped_grpc_server(void *object TSRMLS_DC){
+ wrapped_grpc_server *server = (wrapped_grpc_server*)object;
+ if(server->wrapped != NULL){
+ grpc_server_shutdown(server->wrapped);
+ grpc_server_destroy(server->wrapped);
+ }
+ efree(server);
+}
+
+/* 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_server(
+ zend_class_entry *class_type TSRMLS_DC){
+ zend_object_value retval;
+ wrapped_grpc_server *intern;
+
+ intern = (wrapped_grpc_server*)emalloc(sizeof(wrapped_grpc_server));
+ memset(intern, 0, sizeof(wrapped_grpc_server));
+
+ 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_server,
+ NULL TSRMLS_CC);
+ retval.handlers = zend_get_std_object_handlers();
+ return retval;
+}
+
+/**
+ * Constructs a new instance of the Server class
+ * @param CompletionQueue $queue The completion queue to use with the server
+ * @param array $args The arguments to pass to the server (optional)
+ */
+PHP_METHOD(Server, __construct){
+ wrapped_grpc_server *server =
+ (wrapped_grpc_server*)zend_object_store_get_object(getThis() TSRMLS_CC);
+ zval *queue_obj;
+ zval *args_array = NULL;
+ grpc_channel_args args;
+ HashTable *array_hash;
+ zval **creds_obj = NULL;
+ wrapped_grpc_server_credentials *creds = NULL;
+ /* "O|a" == 1 Object, 1 optional array */
+ if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "O|a",
+ &queue_obj, grpc_ce_completion_queue,
+ &args_array) == FAILURE){
+ zend_throw_exception(spl_ce_InvalidArgumentException,
+ "Server expects a CompletionQueue and an array",
+ 1 TSRMLS_CC);
+ return;
+ }
+ add_property_zval(getThis(), "completion_queue", queue_obj);
+ wrapped_grpc_completion_queue *queue =
+ (wrapped_grpc_completion_queue*)zend_object_store_get_object(
+ queue_obj TSRMLS_CC);
+ if (args_array == NULL) {
+ server->wrapped = grpc_server_create(queue->wrapped, NULL);
+ } else {
+ array_hash = Z_ARRVAL_P(args_array);
+ if(zend_hash_find(array_hash,
+ "credentials",
+ sizeof("credentials"),
+ (void**)&creds_obj) == SUCCESS) {
+ if(zend_get_class_entry(*creds_obj TSRMLS_CC) !=
+ grpc_ce_server_credentials) {
+ zend_throw_exception(spl_ce_InvalidArgumentException,
+ "credentials must be a ServerCredentials object",
+ 1 TSRMLS_CC);
+ return;
+ }
+ creds = (wrapped_grpc_server_credentials*)zend_object_store_get_object(
+ *creds_obj TSRMLS_CC);
+ zend_hash_del(array_hash, "credentials", sizeof("credentials"));
+ }
+ php_grpc_read_args_array(args_array, &args);
+ if (creds == NULL) {
+ server->wrapped = grpc_server_create(queue->wrapped, &args);
+ } else {
+ gpr_log(GPR_DEBUG, "Initialized secure server");
+ server->wrapped = grpc_secure_server_create(creds->wrapped,
+ queue->wrapped,
+ &args);
+ }
+ efree(args.args);
+ }
+}
+
+/**
+ * Request a call on a server. Creates a single GRPC_SERVER_RPC_NEW event.
+ * @param long $tag_new The tag to associate with the new request
+ * @param long $tag_cancel The tag to use if the call is cancelled
+ * @return Void
+ */
+PHP_METHOD(Server, request_call){
+ wrapped_grpc_server *server =
+ (wrapped_grpc_server*)zend_object_store_get_object(getThis() TSRMLS_CC);
+ long tag_new;
+ /* "l" == 1 long */
+ if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "l",
+ &tag_new) == FAILURE){
+ zend_throw_exception(spl_ce_InvalidArgumentException,
+ "request_call expects a long",
+ 1 TSRMLS_CC);
+ return;
+ }
+ grpc_server_request_call(server->wrapped, (void*)tag_new);
+}
+
+/**
+ * Add a http2 over tcp listener.
+ * @param string $addr The address to add
+ * @return true on success, false on failure
+ */
+PHP_METHOD(Server, add_http2_port){
+ wrapped_grpc_server *server =
+ (wrapped_grpc_server*)zend_object_store_get_object(getThis() TSRMLS_CC);
+ const char *addr;
+ int addr_len;
+ /* "s" == 1 string */
+ if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "s",
+ &addr, &addr_len) == FAILURE){
+ zend_throw_exception(spl_ce_InvalidArgumentException,
+ "add_http2_port expects a string",
+ 1 TSRMLS_CC);
+ return;
+ }
+ RETURN_BOOL(grpc_server_add_http2_port(server->wrapped, addr));
+}
+
+PHP_METHOD(Server, add_secure_http2_port){
+ wrapped_grpc_server *server =
+ (wrapped_grpc_server*)zend_object_store_get_object(getThis() TSRMLS_CC);
+ const char *addr;
+ int addr_len;
+ /* "s" == 1 string */
+ if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "s",
+ &addr, &addr_len) == FAILURE){
+ zend_throw_exception(spl_ce_InvalidArgumentException,
+ "add_http2_port expects a string",
+ 1 TSRMLS_CC);
+ return;
+ }
+ RETURN_BOOL(grpc_server_add_secure_http2_port(server->wrapped, addr));
+}
+
+/**
+ * Start a server - tells all listeners to start listening
+ * @return Void
+ */
+PHP_METHOD(Server, start){
+ wrapped_grpc_server *server =
+ (wrapped_grpc_server*)zend_object_store_get_object(getThis() TSRMLS_CC);
+ grpc_server_start(server->wrapped);
+}
+
+static zend_function_entry server_methods[] = {
+ PHP_ME(Server, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
+ PHP_ME(Server, request_call, NULL, ZEND_ACC_PUBLIC)
+ PHP_ME(Server, add_http2_port, NULL, ZEND_ACC_PUBLIC)
+ PHP_ME(Server, add_secure_http2_port, NULL, ZEND_ACC_PUBLIC)
+ PHP_ME(Server, start, NULL, ZEND_ACC_PUBLIC)
+ PHP_FE_END
+};
+
+void grpc_init_server(TSRMLS_D){
+ zend_class_entry ce;
+ INIT_CLASS_ENTRY(ce, "Grpc\\Server", server_methods);
+ ce.create_object = create_wrapped_grpc_server;
+ grpc_ce_server = zend_register_internal_class(&ce TSRMLS_CC);
+}
diff --git a/src/php/ext/grpc/server.h b/src/php/ext/grpc/server.h
new file mode 100755
index 0000000..61ed825
--- /dev/null
+++ b/src/php/ext/grpc/server.h
@@ -0,0 +1,28 @@
+#ifndef NET_GRPC_PHP_GRPC_SERVER_H_
+#define NET_GRPC_PHP_GRPC_SERVER_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "php_grpc.h"
+
+#include "grpc/grpc.h"
+
+/* Class entry for the Server PHP class */
+zend_class_entry *grpc_ce_server;
+
+/* Wrapper struct for grpc_server that can be associated with a PHP object */
+typedef struct wrapped_grpc_server {
+ zend_object std;
+
+ grpc_server *wrapped;
+} wrapped_grpc_server;
+
+/* Initializes the Server class */
+void grpc_init_server(TSRMLS_D);
+
+#endif /* NET_GRPC_PHP_GRPC_SERVER_H_ */
diff --git a/src/php/ext/grpc/server_credentials.c b/src/php/ext/grpc/server_credentials.c
new file mode 100755
index 0000000..b07790b
--- /dev/null
+++ b/src/php/ext/grpc/server_credentials.c
@@ -0,0 +1,117 @@
+#include "server_credentials.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 "grpc/grpc.h"
+#include "grpc/grpc_security.h"
+
+/* Frees and destroys an instace of wrapped_grpc_server_credentials */
+void free_wrapped_grpc_server_credentials(void *object TSRMLS_DC){
+ wrapped_grpc_server_credentials *creds =
+ (wrapped_grpc_server_credentials*)object;
+ if(creds->wrapped != NULL) {
+ grpc_server_credentials_release(creds->wrapped);
+ }
+ efree(creds);
+}
+
+/* Initializes an instace of wrapped_grpc_server_credentials to be associated
+ * with an object of a class specified by class_type */
+zend_object_value create_wrapped_grpc_server_credentials(
+ zend_class_entry *class_type TSRMLS_DC){
+ zend_object_value retval;
+ wrapped_grpc_server_credentials *intern;
+
+ intern = (wrapped_grpc_server_credentials*)emalloc(sizeof(
+ wrapped_grpc_server_credentials));
+ memset(intern, 0, sizeof(wrapped_grpc_server_credentials));
+
+ 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_server_credentials,
+ NULL TSRMLS_CC);
+ retval.handlers = zend_get_std_object_handlers();
+ return retval;
+}
+
+zval *grpc_php_wrap_server_credentials(grpc_server_credentials *wrapped){
+ zval *server_credentials_object;
+ MAKE_STD_ZVAL(server_credentials_object);
+ object_init_ex(server_credentials_object, grpc_ce_server_credentials);
+ wrapped_grpc_server_credentials *server_credentials =
+ (wrapped_grpc_server_credentials*)zend_object_store_get_object(
+ server_credentials_object TSRMLS_CC);
+ server_credentials->wrapped = wrapped;
+ return server_credentials_object;
+}
+
+/**
+ * Create SSL credentials.
+ * @param string pem_root_certs PEM encoding of the server root certificates
+ * @param string pem_private_key PEM encoding of the client's private key
+ * @param string pem_cert_chain PEM encoding of the client's certificate chain
+ * @return Credentials The new SSL credentials object
+ */
+PHP_METHOD(ServerCredentials, createSsl){
+ char *pem_root_certs = 0;
+ char *pem_private_key;
+ char *pem_cert_chain;
+
+ int root_certs_length = 0, private_key_length, cert_chain_length;
+
+ /* "s!ss" == 1 nullable string, 2 strings */
+ if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "s!ss",
+ &pem_root_certs, &root_certs_length,
+ &pem_private_key, &private_key_length,
+ &pem_cert_chain, &cert_chain_length) == FAILURE) {
+ zend_throw_exception(spl_ce_InvalidArgumentException,
+ "createSsl expects 3 strings",
+ 1 TSRMLS_CC);
+ return;
+ }
+ grpc_server_credentials *creds = grpc_ssl_server_credentials_create(
+ (unsigned char*)pem_root_certs, (size_t)root_certs_length,
+ (unsigned char*)pem_private_key, (size_t)private_key_length,
+ (unsigned char*)pem_cert_chain, (size_t)cert_chain_length);
+ zval *creds_object = grpc_php_wrap_server_credentials(creds);
+ RETURN_DESTROY_ZVAL(creds_object);
+}
+
+/**
+ * Create fake credentials. Only to be used for testing.
+ * @return ServerCredentials The new fake credentials object
+ */
+PHP_METHOD(ServerCredentials, createFake){
+ grpc_server_credentials *creds =
+ grpc_fake_transport_security_server_credentials_create();
+ zval *creds_object = grpc_php_wrap_server_credentials(creds);
+ RETURN_DESTROY_ZVAL(creds_object);
+}
+
+static zend_function_entry server_credentials_methods[] = {
+ PHP_ME(ServerCredentials, createSsl, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ PHP_ME(ServerCredentials, createFake, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ PHP_FE_END
+};
+
+void grpc_init_server_credentials(TSRMLS_D){
+ zend_class_entry ce;
+ INIT_CLASS_ENTRY(ce, "Grpc\\ServerCredentials", server_credentials_methods);
+ ce.create_object = create_wrapped_grpc_server_credentials;
+ grpc_ce_server_credentials = zend_register_internal_class(&ce TSRMLS_CC);
+}
diff --git a/src/php/ext/grpc/server_credentials.h b/src/php/ext/grpc/server_credentials.h
new file mode 100755
index 0000000..0a5c785
--- /dev/null
+++ b/src/php/ext/grpc/server_credentials.h
@@ -0,0 +1,30 @@
+#ifndef NET_GRPC_PHP_GRPC_SERVER_CREDENTIALS_H_
+#define NET_GRPC_PHP_GRPC_SERVER_CREDENTIALS_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "php_grpc.h"
+
+#include "grpc/grpc.h"
+#include "grpc/grpc_security.h"
+
+/* Class entry for the Server_Credentials PHP class */
+zend_class_entry *grpc_ce_server_credentials;
+
+/* Wrapper struct for grpc_server_credentials that can be associated with a PHP
+ * object */
+typedef struct wrapped_grpc_server_credentials {
+ zend_object std;
+
+ grpc_server_credentials *wrapped;
+} wrapped_grpc_server_credentials;
+
+/* Initializes the Server_Credentials PHP class */
+void grpc_init_server_credentials(TSRMLS_D);
+
+#endif /* NET_GRPC_PHP_GRPC_SERVER_CREDENTIALS_H_ */
diff --git a/src/php/ext/grpc/timeval.c b/src/php/ext/grpc/timeval.c
new file mode 100755
index 0000000..7b7e0e6
--- /dev/null
+++ b/src/php/ext/grpc/timeval.c
@@ -0,0 +1,255 @@
+#include "timeval.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 <stdbool.h>
+
+#include "grpc/grpc.h"
+#include "grpc/support/time.h"
+
+/* Frees and destroys an instance of wrapped_grpc_call */
+void free_wrapped_grpc_timeval(void *object TSRMLS_DC){
+ efree(object);
+}
+
+/* Initializes an instance of wrapped_grpc_timeval to be associated with an
+ * object of a class specified by class_type */
+zend_object_value create_wrapped_grpc_timeval(
+ zend_class_entry *class_type TSRMLS_DC){
+ zend_object_value retval;
+ wrapped_grpc_timeval *intern;
+ intern = (wrapped_grpc_timeval*)emalloc(sizeof(wrapped_grpc_timeval));
+ memset(intern, 0, sizeof(wrapped_grpc_timeval));
+ 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_timeval,
+ NULL TSRMLS_CC);
+ retval.handlers = zend_get_std_object_handlers();
+ return retval;
+}
+
+zval *grpc_php_wrap_timeval(gpr_timespec wrapped){
+ zval *timeval_object;
+ MAKE_STD_ZVAL(timeval_object);
+ object_init_ex(timeval_object, grpc_ce_timeval);
+ wrapped_grpc_timeval *timeval =
+ (wrapped_grpc_timeval*)zend_object_store_get_object(
+ timeval_object TSRMLS_CC);
+ memcpy(&timeval->wrapped, &wrapped, sizeof(gpr_timespec));
+ return timeval_object;
+}
+
+/**
+ * Constructs a new instance of the Timeval class
+ * @param long $usec The number of microseconds in the interval
+ */
+PHP_METHOD(Timeval, __construct){
+ wrapped_grpc_timeval *timeval =
+ (wrapped_grpc_timeval*)zend_object_store_get_object(getThis() TSRMLS_CC);
+ long microseconds;
+ /* "l" == 1 long */
+ if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "l",
+ µseconds) == FAILURE){
+ zend_throw_exception(spl_ce_InvalidArgumentException,
+ "Timeval expects a long",
+ 1 TSRMLS_CC);
+ return;
+ }
+ gpr_timespec time = gpr_time_from_micros(microseconds);
+ memcpy(&timeval->wrapped, &time, sizeof(gpr_timespec));
+}
+
+/**
+ * Adds another Timeval to this one and returns the sum. Calculations saturate
+ * at infinities.
+ * @param Timeval $other The other Timeval object to add
+ * @return Timeval A new Timeval object containing the sum
+ */
+PHP_METHOD(Timeval, add){
+ zval *other_obj;
+ /* "O" == 1 Object */
+ if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "O",
+ &other_obj, grpc_ce_timeval) == FAILURE){
+ zend_throw_exception(spl_ce_InvalidArgumentException,
+ "add expects a Timeval",
+ 1 TSRMLS_CC);
+ return;
+ }
+ wrapped_grpc_timeval *self =
+ (wrapped_grpc_timeval*)zend_object_store_get_object(getThis() TSRMLS_CC);
+ wrapped_grpc_timeval *other =
+ (wrapped_grpc_timeval*)zend_object_store_get_object(other_obj TSRMLS_CC);
+ zval *sum = grpc_php_wrap_timeval(gpr_time_add(self->wrapped,
+ other->wrapped));
+ RETURN_DESTROY_ZVAL(sum);
+}
+
+/**
+ * Subtracts another Timeval from this one and returns the difference.
+ * Calculations saturate at infinities.
+ * @param Timeval $other The other Timeval object to subtract
+ * @param Timeval A new Timeval object containing the sum
+ */
+PHP_METHOD(Timeval, subtract){
+ zval *other_obj;
+ /* "O" == 1 Object */
+ if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "O",
+ &other_obj, grpc_ce_timeval) == FAILURE){
+ zend_throw_exception(spl_ce_InvalidArgumentException,
+ "subtract expects a Timeval",
+ 1 TSRMLS_CC);
+ return;
+ }
+ wrapped_grpc_timeval *self =
+ (wrapped_grpc_timeval*)zend_object_store_get_object(getThis() TSRMLS_CC);
+ wrapped_grpc_timeval *other =
+ (wrapped_grpc_timeval*)zend_object_store_get_object(other_obj TSRMLS_CC);
+ zval *diff = grpc_php_wrap_timeval(gpr_time_sub(self->wrapped,
+ other->wrapped));
+ RETURN_DESTROY_ZVAL(diff);
+}
+
+/**
+ * Return negative, 0, or positive according to whether a < b, a == b, or a > b
+ * respectively.
+ * @param Timeval $a The first time to compare
+ * @param Timeval $b The second time to compare
+ * @return long
+ */
+PHP_METHOD(Timeval, compare){
+ zval *a_obj, *b_obj;
+ /* "OO" == 2 Objects */
+ if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "OO",
+ &a_obj, grpc_ce_timeval,
+ &b_obj, grpc_ce_timeval) == FAILURE){
+ zend_throw_exception(spl_ce_InvalidArgumentException,
+ "compare expects two Timevals",
+ 1 TSRMLS_CC);
+ return;
+ }
+ wrapped_grpc_timeval *a =
+ (wrapped_grpc_timeval*)zend_object_store_get_object(a_obj TSRMLS_CC);
+ wrapped_grpc_timeval *b =
+ (wrapped_grpc_timeval*)zend_object_store_get_object(b_obj TSRMLS_CC);
+ long result = gpr_time_cmp(a->wrapped, b->wrapped);
+ RETURN_LONG(result);
+}
+
+/**
+ * Checks whether the two times are within $threshold of each other
+ * @param Timeval $a The first time to compare
+ * @param Timeval $b The second time to compare
+ * @param Timeval $threshold The threshold to check against
+ * @return bool True if $a and $b are within $threshold, False otherwise
+ */
+PHP_METHOD(Timeval, similar){
+ zval *a_obj, *b_obj, *thresh_obj;
+ /* "OOO" == 3 Objects */
+ if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "OOO",
+ &a_obj, grpc_ce_timeval,
+ &b_obj, grpc_ce_timeval,
+ &thresh_obj, grpc_ce_timeval) == FAILURE){
+ zend_throw_exception(spl_ce_InvalidArgumentException,
+ "compare expects three Timevals",
+ 1 TSRMLS_CC);
+ return;
+ }
+ wrapped_grpc_timeval *a =
+ (wrapped_grpc_timeval*)zend_object_store_get_object(a_obj TSRMLS_CC);
+ wrapped_grpc_timeval *b =
+ (wrapped_grpc_timeval*)zend_object_store_get_object(b_obj TSRMLS_CC);
+ wrapped_grpc_timeval *thresh =
+ (wrapped_grpc_timeval*)zend_object_store_get_object(thresh_obj TSRMLS_CC);
+ int result = gpr_time_similar(a->wrapped, b->wrapped, thresh->wrapped);
+ RETURN_BOOL(result);
+}
+
+/**
+ * Returns the current time as a timeval object
+ * @return Timeval The current time
+ */
+PHP_METHOD(Timeval, now){
+ zval *now = grpc_php_wrap_timeval(gpr_now());
+ RETURN_DESTROY_ZVAL(now);
+}
+
+/**
+ * Returns the zero time interval as a timeval object
+ * @return Timeval Zero length time interval
+ */
+PHP_METHOD(Timeval, zero){
+ zval *grpc_php_timeval_zero = grpc_php_wrap_timeval(gpr_time_0);
+ RETURN_ZVAL(grpc_php_timeval_zero,
+ false, /* Copy original before returning? */
+ true /* Destroy original before returning */);
+}
+
+/**
+ * Returns the infinite future time value as a timeval object
+ * @return Timeval Infinite future time value
+ */
+PHP_METHOD(Timeval, inf_future){
+ zval *grpc_php_timeval_inf_future = grpc_php_wrap_timeval(gpr_inf_future);
+ RETURN_DESTROY_ZVAL(grpc_php_timeval_inf_future);
+}
+
+/**
+ * Returns the infinite past time value as a timeval object
+ * @return Timeval Infinite past time value
+ */
+PHP_METHOD(Timeval, inf_past){
+ zval *grpc_php_timeval_inf_past = grpc_php_wrap_timeval(gpr_inf_past);
+ RETURN_DESTROY_ZVAL(grpc_php_timeval_inf_past);
+}
+
+/**
+ * Sleep until this time, interpreted as an absolute timeout
+ * @return void
+ */
+PHP_METHOD(Timeval, sleep_until){
+ wrapped_grpc_timeval *this =
+ (wrapped_grpc_timeval*)zend_object_store_get_object(getThis() TSRMLS_CC);
+ gpr_sleep_until(this->wrapped);
+}
+
+static zend_function_entry timeval_methods[] = {
+ PHP_ME(Timeval, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
+ PHP_ME(Timeval, add, NULL, ZEND_ACC_PUBLIC)
+ PHP_ME(Timeval, compare, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ PHP_ME(Timeval, inf_future, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ PHP_ME(Timeval, inf_past, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ PHP_ME(Timeval, now, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ PHP_ME(Timeval, similar, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ PHP_ME(Timeval, sleep_until, NULL, ZEND_ACC_PUBLIC)
+ PHP_ME(Timeval, subtract, NULL, ZEND_ACC_PUBLIC)
+ PHP_ME(Timeval, zero, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ PHP_FE_END
+};
+
+void grpc_init_timeval(TSRMLS_D){
+ zend_class_entry ce;
+ INIT_CLASS_ENTRY(ce, "Grpc\\Timeval", timeval_methods);
+ ce.create_object = create_wrapped_grpc_timeval;
+ grpc_ce_timeval = zend_register_internal_class(&ce TSRMLS_CC);
+}
+
+void grpc_shutdown_timeval(TSRMLS_D){
+}
diff --git a/src/php/ext/grpc/timeval.h b/src/php/ext/grpc/timeval.h
new file mode 100755
index 0000000..cfdb0c7
--- /dev/null
+++ b/src/php/ext/grpc/timeval.h
@@ -0,0 +1,35 @@
+#ifndef NET_GRPC_PHP_GRPC_TIMEVAL_H_
+#define NET_GRPC_PHP_GRPC_TIMEVAL_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "php_grpc.h"
+
+#include "grpc/grpc.h"
+#include "grpc/support/time.h"
+
+/* Class entry for the Timeval PHP Class */
+zend_class_entry *grpc_ce_timeval;
+
+/* Wrapper struct for timeval that can be associated with a PHP object */
+typedef struct wrapped_grpc_timeval {
+ zend_object std;
+
+ gpr_timespec wrapped;
+} wrapped_grpc_timeval;
+
+/* Initialize the Timeval PHP class */
+void grpc_init_timeval(TSRMLS_D);
+
+/* Shutdown the Timeval PHP class */
+void grpc_shutdown_timeval(TSRMLS_D);
+
+/* Creates a Timeval object that wraps the given timeval struct */
+zval *grpc_php_wrap_timeval(gpr_timespec wrapped);
+
+#endif /* NET_GRPC_PHP_GRPC_TIMEVAL_H_ */