Usage Example¶
This page contains a full example about using TwirPHP on both server and client side. Much of it is based on the original usage example, so you might want to check that out as well. You can find the code for this example in the demo-project repository.
Before moving forward, make sure to check the Installation guide as well.
Write a Protobuf service definition¶
Write a protobuf definition for your messages and your service and put it in proto/service.proto
.
The following example is taken from the original Twirp documentation.
syntax = "proto3";
package twirp.example.haberdasher;
option go_package = "haberdasher";
// Haberdasher service makes hats for clients.
service Haberdasher {
// MakeHat produces a hat of mysterious, randomly-selected color!
rpc MakeHat(Size) returns (Hat);
}
// Size of a Hat, in inches.
message Size {
int32 inches = 1; // must be > 0
}
// A Hat is a piece of headwear made by a Haberdasher.
message Hat {
int32 inches = 1;
string color = 2; // anything but "invisible"
string name = 3; // i.e. "bowler"
}
Generate code¶
To generate code run the protoc
compiler pointed at your service definition file:
$ mkdir -p generated
$ protoc -I . --twirp_php_out=generated --php_out=generated ./proto/service.proto
This will generate the standard PHP messages along with the Twirp specific files.
Implement the server¶
Now that everything is in place, it’s time to implement the server implementing the service interface
(Twirp\Example\Haberdasher\Haberdasher
in this case).
<?php
namespace Twirp\Demo;
use Twirp\Example\Haberdasher\Hat;
use Twirp\Example\Haberdasher\Size;
final class Haberdasher implements \Twirp\Example\Haberdasher\Haberdasher
{
private $colors = ['golden', 'black', 'brown', 'blue', 'white', 'red'];
private $hats = ['crown', 'baseball cap', 'fedora', 'flat cap', 'panama', 'helmet'];
public function MakeHat(array $ctx, Size $size): Hat
{
$hat = new Hat();
$hat->setInches($size->getInches());
$hat->setColor($this->colors[array_rand($this->colors, 1)]);
$hat->setName($this->hats[array_rand($this->hats, 1)]);
return $hat;
}
}
Run the server¶
To run the server you need to setup some sort of application entrypoint that processes incoming requests as PSR-7
messages. It is recommended that you use some sort of dispatcher/emitter,
like the SapiEmitter
bundled with Zend Diactoros, but the following example
works perfectly as well:
<?php
require __DIR__.'/vendor/autoload.php';
$request = \GuzzleHttp\Psr7\ServerRequest::fromGlobals();
$server = new \Twirp\Server();
$handler = new \Twirp\Example\Haberdasher\HaberdasherServer(new \Twirp\Demo\Haberdasher());
$server->registerServer(\Twirp\Example\Haberdasher\HaberdasherServer::PATH_PREFIX, $handler);
$response = $server->handle($request);
if (!headers_sent()) {
// status
header(sprintf('HTTP/%s %s %s', $response->getProtocolVersion(), $response->getStatusCode(), $response->getReasonPhrase()), true, $response->getStatusCode());
// headers
foreach ($response->getHeaders() as $header => $values) {
foreach ($values as $value) {
header($header.': '.$value, false, $response->getStatusCode());
}
}
}
echo $response->getBody();
Use the client¶
Client stubs are automatically generated, hooray!
There are two client stubs generated for each proto service: the default one which uses protobuf serialization, and a JSON client stub. Twirp itself supports both protobuf and json, but it recommends using only protobuf in production.
Using the client is quite trivial, you only need to pass an endpoint to the generated client:
<?php
require __DIR__.'/vendor/autoload.php';
$client = new \Twirp\Example\Haberdasher\HaberdasherClient($argv[1]);
while (true) {
$size = new \Twirp\Example\Haberdasher\Size();
$size->setInches(10);
try {
$hat = $client->MakeHat([], $size);
printf("I received a %s %s\n", $hat->getColor(), $hat->getName());
} catch (\Twirp\Error $e) {
if ($cause = $e->getMeta('cause') !== null) {
printf("%s: %s (%s)\n", strtoupper($e->getErrorCode()), $e->getMessage(), $cause);
} else {
printf("%s: %s\n", strtoupper($e->getErrorCode()), $e->getMessage());
}
}
sleep(1);
}
Warning
When no scheme is present in the endpoint, the client falls back to HTTP.