
# はじめに

## インストール

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

`composer.json`が必要とするPhasisの要件は、PHP 8.2以降と、次の3つの拡張機能です:

- `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`もオプションです。`crypto.subtle` AES-CBC、AES-CTR、AES-GCMの暗号化には必要ですが、digestと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/ja/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`とその関連メソッドは、標準出力に書き込む代わりにエンジン上のバッファへ追記します。`getConsoleOutput()`（1つの文字列として、各行を`\n`で連結）または`getConsoleLines()`（行のリスト）で読み取り、`clearConsole()`で空にします。

コールドスタートの負荷は軽く、仕様の読み込みに必要なコア（`Object`、`Function`、`Array`、`Promise`、`Symbol`、`BigInt`、`RegExp`、エラー型）のみが最初にインストールされます。`Map`、`TypedArray`、`Temporal`、`Intl`、`URL`、`fetch`、Web Platform Pack / Fetch Pack全体、およびその他の機能は、初回読み込み時に透過的に展開されます。構築時にすべての組み込み機能をインストールするには`new Engine(eager: true)`を渡します。読み込む前に記述子を調べる適合性ツールに便利ですが、通常の埋め込みではほとんど必要ありません。

## PHPからJS関数を呼び出す

```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と共有できません。通常のPHPオブジェクトを使った`setGlobal()`では`undefined`がバインドされます（[issue #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/ja/docs/interop/shared-objects)をご覧ください。

## リソース上限

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

`maxLoopIterations`（デフォルトは100,000）は実行時に変更できる唯一の上限で、現在はツリーウォーキングインタープリターが実行するコードにのみ適用されます。バイトコードVMが実行するループ（関数本体とトップレベルコードの大部分）はカウントされないため、この上限だけでは`while (true) {}`を止められません（[issue #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/ja/docs/api#setlimit)をご覧ください。

## 次のステップ

- [APIリファレンス](https://phasis.inline0.dev/ja/docs/api) — `Phasis\Engine`の全機能。
- [相互運用](https://phasis.inline0.dev/ja/docs/interop) — 値の変換規則、ホスト関数、PHPオブジェクト。
- [互換性](https://phasis.inline0.dev/ja/docs/compatibility) — 実装済み機能とtest262のカバレッジ。
- [パーサー](https://phasis.inline0.dev/ja/docs/parser)と[フォーマッター](https://phasis.inline0.dev/ja/docs/formatter) — JavaScriptを実行せずに解析・整形します。
- [高度な内容](https://phasis.inline0.dev/ja/docs/advanced) — アーキテクチャ、バイトコードVM、ベンチマーク、オラクルテスト。
