# Erik D'Ercole — contenuti completi > Sviluppatore web e sistemista con approccio DevOps, socio e co-fondatore di Encodia. Appunti tecnici su Laravel, PHP e sistemi. Questo file contiene il testo integrale di tutti gli articoli del blog (IT + EN), in Markdown. Indice sintetico con i link ai singoli file: https://erikdercole.com/llms.txt --- title: "Il mio setup attuale (edizione 2026)" date: 2026-07-04 updated: 2026-07-11 lang: it translationKey: my-setup-2026 series: uses tags: [Setup] description: "Hardware, software e strumenti che uso ogni giorno per sviluppare, gestire server e portare avanti i progetti — edizione 2026." --- Hardware, software e strumenti che uso ogni giorno per sviluppare, gestire server e portare avanti i progetti. Questa è l'**edizione 2026**: la aggiorno di tanto in tanto, quindi troverai le versioni precedenti nell'archivio. ## 🖥️ Hardware ### In ufficio - **Mac Studio M4 Max** — La mia macchina principale per sviluppo e lavoro quotidiano. - **Monitor Dell 27" S2725QC Ultra HD 4K** — Monitor principale. - **Monitor LG 27" 27UP650K Ultra HD 4K** — Monitor per attività secondarie. - **Logitech MX Keys** — È davvero piacevole digitare sui tasti di questa tastiera. - **Cuffie Sony WH-1000XM6** — Grazie alla cancellazione di rumore (eccezionale), posso concentrarmi mentre sviluppo, ascoltare musica ed effettuare chiamate. - **Newline NewEye 62 Full HD** — Webcam esterna con *privacy shutter* e 2 microfoni. - **HyperX SoloCast** — Microfono con sensore tap-to-mute e indicatore LED di stato. ### A casa / In trasferta - **MacBook Pro 14" M4 Pro** — Ottimo sia come postazione fissa sia durante viaggi. - **Monitor Samsung U28E590** — A casa lo uso come monitor singolo. - **Monitor Arzopa Z1FC** — Lo utilizzo come monitor secondario quando sono fuori sede. - **OWC Thunderbolt 3** — Docking station davvero completa. - **Spedal Full HD** — Webcam esterna con *privacy shutter*. - **HyperX SoloCast** — Microfono con sensore tap-to-mute e indicatore LED di stato. - **M-Audio Air 192|6** — Scheda audio essenziale per home recording. - **PreSonus ERIS E4.5** — Monitor audio compatti. - **Logitech Pebble Keys 2** — Layout compatto e tasti morbidi; me la posso anche portare in giro. - **Cuffie JBL Tune510BT** — Per non disturbare durante le chiamate o per ascoltare musica. ## ⌨️ Editor & Terminale - **[PhpStorm](https://www.jetbrains.com/phpstorm/)** — IDE principale per PHP e Laravel (ma non solo): refactoring e ispezioni al meglio. - **[Visual Studio Code](https://code.visualstudio.com/)** — Per modifiche rapide, note e a volte per progetti non-PHP. - **[iTerm2](https://iterm2.com/)** — Il terminale che uso tutti i giorni. - **[JetBrains Mono](https://www.jetbrains.com/lp/mono/)** — Il font monospaziato che preferisco. ## 🛠️ Sviluppo - **[PHP + Laravel](https://laravel.com/)** — Lo stack con cui costruisco la maggior parte dei progetti. - **[Laravel Herd](https://herd.laravel.com/)** — Ambiente di sviluppo locale PHP, veloce e senza attriti. - **[Laradumps](https://laradumps.dev/)** — Debug rapido, senza usare `dd()` o XDebug. - **[Pest](https://pestphp.com/)** — Il framework che rende piacevole scrivere i test. - **[DBNgin](https://dbngin.com/)** — Per avere in locale più versioni di MySQL, MariaDB, PostgreSQL, Redis e altri servizi. - **[Docker](https://www.docker.com/)** — Per ambienti riproducibili quando servono più servizi. - **[TablePlus](https://tableplus.com/)** — GUI per database MySQL, PostgreSQL e SQLite. - **[Git + GitHub](https://github.com/)** — Versionamento e code review di tutti i progetti. - **[Sourcetree](https://www.atlassian.com/software/sourcetree)** — Client GIT semplice e completo. - **[Postman](https://www.postman.com/)** — Per testare e condividere API e relativi ambienti. - **[Claude Code](https://claude.com/it/product/claude-code)** — Sviluppo assistito da IA. ## 🧰 App & Utility - **[Raycast](https://www.raycast.com/)** — Launcher e automazioni: lo uso decine di volte al giorno. - **[Spotify](https://www.spotify.com/)** — La musica mi accompagna quasi tutto il giorno. - **[Discord](https://discord.com/)** — Messaggistica interna con colleghi e collaboratori. - **[Ferdium](https://ferdium.org/)** — Chat, messaggistica, email in un'unica applicazione. - **[1Password](https://1password.com/)** — Gestione di password e secret. - **[BitWarden](https://bitwarden.com/)** — Gestione di password e secret a uso personale. - **[Notion](https://www.notion.com/)** — Note, appunti personali e tracciamento di ciò che sto imparando. - **[ClickUp](https://clickup.com/)** — Gestione attività, flussi di lavoro, progetti. - **[Freshdesk](https://www.freshworks.com/freshdesk/)** — Consente di gestire i ticket aperti dai clienti. - **[Deepl](https://www.deepl.com/)** — Traduzioni affidabili in più lingue. ## ☁️ Hosting & Servizi - **[AWS](http://aws.amazon.com/)** — Hosting di applicazioni, storage, email transazionali e servizi gestiti per i progetti che lo richiedono. - **[DigitalOcean](https://www.digitalocean.com/)** — Droplet e server per applicazioni e siti dei clienti. - **[Cloudflare Pages](https://pages.cloudflare.com/)** — Hosting di questo sito, con deploy automatici. - **[Nginx](https://nginx.org/)** — Web server e reverse proxy sui miei server. - **[GitHub Actions](https://github.com/features/actions)** — CI/CD: test e deploy automatizzati. ======================================================================== --- title: "PHPStan: percorsi dei file cliccabili nel terminale grazie a phpstan.neon" date: 2026-05-01 lang: it translationKey: phpstan-percorsi-file-cliccabili-nel-terminale tags: [PHPStan, PHP] description: "Come configurare phpstan.neon per ottenere percorsi completi negli errori di PHPStan, così da poterli aprire direttamente nell'IDE con un click." --- Supponiamo che il tuo progetto PHP/Laravel abbia una struttura simile a questa: ``` . ├── artisan ├── (...) ├── routes ├── src │ ├── App │ └── Domain ├── (...) ├── vendor └── vite.config.js ``` Stai lavorando con **PHPStan** nel terminale e, quando analizza il tuo codice, gli errori si presentano così: ``` ------ -------------------------------------------------------------------------------------------------------------------------------------------------------------------- Line App/Concerns/PasswordValidationRules.php (in context of class App\Actions\Fortify\CreateNewUser) ------ -------------------------------------------------------------------------------------------------------------------------------------------------------------------- 17 Method App\Actions\Fortify\CreateNewUser::passwordRules() should return array|Illuminate\Contracts\Validation\ValidationRule|string> but returns array. 🪪 return.type ------ -------------------------------------------------------------------------------------------------------------------------------------------------------------------- ``` Apparentemente i percorsi indicati sono corretti, ma scopri che in realtà sono **incompleti**: manca la parte iniziale che li àncora alla *root* del progetto. Terminali come iTerm2, o il terminale integrato di PhpStorm e VS Code, riescono a rendere correttamente *cliccabile* un percorso relativo solo se è completo rispetto alla *root* del progetto. In questo caso, il percorso corretto nel filesystem è `src/App/Concerns/PasswordValidationRules.php`. Con un percorso troncato come `App/Concerns/PasswordValidationRules.php`, il terminale non sa dove cercare il file o interpreta i link in maniera errata. Nel mio caso (utilizzo **iTerm 2** su macOS), quando premo CMD e faccio click sul link, questo viene interpretato come un indirizzo di tipo `http://` e quindi aperto nel browser (che non sa cosa fare). ## Perché succede? Se il tuo `phpstan.neon` è simile a questo: ```yaml showLineNumbers parameters: paths: - src ``` ...allora PHPStan riporta i percorsi degli errori *relativamente* a quest'unica sottodirectory, facendoli risultare incompleti: Il percorso `App/Concerns/PasswordValidationRules.php` è relativo a `src`, non alla root del progetto. Il terminale non riesce a risolverlo e il link non funziona. ## La soluzione Il modo più semplice è impostare due o più percorsi nel nodo `paths` di `phpstan.neon`: ```yaml showLineNumbers parameters: paths: - src - tests ``` In questo modo gli errori saranno riportati con percorsi completi relativi alla root: ``` ------ -------------------------------------------------------------------------------------------------------------------------------------------------------------------- Line src/App/Concerns/PasswordValidationRules.php (in context of class App\Actions\Fortify\CreateNewUser) ------ -------------------------------------------------------------------------------------------------------------------------------------------------------------------- 17 Method App\Actions\Fortify\CreateNewUser::passwordRules() should return array|Illuminate\Contracts\Validation\ValidationRule|string> but returns array. 🪪 return.type ------ -------------------------------------------------------------------------------------------------------------------------------------------------------------------- ``` Il percorso `src/App/Concerns/PasswordValidationRules.php` ora è completo e cliccabile: con un click si apre direttamente il file alla riga giusta nell'IDE. ## Soluzione alternativa E se per qualche motivo vuoi che PHPStan analizzi solamente una cartella (es. `src`)? Aggiungi questo blocco in `phpstan.neon`: ```yaml showLineNumbers services: relativePathHelper: class: PHPStan\File\SimpleRelativePathHelper arguments: currentWorkingDirectory: %currentWorkingDirectory% ``` ## Conclusione Una piccola svista nella configurazione di `phpstan.neon` è sufficiente a rendere i percorsi negli errori inutilizzabili nel terminale. Con la giusta configurazione, i percorsi risulteranno completi e cliccabili, permettendo di navigare direttamente al problema con un click. Migliore **Developer eXperience** = sviluppatore più felice 😄 ======================================================================== --- title: "Proteggere l'endpoint /livewire/update dai bot con un middleware Laravel" date: 2026-02-21 updated: 2026-02-26 lang: it translationKey: proteggere-livewire-update-con-un-middleware-laravel tags: [Laravel, Livewire, Security] description: "Come bloccare i bot che attaccano l'endpoint /livewire/update usando un middleware Laravel che valida Content-Type, X-Livewire, Referer e cookie di sessione." ---
Aggiornamento — 26 febbraio 2026: Livewire 4.2.0 include nativamente un middleware RequireLivewireHeaders che valida la presenza degli header X-Livewire e Content-Type: application/json su ogni richiesta all'endpoint di update (PR #9965). Se usi Livewire 4.2.0 o superiore, non è necessario aggiungere un middleware personalizzato. L'approccio descritto in questo articolo rimane valido per installazioni Livewire 3.x e come spiegazione del meccanismo di difesa adottato anche dal team ufficiale.
Immagina di aprire [BugSnag](https://www.bugsnag.com/) un lunedì mattina e trovare centinaia di errori in arrivo sull'endpoint `/livewire/update`. Errori come: > `Cannot assign array to property App\Livewire\ExampleComponent::$exampleName of type bool` Diverse proprietà coinvolte, ma sempre lo stesso schema: qualcuno sta inviando un array dove PHP si aspetta un tipo primitivo. È quello che è capitato a noi in [Encodia](https://www.encodia.it/). ## Il problema: errori di tipo a raffica su BugSnag In [Encodia](https://www.encodia.it/) abbiamo realizzato più di un'applicazione web utilizzando [Livewire](https://livewire.laravel.com/). Qualche settimana fa, abbiamo iniziato a ricevere segnalazioni da [BugSnag](https://www.bugsnag.com/) relative a errori di questo tipo: > **POST** /livewire/update `Cannot assign array to property App\Livewire\ExampleComponent::$exampleName of type bool` Decine o centinaia di segnalazioni, relative a proprietà pubbliche Livewire, in cui cambia il nome delle proprietà e il tipo atteso, ma l'errore è sempre simile: nel *payload*, viene inviato un array per idratare una specifica proprietà, ma dato che quella proprietà è di tipo differente (`bool`, `string`, ecc.), PHP restituisce un errore. ## Come abbiamo identificato la causa Ispezionando le chiamate su BugSnag, questo header ha attirato subito la mia attenzione: ``` "user-agent": "python-requests/2.32.4" ``` Cercando in rete, salta fuori che si tratta di tentativi di sfruttare la vulnerabilità [CVE-2025-54068](https://www.synacktiv.com/en/publications/livewire-remote-command-execution-through-unmarshaling), presente su installazioni Livewire **3.x** fino alla versione **v3.6.3**. La vulnerabilità consente a un attaccante di eseguire codice arbitrario sul server (*Remote Code Execution*) manipolando il payload di idratazione dei componenti — da qui gli errori di tipo che vedevamo su BugSnag. Avevamo già aggiornato le applicazioni oggetto delle segnalazioni; tuttavia, gli errori relativi al tipo sono continuati ad arrivare, anche su applicazioni che usano **Livewire 4.x**. Il motivo è che gli scanner automatici non verificano la versione di Livewire installata prima di tentare l'attacco: semplicemente *martellano* tutti gli endpoint `/livewire/update` che trovano, indipendentemente dalla versione. ## La soluzione: un middleware Laravel Ho aggiunto questo middleware: ```php showLineNumbers routeIs('default-livewire.update')) { return $next($request); } if ($request->header('Content-Type') !== 'application/json') { $this->abort('Header application/json mancante'); } if (! $request->hasHeader('X-Livewire')) { $this->abort('Header X-Livewire mancante'); } if (! $request->headers->get('referer')) { $this->abort('Referer mancante'); } if (! $request->cookies->has(config()->string('session.cookie'))) { $this->abort('Sessione Laravel non valida'); } return $next($request); } private function abort(string $reason): never { $showDetails = app()->isLocal(); abort(code: 403, message: $showDetails ? $reason : ''); } } ``` Se la richiesta corrente non coinvolge Livewire, procede oltre. Viceversa, controlla che * sia presente l'header `X-Livewire` * sia presente il `referer` * sia presente il cookie di sessione Se almeno una di queste condizioni non è soddisfatta, ritorna un errore `403 (Forbidden)` senza alcun messaggio di spiegazione. Solo in ambiente locale - per rendere più facile un eventuale *debug* - ritorna il motivo per cui è stato erogato `403`. Il middleware va registrato in modo che venga eseguito **prima** di `\Illuminate\Session\Middleware\StartSession`. In questo modo, le richieste BOT vengono bloccate prima ancora che Laravel inizializzi la sessione: nessun overhead inutile, nessun accesso al database per richieste che verranno comunque rifiutate. ## Alternativa: bloccare i BOT a livello di web server Se non si vuole utilizzare un middleware, è anche possibile configurare il web server per bloccare le richieste sospette direttamente a livello Nginx, prima che PHP venga coinvolto. Ad esempio, per bloccare tutte le richieste a `/livewire/update` prive dell'header `X-Livewire`: ```nginx showLineNumbers location /livewire/update { if ($http_x_livewire = "") { return 403; } # resto della configurazione... } ``` Il vantaggio è che Nginx respinge la richiesta senza che PHP venga mai avviato. Lo svantaggio è che la logica di validazione è separata dall'applicazione e va mantenuta in sincronia con eventuali cambiamenti futuri all'endpoint. ## Conclusione Il middleware non sostituisce l'aggiornamento di Livewire, ma agisce come un filtro a monte: richieste prive degli header attesi vengono bloccate prima ancora che PHP cominci a processare il payload. Se usi BugSnag o un sistema analogo, vale la pena controllare se stai ricevendo errori simili: `python-requests` nell'user-agent è un segnale inequivocabile. Il fatto che il team di Livewire abbia adottato esattamente questo approccio nella versione 4.2.0 — un middleware dedicato che valida gli stessi header — conferma che la direzione era quella giusta. Se sei già su Livewire 4.2.0+, la protezione è inclusa senza bisogno di codice aggiuntivo. Se non puoi aggiornare subito, il middleware descritto in questo articolo rimane una soluzione efficace. ======================================================================== --- title: "Laravel Day 2025 @ Verona - Com'è andata" date: 2025-11-21 lang: it tags: [Laravel, Eventi] description: "Laravel Day è una conferenza dedicata alla community Laravel, che si svolge ogni anno a Verona ed è organizzata dal GRUSP." --- ## È sempre bello partecipare in presenza Anche quest'anno si è svolto a **Verona** il **Laravel Day** durante la giornata del 20 novembre 2025. Si tratta di una conferenza dedicata agli **sviluppatori Laravel**, organizzata dal [GRUSP](https://www.grusp.org/), durante la quale i relatori parlano di metodologie, tecnologie, prodotti, esperimenti ed esperienze legati al [nostro framework PHP preferito](https://laravel.com/). È un piacevole appuntamento fisso al quale io e il mio socio Davide non possiamo mancare 😁 È un'ottima occasione per * restare aggiornati sulle nuove tecnologie * condividere esperienze e approcci * migliorare le proprie competenze * rivedere colleghi e amici conosciuti in precedenti edizioni o in occasioni simili * conoscere nuove persone, fare *networking* e avviare nuove relazioni professionali * visitare la sempre splendida Verona, assaggiare i piatti della cucina locale e i vini veneti 😋 È anche possibile seguire l'evento *online*, ma a mio parere così facendo viene a mancare la parte migliore: la possibilità di interagire con altra persone, fare due chiacchiere, conoscere i relatori, "stressarli" con le domande, ecc. Ad esempio, da casa non potresti mai vedere i **Fab Four** riuniti: Jenno, Freek, Marco e Erik 😁 ![Jenno, Freek, Marco, Erik](/blog/laravel-day-2025-verona/fab-four.jpg "Jenno, Freek, Marco, Erik") ## Il calendario dei *talk* Durante la registrazione e la consegna del *badge* nominativo, il GRUSP distribuisce a ciascun partecipante una borsa di tela contenente alcuni gadget, materiale informativo e un apprezzatissimo libro omaggio di **Valerio Barbera** (uno dei relatori): "**Start with AI agents in PHP - Create full featured AI agents in your PHP applications**". ![AI agents](/blog/laravel-day-2025-verona/ai-agents.jpg "AI agents") A pochi metri, **Pascal Baljet** del team Laravel distribuisce, a chi inquadra un codice QR, un simpatico antistress a forma di *Lambo* arancione targata *$PHP*. ![La Lambo antistress](/blog/laravel-day-2025-verona/lambo.jpg "La Lambo antistress") ### Freek van der Herten - "Uncharted packages" Dopo la consueta presentazione del GRUSP, apre le danze nientepopodimeno che **Freek** di Spatie. Non necessita sicuramente di presentazioni 😁 Nel suo *talk* in inglese, illustra dieci package "meno famosi" fra quelli realizzati da lui e i suoi colleghi, ma non per questo meno importanti o utili. Come sempre, le sue presentazioni sono estremamente chiare, semplici e scorrevoli. Personalmente avrei preferito che parlasse di altro, ma è stato comunque molto piacevole ascoltarlo. ![Freek van der Herten](/blog/laravel-day-2025-verona/freek.jpg "Freek van der Herten") ### Eleonora Scala - "Contract testing e Laravel: affidabilità nelle API tra microservizi" Argomento che conosco molto marginalmente ma, a mio parere, parecchio interessante. Eleonora ci spiega come validare e testare che due sistemi comunichino correttamente fra loro (rispettando un contratto), quali strumenti utilizzare (es. Pact), quali metodologie adottare. Probabilmente l'esposizione avrebbe richiesto un tempo maggiore e più esempi pratici, ma in 35 minuti non è semplice. ![Eleonora Scala](/blog/laravel-day-2025-verona/eleonora-scala.jpg "Eleonora Scala") ### Roberto Gallea - "Micro monoliti in Laravel: semplificare la complessità con applicazioni federate" Roberto è una presenza fissa al Laravel Day. Durante il suo intervento, dà un assaggio di ciò di cui parlerà il giorno seguente in un workshop (a cui non ho partecipato): micro monoliti, applicazioni federate, sistemi di Single Sign On, SAML, OAuth, come le applicazioni possono condividere o scambiarsi i dati, vantaggi e svantaggi dei vari approcci. Una breve demo mostra il funzionamento del SSO con applicazioni esposte su domini differenti. ![Roberto Gallea](/blog/laravel-day-2025-verona/roberto-gallea.jpg "Roberto Gallea") ### Daniele Barbaro - "CI/CD per applicazioni Laravel: automatizza, ottimizza e scala" Intervento molto interessante: Daniele ha mostrato quali strumenti utilizza per gestire la *codebase* dei propri progetti (Pint, Larastan, Rector, ecc.) e, soprattutto, come gestisce i rilasci continui di codice entrando in dettaglio su *GitHub Actions*, consigli e buone pratiche relative a CI/CD. Ho apprezzato molto l'entusiasmo e l'esaustività con cui ha parlato di questi argomenti. ![Daniele Barbaro](/blog/laravel-day-2025-verona/daniele-barbaro.jpg "Daniele Barbaro") ### Roberto Butti - "Dentro Laravel: un viaggio tra i sorgenti del framework" Un'altra presenza fissa sia alle conferenze PHP/Laravel che online. Roberto ci spiega come la curiosità lo spinga spesso a "scorrazzare" nella *codebase* del framework Laravel, portandolo a capire bene come funzionano le cose "sotto al cofano": ad esempio, come vengono gestite le chiamate ai metodi esposti dalle *facade* di Laravel, come funzionano le dipendenze dei vari "pezzi" del framework (es. le `Collections` e come sia possibile usarle anche in un progetto PHP senza Laravel) e così via. Discute anche in modo critico ma obiettivo alcune scelte del team Laravel, del tipo *"non sono troppo d'accordo con questa scelta, ma capisco perché l'abbiano fatta"*. ![Roberto Butti](/blog/laravel-day-2025-verona/roberto-butti.jpg "Roberto Butti") ### Lightning talks Dopo la pausa pranzo (nota dolente: trovo che la qualità del catering negli anni sia calata parecchio), si riprende con i *lightning talks*, ovvero dei mini interventi da 5 minuti in cui si può illustrare brevemente un argomento a scelta, dopo essersi iscritti su un tabellone. A tal proposito, rompe nuovamente il ghiaccio **Freek** e, appena sale sul palco per parlarci di **async PHP**, racconta di aver visto il tabellone vuoto, così pensa "scrivo il mio nome, così magari invoglio qualcun altro a seguire il mio esempio". Come si fa a non amare questo uomo? 🤩 Il suo mini-talk è molto interessante, ma purtroppo in 5 minuti non riesce a dire tutto ciò che avrebbe voluto e, giustamente, deve lasciare il posto agli altri. Seguono **Marco Introini** che ci parla di sicurezza e crittografia post-quantistica e, a concludere i *lightning talk*, **Giulio Pecorella** che ci spiega come ha(nno) ridotto drasticamente il numero di tabelle usate da un'applicazione Laravel (un progetto regionale legato alla diffusione della lingua friulana - un **dizionario italiano - friulano**). ![Marco Introini](/blog/laravel-day-2025-verona/marco-introini.jpg "Marco Introini") ![Giulio Pecorella](/blog/laravel-day-2025-verona/giulio-pecorella.jpg "Giulio Pecorella") ### Pascal Baljet - "New in Inertia" Seguo Pascal su Twitter/X da tempo e devo dire che è uno sviluppatore davvero preparato ed entusiasta. Con un inglese impeccabile illustra rapidamente quali sono le principali novità e ottimizzazioni della versione 2.0 di **Inertia**, mostrando una demo basata su Vue. Un po' come ha fatto Freek durante il suo intervento, modifica frammenti di codice ( principalmente **Vue**, nel suo caso) e mostra quali sono i risultati nel browser. Interessante la semplicità con cui è possibile realizzare un **infinite scroll** con Inertia. ![Pascal Baljet](/blog/laravel-day-2025-verona/pascal-baljet.jpg "Pascal Baljet") ### Roberto Negro - "Dominare la complessità: design patterns e pacchetti per backoffice Laravel eccellenti" Roberto prende come esempio una classica situazione che la maggior parte degli sviluppatori ha vissuto: il progetto B è un copia e incolla del progetto A, con qualche variazione; per pigrizia o mancanza di tempo, si fa lo stesso con un progetto C e così via. Con il tempo, tuttavia, occorre effettuare modifiche, correggere bug o introdurre nuove funzionalità su tutti quei progetti, separatamente: non proprio la migliore delle strategie. Meglio allora realizzare alcuni package per sfruttare la riusabilità, seguire determinate buone pratiche e design pattern, in modo da condividere funzionalità fra più progetti. Questo *talk* è stato denso di nozioni, casi d'uso ed esempi pratici molto interessanti, con accenni al *Domain Driven Design* o a come gestire l'aggiornamento in locale dei propri pacchetti privati. ![Roberto Negro](/blog/laravel-day-2025-verona/roberto-negro.jpg "Roberto Negro") ### Valerio Barbera - "PHP AI revolution - integra agenti intelligenti nella tua applicazione Laravel con Neuron AI" Poteva mancare un *talk* su uno degli argomenti più attuali del momento? A dire il vero sono rimasto stupito che in questo Laravel Day non si sia parlato maggiormente di **IA**. Valerio ci spiega come è perché è nato il framework open source [Neuron AI](https://www.neuron-ai.dev/) dedicato alla creazione di **agenti AI**: dal fatto che, circa nel 2023, nel mondo PHP non ci fosse nulla di simile a disposizione ( quantomeno per come lo intendeva lui) e che gli sviluppatori fossero in qualche modo costretti a rivolgersi a Python o altri linguaggi. Dopo una breve demo, spiega brevemente come funziona Neuron AI e come si può integrare con applicazioni PHP o Laravel. Purtroppo il tema scuro dell'IDE e la ridotta dimensione del font hanno un po' penalizzato la possibilità di seguire il codice, ma questo intervento ha dato a tutti sicuramente un ottimo spunto per iniziare a lavorare con gli agenti AI. ![Valerio Barbera](/blog/laravel-day-2025-verona/valerio-barbera.jpg "Valerio Barbera") ### Leonardo Rossi - "Ma davvero Laravel può girare dentro Node.js?" Chiude la giornata l'intervento di Leonardo Rossi, già presente in edizioni precedenti. Leonardo è molto preparato e trasversale su vari argomenti e tecnologie; ci spiega come l'azienda in cui lavora abbia realizzato un application server che, tra i vari linguaggi e framework, consente anche di far girare un'applicazione Laravel, illustrando a grandi linee quali siano le difficoltà tecniche e le soluzioni adottate. Personalmente non sento la necessità di adottare una soluzione del genere, ma in determinate realtà aziendali che fanno uso di numerose tecnologie, linguaggi e framework, probabilmente un prodotto di questo tipo può rivelarsi la soluzione più adatta. ![Leonardo Rossi](/blog/laravel-day-2025-verona/leonardo-rossi.jpg "Leonardo Rossi") ## Cosa ci portiamo a casa Partecipare al Laravel Day è sempre un'**esperienza positiva**. Anche quest'anno i vari *talk* mi hanno dato vari spunti in merito ad argomenti, prodotti o metodologie da iniziare a usare o da approfondire. ![Il mio badge](/blog/laravel-day-2025-verona/erik.jpg "Il mio badge") Sicuramente vorrei riprendere in mano due strumenti con cui finora ho solo "giocato" per fare qualche prova ma nulla di più: **Inertia** e **Neuron AI**. È stato bello rivedere volti noti e fare nuove conoscenze, soprattutto fra i membri del **gruppo Telegram Laravel Italia**, che ringrazio ancora. Ci vediamo al Laravel Day 2026! ======================================================================== --- title: "Come sfruttare gli script Composer per automatizzare il workflow PHP/Laravel" date: 2025-10-24 lang: it translationKey: how-to-use-composer-scripts-to-automate-your-php-laravel-workflow tags: [Composer, PHP, Laravel] description: "Definisci script personalizzati in Composer per automatizzare il tuo workflow PHP/Laravel" --- Molti sviluppatori PHP/Laravel usano [Composer](https://getcomposer.org/) solo per installare dipendenze, ma pochi sfruttano i vantaggi offerti dagli **script Composer**. Gli script permettono di automatizzare task ricorrenti come test, analisi statica del codice, refactoring o setup iniziale del progetto, semplificando il workflow e riducendo errori. Laravel stesso ne definisce alcuni, in modo da semplificare la configurazione del progetto o l'esecuzione di determinati task. ## Cos'è uno script Composer Nella sezione `scripts` di un file `composer.json` è possibile definire script personalizzati. Ad esempio: ```json showLineNumbers { "scripts": { "test": "XDEBUG_MODE=off ./vendor/bin/pest --parallel" } } ``` Dopo aver definito lo script `test`, se da shell esegui ```shell composer test ``` verranno eseguiti i test (unit, feature, ecc.) del progetto, in questo caso utilizzando [Pest](https://pestphp.com/), in parallelo e disabilitando XDebug (che nel mio ambiente locale è sempre attivo - viceverse, potresti tenerlo sempre disattivato e abilitarlo solo all'occorrenza). ## Perché usare gli script Composer? I vantaggi sono molteplici: * ✅ consistenza: tutti i membri del *team* di sviluppo usano gli stessi comandi * ⚙️ automazione: niente più comandi lunghi, dimenticati o con parametri errati * 🧩 integrazione CI/CD: gli script si integrano facilmente nelle pipeline di GitHub Actions, GitLab CI, ecc. * 💡 pulizia del progetto: evita di aggiungere al repository file shell duplicati e mantiene la configurazione nel `composer.json` ## Consigli pratici * per i **nomi degli script**, non usare nomi di librerie, prodotti o tecnologie, ma termini che rappresentino il compito che si vuole eseguire. Il motivo è presto detto: se oggi usi una libreria per eseguire quel task, domani potresti sostituirla con un'altra che esegue lo stesso compito (magari meglio); in quel caso, dovresti cambiare il nome dello script. Esempio: ❌ `composer phpunit` ❌ `composer pest` ✅ `composer test` Altro esempio: ❌ `composer rector` ✅ `composer refactor` * utilizza gli stessi nomi di script in tutti i progetti PHP/Laravel, in modo da ritrovarli ovunque e poterli usare facilmente e con familiarità * se un progetto non prevede ad esempio l'uso di PHPStan o di Rector, non aggiungere gli script corrispondenti al file `composer.json`: renderli disponibili potrebbe confondere gli altri sviluppatori * se hai **XDebug** attivo, usa `XDEBUG_MODE=off` a inizio comando per disabilitarlo e velocizzare l'esecuzione (ad esempio della suite di test o dell'analisi statica) ## Esempi pratici di script utili ### Analisi statica del codice * `analyse` ```json "analyse": "XDEBUG_MODE=off ./vendor/bin/phpstan analyse --memory-limit 2G --ansi" ``` Esegue l'analisi statica del codice con [PHPStan](https://phpstan.org/). * `analyse-clear-cache` ```json "analyse-clear-cache": "./vendor/bin/phpstan clear-result-cache" ``` Elimina la cache di PHPStan (a volte si rende necessario in caso di problemi con l'analisi statica). ### Refactoring del codice * `refactor` ```json "refactor": "XDEBUG_MODE=off rector process --ansi", ``` Esegue il refactoring del codice tramite [Rector](https://getrector.org/). * `refactor-dry` ```json "refactor-dry": "XDEBUG_MODE=off rector --dry-run --ansi" ``` Esegue Rector in modalità `dry run`, senza modificare i file: mostra solo quali modifiche verranno apportate. ### Formattazione del codice * `format` ```json "format": "vendor/bin/pint --dirty --parallel --ansi" ``` Esegue la formattazione del codice con [Pint](https://laravel.com/docs/12.x/pint) solo sui file modificati e in parallelo. ### Test e code coverage * `test` ```json "test": "XDEBUG_MODE=off ./vendor/bin/pest --parallel --bail --colors=always" ``` Esegue i test con [Pest](https://pestphp.com/). I test vengono eseguiti in parallelo e se uno fallisce, il resto viene interrotto. * `test-architecture` ```json "test-architecture": "XDEBUG_MODE=off ./vendor/bin/pest --parallel --bail --group=architectural --colors=always" ``` Esegue solo gli [architecture test](https://pestphp.com/docs/arch-testing) con [Pest](https://pestphp.com/). * `test-coverage` ```json "test-coverage": "php -d memory_limit=-1 ./vendor/bin/pest --coverage --min=80" ``` Esegue i test con *code coverage* e mostra il report. Se la *coverage* è inferiore all'80%, il comando fallisce. * `type-coverage` ```json "type-coverage": "XDEBUG_MODE=off ./vendor/bin/pest --type-coverage" ``` Esegue la [type coverage](https://pestphp.com/docs/type-coverage) con [Pest](https://pestphp.com/) per segnalare in quali file sia possibile tipizzare parametri e valori di ritorno. ## Per concludere Gli script Composer sono un modo semplice ma potente per standardizzare e velocizzare lo sviluppo in PHP e Laravel. Inizia definendo pochi comandi chiave e vedrai subito la differenza nel tuo flusso di lavoro quotidiano. Aiuteranno anche gli altri sviluppatori nel tuo team e forniranno un metodo standardizzato per eseguire i task necessari. Quelli sopra riportati sono quelli che utilizzo più frequentemente; definiscili in base alle tue esigenze e a quelle del tuo team. ======================================================================== --- title: "Perché Nginx crea un file "off"?" date: 2025-10-23 lang: it translationKey: why-nginx-creates-a-file-named-off tags: [Nginx, Linux, DevOps] description: "Una configurazione errata di Nginx può portare a scrivere un misterioso file “off"" --- Se hai mai trovato un file chiamato `off` in `/etc/nginx/`, ecco spiegato il motivo. **Nginx** consente di **disattivare i log degli errori**, ma bisogna farlo utilizzando la sintassi corretta: ```nginx error_log /dev/null; ``` Se per sbaglio scrivi: ```nginx error_log off; ``` Nginx interpreta `off` come il percorso di un file - non come una parola chiave - e tenterà quindi di creare (o scrivere su) un file `/etc/nginx/off`. Ecco perché potresti trovare un file vuoto chiamato `off` nella directory di configurazione. Questo succede perché il parser di configurazione di Nginx non riconosce `off` come valore speciale per la direttiva `error_log`. Si aspetta invece un **percorso** come primo argomento e, opzionalmente, un livello di log (`warn`, `error`, `debug`). Per **disattivare davvero i log**, basta redirigerli a `/dev/null`, che scarta ogni output: ```nginx error_log /dev/null; ``` Con questa semplice correzione, eviterai la creazione del misterioso file `off`. Ti è mai capitato? A me sì 😁 Ecco perché ho pensato di scrivere un breve articolo a riguardo. ======================================================================== --- title: "Perché dimenticare variabili .env può far male (e come Laravel Health Env Vars ti aiuta)" date: 2025-09-21 lang: it translationKey: laravel-health-env-vars-perche-dimenticare-variabili-env-puo-far-male-laravel tags: [Laravel, PHP, DevOps] description: "Laravel Health Env Vars è un package che ti aiuta a gestire le variabili .env in modo efficiente." --- Le **variabili d'ambiente** sono un tassello fondamentale di ogni applicazione Laravel. Vengono usate per dati sensibili, chiavi API, credenziali di accesso ai database e per altri valori di configurazione utilizzati dall'applicazione. Cosa succede se ci si dimentica di definire una variabile .env o la si valorizza in maniera errata? Si possono verificare bug, problemi di deploy o — peggio ancora — problemi di sicurezza. E qui entra in gioco [Laravel Health Env Vars](https://github.com/encodia/laravel-health-env-vars). Questo package per Laravel aiuta a controllare la presenza e la validità delle variabili .env durante i controlli sullo stato di salute dell'applicativo. ## Perché è importante Se lavori da solo sul tuo portatile, è piuttosto difficile che ti dimenticherai di aggiungere una variabile d'ambiente su un server di produzione. Tuttavia, potrebbe succedere e, ancora peggio, potresti impostarla al valore sbagliato. Le cose si fanno più complicate in un **team di sviluppo**: con più sviluppatori — o anche solo lo stesso sviluppatore che utilizza più postazioni — aggiornare una variabile d'ambiente implica che ogni soggetto coinvolto venga notificato (via e-mail, Slack, Discord, Teams, Telegram, a voce), ed è facile dimenticarsi. Qualcuno è destinato a perdersi un pezzo. Persino gli sviluppatori più esperti a volte dimenticato di impostare su un nuovo server o in una pipeline CI/CD una variabile d'ambiente richiesta. Scordarsene può portare a: * errori di runtime * integrazioni mancanti o parti applicative non funzionanti * errori in produzione difficili da identificare e risolvere Controllando in modo proattivo le proprie variabili .env, puoi identificare ed evitare potenziali problemi **prima** che si verifichino e abbiano ripercussioni sugli utenti finali. ## Cosa fa questo package In [Encodia](https://www.encodia.it) utilizziamo da tempo l'eccellente package [Laravel Health](https://github.com/spatie/laravel-health) di Spatie, per il quale sembrava che non ci fosse un **controllo dedicato** per le **variabili .env**. Per questo motivo, **ho sviluppato [Laravel Health Env Vars](https://github.com/encodia/laravel-health-env-vars)**, che si integra con il package di Spatie per fornire uno strumento di controllo semplice ma efficace: * verifica che le variabili .env richieste siano presenti * opzionalmente, verifica i loro valori * può eseguire controlli solo su determinati ambienti * fa fallire gli _health check_ se qualcosa risulta mancante o non configurato correttamente Puoi eseguire i controlli sullo stato di salute dell'applicazione in diversi casi: * quando **configuri/allinei un progetto in locale**: ad esempio, in [Encodia](https://www.encodia.it/) abbiamo sviluppato uno strumento da riga di comando ad uso interno chiamato _EDIT_ per semplificare la vita agli sviluppatori — gestire compiti come clonare un repository GIT o fare _pull_, installare le dipendenze via Composer o NPM, lanciare le _migration_ e i _seed_, ecc. Uno dei compiti principali di _EDIT_ è quella di eseguire automaticamente gli _health check_, in modo da assicurarsi che sia tutto configurato correttamente prima di iniziare a sviluppare * **in fase di deploy**: che sia in una pipeline CI/CD, in uno script bash, o tramite altro _workflow_, i controlli sulla salute applicativa possono essere eseguiti per verificare che tutte le variabili d'ambiente richieste siano presenti e valide, prima di aver terminato il deploy. ## Cosa non fa questo package Laravel Health Env Vars non controlla le variabili .env una volta che il deploy è terminato — e ora ti spiego perché. Nella maggior parte dei deploy, è buona norma eseguire `php artisan config:cache`. Dopo aver lanciato quel comando, ogni chiamata a `env('VARIABLE')` restituirà _null_. Consulta la sezione [Avvertenze](https://github.com/encodia/laravel-health-env-vars?tab=readme-ov-file#caveats) per maggiori dettagli. ## Installazione e setup Installare il package è semplice: ```bash composer require encodia/laravel-health-env-vars ``` ## Utilizzo Registra un controllo come ogni altro _check_ di Laravel Health: ```php showLineNumbers // typically, in a service provider use Spatie\Health\Facades\Health; use Spatie\Health\Checks\Checks\UsedDiskSpaceCheck; use Encodia\Health\Checks\EnvVars; Health::checks([ // From Spatie's examples UsedDiskSpaceCheck::new() ->warnWhenUsedSpaceIsAbovePercentage(70) ->failWhenUsedSpaceIsAbovePercentage(90), // Many other checks... /* * Check that SOME_API_KEY and MAIL_FROM_ADDRESS variables are * set (no matter in which environment) */ EnvVars::new() ->requireVars([ 'SOME_API_KEY', 'MAIL_FROM_ADDRESS', ]) ]); ``` Quando esegui `php artisan health:check --fail-command-on-failing-check`, se `SOME_API_KEY` o `MAIL_FROM_ADDRESS` non sono state impostate nel file `.env`, il comando mostrerà un errore e ritornerà un _exit code_ diverso da zero. Potresti aver bisogno di controllare che una variabile sia valorizzata solo in un **ambiente specifico** (o in più di uno). Supponiamo che tu voglia controllare che `EXTENDED_DEBUG_MODE` sia stata valorizzata (solo in ambiente _local_) e che lo sia stata anche `BUGSNAG_API_KEY` (solo in _produzione_, perché non vogliamo che BugSnag sia abilitato negli ambienti di test/locale/staging/UAT): ```php showLineNumbers use Spatie\Health\Facades\Health; use Encodia\Health\Checks\EnvVars; Health::checks([ // ... // (other checks) // ... /* * Check that SOME_API_KEY and MAIL_FROM_ADDRESS variables are * set (no matter in which environment). * * Only in staging, ensure EXTENDED_DEBUG_MODE has been set. * * Additionally, only in production, * ensure BUGSNAG_API_KEY has been set. */ EnvVars::new() ->requireVars([ 'SOME_API_KEY', 'MAIL_FROM_ADDRESS', ]) ->requireVarsForEnvironment('local', [ 'EXTENDED_DEBUG_MODE' ]) ->requireVarsForEnvironment('production', [ 'BUGSNAG_API_KEY' ]); ]); ``` Hai bisogno di verificare che una variabile sia stata valorizzata a un **valore specifico**? Puoi usare `requireVarsMatchValues()` per effettuare questo controllo, indipendentemente dall'ambiente corrente di esecuzione. Se hai bisogno di eseguire questo controllo solo se l'ambiente di corrente corrisponde a quello desiderato o quelli desiderati, puoi usare `requireVarsForEnvironment()` o `requireVarsForEnvironments()`. Vediamo un esempio: ```php showLineNumbers use Encodia\Health\Checks\EnvVars; use Spatie\Health\Facades\Health; Health::checks([ EnvVars::new() // ... other methods ... ->requireVarsMatchValues([ // Ensure that APP_LOCALE is set to 'en' (no matter which is the current environment) 'APP_LOCALE' => 'en', // Ensure that APP_TIMEZONE is set to 'UTC' (no matter which is the current environment) 'APP_TIMEZONE' => 'UTC', ]) ->requireVarsMatchValuesForEnvironment('staging', [ // Only if current environment is 'staging', we don't want to send e-mails to real customers 'MAIL_MAILER' => 'log', ]) ->requireVarsMatchValuesForEnvironments(['qa', 'production'], [ // Only if current environment is 'qa' or 'production, we want to log 'info' events or above 'LOG_LEVEL' => 'info', // Only if current environment is 'qa' or 'production, we want to store assets to S3 'FILESYSTEM_DISK' => 's3', ]); ]); ``` ⚠️ Quando controlli i valori, non cablare mai e poi mai password, chiavi o altri valori critici — finirebbero nel tuo repository GIT e, come sappiamo tutti, non si fa nel modo più assoluto. ## Per concludere Le variabili d'ambiente sono un piccolo ma molto importante tassello. Con **Laravel Health Env Vars**, puoi assicurarti che siano sempre presenti e contenenti valori validi, in modo da ridurre gli errori e risparmiare tempo sia durante lo sviluppo che in produzione. Provalo e dacci un riscontro su [GitHub](https://github.com/encodia/laravel-health-env-vars) — se vuoi contribuire con una PR sei il benvenuto! ======================================================================== --- title: "Perché è sempre meglio aggiornare Laravel" date: 2025-02-23 lang: it tags: [Laravel, PHP] description: "È molto importante tenere aggiornate le proprie applicazioni affinché usino l'ultima versione di Laravel" --- Ieri (24 febbraio 2025) è uscito [Laravel 12](https://laravel.com/docs/12.x). Questo mi dà lo spunto per parlare di quanto sia importante (e perché lo sia) **aggiornare sempre** le proprie applicazioni affinché usino l'ultima versione del framework. ## Che palle, devo proprio aggiornare? ![Stanco o annoiato?](/blog/perche-e-sempre-meglio-aggiornare-laravel/tired-or-bored.jpg "tired-or-bored") Risposta breve: **sì**, devi. Per vari motivi. Utilizzare la versione più aggiornata del framework ti consente di: - usufruire delle **ultime funzionalità** rilasciate su Laravel: nuovi metodi _Eloquent_ o sulle _Collection_, nuovi helper o _facade_, classi con una sintassi fluente e più gradevole, wrapper di funzioni PHP relative a versioni recenti, nuovi asserzioni nei metodi di test, eccetera - beneficiare delle più recenti correzioni relative alla **sicurezza** - utilizzare **package di prima parte** (es. Reverb, Pulse, Pennant, ecc.) che non sono disponibili per versioni meno recenti - utilizzare **tecnologie**, protocolli, prodotti o API supportati solo dalle più recenti versioni del framework - poter utilizzare le versioni più aggiornate dei **package di terze parti** necessari alla tua applicazione, usufruendo così delle aggiunte rilasciate - godere di una **DX (Developer eXperience)** di alto livello, grazie anche agli strumenti pensati per le versioni più recenti del framework (es. Pest, Pint, Larastan, Rector, ecc.) o alle funzionalità messe a disposizione dalle ultime _release_ di PHP (sintassi, nuovi operatori, attributi, correzioni varie) In poche parole, tutto ciò ti consente di **non restare indietro** ed evitare di andare incontro a obsolescenza certa. ## Non voglio aggiornare ![no](/blog/perche-e-sempre-meglio-aggiornare-laravel/no.png "no") Puoi anche decidere di non farlo, ma credimi: questa decisione, prima o poi, **ti si ritorcerà contro**. Parlo per esperienza diretta! 😁 Supponiamo, ad esempio, che mantieni un'applicazione importante, con una _codebase_ piuttosto grossa, scritta con **Laravel 7**. Per vari motivi, non l'hai mai aggiornata. Il codice ha sempre funzionato senza problemi; il cliente ogni tanto ha chiesto qualche modifica e tu l'hai realizzata; hai sempre sistemato i bug che si sono presentati nel tempo. Nel frattempo, **siccome non vivi nelle caverne** e ami **tenerti aggiornato**, hai sempre tenuto d'occhio le **novità** nel mondo Laravel: nuove funzionalità, strumenti da riga di comando, analizzatori statici, framework per i test, linter, package fantastici, eccetera. Peccato che tutto ciò tu non lo possa usare perché è disponibile, diciamo, dalla versione 10 di Laravel in poi. Inoltre, la tua applicazione gira ancora con PHP 7.x, quindi fino ad ora hai dovuto anche rinunciare ad usare la sintassi e tutte le comodità della versione 8.x. Per non parlare del fatto che il supporto a PHP 7.x è cessato da un pezzo, per cui vai incontro a possibili problemi di sicurezza e con il tempo diventa complicato configurare un VPS con _stack_ applicativi obsoleti (e magari non sei proprio a tuo agio con Docker). All'improvviso hai bisogno di introdurre notifiche in tempo reale via websocket. Perfetto, c'è **Laravel Reverb**! Peccato che tu non lo possa usare perché richiede almeno la versione 10! A un certo punto valuti anche di riscrivere l'applicazione da zero, partendo da `laravel new` e riciclando parti di codice prese dalla attuale applicazione. La scarti quasi immediatamente: troppo lungo e complicato. Non hai scampo: **sei costretto ad aggiornare**. Buona fortuna! 😁 Passare dalla versione N alla N+1 di Laravel può essere relativamente semplice; ma passare da N a N+5 può essere **un vero inferno**. E ti maledirai per aver aspettato così tanto tempo. Avrai a che fare con - package non più supportati - vari _breaking change_ del framework - svariati _breaking change_ in più di un package che utilizzi nell'applicazione - codice deprecato che va riscritto per una versione più attuale di PHP e/o di Laravel - molto altro ancora a cui ora non voglio nemmeno pensare ma che si tradurrà sicuramente in mal di testa, ora spese a cercare in rete, attimi di disperazione ## Quando mi conviene aggiornare? Non immediatamente, ma **non fare passare troppo tempo**. Subito potrebbe essere **prematuro**: potresti andare incontro a problemi, bug o casi particolari per cui non trovi (ancora) soluzioni negli _issue_ di GitHub, su StackOverflow, su Reddit né tantomeno su ChatGPT e simili. Anche se volessi avventurarti, potresti scontrarti con **dipendenze Composer non ancora pronte**: package che non supportano ancora l'ultima versione di Laravel. Quando esce una nuova versione di Laravel, molti addetti ai lavori sono in fibrillazione già da tempo, principalmente per aggiornare i propri **package** affinché siano installabili anche sulla versione più recente del framework. Il panorama è variegato: - chi si porta avanti con largo anticipo, rilasciando una nuova versione già parecchi giorni o, a volte, addirittura settimane prima: fra questi, ad esempio, la nostra web agency spaziale belga preferita 😁 - all'opposto, chi aggiorna il proprio package con settimane o addirittura mesi di ritardo, costringendo chi lo utilizza a non poter aggiornare la propria applicazione o a dover trovare un'alternativa (fork, altro package, ecc.) - chi aiuta altri maintainer aprendo un Pull Request che realizza l'aggiornamento: è un buon metodo per essere utili e riconoscenti alla comunità _open-source_, per imparare qualcosa di nuovo e, non ultimo, per velocizzare i tempi di uscita di quel package - chi decide di non aggiornare il proprio package Si tratta di variabili di cui bisogna necessariamente tenere conto. ## Lo farò un giorno. Ora ho problemi più urgenti ![futuramente](/blog/perche-e-sempre-meglio-aggiornare-laravel/futuramente.jpg "futuramente") Ok, ma **non fare passare troppo tempo**. **Pianifica** questa attività, metti un promemoria e trova il tempo per portarla a termine. Non te ne pentirai. Se lasci passare troppo tempo, finirai per dimenticartene e potresti ritrovarti anni dopo nell'inferno descritto più sopra. ## Il cliente non mi paga, quindi non aggiorno In breve: **dovresti aggiornare comunque**. Sulla carta, il ragionamento non fa una piega: _niente soldi, niente cammello_. Peccato che non vendi frutta o abiti ma software e servizi (che vanno necessariamente mantenuti e aggiornati). ![Niente soldi, niente cammello](/blog/perche-e-sempre-meglio-aggiornare-laravel/camel.jpg "camel") Magari hai anche provato a spiegare il problema al tuo cliente, ma lui ha risposto > "l'applicazione sta funzionando. Non possiamo permetterci di spendere altri soldi; al limite, preferisco tenerli da parte per sviluppi futuri" C'è un grosso errore di fondo: **non spetta al cliente prendere questo tipo di decisioni**. Non avresti nemmeno dovuto porgli questa domanda: il più delle volte, il cliente non ha le **competenze tecniche** per valutare se e quando sia meglio aggiornare un framework o che versione di PHP sia più opportuno usare. E se le ha (ad esempio, ti interfacci con il dipartimento _IT_), dà per scontato che tu segua le _best practice_ relative a manutenzione, aggiornamento, sicurezza. Si aspetta, quindi, che **prenda tu questo genere di decisioni**, eventualmente discutendo con lui eventuali problematiche legate a costi o periodi in cui l'applicazione non può essere utilizzata dagli utenti causa aggiornamento/manutenzione. ## Quindi ci devo rimettere io? Risposta breve: **non ci rimetti tu. Anzi**. Il tuo primo pensiero potrebbe essere questo: > "ora dovrò perdere ore per aggiornare versione del framework, combattendo contro conflitti di versione tra package e test che non ne vogliono sapere di passare. Nessuno mi paga queste ore. Chi me lo fa fare?" Partiamo dalla questione economica: probabilmente stai sbagliando qualcosa nella parte contrattuale/economica con il cliente. Siamo d'accordo: non puoi emettere una fattura dove indichi _aggiornamento Laravel alla versione 12_ o _aggiornamento di spatie/laravel-permission_. Tuttavia, si presume che tu abbia previsto un **canone** mensile o annuo relativo a supporto, aggiornamento e manutenzione applicativa: gli aggiornamenti di Laravel, delle sue dipendenze e simili rientrano in questi compensi. Se non sei proprio alle prime armi e non hai previsto un canone simile, la vedo così: _o hai sbagliato cliente o hai sbagliato lavoro_. So che suona un po' duro e antipatico, ma è la realtà. Non solo non ci rimetterai, ma ne guadagnerai anche, per i motivi spiegati sopra: avrai una **DX** più piacevole, potrai realizzare nuove funzionalità in modo più sicuro e "moderno", manterrai aggiornata la tua applicazione nel lungo termine. ## Ok, mi hai convinto: come faccio? ![Aggiornamento](/blog/perche-e-sempre-meglio-aggiornare-laravel/update.jpg "update") **Test**, test, e ancora test: per cominciare, la tua applicazione deve avere una **suite di test** la più completa possibile (no, non serve una _coverage_ né del 100% né del 75% perché sono numeri che di per sé non vogliono dire molto). Questo è il metodo più rapido per vedere se qualcosa "si rompe" o se continua a funzionare tutto dopo l'aggiornamento. Attenzione: se tutti i test passano, non hai la garanzia assoluta che sia tutto funzionante al 100%; allo stesso tempo, se dopo l'aggiornamento un test non passa più, hai l'evidenza della presenza di un _bug_. Come disse **Edsger Dijkstra**: > "Il test di un programma può essere usato per mostrare la presenza di bug, ma mai per mostrare la loro assenza" A meno che la tua applicazione non sia una semplice _landing page_ (volendo si potrebbe discutere sull'utilità di vari tipi di test anche per un caso così semplice) e testare a mano le funzionalità ti richieda due minuti, pensare di fare la stessa cosa per un'applicazione più grande o complessa è decisamente fuori discussione. Richiederebbe troppo tempo, sarebbe un procedimento inesatto e incompleto e, sicuramente, non considereresti tutti gli scenari, i casi d'uso e gli _input_ dell'utente. Ovviamente, **segui la guida**. Ogni nuova versione di Laravel riporta una guida all'aggiornamento. Ad esempio, se passi dalla versione 11 alla 12, devi eseguire [questi passaggi](https://laravel.com/docs/12.x/upgrade). Per fortuna, in questo caso si tratta di un aggiornamento con zero _breaking change_, quindi dovrebbe essere piuttosto semplice. Altro passaggio obbligatorio: segui la **guida** all'aggiornamento dei **package** presenti fra le dipendenze. Utilizza **strumenti di sviluppo** che ti aiutino a rendere più "moderno" e aggiornato il tuo codice: ad esempio, **Larastan** e **Rector**. Oltra all'ambiente locale - che sia basato su Herd, Laragon, Docker o altro non importa - effettua un rilascio su un **ambiente di staging** o pre-produzione, dove più utenti (specialmente quelli finali) possano testare l'applicazione, possibilmente per diversi giorni. Puoi anche avere una suite con migliaia di test e decine di migliaia di asserzioni, ma i **test utente** sono comunque necessari. Valuta eventualmente se farti aiutare da una **soluzione automatizzata** quale [Laravel Shift](https://laravelshift.com/). **Buon aggiornamento!** ======================================================================== --- title: "Laravel: 4+1 elementi che non devono mai mancare in AppServiceProvider" date: 2025-02-09 lang: it tags: [Laravel, PHP] description: "In ogni applicazione Laravel ci sono alcuni elementi indispensabili" --- In ogni applicazione **Laravel**, ci sono alcuni elementi che non devono mai mancare. Solitamente vanno definiti nel metodo `boot()` di `AppServiceProvider` (o di un altro service provider, a seconda di come strutturi le tue applicazioni) che si presenta quindi molto simile a questo esempio: ```php showLineNumbers isProduction(); // Evita comandi distruttivi in produzione DB::prohibitDestructiveCommands($isProduction); // Abilita la modalità "strict" per i modelli in ambienti non di produzione Model::shouldBeStrict(!$isProduction); // Imposta lo schema degli URL su HTTPS URL::forceScheme('https'); // Utilizza CarbonImmutable per la gestione delle date Date::use(CarbonImmutable::class); // Abilita il prefetching degli asset generati da Vite Vite::prefetch(); } } ``` Analizziamo gli elementi uno per uno, in modo da capire perché è opportuno usarli. ## 1. Niente comandi distruttivi per il DB La sicurezza prima di tutto! A chi non è mai capitato, per distrazione o stanchezza, di eseguire una query o un comando convinto di essere in un ambiente locale o di test, per poi scoprire di aver eliminato dati di produzione? A me è capitato ✋🏻 `DB::prohibitDestructiveCommands($isProduction)` impedisce che vengano eseguiti comandi **distruttivi** sul **database**. Ok, ma quali sono questi _comandi distruttivi_, per i quali è importante proibire l'esecuzione in determinate circostanze? Quelli che fanno uso del trait `Illuminate\Console\Prohibitable`. Laravel definisce alcuni comandi come _distruttivi_: ad esempio, `db:wipe`, `migrate:fresh`, `migrate:refresh`, `migrate:reset`. È possibile aggiungere il trait `Prohibitable` anche ai propri comandi: ```php showLineNumbers use Illuminate\Console\Command; use Illuminate\Console\Prohibitable; class DeleteDocumentCommand extends Command { use Prohibitable; // ... } ``` In un service provider, è sufficiente indicare a Laravel se l'esecuzione di quel comando debba essere proibita o meno, tipicamente con una condizione che risulta vera sull'ambiente di produzione o sugli ambienti considerati "critici": ```php showLineNumbers public function boot(): void { // Impedisce l'esecuzione di questo comando in produzione DeleteDocumentCommand::prohibit($this->app->isProduction()); } ``` ## 2. Modalità strict per i model ```php showLineNumbers public function boot(): void { // ... Model::shouldBeStrict(! $this->app->isProduction()); } ``` Questa riga di codice abilita la modalità _strict_ per i model se l'applicazione non è in esecuzione in produzione. In ambienti non critici, è opportuno mantenerla disattivata, in modo che Laravel sollevi le opportune eccezioni, consentendo di correggere il codice; viceversa, sugli ambienti critici l'esecuzione del codice non si deve interrompere e non devono comparire errori. In cosa consiste esattamente la modalità _strict_? Eseguire `Model::shouldBeStrict(...)` corrisponde a richiamare queste tre metodi: ```php showLineNumbers Model::preventLazyLoading(); // 1. Model::preventAccessingMissingAttributes(); // 2. Model::preventSilentlyDiscardingAttributes(); // 3. ``` 1. consente di identificare i problemi legati alle query **N+1** e al **lazy loading**: se un model dichiara una relazione, ma questa viene usata senza che sia stata caricata tramite `with()` (metodo che consente di sfruttare l'**eager loading**), nel momento in cui utilizzi tale relazione (ad esempio in un ciclo), si verifica il _lazy loading_. Laravel, per accedere ai valori di cui hai bisogno tramite la relazione, è costretto ad eseguire una query aggiuntiva per ogni record presente nella relazione: se la relazione ha ritornato N record, si traduce in N query che, sommate alla query principale sul model, fanno appunto N+1 query. Disabilitando il lazy loading, su ambienti non critici (es. in locale) verrà sollevata un'eccezione: ad esempio, `Attempted to lazy load [user] on model [App\Models\Post] but lazy loading is disabled.`; su ambienti critici, invece, è opportuno lasciare abilitato il lazy loading, in modo che il codice venga comunque eseguito senza ritornare errori. Se la scelta è fra "l'applicazione in produzione si rompe e non ritorna risultati" e "l'applicazione ritorna risultati ma lentamente ed eseguendo un sacco di query", meglio il secondo caso 2. previene comportamenti indesiderati quando si aggiorna un model: ad esempio, se il model `Customer` non riporta il campo `code` nell'array `$fillable` e proviamo ad aggiornare un'istanza di quel model, referenziando `code`, verrà sollevata un'eccezione tramite la quale Laravel si lamenta e suggerisce di aggiungere `$code` all'array `$fillable` 3. evita che possano essere richiamati attributi di un model che non esistono. Ad esempio, se richiamo `$user->las_name` e l'attributo non esiste, verrà sollevata un'eccezione che ci avvisa che l'attributo non esiste o non è stato caricato per quel model. In questo esempio, si tratta di un errore di battitura; in altri casi, l'attributo potrebbe essere previsto (sul db, nel model) ma potrebbe essere stato escluso dalla query. ## 3. Forzare HTTPS HTTPS è il protocollo standard _de facto_ sul web. Inizialmente era considerata "una sicurezza in più"; da qualche anno, ormai, è obbligatorio, tant'è che i moderni browser addirittura impediscono o rendono difficoltoso l'accesso a contenuti serviti su _HTTP_. ```php showLineNumbers addHour(); dump($start, $end); ``` Potresti essere portato a pensare che il codice sia corretto, ovvero che `$start` contenga una specifica data/ora e `$end` sia di un'ora successiva a `$start`. In realtà, `$start` e `$end` conterranno la stessa data/ora. Provare per credere: ```php $start->equalTo($end); // true ``` Questo si verifica proprio perché `$start` contiene un'istanza mutabile: `$start->addHour()` aggiunge sì un'ora a `$start` e ritorna il risultato al chiamante, ma causa anche l'_effetto collaterale_ di modificare il contenuto stesso di `$start`. Vediamola sotto questa luce: Carbon può essere considerato un enorme **value object** e questi, per loro natura, dovrebbero essere **immutabili**. Se vogliamo, aver creato Carbon come oggetto mutabile può essere considerato un errore di progettazione. Per correre al riparo, è stato quindi introdotta la sua versione immutabile: **CarbonImmutable**. Ti consiglio vivamente di usarla al posto di Carbon, per evitare gli errori di cui sopra. Per fare in modo che Laravel di default utilizzi `CarbonImmutable` per le date e, ad esempio, per evitare di doverti ricordare di usare `now()->toImmutable()`, è sufficiente fare in questo modo: ```php showLineNumbers WordPress scritto molto meglio, con tutto ciò che su WordPress spesso è un plugin In effetti Statamic è stata la mia prima scelta. Allora **perché l'ho scartato**? Semplice: il supporto multisito (che comprende anche il multilingua) richiede una licenza professionale a pagamento. Intendiamoci: i 275 dollari richiesti per la versione Pro (primo anno, poi si passa a 65 dollari) valgono fino all'ultimo centesimo, specialmente se viene utilizzato per sviluppare una soluzione professionale dove ci sia _budget_. Tuttavia, nel mio caso, parliamo di un piccolo blog personale, dove il budget è... Zero. Il mio ragionamento è opinabile? Certamente. Se un prodotto a pagamento semplifica e migliora la vita dello sviluppatore e la sua cosiddetta DX (_Developer eXperience_), ben venga la spesa! Tuttavia, nel mio caso, ho ritenuto che il gioco non valesse la candela. * **Pro**: scritto molto bene, mantenuto, estensibile, basato su Laravel * **Contro**: per il multi-sito / multi-lingua è necessaria una licenza a pagamento ## La soluzione: Grav CMS Alla fine ho scelto [Grav CMS](https://getgrav.org/). Come mai? Perché soddisfa le esigenze che ho elencato: * si tratta di un **CMS** la cui installazione e gestione si sono rivelate davvero **semplici**, così come l'utilizzo. Trattandosi di un prodotto che non avevo mai usato ha richiesto un minimo di prove, lettura della documentazione, esplorazione della configurazione, ma si è rivelato tutto piuttosto immediato, grazie anche all'**interfaccia di amministrazione** (opzionale, installabile come plugin). Trovo che abbia una **curva di apprendimento piuttosto bassa** * è un [progetto open-source](https://github.com/getgrav/grav) (quindi non occorre alcuna licenza a pagamento) e l'ultimo _commit_ risale a pochi giorni fa * è scritto in **PHP**: dovessi avere necessità di scrivere un plugin, modificare una funzionalità o aprire una _pull request_ su GitHub, so dove mettere le mani * i requisiti sono semplicissimi: **PHP** >= 7.3.6 (io uso la **8.3.x**) e un web server (**Nginx**, nel mio caso). E, chiaramente, un **nome a dominio** * è un **CMS flat-file**, con tutti i vantaggi che ne conseguono; per creare i **contenuti** si utilizza [Markdown](https://en.wikipedia.org/wiki/Markdown) - comodo, pratico, standard, facilmente trasportabile. E' sufficiente creare un file .md, inserire il contenuto e specificare eventuali opzioni tramite [frontmatter](https://learn.getgrav.org/17/content/headers), il tutto con un qualsiasi editor di testo (ad esempio, **Vim**). In alternativa, è possibile creare un contenuto tramite l'interfaccia di amministrazione: il risultato finale sarà lo stesso * è dotato di numerose **funzionalità** più che sufficienti a produrre e gestire contenuti, con tutte le attenzioni del caso alla **SEO** * consente di gestire **più lingue**, con varie opzioni relative a _fallback_, redirect, ecc. Ma non finisce qui. Ecco una lista di caratteristiche presenti su Grav CMS: * **installazione rapida**: è sufficiente decomprimere un file zip, ma sono disponibili anche altri metodi (tramite Composer, clonando il repository GIT, utilizzando Docker) * sistema di **cache** per servire rapidamente i contenuti * interfaccia a riga di comando (**CLI**) per installare dipendenze, effettuare manutenzione, creare utenti, eliminare la cache, ecc. * gestore di dipendenze (**package manager**) tramite il quale **installare** o **aggiornare** versione di Grav, **temi**, **plugin** * configurazione basata su file **YAML**, facilmente estensibile (ad esempio sulla base dell'ambiente corrente di esecuzione) * gestione di **utenti** e **ruoli** per impostare permessi e limitare l'accesso a determinate pagine * può essere utilizzato in modalità **multi-sito** * possibilità di effettuare **backup** e restore in modo rapido, anche da CLI * possibilità di scrivere contenuto in **Markdown** ma anche in **HTML** * **routing** e **redirect** configurabili per migliorare l'usabilità e la flessibilità * **tassonomie** personalizzabili e **campi dinamici** personalizzabili * i **temi** possono essere estesi, in modo da **ereditare** le funzionalità da un teme esistente, modificando solo ciò che si desidera * possibilità di realizzare **form** e molto altro. Per quanto riguarda l'**aspetto**, non avevo grosse esigenze e mi sono limitato a utilizzare il tema base, modificandolo leggermente; volendo, è possibile realizzare un proprio tema da zero (oppure estendendone uno esistente), utilizzando il _template engine_ **Twig** e scegliendo il proprio framework **CSS**. ## Conclusioni Devo dire di essere rimasto piacevolmente stupito da questo CMS. Si adatta perfettamente alle mie esigenze, senza inutili complicazioni, e offre ben più di quanto mi serva per gestire questo blog. Per il momento si sta comportando egregiamente e sembra che lo sviluppo venga portato avanti. Come ho premesso, a mio parere non esiste uno strumento universale migliore di tutti gli altri, che si presti a soddisfare ogni tipo di esigenza e gusto: ciò che per me risulta ottimale potrebbe non esserlo per un'altra tipologia di utente. Tuttavia, se la necessità è, ad esempio, creare un blog personale, consiglio vivamente di provare **Grav CMS**. Detto così, suona un po' male e lo sminuisce... Rifaccio: il prodotto è ottimo e davvero versatile. Trovo che possa prestarsi bene come strumento per realizzare progetti web di vario tipo. ======================================================================== --- title: "PHP 8.4 è stato rilasciato: principali novità e miglioramenti" date: 2024-11-24 lang: it tags: [PHP] description: "Versione 8.4 di PHP: principali novità e miglioramenti introdotti" --- Il 21 novembre 2024 è stata rilasciata la versione **8.4** di **PHP**. Per la precisione, la versione **8.4.1**. I rilasci più recenti di [Laravel Herd](https://herd.laravel.com/) e [PHPStorm](https://www.jetbrains.com/phpstorm/) supportano già questa versione. Tra le principali novità: - possibilità di concatenare `new` senza parentesi extra - _property hooks_ - visibilità asimmetrica delle proprietà - nuove funzioni relative alla ricerca negli array - nuove funzioni `mb_` - creare un oggetto DateTime partendo da un timestamp Unix - parsing di HTML5 - deprecazioni e varie ## Possibilità di concatenare `new` senza parentesi extra Fino a PHP 8.3 compreso, è necessario utilizzare le parentesi per racchiudere l'istanziazione della classe: ```php showLineNumbers # PHP <= 8.3.x (new MyCustomClass())->myMethod(); ``` Ora è possibile scrivere, ad esempio: ```php showLineNumbers # PHP 8.4.x new MyCustomClass()->myMethod(); ``` Questa sintassi consente di accedere a costanti, proprietà e metodi di classe senza dover utilizzare parentesi extra. ## Property hooks Questa funzionalità, già presente in altri linguaggi quali C#, Kotlin e Swift, consente di specificare in che modo accedere a una proprietà - in lettura o scrittura - senza dover utilizzare metodi aggiuntivi o ricorrere ai metodi magici `__get()` e `__set()`. Nel momento in cui si definisce una proprietà, è possibile specificare un `getter` e/o un `setter` per sovrascrivere, rispettivamente, ciò che viene letto quando si accede a quella proprietà e in che modo viene valorizzata quando la si scrive. Ho scritto _e/o_ perché non è necessario specificarli entrambi. Vediamo un esempio: ```php showLineNumbers "$this->firstName $this->lastName" set => { [$this->firstName, $this->lastName] = explode(' ', $value, 2); } } } ``` ## Visibilità asimmetrica Le proprietà di classe ora possono avere una visibilità differente in lettura e in scrittura. Ad esempio, una proprietà potrebbe essere accessibile pubblicamente in lettura ma avere visibilità _protected_ in scrittura. Ad esempio: ```php showLineNumbers name); // 'Plugin' $item->name = 'Updated name'; // FATAL ERROR! ``` L'utilizzo degli _hook_ delle proprietà in combinazione con la visibilità asimmetrica permette di ottenere un'ampia flessibilità e soddisfare varie esigenze. ## Nuove funzioni relative alla ricerca negli array PHP 8.4 introduce alcune nuove funzioni per la ricerca di elementi in un array: - `array_find()` - `array_find_key()` - `array_any()` - `array_all()` Vediamo un esempio: ```php showLineNumbers 'Pad', 'b' => 'Mouse', 'c' => 'Microphone', 'd' => 'Monitor', 'e' => 'Printer' ]; // Ritorna il primo elemento la cui lunghezza sia di 7 caratteri var_dump(array_find($array, fn (string $value) => strlen($value) == 7)); // 'Monitor' // Ritorna la chiave del primo dipositivo la cui lunghezza sia di almeno 6 caratteri var_dump(array_find_key($array, fn (string $value) => strlen($value) > 6)); // 'c' // Verifica se ci sia almeno un elemento la cui lunghezza sia inferiore a 5 caratteri var_dump(array_any($array, fn (string $value) => strlen($value) < 5)); // true // Verifica se tutti gli elementi abbiano lunghezza inferiore a 5 caratteri var_dump(array_all($array, fn (string $value) => strlen($value) < 5 )); // false ```` ## Nuove funzioni `mb_` PHP 8.4 aggiunge le funzioni che supportano il multibyte: - `mb_trim()` - `mb_ltrim()` - `mb_rtrim()` - `mb_ucfirst()` - `mb_lcfirst()` Funzionamento e parametri sono analoghi alle corrispondenti funzioni non multibyte (es. `trim()`). ## Creare un oggetto DateTime partendo da un timestamp Unix Per semplificare la creazione di un oggetto DateTime a partire da un timestamp Unix, è stato aggiunto il metodo `DateTimeImmutable::createFromTimestamp()`. ```php showLineNumbers format('d-m-Y'); // 24-11-2024 $datetime = DateTimeImmutable::createFromTimestamp(1732436675.432); echo $datetime->format('d-m-Y h:i:s.u'); // 24-11-2024 08:24:35.432000 ``` ## Parsing di HTML5 La classe `DomHTMLDocument` consente di importare il contenuto di una pagina HTML5 e riconosce i tag semantici - ad esempio `main`, `article` e `section`. È anche possibile leggere il contenuto a partire da un file, oppure creare un documento vuoto: ```php showLineNumbers ......'; $path = '/home/foo/dev/bar/file.html'; $htmlDocument = DomHTMLDocument::createFromString($html); $anotherDocument = DomHTMLDocument::createFromFilePath($path); $emptyDocument = DomHTMLDocument::createEmpty(); ``` ## Deprecazioni e varie PHP 8.4 riporta un elenco piuttosto lungo di funzioni e caratteristiche deprecate, ovvero marcate per un'eventuale rimozione in futuro. Sono presenti, inoltre, nuove classi, interfacce e funzioni. Per l'elenco completo, è possibile consultare - [php.net](https://www.php.net/releases/8.4/en.php) - il [changelog](https://www.php.net/ChangeLog-8.php#PHP_8_4) di PHP 8.4 - [PHP.watch](https://php.watch/versions/8.4) di PHP.watch ======================================================================== --- title: "Orrori di pronuncia in inglese" date: 2024-11-17 updated: 2025-10-19 lang: it tags: [Inglese] description: "Molti italiani, in particolare quelli che lavorano in ambito IT, hanno sempre pronunciato male uno o più termini inglesi" --- L'**inglese** ormai è ovunque. _Da mo'..._ dirai tu: non è certo una novità degli ultimi anni. È da un bel pezzo che moltissimi termini della lingua di Albione vengono utilizzati in ogni ambito della nostra vita quotidiana: pubblicità, tecnologia, testi scientifici e non, modi dire, nomi di prodotti, nomi di oggetti, definizioni, ecc. Molto probabilmente il fenomeno ha "contaminato" ogni professione e ambito lavorativo (anche quelli più impensabili), ma una cosa è certa: chi, come il sottoscritto, lavora in **ambito IT** - non importa se sia sviluppatore, sistemista, amministratore di reti, social media manager, addetto all'assistenza cliente, direttore marketing o SEO - quotidianamente legge, scrive, ascolta, **pronuncia** decine e decine di termini in lingua inglese, strettamente tecnici o meno (non importa). Faccio una premessa: non è mia intenzione sminuire nessun popolo o esaltarne un altro, così come non voglio generalizzare o proporre come verità assoluta quanto segue. Detto questo, in base alla mia esperienza diretta (e a qualche [statistica](https://www.britishinstitute.roma.it/classifica-conoscenza-lingua-inglese-italia/)), mi sento di dire che: * gli italiani mediamente parlano/scrivono l'inglese poco e male * gli italiani mediamente hanno una pronuncia inglese un po' maccheronica che li rende facilmente riconoscibili all'estero * in altre nazioni (es. Paesi Bassi, Paesi dell'area scandinava, ecc.) la conoscenza della lingua inglese, sia scritto che parlato, nonché la pronuncia, è a un livello decisamente più avanzato (complice il fatto, per esempio, che i film non sono doppiati) Veniamo al dunque: un numero spropositato di persone **sbaglia sistematicamente la pronuncia** di vari termini inglesi di uso comune o facenti parte del gergo tecnico utilizzato ogni giorno al lavoro. Tornando all'ambito IT, parliamo di addetti ai lavori che, molto spesso, si tengono aggiornati o si formano tramite piattaforme che erogano contenuti video o podcast in lingua inglese. Voglio dire: caro sviluppatore, ti sei sparato un corso di 20 ore di John Doe su un argomento che ti interessa molto. Ok, magari non sei un asso nella comprensione e segui solo contenuti corredati da sottotitoli in italiano (non c'è niente di male)... Ma non hai sentito nemmeno una volta come il docente ha pronunciato _performance_ o _repository_? Dai, non è possibile. Eppure riesci a pronunciarli a _cazzo di cane_ lo stesso, persino quando qualcuno te lo fa notare. Quello che davvero non mi spiego e che mi fa ancora più rabbia è questo: spesso si tratta di persone molto brave nel proprio lavoro, sveglie, intelligenti, preparate, magari anche disinvolte nell'uso dell'inglese parlato. Eppure sbagliano la pronuncia, rischiando in quel modo di perdere un po' di credibilità e apparire imprecise, approssimative. Molti di questi errori derivano dall'erronea convinzione per cui ogni _i_ vada pronunciata _ai_. Non sono un esperto di lingua inglese né, purtroppo, la parlo o scrivo a livello avanzato; tuttavia, ho imparato che le eccezioni alle regole sono talmente tante che l'unico modo per sapere come si pronuncia una parola è... Cercare come si pronuncia. Ci si può aiutare con un dizionario (come il [Cambridge Dictionary](https://dictionary.cambridge.org/)) sul quale verificare la trascrizione fonetica o ascoltare la pronuncia; in alternativa, si possono usare applicazioni semplici e immediate come **Deepl** o **Google Translate**. Della serie: nel 2024, non hai più scuse per sbagliare. Altra doverosa premessa: * non sono un esperto di lingua inglese * alcun parole possono avere diverse pronunce, ma non è questo il punto * tutti possono sbagliare, ma una volta notato l'errore è opportuno non perseverare e correggersi * per semplicità non ho utilizzato la trascrizione fonetica, ma una sorta di trascrizione che dovrebbe a grandi linea rendere l'idea. I puristi mi perdoneranno Di seguito, alcuni degli _orrori_ - pardon, errori - di pronuncia inglese di cui sono stato più volte testimone: * [Nginx](#nginx) * [repository](#repository) * [PayPal](#paypal) * [Udemy](#udemy) * [character](#character) * [performance](#performance) * [management](#management) * [engineering](#engineering) * [request](#request) * [parameter](#parameter) * [developer](#developer) * [control](#control) * [tier](#tier) * [composite](#composite) * [component](#component) * [iteration](#iteration) * [migration](#migration) * [URL](#url) * [default](#default) * [report](#report) * [template](#template) * [support](#support) * [head](#head) * [recipe](#recipe) * [reply](#reply) * [commit](#commit) * [swagger](#swagger) * [promise](#promise) * [alert](#alert) * [address](#address) * [facade](#facade) * [integer](#integer) * [locale](#locale) ### Nginx Scrivendo questo articolo, è il primo che mi sia venuto in mente a pari merito con _repository_. Durante una videochiamata, a una conferenza di sviluppatori o, peggio ancora, in un video tutorial è praticamente certo che qualcuno prima o poi lo pronuncerà male. Credo sia colpa del termine **engine**: anch'esso viene quotidianamente bistrattato. > 👎🏻 en-giàincs > 👍🏻 en-gin-ecs ### Repository Altro errore frequentissimo: l'accento non è su _si_, ma su _po_. > 👎🏻 re-po-sì-to-ry > 👍🏻 re-pò-si-to-ry ### PayPal Scommetto qualunque cosa che, in una riunione sull'argomento, prima o poi qualcuno pronuncerà con estrema convinzione e disinvoltura _peipòl_. Pal (amico) si pronuncia... _pal_! E la nota piattaforma non fa eccezione. La seconda a non diventa magicamente una o. Non c'è nessun motivo per cui debba accadere. > 👎🏻 pei-pòl > 👍🏻 pei-pàl ### Udemy Per restare nella categoria _nomi di prodotti_, più di una volta ho sentito storpiare il nome di questa nota piattaforma che eroga corsi. > 👎🏻 iu-de-mài > 👍🏻 iù-de-mi ### Character / Char / Varchar Nel contesto di carattere, inteso come simbolo, lettera, la _c_ è dura (come in casa). Ricordo ancora quanto il [prof. Elio Giovannetti](https://www.di.unito.it/~elio/) insistesse, durante le sue lezioni di programmazione al Dipartimento di Informatica dell'Università di Torino, sul fatto che _char_ si pronunci _car_ in quanto abbreviazione di character (pronunciato _ca-rac-ter_). Stessa cosa per _varchar_. Discorso diverso per il vocabolo _char_ quando ha significato di carbone: allora si pronuncia _ciar_. Ma esuliamo dall'ambito IT. > 👎🏻 ciar > 👍🏻 car ### Performance Questo è anche facile da ricordare: avete presente quando Virginia Raffaele (bravissima!) imita Marina Abramović? [Peffòmans](https://www.youtube.com/watch?v=znZKkDQ6fXs)! > 👎🏻 pér-formans > 👍🏻 per-fòr-mans ### Management No, non c'è nessun _eig_ come in age (età). > 👎🏻 man-éig-ment > 👍🏻 màn-eg-ment ### Engineering Qui abbiamo diverse storpiature: c'è chi pronuncia la prima _i_ come _ai_ (così come fa con _engine_) e c'è chi pronuncia la prima _i_ correttamente ma poi si perde dopo. Le due _e_ non sono buttate lì a casaccio: _neer_ suona simile a _near_ (vicino). > 👎🏻 en-giài-niring > 👎🏻 en-gìnering > 👍🏻 en-gi-nìir-in(g) ### Request Questa mi irrita particolarmente. La pronuncia corretta, oltretutto, ricorda quella di _richiesta_: anche qui l'accento è sulla _e_ e non sulla prima _i_ (a nessuno verrebbe in mente di pronunciare _rìchiesta_!). > 👎🏻 rì-quest > 👍🏻 ri-quést ### Parameter Lo ammetto: per anni l'ho pronunciato sbagliato. Un bel giorno ho scoperto come si dicesse mentre guardavo un tutorial di sviluppo dove si parlava di metodi e parametri. È importantissimo guardare contenuti in lingua inglese. > 👎🏻 pa-ra-mì-ter > 👍🏻 pa-rà-më-tér ### Developer Su LinkedIn sbandieri il titolo di _senior XXL omnibus evangelist developer_ e poi lo pronunci maccheronicamente? Immagina quanto risulti credibile! > 👎🏻 de-ve-lòp-per > 👍🏻 di-vé-loper ### Control Facile da ricordare: basta pensare a come pronunciamo _controllo_ (a nessuno verrebbe in mente di dire _còntrollo_). > 👎🏻 còn-trol > 👍🏻 con-tròl ### Tier Frequentissimo, per il solito discorso della pronuncia della _i_. Nella prossima riunione, quando a un certo punto dirai _free tier_, ricordati che la _i_ di tier si pronuncia come in _free_. > 👎🏻 tài-er > 👍🏻 tìer ### Composite Non è che se _site_ si pronuncia _sàit_ allora tutto ciò che contiene site segue lo stesso destino... > 👎🏻 com-po-sàit > 👍🏻 com-pò-sit ### Component L'accento è sulla seconda _o_. > 👎🏻 còm-po-nent > 👍🏻 com-pò(u)-nent ### Iteration Dopo aver letto per anni _iterate_, _iteration_ su libri e articoli online, ho scoperto tardi come si pronunciava. > 👎🏻 ài-te-rescion > 👍🏻 i-te-rescion ### Migration Gli sviluppatori **Laravel** usano spesso questo termine (non solo loro, certo). In questo caso, la _i_ si pronuncia _ai_ proprio come ci si aspetterebbe. > 👎🏻 mi-gre-sciòn > 👍🏻 mai-gre-sciòn ### URL Ebbene sì: mi è capitato di sentire anche questo. Non è che se _up_ si pronuncia _àp_ allora... > 👎🏻 àrl > 👍🏻 url ### Default Qui entriamo nel territorio del "va bene lo stesso". > 👎🏻 de-fòult > 👍🏻 dì-foult ### Report Nella categoria "fuori concorso", vale la pena citare _report_. Come riporta l'[Accademia della Crusca](https://accademiadellacrusca.it/it/consulenza/r%C3%A8port-di-un-anglismo/1739), in Italia la pronuncia più diffusa, per non dire l'unica accettata, è _rèport_. Tuttavia, sappiate che in inglese la pronuncia corretta non è quella. > 👎🏻 rèport > 👍🏻 ripòrt ### Virus Ovviamente, _virus_ in Italia si pronuncia esattamente com'è scritto. Anche perché deriva dal latino _virus_ (veleno). Solo per completezza, sappi che se conversi in inglese con qualcuno, dovrai pronunciarlo diversamente. > 👎🏻 vì-rus > 👍🏻 vài-rus ### Template Analogamente a _report_, anche _template_ si pronuncia diversamente da come siamo soliti usare in Italia. L'accento corretto è sulla prima _e_, non sulla seconda. > 👎🏻 temp-léit > 👍🏻 tèm-pleit ### Support In questo caso la _u_ non si pronuncia _a_ (nemmeno u, ma per la pronuncia precisa è meglio sentire un esempio) e, sopratutto, l'accento non è sulla prima sillaba. > 👎🏻 sàp-port / sùp-port > 👍🏻 sup-pòrt ### Head Forse _head_ è il primo _orrore di pronuncia_ di cui abbia memoria, già dai tempi delle scuole superiori (dove iniziai a "giocare" con l'HTML). Purtroppo l'orrore non ha cessato di esistere né all'università né nel mondo del lavoro: sentire quotidianamente addetti ai lavori dire "questo script va inserito nell'_hìd_ della pagina" è un pugno nei denti. > 👎🏻 hìd > 👍🏻 hèd ### Recipe Questo termine non è usato solamente in cucina, ma anche nel mondo del sofware. Se pubblichi un video in inglese in cui spieghi come scrivere una "ricetta" per effettuare deploy della tua applicazione web, ben venga, ma assicurati di pronunciare _recipe_ correttamente! > 👎🏻 re-sàip > 👍🏻 ré-si-pi ### Reply Un altro grande classico. Moltissime persone confondono _reply_ (risposta, ad esempio ad un'e-mail) con _replay_ e quindi lo pronunciano allo stesso modo, ovvero come quest'ultimo. > 👎🏻 re-plèi > 👍🏻 ri-plài ### Commit Se sei uno sviluppatore, a meno che tu non viva ancora nelle caverne, quotidianamente hai a che fare con un sistema di versionamento del codice (ed è molto molto probabile che sia _GIT_). E ogni giorno sicuramente pronunci anche più volte una frase del tipo "non ho ancora fatto commit". Ed è facile che pronunci _commit_ in modo sbagliato. > 👎🏻 còm-mit > 👍🏻 com-mìt ### Swagger Se si parla di API e documentazione, prima o poi questo nome salta fuori. E, puntualmente, sia io che decine di altri sviluppatori lo abbiamo sempre pronunciato male! Ho scoperto la corretta pronuncia solo qualche settimana fa, guardando un video sull'argomento. > 👎🏻 suòg-gher > 👍🏻 suèg-ga ### Promise Se sei uno sviluppatore web, sicuramente hai avuto a che fare con JavaScript, operazioni asincrone, callback e _promise_. No, nemmeno in questo caso _i_ si pronuncia _ai_. > 👎🏻 pro-màis > 👍🏻 prò-mis ### Alert L'accento non è sulla _a_, ma sulla _e_, che si pronuncia come una via di mezzo fra _e_ e _u_. > 👎🏻 à-lert > 👍🏻 a-lərt ### Address Discorso simile a quello di address: l'accento non è sulla _a_, ma sulla _e_. > 👎🏻 àd-dress > 👍🏻 ad-dréss ### Facade Fuori concorso, dato che è un termine francese (la cui grafia corretta è _façade_) e non inglese. Il pattern [facade](https://refactoring.guru/design-patterns/facade) fu introdotto dalla Gang of Four nel celebre [Design patterns: elements of reusable object-oriented software](https://en.wikipedia.org/wiki/Design_Patterns). Inoltre, se sei uno sviluppatore Laravel, non puoi non averlo sentito nominare. Ed è giusto che lo pronunci correttamente. > 👎🏻 fa-séid > 👍🏻 fa-sàad ### Integer Ad esempio, Nuno Maduro [lo pronuncia](https://youtu.be/hcI06mTlbzM?si=YcskFpkRIEH42ik4&t=19) intìger, sbagliando posizione dell'accento e trasformando una _e_ in _i_. A quanto mi risulta, sia in inglese britannico che in inglese americano non si pronuncia in quel modo. > 👎🏻 in-tì-ger > 👍🏻 ìn-te-gër ### Locale Se hai dovuto formattare una data o gestire i file di lingua di un'applicazione, hai sicuramente sentito parlare del termine _locale_ e l'hai usato in un discorso. Anch'io l'ho sempre pronunciato come quando nomino J.J. Cale, ma è sbagliato. > 👎🏻 lo-chéil > 👍🏻 lo-càl Sicuramente ci sono altri esempi, ma per il momento ho raccolto questi. Se dovessero venirmene in mente altri, li aggiungerò. Se conosci qualche altro termine la cui pronuncia viene spesso bistrattata, scrivimi una mail e provvederò ad aggiungerlo! ======================================================================== --- title: "My current setup (2026 edition)" date: 2026-07-04 updated: 2026-07-11 lang: en translationKey: my-setup-2026 series: uses tags: [Setup] description: "Hardware, software and tools I use every day to build, run servers and ship projects — 2026 edition." --- Hardware, software and tools I use every day to build, run servers and ship projects. This is the **2026 edition**: I update it every now and then, so you'll find previous versions in the archive. ## 🖥️ Hardware ### At the office - **Mac Studio M4 Max** — My main machine for development and daily work. - **Dell 27" S2725QC Ultra HD 4K monitor** — Primary display. - **LG 27" 27UP650K Ultra HD 4K monitor** — Display for secondary tasks. - **Logitech MX Keys** — Typing on this keyboard is genuinely pleasant. - **Sony WH-1000XM6 Headphones** — Thanks to the (outstanding) noise cancellation, I can focus while developing, listen to music and join audio/video calls. - **Newline NewEye 62 Full HD** — External webcam with *privacy shutter* and 2 microphones. - **HyperX SoloCast** — Microphone with tap-to-mute sensor and LED status indicator. ### At home / On the road - **MacBook Pro 14" M4 Pro** — Great both as a fixed workstation and while travelling. - **Samsung U28E590 monitor** — At home I use it as a single display. - **Arzopa Z1FC monitor** — I use it as a secondary display when I'm out of the office. - **OWC Thunderbolt 3** — A genuinely complete docking station. - **Spedal Full HD** — External webcam with *privacy shutter*. - **HyperX SoloCast** — Microphone with tap-to-mute sensor and LED status indicator. - **M-Audio Air 192|6** — Essential audio interface for home recording. - **PreSonus ERIS E4.5** — Compact studio monitors. - **Logitech Pebble Keys 2** — Compact layout and soft keys; I can even carry it around. - **JBL Tune510BT headphones** — To avoid disturbing others during calls or to listen to music. ## ⌨️ Editor & Terminal - **[PhpStorm](https://www.jetbrains.com/phpstorm/)** — Main IDE for PHP and Laravel (and beyond): best-in-class refactoring and inspections. - **[Visual Studio Code](https://code.visualstudio.com/)** — For quick edits, notes and the occasional non-PHP project. - **[iTerm2](https://iterm2.com/)** — The terminal I use every day. - **[JetBrains Mono](https://www.jetbrains.com/lp/mono/)** — My favourite monospaced font. ## 🛠️ Development - **[PHP + Laravel](https://laravel.com/)** — The stack I build most of my projects with. - **[Laravel Herd](https://herd.laravel.com/)** — Fast, frictionless local PHP development environment. - **[Laradumps](https://laradumps.dev/)** — Quick debugging, without reaching for `dd()` or XDebug. - **[Pest](https://pestphp.com/)** — The framework that makes writing tests enjoyable. - **[DBNgin](https://dbngin.com/)** — For running multiple local versions of MySQL, MariaDB, PostgreSQL, Redis and other services. - **[Docker](https://www.docker.com/)** — For reproducible environments when multiple services are involved. - **[TablePlus](https://tableplus.com/)** — GUI for MySQL, PostgreSQL and SQLite databases. - **[Git + GitHub](https://github.com/)** — Version control and code review for every project. - **[Sourcetree](https://www.atlassian.com/software/sourcetree)** — A simple, complete Git client. - **[Postman](https://www.postman.com/)** — For testing and sharing APIs and their environments. - **[Claude Code](https://claude.com/product/claude-code)** — AI-assisted development. ## 🧰 Apps & Utilities - **[Raycast](https://www.raycast.com/)** — Launcher and automations: I use it dozens of times a day. - **[Spotify](https://www.spotify.com/)** — Music keeps me company almost all day long. - **[Discord](https://discord.com/)** — Internal messaging with colleagues and collaborators. - **[Ferdium](https://ferdium.org/)** — Chat, messaging and email in a single app. - **[1Password](https://1password.com/)** — Password and secret management. - **[BitWarden](https://bitwarden.com/)** — Password and secret management for personal use. - **[Notion](https://www.notion.com/)** — Notes, personal jottings and tracking what I'm learning. - **[ClickUp](https://clickup.com/)** — Task, workflow and project management. - **[Freshdesk](https://www.freshworks.com/freshdesk/)** — For managing tickets opened by customers. - **[Deepl](https://www.deepl.com/)** — Reliable translations across multiple languages. ## ☁️ Hosting & Services - **[AWS](http://aws.amazon.com/)** — Application hosting, storage, transactional email and managed services for the projects that need them. - **[DigitalOcean](https://www.digitalocean.com/)** — Droplets and servers for client applications and websites. - **[Cloudflare Pages](https://pages.cloudflare.com/)** — Hosting for this site, with automatic deploys. - **[Nginx](https://nginx.org/)** — Web server and reverse proxy on my servers. - **[GitHub Actions](https://github.com/features/actions)** — CI/CD: automated tests and deploys. ======================================================================== --- title: "PHPStan: clickable file paths in the terminal via phpstan.neon" date: 2026-05-01 lang: en translationKey: phpstan-percorsi-file-cliccabili-nel-terminale tags: [PHPStan, PHP] description: "How to configure phpstan.neon to get complete file paths in PHPStan errors, so you can open them directly in your IDE with a single click." --- Suppose your PHP/Laravel project has a structure like this: ``` . ├── artisan ├── (...) ├── routes ├── src │ ├── App │ └── Domain ├── (...) ├── vendor └── vite.config.js ``` You're running **PHPStan** in the terminal and, when it analyses your code, errors look like this: ``` ------ -------------------------------------------------------------------------------------------------------------------------------------------------------------------- Line App/Concerns/PasswordValidationRules.php (in context of class App\Actions\Fortify\CreateNewUser) ------ -------------------------------------------------------------------------------------------------------------------------------------------------------------------- 17 Method App\Actions\Fortify\CreateNewUser::passwordRules() should return array|Illuminate\Contracts\Validation\ValidationRule|string> but returns array. 🪪 return.type ------ -------------------------------------------------------------------------------------------------------------------------------------------------------------------- ``` The paths look correct at first glance, but they are actually **incomplete**: the leading segment that anchors them to the project root is missing. Terminals like iTerm2, or the integrated terminal in PhpStorm and VS Code, can only make a relative path correctly clickable when it is complete relative to the project root. In this case, the correct filesystem path is `src/App/Concerns/PasswordValidationRules.php`. With a truncated path like `App/Concerns/PasswordValidationRules.php`, the terminal cannot locate the file or resolves the link incorrectly. In my case (using **iTerm2** on macOS), when I press CMD and click the link, it is interpreted as an `http://` address and opened in the browser (which has no idea what to do with it). ## Why does this happen? If your `phpstan.neon` looks like this: ```yaml showLineNumbers parameters: paths: - src ``` …then PHPStan reports error paths *relative* to that single subdirectory, making them incomplete: The path `App/Concerns/PasswordValidationRules.php` is relative to `src`, not to the project root. The terminal cannot resolve it and the link does not work. ## The fix The simplest approach is to list two or more paths under the `paths` key in `phpstan.neon`: ```yaml showLineNumbers parameters: paths: - src - tests ``` Errors will then be reported with complete paths relative to the root: ``` ------ -------------------------------------------------------------------------------------------------------------------------------------------------------------------- Line src/App/Concerns/PasswordValidationRules.php (in context of class App\Actions\Fortify\CreateNewUser) ------ -------------------------------------------------------------------------------------------------------------------------------------------------------------------- 17 Method App\Actions\Fortify\CreateNewUser::passwordRules() should return array|Illuminate\Contracts\Validation\ValidationRule|string> but returns array. 🪪 return.type ------ -------------------------------------------------------------------------------------------------------------------------------------------------------------------- ``` The path `src/App/Concerns/PasswordValidationRules.php` is now complete and clickable: a single click opens the file at the exact line in your IDE. ## Alternative solution What if you have a reason to scan only one directory (e.g. `src`)? Add this block to `phpstan.neon`: ```yaml showLineNumbers services: relativePathHelper: class: PHPStan\File\SimpleRelativePathHelper arguments: currentWorkingDirectory: %currentWorkingDirectory% ``` ## Conclusion A small misconfiguration in `phpstan.neon` is enough to make error paths unusable in the terminal. With the right configuration, paths will be complete and clickable, letting you navigate straight to the problem with a single click. Better **Developer eXperience** = happier developer 😄 ======================================================================== --- title: "Protecting the /livewire/update endpoint from bots with a Laravel middleware" date: 2026-02-21 updated: 2026-02-26 lang: en translationKey: proteggere-livewire-update-con-un-middleware-laravel tags: [Laravel, Livewire, Security] description: "How to block bots attacking the /livewire/update endpoint using a Laravel middleware that validates Content-Type, X-Livewire, Referer, and the session cookie." ---
Update — February 26, 2026: Livewire 4.2.0 ships a built-in RequireLivewireHeaders middleware that validates the presence of the X-Livewire and Content-Type: application/json headers on every update request (PR #9965). If you are on Livewire 4.2.0 or later, you no longer need a custom middleware. The approach described in this article remains valid for Livewire 3.x installations and as an explanation of the same defence mechanism now adopted by the Livewire team itself.
Imagine opening [BugSnag](https://www.bugsnag.com/) on a Monday morning and finding hundreds of errors hitting the `/livewire/update` endpoint. Errors like: > `Cannot assign array to property App\\Livewire\\ExampleComponent::$exampleName of type bool` Different properties involved, but always the same pattern: someone is sending an array where PHP expects a primitive type. That's exactly what happened to us at [Encodia](https://www.encodia.it/). ## The problem: a flood of type errors on BugSnag At [Encodia](https://www.encodia.it/) we have built more than one web application using [Livewire](https://livewire.laravel.com/). A few weeks ago, we started receiving reports from [BugSnag](https://www.bugsnag.com/) about errors like this: > **POST** /livewire/update `Cannot assign array to property App\\Livewire\\ExampleComponent::$exampleName of type bool` Dozens or hundreds of reports, all involving public Livewire properties. The property name and the expected type varied, but the error was always similar: the *payload* sent an array to hydrate a specific property, but since that property was of a different type (`bool`, `string`, etc.), PHP threw an error. ## How we identified the cause While inspecting the calls on BugSnag, this header immediately caught my eye: ``` "user-agent": "python-requests/2.32.4" ``` A quick search revealed that these are attempts to exploit the vulnerability [CVE-2025-54068](https://www.synacktiv.com/en/publications/livewire-remote-command-execution-through-unmarshaling), present in **Livewire 3.x** installations up to version **v3.6.3**. The vulnerability allows an attacker to execute arbitrary code on the server (*Remote Code Execution*) by manipulating the component hydration payload — hence the type errors we were seeing on BugSnag. We had already updated the affected applications; however, the type errors kept coming even on applications running **Livewire 4.x**. The reason is that automated scanners don't check the installed Livewire version before attempting an attack: they simply *hammer* every `/livewire/update` endpoint they find, regardless of the version. ## The solution: a Laravel middleware I added this middleware: ```php showLineNumbers routeIs('default-livewire.update')) { return $next($request); } if ($request->header('Content-Type') !== 'application/json') { $this->abort('Missing application/json header'); } if (! $request->hasHeader('X-Livewire')) { $this->abort('Missing X-Livewire header'); } if (! $request->headers->get('referer')) { $this->abort('Missing Referer'); } if (! $request->cookies->has(config()->string('session.cookie'))) { $this->abort('Invalid Laravel session'); } return $next($request); } private function abort(string $reason): never { $showDetails = app()->isLocal(); abort(code: 403, message: $showDetails ? $reason : ''); } } ``` If the current request does not involve Livewire, it passes through. Otherwise, it checks that: * the `X-Livewire` header is present * the `Referer` header is present * the session cookie is present If at least one of these conditions is not met, it returns a `403 (Forbidden)` error with no explanatory message. Only in the local environment — to make debugging easier — it returns the reason why the `403` was issued. The middleware must be registered so that it runs **before** `\Illuminate\Session\Middleware\StartSession`. This way, BOT requests are blocked before Laravel even initializes the session: no unnecessary overhead, no database access for requests that will be rejected anyway. ## Alternative: blocking bots at the web server level If you prefer not to use a middleware, you can also configure your web server to block suspicious requests directly at the Nginx level, before PHP is involved at all. For example, to block all requests to `/livewire/update` that lack the `X-Livewire` header: ```nginx showLineNumbers location /livewire/update { if ($http_x_livewire = "") { return 403; } # rest of the configuration... } ``` The advantage is that Nginx rejects the request without PHP ever being started. The drawback is that the validation logic lives outside the application and must be kept in sync with any future changes to the endpoint. ## Conclusion The middleware does not replace upgrading Livewire, but acts as an upstream filter: requests lacking the expected headers are blocked before PHP even begins to process the payload. If you use BugSnag or a similar tool, it's worth checking whether you're receiving similar errors: `python-requests` in the user-agent is an unmistakable signal. The fact that the Livewire team adopted exactly this approach in version 4.2.0 — a dedicated middleware validating the same headers — confirms the direction was right. If you are already on Livewire 4.2.0+, the protection is included with no extra code needed. If upgrading is not immediately possible, the middleware described in this article remains an effective solution. ======================================================================== --- title: "Automate your PHP and Laravel workflow with custom Composer scripts" date: 2025-10-24 lang: en translationKey: how-to-use-composer-scripts-to-automate-your-php-laravel-workflow tags: [Composer, PHP, Laravel] description: "Create custom Composer scripts to automate your PHP and Laravel workflow" --- Most PHP/Laravel developers use [Composer](https://getcomposer.org/) only for dependency management, but few take advantage of **Composer custom scripts**. Scripts allow you to automate repetitive tasks like testing, static code analysis, refactoring or initial project setup, simplifying the workflow and reducing errors. Laravel itself defines a few, to simplify the configuration of the project or the execution of certain tasks. ## What is a Composer script Inside the `scripts` section of a `composer.json` file you can define custom scripts. For example: ```json showLineNumbers { "scripts": { "test": "XDEBUG_MODE=off ./vendor/bin/pest --parallel" } } ``` After defining the `test` script, if you run it from the shell ```shell composer test ``` you will see that project tests (unit, feature, etc.) will run, in this case using [Pest](https://pestphp.com/), in parallel and disabling XDebug (which in my local environment is always active — vice versa, you might want to keep it always disabled and enable it only when needed). ## Why use Composer scripts? There are many benefits: * ✅ consistency: all *team* members use the same commands * ⚙️ automation: no more long, forgotten, or incorrectly parameterized commands * 🧩 CI/CD integration: scripts easily integrate into GitHub Actions, GitLab CI, etc. pipelines * 💡 project cleanliness: avoid adding duplicate shell files to the repository and keep configuration in the `composer.json` file ## Practical tips * for **script names**, don't use names of libraries, products or technologies, but terms that represent the task you want to perform. The reason is simple: if today you use a library to execute that task, tomorrow you might replace it with another one that performs the same task (perhaps better); in that case, you would have to change also the script name. Example: ❌ `composer phpunit` ❌ `composer pest` ✅ `composer test` Another example: ❌ `composer rector` ✅ `composer refactor` * use the same script names in all PHP/Laravel projects, so you can find them everywhere and use them easily and with familiarity * if a project doesn't use PHPStan or Rector, for example, don't add the corresponding scripts to the `composer.json` file: making them available might confuse other developers * if you have **XDebug** active, use `XDEBUG_MODE=off` at the beginning of the command to disable it and speed up execution (for example of the test suite or static analysis) ## Practical examples of useful scripts ### Static code analysis * `analyse` ```json "analyse": "XDEBUG_MODE=off ./vendor/bin/phpstan analyse --memory-limit 2G --ansi" ``` Performs static code analysis with [PHPStan](https://phpstan.org/). * `analyse-clear-cache` ```json "analyse-clear-cache": "./vendor/bin/phpstan clear-result-cache" ``` Clears PHPStan cache (sometimes necessary in case of static analysis issues). ### Code refactoring * `refactor` ```json "refactor": "XDEBUG_MODE=off rector process --ansi" ``` Performs code refactoring via [Rector](https://getrector.org/). * `refactor-dry` ```json "refactor-dry": "XDEBUG_MODE=off rector --dry-run --ansi" ``` Runs Rector in `dry run` mode, without modifying files: shows only what changes will be made. ### Code formatting * `format` ```json "format": "vendor/bin/pint --dirty --parallel --ansi" ``` Performs code formatting with [Pint](https://laravel.com/docs/12.x/pint) only on modified files and in parallel. ### Tests and code coverage * `test` ```json "test": "XDEBUG_MODE=off ./vendor/bin/pest --parallel --bail --colors=always" ``` Runs tests with [Pest](https://pestphp.com/). Tests are run in parallel, and if one fails, the execution is stopped. * `test-architecture` ```json "test-architecture": "XDEBUG_MODE=off ./vendor/bin/pest --parallel --bail --group=architectural --colors=always" ``` Runs only [architecture tests](https://pestphp.com/docs/arch-testing) with [Pest](https://pestphp.com/). * `test-coverage` ```json "test-coverage": "php -d memory_limit=-1 ./vendor/bin/pest --coverage --min=80" ``` Runs tests with *code coverage* and shows the report. If the *coverage* is lower than 80%, the command fails. * `type-coverage` ```json "type-coverage": "XDEBUG_MODE=off ./vendor/bin/pest --type-coverage" ``` Runs [type coverage](https://pestphp.com/docs/type-coverage) with [Pest](https://pestphp.com/) to report in which files you can add type to parameters and return types. ## Conclusion Composer scripts are a simple but powerful way to standardize and speed up development in PHP and Laravel. Start by defining a few key commands, and you'll immediately see the difference in your daily workflow. They will also help other developers on your team and provide a standardized method for executing the necessary tasks. The ones listed above are the ones I use most frequently; define them based on your needs and those of your team. ======================================================================== --- title: "Why Nginx creates a file named "off"?" date: 2025-10-23 lang: en translationKey: why-nginx-creates-a-file-named-off tags: [Nginx, Linux, DevOps] description: "An incorrect Nginx configuration can lead to a mysterious “off” file being written." --- If you've ever found an unexpected file named `off` under `/etc/nginx/`, here's the reason why. When configuring **Nginx**, it's possible to **disable error logging**. However, the directive must be used correctly: ```nginx error_log /dev/null; ``` If you mistakenly write: ```nginx error_log off; ``` Nginx will interpret `off` as a file path, not as a keyword, and will try to create (or write to) `/etc/nginx/off`. That's why you'll find an empty file named `off` in your configuration directory. This behavior happens because Nginx's configuration parser does not treat `off` as a special keyword for the `error_log` directive. Instead, it expects a **file path** as the first argument, followed optionally by a log level (like `warn`, `error`, or `debug`). To **disable logging properly**, just redirect it to `/dev/null`, which effectively discards all log output: ```nginx error_log /dev/null; ``` This simple change prevents Nginx from creating the mysterious `off` file. Have you ever had this problem? I have 😁 That's why I decided to write a short post about it. ======================================================================== --- title: "Introducing Laravel Health Env Vars: because missing .env variables hurt" date: 2025-09-21 lang: en translationKey: laravel-health-env-vars-perche-dimenticare-variabili-env-puo-far-male-laravel tags: [Laravel, PHP, DevOps] description: "Laravel Health Env Vars is a Composer package which helps to manage .env variables efficiently" --- **Environment variables** are a crucial part of any Laravel application. They store sensitive data, API keys, database credentials, and other configuration values that your app relies on. But what happens if one of them is missing or misconfigured? Bugs, failed deployments, or — even worse — security issues. This is where [Laravel Health Env Vars](https://github.com/encodia/laravel-health-env-vars) comes in. This package helps you check the presence and validity of your environment variables as part of your application’s health checks. ## Why it matters If you’re a solo developer working on your laptop, it’s unlikely that you’ll forget to add a variable on the production server. But it could happen — and even worse, you might accidentally set the wrong value. Things get more complicated in a **development team**: with multiple developers — or even the same developer on different machines — updating an environment variable means everyone needs to be notified (e-mail, Slack, Discord, Teams, Telegram, in person), and it’s easy to forget. Someone is bound to miss it. Even experienced developers sometimes forget to set a required environment variable on a new server or in a CI/CD pipeline. Missing .env values can lead to: * runtime errors * broken features or integrations * hard-to-debug issues in production By proactively checking your environment variables, you catch potential problems **before** they affect users. ## What the package does At [Encodia](https://www.encodia.it), we've been using Spatie’s excellent [Laravel Health](https://github.com/spatie/laravel-health) for a while, but it seemed there wasn’t a **dedicated check** for **.env variables**. That's why **I built [Laravel Health Env Vars](https://github.com/encodia/laravel-health-env-vars)**, which integrates seamlessly with Spatie's package to provide a simple but powerful health check: * ensure that required .env variables are present * optionally validate their values * run environment-specific checks * fail your health check if something is missing or misconfigured You can run the health checks in different situations: * when **setting up a project locally**: for example, at [Encodia](https://www.encodia.it/) we built an internal command-line tool called _EDIT_ to help align a project — handling tasks like cloning/pulling from GIT, installing dependencies via Composer and NPM, running migrations and seeds, and more. One of EDIT’s key functions is to automatically run the health checks, ensuring everything is configured correctly from the start * **during deployment**: whether in a CI/CD pipeline, a bash script, or other automated workflows, health checks can be executed to verify that all required environment variables are present and valid before going live. ## What the package doesn't Laravel Health Env Vars won’t check your .env variables once you’ve deployed — and let me explain why. In most deployments, it’s best practice to run `php artisan config:cache`. After that, every call to `env('VARIABLE')` will simply return null. See [Caveats](https://github.com/encodia/laravel-health-env-vars?tab=readme-ov-file#caveats) for more details. ## Installation & Setup Installing the package is straightforward: ```bash composer require encodia/laravel-health-env-vars ``` ## Usage Register a check just like any other Laravel Health check: ```php showLineNumbers // typically, in a service provider use Spatie\Health\Facades\Health; use Spatie\Health\Checks\Checks\UsedDiskSpaceCheck; use Encodia\Health\Checks\EnvVars; Health::checks([ // From Spatie's examples UsedDiskSpaceCheck::new() ->warnWhenUsedSpaceIsAbovePercentage(70) ->failWhenUsedSpaceIsAbovePercentage(90), // Many other checks... /* * Check that SOME_API_KEY and MAIL_FROM_ADDRESS variables are * set (no matter in which environment) */ EnvVars::new() ->requireVars([ 'SOME_API_KEY', 'MAIL_FROM_ADDRESS', ]) ]); ``` When you run `php artisan health:check --fail-command-on-failing-check`, if `SOME_API_KEY` or `MAIL_FROM_ADDRESS` have not been set in `.env`, the command will show an error and return a non-zero exit code. Sometimes you just want to make sure a variable is set in a **particular environment** (or in some environments). Let's say you you want to check if `EXTENDED_DEBUG_MODE` has been set (only in _local_) and if `BUGSNAG_API_KEY` (only in _production_, because you don't want BugSnag to be enabled in testing/local/staging/UAT): ```php showLineNumbers use Spatie\Health\Facades\Health; use Encodia\Health\Checks\EnvVars; Health::checks([ // ... // (other checks) // ... /* * Check that SOME_API_KEY and MAIL_FROM_ADDRESS variables are * set (no matter in which environment). * * Only in staging, ensure EXTENDED_DEBUG_MODE has been set. * * Additionally, only in production, * ensure BUGSNAG_API_KEY has been set. */ EnvVars::new() ->requireVars([ 'SOME_API_KEY', 'MAIL_FROM_ADDRESS', ]) ->requireVarsForEnvironment('local', [ 'EXTENDED_DEBUG_MODE' ]) ->requireVarsForEnvironment('production', [ 'BUGSNAG_API_KEY' ]); ]); ``` Need to check if a variable has been set to a **specific value**? You can use `requireVarsMatchValues()` to perform this check, regardless of the current environment. If you need to run this check only if the current environment matches the given one(s), you can use `requireVarsForEnvironment()` or `requireVarsForEnvironments()`. Let's see an example: ```php showLineNumbers use Encodia\Health\Checks\EnvVars; use Spatie\Health\Facades\Health; Health::checks([ EnvVars::new() // ... other methods ... ->requireVarsMatchValues([ // Ensure that APP_LOCALE is set to 'en' (no matter which is the current environment) 'APP_LOCALE' => 'en', // Ensure that APP_TIMEZONE is set to 'UTC' (no matter which is the current environment) 'APP_TIMEZONE' => 'UTC', ]) ->requireVarsMatchValuesForEnvironment('staging', [ // Only if current environment is 'staging', we don't want to send e-mails to real customers 'MAIL_MAILER' => 'log', ]) ->requireVarsMatchValuesForEnvironments(['qa', 'production'], [ // Only if current environment is 'qa' or 'production, we want to log 'info' events or above 'LOG_LEVEL' => 'info', // Only if current environment is 'qa' or 'production, we want to store assets to S3 'FILESYSTEM_DISK' => 's3', ]); ]); ``` ⚠️ When checking values, don’t ever hardcode passwords, keys, or other sensitive info — they’d end up in your GIT repo, and as we all know, that’s a big no-no. ## Conclusion Environment variables are small but critical. With **Laravel Health Env Vars**, you can make sure they are always present and valid, reducing errors and saving time in development and production. Try it out and give feedback on [GitHub](https://github.com/encodia/laravel-health-env-vars) — contributions are welcome!