Ugrás a tartalomhoz

AST-keresés

A Greph AST-módja PHP forrást keres a szerkezete, nem a szövege alapján. A minták hétköznapi PHP kódban íródnak, $VARIABLE és $$$VARIADIC metaváltozókkal. A mintát a nikic/php-parser elemzi a keresett forrással azonos AST-struktúrára, majd csomópontról csomópontra illeszti.

Ugyanez a modell, mint a ast-grep esetében, a sg kompatibilitási burkoló pedig ugyanennek a motornak a tetején teszi elérhetővé a ast-grep CLI felületét.

Gyors példák

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

Mintaszintaxis

A minta érvényes PHP forráskód. Bárhol, ahová valódi kifejezés vagy utasítás kerülhet, metaváltozót helyettesíthet be az adott csomópont rögzítéséhez.

Szintaxis Illesztés Példa
$VAR Bármely egy AST-csomópont, rögzítése VAR néven A $x + $y illeszkedik erre: foo() + bar
$_ Bármely egy AST-csomópont, rögzítés nélkül A $_->method() bármely fogadó objektumra illeszkedik
$$$ARGS Nulla vagy több csomópont (változó hosszúságú), rögzítése ARGS néven A func($$$ARGS) illeszkedik ezekre: func(), func(1), func(1, 2, 3)
$VAR (ismételve) Ugyanannak a $VAR-nek mindkét helyen azonos szerkezetűnek kell lennie A $x == $x illeszkedik erre: a == a, de erre nem: a == b

A metaváltozó neve konvenció szerint nagybetűs, de nem kötelező annak lennie. A $_ nem rögzítő helyettesítő karakter.

A $$$VARIADIC mindenhol használható, ahol a PHP csomópontok listáját engedi: függvényargumentumoknál, tömbelemeknél, paraméterlistáknál és utasítástörzsekben. A rögzítés a teljes illesztett sorozatot tárolja, az üres sorozatot is.

Azonosító-metaváltozók

Az osztály-, függvény-, interfész-, trait- és enumnevek azonosítók, nem kifejezések, ezért a PHP normál esetben nem fogadná el utánuk a $Name elemet a class után. A Greph elemzés előtt átírja ezeket a mintákat, így a metaváltozó továbbra is működik:

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

Ugyanez az átírás történik a class, a interface, a trait, a enum és a function esetében is.

Ismétlődő metaváltozók

Ha egy metaváltozót ugyanazon a mintán belül újra felhasznál, azzal előírja, hogy mindkét előfordulásnak szerkezetileg ugyanarra a csomópontra kell illeszkednie. Ez hasznos az ismétlődő kód felismerésére:

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

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

Ugyanez a szabály érvényes a $$$VARIADIC esetében is: a $$$ARGS két előfordulásának ugyanarra a sorozatra kell illeszkednie.

Fájlok szűrése

Az AST-mód ugyanazzal a bejáróval járja be a fájlfát, mint a szöveges mód, és alapértelmezés szerint a php fájltípus-szűrőt használja. Felülbírálható a következőkkel: --type, --type-not, --glob, --no-ignore és --hidden. A bejáró továbbra is figyelembe veszi a .gitignore és a .grephignore értékét, és kihagyja a bináris fájlokat.

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

Kimenet

Az alapértelmezett kimenet a szöveges módéval megegyező file:line:content formátumot használja, a megtalált kódot egyetlen sorba tömörítve:

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

Strukturált találatok kibocsátásához adja meg a --json jelzőt:

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

Elemzési hibák

Alapértelmezés szerint a Greph csendben kihagyja az elemzés során hibát okozó fájlokat. Ez megfelelő viselkedés nagy kódbázisok vizsgálatakor, amelyek kísérleti vagy generált PHP kódot is tartalmaznak. Az elemzési hiba jelzéséhez adja meg a --strict-parse értéket (a greph-index ast-index search és a greph-index ast-cache search esetén), vagy használja közvetlenül a homlokzatot, és állítsa be a skipParseErrors: false értéket a AstSearchOptions objektumon.

Programozott használat

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";
    }
}

A $match->node a rögzített PhpParser\Node (így a PHP-Parser NodeVisitor segítségével bejárhatja), a $match->captures pedig minden metaváltozó nevét a hozzá tartozó rögzített csomópontra vagy csomópontlistára képezi le.

Működés

A folyamat lépései:

  1. Mintafeldolgozás: a Greph\Ast\PatternParser átírja az azonosító-metaváltozókat, majd elemzi a mintát a PHP-Parser segítségével.
  2. Forráskód feldolgozása: minden jelölt fájl egyszer kerül elemzésre. ast-cache módban ezt a lépést a gyorsítótárból történő visszafejtés váltja fel.
  3. Jelöltek szűrése: a Greph\Ast\AstPatternPrefilter durva aláírást nyer ki a mintából (hívásnév, osztálynév, utasítástípus), és ezzel kihagyja azokat a fájlokat, amelyek nyilvánvalóan nem tartalmazhatnak találatot. ast-index módban ehelyett egy előre kiszámított tényadattárban végez lekérdezést.
  4. Illesztés: a Greph\Ast\PatternMatcher bejárja a forrás AST-jét, és összeveti a minta AST-jével; közben hozzárendeli a metaváltozókat, és ellenőrzi, hogy az ismétlődő változók következetesek-e.
  5. Eredmény kibocsátása: az illesztett csomópontok AstMatch objektumokká alakulnak sor- és bájtpozíciókkal, valamint az eredeti forrás megfelelő részletével.

Ismétlődő munkaterhelésnél az indexelt AST-keresés és a gyorsítótárazott AST-keresés kihagyja a 2. és 3. lépés nagy részét.