Siirry sisältöön

AST-haku

Grephn AST-tila etsii PHP-lähdekoodia rakenteen, ei tekstin perusteella. Kuviot kirjoitetaan tavallisena PHP-koodina, jossa käytetään $VARIABLE- ja $$$VARIADIC-metamuuttujia. nikic/php-parser jäsentää kuvion samaan AST-muotoon kuin etsittävä lähdekoodi, minkä jälkeen niitä verrataan solmu kerrallaan.

Tämä vastaa samaa mallia kuin ast-grep, ja sg-yhteensopivuuskerros tarjoaa ast-grepn CLI-rajapinnan saman moottorin päällä.

Esimerkkejä

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

Kuvioiden syntaksi

Kuvion on oltava kelvollista PHP-lähdekoodia. Voit korvata metamuuttujalla minkä tahansa kohdan, johon voisi tulla oikea lauseke tai lause, ja kaapata näin kyseisen solmun.

Syntaksi Vastaa Esimerkki
$VAR Mitä tahansa yksittäistä AST-solmua, kaapataan muodossa VAR $x + $y vastaa foo() + bar
$_ Mitä tahansa yksittäistä AST-solmua, ei kaapata $_->method() vastaa mitä tahansa vastaanottajaa
$$$ARGS Nolla tai useampia solmuja (vaihtuva määrä), kaapataan muodossa ARGS func($$$ARGS) vastaa func(), func(1), func(1, 2, 3)
$VAR (toistuva) Saman $VARn on vastattava molemmilla kerroilla samaa rakennetta $x == $x vastaa a == a, mutta ei a == b

Metamuuttujan nimi kirjoitetaan tavan mukaan isoilla kirjaimilla, mutta niin ei tarvitse tehdä. $_ on kaappaamaton yleismerkki.

$$$VARIADIC toimii kaikkialla, missä PHP sallii solmujen luettelon: funktion argumenteissa, taulukon alkioissa, parametriluetteloissa ja lauseiden rungoissa. Kaappaus tallentaa koko vastaavan jonon (myös tyhjän jonon).

Tunnisteiden metamuuttujat

Luokkien, funktioiden, rajapintojen, traitien ja enumien nimet ovat tunnisteita eivätkä lausekkeita, joten PHP ei tavallisesti hyväksy $Name-merkkiä class-merkin jälkeen. Greph kirjoittaa nämä kuviot uudelleen ennen jäsentämistä, jolloin metamuuttuja toimii:

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

Sama uudelleenkirjoitus tehdään merkeille class, interface, trait, enum ja function.

Toistuvat metamuuttujat

Kun samaa metamuuttujaa käytetään kuviossa useita kertoja, kaikkien esiintymien on vastattava rakenteeltaan samaa solmua. Tämä auttaa löytämään tarpeetonta koodia:

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

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

Sama sääntö koskee myös $$$VARIADIC-merkintää: kahden $$$ARGS-esiintymän on vastattava samaa jonoa.

Tiedostojen suodatus

AST-tila käy hakemistopuun läpi samalla läpikäyjällä kuin tekstihaku ja käyttää oletusarvoisesti tiedostotyyppisuodatinta php. Ohita se valitsimilla --type, --type-not, --glob, --no-ignore ja --hidden. Läpikäyjä noudattaa edelleen asetuksia .gitignore ja .grephignore ja ohittaa binaaritiedostot.

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

Tuloste

Oletustuloste käyttää samaa file:line:content-muotoa kuin tekstihaku, ja osumakoodi on tiivistetty yhdelle riville:

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

Tulosta rakenteiset osumat valitsimella --json:

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

Jäsennysvirheet

Oletusarvoisesti Greph ohittaa äänettömästi tiedostot, joita ei voi jäsentää. Tämä on sopiva toimintatapa, kun käyt läpi suurta koodikantaa, joka sisältää kokeellista tai automaattisesti luotua PHP-koodia. Lisää jäsennysvirhe valitsimella --strict-parse (komennoissa greph-index ast-index search ja greph-index ast-cache search) tai aseta skipParseErrors: false suoraan facadea käyttäessäsi kohteessa AstSearchOptions.

Käyttö ohjelmallisesti

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 on kaapattu PhpParser\Node (jota voit käydä läpi PHP-Parsern NodeVisitor-toiminnolla), ja $match->captures yhdistää kunkin metamuuttujan nimen sen kaapattuun solmuun tai solmujen luetteloon.

Näin se toimii

Putki toimii näin:

  1. Kuvion jäsentäminen: Greph\Ast\PatternParser kirjoittaa tunnisteiden metamuuttujat uudelleen ja jäsentää sitten kuvion komennolla PHP-Parser.
  2. Lähdekoodin jäsentäminen: jokainen ehdokastiedosto jäsennetään kerran. ast-cache-tilassa tämä vaihe korvataan sarjoituksen purkamisella välimuistivarastosta.
  3. Ehdokkaiden suodatus: Greph\Ast\AstPatternPrefilter poimii kuviosta karkean tunnisteen (kutsun nimi, luokan nimi, lauseen tyyppi) ja ohittaa sen avulla tiedostot, joissa ei selvästikään voi olla osumaa. ast-index-tilassa tässä vaiheessa kysytään tietoja esilasketusta faktavarastosta.
  4. Vastaavuuden etsiminen: Greph\Ast\PatternMatcher käy läpi lähteen AST-rakenteen ja vertaa sitä kuvion AST-rakenteeseen, sitoo samalla metamuuttujat ja varmistaa toistuvien muuttujien vastaavuuden.
  5. Tulosten tuottaminen: vastaavat solmut muunnetaan AstMatch-olioiksi, jotka sisältävät rivin ja tavun sijainnit sekä alkuperäisen lähdekoodiosuuden.

Toistuvissa työmalleissa indeksoitu AST-haku ja välimuistiin tallennettu AST-haku ohittavat suurimman osan vaiheista 2 ja 3.