Liigu sisu juurde

AST-i ümberkirjutamine

AST-i ümberkirjutamine on AST-otsing asendusmalliga. Greph kasutab sama sobitajat nagu AST-otsing, seejärel põimib ümberkirjutatud malli lähtekoodi tagasi sobitatud baidivahemikesse. Ümbritsevad tühikud, taanded ja kommentaarid säilivad; sobitatud kood prinditakse uuesti (vt Sobitatud lõigu sees).

Kiired näited

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

Asendusmallid

Asendusmall kasutab sama metamärkide süntaksit mis otsingumuster. Hõivatud sõlmed põimitakse nime järgi tagasi:

Mustri sees Mallis Tulemus
$X $X Lisa hõivatud sõlm
$$$ARGS $$$ARGS Lisa hõivatud sõlmede jada
$NAME (identifikaator) $NAME Lisa hõivatud identifikaator

Metamärgid, mis esinevad mustris, kuid mitte mallis, eemaldatakse ümberkirjutatud koodist. Mallis esinevat metamärki, mida mustris pole, praegu tagasi ei lükata: see prinditakse sõna-sõnalt, nii et -r '$o->$m($$$A, $EXTRA)' kirjutab koodi muutuja $EXTRA (#4). Kontrollige malle esmalt valikuga --dry-run.

Režiimid

Lipp Käitumine
(puudub) Rakenda ümberkirjutus kõigile sobivatele failidele
--dry-run Prindi ümberkirjutatud faili sisu standardväljundisse, ära kirjuta faili
--interactive Küsi enne iga faili kirjutamist Rewrite <file>? [y/N] käest

Vaikimisi kirjutatakse kõik muutunud failid üle. Kasutage mustri täpsustamisel esmalt --dry-run ning eemaldage seejärel lipp, kui erinevus sobib.

Vormingu säilitamine

Greph ei vorminda faili ümber. Sobitaja salvestab iga vaste baidivahemiku algses lähtekoodis ning ümberkirjutaja põimib renderdatud malli otse nendesse vahemikesse. Puutumata kood jääb täpselt samaks, sealhulgas:

  • Taanded ja lõputühikud
  • Reasisesed ja eraldi kommentaarid
  • Tühjade ridade paigutus
  • Stringide jutumärkide stiil ja ühendamine
  • PHP avamis- ja sulgemissiltide stiil

See on teadlik kompromiss täieliku ümbervormindamise arvelt: Greph ei korrasta koodi, millele vaste ei leitud, kuid ei tekita ka seosetute ridade kohta mürarikkaid muudatusi.

Sobitatud lõigu sees

Sobitatud vahemik asendatakse malliga, mis renderdatakse läbi PHP-Parser ilusa printeri; hõivatud sõlmed prinditakse uuesti, mitte ei kopeerita lähtekoodist. Praegu tähendab see järgmist (#2):

  • Kommentaar, mis pole sobitatud lõigus seotud järgneva sõlmega, eemaldatakse; näiteks massiivi viimase elemendi järel olev kommentaar või muidu tühjas massiivis olev kommentaar.
  • Hõivatud sõlmedega seotud kommentaarid säilivad, kuid liiguvad omaette reale; nii võib üherealine avaldis muutuda mitmerealiseks.
  • Reavahetused sobitatud lõigus ei säili: mitmerealisest array(...)-st, milles pole selliseid kommentaare, saab üherealine [...].
  • Literaalide kirjapilt ühtlustatakse: 0x1F muutub kujule 0x1f, 1_000 muutub kujule 1000, paojadad nagu "\x41" kirjutatakse märgina välja ("A") ning paindliku taandega heredoc'id kaotavad oma taande.
PHP
// before
$d = array(
    "a" => 1,  // trailing comment
);

// after -p 'array($$$ITEMS)' -r '[$$$ITEMS]'
$d = ["a" => 1];

Vaadake üle --dry-run väljund, kui vasted sisaldavad kommentaare või käsitsi vormindatud literaale.

Mitmerealised ja pesastatud vasted

Mitmerealised mustrid toimivad samamoodi nagu üherealised. Asenduse renderdab ilus printer, kuhu metamärgid on asendatud, seega tuleb asendatud koodi paigutus printerist, mitte algsest vastest.

Greph kirjutab iga faili ümber ühe läbimisega ega rakenda kunagi kaht kattuvat vastet. See läbib vasteid faili lõpust alguse poole ja jätab vahele iga vaste, mis kattub juba ümberkirjutatuga. Erinevatest nihetest algavate pesastatud vastete korral kirjutatakse sisemine vaste ümber, välimine aga jäetakse muutmata:

PHP
// 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]];

Kui kaks vastet algavad samast nihkest (näiteks meetodite ahel, mille sobitab $obj->$method($$$ARGS)), kirjutatakse ümber välimine vaste. Kui muster võib pesastuda, käivitage ümberkirjutus uuesti, kuni --dry-run ei väljasta midagi (#3). ast-grep kirjutab kahest pesastatud vastest ümber välimise, seega annavad vendor/bin/sg ja ülesvoolu sg siin erinevad tulemused.

Programmipõhine kasutus

PHP
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() ise faile ei kirjuta. Muudatuste kinnitamise (või nende vahelejätmise --dry-run ja tagasilükatud --interactive juhtudel) eest vastutab CLI. Nii saab fassaadi turvaliselt kutsuda automatiseeritud agentidest, kes soovivad enne rakendamist muudatuste erinevust kontrollida.

RewriteResult pakub:

  • file: absoluutne tee
  • originalContents: faili sisu enne ümberkirjutamist
  • rewrittenContents: faili sisu pärast ümberkirjutamist
  • replacementCount: struktuursete asenduste arv
  • changed(): abifunktsioon, mis tagastab true, kui originalContents !== rewrittenContents

Millal ümberkirjutamist kasutada

Rewrite on õige tööriist, kui:

  • Teisendus on lokaalne ja struktuurne (mitte kogu faili ümberkorraldamine).
  • Soovite deterministlikku ja korratavat refaktoreerimist, mida saab uuesti käivitada, kuni muudatusi enam ei toimu.
  • Soovite muudatust enne rakendamist eelvaates näha.
  • Soovite integreerida refaktoreerimise skripti või CI-töösse ilma välist binaarfaili käivitamata.

Rewrite pole õige tööriist, kui:

  • Teisendus sõltub tüübiteabest, mida PHP-Parser ei saa ühe faili põhjal tuvastada.
  • Teisendus peab faile ületades importimisi lisama või eemaldama.
  • Soovite refaktoreerimise asemel vormindajat (kasutage PHP-CS-Fixer).