Yar拓展自帶了一個(gè)Yar的PHP客戶端,這一章先講yar的同步客戶端實(shí)現(xiàn)。
我們假設(shè)使用代碼如下:
//demo.php
$client = new Yar_Client("http://www.demoserver.com/rpc/s.php");
$client->SetOpt(YAR_OPT_CONNECT_TIMEOUT, 1000);
echo $client->getOrderMoney(123456);
Client構(gòu)造器
從Yar_Client類的構(gòu)造器看起,Yar_Client的源碼實(shí)現(xiàn)如下:
//yar_client.c
/* {{{ proto Yar_Client::__construct($uri[, array $options = NULL]) */
PHP_METHOD(yar_client, __construct) {
zend_string *url;
zval *options = NULL;
//使用url,option變量來(lái)接收構(gòu)造器的兩個(gè)參數(shù),第一個(gè)為字符串,第二個(gè)為可選參數(shù),可空,類型為數(shù)組
if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "S|a!", &url, &options) == FAILURE) {
return;
}
zend_update_property_str(yar_client_ce, getThis(), ZEND_STRL("_uri"), url);
if (strncasecmp(ZSTR_VAL(url), "http://", sizeof("http://") - 1) == 0
|| strncasecmp(ZSTR_VAL(url), "https://", sizeof("https://") - 1) == 0) {
//_protocol的默認(rèn)值是 YAR_CLIENT_PROTOCOL_HTTP,無(wú)需處理
} else if (strncasecmp(ZSTR_VAL(url), "tcp://", sizeof("tcp://") - 1) == 0) {
zend_update_property_long(yar_client_ce, getThis(), ZEND_STRL("_protocol"), YAR_CLIENT_PROTOCOL_TCP);
} else if (strncasecmp(ZSTR_VAL(url), "unix://", sizeof("unix://") - 1) == 0) {
zend_update_property_long(yar_client_ce, getThis(), ZEND_STRL("_protocol"), YAR_CLIENT_PROTOCOL_UNIX);
} else {
php_yar_client_trigger_error(1, YAR_ERR_PROTOCOL, "unsupported protocol address %s", ZSTR_VAL(url));
return;
}
if (options) {
zend_update_property(yar_client_ce, getThis(), ZEND_STRL("_options"), options);
}
}
/* }}} */
Yar_Client的構(gòu)造器就是將options參數(shù),以及從$uri中解析出的通信協(xié)議寫入成員變量而已。
萬(wàn)能的__call()
不像Java系的RpcClient實(shí)現(xiàn),動(dòng)不動(dòng)來(lái)個(gè)動(dòng)態(tài)代理設(shè)計(jì),在PHP中一個(gè)__call()足以動(dòng)態(tài)"生成"任意遠(yuǎn)程方法。
對(duì)Yar_Client實(shí)例的所有調(diào)用,都直接通過(guò)__call()魔術(shù)方法對(duì)遠(yuǎn)程Yar服務(wù)器執(zhí)行遠(yuǎn)程調(diào)用。
/* {{{ proto Yar_Client::__call($method, $parameters = NULL) */
PHP_METHOD(yar_client, __call) {
zval *params, *protocol, rv;
zend_string *method;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sa", &method, ¶ms) == FAILURE) {
return;
}
protocol = zend_read_property(yar_client_ce, getThis(), ZEND_STRL("_protocol"), 0, &rv);
//Yar拓展目前客戶端支持UnixSocket Tcp Http三種協(xié)議,根據(jù)使用的uri隱式確認(rèn),不同的協(xié)議會(huì)使用不同的傳輸器
switch (Z_LVAL_P(protocol)) {
case YAR_CLIENT_PROTOCOL_TCP:
case YAR_CLIENT_PROTOCOL_UNIX:
case YAR_CLIENT_PROTOCOL_HTTP:
if ((php_yar_client_handle(Z_LVAL_P(protocol), getThis(), method, params, return_value))) {
return;
}
break;
default:
php_error_docref(NULL, E_WARNING, "unsupported protocol %ld", Z_LVAL_P(protocol));
break;
}
RETURN_FALSE;
}
/* }}} */
而Yar_Client->__call()的唯一有效操作是執(zhí)行php_yar_client_handle()
static int php_yar_client_handle(int protocol, zval *client, zend_string *method, zval *params, zval *retval) /* {{{ */ {
char *msg;
zval *uri, *options;
zval rv;
const yar_transport_t *factory;
yar_transport_interface_t *transport;
yar_request_t *request;
yar_response_t *response;
int flags = 0;
uri = zend_read_property(yar_client_ce, client, ZEND_STRL("_uri"), 0, &rv);
//根據(jù)協(xié)議(from uri)獲取傳輸器實(shí)例
if (protocol == YAR_CLIENT_PROTOCOL_HTTP) {
factory = php_yar_transport_get(ZEND_STRL("curl"));
} else if (protocol == YAR_CLIENT_PROTOCOL_TCP || protocol == YAR_CLIENT_PROTOCOL_UNIX) {
factory = php_yar_transport_get(ZEND_STRL("sock"));
} else {
return 0;
}
//傳輸器和傳輸器工廠的細(xì)節(jié)見傳輸器章節(jié)
transport = factory->init();
options = zend_read_property(yar_client_ce, client, ZEND_STRL("_options"), 1, &rv);
if (IS_ARRAY != Z_TYPE_P(options)) {
options = NULL;
}
//構(gòu)造request
if (!(request = php_yar_request_instance(method, params, options))) {
transport->close(transport);
factory->destroy(transport);
return 0;
}
if (YAR_G(allow_persistent)) {
if (options) {
zval *flag = php_yar_client_get_opt(options, YAR_OPT_PERSISTENT);
if (flag && (Z_TYPE_P(flag) == IS_TRUE || (Z_TYPE_P(flag) == IS_LONG && Z_LVAL_P(flag)))) {
flags |= YAR_PROTOCOL_PERSISTENT;
}
}
}
/* This is tricky to pass options in, for custom headers*/
msg = (char*)options;
//調(diào)用傳輸器實(shí)例的open()接口方法 (具體內(nèi)容見傳輸器章節(jié))
if (!transport->open(transport, Z_STR_P(uri), flags, &msg)) {
php_yar_client_trigger_error(1, YAR_ERR_TRANSPORT, msg);
php_yar_request_destroy(request);
efree(msg);
transport->close(transport);
factory->destroy(transport);
return 0;
}
DEBUG_C(ZEND_ULONG_FMT": call api '%s' at (%c)'%s' with '%d' parameters",
request->id, ZSTR_VAL(request->method), (flags & YAR_PROTOCOL_PERSISTENT)? 'p' : 'r', Z_STRVAL_P(uri),
zend_hash_num_elements(Z_ARRVAL(request->parameters)));
//調(diào)用傳輸器實(shí)例的send()接口方法
if (!transport->send(transport, request, &msg)) {
php_yar_client_trigger_error(1, YAR_ERR_TRANSPORT, msg);
php_yar_request_destroy(request);
efree(msg);
transport->close(transport);
factory->destroy(transport);
return 0;
}
//調(diào)用傳輸器實(shí)例的exec()接口方法,獲取響應(yīng)對(duì)應(yīng)的response變量
response = transport->exec(transport, request);
if (response->status != YAR_ERR_OKEY) {
php_yar_client_handle_error(1, response);
php_yar_request_destroy(request);
php_yar_response_destroy(response);
transport->close(transport);
factory->destroy(transport);
return 0;
} else {
//打印 遠(yuǎn)程服務(wù)器的(如)標(biāo)準(zhǔn)輸出,事實(shí)上沒有人會(huì)糟糕到直接利用遠(yuǎn)程服務(wù)的標(biāo)準(zhǔn)輸出
//這個(gè)功能的主要使用場(chǎng)景有兩個(gè)
//1個(gè)是代替遠(yuǎn)程服務(wù)器打印各種Notice,Warning信息
//2是將RPC服務(wù)文檔從RPC Server的標(biāo)準(zhǔn)輸出轉(zhuǎn)移到RPC Client的標(biāo)準(zhǔn)輸出上。
if (response->out && ZSTR_LEN(response->out)) {
PHPWRITE(ZSTR_VAL(response->out), ZSTR_LEN(response->out));
}
//從responce中獲取遠(yuǎn)程RPC方法的返回值作為PHP方法的返回值返回
ZVAL_COPY(retval, &response->retval);
//相關(guān)資源釋放
php_yar_request_destroy(request);
php_yar_response_destroy(response);
transport->close(transport);
factory->destroy(transport);
return 1;
}
} /* }}} */
整理來(lái)說(shuō)還是比較容易理解的,傳輸細(xì)節(jié)在傳輸器章節(jié)中再講。