Megosztott objektumok
Egy PHP objektum referenciaként történő átadása a JavaScript számára — a tulajdonságok olvasása, a metódusok hívása és a PHP-ban végzett módosítások észlelése — egyelőre nem támogatott. A fejlesztést a 18. számú issue követi. Ez az oldal bemutatja a jelenlegi működést és a helyette használható megoldásokat.
Jelenlegi működés
A setGlobal() az értékét az Értékkonverzió szabályai szerint konvertálja. A nem hívható PHP objektumok nem konvertálhatók, ezért a globális változó értéke undefined lesz:
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')
Ugyanez érvényes a stdClass elemekre, entitásokra és modellekre, szolgáltatástárolókra, generátorokra és erőforrásokra. Két kivétel van: a hívható objektumok:
- A
__invoke()tulajdonsággal rendelkező objektumból JS függvény lesz, amely meghívja azt. - Az olyan hívható tömbből, mint a
[$counter, 'increment'], JS függvény lesz, amely meghívja az adott metódust.
Nincs PhpBridge vagy más burkolóosztály az objektumok közzétételéhez; a src/Interop/ csak az értékkonvertert tartalmazza.
Metódusok közzététele closure-ökként
A closure rögzítheti az objektumot, így a JS egy függvényt hív meg, a metódus pedig az eredeti PHP példányon fut le:
$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
A hívható tömb ugyanezt teszi closure nélkül:
$engine->setGlobal('increment', [$counter, 'increment']);
Csoportosítás tömbbe
Az asszociatív tömb JS objektummá válik, a benne lévő closure-ök pedig függvényekké. Így a JS egy objektumszerű API-t kap, amelynek metódusai az PHP példányon működnek:
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
A JS csak azt látja, amit beletesz a tömbbe, ezért a privát állapot, például a $secret, a PHP környezetében marad.
Az adatmezők másolatként jönnek létre a setGlobal() futásakor. A példában a user.name karakterlánc a JS környezetében; ha értéket rendel hozzá, az JS másolat változik meg, nem a $user->name. Minden írási műveletet closure-ön keresztül végezzen.
Adat átadása és eredmények visszaolvasása
Adatként ábrázolható objektumoknál alakítsa az adatot tömbbé az átadáskor, majd olvassa ki az eredményt visszatéréskor:
$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
A get_object_vars($object) vagy egy toArray() metódus adja vissza az objektum tömbös reprezentációját. Ezután alkalmazza a visszakapott tömböt a PHP környezetben lévő objektumára, entitására vagy modelljére.
Tudnivalók
- Az ezekben a closure-ökben dobott kivételeket jelenleg nem tudja elkapni a JS
try/catch; azok aeval()hívójához jutnak el (20. számú issue). Lásd: Gazdafüggvények. - A
setGlobal()által regisztrált closure életben tartja a rögzített objektumokat, amíg a motor megőrzi a globális változót.