Gedeelde objecten
Een object van PHP via een referentie doorgeven aan JavaScript, de eigenschappen ervan lezen, de methoden aanroepen en wijzigingen zien in PHP — dat wordt nog niet ondersteund. Dit wordt bijgehouden in issue #18. Deze pagina beschrijft wat er momenteel gebeurt en welke patronen wel werken.
Wat er momenteel gebeurt
setGlobal() zet de waarde om volgens de regels in Waard-conversie. Een object van PHP dat niet callable is, kan niet worden geconverteerd; daarom is de globale variabele 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')
Hetzelfde geldt voor stdClass, entiteiten en modellen, servicecontainers, generators en resources. Twee uitzonderingen zijn callable objecten:
- Een object met
__invoke()wordt een functie van JS die het aanroept. - Een callable array zoals
[$counter, 'increment']wordt een functie van JS die die methode aanroept.
Er is geen PhpBridge- of andere wrapperklasse om objecten beschikbaar te maken; src/Interop/ bevat alleen de waardeconverter.
Methoden als closures beschikbaar maken
Een closure kan het object vastleggen, zodat JS een functie aanroept en de methode wordt uitgevoerd op de oorspronkelijke instantie van 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
Een callable array doet hetzelfde zonder closure:
$engine->setGlobal('increment', [$counter, 'increment']);
Groepeer ze in een array
Een associatieve array wordt een object van JS, en closures erin worden functies. Zo krijgt JS een objectvormige API waarvan de methoden werken op de instantie van 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
Alleen wat je in de array plaatst, is zichtbaar voor JS. Privéstatus zoals $secret blijft dus in PHP.
De datavelden zijn kopieën die worden gemaakt wanneer setGlobal() wordt uitgevoerd. In het voorbeeld is user.name een tekenreeks in JS; als je die wijzigt, verandert de kopie van JS, niet $user->name. Laat alle schrijfbewerkingen via een closure lopen.
Gegevens doorgeven en resultaten terugkrijgen
Zet objecten met een gegevensstructuur om naar een array wanneer je ze doorgeeft en lees het resultaat wanneer je ze terugkrijgt:
$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) of een methode van toArray() geeft je de array voor een object. Pas de geretourneerde array vervolgens in PHP toe op je object, entiteit of model.
Kanttekeningen
- Uitzonderingen die binnen deze closures worden gegooid, kunnen momenteel niet worden opgevangen door JS
try/catch; ze bereiken de aanroeper vaneval()(issue #20). Zie Hostfuncties. - Een closure die met
setGlobal()is geregistreerd, houdt de objecten die deze vastlegt in leven zolang de engine de globale variabele bewaart.