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
pull/1/merge
mlumish 10 years ago committed by Michael Lumish
parent 275b3ac04d
commit b892a27e67
  1. 18
      src/php/.gitignore
  2. 56
      src/php/README.md
  3. 5
      src/php/bin/run_gen_code_test.sh
  4. 5
      src/php/bin/run_tests.sh
  5. 38
      src/php/ext/grpc/byte_buffer.c
  6. 12
      src/php/ext/grpc/byte_buffer.h
  7. 454
      src/php/ext/grpc/call.c
  8. 35
      src/php/ext/grpc/call.h
  9. 182
      src/php/ext/grpc/channel.c
  10. 32
      src/php/ext/grpc/channel.h
  11. 145
      src/php/ext/grpc/completion_queue.c
  12. 29
      src/php/ext/grpc/completion_queue.h
  13. 74
      src/php/ext/grpc/config.m4
  14. 171
      src/php/ext/grpc/credentials.c
  15. 30
      src/php/ext/grpc/credentials.h
  16. 191
      src/php/ext/grpc/event.c
  17. 31
      src/php/ext/grpc/event.h
  18. 247
      src/php/ext/grpc/php_grpc.c
  19. 66
      src/php/ext/grpc/php_grpc.h
  20. 202
      src/php/ext/grpc/server.c
  21. 28
      src/php/ext/grpc/server.h
  22. 117
      src/php/ext/grpc/server_credentials.c
  23. 30
      src/php/ext/grpc/server_credentials.h
  24. 255
      src/php/ext/grpc/timeval.c
  25. 35
      src/php/ext/grpc/timeval.h
  26. 98
      src/php/lib/Grpc/ActiveCall.php
  27. 106
      src/php/lib/Grpc/BaseStub.php
  28. 211
      src/php/lib/Grpc/SurfaceActiveCall.php
  29. 70
      src/php/tests/generated_code/GeneratedCodeTest.php
  30. 479
      src/php/tests/generated_code/math.php
  31. 26
      src/php/tests/interop/empty.php
  32. 191
      src/php/tests/interop/interop_client.php
  33. 26
      src/php/tests/interop/message_set.php
  34. 1011
      src/php/tests/interop/messages.php
  35. 52
      src/php/tests/interop/test.php
  36. 48
      src/php/tests/unit_tests/CallTest.php
  37. 14
      src/php/tests/unit_tests/CompletionQueueTest.php
  38. 185
      src/php/tests/unit_tests/EndToEndTest.php
  39. 196
      src/php/tests/unit_tests/SecureEndToEndTest.php
  40. 32
      src/php/tests/unit_tests/TimevalTest.php

18
src/php/.gitignore vendored

@ -0,0 +1,18 @@
.libs/
build/
modules/
autom4te.cache/
*.lo
*.la
.deps
acinclude.m4
aclocal.m4
config.*
configure*
Makefile*
run-tests.php
install-sh
libtool
missing
mkinstalldirs

@ -0,0 +1,56 @@
# PHP wrapper for the GRPC interfaces.
## LAYOUT
Directory structure is as generated by the PHP utility
[ext_skel](http://php.net/manual/en/internals2.buildsys.skeleton.php)
## ENVIRONMENT
To build a PHP environment that works with this extension, download and extract
PHP 5.5 (5.6 may also work), configure it, and install it:
```bash
apt-get install libxml2 libxml2-dev
curl http://php.net/get/php-5.5.16.tar.gz
tar -xf php-5.5.16.tar.gz
cd php-5.5.16
./configure --with-zlib=/usr --with-libxml-dir=ext/libxml --with-openssl=/usr/local/ssl
make
make install
```
To also download and install the patched protoc and PHP code generator:
```bash
apt-get install -y procps
curl -sSL https://get.rvm.io | sudo bash -s stable --ruby
git clone sso://team/one-platform-grpc-team/protobuf
cd protobuf
./configure
make
make install
git clone sso://team/one-platform-grpc-team/grpc-php-protobuf-php
cd grpc-php-protobuf-php
rake pear:package version=1.0
pear install Protobuf-1.0.tgz
```
## BUILDING
1. In ./ext/grpc, run the command `phpize` (distributed with PHP)
2. Run `./ext/grpc/configure`
3. In ./ext/grpc, run `make` and `sudo make install`
4. In your php.ini file, add the line `extension=grpc.so` to load the
extension at PHP startup.
## PHPUnit
This repo now has PHPUnit tests, which can by run by executing
`./bin/run_tests.sh` after building.
There is also a generated code test (`./bin/run_gen_code_test.sh`), which tests
the stub `./tests/generated_code/math.php` against a running localhost server
serving the math service. That stub is generated from
`./tests/generated_code/math.proto` with the head of the repo
`sso://team/one-platform-grpc-team/grpc-php-protobuf-php`.

@ -0,0 +1,5 @@
# Runs the generated code test against the ruby server
cd $(dirname $0)
GRPC_TEST_HOST=localhost:7070 php -d extension_dir=../ext/grpc/modules/ \
-d extension=grpc.so /usr/local/bin/phpunit -v --debug --strict \
../tests/generated_code/GeneratedCodeTest.php

@ -0,0 +1,5 @@
# Loads the local shared library, and runs all of the test cases in tests/
# against it
cd $(dirname $0)
php -d extension_dir=../ext/grpc/modules/ -d extension=grpc.so \
/usr/local/bin/phpunit -v --debug --strict ../tests/unit_tests

@ -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;
}

@ -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_ */

@ -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);
}

@ -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_ */

@ -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);
}

@ -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_ */

@ -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);
}

@ -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_ */

@ -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

@ -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);
}

@ -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_ */

@ -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);
}

@ -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 */

@ -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
*/

@ -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 */

@ -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);
}

@ -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_ */

@ -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);
}

@ -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_ */

@ -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){
}

@ -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_ */

@ -0,0 +1,98 @@
<?php
namespace Grpc;
/**
* Represents an active call that allows sending and recieving binary data
*/
class ActiveCall {
private $completion_queue;
private $call;
private $flags;
private $metadata;
/**
* Create a new active call.
* @param Channel $channel The channel to communicate on
* @param string $method The method to call on the remote server
* @param array $metadata Metadata to send with the call, if applicable
* @param long $flags Write flags to use with this call
*/
public function __construct(Channel $channel,
$method,
$metadata = array(),
$flags = 0) {
$this->completion_queue = new CompletionQueue();
$this->call = new Call($channel, $method, Timeval::inf_future());
$this->call->add_metadata($metadata, 0);
$this->flags = $flags;
// Invoke the call.
$this->call->start_invoke($this->completion_queue,
INVOKE_ACCEPTED,
CLIENT_METADATA_READ,
FINISHED, 0);
$this->completion_queue->pluck(INVOKE_ACCEPTED,
Timeval::inf_future());
$metadata_event = $this->completion_queue->pluck(CLIENT_METADATA_READ,
Timeval::inf_future());
$this->metadata = $metadata_event->get_data();
}
/**
* @return The metadata sent by the server.
*/
public function getMetadata() {
return $this->metadata;
}
/**
* Cancels the call
*/
public function cancel() {
$this->call->cancel();
}
/**
* Read a single message from the server.
* @return The next message from the server, or null if there is none.
*/
public function read() {
$this->call->start_read(READ);
$read_event = $this->completion_queue->pluck(READ, Timeval::inf_future());
return $read_event->get_data();
}
/**
* Write a single message to the server. This cannot be called after
* writesDone is called.
* @param ByteBuffer $data The data to write
*/
public function write($data) {
if($this->call->start_write($data,
WRITE_ACCEPTED,
$this->flags) != OP_OK) {
// TODO(mlumish): more useful error
throw new \Exception("Cannot call write after writesDone");
}
$this->completion_queue->pluck(WRITE_ACCEPTED, Timeval::inf_future());
}
/**
* Indicate that no more writes will be sent.
*/
public function writesDone() {
$this->call->writes_done(FINISH_ACCEPTED);
$this->completion_queue->pluck(FINISH_ACCEPTED, Timeval::inf_future());
}
/**
* Wait for the server to send the status, and return it.
* @return object The status object, with integer $code and string $details
* members
*/
public function getStatus() {
$status_event = $this->completion_queue->pluck(FINISHED,
Timeval::inf_future());
return $status_event->get_data();
}
}

@ -0,0 +1,106 @@
<?php
namespace Grpc;
/**
* Base class for generated client stubs. Stub methods are expected to call
* _simpleRequest or _streamRequest and return the result.
*/
class BaseStub {
private $channel;
public function __construct($hostname) {
$this->channel = new Channel($hostname, []);
}
/**
* Close the communication channel associated with this stub
*/
public function close() {
$channel->close();
}
/* This class is intended to be subclassed by generated code, so all functions
begin with "_" to avoid name collisions. */
/**
* Call a remote method that takes a single argument and has a single output
*
* @param string $method The name of the method to call
* @param $argument The argument to the method
* @param callable $deserialize A function that deserializes the response
* @param array $metadata A metadata map to send to the server
* @return SimpleSurfaceActiveCall The active call object
*/
protected function _simpleRequest($method,
$argument,
callable $deserialize,
$metadata = array()) {
return new SimpleSurfaceActiveCall($this->channel,
$method,
$deserialize,
$argument,
$metadata);
}
/**
* Call a remote method that takes a stream of arguments and has a single
* output
*
* @param string $method The name of the method to call
* @param $arguments An array or Traversable of arguments to stream to the
* server
* @param callable $deserialize A function that deserializes the response
* @param array $metadata A metadata map to send to the server
* @return ClientStreamingSurfaceActiveCall The active call object
*/
protected function _clientStreamRequest($method,
$arguments,
callable $deserialize,
$metadata = array()) {
return new ClientStreamingSurfaceActiveCall($this->channel,
$method,
$deserialize,
$arguments,
$metadata);
}
/**
* Call a remote method that takes a single argument and returns a stream of
* responses
*
* @param string $method The name of the method to call
* @param $argument The argument to the method
* @param callable $deserialize A function that deserializes the responses
* @param array $metadata A metadata map to send to the server
* @return ServerStreamingSurfaceActiveCall The active call object
*/
protected function _serverStreamRequest($method,
$argument,
callable $deserialize,
$metadata = array()) {
return new ServerStreamingSurfaceActiveCall($this->channel,
$method,
$deserialize,
$argument,
$metadata);
}
/**
* Call a remote method with messages streaming in both directions
*
* @param string $method The name of the method to call
* @param callable $deserialize A function that deserializes the responses
* @param array $metadata A metadata map to send to the server
* @return BidiStreamingSurfaceActiveCall The active call object
*/
protected function _bidiRequest($method,
callable $deserialize,
$metadata = array()) {
return new BidiStreamingSurfaceActiveCall($this->channel,
$method,
$deserialize,
$metadata);
}
}

@ -0,0 +1,211 @@
<?php
namespace Grpc;
/**
* Represents an active call that allows sending and recieving messages.
* Subclasses restrict how data can be sent and recieved.
*/
abstract class AbstractSurfaceActiveCall {
private $active_call;
private $deserialize;
/**
* Create a new surface active call.
* @param Channel $channel The channel to communicate on
* @param string $method The method to call on the remote server
* @param callable $deserialize The function to deserialize a value
* @param array $metadata Metadata to send with the call, if applicable
* @param long $flags Write flags to use with this call
*/
public function __construct(Channel $channel,
$method,
callable $deserialize,
$metadata = array(),
$flags = 0) {
$this->active_call = new ActiveCall($channel, $method, $metadata, $flags);
$this->deserialize = $deserialize;
}
/**
* @return The metadata sent by the server
*/
public function getMetadata() {
return $this->metadata();
}
/**
* Cancels the call
*/
public function cancel() {
$this->active_call->cancel();
}
protected function _read() {
$response = $this->active_call->read();
if ($response == null) {
return null;
}
return call_user_func($this->deserialize, $response);
}
protected function _write($value) {
return $this->active_call->write($value->serialize());
}
protected function _writesDone() {
$this->active_call->writesDone();
}
protected function _getStatus() {
return $this->active_call->getStatus();
}
}
/**
* Represents an active call that sends a single message and then gets a single
* response.
*/
class SimpleSurfaceActiveCall extends AbstractSurfaceActiveCall {
/**
* Create a new simple (single request/single response) active call.
* @param Channel $channel The channel to communicate on
* @param string $method The method to call on the remote server
* @param callable $deserialize The function to deserialize a value
* @param $arg The argument to send
* @param array $metadata Metadata to send with the call, if applicable
*/
public function __construct(Channel $channel,
$method,
callable $deserialize,
$arg,
$metadata = array()) {
parent::__construct($channel, $method, $deserialize, $metadata,
\Grpc\WRITE_BUFFER_HINT);
$this->_write($arg);
$this->_writesDone();
}
/**
* Wait for the server to respond with data and a status
* @return [response data, status]
*/
public function wait() {
$response = $this->_read();
$status = $this->_getStatus();
return array($response, $status);
}
}
/**
* Represents an active call that sends a stream of messages and then gets a
* single response.
*/
class ClientStreamingSurfaceActiveCall extends AbstractSurfaceActiveCall {
/**
* Create a new simple (single request/single response) active call.
* @param Channel $channel The channel to communicate on
* @param string $method The method to call on the remote server
* @param callable $deserialize The function to deserialize a value
* @param Traversable $arg_iter The iterator of arguments to send
* @param array $metadata Metadata to send with the call, if applicable
*/
public function __construct(Channel $channel,
$method,
callable $deserialize,
$arg_iter,
$metadata = array()) {
parent::__construct($channel, $method, $deserialize, $metadata, 0);
foreach($arg_iter as $arg) {
$this->_write($arg);
}
$this->_writesDone();
}
/**
* Wait for the server to respond with data and a status
* @return [response data, status]
*/
public function wait() {
$response = $this->_read();
$status = $this->_getStatus();
return array($response, $status);
}
}
/**
* Represents an active call that sends a single message and then gets a stream
* of reponses
*/
class ServerStreamingSurfaceActiveCall extends AbstractSurfaceActiveCall {
/**
* Create a new simple (single request/single response) active call.
* @param Channel $channel The channel to communicate on
* @param string $method The method to call on the remote server
* @param callable $deserialize The function to deserialize a value
* @param $arg The argument to send
* @param array $metadata Metadata to send with the call, if applicable
*/
public function __construct(Channel $channel,
$method,
callable $deserialize,
$arg,
$metadata = array()) {
parent::__construct($channel, $method, $deserialize, $metadata,
\Grpc\WRITE_BUFFER_HINT);
$this->_write($arg);
$this->_writesDone();
}
/**
* @return An iterator of response values
*/
public function responses() {
while(($response = $this->_read()) != null) {
yield $response;
}
}
public function getStatus() {
return $this->_getStatus();
}
}
/**
* Represents an active call that allows for sending and recieving messages in
* streams in any order.
*/
class BidiStreamingSurfaceActiveCall extends AbstractSurfaceActiveCall {
/**
* Reads the next value from the server.
* @return The next value from the server, or null if there is none
*/
public function read() {
return $this->_read();
}
/**
* Writes a single message to the server. This cannot be called after
* writesDone is called.
* @param $value The message to send
*/
public function write($value) {
$this->_write($value);
}
/**
* Indicate that no more writes will be sent
*/
public function writesDone() {
$this->_writesDone();
}
/**
* Wait for the server to send the status, and return it.
* @return object The status object, with integer $code and string $details
* members
*/
public function getStatus() {
return $this->_getStatus();
}
}

@ -0,0 +1,70 @@
<?php
require __DIR__ . '/../../lib/Grpc/ActiveCall.php';
require __DIR__ . '/../../lib/Grpc/SurfaceActiveCall.php';
require __DIR__ . '/../../lib/Grpc/BaseStub.php';
require 'DrSlump/Protobuf.php';
\DrSlump\Protobuf::autoload();
require 'math.php';
class GeneratedCodeTest extends PHPUnit_Framework_TestCase {
/* These tests require that a server exporting the math service must be
* running on $GRPC_TEST_HOST */
protected static $client;
protected static $timeout;
public static function setUpBeforeClass() {
self::$client = new math\MathClient(getenv('GRPC_TEST_HOST'));
}
public function testSimpleRequest() {
$div_arg = new math\DivArgs();
$div_arg->setDividend(7);
$div_arg->setDivisor(4);
list($response, $status) = self::$client->Div($div_arg)->wait();
$this->assertEquals(1, $response->getQuotient());
$this->assertEquals(3, $response->getRemainder());
$this->assertEquals(\Grpc\STATUS_OK, $status->code);
}
public function testServerStreaming() {
$fib_arg = new math\FibArgs();
$fib_arg->setLimit(7);
$call = self::$client->Fib($fib_arg);
$result_array = iterator_to_array($call->responses());
$extract_num = function($num){
return $num->getNum();
};
$values = array_map($extract_num, $result_array);
$this->assertEquals([1, 1, 2, 3, 5, 8, 13], $values);
$status = $call->getStatus();
$this->assertEquals(\Grpc\STATUS_OK, $status->code);
}
public function testClientStreaming() {
$num_iter = function() {
for ($i = 0; $i < 7; $i++) {
$num = new math\Num();
$num->setNum($i);
yield $num;
}
};
$call = self::$client->Sum($num_iter());
list($response, $status) = $call->wait();
$this->assertEquals(21, $response->getNum());
$this->assertEquals(\Grpc\STATUS_OK, $status->code);
}
public function testBidiStreaming() {
$call = self::$client->DivMany();
for ($i = 0; $i < 7; $i++) {
$div_arg = new math\DivArgs();
$div_arg->setDividend(2 * $i + 1);
$div_arg->setDivisor(2);
$call->write($div_arg);
$response = $call->read();
$this->assertEquals($i, $response->getQuotient());
$this->assertEquals(1, $response->getRemainder());
}
$call->writesDone();
$status = $call->getStatus();
$this->assertEquals(\Grpc\STATUS_OK, $status->code);
}
}

@ -0,0 +1,479 @@
<?php
// DO NOT EDIT! Generated by Protobuf-PHP protoc plugin 1.0
// Source: math.proto
// Date: 2014-11-14 00:00:41
namespace math {
class DivArgs extends \DrSlump\Protobuf\Message {
/** @var int */
public $dividend = null;
/** @var int */
public $divisor = null;
/** @var \Closure[] */
protected static $__extensions = array();
public static function descriptor()
{
$descriptor = new \DrSlump\Protobuf\Descriptor(__CLASS__, 'math.DivArgs');
// REQUIRED INT64 dividend = 1
$f = new \DrSlump\Protobuf\Field();
$f->number = 1;
$f->name = "dividend";
$f->type = \DrSlump\Protobuf::TYPE_INT64;
$f->rule = \DrSlump\Protobuf::RULE_REQUIRED;
$descriptor->addField($f);
// REQUIRED INT64 divisor = 2
$f = new \DrSlump\Protobuf\Field();
$f->number = 2;
$f->name = "divisor";
$f->type = \DrSlump\Protobuf::TYPE_INT64;
$f->rule = \DrSlump\Protobuf::RULE_REQUIRED;
$descriptor->addField($f);
foreach (self::$__extensions as $cb) {
$descriptor->addField($cb(), true);
}
return $descriptor;
}
/**
* Check if <dividend> has a value
*
* @return boolean
*/
public function hasDividend(){
return $this->_has(1);
}
/**
* Clear <dividend> value
*
* @return \math\DivArgs
*/
public function clearDividend(){
return $this->_clear(1);
}
/**
* Get <dividend> value
*
* @return int
*/
public function getDividend(){
return $this->_get(1);
}
/**
* Set <dividend> value
*
* @param int $value
* @return \math\DivArgs
*/
public function setDividend( $value){
return $this->_set(1, $value);
}
/**
* Check if <divisor> has a value
*
* @return boolean
*/
public function hasDivisor(){
return $this->_has(2);
}
/**
* Clear <divisor> value
*
* @return \math\DivArgs
*/
public function clearDivisor(){
return $this->_clear(2);
}
/**
* Get <divisor> value
*
* @return int
*/
public function getDivisor(){
return $this->_get(2);
}
/**
* Set <divisor> value
*
* @param int $value
* @return \math\DivArgs
*/
public function setDivisor( $value){
return $this->_set(2, $value);
}
}
}
namespace math {
class DivReply extends \DrSlump\Protobuf\Message {
/** @var int */
public $quotient = null;
/** @var int */
public $remainder = null;
/** @var \Closure[] */
protected static $__extensions = array();
public static function descriptor()
{
$descriptor = new \DrSlump\Protobuf\Descriptor(__CLASS__, 'math.DivReply');
// REQUIRED INT64 quotient = 1
$f = new \DrSlump\Protobuf\Field();
$f->number = 1;
$f->name = "quotient";
$f->type = \DrSlump\Protobuf::TYPE_INT64;
$f->rule = \DrSlump\Protobuf::RULE_REQUIRED;
$descriptor->addField($f);
// REQUIRED INT64 remainder = 2
$f = new \DrSlump\Protobuf\Field();
$f->number = 2;
$f->name = "remainder";
$f->type = \DrSlump\Protobuf::TYPE_INT64;
$f->rule = \DrSlump\Protobuf::RULE_REQUIRED;
$descriptor->addField($f);
foreach (self::$__extensions as $cb) {
$descriptor->addField($cb(), true);
}
return $descriptor;
}
/**
* Check if <quotient> has a value
*
* @return boolean
*/
public function hasQuotient(){
return $this->_has(1);
}
/**
* Clear <quotient> value
*
* @return \math\DivReply
*/
public function clearQuotient(){
return $this->_clear(1);
}
/**
* Get <quotient> value
*
* @return int
*/
public function getQuotient(){
return $this->_get(1);
}
/**
* Set <quotient> value
*
* @param int $value
* @return \math\DivReply
*/
public function setQuotient( $value){
return $this->_set(1, $value);
}
/**
* Check if <remainder> has a value
*
* @return boolean
*/
public function hasRemainder(){
return $this->_has(2);
}
/**
* Clear <remainder> value
*
* @return \math\DivReply
*/
public function clearRemainder(){
return $this->_clear(2);
}
/**
* Get <remainder> value
*
* @return int
*/
public function getRemainder(){
return $this->_get(2);
}
/**
* Set <remainder> value
*
* @param int $value
* @return \math\DivReply
*/
public function setRemainder( $value){
return $this->_set(2, $value);
}
}
}
namespace math {
class FibArgs extends \DrSlump\Protobuf\Message {
/** @var int */
public $limit = null;
/** @var \Closure[] */
protected static $__extensions = array();
public static function descriptor()
{
$descriptor = new \DrSlump\Protobuf\Descriptor(__CLASS__, 'math.FibArgs');
// OPTIONAL INT64 limit = 1
$f = new \DrSlump\Protobuf\Field();
$f->number = 1;
$f->name = "limit";
$f->type = \DrSlump\Protobuf::TYPE_INT64;
$f->rule = \DrSlump\Protobuf::RULE_OPTIONAL;
$descriptor->addField($f);
foreach (self::$__extensions as $cb) {
$descriptor->addField($cb(), true);
}
return $descriptor;
}
/**
* Check if <limit> has a value
*
* @return boolean
*/
public function hasLimit(){
return $this->_has(1);
}
/**
* Clear <limit> value
*
* @return \math\FibArgs
*/
public function clearLimit(){
return $this->_clear(1);
}
/**
* Get <limit> value
*
* @return int
*/
public function getLimit(){
return $this->_get(1);
}
/**
* Set <limit> value
*
* @param int $value
* @return \math\FibArgs
*/
public function setLimit( $value){
return $this->_set(1, $value);
}
}
}
namespace math {
class Num extends \DrSlump\Protobuf\Message {
/** @var int */
public $num = null;
/** @var \Closure[] */
protected static $__extensions = array();
public static function descriptor()
{
$descriptor = new \DrSlump\Protobuf\Descriptor(__CLASS__, 'math.Num');
// REQUIRED INT64 num = 1
$f = new \DrSlump\Protobuf\Field();
$f->number = 1;
$f->name = "num";
$f->type = \DrSlump\Protobuf::TYPE_INT64;
$f->rule = \DrSlump\Protobuf::RULE_REQUIRED;
$descriptor->addField($f);
foreach (self::$__extensions as $cb) {
$descriptor->addField($cb(), true);
}
return $descriptor;
}
/**
* Check if <num> has a value
*
* @return boolean
*/
public function hasNum(){
return $this->_has(1);
}
/**
* Clear <num> value
*
* @return \math\Num
*/
public function clearNum(){
return $this->_clear(1);
}
/**
* Get <num> value
*
* @return int
*/
public function getNum(){
return $this->_get(1);
}
/**
* Set <num> value
*
* @param int $value
* @return \math\Num
*/
public function setNum( $value){
return $this->_set(1, $value);
}
}
}
namespace math {
class FibReply extends \DrSlump\Protobuf\Message {
/** @var int */
public $count = null;
/** @var \Closure[] */
protected static $__extensions = array();
public static function descriptor()
{
$descriptor = new \DrSlump\Protobuf\Descriptor(__CLASS__, 'math.FibReply');
// REQUIRED INT64 count = 1
$f = new \DrSlump\Protobuf\Field();
$f->number = 1;
$f->name = "count";
$f->type = \DrSlump\Protobuf::TYPE_INT64;
$f->rule = \DrSlump\Protobuf::RULE_REQUIRED;
$descriptor->addField($f);
foreach (self::$__extensions as $cb) {
$descriptor->addField($cb(), true);
}
return $descriptor;
}
/**
* Check if <count> has a value
*
* @return boolean
*/
public function hasCount(){
return $this->_has(1);
}
/**
* Clear <count> value
*
* @return \math\FibReply
*/
public function clearCount(){
return $this->_clear(1);
}
/**
* Get <count> value
*
* @return int
*/
public function getCount(){
return $this->_get(1);
}
/**
* Set <count> value
*
* @param int $value
* @return \math\FibReply
*/
public function setCount( $value){
return $this->_set(1, $value);
}
}
}
namespace math {
class MathClient extends \Grpc\BaseStub {
/**
* @param math\DivArgs $input
* @return math\DivReply
*/
public function Div(\math\DivArgs $argument, $metadata = array()) {
return $this->_simpleRequest('/Math/Div', $argument, '\math\DivReply::deserialize', $metadata);
}
/**
* @param math\DivArgs $input
* @return math\DivReply
*/
public function DivMany($metadata = array()) {
return $this->_bidiRequest('/Math/DivMany', '\math\DivReply::deserialize', $metadata);
}
/**
* @param math\FibArgs $input
* @return math\Num
*/
public function Fib($argument, $metadata = array()) {
return $this->_serverStreamRequest('/Math/Fib', $argument, '\math\Num::deserialize', $metadata);
}
/**
* @param math\Num $input
* @return math\Num
*/
public function Sum($arguments, $metadata = array()) {
return $this->_clientStreamRequest('/Math/Sum', $arguments, '\math\Num::deserialize', $metadata);
}
}
}

@ -0,0 +1,26 @@
<?php
// DO NOT EDIT! Generated by Protobuf-PHP protoc plugin 1.0
// Source: net/proto2/proto/empty.proto
// Date: 2014-12-03 22:02:20
namespace proto2 {
class EmptyMessage extends \DrSlump\Protobuf\Message {
/** @var \Closure[] */
protected static $__extensions = array();
public static function descriptor()
{
$descriptor = new \DrSlump\Protobuf\Descriptor(__CLASS__, 'proto2.EmptyMessage');
foreach (self::$__extensions as $cb) {
$descriptor->addField($cb(), true);
}
return $descriptor;
}
}
}

@ -0,0 +1,191 @@
<?php
require __DIR__ . '/../../lib/Grpc/ActiveCall.php';
require __DIR__ . '/../../lib/Grpc/SurfaceActiveCall.php';
require __DIR__ . '/../../lib/Grpc/BaseStub.php';
require 'DrSlump/Protobuf.php';
\DrSlump\Protobuf::autoload();
require 'empty.php';
require 'message_set.php';
require 'messages.php';
require 'test.php';
/**
* Assertion function that always exits with an error code if the assertion is
* falsy
* @param $value Assertion value. Should be true.
* @param $error_message Message to display if the assertion is false
*/
function hardAssert($value, $error_message) {
if(!$value) {
echo $error_message . "\n";
exit(1);
}
}
/**
* Run the empty_unary test.
* Currently not tested against any server as of 2014-12-04
* @param $stub Stub object that has service methods
*/
function emptyUnary($stub) {
list($result, $status) = $stub->EmptyCall(new proto2\EmptyMessage())->wait();
hardAssert($status->code == Grpc\STATUS_OK, 'Call did not complete successfully');
hardAssert($result != null, 'Call completed with a null response');
}
/**
* Run the large_unary test.
* Passes when run against the C++ server as of 2014-12-04
* Not tested against any other server as of 2014-12-04
* @param $stub Stub object that has service methods
*/
function largeUnary($stub) {
$request_len = 271828;
$response_len = 314159;
$request = new grpc\testing\SimpleRequest();
$request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
$request->setResponseSize($response_len);
$payload = new grpc\testing\Payload();
$payload->setType(grpc\testing\PayloadType::COMPRESSABLE);
$payload->setBody(str_repeat("\0", $request_len));
$request->setPayload($payload);
list($result, $status) = $stub->UnaryCall($request)->wait();
hardAssert($status->code == Grpc\STATUS_OK, 'Call did not complete successfully');
hardAssert($result != null, 'Call returned a null response');
$payload = $result->getPayload();
hardAssert($payload->getType() == grpc\testing\PayloadType::COMPRESSABLE,
'Payload had the wrong type');
hardAssert(strlen($payload->getBody()) == $response_len,
'Payload had the wrong length');
hardAssert($payload->getBody() == str_repeat("\0", $response_len),
'Payload had the wrong content');
}
/**
* Run the client_streaming test.
* Not tested against any server as of 2014-12-04.
* @param $stub Stub object that has service methods
*/
function clientStreaming($stub) {
$request_lengths = array(27182, 8, 1828, 45904);
$requests = array_map(
function($length) {
$request = new grpc\testing\StreamingInputCallRequest();
$payload = new grpc\testing\Payload();
$payload->setBody(str_repeat("\0", $length));
$request->setPayload($payload);
return $request;
}, $request_lengths);
list($result, $status) = $stub->StreamingInputCall($requests)->wait();
hardAssert($status->code == Grpc\STATUS_OK, 'Call did not complete successfully');
hardAssert($result->getAggregatedPayloadSize() == 74922,
'aggregated_payload_size was incorrect');
}
/**
* Run the server_streaming test.
* Not tested against any server as of 2014-12-04.
* @param $stub Stub object that has service methods.
*/
function serverStreaming($stub) {
$sizes = array(31415, 9, 2653, 58979);
$request = new grpc\testing\StreamingOutputCallRequest();
$request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
foreach($sizes as $size) {
$response_parameters = new grpc\testing\ResponseParameters();
$response_parameters->setSize($size);
$request->addResponseParameters($response_parameters);
}
$call = $stub->StreamingOutputCall($request);
hardAssert($call->getStatus()->code == Grpc\STATUS_OK,
'Call did not complete successfully');
$i = 0;
foreach($call->responses() as $value) {
hardAssert($i < 4, 'Too many responses');
$payload = $value->getPayload();
hardAssert($payload->getType() == grpc\testing\PayloadType::COMPRESSABLE,
'Payload ' . $i . ' had the wrong type');
hardAssert(strlen($payload->getBody()) == $sizes[$i],
'Response ' . $i . ' had the wrong length');
}
}
/**
* Run the ping_pong test.
* Not tested against any server as of 2014-12-04.
* @param $stub Stub object that has service methods.
*/
function pingPong($stub) {
$request_lengths = array(27182, 8, 1828, 45904);
$response_lengths = array(31415, 9, 2653, 58979);
$call = $stub->FullDuplexCall();
for($i = 0; $i < 4; $i++) {
$request = new grpc\testing\StreamingOutputCallRequest();
$request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE);
$response_parameters = new grpc\testing\ResponseParameters();
$response_parameters->setSize($response_lengths[$i]);
$request->addResponseParameters($response_parameters);
$payload = new grpc\testing\Payload();
$payload->setBody(str_repeat("\0", $request_lengths[$i]));
$request->setPayload($payload);
$call->write($request);
$response = $call->read();
hardAssert($response != null, 'Server returned too few responses');
$payload = $response->getPayload();
hardAssert($payload->getType() == grpc\testing\PayloadType::COMPRESSABLE,
'Payload ' . $i . ' had the wrong type');
hardAssert(strlen($payload->getBody()) == $response_lengths[$i],
'Payload ' . $i . ' had the wrong length');
}
$call->writesDone();
hardAssert($call->read() == null, 'Server returned too many responses');
hardAssert($call->getStatus()->code == Grpc\STATUS_OK,
'Call did not complete successfully');
}
$args = getopt('', array('server_host:', 'server_port:', 'test_case:'));
if (!array_key_exists('server_host', $args) ||
!array_key_exists('server_port', $args) ||
!array_key_exists('test_case', $args)) {
throw new Exception('Missing argument');
}
$server_address = $args['server_host'] . ':' . $args['server_port'];
$credentials = Grpc\Credentials::createSsl(
file_get_contents(dirname(__FILE__) . '/../data/ca.pem'));
$stub = new grpc\testing\TestServiceClient(
$server_address,
[
'grpc.ssl_target_name_override' => 'foo.test.google.com',
'credentials' => $credentials
]);
echo "Connecting to $server_address\n";
echo "Running test case $args[test_case]\n";
switch($args['test_case']) {
case 'empty_unary':
emptyUnary($stub);
break;
case 'large_unary':
largeUnary($stub);
break;
case 'client_streaming':
clientStreaming($stub);
break;
case 'server_streaming':
serverStreaming($stub);
break;
case 'ping_pong':
pingPong($stub);
break;
}

@ -0,0 +1,26 @@
<?php
// DO NOT EDIT! Generated by Protobuf-PHP protoc plugin 1.0
// Source: net/proto2/bridge/proto/message_set.proto
// Date: 2014-12-03 22:02:20
namespace proto2\bridge {
class MessageSet extends \DrSlump\Protobuf\Message {
/** @var \Closure[] */
protected static $__extensions = array();
public static function descriptor()
{
$descriptor = new \DrSlump\Protobuf\Descriptor(__CLASS__, 'proto2.bridge.MessageSet');
foreach (self::$__extensions as $cb) {
$descriptor->addField($cb(), true);
}
return $descriptor;
}
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,52 @@
<?php
// DO NOT EDIT! Generated by Protobuf-PHP protoc plugin 1.0
// Source: third_party/stubby/testing/proto/test.proto
// Date: 2014-12-03 22:02:20
namespace grpc\testing {
class TestServiceClient extends \Grpc\BaseStub {
/**
* @param proto2\EmptyMessage $input
* @return proto2\EmptyMessage
*/
public function EmptyCall(\proto2\EmptyMessage $argument, $metadata = array()) {
return $this->_simpleRequest('/TestService/EmptyCall', $argument, '\proto2\EmptyMessage::deserialize', $metadata);
}
/**
* @param grpc\testing\SimpleRequest $input
* @return grpc\testing\SimpleResponse
*/
public function UnaryCall(\grpc\testing\SimpleRequest $argument, $metadata = array()) {
return $this->_simpleRequest('/TestService/UnaryCall', $argument, '\grpc\testing\SimpleResponse::deserialize', $metadata);
}
/**
* @param grpc\testing\StreamingOutputCallRequest $input
* @return grpc\testing\StreamingOutputCallResponse
*/
public function StreamingOutputCall($argument, $metadata = array()) {
return $this->_serverStreamRequest('/TestService/StreamingOutputCall', $argument, '\grpc\testing\StreamingOutputCallResponse::deserialize', $metadata);
}
/**
* @param grpc\testing\StreamingInputCallRequest $input
* @return grpc\testing\StreamingInputCallResponse
*/
public function StreamingInputCall($arguments, $metadata = array()) {
return $this->_clientStreamRequest('/TestService/StreamingInputCall', $arguments, '\grpc\testing\StreamingInputCallResponse::deserialize', $metadata);
}
/**
* @param grpc\testing\StreamingOutputCallRequest $input
* @return grpc\testing\StreamingOutputCallResponse
*/
public function FullDuplexCall($metadata = array()) {
return $this->_bidiRequest('/TestService/FullDuplexCall', '\grpc\testing\StreamingOutputCallResponse::deserialize', $metadata);
}
/**
* @param grpc\testing\StreamingOutputCallRequest $input
* @return grpc\testing\StreamingOutputCallResponse
*/
public function HalfDuplexCall($metadata = array()) {
return $this->_bidiRequest('/TestService/HalfDuplexCall', '\grpc\testing\StreamingOutputCallResponse::deserialize', $metadata);
}
}
}

@ -0,0 +1,48 @@
<?php
class CallTest extends PHPUnit_Framework_TestCase{
static $server;
public static function setUpBeforeClass() {
$cq = new Grpc\CompletionQueue();
self::$server = new Grpc\Server($cq, []);
self::$server->add_http2_port('localhost:9001');
}
public function setUp() {
$this->channel = new Grpc\Channel('localhost:9001', []);
$this->call = new Grpc\Call($this->channel,
'/foo',
Grpc\Timeval::inf_future());
}
/* These test methods with assertTrue(true) at the end just check that the
method calls completed without errors. PHPUnit warns for tests with no
asserts, and this avoids that warning without changing the meaning of the
tests */
public function testAddEmptyMetadata() {
$this->call->add_metadata([], 0);
/* Dummy assert: Checks that the previous call completed without error */
$this->assertTrue(true);
}
public function testAddSingleMetadata() {
$this->call->add_metadata(['key' => 'value'], 0);
/* Dummy assert: Checks that the previous call completed without error */
$this->assertTrue(true);
}
public function testAddMultiValueMetadata() {
$this->call->add_metadata(['key' => ['value1', 'value2']], 0);
/* Dummy assert: Checks that the previous call completed without error */
$this->assertTrue(true);
}
public function testAddSingleAndMultiValueMetadata() {
$this->call->add_metadata(
['key1' => 'value1',
'key2' => ['value2', 'value3']], 0);
/* Dummy assert: Checks that the previous call completed without error */
$this->assertTrue(true);
}
}

@ -0,0 +1,14 @@
<?php
class CompletionQueueTest extends PHPUnit_Framework_TestCase{
public function testNextReturnsNullWithNoCall() {
$cq = new Grpc\CompletionQueue();
$event = $cq->next(Grpc\Timeval::zero());
$this->assertNull($event);
}
public function testPluckReturnsNullWithNoCall() {
$cq = new Grpc\CompletionQueue();
$event = $cq->pluck(0, Grpc\Timeval::zero());
$this->assertNull($event);
}
}

@ -0,0 +1,185 @@
<?php
class EndToEndTest extends PHPUnit_Framework_TestCase{
public function setUp() {
$this->client_queue = new Grpc\CompletionQueue();
$this->server_queue = new Grpc\CompletionQueue();
$this->server = new Grpc\Server($this->server_queue, []);
$this->server->add_http2_port('localhost:9000');
$this->channel = new Grpc\Channel('localhost:9000', []);
}
public function tearDown() {
unset($this->channel);
unset($this->server);
unset($this->client_queue);
unset($this->server_queue);
}
public function testSimpleRequestBody() {
$deadline = Grpc\Timeval::inf_future();
$status_text = 'xyz';
$call = new Grpc\Call($this->channel,
'dummy_method',
$deadline);
$tag = 1;
$this->assertEquals(Grpc\CALL_OK,
$call->start_invoke($this->client_queue,
$tag,
$tag,
$tag));
$server_tag = 2;
// the client invocation was accepted
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\INVOKE_ACCEPTED, $event->get_type());
$this->assertEquals(Grpc\CALL_OK, $call->writes_done($tag));
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\FINISH_ACCEPTED, $event->get_type());
$this->assertEquals(Grpc\OP_OK, $event->get_data());
// check that a server rpc new was received
$this->server->start();
$this->assertEquals(Grpc\CALL_OK, $this->server->request_call($server_tag));
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\SERVER_RPC_NEW, $event->get_type());
$server_call = $event->get_call();
$this->assertNotNull($server_call);
$this->assertEquals(Grpc\CALL_OK,
$server_call->accept($this->server_queue, $server_tag));
// the server sends the status
$this->assertEquals(Grpc\CALL_OK,
$server_call->start_write_status(Grpc\STATUS_OK,
$status_text,
$server_tag));
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\FINISH_ACCEPTED, $event->get_type());
$this->assertEquals(Grpc\OP_OK, $event->get_data());
// the client gets CLIENT_METADATA_READ
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\CLIENT_METADATA_READ, $event->get_type());
// the client gets FINISHED
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\FINISHED, $event->get_type());
$status = $event->get_data();
$this->assertEquals(Grpc\STATUS_OK, $status->code);
$this->assertEquals($status_text, $status->details);
// and the server gets FINISHED
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\FINISHED, $event->get_type());
$status = $event->get_data();
unset($call);
unset($server_call);
}
public function testClientServerFullRequestResponse() {
$deadline = Grpc\Timeval::inf_future();
$req_text = 'client_server_full_request_response';
$reply_text = 'reply:client_server_full_request_response';
$status_text = 'status:client_server_full_response_text';
$call = new Grpc\Call($this->channel,
'dummy_method',
$deadline);
$tag = 1;
$this->assertEquals(Grpc\CALL_OK,
$call->start_invoke($this->client_queue,
$tag,
$tag,
$tag));
$server_tag = 2;
// the client invocation was accepted
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\INVOKE_ACCEPTED, $event->get_type());
// the client writes
$this->assertEquals(Grpc\CALL_OK, $call->start_write($req_text, $tag));
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\WRITE_ACCEPTED, $event->get_type());
// check that a server rpc new was received
$this->server->start();
$this->assertEquals(Grpc\CALL_OK, $this->server->request_call($server_tag));
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\SERVER_RPC_NEW, $event->get_type());
$server_call = $event->get_call();
$this->assertNotNull($server_call);
$this->assertEquals(Grpc\CALL_OK,
$server_call->accept($this->server_queue, $server_tag));
// start the server read
$this->assertEquals(Grpc\CALL_OK, $server_call->start_read($server_tag));
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\READ, $event->get_type());
$this->assertEquals($req_text, $event->get_data());
// the server replies
$this->assertEquals(Grpc\CALL_OK,
$server_call->start_write($reply_text, $server_tag));
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\WRITE_ACCEPTED, $event->get_type());
// the client reads the metadata
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\CLIENT_METADATA_READ, $event->get_type());
// the client reads the reply
$this->assertEquals(Grpc\CALL_OK, $call->start_read($tag));
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\READ, $event->get_type());
$this->assertEquals($reply_text, $event->get_data());
// the client sends writes done
$this->assertEquals(Grpc\CALL_OK, $call->writes_done($tag));
$event = $this->client_queue->next($deadline);
$this->assertEquals(Grpc\FINISH_ACCEPTED, $event->get_type());
$this->assertEquals(Grpc\OP_OK, $event->get_data());
// the server sends the status
$this->assertEquals(Grpc\CALL_OK,
$server_call->start_write_status(GRPC\STATUS_OK,
$status_text,
$server_tag));
$event = $this->server_queue->next($deadline);
$this->assertEquals(Grpc\FINISH_ACCEPTED, $event->get_type());
$this->assertEquals(Grpc\OP_OK, $event->get_data());
// the client gets FINISHED
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\FINISHED, $event->get_type());
$status = $event->get_data();
$this->assertEquals(Grpc\STATUS_OK, $status->code);
$this->assertEquals($status_text, $status->details);
// and the server gets FINISHED
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\FINISHED, $event->get_type());
unset($call);
unset($server_call);
}
}

@ -0,0 +1,196 @@
<?php
class SecureEndToEndTest extends PHPUnit_Framework_TestCase{
public function setUp() {
$this->client_queue = new Grpc\CompletionQueue();
$this->server_queue = new Grpc\CompletionQueue();
$credentials = Grpc\Credentials::createSsl(
file_get_contents(dirname(__FILE__) . '/../data/ca.pem'));
$server_credentials = Grpc\ServerCredentials::createSsl(
null,
file_get_contents(dirname(__FILE__) . '/../data/server1.key'),
file_get_contents(dirname(__FILE__) . '/../data/server1.pem'));
$this->server = new Grpc\Server($this->server_queue,
['credentials' => $server_credentials]);
$this->server->add_secure_http2_port('localhost:9000');
$this->channel = new Grpc\Channel(
'localhost:9000',
[
'grpc.ssl_target_name_override' => 'foo.test.google.com',
'credentials' => $credentials
]);
}
public function tearDown() {
unset($this->channel);
unset($this->server);
unset($this->client_queue);
unset($this->server_queue);
}
public function testSimpleRequestBody() {
$this->server->start();
$deadline = Grpc\Timeval::inf_future();
$status_text = 'xyz';
$call = new Grpc\Call($this->channel,
'dummy_method',
$deadline);
$tag = 1;
$this->assertEquals(Grpc\CALL_OK,
$call->start_invoke($this->client_queue,
$tag,
$tag,
$tag));
$server_tag = 2;
// the client invocation was accepted
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\INVOKE_ACCEPTED, $event->get_type());
$this->assertEquals(Grpc\CALL_OK, $call->writes_done($tag));
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\FINISH_ACCEPTED, $event->get_type());
$this->assertEquals(Grpc\OP_OK, $event->get_data());
// check that a server rpc new was received
$this->assertEquals(Grpc\CALL_OK, $this->server->request_call($server_tag));
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\SERVER_RPC_NEW, $event->get_type());
$server_call = $event->get_call();
$this->assertNotNull($server_call);
$this->assertEquals(Grpc\CALL_OK,
$server_call->accept($this->server_queue, $server_tag));
// the server sends the status
$this->assertEquals(Grpc\CALL_OK,
$server_call->start_write_status(Grpc\STATUS_OK,
$status_text,
$server_tag));
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\FINISH_ACCEPTED, $event->get_type());
$this->assertEquals(Grpc\OP_OK, $event->get_data());
// the client gets CLIENT_METADATA_READ
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\CLIENT_METADATA_READ, $event->get_type());
// the client gets FINISHED
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\FINISHED, $event->get_type());
$status = $event->get_data();
$this->assertEquals(Grpc\STATUS_OK, $status->code);
$this->assertEquals($status_text, $status->details);
// and the server gets FINISHED
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\FINISHED, $event->get_type());
$status = $event->get_data();
unset($call);
unset($server_call);
}
public function testClientServerFullRequestResponse() {
$this->server->start();
$deadline = Grpc\Timeval::inf_future();
$req_text = 'client_server_full_request_response';
$reply_text = 'reply:client_server_full_request_response';
$status_text = 'status:client_server_full_response_text';
$call = new Grpc\Call($this->channel,
'dummy_method',
$deadline);
$tag = 1;
$this->assertEquals(Grpc\CALL_OK,
$call->start_invoke($this->client_queue,
$tag,
$tag,
$tag));
$server_tag = 2;
// the client invocation was accepted
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\INVOKE_ACCEPTED, $event->get_type());
// the client writes
$this->assertEquals(Grpc\CALL_OK, $call->start_write($req_text, $tag));
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\WRITE_ACCEPTED, $event->get_type());
// check that a server rpc new was received
$this->assertEquals(Grpc\CALL_OK, $this->server->request_call($server_tag));
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\SERVER_RPC_NEW, $event->get_type());
$server_call = $event->get_call();
$this->assertNotNull($server_call);
$this->assertEquals(Grpc\CALL_OK,
$server_call->accept($this->server_queue, $server_tag));
// start the server read
$this->assertEquals(Grpc\CALL_OK, $server_call->start_read($server_tag));
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\READ, $event->get_type());
$this->assertEquals($req_text, $event->get_data());
// the server replies
$this->assertEquals(Grpc\CALL_OK,
$server_call->start_write($reply_text, $server_tag));
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\WRITE_ACCEPTED, $event->get_type());
// the client reads the metadata
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\CLIENT_METADATA_READ, $event->get_type());
// the client reads the reply
$this->assertEquals(Grpc\CALL_OK, $call->start_read($tag));
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\READ, $event->get_type());
$this->assertEquals($reply_text, $event->get_data());
// the client sends writes done
$this->assertEquals(Grpc\CALL_OK, $call->writes_done($tag));
$event = $this->client_queue->next($deadline);
$this->assertEquals(Grpc\FINISH_ACCEPTED, $event->get_type());
$this->assertEquals(Grpc\OP_OK, $event->get_data());
// the server sends the status
$this->assertEquals(Grpc\CALL_OK,
$server_call->start_write_status(GRPC\STATUS_OK,
$status_text,
$server_tag));
$event = $this->server_queue->next($deadline);
$this->assertEquals(Grpc\FINISH_ACCEPTED, $event->get_type());
$this->assertEquals(Grpc\OP_OK, $event->get_data());
// the client gets FINISHED
$event = $this->client_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\FINISHED, $event->get_type());
$status = $event->get_data();
$this->assertEquals(Grpc\STATUS_OK, $status->code);
$this->assertEquals($status_text, $status->details);
// and the server gets FINISHED
$event = $this->server_queue->next($deadline);
$this->assertNotNull($event);
$this->assertEquals(Grpc\FINISHED, $event->get_type());
unset($call);
unset($server_call);
}
}

@ -0,0 +1,32 @@
<?php
class TimevalTest extends PHPUnit_Framework_TestCase{
public function testCompareSame() {
$zero = Grpc\Timeval::zero();
$this->assertEquals(0, Grpc\Timeval::compare($zero, $zero));
}
public function testPastIsLessThanZero() {
$zero = Grpc\Timeval::zero();
$past = Grpc\Timeval::inf_past();
$this->assertLessThan(0, Grpc\Timeval::compare($past, $zero));
$this->assertGreaterThan(0, Grpc\Timeval::compare($zero, $past));
}
public function testFutureIsGreaterThanZero() {
$zero = Grpc\Timeval::zero();
$future = Grpc\Timeval::inf_future();
$this->assertLessThan(0, Grpc\Timeval::compare($zero, $future));
$this->assertGreaterThan(0, Grpc\Timeval::compare($future, $zero));
}
/**
* @depends testFutureIsGreaterThanZero
*/
public function testNowIsBetweenZeroAndFuture() {
$zero = Grpc\Timeval::zero();
$future = Grpc\Timeval::inf_future();
$now = Grpc\Timeval::now();
$this->assertLessThan(0, Grpc\Timeval::compare($zero, $now));
$this->assertLessThan(0, Grpc\Timeval::compare($now, $future));
}
}
Loading…
Cancel
Save