Introduzione
Quando si lavora in team o si distribuisce un progetto PHP, uno dei problemi più comuni è questo: “sul mio computer funziona, ma sul server no”. Spesso la causa non è il codice in sé, ma una differenza nelle versioni delle librerie installate.
Composer risolve gran parte di questo problema grazie a due file fondamentali: composer.json e composer.lock. Se il primo descrive quali dipendenze servono e con quali vincoli di versione, il secondo registra quali versioni esatte sono state installate in un determinato momento.
In questo tutorial vedremo un sotto-argomento molto pratico e spesso sottovalutato: come usare correttamente composer.lock per ottenere installazioni riproducibili. È un tema utile sia per sviluppatori singoli sia per team, perché riduce bug, sorprese e differenze tra ambienti.
Codice completo
Immaginiamo un piccolo progetto PHP che usa una libreria per gestire date e una libreria per fare richieste HTTP. Il focus non è la logica applicativa, ma il flusso corretto di installazione e aggiornamento delle dipendenze.
{
"name": "acme/app-esempio",
"description": "Progetto dimostrativo per installazioni riproducibili con Composer",
"type": "project",
"require": {
"php": "^8.1",
"guzzlehttp/guzzle": "^7.8",
"nesbot/carbon": "^3.0"
}
}
<?php
require __DIR__ . ´/vendor/autoload.php´;
use CarbonCarbon;
use GuzzleHttpClient;
$oggi = Carbon::now();
echo "Data corrente: " . $oggi->toDateTimeString() . PHP_EOL;
$client = new Client([
´base_uri´ => ´https://api.github.com/´,
´headers´ => [
´User-Agent´ => ´AcmeApp/1.0´
]
]);
try {
$response = $client->get(´repos/php/php-src´);
echo "Status HTTP: " . $response->getStatusCode() . PHP_EOL;
echo "Lunghezza risposta: " . strlen($response->getBody()->getContents()) . PHP_EOL;
} catch (Throwable $e) {
echo "Errore: " . $e->getMessage() . PHP_EOL;
}
// Comandi principali da terminale
composer install
composer update
composer require monolog/monolog
composer show
Spiegazione
1. Cos’è composer.lock e perché è importante
Il file composer.json definisce i vincoli di versione. Ad esempio, "guzzlehttp/guzzle": "^7.8" significa che Composer può installare una versione compatibile con 7.8, come 7.8.1, 7.9.0 e così via, purché rispetti le regole del vincolo.
Il file composer.lock, invece, salva le versioni esatte installate, per esempio:
- guzzlehttp/guzzle 7.9.2
- nesbot/carbon 3.8.4
- dipendenze transitive precise e relative informazioni di distribuzione
Questo significa che, se un altro sviluppatore esegue composer install, Composer non sceglierà la versione “più recente compatibile”, ma installerà esattamente quelle bloccate nel lock file.
2. Differenza pratica tra install e update
Qui c’è la distinzione più importante:
- composer install: usa
composer.lockse presente e installa le versioni bloccate. - composer update: ricalcola le versioni consentite da
composer.jsone aggiorna anchecomposer.lock.
In breve:
- install = riproduce un ambiente già definito
- update = modifica l’insieme delle versioni installate
Questo è fondamentale in un team: il file lock deve essere trattato come parte del progetto, non come un dettaglio secondario.
3. Flusso corretto in un progetto reale
Vediamo un flusso tipico e sano:
- si definiscono le dipendenze in
composer.json; - si esegue
composer updatesul proprio ambiente di sviluppo; - Composer genera o aggiorna
composer.lock; - il file lock viene versionato nel repository Git;
- gli altri membri del team eseguono
composer install; - produzione e staging usano lo stesso lock file per installazioni coerenti.
In questo modo si evita che ogni ambiente abbia versioni leggermente diverse delle stesse librerie.
4. Perché il lock file è utile anche in produzione
In produzione non conviene eseguire composer update a ogni deploy, perché ciò potrebbe introdurre nuove versioni non testate. È molto più sicuro eseguire composer install --no-dev --optimize-autoloader usando il lock file già validato in sviluppo o in CI.
Questo approccio garantisce due vantaggi:
- stabilità: le dipendenze non cambiano in modo inatteso;
- tracciabilità: se un bug compare dopo un deploy, sai esattamente quali versioni erano presenti.
5. Esempio di comportamento diverso tra install e update
Supponiamo che nel file composer.json tu abbia questo vincolo:
"nesbot/carbon": "^3.0"
Se oggi esegui composer update, potresti ottenere Carbon 3.8.4. Domani, con lo stesso vincolo, potresti ottenere 3.8.5. Entrambe le versioni rispettano il requisito, ma potrebbero comportarsi in modo leggermente diverso.
Se invece il progetto ha già un composer.lock con 3.8.4, chi esegue composer install continuerà a usare proprio 3.8.4 fino a quando qualcuno non aggiornerà esplicitamente le dipendenze.
6. Quando aggiornare il lock file
Non bisogna aggiornare il lock file “a caso”. È meglio farlo in momenti controllati, ad esempio:
- quando serve una nuova funzionalità di una libreria;
- quando devi correggere una vulnerabilità di sicurezza;
- quando vuoi allineare le dipendenze dopo un ciclo di test;
- quando una pipeline CI verifica che l’aggiornamento non rompe nulla.
Questo riduce il rischio di introdurre regressioni difficili da diagnosticare.
Best practice
- Versiona sempre composer.lock nei progetti applicativi. È una buona pratica essenziale per la riproducibilità.
- Usa composer install in produzione, non composer update.
- Aggiorna le dipendenze in modo controllato, idealmente con test automatici dopo ogni update.
- Non modificare manualmente composer.lock: lascialo generare a Composer.
- Controlla le dipendenze transitive: un aggiornamento diretto può cambiare anche librerie indirette.
- Blocca il processo in CI: esegui installazioni pulite per verificare che il lock file sia coerente.
- Usa --no-dev in produzione per evitare di installare pacchetti di sviluppo non necessari.
Un errore comune è pensare che il lock file serva solo “per comodità”. In realtà è uno strumento di controllo qualità: rende il progetto prevedibile e più facile da mantenere.
Riepilogo
Il file composer.lock è uno degli elementi più importanti nell’uso professionale di Composer. Se composer.json definisce le regole, il lock file registra il risultato concreto di quelle regole in un momento specifico.
Il punto chiave da ricordare è semplice:
- composer install = installazione riproducibile;
- composer update = aggiornamento delle versioni;
- composer.lock = garanzia di coerenza tra ambienti.
Per applicazioni reali, specialmente in team e in produzione, usare correttamente il lock file significa avere meno bug, deploy più affidabili e debugging più semplice.
Approfondisci con risorse ufficiali
- Documentazione ufficiale Composer: https://getcomposer.org/doc/
- Comando install: https://getcomposer.org/doc/03-cli.md#install-i
- Comando update: https://getcomposer.org/doc/03-cli.md#update-u
- File composer.json: https://getcomposer.org/doc/04-schema.md
- Best practices Composer: https://getcomposer.org/doc/faqs/should-i-commit-the-dependencies-in-my-vendor-directory.md
Se vuoi, nel prossimo tutorial posso mostrarti come usare composer.lock insieme alla CI/CD per automatizzare i deploy in modo sicuro e prevedibile.
