İçeriğe geç

Paylaşılan nesneler

Bir PHP nesnesini referansla JavaScript içine aktarma — özelliklerini okuma, yöntemlerini çağırma ve değişiklikleri PHP içinde görme — henüz desteklenmiyor. Bu konu sorun #18 kapsamında izleniyor. Bu sayfa bugün ne olduğunu ve bunun yerine işe yarayan kalıpları açıklar.

Bugün ne oluyor

setGlobal(), değerini Değer dönüştürme içindeki kurallara göre dönüştürür. Çağrılabilir olmayan bir PHP nesnesinin dönüşüm kuralı yoktur; bu nedenle global değer undefined olur:

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')

Aynısı stdClass, varlıklar ve modeller, servis kapsayıcıları, üreteçler ve kaynaklar için de geçerlidir. İki istisna çağrılabilir nesnelerdir:

  • __invoke() içeren bir nesne, onu çağıran bir JS işlevine dönüşür.
  • [$counter, 'increment'] gibi çağrılabilir bir dizi, o yöntemi çağıran bir JS işlevine dönüşür.

Nesneleri sunmak için PhpBridge veya başka bir sarmalayıcı sınıf yoktur; src/Interop/ yalnızca değer dönüştürücüsünü içerir.

Yöntemleri kapanım olarak sunma

Bir kapanım nesneyi yakalayabilir; böylece JS bir işlevi çağırır ve yöntem özgün PHP örneği üzerinde çalışır:

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

Çağrılabilir bir dizi de kapanım olmadan aynı işi yapar:

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

Bunları bir dizide gruplama

İlişkisel bir dizi JS nesnesine, içindeki kapanımlar da işlevlere dönüşür. Böylece JS, yöntemleri PHP örneği üzerinde çalışan nesne biçimli bir API elde eder:

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

Dizide ekledikleriniz dışında hiçbir şey JS tarafından görülemez; bu nedenle $secret gibi özel durumlar PHP içinde kalır.

Veri alanları, setGlobal() çalıştığında alınan kopyalardır. Örnekte user.name, JS içinde bir dizedir; ona atama yapmak JS kopyasını değiştirir, $user->name değerini değil. Her yazma işlemini bir kapanım üzerinden yönlendirin.

Veriyi dışarı, sonuçları geri aktarma

Veri biçimli nesneler için, içeri girerken diziye dönüştürün ve sonucu çıkarken okuyun:

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) veya bir toArray() yöntemi, nesne için diziyi sağlar. Ardından döndürülen diziyi PHP içindeki nesnenize, varlığınıza veya modelinize uygulayın.

Dikkat edilmesi gerekenler

  • Bu kapatmaların içinde fırlatılan istisnalar şu anda JS try / catch tarafından yakalanamaz; eval() çağrısının çağıranına ulaşır (sorun #20). Bkz. Ana makine işlevleri.
  • setGlobal() ile kaydedilen bir kapatma, motor global değişkeni tuttuğu sürece yakaladığı nesneleri bellekte tutar.