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
,但它不會建立 build
或 build/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 中設定的欄位。