Pāriet uz saturu

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

Apvalks
# 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: 0x1F kļūst par 0x1f, 1_000 kļūst par 1000, escape secības, piemēram, "\x41", tiek izrakstītas kā rakstzīme ("A"), un elastīgās heredoc konstrukcijas zaudē atkāpi.
PHP
// 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:

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

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ā

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() 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īšanas
  • rewrittenContents: faila saturs pēc pārrakstīšanas
  • replacementCount: strukturālo aizstāšanu skaits
  • changed(): palīgfunkcija, kas atgriež true, ja originalContents !== 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).