AST-átírás
Az AST-átírás az AST-keresést helyettesítősablonnal egészíti ki. A Greph ugyanazt az illesztőt futtatja, mint az AST-keresés, majd az átírt sablont a megtalált bájttartományokon visszailleszti a forrásba. A környező szóközök, behúzások és megjegyzések megmaradnak; a megtalált kód új formátumot kap (lásd: Illesztésen belül).
Gyors példák
# 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
Helyettesítősablonok
A helyettesítősablon ugyanazt a metaváltozó-szintaxist használja, mint a keresési minta. A rögzített csomópontokat név alapján illeszti vissza:
| A mintában | A sablonban | Hatás |
|---|---|---|
$X |
$X |
A rögzített csomópont beszúrása |
$$$ARGS |
$$$ARGS |
A rögzített csomópontsorozat beszúrása |
$NAME (azonosító) |
$NAME |
A rögzített azonosító beszúrása |
A mintában szereplő, de a sablonból hiányzó metaváltozók kimaradnak az átírt kódból. A sablonban szereplő, de a mintából hiányzó metaváltozókat a rendszer jelenleg nem utasítja el: szó szerint kiírja őket, így a -r '$o->$m($$$A, $EXTRA)' egy $EXTRA változót ír a kódba (#4). Előbb a --dry-run segítségével ellenőrizze a sablonokat.
Módok
| Jelző | Viselkedés |
|---|---|
| (nincs) | Az átírás alkalmazása minden illeszkedő fájlra |
--dry-run |
Az átírt fájl tartalmának kiírása a szabványos kimenetre, írás nélkül |
--interactive |
A rendszer megerősítést kér a Rewrite <file>? [y/N] felhasználótól minden fájl írása előtt |
Az alapértelmezés: „minden módosítás kiírása”. Minta finomításakor először használja a --dry-run jelzőt, majd távolítsa el, ha elégedett a különbséggel.
Formátum megőrzése
A Greph nem formázza újra a fájlt. Az illesztő rögzíti minden találat bájttartományát az eredeti forrásban, az átíró pedig közvetlenül ezekbe a tartományokba illeszti be a megjelenített sablont. Az érintetlen kód pontosan úgy marad, ahogy volt, többek között:
- Behúzás és sorvégi szóközök
- Sorközi és önálló megjegyzések
- Üres sorok elrendezése
- Idézőjelek stílusa és összefűzés
- A PHP nyitó- és zárócímke-stílusa
Ez tudatos kompromisszum a teljes újraformázással szemben: a Greph nem „csinosítja ki” az illesztésben nem szereplő kódot, ugyanakkor nem okoz zajos különbségeket a nem érintett sorokban.
Illesztésen belül
Magát a megtalált tartományt a PHP-Parser szépségformázóján keresztül megjelenített sablon váltja fel; a rögzített csomópontok új formátumot kapnak ahelyett, hogy forrásszövegként másolódnának be. Jelenleg ez a következőket jelenti (#2):
- A megjegyzés, amely nincs a találaton belüli következő csomóponthoz rendelve, kimarad; ilyen például a tömb utolsó eleme utáni záró megjegyzés vagy egy egyébként üres tömbben lévő megjegyzés.
- A rögzített csomópontokhoz rendelt megjegyzések megmaradnak, de külön sorba kerülnek; ettől egy egysoros kifejezés több sorossá válhat.
- A találaton belüli sortörések nem maradnak meg: az ilyen megjegyzések nélküli többsoros
array(...)egysoros[...]lesz. - A literálok írásmódja egységesül: a
0x1Falakja0x1flesz, a1_000alakja1000, az olyan escape-sorozatok pedig, mint a"\x41", karakterként jelennek meg ("A"); a rugalmas heredocok elveszítik behúzásukat.
// before
$d = array(
"a" => 1, // trailing comment
);
// after -p 'array($$$ITEMS)' -r '[$$$ITEMS]'
$d = ["a" => 1];
Ellenőrizze a --dry-run kimenetet, ha a találatok megjegyzéseket vagy kézzel formázott literálokat tartalmaznak.
Többsoros és egymásba ágyazott találatok
A többsoros minták ugyanúgy működnek, mint az egysorosak. A szépségformázó a behelyettesített metaváltozókkal együtt jeleníti meg a helyettesítést, így az átírt kód elrendezését a formázó határozza meg, nem az eredeti találat.
A Greph fájlonként egy menetben végzi az átírást, és soha nem alkalmaz két egymást átfedő találatot. A találatokat a fájl végétől visszafelé járja be, és kihagyja azokat, amelyek átfednek egy már átírt találattal. Eltérő eltolásnál kezdődő, egymásba ágyazott találatoknál a belső találat íródik át, a külső pedig változatlan marad:
// 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]];
Ha két találat ugyanazon az eltoláson kezdődik (például ha egy metódusláncra a $obj->$method($$$ARGS) illeszkedik), a külső találat íródik át. Ha a minta egymásba ágyazható, ismételje meg az átírást addig, amíg a --dry-run már nem ír ki semmit (#3). A ast-grep a két egymásba ágyazott találat közül a külsőt írja át, ezért a vendor/bin/sg és a felsőbb rétegbeli sg itt eltérő eredményt ad.
Programozott használat
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);
}
A Greph::rewriteAst() maga nem ír fájlokat. A CLI felelős a módosítások véglegesítéséért (vagy kihagyásáért --dry-run és elutasított --interactive esetekben). Így a homlokzat biztonságosan hívható automatizált ügynökökből, amelyek alkalmazás előtt meg akarják vizsgálni a különbségeket.
RewriteResult a következőket teszi elérhetővé:
file: abszolút elérési útoriginalContents: a fájl tartalma az átírás előttrewrittenContents: a fájl tartalma az átírás utánreplacementCount: a szerkezeti cserék számachanged(): segédfüggvény, amelyoriginalContents !== rewrittenContentseseténtrueértéket ad vissza
Mikor használja az átírást
Az átírás a megfelelő eszköz, ha:
- Az átalakítás helyi és szerkezeti jellegű (nem az egész fájl átszervezéséről van szó).
- Determinisztikus, megismételhető refaktorálást szeretne, amelyet addig futtathat újra, amíg már nem történik változás.
- Szeretné megtekinteni a módosítást az alkalmazása előtt.
- Külső bináris meghívása nélkül szeretné beépíteni a refaktorálást egy szkriptbe vagy CI-feladatba.
Az átírás nem megfelelő eszköz, ha:
- Az átalakítás olyan típusinformációktól függ, amelyeket a PHP-Parser egyetlen fájlból nem tud feloldani.
- Az átalakításnak fájlhatáron átnyúlóan kell importokat hozzáadnia vagy eltávolítania.
- Refaktorálás helyett formázót szeretne használni (használja a PHP-CS-Fixer eszközt).