PHP 生成コードガイド
このドキュメントを読む前に、proto3 言語ガイドまたはエディション言語ガイドを読む必要があります。Protocol Buffer コンパイラは現在、PHP の proto3 およびエディションのコード生成のみをサポートしています。
コンパイラの呼び出し
Protocol Buffer コンパイラは、`--php_out=` コマンドラインフラグを付けて起動すると PHP 出力を生成します。`--php_out=` オプションのパラメーターは、コンパイラが PHP 出力を書き込むディレクトリです。PSR-4 に準拠するため、コンパイラはプロトファイルで定義されたパッケージに対応するサブディレクトリを作成します。さらに、コンパイラはプロトファイルの入力内の各メッセージに対して、パッケージのサブディレクトリに個別のファイルを作成します。メッセージの出力ファイル名は3つの部分で構成されます。
- ベースディレクトリ: プロトパス (`--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` は作成*しません*。それらはすでに存在している必要があります。
パッケージ
`.proto` ファイルで定義されたパッケージ名は、生成された PHP クラスのモジュール構造を生成するためにデフォルトで使用されます。次のようなファイルが与えられた場合:
package foo.bar;
message MyMessage {}
Protocol コンパイラは、`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`という名前空間が設定されます。
生成されるオプションは大文字と小文字を区別します。デフォルトでは、パッケージはパスカルケースに変換されます。
メッセージ
単純なメッセージ宣言を考えます。
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` も提供しています。これが指定されている場合、生成されるすべてのメッセージクラスにこの接頭辞が追加されます。
フィールド
メッセージ型の各フィールドに対して、Protocol Buffer コンパイラはフィールドを設定および取得するためのアクセサーメソッドのセットを生成します。アクセサーメソッドは、`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);
フィールドを設定するたびに、値はそのフィールドの宣言された型に対して型チェックされます。値が間違った型(または範囲外)である場合、例外がスローされます。デフォルトでは、型変換(たとえば、フィールドに値を割り当てたり、繰り返しのフィールドに要素を追加したりする場合)は、整数、浮動小数点数、および数値文字列との間で許可されます。許可されない変換には、配列またはオブジェクトとの間のすべての変換が含まれます。浮動小数点から整数へのオーバーフロー変換は未定義です。
スカラー値型テーブルで、各スカラーProtocol Buffers型に対応する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);
マップフィールド
Protocol Buffer コンパイラは、各マップフィールドに対して `MapField` を生成します。したがって、次のフィールドが与えられた場合、
map<int32, int32> weight = 1;
生成されたコードでは、次のようにできます。
$m->getWeight()[1] = 1;
列挙型
PHPにはネイティブなEnumがないため、代わりにProtocol Bufferコンパイラは、`.proto`ファイル内の各Enum型に対して、メッセージと同様にPHPクラスを生成し、各値に対して定数を定義します。したがって、次のEnumが与えられた場合、
enum TestEnum {
Default = 0;
A = 1;
}
コンパイラは以下のクラスを生成します。
class TestEnum {
const DEFAULT = 0;
const A = 1;
}
メッセージと同様に、PHPはネストされたクラスをサポートしないため、ネストされたenumは、それを含むメッセージが接頭辞となり、アンダースコアで区切られた同じ名前のPHPクラスになります。
class TestMessage_NestedEnum {...}
Enumクラス名または値名が予約されている場合(例:`Empty`)、クラス名または値名の前に`PB`という接頭辞が追加されます。
class PBEmpty {
const PBECHO = 0;
}
ファイルレベルオプション `php_class_prefix` も提供しています。これが指定されている場合、生成されるすべてのEnumクラスにこの接頭辞が追加されます。
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