AST paieška
Greph AST režimu PHP šaltinio kodo ieško pagal struktūrą, o ne pagal tekstą. Šablonai rašomi įprastu PHP kodu su $VARIABLE ir $$$VARIADIC metavariantais. Šabloną į AST, tokios pačios struktūros kaip ieškomas šaltinio kodas, išanalizuoja nikic/php-parser, tada mazgai lyginami po vieną.
Tai toks pats modelis kaip ast-grep, o sg suderinamumo paketas pateikia ast-grep CLI sąsają to paties variklio pagrindu.
Greiti pavyzdžiai
# 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
Šablonų sintaksė
Šablonas yra taisyklingas PHP šaltinio kodas. Bet kur, kur galima įrašyti tikrą išraišką ar sakinį, galite įterpti metavariantą, kuris užfiksuos tą mazgą.
| Sintaksė | Atitinka | Pavyzdys |
|---|---|---|
$VAR |
Bet kuris vienas AST mazgas, užfiksuojamas kaip VAR |
$x + $y atitinka foo() + bar |
$_ |
Bet kuris vienas AST mazgas, nefiksuojamas | $_->method() atitinka bet kurį objektą |
$$$ARGS |
Nulis arba daugiau mazgų (kintamo ilgio), užfiksuojamų kaip ARGS |
func($$$ARGS) atitinka func(), func(1), func(1, 2, 3) |
$VAR (pakartotas) |
Tas pats $VAR abu kartus turi atitikti tą pačią struktūrą |
$x == $x atitinka a == a, bet ne a == b |
Metavarianto pavadinimas pagal susitarimą rašomas didžiosiomis raidėmis, tačiau tai nėra būtina. $_ yra nefiksuojantis pakaitos simbolis.
$$$VARIADIC veikia visur, kur PHP leidžia mazgų sąrašą: funkcijų argumentuose, masyvo elementuose, parametrų sąrašuose, sakinių blokuose. Fiksacija apima visą atitinkančią seką, įskaitant ir tuščią seką.
Identifikatorių metavariantai
Klasių, funkcijų, sąsajų, savybių ir enum pavadinimai yra identifikatoriai, o ne išraiškos, todėl PHP įprastai nepriimtų $Name po class. Greph prieš analizę perrašo šiuos šablonus, kad metavariantas vis tiek veiktų:
./vendor/bin/greph -p 'class $NAME extends BaseController {}' src
./vendor/bin/greph -p 'function $NAME(): void {}' src
./vendor/bin/greph -p 'interface $NAME {}' src
Tas pats perrašymas atliekamas su class, interface, trait, enum ir function.
Pasikartojantys metavariantai
Pakartotinai naudojant tą patį metavariantą viename šablone nurodoma, kad abu atvejai turi struktūriškai atitikti tą patį mazgą. Tai naudinga ieškant perteklinio kodo:
# Tautological comparisons
./vendor/bin/greph -p '$X == $X' src
# Redundant ternaries
./vendor/bin/greph -p '$X ? $X : null' src
Ta pati taisyklė taikoma ir $$$VARIADIC: du $$$ARGS atvejai turi atitikti tą pačią seką.
Failų filtravimas
AST režimu failų medis pereinamas tuo pačiu vedikliu kaip ir teksto režimu; pagal numatytuosius nustatymus taikomas php failų tipo filtras. Jį galite pakeisti naudodami --type, --type-not, --glob, --no-ignore ir --hidden. Vediklis vis tiek paiso .gitignore ir .grephignore bei praleidžia dvejetainius failus.
./vendor/bin/greph -p 'new $CLASS()' --glob 'src/**/*.php' .
./vendor/bin/greph -p 'new $CLASS()' --type-not phpt src
Išvestis
Pagal numatytuosius nustatymus naudojamas toks pats file:line:content formatas kaip ir teksto režimu, o atitinkantis kodas sutraukiamas į vieną eilutę:
src/Greph.php:38:return (new FileWalker())->walk($paths, $options);
src/Greph.php:49:$searcher = new TextSearcher();
Perduokite --json, kad būtų pateikti struktūrizuoti atitikmenys:
[
{
"file": "src/Greph.php",
"start_line": 38,
"end_line": 38,
"start_file_pos": 1234,
"end_file_pos": 1252,
"code": "new FileWalker()"
}
]
Analizės klaidos
Pagal numatytuosius nustatymus Greph tyliai praleidžia failus, kurių nepavyksta išanalizuoti. Tai tinkama elgsena vykdant paiešką didelėje kodų bazėje, kurioje yra eksperimentinio ar sugeneruoto PHP. Perduokite --strict-parse (naudodami greph-index ast-index search ir greph-index ast-cache search), kad vietoj to būtų pateikta analizės klaida, arba tiesiogiai naudodami sąsają nustatykite skipParseErrors: false ties AstSearchOptions.
Naudojimas programoje
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 yra užfiksuotas PhpParser\Node (todėl galite jį pereiti naudodami PHP-Parser NodeVisitor), o $match->captures kiekvieną metavarianto pavadinimą susieja su užfiksuotu mazgu arba mazgų sąrašu.
Kaip tai veikia
Eiga:
- Šablono analizė:
Greph\Ast\PatternParserperrašo identifikatorių metavariantus, tada išanalizuoja šabloną su PHP-Parser. - Šaltinio analizė: kiekvienas kandidatinis failas analizuojamas vieną kartą.
ast-cacherežimu šį veiksmą pakeičia deserializavimas iš talpyklos saugyklos. - Kandidatų filtravimas:
Greph\Ast\AstPatternPrefilteriš šablono išgauna apytikrį parašą (iškvietimo pavadinimą, klasės pavadinimą, sakinio tipą) ir pagal jį praleidžia failus, kuriuose akivaizdžiai negali būti atitikmens.ast-indexrežimu vietoj to užklausiama iš anksto apskaičiuota faktų saugykla. - Atitikmenų paieška:
Greph\Ast\PatternMatcherpereina šaltinio AST ir lygina jį su šablono AST, susiedama metavariantus su mazgais ir užtikrindama pasikartojančių kintamųjų nuoseklumą. - Rezultatų pateikimas: atitinkantys mazgai paverčiami
AstMatchobjektais, kuriuose nurodytos eilučių ir baitų pozicijos bei pradinė šaltinio iškarpa.
Pasikartojančių užklausų atveju indeksuota AST paieška ir AST paieška iš talpyklos praleidžia didžiąją dalį 2 ir 3 veiksmų.