PHP 生成程式碼指南
在閱讀本文件之前,您應該閱讀proto3 語言指南或Editions 語言指南。請注意,協議緩衝區編譯器目前僅支援 proto3 和 Editions 為 PHP 生成程式碼。
編譯器呼叫
當使用 --php_out= 命令列標誌呼叫時,協議緩衝區編譯器會生成 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 定義如下
edition = "2023";
package foo.bar;
message MyMessage {}
編譯器將讀取檔案 src/foo.proto 並生成輸出檔案:build/gen/Foo/Bar/MyMessage.php。編譯器將根據需要自動建立目錄 build/gen/Foo/Bar,但它不會建立 build 或 build/gen;它們必須已經存在。
包(Packages)
.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 {
int32 int32_value = 1;
string string_value = 2;
repeated int32 repeated_int32_value = 3;
map<int32, int32> map_int32_int32_value = 4;
}
協議緩衝區編譯器生成一個名為 Foo 的 PHP 類。該類繼承自一個公共基類 Google\Protobuf\Internal\Message,它提供了用於編碼和解碼訊息型別的方法,如以下示例所示
$from = new Foo();
$from->setInt32Value(1);
$from->setStringValue('a');
$from->getRepeatedInt32Value()[] = 1;
$from->getMapInt32Int32Value()[1] = 1;
$data = $from->serializeToString();
$to = new Foo();
try {
$to->mergeFromString($data);
} catch (Exception $e) {
// Handle parsing error from invalid data.
...
}
您不應該建立自己的 Foo 子類。生成的類並非設計用於子類化,並可能導致“脆弱的基類”問題。
巢狀訊息會導致生成一個同名的 PHP 類,並以其包含訊息作為字首,用下劃線分隔,因為 PHP 不支援巢狀類。因此,例如,如果您的 .proto 檔案中有以下內容
message TestMessage {
message NestedMessage {
int32 a = 1;
}
}
編譯器將生成以下類
// PHP doesn’t support nested classes.
class TestMessage_NestedMessage {
public function __construct($data = NULL) {...}
public function getA() {...}
public function setA($var) {...}
}
如果訊息類名是保留字(例如,Empty),則在類名前新增字首 PB。
class PBEmpty {...}
我們還提供了檔案級別的選項 php_class_prefix。如果指定了此選項,它將作為字首新增到所有生成的訊息類中。
欄位
對於訊息型別中的每個欄位,協議緩衝區編譯器都會生成一組訪問器方法來設定和獲取該欄位。訪問器方法使用從 snake_case 欄位名轉換為 PascalCase 的名稱。因此,給定欄位 field_name,訪問器方法將是 getFieldName 和 setFieldName。
// optional MyEnum optional_enum
$m->getOptionalEnum();
$m->setOptionalEnum(MyEnum->FOO);
$m->hasOptionalEnum();
$m->clearOptionalEnum();
// MyEnum implicit_enum
$m->getImplicitEnum();
$m->setImplicitEnum(MyEnum->FOO);
每當您設定一個欄位時,值都會根據該欄位的宣告型別進行型別檢查。如果值型別錯誤(或超出範圍),將引發異常。預設情況下,允許在整數、浮點數和數字字串之間進行型別轉換(例如,將值分配給欄位或向重複欄位新增元素)。不允許的轉換包括所有到/從陣列或物件的轉換。浮點數到整數的溢位轉換是未定義的。
您可以在標量值型別表中檢視每個標量協議緩衝區型別對應的 PHP 型別。
has... 和 clear...
對於具有顯式存在的欄位,編譯器會生成一個 has...() 方法。如果欄位已設定,此方法返回 true。
編譯器還會生成一個 clear...() 方法。此方法取消設定欄位。呼叫此方法後,has...() 將返回 false。
對於具有隱式存在的欄位,編譯器不會生成 has...() 或 clear...() 方法。對於這些欄位,您可以透過將欄位值與預設值進行比較來檢查其是否存在。
奇異訊息欄位
對於具有訊息型別的欄位,編譯器會生成與標量型別相同的訪問器方法。
具有訊息型別的欄位預設為 null,並且在訪問時不會自動建立。因此,您需要顯式建立子訊息,如下所示
$m = new MyMessage();
$m->setZ(new SubMessage());
$m->getZ()->setFoo(42);
$m2 = new MyMessage();
$m2->getZ()->setFoo(42); // FAILS with an exception
您可以將任何例項分配給訊息欄位,即使該例項也在其他地方(例如,作為另一個訊息的欄位值)持有。
重複欄位
協議緩衝區編譯器為每個重複欄位生成一個特殊的 RepeatedField。因此,例如,給定以下欄位
repeated int32 foo = 1;
生成的程式碼允許您這樣做
$m->getFoo()[] =1;
$m->setFoo($array);
對映欄位
協議緩衝區編譯器為每個對映欄位生成一個 MapField。因此,給定此欄位
map<int32, int32> weight = 1;
您可以使用生成的程式碼執行以下操作
$m->getWeight()[1] = 1;
列舉
PHP 沒有原生列舉,因此協議緩衝區編譯器會為您的 .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,協議緩衝區編譯器會為 oneof 中的每個欄位生成一個 has 和 clear 方法,以及一個特殊的訪問器方法,讓您找出哪個 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 中當前已設定的欄位。如果 oneof 未設定,該方法返回一個空字串。
當您設定 oneof 中的一個欄位時,它會自動清除 oneof 中的所有其他欄位。如果您想在 oneof 中設定多個欄位,則必須在單獨的語句中進行。
$m = new TestMessage();
$m->setOneofInt32(42); // $m->hasOneofInt32() is true
$m->setOneofInt64(123); // $m->hasOneofInt32() is now false