
# 시작하기

## 설치

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

Phasis를 사용하려면 `composer.json`에서 요구하는 확장 3개가 포함된 PHP 8.2 이상이 필요합니다.

- `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 암호화에는 필요하지만, 다이제스트와 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/ko/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 Packs 및 나머지는 처음 읽을 때 자동으로 로드됩니다. 생성 시 모든 내장 기능을 설치하려면 `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/ko/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\RuntimeError`의 하위 클래스인 `Phasis\Exceptions\InternalError`로 전달됩니다.

엔진에는 고정된 4,096 프레임 호출 스택 한계(및 트랜스파일된 클로저를 위한 8,192 프레임 가드)도 있습니다. 이를 넘으면 다시 `InternalError`로 "Maximum call stack size exceeded" 오류가 발생합니다. 실제 시간 제한은 없으므로 신뢰할 수 없는 코드는 PHP 자체의 `max_execution_time`에서 실행하거나 별도 프로세스에서 실행하세요. [API: setLimit](https://phasis.inline0.dev/ko/docs/api#setlimit)를 참조하세요.

## 다음 단계

- [API 참조](https://phasis.inline0.dev/ko/docs/api) — 전체 `Phasis\Engine` 표면.
- [상호 운용](https://phasis.inline0.dev/ko/docs/interop) — 값 변환 규칙, 호스트 함수, PHP 객체.
- [호환성](https://phasis.inline0.dev/ko/docs/compatibility) — 구현된 기능과 test262 지원 범위.
- [파서](https://phasis.inline0.dev/ko/docs/parser) 및 [포매터](https://phasis.inline0.dev/ko/docs/formatter) — JavaScript를 실행하지 않고 파싱하고 포맷합니다.
- [고급](https://phasis.inline0.dev/ko/docs/advanced) — 아키텍처, 바이트코드 VM, 벤치마크, 오라클 테스트.
