Objek bersama
Meneruskan objek PHP ke JavaScript melalui referensi — membaca propertinya, memanggil metodenya, dan melihat perubahan di PHP — belum didukung. Masalah ini dilacak di masalah #18. Halaman ini menjelaskan apa yang terjadi saat ini dan pola alternatif yang dapat digunakan.
Yang terjadi saat ini
setGlobal() mengonversi nilainya menggunakan aturan di Konversi nilai. Objek PHP yang bukan callable tidak memiliki konversi, sehingga global tersebut bernilai undefined:
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')
Hal yang sama berlaku untuk stdClass, entitas dan model, kontainer layanan, generator, serta resource. Ada dua pengecualian untuk objek callable:
- Objek dengan
__invoke()menjadi fungsi JS yang memanggilnya. - Array callable seperti
[$counter, 'increment']menjadi fungsi JS yang memanggil metode tersebut.
Tidak ada PhpBridge atau kelas wrapper lain untuk mengekspos objek; src/Interop/ hanya berisi konverter nilai.
Ekspos metode sebagai closure
Closure dapat menangkap objek, sehingga JS memanggil fungsi dan metode dijalankan pada instance PHP asli:
$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
Array callable melakukan hal yang sama tanpa closure:
$engine->setGlobal('increment', [$counter, 'increment']);
Kelompokkan dalam array
Array asosiatif menjadi objek JS, dan closure di dalamnya menjadi fungsi. Dengan begitu, JS memperoleh API berbentuk objek yang metodenya bekerja pada instance 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
Hanya hal yang Anda masukkan ke dalam array yang terlihat oleh JS, sehingga status privat seperti $secret tetap berada di PHP.
Field data adalah salinan yang diambil saat setGlobal() berjalan. Dalam contoh ini, user.name berupa string di JS; mengubahnya akan mengubah salinan JS, bukan $user->name. Arahkan semua penulisan melalui closure.
Kirim data keluar, kembalikan hasil
Untuk objek berbentuk data, ubah menjadi array saat masuk, lalu baca hasilnya saat keluar:
$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) atau metode toArray() memberi Anda array untuk sebuah objek. Lalu terapkan array yang dikembalikan ke objek, entitas, atau model Anda di PHP.
Catatan penting
- Exception yang dilempar di dalam closure ini saat ini tidak dapat ditangkap oleh JS
try/catch; exception tersebut mencapai pemanggileval()(masalah #20). Lihat Fungsi host. - Closure yang didaftarkan dengan
setGlobal()membuat objek yang ditangkapnya tetap hidup selama engine menyimpan global tersebut.