
# Căutare AST

Modul AST din Greph caută în codul sursă PHP după structură, nu după text. Tiparele sunt scrise ca PHP obișnuit, cu metavariabile `$VARIABLE` și `$$$VARIADIC`. Tiparul este analizat de [nikic/php-parser](https://github.com/nikic/PHP-Parser), formând aceeași structură AST ca sursa în care se caută, apoi este comparat nod cu nod.

Acesta este același model ca [ast-grep](https://ast-grep.github.io/), iar wrapperul de compatibilitate `sg` expune suprafața ast-grep CLI peste același motor.

## Exemple rapide

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

## Sintaxa tiparelor

Un tipar este cod sursă PHP valid. Oriunde poate apărea o expresie sau o instrucțiune reală, puteți înlocui acel nod cu o metavariabilă pentru a-l captura.

| Sintaxă | Potrivire | Exemplu |
|---|---|---|
| `$VAR` | Orice nod AST, capturat ca `VAR` | `$x + $y` se potrivește cu `foo() + bar` |
| `$_` | Orice nod AST, fără captură | `$_->method()` se potrivește cu orice receptor |
| `$$$ARGS` | Zero sau mai multe noduri (variadic), capturate ca `ARGS` | `func($$$ARGS)` se potrivește cu `func()`, `func(1)`, `func(1, 2, 3)` |
| `$VAR` (repetată) | Același `$VAR` trebuie să aibă aceeași structură în ambele locuri | `$x == $x` se potrivește cu `a == a`, nu cu `a == b` |

Numele metavariabilei este scris cu majuscule, prin convenție, dar nu este obligatoriu. `$_` este un wildcard fără captură.

`$$$VARIADIC` funcționează oriunde PHP permite o listă de noduri: argumente de funcție, elemente de tablou, liste de parametri și corpuri de instrucțiuni. Captura stochează întreaga secvență potrivită, inclusiv când nu conține niciun element.

## Metavariabile pentru identificatori

Numele claselor, funcțiilor, interfețelor, trăsăturilor și enumerărilor sunt identificatori, nu expresii, așa că PHP nu ar accepta în mod normal `$Name` după `class`. Greph rescrie aceste tipare înainte de analiză, astfel încât metavariabila să funcționeze în continuare:

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

Aceeași rescriere are loc pentru `class`, `interface`, `trait`, `enum` și `function`.

## Metavariabile repetate

Reutilizarea unei metavariabile în același tipar impune ca ambele apariții să se potrivească structural cu același nod. Acest lucru este util pentru identificarea codului redundant:

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

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

Aceeași regulă se aplică pentru `$$$VARIADIC`: două apariții ale lui `$$$ARGS` trebuie să se potrivească cu aceeași secvență.

## Filtrarea fișierelor

Modul AST parcurge arborele de fișiere cu același parcurgător ca modul text și folosește implicit filtrul pentru tipul de fișier `php`. Suprascrieți-l cu `--type`, `--type-not`, `--glob`, `--no-ignore` și `--hidden`. Parcurgătorul respectă în continuare `.gitignore` și `.grephignore` și omite fișierele binare.

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

## Rezultat

Rezultatul implicit folosește același format `file:line:content` ca modul text, iar codul potrivit este condensat pe o singură linie:

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

Transmiteți `--json` pentru a emite potriviri structurate:

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

## Erori de analiză

În mod implicit, Greph omite fără să afișeze un mesaj fișierele care nu pot fi analizate. Acesta este comportamentul potrivit când rulați căutarea într-o bază de cod mare, care conține PHP experimentale sau generate. Transmiteți `--strict-parse` (pentru `greph-index ast-index search` și `greph-index ast-cache search`) pentru a genera în schimb o eroare de analiză sau setați `skipParseErrors: false` în `AstSearchOptions` când folosiți direct facade-ul.

## Utilizare programatică

```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` este `PhpParser\Node` capturat (deci îl puteți parcurge cu PHP-Parser`NodeVisitor`), iar `$match->captures` asociază fiecare nume de metavariabilă nodului sau listei de noduri capturate.

## Cum funcționează

Fluxul este:

1. **Analiza tiparului**: `Greph\Ast\PatternParser` rescrie metavariabilele pentru identificatori, apoi analizează tiparul cu PHP-Parser.
2. **Analiza sursei**: fiecare fișier candidat este analizat o singură dată. În modul `ast-cache`, acest pas este înlocuit de deserializarea din cache.
3. **Filtrarea candidaților**: `Greph\Ast\AstPatternPrefilter` extrage o semnătură grosieră din tipar (numele apelului, numele clasei, tipul instrucțiunii) și o folosește pentru a omite fișierele care în mod evident nu pot conține o potrivire. În modul `ast-index`, acest pas interoghează în schimb o stocare de fapte precalculată.
4. **Potrivirea**: `Greph\Ast\PatternMatcher` parcurge AST-ul sursei și îl compară cu AST-ul tiparului, asociind metavariabile pe parcurs și impunând consecvența variabilelor repetate.
5. **Emiterea rezultatelor**: nodurile potrivite sunt convertite în obiecte `AstMatch` cu poziții de linie și octet și fragmentul original din sursă.

Pentru sarcini repetate, [căutarea AST indexată](https://greph.inline0.dev/ro/docs/indexed/ast) și [căutarea AST din cache](https://greph.inline0.dev/ro/docs/indexed/cache) omit majoritatea pașilor 2 și 3.
