Preskočiť na obsah

Vyhľadávanie v AST

Režim AST v Greph vyhľadáva v zdrojovom kóde PHP podľa štruktúry, nie textu. Vzory sa zapisujú ako bežný kód PHP s metavariabilnými premennými $VARIABLE a $$$VARIADIC. Vzor parsuje nikic/php-parser na rovnakú podobu AST, akú má zdrojový kód, v ktorom sa vyhľadáva, a potom sa porovnáva uzol po uzle.

Ide o rovnaký model ako ast-grep a kompatibilný obal sg sprístupňuje rozhranie CLI v ast-grep nad tým istým nástrojom.

Rýchle príklady

Shell
# 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

Syntax vzorov

Vzor je platný zdrojový kód PHP. Všade, kde môže byť skutočný výraz alebo príkaz, môžete dosadiť metavariabilnú premennú, ktorá zachytí daný uzol.

Syntax Zodpovedá Príklad
$VAR Ľubovoľný jeden uzol AST, zachytený ako VAR $x + $y zodpovedá foo() + bar
$_ Ľubovoľný jeden uzol AST, bez zachytenia $_->method() zodpovedá ľubovoľnému prijímaču
$$$ARGS Nula alebo viac uzlov (variadické), zachytených ako ARGS func($$$ARGS) zodpovedá func(), func(1), func(1, 2, 3)
$VAR (opakované) Rovnaké $VAR musí mať pri oboch výskytoch rovnakú štruktúru $x == $x zodpovedá a == a, nie a == b

Názvy metavariabilných premenných sa podľa konvencie píšu veľkými písmenami, nie je to však povinné. $_ je zástupný symbol bez zachytávania.

$$$VARIADIC funguje všade, kde PHP povoľuje zoznam uzlov: v argumentoch funkcií, položkách polí, zoznamoch parametrov a telách príkazov. Zachytenie ukladá celú zodpovedajúcu sekvenciu (vrátane nula položiek).

Metavariabilné premenné identifikátorov

Názvy tried, funkcií, rozhraní, vlastností a výpočtových typov sú identifikátory, nie výrazy, preto by PHP za class bežne neprijalo $Name. Greph pred parsovaním prepíše tieto vzory, aby metavariabilná premenná fungovala:

Shell
./vendor/bin/greph -p 'class $NAME extends BaseController {}' src
./vendor/bin/greph -p 'function $NAME(): void {}' src
./vendor/bin/greph -p 'interface $NAME {}' src

Rovnaký prepis sa vykoná pre class, interface, trait, enum a function.

Opakované metavariabilné premenné

Opätovné použitie metavariabilnej premennej v tom istom vzore vyžaduje, aby obe výskyty štrukturálne zodpovedali tomu istému uzlu. Pomáha to odhaľovať nadbytočný kód:

Shell
# Tautological comparisons
./vendor/bin/greph -p '$X == $X' src

# Redundant ternaries
./vendor/bin/greph -p '$X ? $X : null' src

Rovnaké pravidlo platí pre $$$VARIADIC: dva výskyty $$$ARGS musia zodpovedať rovnakej sekvencii.

Filtrovanie súborov

Režim AST prechádza strom súborov rovnakým nástrojom ako textový režim a štandardne filtruje podľa typu súboru php. Prepíšte filter pomocou --type, --type-not, --glob, --no-ignore a --hidden. Nástroj naďalej rešpektuje .gitignore a .grephignore a vynecháva binárne súbory.

Shell
./vendor/bin/greph -p 'new $CLASS()' --glob 'src/**/*.php' .
./vendor/bin/greph -p 'new $CLASS()' --type-not phpt src

Výstup

Predvolený výstup používa rovnaký formát file:line:content ako textový režim, pričom zodpovedajúci kód je zobrazený na jednom riadku:

Text
src/Greph.php:38:return (new FileWalker())->walk($paths, $options);
src/Greph.php:49:$searcher = new TextSearcher();

Ak chcete vypísať štruktúrované zhody, zadajte --json:

JSON
[
  {
    "file": "src/Greph.php",
    "start_line": 38,
    "end_line": 38,
    "start_file_pos": 1234,
    "end_file_pos": 1252,
    "code": "new FileWalker()"
  }
]

Chyby parsovania

V predvolenom nastavení Greph potichu vynecháva súbory, ktoré sa nepodarí parsovať. Je to vhodné pri prehľadávaní veľkej kódovej základne s experimentálnym alebo generovaným kódom PHP. Ak chcete namiesto toho vyvolať chybu parsovania, zadajte --strict-parse (pre greph-index ast-index search a greph-index ast-cache search), alebo pri priamom použití fasády nastavte skipParseErrors: false na AstSearchOptions.

Programové použitie

PHP
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 je zachytené PhpParser\Node (môžete ho teda prechádzať pomocou NodeVisitor z PHP-Parser) a $match->captures mapuje každý názov metavariabilnej premennej na zachytený uzol alebo zoznam uzlov.

Ako to funguje

Postup je takýto:

  1. Parsovanie vzoru: Greph\Ast\PatternParser prepíše metavariabilné premenné identifikátorov a potom vzor parsuje pomocou PHP-Parser.
  2. Parsovanie zdroja: každý kandidátny súbor sa parsuje raz. V režime ast-cache tento krok nahrádza deserializácia z úložiska cache.
  3. Filtrovanie kandidátov: Greph\Ast\AstPatternPrefilter získa zo vzoru hrubý podpis (názov volania, názov triedy, typ príkazu) a použije ho na preskočenie súborov, ktoré zjavne nemôžu obsahovať zhodu. V režime ast-index sa namiesto toho vyhľadáva v predpočítanom úložisku faktov.
  4. Porovnávanie: Greph\Ast\PatternMatcher prechádza zdrojové AST a porovnáva ho s AST vzoru, pričom priraďuje hodnoty metavariabilným premenným a priebežne kontroluje konzistentnosť opakovaných premenných.
  5. Vytvorenie výsledku: zodpovedajúce uzly sa prevedú na objekty AstMatch s pozíciami riadkov a bajtov a pôvodným úsekom zdroja.

Pri opakovaných úlohách indexované vyhľadávanie v AST a vyhľadávanie v AST s cache preskakujú väčšinu krokov 2 a 3.