php5.5.12 yar,[原]PHP-yar拓展源码解读五-server篇

Server模块提供了一个基于Http的Yar协议的Server实现。

常见的使用方法如下

//随手写的demo.php

function serviceAction(){

$service = new Yar_Server(new OrderServer());

$service->handle();

}

在你所用的框架的action下执行以上代码,并为Ycf-Client提供能通过具体路由访问到以上代码的url,OrderServer即可为所有对Yar_Client的调用提供远程实现。

Server构造器

Yac_Server构造器源码如下:

//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的实现需要检查http header头是否已发送

if (SG(headers_sent)) {

php_error_docref(NULL, E_WARNING, "headers already has been sent");

RETURN_FALSE;

} else {

const char *method;

zval *executor, rv;

//再次检查下构造器中存起来的那个_executor实例变量是否对象类型

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的情况下,根据yar.expose_info输出自动生成的RPC文档或报错

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;

}

}

//执行服务端rpc方法

php_yar_server_handle(executor);

}

RETURN_TRUE;

}

handle()根据请求内容返回 自动生成的RPC文档或者执行远程方法

自动生成文档

由于Yar拓展在PHP Extension层面实现,可以直接访问内核API,所以RPC文档生成 这块 完全不需要使用反射。

php_yar_server_info()通过直接遍历zend_class_entry(内核中用于表示一个类的信息)的function_table(类中的成员方法表)调用php_yar_print_info()来生成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()实现见下

if ((prototype = php_yar_get_function_declaration(f))) {

char *buf, *doc_comment = NULL;

//只有用户函数(PHP语言实现的)才能通过zend_function->op_array拿到注释信息.

//内部函数(内核或拓展实现的方法)对应的Union成员zend_function->zend_internal_function没有注释信息

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

}

}

//内核遍历方法zend_hash_apply_with_argument()用的,表示继续遍历

return ZEND_HASH_APPLY_KEEP;

} /* }}} */

php_yar_get_function_declaration()实现见下

//yar_server.c

static char * php_yar_get_function_declaration(zend_function *fptr) /* {{{ */ {

char *offset, *buf;

uint32_t length = 1024;

//重新分配内存的快捷宏

#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;

}

//方法参数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;) {

//声明了类型为对象的参数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)) {

//其他显式声明类型的参数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++) = ' ';

}

//引用传递的参数format

if (arg_info->pass_by_reference) {

*(offset++) = '&';

}

*(offset++) = '$';

//参数名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);

}

//可选参数默认值format

if (i >= required) {

*(offset++) = ' ';

*(offset++) = '=';

*(offset++) = ' ';

//用户方法

if (fptr->type == ZEND_USER_FUNCTION) {

zend_op *precv = NULL;

//看懂这块代码需要点背景知识,稍微解释下

//用户方法(就是用PHP写的那种),在内核和其他PHP代码一样表现为一个典型的zend_op_array结构,由Zend编译器生成

//zend_op_array包含一个zend_op链表,zend_op这玩意俗称opline指令,是ZendVM的执行指令

//方法的每个参数在zend_op_array 也会有相应的 zend_op对应,这种zend_op的zend_op->opcode 为ZEND_RECV*

//除了zend_function->common里面能拿到的,用户函数参数的其他信息要通过该zend_op获取

//下面这段循环就是遍历zend_op_array中的所有zend_op,找到第i个参数对应的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对应有默认值

if (precv && precv->opcode == ZEND_RECV_INIT

&& precv->op2_type != IS_UNUSED

) {

zval zv, zv_copy;

int use_copy;

//默认值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远程服务的实现

RPC服务的核心方法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};

//构造request实例

response = php_yar_response_instance();

//打开http输入流,获取所有数据

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;

}

//从数据中解析出协议头信息(见header章节)

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

//去掉协议头 获取载荷

payload += sizeof(yar_header_t);

payload_len -= sizeof(yar_header_t);

//通过打包器将载荷反序列化成 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数组转回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;

}

//调用executor的对应方法

//这里是RPC-server的核心,远程服务方法的真正调用点

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;

}

//有返回设置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();

//设置response->err字段

if (EG(exception)) {

php_yar_response_set_exception(response, EG(exception));

EG(exception) = NULL;

}

//正常返回跳转点

response:

//关闭缓冲区,并将缓冲区内容填充到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);

//错误跳转处

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;

} /* }}} */

整体来说还是很好懂的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值