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ä
# 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:
./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:
# 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.
./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:
src/Greph.php:38:return (new FileWalker())->walk($paths, $options);
src/Greph.php:49:$searcher = new TextSearcher();
Tulosta rakenteiset osumat valitsimella --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
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:
- Kuvion jäsentäminen:
Greph\Ast\PatternParserkirjoittaa tunnisteiden metamuuttujat uudelleen ja jäsentää sitten kuvion komennolla PHP-Parser. - Lähdekoodin jäsentäminen: jokainen ehdokastiedosto jäsennetään kerran.
ast-cache-tilassa tämä vaihe korvataan sarjoituksen purkamisella välimuistivarastosta. - Ehdokkaiden suodatus:
Greph\Ast\AstPatternPrefilterpoimii 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. - Vastaavuuden etsiminen:
Greph\Ast\PatternMatcherkäy läpi lähteen AST-rakenteen ja vertaa sitä kuvion AST-rakenteeseen, sitoo samalla metamuuttujat ja varmistaa toistuvien muuttujien vastaavuuden. - 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.