Light, concurrent RPC framework for PHP(see also: Yar C framework, Yar Java framework)
Yar is a RPC framework which provides a simple and easy way to do communication between PHP applications, it also offers an ability of doing multiple calls to remote services concurrently.
Yar is an PECL extension, could be installed simply by:
pecl install yar
$/path/to/phpize
$./configure --with-php-config=/path/to/php-config/
$make && make install
Available instructions to configure are
--with-curl=DIR
--enable(disable)-msgpack
--enable(disable)-epoll (require Yar 2.1.2)
Install msgpack for PHP extension:
pecl install msgpack
or for ubuntu user
apt-get install msgpack-php
or , you can get the github source here: https://github.com/msgpack/msgpack-php
configuration:
$phpize
$configure --with-php-config=/path/to/php-config/ --enable-msgpack
$make && make install
NOTE yar.connect_time is a value in milliseconds, which was measured in seconds before 1.2.1.
It's very easy to setup a Yar HTTP RPC Server
<?php
class API {
/**
* the doc info will be generated automatically into service info page.
* @params
* @return
*/
public function some_method($parameter, $option = "foo") {
}
protected function client_can_not_see() {
}
}
$service = new Yar_Server(new API());
$service->handle();
?>
Usual RPC calls are issued as HTTP POST requests.
If a HTTP GET request is issued to the uri(access the api address directly via a browser), the service information (commented section above) will be returned, like:
Since 2.3.0, Yar allows you to custom the output in above example by defining "__info" magic method:
<?php
class API {
protected function __info($markup) {
return "Hello world";
}
}
then If a HTTP GET request is issued, "hello world" will be sent instead.
Since 2.3.0, Yar allows server to authentic client request by Provider/Token fileds in header, for achieve this, you should define a protected magic method named "__auth" in server side:
<?php
class API {
protected function __auth($provider, $token) {
return verify($provider, $token);
}
NOTE __auth method should always be defined as protected
if a Yar server has auth defined, then auth will be called at the very first time for any request,
if __auth method return true, the request will be processed further, otherwise the request will be terminated by an error of "authentication failed"
in clent side, you can specific the provider/token by:
<?php
$client->setOpt(YAR_OPT_PROVIDER, "provider");
$client->setOpt(YAR_OPT_TOKEN, "token");
$client->call();
It's very simple for a PHP client to call remote RPC:
<?php
$client = new Yar_Client("http://host/api/");
/* the following setopt is optinal */
$client->SetOpt(YAR_OPT_CONNECT_TIMEOUT, 1000);
$client->SetOpt(YAR_OPT_HEADER, array("hd1: val", "hd2: val")); //Custom headers, Since 2.0.4
/* call remote service */
$result = $client->some_method("parameter");
?>
<?php
function callback($retval, $callinfo) {
var_dump($retval);
}
function error_callback($type, $error, $callinfo) {
error_log($error);
}
Yar_Concurrent_Client::call("http://host/api/", "some_method", array("parameters"), "callback");
Yar_Concurrent_Client::call("http://host/api/", "some_method", array("parameters")); // if the callback is not specificed,
// callback in loop will be used
Yar_Concurrent_Client::call("http://host/api/", "some_method", array("parameters"), "callback", "error_callback", array(YAR_OPT_PACKAGER => "json"));
//this server accept json packager
Yar_Concurrent_Client::call("http://host/api/", "some_method", array("parameters"), "callback", "error_callback", array(YAR_OPT_TIMEOUT=>1));
//custom timeout
Yar_Concurrent_Client::loop("callback", "error_callback"); //send the requests,
//the error_callback is optional
?>
After Yar 2.1.0, if YAR_OPT_PERSISTENT is set to true, then Yar is able to use HTTP keep-alive to speedup repeated calls to a same address, the link will be released at the end of the PHP request lifecycle.
<?php
$client = new Yar_Client("http://host/api/");
$client->SetOpt(YAR_OPT_PERSISTENT, 1);
$result = $client->some_method("parameter");
/* The following calls will speed up due to keep-alive */
$result = $client->some_other_method1("parameter");
$result = $client->some_other_method2("parameter");
$result = $client->some_other_method3("parameter");
?>
After Yar 2.1.0, if Yar runs on HTTP protocol, YAR_OPT_RESOLVE could be used to define custom hostname resolving.
<?php
$client = new Yar_Client("http://host/api/");
$client->SetOpt(YAR_OPT_RESOLVE, array("host:80:127.0.0.1"));
/* call goes to 127.0.0.1 */
$result = $client->some_method("parameter");
After Yar 2.2.1, if Yar runs on HTTP protocol, YAR_OPT_PROXY could be used to define http proxy , such as fidder or charles.
<?php
$client = new Yar_Client("http://host/api/");
$client->SetOpt(YAR_OPT_PROXY,"127.0.0.1:8888"); //http proxy , Since 2.2.0
/* call goes to 127.0.0.1 */
$result = $client->some_method("parameter");
Yar is no only designed for PHP only, all RPC request and response are transferred by binary data stream.
Key messages are exchanged by a struct called "Yar Header":
#ifdef PHP_WIN32
#pragma pack(push)
#pragma pack(1)
#endif
typedef struct _yar_header {
uint32_t id; // transaction id
uint16_t version; // protocol version
uint32_t magic_num; // default is: 0x80DFEC60
uint32_t reserved;
unsigned char provider[32]; // reqeust from who
unsigned char token[32]; // request token, used for authentication
uint32_t body_len; // request body len
}
#ifndef PHP_WIN32
__attribute__ ((packed))
#endif
yar_header_t;
#ifdef PHP_WIN32
#pragma pack(pop)
#endif
Yar also supports multi packager protocols, which is a char[8] before the header struct, to identicate which packager the body is packaged by.
When a Client do an RPC request , the request is sent as an array(in PHP) like:
<?php
array(
"i" => '', //transaction id
"m" => '', //the method which being called
"p" => array(), //parameters
)
When a server responses, the response is sent also as an array (in PHP) like:
<?php
array(
"i" => '',
"s" => '', //status
"r" => '', //return value
"o" => '', //output
"e" => '', //error or exception
)