
# Начало работы

## Установка

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

Для Phasis требуется PHP 8.2 или новее и три расширения, необходимые `composer.json`:

- `mbstring` — обработка строк.
- `bcmath` — арифметика BigInt и работа с числами с точностью до целых.
- `curl` — транспорт, используемый по умолчанию в `fetch()`.

`mbstring` и `curl` включены в большинстве сборок. Часто отсутствует `bcmath`: в официальных образах `php` Docker (`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`), а также календари, отличные от ISO Temporal (`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`; значение последнего выражения он не выводит.

Полный список команд см. в справочнике [CLICLI](https://phasis.inline0.dev/ru/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/ru/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`. Ограничения по реальному времени нет, поэтому запускайте недоверенный код с собственным `max_execution_time` среды PHP или в отдельном процессе. См. раздел [APIsetLimit](https://phasis.inline0.dev/ru/docs/api#setlimit).

## Следующие шаги

- [APIСправочник CLI](https://phasis.inline0.dev/ru/docs/api) — полный интерфейс `Phasis\Engine`.
- [Взаимодействие](https://phasis.inline0.dev/ru/docs/interop) — правила преобразования значений, функции хоста, объекты PHP.
- [Совместимость](https://phasis.inline0.dev/ru/docs/compatibility) — реализованные возможности, охват test262.
- [Парсер](https://phasis.inline0.dev/ru/docs/parser) и [Форматтер](https://phasis.inline0.dev/ru/docs/formatter) — разбор и форматирование JavaScript без его выполнения.
- [Дополнительно](https://phasis.inline0.dev/ru/docs/advanced) — архитектура, виртуальная машина байткода, тесты производительности, проверка по эталону.
