Light, concurrent RPC framework for PHP (see also: Yar C framework, Yar Java framework, Lua Yar framework)
- PHP 7.0+ (master branch)
- PHP 5.2+ (php5 branch)
- Curl
- Json
- Msgpack (Optional)
Yar is an RPC framework which provides a simple and easy way to do communication between PHP applications. It also offers the ability to make multiple calls to remote services concurrently.
Yar is a native PHP extension — not a userland library. It uses a compact binary protocol (yar_header_t + packager payload) over HTTP or TCP, with no external runtime dependencies beyond curl. This means no Composer packages, no framework bootstrap, no separate proxy process. It's designed for the "just works" experience: install the extension, write a few lines of PHP, and you have RPC.
- Best for: RPC between PHP applications (or any combination of PHP, C, Java, and Lua via the respective Yar implementations). Microservices within the same infrastructure. Scenarios where low latency and minimal operational overhead matter more than schema-driven code generation.
- Not ideal for: Public-facing APIs consumed by arbitrary third-party clients (use REST or gRPC with Protobuf instead). Environments that require built-in service discovery, load balancing, or streaming (gRPC is a better fit there).
- Fast, easy, simple
- Concurrent RPC calls
- Multiple data packagers supported (php, json, msgpack built-in)
- Multiple transfer protocols supported (HTTP, HTTPS, TCP)
- Detailed debug information
Yar is a PECL extension, simply install it by:
$ pecl install yar$ /path/to/phpize
$ ./configure --with-php-config=/path/to/php-config/
$ make && make installAvailable configure options:
--with-curl=DIR
--enable-msgpack / --disable-msgpack
--enable-epoll / --disable-epoll (requires Yar 2.1.2)--enable-epoll replaces the default select()-based I/O multiplexing with Linux epoll. This can improve performance for Yar_Concurrent_Client under high concurrency. It only affects Linux; on other platforms it has no effect.
- Install msgpack extension for PHP:
$ pecl install msgpackOr for Ubuntu:
$ apt-get install msgpack-phpOr, get the source from GitHub: https://github.com/msgpack/msgpack-php
- Configure with msgpack enabled:
$ /path/to/phpize
$ ./configure --with-php-config=/path/to/php-config/ --enable-msgpack
$ make && make install| INI Setting | Default | Description |
|---|---|---|
yar.timeout |
5000 |
Timeout in milliseconds |
yar.connect_timeout |
1000 |
Connection timeout in milliseconds |
yar.packager |
"php" (or "msgpack" if built with --enable-msgpack) |
One of "php", "json", "msgpack" |
yar.debug |
Off |
Enable debug mode. When enabled, Yar emits E_WARNING messages with detailed protocol-level information for every request and response, prefixed with [Debug Yar_Server] or [Debug Yar_Client] and including timestamps. |
yar.expose_info |
On |
Whether to output the API info page for GET requests |
yar.content_type |
"application/octet-stream" |
Content-Type sent in responses |
yar.ssl_verify |
Off |
Whether to verify the TLS certificate of HTTPS servers. When enabled, the curl transport sets CURLOPT_SSL_VERIFYPEER and CURLOPT_SSL_VERIFYHOST, and requests against servers with invalid certificates will fail. Disabled by default for backward compatibility. Available since 2.4.0. |
Note:
yar.connect_timeoutis in milliseconds. Prior to 1.2.1 it was measured in seconds.
YAR_OPT_PACKAGER
YAR_OPT_PERSISTENT
YAR_OPT_TIMEOUT
YAR_OPT_CONNECT_TIMEOUT
YAR_OPT_HEADER // Since 2.0.4
YAR_OPT_PROXY // Since 2.2.0
YAR_OPT_RESOLVE // Since 2.1.0
YAR_OPT_PROVIDER // Since 2.3.0
YAR_OPT_TOKEN // Since 2.3.0YAR_VERSION
YAR_HAS_MSGPACK // 1 if compiled with --enable-msgpack, 0 otherwiseUsed by Yar_Server_Exception::getType() and Yar_Client_Exception::getType() to indicate the nature of the error:
YAR_ERR_OKEY = 0x00 // No error
YAR_ERR_PACKAGER = 0x01 // Packager error
YAR_ERR_PROTOCOL = 0x02 // Protocol error
YAR_ERR_REQUEST = 0x04 // Request error
YAR_ERR_OUTPUT = 0x08 // Output error
YAR_ERR_TRANSPORT = 0x10 // Transport error
YAR_ERR_FORBIDDEN = 0x20 // Forbidden (auth failed or info page disabled)
YAR_ERR_EXCEPTION = 0x40 // General exceptionYar defines a structured exception hierarchy for both server and client errors:
Exception / RuntimeException
├── Yar_Server_Exception
│ ├── Yar_Server_Request_Exception
│ ├── Yar_Server_Protocol_Exception
│ ├── Yar_Server_Packager_Exception
│ └── Yar_Server_Output_Exception
└── Yar_Client_Exception
├── Yar_Client_Transport_Exception
├── Yar_Client_Protocol_Exception
└── Yar_Client_Packager_Exception
Both Yar_Server_Exception and Yar_Client_Exception extend Exception (or RuntimeException if SPL is available).
Yar_Server_Exception::getType() returns the error type constant (e.g. YAR_ERR_TRANSPORT).
Yar_Client_Exception::getType() returns the string "Yar_Exception_Client" — this is the exception class name, not an error type constant. If you need the actual error type on the client side, catch the specific sub-exception classes instead (e.g. Yar_Client_Transport_Exception, Yar_Client_Protocol_Exception, Yar_Client_Packager_Exception).
Note:
Yar_Serveris afinalclass and cannot be extended.
It's very easy to set up a Yar HTTP RPC Server:
<?php
class API
{
/**
* The doc info will be generated automatically into the 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 an HTTP GET request is issued to the URI (access the API address directly via a browser), the service info page (generated from the doc comments above) will be returned:
Yar_Server::__construct(object $executor)Creates a new Yar server wrapping the given $executor object. All public methods of $executor are exposed as RPC endpoints.
Yar_Server::handle(): boolStarts processing the incoming RPC request. Returns true on success.
Yar's PHP extension only provides an HTTP server. For TCP-based RPC servers, use the standalone Yar C framework. It supports TCP and Unix socket protocols and is fully compatible with Yar PHP clients. The C framework is also the recommended backend for production deployments with high throughput requirements.
Since 2.3.0, you can customise the output of the service info page by defining a __info magic method:
<?php
class API
{
protected function __info($markup)
{
return "Hello world";
}
}Then if an HTTP GET request is issued, "Hello world" will be returned instead.
Since 2.3.0, Yar allows the server to authenticate client requests via Provider / Token fields in the header. To enable this, define a protected magic method named __auth on the server side:
<?php
class API
{
protected function __auth($provider, $token)
{
return verify($provider, $token);
}
}Note:
__authmust always be defined asprotected.
If __auth is defined, it will be called at the very beginning of every request:
- If
__authreturnstrue(or any truthy value —1, a non-empty string, a non-empty array, etc.), the request proceeds. - If
__authreturnsfalse(exactlyfalse), the request is terminated with an "authentication failed" error (YAR_ERR_FORBIDDEN).
On the client side, specify the provider and token via:
<?php
$client->setOpt(YAR_OPT_PROVIDER, "provider");
$client->setOpt(YAR_OPT_TOKEN, "token");
$client->call();Note:
Yar_Clientis afinalclass and cannot be extended.
Yar_Client::__construct(string $uri[, array $options = null])Creates a new Yar client. The $uri is the server address (e.g. "http://host/api/" or "tcp://host:port").
$options is an optional array of initial options, e.g.:
$client = new Yar_Client("http://host/api/", [
YAR_OPT_CONNECT_TIMEOUT => 1000,
YAR_OPT_PERSISTENT => 1,
]);Yar_Client::setOpt(int $type, mixed $value): Yar_Client|boolSet a client option. Returns $this on success (for chaining), false on failure.
See Option Constants for available $type values.
Yar_Client::getOpt(int $type): mixedGet the current value of a client option.
Yar_Client::call(string $method, array $arguments): mixedCall a remote method by name. Returns the result on success.
Yar_Client supports PHP's __call, so these are equivalent:
$client->call("some_method", [$arg1, $arg2]);
$client->some_method($arg1, $arg2);<?php
$client = new Yar_Client("http://host/api/");
/* the following setOpt calls are optional */
$client->setOpt(YAR_OPT_CONNECT_TIMEOUT, 1000);
$client->setOpt(YAR_OPT_HEADER, ["hd1: val", "hd2: val"]); // Custom headers, Since 2.0.4
/* call remote service */
$result = $client->some_method("parameter");Yar supports sending multiple calls concurrently and collecting the results via a callback loop.
Note:
Yar_Concurrent_Clientonly supports HTTP/HTTPS protocol. TCP and Unix socket concurrent calls are not available — use individualYar_Clientinstances for those.Note: A maximum of 128 concurrent calls can be registered in a single
loop(). Exceeding this limit triggers a warning.
Each callback receives two arguments:
$retval— the return value of the remote method$callinfo— an array with call metadata
Each error_callback receives three arguments:
$type— the error type constant$error— the error message$callinfo— an array with call metadata
<?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", ["parameters"], "callback");
// If no callback is specified, the callback in loop() will be used
Yar_Concurrent_Client::call("http://host/api/", "some_method", ["parameters"]);
// This server accepts json packager
Yar_Concurrent_Client::call("http://host/api/", "some_method", ["parameters"],
"callback", "error_callback", [YAR_OPT_PACKAGER => "json"]);
// Custom timeout
Yar_Concurrent_Client::call("http://host/api/", "some_method", ["parameters"],
"callback", "error_callback", [YAR_OPT_TIMEOUT => 1]);
// Send all requests. The error_callback and options are optional.
Yar_Concurrent_Client::loop("callback", "error_callback", [YAR_OPT_PACKAGER => "json"]);Yar_Concurrent_Client::call(
string $uri,
string $method,
?array $arguments = null,
?callable $callback = null,
?callable $error_callback = null,
?array $options = null
): null|int|boolRegisters a concurrent call. Returns null on error, or an opaque int ID on success.
Yar_Concurrent_Client::loop(
?callable $callback = null,
?callable $error_callback = null,
?array $options = null
): ?boolSends all registered concurrent calls and waits for responses. Returns true on success, null on failure.
Yar_Concurrent_Client::reset(): boolClears all registered concurrent calls without sending them. Returns true on success.
Since 2.1.0, if YAR_OPT_PERSISTENT is set to true, Yar will use HTTP keep-alive to speed up repeated calls to the same address. The connection is 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");Since 2.1.0, when running over HTTP, YAR_OPT_RESOLVE can be used to override hostname resolution.
<?php
$client = new Yar_Client("http://host/api/");
$client->setOpt(YAR_OPT_RESOLVE, ["host:80:127.0.0.1"]);
/* call goes to 127.0.0.1 instead of the DNS-resolved host */
$result = $client->some_method("parameter");Since 2.2.1, when running over HTTP, YAR_OPT_PROXY can be used to route calls through an HTTP proxy (e.g. Fiddler 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 is routed through the proxy */
$result = $client->some_method("parameter");Yar is not only designed for PHP — all RPC requests and responses are transferred as binary data streams.
Key messages are exchanged via 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]; // request from who
unsigned char token[32]; // request token, used for authentication
uint32_t body_len; // request body length
}
#ifndef PHP_WIN32
__attribute__ ((packed))
#endif
yar_header_t;
#ifdef PHP_WIN32
#pragma pack(pop)
#endifYar supports multiple packager protocols via a char[8] identifier placed before the header struct. This indicates which packager was used to encode the body.
When a client makes an RPC request, the request body is sent as an array (in PHP):
<?php
[
"i" => '', // transaction id
"m" => '', // the method being called
"p" => [], // parameters
]When a server responds, the response body is also sent as an array (in PHP):
<?php
[
"i" => '', // transaction id
"s" => '', // status
"r" => '', // return value
"o" => '', // output
"e" => '', // error or exception
]Since 2.4.0, the client validates the transaction id ("i") of a response against the id of the request it belongs to. A response carrying a different, non-zero transaction id (for example, one misrouted by a proxy) is rejected with a protocol error (response id mismatch). This applies to both synchronous calls and Yar_Concurrent_Client. Responses with a zero/missing transaction id are still accepted for backward compatibility with older servers.
