Delte objekter
Det er endnu ikke understøttet at sende et PHP-objekt til JavaScript som reference, læse dets egenskaber, kalde dets metoder og se ændringerne i PHP. Det spores i issue #18. Denne side beskriver, hvad der sker i dag, og hvilke mønstre der virker i stedet.
Hvad der sker i dag
setGlobal() konverterer værdien efter reglerne i Værdikonvertering. Et PHP-objekt, der ikke kan kaldes, har ingen konvertering, så den globale variabel bliver undefined:
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')
Det samme gælder stdClass, entiteter og modeller, servicecontainere, generatorer og ressourcer. To undtagelser er objekter, der kan kaldes:
- Et objekt med
__invoke()bliver til en JS-funktion, der kalder det. - Et callable array som
[$counter, 'increment']bliver til en JS-funktion, der kalder den pågældende metode.
Der findes ingen PhpBridge eller anden wrapperklasse til eksponering af objekter; src/Interop/ indeholder kun værdikonverteren.
Eksponér metoder som closure-funktioner
En closure-funktion kan indfange objektet, så JS kalder en funktion, og metoden kører på den oprindelige PHP-instans:
$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
Et callable array gør det samme uden en closure-funktion:
$engine->setGlobal('increment', [$counter, 'increment']);
Saml dem i et array
Et associativt array bliver til et JS-objekt, og closure-funktionerne i det bliver til funktioner. Dermed får JS et API med objektform, hvis metoder arbejder på PHP-instansen:
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
Kun det, du lægger i arrayet, er synligt for JS, så privat tilstand som $secret forbliver i PHP.
Datafelterne er kopier, der tages, når setGlobal() kører. I eksemplet er user.name en streng i JS; hvis du tildeler den en værdi, ændres JS-kopien, ikke $user->name. Send alle skrivninger gennem en closure-funktion.
Send data ud, og resultater tilbage
For dataformede objekter skal du konvertere til et array på vej ind og læse resultatet på vej ud:
$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) eller en toArray()-metode giver dig arrayet for et objekt. Anvend derefter det returnerede array på dit objekt, din entitet eller din model i PHP.
Forbehold
- Undtagelser, der kastes inde i disse closure-funktioner, kan i øjeblikket ikke fanges af JS
try/catch; de når den, der kaldteeval()(issue #20). Se Værtsfunktioner. - En closure-funktion, der er registreret med
setGlobal(), holder de objekter, den indfanger, i live, så længe motoren har den globale variabel.