Darbo pradžia
Diegimas
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ė, naudojamafetch().
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:
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
./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
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
$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
$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:
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
$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
- API žinynas — visa
Phasis\Enginesąsaja. - Sąveika — reikšmių konvertavimo taisyklės, pagrindinės funkcijos, PHP objektai.
- Suderinamumas — kas įgyvendinta, test262 aprėptis.
- Analizatorius ir Formatavimo įrankis — analizuokite ir formatuokite JavaScript jo nevykdydami.
- Išplėstinės temos — architektūra, baitkodo VM, našumo testai, tikrinimas pagal etaloninę realizaciją.