Aller au contenu

Premiers pas

Installation

Shell
composer require phasis/phasis

Phasis nécessite PHP 8.2 ou une version ultérieure et trois extensions, toutes requises par composer.json :

  • mbstring — gestion des chaînes.
  • bcmath — arithmétique BigInt et gestion des nombres avec précision entière.
  • curl — le transport par défaut derrière fetch().

mbstring et curl sont activées dans la plupart des compilations. Ce n'est souvent pas le cas de bcmath : les images officielles php Docker (php:8.x-cli, php:8.x-fpm) ne l'incluent pas, et certaines distributions la fournissent séparément (php-bcmath). Sans elle, Composer refuse d'installer Phasis. Dans un Dockerfile basé sur l'image officielle, ajoutez :

Dockerfile
RUN docker-php-ext-install bcmath

Exécutez php -m pour voir quelles extensions sont disponibles dans votre compilation.

ext-intl est facultative. Phasis fonctionne sans elle, mais toute la famille Intl.* (Collator, NumberFormat, DateTimeFormat, PluralRules, Locale, DisplayNames, ListFormat, RelativeTimeFormat, Segmenter, DurationFormat) ainsi que les calendriers Temporal non-ISO (hebrew, islamic, japanese, persian, etc.) en ont besoin. Installez-la dès que l'application hôte expose JS qui touche au formatage tenant compte des paramètres régionaux. ext-openssl est également facultative : le chiffrement AES-CBC, AES-CTR et AES-GCM de crypto.subtle en a besoin, tandis que les condensats et HMAC fonctionnent sans elle.

Pas de exec, pas de FFI, pas de Node.js, pas d'extension native personnalisée.

Exécuter un script depuis le 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

Le CLI affiche ce que le script écrit avec console.log ; il n'affiche pas la valeur de la dernière expression.

Consultez la référence CLI pour connaître toutes les commandes disponibles.

Intégrer dans 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

L'instance Engine conserve son état global entre les appels ; les appels suivants à eval() voient donc tout ce que vous avez défini auparavant.

console.log et ses équivalents ajoutent du contenu à un tampon sur le moteur au lieu de l'écrire sur stdout. Lisez-le avec getConsoleOutput() (une chaîne, lignes jointes par \n) ou getConsoleLines() (une liste de lignes), et videz-le avec clearConsole().

Le démarrage à froid est rapide : seul le cœur nécessaire au chargement des spécifications (Object, Function, Array, Promise, Symbol, BigInt, RegExp et les types d'erreur) est installé d'emblée. Map, TypedArray, Temporal, Intl, URL, fetch, les packs complets Web Platform et Fetch, ainsi que tout le reste sont chargés de façon transparente à la première lecture. Passez new Engine(eager: true) pour installer tous les éléments intégrés à la construction — utile pour les outils de conformité qui inspectent les descripteurs avant de les lire, rarement nécessaire pour une intégration normale.

Appeler des fonctions JS depuis PHP

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

call() recherche la fonction par son nom sur l'objet global et l'appelle avec les arguments PHP fournis (convertis automatiquement en valeurs JS).

Exposer un appelable PHP comme fonction 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")');

Les fermetures PHP sont enveloppées comme fonctions JS. Lors de l'appel, leurs arguments sont convertis de JS en PHP et leurs valeurs de retour sont reconverties de PHP en JS.

Objets PHP

Les objets PHP ne peuvent pas encore être partagés avec JavaScript. setGlobal() avec un objet PHP simple lie undefined (suivi dans l'issue #18). Transmettez les données sous forme de tableaux, qui sont copiés dans JS, et exposez le comportement sous forme de fermetures :

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

Consultez Objets partagés pour plus de détails et d'autres solutions de contournement.

Limites de ressources

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

maxLoopIterations (100,000 par défaut) est la seule limite réglable à l'exécution ; pour l'instant, elle ne s'applique qu'au code exécuté par l'interpréteur qui parcourt l'arbre. Les boucles exécutées par la VM à bytecode (corps de fonctions et la plupart du code de premier niveau) ne sont pas comptabilisées ; cette limite seule n'arrête donc pas while (true) {} (issue #19). Lorsque la limite est atteinte, l'erreur peut être interceptée dans JS ; si elle ne l'est pas, elle remonte à PHP sous la forme de Phasis\Exceptions\InternalError, une sous-classe de Phasis\Exceptions\RuntimeError.

Le moteur possède également une limite fixe de pile d'appels de 4,096 trames (et une protection à 8,192 trames pour les fermetures transpilées) ; la dépasser déclenche "Maximum call stack size exceeded", là encore sous la forme de InternalError. Il n'y a pas de limite de temps réel : exécutez donc le code non fiable sous le max_execution_time propre à PHP ou dans un processus séparé. Voir API : setLimit.

Étapes suivantes

  • Référence API — surface complète de Phasis\Engine.
  • Interopérabilité — règles de conversion des valeurs, fonctions hôtes, objets PHP.
  • Compatibilité — fonctionnalités implémentées et couverture test262.
  • Analyseur et Formateur — analyser et formater JavaScript sans l'exécuter.
  • Avancé — architecture, VM à bytecode, benchmarks et modèle de tests oracle.