Delade objekt
Det går inte ännu att skicka ett objekt av typen PHP till JavaScript som referens — läsa dess egenskaper, anropa dess metoder och se ändringarna i PHP. Detta följs i ärende nr 18. Den här sidan beskriver vad som händer i dag och vilka mönster som fungerar i stället.
Vad som händer i dag
setGlobal() konverterar sitt värde enligt reglerna i Värdekonvertering. Ett objekt av typen PHP som inte är anropsbart kan inte konverteras, så den globala variabeln blir 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')
Samma sak gäller stdClass, entiteter och modeller, tjänstebehållare, generatorer och resurser. Det finns två undantag: anropsbara objekt:
- Ett objekt med
__invoke()blir en funktion av typen JS som anropar det. - En anropsbar array, till exempel
[$counter, 'increment'], blir en funktion av typen JS som anropar metoden.
Det finns ingen PhpBridge eller annan omslagsklass för att exponera objekt; src/Interop/ innehåller bara värdekonverteraren.
Exponera metoder som slutningar
En slutning kan fånga objektet, så JS anropar en funktion och metoden körs på den ursprungliga instansen av typen 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
En anropsbar array gör samma sak utan en slutning:
$engine->setGlobal('increment', [$counter, 'increment']);
Samla dem i en array
En associativ array blir ett objekt av typen JS och slutningar i den blir funktioner. Då får JS ett objektformat API vars metoder arbetar på instansen av typen 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
Endast det du lägger i arrayen är synligt för JS, så privat tillstånd som $secret stannar i PHP.
Datafälten kopieras när setGlobal() körs. I exemplet är user.name en sträng i JS; om du tilldelar ett nytt värde ändras kopian i JS, inte $user->name. Låt en slutning hantera varje skrivning.
Skicka ut data och ta emot resultat
För objekt formade som data konverterar du till en array på vägen in och läser resultatet på vägen ut:
$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 metod för toArray() ger dig arrayen för ett objekt. Applicera sedan den returnerade arrayen på objektet, entiteten eller modellen i PHP.
Förbehåll
- Undantag som kastas inuti dessa closures kan för närvarande inte fångas av JS
try/catch; de når anroparen aveval()(problem nr 20). Se Värdfunktioner. - En closure som registreras med
setGlobal()håller objekten den fångar vid liv så länge motorn behåller globalen.