
# البدء

## التثبيت

```bash
composer require phasis/phasis
```

يحتاج Phasis إلى PHP 8.2 أو أحدث مع ثلاث إضافات، وكلها مطلوبة من `composer.json`:

- `mbstring` — لمعالجة السلاسل.
- `bcmath` — لعمليات BigInt الحسابية ومعالجة الأرقام بدقة الأعداد الصحيحة.
- `curl` — لنقل `fetch()` الافتراضي.

تُفعّل `mbstring` و`curl` في معظم إصدارات البناء. أما `bcmath` فغالبًا لا: إذ تستبعده صور Docker الرسمية لـ `php` ‏(`php:8.x-cli`، `php:8.x-fpm`)، وتوفره بعض التوزيعات في حزمة منفصلة (`php-bcmath`). من دونه، يرفض Composer تثبيت Phasis. أضفه إلى Dockerfile مبني على الصورة الرسمية:

```dockerfile
RUN docker-php-ext-install bcmath
```

شغّل `php -m` لمعرفة الإضافات المتاحة في إصدار البناء لديك.

إن `ext-intl` اختياري. يعمل Phasis من دونه، لكن عائلة `Intl.*` بأكملها (`Collator`، `NumberFormat`، `DateTimeFormat`، `PluralRules`، `Locale`، `DisplayNames`، `ListFormat`، `RelativeTimeFormat`، `Segmenter`، `DurationFormat`) وتقويمات Temporal غير التابعة لـ ISO ‏(`hebrew`، `islamic`، `japanese`، `persian`، إلخ) تحتاج إليه. ثبّته كلما أتاحت التطبيق المضيفة JS تستخدم تنسيقًا يراعي الإعدادات المحلية. كما أن `ext-openssl` اختياري: تحتاجه عمليات تشفير AES-CBC وAES-CTR وAES-GCM ضمن `crypto.subtle`، بينما تعمل الملخصات وHMAC من دونه.

لا `exec`، ولا FFI، ولا Node.js، ولا إضافة أصلية مخصصة.

## تشغيل برنامج نصي من CLI

```bash
./vendor/bin/phasis -e 'console.log(1 + 2 * 3)'
# 7

./vendor/bin/phasis -e 'console.log([1, 2, 3].map(x => x * x).join(","))'
# 1,4,9

./vendor/bin/phasis path/to/script.js
```

يطبع CLI ما يكتبه البرنامج النصي باستخدام `console.log`؛ ولا يطبع قيمة التعبير الأخير.

راجع [CLI المرجع](https://phasis.inline0.dev/ar/docs/cli) للاطلاع على مجموعة الأوامر كاملة.

## التضمين في PHP

```php
<?php

require __DIR__ . '/vendor/autoload.php';

use Phasis\Engine;

$engine = new Engine();

// Evaluate an expression
$result = $engine->eval('1 + 2 * 3');
echo $result;
// 7

// Execute a script file
$engine->execFile('path/to/script.js');

// Bridge a PHP value into the JS global scope
$engine->setGlobal('config', ['debug' => true, 'version' => '1.0']);
$engine->eval('console.log(config.version)');

// console output is buffered on the engine, not printed
echo $engine->getConsoleOutput();
// 1.0
```

تحافظ نسخة `Engine` على حالتها العامة بين الاستدعاءات، لذا ترى الاستدعاءات اللاحقة لـ `eval()` كل ما سبق تعريفه.

تضيف `console.log` والأدوات الشقيقة لها البيانات إلى مخزن مؤقت في المحرك بدلاً من كتابتها إلى stdout. اقرأها باستخدام `getConsoleOutput()` (سلسلة واحدة، والأسطر مفصولة بـ `\n`) أو `getConsoleLines()` (قائمة من الأسطر)، وأفرغ المخزن باستخدام `clearConsole()`.

بدء التشغيل البارد قليل التكلفة: لا يُثبّت مسبقًا سوى النواة اللازمة لتحميل المواصفات (`Object` و`Function` و`Array` و`Promise` و`Symbol` و`BigInt` و`RegExp` وأنواع الأخطاء). أما `Map` و`TypedArray` و`Temporal` و`Intl` و`URL` و`fetch` وحزمتا Web Platform وFetch الكاملتان، وسائر المكونات، فتُحمّل تلقائيًا عند أول قراءة. مرّر `new Engine(eager: true)` لتثبيت كل المكونات المضمنة عند الإنشاء؛ يفيد ذلك أدوات التوافق التي تفحص الواصفات قبل قراءتها، ونادرًا ما يلزم للتضمين المعتاد.

## استدعاء دوال JS من PHP

```php
$engine->eval('function add(a, b) { return a + b; }');
echo $engine->call('add', 2, 3);
// 5
```

يبحث `call()` عن الدالة بالاسم في الكائن العام ويستدعيها باستخدام وسيطات PHP المقدمة (مع تحويلها تلقائيًا إلى قيم JS).

## إتاحة دالة قابلة للاستدعاء من PHP كدالة JS

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

$engine->eval('const users = fetchData("https://api.example.com/users")');
```

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

## كائنات PHP

لا يمكن مشاركة كائنات PHP مع JavaScript حتى الآن. يؤدي `setGlobal()` مع كائن PHP عادي إلى ربط `undefined` (تتبعه [المشكلة #18](https://github.com/inline0/phasis/issues/18)). مرّر البيانات كمصفوفات تُنسخ إلى JS، وأتح السلوك عبر الإغلاقات:

```php
class Counter {
    public int $value = 0;
    public function increment(): void { $this->value++; }
}

$counter = new Counter();
$engine->setGlobal('increment', fn () => $counter->increment());

$engine->eval('increment(); increment();');

echo $counter->value;
// 2
```

راجع [الكائنات المشتركة](https://phasis.inline0.dev/ar/docs/interop/shared-objects) للاطلاع على التفاصيل والحلول البديلة الأخرى.

## حدود الموارد

```php
$engine = new Engine();
$engine->setLimit('maxLoopIterations', 100_000);
```

`maxLoopIterations` (القيمة الافتراضية 100,000) هو الحد الوحيد القابل للضبط وقت التشغيل، وينطبق حاليًا على الشيفرة التي ينفذها المفسر المتجول في الشجرة فقط. لا تُحتسب الحلقات التي تنفذها آلة البايت كود الافتراضية (أجسام الدوال ومعظم الشيفرة على المستوى الأعلى)، لذا لا يكفي الحد وحده لإيقاف `while (true) {}` ([المشكلة #19](https://github.com/inline0/phasis/issues/19)). عند بلوغ الحد، يمكن التقاط الخطأ في JS؛ وإذا لم يُلتقط، يصل إلى PHP بوصفه `Phasis\Exceptions\InternalError`، وهو فئة فرعية من `Phasis\Exceptions\RuntimeError`.

لدى المحرك أيضًا حد ثابت لمكدس الاستدعاءات يبلغ 4,096 إطارًا (وحاجزًا من 8,192 إطارًا للإغلاقات المحوّلة)، وتجاوز هذا الحد يطلق الخطأ "Maximum call stack size exceeded"، ويكون كذلك من النوع `InternalError`. لا يوجد حد للوقت الفعلي، لذا شغّل الشيفرة غير الموثوقة ضمن PHP الخاص بها `max_execution_time` أو في عملية منفصلة. راجع [API: setLimit](https://phasis.inline0.dev/ar/docs/api#setlimit).

## الخطوات التالية

- [مرجع API](https://phasis.inline0.dev/ar/docs/api) — واجهة `Phasis\Engine` كاملة.
- [التشغيل البيني](https://phasis.inline0.dev/ar/docs/interop) — قواعد تحويل القيم، ودوال المضيف، وكائنات PHP.
- [التوافق](https://phasis.inline0.dev/ar/docs/compatibility) — الميزات المنفذة وتغطية test262.
- [المحلّل](https://phasis.inline0.dev/ar/docs/parser) و[المنسّق](https://phasis.inline0.dev/ar/docs/formatter) — تحليل JavaScript وتنسيقه دون تنفيذه.
- [متقدم](https://phasis.inline0.dev/ar/docs/advanced) — البنية، وآلة البايت كود الافتراضية، والمعايير، واختبارات المقارنة المرجعية.
