بحث AST
يبحث وضع AST في Greph عن مصدر PHP وفق بنيته لا نصه. تُكتب الأنماط على هيئة شيفرة PHP عادية باستخدام المتغيرات الشكلية $VARIABLE و$$$VARIADIC. يحلل nikic/php-parser النمط إلى بنية AST نفسها التي للمصدر الجاري البحث فيه، ثم يطابق العقدة تلو الأخرى.
هذا هو النموذج نفسه الذي يستخدمه ast-grep، وتتيح واجهة التوافق sg سطح ast-grep الخاص بـCLI فوق المحرك نفسه.
أمثلة سريعة
# Find every constructor call
./vendor/bin/greph -p 'new $CLASS()' src
# Find every method call with any number of arguments
./vendor/bin/greph -p '$obj->$method($$$ARGS)' src
# Find old-style array() calls
./vendor/bin/greph -p 'array($$$ITEMS)' src
# Find void-returning functions
./vendor/bin/greph -p 'function $name($$$PARAMS): void {}' src
# Find isset ternaries that should be ??
./vendor/bin/greph -p 'isset($x) ? $x : $default' src
صياغة الأنماط
النمط هو مصدر PHP صالح. في أي موضع يمكن أن يوجد فيه تعبير أو تصريح حقيقي، يمكنك وضع متغير شكلي لالتقاط تلك العقدة.
| الصياغة | ما تطابقه | مثال |
|---|---|---|
$VAR |
أي عقدة AST مفردة، تُلتقط باسم VAR |
يطابق $x + $y foo() + bar |
$_ |
أي عقدة AST مفردة، دون التقاطها | يطابق $_->method() أي مستقبل |
$$$ARGS |
صفرًا أو أكثر من العقد (متغيرة الطول)، تُلتقط باسم ARGS |
يطابق func($$$ARGS) func() وfunc(1) وfunc(1, 2, 3) |
$VAR (مكررة) |
يجب أن يطابق $VAR البنية نفسها في المرتين |
يطابق $x == $x a == a، لا a == b |
يكون اسم المتغير الشكلي بحروف كبيرة عرفًا، لكن ذلك ليس إلزاميًا. $_ بدل شامل لا يلتقط العقدة.
يعمل $$$VARIADIC في أي موضع يسمح فيه PHP بقائمة من العقد: وسائط الدوال، وعناصر المصفوفات، وقوائم المعاملات، وأجسام التصريحات. ويخزن الالتقاط التسلسل المطابق بأكمله، بما في ذلك حالة عدم وجود عناصر.
المتغيرات الشكلية للمعرّفات
أسماء الأصناف والدوال والواجهات والسمات والتعدادات معرّفات وليست تعبيرات، لذا لا يقبل PHP عادةً $Name بعد class. يعيد Greph كتابة هذه الأنماط قبل تحليلها، ليظل المتغير الشكلي صالحًا:
./vendor/bin/greph -p 'class $NAME extends BaseController {}' src
./vendor/bin/greph -p 'function $NAME(): void {}' src
./vendor/bin/greph -p 'interface $NAME {}' src
تُجرى إعادة الكتابة نفسها مع class وinterface وtrait وenum وfunction.
المتغيرات الشكلية المتكررة
إن إعادة استخدام متغير شكلي داخل النمط نفسه تعني أن موضعيه يجب أن يطابقا العقدة نفسها بنيويًا. وهذا مفيد لاكتشاف الشيفرة الزائدة:
# Tautological comparisons
./vendor/bin/greph -p '$X == $X' src
# Redundant ternaries
./vendor/bin/greph -p '$X ? $X : null' src
تنطبق القاعدة نفسها على $$$VARIADIC: يجب أن يطابق موضعا $$$ARGS التسلسل نفسه.
تصفية الملفات
يجتاز وضع AST شجرة الملفات باستخدام أداة الاجتياز نفسها المستخدمة في وضع النص، ويستخدم افتراضيًا مرشح نوع الملفات php. يمكنك تغييره باستخدام --type و--type-not و--glob و--no-ignore و--hidden. ويظل الاجتياز ملتزمًا بـ.gitignore و.grephignore ويتخطى الملفات الثنائية.
./vendor/bin/greph -p 'new $CLASS()' --glob 'src/**/*.php' .
./vendor/bin/greph -p 'new $CLASS()' --type-not phpt src
المخرجات
يستخدم الناتج الافتراضي تنسيق file:line:content نفسه المستخدم في وضع النص، مع طي الشيفرة المطابقة في سطر واحد:
src/Greph.php:38:return (new FileWalker())->walk($paths, $options);
src/Greph.php:49:$searcher = new TextSearcher();
مرر --json لإصدار مطابقات منظّمة:
[
{
"file": "src/Greph.php",
"start_line": 38,
"end_line": 38,
"start_file_pos": 1234,
"end_file_pos": 1252,
"code": "new FileWalker()"
}
]
أخطاء التحليل
يتخطى Greph افتراضيًا بصمت الملفات التي يتعذر تحليلها. وهذا السلوك مناسب عند العمل على قاعدة شيفرة كبيرة تحتوي على PHP تجريبية أو مولّدة. مرر --strict-parse (مع greph-index ast-index search وgreph-index ast-cache search) لإظهار خطأ التحليل، أو اضبط skipParseErrors: false على AstSearchOptions عند استخدام الواجهة مباشرةً.
الاستخدام البرمجي
use Greph\Greph;
use Greph\Ast\AstSearchOptions;
$matches = Greph::searchAst(
'$obj->$method($$$ARGS)',
'src',
new AstSearchOptions(jobs: 4),
);
foreach ($matches as $match) {
echo "{$match->file}:{$match->startLine}\n";
echo $match->code . "\n";
foreach ($match->captures as $name => $node) {
echo " {$name} = " . get_debug_type($node) . "\n";
}
}
$match->node هي PhpParser\Node الملتقطة (لذا يمكنك اجتيازها باستخدام PHP-Parser NodeVisitor)، ويربط $match->captures كل اسم متغير وصفي بالعقدة الملتقطة أو قائمة العقد.
طريقة العمل
يتكون المسار من:
- تحليل النمط: يعيد
Greph\Ast\PatternParserكتابة متغيرات المعرّفات الشكلية، ثم يحلل النمط باستخدام PHP-Parser. - تحليل المصدر: يُحلل كل ملف مرشح مرة واحدة. في وضع
ast-cache، تُستبدل هذه الخطوة بإلغاء تسلسل من مخزن الذاكرة المؤقتة. - تصفية المرشحين: يستخرج
Greph\Ast\AstPatternPrefilterتوقيعًا تقريبيًا من النمط (اسم استدعاء أو اسم صنف أو نوع تصريح)، ويستخدمه لتجاوز الملفات التي يستحيل أن تحتوي على تطابق. وفي وضعast-index، تستعلم هذه الخطوة عن مخزن حقائق محسوب مسبقًا. - المطابقة: يجتاز
Greph\Ast\PatternMatcherشجرة AST المصدر ويقارنها بشجرة النمط، رابطًا المتغيرات الشكلية أثناء ذلك ومتحققًا من اتساق المتغيرات المكررة. - إصدار النتائج: تُحوّل العقد المطابقة إلى كائنات
AstMatchتحتوي على مواضع الأسطر والبايتات ومقطع المصدر الأصلي.
في أعباء العمل المتكررة، يتجاوز بحث AST المفهرس وبحث AST المخزّن مؤقتًا معظم الخطوتين 2 و3.