
# 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](https://github.com/inline0/phasis/issues/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](https://phasis.inline0.dev/sv/docs/interop/values). Ett objekt av typen PHP som inte är anropsbart kan inte konverteras, så den globala variabeln blir `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')
```

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:

```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:

```php
$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:

```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:

```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)` 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 av `eval()` ([problem nr 20](https://github.com/inline0/phasis/issues/20)). Se [Värdfunktioner](https://phasis.inline0.dev/sv/docs/interop/host-functions#throwing-back-into-js).
- En closure som registreras med `setGlobal()` håller objekten den fångar vid liv så länge motorn behåller globalen.
