Preskočiť na obsah

Začíname

Inštalácia

Shell
composer require phasis/phasis

Phasis vyžaduje PHP 8.2 alebo novší a tri rozšírenia, ktoré všetky vyžaduje composer.json:

  • mbstring — práca s reťazcami.
  • bcmath — aritmetika BigInt a spracovanie čísel s celočíselnou presnosťou.
  • curl — predvolený prenosový mechanizmus pre fetch().

mbstring a curl sú povolené vo väčšine zostáv. bcmath často chýba: oficiálne obrazy php Docker (php:8.x-cli, php:8.x-fpm) ho neobsahujú a niektoré distribúcie ho balia samostatne (php-bcmath). Bez neho Composer odmietne nainštalovať Phasis. Do Dockerfile založeného na oficiálnom obraze pridajte:

Dockerfile
RUN docker-php-ext-install bcmath

Spustite php -m a zistite, ktoré rozšírenia obsahuje vaša zostava.

ext-intl je voliteľné. Phasis funguje aj bez neho, ale celá rodina Intl.* (Collator, NumberFormat, DateTimeFormat, PluralRules, Locale, DisplayNames, ListFormat, RelativeTimeFormat, Segmenter, DurationFormat) a kalendáre Temporal, ktoré nie sú ISO (hebrew, islamic, japanese, persian atď.), ho potrebujú. Nainštalujte ho vždy, keď hostiteľská aplikácia poskytuje JS pracujúce s formátovaním závislým od miestnych nastavení. Voliteľné je aj ext-openssl: šifrovanie AES-CBC, AES-CTR a AES-GCM cez crypto.subtle ho vyžaduje, kým súhrny a HMAC fungujú aj bez neho.

Bez exec, bez FFI, bez Node.js, bez vlastného natívneho rozšírenia.

Spustenie skriptu z 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 vypíše to, čo skript zapisuje pomocou console.log; hodnotu posledného výrazu nevypíše.

Úplný zoznam príkazov nájdete v referenčnej príručke CLI.

Vloženie do 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

Inštancia Engine si medzi volaniami zachováva globálny stav, takže pri ďalších volaniach eval() budú k dispozícii všetky predtým definované prvky.

console.log a súvisiace funkcie pridávajú obsah do vyrovnávacej pamäte modulu namiesto zapisovania na stdout. Prečítajte ho pomocou getConsoleOutput() (jeden reťazec, riadky spojené pomocou \n) alebo getConsoleLines() (zoznam riadkov) a vymažte ho pomocou clearConsole().

Studený štart je rýchly: vopred sa nainštaluje iba jadro potrebné na načítanie špecifikácie (Object, Function, Array, Promise, Symbol, BigInt, RegExp a typy chýb). Map, TypedArray, Temporal, Intl, URL, fetch, úplné balíky Web Platform / Fetch aj všetko ostatné sa transparentne vytvoria pri prvom čítaní. Ak chcete pri vytváraní nainštalovať všetky vstavané prvky, odovzdajte new Engine(eager: true) — užitočné pre nástroje na kontrolu zhody, ktoré skúmajú deskriptory ešte pred čítaním; pri bežnom vkladacom použití je to zriedka potrebné.

Volanie funkcií JS z PHP

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

call() vyhľadá funkciu podľa názvu na globálnom objekte a zavolá ju s dodanými argumentmi PHP (automaticky prevedenými na hodnoty JS).

Sprístupnenie volateľnej funkcie PHP ako funkcie 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")');

Uzávery PHP sa zabalia ako funkcie JS. Ich argumenty sa pri volaní prevedú z JS na PHP a návratové hodnoty sa prevedú späť z PHP na JS.

Objekty PHP

Objekty PHP zatiaľ nemožno zdieľať s JavaScript. setGlobal() s obyčajným objektom PHP naviaže undefined (sledované v probléme č. 18). Údaje odovzdávajte ako polia, ktoré sa skopírujú do JS, a správanie sprístupnite ako uzávery:

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

Podrobnosti a ďalšie riešenia nájdete v časti Zdieľané objekty.

Limity zdrojov

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

maxLoopIterations (predvolená hodnota 100,000) je jediný limit nastaviteľný počas behu programu a momentálne sa vzťahuje len na kód spúšťaný interpretom prechádzajúcim strom. Slučky spúšťané VM bajtkódu (telá funkcií a väčšina kódu najvyššej úrovne) sa nezapočítavajú, takže samotný limit nezastaví while (true) {} (problém č. 19). Po dosiahnutí limitu možno chybu zachytiť v JS; ak zostane nezachytená, dostane sa do PHP ako Phasis\Exceptions\InternalError, podtrieda Phasis\Exceptions\RuntimeError.

Modul má tiež pevný limit zásobníka volaní 4,096 rámcov (a ochranný limit 8,192 rámcov pre transpilované uzávery); jeho prekročenie vyvolá chybu „Maximum call stack size exceeded“, opäť ako InternalError. Časový limit v reálnom čase neexistuje, preto nedôveryhodný kód spúšťajte pod vlastným max_execution_time nástroja PHP alebo v samostatnom procese. Pozrite si API: setLimit.

Ďalšie kroky

  • API referencia — úplné rozhranie Phasis\Engine.
  • Prepojenie — pravidlá konverzie hodnôt, hostiteľské funkcie, objekty PHP.
  • Kompatibilita — implementované funkcie a pokrytie test262.
  • Analyzátor a Formátovač — analyzovanie a formátovanie JavaScript bez jeho spustenia.
  • Rozšírené témy — architektúra, VM bajtkódu, testy výkonu a testovanie pomocou referenčnej implementácie.