AST-søgning
AST-tilstanden i Greph søger i PHP-kilde efter struktur, ikke tekst. Mønstre skrives som almindelig PHP-kode med metavariablerne $VARIABLE og $$$VARIADIC. Mønsteret parses af nikic/php-parser til samme AST-struktur som kilden, der søges i, og matches derefter node for node.
Det er samme model som ast-grep, og kompatibilitetsomslaget sg viser ast-greps overflade CLI oven på den samme motor.
Hurtige eksempler
# 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
Mønstersyntaks
Et mønster er gyldig PHP-kilde. Alle steder, hvor et rigtigt udtryk eller en sætning kan stå, kan du indsætte en metavariabel for at indfange den node.
| Syntaks | Matcher | Eksempel |
|---|---|---|
$VAR |
Enhver enkelt AST-node, indfanget som VAR |
$x + $y matcher foo() + bar |
$_ |
Enhver enkelt AST-node, ikke indfanget | $_->method() matcher enhver modtager |
$$$ARGS |
Nul eller flere noder (variadisk), indfanget som ARGS |
func($$$ARGS) matcher func(), func(1), func(1, 2, 3) |
$VAR (gentaget) |
Samme $VAR skal matche den samme struktur begge gange |
$x == $x matcher a == a, men ikke a == b |
Metavariablens navn skrives traditionelt med store bogstaver, men det er ikke et krav. $_ er et jokertegn, der ikke indfanger noget.
$$$VARIADIC fungerer alle steder, hvor PHP tillader en liste af noder: funktionsargumenter, arrayelementer, parameterlister og sætningsblokke. Indfangningen gemmer hele den matchede sekvens (også når den indeholder nul elementer).
Metavariabler for identifikatorer
Klasse-, funktions-, interface-, trait- og enum-navne er identifikatorer, ikke udtryk, så PHP ville normalt ikke acceptere $Name efter class. Greph omskriver disse mønstre før parsing, så metavariablen stadig fungerer:
./vendor/bin/greph -p 'class $NAME extends BaseController {}' src
./vendor/bin/greph -p 'function $NAME(): void {}' src
./vendor/bin/greph -p 'interface $NAME {}' src
Den samme omskrivning sker for class, interface, trait, enum og function.
Gentagne metavariabler
Hvis en metavariabel bruges igen i det samme mønster, kræves det, at begge forekomster strukturelt matcher den samme node. Det er nyttigt til at finde overflødig kode:
# Tautological comparisons
./vendor/bin/greph -p '$X == $X' src
# Redundant ternaries
./vendor/bin/greph -p '$X ? $X : null' src
Den samme regel gælder for $$$VARIADIC: to forekomster af $$$ARGS skal matche den samme sekvens.
Filtrering af filer
AST-tilstanden gennemgår filtræet med samme gennemløber som teksttilstanden og bruger som standard filtypefilteret php. Tilsidesæt det med --type, --type-not, --glob, --no-ignore og --hidden. Gennemløberen respekterer stadig .gitignore og .grephignore og springer binære filer over.
./vendor/bin/greph -p 'new $CLASS()' --glob 'src/**/*.php' .
./vendor/bin/greph -p 'new $CLASS()' --type-not phpt src
Output
Standardoutputtet bruger samme file:line:content-format som teksttilstanden, hvor den matchede kode er samlet på én linje:
src/Greph.php:38:return (new FileWalker())->walk($paths, $options);
src/Greph.php:49:$searcher = new TextSearcher();
Send --json for at generere strukturerede match:
[
{
"file": "src/Greph.php",
"start_line": 38,
"end_line": 38,
"start_file_pos": 1234,
"end_file_pos": 1252,
"code": "new FileWalker()"
}
]
Parsefejl
Som standard springer Greph lydløst filer over, der ikke kan parses. Det er den rette adfærd ved kørsel på tværs af en stor kodebase, der indeholder eksperimentel eller genereret PHP. Send --strict-parse (med greph-index ast-index search og greph-index ast-cache search) for i stedet at udløse parsefejlen, eller angiv skipParseErrors: false på AstSearchOptions, når du bruger facaden direkte.
Programmatisk brug
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 er den indfangede PhpParser\Node (så du kan gennemløbe den med PHP-Parsers NodeVisitor), og $match->captures knytter hvert metavariabelnavn til dets indfangede node eller liste af noder.
Sådan fungerer det
Forløbet er:
- Parsing af mønster:
Greph\Ast\PatternParseromskriver metavariabler for identifikatorer og parser derefter mønsteret med PHP-Parser. - Parsing af kilde: hver kandidatfil parses én gang. I tilstanden
ast-cacheerstattes dette trin af en deserialisering fra cachelageret. - Filtrering af kandidater:
Greph\Ast\AstPatternPrefilterudtrækker en grov signatur fra mønsteret (kaldnavn, klassenavn, sætningstype) og bruger den til at springe filer over, der tydeligvis ikke kan indeholde et match. I tilstandenast-indexslår dette trin i stedet op i et forudberegnet faktalager. - Matchning:
Greph\Ast\PatternMatchergennemløber kilde-AST'et og sammenligner det med mønster-AST'et. Undervejs bindes metavariablerne, og det sikres, at gentagne variabler stemmer overens. - Generering af resultater: matchede noder konverteres til
AstMatch-objekter med linje- og bytepositioner samt det oprindelige udsnit af kilden.
Ved gentagne kørsler springer indekseret AST-søgning og cached AST-søgning de fleste dele af trin 2 og 3 over.