Pereiti prie turinio

Darbo pradžia

Diegimas

Shell
composer require phasis/phasis

Phasis reikia PHP 8.2 arba naujesnės versijos su trimis plėtiniais, kurių visų reikia composer.json:

  • mbstring — eilučių apdorojimas.
  • bcmath — BigInt aritmetika ir tikslaus sveikųjų skaičių apdorojimas.
  • curl — numatytoji transporto priemonė, naudojama fetch().

mbstring ir curl daugumoje darinių įjungti. bcmath dažnai neįjungtas: oficialiuose php Docker atvaizduose (php:8.x-cli, php:8.x-fpm) jo nėra, o kai kuriuose platinamuosiuose paketuose jis pateikiamas atskirai (php-bcmath). Be jo Composer atsisako įdiegti Phasis. Jei Dockerfile pagrįstas oficialiu atvaizdu, pridėkite:

Dockerfile
RUN docker-php-ext-install bcmath

Paleiskite php -m, kad pamatytumėte, kurie plėtiniai įtraukti į jūsų darinį.

ext-intl yra pasirenkamas. Phasis veikia ir be jo, tačiau visai Intl.* šeimai (Collator, NumberFormat, DateTimeFormat, PluralRules, Locale, DisplayNames, ListFormat, RelativeTimeFormat, Segmenter, DurationFormat) ir ne ISO Temporal kalendoriams (hebrew, islamic, japanese, persian ir kt.) jo reikia. Įdiekite jį, kai tik pagrindinė programa pateikia JS, kuri naudoja nuo lokalės priklausantį formatavimą. ext-openssl taip pat pasirenkamas: crypto.subtle AES-CBC, AES-CTR ir AES-GCM šifravimui jo reikia, o santraukos ir HMAC veikia be jo.

Nereikia exec, FFI, Node.js ar pasirinktinio gimtojo plėtinio.

Paleiskite scenarijų iš 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 išveda tai, ką scenarijus įrašo naudodamas console.log; paskutinės išraiškos reikšmės jis neišveda.

Visą komandų sąrašą rasite CLI žinyne.

Įterpimas į 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 egzempliorius išsaugo savo globalią būseną tarp iškvietimų, todėl vėlesni eval() iškvietimai gali pasiekti viską, ką apibrėžėte anksčiau.

console.log ir giminingi metodai prideda duomenis į variklio buferį, o ne rašo į stdout. Nuskaitykite naudodami getConsoleOutput() (vieną eilutę, kurioje eilutės sujungtos su \n) arba getConsoleLines() (eilučių sąrašą), o buferį išvalykite naudodami clearConsole().

Pirmasis paleidimas greitas: iš anksto įdiegiama tik specifikacijai būtina branduolio dalis (Object, Function, Array, Promise, Symbol, BigInt, RegExp, klaidų tipai). Map, TypedArray, Temporal, Intl, URL, fetch, visi Web Platform / Fetch paketai ir visa kita skaidriai įkeliama pirmą kartą nuskaičius. Perduokite new Engine(eager: true), kad kuriant būtų įdiegtos visos integruotos funkcijos — naudinga atitikties tikrinimo įrankiams, kurie tikrina deskriptorius prieš juos nuskaitant; įprastam įterpimui retai prireikia.

Funkcijų iškvietimas iš PHP naudojant JS

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

call() ieško funkcijos pagal pavadinimą globaliajame objekte ir iškviečia ją su pateiktais PHP argumentais (automatiškai konvertuotais į JS reikšmes).

PHP iškviečiamos funkcijos pateikimas kaip JS funkcijos

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 uždarymai apgaubiami kaip JS funkcijos. Jų argumentai iškvietimo metu konvertuojami iš JS į PHP, o grąžinamos reikšmės konvertuojamos atgal iš PHP į JS.

PHP objektai

PHP objektų dar negalima bendrinti su JavaScript. Susiejus setGlobal() su paprastu PHP objektu, susiejamas undefined (tai stebima per 18 problemą). Perduokite duomenis kaip masyvus, kurie nukopijuojami į JS, o elgseną pateikite kaip uždarymus:

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

Išsamią informaciją ir kitus sprendimo būdus rasite skiltyje Bendrinami objektai.

Išteklių ribos

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

maxLoopIterations (numatytoji reikšmė 100,000) yra vienintelis vykdymo metu keičiamas apribojimas; šiuo metu jis taikomas tik kodui, kurį vykdo medį interpretuojantis interpretatorius. Baitkodo VM vykdomos kilpos (funkcijų turiniai ir didžioji dalis aukščiausio lygio kodo) neskaičiuojamos, todėl vien šis apribojimas while (true) {} nesustabdo ( 19 problema). Pasiekus apribojimą, klaidą galima pagauti JS; jei ji nepagauta, PHP gauna ją kaip Phasis\Exceptions\InternalError, kuri yra Phasis\Exceptions\RuntimeError poklasė.

Variklis taip pat turi fiksuotą 4,096 kadrų iškvietimų dėklo ribą (ir 8,192 kadrų apsaugą transpilintoms uždarosioms funkcijoms); ją viršijus išmetama „Maximum call stack size exceeded“ klaida, taip pat kaip InternalError. Laiko pagal sieninį laikrodį apribojimo nėra, todėl nepatikimą kodą vykdykite naudodami PHP nuosavą max_execution_time arba atskirame procese. Žr. : setLimitAPI.

Tolesni veiksmai