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",
+                           &microseconds) == 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_ */