Protocel Buffers - 生成PHP代码

编译器调用

protobuf编译器使用--php_out = command-line 产生PHP输出。 --php_out 参数编译器编写PHP输出的目录。 为了符合PSR-4,编译器创建了与proto文件中定义的包相对应的子目录。 另外,对于proto文件输入中的每条消息,编译器会在程序包的子目录中创建一个单独的文件。 消息输出文件的名称由三部分组成:

  • 基本目录:使用输出路径(用--php_out = flag指定)替换原路径(使用--proto_path-I命令行标志指定)。
  • 子目录:. 在包名中被操作系统目录分隔符替换。 每个包名称组件都是大写的。
  • 文件:消息名称由.php文件附加。

举个例子:

base
$ protoc --proto_path=src --php_out=build/gen src/example.proto

其中src/example.proto声明为:

syntax = "proto3";
package foo.bar;
message MyMessage {}

编译器将读取src/foo.proto文件并生成输出文件:build/gen/Foo/Bar/MyMessage.php。 编译器会根据需要自动创建目录build/gen/Foo/Bar,但不会创建buildbuild/gen; 他们必须已经存在。

.proto文件中定义的包名称用于为生成的PHP类生成模块结构。 给定一个文件,如:

package foo.bar;

message MyMessage {}

消息

给出一个简单的消息声明:

message Foo {}

protobuf编译器生成一个名为Foo的PHP类。 该类继承自通用基类Google\Protobuf\Internal\Message,它提供了用于编码和解码消息类型的方法,如以下示例所示:

$from = new Foo();
$from->setInt32(1);
$from->setString('a');
$from->getRepeatedInt32()[] = 1;
$from->getMapInt32Int32()[1] = 1;
$data = $from->serializeToString();
try {
  $to->mergeFromString($data);
} catch (Exception $e) {
  // Handle parsing error from invalid data.
  ...
}

你不应该创建你自己的Foo子类。 生成的类不是为子类设计的,可能会导致“脆弱的基类”问题。

由于PHP不支持嵌套类,所以嵌套消息导致一个具有相同名称前缀的PHP类,它们包含消息的前缀并用下划线分隔。 所以,例如,如果你在.proto中有这个:

message TestMessage {
  optional int32 a = 1;
  message NestedMessage {...}
}

编译器将生成以下类:

class TestMessage {
  public a;
}

// PHP doesn’t support nested classes.
class TestMessage_NestedMessage {...}

如果消息类名称是保留的(例如,Empty),则前缀PB将预先添加到类名称中:

class PBEmpty {...}

我们还提供了文件级选项php_class_prefix。 如果这是指定的,则它被预先添加到所有生成的消息类别中。

字段

对于消息类型中的每个字段,都有访问器方法来设置和获取字段。 所以给定一个字段x,你可以写:

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

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

无论何时设置字段,都会根据该字段的声明类型对该值进行类型检查。 如果该值的类型错误(或超出范围),则会引发异常。 通过默认的类型转换(例如,向字段赋值或向重复字段添加元素时)允许来自整数,浮点和数字字符串。 不允许的转换包括所有转换为数组或对象的转换。 浮点到整数溢出转换未定义。

您可以在标量值类型表中看到每个标量协议缓冲区类型的相应PHP类型。

单一消息字段

消息类型的字段默认为nil,并且在字段被访问时不会自动创建。 因此您需要明确地创建子消息,如下所示:

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

$m2 = new MyMessage();
$m2->getZ()->setFoo(42);  // FAILS with an exception

您可以将任何实例指定给消息字段,即使实例也在其他位置(例如,作为另一个消息的字段值)保存。

重复的字段

protobuf编译器为每个重复字段生成一个特殊的RepeatedField。 因此,例如,给出以下字段:

repeated int32 foo = 1;

生成的代码可以让你这样做:

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

字段映射

protobuf编译器为每个map字段生成一个MapField。 所以考虑到这个领域:

map weight = 1;

您可以使用生成的代码执行以下操作:

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

枚举

PHP没有本地枚举,所以protobuf编译器.proto文件中的每个枚举类型生成一个PHP类,就像消息一样,每个值都定义了常量。 所以,鉴于这个枚举:

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

编译器生成以下类:

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

与消息一样,嵌套的枚举将导致一个具有相同名称前缀的PHP类,其中包含消息并以下划线分隔,因为PHP不支持嵌套类。

class TestMessage_NestedEnum {...}

如果保留了枚举类或值名称(例如,Empty),则前缀PB将预先添加到类或值名称中:

class PBEmpty {
  const PBECHO = 0;
}

我们还提供了文件级选项php_class_prefix。 如果指定了这个参数,它将被预置给所有生成的枚举类。

Oneof

对于oneofs,protobuf编译器生成的代码与常规单数字段的代码相同,但是也添加了一个特殊的访问器方法,可以让您找出设置哪个字段(如果有)。 所以,给出这个消息:

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

编译器生成以下字段和特殊方法:

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();  // Return field name
}

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

你可能感兴趣的:(PHP,其他)