
# AST-otsing

Greph AST-režiim otsib PHP lähtekoodi struktuuri, mitte teksti järgi. Mustrid kirjutatakse tavalise PHP koodina koos `$VARIABLE` ja `$$$VARIADIC` metamärkidega. Muster parsitakse [nikic/php-parser](https://github.com/nikic/PHP-Parser) abil samakujuliseks AST-puuks nagu lähtekood, millest otsitakse, ning seejärel sobitatakse sõlmhaaval.

See kasutab sama mudelit nagu [ast-grep](https://ast-grep.github.io/) ning `sg` ühilduvuskest pakub sama mootori peal ast-grep CLI-liidest.

## Kiired näited

```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
```

## Mustri süntaks

Muster on kehtiv PHP lähtekood. Kõikjal, kus võiks olla päris avaldis või lause, saate selle sõlme hõivamiseks asendada metamärgi.

| Süntaks | Sobitub | Näide |
|---|---|---|
| `$VAR` | Mis tahes üksik AST-sõlm, hõivatud kui `VAR` | `$x + $y` sobitub mustriga `foo() + bar` |
| `$_` | Mis tahes üksik AST-sõlm, mida ei hõivata | `$_->method()` sobitub mis tahes vastuvõtjaga |
| `$$$ARGS` | Null või rohkem sõlmi (muutliku pikkusega), hõivatud kui `ARGS` | `func($$$ARGS)` sobitub mustriga `func()`, `func(1)`, `func(1, 2, 3)` |
| `$VAR` (korduv) | Sama `$VAR` peab mõlemal korral sobituma sama struktuuriga | `$x == $x` sobitub mustriga `a == a`, mitte `a == b` |

Metamuutuja nimi algab tava kohaselt suurtähega, kuid see pole kohustuslik. `$_` on hõivamiseta metamärk.

`$$$VARIADIC` töötab seal, kus PHP lubab sõlmede loendit: funktsiooniargumendid, massiivi elemendid, parameetriloendid ja lausete kehad. Hõive salvestab kogu sobitatud jada (ka siis, kui see on tühi).

## Identifikaatori metamärgid

Klasside, funktsioonide, liideste, trait'ide ja enum'ite nimed on identifikaatorid, mitte avaldised, seega PHP ei aktsepteeriks tavaliselt `$Name` pärast `class`. Greph kirjutab need mustrid enne parsimist ümber, nii et metamärk toimib endiselt:

```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
```

Sama ümberkirjutamine toimub järgmiste puhul: `class`, `interface`, `trait`, `enum` ja `function`.

## Korduvad metamärgid

Metamärgi korduv kasutamine samas mustris eeldab, et mõlemad esinemiskohad sobituvad struktuurselt sama sõlmega. See aitab leida üleliigset koodi:

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

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

Sama reegel kehtib `$$$VARIADIC` kohta: kaks `$$$ARGS` esinemiskohta peavad sobituma sama jadaga.

## Failide filtreerimine

AST-režiim läbib failipuu tekstiotsinguga sama läbikäijaga ning kasutab vaikimisi failitüübi filtrit `php`. Alistage see valikutega `--type`, `--type-not`, `--glob`, `--no-ignore` ja `--hidden`. Läbikäija järgib endiselt `.gitignore` ja `.grephignore` ning jätab binaarfailid vahele.

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

## Väljund

Vaikimisi kasutab väljund sama vormingut `file:line:content` nagu tekstiotsingus ning koondab sobitatud koodi ühele reale:

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

Struktureeritud vastete väljastamiseks edastage `--json`:

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

## Parsimisvead

Vaikimisi jätab Greph vaikselt vahele failid, mida ei saa parsida. See on sobiv käitumine suure koodibaasi puhul, kus leidub eksperimentaalset või genereeritud PHP-i. Parsimisvea väljastamiseks edastage `--strict-parse` (valikutele `greph-index ast-index search` ja `greph-index ast-cache search`) või määrake `skipParseErrors: false` väärtus suvandile `AstSearchOptions`, kui kasutate fassaadi otse.

## Programmipõhine kasutus

```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 hõivatud `PhpParser\Node` (nii et saate seda läbida PHP-Parser `NodeVisitor` abil) ning `$match->captures` vastendab iga metamärgi nime hõivatud sõlmele või sõlmede loendile.

## Kuidas see töötab

Töövoog on järgmine:

1. **Mustri parsimine**: `Greph\Ast\PatternParser` kirjutab identifikaatorite metamärgid ümber ja seejärel parsib mustri valikuga PHP-Parser.
2. **Lähtekoodi parsimine**: iga kandidaatfail parsitakse üks kord. `ast-cache` režiimis asendab selle sammu hoidlast deserialiseerimine.
3. **Kandidaatide filtreerimine**: `Greph\Ast\AstPatternPrefilter` eraldab mustrist ligikaudse signatuuri (väljakutse nimi, klassi nimi, lausetüüp) ja kasutab seda failide vahelejätmiseks, kui need ilmselgelt vastet sisaldada ei saa. `ast-index` režiimis küsitakse selle asemel eelarvutatud faktide hoidlast.
4. **Sobitamine**: `Greph\Ast\PatternMatcher` läbib lähtekoodi AST-puu ja võrdleb seda mustri AST-puuga, seob seejuures metamärgid ning kontrollib korduvate muutujate kooskõla.
5. **Tulemuste väljastamine**: sobitatud sõlmed teisendatakse `AstMatch` objektideks, mis sisaldavad rea- ja baidipositsioone ning lähtekoodi algset lõiku.

Korduvate töökoormuste korral jätavad [indekseeritud AST-otsing](https://greph.inline0.dev/et/docs/indexed/ast) ja [vahemällu salvestatud AST-otsing](https://greph.inline0.dev/et/docs/indexed/cache) enamiku sammudest 2 ja 3 vahele.
