Αναζήτηση 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 με τη λειτουργία κειμένου και συμπτύσσει τον κώδικα που αντιστοιχίστηκε σε μία γραμμή:
src/Greph.php:38:return (new FileWalker())->walk($paths, $options);
src/Greph.php:49:$searcher = new TextSearcher();
Περάστε το --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.
Προγραμματιστική χρήση
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 αντιστοιχίζει κάθε όνομα μεταβλητής μετά-επιπέδου στον κόμβο ή στη λίστα κόμβων που συλλήφθηκε.
Πώς λειτουργεί
Η ροή εκτέλεσης είναι η εξής:
- Ανάλυση μοτίβου: το
Greph\Ast\PatternParserξαναγράφει τις μεταβλητές μετά-επιπέδου αναγνωριστικών και, στη συνέχεια, αναλύει το μοτίβο με το PHP-Parser. - Ανάλυση πηγαίου κώδικα: κάθε υποψήφιο αρχείο αναλύεται μία φορά. Στη λειτουργία
ast-cache, αυτό το βήμα αντικαθίσταται από αποσειριοποίηση από το αποθηκευτικό cache. - Φιλτράρισμα υποψηφίων: το
Greph\Ast\AstPatternPrefilterεξάγει μια γενική υπογραφή από το μοτίβο (όνομα κλήσης, όνομα κλάσης, τύπος εντολής) και τη χρησιμοποιεί για να παραλείπει αρχεία που προφανώς δεν μπορούν να περιέχουν αντιστοιχία. Στη λειτουργίαast-index, αντί γι’ αυτό, γίνεται αναζήτηση σε ένα προϋπολογισμένο αποθηκευτικό σύνολο γεγονότων. - Αντιστοίχιση: το
Greph\Ast\PatternMatcherδιατρέχει το AST του πηγαίου κώδικα και το συγκρίνει με το AST του μοτίβου, αντιστοιχίζοντας μεταβλητές μετά-επιπέδου και επιβάλλοντας τη συνέπεια των επαναλαμβανόμενων μεταβλητών. - Έκδοση αποτελεσμάτων: οι κόμβοι που αντιστοιχίστηκαν μετατρέπονται σε αντικείμενα
AstMatchμε θέσεις γραμμής και byte, καθώς και το αρχικό τμήμα πηγαίου κώδικα.
Για επαναλαμβανόμενους φόρτους εργασίας, η αναζήτηση AST με ευρετήριο και η αναζήτηση AST με cache παραλείπουν τα περισσότερα βήματα 2 και 3.