コンテンツへスキップ

共有オブジェクト

PHPオブジェクトを参照渡しでJavaScriptに渡し、プロパティの読み取り、メソッドの呼び出し、PHP内の変更の確認を行うことは、まだサポートされていません。issue #18で追跡されています。このページでは現在の動作と、代わりに使えるパターンを説明します。

現在の動作

setGlobal()は値の変換のルールに従って値を変換します。呼び出し可能ではないPHPオブジェクトには変換方法がないため、グローバル変数はundefinedになります:

PHP
class Counter {
    public int $value = 0;
    public function increment(): void { $this->value++; }
}

$counter = new Counter();
$engine->setGlobal('counter', $counter);

$engine->eval('typeof counter');
// "undefined"

$engine->eval('counter.increment()');
// throws Phasis\Exceptions\TypeError:
// Cannot read properties of undefined (reading 'increment')

stdClass、エンティティやモデル、サービスコンテナ、ジェネレーター、リソースも同様です。呼び出し可能オブジェクトには次の2つの例外があります:

  • __invoke()を持つオブジェクトは、それを呼び出すJS関数になります。
  • [$counter, 'increment']のような呼び出し可能配列は、そのメソッドを呼び出すJS関数になります。

オブジェクトを公開するPhpBridgeやその他のラッパークラスはありません。src/Interop/には値コンバーターだけが含まれます。

メソッドをクロージャとして公開する

クロージャにはオブジェクトを保持させられるため、JSから関数を呼び出すと、元のPHPインスタンスでメソッドが実行されます:

PHP
$counter = new Counter();
$engine->setGlobal('increment', fn () => $counter->increment());
$engine->setGlobal('count', fn () => $counter->value);

$engine->eval('increment(); increment(); count()');
// 2

echo $counter->value;
// 2

呼び出し可能配列でも、クロージャを使わずに同じことができます:

PHP
$engine->setGlobal('increment', [$counter, 'increment']);

配列にまとめる

連想配列はJSオブジェクトになり、その中のクロージャは関数になります。これにより、メソッドがPHPインスタンスに対して動作する、オブジェクト形式のAPIをJSに渡せます:

PHP
class User {
    public function __construct(
        public string $name,
        public int $age,
        private string $secret,
    ) {}

    public function greet(): string {
        return "Hi, I'm $this->name";
    }
}

$user = new User('Ada', 36, 'topsecret');

$engine->setGlobal('user', [
    'name'   => $user->name,
    'greet'  => fn () => $user->greet(),
    'setAge' => function (int $age) use ($user): void { $user->age = $age; },
]);

$engine->eval('user.greet()');
// "Hi, I'm Ada"

$engine->eval('user.setAge(37)');
echo $user->age;
// 37

配列に含めたものだけがJSから見えるため、$secretのようなプライベート状態はPHP内に保持されます。

データフィールドはsetGlobal()の実行時にコピーされます。この例ではuser.nameはJS内の文字列です。これに代入するとJS側のコピーは変わりますが、$user->nameは変わりません。すべての書き込みをクロージャ経由にしてください。

データを渡し、結果を受け取る

データ形式のオブジェクトでは、渡すときに配列へ変換し、戻すときに結果を読み取ります:

PHP
$post = ['title' => 'Draft', 'tags' => ['php']];

$engine->setGlobal('post', $post);
$engine->eval('function edit(p) { p.title = "Edited from JS"; return p; }');

$post = $engine->call('edit', $post);
echo $post['title'];
// Edited from JS

get_object_vars($object)またはtoArray()メソッドを使うと、オブジェクトの配列を取得できます。返された配列をPHPでオブジェクト、エンティティ、またはモデルに適用してください。

注意点

  • これらのクロージャ内でスローされた例外は、現時点ではJSのtry / catchで捕捉できず、eval()の呼び出し元に届きます(issue #20)。ホスト関数を参照してください。
  • setGlobal()で登録されたクロージャは、エンジンがグローバルを保持する間、キャプチャしたオブジェクトを存続させます。