Prepísať, alebo migrovať?
Prvá otázka nie je technická. Prepis od nuly znie lákavo — nový kód, moderný framework, žiadne dedičstvo. V praxi je to najrizikovejšia možnosť. Stará aplikácia obsahuje roky nahromadených výnimiek, opráv a špeciálnych prípadov, ktoré nie sú nikde zdokumentované. Pri prepise sa strácajú a objavia sa až v produkcii, jedna po druhej.
Migrácia je naopak postupná a merateľná. Po každom kroku máte fungujúcu aplikáciu a viete sa vrátiť späť. Prepis odporúčam len vtedy, keď je splnená aspoň jedna z týchto podmienok:
- Aplikácia už nerobí to, čo firma potrebuje, a zmeny by boli väčšie než pôvodný rozsah
- Kód je natoľko previazaný, že akákoľvek zmena rozbije tri iné miesta
- Aplikácia obsahuje bezpečnostné problémy v samotnom návrhu, nie v jednotlivých riadkoch
Vo všetkých ostatných prípadoch je migrácia rýchlejšia, lacnejšia a bezpečnejšia.
Krok 1: zistite, s čím pracujete
Pred prvou zmenou kódu potrebujete obraz o rozsahu. Bez neho neviete odhadnúť čas ani riziko.
Automatická kontrola kompatibility
Nástroj PHPCompatibility prejde kód a vypíše konštrukcie, ktoré v cieľovej verzii nefungujú:
composer require --dev phpcompatibility/php-compatibility squizlabs/php_codesniffer
./vendor/bin/phpcs -p . \
--standard=PHPCompatibility \
--runtime-set testVersion 8.4 \
--extensions=php \
--ignore=vendor/,storage/ \
--report=summary
Výstup je zoznam súborov s počtom chýb. Nepanikárte z vysokých čísel — veľká časť bývajú opakovania tej istej veci, ktoré opraví jeden nástroj hromadne.
Inventúra rozšírení a závislostí
# ktoré rozšírenia kód používa
grep -rhoE "\b(mysql_|ereg|mcrypt_|each\()" --include="*.php" . | sort | uniq -c
# aké externé knižnice sú v projekte
ls vendor/ 2>/dev/null || find . -name "*.php" -path "*lib*" -maxdepth 3
Staré projekty často nemajú Composer vôbec — knižnice sú skopírované do adresára a upravené. Každá takáto knižnica je samostatná položka v pláne: buď má modernú verziu na Packagiste, alebo ju treba nahradiť.
Zmapujte vstupné body
Pri aplikáciách bez smerovača je vstupným bodom každý PHP súbor dostupný cez web. Ich zoznam je zároveň zoznamom toho, čo treba otestovať.
Krok 2: záchranná sieť pred zmenami
Starý projekt spravidla nemá testy. Písať kompletné pokrytie pred migráciou je neúmerné — ale úplne bez siete sa migrovať nedá. Rozumný kompromis je zachytiť správanie zvonku.
Vyberte dvadsať najdôležitejších URL, zavolajte ich na súčasnej verzii a výstup si uložte. Po migrácii porovnáte:
#!/bin/bash
# zachyt-vystupy.sh — pred migráciou
while read -r cesta; do
nazov=$(echo "$cesta" | tr '/?=&' '_')
curl -s "https://stara-verzia.sk${cesta}" > "vystupy/${nazov}.html"
done < zoznam-url.txt
Po migrácii rovnaký skript spustíte proti novej verzii a porovnáte cez diff. Rozdiely v dynamických častiach (čas, tokeny) odfiltrujete, zvyšok sú kandidáti na chybu. Táto jednoduchá metóda zachytí prekvapivo veľkú časť regresií.
Na kritické biznis toky — objednávka, platba, prihlásenie — napíšte skutočné testy. Investícia niekoľkých dní sa vráti pri prvom nasadení.
Krok 3: migrujte po verziách, nie skokom
Najčastejšia chyba je zmeniť PHP z 5.6 rovno na 8.4 a riešiť lavínu chýb naraz. Postupujte po major verziách: 5.6 → 7.0 → 7.4 → 8.0 → 8.4. Po každom kroku aplikácia beží a viete, ktorá zmena čo spôsobila.
Najbolestivejší skok: 5.6 na 7.0
- Odstránené rozšírenie
mysql_*— najčastejšia a najrozsiahlejšia zmena v starých projektoch. Prepis na PDO alebomysqlis viazanými parametrami. Pri tejto príležitosti odstránite aj SQL injection, ktoré tam takmer isto je. - Konštruktory v štýle PHP 4 (metóda s názvom triedy) prestali fungovať — nahradí ich
__construct(). - Funkcie
ereg_*asplit()— náhradou súpreg_*. - Rozšírenie
mcrypt— nahrádza hoopensslalebo Sodium. Pozor: ak sú ním zašifrované uložené dáta, potrebujete prechodné obdobie s dešifrovaním starým a šifrovaním novým spôsobom.
<?php
// pôvodne
$vysledok = mysql_query("SELECT * FROM pouzivatelia WHERE email = '$email'");
$riadok = mysql_fetch_assoc($vysledok);
// po migrácii
$prikaz = $pdo->prepare('SELECT * FROM pouzivatelia WHERE email = :email');
$prikaz->execute(['email' => $email]);
$riadok = $prikaz->fetch(PDO::FETCH_ASSOC);
Skok 7.4 na 8.0
- Prísnejšie porovnávanie reťazca a čísla —
0 == 'text'už nie je pravda. V starom kóde to môže ticho prevrátiť vyhodnotenie podmienok. Toto je zmena, ktorú žiadny nástroj spoľahlivo neodhalí a práve preto potrebujete porovnanie výstupov. - Chyby namiesto varovaní — viaceré situácie, ktoré predtým vypísali varovanie a pokračovali, teraz vyhodia výnimku.
- Povinné parametre po voliteľných a ďalšie sprísnenia signatúr.
Rector na hromadné úpravy
Veľkú časť mechanických zmien zvládne Rector automaticky. Nastavte cieľovú verziu a púšťajte ho po dávkach:
<?php
// rector.php
use Rector\Config\RectorConfig;
return RectorConfig::configure()
->withPaths([__DIR__ . '/app', __DIR__ . '/src'])
->withPhpSets(php74: true) // v ďalšom kroku php80, php84
->withPreparedSets(deadCode: true);
./vendor/bin/rector process --dry-run # najprv náhľad
./vendor/bin/rector process
Rector neopraví všetko a občas sa mýli. Každú dávku commitnite samostatne a prejdite zmeny okom, hlavne pri kóde, ktorý pracuje s typmi.
Krok 4: prostredie a knižnice
Zmena verzie PHP je len časť práce. Spolu s ňou zvyčajne treba riešiť aj okolie:
- Databáza — stará MySQL má iné predvolené nastavenie prísneho režimu. Dotazy, ktoré predtým prešli, môžu skončiť chybou. Overte hlavne prácu s dátumami a zoskupovaním.
- Kódovanie — pri prechode na
utf8mb4si dajte pozor na dĺžky indexovaných stĺpcov. - Webový server — prechod z Apache s
mod_phpna Nginx s PHP-FPM znamená preložiť pravidlá z.htaccessdo konfigurácie servera. - Knižnice — skopírované a upravené knižnice nahraďte balíkmi cez Composer. Ak bola knižnica upravená, úpravu vyčleňte do vlastnej triedy, ktorá pôvodnú rozširuje.
- Session a cache — overte, kde sa ukladajú a či to v novom prostredí funguje rovnako.
Krok 5: nasadenie po častiach
Migrovanú verziu nenasadzujte naraz pre všetkých. Osvedčený postup má tri fázy:
- Paralelná prevádzka — nová verzia beží na inej doméne alebo porte s kópiou databázy. Vy a klient ju používate niekoľko dní.
- Obmedzené spustenie — časť prevádzky (interní používatelia, malé percento návštevníkov) ide na novú verziu, zvyšok na starú. Porovnávate chybovosť a časy odpovedí.
- Prepnutie — celá prevádzka na novú verziu, pričom stará zostáva pripravená na okamžitý návrat aspoň týždeň.
Nasadzujte v čase najnižšej prevádzky — pri slovenskom e-shope býva najpokojnejšie utorkové ráno, nie piatok popoludní. A nikdy nie deň pred dovolenkou.
Po prepnutí je kľúčový dohľad. Prvé dni sledujte chyby, časy odpovedí a fronty podstatne pozornejšie než bežne — postup popisujem v článku o monitoringu PHP aplikácie.
Čo získate okrem podpory
Migrácia sa často obhajuje len tým, že stará verzia už nedostáva bezpečnostné záplaty. To je dobrý dôvod, ale nie jediný:
- Výkon — prechod z PHP 5.6 na 8.x prináša pri typickej aplikácii výrazné zrýchlenie bez zásahu do logiky. Menej serverov pre rovnakú prevádzku je merateľná úspora.
- Udržiavateľnosť — na modernú verziu viete nájsť vývojára. Na PHP 5.6 ich je rok od roka menej a sú drahší.
- Prístup k ekosystému — moderné knižnice, platobné brány a API klienti staré verzie PHP nepodporujú.
- Bezpečnosť ako vedľajší produkt — pri prepise dotazov na viazané parametre zmiznú zraniteľnosti, o ktorých ste nevedeli. Prehľad nájdete v článku o bezpečnosti PHP aplikácií.
Zhrnutie: postup migrácie
- Rozhodnite medzi migráciou a prepisom — prepis len pri zásadných dôvodoch
- Spustite PHPCompatibility a spravte inventúru rozšírení a knižníc
- Zachyťte výstupy kľúčových URL pred akoukoľvek zmenou
- Kritické biznis toky pokryte skutočnými testami
- Migrujte po major verziách, nie jedným skokom
- Mechanické zmeny nechajte na Rector, každú dávku commitnite zvlášť
- Prepíšte
mysql_*na PDO s viazanými parametrami - Rátajte so zmenou porovnávania reťazcov a čísel v PHP 8
- Riešte aj okolie: databázu, kódovanie, webový server, knižnice
- Nasadzujte po častiach s možnosťou okamžitého návratu
- Starú verziu držte pripravenú aspoň týždeň po prepnutí