PHP使用 Google Protocol Buffers (protobuf)

很久之前,寫PHP的時候,使用 Protobuf 做了聊天APP, 遊戲服務器。 那個時候還用的是protobuf 2.5。
看了下proto3的語法,來測試下:

服務器環境 與 protoc 版本:

# cat /etc/redhat-release 
    CentOS Linux release 7.8.2003 (Core)

# protoc --version
    libprotoc 3.11.4

一、安裝 PHP 的 Protocol Buffers 擴展:

1.1 默認安裝最新版本:

# pecl install protobuf

1.2 指定版本號安裝:

# pecl install protobuf-{VERSION}

1.3 查看擴展是否已安裝:

# php -m | grep protobuf
protobuf

1.4 查看protobuf擴展的版本信息:

# php --ri protobuf

protobuf

Version => 3.13.0

Directive => Local Value => Master Value
protobuf.keep_descriptor_pool_after_request => 0 => 0

二、編寫編譯proto文件:

2.1 編寫proto文件:

# cat pack.proto

syntax = "proto3";  // 語法協議
package pack.base;  // 命名空間

// 枚舉,登錄類型
enum LoginType{
    GUEST = 0;
    APPLE = 1;
    RENREN = 2;
    FACEBOOK = 3;
    KAIXIN = 4;
    GAMECENTER = 5;
    SINA = 6;
    WEICHAT = 7;
    ALIPAY = 8;
}

message LoginReq{
    string sig = 1;
    LoginType login_type = 2;
    string openid = 3;
    string channel = 4;
    string version = 5;
    string device_id = 6;
    string mac_id = 7;
    repeated int32 login_num = 8;
}

注意: proto3 僅僅支持 repeated字段修飾,如果使用required,optional編譯會報錯。

2.2 在proto文件目錄,創建編譯文件夾:

# mkdir proto_php

2.3 編譯proto文件,生成 php類:

# protoc --php_out=./proto_php  pack.proto

目錄結構如下:

├─proto_php
│  ├─GPBMetadata
│  │   -- Pack.php
│  │
│  └─Pack
│      └─Base
│          -- LoginReq.php
│          -- LoginType.php
├─ test_proto.php

三、測試腳本:

# cat test_proto.php
<?php

defined('DS') or define('DS', DIRECTORY_SEPARATOR);
/**
 * protobuf 測試, php
 * 
 * 編譯proto文件,生成 php類
 * # protoc --php_out=./proto_php  pack.proto
 */

require __DIR__ . DS . 'proto_php' .DS. 'GPBMetadata' . DS . 'Pack.php';
require __DIR__ . DS . 'proto_php' .DS. 'Pack' . DS . 'Base' . DS .'LoginReq.php';
require __DIR__ . DS . 'proto_php' .DS. 'Pack' . DS . 'Base' . DS . 'LoginType.php';

use Pack\Base\{LoginReq, LoginType};

// 實例化 message類
$login_req = new LoginReq();

$login_req->setSig(md5(microtime(true)));
$login_req->setLoginType(LoginType::APPLE);
$login_req->setOpenid(sprintf('openid_%u', mt_rand(1000, 29520)));
$login_req->setChannel(sprintf('channel_00%u', mt_rand(1000, 29520)));
$login_req->setVersion(sprintf('V.%u.%u', mt_rand(1, 3), mt_rand(4, 20)));
$login_req->setDeviceId(sprintf('A00-B%s-C%s-D%s', mt_rand(1,6), mt_rand(8,70), mt_rand(100,237)));
$login_req->setMacId(str_pad('MAC', 20));
$login_req->setLoginNum(range(1,10));

// 序列化成二進制字符串
$data = $login_req->SerializeToString();

// 解析
$login_rsp = new LoginReq();
// 二進制字符串反序列化
$login_rsp->mergeFromString($data);

echo 'sig: ', $login_rsp->getSig(), PHP_EOL;
echo 'login_type: ', $login_rsp->getLoginType(), PHP_EOL;
echo 'openid: ', $login_rsp->getOpenid(), PHP_EOL;
echo 'channel: ', $login_rsp->getChannel(), PHP_EOL;
echo 'version: ', $login_rsp->getVersion(), PHP_EOL;
echo 'device_id: ', $login_rsp->getDeviceId(), PHP_EOL;

// repeated 類型的處理:
echo 'login_num size: ', count($login_rsp->getLoginNum()), PHP_EOL;

$login_num = [];
foreach ($login_rsp->getLoginNum() as $key => $value) {
    $login_num[] = $value;
}

echo 'login_num: ', implode(',', $login_num), PHP_EOL;

輸出結果如下:

# php -f test_proto.php

sig: 9908432d8bddb847a25c45fce00656ab
login_type: 1
openid: openid_23903
channel: channel_0018442
version: V.2.9
device_id: A00-B1-C24-D170
login_num size: 10
login_num: 1,2,3,4,5,6,7,8,9,10
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章