المحلل
يتيح Phasis محلل JavaScript المستقل الخاص به ضمن PHP API. يمكنك تحليل النص المصدري إلى شجرة AST مكتوبة الأنواع، والتنقل فيها، وتصديرها كمصفوفات على نمط ESTree أو JSON، دون إنشاء Engine أو تنفيذ أي شيء.
parseSource
public static function parseSource(string $source, string $sourceType = 'script'): Program
نقطة الدخول باستدعاء واحد في Phasis\Parser\Parser. تحلل $source وتعيد العقدة الجذرية لـ Phasis\Ast\Program.
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');
يحدد $sourceType هدف القواعد النحوية، وتُسمّى قيمه على غرار الخيار نفسه في محللات JS:
| القيمة | الهدف |
|---|---|
script |
Script تقليدي. تكون import/export على المستوى الأعلى أخطاء نحوية، بينما await معرّف |
module |
وحدة ES. تلتزم الصرامة ضمنيًا، ويُقبل تحليل import/export وawait على المستوى الأعلى |
تؤدي أي قيمة أخرى إلى طرح \InvalidArgumentException.
لمزيد من التحكم (مثل تهيئة الوضع الصارم مسبقًا لسياق direct-eval، أو قراءة توجيه //# sourceURL)، أنشئ المحلل مباشرة: new Parser($source) بالإضافة إلى setModuleMode() وsetStrictMode() وparse().
العقد
ترث كل فئة AST من Phasis\Ast\Node وتقع ضمن Phasis\Ast\{Declaration, Expression, Pattern, Statement}. العقد كائنات قيم عادية للقراءة فقط:
- تعيد
$node->type()سلسلة نوع العقدة، وهي الاسم الأساسي للفئة ('BinaryExpression'و'ArrowFunction'وغيرهما). - تمثل
$node->locationقيمةPhasis\Lexer\SourceLocationتضمline(بدءًا من 1)، وcolumn(بدءًا من 0)، وإزاحة البايتoffset. تُخزّن مواضع البداية فقط؛ ولا تحمل العقد إزاحة النهاية. - جميع الحقول العامة الأخرى هي أبناء العقدة وسماتها (
$binary->leftو$binary->operatorو$binary->right).
تطابق معظم أسماء الأنواع ESTree. أما الاختلافات فهي:
| نوع Phasis | نوع ESTree |
|---|---|
ArrowFunction |
ArrowFunctionExpression |
TaggedTemplate |
TaggedTemplateExpression |
ClassMethod |
MethodDefinition |
ClassProperty |
PropertyDefinition |
AssignmentProperty |
Property (داخل ObjectPattern) |
ExportDeclaration |
ExportNamedDeclaration / ExportDefaultDeclaration / ExportAllDeclaration |
ImportSpecifier |
ImportSpecifier / ImportDefaultSpecifier / ImportNamespaceSpecifier |
تختلف بعض البنى أيضًا: أجسام الفئات مصفوفة عناصر عادية في عقدة الفئة (من دون غلاف ClassBody)، وتُخزّن أسماء الاستيراد/التصدير وتسميات العبارات وأسماء FunctionExpression كسلاسل بدلًا من عقد Identifier متداخلة، كما يُحلّل super على أنه Identifier باسم super (ولا توجد فئة عقدة Super). يعيد Phasis\Ast\EstreeSerializer تشكيل كل ذلك وفق صيغة ESTree.
الأخطاء النحوية
يؤدي الإدخال غير الصالح إلى طرح Phasis\Exceptions\SyntaxError (غالبًا فئته الفرعية Phasis\Parser\ParseError). تنتهي رسالة الاستثناء بالموضع، ويكون الموقع المنظّم متاحًا ضمن الاستثناء:
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
}
المتجوّل
يتنقل Phasis\Ast\Walker في الشجرة بترتيب العمق أولًا، مع استدعاءات رد عند الدخول والمغادرة، على غرار estraverse:
public static function walk(Node $node, callable $enter, ?callable $leave = null): void
يتلقى كلا الاستدعاءين (Node $node, ?Node $parent). وتؤدي إعادة Walker::SKIP من $enter إلى تخطي أبناء العقدة، مع الاستمرار في استدعاء $leave الخاص بالعقدة نفسها.
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;
}
});
التنقل للقراءة فقط. العقد كائنات قيم للقراءة فقط، لذا لا يتوفر دعم للاستبدال أو التعديل.
المُسلسِل
يحوّل Phasis\Ast\Serializer شجرة AST المكتوبة الأنواع إلى مصفوفات أو JSON باستخدام أسماء الأنواع وتخطيط الحقول في Phasis، مع إزالة حقول الذاكرة المؤقتة الداخلية للمحلل:
public static function toArray(Node $node): array
public static function toJson(Node $node, bool $pretty = false): string
هذا هو التنسيق الذي يطبعه bin/phasis --ast.
EstreeSerializer
يحوّل Phasis\Ast\EstreeSerializer شجرة AST المكتوبة الأنواع إلى مصفوفات على نمط ESTree بدلًا من ذلك: أسماء أنواع ESTree (وفق جدول الاختلافات أعلاه)، وأسماء حقول ESTree وترتيبها، وعقد ClassBody / specifier / source-Literal مُنشأة، وإزاحة بايت start لكل عقدة محللة من المصدر. لا توجد إزاحات end لأن العقد لا تخزّن إلا مواضع البداية، كما أن عقد السلاسل Literal لا تحمل نص raw.
public static function toArray(Node $node): array
public static function toJson(Node $node, bool $pretty = false): string
public static function summarize(Node $node): string
يصدر summarize() ملخصًا حتميًا بسطر لكل عقدة (النوع بالإضافة إلى سمات name= / value= / op= / kind=، مع مسافتين بادئتين لكل مستوى عمق). التنسيق مطابق بايتًا ببايت لملخص AST الذي ينتجه اختبار acorn في tests/Popular/acorn، لذا تتحقق الأداة المرجعية نفسها من مسار محلل JS ومن تصدير PHP هذا:
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
يحلّل bin/phasis --ast script.js ملفًا (بهدف Script) ويطبع تفريغ Serializer JSON بمسافتين بادئتين. راجع CLI للاطلاع على مرجع الخيارات كاملًا.