본문으로 건너뛰기

시작하기

설치

셸
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에서 스크립트 실행하기

셸
./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 참조를 확인하세요.

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에서 추적 중). 데이터는 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

자세한 내용과 다른 해결 방법은 공유 객체를 참조하세요.

리소스 제한

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

maxLoopIterations(기본값 100,000)은 실행 중 조정할 수 있는 유일한 제한이며, 현재는 트리 순회 인터프리터가 실행하는 코드에만 적용됩니다. 바이트코드 VM이 실행하는 루프(함수 본문과 대부분의 최상위 코드)는 계산에 포함되지 않으므로 제한만으로는 while (true) {}를 막을 수 없습니다(issue #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를 참조하세요.

다음 단계

  • API 참조 — 전체 Phasis\Engine 표면.
  • 상호 운용 — 값 변환 규칙, 호스트 함수, PHP 객체.
  • 호환성 — 구현된 기능과 test262 지원 범위.
  • 파서 및 포매터 — JavaScript를 실행하지 않고 파싱하고 포맷합니다.
  • 고급 — 아키텍처, 바이트코드 VM, 벤치마크, 오라클 테스트.