ИТ развој

PHP Refactoring: Navigating the Maze of Legacy Code with Confidence

PHP рефакторирање: Самоуверено снаоѓање низ лавиринтот на наследениот код

Застарениот PHP ретко се најавува со еден единствен катастрофален проблем. Почесто, наликува на лавиринт: контролер што истовремено валидира влезни податоци и запишува SQL, помошна функција со скриена глобална состојба, барање кон базата на податоци копирано на четири места и процес на распоредување што никој не сака да го допре. Апликацијата можеби сè уште си ја оправдува вредноста, токму затоа нејзиното рефакторирање бара внимателност, а не дрскост.

Целта не е стариот код да изгледа модерно. Целта е промените да бидат побезбедни, однесувањето појасно, а неуспесите полесни за дијагностицирање. Доброто рефакторирање ја штити вредноста веќе вградена во функционален систем, притоа создавајќи простор за следната функционалност, миграција или оперативно подобрување.

Почнете со однесувањето, а не со архитектурата

Најскапата грешка при рефакторирање е да се промени премногу пред да се разбере што навистина прави системот. Застарениот код често содржи незграпна логика бидејќи кодира деловно правило што никаде на друго место не било документирано. Изненадувачки услов може да биде дефект, но може да биде и единствената заштита од необичен случај на наплата или особеност на API од трета страна.

Пред да преместувате класи или да воведувате нов архитектонски слој, мапирајте еден значаен работен тек од почеток до крај. На пример: HTTP барање влегува во контролер, податоците од барањето се нормализираат, услуга пресметува резултат, записите се вчитуваат или ажурираат и се враќа одговор. Следете ги податоците, вклучително и патеките на грешки.

Создадете мала заштитна мрежа околу однесувањето што имате намера да го промените. Во PHP апликација, ова може да започне со фокусирани тестови на местото што најлесно можете да го достигнете:

public function testInvoiceIsRejectedWhenCustomerIsInactive(): void
{
    $customer = new Customer(id: 42, active: false);
    $service = new InvoiceService($this->repository);

    $result = $service->create($customer, 1500);

    self::assertFalse($result->isAccepted());
    self::assertSame('customer_inactive', $result->reason());
}

Ова не значи дека секој застарен систем може целосно да се тестира пред да започне работата. Тоа е практична почетна точка: опишете го однесувањето најблиску до вашата промена, а потоа зачувајте го додека го подобрувате дизајнот.

Пронајдете ги границите што овозможуваат промени

Граница е место каде што можете да измените еден дел од системот без да преработите сè околу него. Во PHP, вообичаени граници вклучуваат граница на репозиториум околу пристапот до базата на податоци, обвивка за клиент околу надворешен API или услуга извлечена од голем контролер.

Разгледајте контролер што врши валидација, пресметува вкупна сума на нарачка, внесува редови и испраќа е-пошта. Ризично е одеднаш да се замени со целосно нова архитектура. Побезбеден чекор е да се извлече една одговорност, притоа задржувајќи го постојното однесување на барањето и одговорот недопрено.

final class OrderController
{
    public function store(Request $request): Response
    {
        $order = $this->orderCreator->create(
            $request->input('customer_id'),
            $request->input('items', [])
        );

        return Response::json(['id' => $order->id()], 201);
    }
}

Извлечениот OrderCreator потоа може да ја преземе деловната операција. Неговите зависности стануваат експлицитни: репозиториум за нарачки, проверувач на залихи, граница на трансакција и испраќач на известувања. Таа јасност е повредна од импресивна структура на директориуми.

Претпочитајте тесни интерфејси

Не градете генеричка апстракција само затоа што апстракцијата звучи чисто. Ако на една услуга ѝ треба само да вчита и зачува нарачка, фокусиран интерфејс е полесен за разбирање и тестирање од репозиториум со десетици неповрзани методи.

  • Изложете операции што одговараат на деловната потреба.
  • Кога е можно, задржете ги објектите за барања од рамката и ORM моделите на работ на апликацијата.
  • Предавајте едноставни вредности или објекти насочени кон доменот во основната логика.
  • Воведете нов слој само кога отстранува вистинска поврзаност или ја појаснува одговорноста.

Расплетете го пристапот до податоци намерно

Кодот за базата на податоци често е местото каде што застарениот PHP станува кревок. SQL може да биде расфрлан низ шаблони, контролери, cron скрипти и помошни функции. Тоа ги прави работата на перформансите, промените во шемата и управувањето со трансакции непотребно опасни.

Централизирањето на секое барање во една класа за „пристап до податоци“ не е автоматски подобро. Групирајте ги барањата според делот од системот на кој му служат и јасно определете кој ја поседува трансакцијата. На пример, текот за создавање плаќање не треба тивко да изврши потврдување на половина од операција со повеќе чекори.

$connection->beginTransaction();

try {
    $orderRepository->save($order);
    $paymentRepository->save($payment);
    $connection->commit();
} catch (Throwable $exception) {
    $connection->rollBack();
    throw $exception;
}

Рефакторирањето на оваа област создава и можност да се испита обликот на барањата. Внимавајте на повторени барања внатре во циклуси, неограничени множества резултати и колони што се преземаат, но никогаш не се користат. Решавањето на овие проблеми може да ја подобри одзивноста без прерано додавање кешови или преработување на слојот за базата на податоци.

Бидете внимателни со повторните обиди. Повторувањето на неуспешно читање може да биде разумно кога зависноста и видот на неуспех го оправдуваат тоа. Повторувањето на запишување е поинакво: нарачка, наплата или порака може да се дуплира, освен ако операцијата е дизајнирана да биде идемпотентна. Доверливоста не е едноставно „обиди се повторно“; таа е знаење дали повторувањето на дејството е безбедно.

Направете ги зависностите видливи

Скриените зависности се главен извор на изненадувања во постар PHP код. Статички локатори на услуги, глобални низи за конфигурација, директни повици до new PDO() и читања од околината длабоко во деловната логика ги отежнуваат тестовите и го прават производното однесување помалку предвидливо.

Инјектирањето зависности не бара сложен контејнер за да биде корисно. Само инјектирањето преку конструктор ги прави потребите на класата очигледни:

final class ExchangeRateService
{
    public function __construct(
        private RateProvider $provider,
        private Logger $logger
    ) {
    }
}

Ова исто така го подобрува справувањето со неуспеси. Повиканиот код може да одлучи како да го конструира вистинскиот добавувач во продукција и контролирана замена во тестовите. На границата, евидентирајте корисен контекст без да откривате акредитиви, токени или лични податоци. Внатрешно, зачувајте го оригиналниот исклучок кога помага во дијагностицирањето, но вратете стабилни, намерни одговори за грешка до корисниците на API.

Рефакторирајте ја испораката заедно со кодот

Кодна база не е одржлива ако програмерите не можат доследно да ја извршуваат. Docker може да го намали отстапувањето „работи на мојата машина“ кога сликата, конфигурацијата за извршување и инсталацијата на зависности се експлицитни. Важна е повторливоста, а не контејнеризацијата сама по себе.

Чувајте ја конфигурацијата на апликацијата надвор од сликата кога таа варира по околина, проверувајте ги задолжителните поставки при стартување и избегнувајте вградување тајни во контролата на изворниот код или во слоевите на контејнерот. Ако распоредувањето менува PHP екстензии, работнички процеси или променливи на околината, третирајте го тоа како дел од истата промена како и кодот што зависи од него.

Распоредувањето треба да биде досадно: изградете познат артефакт, извршете автоматизирани проверки соодветни на ризикот, применете миграции на базата на податоци со план што зема предвид враќање назад и проверете ја состојбата на активната услуга. Промените во шемата заслужуваат особена внимателност. Дополнителните промени обично полесно се распоредуваат безбедно од промените што веднаш отстрануваат или преименуваат полиња што сè уште ги користат постари инстанци на апликацијата.

Изберете напредок наместо чистота

Не на секоја застарена класа ѝ е потребна итна поправка. Дајте приоритет на кодот што често се менува, предизвикува инциденти, ја блокира испораката, обработува чувствителни податоци или се наоѓа на критична патека на барање. Оставете го стабилниот, изолиран код на мир додека не постои конкретна причина да го допрете.

Малите рефакторирања се насобираат. Појаснето име, отстрането дуплирано барање, фокусиран тест и чиста граница околу една надворешна услуга можеби нема да изгледаат драматично во барање за спојување. Со текот на времето, тие ја преобразуваат цената на промената.

Довербата во застарениот код не доаѓа од преправањето дека лавиринтот е едноставен. Таа доаѓа од осветлување на еден ходник во исто време, означување на она што го учите и оставање на секој дел побезбеден отколку што сте го нашле.

Тоа е трајната дисциплина зад PHP рефакторирањето: зачувајте го однесувањето, создадете граници, направете ги зависностите и протокот на податоци видливи и подобрете ја патеката на испорака со истата внимателност како и кодот. Резултатот не е само почист PHP. Тоа е систем што вашиот тим може доволно добро да го разбере за да го развива.

Портрет на автор на блогот

Mihajlo

Јас сум Михајло - развивач поттикнат од љубопитност, дисциплина и постојаната желба да создадам нешто значајно. Споделувам увиди, упатства и бесплатни услуги за да им помогнам на другите да ја поедностават својата работа и да растат во постојано развивачкиот свет на софтверот и вештачката интелигенција.