Μετάβαση στο περιεχόμενο

Αναζήτηση AST

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

Αυτό ακολουθεί το ίδιο μοντέλο με το ast-grep, ενώ το συμβατό περιτύλιγμα sg εκθέτει τη διεπαφή CLI του ast-grep πάνω από την ίδια μηχανή.

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

Κέλυφος
# 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 ξαναγράφει αυτά τα μοτίβα πριν από την ανάλυση, ώστε η μεταβλητή μετά-επιπέδου να εξακολουθεί να λειτουργεί:

Κέλυφος
./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.

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

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

Κέλυφος
# 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 και να παραλείπει δυαδικά αρχεία.

Κέλυφος
./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 με ευρετήριο και η αναζήτηση AST με cache παραλείπουν τα περισσότερα βήματα 2 και 3.