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

تحويل القيم

كل استدعاء يعبر حدود PHP↔JS — setGlobal أو call أو دالة مضيف تستدعيها JS — يمر عبر محوّل:

  • PHP → JS عندما تنتقل قيم المضيف إلى المحرك (setGlobal() ووسيطات call() والقيم المعادة من دوال المضيف).
  • JS → PHP عندما تعود قيم JS إلى الخارج. يوجد مساران بقواعد مختلفة قليلًا: نتائج eval() وcall()، ووسيطات دالة المضيف التي تمررها JS.

ينسخ التحويل القيم. تتحول القيم البدائية إلى نظيراتها، وتُنسخ المصفوفات والكائنات إلى بنى جديدة، وتُغلّف دوال PHP القابلة للاستدعاء كدوال JS. لا شيء يُشارك بالمرجع سوى الدوال القابلة للاستدعاء نفسها.

PHP → JS

قيمة PHP نتيجة JS
null null
true / false true / false
int number (عدد عشري مزدوج وفق IEEE 754؛ تفقد الأعداد الصحيحة التي تتجاوز 2^53 دقتها)
float number (تُحفظ NaN و±Infinity و±0)
string string (تُفسر بايتات PHP بترميز UTF-8 كوحدات شيفرة UTF-16)، حتى عندما تسمي دالة PHP
قائمة array (المفاتيح 0 و1 و2 و… بالترتيب) Array جديدة، وتُحوّل العناصر تكراريًا
أي array أخرى Object عادي جديد، وتُحوّل القيم تكراريًا
Closure أو كائن قابل للاستدعاء أو مصفوفة قابلة للاستدعاء مثل [$object, 'method'] function تستدعي الدالة القابلة للاستدعاء في PHP
أي كائن آخر أو Generator أو مورد undefined

تصبح المصفوفات ذات المفاتيح المختلطة (PHP من نوع [0 => 'a', 'name' => 'b']) كائنات عادية، إذ يجب أن تستخدم المصفوفات في JS فهارس صحيحة متصلة.

PHP
$engine->setGlobal('list', [1, 2, 3]);                 // → JS Array
$engine->setGlobal('map',  ['a' => 1, 'b' => 2]);      // → JS Object
$engine->setGlobal('mixed', [0 => 'x', 'k' => 'v']);   // → JS Object

بما أن المصفوفات تُنسخ، تبقى تغييرات JS في JS:

PHP
$list = [1, 2, 3];
$engine->setGlobal('list', $list);
$engine->eval('list.push(4)');
count($list);                 // 3
$engine->eval('list.length'); // 4

لا تتحول كائنات PHP غير القابلة للاستدعاء؛ بل تصل على هيئة undefined (المسألة #18). حوّلها إلى مصفوفات أولًا، مثلًا باستخدام get_object_vars()، أو أتِح أساليبها عبر الإغلاقات. راجع الكائنات المشتركة.

JS → PHP: نتائج eval() وcall()

قيمة JS نتيجة PHP
undefined، null null
boolean bool
number (عدد صحيح ضمن نطاق الأعداد الصحيحة) int (يصبح -0 هو 0)
NaN وInfinity و-Infinity NAN وINF و-INF
number (غير ذلك) float
string string (UTF-8)
bigint وsymbol null
function null
Array قائمة array، وتُحوّل العناصر تكراريًا
TypedArray قائمة array من الأعداد
أي كائن آخر (كائنات عادية وError وDate وMap وSet وPromise وArrayBuffer و…) array ترابطية لخصائصه الذاتية، مع تخطي خصائص الوصول والخصائص التي تكون قيمها دوال

يعني الصف الأخير أن الكائنات التي تعيش حالتها في خانات داخلية تتحول إلى مصفوفة فارغة: تعيد new Date(0) وnew Map([["a", 1]]) وPromise.resolve(5) جميعها []. ويعيد Error قيمتي message وstack الخاصتين به. أما الكائن الذي يشير إلى نفسه فتحوّل المراجع الداخلية فيه إلى null.

PHP
$engine->eval('[1, 2, 3]');                // [1, 2, 3]
$engine->eval('({a: 1, b: 2})');           // ['a' => 1, 'b' => 2]
$engine->eval('new Uint8Array([1, 2, 3])'); // [1, 2, 3]
$engine->eval('1234567890123456789n');     // null
$engine->eval('new Date(0)');              // []

حوّل هذه القيم داخل JS عند الحاجة إليها في PHP:

PHP
$engine->eval('(1234567890123456789n).toString()');  // "1234567890123456789"
$engine->eval('new Date(0).toISOString()');           // "1970-01-01T00:00:00.000Z"
$engine->eval('Object.fromEntries(new Map([["a", 1]]))'); // ['a' => 1]

JS → PHP: وسيطات دوال المضيف

تتبع الوسيطات التي تمررها JS إلى دالة مضيف PHP القواعد نفسها للقيم البدائية والمصفوفات والكائنات العادية، مع الفروق التالية:

قيمة JS وسيطة PHP
TypedArray وArrayBuffer وDataView كائن JsValue الخاص بالمحرك نفسه، دون تحويل
كائن array ترابطية لكل خصائصه الذاتية، بما فيها خصائص الوصول (التي تُشغّل دوال getter الخاصة بها) والخصائص التي تكون قيمها دوال
دالة سهمية array (فارغة عادةً)
function معرّف بالكلمة المفتاحية function أو أي كائن يشير إلى نفسه لا تحويل: لا تنتهي عملية النسخ مطلقًا وتنُفد ذاكرة PHP (المسألة #21)

لا توجد حتى الآن طريقة لاستقبال رد نداء JS في دالة مضيف. مرّر البيانات، لا الدوال، عبر هذه الحدود.

الأعداد والدقة

أعداد JavaScript أعداد عشرية مزدوجة وفق IEEE 754. أما أعداد PHP الصحيحة فهي أصلية للمنصة (64 بت في كل بنية حديثة). يحافظ التحويل على دقة الأعداد الصحيحة حتى ±2^53؛ أما أعداد PHP الصحيحة الأكبر فتتحول إلى number وتفقد الدقة بعد 16 خانة عشرية. استخدم BigInt داخل JS إذا احتجت إلى حساب دقيق لأعداد صحيحة أكبر، ومرّر النتيجة كسلسلة.

السلاسل

سلاسل PHP مصفوفات بايتات؛ أما سلاسل JS فهي تسلسلات من وحدات شيفرة UTF-16. يصل المحرك بينهما بتفسير بايتات PHP على أنها UTF-8، ثم يعيد ترميزها داخليًا إلى UTF-16. لا تُفقد البيانات عند إدخال UTF-8 صالح. أما البايتات غير الصالحة بترميز UTF-8 فتمر كما هي بدلًا من استبدالها بـ U+FFFD.

String.length، والوصول المفهرس (s[0])، والتقطيع كلها تحسب وحدات UTF-16 code units وفقًا لمواصفة ECMAScript، لا نقاط الترميز ولا البايتات. لذلك فإن السلاسل التي تحتوي على محارف خارج النطاق الأساسي مثل '🍕' لها .length === 2 (نصفي الاستبدال لـ U+1F355).

استقرار الذهاب والإياب

تظل البيانات العادية كما هي بعد جولة ذهاب وإياب: فقيمة PHP المؤلفة من قيم مفردة وقوائم ومصفوفات ترابطية، عند تمريرها عبر setGlobal() ثم إعادتها عبر eval() أو call()، تنتج البنية نفسها، باستثناء أن الأعداد العشرية الصحيحة تعود بصيغة int (تعيد 1.0 القيمة 1).

أما كل ما عدا ذلك في الجداول أعلاه مما يتحول إلى null أو undefined أو [] فلا يعود إلى صيغته الأصلية.