Server模塊提供了一個(gè)基于Http的Yar協(xié)議的Server實(shí)現(xiàn)殉挽。
常見的使用方法如下
//隨手寫的demo.php
function serviceAction(){
$service = new Yar_Server(new OrderServer());
$service->handle();
}
在你所用的框架的action下執(zhí)行以上代碼,并為Ycf-Client
提供能通過(guò)具體路由訪問(wèn)到以上代碼的url空凸,OrderServer
即可為所有對(duì)Yar_Client
的調(diào)用提供遠(yuǎn)程實(shí)現(xiàn)福贞。
Server構(gòu)造器
Yac_Server構(gòu)造器源碼如下:
//yar_server.c
PHP_METHOD(yar_server, __construct) {
zval *obj;
if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "o", &obj) == FAILURE) {
return;
}
zend_update_property(yar_server_ce, getThis(), "_executor", sizeof("_executor")-1, obj);
}
僅僅是Yac_Server將new Yar_Server(new ServerObject())
中的$serverObject
寫入成員變量$_executor
而已寡润。
Yar_Server->handle()
//yar_server.c
PHP_METHOD(yar_server, handle)
{
//基于http的實(shí)現(xiàn)需要檢查http header頭是否已發(fā)送
if (SG(headers_sent)) {
php_error_docref(NULL, E_WARNING, "headers already has been sent");
RETURN_FALSE;
} else {
const char *method;
zval *executor, rv;
//再次檢查下構(gòu)造器中存起來(lái)的那個(gè)_executor實(shí)例變量是否對(duì)象類型
executor = zend_read_property(yar_server_ce, getThis(), ZEND_STRL("_executor"), 0, &rv);
if (IS_OBJECT != Z_TYPE_P(executor)) {
php_error_docref(NULL, E_WARNING, "executor is not a valid object");
RETURN_FALSE;
}
//非post的情況下我注,根據(jù)yar.expose_info輸出自動(dòng)生成的RPC文檔或報(bào)錯(cuò)
method = SG(request_info).request_method;
if (!method || strncasecmp(method, "POST", 4)) {
if (YAR_G(expose_info)) {
php_yar_server_info(executor);
RETURN_TRUE;
} else {
zend_throw_exception(yar_server_exception_ce, "server info is not allowed to access", YAR_ERR_FORBIDDEN);
return;
}
}
//執(zhí)行服務(wù)端rpc方法
php_yar_server_handle(executor);
}
RETURN_TRUE;
}
handle()
根據(jù)請(qǐng)求內(nèi)容返回 自動(dòng)生成的RPC文檔或者執(zhí)行遠(yuǎn)程方法
自動(dòng)生成文檔
由于Yar拓展在PHP Extension層面實(shí)現(xiàn),可以直接訪問(wèn)內(nèi)核API迟隅,所以RPC文檔生成 這塊 完全不需要使用反射
但骨。
php_yar_server_info()
通過(guò)直接遍歷zend_class_entry
(內(nèi)核中用于表示一個(gè)類的信息)的function_table
(類中的成員方法表)調(diào)用php_yar_print_info()
來(lái)生成API文檔并打印,
//yar_server.c
static int php_yar_print_info(zval *ptr, void *argument) /* {{{ */ {
zend_function *f = Z_FUNC_P(ptr);
//僅展示可見性為public励七,且方法名不以“_”開頭的方法
if (f->common.fn_flags & ZEND_ACC_PUBLIC
&& f->common.function_name && *(ZSTR_VAL(f->common.function_name)) != '_') {
char *prototype = NULL;
//從zend_function獲取固定格式的方法聲明信息,php_yar_get_function_declaration()實(shí)現(xiàn)見下
if ((prototype = php_yar_get_function_declaration(f))) {
char *buf, *doc_comment = NULL;
//只有用戶函數(shù)(PHP語(yǔ)言實(shí)現(xiàn)的)才能通過(guò)zend_function->op_array拿到注釋信息.
//內(nèi)部函數(shù)(內(nèi)核或拓展實(shí)現(xiàn)的方法)對(duì)應(yīng)的Union成員zend_function->zend_internal_function沒(méi)有注釋信息
if (f->type == ZEND_USER_FUNCTION) {
if (f->op_array.doc_comment != NULL) {
doc_comment = (char *)ZSTR_VAL(f->op_array.doc_comment);
}
}
//使用html模板format出接口文檔并打印
spprintf(&buf, 0, HTML_MARKUP_ENTRY, prototype, doc_comment? doc_comment : "");
efree(prototype);
PHPWRITE(buf, strlen(buf));
efree(buf);
}
}
//內(nèi)核遍歷方法zend_hash_apply_with_argument()用的奔缠,表示繼續(xù)遍歷
return ZEND_HASH_APPLY_KEEP;
} /* }}} */
php_yar_get_function_declaration()
實(shí)現(xiàn)見下
//yar_server.c
static char * php_yar_get_function_declaration(zend_function *fptr) /* {{{ */ {
char *offset, *buf;
uint32_t length = 1024;
//重新分配內(nèi)存的快捷宏
#define REALLOC_BUF_IF_EXCEED(buf, offset, length, size) \
if (offset - buf + size >= length) { \
length += size + 1; \
buf = erealloc(buf, length); \
}
//可見性檢查
if (!(fptr->common.fn_flags & ZEND_ACC_PUBLIC)) {
return NULL;
}
offset = buf = (char *)emalloc(length * sizeof(char));
//方法返回值是否引用類型format
if (fptr->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) {
*(offset++) = '&';
*(offset++) = ' ';
}
//類名format
if (fptr->common.scope) {
memcpy(offset, ZSTR_VAL(fptr->common.scope->name), ZSTR_LEN(fptr->common.scope->name));
offset += ZSTR_LEN(fptr->common.scope->name);
*(offset++) = ':';
*(offset++) = ':';
}
//方法名format
{
size_t name_len = ZSTR_LEN(fptr->common.function_name);
REALLOC_BUF_IF_EXCEED(buf, offset, length, name_len);
memcpy(offset, ZSTR_VAL(fptr->common.function_name), name_len);
offset += name_len;
}
//方法參數(shù)format
*(offset++) = '(';
if (fptr->common.arg_info) {
uint32_t i, required;
zend_arg_info *arg_info = fptr->common.arg_info;
required = fptr->common.required_num_args;
for (i = 0; i < fptr->common.num_args;) {
//聲明了類型為對(duì)象的參數(shù)format
if (ZEND_TYPE_IS_CLASS(arg_info->type)) {
const char *class_name;
uint32_t class_name_len;
zend_string *class_str = ZEND_TYPE_NAME(arg_info->type);
class_name = ZSTR_VAL(class_str);
class_name_len = ZSTR_LEN(class_str);
if (strncasecmp(class_name, "self", sizeof("self")) && fptr->common.scope ) {
class_name = ZSTR_VAL(fptr->common.scope->name);
class_name_len = ZSTR_LEN(fptr->common.scope->name);
} else if (strncasecmp(class_name, "parent", sizeof("parent")) && fptr->common.scope->parent) {
class_name = ZSTR_VAL(fptr->common.scope->parent->name);
class_name_len = ZSTR_LEN(fptr->common.scope->parent->name);
}
REALLOC_BUF_IF_EXCEED(buf, offset, length, class_name_len);
memcpy(offset, class_name, class_name_len);
offset += class_name_len;
*(offset++) = ' ';
} else if (ZEND_TYPE_IS_CODE(arg_info->type)) {
//其他顯式聲明類型的參數(shù)format
uint32_t type_name_len;
char *type_name = zend_get_type_by_const(ZEND_TYPE_CODE(arg_info->type));
type_name_len = strlen(type_name);
REALLOC_BUF_IF_EXCEED(buf, offset, length, type_name_len);
memcpy(offset, type_name, type_name_len);
offset += type_name_len;
*(offset++) = ' ';
}
//引用傳遞的參數(shù)format
if (arg_info->pass_by_reference) {
*(offset++) = '&';
}
*(offset++) = '$';
//參數(shù)名format
if (arg_info->name) {
const char *name;
uint32_t name_len;
if (fptr->type == ZEND_INTERNAL_FUNCTION) {
name = ((zend_internal_arg_info*)arg_info)->name;
name_len = strlen(name);
} else {
name = ZSTR_VAL(arg_info->name);
name_len = ZSTR_LEN(arg_info->name);
}
REALLOC_BUF_IF_EXCEED(buf, offset, length, name_len);
memcpy(offset, name, name_len);
offset += name_len;
} else {
uint32_t idx = i;
memcpy(offset, "param", 5);
offset += 5;
do {
*(offset++) = (char) (idx % 10) + '0';
idx /= 10;
} while (idx > 0);
}
//可選參數(shù)默認(rèn)值format
if (i >= required) {
*(offset++) = ' ';
*(offset++) = '=';
*(offset++) = ' ';
//用戶方法
if (fptr->type == ZEND_USER_FUNCTION) {
zend_op *precv = NULL;
//看懂這塊代碼需要點(diǎn)背景知識(shí)掠抬,稍微解釋下
//用戶方法(就是用PHP寫的那種),在內(nèi)核和其他PHP代碼一樣表現(xiàn)為一個(gè)典型的zend_op_array結(jié)構(gòu)校哎,由Zend編譯器生成
//zend_op_array包含一個(gè)zend_op鏈表,zend_op這玩意俗稱opline指令两波,是ZendVM的執(zhí)行指令
//方法的每個(gè)參數(shù)在zend_op_array 也會(huì)有相應(yīng)的 zend_op對(duì)應(yīng),這種zend_op的zend_op->opcode 為ZEND_RECV*
//除了zend_function->common里面能拿到的闷哆,用戶函數(shù)參數(shù)的其他信息要通過(guò)該zend_op獲取
//下面這段循環(huán)就是遍歷zend_op_array中的所有zend_op腰奋,找到第i個(gè)參數(shù)對(duì)應(yīng)的zend_op
//有興趣的可以看下zend_compile.c的zend_compile_params()
{
uint32_t idx = i;
zend_op *op = ((zend_op_array *)fptr)->opcodes;
zend_op *end = op + ((zend_op_array *)fptr)->last;
++idx;
while (op < end) {
if ((op->opcode == ZEND_RECV || op->opcode == ZEND_RECV_INIT)
&& op->op1.num == (long)idx
) {
precv = op;
}
++op;
}
}
//ZEND_RECV_INIT對(duì)應(yīng)有默認(rèn)值
if (precv && precv->opcode == ZEND_RECV_INIT
&& precv->op2_type != IS_UNUSED
) {
zval zv, zv_copy;
int use_copy;
//默認(rèn)值format
ZVAL_DUP(&zv, RT_CONSTANT(&fptr->op_array, precv->op2));
#if PHP_VERSION_ID < 70100
zval_update_constant_ex(&zv, 1, fptr->common.scope);
#else
zval_update_constant_ex(&zv, fptr->common.scope);
#endif
if (Z_TYPE(zv) == IS_TRUE) {
memcpy(offset, "true", 4);
offset += 4;
} else if (Z_TYPE(zv) == IS_FALSE) {
memcpy(offset, "false", 5);
offset += 5;
} else if (Z_TYPE(zv) == IS_NULL) {
memcpy(offset, "NULL", 4);
offset += 4;
} else if (Z_TYPE(zv) == IS_STRING) {
*(offset++) = '\'';
REALLOC_BUF_IF_EXCEED(buf, offset, length, MIN(Z_STRLEN(zv), 10));
memcpy(offset, Z_STRVAL(zv), MIN(Z_STRLEN(zv), 10));
offset += MIN(Z_STRLEN(zv), 10);
if (Z_STRLEN(zv) > 10) {
*(offset++) = '.';
*(offset++) = '.';
*(offset++) = '.';
}
*(offset++) = '\'';
} else if (Z_TYPE(zv) == IS_ARRAY) {
memcpy(offset, "Array", 5);
offset += 5;
} else {
use_copy = zend_make_printable_zval(&zv, &zv_copy);
REALLOC_BUF_IF_EXCEED(buf, offset, length, Z_STRLEN(zv_copy));
memcpy(offset, Z_STRVAL(zv_copy), Z_STRLEN(zv_copy));
offset += Z_STRLEN(zv_copy);
if (use_copy) {
zval_dtor(&zv_copy);
}
}
zval_ptr_dtor(&zv);
}
} else {
memcpy(offset, "NULL", 4);
offset += 4;
}
}
if (++i < fptr->common.num_args) {
*(offset++) = ',';
*(offset++) = ' ';
}
arg_info++;
REALLOC_BUF_IF_EXCEED(buf, offset, length, 32);
}
}
*(offset++) = ')';
*offset = '\0';
return buf;
}
RPC遠(yuǎn)程服務(wù)的實(shí)現(xiàn)
RPC服務(wù)的核心方法php_yar_server_handle 如下:
static void php_yar_server_handle(zval *obj) /* {{{ */ {
char *payload, *err_msg;
char *pkg_name = NULL;
size_t payload_len;
zend_bool bailout = 0;
zend_string *method;
zval *post_data = NULL, output, func, rret;
zend_class_entry *ce;
yar_response_t *response;
yar_request_t *request = NULL;
yar_header_t *header;
php_stream *s;
smart_str raw_data = {0};
//構(gòu)造request實(shí)例
response = php_yar_response_instance();
//打開http輸入流,獲取所有數(shù)據(jù)
s = SG(request_info).request_body;
if (!s || FAILURE == php_stream_rewind(s)) {
php_yar_error(response, YAR_ERR_PACKAGER, "empty request");
DEBUG_S("0: empty request");
goto response_no_output;
}
while (!php_stream_eof(s)) {
char buf[512];
size_t len = php_stream_read(s, buf, sizeof(buf));
if (len && len != (size_t) -1) {
smart_str_appendl(&raw_data, buf, len);
}
}
//從smart_str獲得payload的zend_string
if (raw_data.s) {
smart_str_0(&raw_data);
payload = ZSTR_VAL(raw_data.s);
payload_len = ZSTR_LEN(raw_data.s);
} else {
php_yar_error(response, YAR_ERR_PACKAGER, "empty request body");
DEBUG_S("0: empty request '%s'");
goto response_no_output;
}
//從數(shù)據(jù)中解析出協(xié)議頭信息(見header章節(jié))
if (!(header = php_yar_protocol_parse(payload))) {
php_yar_error(response, YAR_ERR_PACKAGER, "malformed request header '%.10s'", payload);
DEBUG_S("0: malformed request '%s'", payload);
goto response_no_output;
}
DEBUG_S("%ld: accpect rpc request form '%s'",
header->id, header->provider? (char *)header->provider : "Yar PHP " PHP_YAR_VERSION);
//去掉協(xié)議頭 獲取載荷
payload += sizeof(yar_header_t);
payload_len -= sizeof(yar_header_t);
//通過(guò)打包器將載荷反序列化成 PHP變量到rret/post_data
if (!(post_data = php_yar_packager_unpack(payload, payload_len, &err_msg, &rret))) {
php_yar_error(response, YAR_ERR_PACKAGER, err_msg);
efree(err_msg);
goto response_no_output;
}
pkg_name = payload;
//IMP數(shù)組轉(zhuǎn)回request
request = php_yar_request_unpack(post_data);
zval_ptr_dtor(post_data);
if (!php_yar_request_valid(request, response, &err_msg)) {
php_yar_error(response, YAR_ERR_REQUEST, "%s", err_msg);
efree(err_msg);
goto response_no_output;
}
//打開輸出緩沖
if (php_output_start_user(NULL, 0, PHP_OUTPUT_HANDLER_STDFLAGS) == FAILURE) {
php_yar_error(response, YAR_ERR_OUTPUT, "start output buffer failed");
goto response_no_output;
}
ce = Z_OBJCE_P(obj);
method = zend_string_tolower(request->method);
if (!zend_hash_exists(&ce->function_table, method)) {
zend_string_release(method);
php_yar_error(response, YAR_ERR_REQUEST, "call to undefined api %s::%s()", ce->name, ZSTR_VAL(request->method));
goto response;
}
zend_string_release(method);
//用sigsetjmp()模擬的try catch
zend_try {
int count;
zval *func_params;
zval *tmp_zval;
zval retval;
HashTable *func_params_ht;
func_params_ht = Z_ARRVAL(request->parameters);
count = zend_hash_num_elements(func_params_ht);
if (count) {
int i = 0;
func_params = safe_emalloc(sizeof(zval), count, 0);
ZEND_HASH_FOREACH_VAL(func_params_ht, tmp_zval) {
ZVAL_COPY(&func_params[i++], tmp_zval);
} ZEND_HASH_FOREACH_END();
} else {
func_params = NULL;
}
//調(diào)用executor的對(duì)應(yīng)方法
//這里是RPC-server的核心抱怔,遠(yuǎn)程服務(wù)方法的真正調(diào)用點(diǎn)
ZVAL_STR(&func, request->method);
if (FAILURE == call_user_function_ex(NULL, obj, &func, &retval, count, func_params, 0, NULL)) {
if (count) {
int i = 0;
for (; i < count; i++) {
zval_ptr_dtor(&func_params[i]);
}
}
php_yar_error(response, YAR_ERR_REQUEST, "call to api %s::%s() failed", ce->name, request->method);
goto response;
}
//有返回設(shè)置response->retval
if (!Z_ISUNDEF(retval)) {
php_yar_response_set_retval(response, &retval);
zval_ptr_dtor(&retval);
}
if (count) {
int i = 0;
for (; i < count; i++) {
zval_ptr_dtor(&func_params[i]);
}
}
} zend_catch {
bailout = 1;
} zend_end_try();
//設(shè)置response->err字段
if (EG(exception)) {
php_yar_response_set_exception(response, EG(exception));
EG(exception) = NULL;
}
//正常返回跳轉(zhuǎn)點(diǎn)
response:
//關(guān)閉緩沖區(qū)劣坊,并將緩沖區(qū)內(nèi)容填充到response->out字段
if (php_output_get_contents(&output) == FAILURE) {
php_output_end();
php_yar_error(response, YAR_ERR_OUTPUT, "unable to get ob content");
goto response_no_output;
}
php_output_discard();
php_yar_response_alter_body(response, Z_STR(output), YAR_RESPONSE_REPLACE);
//錯(cuò)誤跳轉(zhuǎn)處
response_no_output:
php_yar_server_response(request, response, pkg_name);
if (request) {
php_yar_request_destroy(request);
}
smart_str_free(&raw_data);
php_yar_response_destroy(response);
if (bailout) {
zend_bailout();
}
return;
} /* }}} */
整體來(lái)說(shuō)還是很好懂的。