
# Ricerca AST

La modalità AST di Greph cerca nel codice sorgente PHP in base alla struttura, non al testo. I pattern sono scritti come normale codice PHP con metavariabili `$VARIABLE` e `$$$VARIADIC`. Il pattern viene analizzato da [nikic/php-parser](https://github.com/nikic/PHP-Parser) ottenendo la stessa struttura AST del sorgente in cui si cerca, poi viene confrontato nodo per nodo.

È lo stesso modello di [ast-grep](https://ast-grep.github.io/), e il wrapper di compatibilità `sg` espone l’interfaccia CLI di ast-grep sullo stesso motore.

## Esempi rapidi

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

## Sintassi dei pattern

Un pattern è codice sorgente PHP valido. Ovunque possa comparire un’espressione o un’istruzione reale, potete sostituire un nodo con una metavariabile per catturarlo.

| Sintassi | Corrispondenze | Esempio |
|---|---|---|
| `$VAR` | Qualsiasi singolo nodo AST, catturato come `VAR` | `$x + $y` corrisponde a `foo() + bar` |
| `$_` | Qualsiasi singolo nodo AST, non catturato | `$_->method()` corrisponde a qualsiasi ricevitore |
| `$$$ARGS` | Zero o più nodi (variadici), catturati come `ARGS` | `func($$$ARGS)` corrisponde a `func()`, `func(1)`, `func(1, 2, 3)` |
| `$VAR` (ripetuta) | La stessa `$VAR` deve corrispondere alla stessa struttura entrambe le volte | `$x == $x` corrisponde a `a == a`, ma non a `a == b` |

Per convenzione, il nome della metavariabile è in maiuscolo, ma non è obbligatorio. `$_` è un carattere jolly che non acquisisce valori.

`$$$VARIADIC` funziona ovunque PHP ammetta un elenco di nodi: argomenti di funzione, elementi di array, elenchi di parametri e corpi delle istruzioni. La cattura memorizza l’intera sequenza corrispondente, anche se contiene zero elementi.

## Metavariabili identificative

I nomi di classi, funzioni, interfacce, trait ed enum sono identificatori, non espressioni; quindi PHP normalmente non accetterebbe `$Name` dopo `class`. Greph riscrive questi pattern prima dell’analisi, così la metavariabile continua a funzionare:

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

La stessa riscrittura avviene per `class`, `interface`, `trait`, `enum` e `function`.

## Metavariabili ripetute

Riutilizzare una metavariabile nello stesso pattern significa che entrambe le occorrenze devono corrispondere strutturalmente allo stesso nodo. È utile per individuare codice ridondante:

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

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

La stessa regola si applica a `$$$VARIADIC`: due occorrenze di `$$$ARGS` devono corrispondere alla stessa sequenza.

## Filtraggio dei file

La modalità AST attraversa l’albero dei file con lo stesso iteratore della modalità testo e, per impostazione predefinita, filtra i file di tipo `php`. Potete sovrascrivere il filtro con `--type`, `--type-not`, `--glob`, `--no-ignore` e `--hidden`. L’iteratore continua a rispettare `.gitignore` e `.grephignore` e salta i file binari.

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

## Output

Il formato di output predefinito è lo stesso `file:line:content` della modalità testo, con il codice corrispondente compattato su una sola riga:

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

Passate `--json` per produrre corrispondenze strutturate:

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

## Errori di analisi

Per impostazione predefinita, Greph salta senza avvisare i file che non riesce ad analizzare. È il comportamento giusto quando si opera su una base di codice ampia che contiene PHP sperimentali o generate. Passate `--strict-parse` (su `greph-index ast-index search` e `greph-index ast-cache search`) per generare invece l’errore di analisi, oppure impostate `skipParseErrors: false` su `AstSearchOptions` quando usate direttamente la facade.

## Uso programmatico

```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` è l’`PhpParser\Node` acquisito (quindi potete attraversarlo con `NodeVisitor` di PHP-Parser), mentre `$match->captures` associa ogni nome di metavariabile al nodo o all’elenco di nodi acquisito.

## Come funziona

La pipeline è composta da:

1. **Analisi del pattern**: `Greph\Ast\PatternParser` riscrive le metavariabili identificative, poi analizza il pattern con PHP-Parser.
2. **Analisi del sorgente**: ogni file candidato viene analizzato una volta. In modalità `ast-cache`, questo passaggio viene sostituito dalla deserializzazione dall’archivio della cache.
3. **Filtraggio dei candidati**: `Greph\Ast\AstPatternPrefilter` estrae dal pattern una firma approssimativa (nome della chiamata, nome della classe, tipo di istruzione) e la usa per saltare i file che chiaramente non possono contenere corrispondenze. In modalità `ast-index`, questo passaggio interroga invece un archivio di fatti precalcolato.
4. **Corrispondenza**: `Greph\Ast\PatternMatcher` attraversa l’AST del sorgente e lo confronta con l’AST del pattern, associando le metavariabili ai nodi e verificando la coerenza delle variabili ripetute.
5. **Produzione dei risultati**: i nodi corrispondenti vengono convertiti in oggetti `AstMatch` con posizioni di riga e byte e la porzione corrispondente del sorgente originale.

Per carichi di lavoro ripetuti, [la ricerca AST indicizzata](https://greph.inline0.dev/it/docs/indexed/ast) e [la ricerca AST memorizzata nella cache](https://greph.inline0.dev/it/docs/indexed/cache) saltano gran parte dei passaggi 2 e 3.
