PHP gRPC Go

Mastering Client-Server Communication with gRPC: A Comprehensive Guide

Serhii Samoilenko
4 min readJan 26, 2024

In the ever-evolving landscape of software development, efficient communication between client and server is paramount. Enter gRPC, a high-performance open-source framework that facilitates seamless and robust communication across various programming languages. This article series delves into the intricacies of setting up a client-server architecture using gRPC, with a focus on PHP and Go.

Part 1: PHP gRPC Client Configuration

In this initial installment, we guide you through configuring a gRPC client in PHP. From setting up the project structure and creating proto files to dockerizing your environment and writing PHP gRPC client code, each step is carefully explained. By the end of this article, you’ll have a fully functional PHP gRPC client ready to communicate with the server.

Explore the essentials:

  • Initial folder structures
  • Proto file creation
  • Composer file creation
  • Composer autoloading configuration
  • Composer installation script
  • Dockerfile setup for PHP
  • PHP gRPC client code
  • Docker Compose configuration

Ready to embark on the journey of mastering client-server communication? Let’s begin with configuring the PHP gRPC client environment. Once you’ve mastered this, proceed to the next article to Configure gRPC Server Environment in Go.

1. Initial Folder Structures

Ensure your project has the following directory structure:

|-php-grpc
|- docker
| |- php
| | |- Dockerfile
| | |- install-composer.sh
| |- go
| | |- Dockerfile
| |- docker-compose.yml
|- grpc-client-php
| |- composer.json
| |- src
| | |- user.php
|- grpc-server-go
| |- main.go
|- protos
|- userService.proto

2. Create Proto File

Create the protos/serService.proto file with the following content:

// The user service definition.
service User {
rpc FindById (FindByIdRequest) returns (FindByIdReply) {}
}

// The request message containing the user's id.
message FindByIdRequest {
string id= 1;
}

// The response message containing the user
message FindByIdReply {
string id = 1;
string name = 2;
}

After compiling userService.proto file on docker image building process, two folders will be created ./grpc-client-php/src/GPBMetadata and ./grpc-client-php/src/Grpc with the following structure

   app
|- grpc-client-php
|- GPBMetadata
| |- UserService.php
|- Grpc
| |- FindByIdReply.php
| |- FindByIdRequest.php
| |- UserClient.php

3. Composer file

{
"name": "php/grpc",
"description": "grpc example",
"type": "project",
"require": {
"grpc/grpc": "^1.57",
"google/protobuf": "^4.0@RC"
},
"license": "Free"
}

4. Composer Autoloading Configuration

In your composer.json file located at ./grpc-client-php/composer.json, add the following lines:

{
...,
"autoload": {
"psr-4": {
"Grpc\\": "src/Grpc",
"GPBMetadata\\": "src/GPBMetadata"
}
}
}

5. Composer Installation Script

Composer will be installed during creating docker image. The sh script below downloads and installscomposer

Create the ./docker/php/install-composer.sh file with the following content:

#!/bin/sh
echo 'It is being installed a COMPOSER'
EXPECTED_CHECKSUM="$(php -r 'copy("https://composer.github.io/installer.sig", "php://stdout");')"
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
ACTUAL_CHECKSUM="$(php -r "echo hash_file('sha384', 'composer-setup.php');")"
if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ]
then
>&2 echo 'ERROR: Invalid installer checksum'
rm composer-setup.php
exit 1
fi
php composer-setup.php --quiet
RESULT=$?
rm composer-setup.php

mv composer.phar /usr/local/bin/composer
exit $RESULT

6. Dockerfile for PHP

create the./docker/php/Dockerfile file with following data

FROM php:8.3-fpm-alpine

COPY docker/php/install-composer.sh /tmp
RUN chmod +x /tmp/install-composer.sh && sh /tmp/install-composer.sh

COPY ./grpc-client-php /app/grpc-client-php
COPY ./protos /app/protos

RUN apk add --update linux-headers \
&& apk add --no-cache autoconf gcc make g++ zlib-dev git cmake \
&& pecl channel-update pecl.php.net \
# Install the grpc extension
&& pecl install grpc \
&& docker-php-ext-enable grpc

# INstall gRPC PHP plugin
RUN cd /app && git clone https://github.com/grpc/grpc \
&& cd /app/grpc && git submodule update --init \
&& mkdir -p cmake/build && cd cmake/build && cmake ../.. && make protoc grpc_php_plugin

RUN cd /app/grpc-client-php \
#Install required packages
&& composer install \
# install protobuf compiler
&& apk add protobuf \
# Compile userService.proto file
&& cd /app && protoc --proto_path=protos \
--php_out=./grpc-client-php/src \
--grpc_out=./grpc-client-php/src \
--plugin=protoc-gen-grpc=./grpc/cmake/build/grpc_php_plugin \
./protos/userService.proto

WORKDIR /app

CMD ["php-fpm"]

7. PHP gRPC Client Code

Create the file grpc-client-php/src/user.php with the following content:

<?php

use Grpc\UserClient;
use Grpc\ChannelCredentials;
use Grpc\FindByIdRequest;

require dirname(__FILE__) . '/../vendor/autoload.php';

/**
* parses GRPC errors
*/
function getErrorMessage(\stdClass $status, string $id): string
{
if ($status->code !== \Grpc\STATUS_NOT_FOUND)
return sprintf("User %s was not found", $id);

return "ERROR: " . $status->code . ", " . $status->details;
}

function run(string $hostname, string $id): void
{
// creates grpc client
$userClient = new UserClient($hostname, [
'credentials' => ChannelCredentials::createInsecure(),
]);

// creates grpc request
$request = new FindByIdRequest();
$request->setId($id);


// calls the FindById remote procedure
list($response, $status) = $userClient->FindById($request)->wait();

// in case of an error, show it and exit
if ($status->code !== \Grpc\STATUS_OK) {
echo getErrorMessage($status, $id) . PHP_EOL;
exit(1);
}

// shows received user name
echo $response->getName() . PHP_EOL;
}

// desire user id
$id = !empty($argv[1]) ? $argv[1] : 'some-unique-id';

// grpc server address
$hostname = !empty($argv[2]) ? $argv[2] : 'grpc-server:9090';


run($hostname, $id);

8. Docker Compose

Create the docker/docker-compose.yml file:

version: '3.6'
services:
grpc-client:
build:
context: ../
dockerfile: docker/php/Dockerfile
container_name: grpc-client
volumes:
- ../grpc-client-php:/project
networks:
- dev_grpc_network

networks:
dev_grpc_network:
external: true

9. Build and Run

Create a Docker network:

docker network create dev_grpc_network

Build and run the gRPC client container:

cd ./docker
docker compose up --build grpc-client -d

10. Links

Continue to the next article: Configure gRPC Server (Go) Environment

--

--