Споделени обекти
Подаването на обект PHP към JavaScript по референция — четене на свойствата му, извикване на методите му и виждане на промените в PHP — все още не се поддържа. Това се проследява в issue #18. Тази страница описва какво се случва днес и кои модели работят вместо това.
Какво се случва днес
setGlobal() преобразува стойността си според правилата в Преобразуване на стойности. Обект PHP, който не е извикваем, няма преобразуване, затова глобалната стойност е 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')
Същото важи за stdClass, обекти-същности и модели, контейнери за услуги, генератори и ресурси. Два типа изключения са извикваемите обекти:
- Обект с
__invoke()става функция JS, която го извиква. - Извикваем масив като
[$counter, 'increment']става функция JS, която извиква този метод.
Няма PhpBridge или друг клас обвивка за предоставяне на обекти; src/Interop/ съдържа само конвертора на стойности.
Предоставяне на методи като затваряния
Затварянето може да запази обекта, така че JS извиква функция и методът се изпълнява върху оригиналната инстанция 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
Извикваемият масив прави същото без затваряне:
$engine->setGlobal('increment', [$counter, 'increment']);
Групиране в масив
Асоциативният масив става обект JS, а затварянията в него стават функции. Така JS получава обект API, чиито методи действат върху инстанцията 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 е видимо само това, което поставите в масива, затова частно състояние като $secret остава в PHP.
Полетата с данни са копия, направени при изпълнение на setGlobal(). В примера user.name е низ в JS; присвояването му променя копието JS, а не $user->name. Насочвайте всяка промяна през затваряне.
Предаване на данни и връщане на резултати
За обекти с форма на данни преобразувайте в масив при подаване и прочетете резултата при връщане:
$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) или метод toArray() ви дава масива за обект. След това приложете върнатия масив към вашия обект, същност или модел в PHP.
Особености
- Изключенията, хвърлени в тези затваряния, понастоящем не могат да бъдат уловени от JS
try/catch; те достигат до извикващияeval()(issue #20). Вижте Хост функции. - Затваряне, регистрирано с
setGlobal(), запазва обектите, които обхваща, докато енджинът държи глобалната стойност.