更多优质内容
请关注公众号

 PB协议(二)Protobuf的PHP开发教程-张柏沛IT博客

正文内容

PB协议(二)Protobuf的PHP开发教程

栏目:其他内容 系列:PB协议 发布时间:2022-10-23 12:54 浏览量:1320

本文介绍PB协议与PHP语言相关的特性和知识点,以及如何使用PHP解析pb协议。


· packages

proto文件指定了package后,在编译为PHP文件时会根据package包名路径为构建出的PHP文件设置命名空间,并且创建对应命名空间的目录。

package foo.bar;
message MyMessage {}

protoc编译器会为上述proto文件生成的PHP文件设置命名空间为 "Foo\Bar\MyMessage"。


· Messages

message Person {
    string name = 1;
    int32 age = 2;
    string sex = 3;
}


如上所示,PB编译器生成PHP代码后,PHP代码里会包含一个Foo的类。Foo类继承了PB提供的Message基类,Message基类提供了一系列的访问器和修改器供我们访问和修改message中的字段。

$person = new Test\Person();
$person->setName("lailaiji");
$person->setAge("28");
$person->setSex(true);
$data = $person->serializeToString();
file_put_contents('data.bin',$data);


又例如:

$m = new MyMessage();
$m->setX(1);
$val = $m->getX();

$a = 1;
$m->setX($a);


Message对象还提供了序列化和反序列化的方法。

serializeToString:序列化成二进制字符串

serializeToJsonString:序列化成JSON字符串

mergeFromString:二进制字符串反序列化

mergeFromJsonString:Json字符串反序列化


序列化示例

<?php
include 'vendor/autoload.php';
include 'GPBMetadata/Person.php';
include 'Test/Person.php';
$person = new Test\Person();
$person->setName("lailaiji");
$person->setAge("28");
$person->setSex(true);
$data = $person->serializeToString();
file_put_contents('data.bin',$data);


反序列化示例

<?php
include 'vendor/autoload.php';
include 'GPBMetadata/Person.php';
include 'Test/Person.php';

$bindata = file_get_contents('./data.bin');
$person = new Test\Person();
$person->mergeFromString($bindata);
echo $person->getName();


获取一个已有内容的message的所有字段值

$json = $message->serializeToJsonString();
$data = json_decode($json, true);


注意:

1、当你使用修改器(set方法)修改message的一个字段时,PHP 会根据该字段的声明类型对该值进行类型检查。如果值的类型错误(或超出范围),则会引发异常。默认情况下,允许在整数、浮点数和数字字符串之间进行类型转换。不允许的转换包括与数组或对象的所有转换。


2、消息类型的字段(即字段的类型是一个嵌套message)的默认值是null,且访问该字段时不会自动创建。因此,您需要显式创建子消息。

例如:

$m = new MyMessage();
$m->setZ(new SubMessage());
$m->getZ()->setFoo(42);

$m2 = new MyMessage();
$m2->getZ()->setFoo(42);  // 报错,原因是getZ()返回null


3、repeated字段和map字段的操作类似数组

proto文件内容:

repeated int32 foo = 1;
map<int32, int32> weight = 1;


生成的PHP代码允许您执行此操作:

$m->getFoo()[] =1;
$m->setFoo($array);
$m->getWeight()[1] = 1;

需要注意,PHP中使用message对象的访问器获取一个repeated对象时,返回的是一个RepeatedField对象,而非数组。如果需要得到数组,可以使用iterator_to_array()函数。

$foo = iterator_to_array($m->getFoo();


4、PHP没有原生枚举,因此PB编译器会为.proto文件中的每个枚举类型生成一个 PHP 类。

enum TestEnum {
  Default = 0;
  A = 1;
}

编译后:

class TestEnum {
  const DEFAULT = 0;
  const A = 1;
}


5、对于oneof,PB 编译器生成与常规单数字段相同的代码,但还添加了一个特殊的访问器方法,可让您找出设置了哪个 oneof 字段(如果有)。此时如果要用访问器或修改器方法,就不再是get某个具体字段。

message TestMessage {
  oneof test_oneof {
    int32 oneof_int32 = 1;
    int64 oneof_int64 = 2;
  }
}


编译器编译成的PHP文件会生成以下字段和特殊方法:

class TestMessage {
  private oneof_int32;
  private oneof_int64;
  public function getOneofInt32();
  public function setOneofInt32($var);
  public function getOneofInt64();
  public function setOneofInt64($var);
  public function getTestOneof();  // 返回字段名
}

访问器方法的名称基于 oneof 的名称,并返回一个枚举值,该值表示当前设置的 oneof 中的字段值。


6、对any类型的字段设置值时,不能够直接设置而是要使用setValue()方法。

message TestProductAttr{
    map<string, google.protobuf.Any> attr = 1;
}


正确做法:

$attrs = ["color"=>"read", "size"=>16];
$attrPb = new \protobuf\Test\TestProductAttr();
foreach($attr as $k=>$v){
    $any = new \Google\Protobuf\Any();
    $any->setTypeUrl('type.googleapis.com/google.protobuf.StringValue');
    $any->setValue($v);
    $attr[$k] = $any;
}
$attrPb->setAttr($attrs);

需要注意,any类型是一种泛型,因此设置的时候必须指定any实际的类型,否则在反编码的时候程序就不知道应该按整型、字符串还是map或数组等类型对其进行反编码。因此 protobuf 提供了type url属性供用户指明一个any类型的值应该按protobuf规定的哪种类型进行编码和反编码。

type url的格式一般为 type.googleapis.com/包名.message名,例如type.googleapis.com/google.protobuf.StringValue 就表示要按照protobuf官方内置的 StringValue这种内置的message类型对any类型进行解析,也就是解析为字符串格式。


protobuf内置的message类型可以在 google/protobuf 目录中查看,该目录是PHP使用composer安装 google/protobuf 依赖时生成的。




更多内容请关注微信公众号
zbpblog微信公众号

如果您需要转载,可以点击下方按钮可以进行复制粘贴;本站博客文章为原创,请转载时注明以下信息

张柏沛IT技术博客 > PB协议(二)Protobuf的PHP开发教程

热门推荐
推荐新闻