PHP 生成コードガイド

プロトコルバッファコンパイラが与えられたプロトコル定義に対して生成する PHP コードについて説明します。

このドキュメントを読む前に、proto3 言語ガイドまたはEditions 言語ガイドを読む必要があります。プロトコルバッファコンパイラは現在、PHP の proto3 および Editions のコード生成のみをサポートしていることに注意してください。

コンパイラの呼び出し

プロトコルバッファコンパイラは、--php_out= コマンドラインフラグを指定して呼び出されると、PHP 出力を生成します。--php_out= オプションのパラメータは、コンパイラが PHP 出力を書き込むディレクトリです。PSR-4 に準拠するため、コンパイラは proto ファイルで定義されたパッケージに対応するサブディレクトリを作成します。さらに、proto ファイル入力内の各メッセージに対して、コンパイラはパッケージのサブディレクトリに個別のファイルを作成します。メッセージの出力ファイル名は、次の3つの部分で構成されます。

  • ベースディレクトリ: 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 は作成しません。これらはすでに存在している必要があります。

パッケージ

.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 という名前空間が付きます。

生成されるオプションは大文字と小文字を区別します。デフォルトでは、パッケージはパスカルケースに変換されます。

メッセージ

単純なメッセージ宣言を考えます。

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 が与えられた場合、アクセサメソッドは getFieldNamesetFieldName になります。

// 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;
}

メッセージと同様に、ネストされた enum も、PHP がネストされたクラスをサポートしていないため、その包含メッセージがプレフィックスとして付き、アンダースコアで区切られた同じ名前の 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