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 buffers 類型的對應 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);
地圖欄位
protocol buffer 編譯器為每個地圖欄位產生一個 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 中的欄位。