Zum Inhalt springen

Indexierte AST-Suche

Die indexierte AST-Suche ist die Variante der AST-Suche mit aufgewärmtem Cache. Beim ersten Aufruf werden Fakten auf Knotenebene (Aufrufnamen, Instanziierungen, Klassendeklarationen, Methodendeklarationen) extrahiert und in einem Speicher auf der Festplatte abgelegt. Bei späteren Aufrufen wird damit das Durchsuchen von Dateien übersprungen, die offensichtlich keinen strukturellen Treffer enthalten können. Anschließend prüft der Matcher die Kandidaten erneut anhand des Quellcodes.

Du erreichst den Faktenindex über den Unterbefehl greph-index ast-index CLI und die Fassadenmethoden Greph::buildAstIndex() / Greph::searchAstIndexed(). Er wird auch bei aufgewärmten Suchen über mehrere Indizes und manifestbasierten Gruppen verwendet.

Index erstellen

Shell
# Full build at the current directory
./vendor/bin/greph-index ast-index build .

# Full build at an explicit root
./vendor/bin/greph-index ast-index build path/to/repo

# Use a non-default index directory
./vendor/bin/greph-index ast-index build . --index-dir /tmp/greph-ast-index

# Build a mutable overlay that can refresh cheaply on demand
./vendor/bin/greph-index ast-index build . \
  --lifecycle opportunistic-refresh \
  --auto-refresh-max-files 32 \
  --auto-refresh-max-bytes 1048576

Standardmäßig wird der Faktenindex unter <root>/.greph-ast-index/ gespeichert.

Nach erfolgreicher Erstellung wird eine einzeilige Zusammenfassung ausgegeben:

Text
Built AST index for 2547 files in .greph-ast-index (84931 fact rows, +2547 ~0 -0 =0)

Die vier Zähler geben an, wie viele Dateien seit dem letzten Erstellen des Indexes hinzugefügt, aktualisiert, gelöscht oder nicht verändert wurden.

Index aktualisieren

Shell
./vendor/bin/greph-index ast-index refresh .

refresh durchsucht den indexierten Stammordner erneut, parst geänderte Dateien neu und schreibt nur die veränderten Faktzeilen um. Die Ausgabe verwendet dieselben vier Zähler wie der Textindex.

Lebenszyklusprofile funktionieren genauso wie bei aufgewärmten Textindizes:

  • static: Aktualität nie prüfen oder automatisch ändern
  • manual-refresh: veralteten Zustand in stats melden, aber bei der Suche nie aktualisieren
  • opportunistic-refresh: bei der Suche nur aktualisieren, wenn die Zahl geänderter Dateien gering genug ist
  • strict-stale-check: veraltete Suchen ablehnen, anstatt den Index zu aktualisieren

Index abfragen

Shell
./vendor/bin/greph-index ast-index search 'new $CLASS()' src
./vendor/bin/greph-index ast-index search '$obj->$method($$$ARGS)' src
./vendor/bin/greph-index ast-index search --json 'array($$$ITEMS)' src
./vendor/bin/greph-index ast-index search -l 'array($$$ITEMS)' src
./vendor/bin/greph-index ast-index search --trace-plan 'new $CLASS()' src

Die Metavariablen-Syntax entspricht der nativen AST-Suche. Die vollständige Grammatik findest du unter Modi / AST-Suche.

Optionen

Flag Bedeutung
--lang NAME AST-Sprache. Standard: php
-j N, --jobs N Anzahl paralleler Worker
-l, --files-with-matches Nur passende Dateien auflisten
--glob GLOB Nur Dateien einschließen, deren Pfad auf GLOB passt
--type NAME / --type-not NAME Dateitypfilter
--json JSON-Ausgabe erzeugen
--no-ignore .gitignore und .grephignore ignorieren
--hidden Versteckte Dateien einschließen
--strict-parse Bei Parse-Fehlern abbrechen, statt sie zu überspringen
--fallback MODE Verhalten bei fehlendem Index: fail (Standard) oder scan
--index-dir DIR Ein anderes Indexverzeichnis verwenden
--trace-plan Planner-Diagnosen der aufgewärmten AST-Suche an stderr ausgeben
--show-index-origin Bei Suchen über mehrere Indizes oder Gruppen die Ausgabe mit der Bezeichnung des passenden Indexes versehen

Rückfall bei fehlendem Index

Standardmäßig meldet ast-index search einen Fehler, wenn am angegebenen Speicherort kein Index vorhanden ist. Übergib --fallback scan, um stattdessen eine native AST-Suche auszuführen. Das ist sinnvoll für Tools, die nach Möglichkeit die indexierte Suche verwenden, aber auch ohne zuvor erstellten Index Ergebnisse liefern sollen.

Shell
./vendor/bin/greph-index ast-index search --fallback scan 'new $CLASS()' src

Du kannst mit einem einzigen Befehl mehrere aufgewärmte AST-Indizes durchsuchen, indem du --index-dir wiederholst. Das ist nützlich für Setups wie WordPress-Kern plus Plugin oder Theme.

Shell
./vendor/bin/greph-index ast-index search \
  --index-dir ../wordpress/.greph-ast-index \
  --index-dir ./.greph-ast-index \
  --show-index-origin \
  'new $CLASS()' .

Wenn --show-index-origin aktiviert ist, wird der Name des passenden aufgewärmten Indexeintrags vorangestellt. So siehst du, aus welchem Stammverzeichnis der Treffer stammt.

Manifestbasierte Indexgruppen

Für stabile Setups mit aufgewärmten Indizes kannst du ein Manifest definieren und greph-index set die gruppierten Indizes durchsuchen lassen:

Shell
./vendor/bin/greph-index set build
./vendor/bin/greph-index set stats --mode ast-index --dry-refresh
./vendor/bin/greph-index set search --mode ast-index --show-index-origin 'new $CLASS()' .

Die Unterbefehle für Gruppen verwenden dieselbe Lebenszyklussteuerung, dieselben Prüfungen auf veraltete Indizes und dieselben Planner-Diagnosen wie die direkte Verwendung von ast-index.

Planner-Diagnosen

--trace-plan zeigt, wie Greph die Abfrage vor der Ausführung des vollständigen Matchers eingegrenzt hat. Bei ast-index umfasst das üblicherweise:

  • die grobe Faktensignatur, die aus dem Muster extrahiert wurde
  • die Anzahl der durchsuchten aufgewärmten Indizes
  • die Anzahl der Dateien, die der Faktenfilter ausgeschlossen hat
  • die Anzahl der Kandidatendateien, die für die genaue strukturelle Prüfung erneut geparst wurden

Gespeicherte Indexdaten

Für jede indexierte Datei zeichnet der AST-Faktenextraktor für jeden „interessanten“ Knoten eine Zeile auf. Die verschiedenen Faktarten decken die Konstrukte ab, auf die AST-Muster am häufigsten abzielen:

  • Funktions- und Methodenaufrufe (nach aufgerufenem Namen)
  • Klasseninstanziierungen (nach Klassenname)
  • Deklarationen von Klassen, Interfaces, Traits und Enums (nach Name)
  • Funktionsdeklarationen (nach Name)
  • Methodendeklarationen (nach Name und Sichtbarkeit)
  • Use-Anweisungen

Bei einer Abfrage wandelt Greph\Ast\AstFactQuery das Suchmuster in eine grobe Faktensignatur um (zum Beispiel „muss ein new einer beliebigen Klasse enthalten“) und gleicht diese mit dem Faktenspeicher ab. Die übrig gebliebenen Dateien werden dann erneut geparst und mit dem vollständigen Muster verglichen.

Der Faktenindex ist also ein Vorfilter und kein vollständiger Speicher für Treffer. Beim Abgleich wird der Quellcode weiterhin geparst. Die strukturelle Genauigkeit entspricht daher der nativen AST-Suche; lediglich die Menge der Kandidaten ist kleiner.

Wann du ihn verwenden solltest

Der Modus „Indexierte AST-Suche“ eignet sich, wenn:

  • du wiederholt dieselben strukturellen Abfragen für dieselbe Codebasis ausführst.
  • die Muster auf erkennbare Strukturen abzielen (Aufrufname, Klassenname, Deklarationsart), die der Faktenextraktor speichert.
  • du schneller Ergebnisse erhalten möchtest, als wenn jede Datei erneut geparst würde.

Bei Mustern, deren Unterscheidungsmerkmal nicht im Faktensatz enthalten ist (zum Beispiel komplexen verschachtelten Ausdrücken ohne erkennbaren Kopf), eignet sich der AST-Cache-Modus meist besser. Er überspringt den Parse-Schritt vollständig, ohne sich auf eine grobe Signatur zu stützen.

Programmatische Verwendung

PHP
use Greph\Greph;
use Greph\Ast\AstSearchOptions;
use Greph\Index\IndexLifecycleProfile;

Greph::buildAstIndex('.', lifecycle: IndexLifecycleProfile::OpportunisticRefresh);

$matches = Greph::searchAstIndexed(
    'new $CLASS()',
    'src',
    new AstSearchOptions(jobs: 4, tracePlan: true),
);

searchAstIndexed akzeptiert dieselben AstSearchOptions wie die native AST-Suche und gibt dieselbe list<AstMatch>-Struktur zurück. Für Abläufe über mehrere Indizes oder manifestbasierte Gruppen kannst du searchAstIndexedMany(...) und searchAstIndexedSet(...) verwenden.