انتقل إلى المحتوى

المحلل

يتيح Phasis محلل JavaScript المستقل الخاص به ضمن PHP API. يمكنك تحليل النص المصدري إلى شجرة AST مكتوبة الأنواع، والتنقل فيها، وتصديرها كمصفوفات على نمط ESTree أو JSON، دون إنشاء Engine أو تنفيذ أي شيء.

parseSource

PHP
public static function parseSource(string $source, string $sourceType = 'script'): Program

نقطة الدخول باستدعاء واحد في Phasis\Parser\Parser. تحلل $source وتعيد العقدة الجذرية لـ Phasis\Ast\Program.

PHP
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). تنتهي رسالة الاستثناء بالموضع، ويكون الموقع المنظّم متاحًا ضمن الاستثناء:

PHP
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:

PHP
public static function walk(Node $node, callable $enter, ?callable $leave = null): void

يتلقى كلا الاستدعاءين (Node $node, ?Node $parent). وتؤدي إعادة Walker::SKIP من $enter إلى تخطي أبناء العقدة، مع الاستمرار في استدعاء $leave الخاص بالعقدة نفسها.

PHP
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، مع إزالة حقول الذاكرة المؤقتة الداخلية للمحلل:

PHP
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.

PHP
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 هذا:

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 للاطلاع على مرجع الخيارات كاملًا.