
# Αναζήτηση AST

Η λειτουργία AST του Greph αναζητά στον πηγαίο κώδικα PHP βάσει δομής και όχι κειμένου. Τα μοτίβα γράφονται ως συνηθισμένος κώδικας PHP με μεταβλητές μετά-επιπέδου `$VARIABLE` και `$$$VARIADIC`. Το μοτίβο αναλύεται από το [nikic/php-parser](https://github.com/nikic/PHP-Parser) στην ίδια μορφή AST με τον κώδικα που αναζητά και, έπειτα, γίνεται αντιστοίχιση κόμβο προς κόμβο.

Αυτό ακολουθεί το ίδιο μοντέλο με το [ast-grep](https://ast-grep.github.io/), ενώ το συμβατό περιτύλιγμα `sg` εκθέτει τη διεπαφή CLI του ast-grep πάνω από την ίδια μηχανή.

## Γρήγορα παραδείγματα

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

## Σύνταξη μοτίβων

Ένα μοτίβο είναι έγκυρος πηγαίος κώδικας PHP. Όπου μπορεί να τοποθετηθεί μια πραγματική έκφραση ή εντολή, μπορείτε να βάλετε μια μεταβλητή μετά-επιπέδου για να συλλάβετε αυτόν τον κόμβο.

| Σύνταξη | Αντιστοιχίες | Παράδειγμα |
|---|---|---|
| `$VAR` | Οποιοσδήποτε μεμονωμένος κόμβος AST, συλλαμβάνεται ως `VAR` | Το `$x + $y` αντιστοιχεί στο `foo() + bar` |
| `$_` | Οποιοσδήποτε μεμονωμένος κόμβος AST, χωρίς σύλληψη | Το `$_->method()` αντιστοιχεί σε οποιονδήποτε δέκτη |
| `$$$ARGS` | Μηδέν ή περισσότεροι κόμβοι (μεταβλητό), συλλαμβάνονται ως `ARGS` | Το `func($$$ARGS)` αντιστοιχεί στα `func()`, `func(1)`, `func(1, 2, 3)` |
| `$VAR` (επαναλαμβανόμενη) | Το ίδιο `$VAR` πρέπει να αντιστοιχεί στην ίδια δομή και τις δύο φορές | Το `$x == $x` αντιστοιχεί στο `a == a`, όχι όμως στο `a == b` |

Συμβατικά, το όνομα της μεταβλητής μετά-επιπέδου γράφεται με κεφαλαία, αλλά αυτό δεν είναι υποχρεωτικό. Το `$_` είναι χαρακτήρας μπαλαντέρ χωρίς σύλληψη.

Το `$$$VARIADIC` λειτουργεί οπουδήποτε το PHP επιτρέπει λίστα κόμβων: ορίσματα συναρτήσεων, στοιχεία πινάκων, λίστες παραμέτρων, σώματα εντολών. Η σύλληψη αποθηκεύει ολόκληρη την ακολουθία που αντιστοιχίστηκε (ακόμη κι αν δεν περιέχει στοιχεία).

## Μεταβλητές μετά-επιπέδου αναγνωριστικών

Τα ονόματα κλάσεων, συναρτήσεων, διεπαφών, γνωρισμάτων και απαριθμήσεων είναι αναγνωριστικά, όχι εκφράσεις· γι’ αυτό το PHP κανονικά δεν θα δεχόταν το `$Name` μετά το `class`. Το Greph ξαναγράφει αυτά τα μοτίβα πριν από την ανάλυση, ώστε η μεταβλητή μετά-επιπέδου να εξακολουθεί να λειτουργεί:

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

Η ίδια επανεγγραφή γίνεται για τα `class`, `interface`, `trait`, `enum` και `function`.

## Επαναλαμβανόμενες μεταβλητές μετά-επιπέδου

Η επαναχρησιμοποίηση μιας μεταβλητής μετά-επιπέδου στο ίδιο μοτίβο απαιτεί και οι δύο εμφανίσεις της να αντιστοιχούν δομικά στον ίδιο κόμβο. Αυτό είναι χρήσιμο για τον εντοπισμό πλεονάζοντος κώδικα:

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

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

Ο ίδιος κανόνας ισχύει για το `$$$VARIADIC`: δύο εμφανίσεις του `$$$ARGS` πρέπει να αντιστοιχούν στην ίδια ακολουθία.

## Φιλτράρισμα αρχείων

Η λειτουργία AST διατρέχει το δέντρο αρχείων με τον ίδιο περιηγητή όπως η λειτουργία κειμένου και, από προεπιλογή, φιλτράρει κατά τύπο αρχείου `php`. Μπορείτε να παρακάμψετε αυτή την προεπιλογή με τα `--type`, `--type-not`, `--glob`, `--no-ignore` και `--hidden`. Ο περιηγητής εξακολουθεί να σέβεται τα `.gitignore` και `.grephignore` και να παραλείπει δυαδικά αρχεία.

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

## Έξοδος

Από προεπιλογή, η έξοδος χρησιμοποιεί την ίδια μορφή `file:line:content` με τη λειτουργία κειμένου και συμπτύσσει τον κώδικα που αντιστοιχίστηκε σε μία γραμμή:

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

Περάστε το `--json` για να λάβετε δομημένες αντιστοιχίες:

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

## Σφάλματα ανάλυσης

Από προεπιλογή, το Greph παραλείπει σιωπηρά αρχεία που δεν αναλύονται επιτυχώς. Αυτή είναι η κατάλληλη συμπεριφορά όταν εκτελείται σε μεγάλη βάση κώδικα που περιέχει πειραματικό ή παραγόμενο PHP. Περάστε το `--strict-parse` (στα `greph-index ast-index search` και `greph-index ast-cache search`) για να εμφανιστεί το σφάλμα ανάλυσης ή ορίστε το `skipParseErrors: false` στο `AstSearchOptions` όταν χρησιμοποιείτε απευθείας το facade.

## Προγραμματιστική χρήση

```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` είναι το `PhpParser\Node` που συλλάβατε (ώστε να μπορείτε να το διατρέξετε με το `NodeVisitor` του PHP-Parser), ενώ το `$match->captures` αντιστοιχίζει κάθε όνομα μεταβλητής μετά-επιπέδου στον κόμβο ή στη λίστα κόμβων που συλλήφθηκε.

## Πώς λειτουργεί

Η ροή εκτέλεσης είναι η εξής:

1. **Ανάλυση μοτίβου**: το `Greph\Ast\PatternParser` ξαναγράφει τις μεταβλητές μετά-επιπέδου αναγνωριστικών και, στη συνέχεια, αναλύει το μοτίβο με το PHP-Parser.
2. **Ανάλυση πηγαίου κώδικα**: κάθε υποψήφιο αρχείο αναλύεται μία φορά. Στη λειτουργία `ast-cache`, αυτό το βήμα αντικαθίσταται από αποσειριοποίηση από το αποθηκευτικό cache.
3. **Φιλτράρισμα υποψηφίων**: το `Greph\Ast\AstPatternPrefilter` εξάγει μια γενική υπογραφή από το μοτίβο (όνομα κλήσης, όνομα κλάσης, τύπος εντολής) και τη χρησιμοποιεί για να παραλείπει αρχεία που προφανώς δεν μπορούν να περιέχουν αντιστοιχία. Στη λειτουργία `ast-index`, αντί γι’ αυτό, γίνεται αναζήτηση σε ένα προϋπολογισμένο αποθηκευτικό σύνολο γεγονότων.
4. **Αντιστοίχιση**: το `Greph\Ast\PatternMatcher` διατρέχει το AST του πηγαίου κώδικα και το συγκρίνει με το AST του μοτίβου, αντιστοιχίζοντας μεταβλητές μετά-επιπέδου και επιβάλλοντας τη συνέπεια των επαναλαμβανόμενων μεταβλητών.
5. **Έκδοση αποτελεσμάτων**: οι κόμβοι που αντιστοιχίστηκαν μετατρέπονται σε αντικείμενα `AstMatch` με θέσεις γραμμής και byte, καθώς και το αρχικό τμήμα πηγαίου κώδικα.

Για επαναλαμβανόμενους φόρτους εργασίας, η [αναζήτηση AST με ευρετήριο](https://greph.inline0.dev/el/docs/indexed/ast) και η [αναζήτηση AST με cache](https://greph.inline0.dev/el/docs/indexed/cache) παραλείπουν τα περισσότερα βήματα 2 και 3.
