انتقل إلى المحتوى

الكائنات المشتركة

تمرير كائن PHP إلى JavaScript بالمرجع — وقراءة خصائصه واستدعاء أساليبه ورؤية التغييرات في PHP — غير مدعوم بعد. يُتابع ذلك في المسألة #18. تصف هذه الصفحة ما يحدث حاليًا والأنماط البديلة المناسبة.

ما يحدث حاليًا

يحوّل setGlobal() قيمته وفق القواعد الواردة في تحويل القيم. لا يملك كائن PHP غير القابل للاستدعاء قاعدة تحويل، لذا تكون القيمة العامة 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')

ينطبق الأمر نفسه على stdClass والكيانات والنماذج وحاويات الخدمات والمولدات والموارد. والاستثناءان هما الكائنات القابلة للاستدعاء:

  • يتحول كائن يتضمن __invoke() إلى دالة JS تستدعيه.
  • تتحول المصفوفة القابلة للاستدعاء مثل [$counter, 'increment'] إلى دالة JS تستدعي ذلك الأسلوب.

لا توجد فئة غلاف PhpBridge أو فئة أخرى لإتاحة الكائنات؛ تحتوي src/Interop/ على محوّل القيم فقط.

إتاحة الأساليب عبر الإغلاقات

يمكن للإغلاق التقاط الكائن، بحيث يستدعي JS دالةً وينفّذ الأسلوب على مثيل 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

تؤدي المصفوفة القابلة للاستدعاء الغرض نفسه دون إغلاق:

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

تجميعها في مصفوفة

تتحول المصفوفة الترابطية إلى كائن JS، وتتحول الإغلاقات الموجودة فيها إلى دوال. يمنح ذلك JS واجهة API شبيهة بالكائن، تعمل أساليبها على مثيل 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

لا يظهر لـJS سوى ما تضعه في المصفوفة، لذا تبقى الحالة الخاصة مثل $secret داخل PHP.

حقول البيانات نُسخ تؤخذ عند تشغيل setGlobal(). في المثال، تكون user.name سلسلة في JS؛ وإسناد قيمة إليها يغيّر نسخة JS لا $user->name. مرّر كل عمليات الكتابة عبر إغلاق.

تمرير البيانات وإعادة النتائج

بالنسبة إلى الكائنات التي تمثل بيانات، حوّلها إلى مصفوفة عند تمريرها، ثم اقرأ النتيجة عند إعادتها:

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) أو إحدى طرق toArray() مصفوفةً لكائن. ثم طبّق المصفوفة المعادة على كائنك أو كيانك أو نموذجك في PHP.

ملاحظات

  • لا يمكن حاليًا التقاط الاستثناءات التي تُرمى داخل هذه الإغلاقات باستخدام try / catch في JS؛ بل تصل إلى مستدعي eval() (المسألة #20). راجع دوال المضيف.
  • يُبقي الإغلاق المسجل عبر setGlobal() الكائنات التي يلتقطها حيةً ما دام المحرك يحتفظ بالقيمة العامة.