Come recuperare il controllo di un codebase PHP legacy senza documentazione: strategie operative per PMI

Come recuperare il controllo di un codebase PHP legacy senza documentazione: strategie operative per PMI

A marzo 2025 mi ha contattato il titolare di una piccola azienda piemontese - 12 dipendenti, settore distribuzione ricambi industriali - con un problema che nella mia esperienza si ripete almeno tre volte l'anno: il gestionale web dell'azienda funzionava, ma nessuno sapeva più come. Lo sviluppatore freelance che l'aveva costruito cinque anni prima era sparito da sei mesi - telefono spento, PEC non letta, partita IVA ancora attiva ma nessuna risposta. Il gestionale - un'applicazione custom in PHP 5.6 senza framework, con MySQL 5.7 su un VPS OVH - gestiva ordini, magazzino, fatturazione e listini fornitori. Tredici persone lo usavano ogni giorno. Nessuno sapeva come funzionasse internamente.

L'audit iniziale ha rivelato un quadro che per molti sysadmin sarebbe scoraggiante: 147 file PHP distribuiti in 23 directory senza logica apparente, nessun version control (deploy via FTP diretto sul server di produzione), credenziali MySQL hardcodate in 9 file diversi con la stessa password root, nessun file .env o sistema di configurazione, zero test, zero documentazione, e un php.ini con display_errors = On e error_reporting = E_ALL in produzione. Il codice usava funzioni deprecate da PHP 7 (mysql_connect, ereg, each) e mix di logica applicativa e HTML nello stesso file.

In trenta giorni ho trasformato quella situazione da "nessuno osa toccarlo" a "possiamo fare modifiche in sicurezza". In questo articolo ti racconto il metodo - lo stesso che applico a ogni subentro su codebase PHP legacy senza documentazione.

Stai cercando un Consulente Informatico esperto per riprendere il controllo di un progetto PHP legacy? Nel mio profilo professionale trovi l'esperienza concreta su subentri, refactoring e modernizzazione di applicazioni PHP 5.x-8.x. Contattami per una consulenza diretta.

Settimana 1: l'analisi forense - capire cosa hai prima di toccare qualsiasi cosa

Il primo errore che vedo fare è iniziare subito a "sistemare" il codice. È l'approccio sbagliato: senza capire cosa fa l'applicazione, qualsiasi modifica è una scommessa al buio. La prima settimana è dedicata interamente all'analisi - senza modificare nemmeno una riga.

Lo script di ricognizione che uso come punto di partenza:

# 1. Dimensione e struttura del progetto
find . -name "*.php" -type f | wc -l
find . -name "*.php" -type f | xargs wc -l | tail -1
find . -type d | head -30

# 2. Versione PHP e funzioni deprecate
grep -rn "mysql_connect\|mysql_query\|ereg\|split(" . --include="*.php" | wc -l

# 3. Credenziali hardcodate (CRITICO per sicurezza)
grep -rn "password\|passwd\|pwd\|DB_PASS\|db_pass" . --include="*.php" | grep -v "// "

# 4. Punti di ingresso (entry point dell'applicazione)
grep -rn "require\|include" index.php 2>/dev/null | head -20

# 5. Connessioni esterne (database, API, SMTP)
grep -rn "mysqli\|PDO\|curl_init\|file_get_contents.*http\|fsockopen\|mail(" . --include="*.php" | wc -l

# 6. Gestione sessioni e autenticazione
grep -rn "session_start\|\$_SESSION\|login\|auth" . --include="*.php" | wc -l

Questo script in due minuti ti dice: quanto è grande il problema (147 file, 23.000 righe nel caso piemontese), quanto è obsoleto il codice (86 occorrenze di funzioni deprecate), dove sono le credenziali esposte, e quali sono le dipendenze esterne. Da questi numeri pianifichi il resto dell'intervento.

Il passo successivo è PHPStan - l'analizzatore statico che trova bug senza eseguire il codice. Su un codebase legacy senza test, PHPStan è il sostituto più vicino a una test suite che puoi avere in poche ore. La chiave è usare la baseline strategy: esegui PHPStan al livello più basso (0), generi una baseline che registra tutti gli errori esistenti, e da quel momento la CI fallisce solo su errori nuovi. Gli errori vecchi restano registrati ma non bloccano il lavoro - li correggerai progressivamente.

# Installare PHPStan
composer require --dev phpstan/phpstan

# Primo run al livello 0 (minimo, solo errori gravi)
vendor/bin/phpstan analyse src/ --level 0 --error-format=json > phpstan-report.json

# Generare la baseline (registra gli errori esistenti come "debito noto")
vendor/bin/phpstan analyse src/ --level 0 --generate-baseline
# Crea phpstan-baseline.neon con tutti gli errori attuali

# Da ora in poi, PHPStan segnala SOLO errori nuovi
vendor/bin/phpstan analyse src/ --level 0

Nel caso piemontese, il primo run di PHPStan al livello 0 ha trovato 342 errori - variabili potenzialmente undefined, classi non trovate, tipi incompatibili. La baseline li ha registrati tutti, e da quel momento ogni nuova modifica al codice veniva verificata: se introduceva errori aggiuntivi, il check falliva. È la rete di sicurezza minima per lavorare su codice senza test.

Settimana 2: implementare Git senza fermare la produzione

Il version control è il fondamento di qualsiasi lavoro serio su un codebase. Senza Git, ogni modifica in produzione è irreversibile - se qualcosa si rompe, non c'è modo di tornare indietro se non da un backup (che nel caso piemontese era un dump MySQL giornaliero, senza backup del codice). Ho descritto la procedura completa nell'articolo sull'implementazione di Git su sistemi PHP legacy in produzione, ma qui riassumo i passaggi chiave:

# 1. Backup completo PRIMA di qualsiasi operazione
tar -czf /root/backup-codice-$(date +%Y%m%d).tar.gz /var/www/html/

# 2. Inizializzare Git nella directory di produzione
cd /var/www/html
git init

# 3. Creare .gitignore PRIMA del primo commit
cat > .gitignore << 'EOF'
*.log
/tmp/
/cache/
config_local.php
.env
EOF

# 4. Primo commit: snapshot di tutto il codice esistente
git add .
git commit -m "chore: initial import of legacy codebase as-is"

# 5. Configurare un remote privato (GitLab self-hosted o GitHub private)
git remote add origin [email protected]:azienda/gestionale.git
git push -u origin main

Il primo commit deve essere un'istantanea fedele del codice così com'è - nessuna modifica, nessuna pulizia, nessun refactoring. Quel commit è il tuo punto di ritorno sicuro: se qualsiasi modifica successiva rompe qualcosa, git checkout main ti riporta allo stato funzionante.

Settimana 3: le prime modifiche sicure - credenziali e sicurezza

Con Git attivo e PHPStan come rete di sicurezza, le prime modifiche che affronto sono sempre quelle di sicurezza - perché le credenziali hardcodate e le funzioni deprecate sono vulnerabilità attive, non debito tecnico teorico.

La priorità nel caso piemontese: estrarre le credenziali MySQL da 9 file diversi e centralizzarle in un unico file di configurazione escluso dal repository:

<?php
// config.php - unico punto di configurazione
// Questo file è in .gitignore e non viene committato
return [
    'db_host' => '127.0.0.1',
    'db_name' => 'gestionale_prod',
    'db_user' => 'app_gestionale',  // utente dedicato, NON root
    'db_pass' => 'password_generata_sicura',
    'debug'   => false,
];

Per ogni file che conteneva credenziali hardcodate, la sostituzione:

<?php
// PRIMA (insicuro, credenziali esposte in 9 file)
$conn = mysql_connect("localhost", "root", "password123");

// DOPO (sicuro, un unico punto di configurazione)
$config = require __DIR__ . '/../config.php';
$conn = new mysqli($config['db_host'], $config['db_user'], $config['db_pass'], $config['db_name']);

Questa modifica fa tre cose: centralizza le credenziali, elimina l'utente root dall'applicazione (ho creato un utente MySQL dedicato con permessi minimi), e migra da mysql_connect (rimosso in PHP 7) a mysqli. Ogni modifica viene committata separatamente con un messaggio descrittivo, testata sull'applicazione, e pushata. Se qualcosa si rompe, il rollback è un git revert.

Settimana 4: Rector per modernizzazione automatica e documentazione

Rector è lo strumento che uso per la modernizzazione automatica del codice PHP. Rector analizza l'AST (Abstract Syntax Tree) del codice e applica trasformazioni predefinite: conversione da mysql_* a mysqli_*, aggiunta di type hint, rimozione di codice morto, upgrade di sintassi da PHP 5.6 a PHP 7.4/8.x. Su un codebase legacy, Rector può automatizzare in minuti ciò che richiederebbe settimane di lavoro manuale.

# Installare Rector
composer require --dev rector/rector

# Configurazione minima per upgrade PHP 5.6 → 7.4
# rector.php
# return RectorConfig::configure()
#     ->withPaths([__DIR__ . '/src'])
#     ->withPhpSets(php74: true)
#     ->withPreparedSets(deadCode: true);

# Dry run (mostra cosa cambierebbe senza modificare)
vendor/bin/rector process --dry-run

# Applicare le trasformazioni
vendor/bin/rector process
git add . && git commit -m "refactor: automated PHP 5.6 → 7.4 upgrade via Rector"

La regola fondamentale con Rector: mai applicare su un codebase senza test e senza Git. Rector trasforma il codice - non verifica che la semantica sia preservata. Senza una test suite (anche minima) o almeno PHPStan, un'applicazione automatica di Rector può introdurre bug silenti. Per questo l'ordine è: Git → PHPStan baseline → Rector → verifica manuale → commit.

L'ultimo deliverable della settimana 4 è un runbook - un documento di 8-10 pagine in Markdown versionato nel repository che descrive: architettura dell'applicazione, flussi critici (login, ordini, fatturazione), configurazione del server, procedure di backup e ripristino, e contatti. Quel runbook è la documentazione che non c'era - e che impedisce alla prossima persona che eredita il progetto di trovarsi nella stessa situazione. Ho descritto l'intero processo di audit iniziale nell'articolo sull'audit tecnico nei primi 30 giorni di un subentro PHP legacy, e le tecniche specifiche per il subentro senza lo sviluppatore originario.

Il recupero di un codebase PHP legacy senza documentazione non è un'impresa epica - è un lavoro metodico con passaggi precisi e verificabili. Trent'anni di debito tecnico non si risolvono in una settimana, ma in trenta giorni puoi passare da "nessuno osa toccarlo" a "possiamo fare modifiche in sicurezza con Git, PHPStan e un runbook che documenta tutto". Se il tuo gestionale o e-commerce PHP è in questa situazione - funziona ma nessuno sa come, lo sviluppatore è sparito e ogni modifica è un rischio - il costo dell'intervento è una frazione del costo di continuare a operare alla cieca. Contattami e valutiamo insieme il percorso.

Ultima modifica: