
# Vyhledávání AST

Režim AST v Greph prohledává zdrojový kód PHP podle struktury, nikoli textu. Vzory se zapisují jako běžný kód PHP s metavariables `$VARIABLE` a `$$$VARIADIC`. Vzor se pomocí [nikic/php-parser](https://github.com/nikic/PHP-Parser) parsuje do stejného tvaru AST, jaký má prohledávaný zdroj, a potom se porovnává uzel po uzlu.

Funguje to na stejném principu jako [ast-grep](https://ast-grep.github.io/) a obal kompatibility `sg` zpřístupňuje povrch CLI v ast-grep nad stejným modulem.

## Rychlé pří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
```

## Syntaxe vzorů

Vzor je platný zdrojový kód PHP. Kdekoli může být výraz nebo příkaz, lze ho nahradit metavariable, která tento uzel zachytí.

| Syntaxe | Odpovídá | Příklad |
|---|---|---|
| `$VAR` | Libovolnému uzlu AST, který se zachytí jako `VAR` | `$x + $y` odpovídá `foo() + bar` |
| `$_` | Libovolnému uzlu AST bez zachycení | `$_->method()` odpovídá libovolnému příjemci |
| `$$$ARGS` | Nule nebo více uzlům (variadicky), zachyceným jako `ARGS` | `func($$$ARGS)` odpovídá `func()`, `func(1)`, `func(1, 2, 3)` |
| `$VAR` (opakovaně) | Stejné `$VAR` musí v obou výskytech odpovídat stejné struktuře | `$x == $x` odpovídá `a == a`, nikoli `a == b` |

Názvy metavariables se podle konvence píší velkými písmeny, není to však nutné. `$_` je zástupný symbol bez zachycení.

`$$$VARIADIC` funguje všude, kde PHP umožňuje seznam uzlů: v argumentech funkcí, položkách polí, seznamech parametrů i tělech příkazů. Zachycení uloží celou odpovídající sekvenci, včetně prázdné.

## Metavariables identifikátorů

Názvy tříd, funkcí, rozhraní, traitů a výčtových typů jsou identifikátory, nikoli výrazy, takže PHP by obvykle za `class` nepřijalo `$Name`. Greph tyto vzory před parsováním upraví, aby metavariable 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
```

Stejná úprava se provádí pro `class`, `interface`, `trait`, `enum` a `function`.

## Opakované metavariables

Opětovné použití metavariable ve stejném vzoru znamená, že oba výskyty musí strukturálně odpovídat stejnému uzlu. To se hodí při hledání nadbytečného kódu:

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

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

Stejné pravidlo platí pro `$$$VARIADIC`: dva výskyty `$$$ARGS` musí odpovídat stejné sekvenci.

## Filtrování souborů

Režim AST prochází strom souborů stejným průchodem jako textový režim a ve výchozím nastavení filtruje soubory typu `php`. Filtr lze změnit pomocí `--type`, `--type-not`, `--glob`, `--no-ignore` a `--hidden`. Průchod stále respektuje `.gitignore` a `.grephignore` a přeskakuje binární soubory.

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

## Výstup

Výchozí výstup používá stejný formát `file:line:content` jako textový režim; odpovídající kód je sloučen do jednoho řádku:

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

Strukturované shody vypíšete pomocí `--json`:

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

## Chyby parsování

Ve výchozím nastavení Greph tiše přeskakuje soubory, které nelze parsovat. To je vhodné při prohledávání rozsáhlé kódové základny s experimentálním nebo generovaným kódem PHP. Chcete-li místo toho vyvolat chybu parsování, použijte `--strict-parse` (v `greph-index ast-index search` a `greph-index ast-cache search`), nebo při přímém použití fasády nastavte `skipParseErrors: false` na `AstSearchOptions`.

## Programové použití

```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 zachycené `PhpParser\Node` (můžete ho tedy procházet pomocí PHP-Parser z `NodeVisitor`) a `$match->captures` mapuje každý název metavariable na zachycený uzel nebo seznam uzlů.

## Jak to funguje

Postup:

1. **Parsování vzoru**: `Greph\Ast\PatternParser` upraví metavariables identifikátorů a poté vzor parsuje pomocí PHP-Parser.
2. **Parsování zdroje**: každý kandidátní soubor se parsuje jednou. V režimu `ast-cache` tento krok nahrazuje deserializace z úložiště mezipaměti.
3. **Filtrování kandidátů**: `Greph\Ast\AstPatternPrefilter` extrahuje z vzoru hrubý podpis (název volání, název třídy, typ příkazu) a vynechá soubory, které zjevně nemohou obsahovat shodu. V režimu `ast-index` se místo toho dotazuje na předem vypočítané úložiště faktů.
4. **Porovnání**: `Greph\Ast\PatternMatcher` prochází zdrojový AST a porovnává ho s AST vzoru, přitom přiřazuje hodnoty metavariables a ověřuje konzistenci opakovaných proměnných.
5. **Vytvoření výsledku**: odpovídající uzly se převedou na objekty `AstMatch` s pozicemi řádků a bajtů a původním výřezem zdrojového kódu.

Při opakovaném použití [indexované vyhledávání AST](https://greph.inline0.dev/cs/docs/indexed/ast) a [vyhledávání AST z mezipaměti](https://greph.inline0.dev/cs/docs/indexed/cache) přeskočí většinu kroků 2 a 3.
