
# Deljeni objekti

Posredovanje objekta PHP v JavaScript po sklicu — branje njegovih lastnosti, klicanje metod in spremljanje sprememb v PHP — **še ni podprto**. Spremljajte [težavo št. 18](https://github.com/inline0/phasis/issues/18). Ta stran opisuje trenutno vedenje in vzorce, ki delujejo kot alternativa.

## Trenutno vedenje

`setGlobal()` pretvori vrednost po pravilih v [Pretvorba vrednosti](https://phasis.inline0.dev/sl/docs/interop/values). Objekt PHP, ki ni klicen, nima ustrezne pretvorbe, zato je globalna vrednost `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')
```

Enako velja za `stdClass`, entitete in modele, vsebnike storitev, generatorje ter vire. Izjemi sta dva klicna objekta:

- Objekt z `__invoke()` postane funkcija JS, ki ga pokliče.
- Klicna tabela, kot je `[$counter, 'increment']`, postane funkcija JS, ki pokliče to metodo.

Za izpostavljanje objektov ni `PhpBridge` ali drugega ovijalnega razreda; `src/Interop/` vsebuje samo pretvornik vrednosti.

## Izpostavljanje metod kot zaprtih funkcij

Zaprta funkcija lahko zajame objekt, zato JS pokliče funkcijo, metoda pa se izvede na prvotnem primerku 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
```

Klicna tabela doseže enako brez zaprte funkcije:

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

## Združevanje v tabeli

Asociativna tabela postane objekt JS, zaprte funkcije v njej pa postanejo funkcije. Tako JS dobi objektu podobno vrednost API, katere metode delujejo na primerku 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
```

Za JS je vidno samo to, kar dodate v tabelo, zato zasebno stanje, kot je `$secret`, ostane v PHP.

Podatkovna polja so kopije, ustvarjene ob klicu `setGlobal()`. V tem primeru je `user.name` niz v JS; njegova sprememba vpliva na kopijo JS, ne pa na `$user->name`. Vsako pisanje izvedite prek zaprte funkcije.

## Posredovanje podatkov navzven in rezultatov nazaj

Pri objektih, oblikovanih kot podatki, jih ob vhodu pretvorite v polje, rezultat pa ob izhodu preberite:

```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)` ali metoda `toArray()` za objekt vrne polje. Nato vrnjeno polje uporabite za objekt, entiteto ali model v PHP.

## Omejitve

- Izjem, sproženih znotraj teh zaprtij, trenutno ni mogoče ujeti z JS `try` / `catch`; dosežejo klicatelja `eval()` ([težava #20](https://github.com/inline0/phasis/issues/20)). Glejte [Gostiteljske funkcije](https://phasis.inline0.dev/sl/docs/interop/host-functions#throwing-back-into-js).
- Zaprtje, registrirano z `setGlobal()`, ohrani objekte, ki jih zajame, pri življenju, dokler mehanizem hrani globalno spremenljivko.
