
# Objets partagés

Le passage d’un objet PHP par référence à JavaScript — pour lire ses propriétés, appeler ses méthodes et voir les changements dans PHP — n’est **pas encore pris en charge**. Le suivi est disponible dans [le problème #18](https://github.com/inline0/phasis/issues/18). Cette page décrit le comportement actuel et les modèles qui fonctionnent à la place.

## Comportement actuel

`setGlobal()` convertit sa valeur selon les règles de [Conversion des valeurs](https://phasis.inline0.dev/fr/docs/interop/values). Un objet PHP non appelable n’a pas de conversion ; la variable globale vaut donc `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')
```

Cela vaut aussi pour `stdClass`, les entités et modèles, les conteneurs de services, les générateurs et les ressources. Deux exceptions existent pour les objets appelables :

- Un objet doté de `__invoke()` devient une fonction JS qui l’appelle.
- Un tableau appelable comme `[$counter, 'increment']` devient une fonction JS qui appelle cette méthode.

Il n’existe pas de `PhpBridge` ni d’autre classe wrapper pour exposer des objets ; `src/Interop/` ne contient que le convertisseur de valeurs.

## Exposer des méthodes sous forme de fermetures

Une fermeture peut capturer l’objet ; JS appelle alors une fonction et la méthode s’exécute sur l’instance PHP d’origine :

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

Un tableau appelable fait la même chose sans fermeture :

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

## Les regrouper dans un tableau

Un tableau associatif devient un objet JS et les fermetures qu’il contient deviennent des fonctions. JS obtient ainsi un API en forme d’objet, dont les méthodes agissent sur l’instance 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
```

Seuls les éléments du tableau sont visibles par JS ; l’état privé comme `$secret` reste donc dans PHP.

Les champs de données sont des copies créées à l’exécution de `setGlobal()`. Dans l’exemple, `user.name` est une chaîne dans JS ; la modifier change la copie JS, pas `$user->name`. Faites passer toutes les écritures par une fermeture.

## Transmettre des données et récupérer les résultats

Pour les objets structurés en données, convertissez-les en tableau à l’entrée, puis lisez le résultat à la sortie :

```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)` ou une méthode `toArray()` fournit le tableau d’un objet. Appliquez ensuite le tableau obtenu à votre objet, entité ou modèle dans PHP.

## Réserves

- Les exceptions levées dans ces closures ne peuvent actuellement pas être interceptées par JS `try` / `catch` ; elles remontent jusqu’à l’appelant de `eval()` ([issue #20](https://github.com/inline0/phasis/issues/20)). Voir [Fonctions hôtes](https://phasis.inline0.dev/fr/docs/interop/host-functions#throwing-back-into-js).
- Une closure enregistrée avec `setGlobal()` conserve en vie les objets qu’elle capture tant que le moteur détient le global.
