
# Ana makine işlevleri

`setGlobal()` aracılığıyla motora verilen herhangi bir PHP kapanımı veya çağrılabilir işlevi, çağrılabilir bir JS işlevine dönüşür. Elle kayıt adımı, şema veya sıralama yoktur — çağrılabilir işlevi vermeniz yeterlidir.

## Temel bilgiler

```php
$engine->setGlobal('greet', function (string $name): string {
    return "Hello, $name!";
});

echo $engine->eval('greet("world")');
// Hello, world!
```

`greet("world")`, JS içinde çalıştığında:

1. `"world"` dizesi JS biçiminden PHP biçimine dönüştürülür.
2. PHP kapanımı dönüştürülmüş bağımsız değişkenle çağrılır.
3. Dönüş değeri PHP biçiminden tekrar JS biçimine dönüştürülür.

Dönüştürme, [değer dönüştürme kurallarını](https://phasis.inline0.dev/tr/docs/interop/values) izler. Bağımsız değişkenler kopyadır: JS dizisi veya nesnesi, yeni bir PHP dizisi olarak gelir ve PHP içinde bunu değiştirmek JS değerini değiştirmez.

## Arite ve parametreler

Motor, JS bağımsız değişkenleri oldukları gibi, sırayla çağrılabilir işleve aktarır; imzayı incelemez. Fazladan bağımsız değişkenler sıradan kapanımlar tarafından yok sayılır, değişken sayıda bağımsız değişken alan parametrelerde toplanır. Eksikler tamamlanmaz: JS çağrılabilir işlevin gerektirdiğinden az bağımsız değişken aktarırsa PHP, `ArgumentCountError` fırlatır. İsteğe bağlı parametrelere varsayılan değerler verin.

```php
$engine->setGlobal('format', function (string $template, ...$args): string {
    return vsprintf($template, $args);
});

echo $engine->eval('format("%s is %d years old", "Ada", 36)');
// Ada is 36 years old
```

## Tür dönüşümleri

Bağımsız değişkenler, JS→PHP kuralları kullanılarak PHP biçimine dönüştürülür. Motor, çağrılabilir işlevinizi `strict_types=1` bildiren bir koddan çağırır; bu nedenle PHP skaler parametre türlerini katı biçimde denetler ve tür dönüşümü yapmaz: `string` parametresine aktarılan bir JS sayı, `TypeError` olur. Ana makine işlevindeki diğer PHP istisnaları gibi, bu da JS yerine `eval()` çağrısının sahibine ulaşır (aşağıya bakın).

```php
$engine->setGlobal('len', fn(string $s): int => strlen($s));

$engine->eval('len(42)');  // PHP TypeError: Argument #1 ($s) must be of type string, int given
```

Kapanım içinde elle dönüştürme yapın (`(string) $value`) veya birleşim türleri kullanın (`int|string`).

## JS tarafına hata fırlatma

Bir ana makine işlevi içinde fırlatılan sıradan PHP istisnaları bugün JS hatalarına dönüştürülmez. Tüm JS `try` / `catch` ve `.catch()` işleyicilerinden geçerek, özgün sınıflarıyla `call()` veya `eval()` çağıran PHP koduna ulaşırlar ([sorun #20](https://github.com/inline0/phasis/issues/20)). Buna `\Exception`, `\RuntimeException`, `\DivisionByZeroError` ve hatalı bağımsız değişkenler için `\TypeError` ile `\ArgumentCountError` PHP hataları da dahildir.

```php
$engine->setGlobal('divide', function (int $a, int $b): int {
    if ($b === 0) {
        throw new \DivisionByZeroError('cannot divide by zero');
    }
    return intdiv($a, $b);
});

try {
    $engine->eval('
        try { divide(10, 0); } catch (e) { /* never runs */ }
    ');
} catch (\DivisionByZeroError $e) {
    echo $e->getMessage();  // cannot divide by zero
}
```

JS kodunun yakalayabileceği bir hata oluşturmak için motorun kendi hata sınıflarından birini fırlatın: `Phasis\Exceptions\TypeError`, `RangeError` veya `ReferenceError`, JS içinde eşleşen JS hata türü olarak gelir; diğer tüm `Phasis\Exceptions\RuntimeError` değerleri ileti metninizle düz bir `Error` olarak gelir.

```php
use Phasis\Exceptions\RangeError;

$engine->setGlobal('divide', function (int $a, int $b): int {
    if ($b === 0) {
        throw new RangeError('cannot divide by zero');
    }
    return intdiv($a, $b);
});

$engine->eval('
    try {
        divide(10, 0);
    } catch (e) {
        console.log(e.name, e.message);  // RangeError cannot divide by zero
    }
');
```

## Zengin değerler döndürme

Dönüş değeri PHP→JS kurallarıyla dönüştürülür. Bir JS nesne sabiti döndürmek için PHP ilişkisel dizisi döndürün:

```php
$engine->setGlobal('lookup', function (string $key): array {
    return ['key' => $key, 'value' => 42, 'meta' => ['ts' => time()]];
});

$engine->eval('
    const r = lookup("answer");
    console.log(r.value, r.meta.ts);
');
```

Bir JS dizisi döndürmek için sıralı bir PHP listesi döndürün:

```php
$engine->setGlobal('range', fn(int $a, int $b): array => range($a, $b));

$engine->eval('range(1, 5).reduce((a, b) => a + b)');  // 15
```

ReactPHP, Amp veya Guzzle kaynaklı PHP promise da dahil olmak üzere bir PHP nesnesi döndürmek, JS `undefined` sonucunu verir (bkz. [Değer dönüştürme](https://phasis.inline0.dev/tr/docs/interop/values)).

## Eşzamansız ana makine işlevleri

Ana makine işlevleri eşzamanlıdır. Bir PHP promise veya başka bir thenable nesnesi beklenmez: `undefined` biçimine dönüştürülür ve bu nedenle `await hostFn()`, `undefined` sonucuna çözülür. İşi eşzamanlı yapıp sonucu döndürün; JS kodu çağrıyı yine de `await` yapabilir; bu, değeri çözülmüş bir promise içine sarar:

```php
$engine->setGlobal('loadBody', function (string $url): string {
    return file_get_contents($url);
});

$engine->eval('
    async function main() {
        const body = await loadBody("https://example.com");
        console.log(body.length);
    }
    main();
');
```

Özellikle HTTP için yerleşik `fetch()` zaten gerçek bir JS promise döndürür ve [özel fetch aktarımı](https://phasis.inline0.dev/tr/docs/interop/fetch-transport) ile kendi PHP istemciniz üzerinden yönlendirilebilir.

## Performans notları

Her ana makine işlevi çağrısı, bağımsız değişkenler için PHP↔JS sınırını bir kez, dönüş değeri için de bir kez geçer. Her iki dönüştürme de kopyalama yapar ve bu yüzden O(size of value) maliyetlidir — ilkel değerler sabit zamanda, diziler ve nesneler ise boyutlarıyla orantılı sürede işlenir.

Sık çalışan iç döngülerde, çok sayıda küçük çağrı yerine daha fazla iş yapan tek bir çağrıyı tercih edin ve yalnızca birkaç alan değişirken büyük dizileri gidip gelmek üzere aktarmaktan kaçının.
