İçeriğe geç

Değer dönüştürme

PHP↔JS sınırını geçen her çağrı — setGlobal, call, JS üzerinden çağrılan bir ana makine işlevi — bir dönüştürücüden geçer:

  • Ana makine değerleri motora aktarılırken PHP → JS (setGlobal(), call() bağımsız değişkenleri, ana makine işlevlerinin dönüş değerleri).
  • JS değerleri geri dönerken JS → PHP. Biraz farklı kuralları olan iki yol vardır: eval() ve call() sonuçları ile JS'ın bir ana makine işlevine ilettiği bağımsız değişkenler.

Dönüştürme kopyalama yapar. İlkel değerler ilkel değerlere eşlenir, diziler ve nesneler yeni yapılara kopyalanır, PHP çağrılabilirleri ise JS işlevleri olarak sarılır. Çağrılabilirlerin kendileri dışında hiçbir şey başvuru yoluyla paylaşılmaz.

PHP → JS

PHP değeri JS sonucu
null null
true / false true / false
int number (IEEE 754 çift duyarlık; 2^53 üzerindeki tam sayılar hassasiyet kaybeder)
float number (NaN, ±Infinity, ±0 korunur)
string string (UTF-8 PHP baytları UTF-16 kod birimleri olarak yorumlanır); PHP işlevini adlandırsa bile
liste array (anahtarlar sırayla 0, 1, 2, …) yeni Array, öğeler yinelemeli olarak dönüştürülür
diğer tüm array değerleri yeni düz Object, değerler yinelemeli olarak dönüştürülür
Closure, çağrılabilir nesne, [$object, 'method'] gibi çağrılabilir dizi PHP çağrılabilirini çağıran function
diğer tüm nesneler, Generator, kaynak undefined

Karma anahtarlı diziler (PHP'ın [0 => 'a', 'name' => 'b'] özelliği), düz nesnelere dönüşür; çünkü JS dizilerinde bitişik bir tamsayı indeks alanı bulunmalıdır.

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

Diziler kopyalandığından, JS değişiklikleri JS içinde kalır:

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

Çağrılabilir olmayan PHP nesneleri dönüştürülmez; undefined olarak gelirler (sorun #18). Önce dizilere dönüştürün; örneğin get_object_vars() kullanın veya yöntemlerini kapatmalar olarak sunun. Bkz. Paylaşılan nesneler.

JS → PHP: eval() ve call() sonuçları

JS değeri PHP sonucu
undefined, null null
boolean bool
number (tam sayı, int aralığında) int (-0, 0 olur)
NaN, Infinity, -Infinity NAN, INF, -INF
number (diğer) float
string string (UTF-8)
bigint, symbol null
function null
Array liste array, öğeler yinelemeli olarak dönüştürülür
TypedArray sayılardan oluşan liste array
diğer tüm nesneler (düz nesneler, Error, Date, Map, Set, Promise, ArrayBuffer, …) kendi veri özelliklerini içeren ilişkisel array; erişimciler ve işlev değerli özellikler atlanır

Son satır, durumu dahili yuvalarda bulunan nesnelerin boş bir diziye dönüştüğü anlamına gelir: new Date(0), new Map([["a", 1]]) ve Promise.resolve(5) tümü [] döndürür. Bir Error kendi message ve stack değerlerini döndürür. Kendine başvuran bir nesne, iç başvurularını null değerine dönüştürür.

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)');              // []

Bu değerlerden birine PHP içinde ihtiyacınız varsa, JS içinde dönüştürün:

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: ana makine işlevi bağımsız değişkenleri

JS'ın bir PHP ana makine işlevine ilettiği bağımsız değişkenler; ilkel değerler, diziler ve düz nesneler için aynı kuralları izler, şu farklarla:

JS değeri PHP argümanı
TypedArray, ArrayBuffer, DataView motorun kendi JsValue nesnesi, dönüştürülmeden
nesne erişimciler (getter'ları çalıştırılır) ve işlev değerli özellikler dahil tüm kendi özelliklerini içeren ilişkisel array
ok işlevi bir array (genellikle boş)
function anahtar sözcüğüyle tanımlanan function veya kendisine başvuran herhangi bir nesne dönüştürme yapılamaz: kopyalama hiç bitmez ve PHP belleği tükenir (sorun #21)

Bir ana makine işlevinde henüz JS geri çağrısı alma yolu yoktur. Bu sınırdan işlev değil, veri geçirin.

Sayılar ve hassasiyet

JavaScript sayıları IEEE 754 çift duyarlıklıdır. PHP int değerleri platformun yerel türündedir (her modern derlemede 64 bit). Dönüştürme, ±2^53'e kadar olan tamsayıları tam olarak korur; daha büyük PHP int değerleri number olur ve 16 ondalık basamaktan sonra hassasiyetini kaybeder. Bundan daha büyük tamsayılarda tam aritmetik gerekiyorsa JS içinde BigInt kullanın ve sonucu dizge olarak dışarı aktarın.

Dizgeler

PHP dizgeleri bayt dizileridir; JS dizgeleri UTF-16 kod birimi dizileridir. Motor, PHP baytlarını UTF-8 olarak yorumlayıp dahili olarak UTF-16'ya yeniden kodlayarak aralarında köprü kurar. Geçerli UTF-8 girdilerinde bu işlem kayıpsızdır. Geçerli UTF-8 olmayan baytlar, U+FFFD ile değiştirilmek yerine değiştirilmeden aktarılır.

String.length, indeksli erişim (s[0]) ve dilimleme işlemlerinin tümü ECMAScript belirtimine göre UTF-16 kod birimlerini sayar; kod noktalarını veya baytları değil. Bu nedenle '🍕' gibi astral karakterler içeren dizgelerde .length === 2 bulunur (U+1F355'in iki vekil yarısı).

Gidiş dönüş kararlılığı

Düz veriler gidiş dönüşte korunur: setGlobal() üzerinden girip eval() veya call() üzerinden dönen, skaler değerlerden, listelerden ve ilişkisel dizilerden oluşan bir PHP değeri aynı yapıyı üretir; tam sayı değerindeki kayan noktalı sayılar int olarak geri döner (1.0, 1 döndürür).

Yukarıdaki tablolarda null, undefined veya [] ile eşlenen diğer hiçbir değer gidiş dönüşte korunmaz.