Elemző
A Phasis önálló PHP API csomagként teszi elérhetővé a saját JavaScript elemzőjét. A forrásszöveg típusos AST-vé elemezhető, bejárható, majd ESTree-alakú tömbként vagy JSON-ként exportálható, mindezt Engine létrehozása vagy kód futtatása nélkül.
parseSource
public static function parseSource(string $source, string $sourceType = 'script'): Program
A Phasis\Parser\Parser egyhívásos belépési pontja. Elemzi a $source-t, majd visszaadja a Phasis\Ast\Program gyökércsomópontját.
use Phasis\Parser\Parser;
$program = Parser::parseSource('const x = [1, 2].map(n => n * 2);');
$module = Parser::parseSource('import { x } from "./x.js"; export default x;', 'module');
A $sourceType választja ki a nyelvtani célt, az JS elemzők azonos nevű beállítása alapján:
| Érték | Cél |
|---|---|
script |
klasszikus Script. A legfelső szintű import/export szintaktikai hibát okoz, a await azonosító |
module |
ES-modul. Implicit módon szigorú, a import/export és a legfelső szintű await elemezhető |
Minden más érték esetén \InvalidArgumentException keletkezik.
Részletesebb vezérléshez (például a szigorú mód előzetes bekapcsolásához közvetlen eval-kontextusban vagy a //# sourceURL pragma beolvasásához) hozza létre közvetlenül az elemzőt: new Parser($source), valamint setModuleMode(), setStrictMode() és parse() használatával.
Csomópontok
Minden AST-osztály a Phasis\Ast\Node osztályból származik, és a Phasis\Ast\{Declaration, Expression, Pattern, Statement} alatt található. A csomópontok egyszerű, csak olvasható értékobjektumok:
- A
$node->type()adja vissza a csomópont típusát jelölő karakterláncot, amely az osztály alapneve ('BinaryExpression','ArrowFunction', ...). - A
$node->locationegyPhasis\Lexer\SourceLocation, amely tartalmazza aline-t (1-től számozva), acolumn-t (0-tól számozva) és aoffset-t (bájtoffset). A rendszer csak a kezdőpozíciókat tárolja; a csomópontok végpozíciót nem tartalmaznak. - Minden más nyilvános mező a csomópont gyermekét vagy attribútumát jelöli (
$binary->left,$binary->operator,$binary->right).
A legtöbb típusnév megegyezik az ESTree megfelelőjével. Az eltérések:
| Phasis típus | ESTree-típus |
|---|---|
ArrowFunction |
ArrowFunctionExpression |
TaggedTemplate |
TaggedTemplateExpression |
ClassMethod |
MethodDefinition |
ClassProperty |
PropertyDefinition |
AssignmentProperty |
Property (a ObjectPattern belsejében) |
ExportDeclaration |
ExportNamedDeclaration / ExportDefaultDeclaration / ExportAllDeclaration |
ImportSpecifier |
ImportSpecifier / ImportDefaultSpecifier / ImportNamespaceSpecifier |
Néhány adatforma is eltér: az osztály törzse egyszerű elemtömb az osztálycsomóponton (nincs ClassBody burkoló), az import/exportnevek, utasításcímkék és FunctionExpression-nevek karakterláncként tárolódnak beágyazott Identifier csomópontok helyett, a super pedig egy Identifier típusú, super nevű elemként kerül feldolgozásra (nincs Super csomópontosztály). A Phasis\Ast\EstreeSerializer mindezt ESTree-formára alakítja.
Szintaktikai hibák
Érvénytelen bemenet esetén Phasis\Exceptions\SyntaxError keletkezik (általában az Phasis\Parser\ParseError altípusa). A kivételüzenet végén szerepel a pozíció, a strukturált hely pedig a kivétel objektumán érhető el:
use Phasis\Exceptions\SyntaxError;
use Phasis\Parser\Parser;
try {
Parser::parseSource("const a = 1;\nconst b = ;");
} catch (SyntaxError $e) {
echo $e->getMessage(); // Unexpected token (got ; ";") at line 2, column 10
echo $e->location->line; // 2
echo $e->location->column; // 10
echo $e->location->offset; // 23
}
Bejáró
A Phasis\Ast\Walker mélységi bejárással halad végig a fán, belépési és kilépési visszahívásokkal, az estraverse mintájára:
public static function walk(Node $node, callable $enter, ?callable $leave = null): void
Mindkét visszahívás megkapja a (Node $node, ?Node $parent)-t. Ha a $enter visszatérési értéke Walker::SKIP, a csomópont gyermekei kimaradnak a bejárásból, de a saját $leave eseménye továbbra is lefut.
use Phasis\Ast\Expression\Identifier;
use Phasis\Ast\Node;
use Phasis\Ast\Walker;
$names = [];
Walker::walk($program, function (Node $node) use (&$names) {
if ($node instanceof Identifier) {
$names[] = $node->name;
}
if ($node instanceof \Phasis\Ast\Expression\ArrowFunction) {
return Walker::SKIP;
}
});
A bejárás csak olvasásra szolgál. A csomópontok csak olvasható értékobjektumok, ezért nem támogatott a csere és a módosítás.
Sorosító
A Phasis\Ast\Serializer a típusozott AST-t tömbökké vagy JSON alakítja a Phasis típusnevek és mezőelrendezés használatával, a parser belső gyorsítótármezőinek elhagyásával:
public static function toArray(Node $node): array
public static function toJson(Node $node, bool $pretty = false): string
Ezt a formátumot írja ki a bin/phasis --ast.
EstreeSerializer
A Phasis\Ast\EstreeSerializer ehelyett ESTree-formájú tömbökké alakítja a típusozott AST-t: ESTree-típusneveket (a fenti eltéréstáblázat szerint), ESTree-mezőneveket és -sorrendet, létrehozott ClassBody / specifier / source-Literal csomópontokat, valamint a forrásból elemzett minden csomóponthoz start bájtoffsetet. end offset nincs, mert a csomópontok csak kezdőpozíciót tárolnak, a string Literal csomópontok pedig nem tartalmaznak raw szöveget.
public static function toArray(Node $node): array
public static function toJson(Node $node, bool $pretty = false): string
public static function summarize(Node $node): string
A summarize() determinisztikus, csomópontonként egysoros összefoglalót készít (típus, valamint name= / value= / op= / kind= attribútumok, mélységi szintenként két szóköznyi behúzás). A formátum bájtról bájtra megegyezik azzal az AST-összefoglalóval, amelyet az acorn fixture a tests/Popular/acorn alatt készít, így ugyanaz az oracle ellenőrzi a JS parserútvonalat és ezt a PHP exportot is:
use Phasis\Ast\EstreeSerializer;
use Phasis\Parser\Parser;
echo EstreeSerializer::summarize(Parser::parseSource('let x = a + 1;'));
// Program
// VariableDeclaration kind=let
// VariableDeclarator
// Identifier name=x
// BinaryExpression op=+
// Identifier name=a
// Literal value=1
CLI
A bin/phasis --ast script.js elemzi a fájlt (script célként), majd kiírja a Serializer JSON dumpot két szóközös behúzással. A jelzők teljes leírását lásd: CLI.