
# Sdílené objekty

Předávání objektu PHP do JavaScript odkazem — čtení jeho vlastností, volání metod a sledování změn v PHP — zatím **není podporováno**. Je to evidováno v [issue #18](https://github.com/inline0/phasis/issues/18). Tato stránka popisuje současné chování a funkční alternativní postupy.

## Současné chování

`setGlobal()` převádí svou hodnotu podle pravidel v části [Převod hodnot](https://phasis.inline0.dev/cs/docs/interop/values). Objekt PHP, který nelze volat, nemá žádný převod, takže globální proměnná má hodnotu `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')
```

Totéž platí pro `stdClass`, entity a modely, kontejnery služeb, generátory a zdroje. Výjimkou jsou dva typy volatelných objektů:

- Objekt s `__invoke()` se změní na funkci JS, která jej zavolá.
- Volatelné pole, například `[$counter, 'increment']`, se změní na funkci JS, která zavolá danou metodu.

Pro zpřístupnění objektů neexistuje žádná třída `PhpBridge` ani jiný obal; `src/Interop/` obsahuje pouze převaděč hodnot.

## Zpřístupnění metod pomocí uzávěrů

Uzávěr může zachytit objekt, takže JS volá funkci a metoda se spustí nad původní instancí 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
```

Volatelné pole dosáhne stejného výsledku bez uzávěru:

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

## Seskupení do pole

Asociativní pole se změní na objekt JS a uzávěry uvnitř na funkce. Tím získá JS objekt ve tvaru API, jehož metody pracují s instancí 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
```

JS vidí pouze to, co vložíte do pole, takže soukromý stav, například `$secret`, zůstává v PHP.

Datová pole jsou kopie pořízené při spuštění `setGlobal()`. V příkladu je `user.name` řetězec v JS; přiřazením se změní jeho kopie JS, nikoli `$user->name`. Všechny zápisy provádějte prostřednictvím uzávěru.

## Předání dat ven a výsledků zpět

U objektů tvořených daty je při předání převeďte na pole a při návratu přečtěte výsledek:

```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)` nebo metoda `toArray()` vám poskytne pole odpovídající objektu. Poté použijte vrácené pole na objekt, entitu nebo model v PHP.

## Omezení

- Výjimky vyvolané uvnitř těchto uzávěrů aktuálně nemůže zachytit JS `try` / `catch`; dostanou se k volajícímu `eval()` ([issue #20](https://github.com/inline0/phasis/issues/20)). Viz [Hostitelské funkce](https://phasis.inline0.dev/cs/docs/interop/host-functions#throwing-back-into-js).
- Uzávěr registrovaný pomocí `setGlobal()` udržuje zachycené objekty naživu, dokud engine drží globální proměnnou.
