Рефакторирање на наследен PHP: прагматичен пат кон модерна одржливост
Застарениот PHP ретко откажува затоа што е стар. Откажува затоа што кодот престанал јасно да ги изразува своите намери.
Познатата апликација сè уште може да поддржува вредни работни процеси, добро да ги познава своите клиенти и да содржи години тешко стекнати деловни правила. Нејзиното целосно препишување може да биде скапо, ризично и изненадувачки ефикасно во повторното создавање стари грешки. Попрагматичната цел е промената да се направи побезбедна, по една намерно поставена граница во даден момент.
Современата одржливост не е значка за миграција на рамка. Таа е способност да се разбере барање, да се направи мала промена, да се потврди и да се распореди без коцкање со скриени несакани ефекти.
Започнете со намалување на неизвесноста
Пред да ја промените архитектурата, утврдете што навистина прави апликацијата и како моментално се испорачува. Застарените системи често имаат недокументирани cron задачи, webhook-и, процедури во базата на податоци, зависности од датотечниот систем и конфигурациски вредности што се поважни отколку што сугерира структурата на директориумите.
Направете едноставна мапа на системот. Идентификувајте ги влезните точки, клучните табели, интеграциите, закажаната работа, чекорите за распоредување и патеките во кодот што ги поддржуваат најважните деловни операции. Ова не е бирократија; тоа е начин да избегнете „средување“ на единствениот услов што штити критичен граничен случај.
Потоа создадете повторлива локална околина. Docker е корисен тука бидејќи ги претвора претпоставките за PHP екстензии, верзии на базата на податоци и конфигурација на веб-серверот во видлива конфигурација.
services:
app:
build: .
volumes:
- ./:/var/www/html
ports:
- "8080:80"
environment:
APP_ENV: development
DB_HOST: db
db:
image: mysql:8
environment:
MYSQL_DATABASE: app
MYSQL_ROOT_PASSWORD: local-root-password
ports:
- "3306:3306"
Точните слики и поставки мора да одговараат на вистинските барања на апликацијата. Поентата не е веднаш да се наметне современ стек. Таа е тековниот стек да стане репродуцибилен пред да се обидете да го развивате.
Создадете заштитна мрежа пред да поместувате ѕидови
Тестовите се највредни таму каде што промената е веројатна, а грешките се скапи. Застарената кодна база може да има малку или воопшто да нема автоматизирани тестови, затоа започнете со карактеризациски тестови: тестови што го бележат тековното однесување, вклучително и однесувањето што изгледа незграпно.
На пример, ако функцијата за вкупниот износ на нарачка применува попусти по нејасен редослед, најпрво забележете ги нејзините резултати за репрезентативни влезови. Дури откако тоа однесување ќе биде видливо, треба да одлучите дали е правилно. Ова го раздвојува рефакторирањето од промените во политиката, што е еден од најважните начини да се задржи под контрола напорот за модернизација.
- Прво заштитете ги приходите, автентикацијата, дозволите, наплатата и увозите на податоци.
- Тестирајте ги јавните HTTP одговори и договорите за интеграција на нивните граници.
- Додавајте насочени единечни тестови додека кодот се извлекува во помали компоненти.
- Користете околина за тестирање или внимателно ограничени проверки во продукција за да го потврдите однесувањето при распоредување.
Статичката анализа исто така може да открие проблеми што рачната проверка ги пропушта: вредности што можат да бидат null, неточни повратни типови, недостижни гранки и неконзистентни облици на низи. Воведувајте ја постепено. Базната линија може да спречи постојните наоди да го блокираат усвојувањето, притоа осигурувајќи дека новоизменетиот код не додава нови.
Раздвојувајте одговорности, не сè одеднаш
Класичната застарена PHP-датотека често меша обработка на барања, авторизација, SQL, деловни одлуки, HTML-излез и обработка на грешки. Нејзината замена во едно големо барање за спојување создава премногу подвижни делови. Наместо тоа, извлечете една одговорност зад мал интерфејс.
Размислете за скрипта слична на контролер што пресметува и зачувува фактура. Првото корисно извлекување често е сервис што го изразува деловното дејство, додека репозиториумот го поседува пристапот до базата на податоци.
final class InvoiceService
{
public function __construct(
private InvoiceRepository $invoices,
private TaxCalculator $taxes,
) {
}
public function create(CreateInvoice $command): Invoice
{
$subtotal = $command->subtotal();
$tax = $this->taxes->calculate($subtotal, $command->taxRate());
$invoice = Invoice::open(
$command->customerId(),
$subtotal,
$tax
);
$this->invoices->save($invoice);
return $invoice;
}
}
Ова не бара усвојување на секоја функција од целосна рамка. Тоа му дава на кодот корисна точка на раздвојување: сервисот може да се тестира без HTTP барање или активна база на податоци, а репозиториумот подоцна може да ја смени својата имплементација на барања без да го препишува деловното правило.
Чувајте ги деталите за рамката и инфраструктурата блиску до рабовите. Објектите за барања, рачките за базата на податоци, клиентите за кеш и испраќачите на пошта не треба незабележливо да се шират низ секоја доменска класа. Експлицитните зависности полесно се заменуваат, тестираат и разбираат.
Внимателно модернизирајте го договорот со базата на податоци
Кодот за базата на податоци често е местото каде што PHP апликацијата ја носи својата најтрајна сложеност. Заменете го SQL составен со конкатенација на низи со параметризирани барања, но не го мешајте тоа суштинско безбедносно подобрување со целосен редизајн на пристапот до податоци.
Правете ги промените во шемата адитивни кога е можно. Додајте колона што дозволува null, напишете код што ги обработува и старите и новите податоци, пополнете ги податоците наназад во контролиран процес, па потоа заострете ги ограничувањата само откако апликацијата и податоците ќе бидат подготвени. Овој пристап поддржува побезбедно распоредување и враќање назад отколку миграција што бара секој сервер да се префрли во истиот миг.
Трансакциите заслужуваат иста грижа. Трансакцијата треба да опфаќа една кохерентна промена на состојбата, како создавање фактура и нејзините ставки. Не треба да обвиткува бавни мрежни повици. Ако мора да се извести надворешен API, зачувајте настан или работна ставка во истата трансакција на базата на податоци и обработете ја одделно со заштити за повторување и идемпотентност.
Направете ги патеките на неуспех експлицитни
Застарениот код често го третира неуспехот како исклучителен детал сè додека не истече времето за поврзување или оддалечена услуга не врати неочекуван одговор. Современиот код треба да прави разлика меѓу грешки при валидација, очекувани оперативни неуспеси и вистински дефекти.
За интеграција што може повторно да се обиде, забележете доволно информации за безбедно повторување, користете ограничен број обиди и осигурете се дека повторено барање не може да создаде дупликатни несакани ефекти. Циклус за повторување без идемпотентност едноставно претвора привремен дефект во дуплирана работа.
Евидентирајте ги неуспесите со значаен контекст, но избегнувајте да ставате тајни, токени, лозинки или непотребни лични податоци во дневниците. Јасен идентификатор на грешката и ID на релевантниот деловен запис обично се покорисни од целосен исфрлен запис на барањето.
Подобрувајте го процесот на испорака во мали чекори
Рефакторирањето е нецелосно ако распоредувањето остане мистериозно. Воспоставете предвидлив редослед: инсталирајте зависности, извршете тестови и статички проверки, изградете или потврдете го артефактот подготвен за распоредување, намерно применете миграции и потврдете го здравјето на апликацијата по објавувањето.
Конфигурацијата припаѓа надвор од изворниот код. Вредностите специфични за околината, како акредитиви, крајни точки на услуги и ознаки за функции, треба да ги обезбеди околината за извршување или безбеден конфигурациски систем. Потврдете ја задолжителната конфигурација при стартување, така што недостасувачката вредност ќе откаже рано и јасно, наместо за време на барање од клиент.
Ознаките за функции можат да го намалат ризикот кога промените во однесувањето се значајни. Тие не се замена за тестирање и треба да имаат сопственик и план за отстранување. Трајните ознаки стануваат друг вид застарен код.
Мерете го напредокот според цената на промената
Поздрав PHP систем не е нужно оној со најновата синтакса или најмногу зависности. Тој е оној во кој развивачот може да лоцира правило, да ги разбере неговите влезови, да го промени со сигурност и да го набљудува резултатот.
Рефакторирањето на застарен PHP е дисциплина на зачувување на вредноста додека се намалува двосмисленоста. Додајте тест околу ризично однесување. Извлечете една граница. Направете еден чекор на распоредување повторлив. Отстранете една скриена зависност. Тие промени се надоврзуваат, и на крај кодната база престанува да изгледа како проблем со наследство и почнува да се однесува како систем што тимот може сигурно да го подобрува.