Vi è mai capitato di arrivare ad un punto della vostra vita da programmatori e dire: “Che cavolo: devo sviluppare di nuovo una mappa con geolocalizzazone… vabbè, lo copio da un altro progetto!” E poi ritrovarvi a migliorare la vostro mappa che vi state portando dietro da decine di progetti come la ruota di Fantozzi… e dopo averla migliorata, desiderare di aggiornarla ovunque?
Scordatevelo di farlo a mano! Riuscireste a tenere allineati massimo 2-3 progetti senza causare esplosioni termonucleari! Però viene la curiosità di capire come fa il resto del mondo, faccio ricerca e… ora sono più di tre anni che abbiamo trovato una soluzione a questo problema, e ci piace condividere con voi il nostro modo di lavorare.
Se siete sviluppatori Symfony o Laravel saprete bene che cosa sia Composer, se invece usate WordPress… le mie più sentite condoglianze!
Composer, per chi non lo sapesse, è un gestore di dipendenze per librerie PHP, e tramite la configurazione di un file json è possibile richiedere le librerie che intendiamo importare all’interno del nostro progetto. Il suo repository manager pubblico è Packagist dove sono disponibili decine di migliaia di librerie PHP che possono aiutarti a velocizzare il tuo sviluppo, tra cui circa 4000 librerie pubbliche per Symfony e 24000 per Laravel. Chiunque può contribuire alle community che usano composer come base dei propri progetti, ma come si fa?
Non è difficile: basta un repository git e sapere un minimo come funziona composer.
Proviamo a creare la nostra prima libreria: come esempio proveremo a fare un bundle per Symfony.
Creiamo il nostro repository (es: su github o su bitbucket) e cloniamolo su computer.
Il primo passo da fare è creare il file composer.json, che sarà letto dal repository manager e indica tutte le dipendenze necessarie per il corretto funzionamento della libreria che sto creando.
Un esempio di composer.json, già arricchito di qualche info accessoria può essere
{
"name": "oi/example",
"type": "lib",
"description": "Bundle di esempio",
"keywords": ["Example"],
"homepage": "https://www.oimmei.com",
"license": "MIT",
"authors": [
{
"name": "Ephraim Pepe <ephraim@oimmei.com>"
}
],
"require": {
"php": ">=7.1"
},
"autoload": {
"psr-4": {
"OiExampleBundle": "src/"
},
"exclude-from-classmap": [
"/Tests/"
]
}
}
Il file json ha diverse chiavi, nel nostro caso d’uso le più significative sono:
- name: nome della libreria, deve essere univoco in assoluto. Per installarla si dovrà chiamare il comando composer require oi/example, inoltre il name indica in quale cartella di vendor verrà copiato il codice, quindi se voglio modificare la mia libreria potrò andare cercare in /vendors/oi/example
- require: è la parte più importante del nostro composer.json e contiene tutte le dipendenze della nostra libreria, è qui che vengono indicate le librerie necessarie e le loro versioni per il corretto funzionamento del codice che state scrivendo.
- autoload/psr-4: contiene la mappatura dei namespaces (solitamente è solo una, ma dà comunque la possibilità di specificare anche più namespaces), in questo caso il namespace OiExampleBundle avrà come base /vendors/oi/example/src (nb: il valore “src/” può essere anche omesso, e nel caso avessimo messo “OiExampleBundle”: “” OiExampleBundle avrebbe avuto come base /vendors/oi/example/).
La documentazione completa di composer.json potete trovarla all’indirizzo https://getcomposer.org/doc/ e qui sarà facile reperire tutte le informazioni su come strutturare il vostro file json.
Prima di proseguire, vi invito a visitare Packagist il repository manager di Composer. Qui possiamo ricercare le librerie pubbliche esistenti da includere eventualmente nel nostro progetto.
Il sito si presenta con il motore di ricerca e le istruzioni base per poter usare le librerie registrate e/o contribuire con le proprie. Ipotizziamo di voler usare le funzionalità di Mapbox all’interno del nostro codice e dopo aver effettuato la ricerca decidiamo ad esempio di voler usare la libreria geocoder-php/mapbox-provider, scelta fatta perché confrontando il numero di utilizzi e la documentazione, rispetto ad altre è decisamente la libreria migliore.
Ora possiamo aggiornare il nostro file composer.json aggiungendo delle dipendenze:
{
"name": "oi/example",
"type": "lib",
"description": "Bundle di esempio",
"keywords": ["Example"],
"homepage": "https://www.oimmei.com",
"license": "MIT",
"authors": [
{
"name": "Ephraim Pepe <ephraim@oimmei.com>"
}
],
"require": {
"php": ">=7.1",
"geocoder-php/mapbox-provider": "1.1.*",
"symfony/framework-bundle": "^4.2 || ^5.0"
},
"conflict": {
"symfony/framework-bundle": "<4.2"
},
"autoload": {
"psr-4": {
"OiExampleBundle": "src/"
},
"exclude-from-classmap": [
"/Tests/"
]
}
}
Vediamo che abbiamo aggiunto due dipendenze: la prima relativa a geocoder-php/mapbox-provider per tutte le versioni che iniziano per 1.1 (da qui la sintassi “1.1.*”), la seconda a symfony/framework-bundle per le versioni 4.2 o superiore e 5.0 o superiore (la sintassi ^4.2 permette di prendere tutte le versioni di 4.2, ma anche 4.3 e 4.4, mentre un cambio di major version non viene contemplato, per questo è necessario mettere in or le versioni con la sintassi “^4.2 || ^5.0”)
Notate anche la nuova chiave conflict: viene usata per indicare esplicitamente quando alcune versioni di librerie richieste non sono compatibili con la libreria che stiamo sviluppando. |
Dopo questa breve spiegazione di come gestire le dipendenze, possiamo scrivere il nostro codice (seguiamo le best practices indicate sul sito ufficiale per mantenerci coerenti con gli standard di Symfony) e ora abbiamo la seguente struttura di cartelle:
Effettuiamo il push del codice su git e aggiungiamo un tag (es: 1.0.0). Consiglio di avere almeno 3 cifre, noi le chiamiamo ..:
- la version si aggiorna quando effettuiamo bugfix, aggiungiamo funzionalità, ottimizziamo il codice.
- la minor si aggiorna quando c’è un cambio di logica che rischia di poter rendere incompatibile il codice con versioni precedenti.
- la major si aggiorna quando c’è un salto tecnologico o logico molto importante o un refactoring del codice, un cambio di modello dati o altri interventi talmente grandi da essere certi di una mancata retrocompatibilità con la versione precedente.
Ora per pubblicare la vostra libreria, potete registrarvi a Packagist e seguire le istruzioni, e così la vostra libreria sarà pubblica, e chiunque potrà scaricarla ed utilizzarla.
Tutto molto bello, vero? Scommetto però che ci sono alcuni di voi che dicono “Sì, ma io non voglio condividere il mio codice, lo voglio usare solo io!”
Ci sono 2 soluzioni, una sporca e una pulita:
- La fémo sporca? Questa soluzione è adatta a chi è a corto di tempo e di denaro, quindi usiamo un repository pubblico, ma non scriviamo la documentazione per il mio codice… vedrete che saranno in pochi a scaricare le vostre librerie… ma qualcuno può farlo e provare a fare un po’ di retroingegneria…
- Fémola pulita! Prendiamo un server, ci installiamo un server git o acquistiamo un account a pagamento che ci permette di avere repository privati, e sul server installiamo Satis, ovvero un repository manager privato, scritto dagli stessi autori di Composer!
Guardiamo quindi come poter installare Satis sul nostro server, e scopriremo che è semplicissimo!
Naturalmente si installa via Composer ed il comando da lanciare è
php composer.phar create-project composer/satis --stability=dev --keep-vcs
e verrà creata una cartella ./satis là dove avete lanciato il comando, e questo è il contenuto della cartella:
nella root del progetto dovrete creare un file satis.json con il riferimento ai vostri repository
{
"name": "oi/bundles",
"homepage": "http://satis.oimmei.com",
"repositories": [
{ "type": "vcs", "url": "https://oimmei@bitbucket.org/oimmei/examplebundle.git" },
{ "type": "vcs", "url": "https://oimmei@bitbucket.org/oimmei/testbundle.git" }
],
"require-all": true
}
dovrete assicurarvi di caricare la vostra chiave ssh del server sul vostro github o bitbucket di turno e poi potete lanciare il comando
php bin/satis build satis.json packages
Satis aggiornerà la sua libreria interna e ora è pronto per essere esposto via web: configurate il vostro web server (il nostro esempio è di nginx, anche perché gli esempi di apache abbondano online)
server {
server_name packagist.oimmei.com;
return 301 https://$server_name$request_uri;
}
server {
server_name satis.oimmei.com;
root /var/htdocs/satis/packages;
include global/letsencrypt.conf; #configurazione comune a tutti i progetti https
index index.html;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ .php$ {
include fastcgi.conf;
fastcgi_split_path_info ^(.+.php)(/.+)$;
fastcgi_index index.php;
}
location ~* .(js|css|png|jpg|jpeg|gif|ico)$ {
expires max;
log_not_found off;
}
rewrite ^http://(.*)$ https://$1;
listen 443 ssl;
ssl_certificate /var/www/letsencrypt/certs/packagist.oimmei.com/fullchain.pem;
ssl_certificate_key /var/www/letsencrypt/certs/packagist.oimmei.com/privkey.pem;
error_log /var/log/nginx/packagist.oimmei.com_error.log;
access_log /var/log/nginx/packagist.oimmei.com_access.log;
}
riavviate il web server et voilà, il vostro repository manager è pronto ad essere usato!
Ma non è finita! Per usare le vostre librerie all’interno del progetto dovrete aggiungere il codice indicato nell’intestazione della pagina web del vostro satis.
{
"type": "project",
"license": "proprietary",
"require": {
...
"oi/example": "4.3.1.*",
...
},
...
"repositories": [{
"type": "composer",
"url": "https://packagist.oimmei.com"
}]
}
Il file satis.json può contenere un array di repository, e ogni volta che aggiungete una nuova libreria o rilasciate una nuova versione di una libreria già registrata dovrete lanciare nuovamente il build.
Ora che avete il vostro repository manager sarà molto semplice riutilizzare lo stesso codice che avete scritto per gestire le mappe con geolocalizzazione in tutti i progetti che volete, e quando create una nuova versione sarà sufficiente andare sui progetti desiderati, cambiare eventualmente il vostro composer.json di progetto e lanciare il comando
php composer.phar up
Ephraim Pepe