İçeriğe geç

AST Arama

Greph AST modu, PHP kaynak kodunu metne göre değil, yapısına göre arar. Desenler, $VARIABLE ve $$$VARIADIC üst değişkenleri içeren sıradan PHP kodu olarak yazılır. Desen, aradığı kaynakla aynı AST yapısına ayrıştırılır; bunu nikic/php-parser yapar. Ardından düğümler tek tek eşleştirilir.

Bu, ast-grep ile aynı modeldir ve sg uyumluluk sarmalayıcısı aynı altyapının üzerinde ast-grep'ün CLI arayüzünü sunar.

Hızlı örnekler

Kabuk
# 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

Desen söz dizimi

Bir desen, geçerli bir PHP kaynak kodudur. Gerçek bir ifade veya deyimin yer alabileceği her yerde, o düğümü yakalamak için yerine bir üst değişken koyabilirsiniz.

Söz dizimi Eşleşen Örnek
$VAR VAR olarak yakalanan herhangi bir AST düğümü $x + $y, foo() + bar ile eşleşir
$_ Yakalanmayan herhangi bir AST düğümü $_->method() herhangi bir alıcıyla eşleşir
$$$ARGS ARGS olarak yakalanan sıfır veya daha fazla düğüm (değişken sayıda) func($$$ARGS), func(), func(1), func(1, 2, 3) ile eşleşir
$VAR (tekrarlanan) Aynı $VAR her iki yerde de aynı yapıyla eşleşmelidir $x == $x, a == a ile eşleşir, a == b ile eşleşmez

Üst değişken adı geleneksel olarak büyük harfle yazılır, ancak böyle olması gerekmez. $_ yakalama yapmayan bir joker karakterdir.

$$$VARIADIC, PHP düğüm listesine izin verdiği her yerde kullanılabilir: işlev bağımsız değişkenlerinde, dizi öğelerinde, parametre listelerinde ve deyim gövdelerinde. Yakalama, eşleşen dizinin tamamını (sıfır öğe olsa bile) saklar.

Tanımlayıcı üst değişkenleri

Sınıf, işlev, arayüz, trait ve enum adları ifade değil, tanımlayıcıdır; bu nedenle PHP normalde class sonrasında $Name kabul etmez. Greph ayrıştırmadan önce bu desenleri yeniden yazar; böylece üst değişken çalışmaya devam eder:

Kabuk
./vendor/bin/greph -p 'class $NAME extends BaseController {}' src
./vendor/bin/greph -p 'function $NAME(): void {}' src
./vendor/bin/greph -p 'interface $NAME {}' src

Aynı yeniden yazma class, interface, trait, enum ve function için de yapılır.

Yinelenen üst değişkenler

Aynı desen içinde bir üst değişkeni yeniden kullanmak, her iki kullanımın da yapısal olarak aynı düğümle eşleşmesi gerektiğini belirtir. Bu, gereksiz kodu saptamak için kullanışlıdır:

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

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

Aynı kural $$$VARIADIC için de geçerlidir: $$$ARGS değişkeninin iki kullanımı da aynı diziyle eşleşmelidir.

Dosyaları filtreleme

AST modu, dosya ağacını metin moduyla aynı geziciyle tarar ve varsayılan olarak php dosya türü filtresini kullanır. --type, --type-not, --glob, --no-ignore ve --hidden ile değiştirin. Gezici, .gitignore ve .grephignore kurallarına uymaya devam eder ve ikili dosyaları atlar.

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

Çıktı

Varsayılan çıktı, metin moduyla aynı file:line:content biçimini kullanır; eşleşen kod tek satıra indirgenir:

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

Yapılandırılmış eşleşmeler üretmek için --json iletin:

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

Ayrıştırma hataları

Varsayılan olarak Greph, ayrıştırılamayan dosyaları sessizce atlar. Deneysel veya oluşturulmuş PHP içeren büyük bir kod tabanında çalışırken doğru davranış budur. Bunun yerine ayrıştırma hatasını yükseltmek için --strict-parse kullanın (greph-index ast-index search ve greph-index ast-cache search üzerinde) ya da cepheyi doğrudan kullanırken AstSearchOptions üzerinde skipParseErrors: false ayarlayın.

Programatik kullanım

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 yakalanan PhpParser\Node değeridir (bu nedenle PHP-Parser'in NodeVisitor özelliğiyle gezebilirsiniz); $match->captures ise her üst değişken adını yakalanan düğüm veya düğüm listesiyle eşleştirir.

Nasıl çalışır

İşlem hattı şöyledir:

  1. Desen ayrıştırma: Greph\Ast\PatternParser, tanımlayıcı üst değişkenlerini yeniden yazar ve ardından deseni PHP-Parser ile ayrıştırır.
  2. Kaynak ayrıştırma: Her aday dosya bir kez ayrıştırılır. ast-cache modunda bu adımın yerini önbellek deposundan serileştirilmiş veriyi okuma alır.
  3. Aday filtreleme: Greph\Ast\AstPatternPrefilter, desenden kaba bir imza (çağrı adı, sınıf adı, deyim türü) çıkarır ve eşleşme içermesi açıkça mümkün olmayan dosyaları atlamak için bunu kullanır. ast-index modunda bu adım, önceden hesaplanmış bir olgu deposunu sorgular.
  4. Eşleştirme: Greph\Ast\PatternMatcher kaynak AST'sini tarar ve desen AST'siyle karşılaştırır; bu sırada üst değişkenleri bağlar ve yinelenen değişkenlerin tutarlı olmasını sağlar.
  5. Sonuç üretimi: Eşleşen düğümler, satır ve bayt konumlarıyla özgün kaynak dilimini içeren AstMatch nesnelerine dönüştürülür.

Tekrarlanan iş yüklerinde dizinli AST araması ve önbelleğe alınmış AST araması, 2. ve 3. adımların çoğunu atlar.