Siirry sisältöön

Jaetut oliot

PHP-olion välittäminen JavaScript:lle viitteenä — sen ominaisuuksien lukeminen, metodien kutsuminen ja muutosten näkeminen kohteessa PHP — ei ole vielä tuettua. Asiaa seurataan kohteessa issue #18. Tällä sivulla kuvataan, mitä nyt tapahtuu ja mitkä toimintatavat toimivat sen sijaan.

Mitä tällä hetkellä tapahtuu

setGlobal() muuntaa arvonsa arvojen muunnos-sivun sääntöjen mukaan. PHP-oliolle, joka ei ole kutsuttava, ei ole muunnosta, joten globaali saa arvon 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')

Sama koskee stdClass-olioita, entiteettejä ja malleja, palvelusäiliöitä, generaattoreita ja resursseja. Poikkeuksia ovat kaksi kutsuttavaa oliotyyppiä:

  • Olio, jolla on __invoke(), muuttuu JS-funktioksi, joka kutsuu sitä.
  • Kutsuttava taulukko, kuten [$counter, 'increment'], muuttuu JS-funktioksi, joka kutsuu kyseistä metodia.

Olioiden tarjoamiseen ei ole PhpBridge-luokkaa tai muuta kääreluokkaa; src/Interop/ sisältää vain arvomuuntimen.

Metodien tarjoaminen sulkeumina

Sulkeuma voi pitää olion sisällään, jolloin JS kutsuu funktiota ja metodi suoritetaan alkuperäisessä PHP-instanssissa:

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

Kutsuttavalla taulukolla saadaan sama aikaan ilman sulkeumaa:

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

Ryhmittele ne taulukkoon

Assosiatiivinen taulukko muuttuu JS-olioksi, ja sen sisällä olevista sulkeumista tulee funktioita. Näin JS saa olion muotoisen API-olion, jonka metodit toimivat PHP-instanssissa:

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

Vain taulukkoon lisäämäsi sisältö näkyy kohteessa JS, joten yksityinen tila, kuten $secret, pysyy kohteessa PHP.

Tietokentät ovat kopioita, jotka otetaan, kun setGlobal() suoritetaan. Esimerkissä user.name on merkkijono kohteessa JS; sen asettaminen muuttaa JS-kopiota, ei arvoa $user->name. Ohjaa kaikki kirjoitukset sulkeuman kautta.

Vie tiedot ulos ja tulokset takaisin

Tietomaisille olioille voit muuntaa ne taulukoksi sisään mennessä ja lukea tuloksen ulos tullessa:

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) tai toArray()-metodi antaa olion taulukkomuodossa. Käytä sitten palautettua taulukkoa olioon, entiteettiin tai malliin kohteessa PHP.

Huomioitavaa

  • Näissä sulkeumissa heitettyjä poikkeuksia ei tällä hetkellä voi siepata JS-kohteen try- tai catch-käsittelijöillä; ne päätyvät kohteen eval() kutsujalle (issue #20). Katso Isäntäfunktiot.
  • setGlobal()-määrityksellä rekisteröity sulkeuma pitää sisälläänsä kaappaamansa oliot niin kauan kuin moottori säilyttää globaalin.