
# Vyhľadávanie v AST

Režim AST v Greph vyhľadáva v zdrojovom kóde PHP podľa štruktúry, nie textu. Vzory sa zapisujú ako bežný kód PHP s metavariabilnými premennými `$VARIABLE` a `$$$VARIADIC`. Vzor parsuje [nikic/php-parser](https://github.com/nikic/PHP-Parser) na rovnakú podobu AST, akú má zdrojový kód, v ktorom sa vyhľadáva, a potom sa porovnáva uzol po uzle.

Ide o rovnaký model ako [ast-grep](https://ast-grep.github.io/) a kompatibilný obal `sg` sprístupňuje rozhranie CLI v ast-grep nad tým istým nástrojom.

## Rýchle príklady

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

## Syntax vzorov

Vzor je platný zdrojový kód PHP. Všade, kde môže byť skutočný výraz alebo príkaz, môžete dosadiť metavariabilnú premennú, ktorá zachytí daný uzol.

| Syntax | Zodpovedá | Príklad |
|---|---|---|
| `$VAR` | Ľubovoľný jeden uzol AST, zachytený ako `VAR` | `$x + $y` zodpovedá `foo() + bar` |
| `$_` | Ľubovoľný jeden uzol AST, bez zachytenia | `$_->method()` zodpovedá ľubovoľnému prijímaču |
| `$$$ARGS` | Nula alebo viac uzlov (variadické), zachytených ako `ARGS` | `func($$$ARGS)` zodpovedá `func()`, `func(1)`, `func(1, 2, 3)` |
| `$VAR` (opakované) | Rovnaké `$VAR` musí mať pri oboch výskytoch rovnakú štruktúru | `$x == $x` zodpovedá `a == a`, nie `a == b` |

Názvy metavariabilných premenných sa podľa konvencie píšu veľkými písmenami, nie je to však povinné. `$_` je zástupný symbol bez zachytávania.

`$$$VARIADIC` funguje všade, kde PHP povoľuje zoznam uzlov: v argumentoch funkcií, položkách polí, zoznamoch parametrov a telách príkazov. Zachytenie ukladá celú zodpovedajúcu sekvenciu (vrátane nula položiek).

## Metavariabilné premenné identifikátorov

Názvy tried, funkcií, rozhraní, vlastností a výpočtových typov sú identifikátory, nie výrazy, preto by PHP za `class` bežne neprijalo `$Name`. Greph pred parsovaním prepíše tieto vzory, aby metavariabilná premenná fungovala:

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

Rovnaký prepis sa vykoná pre `class`, `interface`, `trait`, `enum` a `function`.

## Opakované metavariabilné premenné

Opätovné použitie metavariabilnej premennej v tom istom vzore vyžaduje, aby obe výskyty štrukturálne zodpovedali tomu istému uzlu. Pomáha to odhaľovať nadbytočný kód:

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

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

Rovnaké pravidlo platí pre `$$$VARIADIC`: dva výskyty `$$$ARGS` musia zodpovedať rovnakej sekvencii.

## Filtrovanie súborov

Režim AST prechádza strom súborov rovnakým nástrojom ako textový režim a štandardne filtruje podľa typu súboru `php`. Prepíšte filter pomocou `--type`, `--type-not`, `--glob`, `--no-ignore` a `--hidden`. Nástroj naďalej rešpektuje `.gitignore` a `.grephignore` a vynecháva binárne súbory.

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

## Výstup

Predvolený výstup používa rovnaký formát `file:line:content` ako textový režim, pričom zodpovedajúci kód je zobrazený na jednom riadku:

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

Ak chcete vypísať štruktúrované zhody, zadajte `--json`:

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

## Chyby parsovania

V predvolenom nastavení Greph potichu vynecháva súbory, ktoré sa nepodarí parsovať. Je to vhodné pri prehľadávaní veľkej kódovej základne s experimentálnym alebo generovaným kódom PHP. Ak chcete namiesto toho vyvolať chybu parsovania, zadajte `--strict-parse` (pre `greph-index ast-index search` a `greph-index ast-cache search`), alebo pri priamom použití fasády nastavte `skipParseErrors: false` na `AstSearchOptions`.

## Programové použitie

```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` je zachytené `PhpParser\Node` (môžete ho teda prechádzať pomocou `NodeVisitor` z PHP-Parser) a `$match->captures` mapuje každý názov metavariabilnej premennej na zachytený uzol alebo zoznam uzlov.

## Ako to funguje

Postup je takýto:

1. **Parsovanie vzoru**: `Greph\Ast\PatternParser` prepíše metavariabilné premenné identifikátorov a potom vzor parsuje pomocou PHP-Parser.
2. **Parsovanie zdroja**: každý kandidátny súbor sa parsuje raz. V režime `ast-cache` tento krok nahrádza deserializácia z úložiska cache.
3. **Filtrovanie kandidátov**: `Greph\Ast\AstPatternPrefilter` získa zo vzoru hrubý podpis (názov volania, názov triedy, typ príkazu) a použije ho na preskočenie súborov, ktoré zjavne nemôžu obsahovať zhodu. V režime `ast-index` sa namiesto toho vyhľadáva v predpočítanom úložisku faktov.
4. **Porovnávanie**: `Greph\Ast\PatternMatcher` prechádza zdrojové AST a porovnáva ho s AST vzoru, pričom priraďuje hodnoty metavariabilným premenným a priebežne kontroluje konzistentnosť opakovaných premenných.
5. **Vytvorenie výsledku**: zodpovedajúce uzly sa prevedú na objekty `AstMatch` s pozíciami riadkov a bajtov a pôvodným úsekom zdroja.

Pri opakovaných úlohách [indexované vyhľadávanie v AST](https://greph.inline0.dev/sk/docs/indexed/ast) a [vyhľadávanie v AST s cache](https://greph.inline0.dev/sk/docs/indexed/cache) preskakujú väčšinu krokov 2 a 3.
