Fino ad ora ci siamo concentrati sull'utilizzo di Composer per l'installazione di pacchetti sviluppati da altri programmatori, permettendoci d'implementare rapidamente funzionalità nei nostri progetti senza dover reinventare la ruota, ma esiste ovviamente anche l'altra faccia della medaglia. Vediamo quindi cosa dobbiamo fare per creare un pacchetto autonomo installabile con Composer e pubblicabile su Packagist.
Dati basilari
La prima cosa da fare per creare un pacchetto è scegliere un nome per l'identificazione dello stesso. Questo è composto da due parti separate da uno slash (/):
- vendor name, cioè un nome legato ad uno specifico sviluppatore o a un'organizzazione;
- project name, cioè un nome che identifica il progetto.
E' necessario comunque notare che all'interno dell'ecosistema di Composer non esiste un organismo abilitato a registrare i vendor name, quindi in linea teorica è possibile che due sviluppatori utilizzino lo stesso nome organizzazione creando problemi di compatibilità nella pubblicazione su Packagist, dove il nome completo deve risultare univoco.
Per indicare il nome del pacchetto è necessario usare la direttiva name
all'interno del file composer.json
specificando il nome completo. Con la direttiva description
è necessario invece fornire una breve descrizione (circa una riga di testo) sull'obiettivo della libreria. Una descrizione deve essere obbligatoriamente specificata per poter pubblicare un pacchetto su un repository.
Altra informazione che non dovrebbe mancare nella creazione di un pacchetto è la licenza di distribuzione da assegnare al codice. Tramite la direttiva license
, facoltativa ma consigliata, possiamo indicare il nostro codice come closed source tramite il valore proprietary
o scegliere una delle numerose licenze open source disponibili, come ad esempio MIT o LGPL.
Indicare la versione
Assegnare una versione al codice di un pacchetto è fondamentale per fare in modo che il gestore possa risolvere le dipendenze e per distribuire il proprio lavoro. La direttiva da utilizzare è version
e il formato supportato è quello standard a tre numeri separati da punti, come ad esempio 1.5.0 preceduti o meno da una v.
In maniera facoltativa è possibile aggiungere dei suffissi preceduti da un segno - per indicare il livello di maturità:
- dev per una versione di sviluppo che in quanto tale può cambiare le proprie interfacce in qualsiasi momento;
- alpha, beta e rc (release candidate) per versioni ad accesso anticipato di cui non si garantisce il pieno funzionamento e comunque suscettibili di modifiche importanti;
- patch per versioni con esclusiva correzione di bug.
Composer da questo punto di vista è estremamente autonomo e permette di non specificare all'interno del file di configurazione la versione estrapolandola in automatico dai tag sui server di versioning (ad esempio Git). Questa modalità è quella consigliata perché evita problemi di sincronizzazione tra il repository e la versione reale di lavoro.
La suddivisione del codice in librerie
Composer permette di rilasciare librerie focalizzate su specifiche funzionalità o librerie monolitiche omnicomprensive sulla base delle proprie scelte. Il problema che può sorgere nella distribuzione di librerie monolitiche è quello di costringere l'utente finale a dipendere da altre librerie che non userà mai magari perché un file del pacchetto interagisce con esse. Immaginiamo ad esempio di sviluppare una libreria di astrazione dal file system per scrivere un codice che in base alla configurazione vada a leggere file in locale, su uno spazio ftp o sul cloud. Perché costringere l'utente a dipendere dall'SDK del servizio cloud se utilizzerà la libreria solo per interagire con file in locale?
Può quindi essere funzionale creare una libreria base contenente le funzioni essenziali, e le interfacce generiche che il progetto andrà ad esporre, e poi creare singoli pacchetti che implementeranno quelle interfacce con le dipendenze di cui ha bisogno l'implementazione specifica. In questo modo migliora la gestione delle dipendenze ma viene frammentato il codice in tanti micropacchetti: si tratta di trovare il giusto equilibrio tra le varie esigenze.
Oltre alla semplice dipendenza è possibile indicare altre relazioni, come ad esempio pacchetti che entrano in conflitto con il nostro. La direttiva conflict
ha questo scopo e serve in quei casi in cui un pacchetto richiesto rompe la retrocompatibilità da una certa versione oppure ha un bug prima non presente.
Ritornando all'esempio della libreria per astrarre il file system, potremmo decidere di includere le implementazioni più usate nel pacchetto base e relegare versioni meno utilizzate e con dipendenze specifiche in altri pacchetti. Per informare gli utilizzatori dell'esistenza di questi pacchetti con le funzionalità aggiuntive abbiamo la direttiva suggest
, essa permette di elencarli con lo stesso formato che avrebbe la direttiva require
, indicando però al posto del numero di versione il motivo del suggerimento.