Pereiti prie turinio

AST paieška

Greph AST režimu PHP šaltinio kodo ieško pagal struktūrą, o ne pagal tekstą. Šablonai rašomi įprastu PHP kodu su $VARIABLE ir $$$VARIADIC metavariantais. Šabloną į AST, tokios pačios struktūros kaip ieškomas šaltinio kodas, išanalizuoja nikic/php-parser, tada mazgai lyginami po vieną.

Tai toks pats modelis kaip ast-grep, o sg suderinamumo paketas pateikia ast-grep CLI sąsają to paties variklio pagrindu.

Greiti pavyzdžiai

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

Šablonų sintaksė

Šablonas yra taisyklingas PHP šaltinio kodas. Bet kur, kur galima įrašyti tikrą išraišką ar sakinį, galite įterpti metavariantą, kuris užfiksuos tą mazgą.

Sintaksė Atitinka Pavyzdys
$VAR Bet kuris vienas AST mazgas, užfiksuojamas kaip VAR $x + $y atitinka foo() + bar
$_ Bet kuris vienas AST mazgas, nefiksuojamas $_->method() atitinka bet kurį objektą
$$$ARGS Nulis arba daugiau mazgų (kintamo ilgio), užfiksuojamų kaip ARGS func($$$ARGS) atitinka func(), func(1), func(1, 2, 3)
$VAR (pakartotas) Tas pats $VAR abu kartus turi atitikti tą pačią struktūrą $x == $x atitinka a == a, bet ne a == b

Metavarianto pavadinimas pagal susitarimą rašomas didžiosiomis raidėmis, tačiau tai nėra būtina. $_ yra nefiksuojantis pakaitos simbolis.

$$$VARIADIC veikia visur, kur PHP leidžia mazgų sąrašą: funkcijų argumentuose, masyvo elementuose, parametrų sąrašuose, sakinių blokuose. Fiksacija apima visą atitinkančią seką, įskaitant ir tuščią seką.

Identifikatorių metavariantai

Klasių, funkcijų, sąsajų, savybių ir enum pavadinimai yra identifikatoriai, o ne išraiškos, todėl PHP įprastai nepriimtų $Name po class. Greph prieš analizę perrašo šiuos šablonus, kad metavariantas vis tiek veiktų:

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

Tas pats perrašymas atliekamas su class, interface, trait, enum ir function.

Pasikartojantys metavariantai

Pakartotinai naudojant tą patį metavariantą viename šablone nurodoma, kad abu atvejai turi struktūriškai atitikti tą patį mazgą. Tai naudinga ieškant perteklinio kodo:

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

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

Ta pati taisyklė taikoma ir $$$VARIADIC: du $$$ARGS atvejai turi atitikti tą pačią seką.

Failų filtravimas

AST režimu failų medis pereinamas tuo pačiu vedikliu kaip ir teksto režimu; pagal numatytuosius nustatymus taikomas php failų tipo filtras. Jį galite pakeisti naudodami --type, --type-not, --glob, --no-ignore ir --hidden. Vediklis vis tiek paiso .gitignore ir .grephignore bei praleidžia dvejetainius failus.

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

Išvestis

Pagal numatytuosius nustatymus naudojamas toks pats file:line:content formatas kaip ir teksto režimu, o atitinkantis kodas sutraukiamas į vieną eilutę:

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

Perduokite --json, kad būtų pateikti struktūrizuoti atitikmenys:

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

Analizės klaidos

Pagal numatytuosius nustatymus Greph tyliai praleidžia failus, kurių nepavyksta išanalizuoti. Tai tinkama elgsena vykdant paiešką didelėje kodų bazėje, kurioje yra eksperimentinio ar sugeneruoto PHP. Perduokite --strict-parse (naudodami greph-index ast-index search ir greph-index ast-cache search), kad vietoj to būtų pateikta analizės klaida, arba tiesiogiai naudodami sąsają nustatykite skipParseErrors: false ties AstSearchOptions.

Naudojimas programoje

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 yra užfiksuotas PhpParser\Node (todėl galite jį pereiti naudodami PHP-Parser NodeVisitor), o $match->captures kiekvieną metavarianto pavadinimą susieja su užfiksuotu mazgu arba mazgų sąrašu.

Kaip tai veikia

Eiga:

  1. Šablono analizė: Greph\Ast\PatternParser perrašo identifikatorių metavariantus, tada išanalizuoja šabloną su PHP-Parser.
  2. Šaltinio analizė: kiekvienas kandidatinis failas analizuojamas vieną kartą. ast-cache režimu šį veiksmą pakeičia deserializavimas iš talpyklos saugyklos.
  3. Kandidatų filtravimas: Greph\Ast\AstPatternPrefilter iš šablono išgauna apytikrį parašą (iškvietimo pavadinimą, klasės pavadinimą, sakinio tipą) ir pagal jį praleidžia failus, kuriuose akivaizdžiai negali būti atitikmens. ast-index režimu vietoj to užklausiama iš anksto apskaičiuota faktų saugykla.
  4. Atitikmenų paieška: Greph\Ast\PatternMatcher pereina šaltinio AST ir lygina jį su šablono AST, susiedama metavariantus su mazgais ir užtikrindama pasikartojančių kintamųjų nuoseklumą.
  5. Rezultatų pateikimas: atitinkantys mazgai paverčiami AstMatch objektais, kuriuose nurodytos eilučių ir baitų pozicijos bei pradinė šaltinio iškarpa.

Pasikartojančių užklausų atveju indeksuota AST paieška ir AST paieška iš talpyklos praleidžia didžiąją dalį 2 ir 3 veiksmų.