Il file composer.json è molto più di un semplice elenco di dipendenze per i progetti PHP. È il vero e proprio manifesto del tuo progetto, una dichiarazione che descrive non solo quali librerie il tuo software utilizza, ma anche come è strutturato, come deve essere caricato e quali operazioni automatiche può eseguire. Comprendere a fondo la sua struttura e le sue capacità è fondamentale per ogni sviluppatore web che lavora con PHP, dal principiante all'esperto.
Questo articolo ti guiderà passo dopo passo attraverso le sezioni più importanti del file composer.json, spiegando il perché di ogni configurazione e fornendo esempi pratici per aiutarti a padroneggiare questo strumento indispensabile.
Cos'è Composer e Perché composer.json è Fondamentale?
Prima di immergerci nei dettagli di composer.json, facciamo un rapido ripasso su Composer stesso. Composer è un gestore di dipendenze per PHP. Immagina di costruire una casa: non inizi a fabbricare ogni singolo mattone, finestra o porta da zero. Ti affidi a fornitori che producono questi componenti. Allo stesso modo, nello sviluppo software, è raro che tu debba scrivere ogni singola riga di codice per ogni funzionalità. Ti affidi a librerie e framework esistenti che risolvono problemi comuni (gestione database, autenticazione, routing, ecc.).
Composer ti permette di dichiarare le librerie (o "pacchetti") di cui il tuo progetto ha bisogno e le installa automaticamente. Non solo, gestisce anche le dipendenze di queste dipendenze (le cosiddette "dipendenze transitive"), assicurandosi che tutte le parti necessarie siano presenti e compatibili tra loro.
Il file composer.json è il cuore di questo processo. È un file in formato JSON (JavaScript Object Notation) che risiede nella directory principale del tuo progetto PHP. Contiene tutte le informazioni che Composer necessita per operare sul tuo progetto. Senza di esso, Composer non saprebbe cosa installare, come configurare l'autoloading o quali script eseguire. È, in pratica, il "contratto" tra il tuo progetto e l'ecosistema PHP di pacchetti.
Capire composer.json significa avere il controllo completo sul tuo ambiente di sviluppo, dalla gestione delle versioni delle librerie alla standardizzazione dei processi di build e deploy. Ti permette di collaborare più facilmente in team, garantendo che tutti utilizzino le stesse versioni delle dipendenze e che i processi siano coerenti.
La Struttura Base di composer.json
Il file composer.json è un oggetto JSON che contiene diverse proprietà chiave. Ogni proprietà ha un ruolo specifico nel descrivere il tuo progetto e le sue esigenze. Vediamo le sezioni fondamentali che ogni sviluppatore dovrebbe conoscere.
{
"name": "mioprogetto/mia-applicazione",
"description": "Una breve descrizione del mio fantastico progetto web.",
"type": "project",
"license": "MIT",
"authors": [
{
"name": "Il Mio Nome",
"email": "il.mio.nome@example.com"
}
],
"require": {
"php": ">=8.1",
"monolog/monolog": "^2.0"
},
"require-dev": {
"phpunit/phpunit": "^9.5"
},
"autoload": {
"psr-4": {
"MiaApplicazione\\\\": "src/"
}
},
"scripts": {
"test": "phpunit --colors=always",
"start": "php -S localhost:8000 -t public/"
},
"config": {
"optimize-autoloader": true,
"preferred-install": "dist"
},
"minimum-stability": "dev",
"prefer-stable": true
}
Analizziamo le proprietà principali:
name (Obbligatorio per i pacchetti, consigliato per i progetti)
Questa proprietà definisce il nome del tuo pacchetto o progetto. Segue il formato vendor/nome-pacchetto (es. symfony/symfony, mioprogetto/mia-applicazione). Il vendor è il nome dell'organizzazione o dell'autore, mentre nome-pacchetto è il nome del progetto stesso. È cruciale per identificare univocamente il tuo pacchetto all'interno dell'ecosistema Composer, specialmente se intendi pubblicarlo su Packagist.
description
Una breve descrizione testuale del tuo progetto o pacchetto. È utile per gli esseri umani che leggono il file e appare nelle liste dei pacchetti su Packagist.
type
Indica il tipo di pacchetto. I tipi comuni includono:
library: La maggior parte dei pacchetti rientra in questa categoria. È una libreria generica.project: Indica che questo pacchetto è un'applicazione completa, non solo una libreria da includere altrove.metapackage: Un pacchetto che non contiene codice, ma serve solo a raggruppare altre dipendenze.composer-plugin: Un plugin per Composer stesso.
Per un'applicazione web standard, project è la scelta più comune.
license
Specifica la licenza con cui il tuo codice è rilasciato (es. MIT, GPL-3.0-or-later, proprietary). È una pratica fondamentale per la trasparenza e la legalità, indicando come altri possono utilizzare, modificare e distribuire il tuo software.
authors
Un array di oggetti, dove ogni oggetto rappresenta un autore del progetto. Tipicamente include name ed email, e a volte homepage o role.
Gestione delle Dipendenze: require e require-dev
Questa è la spina dorsale di Composer. Le sezioni require e require-dev sono oggetti che elencano i pacchetti di cui il tuo progetto ha bisogno, insieme alle versioni desiderate.
require
Contiene i pacchetti essenziali per il funzionamento dell'applicazione in produzione. Se la tua applicazione non può funzionare senza un certo pacchetto, lo metti qui. Ogni chiave è il nome del pacchetto (vendor/nome-pacchetto) e il valore è la stringa della versione desiderata.
Esempio:
"monolog/monolog": "^2.0" significa che hai bisogno della libreria Monolog (un popolare logger PHP) e accetti qualsiasi versione dalla 2.0.0 (inclusa) fino alla 3.0.0 (esclusa). Questo è un esempio di Semantic Versioning.
Semantic Versioning (SemVer)
Comprendere SemVer è cruciale. Le versioni sono tipicamente nel formato MAGGIORE.MINORE.PATCH (es. 2.5.1).
- PATCH (es.
2.5.1->2.5.2): Correzioni di bug retrocompatibili. - MINORE (es.
2.5.1->2.6.0): Nuove funzionalità retrocompatibili. - MAGGIORE (es.
2.5.1->3.0.0): Cambiamenti non retrocompatibili.
I simboli di versione più comuni in composer.json sono:
^(caret): Il più comune. Permette aggiornamenti a versioni MINORI e PATCH, ma blocca gli aggiornamenti MAGGIORI. Esempio:^2.5accetta2.5.0fino a<3.0.0.^0.5è speciale: accetta0.5.0fino a<0.6.0(perché la versione 0 non garantisce retrocompatibilità).~(tilde): Meno permissivo del caret. Permette aggiornamenti solo a PATCH se specificatoMAGGIORE.MINORE.PATCH, o a MINORE se specificatoMAGGIORE.MINORE. Esempio:~2.5.1accetta2.5.1fino a<2.6.0.~2.5accetta2.5.0fino a<2.6.0.*(asterisco): Qualsiasi versione (sconsigliato in produzione per la scarsa prevedibilità).1.2.3: Versione esatta (troppo restrittivo, può bloccare correzioni di bug).>=1.2 <2.0: Un intervallo specifico.
È buona norma essere il più flessibile possibile senza rompere la compatibilità, per beneficiare di correzioni di bug e nuove funzionalità. Il ^ è un ottimo compromesso per la maggior parte dei casi.
Oltre ai pacchetti, in require si specifica anche la versione minima di PHP richiesta dal progetto (es. "php": ">=8.1"). Questo è cruciale per garantire che il tuo codice venga eseguito su un ambiente compatibile.
require-dev
Contiene i pacchetti necessari solo durante lo sviluppo o il testing. Questi pacchetti non vengono installati quando si esegue composer install --no-dev (tipico in ambienti di produzione per ridurre le dimensioni e le dipendenze). Esempi comuni includono framework di testing (es. PHPUnit), strumenti di linting, debug, o librerie per la generazione di dati fittizi.
Esempio:
"phpunit/phpunit": "^9.5" indica che PHPUnit versione 9.5 o successiva (ma prima della 10.0) è richiesta per eseguire i test, ma non per il funzionamento dell'applicazione in produzione.
Autoloading: Come Composer Carica le Tue Classi
L'autoloading è una delle funzionalità più potenti di Composer e la sua configurazione in composer.json è vitale per qualsiasi progetto PHP moderno. Prima dell'autoloading, dovevi includere manualmente ogni file di classe con require o include. Con l'autoloading, PHP carica automaticamente le classi solo quando sono necessarie, basandosi su regole definite.
La sezione autoload (e autoload-dev per l'autoloading specifico di sviluppo) definisce queste regole. I tipi più comuni sono psr-4 e classmap.
psr-4
Questo è lo standard moderno e raccomandato per l'autoloading. Mappa un "namespace" (spazio dei nomi) a una directory fisica. Quando PHP cerca una classe all'interno di quel namespace, Composer sa esattamente in quale directory cercare il file corrispondente.
"autoload": {
"psr-4": {
"MiaApplicazione\\\\": "src/",
"AltroModulo\\\\": "modules/altro/src/"
}
}
Nell'esempio:
"MiaApplicazione\\\\": Questo è il namespace radice del tuo progetto. La doppia backslash (\\\\) è necessaria perché la backslash singola è un carattere di escape in JSON."src/": Questa è la directory fisica dove si trovano i file delle classi per quel namespace. Composer cercherà i file qui, seguendo la convenzione che il nome del file corrisponde al nome della classe (es.MiaApplicazione\\Controller\\HomeControllersi troverà insrc/Controller/HomeController.php).
Dopo aver modificato la sezione autoload, è essenziale eseguire composer dump-autoload (o composer install/composer update) per rigenerare i file dell'autoloader. Composer crea un file vendor/autoload.php che devi includere all'inizio del tuo script principale (index.php o public/index.php) per far funzionare l'autoloading.
<?php
// public/index.php
require __DIR__ . '/../vendor/autoload.php';
// Ora puoi usare le tue classi senza includerle manualmente
use MiaApplicazione\\Controller\\HomeController;
$controller = new HomeController();
$controller->index();
classmap
Questa opzione è utile per progetti legacy o classi che non seguono le convenzioni PSR-4. Composer scansiona le directory specificate e crea una mappa statica di tutte le classi trovate. È meno flessibile di PSR-4 ma può essere utile per classi non namespaced o per prestazioni leggermente migliori in ambienti con molte classi.
"autoload": {
"classmap": [
"src/Legacy/",
"lib/old-classes/"
]
}
files
Per includere file PHP che non contengono classi ma funzioni globali o costanti. Questi file vengono sempre inclusi all'avvio dell'applicazione.
"autoload": {
"files": [
"src/helpers.php"
]
}
autoload-dev
Funziona esattamente come autoload ma le regole definite qui vengono caricate solo quando le dipendenze di sviluppo sono installate (cioè, non con composer install --no-dev). È ideale per le classi di test, i mock o i seeder di database che non devono essere presenti in produzione.
"autoload-dev": {
"psr-4": {
"MiaApplicazione\\Tests\\\\": "tests/"
}
}
Questo significa che MiaApplicazione\\Tests\\Unit\\ExampleTest sarà cercato in tests/Unit/ExampleTest.php.
Script: Automatizzare i Task con Composer
La sezione scripts è una delle funzionalità più sottovalutate ma potenti di composer.json. Ti permette di definire comandi personalizzati che possono essere eseguiti tramite Composer, trasformandolo in un vero e proprio task runner per il tuo progetto. Questo è incredibilmente utile per automatizzare operazioni ripetitive come lanciare test, eseguire migration di database, avviare un server di sviluppo, linting del codice, ecc.
Definizione degli Script
La sezione scripts è un oggetto dove le chiavi sono i nomi degli script e i valori sono i comandi da eseguire. Un comando può essere una singola stringa (un comando da shell) o un array di stringhe (per eseguire più comandi in sequenza).
"scripts": {
"test": "phpunit --colors=always",
"lint": [
"php -l src/",
"phpcs --standard=PSR12 src/"
],
"db:migrate": "php artisan migrate",
"start": "php -S localhost:8000 -t public/",
"post-install-cmd": [
"@php -r \\"file_exists('.env') || copy('.env.example', '.env');\\"",
"php artisan key:generate",
"php artisan migrate"
]
}
Per eseguire uno script personalizzato, usi composer run-script <nome-script> o la sua forma abbreviata composer <nome-script> se il nome non è in conflitto con un comando Composer nativo. Ad esempio, composer test eseguirà phpunit --colors=always.
Eventi Predefiniti di Composer
Composer non solo ti permette di definire script personalizzati, ma offre anche una serie di "eventi" predefiniti ai quali puoi agganciare i tuoi script. Questi eventi si scatenano in momenti specifici del ciclo di vita di Composer (installazione, aggiornamento, dump dell'autoloader, ecc.).
Alcuni degli eventi più comuni includono:
pre-install-cmd: Eseguito prima dell'installazione delle dipendenze.post-install-cmd: Eseguito dopo l'installazione delle dipendenze.pre-update-cmd: Eseguito prima dell'aggiornamento delle dipendenze.post-update-cmd: Eseguito dopo l'aggiornamento delle dipendenze.pre-autoload-dump: Eseguito prima di rigenerare i file dell'autoloader.post-autoload-dump: Eseguito dopo aver rigenerato i file dell'autoloader.
Questi eventi sono estremamente utili per automatizzare task di setup. Ad esempio, post-install-cmd è perfetto per:
- Creare un file
.envse non esiste. - Generare una chiave di sicurezza per un framework (es. Laravel).
- Eseguire le migration del database.
- Compilare asset frontend.
Nell'esempio precedente, il post-install-cmd esegue tre comandi: uno per copiare un file .env.example in .env se non esiste (notare l'uso di @php -r per eseguire codice PHP direttamente), e poi due comandi artisan per Laravel.
Un trucco utile è usare il prefisso @ davanti a un comando per indicare che si riferisce a uno script Composer definito localmente. Ad esempio, in un post-install-cmd, potresti avere "@db:migrate" per richiamare lo script db:migrate definito altrove nel tuo composer.json.
Altre Sezioni Utili di composer.json
Per un controllo ancora maggiore sul tuo progetto, ci sono altre sezioni che possono essere molto utili.
config
Questa sezione ti permette di configurare il comportamento di Composer stesso. Alcune opzioni comuni includono:
optimize-autoloader:trueofalse. Se impostato sutrue(raccomandato per la produzione), Composer genera un autoloader più performante, ma il processo di dump richiede più tempo. In sviluppo, puoi lasciarlo afalseonullper un dump più rapido.preferred-install:dist(default, download di un archivio zip) osource(clona il repository Git).distè più veloce e leggero per la produzione.sourceè utile in sviluppo se hai bisogno di modificare direttamente il codice di una dipendenza.allow-plugins: Un oggetto che definisce quali plugin Composer sono permessi e quali no. Questo è una misura di sicurezza importante.bin-dir: Specifica dove verranno installati gli eseguibili dei pacchetti (es.vendor/bin/è il default).
extra
Questa è una sezione generica che può contenere dati aggiuntivi specifici del tuo progetto o del framework che stai utilizzando. Composer non interpreta direttamente il contenuto di extra, ma i framework o i pacchetti possono leggerlo per la propria configurazione. Ad esempio, Laravel e Symfony usano extra per configurare i loro plugin Composer.
"extra": {
"laravel": {
"dont-discover": [
"barryvdh/laravel-debugbar"
]
},
"my-app-settings": {
"api_key": "some_value"
}
}
minimum-stability e prefer-stable
Queste due opzioni influenzano quali versioni dei pacchetti Composer è disposto a installare.
minimum-stability: Accetta i valoridev,alpha,beta,RC(Release Candidate),stable(default). Se impostato sudev, Composer considererà anche le versioni di sviluppo dei pacchetti. Questo è utile se stai testando librerie molto recenti o ancora in fase di sviluppo. Per la produzione,stableè fortemente raccomandato.prefer-stable:true(default) ofalse. Setrue, Composer cercherà di installare la versione più stabile disponibile di un pacchetto, anche seminimum-stabilityè impostato su un valore meno restrittivo (es.dev). È una buona pratica per bilanciare la sperimentazione con la stabilità in sviluppo.
Esempi Pratici e Casi d'Uso Reali
Vediamo come mettere insieme tutte queste conoscenze in scenari reali di sviluppo web.
Esempio 1: Progetto Laravel Semplice
Un tipico composer.json per un'applicazione Laravel.
{
"name": "laravel/laravel",
"description": "A Laravel application.",
"keywords": ["framework", "laravel"],
"license": "MIT",
"type": "project",
"require": {
"php": "^8.1",
"guzzlehttp/guzzle": "^7.2",
"laravel/framework": "^10.0",
"laravel/sanctum": "^3.2",
"laravel/tinker": "^2.8"
},
"require-dev": {
"fakerphp/faker": "^1.9.1",
"laravel/pint": "^1.0",
"laravel/sail": "^1.18",
"mockery/mockery": "^1.4.4",
"nunomaduro/collision": "^7.0",
"phpunit/phpunit": "^10.0",
"spatie/laravel-ignition": "^2.0"
},
"autoload": {
"psr-4": {
"App\\\\": "app/",
"Database\\\\Factories\\\\": "database/factories/",
"Database\\\\Seeders\\\\": "database/seeders/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\\\": "tests/"
}
},
"scripts": {
"post-autoload-dump": [
"Illuminate\\\\Foundation\\\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
],
"post-update-cmd": [
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-root-package-install": [
"@php -r \\"file_exists('.env') || copy('.env.example', '.env');\\"",
"@php artisan key:generate --ansi"
],
"post-create-project-cmd": [
"@php artisan migrate --ansi"
],
"test": "vendor/bin/phpunit",
"lint": "vendor/bin/pint"
},
"extra": {
"laravel": {
"dont-discover": []
}
},
"config": {
"optimize-autoloader": true,
"preferred-install": "dist",
"sort-packages": true,
"allow-plugins": {
"pestphp/pest-plugin": true,
"php-http/discovery": true
}
},
"minimum-stability": "stable",
"prefer-stable": true
}
Spiegazione:
requireerequire-dev: Elencano le dipendenze core di Laravel e quelle per lo sviluppo/testing. Notarelaravel/sailinrequire-devper l'ambiente di sviluppo Docker.autoload: Mappa i namespace principali di Laravel (App\\,Database\\Factories\\,Database\\Seeders\\) alle rispettive directory.autoload-dev: Mappa il namespaceTests\\alla directorytests/.scripts: Contiene numerosi script agganciati agli eventi di Composer.post-autoload-dumpper la scoperta dei pacchetti di Laravel,post-root-package-installper creare il file.enve generare la chiave,post-create-project-cmdper eseguire le migration dopo la creazione del progetto. Sono presenti anche script personalizzati cometestelintche richiamano gli eseguibili di PHPUnit e Laravel Pint.extra: Usato da Laravel per la configurazione interna, comedont-discoverper disabilitare la scoperta automatica di alcuni pacchetti.config: Ottimizza l'autoloader per la produzione, preferisce l'installazionediste permette plugin specifici.
Esempio 2: Libreria PHP Standalone con Script di Linting e Test
Immaginiamo di creare una piccola libreria riutilizzabile.
{
"name": "mioutente/mia-libreria-utile",
"description": "Una libreria PHP per gestire le date in modo elegante.",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Mario Rossi",
"email": "mario.rossi@example.com"
}
],
"require": {
"php": ">=8.1",
"carbon/carbon": "^2.0"
},
"require-dev": {
"phpunit/phpunit": "^9.5",
"squizlabs/php_codesniffer": "^3.7"
},
"autoload": {
"psr-4": {
"MioUtente\\\\MiaLibreria\\\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"MioUtente\\\\MiaLibreria\\\\Tests\\\\": "tests/"
}
},
"scripts": {
"test": "phpunit",
"check-style": "phpcs --standard=PSR12 src/ tests/",
"fix-style": "phpcbf --standard=PSR12 src/ tests/",
"ci": [
"@test",
"@check-style"
]
},
"config": {
"sort-packages": true
}
}
Spiegazione:
type:library: Indica che questo è un pacchetto riutilizzabile.require: Dipende da Carbon per la gestione delle date.require-dev: Include PHPUnit per i test e PHP_CodeSniffer per il controllo dello stile del codice.autoload/autoload-dev: Configurazione PSR-4 per le classi della libreria e quelle di test.scripts:test: Esegue i test con PHPUnit.check-style: Controlla lo stile del codice usando PSR-12.fix-style: Tenta di correggere automaticamente gli errori di stile.ci: Uno script composito che esegue sia i test che il controllo dello stile, utile per i sistemi di Continuous Integration.
Errori Comuni e Risoluzione
Anche se composer.json è potente, i principianti possono incontrare alcuni ostacoli. Ecco i più comuni:
- Sintassi JSON errata: Un solo errore di virgola, parentesi o virgolette può rendere il file illeggibile. Usa un editor di codice con validazione JSON (come VS Code) o un validatore online.
- Problemi di versione delle dipendenze:
- Conflitti: Composer segnala che due pacchetti richiedono versioni incompatibili della stessa dipendenza. Spesso la soluzione è aggiornare uno dei pacchetti o cercare alternative.
composer why-not <pacchetto> <versione>può aiutare a capire il conflitto. - Versioni troppo restrittive: Usare
1.2.3invece di^1.2.3può impedire l'installazione di correzioni di bug importanti. Sii flessibile con il^. minimum-stabilitytroppo alto: Se stai usando una libreria in sviluppo e Composer non la trova, potresti dover impostare"minimum-stability": "dev"(con cautela).
- Conflitti: Composer segnala che due pacchetti richiedono versioni incompatibili della stessa dipendenza. Spesso la soluzione è aggiornare uno dei pacchetti o cercare alternative.
- Autoloading non funzionante:
- Dimenticare
composer dump-autoload: Ogni volta che modifichi la sezioneautoloadoautoload-dev, devi rigenerare l'autoloader. - Namespace o path errati: Controlla attentamente che il namespace in
psr-4corrisponda a quello nel tuo codice e che il path alla directory sia corretto. - Mancata inclusione di
vendor/autoload.php: Assicurati cherequire __DIR__ . '/../vendor/autoload.php';sia presente all'inizio del tuo script principale.
- Dimenticare
- Script non eseguiti:
- Nome script errato: Assicurati di usare il nome esatto definito in
composer.json. - Permessi di esecuzione: Se uno script cerca di eseguire un file PHP o uno script bash, assicurati che abbia i permessi corretti.
- Comandi non trovati: Se uno script esegue
phpunitmaphpunitnon è nel PATH del sistema o invendor/bin, il comando fallirà. Usa sempre il percorso completo se non sei sicuro, es.vendor/bin/phpunit.
- Nome script errato: Assicurati di usare il nome esatto definito in
Quando incontri problemi, leggi attentamente i messaggi di errore di Composer. Sono spesso molto descrittivi e ti guidano verso la soluzione.
Prossimi Passi
Congratulazioni! Hai fatto un passo significativo nella comprensione di composer.json, uno strumento fondamentale per ogni sviluppatore PHP. Ma il viaggio non finisce qui. Ecco alcuni suggerimenti per approfondire:
- Esplora
composer.lock: Questo file, generato da Composer, registra le versioni esatte di ogni pacchetto installato. È cruciale per la riproducibilità degli ambienti. Scopri perché è importante commetterecomposer.locknel tuo repository Git (e.gitignorevendor/). - Approfondisci i comandi Composer: Oltre a
installeupdate, Composer offre molti altri comandi utili comerequire,remove,search,validate,diagnose,outdated,show. Eseguicomposer listper vederli tutti ecomposer help <comando>per i dettagli. - Crea un tuo pacchetto PHP: Prova a sviluppare una piccola libreria e a configurare il suo
composer.jsonper renderla installabile tramite Composer. Questo ti darà una prospettiva più profonda sulname,type,licenseeautoload. - Utilizza i plugin Composer: Alcuni pacchetti estendono le funzionalità di Composer stesso. Ad esempio,
johnkary/phpunit-speedtrapper identificare test lenti. Esplora come funzionano e come vengono abilitati tramite la sezioneconfig.allow-plugins. - Pratica con i deployment: Sperimenta come
composer install --no-dev --optimize-autoloaderviene utilizzato negli ambienti di produzione per ottimizzare le prestazioni e ridurre le dimensioni dell'applicazione.
Dominare composer.json non è solo una questione di sintassi, ma di comprensione profonda di come i progetti PHP moderni sono costruiti e gestiti. Con questa conoscenza, sarai in grado di creare applicazioni più robuste, manutenibili e performanti. Buon divertimento con il tuo sviluppo web!