Window环境PHP7使用Protobuf开发详解 转

转自:https://imsoul.blog.csdn.net/article/details/104342034?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.add_param_isCf&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.add_param_isCf

安装Protoc

用于生成PHP代码
下载地址: https://github.com/protocolbuffers/protobuf/releases
选择Window平台下载地址
Protoc下载
这里选择最新版
https://github.com/protocolbuffers/protobuf/releases/download/v3.11.4/protoc-3.11.4-win64.zip

 

 

下载后选择一个目录解压, 这里解压到

D:\Program Files\protoc-3.11.4-win64

设置环境变量
设置环境变量
查看是否安装成功
查看版本号
编写 protocol buffers 文件, 安装protoc解压目录的include包含一些样例

D:\Program Files\protoc-3.11.4-win64\include

这里简单编写一个User.proto

syntax = "proto3";

package App.Bean;

message User{
  int32 uid = 1;
  string username = 2;
  string nickname = 3;
  int32 age = 4;
  int32 sex = 5;
}

新建一个项目, E:\wamp64\www\wwwroot\gitroot\prototest
进入目录新建文件夹proto
把User.proto文件放在proto目录中

执行命令生成文件

protoc --php_out=.  proto/User.proto

–php_out=.表示编译成PHP代码,放在当前目录(.),也可以指定文件夹。

protoc --php_out=mylib  proto/User.proto

protoc还支持编译其他语言:

$ protoc | grep "=OUT_DIR"
  --cpp_out=OUT_DIR           Generate C++ header and source.
  --csharp_out=OUT_DIR        Generate C# source file.
  --java_out=OUT_DIR          Generate Java source file.
  --js_out=OUT_DIR            Generate JavaScript source.
  --objc_out=OUT_DIR          Generate Objective C header and source.
  --php_out=OUT_DIR           Generate PHP source file.
  --python_out=OUT_DIR        Generate Python source file.
  --ruby_out=OUT_DIR          Generate Ruby source file.

编译结果:
生成文件
生成后的文件结构:

├── GPBMetadata
│   └── Proto
│       └── User.php
└── App
│   └── Bean
│       └── User.php
├── proto
│   └── User.proto

测试代码

	$data = [
    'uid'=>1,
    'username'=>'Soul'
    ];
    $userProto = new User($data);
    $userProto->setAge(20);
    $userProto->setSex(1);
    $str = $userProto->serializeToString();    
    print_r($userProto->serializeToJsonString());
    
    $userProto2= new User();    
    $userProto2->mergeFromString($str);

proto语法

官方文档: https://developers.google.com/protocol-buffers/docs/overview
也可以参考 Protobuf3语言指南 写得不错

1、proto3

proto 有proto3 和 proto2。proto3 比 proto2 支持更多语言但 更简洁。去掉了一些复杂的语法和特性,更强调约定而弱化语法。如果是首次使用 Protobuf ,建议使用 proto3 。详见参考文献说明。

需要在proto头部申明:

syntax = "proto3";

如果你没有指定这个,编译器会使用proto2。

2、注释

使用 //,示例:

message UserList {
    repeated User list = 1; //用户列表
    int32 page = 2; //分页
    int32 limit = 3; //分页条数
}

其中写在每个属性后面的注释在生产的代码里面有保留。

3、message

message类似于结构体的概念,最终编译为代码在PHP、JAVA里就是一个类,在golang里是结构体。每一个属性都会生成对应的getXXX、setXXX方法。

4、字段规则

repeated表示这个属性重复N次,在相对应的编程语言中通常是一个空的list。PHP里对应数组。

reserved表示标识号保留暂时不用。

message Foo {
  reserved 2, 15, 9 to 11;
  reserved "foo", "bar";
}

在消息定义中,每个字段都有唯一的一个数字标识符。这些标识符是用来在消息的二进制格式中识别各个字段的,一旦开始使用就不能够再改变。注:[1,15]之内的标识号在编码的时候会占用一个字节。[16,2047]之内的标识号则占用2个字节。所以应该为那些频繁出现的消息元素保留 [1,15]之内的标识号。**切记:要为将来有可能添加的、频繁出现的标识号预留一些标识号。**最小的标识号可以从1开始,最大到2^29 - 1, or 536,870,911。

5、支持的数据类型

.proto Type Notes C++ Type Java Type Python Type[2] Go Type Ruby Type C# Type PHP Type
double double double float float64 Float double float
float float float float float32 Float float float
int32 使用变长编码,对于负值的效率很低,如果你的域有可能有负值,请使用sint64替代 int32 int int int32 Fixnum 或者 Bignum(根据需要) int
uint32 使用变长编码 uint32 int int/long uint32 Fixnum 或者 Bignum(根据需要) uint
uint64 使用变长编码 uint64 long int/long uint64 Bignum ulong
sint32 使用变长编码,这些编码在负值时比int32高效的多 int32 int int int32 Fixnum 或者 Bignum(根据需要) int
sint64 使用变长编码,有符号的整型值。编码时比通常的int64高效。 int64 long int/long int64 Bignum long
fixed32 总是4个字节,如果数值总是比总是比228大的话,这个类型会比uint32高效。 uint32 int int uint32 Fixnum 或者 Bignum(根据需要) uint
fixed64 总是8个字节,如果数值总是比总是比256大的话,这个类型会比uint64高效。 uint64 long int/long uint64 Bignum ulong
sfixed32 总是4个字节 int32 int int int32 Fixnum 或者 Bignum(根据需要) int
sfixed64 总是8个字节 int64 long int/long int64 Bignum long
bool bool boolean bool bool TrueClass/FalseClass bool boolean
string 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。 string String str/unicode string String (UTF-8) string
bytes 可能包含任意顺序的字节数据。 string ByteString str []byte String (ASCII-8BIT) ByteString

6、默认值说明

string类型,默认值是空字符串
bytes类型,默认值是空bytes
bool类型,默认值是false
数字类型,默认值是0
枚举类型,默认值是第一个枚举值,即0
repeated修饰的属性,默认值是空.

7、枚举

使用enum关键字定义枚举,值必须从0开始:

enum Corpus {
    UNIVERSAL = 0;
    WEB = 1;
    IMAGES = 2;
    LOCAL = 3;
    NEWS = 4;
    PRODUCTS = 5;
    VIDEO = 6;
}

8、引用类型

上面的UserList就引用了User类型。大家可以看一下。

9、import

如果一个proto文件引用了另外一个proto文件,那么可以使用import关键字在头部申明:

import "User.proto";

10、Map类型

proto支持map属性类型的定义,语法如下:

map<key_type,value_type> map_field = N;

示例:

map<string, string> ext = 6; //扩展信息

这个map对于PHP来说就是关联数组,对于golang来说就是Map。

10、Any

Any类型允许包装任意的message类型,可以通过pack()和unpack()(方法名在不同的语言中可能不同)方法打包/解包:

import "google/protobuf/any.proto";

message Response {
    google.protobuf.Any data = 1;
}

PHP开发的同学可能觉得Any没必要,因为数组里任何类型都可以放,但是对于强类型语言,数组里的值类型必须是一致的,使用Any类型可以解决这个问题。Any相当于把值包装了一层,这样都是Any类型。

11、服务定义

service UserService {
    //  方法名  方法参数                 返回值
    rpc GetUser(Request) returns (Response); 
}

这相当于定义了一个类,里面有一个对外的GetUser()方法。这个通常用于定义RPC服务,与gRPC结合使用。

12、从.proto文件生成了什么?

当用protocol buffer编译器来运行.proto文件时,编译器将生成所选择语言的代码,这些代码可以操作在.proto文件中定义的消息类型,包括获取、设置字段值,将消息序列化到一个输出流中,以及从一个输入流中解析消息。

PHP:每一个Message或者Enum生成一个类,另外还会生成GPBMetadata。
C++:编译器会为每个.proto文件生成一个.h文件和一个.cc文件,.proto文件中的每一个消息有一个对应的类。
Java:编译器为每一个消息类型生成了一个.java文件,以及一个特殊的Builder类(该类是用来创建消息类接口的)。
Python:Python编译器为.proto文件中的每个消息类型生成一个含有静态描述符的模块,该模块与一个元类(metaclass)在运行时(runtime)被用来创建所需的Python数据访问类。
go:编译器会位每个消息类型生成了一个.pd.go文件。
Ruby:编译器会为每个消息类型生成了一个.rb文件。
Objective-C:编译器会为每个消息类型生成了一个pbobjc.h文件和pbobjcm文件,.proto文件中的每一个消息有一个对应的类。
C#:编译器会为每个消息类型生成了一个.cs文件,.proto文件中的每一个消息有一个对应的类。

IDE插件

1、JetBrains PhpStorm 可以在插件里找到Protobuf安装,重启IDE后就支持proto格式语法了。

2、VScode 在扩展里搜索 Protobuf,安装即可。

3、protobuf的 php 扩展类在ide中没有提示,可将https://github.com/protocolbuffers/protobuf/tree/master/php/src 目录下载到本地,将此目录加到ide的include_path中即可。

点赞

发表回复

电子邮件地址不会被公开。必填项已用 * 标注