PHP 產生程式碼指南

描述 protocol buffer 編譯器針對任何給定的協定定義所產生的 PHP 程式碼。

在閱讀本文檔之前,您應該先閱讀proto3 語言指南。請注意,protocol buffer 編譯器目前僅支援 PHP 的 proto3 程式碼產生。

編譯器調用

當使用 --php_out= 命令列旗標調用時,protocol buffer 編譯器會產生 PHP 輸出。--php_out= 選項的參數是您希望編譯器寫入 PHP 輸出的目錄。為了符合 PSR-4,編譯器會建立一個對應於 proto 檔案中定義的套件的子目錄。此外,對於 proto 檔案輸入中的每個訊息,編譯器會在套件的子目錄中建立一個單獨的檔案。訊息的輸出檔案名稱由三部分組成

  • 基本目錄:proto 路徑(使用 --proto_path=-I 命令列旗標指定)會被輸出路徑(使用 --php_out= 旗標指定)取代。
  • 子目錄:套件名稱中的 . 會被作業系統目錄分隔符號取代。每個套件名稱元件都會轉換為大寫。
  • 檔案:訊息名稱會附加 .php

因此,舉例來說,假設您如下調用編譯器

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

src/example.proto 定義為

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 {}

協定編譯器會產生名稱為 Foo\Bar\MyMessage 的輸出類別。

命名空間選項

編譯器支援額外的選項來定義 PHP 和中繼資料命名空間。當定義時,這些會被用於產生模組結構和命名空間。給定像這樣的選項

package foo.bar;
option php_namespace = "baz\\qux";
option php_metadata_namespace = "Foo";
message MyMessage {}

協定編譯器會產生名稱為 baz\qux\MyMessage 的輸出類別。該類別將會具有命名空間 namespace baz\qux

協定編譯器會產生名稱為 Foo\Metadata 的中繼資料類別。該類別將會具有命名空間 namespace Foo

產生的選項區分大小寫。預設情況下,套件會轉換為 Pascal 大小寫。

訊息

給定一個簡單的訊息宣告

message Foo {}

protocol buffer 編譯器會產生一個名為 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);

每當您設定欄位時,都會根據該欄位的宣告類型檢查值。如果值的類型錯誤(或超出範圍),則會引發例外。預設情況下,允許在整數、浮點數和數字字串之間進行類型轉換(例如,將值指定給欄位或將元素新增至重複欄位)。不允許的轉換包括所有轉換為/從陣列或物件的轉換。浮點數到整數的溢位轉換是未定義的。

您可以在純量值類型表格中看到每個純量 protocol buffer 類型的對應 PHP 類型。

單數訊息欄位

訊息類型的欄位預設為 nil,並且在存取該欄位時不會自動建立。因此,您需要明確建立子訊息,如下所示

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

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

您可以將任何執行個體指派給訊息欄位,即使該執行個體也保留在其他位置(例如,作為另一個訊息上的欄位值)。

重複欄位

protocol buffer 編譯器會為每個重複欄位產生一個特殊的 RepeatedField。因此,舉例來說,給定以下欄位

repeated int32 foo = 1;

產生的程式碼可讓您執行此操作

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

Map 欄位

protocol buffer 編譯器會為每個 map 欄位產生一個 MapField。因此,給定這個欄位

map<int32, int32> weight = 1;

您可以使用產生的程式碼執行以下操作

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

列舉

PHP 沒有原生列舉,因此 protocol buffer 編譯器會為您的 .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

對於 oneof,protocol buffer 編譯器會產生與一般單數欄位相同的程式碼,但也會新增一個特殊的存取器方法,可讓您找出已設定的 oneof 欄位(如果有的話)。因此,給定此訊息

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
}

存取器方法的名稱基於 oneof 的名稱,並傳回一個列舉值,代表目前在 oneof 中設定的欄位。