AST Yeniden Yazma
AST yeniden yazma, yerine koyma şablonu içeren AST aramasıdır. Greph, AST araması ile aynı eşleştiriciyi çalıştırır; ardından yeniden yazılmış şablonu, eşleşen bayt aralıklarındaki kaynak koda ekler. Çevredeki boşluklar, girintiler ve yorumlar korunur; eşleşen kodun kendisi yeniden yazdırılır (bkz. Eşleşme içi).
Hızlı örnekler
# Convert array() literals to []
./vendor/bin/greph -p 'array($$$ITEMS)' -r '[$$$ITEMS]' --dry-run src
./vendor/bin/greph -p 'array($$$ITEMS)' -r '[$$$ITEMS]' src
# isset ternary -> null coalesce
./vendor/bin/greph -p 'isset($X) ? $X : $Y' -r '$X ?? $Y' --dry-run src
# Rename a method call
./vendor/bin/greph -p '$obj->oldName($$$ARGS)' -r '$obj->newName($$$ARGS)' src
# Confirm each file before writing
./vendor/bin/greph -p 'array($$$ITEMS)' -r '[$$$ITEMS]' --interactive src
Yerine koyma şablonları
Yerine koyma şablonu, arama deseniyle aynı üst değişken söz dizimini kullanır. Yakalanan düğümler adlarına göre yeniden eklenir:
| Desende | Şablonda | Etki |
|---|---|---|
$X |
$X |
Yakalanan düğümü ekler |
$$$ARGS |
$$$ARGS |
Yakalanan düğüm dizisini ekler |
$NAME (tanımlayıcı) |
$NAME |
Yakalanan tanımlayıcıyı ekler |
Desende yer alıp şablonda yer almayan üst değişkenler yeniden yazılan koddan çıkarılır. Şablonda yer alıp desende bulunmayan bir üst değişken şu anda reddedilmez: olduğu gibi yazdırılır; dolayısıyla -r '$o->$m($$$A, $EXTRA)' koda bir $EXTRA değişkeni yazar (#4). Önce şablonları --dry-run ile denetleyin.
Modlar
| Bayrak | Davranış |
|---|---|
| (yok) | Yeniden yazmayı eşleşen her dosyaya uygular |
--dry-run |
Yeniden yazılmış dosya içeriğini stdout'a yazdırır, dosyaya yazmaz |
--interactive |
Her dosyaya yazmadan önce Rewrite <file>? [y/N] sorar |
Varsayılan davranış, "değişen her şeyi yaz" şeklindedir. Bir desen üzerinde çalışırken önce --dry-run kullanın, ardından farktan memnun kaldığınızda bu bayrağı kaldırın.
Biçimi koruma
Greph dosyayı yeniden biçimlendirmez. Eşleştirici, özgün kaynakta her eşleşmenin bayt aralığını kaydeder; yeniden yazıcı da oluşturulan şablonu doğrudan bu aralıklara ekler. Dokunulmayan kod, aşağıdakiler de dahil olmak üzere aynen kalır:
- Girinti ve sondaki boşluklar
- Satır içi ve bağımsız yorumlar
- Boş satır düzeni
- Dize tırnak stili ve birleştirme biçimi
- PHP açma/kapatma etiketi stili
Bu, dosyanın tamamını yeniden biçimlendirmeye karşı bilinçli bir ödünleşimdir: Greph eşleşmediği kodu "düzeltmez", ancak ilgisiz satırlarda gereksiz farklar da oluşturmaz.
Eşleşme içi
Eşleşen aralığın kendisi, yakalanan düğümler kaynak metinden kopyalanmak yerine yeniden yazdırılarak, şablonun PHP-Parser biçimlendiricisiyle oluşturulmuş hâliyle değiştirilir. Şimdilik bunun anlamı şudur (#2):
- Eşleşme içinde sonraki bir düğüme bağlı olmayan yorumlar atılır; örneğin son dizi öğesinden sonraki bir yorum veya başka öğe içermeyen bir dizideki yorum.
- Yakalanan düğümlere bağlı yorumlar korunur, ancak kendi satırlarına taşınır; bu da tek satırlık bir ifadeyi birkaç satıra dönüştürebilir.
- Eşleşme içindeki satır sonları korunmaz: bu tür yorumlar içermeyen çok satırlı bir
array(...), tek satırlık bir[...]hâline gelir. - Değişmezlerin yazımı normalleştirilir:
0x1F,0x1folur;1_000,1000olur;"\x41"gibi kaçış dizileri karakter olarak ("A") yazılır ve esnek heredoc'ların girintisi kaldırılır.
// before
$d = array(
"a" => 1, // trailing comment
);
// after -p 'array($$$ITEMS)' -r '[$$$ITEMS]'
$d = ["a" => 1];
Yorumlar veya elle biçimlendirilmiş değişmezler içeren eşleşmeler için --dry-run çıktısını inceleyin.
Çok satırlı ve iç içe eşleşmeler
Çok satırlı desenler, tek satırlı desenlerle aynı şekilde çalışır. Yerine koyma, üst değişkenler eklenmiş olarak biçimlendirici tarafından oluşturulur; dolayısıyla değiştirilen kodun düzeni özgün eşleşmeden değil, biçimlendiriciden gelir.
Greph her dosyayı tek geçişte yeniden yazar ve çakışan eşleşmelerin ikisini birden hiçbir zaman uygulamaz. Eşleşmeleri dosyanın sonundan geriye doğru tarar ve daha önce yeniden yazdığı bir eşleşmeyle çakışanları atlar. Farklı konumlarda başlayan iç içe eşleşmelerde içteki eşleşme yeniden yazılır, dıştaki ise olduğu gibi bırakılır:
// before
$b = array(1, array(2));
// after one run of -p 'array($$$ITEMS)' -r '[$$$ITEMS]'
$b = array(1, [2]);
// after a second run
$b = [1, [2]];
Aynı konumda başlayan iki eşleşme varsa (örneğin $obj->$method($$$ARGS) ile eşleşen bir yöntem zinciri), dıştaki eşleşme yeniden yazılır. Bir desen iç içe eşleşebiliyorsa, --dry-run hiçbir şey yazdırmayana kadar yeniden yazma işlemini çalıştırın (#3). ast-grep, iç içe iki eşleşmeden dıştakini yeniden yazar; bu nedenle vendor/bin/sg ile yukarı akıştaki sg burada farklı sonuçlar üretir.
Programatik kullanım
use Greph\Greph;
use Greph\Ast\AstSearchOptions;
$rewrites = Greph::rewriteAst(
'array($$$ITEMS)',
'[$$$ITEMS]',
'src',
new AstSearchOptions(jobs: 4),
);
foreach ($rewrites as $result) {
if (!$result->changed()) {
continue;
}
echo "{$result->file}: {$result->replacementCount} replacement(s)\n";
file_put_contents($result->file, $result->rewrittenContents);
}
Greph::rewriteAst() dosyaları kendisi yazmaz. Değişiklikleri kaydetmekten (veya --dry-run ile reddedilen --interactive durumlarında atlamaktan) CLI sorumludur. Böylece bu cephe, değişiklikleri uygulamadan önce farkı incelemek isteyen otomatik ajanlar tarafından güvenle çağrılabilir.
RewriteResult şunları sunar:
file: mutlak yoloriginalContents: yeniden yazma öncesindeki dosya içeriğirewrittenContents: yeniden yazma sonrasındaki dosya içeriğireplacementCount: yapısal değişiklik sayısıchanged():originalContents !== rewrittenContentsolduğundatruedöndüren yardımcı
Yeniden yazma ne zaman kullanılmalı
Yeniden yazma şu durumlarda doğru araçtır:
- Dönüştürme yerel ve yapısalsa (dosyanın tamamını yeniden düzenlemiyorsa).
- Hiçbir şey değişmeyene kadar yeniden çalıştırabileceğiniz, belirleyici ve tekrarlanabilir bir yeniden düzenleme istiyorsanız.
- Uygulamadan önce değişikliği önizlemek istiyorsanız.
- Yeniden düzenlemeyi harici bir ikili dosya çağırmadan bir betiğe veya CI işine entegre etmek istiyorsanız.
Yeniden yazma şu durumlarda doğru araç değildir:
- Dönüştürme, PHP-Parser'ün tek bir dosyadan çözemeyeceği tür bilgilerine bağlıysa.
- Dönüştürmenin dosya sınırları arasında içe aktarmalar eklemesi veya kaldırması gerekiyorsa.
- Yeniden düzenleme yerine bir biçimlendirici istiyorsanız (PHP-CS-Fixer kullanın).