
# Zdieľané objekty

Odovzdanie objektu PHP do JavaScript prostredníctvom referencie — čítanie jeho vlastností, volanie jeho metód a sledovanie zmien v PHP — **zatiaľ nie je podporované**. Eviduje sa to v [probléme č. 18](https://github.com/inline0/phasis/issues/18). Táto stránka opisuje, čo sa deje dnes a ktoré postupy možno použiť namiesto toho.

## Čo sa deje dnes

`setGlobal()` konvertuje svoju hodnotu podľa pravidiel v [Konverzia hodnôt](https://phasis.inline0.dev/sk/docs/interop/values). Objekt PHP, ktorý nemožno volať, sa nekonvertuje, takže globálna premenná je `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')
```

To isté platí pre `stdClass`, entity a modely, servisné kontajnery, generátory a zdroje. Výnimkou sú volateľné objekty:

- Objekt s `__invoke()` sa zmení na funkciu JS, ktorá ho zavolá.
- Volateľné pole, napríklad `[$counter, 'increment']`, sa zmení na funkciu JS, ktorá zavolá danú metódu.

Neexistuje `PhpBridge` ani iná obalová trieda na sprístupnenie objektov; `src/Interop/` obsahuje iba konvertor hodnôt.

## Sprístupnenie metód ako uzáverov

Uzáver môže zachytiť objekt, takže JS zavolá funkciu a metóda sa vykoná nad pôvodnou inštanciou 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
```

Volateľné pole dosahuje to isté bez uzáveru:

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

## Zoskupenie do poľa

Asociatívne pole sa zmení na objekt JS a uzávery v ňom sa zmenia na funkcie. Takto získa JS objekt API, ktorého metódy pracujú s inštanciou 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
```

Pre JS sú viditeľné iba položky, ktoré vložíte do poľa, takže súkromný stav, napríklad `$secret`, zostane v PHP.

Dátové polia sú kópie vytvorené pri spustení `setGlobal()`. V príklade je `user.name` reťazec v JS; priradenie zmení kópiu JS, nie `$user->name`. Každý zápis vykonávajte prostredníctvom uzáveru.

## Odovzdanie dát a získanie výsledkov

Pri objektoch s dátovou štruktúrou ich na vstupe preveďte na pole a na výstupe načítajte výsledok:

```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)` alebo metóda `toArray()` vám poskytne pole objektu. Potom v PHP použite vrátené pole na aktualizáciu objektu, entity alebo modelu.

## Upozornenia

- Výnimky vyvolané v týchto uzáveroch zatiaľ nemožno zachytiť v JS `try` / `catch`; dostanú sa k volajúcemu `eval()` ([problém č. 20](https://github.com/inline0/phasis/issues/20)). Pozrite si [Hostiteľské funkcie](https://phasis.inline0.dev/sk/docs/interop/host-functions#throwing-back-into-js).
- Uzáver zaregistrovaný pomocou `setGlobal()` udržiava zachytené objekty nažive, kým engine uchováva globálnu premennú.
