m-gRPC Server Library

gRPC Server

m-gRPC Server Library

minichan-v1 is a PHP gRPC library that leverages Swoole coroutines. It encompasses a protoc code generator, server, and client components to facilitate the development of gRPC-based applications.

Introduction

In the ever-evolving landscape of web development, efficiency and scalability are paramount. Minichan-v1, a PHP gRPC library that is changing the game. Powered by Swoole coroutines, minichan-v1 simplifies the development of gRPC-based applications, offering a suite of tools to streamline the process. In this blog post, we’ll delve into the features and benefits of minichan-v1, exploring how it revolutionizes PHP development.

What is minichan-v1?

At its core, minichan-v1 is a PHP gRPC library that leverages Swoole coroutines. It provides developers with a protoc code generator, server, and client components, making it easier than ever to build efficient and scalable gRPC services in PHP. By abstracting away the complexities of gRPC communication, minichan-v1 empowers developers to focus on building robust applications without getting bogged down in implementation details.

Key Features:

  1. Protoc Code Generator: minichan-v1 includes a powerful code generator that simplifies the process of generating PHP classes from .proto files. This automates much of the boilerplate code required for gRPC communication, saving developers time and effort.

  2. Server Component: With minichan-v1, setting up a gRPC server in PHP is a breeze. The server component handles incoming requests, executes the corresponding logic, and sends back responses asynchronously, thanks to Swoole coroutines. This enables developers to build highly responsive and scalable services.

  3. Client Component: On the client side, minichan-v1 provides an intuitive interface for making gRPC calls. Developers can easily instantiate client objects, make remote procedure calls, and handle responses asynchronously, all with minimal overhead.

Benefits of minichan-v1:

  • Simplicity: By abstracting away the complexities of gRPC communication, minichan-v1 simplifies the development process, allowing developers to focus on building functionality rather than wrestling with low-level details.

  • Efficiency: Leveraging Swoole coroutines, minichan-v1 enables asynchronous communication, resulting in faster response times and better resource utilization.

  • Scalability: With its built-in support for coroutines, minichan-v1 facilitates the development of highly scalable gRPC services that can handle a large number of concurrent requests with ease.

GitHub Repository: minichan-grpc

Installation Walk through and Sample usage:

Let’s walk through how you can install minichan-v1 and get started with building your gRPC-based applications:

Ensuring that Swoole and Protoc are present in the environment is crucial for successfully utilizing minichan-v1. Here’s a brief elaboration:

Swoole Dependency: Swoole, a high-performance networking framework for PHP, is essential for minichan-v1. Version 5.1.0 or higher is required, with the --enable-http2 option enabled during configuration for HTTP/2 support crucial for gRPC communication.

Protoc: Protoc is vital for generating code from .proto files defining data structures for gRPC communication. Its installation is necessary for creating language-specific classes and structs essential for program implementation.

  1. Begin by cloning the minichan-grpc repository from GitHub:
git clone https://github.com/Leincentes/minichan-grpc

2. Navigate to the cloned repository directory and install the required dependencies using Composer:

cd minichan-grpc
composer install

3. Create a .proto file inside the proto folder:

// userauth.proto
syntax = "proto3";

package PHP.UserService;

message User {
  string username = 1;
  string password = 2;
  string new_password = 3;
}
message UserResponse {
    string message = 1;
}

service UserService {
  rpc RegisterUser(User) returns (UserResponse);
  rpc Login(User) returns (UserResponse);
  rpc UpdateUser(User) returns (UserResponse);
  rpc DeleteUser(User) returns (UserResponse);
  rpc GetUser(User) returns (UserResponse);
  rpc GetAllUser(User) returns (stream UserResponse);
}

service Stream {
    rpc FetchRegistered(stream User) returns (stream UserResponse);
    rpc FetchLogin(stream User) returns (stream UserResponse);
    rpc FetchDelete(stream User) returns (stream UserResponse);
    rpc FetchUpdates(stream User) returns (stream UserResponse);
    rpc FetchUser(stream User) returns (stream UserResponse);
    rpc FetchAllUser(stream User) returns (stream UserResponse);
}

4. Then use the protoc to generate the code:

protoc --php_out=. --grpc_out=. --plugin=protoc-gen-grpc=lib/Bin/minichan-grpc proto/userauth.proto

When the command is executed, the following files will be generated in the current directory:

|--GPBMetadata/Proto
| `--Userauth.php
|--PHP/UserService
| `--UserResponse.php
| `--UserServiceClient.php
| `--UserServiceInterface.php
| `--User.php
| `--StreamClient.php
| `--StreamInterface.php
``--userauth.proto

Now that the stubs are generated using our binary plugin.

Create Services Usage:

Under the Services folder your going to create the AuthService class that will implement UserServiceInterface that the binary plugin generated.

// Without Database Sample
declare(strict_types=1);

namespace Services;

use PHP\UserService\User;
use PHP\UserService\UserResponse;
use PHP\UserService\UserServiceInterface;

class AuthService implements UserServiceInterface
{
    private UserResponse $response;
    private array $dummyUsers;

    public function __construct(array $initialUsers = [])
    {
        $this->response = new UserResponse();
        $this->dummyUsers = [];

        foreach ($initialUsers as $user) {
            $this->addDummyUser($user['username'], $user['password']);
        }

    }

    private function addDummyUser(string $username, string $password): void
    {
        $this->dummyUsers[strtolower($username)] = [
            'username' => $username,
            'password' => password_hash($password, PASSWORD_DEFAULT),
        ];
    }

    public function RegisterUser(\Minichan\Grpc\ContextInterface $ctx, User $request): UserResponse
    {
        $username = $request->getUsername();
        $password = $request->getPassword();

        if (isset($this->dummyUsers[$username])) {
            throw new \Minichan\Exception\AlreadyExistsException("User with the provided username already exists");
        }

        $this->addDummyUser($request->getUsername(), $request->getPassword());

        return $this->response->setMessage('Registered user: ' . $this->dummyUsers[$username]['username']. $this->dummyUsers[$password]['password']);
    }

    public function Login(\Minichan\Grpc\ContextInterface $ctx, User $request): UserResponse
    {
        $username = $request->getUsername();
        $password = $request->getPassword();
        print_r($username . " " . $password);

        if (isset($this->dummyUsers[$username])) {
            $user = $this->dummyUsers[$username];
            print_r($user);

            if (password_verify($request->getPassword(), $user['password'])) {
                return $this->response->setMessage('User login successful');
            } else {
                throw new \Minichan\Exception\InvokeException("Authentication failed");
            }
        } else {
            throw new \Minichan\Exception\InvokeException("User not found");
        }
    }


    public function UpdateUser(\Minichan\Grpc\ContextInterface $ctx, User $request): UserResponse
    {
        if (isset($this->dummyUsers[$request->getUsername()])) {
            $this->dummyUsers[$request->getUsername()] = [
                'username' => $request->getUsername(),
                'password' => password_hash($request->getPassword(), PASSWORD_DEFAULT),
            ];

            return $this->response->setMessage('User updated successfully');
        } else {
            throw new \Minichan\Exception\InvokeException("Update user failed");
        }
    }

    public function DeleteUser(\Minichan\Grpc\ContextInterface $ctx, User $request): UserResponse
    {
        if (isset($this->dummyUsers[$request->getUsername()])) {
            unset($this->dummyUsers[$request->getUsername()]);
            return $this->response->setMessage('User deleted successfully');
        } else {
            throw new \Minichan\Exception\InvokeException("Delete user failed");
        }
    }

    public function GetUser(\Minichan\Grpc\ContextInterface $ctx, User $request): UserResponse
    {
        if (isset($this->dummyUsers[$request->getUsername()])) {
            return $this->response->setMessage('User: ' . $this->dummyUsers[$request->getUsername()]['username']);
        } else {
            throw new \Minichan\Exception\InvokeException("User does not exist");
        }
    }

    public function GetAllUser(\Minichan\Grpc\ContextInterface $ctx, User $request): UserResponse
    {
        $responseMessage = '';

        foreach ($this->dummyUsers as $user) {
            $responseMessage .= 'Users: ' . $user['username'] . PHP_EOL;
        }

        return $this->response->setMessage($responseMessage);
    }
}

Next is to add the service in the config: In the folder Services you’ll find Cofig folder where Config.php reside. This is where you can register the Service you created.

<?php

declare(strict_types=1);

namespace Services\Config;

use Services\AuthService;

/**
 * Configuration class for defining gRPC services.
 */
class Config
{
    /**
     * Register gRPC services.
     *
     * @return array
     */
    public static function registerServices(): array
    {
        // Add your services here
        return [
            AuthService::class, // this is the new class created
        ];
    }
}

Then to serve the server:

php minichan serve

Basic gRPC Client:

A basic example how the client will be handled.

// client.php

declare(strict_types=1);

use Minichan\Config\Constant;
use Minichan\Grpc\Client;
use Swoole\Coroutine as Co;
use PHP\UserService\User;

require __DIR__ . '/vendor/autoload.php';

function connectToGrpcServer()
{
    return (new Client('localhost', 9502, Constant::GRPC_CALL))
        ->set(['open_http2_protocol' => 1])
        ->connect();
}

function sendDataToGrpcServer($conn, $method, $message, $type = 'proto', $user_agent = 'grpc-java-net/sample')
{
    $streamId = $conn->send($method, $message, $type, $user_agent);
    return $conn->recv($streamId);
}

function performGrpcMethod($method, User $message)
{
    $conn = connectToGrpcServer();
    $data = sendDataToGrpcServer($conn, $method, $message);
    $conn->close();
    echo "Closed\n";
    return $data;
}

Co::set(['log_level' => SWOOLE_LOG_ERROR]);
Co::set(['log_level' => SWOOLE_LOG_DEBUG]);

Co::create(function () {
    // uncomment - comment to test all the service available
    $method = '/PHP.UserService.UserService/RegisterUser';
    // $method = '/PHP.UserService.UserService/Login';
    // $method = '/PHP.UserService.UserService/UpdateUser';
    // $method = '/PHP.UserService.UserService/DeleteUser';
    // $method = '/PHP.UserService.UserService/GetUser';
    // $method = '/PHP.UserService.UserService/GetAllUser';
    $message = new User();
    $message->setUsername('Hello');
    $message->setPassword('Hi2');
    // $message->setNewPassword('Hi');

    $data = performGrpcMethod($method, $message);

    print_r($data);
});

By following the installation walkthrough and sample usage provided above, developers can seamlessly set up minichan-v1 and begin building gRPC-based applications in PHP. Ensuring the presence of Swoole and Protoc in the environment is crucial for the successful utilization of minichan-v1. By adhering to best practices and leveraging the provided code examples, developers can efficiently create and deploy gRPC services, simplifying the development process and enhancing the scalability and performance of their applications.

For more information you can visit this documentation: minichan-docs

Hope that you can write feedback.

Conclusion

This concludes, minichan-v1 is a nice-tool to have for PHP developers looking to build efficient and scalable gRPC-based applications. By providing a comprehensive set of tools and leveraging the power of Swoole coroutines, minichan-v1 simplifies the development process, enabling developers to build robust services with ease. Whether you’re a seasoned PHP developer or just getting started with gRPC, minichan-v1 is definitely worth exploring.