Revision: Wed, 06 Dec 2023 18:32:33 GMT
v3.0 – outdated
This version of the documentation is outdated. Consider upgrading your project to Spiral Framework 3.10
Edit this page

GRPC - Client SDK

You must generate the client SDK to access your endpoints. This article is based on the proto file described earlier.

Prerequisites

The PHP client library requires the PHP grpc extension to be installed.

sudo pecl install grpc

Note
Read more about the extension here.

Compile grpc-php-plugin

You must compile grpc-php-plugin in order to generate PHP client code. Follow this guide.

git clone --recursive -b v1.27.x https://github.com/grpc/grpc
cd grpc
cd third_party/protobuf 
./autogen.sh
./configure CC=clang CXX=clang++
make
make install
cd ../..
make 
make grpc_php_plugin

Note
Make sure to install clang.

The location of generated plugin is grpc/bin/opt/grpc_php_plugin.

Generate SDK

We can generate the PHP client SDK in a new project. Copy the proto/ directory into the designated folder.

protoc -I proto/ --plugin=protoc-gen-grpc=grpc/bins/opt/grpc_php_plugin --php_out=. --grpc_out=. proto/calculator.proto 

You can see the generated client code in App/Calculator/CalculatorClient.php.

Composer

Make sure to create composer.json and run composer update to make the generated code loadable:

json
{
  "require": {
    "grpc/grpc": "^1.27",
    "google/protobuf": "^3.11"
  },
  "autoload": {
    "psr-4": {
      "App\\": "App"
    }
  }
}

Certificate

To connect to the running server, make sure to copy app.crt to your client application.

Example

Now we can create the client in client.php:

php
<?php

declare(strict_types=1);

require 'vendor/autoload.php';

$client = new \App\Calculator\CalculatorClient(
    'localhost:50051',
    [
        'credentials' => \Grpc\ChannelCredentials::createSsl(file_get_contents("app.crt")),
    ]
);

[$result, $mt] = $client->Sum(
    new \App\Calculator\Sum([
            'a' => 1,
            'b' => 2
    ])
)->wait();

print_r($result->getResult());

Run php client.php to test your client.

Passing Metadata

You can pass the metadata using the second argument of the Sum function, use $mt->metadata to read the response metadata.

To read and send metadata in Calculator:

php
<?php

declare(strict_types=1);

namespace App\Calculator;

use Spiral\GRPC;

class Calculator implements CalculatorInterface
{
    public function Sum(GRPC\ContextInterface $ctx, Sum $in): Result
    {
        dumprr($ctx->getValue('client-key'));
        $ctx->getValue(GRPC\ResponseHeaders::class)->set('server-key', 'serverValue');

        return new Result([
            'result' => $in->getB() + $in->getA()
        ]);
    }
}

Change client.php to read metadata from the call:

php
<?php

declare(strict_types=1);

require 'vendor/autoload.php';

$client = new \App\Calculator\CalculatorClient(
    'localhost:50051',
    [
        'credentials' => \Grpc\ChannelCredentials::createSsl(file_get_contents("app.crt")),
    ]
);

$call = $client->Sum(
    new \App\Calculator\Sum([
            'a' => 1,
            'b' => 2
    ]),
    [
        'client-key' => ['value']
    ]
);

print_r($call->wait()[0]->getResult());
print_r($call->getMetadata()['server-key']);

Now you can observe the metadata being passed from the client and server.

Golang Clients

GRPC allows you to create a client SDK in any supported language. To generate client on Golang, install the GRPC toolkit first:

go get -u github.com/golang/protobuf/protoc-gen-go

Note
Read more about how to create Golang GRPC clients and server here.

Init the project in the same folder as the PHP client to reuse the .proto and .crt files. To generate client stub code:

go mod init client
go get google.golang.org/grpc
mkdir calculator
protoc -I proto/ proto/calculator.proto --go_out=plugins=grpc:calculator

Note
Notice the package name in calculator.proto.

You can now create main.go:

golang
package main

import (
	app "client/calculator"
	"context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"log"
)

func main() {
	creds, err := credentials.NewClientTLSFromFile("app.crt", "")
	if err != nil {
		panic(err)
	}

	conn, err := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(creds))
	if err != nil {
		panic(err)
	}

	client := app.NewCalculatorClient(conn)

	res, err := client.Sum(context.Background(), &app.Sum{A: 1,B: 2})
	if err != nil {
		panic(err)
	}

	log.Printf("Result: %v", res.Result)
}

To test your client:

go run main.go

Passing Metadata

To pass metadata between server and client:

golang
package main

import (
	app "client/calculator"
	"context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/metadata"

	"log"
)

func main() {
	creds, err := credentials.NewClientTLSFromFile("app.crt", "")
	if err != nil {
		panic(err)
	}

	conn, err := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(creds))
	if err != nil {
		panic(err)
	}

	client := app.NewCalculatorClient(conn)

	// attach value to the server
	ctx := metadata.AppendToOutgoingContext(context.Background(), "client-key", "client-value")

	var header metadata.MD
	res, err := client.Sum(ctx, &app.Sum{A: 1, B: 2}, grpc.Header(&header))
	if err != nil {
		panic(err)
	}

	log.Println(header["server-key"])

	log.Printf("Result: %v", res.Result)
}

Note
Read more about working with metadata in Golang here.