La nave di Teseo del codice
Se sostituisci ogni tavola di una nave, una alla volta, la nave alla fine e ancora la stessa nave? Il paradosso di Teseo e la metafora perfetta per una migrazione di linguaggio. Passando da PHP 8.1 a 8.4, ogni file e stato toccato, ogni dipendenza aggiornata, ogni pattern modernizzato — eppure il framework e rimasto lo stesso. O forse no?
Rector ha trasformato 108 file in modo automatico: aggiunto #[\Override] dove un metodo sovrascrive il genitore, convertito in readonly le property che non cambiano, promosso parametri del costruttore. Sono trasformazioni che non cambiano il comportamento — cambiano l'intenzione espressa nel codice. E l'intenzione conta quanto il comportamento.
La distinzione e sottile ma fondamentale. Un metodo che sovrascrive il genitore funziona identicamente con o senza #[\Override]. Ma con l'attributo, l'intenzione e dichiarata: "questo metodo esiste perche sovrascrive qualcosa". Se il genitore cambia firma e il metodo figlio non viene aggiornato, PHP 8.4 lancia un errore a compile time invece di lasciare un metodo orfano che nessuno chiama.
Perche aggiornare se funziona?
La domanda e legittima e la risposta non e scontata. PHP 8.1 funzionava. I test passavano. Il deploy era stabile. Perche rischiare? Eraclito direbbe che tutto scorre — e nel software, restare fermi e un'illusione. Le dipendenze si aggiornano, le vulnerabilita vengono scoperte, la community si sposta. Un framework su PHP 8.1 nel 2026 non e stabile: e abbandonato.
Ma c'e una ragione piu profonda: PHP 8.4 offre strumenti che rendono il codice piu espressivo. #[\Override] dice esplicitamente "questo metodo sovrascrive il genitore" — se il genitore cambia firma, il compilatore te lo dice subito invece di lasciarti con un bug silenzioso. readonly dice "questo valore non cambiera mai dopo l'inizializzazione" — un invariante che prima potevi solo documentare, ora puoi far rispettare.
Le feature di PHP 8.4 usate nel framework
Soft PHP MVC sfrutta diverse novita introdotte tra PHP 8.2 e 8.4:
- #[\Override] — applicato sistematicamente a tutti i metodi che sovrascrivono un genitore, da Rector in modo automatico su 108 file
- readonly properties — usate nei modelli e nei value object per garantire immutabilita dopo l'inizializzazione
- Constructor property promotion — riduce il boilerplate nei costruttori, eliminando la duplicazione tra parametri e assegnamenti
- Enum backed — cinque enum nel DataLayer (SortDirection, AggregateFunction, JoinType, ForeignKeyAction, ColumnType) sostituiscono le stringhe magiche
- Attributi nativi — #[CliCommand] per i comandi CLI, eliminando la necessita di registrazione manuale
Le dipendenze come ecosistema
Aggiornare PHP ha significato aggiornare tutto il grafo delle dipendenze: illuminate/validation da ^10 a ^12, nesbot/carbon da ^2 a ^3, phpunit/phpunit da ^10 a ^11, symfony/var-dumper da ^6 a ^7. Ogni salto di major version e un contratto rinegoziato — breaking change incluse.
Il caso piu interessante e stato sendinblue/api-v3-sdk, rinominato in getbrevo/brevo-php. Non un aggiornamento: un cambio di identita. Il namespace e cambiato da SendinBlue\Client a Brevo\Client. Questo ricorda che le dipendenze non sono solo codice: sono relazioni. E le relazioni cambiano.
Gestire i breaking change senza panico
La strategia per gestire i breaking change e stata metodica. Per ogni dipendenza aggiornata:
- Leggere il changelog — identificare le breaking change prima di aggiornare, non dopo
- Aggiornare una dipendenza alla volta — mai aggiornare tutto insieme, perche quando un test fallisce devi sapere chi e il colpevole
- Eseguire la suite di test completa — 813 test come rete di sicurezza dopo ogni aggiornamento
- Committare ogni aggiornamento separatamente — se qualcosa va storto, il rollback e chirurgico
Carbon 3 ha cambiato il comportamento di diffInDays() restituendo valori negativi per date passate. PHPUnit 11 ha deprecato molti metodi di asserzione. Illuminate Validation 12 ha modificato le firme di alcuni rule object. Ogni cambiamento ha richiesto adattamenti — piccoli, localizzati, testabili.
Il driver MariaDB: estendere senza modificare
La migrazione a PHP 8.4 ha portato anche un nuovo driver: MariaDB. Tecnicamente MariaDB e un fork di MySQL, ma le differenze si accumulano — collation utf8mb4_unicode_ci invece di general_ci, supporto per RETURNING clause come PostgreSQL. Il pattern Open/Closed — estendi, non modificare — ha guidato la scelta: MariaDbBuilder estende MySqlBuilder, MariadbSchemaGrammar estende MysqlSchemaGrammar. Solo cio che diverge viene sovrascritto.
Bertrand Meyer, che formulo il principio Open/Closed nel 1988, lo intendeva come una proprieta delle astrazioni ben progettate: un modulo dovrebbe essere aperto all'estensione ma chiuso alla modifica. In pratica, questo significa che aggiungere MariaDB non ha richiesto di toccare una sola riga del codice MySQL esistente.
L'architettura multi-driver del DataLayer
Il sistema di migrazioni di Soft PHP MVC supporta quattro database: MySQL, MariaDB, PostgreSQL e SQLite. Ogni driver ha il proprio SchemaGrammar che traduce le operazioni astratte (crea tabella, aggiungi colonna, crea indice) nella sintassi specifica del database. Il SchemaBuilder e il punto di ingresso unico — il codice applicativo non sa quale database sta usando, e non dovrebbe saperlo.
Questa astrazione non e accademica: e pratica. I test girano su SQLite in memoria per velocita. Lo sviluppo locale usa MariaDB. La produzione potrebbe usare PostgreSQL. Lo stesso codice di migrazione funziona su tutti e quattro — le differenze sono gestite dai grammar, non dal codice utente.
813 test come rete di sicurezza
La migrazione non sarebbe stata possibile senza i test. 813 test che coprono ORM, routing, middleware, cache, modelli, helper — ogni trasformazione di Rector verificata automaticamente. Il test non e solo una verifica: e un'asserzione su come il codice dovrebbe comportarsi. Se il comportamento cambia dopo un aggiornamento, il test lo cattura prima che arrivi in produzione.
L'evoluzione nel software, come nella biologia, non e un salto nel vuoto: e una serie di piccole mutazioni, ciascuna verificata dall'ambiente. I test sono l'ambiente che seleziona le mutazioni valide e scarta quelle dannose. Senza questa pressione selettiva, l'evoluzione diventa degenerazione.
La copertura dei test non e solo una metrica da mostrare in un badge. E la fiducia quantificata che puoi riporre nel tuo codice quando fai un cambiamento. Senza test, aggiornare PHP da 8.1 a 8.4 sarebbe stato un atto di fede. Con 813 test, e stato un processo ingegneristico: cambia, verifica, procedi. La differenza tra speranza e certezza.