
# 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](https://github.com/nikic/PHP-Parser), tada mazgai lyginami po vieną.

Tai toks pats modelis kaip [ast-grep](https://ast-grep.github.io/), o `sg` suderinamumo paketas pateikia ast-grep CLI sąsają to paties variklio pagrindu.

## Greiti pavyzdžiai

```bash
# 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ų:

```bash
./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:

```bash
# 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.

```bash
./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](https://greph.inline0.dev/lt/docs/indexed/ast) ir [AST paieška iš talpyklos](https://greph.inline0.dev/lt/docs/indexed/cache) praleidžia didžiąją dalį 2 ir 3 veiksmų.
