Gå til indhold

Kom godt i gang

Installation

Shell
composer require phasis/phasis

Phasis kræver PHP 8.2 eller nyere med tre udvidelser, som alle kræves af composer.json:

  • mbstring — strenghåndtering.
  • bcmath — BigInt-aritmetik og håndtering af tal med heltalspræcision.
  • curl — standardtransporten bag fetch().

mbstring og curl er aktiveret i de fleste builds. Det er bcmath ofte ikke: de officielle php Docker-images (php:8.x-cli, php:8.x-fpm) udelader det, og nogle distributioner pakker det separat (php-bcmath). Uden det afviser Composer installation af Phasis. Tilføj følgende i en Dockerfile baseret på det officielle image:

Dockerfile
RUN docker-php-ext-install bcmath

Kør php -m for at se, hvilke udvidelser dit build har.

ext-intl er valgfrit. Phasis virker uden det, men hele Intl.*-familien (Collator, NumberFormat, DateTimeFormat, PluralRules, Locale, DisplayNames, ListFormat, RelativeTimeFormat, Segmenter, DurationFormat) og ikke-ISO Temporal-kalendere (hebrew, islamic, japanese, persian osv.) kræver det. Installér det, når værtsprogrammet stiller JS til rådighed, som håndterer lokalitetsbevidst formatering. ext-openssl er også valgfrit: kryptering med crypto.subtle AES-CBC, AES-CTR og AES-GCM kræver det, mens digest og HMAC fungerer uden.

Ingen exec, ingen FFI, ingen Node.js, ingen brugerdefineret native-udvidelse.

Kør et script fra CLI

Shell
./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 udskriver det, scriptet skriver med console.log; det udskriver ikke værdien af det sidste udtryk.

Se CLI-referencen for den fulde kommandoliste.

Indlejr i 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-instansen bevarer sin globale tilstand mellem kald, så efterfølgende eval()-kald kan se alt, du tidligere har defineret.

console.log og dens søskendefunktioner føjer indhold til en buffer på motoren i stedet for at skrive til stdout. Læs den med getConsoleOutput() (én streng, linjer adskilt med \n) eller getConsoleLines() (en liste over linjer), og tøm den med clearConsole().

Koldstart er hurtig: Kun den kerne, der indlæser specifikationerne (Object, Function, Array, Promise, Symbol, BigInt, RegExp og fejltyperne) installeres på forhånd. Map, TypedArray, Temporal, Intl, URL, fetch, de fulde Web Platform- og Fetch-pakker samt alt andet materialiseres automatisk ved første læsning. Angiv new Engine(eager: true) for at installere alle indbyggede komponenter under oprettelsen — nyttigt til værktøjer til overensstemmelsestests, der undersøger deskriptorer, før de læser dem; sjældent nødvendigt ved normal indlejring.

Kald JS-funktioner fra PHP

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

call() finder funktionen ved navn på det globale objekt og kalder den med de angivne PHP-argumenter (der automatisk konverteres til JS-værdier).

Eksponér en PHP-funktion som en JS-funktion

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-closures omsluttes som JS-funktioner. Deres argumenter konverteres fra JS til PHP ved kald, og returværdierne konverteres tilbage fra PHP til JS.

PHP-objekter

PHP-objekter kan endnu ikke deles med JavaScript. setGlobal() med et almindeligt PHP-objekt binder undefined (registreret i issue #18). Send data som arrays, der kopieres til JS, og eksponér adfærd som closures:

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

Se Delte objekter for detaljer og andre løsninger.

Ressourcegrænser

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

maxLoopIterations (standardværdi 100,000) er den eneste grænse, der kan indstilles ved kørsel, og i dag gælder den kun for kode, som den trævandrende fortolker kører. Løkker, som bytecode-VM'en kører (funktionsindhold og det meste kode på øverste niveau), tælles ikke med, så en grænse alene stopper ikke while (true) {} (issue #19). Når grænsen nås, kan fejlen indfanges i JS; hvis den ikke indfanges, når den PHP som Phasis\Exceptions\InternalError, en underklasse af Phasis\Exceptions\RuntimeError.

Motoren har også en fast grænse på 4,096 frames for kaldestakken (og en vagt på 8,192 frames for transpilerede closures); overskrides den, udløses "Maximum call stack size exceeded", igen som InternalError. Der er ingen grænse for vægurtid, så kør ikke-betroet kode under PHPs eget max_execution_time eller i en separat proces. Se API: setLimit.

Næste trin

  • API-reference — hele Phasis\Engine-overfladen.
  • Interop — regler for værdikonvertering, værtsfunktioner, PHP-objekter.
  • Kompatibilitet — hvad der er implementeret, test262-dækning.
  • Parser og Formatter — parse og formatér JavaScript uden at køre det.
  • Avanceret — arkitektur, bytecode-VM, benchmarks, oracle-tests.