diff --git a/reference/yar/book.xml b/reference/yar/book.xml
index f75320892348..b2b4494fdcd2 100644
--- a/reference/yar/book.xml
+++ b/reference/yar/book.xml
@@ -24,10 +24,13 @@
The protocol is language agnostic: compatible implementations exist
- for C, Java and Lua.
+ for C, Java and Lua. The C implementation additionally provides a TCP
+ server that
+ Yar_Client can talk to directly.
+ &reference.yar.protocol;
&reference.yar.setup;
&reference.yar.constants;
&reference.yar.examples;
diff --git a/reference/yar/constants.xml b/reference/yar/constants.xml
index 3845c58a289e..0a09192440b1 100644
--- a/reference/yar/constants.xml
+++ b/reference/yar/constants.xml
@@ -310,6 +310,20 @@
+
+
+ YAR_ERR_FORBIDDEN
+ (int)
+
+
+
+ The request was rejected by the server's authentication
+ (see the provider and
+ token fields of the
+ Yar protocol header).
+
+
+
diff --git a/reference/yar/examples.xml b/reference/yar/examples.xml
index 4ec8acf9b320..42a99ca59fe7 100644
--- a/reference/yar/examples.xml
+++ b/reference/yar/examples.xml
@@ -3,13 +3,33 @@
&reftitle.examples;
+
+ The examples below walk through a complete service: a server that
+ exposes a few arithmetic methods, a synchronous client that calls
+ them, a client that fans several calls out concurrently, and a
+ client that talks to a server over TCP.
+
+
Yar Server Example
+
+ A Yar service is just a regular PHP class wrapped by
+ Yar_Server. Every public method of the
+ object becomes an RPC endpoint; protected and private methods, as
+ well as methods whose names start with an underscore, stay hidden
+ from clients. The doc comments of the public methods are collected
+ and shown on the service information page.
+
+
+ RPC requests arrive as HTTP POST requests carrying a binary Yar
+ protocol payload, so the script is usually mapped to a URI on a
+ regular web server.
+
handle();
Access the server in browser (GET request)
- When a GET request is issued to the service URI, Yar renders an
- information page listing every public method of the executor object
- together with its doc comment. This is controlled by the
- yar.expose_info
- directive.
+ When a GET request is issued to the service URI — for instance by
+ opening it in a browser — Yar does not perform an RPC call but
+ renders an information page listing every public method of the
+ executor object together with its doc comment. This is controlled
+ by the yar.expose_info
+ directive; when it is off, a GET request fails instead.
&example.outputs.similar;
@@ -73,10 +94,22 @@ $server->handle();
Yar Client Example
+
+ A Yar_Client is bound to a single service
+ address. Calling any undefined method on it is transparently turned
+ into a synchronous RPC call, so remote methods look and feel like
+ local ones; Yar_Client::call does the same
+ thing explicitly by name.
+
+
+ Protected methods are not exposed: calling one fails with a
+ Yar_Client_Exception whose code is
+ YAR_ERR_REQUEST.
+
add(1, 2));
@@ -94,17 +127,34 @@ var_dump($client->_add(1, 2));
Yar Concurrent Client Example
+
+ Instead of calling services one after another,
+ Yar_Concurrent_Client registers several calls
+ first and then dispatches them all at once with
+ Yar_Concurrent_Client::loop. The responses
+ are passed to the callback in the order they arrive, not in the
+ order the calls were registered.
+
+
+ Right after all requests have been sent, the callback is invoked
+ once with &null; arguments so that the caller knows no further
+ request is pending; the example below checks for this notification.
+
Besides HTTP, Yar_Client can talk to Yar
compatible servers over TCP or Unix sockets, for example a service
- implemented with the Yar C framework. The remote server must
- implement the same binary Yar protocol.
+ implemented with the
+ Yar C framework,
+ which serves the same binary Yar protocol that the PHP server uses.
+
+
+
+ The Yar Protocol
+
+ Yar does not rely on a schema or IDL file: everything is exchanged on
+ the wire as plain bytes. Any language that can read and write bytes
+ can speak to a Yar service, without installing any framework at all —
+ build one fixed-size binary header and a serialized request body,
+ send them to the service URI, and parse the reply.
+
+
+ A message consists of a fixed-size header of 82 bytes followed by a
+ body. The header is laid out exactly like the following C structure,
+ packed with no padding, and is written to the wire field after field
+ in declaration order:
+
+
+
+
+
+ The id, magic_num,
+ reserved and body_len fields
+ are stored in network byte order (big-endian); the remaining fields
+ are raw bytes.
+
+
+ The body starts with an 8-byte packager identifier —
+ PHP, JSON or
+ MSGPACK, zero-padded — telling the receiver how
+ the remainder was encoded, followed by the serialized content itself.
+
+
+
+
+ The request body decodes to an array with the keys
+ i (the transaction id), m
+ (the method being called) and p (the list of
+ parameters).
+
+
+
+
+ The response body decodes to an array with the keys
+ i (the transaction id), s
+ (the status, one of the YAR_ERR_* codes),
+ r (the return value), o (any
+ output the service method produced) and e (the
+ error or exception, when the call failed).
+
+
+
+
+ Over HTTP the message is sent as the body of a POST request, with
+ the response arriving as the body of the reply; over TCP or Unix
+ sockets it is written directly on the stream.
+
+
+ Calling a Yar service without the extension
+
+ The following self-contained script builds a valid Yar request for
+ the php packager with nothing but standard
+ sockets, sends it to a service URI, and prints the decoded
+ response. Running it against the
+ Operator service from the
+ examples prints
+ int(3).
+
+
+ 1, "m" => "add", "p" => array(1, 2)));
+$body = str_pad("PHP", 8, "\0") . $serialized;
+
+/* 2. the header: 82 bytes, multi-byte integers in network byte order */
+$header = pack("N", 1) /* id */
+ . pack("v", 0) /* version */
+ . pack("N", 0x80DFEC60) /* magic number */
+ . pack("N", 0) /* reserved */
+ . str_pad("", 32, "\0") /* provider */
+ . str_pad("", 32, "\0") /* token */
+ . pack("N", strlen($body)); /* body length */
+
+/* 3. send it as the body of a POST request */
+$stream = stream_context_create(array("http" => array(
+ "method" => "POST",
+ "header" => "Content-Type: application/octet-stream\r\n",
+ "content" => $header . $body,
+)));
+$reply = file_get_contents($uri, false, $stream);
+
+/* 4. parse the reply: 82-byte header, then the response body */
+$response = unserialize(substr($reply, 82 + 8));
+var_dump($response["r"]);
+?>
+]]>
+
+
+
+ A more complete client implementation in plain PHP, which also
+ decodes the response header and supports concurrent calls, lives in
+ the tools/ directory of the
+ Yar source
+ repository.
+
+
+
+
diff --git a/reference/yar/setup.xml b/reference/yar/setup.xml
index 7035e5a05bb1..deb2031c1bf8 100644
--- a/reference/yar/setup.xml
+++ b/reference/yar/setup.xml
@@ -26,6 +26,10 @@
&reftitle.install;
+
+ Yar can be installed in one of three ways: via PECL, via PIE, or
+ by building it from source.
+
&pecl.moved;
@@ -36,66 +40,100 @@
&pecl.windows.download.avail;
-
+
+ Installing Yar with PECL
+
+
+
+
+
+ As of Yar 2.4.0, the extension can be installed with
+ &link.pie;, the PHP Installer for Extensions, by running the
+ following on the command line.
+
+
+ Installing Yar with PIE
+
+
+
+
+
+ The msgpack packager can be enabled at install time:
+
+
+ Installing Yar with PIE and msgpack
+
+
+
+
+
The source code is hosted on
- GitHub. To build
- the extension from source:
-
+ GitHub. To
+ build the extension from source, run the following on the command
+ line, replacing the paths with those of the local PHP
+ installation.
+
+
+ Building Yar from source
+
-
-
-
+
+
+
The following configure options are available:
-
-
-
-
-
-
-
- Location of the cURL installation, if it is not found in the
- default include paths.
-
-
-
-
-
-
-
-
-
- Enables the msgpack packager and makes the
- msgpack extension an optional dependency. When
- Yar is built with this option, the default value of
- yar.packager becomes
- msgpack.
-
-
-
-
-
-
-
-
-
- Uses Linux epoll instead of
- select() for I/O multiplexing, available as of
- Yar 2.1.2. This can improve
- Yar_Concurrent_Client performance under
- high concurrency. It only takes effect on Linux; on other
- platforms it is silently ignored.
-
-
-
-
-
+
+
+
+
+
+
+
+
+ Location of the cURL installation, if it is not found in the
+ default include paths.
+
+
+
+
+
+
+
+
+
+ Enables the msgpack packager and makes the
+ msgpack extension an optional dependency. When
+ Yar is built with this option, the default value of
+ yar.packager becomes
+ msgpack.
+
+
+
+
+
+
+
+
+
+ Uses Linux epoll instead of
+ select() for I/O multiplexing, available as of
+ Yar 2.1.2. This can improve
+ Yar_Concurrent_Client performance under
+ high concurrency. It only takes effect on Linux; on other
+ platforms it is silently ignored.
+
+
+
+
diff --git a/reference/yar/versions.xml b/reference/yar/versions.xml
index 7868cdcc7b9a..9319614bc56e 100644
--- a/reference/yar/versions.xml
+++ b/reference/yar/versions.xml
@@ -7,7 +7,6 @@
-
diff --git a/reference/yar/yar-concurrent-client.xml b/reference/yar/yar-concurrent-client.xml
index fa6dedb85ea7..d484d1c54204 100644
--- a/reference/yar/yar-concurrent-client.xml
+++ b/reference/yar/yar-concurrent-client.xml
@@ -17,9 +17,21 @@
immediately; they are all dispatched together, in parallel, by
Yar_Concurrent_Client::loop.
+
+ The class is meant for applications that need the results of several
+ remote calls. Only independent calls — ones that do not need each
+ other's results — can be batched this way; when one call depends on
+ the result of another, the two have to run sequentially. Through
+ Yar_Client the calls are sent one after
+ another, each paying its full round-trip time; with the concurrent
+ client they are sent at the same time, so the overall waiting time
+ drops to the duration of the single slowest call.
+
- Only HTTP(S) services are supported for concurrent calls.
+ Only HTTP(S) services are supported for concurrent calls. They are
+ sent concurrently through curl's multi-handle interface, which the
+ TCP/Unix socket transport does not implement.
diff --git a/reference/yar/yar-server.xml b/reference/yar/yar-server.xml
index 1a7f1eb61a2d..4ffdc1203f36 100644
--- a/reference/yar/yar-server.xml
+++ b/reference/yar/yar-server.xml
@@ -16,6 +16,11 @@
service, served over HTTP by
Yar_Server::handle.
+
+ The PHP extension only provides an HTTP server. A TCP and Unix
+ socket server speaking the same Yar protocol is provided by the
+ Yar C framework.
+
diff --git a/reference/yar/yar_client/call.xml b/reference/yar/yar_client/call.xml
index 702bfee10e0c..cfb5ccc8dc2d 100644
--- a/reference/yar/yar_client/call.xml
+++ b/reference/yar/yar_client/call.xml
@@ -17,8 +17,8 @@
Issues an RPC call to the remote method method. This
is exactly what happens when a non-existent method is invoked on a
- Yar_Client object (see
- Yar_Client::__call);
+ Yar_Client object (via PHP's
+ __call magic method);
Yar_Client::call only exists so that remote
methods literally named call or
__call can still be reached.
@@ -57,17 +57,51 @@
&reftitle.errors;
- When the call method does not exist on the server
- side, a Yar_Server_Request_Exception is
- thrown. See Yar_Client::__call for the full
- list of failures and their exception classes.
+ When the remote method does not exist on the server, or is not
+ public, the client throws a
+ Yar_Client_Exception with the code
+ YAR_ERR_REQUEST. See
+ Yar_Client_Exception
+ for the other client-side failures and their exception classes.
+
+ &reftitle.examples;
+
+ Yar_Client::call example
+
+add(1, 2));
+
+/* Equivalent: call the method explicitly by name */
+var_dump($client->call("add", array(1, 2)));
+
+/* Needed only when the remote service literally exposes a method
+ * named call (or __call), which the magic __call cannot reach */
+var_dump($client->call("call", array($some_argument)));
+?>
+]]>
+
+ &example.outputs.similar;
+
+
+
+
+
+
&reftitle.seealso;
- Yar_Client::__callYar_Client::setOpt
diff --git a/reference/yar/yar_client/construct.xml b/reference/yar/yar_client/construct.xml
index 003fc848a0f7..6e2bde9899fd 100644
--- a/reference/yar/yar_client/construct.xml
+++ b/reference/yar/yar_client/construct.xml
@@ -70,10 +70,10 @@
1000,
YAR_OPT_PACKAGER => "json",
]);
@@ -86,7 +86,7 @@ $client = new Yar_Client("http://host/api/", [
&reftitle.seealso;
- Yar_Client::__call
+ Yar_Client::callYar_Client::setOpt
diff --git a/reference/yar/yar_client/getopt.xml b/reference/yar/yar_client/getopt.xml
index 152194e02ef2..b701db0ccbe0 100644
--- a/reference/yar/yar_client/getopt.xml
+++ b/reference/yar/yar_client/getopt.xml
@@ -51,7 +51,7 @@
setOpt(YAR_OPT_TIMEOUT, 1000);
var_dump($client->getOpt(YAR_OPT_TIMEOUT));
diff --git a/reference/yar/yar_client/setopt.xml b/reference/yar/yar_client/setopt.xml
index 233f30b07020..860c7776a5d3 100644
--- a/reference/yar/yar_client/setopt.xml
+++ b/reference/yar/yar_client/setopt.xml
@@ -102,7 +102,7 @@
setOpt(YAR_OPT_TIMEOUT, 1000);
@@ -128,7 +128,7 @@ $result = $client->some_method("parameter");
&reftitle.seealso;
Yar_Client::getOpt
- Yar_Client::__call
+ Yar_Client::call
diff --git a/reference/yar/yar_client_exception/gettype.xml b/reference/yar/yar_client_exception/gettype.xml
index f7b4c050313b..9b664bb3b641 100644
--- a/reference/yar/yar_client_exception/gettype.xml
+++ b/reference/yar/yar_client_exception/gettype.xml
@@ -43,7 +43,7 @@
some_method("parameter");
diff --git a/reference/yar/yar_concurrent_client/call.xml b/reference/yar/yar_concurrent_client/call.xml
index 5611b3920d4b..79d6fb89e5a0 100644
--- a/reference/yar/yar_concurrent_client/call.xml
+++ b/reference/yar/yar_concurrent_client/call.xml
@@ -26,8 +26,9 @@
- Only HTTP and HTTPS URIs are supported. TCP and Unix socket transports
- are not available for concurrent calls.
+ Only HTTP and HTTPS URIs are supported. Concurrent calls are sent
+ simultaneously using curl's multi-handle interface, and the TCP/Unix
+ socket transport does not provide this facility.
@@ -66,11 +67,18 @@
A callable invoked when the response for this call
arrives, with two arguments: the response value, and a
- callinfo &array; with the keys
- sequence, uri and
- method. If omitted, the
- callback of
- Yar_Concurrent_Client::loop is used.
+ callinfo &array; describing the call:
+
+
+ sequence — the sequence number returned
+ when the call was registered
+ uri — the address of the service
+ method — the name of the remote method
+
+
+ If omitted, the callback of
+ Yar_Concurrent_Client::loop is used;
+ see that method for what happens when neither is given.
@@ -81,8 +89,10 @@
A callable invoked when this call fails, with three
arguments: the error type (one of the YAR_ERR_*
codes), the error message, and the callinfo
- &array;. If omitted, the error_callback of
- Yar_Concurrent_Client::loop is used.
+ &array; described above. If omitted, the
+ error_callback of
+ Yar_Concurrent_Client::loop is used;
+ see that method for what happens when neither is given.
@@ -130,16 +140,16 @@ 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://api.example.com/operator.php", "some_method", array("parameters"), "callback");
/* If the callback is not specified, the callback of loop() will be used */
-Yar_Concurrent_Client::call("http://host/api/", "some_method", array("parameters"));
+Yar_Concurrent_Client::call("http://api.example.com/operator.php", "some_method", array("parameters"));
/* This server accepts the JSON packager */
-Yar_Concurrent_Client::call("http://host/api/", "some_method", array("parameters"), "callback", NULL, array(YAR_OPT_PACKAGER => "json"));
+Yar_Concurrent_Client::call("http://api.example.com/operator.php", "some_method", array("parameters"), "callback", NULL, array(YAR_OPT_PACKAGER => "json"));
/* Custom timeout */
-Yar_Concurrent_Client::call("http://host/api/", "some_method", array("parameters"), "callback", NULL, array(YAR_OPT_TIMEOUT => 1000));
+Yar_Concurrent_Client::call("http://api.example.com/operator.php", "some_method", array("parameters"), "callback", NULL, array(YAR_OPT_TIMEOUT => 1000));
/* The requests are not sent yet */
]]>
diff --git a/reference/yar/yar_concurrent_client/loop.xml b/reference/yar/yar_concurrent_client/loop.xml
index ad4b5c5bafac..8376bc566d4f 100644
--- a/reference/yar/yar_concurrent_client/loop.xml
+++ b/reference/yar/yar_concurrent_client/loop.xml
@@ -21,6 +21,20 @@
blocks until every response has arrived and been handled. The call list
is emptied when this method returns.
+
+ Because the calls run simultaneously, the loop takes only as long as
+ the slowest single call rather than the sum of all calls. The
+ callbacks, however, are not invoked in the order the calls were
+ registered: a callback fires as soon as its response arrives. To find
+ out which call a response belongs to, check the
+ sequence entry of the callinfo
+ argument against the sequence number returned by
+ Yar_Concurrent_Client::call. The
+ callinfo &array; holds the
+ sequence, uri and
+ method of the call; see
+ Yar_Concurrent_Client::call.
+
@@ -35,6 +49,10 @@
been sent, it is additionally called once with two &null; arguments,
before any response arrives.
+
+ If this parameter is omitted, the return value of a successful call
+ is simply printed when its response arrives.
+
@@ -47,6 +65,11 @@
codes), the error message, and the callinfo
&array;.
+
+ If this parameter is omitted, a failed call raises a PHP warning
+ instead; unlike the synchronous
+ Yar_Client, no exception is thrown.
+
@@ -93,10 +116,10 @@ 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://api.example.com/operator.php", "some_method", array("parameters"), "callback");
/* if the callback is not specified, the callback of loop() will be used */
-Yar_Concurrent_Client::call("http://host/api/", "some_method", array("parameters"));
+Yar_Concurrent_Client::call("http://api.example.com/operator.php", "some_method", array("parameters"));
Yar_Concurrent_Client::loop("callback", "error_callback");
?>
diff --git a/reference/yar/yar_concurrent_client/reset.xml b/reference/yar/yar_concurrent_client/reset.xml
index 1c3d160d55ad..c05960ff6f98 100644
--- a/reference/yar/yar_concurrent_client/reset.xml
+++ b/reference/yar/yar_concurrent_client/reset.xml
@@ -52,7 +52,7 @@ function callback($retval, $callinfo) {
var_dump($retval);
}
-Yar_Concurrent_Client::call("http://host/api/", "some_method", array("parameters"), "callback");
+Yar_Concurrent_Client::call("http://api.example.com/operator.php", "some_method", array("parameters"), "callback");
/* never sent: throw the registered call away */
Yar_Concurrent_Client::reset();
diff --git a/reference/yar/yar_server/construct.xml b/reference/yar/yar_server/construct.xml
index 1b2992cc7ba9..c8cd795f3a78 100644
--- a/reference/yar/yar_server/construct.xml
+++ b/reference/yar/yar_server/construct.xml
@@ -30,6 +30,42 @@
services. Protected and private methods, and methods whose name
starts with an underscore, are not exposed.
+
+ The executor can optionally define two protected magic methods,
+ which are recognized by
+ Yar_Server::handle and never exposed as
+ RPC endpoints:
+
+
+
+
+ protected function __info(string $markup):
+ string — as of Yar 2.3.0. Called when the service
+ information page is requested; it receives the markup of the
+ page Yar would otherwise render, and if it returns a &string;,
+ that string is sent to the client instead. See
+ Yar_Server::handle.
+
+
+
+
+ protected function __auth(string $provider, string
+ $token): bool — as of Yar 2.3.0. Called at the very
+ beginning of every request with the
+ provider and token
+ fields of the request header, set by the client with the
+ YAR_OPT_PROVIDER and
+ YAR_OPT_TOKEN options. Returning exactly
+ &false; rejects the request; any other return value lets it
+ proceed. See Yar_Server::handle.
+
+
+
+
+ Both methods only take effect when they are
+ protected; a public or private method with
+ the same name is ignored.
+
diff --git a/reference/yar/yar_server/handle.xml b/reference/yar/yar_server/handle.xml
index 81d05acfc003..feaa5ef686c5 100644
--- a/reference/yar/yar_server/handle.xml
+++ b/reference/yar/yar_server/handle.xml
@@ -29,12 +29,11 @@
- As of Yar 2.3.0, the service information page can be customized by
- defining a protected method named
- __info on the executor object. When a GET request
- arrives, Yar calls this method, passing it the markup of the page it
- would otherwise render; if the method returns a &string;, that string
- is sent to the client instead of the default page.
+ As of Yar 2.3.0, the executor object may define the protected
+ magic methods __info and
+ __auth to customize the service information
+ page and to authenticate requests; see
+ Yar_Server::__construct for details.
diff --git a/reference/yar/yar_server_exception/gettype.xml b/reference/yar/yar_server_exception/gettype.xml
index 3a0d8bcb38ea..e9edbdbad86d 100644
--- a/reference/yar/yar_server_exception/gettype.xml
+++ b/reference/yar/yar_server_exception/gettype.xml
@@ -60,7 +60,7 @@ $service->handle();
throw_exception("client");