AST-zoeken
De AST-modus van Greph doorzoekt PHP-broncode op basis van structuur, niet op basis van tekst. Patronen worden geschreven als gewone PHP-code met metavariabelen $VARIABLE en $$$VARIADIC. nikic/php-parser parseert het patroon tot dezelfde AST-vorm als de broncode die wordt doorzocht en vergelijkt ze vervolgens knoop voor knoop.
Dit is hetzelfde model als ast-grep, en de compatibiliteitswrapper sg biedt de ast-grep-interface CLI boven op dezelfde engine.
Snelle voorbeelden
# 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
Patroonsyntaxis
Een patroon is geldige PHP-broncode. Overal waar een echte expressie of instructie kan staan, kun je een metavariabele invullen om die knoop vast te leggen.
| Syntaxis | Komt overeen met | Voorbeeld |
|---|---|---|
$VAR |
Elke afzonderlijke AST-knoop, vastgelegd als VAR |
$x + $y komt overeen met foo() + bar |
$_ |
Elke afzonderlijke AST-knoop, niet vastgelegd | $_->method() komt overeen met elke ontvanger |
$$$ARGS |
Nul of meer knopen (variadisch), vastgelegd als ARGS |
func($$$ARGS) komt overeen met func(), func(1), func(1, 2, 3) |
$VAR (herhaald) |
Dezelfde $VAR moet beide keren overeenkomen met dezelfde structuur |
$x == $x komt overeen met a == a, niet met a == b |
De naam van de metavariabele is volgens afspraak in hoofdletters, maar dat hoeft niet. $_ is een jokerteken dat niets vastlegt.
$$$VARIADIC werkt overal waar PHP een lijst met knopen toestaat: functieargumenten, array-items, parameterlijsten en instructieblokken. De capture bevat de hele overeenkomende reeks, inclusief nul items.
Identifier-metavariabelen
Namen van klassen, functies, interfaces, traits en enums zijn identifiers, geen expressies. Daarom accepteert PHP normaal gesproken geen $Name na class. Greph herschrijft deze patronen voordat ze worden geparseerd, zodat de metavariabele toch werkt:
./vendor/bin/greph -p 'class $NAME extends BaseController {}' src
./vendor/bin/greph -p 'function $NAME(): void {}' src
./vendor/bin/greph -p 'interface $NAME {}' src
Dezelfde herschrijving vindt plaats voor class, interface, trait, enum en function.
Herhaalde metavariabelen
Als je een metavariabele binnen hetzelfde patroon opnieuw gebruikt, moeten beide voorkomens structureel met dezelfde knoop overeenkomen. Dit is handig om overbodige code op te sporen:
# Tautological comparisons
./vendor/bin/greph -p '$X == $X' src
# Redundant ternaries
./vendor/bin/greph -p '$X ? $X : null' src
Dezelfde regel geldt voor $$$VARIADIC: twee voorkomens van $$$ARGS moeten overeenkomen met dezelfde reeks.
Bestanden filteren
De AST-modus doorloopt de bestandsstructuur met dezelfde walker als de tekstmodus en gebruikt standaard het bestandstypefilter php. Overschrijf dit met --type, --type-not, --glob, --no-ignore en --hidden. De walker houdt nog steeds rekening met .gitignore en .grephignore en slaat binaire bestanden over.
./vendor/bin/greph -p 'new $CLASS()' --glob 'src/**/*.php' .
./vendor/bin/greph -p 'new $CLASS()' --type-not phpt src
Uitvoer
De standaarduitvoer gebruikt dezelfde file:line:content-indeling als de tekstmodus, waarbij de overeenkomende code op één regel wordt samengevouwen:
src/Greph.php:38:return (new FileWalker())->walk($paths, $options);
src/Greph.php:49:$searcher = new TextSearcher();
Geef --json mee om overeenkomsten in een gestructureerd formaat uit te voeren:
[
{
"file": "src/Greph.php",
"start_line": 38,
"end_line": 38,
"start_file_pos": 1234,
"end_file_pos": 1252,
"code": "new FileWalker()"
}
]
Parseerfouten
Standaard slaat Greph bestanden die niet kunnen worden geparseerd stilzwijgend over. Dat is het juiste gedrag bij het doorzoeken van een grote codebase met experimentele of gegenereerde PHP. Geef --strict-parse mee (bij greph-index ast-index search en greph-index ast-cache search) om in plaats daarvan de parseerfout te melden, of stel skipParseErrors: false in op AstSearchOptions wanneer je de facade rechtstreeks gebruikt.
Programmatisch gebruik
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 is de vastgelegde PhpParser\Node (zodat je erdoorheen kunt lopen met PHP-Parser-NodeVisitor), en $match->captures koppelt elke metavariabelenaam aan de vastgelegde knoop of lijst met knopen.
Werking
De pipeline bestaat uit:
- Patroon parsen:
Greph\Ast\PatternParserherschrijft identifier-metavariabelen en parseert het patroon vervolgens met PHP-Parser. - Broncode parsen: elk kandidaatbestand wordt één keer geparseerd. In de modus
ast-cachewordt deze stap vervangen door deserialisatie uit de cacheopslag. - Kandidaten filteren:
Greph\Ast\AstPatternPrefilterhaalt een grove signatuur uit het patroon (aanroepnaam, klassenaam, instructietype) en gebruikt deze om bestanden over te slaan die duidelijk geen overeenkomst kunnen bevatten. In de modusast-indexwordt in plaats daarvan een vooraf berekende feitenopslag doorzocht. - Matchen:
Greph\Ast\PatternMatcherdoorloopt de AST van de broncode en vergelijkt deze met de AST van het patroon. Daarbij worden metavariabelen gebonden en wordt afgedwongen dat herhaalde variabelen overeenkomen. - Resultaten uitvoeren: overeenkomende knopen worden omgezet in
AstMatch-objecten met regel- en byteposities en het oorspronkelijke brondeel.
Bij herhaalde workloads slaan zoeken in een geïndexeerde AST en zoeken in een gecachte AST de meeste delen van stap 2 en 3 over.