AST pārveide
AST pārveide ir AST meklēšana ar aizstāšanas veidni. Greph izmanto to pašu salīdzināšanas algoritmu, ko AST meklēšana, pēc tam ievieto pārveidoto veidni avota kodā atbilstošajos baitu apgabalos. Apkārtējās atstarpes, atkāpes un komentāri tiek saglabāti; pats atbilstošais kods tiek drukāts no jauna (skatiet Atbilstības iekšpusē).
Īsi piemēri
# 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
Aizstāšanas veidnes
Aizstāšanas veidnē tiek izmantota tā pati metavariablu sintakse, kas meklēšanas rakstā. Uztvertie mezgli tiek ievietoti atpakaļ pēc nosaukuma:
| Meklēšanas rakstā | Veidnē | Rezultāts |
|---|---|---|
$X |
$X |
Ievieto uztverto mezglu |
$$$ARGS |
$$$ARGS |
Ievieto uztverto mezglu secību |
$NAME (identifikators) |
$NAME |
Ievieto uztverto identifikatoru |
Metavariablas, kas ir meklēšanas rakstā, bet nav veidnē, tiek izlaistas pārveidotajā kodā. Metavariabla, kas ir veidnē, bet nav meklēšanas rakstā, pašlaik netiek noraidīta: tā tiek izdrukāta burtiski, tāpēc -r '$o->$m($$$A, $EXTRA)' kodā ieraksta $EXTRA mainīgo (#4). Vispirms pārbaudiet veidnes ar --dry-run.
Režīmi
| Karodziņš | Darbība |
|---|---|
| (nav) | Veic pārveidi visos failos ar atbilstību |
--dry-run |
Izvada pārveidotā faila saturu uz stdout, bet to neieraksta |
--interactive |
Pirms katra faila ierakstīšanas vaicā Rewrite <file>? [y/N] |
Pēc noklusējuma tiek „ierakstīts viss, kas mainās”. Vispirms, veidojot rakstu, izmantojiet --dry-run, bet noņemiet karodziņu, kad esat apmierināti ar atšķirībām.
Formatējuma saglabāšana
Greph nepārformatē failu. Salīdzinātājs reģistrē katras atbilstības baitu apgabalu sākotnējā avota kodā, un pārveidotājs renderēto veidni ievieto tieši šajos apgabalos. Neaizskartais kods paliek tieši tāds, kāds bija, tostarp:
- Atkāpes un atstarpes rindu beigās
- Iekļautie un atsevišķās rindās izvietotie komentāri
- Tukšo rindu izkārtojums
- Pēdiņu stils virknēs un konkatenācija
- PHP atvērto/aizvērto tagu stils
Tā ir apzināta kompromisa izvēle salīdzinājumā ar pilnu pārformatēšanu: Greph „nesakārtos” kodu, kam tas nav pieskāries, taču arī neradīs liekas atšķirības nesaistītās rindās.
Atbilstības iekšpusē
Pati atbilstošā koda daļa tiek aizstāta ar veidni, kas renderēta, izmantojot PHP-Parser skaistdrukas rīku; uztvertie mezgli tiek izdrukāti no jauna, nevis kopēti kā pirmkoda teksts. Pašlaik tas nozīmē (#2):
- Komentārs, kas atbilstības ietvaros nav piesaistīts nākamajam mezglam, tiek atmests, piemēram, noslēdzošais komentārs pēc pēdējā masīva elementa vai komentārs citādi tukšā masīvā.
- Uzņemtajiem mezgliem piesaistītie komentāri tiek saglabāti, bet pārvietoti jaunā rindā, tāpēc vienas rindas izteiksme var izvērsties vairākās rindās.
- Rindas pārtraukumi atbilstības ietvaros netiek saglabāti: vairākās rindās rakstīts
array(...)bez šādiem komentāriem kļūst par vienas rindas[...]. - Literāļu pieraksts tiek normalizēts:
0x1Fkļūst par0x1f,1_000kļūst par1000, escape secības, piemēram,"\x41", tiek izrakstītas kā rakstzīme ("A"), un elastīgās heredoc konstrukcijas zaudē atkāpi.
// before
$d = array(
"a" => 1, // trailing comment
);
// after -p 'array($$$ITEMS)' -r '[$$$ITEMS]'
$d = ["a" => 1];
Pārskatiet --dry-run izvadi, lai atrastu atbilstības, kurās ir komentāri vai manuāli formatēti literāļi.
Vairāku rindu un ligzdotas atbilstības
Vairāku rindu raksti darbojas tāpat kā vienas rindas raksti. Aizstāšanu attēlo skaistais drukātājs, ievietojot tajā metamainīgos, tāpēc aizstātā koda izkārtojumu nosaka drukātājs, nevis sākotnējā atbilstība.
Greph katru failu pārraksta vienā piegājienā un nekad nepiemēro divas pārklājošās atbilstības. Tas apstaigā atbilstības no faila beigām uz sākumu un izlaiž ikvienu atbilstību, kas pārklājas ar jau pārrakstītu. Ligzdotām atbilstībām, kas sākas dažādās pozīcijās, tiek pārrakstīta iekšējā atbilstība, bet ārējā paliek nemainīta:
// 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]];
Ja divas atbilstības sākas vienā pozīcijā (piemēram, metožu ķēde, ko atrod $obj->$method($$$ARGS)), tā vietā tiek pārrakstīta ārējā atbilstība. Ja raksts var būt ligzdots, atkārtojiet pārrakstīšanu, līdz --dry-run vairs neko neizvada (#3). ast-grep pārraksta ārējo no divām ligzdotām atbilstībām, tāpēc vendor/bin/sg un augšupējais sg šeit dod atšķirīgus rezultātus.
Izmantošana programmā
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() pats failus neraksta. Par izmaiņu apstiprināšanu (vai izlaišanu --dry-run un noraidīto --interactive gadījumos) ir atbildīgs CLI. Tāpēc šo fasādi var droši izsaukt automatizēti aģenti, kas pirms izmaiņu piemērošanas vēlas pārbaudīt atšķirības.
RewriteResult nodrošina:
file: absolūtais ceļšoriginalContents: faila saturs pirms pārrakstīšanasrewrittenContents: faila saturs pēc pārrakstīšanasreplacementCount: strukturālo aizstāšanu skaitschanged(): palīgfunkcija, kas atgriežtrue, jaoriginalContents !== rewrittenContents
Kad izmantot Rewrite
Rewrite ir piemērots rīks, ja:
- Pārveidošana ir lokāla un strukturāla (nevis visa faila pārstrukturēšana).
- Vēlaties deterministisku, atkārtojamu refaktorēšanu, ko varat atkārtot, līdz nekas nemainās.
- Vēlaties pirms piemērošanas priekšskatīt izmaiņas.
- Vēlaties integrēt refaktorēšanu skriptā vai CI darbā, neizsaucot ārēju bināro failu.
Rewrite nav piemērots rīks, ja:
- Pārveidošana ir atkarīga no tipa informācijas, ko PHP-Parser nevar noteikt, analizējot vienu failu.
- Pārveidošanai ir jāpievieno vai jānoņem importi pāri failu robežām.
- Vēlaties formatētāju, nevis refaktorēšanas rīku (izmantojiet PHP-CS-Fixer).