<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Sviluppo Software &amp; Programmazione | RENOR &amp; Partners S.r.l.</title>
	<atom:link href="https://renor.it/blog/sviluppo-software-programmazione/feed/" rel="self" type="application/rss+xml" />
	<link>https://renor.it/blog/sviluppo-software-programmazione/</link>
	<description></description>
	<lastBuildDate>Mon, 29 Dec 2025 01:45:12 +0000</lastBuildDate>
	<language>it-IT</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
	<item>
		<title>AI per la scrittura di articoli: scrivere senza delegare il pensiero</title>
		<link>https://renor.it/blog/sviluppo-software-programmazione/ai-per-la-scrittura-di-articoli/</link>
		
		<dc:creator><![CDATA[Simone Renzi]]></dc:creator>
		<pubDate>Mon, 29 Dec 2025 01:10:36 +0000</pubDate>
				<category><![CDATA[Sviluppo Software & Programmazione]]></category>
		<category><![CDATA[ai writing]]></category>
		<category><![CDATA[content marketing]]></category>
		<category><![CDATA[intelligenza artificiale]]></category>
		<category><![CDATA[scrittura assistita]]></category>
		<category><![CDATA[seo editoriale]]></category>
		<category><![CDATA[wordpress]]></category>
		<category><![CDATA[writerflow]]></category>
		<guid isPermaLink="false">https://renor.it/?p=2852</guid>

					<description><![CDATA[<p>Con la creazione dell&#8217;infrastruttura di RunAI in RENOR &#38; Partners, abbiamo sviluppato un plugin WordPress che aiuta gli autori ad aumentare la visibilità online pubblicando contenuti di qualità: WriterFlow.WriterFlow nasce proprio per un uso consapevole dell’AI per la scrittura di articoli, come supporto al pensiero umano e non come sua sostituzione. Chi si affida a [&#8230;]</p>
<p>L'articolo <a href="https://renor.it/blog/sviluppo-software-programmazione/ai-per-la-scrittura-di-articoli/">AI per la scrittura di articoli: scrivere senza delegare il pensiero</a> proviene da <a href="https://renor.it">RENOR &amp; Partners S.r.l.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Con la creazione dell&#8217;infrastruttura di <strong><a href="https://runai.it">RunAI</a> </strong>in RENOR &amp; Partners, abbiamo sviluppato un plugin WordPress che aiuta gli autori ad aumentare la visibilità online pubblicando contenuti di qualità: <strong><a href="https://runai.it/writerflow">WriterFlow</a></strong>.<br>WriterFlow nasce proprio per un uso consapevole dell’AI per la scrittura di articoli, come supporto al pensiero umano e non come sua sostituzione.</p>



<p>Chi si affida a ChatGPT per la creazione automatica di contenuti rischia di rimanere indietro. La SEO richiede strategie complesse e non basta fare un copia e incolla, necessita di creatività e di contenuti pensati e scritti per il lettore. Gli articoli scritti interamente da ChatGPT presentano sempre queste caratteristiche:</p>



<ul class="wp-block-list">
<li>Sono in linea con la media</li>



<li>Non introducono esperienza reale</li>



<li>Non risolvono un problema meglio di altri</li>



<li>Sono facilmente riconoscibili anche senza l&#8217;utilizzo di un detector AI</li>



<li>Non tengono conto delle esigenze dell&#8217;utente.</li>
</ul>



<p>Proprio sull&#8217;ultimo punto vale la pena soffermarci per un secondo. </p>



<h2 class="wp-block-heading" id="h-ai-per-la-scrittura-di-articoli-scrivere-pensando-alle-persone-non-alla-seo">AI per la scrittura di articoli: scrivere pensando alle persone, non alla SEO</h2>



<p>Ti sembrerà assurdo ma gli algoritmi dei motori di ricerca tendono a preferire contenuti originali e di valore che risultino utili e interessanti per l&#8217;utente finale. Questo è ciò che posiziona meglio un sito web o un articolo nei risultati di ricerca. Abbiamo affrontato questo argomento anche in questo articolo: <a href="https://renor.it/blog/marketing-business-digitale/come-scrivere-un-articolo-di-blog/">come scrivere un articolo di blog</a>. </p>



<h2 class="wp-block-heading" id="h-writerflow-e-l-ai-per-la-scrittura-di-articoli-non-si-sostituiscono-all-umano">WriterFlow e l’AI per la scrittura di articoli non si sostituiscono all’umano</h2>



<p>Il plugin si avvale di un’intelligenza artificiale dedicata alla SEO, pensata come AI per la scrittura di articoli, capace di generare contenuti ottimizzati in base al ragionamento dell&#8217;utente, fornendo opzioni diverse, sinonimi e suggerimenti per migliorare la qualità della scrittura, rimanendo un semplice supporto alla stesura dell&#8217;idea e non sostituendosi all&#8217;autore.</p>



<p>Ad esempio potrei scrivere:<br><em>&#8220;WriterFlow è un plugin AI per WordPress che ti dà una mano a scrivere gli articoli&#8221;</em></p>



<p>Richiedendo un&#8217;alternativa, quello che ottengo grazie a WriterFlow è questo:</p>



<p>&#8220;<em>WriterFlow è un plugin per WordPress che utilizza l&#8217;intelligenza artificiale per supportare gli autori nella scrittura di articoli.<em>&#8220;</em></em></p>



<h2 class="wp-block-heading" id="h-ai-per-la-scrittura-di-articoli-e-selezione-di-titoli-efficaci">AI per la scrittura di articoli e selezione di titoli efficaci</h2>



<figure class="wp-block-image size-large"><img decoding="async" src="https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.12.53.png" alt="AI per la scrittura di articoli con WriterFlow: suggerimenti automatici di titoli in WordPress" class="wp-image-2855"/><figcaption class="wp-element-caption">WriterFlow propone automaticamente idee per titoli in linea con categoria e argomento dell’articolo.</figcaption></figure>



<p>Per far crescere il proprio sito è indispensabile nutrirlo continuamente di nuovi contenuti. Google non fornisce un numero esatto di articoli da pubblicare ogni giorno, non c&#8217;è un vademecum ma è possibile affermare con buona probabilità che la frequenza di pubblicazione dipenda fortemente dal tipo di sito web. </p>



<p>Un piccolo/medio blog dovrebbe pubblicare non più di 2/3 articoli a settimana, un sito di News è chiaro che ne debba pubblicare molti di più. Ma per un blog aziendale o per un blog personale le idee si esauriscono velocemente. </p>



<p>Con WriterFlow trovare il titolo perfetto è facile. Basta specificare categoria e tag, poi lasciare che WriterFlow generi 10 idee per titoli in linea con l&#8217;argomento selezionato.</p>



<p>Tanto tempo risparmiato e contenuti sempre di qualità.</p>



<h2 class="wp-block-heading" id="h-dalla-scelta-all-azione">Dalla scelta all&#8217;azione</h2>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1416" height="780" src="https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.32.57.png" alt="AI per la scrittura di articoli con WriterFlow: inserimento automatico del titolo in WordPress" class="wp-image-2858" srcset="https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.32.57.png 1416w, https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.32.57-300x165.png 300w, https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.32.57-768x423.png 768w" sizes="(max-width: 1416px) 100vw, 1416px" /><figcaption class="wp-element-caption">Con un solo click, WriterFlow inserisce il titolo scelto direttamente nell’editor WordPress.</figcaption></figure>



<p>Basta un click sul titolo prescelto e viene automaticamente inserito. WriterFlow si integra perfettamente con il tuo workflow WordPress, semplificando la creazione di contenuti ottimizzati per la SEO.</p>



<p>Basta perdere tempo con la ricerca del titolo perfetto. È sufficiente fornire l&#8217;argomento e WriterFlow proporrà diverse opzioni. Se non trovate quello che cercate, basta cliccare un tasto per rigenerare nuove idee.</p>



<h2 class="wp-block-heading" id="h-scrittura-assistita-un-uso-consapevole-dell-ai-per-la-scrittura-di-articoli">Scrittura assistita: un uso consapevole dell’AI per la scrittura di articoli</h2>



<figure class="wp-block-image size-large"><img decoding="async" width="1414" height="632" src="https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.37.11.png" alt="AI per la scrittura di articoli con WriterFlow: suggerimenti di testo durante la scrittura" class="wp-image-2859" srcset="https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.37.11.png 1414w, https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.37.11-300x134.png 300w, https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.37.11-768x343.png 768w" sizes="(max-width: 1414px) 100vw, 1414px" /><figcaption class="wp-element-caption">WriterFlow propone suggerimenti di testo mentre scrivi, lasciando all’autore la libertà di accettarli o modificarli.</figcaption></figure>



<p>Nei suggerimenti di testo hai due modalità. Questo approccio rende l’AI per la scrittura di articoli uno strumento discreto e realmente utile. Accettare il testo proposto, oppure iniziare a scrivere per poi variare il contenuto con nuove idee che ti vengono in mente. </p>



<h3 class="wp-block-heading" id="h-accettazione-del-testo-proposto">Accettazione del testo proposto</h3>



<figure class="wp-block-image size-large"><img decoding="async" width="1388" height="676" src="https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.42.23.png" alt="AI per la scrittura di articoli con WriterFlow: inserimento del testo suggerito nei blocchi Gutenberg" class="wp-image-2861" srcset="https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.42.23.png 1388w, https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.42.23-300x146.png 300w, https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.42.23-768x374.png 768w" sizes="(max-width: 1388px) 100vw, 1388px" /><figcaption class="wp-element-caption">Nei blocchi di paragrafo è possibile inserire il suggerimento di testo con un click o premendo TAB.</figcaption></figure>



<p>Nei blocchi di paragrafo di Gutenberg ti basta cliccare sul suggerimento per inserire tutta la frase suggerita. Oppure premere semplicemente il tasto &#8220;TAB&#8221;. </p>



<h2 class="wp-block-heading" id="h-frasi-alternative">Frasi alternative</h2>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1396" height="766" src="https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.44.49.png" alt="AI per la scrittura di articoli con WriterFlow: riformulazione di una frase selezionata" class="wp-image-2862" style="aspect-ratio:1.8224964404366397" srcset="https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.44.49.png 1396w, https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.44.49-300x165.png 300w, https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.44.49-768x421.png 768w" sizes="auto, (max-width: 1396px) 100vw, 1396px" /><figcaption class="wp-element-caption">Selezionando una frase, WriterFlow genera alternative più coerenti e professionali.</figcaption></figure>



<p>Non sei soddisfatto di come hai scritto una frase e la vorresti riformulata in modo più professionale e aderente al resto dell&#8217;articolo? Nulla di più semplice. <br>Con WriterFlow attivo è sufficiente selezionare la frase che vuoi riformulare e verranno generate immediatamente delle alternative. Ti basta premere l&#8217;alternativa che preferisci oppure generare nuove alternative se quelle proposte non ti soddisfano. </p>



<h2 class="wp-block-heading" id="h-scegli-la-parola-perfetta">Scegli la parola perfetta</h2>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1418" height="800" src="https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.47.20.png" alt="AI per la scrittura di articoli con WriterFlow: suggerimento di sinonimi durante la revisione del testo" class="wp-image-2863" srcset="https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.47.20.png 1418w, https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.47.20-300x169.png 300w, https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.47.20-768x433.png 768w" sizes="auto, (max-width: 1418px) 100vw, 1418px" /><figcaption class="wp-element-caption">Selezionando una parola, WriterFlow propone sinonimi alternativi per migliorare precisione e stile del testo.</figcaption></figure>



<p>Stai cercando un sinonimo che non ti viene? WriterFlow ti velocizza anche in questo, mostrando il lato più pratico dell’AI per la scrittura di articoli. Fai doppio click sulla parola di cui vuoi cercare un sinonimo in modo che sia evidenziata e ti verranno consigliati 5 sinonimi. Se i sinonimi proposti non ti convincono, puoi cliccare su &#8220;rigenera&#8221; per ottenere 5 nuove opzioni.  Scegli il sinonimo che preferisci e verrà inserito al posto della parola originale.</p>



<h2 class="wp-block-heading" id="h-modalita-automatica-o-manuale">Modalità automatica o manuale?</h2>



<h3 class="wp-block-heading" id="h-in-modalita-automatica-puoi-impostare-un-tempo-di-inattivita-per-ricevere-suggerimenti">In modalità automatica puoi impostare un tempo di inattività per ricevere suggerimenti</h3>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1424" height="786" src="https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.50.28.png" alt="AI per la scrittura di articoli con WriterFlow: impostazioni della modalità automatica dei suggerimenti" class="wp-image-2864" srcset="https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.50.28.png 1424w, https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.50.28-300x166.png 300w, https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.50.28-768x424.png 768w" sizes="auto, (max-width: 1424px) 100vw, 1424px" /><figcaption class="wp-element-caption">La modalità automatica consente di ricevere suggerimenti dopo un periodo di inattività configurabile.</figcaption></figure>



<p>La modalità automatica è molto comoda quando si vuole ricevere il suggerimento dopo un certo periodo di inattività. La durata del tempo di inattività è configurabile tramite le impostazioni di WriterFlow.</p>



<p>Con un tempo di 5 secondi, come illustrato nell&#8217;esempio, il suggerimento si attiverà dopo 5 secondi di stop alla scrittura.</p>



<h3 class="wp-block-heading" id="h-in-modalita-manuale-sei-tu-a-scegliere-quando-vuoi-ricevere-un-suggerimento">In modalità manuale sei tu a scegliere quando vuoi ricevere un suggerimento</h3>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1254" height="270" src="https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.54.52.png" alt="Pulsante Suggerisci nella modalità manuale" class="wp-image-2865" srcset="https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.54.52.png 1254w, https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.54.52-300x65.png 300w, https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.54.52-768x165.png 768w" sizes="auto, (max-width: 1254px) 100vw, 1254px" /><figcaption class="wp-element-caption">In modalità manuale, il pulsante “Suggerisci” compare accanto al cursore dopo alcuni secondi di inattività.</figcaption></figure>



<p>Nella modalità manuale, dopo tre secondi di stop alla scrittura comparirà il tasto suggerisci a destra del cursore. Ti basterà cliccare sul pulsante per ricevere i suggerimenti di scrittura. Nella modalità manuale, le funzioni di generazione alternative e di suggerimento sinonimico saranno ancora disponibili. Come nel caso della modalità automatica dovrai solamente selezionare la frase o la parola per ottenere rispettivamente il testo alternativo o il sinonimo desiderato.</p>



<h1 class="wp-block-heading" id="h-con-writerflow-paghi-solo-quello-che-usi">Con WriterFlow paghi solo quello che usi</h1>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img loading="lazy" decoding="async" width="478" height="422" src="https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.58.40.png" alt="Visualizzazione del credito residuo nell’editor WordPress" class="wp-image-2866 size-full" srcset="https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.58.40.png 478w, https://renor.it/wp-content/uploads/2025/12/Screenshot-2025-12-29-alle-01.58.40-300x265.png 300w" sizes="auto, (max-width: 478px) 100vw, 478px" /></figure><div class="wp-block-media-text__content">
<p>Nel menu a destra dell&#8217;editor dell&#8217;articolo hai sempre modo di controllare il credito residuo. Ad ogni interazione il credito verrà aggiornato in modo automatico. Niente sorprese. </p>



<p>Inoltre i nuovi iscritti possono godere di un credito gratuito di 5 euro per testare le funzionalità di WriterFlow. I 5 euro di credito ti basteranno per almeno 15 articoli di media lunghezza. </p>
</div></div>



<p>Iscrizione e ricarica sono operazioni semplicissime. La registrazione e la creazione di una chiave API sono operazioni che avvengono in modo automatico direttamente dalla tua area amministrativa di WordPress. La ricarica richiede invece l&#8217;accesso al portale di RunAI e potrai ricaricare il tuo credito con un minimo di 5 euro. </p>



<p>RunAI utilizza transazioni sicure tramite l&#8217;introduzione dei gateway di pagamento PayPal e Stripe. È necessaria una carta di credito. Non ci sono costi ricorrenti, non ci sono piani in abbonamento. Ricarichi il tuo credito e utilizzi WriterFlow. Nulla di più semplice. </p>



<p>Puoi usare lo stesso account WriterFlow anche su più siti WordPress e il credito non ha scadenza. Se un mese sei in ferie e non stai lavorando non dovrai continuare a pagare per un servizio che non utilizzi. I costi sono puramente a consumo. </p>



<h2 class="wp-block-heading" id="h-privacy-e-data-retention">Privacy e data retention</h2>



<p>La privacy è garantita da <a href="https://runai.it">RunAI</a>, la nostra piattaforma di intelligenza artificiale con nodi di inferenza Apple Silicon criptati end-to-end. Nessun contenuto viene salvato, i dati vengono elaborati solo durante l&#8217;utilizzo del servizio e cancellati un microsecondo dopo averti fornito il suggerimento nel rispetto delle normative GDPR in modo trasparente e sicuro per gli utenti. </p>



<h2 class="wp-block-heading" id="h-conclusione">Conclusione</h2>



<p>WriterFlow è lo strumento innovativo che rivoluziona il modo di scrivere online, offrendo un uso consapevole dell’AI per la scrittura di articoli.</p>



<p>Scarica WriterFlow e inizia ad usarlo con il credito gratuito. Diventerà il tuo compagno di scrittura e ti aiuterà a creare contenuti di qualità.</p>
<p>L'articolo <a href="https://renor.it/blog/sviluppo-software-programmazione/ai-per-la-scrittura-di-articoli/">AI per la scrittura di articoli: scrivere senza delegare il pensiero</a> proviene da <a href="https://renor.it">RENOR &amp; Partners S.r.l.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Da Zero a Packagist: pubblicare una libreria PHP su Composer</title>
		<link>https://renor.it/blog/sviluppo-software-programmazione/da-zero-a-packagist-pubblicare-una-libreria-php-su-composer/</link>
		
		<dc:creator><![CDATA[Simone Renzi]]></dc:creator>
		<pubDate>Mon, 12 May 2025 22:06:01 +0000</pubDate>
				<category><![CDATA[Sviluppo Software & Programmazione]]></category>
		<category><![CDATA[CI/CD]]></category>
		<category><![CDATA[Composer]]></category>
		<category><![CDATA[dependency management]]></category>
		<category><![CDATA[GitHub]]></category>
		<category><![CDATA[open-source]]></category>
		<category><![CDATA[package publishing]]></category>
		<category><![CDATA[Packagist]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[PHP library]]></category>
		<category><![CDATA[PHPUnit]]></category>
		<category><![CDATA[PSR-4]]></category>
		<category><![CDATA[release workflow]]></category>
		<category><![CDATA[semantic versioning]]></category>
		<guid isPermaLink="false">https://renor.it/?p=490</guid>

					<description><![CDATA[<p>Pubblicare un pacchetto Composer significa rendere il proprio codice installabile con un semplice comando da CLI composer require vendor/package e tenere aggiornamenti, versioni e dipendenze sotto controllo. Per seguire la guida abbiamo bisogno di: Rifacendoci all&#8217;articolo in cui avevamo visto come sviluppare una libreria di Statistica descrittiva la struttura della repository era la seguente: Nel [&#8230;]</p>
<p>L'articolo <a href="https://renor.it/blog/sviluppo-software-programmazione/da-zero-a-packagist-pubblicare-una-libreria-php-su-composer/">Da Zero a Packagist: pubblicare una libreria PHP su Composer</a> proviene da <a href="https://renor.it">RENOR &amp; Partners S.r.l.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Pubblicare un pacchetto Composer significa rendere il proprio codice installabile con un semplice comando da CLI</p>



<p><code>composer require vendor/package</code></p>



<p>e tenere aggiornamenti, versioni e dipendenze sotto controllo. Per seguire la guida  abbiamo bisogno di: </p>



<ul class="wp-block-list">
<li>Git versione maggiore o uguale alla 2.0 installata e configurata</li>



<li>Un account GitHub e token con permesso repo</li>



<li>Composer con versione maggiore o uguale alla 2.5 in <code>$PATH</code></li>



<li>Account gratuito su Packagist.org (che si registra tramite GitHub via OAuth)</li>
</ul>



<p>Rifacendoci all&#8217;articolo in cui avevamo visto come sviluppare una libreria di <a href="https://renor.it/una-classe-di-statistica-descrittiva-in-php/" data-type="link" data-id="https://renor.it/una-classe-di-statistica-descrittiva-in-php/">Statistica descrittiva</a> la struttura della repository era la seguente:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="descriptive-statistics-php/
├─ composer.json
├─ composer.lock
├─ phpunit.xml
├─ examples/
│  └─ geometric_mean_demo.php
│  └─ harmonic_mean_demo.php
│  └─ iqr_demo.php
│  └─ mad_demo.php
│  └─ mean_demo.php
│  └─ median_demo.php
│  └─ min_max_demo.php
│  └─ mode_demo.php
│  └─ percentile_demo.php
│  └─ range_demo.php
│  └─ standard_deviation_demo.php
│  └─ trimmed_mean_demo.php
│  └─ variance_demo.php
├─ src/
│  └─ DescriptiveStats.php
├─ tests/
│  └─ DescriptiveStatsTest.php
└─ vendor/
    └─ autoload.php" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">descriptive-statistics-php/</span></span>
<span class="line"><span style="color: #D4D4D4">├─ composer</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">json</span></span>
<span class="line"><span style="color: #D4D4D4">├─ composer</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">lock</span></span>
<span class="line"><span style="color: #D4D4D4">├─ phpunit</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">xml</span></span>
<span class="line"><span style="color: #D4D4D4">├─ examples/</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ geometric_mean_demo</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ harmonic_mean_demo</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ iqr_demo</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ mad_demo</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ mean_demo</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ median_demo</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ min_max_demo</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ mode_demo</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ percentile_demo</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ range_demo</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ standard_deviation_demo</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ trimmed_mean_demo</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ variance_demo</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">├─ src/</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ DescriptiveStats</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">├─ tests/</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ DescriptiveStatsTest</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">└─ vendor/</span></span>
<span class="line"><span style="color: #D4D4D4">    └─ autoload</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span></code></pre></div>



<p>Nel terminale, all&#8217;interno della cartella del progetto digitiamo il comando: </p>



<p><code>composer init</code></p>



<p>Composer ci chiederà alcune informazioni partendo dal Package name che viene espresso come vendor/package-name. Nel mio caso essendo il mio account denominato &#8220;thesimon82&#8221; il nome del pacchetto sarà <code>thesimon82/descriptive-statistics</code>. Confermiamo con invio.</p>



<p>Ora ci viene chiesta la descrizione, per questo progetto inseriamo: <code>Lightweight PHP class for descriptive statistics</code>.</p>



<p>L&#8217;autore e ci viene restituito l&#8217;autore di default se l&#8217;avevamo già impostato, altrimenti inseriamo il nostro nome e cognome. </p>



<p>Minimum stability, se è una versione stabile inseriamo &#8220;stable&#8221;.</p>



<p>Per Package Type inseriamo: &#8220;library&#8221;</p>



<p>In license: MIT</p>



<p>Ci viene chiesto se vogliamo inserire, uno per uno, i pacchetti di produzione (require) che la libreria necessita per funzionare. In questo caso la classe è stand-alone (usa solo funzioni native di PHP), quindi non abbiamo altre dipendenze oltre alle versione minima di PHP pertanto possiamo anche dichiarare &#8220;no&#8221;. Se stai pensando a PHPUnit per i test sui metodi della classe, ti ricordo che PHPUnit è stato settato solo per la fase di sviluppo e test pertanto va inserito in require-dev e non in require. In questo modo chi installerà il pacchetto in produzione non si troverà PHPUnit tra le dipendenze di runtime. </p>



<p>La domanda successiva infatti richiede proprio se ho intenzione di definire le mie dipendenze dev in modo interattivo (require-dev), qui digitiamo &#8220;yes&#8221; e ci chiederà di inserire il nome del pacchetto. Digitiamo ora <code>phpunit/phpunit</code>. Ci darà una lista di pacchetti trovati e selezioniamo phpunit/phpunit con il numero corrispondente. Confermiamo con invio e ci viene chiesto quale versione intendiamo includere. Confermiamo semplicemente con invio per installare l&#8217;ultima versione.</p>



<p>Ci chiederà ora se vogliamo installare altri pacchetti di dipendenze, ma non ne abbiamo altri quindi premiamo semplicemente invio. <br>Ora ci chiederà se intendiamo aggiungere un mapping PSR-4 per l&#8217;autoload delle classi chiedendoci di mappare il namespace Thesimon82DescriptiveStatistics ma nel progetto avevamo utilizzato Renor\Statistics quindi digitiamo n per skippare la proposta.</p>



<p>Finalmente otteniamo un resoconto dei dati inseriti:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="{
    &quot;name&quot;: &quot;thesimon82/descriptive-statistics&quot;,
    &quot;description&quot;: &quot;Lightweight PHP class for descriptive statistics&quot;,
    &quot;type&quot;: &quot;library&quot;,
    &quot;require-dev&quot;: {
        &quot;phpunit/phpunit&quot;: &quot;^12.1&quot;
    },
    &quot;license&quot;: &quot;MIT&quot;,
    &quot;authors&quot;: [
        {
            &quot;name&quot;: &quot;Simone Renzi&quot;,
            &quot;email&quot;: &quot;info@simonerenzi.com&quot;
        }
    ],
    &quot;minimum-stability&quot;: &quot;stable&quot;,
    &quot;require&quot;: {},
    &quot;autoload&quot;: {
        &quot;psr-4&quot;: {
            &quot;Renor\Statistics\&quot;: &quot;src/&quot;
        }
    }
}

Do you confirm generation [yes]? " style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    &quot;name&quot;: &quot;thesimon82/descriptive-statistics&quot;,</span></span>
<span class="line"><span style="color: #D4D4D4">    &quot;description&quot;: &quot;Lightweight PHP class for descriptive statistics&quot;,</span></span>
<span class="line"><span style="color: #D4D4D4">    &quot;type&quot;: &quot;library&quot;,</span></span>
<span class="line"><span style="color: #D4D4D4">    &quot;require-dev&quot;: {</span></span>
<span class="line"><span style="color: #D4D4D4">        &quot;phpunit/phpunit&quot;: &quot;^12.1&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">    },</span></span>
<span class="line"><span style="color: #D4D4D4">    &quot;license&quot;: &quot;MIT&quot;,</span></span>
<span class="line"><span style="color: #D4D4D4">    &quot;authors&quot;: [</span></span>
<span class="line"><span style="color: #D4D4D4">        {</span></span>
<span class="line"><span style="color: #D4D4D4">            &quot;name&quot;: &quot;Simone Renzi&quot;,</span></span>
<span class="line"><span style="color: #D4D4D4">            &quot;email&quot;: &quot;info@simonerenzi.com&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    ],</span></span>
<span class="line"><span style="color: #D4D4D4">    &quot;minimum-stability&quot;: &quot;stable&quot;,</span></span>
<span class="line"><span style="color: #D4D4D4">    &quot;require&quot;: {},</span></span>
<span class="line"><span style="color: #D4D4D4">    &quot;autoload&quot;: {</span></span>
<span class="line"><span style="color: #D4D4D4">        &quot;psr-4&quot;: {</span></span>
<span class="line"><span style="color: #D4D4D4">            &quot;Renor\Statistics\&quot;: &quot;src/&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">Do you confirm generation [yes]? </span></span></code></pre></div>



<p>Qui controlliamo i dati e se tutto è giusto confermiamo con &#8220;yes&#8221; e installiamo le dipendenze.</p>



<p>Al termine digitiamo il comando:</p>



<p><code>composer dump-autoload</code> per rigenerare l&#8217;autoloader delle classi.</p>



<p>È arrivato il momento di fare il commit con questa serie di comandi: </p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="git add .
git commit -m &quot;feat: first stable release&quot;
git tag v1.0.0
git push origin main v1.0.0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">git add .</span></span>
<span class="line"><span style="color: #D4D4D4">git commit -m &quot;feat: first stable release&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">git tag v1.0.0</span></span>
<span class="line"><span style="color: #D4D4D4">git push origin main v1.0.0</span></span></code></pre></div>



<p>Ora ci muoviamo su GitHub e dovremmo vedere la nostra repo con l&#8217;ultimo commit, nel mio caso thesimon82 feat: first stable release.</p>



<p>Copiamo l&#8217;URL in alto sulla barra degli indirizzi e ci spostiamo su packagist.</p>



<p>Clicchiamo in alto su &#8220;Submit&#8221; e incolliamo nel campo di testo il link di GitHub che abbiamo copiato. Poi premiamo il pulsante Check. A questo punto Packagist analizzerà il composer.json e mostrerà subito la scheda del pacchetto. Cliccando sul pulsante in basso &#8220;Submit&#8221; viene creato il Package. </p>



<p>Abbiamo terminato, ora è il momento di testare il funzionamento.</p>



<p>Creiamo una nuova cartella &#8220;Stats Test&#8221; poi andiamo su terminale ed entriamo all&#8217;interno della cartella. <br>Qui digitiamo:</p>



<p><code>composer require thesimon82/descriptive-statistics</code></p>



<p>Ora importiamo questa cartella all&#8217;interno del nostro ambiente di sviluppo e creiamo un nuovo file nella root: test.php</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="&lt;?php
require_once 'vendor/autoload.php';
use RenorStatisticsDescriptiveStats;

$stats = new DescriptiveStats([1, 2, 3, 4, 5,6.22]);
echo $stats-&gt;mean();" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">&lt;?php</span></span>
<span class="line"><span style="color: #C586C0">require_once</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&#39;vendor/autoload.php&#39;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #569CD6">use</span><span style="color: #D4D4D4"> RenorStatistics</span><span style="color: #4EC9B0">DescriptiveStats</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #9CDCFE">$stats</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">DescriptiveStats</span><span style="color: #D4D4D4">([</span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">2</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">3</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">4</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">5</span><span style="color: #D4D4D4">,</span><span style="color: #B5CEA8">6.22</span><span style="color: #D4D4D4">]);</span></span>
<span class="line"><span style="color: #DCDCAA">echo</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$stats</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #DCDCAA">mean</span><span style="color: #D4D4D4">();</span></span></code></pre></div>



<p>Come possiamo vedere, stiamo includendo l&#8217;autoloader generato da Composer in cui tutti le classi mappate con PSR-4 diventano disponibili senza ulteriori require. </p>



<p>Definiamo il namespace utilizzato nella classe DescriptiveStats e istanziamo un nuovo oggetto con un dataset numerico. Ti ricorderai che il costruttore della classe filtrava automaticamente eventuali valori non numerici, riallineava le chiavi dell&#8217;array e lanciava un&#8217;eccezione se l&#8217;insieme fosse risultato vuoto. Viene quindi richiamato il metodo <code>mean()</code> che esegue la media e lo visualizza a schermo. </p>



<p>Se adesso dal terminale lanci il comando <code>php test.php</code> se hai fatto tutto correttamente dovresti avere come risultato 3.5366666666667 che è proprio la media approssimata dei valori che abbiamo passato al costruttore della classe. </p>



<h2 class="wp-block-heading">Conclusioni</h2>



<p>Abbiamo visto come trasformare una semplice classe PHP in un pacchetto Composer distribuito su Packagist. Ora la libreria può essere installata con un unico comando, beneficia di versionamento semantico, integrazione continua e di un&#8217;automazione che aggiorna automaticamente la scheda su Packagist a ogni nuovo tag. </p>



<p>Da questo momento il ciclo di vita è lineare: implementi una funazionalità, esegui i test, incrementi la versione, crei il tag e fa il push su GitHub. Packagist e Composer faranno il resto!</p>



<p>[starbox]</p>
<p>L'articolo <a href="https://renor.it/blog/sviluppo-software-programmazione/da-zero-a-packagist-pubblicare-una-libreria-php-su-composer/">Da Zero a Packagist: pubblicare una libreria PHP su Composer</a> proviene da <a href="https://renor.it">RENOR &amp; Partners S.r.l.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Una Classe di Statistica descrittiva in PHP</title>
		<link>https://renor.it/blog/sviluppo-software-programmazione/una-classe-di-statistica-descrittiva-in-php/</link>
		
		<dc:creator><![CDATA[Simone Renzi]]></dc:creator>
		<pubDate>Mon, 12 May 2025 21:06:41 +0000</pubDate>
				<category><![CDATA[Sviluppo Software & Programmazione]]></category>
		<category><![CDATA[analisi dati]]></category>
		<category><![CDATA[Composer]]></category>
		<category><![CDATA[deviazione standard]]></category>
		<category><![CDATA[errore standard]]></category>
		<category><![CDATA[IQR]]></category>
		<category><![CDATA[libreria PHP]]></category>
		<category><![CDATA[matematica applicata]]></category>
		<category><![CDATA[media aritmetica]]></category>
		<category><![CDATA[mediana]]></category>
		<category><![CDATA[moda]]></category>
		<category><![CDATA[Packagist]]></category>
		<category><![CDATA[percentili]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[programmazione]]></category>
		<category><![CDATA[scarto assoluto medio]]></category>
		<category><![CDATA[statistica descrittiva]]></category>
		<category><![CDATA[tutorial PHP]]></category>
		<category><![CDATA[varianza]]></category>
		<guid isPermaLink="false">https://renor.it/?p=444</guid>

					<description><![CDATA[<p>Nel mondo dello sviluppo software l&#8217;analisi dei dati è sempre più centrale. Con l&#8217;avvento dell&#8217;intelligenza artificiale nel mercato consumer molti la utilizzano anche per analizzare dati statistici. In molti casi però l&#8217;uso di questa tecnologia non è richiesta ed è anzi meno efficiente di un solido algoritmo che si occupi di prendere dati in ingresso [&#8230;]</p>
<p>L'articolo <a href="https://renor.it/blog/sviluppo-software-programmazione/una-classe-di-statistica-descrittiva-in-php/">Una Classe di Statistica descrittiva in PHP</a> proviene da <a href="https://renor.it">RENOR &amp; Partners S.r.l.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Nel mondo dello sviluppo software l&#8217;analisi dei dati è sempre più centrale. Con l&#8217;avvento dell&#8217;intelligenza artificiale nel mercato consumer molti la utilizzano anche per analizzare dati statistici. In molti casi però l&#8217;uso di questa tecnologia non è richiesta ed è anzi meno efficiente di un solido algoritmo che si occupi di prendere dati in ingresso e restituire &#8220;black-box&#8221; dati in uscita.</p>



<p>L&#8217;intelligenza artificiale è ormai interpretata da molti imprenditori come una moda ma andrebbe utilizzata solo laddove è veramente un valore aggiunto. Parlando di efficienza, utilizzare l&#8217;intelligenza artificiale per calcolare una media, una mediana, una moda non è solamente come sparare con un bazooka per uccidere un paio di mosche ma può rivelarsi anche più lenta, dal momento che bisogna calcolare i tempi di inferenza, rispetto ad un algoritmo a stati finiti. </p>



<p>Anche in contesti non strettamente scientifici o accademici, conoscere i fondamenti della statistica descrittiva, può rivelarsi estremamente utile: dalla generazione di report automatizzati, al monitoraggio delle performance di un&#8217;applicazione, fino alla costruzione di dashboard o strumenti analitici. </p>



<p>In questo articolo realizzeremo una classe PHP che calcola i principali indicatori statistici descrittivi, come media, mediana, moda, varianza, deviazione standard, quartili, ecc., partendo da zero, senza librerie esterne. Il codice sarà pulito, riutilizzabile e pronto per essere trasfromato in un pacchetto Composer su cui verrà realizzato un altro articolo. </p>



<h2 class="wp-block-heading">Progettare una classe semplice ma estensibile</h2>



<p>Per costruire una classe di statistica descrittiva utile e facilmente integrabile in altri progetti, adotteremo una struttura ad oggetti aderente ai principi SOLID, evitando dipendenze esterne e garantendo la possibilità di estensoine futura (es. supporto a dataset associativi o lettura da file CSV). </p>



<p>La classe sarà progettata per lavorare su array di valori numerici, e calcolare in modo efficiente i principali indici statistici. L&#8217;obiettivo è offrire un&#8217;interfaccia semplice. </p>



<p>Per prima cosa andiamo a creare una nuova repo su GitHub, la trovate al link:<br><a href="https://github.com/thesimon82/descriptive-statistics-php">https://github.com/thesimon82/descriptive-statistics-php</a></p>



<p>Iniziamo ad implementare i metodi.</p>



<p>La struttura del progetto dovrà avere questa forma: </p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="descriptive-statistics-php/
├─ composer.json
├─ composer.lock
├─ phpunit.xml
├─ examples/
│  └─ geometric_mean_demo.php
│  └─ harmonic_mean_demo.php
│  └─ iqr_demo.php
│  └─ mad_demo.php
│  └─ mean_demo.php
│  └─ median_demo.php
│  └─ min_max_demo.php
│  └─ mode_demo.php
│  └─ percentile_demo.php
│  └─ range_demo.php
│  └─ standard_deviation_demo.php
│  └─ trimmed_mean_demo.php
│  └─ variance_demo.php
├─ src/
│  └─ DescriptiveStats.php
├─ tests/
│  └─ DescriptiveStatsTest.php
└─ vendor/
    └─ autoload.php" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">descriptive-statistics-php/</span></span>
<span class="line"><span style="color: #D4D4D4">├─ composer</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">json</span></span>
<span class="line"><span style="color: #D4D4D4">├─ composer</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">lock</span></span>
<span class="line"><span style="color: #D4D4D4">├─ phpunit</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">xml</span></span>
<span class="line"><span style="color: #D4D4D4">├─ examples/</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ geometric_mean_demo</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ harmonic_mean_demo</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ iqr_demo</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ mad_demo</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ mean_demo</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ median_demo</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ min_max_demo</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ mode_demo</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ percentile_demo</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ range_demo</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ standard_deviation_demo</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ trimmed_mean_demo</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ variance_demo</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">├─ src/</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ DescriptiveStats</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">├─ tests/</span></span>
<span class="line"><span style="color: #D4D4D4">│  └─ DescriptiveStatsTest</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span>
<span class="line"><span style="color: #D4D4D4">└─ vendor/</span></span>
<span class="line"><span style="color: #D4D4D4">    └─ autoload</span><span style="color: #D4D4D4">.</span><span style="color: #D4D4D4">php</span></span></code></pre></div>



<p>Come puoi notare ho previsto anche una cartella per l&#8217;inserimento dei test sui metodi della classe (opzionale) con phpunit. Il file phpunit.xml definisce la cartella dei test ed i sorgenti di phpunit che deve essere installato tramite composer alla repo packagist <code>phpunit/phpunit</code> inserendolo come dipendenza di sviluppo tramite il comando <code>composer require --dev phpunit/phpunit ^10</code>.</p>



<h2 class="wp-block-heading">Media</h2>



<p>La <strong>media aritmetica</strong> (o valore medio) di un insieme di <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-b170995d512c659d8668b4e42e1fef6b_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#110;" title="Rendered by QuickLaTeX.com" height="8" width="11" style="vertical-align: 0px;"/> osservazioni numeriche <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-8d3c2b80f026f94f40369cb7e40d0c8c_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#120;&#95;&#49;&#44;&#32;&#120;&#95;&#50;&#44;&#32;&#100;&#111;&#116;&#115;&#44;&#32;&#120;&#95;&#110;" title="Rendered by QuickLaTeX.com" height="16" width="110" style="vertical-align: -4px;"/> si ottiene sommando tutti i valori e dividendo il risultato per il numero totale di osservazioni. Formalmente si scrive:</p>



<p><br><img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-f3bd3be38e3f10cd90e07cd308b2a5e6_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#98;&#97;&#114;&#123;&#120;&#125;&#32;&#59;&#61;&#59;&#32;&#102;&#114;&#97;&#99;&#123;&#49;&#125;&#123;&#110;&#125;&#32;&#115;&#117;&#109;&#95;&#123;&#105;&#61;&#49;&#125;&#94;&#123;&#110;&#125;&#32;&#120;&#95;&#105;" title="Rendered by QuickLaTeX.com" height="17" width="193" style="vertical-align: -5px;"/></p>



<p><br>dove <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-9487a081c1c399d64321b8ddbb018fc9_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#98;&#97;&#114;&#123;&#120;&#125;" title="Rendered by QuickLaTeX.com" height="12" width="35" style="vertical-align: 0px;"/> rappresenta la media aritmetica, <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-b170995d512c659d8668b4e42e1fef6b_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#110;" title="Rendered by QuickLaTeX.com" height="8" width="11" style="vertical-align: 0px;"/> è la cardinalità del campione e <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-1520e563917c0f427d49ce6a87b8699b_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#115;&#117;&#109;&#95;&#123;&#105;&#61;&#49;&#125;&#94;&#123;&#110;&#125;&#32;&#120;&#95;&#105;" title="Rendered by QuickLaTeX.com" height="17" width="72" style="vertical-align: -5px;"/> indica la somma di tutte le osservazioni. Questa misura fornisce un’indicazione sintetica della tendenza centrale dei dati, pur essendo sensibile alla presenza di valori anomali (outlier) che possono farla divergere significativamente dal “centro” reale della distribuzione.</p>



<p>Implementiamo questo metodo nella nostra classe <code>DescriptiveStats.php</code></p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="&lt;?php

declare(strict_types=1);

namespace RenorStatistics;

/**
 * Class DescriptiveStats
 *
 * A lightweight class to perform basic descriptive statistics on numeric datasets.
 */
class DescriptiveStats
{
    /**
     * @var float[] Filtered and normalized numeric dataset.
     */
    private array $data;

    /**
     * Main constructor.
     *
     * @param array $data An array containing the numeric values to be analyzed.
     * @throws InvalidArgumentException If the array is empty or contains no numeric values.
     */
    public function __construct(array $data)
    {
        // Filter only numeric values (int or float) and reset array keys
        $filtered = array_filter($data, 'is_numeric');
        $this-&gt;data = array_values($filtered);

        if (count($this-&gt;data) === 0) {
            throw new InvalidArgumentException('The dataset must contain at least one numeric value.');
        }
    }

    /**
     * Calculates the arithmetic mean of the dataset.
     *
     * @return float The arithmetic mean.
     */
    public function mean(): float
    {
        return array_sum($this-&gt;data) / count($this-&gt;data);
    }
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">&lt;?php</span></span>
<span class="line"></span>
<span class="line"><span style="color: #C586C0">declare</span><span style="color: #D4D4D4">(strict_types=</span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">namespace</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">RenorStatistics</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">/**</span></span>
<span class="line"><span style="color: #6A9955"> * Class DescriptiveStats</span></span>
<span class="line"><span style="color: #6A9955"> *</span></span>
<span class="line"><span style="color: #6A9955"> * A lightweight class to perform basic descriptive statistics on numeric datasets.</span></span>
<span class="line"><span style="color: #6A9955"> */</span></span>
<span class="line"><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">DescriptiveStats</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">/**</span></span>
<span class="line"><span style="color: #6A9955">     * </span><span style="color: #569CD6">@var</span><span style="color: #6A9955"> </span><span style="color: #569CD6">float[]</span><span style="color: #6A9955"> Filtered and normalized numeric dataset.</span></span>
<span class="line"><span style="color: #6A9955">     */</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">array</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$data</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">/**</span></span>
<span class="line"><span style="color: #6A9955">     * Main constructor.</span></span>
<span class="line"><span style="color: #6A9955">     *</span></span>
<span class="line"><span style="color: #6A9955">     * </span><span style="color: #569CD6">@param</span><span style="color: #6A9955"> </span><span style="color: #569CD6">array</span><span style="color: #6A9955"> $data An array containing the numeric values to be analyzed.</span></span>
<span class="line"><span style="color: #6A9955">     * </span><span style="color: #569CD6">@throws</span><span style="color: #6A9955"> </span><span style="color: #4EC9B0">InvalidArgumentException</span><span style="color: #6A9955"> If the array is empty or contains no numeric values.</span></span>
<span class="line"><span style="color: #6A9955">     */</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">__construct</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">array</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$data</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #6A9955">// Filter only numeric values (int or float) and reset array keys</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">$filtered</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">array_filter</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$data</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&#39;is_numeric&#39;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">array_values</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$filtered</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #DCDCAA">count</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">) === </span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">throw</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">InvalidArgumentException</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&#39;The dataset must contain at least one numeric value.&#39;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">/**</span></span>
<span class="line"><span style="color: #6A9955">     * Calculates the arithmetic mean of the dataset.</span></span>
<span class="line"><span style="color: #6A9955">     *</span></span>
<span class="line"><span style="color: #6A9955">     * </span><span style="color: #569CD6">@return</span><span style="color: #6A9955"> </span><span style="color: #569CD6">float</span><span style="color: #6A9955"> The arithmetic mean.</span></span>
<span class="line"><span style="color: #6A9955">     */</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">mean</span><span style="color: #D4D4D4">(): </span><span style="color: #569CD6">float</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">array_sum</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">) / </span><span style="color: #DCDCAA">count</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p>Come puoi notare abbiamo dichiarato lo strict_types. Questa opzione impone a PHP di non effettuare il cast automatico del valori. Questo è necessario per avere il pieno controllo del tipo di dato. </p>



<p>Dal momento che accetteremo i dati come un array di valori dichiariamo una proprietà privata della classe <code>private array $data</code>.</p>



<p>Nel costruttore della classe ci occupiamo di recuperare i dati che vengono passati quando andremo ad istanziare la classe per creare un nuovo oggetto, verificando che il contenuto dell&#8217;array <code>$data</code> sia di tipo numerico, ed andiamo a posizionare i valori all&#8217;interno della proprietà <code>$this-&gt;data</code>.</p>



<p>Nel metodo <code>mean()</code> andiamo a implementare la formula matematica della media: sommiamo tutti gli elementi dell&#8217;array passato e dividiamo per il numero di elementi ritornando il risultato. </p>



<h2 class="wp-block-heading">Mediana</h2>



<p>La mediana rappresenta il valore centrale di un insieme ordinato di osservazioni e suddivide il campione in due metà di egual numerosità. Data una serie di <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-b170995d512c659d8668b4e42e1fef6b_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#110;" title="Rendered by QuickLaTeX.com" height="8" width="11" style="vertical-align: 0px;"/> valori <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-8d3c2b80f026f94f40369cb7e40d0c8c_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#120;&#95;&#49;&#44;&#32;&#120;&#95;&#50;&#44;&#32;&#100;&#111;&#116;&#115;&#44;&#32;&#120;&#95;&#110;" title="Rendered by QuickLaTeX.com" height="16" width="110" style="vertical-align: -4px;"/> tali che <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-325f3ecee7883f4dc515f0ec99db3dff_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#120;&#95;&#49;&#32;&#108;&#101;&#32;&#120;&#95;&#50;&#32;&#108;&#101;&#32;&#100;&#111;&#116;&#115;&#32;&#108;&#101;&#32;&#120;&#95;&#110;" title="Rendered by QuickLaTeX.com" height="15" width="128" style="vertical-align: -3px;"/>:</p>



<ul class="wp-block-list">
<li>Se <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-b170995d512c659d8668b4e42e1fef6b_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#110;" title="Rendered by QuickLaTeX.com" height="8" width="11" style="vertical-align: 0px;"/> è dispari, la mediana è semplicemente l’elemento in posizione <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-a936d517d98ad0525c174351196ae84c_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#102;&#114;&#97;&#99;&#123;&#110;&#43;&#49;&#125;&#123;&#50;&#125;" title="Rendered by QuickLaTeX.com" height="16" width="85" style="vertical-align: -4px;"/>: <br><img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-82b39c75a3b5aed8d1c3cfee5495d72e_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#116;&#101;&#120;&#116;&#123;&#77;&#101;&#100;&#105;&#97;&#110;&#97;&#125;&#32;&#61;&#32;&#120;&#95;&#123;&#102;&#114;&#97;&#99;&#123;&#110;&#43;&#49;&#125;&#123;&#50;&#125;&#125;" title="Rendered by QuickLaTeX.com" height="18" width="198" style="vertical-align: -6px;"/></li>



<li>Se <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-b170995d512c659d8668b4e42e1fef6b_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#110;" title="Rendered by QuickLaTeX.com" height="8" width="11" style="vertical-align: 0px;"/> è pari, la mediana è la media aritmetica dei due elementi centrali: <br><img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-d46e509d43f1041d439f271c8de1b94d_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#116;&#101;&#120;&#116;&#123;&#77;&#101;&#100;&#105;&#97;&#110;&#97;&#125;&#32;&#61;&#32;&#102;&#114;&#97;&#99;&#123;&#120;&#95;&#123;&#102;&#114;&#97;&#99;&#123;&#110;&#125;&#123;&#50;&#125;&#125;&#32;&#43;&#32;&#120;&#95;&#123;&#102;&#114;&#97;&#99;&#123;&#110;&#125;&#123;&#50;&#125;&#43;&#49;&#125;&#125;&#123;&#50;&#125;" title="Rendered by QuickLaTeX.com" height="18" width="318" style="vertical-align: -6px;"/></li>
</ul>



<p>A differenza della media aritmetica, la mediana è robusta rispetto agli outlier, poiché dipende solo dall&#8217;ordinamento dei dati e non dalla loro ampiezza. </p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code=" /**
     * Calculates the median (50th percentile) of the dataset.
     *
     * @return float The median value.
     */
    public function median(): float
    {
        // Clone and sort the dataset to avoid mutating the original array
        $sorted = $this-&gt;data;
        sort($sorted, SORT_NUMERIC);

        $count = count($sorted);
        $mid   = intdiv($count, 2);

        // If the count is odd, return the middle value
        if ($count % 2 === 1) {
            return (float) $sorted[$mid];
        }

        // If even, return the average of the two central values
        return ($sorted[$mid - 1] + $sorted[$mid]) / 2.0;
    }" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4"> </span><span style="color: #6A9955">/**</span></span>
<span class="line"><span style="color: #6A9955">     * Calculates the median (50th percentile) of the dataset.</span></span>
<span class="line"><span style="color: #6A9955">     *</span></span>
<span class="line"><span style="color: #6A9955">     * </span><span style="color: #569CD6">@return</span><span style="color: #6A9955"> </span><span style="color: #569CD6">float</span><span style="color: #6A9955"> The median value.</span></span>
<span class="line"><span style="color: #6A9955">     */</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">median</span><span style="color: #D4D4D4">(): </span><span style="color: #569CD6">float</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #6A9955">// Clone and sort the dataset to avoid mutating the original array</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #DCDCAA">sort</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4">, SORT_NUMERIC);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">$count</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">count</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">$mid</span><span style="color: #D4D4D4">   = </span><span style="color: #DCDCAA">intdiv</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$count</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">2</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #6A9955">// If the count is odd, return the middle value</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">$count</span><span style="color: #D4D4D4"> % </span><span style="color: #B5CEA8">2</span><span style="color: #D4D4D4"> === </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> (</span><span style="color: #569CD6">float</span><span style="color: #D4D4D4">) </span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">$mid</span><span style="color: #D4D4D4">];</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #6A9955">// If even, return the average of the two central values</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">$mid</span><span style="color: #D4D4D4"> - </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">] + </span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">$mid</span><span style="color: #D4D4D4">]) / </span><span style="color: #B5CEA8">2.0</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span></code></pre></div>



<p>In questo metodo recupero i dati e li ordino. Otteniamo la dimensione e ricarivamo l&#8217;indice centrale con la divisione intera. Ad esempio se abbiamo 7 elementi, <code>intdiv(7,2)</code> restituisce 3, che corrisponde alla quarta posizione (ricorda che gli array in PHP sono zero-based!).  Se il numero di valori è dispari, esiste un unico elemento perfettamente centrale e lo restituisce. In caso di numerosità pari, invece non c&#8217;è un solo valore centrale, ce ne sono due, quello in posizione <code>$mid - 1</code> e quello in posizione <code>$mid</code>. La mediana per definizione è la media aritmetica di questi due valori. Sommiamo quindi i centrali e dividiamo per 2.0 (con la notazione decimale per forzare la divisione in virgola mobile) ottenendo a sua volta un risultato float. In questo modo il metodo restituisce il cinquantesimo percentile del campione senza modificare il dataset originario e rispettando la definizione statistica sia per serie di lunghezza dispari che pari. </p>



<h2 class="wp-block-heading">Moda</h2>



<p>Dopo media e mediana, il terzo indicatore classicamente più utilizzato della statistica descrittiva è la moda, ossia il valore, o i valori, che ricorrono più spesso all&#8217;interno di un insieme di osservazioni. se denotiamo con <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-a7ee323bc5a3f73ad5e066b13bed5504_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#102;&#40;&#120;&#41;" title="Rendered by QuickLaTeX.com" height="19" width="34" style="vertical-align: -5px;"/> la frequenza assoluta di un valore <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-ede05c264bba0eda080918aaa09c4658_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#120;" title="Rendered by QuickLaTeX.com" height="8" width="10" style="vertical-align: 0px;"/> nel campione, la moda si ottiene come</p>



<p><img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-b16c3ba084a06064cee7b4602248a7d6_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#116;&#101;&#120;&#116;&#123;&#77;&#111;&#100;&#97;&#125;&#32;&#61;&#32;&#111;&#112;&#101;&#114;&#97;&#116;&#111;&#114;&#110;&#97;&#109;&#101;&#42;&#123;&#97;&#114;&#103;&#44;&#109;&#97;&#120;&#125;&#95;&#123;&#120;&#125;&#59;&#32;&#102;&#40;&#120;&#41;" title="Rendered by QuickLaTeX.com" height="19" width="349" style="vertical-align: -5px;"/>.</p>



<p>Possiamo distinguere per la serie di valori in ingresso:</p>



<ul class="wp-block-list">
<li>Serie unimodale quando un solo valore ha la frequenza massima</li>



<li>Serie multimodale quando due o più valori condividono la frequenza massima</li>



<li>Serie senza moda quanto tutti i valori della serie compaiono all&#8217;interno della stessa una sola volta</li>
</ul>



<p>La moda è particolarmente utile quando i dati sono categorici o quando si vuole evidenziare la concentrazione attorno a determinati valori interi. A differenza di media e mediana non misura la posizione centrale ma il valore o i valori che si ripetono maggiormente nella serie. Non a caso quando un capo d&#8217;abbigliamento è di moda è perché questo, rispetto ad altri capi d&#8217;abbigliamento in una serie di vendite, risulta quello più venduto e quindi &#8220;quello che va più di moda&#8221;. </p>



<p>Aggiungiamo quindi il metodo alla nostra classe:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="/**
 * Returns the mode(s) of the dataset.
 *
 * If the dataset is multimodal, an array with all modal values is returned.
 * If every value occurs only once, an empty array is returned (no mode).
 *
 * @return float[] list of modal values
 */
public function mode(): array
{
    // Build a frequency table: value =&gt; occurrences
    $frequencies = array_count_values($this-&gt;data);

    // Determine the highest frequency
    $maxFrequency = max($frequencies);

    // If every value appears only once, there is no mode
    if ($maxFrequency === 1) {
        return [];
    }

    // Collect all values that share the highest frequency
    $modes = [];
    foreach ($frequencies as $value =&gt; $count) {
        if ($count === $maxFrequency) {
            // Cast to float so that return type is consistent
            $modes[] = (float) $value;
        }
    }

    sort($modes, SORT_NUMERIC); // return modes in ascending order
    return $modes;
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">/**</span></span>
<span class="line"><span style="color: #6A9955"> * Returns the mode(s) of the dataset.</span></span>
<span class="line"><span style="color: #6A9955"> *</span></span>
<span class="line"><span style="color: #6A9955"> * If the dataset is multimodal, an array with all modal values is returned.</span></span>
<span class="line"><span style="color: #6A9955"> * If every value occurs only once, an empty array is returned (no mode).</span></span>
<span class="line"><span style="color: #6A9955"> *</span></span>
<span class="line"><span style="color: #6A9955"> * </span><span style="color: #569CD6">@return</span><span style="color: #6A9955"> </span><span style="color: #569CD6">float[]</span><span style="color: #6A9955"> list of modal values</span></span>
<span class="line"><span style="color: #6A9955"> */</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">mode</span><span style="color: #D4D4D4">(): </span><span style="color: #569CD6">array</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">// Build a frequency table: value =&gt; occurrences</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$frequencies</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">array_count_values</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">// Determine the highest frequency</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$maxFrequency</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">max</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$frequencies</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">// If every value appears only once, there is no mode</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">$maxFrequency</span><span style="color: #D4D4D4"> === </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> [];</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">// Collect all values that share the highest frequency</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$modes</span><span style="color: #D4D4D4"> = [];</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">foreach</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">$frequencies</span><span style="color: #D4D4D4"> as </span><span style="color: #9CDCFE">$value</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #9CDCFE">$count</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">$count</span><span style="color: #D4D4D4"> === </span><span style="color: #9CDCFE">$maxFrequency</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #6A9955">// Cast to float so that return type is consistent</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">$modes</span><span style="color: #D4D4D4">[] = (</span><span style="color: #569CD6">float</span><span style="color: #D4D4D4">) </span><span style="color: #9CDCFE">$value</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #DCDCAA">sort</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$modes</span><span style="color: #D4D4D4">, SORT_NUMERIC); </span><span style="color: #6A9955">// return modes in ascending order</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$modes</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p><br><br><code>array_count_values()</code> scorre l’intero dataset e restituisce un array associativo in cui la <strong>chiave</strong> è il valore osservato e il <strong>valore</strong> è quante volte ricorre. È un modo rapido per calcolare <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-a7ee323bc5a3f73ad5e066b13bed5504_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#102;&#40;&#120;&#41;" title="Rendered by QuickLaTeX.com" height="19" width="34" style="vertical-align: -5px;"/> per ogni <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-ede05c264bba0eda080918aaa09c4658_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#120;" title="Rendered by QuickLaTeX.com" height="8" width="10" style="vertical-align: 0px;"/>.<br>Con <code>max($frequencies)</code> otteniamo il numero di occorrenze più alto presente nella tabella; è il valore di <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-c7c3fb4625f82376abb8dacd466e39b6_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#109;&#97;&#120;&#32;&#102;&#40;&#120;&#41;" title="Rendered by QuickLaTeX.com" height="19" width="69" style="vertical-align: -5px;"/> nella definizione matematica.<br>Se la frequenza massima è 1, significa che ogni osservazione è unica. In tal caso il metodo restituisce un array vuoto per indicare che non esiste alcun valore modale.<br>Si itera sulla tabella delle frequenze: per ogni chiave il cui conteggio eguaglia la frequenza massima, si aggiunge quel valore (cast a float) all’array <code>$modes</code>. In questo modo vengono inclusi <strong>tutti</strong> i valori modali in caso di multimodalità.<br>Prima di restituire il risultato, <code>sort($modes, SORT_NUMERIC)</code> assicura che le mode siano ordinate in senso crescente, rendendo più prevedibile l’output.</p>



<p>In questo modo abbiamo coperto tutti i possibili scenari: unimodali, multimodali e senza moda. </p>



<h2 class="wp-block-heading">Media geometrica</h2>



<p>La <strong>media geometrica</strong> è la misura di tendenza centrale più adatta quando i dati rappresentano <em>tassi di crescita</em> o <em>rapporti</em> (per esempio rendimenti percentuali, indici di variazione, scale logaritmiche).</p>



<p>Data una serie di <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-b170995d512c659d8668b4e42e1fef6b_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#110;" title="Rendered by QuickLaTeX.com" height="8" width="11" style="vertical-align: 0px;"/> valori <strong>tutti strettamente positivi</strong> <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-8d3c2b80f026f94f40369cb7e40d0c8c_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#120;&#95;&#49;&#44;&#32;&#120;&#95;&#50;&#44;&#32;&#100;&#111;&#116;&#115;&#44;&#32;&#120;&#95;&#110;" title="Rendered by QuickLaTeX.com" height="16" width="110" style="vertical-align: -4px;"/>, la media geometrica <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-30a79c32f18567063fe44716929e7ced_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#71;" title="Rendered by QuickLaTeX.com" height="12" width="14" style="vertical-align: 0px;"/> si definisce come:</p>



<p><br><img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-75330234700a6ebfb65bb440913bb3fd_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#71;&#32;&#59;&#61;&#59;&#32;&#66;&#105;&#103;&#108;&#40;&#32;&#112;&#114;&#111;&#100;&#95;&#123;&#105;&#61;&#49;&#125;&#94;&#123;&#110;&#125;&#32;&#120;&#95;&#105;&#32;&#66;&#105;&#103;&#114;&#41;&#94;&#123;&#102;&#114;&#97;&#99;&#123;&#49;&#125;&#123;&#110;&#125;&#125;" title="Rendered by QuickLaTeX.com" height="20" width="247" style="vertical-align: -5px;"/></p>



<p>oppure in forma logaritmica più stabile numericamente</p>



<p><img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-c1f114071ec597be378ab22cd45cd54e_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#108;&#110;&#32;&#71;&#32;&#59;&#61;&#59;&#32;&#102;&#114;&#97;&#99;&#123;&#49;&#125;&#123;&#110;&#125;&#32;&#115;&#117;&#109;&#95;&#123;&#105;&#61;&#49;&#125;&#94;&#123;&#110;&#125;&#32;&#108;&#110;&#32;&#120;&#95;&#105;" title="Rendered by QuickLaTeX.com" height="17" width="204" style="vertical-align: -5px;"/>.</p>



<p><br>Questa quantità corrisponde al fattore di crescita “medio” che, applicato n volte in sequenza, produce lo stesso risultato del prodotto reale dei valori.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="/**
 * Calculates the geometric mean of the dataset.
 *
 * @throws DomainException If any value is zero or negative.
 * @return float The geometric mean.
 */
public function geometricMean(): float
{
    // The geometric mean is defined only for strictly positive numbers.
    foreach ($this-&gt;data as $value) {
        if ($value &lt;= 0) {
            throw new DomainException('Geometric mean requires all values to be greater than zero.');
        }
    }

    // Use logarithms for numerical stability: exp( (1/n) * sum(log(x_i)) )
    $logSum = array_sum(array_map('log', $this-&gt;data));
    return exp($logSum / count($this-&gt;data));
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">/**</span></span>
<span class="line"><span style="color: #6A9955"> * Calculates the geometric mean of the dataset.</span></span>
<span class="line"><span style="color: #6A9955"> *</span></span>
<span class="line"><span style="color: #6A9955"> * </span><span style="color: #569CD6">@throws</span><span style="color: #6A9955"> </span><span style="color: #4EC9B0">DomainException</span><span style="color: #6A9955"> If any value is zero or negative.</span></span>
<span class="line"><span style="color: #6A9955"> * </span><span style="color: #569CD6">@return</span><span style="color: #6A9955"> </span><span style="color: #569CD6">float</span><span style="color: #6A9955"> The geometric mean.</span></span>
<span class="line"><span style="color: #6A9955"> */</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">geometricMean</span><span style="color: #D4D4D4">(): </span><span style="color: #569CD6">float</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">// The geometric mean is defined only for strictly positive numbers.</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">foreach</span><span style="color: #D4D4D4"> (</span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4"> as </span><span style="color: #9CDCFE">$value</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">$value</span><span style="color: #D4D4D4"> &lt;= </span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">throw</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">DomainException</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&#39;Geometric mean requires all values to be greater than zero.&#39;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">// Use logarithms for numerical stability: exp( (1/n) * sum(log(x_i)) )</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$logSum</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">array_sum</span><span style="color: #D4D4D4">(</span><span style="color: #DCDCAA">array_map</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&#39;log&#39;</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">exp</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$logSum</span><span style="color: #D4D4D4"> / </span><span style="color: #DCDCAA">count</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p><br><br>Poiché la media geometrica è definita solo per numeri positivi, il metodo scorre il dataset e lancia una DomainException se trova valori ≤ 0. Questo previene risultati matematicamente scorretti (o complessi).<br>Invece di calcolare direttamente il prodotto (che potrebbe facilmente andare in overflow), convertiamo ogni valore con <code>log()</code>, sommiamo i logaritmi e dividiamo per <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-b170995d512c659d8668b4e42e1fef6b_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#110;" title="Rendered by QuickLaTeX.com" height="8" width="11" style="vertical-align: 0px;"/>. In base alle proprietà dei logaritmi: <br><img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-d599ef87ea9b721f46e20b5ca8a11b55_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#108;&#110;&#33;&#66;&#105;&#103;&#108;&#40;&#112;&#114;&#111;&#100;&#32;&#120;&#95;&#105;&#66;&#105;&#103;&#114;&#41;&#32;&#59;&#61;&#59;&#32;&#115;&#117;&#109;&#32;&#108;&#110;&#32;&#120;&#95;&#105;" title="Rendered by QuickLaTeX.com" height="19" width="254" style="vertical-align: -5px;"/><br><br>Applicando <code>exp()</code> al valore medio dei logaritmi otteniamo la media geometrica.<br><img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-8651fa952370316578a3795cbb11731e_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#71;&#32;&#59;&#61;&#59;&#32;&#101;&#120;&#112;&#33;&#66;&#105;&#103;&#108;&#40;&#116;&#102;&#114;&#97;&#99;&#123;&#49;&#125;&#123;&#110;&#125;&#115;&#117;&#109;&#32;&#108;&#110;&#32;&#120;&#95;&#105;&#66;&#105;&#103;&#114;&#41;" title="Rendered by QuickLaTeX.com" height="19" width="290" style="vertical-align: -5px;"/></p>



<h2 class="wp-block-heading">Media armonica</h2>



<p>La <strong>media armonica</strong> è la misura di tendenza centrale più indicata quando i dati rappresentano <strong>velocità, rapporti o frazioni</strong>: per esempio km / h percorsi a diversi ritmi, costo medio per unità o rendimento medio di investimenti calcolato come “quote per euro”.</p>



<p>Data una serie di <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-b170995d512c659d8668b4e42e1fef6b_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#110;" title="Rendered by QuickLaTeX.com" height="8" width="11" style="vertical-align: 0px;"/> valori positivi <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-107a25222e3ebe1d51707bf295b0ee67_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#120;&#95;&#49;&#44;&#120;&#95;&#50;&#44;&#100;&#111;&#116;&#115;&#44;&#120;&#95;&#110;" title="Rendered by QuickLaTeX.com" height="16" width="110" style="vertical-align: -4px;"/> (nessun valore può essere zero, perché apparirebbe al denominatore), la media armonica <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-379db1fc1f84b7ce56b92463183097f9_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#72;" title="Rendered by QuickLaTeX.com" height="12" width="16" style="vertical-align: 0px;"/> si definisce come:<br><br><img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-13a1b07aa4138bff73078afb74468178_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#72;&#32;&#59;&#61;&#59;&#32;&#102;&#114;&#97;&#99;&#123;&#110;&#125;&#123;&#100;&#105;&#115;&#112;&#108;&#97;&#121;&#115;&#116;&#121;&#108;&#101;&#115;&#117;&#109;&#95;&#123;&#105;&#61;&#49;&#125;&#94;&#123;&#110;&#125;&#32;&#102;&#114;&#97;&#99;&#123;&#49;&#125;&#123;&#120;&#95;&#105;&#125;&#125;" title="Rendered by QuickLaTeX.com" height="17" width="304" style="vertical-align: -5px;"/></p>



<p>In altre parole si calcola l’inverso della <strong>media aritmetica degli inversi</strong>. Rispetto alla media aritmetica, la media armonica <strong>pesa di più i valori piccoli</strong>: è quindi preziosa quando si desidera penalizzare fortemente le prestazioni peggiori (es.: il tempo medio per percorrere un chilometro su più tratte di diversa velocità).</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="/**
 * Calculates the harmonic mean of the dataset.
 *
 * @throws DomainException If any value is zero or negative.
 * @return float The harmonic mean.
 */
public function harmonicMean(): float
{
    // Harmonic mean is defined only for strictly positive numbers.
    foreach ($this-&gt;data as $value) {
        if ($value &lt;= 0) {
            throw new DomainException('Harmonic mean requires all values to be greater than zero.');
        }
    }

    $inverseSum = array_sum(array_map(
        static fn (float $v): float =&gt; 1.0 / $v,
        $this-&gt;data
    ));

    return count($this-&gt;data) / $inverseSum;
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">/**</span></span>
<span class="line"><span style="color: #6A9955"> * Calculates the harmonic mean of the dataset.</span></span>
<span class="line"><span style="color: #6A9955"> *</span></span>
<span class="line"><span style="color: #6A9955"> * </span><span style="color: #569CD6">@throws</span><span style="color: #6A9955"> </span><span style="color: #4EC9B0">DomainException</span><span style="color: #6A9955"> If any value is zero or negative.</span></span>
<span class="line"><span style="color: #6A9955"> * </span><span style="color: #569CD6">@return</span><span style="color: #6A9955"> </span><span style="color: #569CD6">float</span><span style="color: #6A9955"> The harmonic mean.</span></span>
<span class="line"><span style="color: #6A9955"> */</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">harmonicMean</span><span style="color: #D4D4D4">(): </span><span style="color: #569CD6">float</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">// Harmonic mean is defined only for strictly positive numbers.</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">foreach</span><span style="color: #D4D4D4"> (</span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4"> as </span><span style="color: #9CDCFE">$value</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">$value</span><span style="color: #D4D4D4"> &lt;= </span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">throw</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">DomainException</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&#39;Harmonic mean requires all values to be greater than zero.&#39;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$inverseSum</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">array_sum</span><span style="color: #D4D4D4">(</span><span style="color: #DCDCAA">array_map</span><span style="color: #D4D4D4">(</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">static</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">fn</span><span style="color: #D4D4D4"> (</span><span style="color: #569CD6">float</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$v</span><span style="color: #D4D4D4">): </span><span style="color: #569CD6">float</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #B5CEA8">1.0</span><span style="color: #D4D4D4"> / </span><span style="color: #9CDCFE">$v</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">data</span></span>
<span class="line"><span style="color: #D4D4D4">    ));</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">count</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">) / </span><span style="color: #9CDCFE">$inverseSum</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p>Per prima cosa si effettua un controllo di dominio, qualunque valore maggiore o uguale a zero genera una DomainException, altrimenti il risultato non sarebbe definitivo o diverrebbe infinito. Poi si effettua una somma degli inversi: <code>array_map()</code> calcola l&#8217;inverso di ogni elemento, <code>array_sum()</code> li somma. <br>La formula diretta divide il numero di osservazioni <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-b170995d512c659d8668b4e42e1fef6b_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#110;" title="Rendered by QuickLaTeX.com" height="8" width="11" style="vertical-align: 0px;"/> per la somma ottenuta, secondo la definizione matematica. </p>



<h2 class="wp-block-heading">Troncamento della media (o media troncata)</h2>



<p>Quando un campione contiene <strong>outlier</strong> estremi che rischiano di distorcere la media aritmetica, una soluzione elegante è la <strong>media troncata</strong> (o <em>trimmed mean</em>).</p>



<p>Si sceglie una percentuale <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-3bf85f1087e9fbed3a319341134ac1a2_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#112;" title="Rendered by QuickLaTeX.com" height="12" width="10" style="vertical-align: -4px;"/> % (tipicamente 5 % oppure 10 %), si ordina il campione e si <strong>scartano</strong> le prime <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-3422b6bb5c160593658b7c39425d9880_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#107;" title="Rendered by QuickLaTeX.com" height="12" width="9" style="vertical-align: 0px;"/> osservazioni più piccole e le ultime <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-3422b6bb5c160593658b7c39425d9880_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#107;" title="Rendered by QuickLaTeX.com" height="12" width="9" style="vertical-align: 0px;"/> più grandi, dove</p>



<p><img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-4b156905adbc86e225dd822194775209_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#98;&#97;&#114;&#123;&#120;&#125;&#123;&#116;&#101;&#120;&#116;&#123;&#116;&#114;&#105;&#109;&#125;&#125;&#32;&#59;&#61;&#59;&#32;&#102;&#114;&#97;&#99;&#123;&#49;&#125;&#123;&#44;&#110;&#45;&#50;&#107;&#44;&#125;&#32;&#115;&#117;&#109;&#123;&#105;&#61;&#107;&#43;&#49;&#125;&#94;&#123;&#44;&#110;&#45;&#107;&#125;&#32;&#120;&#95;&#123;&#40;&#105;&#41;&#125;&#32;" title="Rendered by QuickLaTeX.com" height="24" width="401" style="vertical-align: -8px;"/>.<br><br>In questo modo si ottiene una misura di tendenza centrale <strong>più robusta</strong> della media aritmetica ma meno drastica della mediana.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="/**
 * Calculates the trimmed mean of the dataset.
 *
 * @param float $percent Percentage (0–50) of data to trim at each tail.
 * @throws DomainException If $percent is out of range or removes all data.
 * @return float The trimmed mean.
 */
public function trimmedMean(float $percent): float
{
    if ($percent &lt; 0.0 || $percent &gt;= 50.0) {
        throw new DomainException('Percent must be in the range 0 &lt;= p &lt; 50.');
    }

    $count = count($this-&gt;data);
    if ($count &lt; 3) {
        // Too few values to trim meaningfully; fall back to arithmetic mean
        return $this-&gt;mean();
    }

    // Clone and sort to preserve original order
    $sorted = $this-&gt;data;
    sort($sorted, SORT_NUMERIC);

    // Number of elements to trim from each end
    $k = (int) floor($count * $percent / 100.0);

    // Ensure at least one value remains
    if ($k * 2 &gt;= $count) {
        throw new DomainException('Trim percentage removes all data.');
    }

    $trimmed = array_slice($sorted, $k, $count - 2 * $k);

    return array_sum($trimmed) / count($trimmed);
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">/**</span></span>
<span class="line"><span style="color: #6A9955"> * Calculates the trimmed mean of the dataset.</span></span>
<span class="line"><span style="color: #6A9955"> *</span></span>
<span class="line"><span style="color: #6A9955"> * </span><span style="color: #569CD6">@param</span><span style="color: #6A9955"> </span><span style="color: #569CD6">float</span><span style="color: #6A9955"> $percent Percentage (0–50) of data to trim at each tail.</span></span>
<span class="line"><span style="color: #6A9955"> * </span><span style="color: #569CD6">@throws</span><span style="color: #6A9955"> </span><span style="color: #4EC9B0">DomainException</span><span style="color: #6A9955"> If $percent is out of range or removes all data.</span></span>
<span class="line"><span style="color: #6A9955"> * </span><span style="color: #569CD6">@return</span><span style="color: #6A9955"> </span><span style="color: #569CD6">float</span><span style="color: #6A9955"> The trimmed mean.</span></span>
<span class="line"><span style="color: #6A9955"> */</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">trimmedMean</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">float</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$percent</span><span style="color: #D4D4D4">): </span><span style="color: #569CD6">float</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">$percent</span><span style="color: #D4D4D4"> &lt; </span><span style="color: #B5CEA8">0.0</span><span style="color: #D4D4D4"> || </span><span style="color: #9CDCFE">$percent</span><span style="color: #D4D4D4"> &gt;= </span><span style="color: #B5CEA8">50.0</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">throw</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">DomainException</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&#39;Percent must be in the range 0 &lt;= p &lt; 50.&#39;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$count</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">count</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">$count</span><span style="color: #D4D4D4"> &lt; </span><span style="color: #B5CEA8">3</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #6A9955">// Too few values to trim meaningfully; fall back to arithmetic mean</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #DCDCAA">mean</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">// Clone and sort to preserve original order</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #DCDCAA">sort</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4">, SORT_NUMERIC);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">// Number of elements to trim from each end</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$k</span><span style="color: #D4D4D4"> = (</span><span style="color: #569CD6">int</span><span style="color: #D4D4D4">) </span><span style="color: #DCDCAA">floor</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$count</span><span style="color: #D4D4D4"> * </span><span style="color: #9CDCFE">$percent</span><span style="color: #D4D4D4"> / </span><span style="color: #B5CEA8">100.0</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">// Ensure at least one value remains</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">$k</span><span style="color: #D4D4D4"> * </span><span style="color: #B5CEA8">2</span><span style="color: #D4D4D4"> &gt;= </span><span style="color: #9CDCFE">$count</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">throw</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">DomainException</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&#39;Trim percentage removes all data.&#39;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$trimmed</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">array_slice</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">$k</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">$count</span><span style="color: #D4D4D4"> - </span><span style="color: #B5CEA8">2</span><span style="color: #D4D4D4"> * </span><span style="color: #9CDCFE">$k</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">array_sum</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$trimmed</span><span style="color: #D4D4D4">) / </span><span style="color: #DCDCAA">count</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$trimmed</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p>Il metodo inizia verificando che la percentuale scelta per il taglio sia sensata: deve essere maggiore o uguale a zero e strettamente minore di cinquanta, altrimenti si solleva un’eccezione; se chiedessimo, ad esempio, di eliminare il 60 % di dati a ciascuna coda non rimarrebbe nulla da mediare.</p>



<p>Se il campione contiene meno di tre osservazioni, la funzione considera il taglio privo di significato e restituisce semplicemente la media aritmetica: con due valori, infatti eliminare anche solo un elemento farebbe sparire metà dei dati, mentre con uno solo non esiste nulla da troncare.</p>



<p>Si procede poi a <strong>clonare</strong> l’array originale e a <strong>ordinarlo</strong> in senso crescente; la clonazione preserva l’ordine in cui i dati erano stati forniti all’oggetto, mentre l’ordinamento è indispensabile perché il troncamento viene applicato partendo dagli estremi della distribuzione.</p>



<p>Il numero di elementi da scartare in ciascuna coda, indicato con <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-3422b6bb5c160593658b7c39425d9880_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#107;" title="Rendered by QuickLaTeX.com" height="12" width="9" style="vertical-align: 0px;"/>, si ottiene moltiplicando la dimensione del campione per la percentuale richiesta e arrotondando verso il basso; se ad esempio abbiamo dieci valori e vogliamo una trimmed mean al 10 %, toglieremo un elemento all’inizio e uno alla fine.</p>



<p>Prima di procedere, il metodo controlla che ‟due volte <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-3422b6bb5c160593658b7c39425d9880_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#107;" title="Rendered by QuickLaTeX.com" height="12" width="9" style="vertical-align: 0px;"/>” non sia pari o superiore alla lunghezza dell’array: se così fosse, il taglio eliminerebbe tutti i dati e la media non avrebbe più senso; in tal caso viene lanciata un’ulteriore eccezione.</p>



<p>Superata questa verifica, <code>array_slice()</code> preleva la porzione centrale che rimane dopo aver scartato i <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-3422b6bb5c160593658b7c39425d9880_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#107;" title="Rendered by QuickLaTeX.com" height="12" width="9" style="vertical-align: 0px;"/> valori minori e i <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-3422b6bb5c160593658b7c39425d9880_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#107;" title="Rendered by QuickLaTeX.com" height="12" width="9" style="vertical-align: 0px;"/> maggiori; su tale sottoinsieme si calcola infine la media aritmetica, che è proprio la <strong>media troncata</strong> desiderata.</p>



<p>La funzione restituisce così un indicatore di tendenza centrale più <strong>robusto</strong> della media classica: gli outlier, rimossi prima del calcolo, non possono più trascinare il risultato verso valori estremi.</p>



<h2 class="wp-block-heading">Range</h2>



<p>Il <strong>range</strong> è la misura di dispersione più semplice: indica l’ampiezza totale dei valori osservati, ossia la distanza fra l’estremo minimo e quello massimo del campione. Se chiamiamo</p>



<p><img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-0a3fcaa547e8453d650496a88ffb7ea1_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#120;&#95;&#123;&#109;&#105;&#110;&#125;&#32;&#61;&#32;&#109;&#105;&#110;&#40;&#120;&#95;&#49;&#44;&#100;&#111;&#116;&#115;&#44;&#120;&#95;&#110;&#41;&#32;&#113;&#117;&#97;&#100;&#116;&#101;&#120;&#116;&#123;&#101;&#125;&#113;&#117;&#97;&#100;&#32;&#120;&#95;&#123;&#109;&#97;&#120;&#125;&#32;&#61;&#32;&#109;&#97;&#120;&#40;&#120;&#95;&#49;&#44;&#100;&#111;&#116;&#115;&#44;&#120;&#95;&#110;&#41;" title="Rendered by QuickLaTeX.com" height="19" width="500" style="vertical-align: -5px;"/>,</p>



<p>allora il range <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-dae6bae3dcdac4629730754352c5e329_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#82;" title="Rendered by QuickLaTeX.com" height="12" width="14" style="vertical-align: 0px;"/> si definisce come</p>



<p><img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-5189ce7b9df8dcd3987a4886a7742699_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#82;&#32;&#61;&#32;&#120;&#95;&#123;&#109;&#97;&#120;&#125;&#32;&#59;&#45;&#59;&#32;&#120;&#95;&#123;&#109;&#105;&#110;&#125;" title="Rendered by QuickLaTeX.com" height="16" width="140" style="vertical-align: -4px;"/>.</p>



<p>Pur essendo sensibile agli outlier (lo stesso valore che condiziona il massimo o il minimo condiziona anche il range), questa misura offre un’indicazione immediata della <strong>larghezza</strong> della distribuzione e viene spesso riportata accanto a medie o mediana per dare un colpo d’occhio sulla dispersione complessiva.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="/**
 * Calculates the range (max – min) of the dataset.
 *
 * @return float The range of the data.
 */
public function range(): float
{
    // min() e max() sono O(n) ma il dataset è già in memoria: soluzione lineare
    return max($this-&gt;data) - min($this-&gt;data);
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">/**</span></span>
<span class="line"><span style="color: #6A9955"> * Calculates the range (max – min) of the dataset.</span></span>
<span class="line"><span style="color: #6A9955"> *</span></span>
<span class="line"><span style="color: #6A9955"> * </span><span style="color: #569CD6">@return</span><span style="color: #6A9955"> </span><span style="color: #569CD6">float</span><span style="color: #6A9955"> The range of the data.</span></span>
<span class="line"><span style="color: #6A9955"> */</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">range</span><span style="color: #D4D4D4">(): </span><span style="color: #569CD6">float</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">// min() e max() sono O(n) ma il dataset è già in memoria: soluzione lineare</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">max</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">) - </span><span style="color: #DCDCAA">min</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p>Il metodo come è possibile vedere è molto essenziale. Si invocano le funzioni PHP native <code>max()</code> e <code>min()</code>che eseguono una sola scansione lineare dell&#8217;array per individuare rispettivamente il valore più grande e quello più piccolo. Sottraendo il minimo dal massimo si ottiene l&#8217;ampiezza totale del campione; il risultato è restituito come float, coerente con gli altri metodi della classe. </p>



<h2 class="wp-block-heading">Quartili e intervallo quartile (IQR)</h2>



<p>Per descrivere la dispersione di un campione in modo più robusto rispetto al semplice range, si ricorre ai <strong>quartili</strong>:</p>



<ul class="wp-block-list">
<li><img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-6b6b12b50fe43a209ecce557539ee185_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#81;&#95;&#49;" title="Rendered by QuickLaTeX.com" height="16" width="20" style="vertical-align: -4px;"/> – 25° percentile (primo quartile)</li>



<li><img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-7a8f7f7bc05736504761c873bfd99aa1_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#81;&#95;&#50;" title="Rendered by QuickLaTeX.com" height="16" width="21" style="vertical-align: -4px;"/> – 50° percentile (mediana)</li>



<li><img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-6cef68a5946963445c5d51e5093bd228_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#81;&#95;&#51;" title="Rendered by QuickLaTeX.com" height="16" width="21" style="vertical-align: -4px;"/> – 75° percentile (terzo quartile)</li>
</ul>



<p>L&#8217;intervallo interquartile si definisce come <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-0ab21dea3358259329317d6ca6022f83_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#116;&#101;&#120;&#116;&#123;&#73;&#81;&#82;&#125;&#32;&#61;&#32;&#81;&#95;&#51;&#32;&#45;&#32;&#81;&#95;&#49;" title="Rendered by QuickLaTeX.com" height="16" width="155" style="vertical-align: -4px;"/> e rappresenta l&#8217;ampiezza della metà centrale dei dati. È poco sensibile agli outlier perché si basa solo sui valori compresi fra il 25% e il 75% della distribuzione. </p>



<p>Un uso classico dell&#8217;IQR è il rilevamento degli outlier col metodo di Tukey  (valori minori di <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-6225a71558f70dc2d62e2fd64e6b521d_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#81;&#95;&#49;&#32;&#45;&#32;&#49;&#46;&#53;&#32;&#44;&#32;&#116;&#101;&#120;&#116;&#123;&#73;&#81;&#82;&#125;" title="Rendered by QuickLaTeX.com" height="17" width="142" style="vertical-align: -4px;"/> o maggiori di <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-1a7843a694c241d09006898dc036a324_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#81;&#95;&#51;&#32;&#43;&#32;&#49;&#46;&#53;&#32;&#44;&#32;&#116;&#101;&#120;&#116;&#123;&#73;&#81;&#82;&#125;" title="Rendered by QuickLaTeX.com" height="17" width="142" style="vertical-align: -4px;"/>)</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="/**
 * Returns an array with the first, second (median) and third quartile.
 *
 * Method: &quot;Tukey hinges&quot;.
 *  - Sort the dataset.
 *  - For Q1 and Q3, exclude the median when the sample size is odd.
 *
 * @return float[] [Q1, Q2, Q3] in ascending order.
 */
 public function quartiles(): array
    {
        $sorted = $this-&gt;data;
        sort($sorted, SORT_NUMERIC);

        $n = count($sorted);
        if ($n === 1) {
            return [$sorted[0], $sorted[0], $sorted[0]];
        }
        $mid = intdiv($n, 2);

        // Median (Q2)
        $q2 = ($n % 2 === 0)
            ? ($sorted[$mid - 1] + $sorted[$mid]) / 2.0
            : (float) $sorted[$mid];

        // Lower half (exclude median if n is odd)
        $lower = array_slice($sorted, 0, $mid);
        // Upper half (exclude median if n is odd)
        $upper = array_slice($sorted, ($n % 2 === 0) ? $mid : $mid + 1);

        // Q1 and Q3 are medians of the two halves
        $q1 = $this-&gt;medianOfArray($lower);
        $q3 = $this-&gt;medianOfArray($upper);

        return [$q1, $q2, $q3];
    }

/**
 * Calculates the interquartile range (Q3 – Q1).
 *
 * @return float The interquartile range.
 */
public function iqr(): float
{
    [$q1, , $q3] = $this-&gt;quartiles();
    return $q3 - $q1;
}

/* ---------- Helper ---------- */
/**
 * Median of a pre-sorted array (helper for quartiles).
 *
 * @param float[] $arr Sorted numeric array.
 * @return float Median value.
 */
private function medianOfArray(array $arr): float
{
    $count = count($arr);
    if ($count === 0) {
        throw new LogicException('Cannot compute median of an empty array.');
    }

    $mid = intdiv($count, 2);

    return ($count % 2 === 0)
        ? ($arr[$mid - 1] + $arr[$mid]) / 2.0
        : (float) $arr[$mid];
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">/**</span></span>
<span class="line"><span style="color: #6A9955"> * Returns an array with the first, second (median) and third quartile.</span></span>
<span class="line"><span style="color: #6A9955"> *</span></span>
<span class="line"><span style="color: #6A9955"> * Method: &quot;Tukey hinges&quot;.</span></span>
<span class="line"><span style="color: #6A9955"> *  - Sort the dataset.</span></span>
<span class="line"><span style="color: #6A9955"> *  - For Q1 and Q3, exclude the median when the sample size is odd.</span></span>
<span class="line"><span style="color: #6A9955"> *</span></span>
<span class="line"><span style="color: #6A9955"> * </span><span style="color: #569CD6">@return</span><span style="color: #6A9955"> </span><span style="color: #569CD6">float[]</span><span style="color: #6A9955"> [Q1, Q2, Q3] in ascending order.</span></span>
<span class="line"><span style="color: #6A9955"> */</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">quartiles</span><span style="color: #D4D4D4">(): </span><span style="color: #569CD6">array</span></span>
<span class="line"><span style="color: #D4D4D4">    {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #DCDCAA">sort</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4">, SORT_NUMERIC);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">$n</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">count</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">$n</span><span style="color: #D4D4D4"> === </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> [</span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4">[</span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">], </span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4">[</span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">], </span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4">[</span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">]];</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">$mid</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">intdiv</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$n</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">2</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #6A9955">// Median (Q2)</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">$q2</span><span style="color: #D4D4D4"> = (</span><span style="color: #9CDCFE">$n</span><span style="color: #D4D4D4"> % </span><span style="color: #B5CEA8">2</span><span style="color: #D4D4D4"> === </span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">            ? (</span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">$mid</span><span style="color: #D4D4D4"> - </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">] + </span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">$mid</span><span style="color: #D4D4D4">]) / </span><span style="color: #B5CEA8">2.0</span></span>
<span class="line"><span style="color: #D4D4D4">            : (</span><span style="color: #569CD6">float</span><span style="color: #D4D4D4">) </span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">$mid</span><span style="color: #D4D4D4">];</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #6A9955">// Lower half (exclude median if n is odd)</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">$lower</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">array_slice</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">$mid</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #6A9955">// Upper half (exclude median if n is odd)</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">$upper</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">array_slice</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4">, (</span><span style="color: #9CDCFE">$n</span><span style="color: #D4D4D4"> % </span><span style="color: #B5CEA8">2</span><span style="color: #D4D4D4"> === </span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">) ? </span><span style="color: #9CDCFE">$mid</span><span style="color: #D4D4D4"> : </span><span style="color: #9CDCFE">$mid</span><span style="color: #D4D4D4"> + </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #6A9955">// Q1 and Q3 are medians of the two halves</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">$q1</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #DCDCAA">medianOfArray</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$lower</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">$q3</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #DCDCAA">medianOfArray</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$upper</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> [</span><span style="color: #9CDCFE">$q1</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">$q2</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">$q3</span><span style="color: #D4D4D4">];</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">/**</span></span>
<span class="line"><span style="color: #6A9955"> * Calculates the interquartile range (Q3 – Q1).</span></span>
<span class="line"><span style="color: #6A9955"> *</span></span>
<span class="line"><span style="color: #6A9955"> * </span><span style="color: #569CD6">@return</span><span style="color: #6A9955"> </span><span style="color: #569CD6">float</span><span style="color: #6A9955"> The interquartile range.</span></span>
<span class="line"><span style="color: #6A9955"> */</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">iqr</span><span style="color: #D4D4D4">(): </span><span style="color: #569CD6">float</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    [</span><span style="color: #9CDCFE">$q1</span><span style="color: #D4D4D4">, , </span><span style="color: #9CDCFE">$q3</span><span style="color: #D4D4D4">] = </span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #DCDCAA">quartiles</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$q3</span><span style="color: #D4D4D4"> - </span><span style="color: #9CDCFE">$q1</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">/* ---------- Helper ---------- */</span></span>
<span class="line"><span style="color: #6A9955">/**</span></span>
<span class="line"><span style="color: #6A9955"> * Median of a pre-sorted array (helper for quartiles).</span></span>
<span class="line"><span style="color: #6A9955"> *</span></span>
<span class="line"><span style="color: #6A9955"> * </span><span style="color: #569CD6">@param</span><span style="color: #6A9955"> </span><span style="color: #569CD6">float[]</span><span style="color: #6A9955"> $arr Sorted numeric array.</span></span>
<span class="line"><span style="color: #6A9955"> * </span><span style="color: #569CD6">@return</span><span style="color: #6A9955"> </span><span style="color: #569CD6">float</span><span style="color: #6A9955"> Median value.</span></span>
<span class="line"><span style="color: #6A9955"> */</span></span>
<span class="line"><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">medianOfArray</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">array</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$arr</span><span style="color: #D4D4D4">): </span><span style="color: #569CD6">float</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$count</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">count</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$arr</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">$count</span><span style="color: #D4D4D4"> === </span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">throw</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">LogicException</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&#39;Cannot compute median of an empty array.&#39;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$mid</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">intdiv</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$count</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">2</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">$count</span><span style="color: #D4D4D4"> % </span><span style="color: #B5CEA8">2</span><span style="color: #D4D4D4"> === </span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">        ? (</span><span style="color: #9CDCFE">$arr</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">$mid</span><span style="color: #D4D4D4"> - </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">] + </span><span style="color: #9CDCFE">$arr</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">$mid</span><span style="color: #D4D4D4">]) / </span><span style="color: #B5CEA8">2.0</span></span>
<span class="line"><span style="color: #D4D4D4">        : (</span><span style="color: #569CD6">float</span><span style="color: #D4D4D4">) </span><span style="color: #9CDCFE">$arr</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">$mid</span><span style="color: #D4D4D4">];</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p>Quando il metodo <code>quartiles()</code> viene invocato, la prima cosa che fa è creare una copia dei dati interni e ordinarla in senso crescente; questa copia protegge l’ordine originale fornito dall’utente e consente di lavorare con un vettore monotonicamente ordinato, prerequisito indispensabile per individuare i quartili. Subito dopo, la variabile <code>$n</code> memorizza la lunghezza del campione e <code>$mid</code> rappresenta l’indice centrale calcolato tramite divisione intera. Con queste due informazioni si procede a determinare la mediana dell’intero campione, che diventa il secondo quartile <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-7a8f7f7bc05736504761c873bfd99aa1_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#81;&#95;&#50;" title="Rendered by QuickLaTeX.com" height="16" width="21" style="vertical-align: -4px;"/>; se il numero di osservazioni è pari, la mediana è la media aritmetica dei due valori centrali, mentre nel caso dispari coincide con il valore in posizione centrale.</p>



<p>Una volta noto <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-7a8f7f7bc05736504761c873bfd99aa1_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#81;&#95;&#50;" title="Rendered by QuickLaTeX.com" height="16" width="21" style="vertical-align: -4px;"/>, l’array ordinato viene spezzato in due metà. Se la numerosità è dispari la mediana non va inclusa né nella parte inferiore né in quella superiore, perciò la funzione <code>array_slice</code> la esclude esplicitamente; se è pari la suddivisione avviene esattamente a metà. A questo punto entrano in gioco i quartili estremi: per calcolare <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-6b6b12b50fe43a209ecce557539ee185_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#81;&#95;&#49;" title="Rendered by QuickLaTeX.com" height="16" width="20" style="vertical-align: -4px;"/> e <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-6cef68a5946963445c5d51e5093bd228_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#81;&#95;&#51;" title="Rendered by QuickLaTeX.com" height="16" width="21" style="vertical-align: -4px;"/> non si crea alcun nuovo oggetto, bensì si invoca l’helper privato <code>medianOfArray</code> sulle due metà già ordinate. Questa piccola routine riceve un vettore, ne conta gli elementi, determina l’indice centrale e restituisce la mediana con la stessa logica adottata in precedenza; il tutto resta confinato all’interno della classe, così l’interfaccia pubblica rimane pulita e ogni duplicazione di codice è evitata.</p>



<p>Il metodo <code>quartiles()</code> restituisce infine un array con i tre valori <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-2a57835ccc4419c852fb77d64952d763_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#91;&#81;&#95;&#49;&#44;&#32;&#81;&#95;&#50;&#44;&#32;&#81;&#95;&#51;&#93;" title="Rendered by QuickLaTeX.com" height="18" width="87" style="vertical-align: -5px;"/> in ordine crescente. Il fratello <code>iqr()</code> si limita a scompattare quell’array, sottrae <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-6b6b12b50fe43a209ecce557539ee185_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#81;&#95;&#49;" title="Rendered by QuickLaTeX.com" height="16" width="20" style="vertical-align: -4px;"/> da <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-6cef68a5946963445c5d51e5093bd228_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#81;&#95;&#51;" title="Rendered by QuickLaTeX.com" height="16" width="21" style="vertical-align: -4px;"/> e ritorna l’intervallo interquartile, fornendo in un’unica chiamata la misura di dispersione più robusta della classe.</p>



<p>In questo assetto la logica della mediana resta centralizzata dentro l’helper privato, viene riutilizzata sia per i quartili sia, in modo implicito, per qualsiasi eventuale altra funzionalità interna che avesse bisogno di calcolare una mediana su un sottoinsieme ordinato, mentre l’API pubblica continua ad offrire metodi autodidattici e facili da comprendere per chi integrerà la tua libreria.</p>



<h2 class="wp-block-heading">Varianza</h2>



<p>Per misurare quanto i valori si discostino dalla loro tendenza centrale si introduce la <strong>varianza</strong>, che calcola la media dei quadrati degli scarti dalla media aritmetica. Date <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-b170995d512c659d8668b4e42e1fef6b_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#110;" title="Rendered by QuickLaTeX.com" height="8" width="11" style="vertical-align: 0px;"/> osservazioni <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-6153870aadf3b5d0d60c3341bfc53bc7_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#120;&#95;&#49;&#44;&#32;&#120;&#95;&#50;&#44;&#32;&#8230;&#32;&#44;&#32;&#120;&#95;&#110;" title="Rendered by QuickLaTeX.com" height="12" width="101" style="vertical-align: -4px;"/> con media <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-9487a081c1c399d64321b8ddbb018fc9_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#98;&#97;&#114;&#123;&#120;&#125;" title="Rendered by QuickLaTeX.com" height="12" width="35" style="vertical-align: 0px;"/>, la <strong>varianza della popolazione</strong> si definisce</p>



<p><img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-b6ef84d45707fb4ab7258704fdf0a977_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#115;&#105;&#103;&#109;&#97;&#94;&#123;&#50;&#125;&#32;&#59;&#61;&#59;&#32;&#102;&#114;&#97;&#99;&#123;&#49;&#125;&#123;&#110;&#125;&#115;&#117;&#109;&#95;&#123;&#105;&#61;&#49;&#125;&#94;&#123;&#110;&#125;&#40;&#120;&#95;&#105;&#32;&#45;&#32;&#98;&#97;&#114;&#123;&#120;&#125;&#41;&#94;&#123;&#50;&#125;" title="Rendered by QuickLaTeX.com" height="20" width="292" style="vertical-align: -5px;"/></p>



<p>Se invece i dati rappresentano un <strong>campione</strong> estratto da una popolazione più ampia, lo stimatore corretto (varianza campionaria) divide per <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-f30b71e7fcec69d119f30f67cf09c975_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#110;&#45;&#49;" title="Rendered by QuickLaTeX.com" height="12" width="40" style="vertical-align: 0px;"/>:</p>



<p><img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-ca19eb3a0b2b1fdf4d7db11cb0dab994_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#115;&#94;&#123;&#50;&#125;&#32;&#59;&#61;&#59;&#32;&#102;&#114;&#97;&#99;&#123;&#49;&#125;&#123;&#110;&#45;&#49;&#125;&#115;&#117;&#109;&#95;&#123;&#105;&#61;&#49;&#125;&#94;&#123;&#110;&#125;&#40;&#120;&#95;&#105;&#32;&#45;&#32;&#98;&#97;&#114;&#123;&#120;&#125;&#41;&#94;&#123;&#50;&#125;" title="Rendered by QuickLaTeX.com" height="20" width="282" style="vertical-align: -5px;"/></p>



<p>La varianza restituisce un valore <strong>quadratico</strong>: è sempre non negativa e cresce rapidamente quando gli scarti aumentano, perciò un suo uso frequente è passare alla radice quadrata (deviazione standard) per tornare alle stesse unità di misura dei dati.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="/**
 * Calculates the variance of the dataset.
 *
 * @param bool $sample If true, uses (n-1) in the denominator (sample variance).
 *                     If false, uses n (population variance).
 * @return float The variance value.
 */
public function variance(bool $sample = false): float
{
    $n = count($this-&gt;data);

    // For a single value, population variance is 0, sample variance is undefined
    if ($n &lt; 2 &amp;&amp; $sample) {
        throw new DomainException('Sample variance requires at least two observations.');
    }
    if ($n === 1) {
        return 0.0;
    }

    $mean = $this-&gt;mean();
    $sumSquares = 0.0;

    foreach ($this-&gt;data as $v) {
        $diff        = $v - $mean;
        $sumSquares += $diff * $diff;
    }

    $denominator = $sample ? ($n - 1) : $n;
    return $sumSquares / $denominator;
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">/**</span></span>
<span class="line"><span style="color: #6A9955"> * Calculates the variance of the dataset.</span></span>
<span class="line"><span style="color: #6A9955"> *</span></span>
<span class="line"><span style="color: #6A9955"> * </span><span style="color: #569CD6">@param</span><span style="color: #6A9955"> </span><span style="color: #569CD6">bool</span><span style="color: #6A9955"> $sample If true, uses (n-1) in the denominator (sample variance).</span></span>
<span class="line"><span style="color: #6A9955"> *                     If false, uses n (population variance).</span></span>
<span class="line"><span style="color: #6A9955"> * </span><span style="color: #569CD6">@return</span><span style="color: #6A9955"> </span><span style="color: #569CD6">float</span><span style="color: #6A9955"> The variance value.</span></span>
<span class="line"><span style="color: #6A9955"> */</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">variance</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">bool</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$sample</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">false</span><span style="color: #D4D4D4">): </span><span style="color: #569CD6">float</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$n</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">count</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">// For a single value, population variance is 0, sample variance is undefined</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">$n</span><span style="color: #D4D4D4"> &lt; </span><span style="color: #B5CEA8">2</span><span style="color: #D4D4D4"> &amp;&amp; </span><span style="color: #9CDCFE">$sample</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">throw</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">DomainException</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&#39;Sample variance requires at least two observations.&#39;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">$n</span><span style="color: #D4D4D4"> === </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #B5CEA8">0.0</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$mean</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #DCDCAA">mean</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$sumSquares</span><span style="color: #D4D4D4"> = </span><span style="color: #B5CEA8">0.0</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">foreach</span><span style="color: #D4D4D4"> (</span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4"> as </span><span style="color: #9CDCFE">$v</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">$diff</span><span style="color: #D4D4D4">        = </span><span style="color: #9CDCFE">$v</span><span style="color: #D4D4D4"> - </span><span style="color: #9CDCFE">$mean</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">$sumSquares</span><span style="color: #D4D4D4"> += </span><span style="color: #9CDCFE">$diff</span><span style="color: #D4D4D4"> * </span><span style="color: #9CDCFE">$diff</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$denominator</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">$sample</span><span style="color: #D4D4D4"> ? (</span><span style="color: #9CDCFE">$n</span><span style="color: #D4D4D4"> - </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">) : </span><span style="color: #9CDCFE">$n</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$sumSquares</span><span style="color: #D4D4D4"> / </span><span style="color: #9CDCFE">$denominator</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p><br>Il metodo riceve un flag che indica se calcolare la varianza della popolazione o lo stimatore campionario. Inizia valutando la numerosità del dataset: con un solo valore la varianza di popolazione è per definizione zero, mentre quella campionaria non esiste e viene sollevata un’eccezione. Una volta nota la media, il ciclo attraversa ogni osservazione, sottrae la media, eleva lo scarto al quadrato e lo accumula. Terminata la sommatoria, la divisione avviene per <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-b170995d512c659d8668b4e42e1fef6b_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#110;" title="Rendered by QuickLaTeX.com" height="8" width="11" style="vertical-align: 0px;"/> o <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-f30b71e7fcec69d119f30f67cf09c975_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#110;&#45;&#49;" title="Rendered by QuickLaTeX.com" height="12" width="40" style="vertical-align: 0px;"/> a seconda del contesto specificato, restituendo così la misura di dispersione desiderata.</p>



<h2 class="wp-block-heading">Deviazione standard</h2>



<p><br>La <strong>deviazione standard</strong> non è altro che la radice quadrata della varianza: serve a riportare la misura di dispersione alle stesse unità dei dati originali. Se la varianza indica “quanti quadrati di unità” si distanziano in media le osservazioni dalla loro media, la deviazione standard esprime quello scarto in unità lineari, dunque risulta molto più intuitiva per un lettore non specialista. Per la popolazione la si ottiene come <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-007d91346dbd6d636ef1233fac580c32_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#115;&#105;&#103;&#109;&#97;&#32;&#61;&#32;&#115;&#113;&#114;&#116;&#123;&#115;&#105;&#103;&#109;&#97;&#94;&#123;&#50;&#125;&#125;" title="Rendered by QuickLaTeX.com" height="19" width="159" style="vertical-align: -4px;"/>, mentre nel caso campionario si usa <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-a96d380286d8ed81eb379ea8ec3885fd_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#115;&#32;&#61;&#32;&#115;&#113;&#114;&#116;&#123;&#115;&#94;&#123;&#50;&#125;&#125;" title="Rendered by QuickLaTeX.com" height="19" width="78" style="vertical-align: -4px;"/>. Valori di deviazione standard piccoli raccontano una distribuzione raccolta intorno alla media; valori grandi parlano di dati molto dispersi.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="/**
 * Calculates the standard deviation of the dataset.
 *
 * @param bool $sample If true, returns the sample standard deviation (n-1 in the denominator).
 *                     If false, returns the population standard deviation.
 * @return float The standard deviation.
 */
public function standardDeviation(bool $sample = false): float
{
    return sqrt($this-&gt;variance($sample));
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">/**</span></span>
<span class="line"><span style="color: #6A9955"> * Calculates the standard deviation of the dataset.</span></span>
<span class="line"><span style="color: #6A9955"> *</span></span>
<span class="line"><span style="color: #6A9955"> * </span><span style="color: #569CD6">@param</span><span style="color: #6A9955"> </span><span style="color: #569CD6">bool</span><span style="color: #6A9955"> $sample If true, returns the sample standard deviation (n-1 in the denominator).</span></span>
<span class="line"><span style="color: #6A9955"> *                     If false, returns the population standard deviation.</span></span>
<span class="line"><span style="color: #6A9955"> * </span><span style="color: #569CD6">@return</span><span style="color: #6A9955"> </span><span style="color: #569CD6">float</span><span style="color: #6A9955"> The standard deviation.</span></span>
<span class="line"><span style="color: #6A9955"> */</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">standardDeviation</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">bool</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$sample</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">false</span><span style="color: #D4D4D4">): </span><span style="color: #569CD6">float</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">sqrt</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #DCDCAA">variance</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$sample</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p><br>Il metodo non contiene logica aggiuntiva: richiama semplicemente <code>variance()</code> con lo stesso flag e ne restituisce la radice quadrata, delegando all’algoritmo già collaudato tutta la parte di calcolo e di controlli di dominio.</p>



<h2 class="wp-block-heading">Errore standard della media</h2>



<p>Quando dalla popolazione osserviamo soltanto un campione, la media campionaria è una stima soggetta a fluttuazioni: più il campione è piccolo, più la stima può variare da un campione all’altro. L’<strong>errore standard della media</strong> (in inglese <em>Standard Error of the Mean</em>, SEM) quantifica esattamente questa variabilità prevista. Se s è la deviazione standard campionaria e n è la numerosità del campione, l’errore standard si calcola con</p>



<p><img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-b9b6b8a35670d72d1598feb5aa13d749_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#116;&#101;&#120;&#116;&#123;&#83;&#69;&#77;&#125;&#32;&#59;&#61;&#59;&#32;&#102;&#114;&#97;&#99;&#123;&#115;&#125;&#123;&#115;&#113;&#114;&#116;&#123;&#110;&#125;&#125;" title="Rendered by QuickLaTeX.com" height="16" width="193" style="vertical-align: -4px;"/>.</p>



<p>Un SEM contenuto indica che la media calcolata su quel campione è verosimilmente vicina alla media vera della popolazione; un SEM ampio suggerisce un’incertezza maggiore. Il SEM è inoltre la base per costruire gli intervalli di confidenza della media.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="/**
 * Calculates the standard error of the mean (SEM) of the dataset.
 *
 * SEM = sample standard deviation / sqrt(n)
 * Requires at least two observations.
 *
 * @throws DomainException If the dataset size is less than 2.
 * @return float The standard error of the mean.
 */
public function standardError(): float
{
    $n = count($this-&gt;data);

    if ($n &lt; 2) {
        throw new DomainException('Standard error requires at least two observations.');
    }

    return $this-&gt;standardDeviation(true) / sqrt($n);
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">/**</span></span>
<span class="line"><span style="color: #6A9955"> * Calculates the standard error of the mean (SEM) of the dataset.</span></span>
<span class="line"><span style="color: #6A9955"> *</span></span>
<span class="line"><span style="color: #6A9955"> * SEM = sample standard deviation / sqrt(n)</span></span>
<span class="line"><span style="color: #6A9955"> * Requires at least two observations.</span></span>
<span class="line"><span style="color: #6A9955"> *</span></span>
<span class="line"><span style="color: #6A9955"> * </span><span style="color: #569CD6">@throws</span><span style="color: #6A9955"> </span><span style="color: #4EC9B0">DomainException</span><span style="color: #6A9955"> If the dataset size is less than 2.</span></span>
<span class="line"><span style="color: #6A9955"> * </span><span style="color: #569CD6">@return</span><span style="color: #6A9955"> </span><span style="color: #569CD6">float</span><span style="color: #6A9955"> The standard error of the mean.</span></span>
<span class="line"><span style="color: #6A9955"> */</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">standardError</span><span style="color: #D4D4D4">(): </span><span style="color: #569CD6">float</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$n</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">count</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">$n</span><span style="color: #D4D4D4"> &lt; </span><span style="color: #B5CEA8">2</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">throw</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">DomainException</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&#39;Standard error requires at least two observations.&#39;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #DCDCAA">standardDeviation</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">true</span><span style="color: #D4D4D4">) / </span><span style="color: #DCDCAA">sqrt</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$n</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p><br>Il metodo prende innanzitutto la dimensione del campione; se c’è un solo valore non ha senso parlare di errore standard, perciò viene sollevata un’eccezione di dominio. In tutti gli altri casi richiama la deviazione standard campionaria già implementata, ottenendo <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-ae1901659f469e6be883797bfd30f4f8_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#115;" title="Rendered by QuickLaTeX.com" height="8" width="8" style="vertical-align: 0px;"/>. Dividendo <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-ae1901659f469e6be883797bfd30f4f8_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#115;" title="Rendered by QuickLaTeX.com" height="8" width="8" style="vertical-align: 0px;"/> per la radice quadrata di n ricava il SEM secondo la definizione statistica canonica e restituisce il risultato come valore in virgola mobile. In questo modo tutta la logica numerica rimane coerente con le altre misure di dispersione: la funzione si appoggia a metodi collaudati, non replica calcoli già presenti e garantisce un contratto d’uso molto semplice.</p>



<h2 class="wp-block-heading">Scarto assoluto medio</h2>



<p>Per misurare la dispersione in modo intuitivo, senza amplificare gli scarti con il quadrato come fa la varianza, si utilizza lo <strong>scarto assoluto medio</strong>. L’idea è semplice: si calcola la distanza assoluta fra ogni osservazione e la media aritmetica, poi si fa la media di quelle distanze. Se il campione è composto dai valori <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-3e999a4126bf3f317dd488c4ee61a475_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#120;&#95;&#49;&#44;&#32;&#120;&#95;&#50;&#44;&#32;&#8230;&#44;&#32;&#120;&#95;&#110;" title="Rendered by QuickLaTeX.com" height="12" width="101" style="vertical-align: -4px;"/> e la loro media è <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-9487a081c1c399d64321b8ddbb018fc9_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#98;&#97;&#114;&#123;&#120;&#125;" title="Rendered by QuickLaTeX.com" height="12" width="35" style="vertical-align: 0px;"/>, lo scarto assoluto medio si ottiene con</p>



<p><img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-6079e361cc71091777d00ac9ff7d76d7_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#116;&#101;&#120;&#116;&#123;&#77;&#65;&#68;&#125;&#32;&#59;&#61;&#59;&#32;&#102;&#114;&#97;&#99;&#123;&#49;&#125;&#123;&#110;&#125;&#115;&#117;&#109;&#95;&#123;&#105;&#61;&#49;&#125;&#94;&#123;&#110;&#125;&#32;&#108;&#118;&#101;&#114;&#116;&#32;&#120;&#95;&#105;&#32;&#45;&#32;&#98;&#97;&#114;&#123;&#120;&#125;&#32;&#114;&#118;&#101;&#114;&#116;" title="Rendered by QuickLaTeX.com" height="18" width="372" style="vertical-align: -5px;"/>.</p>



<p>Lo scarto assoluto medio mantiene le stesse unità di misura dei dati, è meno sensibile agli outlier rispetto alla deviazione standard e offre un’interpretazione immediata: indica di quante unità, in media, ogni valore si discosta dal centro della distribuzione.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="/**
 * Calculates the mean absolute deviation (MAD) of the dataset.
 *
 * @return float The mean absolute deviation.
 */
public function meanAbsoluteDeviation(): float
{
    $mean = $this-&gt;mean();
    $sumAbs = 0.0;

    foreach ($this-&gt;data as $v) {
        $sumAbs += abs($v - $mean);
    }

    return $sumAbs / count($this-&gt;data);
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">/**</span></span>
<span class="line"><span style="color: #6A9955"> * Calculates the mean absolute deviation (MAD) of the dataset.</span></span>
<span class="line"><span style="color: #6A9955"> *</span></span>
<span class="line"><span style="color: #6A9955"> * </span><span style="color: #569CD6">@return</span><span style="color: #6A9955"> </span><span style="color: #569CD6">float</span><span style="color: #6A9955"> The mean absolute deviation.</span></span>
<span class="line"><span style="color: #6A9955"> */</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">meanAbsoluteDeviation</span><span style="color: #D4D4D4">(): </span><span style="color: #569CD6">float</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$mean</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #DCDCAA">mean</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$sumAbs</span><span style="color: #D4D4D4"> = </span><span style="color: #B5CEA8">0.0</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">foreach</span><span style="color: #D4D4D4"> (</span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4"> as </span><span style="color: #9CDCFE">$v</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">$sumAbs</span><span style="color: #D4D4D4"> += </span><span style="color: #DCDCAA">abs</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$v</span><span style="color: #D4D4D4"> - </span><span style="color: #9CDCFE">$mean</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$sumAbs</span><span style="color: #D4D4D4"> / </span><span style="color: #DCDCAA">count</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p><br>Il metodo ricava innanzitutto la media aritmetica del campione tramite la funzione già esistente <code>mean()</code>. Con questa informazione scorre ogni osservazione, sottrae la media, prende il valore assoluto dello scarto e lo accumula in <code>$sumAbs</code>. Una volta terminato il ciclo divide la somma degli scarti assoluti per la numerosità del campione, restituendo il risultato in virgola mobile. La logica rimane lineare e senza rami condizionali perché la formula del MAD non richiede distinzioni tra popolazione e campione: vale sempre la divisione per <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-b170995d512c659d8668b4e42e1fef6b_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#110;" title="Rendered by QuickLaTeX.com" height="8" width="11" style="vertical-align: 0px;"/>.</p>



<h2 class="wp-block-heading">Percentili</h2>



<p>Per localizzare un valore qualsiasi lungo la distribuzione si utilizzano i <strong>percentili</strong>: il percentile <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-3bf85f1087e9fbed3a319341134ac1a2_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#112;" title="Rendered by QuickLaTeX.com" height="12" width="10" style="vertical-align: -4px;"/> individua il punto sotto il quale ricade esattamente la percentuale <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-99e832fcda204df594c5836eadfc494f_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#112;&#44;&#37;" title="Rendered by QuickLaTeX.com" height="12" width="14" style="vertical-align: -4px;"/> dei dati ordinati. Il 50° percentile coincide con la mediana, il 25° e il 75° formano i quartili già implementati e, in generale, conoscere più percentili permette di descrivere la forma della distribuzione in maniera molto dettagliata. Poiché il campione è finito, la posizione del percentile raramente coincide con un indice intero: occorre dunque interpolare fra gli elementi adiacenti. Una convenzione ampiamente adottata (Excel e NumPy “linear” method) consiste nel calcolare</p>



<p><img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-81fa092cb3f5413f8914787cc4363b76_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#114;&#32;&#61;&#32;&#102;&#114;&#97;&#99;&#123;&#112;&#125;&#123;&#49;&#48;&#48;&#125;&#44;&#40;&#110;&#45;&#49;&#41;" title="Rendered by QuickLaTeX.com" height="19" width="166" style="vertical-align: -5px;"/></p>



<p>dove <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-b170995d512c659d8668b4e42e1fef6b_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#110;" title="Rendered by QuickLaTeX.com" height="8" width="11" style="vertical-align: 0px;"/> è la numerosità: se r cade fra gli indici <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-3422b6bb5c160593658b7c39425d9880_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#107;" title="Rendered by QuickLaTeX.com" height="12" width="9" style="vertical-align: 0px;"/> e <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-1e22674f5099474b9902f53541dfc8dc_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#107;&#43;&#49;" title="Rendered by QuickLaTeX.com" height="14" width="39" style="vertical-align: -2px;"/>, il percentile è la combinazione lineare dei due valori con peso uguale alla parte frazionaria di <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-c409433a9e2dfcdb83360a974d243f18_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#114;" title="Rendered by QuickLaTeX.com" height="8" width="8" style="vertical-align: 0px;"/>.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="/**
 * Returns the p-th percentile of the dataset (linear interpolation).
 *
 * @param float $p Percentile in the closed range [0, 100].
 * @throws DomainException If $p is outside 0–100.
 * @return float The requested percentile.
 */
public function percentile(float $p): float
{
    if ($p &lt; 0.0 || $p &gt; 100.0) {
        throw new DomainException('Percentile must be between 0 and 100.');
    }

    $sorted = $this-&gt;data;
    sort($sorted, SORT_NUMERIC);
    $n = count($sorted);

    // Edge cases: 0th and 100th percentile
    if ($p === 0.0)   { return (float) $sorted[0]; }
    if ($p === 100.0) { return (float) $sorted[$n - 1]; }

    // Linear-interpolated rank
    $rank        = ($p / 100.0) * ($n - 1);
    $lowerIndex  = (int) floor($rank);
    $upperIndex  = (int) ceil($rank);
    $weightUpper = $rank - $lowerIndex;

    // If rank is an integer, no interpolation is needed
    if ($lowerIndex === $upperIndex) {
        return (float) $sorted[$lowerIndex];
    }

    $lowerValue = $sorted[$lowerIndex];
    $upperValue = $sorted[$upperIndex];

    return (1.0 - $weightUpper) * $lowerValue + $weightUpper * $upperValue;
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">/**</span></span>
<span class="line"><span style="color: #6A9955"> * Returns the p-th percentile of the dataset (linear interpolation).</span></span>
<span class="line"><span style="color: #6A9955"> *</span></span>
<span class="line"><span style="color: #6A9955"> * </span><span style="color: #569CD6">@param</span><span style="color: #6A9955"> </span><span style="color: #569CD6">float</span><span style="color: #6A9955"> $p Percentile in the closed range [0, 100].</span></span>
<span class="line"><span style="color: #6A9955"> * </span><span style="color: #569CD6">@throws</span><span style="color: #6A9955"> </span><span style="color: #4EC9B0">DomainException</span><span style="color: #6A9955"> If $p is outside 0–100.</span></span>
<span class="line"><span style="color: #6A9955"> * </span><span style="color: #569CD6">@return</span><span style="color: #6A9955"> </span><span style="color: #569CD6">float</span><span style="color: #6A9955"> The requested percentile.</span></span>
<span class="line"><span style="color: #6A9955"> */</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">percentile</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">float</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">$p</span><span style="color: #D4D4D4">): </span><span style="color: #569CD6">float</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">$p</span><span style="color: #D4D4D4"> &lt; </span><span style="color: #B5CEA8">0.0</span><span style="color: #D4D4D4"> || </span><span style="color: #9CDCFE">$p</span><span style="color: #D4D4D4"> &gt; </span><span style="color: #B5CEA8">100.0</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">throw</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">DomainException</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&#39;Percentile must be between 0 and 100.&#39;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #DCDCAA">sort</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4">, SORT_NUMERIC);</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$n</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">count</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">// Edge cases: 0th and 100th percentile</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">$p</span><span style="color: #D4D4D4"> === </span><span style="color: #B5CEA8">0.0</span><span style="color: #D4D4D4">)   { </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> (</span><span style="color: #569CD6">float</span><span style="color: #D4D4D4">) </span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4">[</span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">]; }</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">$p</span><span style="color: #D4D4D4"> === </span><span style="color: #B5CEA8">100.0</span><span style="color: #D4D4D4">) { </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> (</span><span style="color: #569CD6">float</span><span style="color: #D4D4D4">) </span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">$n</span><span style="color: #D4D4D4"> - </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">]; }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">// Linear-interpolated rank</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$rank</span><span style="color: #D4D4D4">        = (</span><span style="color: #9CDCFE">$p</span><span style="color: #D4D4D4"> / </span><span style="color: #B5CEA8">100.0</span><span style="color: #D4D4D4">) * (</span><span style="color: #9CDCFE">$n</span><span style="color: #D4D4D4"> - </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$lowerIndex</span><span style="color: #D4D4D4">  = (</span><span style="color: #569CD6">int</span><span style="color: #D4D4D4">) </span><span style="color: #DCDCAA">floor</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$rank</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$upperIndex</span><span style="color: #D4D4D4">  = (</span><span style="color: #569CD6">int</span><span style="color: #D4D4D4">) </span><span style="color: #DCDCAA">ceil</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">$rank</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$weightUpper</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">$rank</span><span style="color: #D4D4D4"> - </span><span style="color: #9CDCFE">$lowerIndex</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">// If rank is an integer, no interpolation is needed</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">$lowerIndex</span><span style="color: #D4D4D4"> === </span><span style="color: #9CDCFE">$upperIndex</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> (</span><span style="color: #569CD6">float</span><span style="color: #D4D4D4">) </span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">$lowerIndex</span><span style="color: #D4D4D4">];</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$lowerValue</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">$lowerIndex</span><span style="color: #D4D4D4">];</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">$upperValue</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">$sorted</span><span style="color: #D4D4D4">[</span><span style="color: #9CDCFE">$upperIndex</span><span style="color: #D4D4D4">];</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> (</span><span style="color: #B5CEA8">1.0</span><span style="color: #D4D4D4"> - </span><span style="color: #9CDCFE">$weightUpper</span><span style="color: #D4D4D4">) * </span><span style="color: #9CDCFE">$lowerValue</span><span style="color: #D4D4D4"> + </span><span style="color: #9CDCFE">$weightUpper</span><span style="color: #D4D4D4"> * </span><span style="color: #9CDCFE">$upperValue</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p><br>Il metodo convalida innanzitutto che il percentile richiesto sia compreso fra 0 e 100 inclusi, garantendo così coerenza con la definizione statistica. Subito dopo crea una copia ordinata dei dati, perché qualsiasi localizzazione percentuale richiede il vettore monotònicamente crescente. Gli estremi 0 e 100 vengono gestiti esplicitamente restituendo rispettivamente il minimo e il massimo del campione. Per tutti gli altri valori si calcola il rango reale <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-c409433a9e2dfcdb83360a974d243f18_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#114;" title="Rendered by QuickLaTeX.com" height="8" width="8" style="vertical-align: 0px;"/>, che può cadere fra due indici interi; l’indice inferiore e quello superiore delimitano l’intervallo che contiene la posizione frazionaria. Se <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-c409433a9e2dfcdb83360a974d243f18_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#114;" title="Rendered by QuickLaTeX.com" height="8" width="8" style="vertical-align: 0px;"/> è già intero non serve interpolazione e il metodo restituisce direttamente l’elemento corrispondente. In caso contrario si combinano linearmente il valore inferiore e quello superiore con un peso uguale alla parte decimale di <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-c409433a9e2dfcdb83360a974d243f18_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#114;" title="Rendered by QuickLaTeX.com" height="8" width="8" style="vertical-align: 0px;"/>, producendo un risultato continuo che scorre uniformemente fra gli elementi del campione.</p>



<h2 class="wp-block-heading">Minimo e Massimo</h2>



<p><br>Per completare il quadro delle misure descrittive è utile poter recuperare rapidamente l’estremo inferiore e quello superiore della distribuzione. Il <strong>valore minimo</strong> indica l’osservazione più piccola registrata nel campione; il <strong>valore massimo</strong> segnala la più grande. Queste due grandezze, pur estremamente semplici, sono indispensabili sia per dare il contesto ai dati (sapere dove inizia e dove finisce l’intervallo effettivamente osservato) sia come ingrediente di altre statistiche, ad esempio il range che hai già implementato. Poiché il dataset è interamente in memoria, individuare minimo e massimo richiede una sola scansione lineare e il costo computazionale è trascurabile.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="/**
 * Returns the minimum value of the dataset.
 *
 * @return float The smallest observation.
 */
public function minValue(): float
{
    return (float) min($this-&gt;data);
}

/**
 * Returns the maximum value of the dataset.
 *
 * @return float The largest observation.
 */
public function maxValue(): float
{
    return (float) max($this-&gt;data);
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">/**</span></span>
<span class="line"><span style="color: #6A9955"> * Returns the minimum value of the dataset.</span></span>
<span class="line"><span style="color: #6A9955"> *</span></span>
<span class="line"><span style="color: #6A9955"> * </span><span style="color: #569CD6">@return</span><span style="color: #6A9955"> </span><span style="color: #569CD6">float</span><span style="color: #6A9955"> The smallest observation.</span></span>
<span class="line"><span style="color: #6A9955"> */</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">minValue</span><span style="color: #D4D4D4">(): </span><span style="color: #569CD6">float</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> (</span><span style="color: #569CD6">float</span><span style="color: #D4D4D4">) </span><span style="color: #DCDCAA">min</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">/**</span></span>
<span class="line"><span style="color: #6A9955"> * Returns the maximum value of the dataset.</span></span>
<span class="line"><span style="color: #6A9955"> *</span></span>
<span class="line"><span style="color: #6A9955"> * </span><span style="color: #569CD6">@return</span><span style="color: #6A9955"> </span><span style="color: #569CD6">float</span><span style="color: #6A9955"> The largest observation.</span></span>
<span class="line"><span style="color: #6A9955"> */</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">maxValue</span><span style="color: #D4D4D4">(): </span><span style="color: #569CD6">float</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> (</span><span style="color: #569CD6">float</span><span style="color: #D4D4D4">) </span><span style="color: #DCDCAA">max</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">$this</span><span style="color: #D4D4D4">-&gt;</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p><br>Le funzioni PHP min() e max() attraversano l’array una sola volta; il cast garantisce che il tipo restituito sia float, uniforme con il resto dell’API.</p>



<h2 class="wp-block-heading">Conclusioni</h2>



<p>Con questo ultimo metodo abbiamo completato la nostra Classe <code>DescriptiveStats</code> che ora racchiude in un unico componente i più importanti strumenti di statistica descrittiva: dalle misure di posizione (media aritmetica, mediana, moda, percentili) alle misure di dispersione (range, varianza, deviazione standard, IQR, MAD), passando per indicatori di robustezza (media troncata) e incertezza (errore standard). Ogni funzione è autonoma, tipizzata e corredata di test automatici con PHPUnit che non abbiamo riportato per non allungare ulteriormente questo articolo già abbastanza pregno di nozioni. </p>



<p>Grazie a questa libreria, un progetto PHP può analizzare rapidamente piccoli dataset senza dipendenze esterne e senza dover utilizzare l&#8217;intelligenza artificiale solo per &#8220;moda&#8221;, integrare calcoli statistici in report, dashboard o API, estendere la classe con ulteriori indicatori sfruttando un&#8217;architettura chiara e coerente. </p>



<p>Nel prossimo articolo vedremo come trasformare il codice in un pacchetto Composer: creeremo la struttura definitiva della repository, vedremo come modificare il file <code>composer.json</code>, configureremo la CI per eseguire i test e pubblicheremo la libreria su Packagist, rendendola installabile con un semplice:</p>



<p><code>composer require thesimon82/descriptive-statistics</code></p>



<p>In questo modo potrai distribuire la tua soluzione open-source, ricevere contributi dalla community e riutilizzarla in qualsiasi progetto con la massima semplicità.</p>



<p>[starbox]</p>
<p>L'articolo <a href="https://renor.it/blog/sviluppo-software-programmazione/una-classe-di-statistica-descrittiva-in-php/">Una Classe di Statistica descrittiva in PHP</a> proviene da <a href="https://renor.it">RENOR &amp; Partners S.r.l.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Watermark Invisibile in JPEG con PHP 8</title>
		<link>https://renor.it/blog/sviluppo-software-programmazione/watermark-invisibile-in-jpeg-con-php-8/</link>
		
		<dc:creator><![CDATA[Simone Renzi]]></dc:creator>
		<pubDate>Sun, 11 May 2025 04:27:30 +0000</pubDate>
				<category><![CDATA[Sviluppo Software & Programmazione]]></category>
		<category><![CDATA[blog fotografico]]></category>
		<category><![CDATA[compressione jpeg]]></category>
		<category><![CDATA[copyright]]></category>
		<category><![CDATA[dct]]></category>
		<category><![CDATA[firma digitale]]></category>
		<category><![CDATA[fotografia digitale]]></category>
		<category><![CDATA[gd]]></category>
		<category><![CDATA[imagick]]></category>
		<category><![CDATA[jpeg]]></category>
		<category><![CDATA[metadata invisibili]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[protezione immagini]]></category>
		<category><![CDATA[script php]]></category>
		<category><![CDATA[sicurezza immagini]]></category>
		<category><![CDATA[steganografia]]></category>
		<category><![CDATA[trasformata coseno discreta]]></category>
		<category><![CDATA[watermark invisibile]]></category>
		<category><![CDATA[watermarking robusto]]></category>
		<guid isPermaLink="false">https://renor.it/?p=334</guid>

					<description><![CDATA[<p>Proteggi le tue immagini modificando i coefficienti, senza rovinarle Pubblicare fotografie online è diventato indispensabile per fotografi, e-commerce e blogger, ma secondo gli studi più recenti sul furto d&#8217;immagini, oltre il 70% dei contenuti visivi viene ricondiviso senza credito o autorizzazione. Una filigrana visibile protegge certamente l&#8217;autore, ma per renderla effettivamente valida ai fini del [&#8230;]</p>
<p>L'articolo <a href="https://renor.it/blog/sviluppo-software-programmazione/watermark-invisibile-in-jpeg-con-php-8/">Watermark Invisibile in JPEG con PHP 8</a> proviene da <a href="https://renor.it">RENOR &amp; Partners S.r.l.</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2 class="wp-block-heading">Proteggi le tue immagini modificando i coefficienti, senza rovinarle</h2>
<p>Pubblicare fotografie online è diventato indispensabile per fotografi, e-commerce e blogger, ma secondo gli studi più recenti sul furto d&#8217;immagini, oltre il 70% dei contenuti visivi viene ricondiviso senza credito o autorizzazione. Una filigrana visibile protegge certamente l&#8217;autore, ma per renderla effettivamente valida ai fini del furto della proprietà intellettuale, spesso rovina l&#8217;estetica dello scatto con una &#8220;pecetta&#8221; tutt&#8217;altro che piacevole!</p>
<p>Al contrario, un watermark “invisibile” inscritto nei coefficienti della Trasformata Coseno Discreta (DCT), su cui si basa il formato JPEG, preserva la qualità percepita e può resistere a ridimensionamenti o compressioni leggere.</p>
<p>Codificando l’informazione nel <strong>segno</strong> di specifici coefficienti a bassa frequenza, è possibile garantire un compromesso efficace tra invisibilità e robustezza.</p>
<p>In questo progetto open-source, disponibile nella mia repository GitHub: <a href="https://github.com/thesimon82/php-dct-invisible-watermark">https://github.com/thesimon82/php-dct-invisible-watermark</a>, scritto in PHP 8.3 e basato su GD/Imagick per la manipolazione dei pixel, mostreremo passo passo come incorporare una firma testuale nei coefficienti a bassa frequenza (AC) di ciascun blocco 8×8, come recuperarne l’impronta e perché il <strong>segno del coefficiente</strong> rappresenta una strategia robusta contro le comuni compressioni JPEG, anche a qualità 80% o inferiore.</p>
<p>L’obiettivo non è solo didattico: il micro-servizio risultante può essere clonato dal repository e integrato, via CLI o API, in qualunque workflow di pubblicazione immagini.</p>
<h2 class="wp-block-heading">I fondamenti tecnici</h2>
<p>Prima di scrivere una sola riga di codice conviene chiarire come l&#8217;algoritmo di compressione JPEG agisce sulle immagini che deve comprimere.</p>
<p>Il formato JPEG divide l’immagine in una griglia di blocchi 8×8 pixel; ciascun blocco viene trasformato tramite la Discrete Cosine Transform (DCT), che scompone l’informazione visiva in 64 coefficienti ordinati dalla frequenza più bassa (l’angolo in alto a sinistra, responsabile dell’intensità media) a quella più alta (i dettagli più fini).</p>
<p>I coefficienti a media-bassa frequenza sono il punto ideale per il nostro watermark: toccare il coefficiente a frequenza zero (DC) produrrebbe aloni visibili, mentre alterare solo le frequenze più alte porterebbe a una loro eliminazione già alla prima compressione JPEG.</p>
<p>Intervenendo invece su coefficienti intermedi possiamo codificare singoli bit <strong>modificando il segno del coefficiente</strong> (positivo = 0, negativo = 1), ottenendo così un marcatore invisibile all’occhio ma sufficientemente robusto da sopravvivere a ridimensionamenti e compressioni JPEG fino a qualità 80.</p>
<p>Questo principio matematico guiderà l’intera implementazione.</p>
<p>Leggeremo ogni blocco 8×8, calcoleremo la DCT tramite una funzione nativa in PHP, modificheremo il <strong>segno</strong> di uno o più coefficienti scelti e salveremo il nuovo JPEG tramite GD o Imagick.</p>
<p>In altre parole, il watermark sarà nascosto proprio nei coefficienti che l’algoritmo di compressione JPEG tende a preservare: sarà invisibile all’occhio ma difficile da rimuovere, anche in presenza di ricompressioni leggere.</p>
<h2 class="wp-block-heading">La matematica che rende invisibile il watermark</h2>
<p>Ogni blocco <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-c3b157a64793d2e5169dbf423fde6cfa_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#56;&#32;&#116;&#105;&#109;&#101;&#115;&#32;&#56;" title="Rendered by QuickLaTeX.com" height="12" width="62" style="vertical-align: 0px;"/> di intensità <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-533196a0d71c414a47d0753600045b35_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#102;&#95;&#123;&#120;&#44;&#121;&#125;" title="Rendered by QuickLaTeX.com" height="18" width="28" style="vertical-align: -6px;"/> (dove <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-0afbe70e98cf10d4d189b1a8199bc397_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#120;&#44;&#121;&#32;&#105;&#110;&#32;&#123;&#48;&#44;&#100;&#111;&#116;&#115;&#44;&#55;&#125;" title="Rendered by QuickLaTeX.com" height="17" width="110" style="vertical-align: -4px;"/>) viene convertito in 64 coefficienti <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-c82f152c8e6619cbeddd318d16e9aa3c_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#67;&#95;&#123;&#117;&#44;&#118;&#125;" title="Rendered by QuickLaTeX.com" height="18" width="32" style="vertical-align: -6px;"/> (con <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-40ad4f66f715fa83c6fee834af870d2f_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#117;&#44;&#118;&#32;&#105;&#110;&#32;&#123;&#48;&#44;&#100;&#111;&#116;&#115;&#44;&#55;&#125;" title="Rendered by QuickLaTeX.com" height="17" width="110" style="vertical-align: -4px;"/>) tramite:</p>
<p class="p1">[</p>
<p class="p1">C_{u,v}= frac{1}{4},alpha(u),alpha(v)!</p>
<p class="p1">sum_{x=0}^{7},sum_{y=0}^{7}</p>
<p class="p1">f_{x,y};</p>
<p class="p1">cos!Bigl[frac{(2x+1)upi}{16}Bigr],</p>
<p class="p1">cos!Bigl[frac{(2y+1)vpi}{16}Bigr]</p>
<p class="p1">tag{1}</p>
<p class="p1">]</p>
<p> </p>
<p>dove</p>
<p class="p1">[</p>
<p class="p1">alpha(k)=</p>
<p class="p1">begin{cases}</p>
<p class="p1">dfrac1{sqrt{2}}, &amp; k=0,\[6pt]</p>
<p class="p1">1, &amp; kge 1.</p>
<p class="p1">end{cases}</p>
<p class="p1">]</p>
<p><img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-27c529d7ad2707ccbbbfcd907e7199e4_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#32;&#67;&#95;&#123;&#48;&#44;&#48;&#125;&#32;" title="Rendered by QuickLaTeX.com" height="18" width="31" style="vertical-align: -6px;"/> (coefficiente DC) è la media luminosa del blocco. I coefficienti<b>AC</b><img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-e4e278f97101e873184bfd4ff8331834_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#40;&#117;&#44;&#118;&#110;&#101;&#113;&#48;&#41;" title="Rendered by QuickLaTeX.com" height="19" width="76" style="vertical-align: -5px;"/> descrivono contenuti sempre più fini man mano che <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-43fe27dc3e528266a619764d90fce60b_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#117;" title="Rendered by QuickLaTeX.com" height="8" width="10" style="vertical-align: 0px;"/> o <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-ef71511c70f0e4b25cc6bd69f3bc20c2_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#118;" title="Rendered by QuickLaTeX.com" height="8" width="9" style="vertical-align: 0px;"/> crescono.</p>
<h3 class="wp-block-heading">Esempio numerico semplificato</h3>
<p>Supponiamo un blocco che abbia tutto un valore di 128, un grigio medio. Allora:</p>
<p><img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-790b5e1f935a1afb7015162118b075c5_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#67;&#95;&#123;&#48;&#44;&#48;&#125;&#61;&#56;&#99;&#100;&#111;&#116;&#56;&#99;&#100;&#111;&#116;&#49;&#50;&#56;&#99;&#100;&#111;&#116;&#102;&#114;&#97;&#99;&#123;&#49;&#125;&#123;&#52;&#125;&#44;&#97;&#108;&#112;&#104;&#97;&#40;&#48;&#41;&#94;&#50;&#32;&#61;&#54;&#52;&#99;&#100;&#111;&#116;&#49;&#50;&#56;&#99;&#100;&#111;&#116;&#102;&#114;&#97;&#99;&#123;&#49;&#125;&#123;&#52;&#125;&#99;&#100;&#111;&#116;&#102;&#114;&#97;&#99;&#49;&#50;&#32;&#61;&#49;&#48;&#50;&#52;" title="Rendered by QuickLaTeX.com" height="41" width="582" style="vertical-align: -4px;"/>, mentre <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-173279858d8b2982ad84681cdf99b8d8_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#67;&#95;&#123;&#117;&#44;&#118;&#125;&#61;&#48;" title="Rendered by QuickLaTeX.com" height="18" width="65" style="vertical-align: -6px;"/> per <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-67357d3389d1b5560617b69ae37c2ce6_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#40;&#117;&#44;&#118;&#41;&#110;&#101;&#113;&#40;&#48;&#44;&#48;&#41;" title="Rendered by QuickLaTeX.com" height="19" width="106" style="vertical-align: -5px;"/> perché non ci sono variazioni di frequenza.</p>
<p>L&#8217;immagine compressa conserva i coefficienti più bassi, quelli più alti sono quantizzati in modo aggressivo, spesso fino a 0.</p>
<h3 class="wp-block-heading">Quantizzazione e scelta delle frequenze sicure</h3>
<p>Prima di essere salvati in JPEG, i coefficienti DCT vengono divisi per una matrice di quantizzazione <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-cd69090f5c2d8ab2c05b77ea4f2526b5_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#81;&#95;&#123;&#117;&#44;&#118;&#125;" title="Rendered by QuickLaTeX.com" height="18" width="33" style="vertical-align: -6px;"/>, specifica per ogni frequenza:</p>
<p class="p1">[</p>
<p class="p1">tilde{C}{u,v}= operatorname{round}!Bigl(frac{C{u,v}}{Q_{u,v}}right)Big).</p>
<p class="p1">tag{2}</p>
<p class="p1">]</p>
<p>Più alto è il valore di <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-cd69090f5c2d8ab2c05b77ea4f2526b5_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#81;&#95;&#123;&#117;&#44;&#118;&#125;" title="Rendered by QuickLaTeX.com" height="18" width="33" style="vertical-align: -6px;"/>, maggiore sarà la perdita di precisione, con la possibilità che i coefficienti vengano ridotti a zero.<br />Per questo motivo, nel nostro algoritmo operiamo su un sottoinsieme a bassa frequenza dei coefficienti AC, in posizioni dove la quantizzazione è moderata e i valori sopravvivono meglio alla compressione.<br />Evitiamo inoltre i coefficienti troppo bassi (DC incluso), che potrebbero introdurre artefatti visibili se modificati.</p>
<h3 class="wp-block-heading">Codifica di un bit nel coefficiente</h3>
<p>Per garantire la sopravvivenza del bit dopo la compressione JPEG, invece di usare la parità, imponiamo il segno del coefficiente in base al bit da scrivere:</p>
<p class="p1">[</p>
<p>operatorname{embed}(c, b) =</p>
<p>begin{cases}</p>
<p>+text{minAmp} + text{boost}, &amp; b = 0 \\</p>
<p>-text{minAmp} &#8211; text{boost}, &amp; b = 1</p>
<p>end{cases}</p>
<p>tag{3’}</p>
<p class="p1">]</p>
<p class="p1">Dove:<br />• <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-a07f336241093f9cf6c293f2baa1c6ff_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#116;&#101;&#120;&#116;&#123;&#109;&#105;&#110;&#65;&#109;&#112;&#125;" title="Rendered by QuickLaTeX.com" height="17" width="101" style="vertical-align: -4px;"/> garantisce un valore sufficientemente lontano da zero (tipicamente ≥ 80),<br />• <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-a4c0ceae0b7876de8f30205bc021cdd0_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#116;&#101;&#120;&#116;&#123;&#98;&#111;&#111;&#115;&#116;&#125;" title="Rendered by QuickLaTeX.com" height="12" width="70" style="vertical-align: 0px;"/> (es. 64) aumenta la distanza dal centro per evitare l’annullamento in fase di quantizzazione.</p>
<p class="p2">In questo modo, anche dopo trasformazioni lossy, il segno resta inalterato e il bit può essere recuperato in lettura.</p>
<p>Se successivamente desideriamo leggere il bit, ci basterà valutare il <strong>segno</strong> del coefficiente DCT quantizzato:</p>
<p class="p1">[</p>
<p>b =</p>
<p>begin{cases}</p>
<p>0, &amp; tilde{C}{u,v} ge 0 \\</p>
<p>1, &amp; tilde{C}{u,v} &lt; 0</p>
<p>end{cases}</p>
<p>tag{4’}</p>
<p class="p1">]</p>
<p>In altre parole, un coefficiente positivo indica bit 0, uno negativo indica bit 1.</p>
<p>Questo metodo è più robusto, perché il segno di un coefficiente sopravvive a molte trasformazioni lossy, a differenza del singolo bit meno significativo.</p>
<h3 class="wp-block-heading">Ricostruzione (Inverse DCT)</h3>
<p>Dopo aver modificato i coefficienti, invertiamo la trasformazione per ottenere il blocco d&#8217;immagine marcato: </p>
<p class="p1">[</p>
<p class="p1">f_{x,y}= frac{1}{4}!</p>
<p class="p1">sum_{u=0}^{7}sum_{v=0}^{7}</p>
<p class="p1">alpha(u),alpha(v),</p>
<p class="p1">tilde{C}_{u,v},</p>
<p class="p1">cos!Bigl[frac{(2x+1)upi}{16}Bigr],</p>
<p class="p1">cos!Bigl[frac{(2y+1)vpi}{16}Bigr].</p>
<p class="p1">tag{5}</p>
<p class="p1">]</p>
<p>L’errore massimo introdotto da una variazione di ±1 su un coefficiente di media frequenza è inferiore a 1 quantum in luminanza, sotto la soglia di percezione umano-visiva per fotografie reali.</p>
<h3 class="wp-block-heading">Ridondanza ciclica per indurre robustezza</h3>
<p>Per fronteggiare compressioni o ritagli, codifichiamo il messaggio M duplicando ciascun bit k volte e applicando un voto di maggioranza in lettura. Con k=3:</p>
<p class="p1">[</p>
<p class="p1">M = b_1b_2dots b_n</p>
<p class="p1">;longrightarrow;</p>
<p class="p1">b_1b_1b_1,b_2b_2b_2dots b_nb_nb_n</p>
<p class="p1">tag{6}</p>
<p class="p1">]</p>
<p>Il recupero corretto è garantito finché <strong>almeno due copie su tre sopravvivono</strong>.</p>
<h2 class="wp-block-heading">Dalle formule al codice: la classe DCT</h2>
<p>Ora che abbiamo formalizzato l’intero impianto matematico, possiamo tradurre l’equazione (1) e la sua inversa direttamente in PHP.</p>
<p>Per mantenere il progetto in puro PHP, senza estensioni native né dipendenze esterne, implementiamo la DCT manualmente su blocchi 8×8.</p>
<p>Questo approccio è sufficientemente veloce per immagini fino a qualche megapixel e, soprattutto, è trasparente e facile da comprendere e modificare.</p>
<p>Il progetto è organizzato con Composer e autoload PSR-4, ma la classe Dct è <strong>completamente autonoma</strong> e <strong>non richiede librerie esterne</strong>.</p>
<p>Se in futuro vorrai estendere il sistema con analisi matriciali (es. box counting o misure di distorsione), potrai integrare MathPHP, ma non è necessaria per la DCT.</p>
<h3 class="wp-block-heading">composer.json</h3>


<div class="wp-block-kevinbatdorf-code-block-pro" style="font-size: .875rem; font-family: Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace; line-height: 1.25rem; --cbp-tab-width: 2; tab-size: var(--cbp-tab-width, 2);" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono">
<pre class="shiki nord" style="background-color: #2e3440ff;" tabindex="0"><code><span class="line"><span style="color: #eceff4;">{</span></span>
<span class="line">    <span style="color: #eceff4;">"</span><span style="color: #8fbcbb;">name</span><span style="color: #eceff4;">"</span><span style="color: #eceff4;">:</span> <span style="color: #eceff4;">"</span><span style="color: #a3be8c;">renornad/php-dct-invisible-watermark</span><span style="color: #eceff4;">"</span><span style="color: #eceff4;">,</span></span>
<span class="line">    <span style="color: #eceff4;">"</span><span style="color: #8fbcbb;">description</span><span style="color: #eceff4;">"</span><span style="color: #eceff4;">:</span> <span style="color: #eceff4;">"</span><span style="color: #a3be8c;">Pure-PHP DCT watermarking library for JPEG images.</span><span style="color: #eceff4;">"</span><span style="color: #eceff4;">,</span></span>
<span class="line">    <span style="color: #eceff4;">"</span><span style="color: #8fbcbb;">type</span><span style="color: #eceff4;">"</span><span style="color: #eceff4;">:</span> <span style="color: #eceff4;">"</span><span style="color: #a3be8c;">project</span><span style="color: #eceff4;">"</span><span style="color: #eceff4;">,</span></span>
<span class="line">    <span style="color: #eceff4;">"</span><span style="color: #8fbcbb;">license</span><span style="color: #eceff4;">"</span><span style="color: #eceff4;">:</span> <span style="color: #eceff4;">"</span><span style="color: #a3be8c;">MIT</span><span style="color: #eceff4;">"</span><span style="color: #eceff4;">,</span></span>
<span class="line">    <span style="color: #eceff4;">"</span><span style="color: #8fbcbb;">require</span><span style="color: #eceff4;">"</span><span style="color: #eceff4;">:</span> <span style="color: #eceff4;">{</span></span>
<span class="line">      <span style="color: #eceff4;">"</span><span style="color: #8fbcbb;">php</span><span style="color: #eceff4;">"</span><span style="color: #eceff4;">:</span> <span style="color: #eceff4;">"</span><span style="color: #a3be8c;">&gt;=8.3</span><span style="color: #eceff4;">"</span></span>
<span class="line">    <span style="color: #eceff4;">},</span></span>
<span class="line">    <span style="color: #eceff4;">"</span><span style="color: #8fbcbb;">autoload</span><span style="color: #eceff4;">"</span><span style="color: #eceff4;">:</span> <span style="color: #eceff4;">{</span></span>
<span class="line">      <span style="color: #eceff4;">"</span><span style="color: #8fbcbb;">psr-4</span><span style="color: #eceff4;">"</span><span style="color: #eceff4;">:</span> <span style="color: #eceff4;">{</span></span>
<span class="line">        <span style="color: #eceff4;">"</span><span style="color: #8fbcbb;">Renor</span><span style="color: #ebcb8b;">\</span><span style="color: #8fbcbb;">Watermark</span><span style="color: #ebcb8b;">\</span><span style="color: #eceff4;">"</span><span style="color: #eceff4;">:</span> <span style="color: #eceff4;">"</span><span style="color: #a3be8c;">src/</span><span style="color: #eceff4;">"</span></span>
<span class="line">      <span style="color: #eceff4;">}</span></span>
<span class="line">    <span style="color: #eceff4;">}</span></span>
<span class="line">  <span style="color: #eceff4;">}</span></span></code></pre>
</div>


<p>Dopo aver definito il nostro file composer.json procediamo all&#8217;installazione. Andiamo su terminale e digitiamo il comando:</p>


<pre class="wp-block-code"><code>composer install</code></pre>


<p>A questo punto composer creerà il file composer.lock e la cartella vendor che però non installerà pacchetti da Packagist perché non stiamo includendo dipendenze.</p>
<h2 class="wp-block-heading">src/Dct.php</h2>
<p>Ora possiamo finalmente scrivere la classe Dct. Ricordo che è preferibile inserire i commenti al codice, come standard in inglese:</p>


<div class="wp-block-kevinbatdorf-code-block-pro" style="font-size: .875rem; font-family: Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace; line-height: 1.25rem; --cbp-tab-width: 2; tab-size: var(--cbp-tab-width, 2);" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono">
<pre class="shiki dark-plus" style="background-color: #1e1e1e;" tabindex="0"><code><span class="line"><span style="color: #d4d4d4;">&lt;?php</span></span>

<span class="line"><span style="color: #c586c0;">declare</span><span style="color: #d4d4d4;">(strict_types=</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">);</span></span>

<span class="line"><span style="color: #569cd6;">namespace</span> <span style="color: #4ec9b0;">RenorWatermark</span><span style="color: #d4d4d4;">;</span></span>

<span class="line"><span style="color: #6a9955;">/**</span></span>
<span class="line"><span style="color: #6a9955;"> * 2-D Discrete Cosine Transform (DCT) and its inverse (IDCT) for 8x8 blocks.</span></span>
<span class="line"><span style="color: #6a9955;"> */</span></span>
<span class="line"><span style="color: #569cd6;">final</span> <span style="color: #569cd6;">class</span> <span style="color: #4ec9b0;">Dct</span></span>
<span class="line"><span style="color: #d4d4d4;">{</span></span>
<span class="line">    <span style="color: #569cd6;">private</span> <span style="color: #569cd6;">const</span><span style="color: #d4d4d4;"> N = </span><span style="color: #b5cea8;">8</span><span style="color: #d4d4d4;">;</span></span>
<span class="line">    <span style="color: #569cd6;">private</span> <span style="color: #569cd6;">const</span><span style="color: #d4d4d4;"> SQRT_2_INV = </span><span style="color: #b5cea8;">0.7071067811865476</span><span style="color: #d4d4d4;">; </span><span style="color: #6a9955;">// 1 / sqrt(2)</span></span>

<span class="line">    <span style="color: #6a9955;">/**</span></span>
<span class="line"><span style="color: #6a9955;">     * Performs the forward DCT on an 8x8 block of pixel intensities (0-255).</span></span>
<span class="line"><span style="color: #6a9955;">     */</span></span>
<span class="line">    <span style="color: #569cd6;">public</span> <span style="color: #569cd6;">static</span> <span style="color: #569cd6;">function</span> <span style="color: #dcdcaa;">forward</span><span style="color: #d4d4d4;">(</span><span style="color: #569cd6;">array</span> <span style="color: #9cdcfe;">$block</span><span style="color: #d4d4d4;">): </span><span style="color: #569cd6;">array</span></span>
<span class="line"><span style="color: #d4d4d4;">    {</span></span>
<span class="line">        <span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::</span><span style="color: #dcdcaa;">assertBlock</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$block</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">        <span style="color: #9cdcfe;">$C</span><span style="color: #d4d4d4;"> = [];</span></span>

<span class="line">        <span style="color: #c586c0;">for</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$u</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$u</span><span style="color: #d4d4d4;"> &lt; </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::N; ++</span><span style="color: #9cdcfe;">$u</span><span style="color: #d4d4d4;">) {</span></span>
<span class="line">            <span style="color: #9cdcfe;">$alphaU</span><span style="color: #d4d4d4;"> = (</span><span style="color: #9cdcfe;">$u</span><span style="color: #d4d4d4;"> === </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">) ? </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::SQRT_2_INV : </span><span style="color: #b5cea8;">1.0</span><span style="color: #d4d4d4;">;</span></span>
<span class="line">            <span style="color: #c586c0;">for</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$v</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$v</span><span style="color: #d4d4d4;"> &lt; </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::N; ++</span><span style="color: #9cdcfe;">$v</span><span style="color: #d4d4d4;">) {</span></span>
<span class="line">                <span style="color: #9cdcfe;">$alphaV</span><span style="color: #d4d4d4;"> = (</span><span style="color: #9cdcfe;">$v</span><span style="color: #d4d4d4;"> === </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">) ? </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::SQRT_2_INV : </span><span style="color: #b5cea8;">1.0</span><span style="color: #d4d4d4;">;</span></span>
<span class="line">                <span style="color: #9cdcfe;">$sum</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0.0</span><span style="color: #d4d4d4;">;</span></span>
<span class="line">                <span style="color: #c586c0;">for</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$x</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$x</span><span style="color: #d4d4d4;"> &lt; </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::N; ++</span><span style="color: #9cdcfe;">$x</span><span style="color: #d4d4d4;">) {</span></span>
<span class="line">                    <span style="color: #c586c0;">for</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$y</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$y</span><span style="color: #d4d4d4;"> &lt; </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::N; ++</span><span style="color: #9cdcfe;">$y</span><span style="color: #d4d4d4;">) {</span></span>
<span class="line">                        <span style="color: #9cdcfe;">$cosX</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">cos</span><span style="color: #d4d4d4;">((</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;"> * </span><span style="color: #9cdcfe;">$x</span><span style="color: #d4d4d4;"> + </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">) * </span><span style="color: #9cdcfe;">$u</span><span style="color: #d4d4d4;"> * M_PI / (</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;"> * </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::N));</span></span>
<span class="line">                        <span style="color: #9cdcfe;">$cosY</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">cos</span><span style="color: #d4d4d4;">((</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;"> * </span><span style="color: #9cdcfe;">$y</span><span style="color: #d4d4d4;"> + </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">) * </span><span style="color: #9cdcfe;">$v</span><span style="color: #d4d4d4;"> * M_PI / (</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;"> * </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::N));</span></span>
<span class="line">                        <span style="color: #9cdcfe;">$sum</span><span style="color: #d4d4d4;"> += </span><span style="color: #9cdcfe;">$block</span><span style="color: #d4d4d4;">[</span><span style="color: #9cdcfe;">$x</span><span style="color: #d4d4d4;">][</span><span style="color: #9cdcfe;">$y</span><span style="color: #d4d4d4;">] * </span><span style="color: #9cdcfe;">$cosX</span><span style="color: #d4d4d4;"> * </span><span style="color: #9cdcfe;">$cosY</span><span style="color: #d4d4d4;">;</span></span>
<span class="line"><span style="color: #d4d4d4;">                    }</span></span>
<span class="line"><span style="color: #d4d4d4;">                }</span></span>
<span class="line">                <span style="color: #6a9955;">// The DCT formula includes a factor of 1/4</span></span>
<span class="line">                <span style="color: #9cdcfe;">$C</span><span style="color: #d4d4d4;">[</span><span style="color: #9cdcfe;">$u</span><span style="color: #d4d4d4;">][</span><span style="color: #9cdcfe;">$v</span><span style="color: #d4d4d4;">] = </span><span style="color: #b5cea8;">0.25</span><span style="color: #d4d4d4;"> * </span><span style="color: #9cdcfe;">$alphaU</span><span style="color: #d4d4d4;"> * </span><span style="color: #9cdcfe;">$alphaV</span><span style="color: #d4d4d4;"> * </span><span style="color: #9cdcfe;">$sum</span><span style="color: #d4d4d4;">;</span></span>
<span class="line"><span style="color: #d4d4d4;">            }</span></span>
<span class="line"><span style="color: #d4d4d4;">        }</span></span>
<span class="line">        <span style="color: #c586c0;">return</span> <span style="color: #9cdcfe;">$C</span><span style="color: #d4d4d4;">;</span></span>
<span class="line"><span style="color: #d4d4d4;">    }</span></span>

<span class="line">    <span style="color: #6a9955;">/**</span></span>
<span class="line"><span style="color: #6a9955;">     * Performs the inverse DCT on an 8x8 block of coefficients, </span></span>
<span class="line"><span style="color: #6a9955;">     * returning pixel values in the range [0..255].</span></span>
<span class="line"><span style="color: #6a9955;">     */</span></span>
<span class="line">    <span style="color: #569cd6;">public</span> <span style="color: #569cd6;">static</span> <span style="color: #569cd6;">function</span> <span style="color: #dcdcaa;">inverse</span><span style="color: #d4d4d4;">(</span><span style="color: #569cd6;">array</span> <span style="color: #9cdcfe;">$coeff</span><span style="color: #d4d4d4;">): </span><span style="color: #569cd6;">array</span></span>
<span class="line"><span style="color: #d4d4d4;">    {</span></span>
<span class="line">        <span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::</span><span style="color: #dcdcaa;">assertBlock</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$coeff</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">        <span style="color: #9cdcfe;">$f</span><span style="color: #d4d4d4;"> = [];</span></span>

<span class="line">        <span style="color: #c586c0;">for</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$x</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$x</span><span style="color: #d4d4d4;"> &lt; </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::N; ++</span><span style="color: #9cdcfe;">$x</span><span style="color: #d4d4d4;">) {</span></span>
<span class="line">            <span style="color: #c586c0;">for</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$y</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$y</span><span style="color: #d4d4d4;"> &lt; </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::N; ++</span><span style="color: #9cdcfe;">$y</span><span style="color: #d4d4d4;">) {</span></span>
<span class="line">                <span style="color: #9cdcfe;">$sum</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0.0</span><span style="color: #d4d4d4;">;</span></span>
<span class="line">                <span style="color: #c586c0;">for</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$u</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$u</span><span style="color: #d4d4d4;"> &lt; </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::N; ++</span><span style="color: #9cdcfe;">$u</span><span style="color: #d4d4d4;">) {</span></span>
<span class="line">                    <span style="color: #9cdcfe;">$alphaU</span><span style="color: #d4d4d4;"> = (</span><span style="color: #9cdcfe;">$u</span><span style="color: #d4d4d4;"> === </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">) ? </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::SQRT_2_INV : </span><span style="color: #b5cea8;">1.0</span><span style="color: #d4d4d4;">;</span></span>
<span class="line">                    <span style="color: #c586c0;">for</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$v</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$v</span><span style="color: #d4d4d4;"> &lt; </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::N; ++</span><span style="color: #9cdcfe;">$v</span><span style="color: #d4d4d4;">) {</span></span>
<span class="line">                        <span style="color: #9cdcfe;">$alphaV</span><span style="color: #d4d4d4;"> = (</span><span style="color: #9cdcfe;">$v</span><span style="color: #d4d4d4;"> === </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">) ? </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::SQRT_2_INV : </span><span style="color: #b5cea8;">1.0</span><span style="color: #d4d4d4;">;</span></span>
<span class="line">                        <span style="color: #9cdcfe;">$cosX</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">cos</span><span style="color: #d4d4d4;">((</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;"> * </span><span style="color: #9cdcfe;">$x</span><span style="color: #d4d4d4;"> + </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">) * </span><span style="color: #9cdcfe;">$u</span><span style="color: #d4d4d4;"> * M_PI / (</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;"> * </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::N));</span></span>
<span class="line">                        <span style="color: #9cdcfe;">$cosY</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">cos</span><span style="color: #d4d4d4;">((</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;"> * </span><span style="color: #9cdcfe;">$y</span><span style="color: #d4d4d4;"> + </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">) * </span><span style="color: #9cdcfe;">$v</span><span style="color: #d4d4d4;"> * M_PI / (</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;"> * </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::N));</span></span>
<span class="line">                        <span style="color: #9cdcfe;">$sum</span><span style="color: #d4d4d4;"> += </span><span style="color: #9cdcfe;">$alphaU</span><span style="color: #d4d4d4;"> * </span><span style="color: #9cdcfe;">$alphaV</span><span style="color: #d4d4d4;"> * </span><span style="color: #9cdcfe;">$coeff</span><span style="color: #d4d4d4;">[</span><span style="color: #9cdcfe;">$u</span><span style="color: #d4d4d4;">][</span><span style="color: #9cdcfe;">$v</span><span style="color: #d4d4d4;">] * </span><span style="color: #9cdcfe;">$cosX</span><span style="color: #d4d4d4;"> * </span><span style="color: #9cdcfe;">$cosY</span><span style="color: #d4d4d4;">;</span></span>
<span class="line"><span style="color: #d4d4d4;">                    }</span></span>
<span class="line"><span style="color: #d4d4d4;">                }</span></span>
<span class="line">                <span style="color: #9cdcfe;">$pixel</span><span style="color: #d4d4d4;"> = (</span><span style="color: #569cd6;">int</span><span style="color: #d4d4d4;">) </span><span style="color: #dcdcaa;">round</span><span style="color: #d4d4d4;">(</span><span style="color: #b5cea8;">0.25</span><span style="color: #d4d4d4;"> * </span><span style="color: #9cdcfe;">$sum</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">                <span style="color: #9cdcfe;">$f</span><span style="color: #d4d4d4;">[</span><span style="color: #9cdcfe;">$x</span><span style="color: #d4d4d4;">][</span><span style="color: #9cdcfe;">$y</span><span style="color: #d4d4d4;">] = </span><span style="color: #dcdcaa;">max</span><span style="color: #d4d4d4;">(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #dcdcaa;">min</span><span style="color: #d4d4d4;">(</span><span style="color: #b5cea8;">255</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$pixel</span><span style="color: #d4d4d4;">));</span></span>
<span class="line"><span style="color: #d4d4d4;">            }</span></span>
<span class="line"><span style="color: #d4d4d4;">        }</span></span>
<span class="line">        <span style="color: #c586c0;">return</span> <span style="color: #9cdcfe;">$f</span><span style="color: #d4d4d4;">;</span></span>
<span class="line"><span style="color: #d4d4d4;">    }</span></span>

<span class="line">    <span style="color: #6a9955;">/**</span></span>
<span class="line"><span style="color: #6a9955;">     * Checks that the array is exactly 8x8.</span></span>
<span class="line"><span style="color: #6a9955;">     */</span></span>
<span class="line">    <span style="color: #569cd6;">private</span> <span style="color: #569cd6;">static</span> <span style="color: #569cd6;">function</span> <span style="color: #dcdcaa;">assertBlock</span><span style="color: #d4d4d4;">(</span><span style="color: #569cd6;">array</span> <span style="color: #9cdcfe;">$block</span><span style="color: #d4d4d4;">): </span><span style="color: #569cd6;">void</span></span>
<span class="line"><span style="color: #d4d4d4;">    {</span></span>
<span class="line">        <span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> (</span><span style="color: #dcdcaa;">count</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$block</span><span style="color: #d4d4d4;">) !== </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::N) {</span></span>
<span class="line">            <span style="color: #c586c0;">throw</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">InvalidArgumentException</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">'Block must have 8 rows.'</span><span style="color: #d4d4d4;">);</span></span>
<span class="line"><span style="color: #d4d4d4;">        }</span></span>
<span class="line">        <span style="color: #c586c0;">foreach</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$block</span><span style="color: #d4d4d4;"> as </span><span style="color: #9cdcfe;">$row</span><span style="color: #d4d4d4;">) {</span></span>
<span class="line">            <span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> (</span><span style="color: #dcdcaa;">count</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$row</span><span style="color: #d4d4d4;">) !== </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::N) {</span></span>
<span class="line">                <span style="color: #c586c0;">throw</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">InvalidArgumentException</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">'Each row must have 8 columns.'</span><span style="color: #d4d4d4;">);</span></span>
<span class="line"><span style="color: #d4d4d4;">            }</span></span>
<span class="line"><span style="color: #d4d4d4;">        }</span></span>
<span class="line"><span style="color: #d4d4d4;">    }</span></span>

<span class="line">    <span style="color: #6a9955;">/**</span></span>
<span class="line"><span style="color: #6a9955;">     * Disallow instantiation.</span></span>
<span class="line"><span style="color: #6a9955;">     */</span></span>
<span class="line">    <span style="color: #569cd6;">private</span> <span style="color: #569cd6;">function</span> <span style="color: #dcdcaa;">__construct</span><span style="color: #d4d4d4;">()</span></span>
<span class="line"><span style="color: #d4d4d4;">    {</span></span>
<span class="line"><span style="color: #d4d4d4;">    }</span></span>
<span class="line"><span style="color: #d4d4d4;">}</span></span></code></pre>
</div>


<p>Questa classe è il cuore del progetto. Racchiude l’algoritmo della Trasformata Coseno Discreta bidimensionale applicata a blocchi 8×8 pixel, lo stesso schema impiegato dallo standard JPEG. All’avvio del metodo forward, la classe controlla che l’array ricevuto contenga esattamente otto righe e otto colonne; in caso contrario, lancia un’eccezione per evitare che un errore di dimensionamento si propaghi silenziosamente.</p>
<p>Una volta verificata la forma, per ogni coppia di indici di frequenza <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-75fa73f534ffea86ffac4074123b96b5_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#117;&#44;&#32;&#118;" title="Rendered by QuickLaTeX.com" height="12" width="27" style="vertical-align: -4px;"/>, calcola la doppia somma definita dalla formula canonica della DCT: prende ciascun valore di luminosità <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-533196a0d71c414a47d0753600045b35_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#102;&#95;&#123;&#120;&#44;&#121;&#125;" title="Rendered by QuickLaTeX.com" height="18" width="28" style="vertical-align: -6px;"/>, lo moltiplica per due coseni — uno in funzione di <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-43fe27dc3e528266a619764d90fce60b_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#117;" title="Rendered by QuickLaTeX.com" height="8" width="10" style="vertical-align: 0px;"/> e della coordinata <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-ede05c264bba0eda080918aaa09c4658_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#120;" title="Rendered by QuickLaTeX.com" height="8" width="10" style="vertical-align: 0px;"/>, l’altro in funzione di <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-ef71511c70f0e4b25cc6bd69f3bc20c2_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#118;" title="Rendered by QuickLaTeX.com" height="8" width="9" style="vertical-align: 0px;"/> e della coordinata <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-0af556714940c351c933bba8cf840796_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#121;" title="Rendered by QuickLaTeX.com" height="12" width="9" style="vertical-align: -4px;"/> — e accumula il risultato. Terminata la scansione del blocco, applica il fattore di normalizzazione <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-fcd08c79ecaf47db47dc99faebe00bca_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#116;&#102;&#114;&#97;&#99;&#123;&#49;&#125;&#123;&#52;&#125;&#32;&#97;&#108;&#112;&#104;&#97;&#40;&#117;&#41;&#97;&#108;&#112;&#104;&#97;&#40;&#118;&#41;" title="Rendered by QuickLaTeX.com" height="19" width="193" style="vertical-align: -5px;"/>, dove <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-11ff76fdb61f1bd31cc0442d9cb64c39_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#97;&#108;&#112;&#104;&#97;&#40;&#48;&#41;&#61;&#49;&#47;&#115;&#113;&#114;&#116;&#123;&#50;&#125;" title="Rendered by QuickLaTeX.com" height="19" width="147" style="vertical-align: -5px;"/> e <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-18902120d510358659a3a0338c6cf6e0_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#97;&#108;&#112;&#104;&#97;&#40;&#107;&#41;&#61;&#49;" title="Rendered by QuickLaTeX.com" height="19" width="99" style="vertical-align: -5px;"/> per <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-271cc266bbf4608f3faee046ead94a1e_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#107;&#103;&#101;&#49;" title="Rendered by QuickLaTeX.com" height="16" width="35" style="vertical-align: -4px;"/>, così che il coefficiente <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-d28eb1caf4d0c1c6fad49a76dcf1fcd7_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#67;&#95;&#123;&#48;&#44;&#48;&#125;" title="Rendered by QuickLaTeX.com" height="18" width="31" style="vertical-align: -6px;"/> conservi la media luminosa e tutti gli altri rappresentino, con la corretta scala, le componenti di dettaglio via via più fini. Il metodo restituisce quindi una matrice 8×8 di coefficienti in virgola mobile: è su questa rappresentazione in frequenza che più avanti incideremo i bit del watermark.</p>
<p>Il percorso inverso è affidato al metodo inverse. Anche qui, prima di qualsiasi elaborazione, la classe impone la verifica della forma del blocco. Per ogni coppia di coordinate di pixel <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-adc5a68ffaee1d41b37612d9733b4496_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#120;&#44;&#121;" title="Rendered by QuickLaTeX.com" height="12" width="27" style="vertical-align: -4px;"/>, ricostruisce la luminanza sommando le 64 frequenze, ciascuna ponderata dal prodotto dei coseni e dai fattori di normalizzazione <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-926539c4099b535d53fdf307483e151e_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#97;&#108;&#112;&#104;&#97;&#40;&#117;&#41;&#97;&#108;&#112;&#104;&#97;&#40;&#118;&#41;" title="Rendered by QuickLaTeX.com" height="19" width="133" style="vertical-align: -5px;"/>. Il valore finale viene moltiplicato per <img loading="lazy" decoding="async" src="https://renor.it/wp-content/ql-cache/quicklatex.com-567485e42ded17b46007ba391528444e_l3.png" class="ql-img-inline-formula quicklatex-auto-format" alt="&#116;&#102;&#114;&#97;&#99;&#123;&#49;&#125;&#123;&#52;&#125;" title="Rendered by QuickLaTeX.com" height="16" width="60" style="vertical-align: -4px;"/>, arrotondato all’intero più vicino e limitato all’intervallo 0–255, così da evitare che eventuali errori numerici facciano uscire il risultato dallo spazio colore.</p>
<p>L’implementazione segue fedelmente la definizione matematica, senza ricorrere ad accelerazioni FFT o SIMD, privilegiando la chiarezza didattica rispetto alle prestazioni. Il costruttore è privato, la classe non può essere istanziata, e tutti i metodi sono statici e privi di stato interno: in questo modo la DCT si comporta come una funzione pura, facilmente testabile e riutilizzabile. Quando, nei prossimi paragrafi, la useremo per incastonare un bit modificando il segno di un coefficiente a bassa frequenza, potremo essere certi che la trasformazione diretta e inversa siano numericamente coerenti e che l’alterazione resti confinata ai valori che intendiamo manipolare.</p>
<h2 class="wp-block-heading">src/Watermarker.php</h2>
<p>La classe Watermarker è la regista che orchestra la fase più delicata dell’intero progetto. Riceve in ingresso o in uscita una sequenza di blocchi 8×8 di coefficienti DCT già calcolati dalla classe Dct e, senza occuparsi della lettura fisica del file JPEG, si limita a manipolare quei numeri per nascondere o recuperare un testo ASCII.</p>


<div class="wp-block-kevinbatdorf-code-block-pro" style="font-size: .875rem; font-family: Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace; line-height: 1.25rem; --cbp-tab-width: 2; tab-size: var(--cbp-tab-width, 2);" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono">
<pre class="shiki dark-plus" style="background-color: #1e1e1e;" tabindex="0"><code><span class="line"><span style="color: #d4d4d4;">&lt;?php</span></span>

<span class="line"><span style="color: #c586c0;">declare</span><span style="color: #d4d4d4;">(strict_types=</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">);</span></span>

<span class="line"><span style="color: #569cd6;">namespace</span> <span style="color: #4ec9b0;">RenorWatermark</span><span style="color: #d4d4d4;">;</span></span>

<span class="line"><span style="color: #6a9955;">/**</span></span>
<span class="line"><span style="color: #6a9955;"> * Watermarker class</span></span>
<span class="line"><span style="color: #6a9955;"> *</span></span>
<span class="line"><span style="color: #6a9955;"> * Embeds/extracts a short ASCII message (max 10 characters + terminator)</span></span>
<span class="line"><span style="color: #6a9955;"> * into low-frequency DCT coefficients. Instead of relying on the single</span></span>
<span class="line"><span style="color: #6a9955;"> * parity bit – too fragile after rounding and quantization – here</span></span>
<span class="line"><span style="color: #6a9955;"> * we encode the information into the **sign** of the coefficient:</span></span>
<span class="line"><span style="color: #6a9955;"> *</span></span>
<span class="line"><span style="color: #6a9955;"> *   bit 0  → positive coefficient</span></span>
<span class="line"><span style="color: #6a9955;"> *   bit 1  → negative coefficient</span></span>
<span class="line"><span style="color: #6a9955;"> *</span></span>
<span class="line"><span style="color: #6a9955;"> * The sign is never affected during:</span></span>
<span class="line"><span style="color: #6a9955;"> *   1. IDCT → integer rounding,</span></span>
<span class="line"><span style="color: #6a9955;"> *   2. division by JPEG quantization tables,</span></span>
<span class="line"><span style="color: #6a9955;"> *   3. further rounding or transformations.</span></span>
<span class="line"><span style="color: #6a9955;"> */</span></span>
<span class="line"><span style="color: #569cd6;">final</span> <span style="color: #569cd6;">class</span> <span style="color: #4ec9b0;">Watermarker</span></span>
<span class="line"><span style="color: #d4d4d4;">{</span></span>
<span class="line">    <span style="color: #6a9955;">/** Low-frequency coordinates (survive even at q≈90–100). */</span></span>
<span class="line">    <span style="color: #569cd6;">private</span> <span style="color: #569cd6;">const</span><span style="color: #d4d4d4;"> EMBED_COORDS = [</span></span>
<span class="line"><span style="color: #d4d4d4;">        [</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">], [</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">], [</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">], [</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">],</span></span>
<span class="line"><span style="color: #d4d4d4;">        [</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">], [</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">], [</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">], [</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">]</span></span>
<span class="line"><span style="color: #d4d4d4;">    ];</span></span>

<span class="line">    <span style="color: #6a9955;">/** Redundancy: each bit is repeated 25 times. */</span></span>
<span class="line">    <span style="color: #569cd6;">private</span> <span style="color: #569cd6;">const</span><span style="color: #d4d4d4;"> REDUNDANCY = </span><span style="color: #b5cea8;">25</span><span style="color: #d4d4d4;">;</span></span>

<span class="line">    <span style="color: #569cd6;">private</span> <span style="color: #569cd6;">const</span><span style="color: #d4d4d4;"> TERMINATOR = </span><span style="color: #ce9178;">"</span><span style="color: #d7ba7d;"> </span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">;</span></span>

<span class="line">    <span style="color: #6a9955;">/* ------------------------------------------------------------------</span></span>
<span class="line"><span style="color: #6a9955;">       Public API</span></span>
<span class="line"><span style="color: #6a9955;">    -------------------------------------------------------------------*/</span></span>

<span class="line">    <span style="color: #569cd6;">public</span> <span style="color: #569cd6;">static</span> <span style="color: #569cd6;">function</span> <span style="color: #dcdcaa;">embed</span><span style="color: #d4d4d4;">(</span><span style="color: #569cd6;">array</span> <span style="color: #569cd6;">&amp;</span><span style="color: #9cdcfe;">$blocks</span><span style="color: #d4d4d4;">, </span><span style="color: #569cd6;">string</span> <span style="color: #9cdcfe;">$msg</span><span style="color: #d4d4d4;">): </span><span style="color: #569cd6;">void</span></span>
<span class="line"><span style="color: #d4d4d4;">    {</span></span>
<span class="line">        <span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> (</span><span style="color: #dcdcaa;">strlen</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$msg</span><span style="color: #d4d4d4;">) &gt; </span><span style="color: #b5cea8;">10</span><span style="color: #d4d4d4;">) {</span></span>
<span class="line">            <span style="color: #c586c0;">throw</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">RuntimeException</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">'Message too long (max 10).'</span><span style="color: #d4d4d4;">);</span></span>
<span class="line"><span style="color: #d4d4d4;">        }</span></span>

<span class="line">        <span style="color: #9cdcfe;">$bits</span><span style="color: #d4d4d4;">   = </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::</span><span style="color: #dcdcaa;">stringToBitStream</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$msg</span> <span style="color: #d4d4d4;">.</span> <span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::TERMINATOR);</span></span>
<span class="line">        <span style="color: #9cdcfe;">$cursor</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">;</span></span>

<span class="line">        <span style="color: #c586c0;">foreach</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$blocks</span><span style="color: #d4d4d4;"> as </span><span style="color: #569cd6;">&amp;</span><span style="color: #9cdcfe;">$block</span><span style="color: #d4d4d4;">) {</span></span>
<span class="line">            <span style="color: #c586c0;">foreach</span><span style="color: #d4d4d4;"> (</span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::EMBED_COORDS as [</span><span style="color: #9cdcfe;">$u</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$v</span><span style="color: #d4d4d4;">]) {</span></span>
<span class="line">                <span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$cursor</span><span style="color: #d4d4d4;"> &gt;= </span><span style="color: #dcdcaa;">count</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$bits</span><span style="color: #d4d4d4;">)) {</span></span>
<span class="line">                    <span style="color: #c586c0;">return</span><span style="color: #d4d4d4;">;</span></span>
<span class="line"><span style="color: #d4d4d4;">                }</span></span>
<span class="line">                <span style="color: #9cdcfe;">$bit</span><span style="color: #d4d4d4;">           = </span><span style="color: #9cdcfe;">$bits</span><span style="color: #d4d4d4;">[</span><span style="color: #9cdcfe;">$cursor</span><span style="color: #d4d4d4;">++];</span></span>
<span class="line">                <span style="color: #9cdcfe;">$block</span><span style="color: #d4d4d4;">[</span><span style="color: #9cdcfe;">$u</span><span style="color: #d4d4d4;">][</span><span style="color: #9cdcfe;">$v</span><span style="color: #d4d4d4;">] = </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::</span><span style="color: #dcdcaa;">forceSign</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$block</span><span style="color: #d4d4d4;">[</span><span style="color: #9cdcfe;">$u</span><span style="color: #d4d4d4;">][</span><span style="color: #9cdcfe;">$v</span><span style="color: #d4d4d4;">], </span><span style="color: #9cdcfe;">$bit</span><span style="color: #d4d4d4;">);</span></span>
<span class="line"><span style="color: #d4d4d4;">            }</span></span>
<span class="line"><span style="color: #d4d4d4;">        }</span></span>
<span class="line">        <span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$cursor</span><span style="color: #d4d4d4;"> &lt; </span><span style="color: #dcdcaa;">count</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$bits</span><span style="color: #d4d4d4;">)) {</span></span>
<span class="line">            <span style="color: #c586c0;">throw</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">RuntimeException</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">'Image too small for the message.'</span><span style="color: #d4d4d4;">);</span></span>
<span class="line"><span style="color: #d4d4d4;">        }</span></span>
<span class="line"><span style="color: #d4d4d4;">    }</span></span>

<span class="line">    <span style="color: #569cd6;">public</span> <span style="color: #569cd6;">static</span> <span style="color: #569cd6;">function</span> <span style="color: #dcdcaa;">extract</span><span style="color: #d4d4d4;">(</span><span style="color: #569cd6;">array</span> <span style="color: #9cdcfe;">$blocks</span><span style="color: #d4d4d4;">): </span><span style="color: #569cd6;">string</span></span>
<span class="line"><span style="color: #d4d4d4;">    {</span></span>
<span class="line">        <span style="color: #9cdcfe;">$bits</span><span style="color: #d4d4d4;"> = [];</span></span>
<span class="line">        <span style="color: #c586c0;">foreach</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$blocks</span><span style="color: #d4d4d4;"> as </span><span style="color: #9cdcfe;">$block</span><span style="color: #d4d4d4;">) {</span></span>
<span class="line">            <span style="color: #c586c0;">foreach</span><span style="color: #d4d4d4;"> (</span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::EMBED_COORDS as [</span><span style="color: #9cdcfe;">$u</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$v</span><span style="color: #d4d4d4;">]) {</span></span>
<span class="line">                <span style="color: #9cdcfe;">$bits</span><span style="color: #d4d4d4;">[] = ( (</span><span style="color: #569cd6;">int</span><span style="color: #d4d4d4;">) </span><span style="color: #dcdcaa;">round</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$block</span><span style="color: #d4d4d4;">[</span><span style="color: #9cdcfe;">$u</span><span style="color: #d4d4d4;">][</span><span style="color: #9cdcfe;">$v</span><span style="color: #d4d4d4;">]) &lt; </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;"> ) ? </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;"> : </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">;</span></span>
<span class="line"><span style="color: #d4d4d4;">            }</span></span>
<span class="line"><span style="color: #d4d4d4;">        }</span></span>

<span class="line">        <span style="color: #9cdcfe;">$filtered</span><span style="color: #d4d4d4;">   = </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::</span><span style="color: #dcdcaa;">majority</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$bits</span><span style="color: #d4d4d4;">, </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::REDUNDANCY);</span></span>
<span class="line">        <span style="color: #9cdcfe;">$byteChunks</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">array_chunk</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$filtered</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">8</span><span style="color: #d4d4d4;">);</span></span>

<span class="line">        <span style="color: #9cdcfe;">$out</span><span style="color: #d4d4d4;"> = </span><span style="color: #ce9178;">''</span><span style="color: #d4d4d4;">;</span></span>
<span class="line">        <span style="color: #c586c0;">foreach</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$byteChunks</span><span style="color: #d4d4d4;"> as </span><span style="color: #9cdcfe;">$chunk</span><span style="color: #d4d4d4;">) {</span></span>
<span class="line">            <span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> (</span><span style="color: #dcdcaa;">count</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$chunk</span><span style="color: #d4d4d4;">) &lt; </span><span style="color: #b5cea8;">8</span><span style="color: #d4d4d4;">) </span><span style="color: #c586c0;">break</span><span style="color: #d4d4d4;">;</span></span>
<span class="line">            <span style="color: #9cdcfe;">$ch</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">chr</span><span style="color: #d4d4d4;">(</span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::</span><span style="color: #dcdcaa;">bitsToInt</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$chunk</span><span style="color: #d4d4d4;">));</span></span>
<span class="line">            <span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$ch</span><span style="color: #d4d4d4;"> === </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::TERMINATOR) </span><span style="color: #c586c0;">break</span><span style="color: #d4d4d4;">;</span></span>
<span class="line">            <span style="color: #9cdcfe;">$out</span> <span style="color: #d4d4d4;">.=</span> <span style="color: #9cdcfe;">$ch</span><span style="color: #d4d4d4;">;</span></span>
<span class="line"><span style="color: #d4d4d4;">        }</span></span>
<span class="line">        <span style="color: #c586c0;">return</span> <span style="color: #9cdcfe;">$out</span><span style="color: #d4d4d4;">;</span></span>
<span class="line"><span style="color: #d4d4d4;">    }</span></span>

<span class="line">    <span style="color: #6a9955;">/* ------------------------------------------------------------------</span></span>
<span class="line"><span style="color: #6a9955;">       Private helpers</span></span>
<span class="line"><span style="color: #6a9955;">    -------------------------------------------------------------------*/</span></span>

<span class="line">    <span style="color: #569cd6;">private</span> <span style="color: #569cd6;">static</span> <span style="color: #569cd6;">function</span> <span style="color: #dcdcaa;">stringToBitStream</span><span style="color: #d4d4d4;">(</span><span style="color: #569cd6;">string</span> <span style="color: #9cdcfe;">$s</span><span style="color: #d4d4d4;">): </span><span style="color: #569cd6;">array</span></span>
<span class="line"><span style="color: #d4d4d4;">    {</span></span>
<span class="line">        <span style="color: #9cdcfe;">$bits</span><span style="color: #d4d4d4;"> = [];</span></span>
<span class="line">        <span style="color: #c586c0;">foreach</span><span style="color: #d4d4d4;"> (</span><span style="color: #dcdcaa;">str_split</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$s</span><span style="color: #d4d4d4;">) as </span><span style="color: #9cdcfe;">$c</span><span style="color: #d4d4d4;">) {</span></span>
<span class="line">            <span style="color: #9cdcfe;">$byte</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">ord</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$c</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">            <span style="color: #c586c0;">for</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$i</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">7</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$i</span><span style="color: #d4d4d4;"> &gt;= </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">; --</span><span style="color: #9cdcfe;">$i</span><span style="color: #d4d4d4;">) {</span></span>
<span class="line">                <span style="color: #9cdcfe;">$bit</span><span style="color: #d4d4d4;"> = (</span><span style="color: #9cdcfe;">$byte</span><span style="color: #d4d4d4;"> &gt;&gt; </span><span style="color: #9cdcfe;">$i</span><span style="color: #d4d4d4;">) &amp; </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">;</span></span>
<span class="line">                <span style="color: #9cdcfe;">$bits</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">array_merge</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$bits</span><span style="color: #d4d4d4;">, </span><span style="color: #dcdcaa;">array_fill</span><span style="color: #d4d4d4;">(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::REDUNDANCY, </span><span style="color: #9cdcfe;">$bit</span><span style="color: #d4d4d4;">));</span></span>
<span class="line"><span style="color: #d4d4d4;">            }</span></span>
<span class="line"><span style="color: #d4d4d4;">        }</span></span>
<span class="line">        <span style="color: #c586c0;">return</span> <span style="color: #9cdcfe;">$bits</span><span style="color: #d4d4d4;">;</span></span>
<span class="line"><span style="color: #d4d4d4;">    }</span></span>

<span class="line">    <span style="color: #569cd6;">private</span> <span style="color: #569cd6;">static</span> <span style="color: #569cd6;">function</span> <span style="color: #dcdcaa;">majority</span><span style="color: #d4d4d4;">(</span><span style="color: #569cd6;">array</span> <span style="color: #9cdcfe;">$bits</span><span style="color: #d4d4d4;">, </span><span style="color: #569cd6;">int</span> <span style="color: #9cdcfe;">$group</span><span style="color: #d4d4d4;">): </span><span style="color: #569cd6;">array</span></span>
<span class="line"><span style="color: #d4d4d4;">    {</span></span>
<span class="line">        <span style="color: #9cdcfe;">$out</span><span style="color: #d4d4d4;"> = [];</span></span>
<span class="line">        <span style="color: #c586c0;">foreach</span><span style="color: #d4d4d4;"> (</span><span style="color: #dcdcaa;">array_chunk</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$bits</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$group</span><span style="color: #d4d4d4;">) as </span><span style="color: #9cdcfe;">$chunk</span><span style="color: #d4d4d4;">) {</span></span>
<span class="line">            <span style="color: #9cdcfe;">$out</span><span style="color: #d4d4d4;">[] = (</span><span style="color: #dcdcaa;">array_sum</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$chunk</span><span style="color: #d4d4d4;">) &gt; </span><span style="color: #9cdcfe;">$group</span><span style="color: #d4d4d4;"> / </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">) ? </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;"> : </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">;</span></span>
<span class="line"><span style="color: #d4d4d4;">        }</span></span>
<span class="line">        <span style="color: #c586c0;">return</span> <span style="color: #9cdcfe;">$out</span><span style="color: #d4d4d4;">;</span></span>
<span class="line"><span style="color: #d4d4d4;">    }</span></span>

<span class="line">    <span style="color: #569cd6;">private</span> <span style="color: #569cd6;">static</span> <span style="color: #569cd6;">function</span> <span style="color: #dcdcaa;">bitsToInt</span><span style="color: #d4d4d4;">(</span><span style="color: #569cd6;">array</span> <span style="color: #9cdcfe;">$bits</span><span style="color: #d4d4d4;">): </span><span style="color: #569cd6;">int</span></span>
<span class="line"><span style="color: #d4d4d4;">    {</span></span>
<span class="line">        <span style="color: #c586c0;">return</span> <span style="color: #dcdcaa;">array_reduce</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$bits</span><span style="color: #d4d4d4;">, </span><span style="color: #569cd6;">fn</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$v</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$b</span><span style="color: #d4d4d4;">) =&gt; (</span><span style="color: #9cdcfe;">$v</span><span style="color: #d4d4d4;"> &lt;&lt; </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">) | </span><span style="color: #9cdcfe;">$b</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">);</span></span>
<span class="line"><span style="color: #d4d4d4;">    }</span></span>

<span class="line">    <span style="color: #6a9955;">/**</span></span>
<span class="line"><span style="color: #6a9955;">     * Forces the sign of the coefficient:</span></span>
<span class="line"><span style="color: #6a9955;">     *   bit 0 → positive   (&gt;= +minAmp)</span></span>
<span class="line"><span style="color: #6a9955;">     *   bit 1 → negative   (&lt;= -minAmp)</span></span>
<span class="line"><span style="color: #6a9955;">     * so the quantizer cannot flip it.</span></span>
<span class="line"><span style="color: #6a9955;">     */</span></span>
<span class="line">    <span style="color: #569cd6;">private</span> <span style="color: #569cd6;">static</span> <span style="color: #569cd6;">function</span> <span style="color: #dcdcaa;">forceSign</span><span style="color: #d4d4d4;">(</span></span>
<span class="line">        <span style="color: #569cd6;">float</span> <span style="color: #9cdcfe;">$coeff</span><span style="color: #d4d4d4;">,</span></span>
<span class="line">        <span style="color: #569cd6;">int</span>   <span style="color: #9cdcfe;">$bit</span><span style="color: #d4d4d4;">,</span></span>
<span class="line">        <span style="color: #569cd6;">int</span>   <span style="color: #9cdcfe;">$minAmp</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">80</span><span style="color: #d4d4d4;">,   </span><span style="color: #6a9955;">// safe amplitude &gt; luminance quantization divisors</span></span>
<span class="line">        <span style="color: #569cd6;">int</span>   <span style="color: #9cdcfe;">$boost</span><span style="color: #d4d4d4;">  = </span><span style="color: #b5cea8;">64</span>    <span style="color: #6a9955;">// further offset to move away from zero</span></span>
<span class="line"><span style="color: #d4d4d4;">    ): </span><span style="color: #569cd6;">float</span><span style="color: #d4d4d4;"> {</span></span>
<span class="line">        <span style="color: #9cdcfe;">$rounded</span><span style="color: #d4d4d4;"> = (</span><span style="color: #569cd6;">int</span><span style="color: #d4d4d4;">) </span><span style="color: #dcdcaa;">round</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$coeff</span><span style="color: #d4d4d4;">);</span></span>

<span class="line">        <span style="color: #6a9955;">// Enforce minimum amplitude</span></span>
<span class="line">        <span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> (</span><span style="color: #dcdcaa;">abs</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$rounded</span><span style="color: #d4d4d4;">) &lt; </span><span style="color: #9cdcfe;">$minAmp</span><span style="color: #d4d4d4;">) {</span></span>
<span class="line">            <span style="color: #9cdcfe;">$rounded</span><span style="color: #d4d4d4;"> = (</span><span style="color: #9cdcfe;">$rounded</span><span style="color: #d4d4d4;"> &gt;= </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">) ? </span><span style="color: #9cdcfe;">$minAmp</span><span style="color: #d4d4d4;"> : -</span><span style="color: #9cdcfe;">$minAmp</span><span style="color: #d4d4d4;">;</span></span>
<span class="line"><span style="color: #d4d4d4;">        }</span></span>

<span class="line">        <span style="color: #6a9955;">// Set the sign according to the bit</span></span>
<span class="line">        <span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$bit</span><span style="color: #d4d4d4;"> === </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;"> &amp;&amp; </span><span style="color: #9cdcfe;">$rounded</span><span style="color: #d4d4d4;"> &lt; </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">) </span><span style="color: #9cdcfe;">$rounded</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">abs</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$rounded</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">        <span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$bit</span><span style="color: #d4d4d4;"> === </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;"> &amp;&amp; </span><span style="color: #9cdcfe;">$rounded</span><span style="color: #d4d4d4;"> &gt; </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">) </span><span style="color: #9cdcfe;">$rounded</span><span style="color: #d4d4d4;"> = -</span><span style="color: #9cdcfe;">$rounded</span><span style="color: #d4d4d4;">;</span></span>

<span class="line">        <span style="color: #6a9955;">// Push further from zero: +/-boost (preserving the sign)</span></span>
<span class="line">        <span style="color: #9cdcfe;">$rounded</span><span style="color: #d4d4d4;"> += (</span><span style="color: #9cdcfe;">$rounded</span><span style="color: #d4d4d4;"> &gt; </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">) ? </span><span style="color: #9cdcfe;">$boost</span><span style="color: #d4d4d4;"> : -</span><span style="color: #9cdcfe;">$boost</span><span style="color: #d4d4d4;">;</span></span>

<span class="line">        <span style="color: #c586c0;">return</span><span style="color: #d4d4d4;"> (</span><span style="color: #569cd6;">float</span><span style="color: #d4d4d4;">) </span><span style="color: #9cdcfe;">$rounded</span><span style="color: #d4d4d4;">;</span></span>
<span class="line"><span style="color: #d4d4d4;">    }</span></span>

<span class="line">    <span style="color: #569cd6;">private</span> <span style="color: #569cd6;">function</span> <span style="color: #dcdcaa;">__construct</span><span style="color: #d4d4d4;">() {}</span></span>
<span class="line"><span style="color: #d4d4d4;">}</span></span></code></pre>
</div>


<p>Quando viene invocato il metodo embed, il chiamante le passa l’array dei blocchi DCT e il messaggio da inserire. Il metodo trasforma ogni carattere in 8 bit, e ciascun bit viene replicato 25 volte (come specificato dalla costante REDUNDANCY), così da poter essere letto con un voto di maggioranza e resistere ad alterazioni dovute alla compressione JPEG. Il flusso così ridondato scorre blocco dopo blocco e si appoggia su 8 coordinate a <strong>bassa frequenza</strong>, definite nell’array costante EMBED_COORDS. Queste posizioni sono state scelte perché non intaccano la percezione visiva ma tendono a sopravvivere anche a compressioni JPEG moderate (qualità 60–80).</p>
<p>Per ogni coefficiente selezionato, la funzione privata forceSign() impone un segno al coefficiente: positivo per rappresentare 0, negativo per rappresentare 1. Questo approccio, più robusto rispetto all’uso della parità, garantisce che l’informazione rimanga leggibile anche dopo un passaggio di quantizzazione JPEG. Se l’immagine non contiene abbastanza blocchi per rappresentare l’intero messaggio, viene lanciata un’eccezione con un messaggio esplicito.</p>
<p>Il percorso inverso è affidato al metodo extract. Anche qui, la classe legge il segno degli stessi 8 coefficienti in ogni blocco, ricostruendo un lungo vettore di zeri e uno. I bit vengono poi raggruppati secondo la ridondanza e passano attraverso una funzione di voto di maggioranza (majority()), che recupera il singolo bit originario. Ogni gruppo di 8 bit restituisce un carattere, fino a incontrare il terminatore NUL che segnala la fine del messaggio. L’insieme dei caratteri così ottenuti forma il messaggio originale.</p>
<p>Le funzioni di supporto stringToBitStream, bitsToInt e majority incapsulano i dettagli binari, lasciando i metodi pubblici brevi e leggibili. Come la classe Dct, anche Watermarker è priva di stato interno e non può essere istanziata: tutti i metodi sono statici. Questo approccio funzionale facilita i test automatici e l’uso in ambienti concorrenti. In sintesi, la classe Watermarker realizza concretamente i principi matematici descritti in precedenza: ridondanza ciclica, codifica del bit tramite segno, terminazione esplicita del messaggio, in un flusso compatto e robusto pensato per sopravvivere alle trasformazioni tipiche delle immagini pubblicate online.</p>
<h2 class="wp-block-heading">ImageIO: dall&#8217;immagine ai blocchi DCT e ritorno</h2>
<p>La classe ImageIO è il ponte tra la teoria matematica e il file JPEG concreto.</p>


<div class="wp-block-kevinbatdorf-code-block-pro" style="font-size: .875rem; font-family: Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace; line-height: 1.25rem; --cbp-tab-width: 2; tab-size: var(--cbp-tab-width, 2);" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono">
<pre class="shiki dark-plus" style="background-color: #1e1e1e;" tabindex="0"><code><span class="line"><span style="color: #d4d4d4;">&lt;?php</span></span>

<span class="line"><span style="color: #c586c0;">declare</span><span style="color: #d4d4d4;">(strict_types=</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">);</span></span>

<span class="line"><span style="color: #569cd6;">namespace</span> <span style="color: #4ec9b0;">RenorWatermark</span><span style="color: #d4d4d4;">;</span></span>

<span class="line"><span style="color: #569cd6;">use</span> <span style="color: #4ec9b0;">GdImage</span><span style="color: #d4d4d4;">;</span></span>

<span class="line"><span style="color: #6a9955;">/**</span></span>
<span class="line"><span style="color: #6a9955;"> * Class ImageIO</span></span>
<span class="line"><span style="color: #6a9955;"> *</span></span>
<span class="line"><span style="color: #6a9955;"> * - Loads a JPEG image.</span></span>
<span class="line"><span style="color: #6a9955;"> * - Converts RGB to the Y (luminance) channel.</span></span>
<span class="line"><span style="color: #6a9955;"> * - Splits it into 8x8 blocks and applies DCT/IDCT.</span></span>
<span class="line"><span style="color: #6a9955;"> * - Overwrites the Y channel with the embedded watermark, merging with the original CbCr.</span></span>
<span class="line"><span style="color: #6a9955;"> * - Saves the final image as JPEG with a specified quality.</span></span>
<span class="line"><span style="color: #6a9955;"> */</span></span>
<span class="line"><span style="color: #569cd6;">final</span> <span style="color: #569cd6;">class</span> <span style="color: #4ec9b0;">ImageIO</span></span>
<span class="line"><span style="color: #d4d4d4;">{</span></span>
<span class="line">    <span style="color: #569cd6;">private</span> <span style="color: #569cd6;">const</span><span style="color: #d4d4d4;"> BLOCK_SIZE = </span><span style="color: #b5cea8;">8</span><span style="color: #d4d4d4;">;</span></span>
<span class="line">    <span style="color: #569cd6;">private</span> <span style="color: #569cd6;">const</span><span style="color: #d4d4d4;"> MIN_WIDTH = </span><span style="color: #b5cea8;">200</span><span style="color: #d4d4d4;">;</span></span>
<span class="line">    <span style="color: #569cd6;">private</span> <span style="color: #569cd6;">const</span><span style="color: #d4d4d4;"> MIN_HEIGHT = </span><span style="color: #b5cea8;">200</span><span style="color: #d4d4d4;">;</span></span>

<span class="line">    <span style="color: #6a9955;">/**</span></span>
<span class="line"><span style="color: #6a9955;">     * Embed a watermark in a JPEG image and save the result.</span></span>
<span class="line"><span style="color: #6a9955;">     *</span></span>
<span class="line"><span style="color: #6a9955;">     * </span><span style="color: #569cd6;">@param</span> <span style="color: #569cd6;">string</span><span style="color: #6a9955;"> $src      Path to the original JPEG</span></span>
<span class="line"><span style="color: #6a9955;">     * </span><span style="color: #569cd6;">@param</span> <span style="color: #569cd6;">string</span><span style="color: #6a9955;"> $dest     Output path for the watermarked image</span></span>
<span class="line"><span style="color: #6a9955;">     * </span><span style="color: #569cd6;">@param</span> <span style="color: #569cd6;">string</span><span style="color: #6a9955;"> $message  ASCII watermark (up to 10 chars)</span></span>
<span class="line"><span style="color: #6a9955;">     * </span><span style="color: #569cd6;">@param</span> <span style="color: #569cd6;">int</span><span style="color: #6a9955;">    $quality  JPEG quality in [0..100], default 90</span></span>
<span class="line"><span style="color: #6a9955;">     */</span></span>
<span class="line">    <span style="color: #569cd6;">public</span> <span style="color: #569cd6;">static</span> <span style="color: #569cd6;">function</span> <span style="color: #dcdcaa;">embed</span><span style="color: #d4d4d4;">(</span></span>
<span class="line">        <span style="color: #569cd6;">string</span> <span style="color: #9cdcfe;">$src</span><span style="color: #d4d4d4;">,</span></span>
<span class="line">        <span style="color: #569cd6;">string</span> <span style="color: #9cdcfe;">$dest</span><span style="color: #d4d4d4;">,</span></span>
<span class="line">        <span style="color: #569cd6;">string</span> <span style="color: #9cdcfe;">$message</span><span style="color: #d4d4d4;">,</span></span>
<span class="line">        <span style="color: #569cd6;">int</span> <span style="color: #9cdcfe;">$quality</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">90</span></span>
<span class="line"><span style="color: #d4d4d4;">    ): </span><span style="color: #569cd6;">void</span><span style="color: #d4d4d4;"> {</span></span>
<span class="line">        <span style="color: #9cdcfe;">$im</span><span style="color: #d4d4d4;"> = </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::</span><span style="color: #dcdcaa;">loadJpeg</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$src</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">        <span style="color: #9cdcfe;">$w</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">imagesx</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$im</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">        <span style="color: #9cdcfe;">$h</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">imagesy</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$im</span><span style="color: #d4d4d4;">);</span></span>

<span class="line">        <span style="color: #6a9955;">// Ensure a minimum image size</span></span>
<span class="line">        <span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$w</span><span style="color: #d4d4d4;"> &lt; </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::MIN_WIDTH || </span><span style="color: #9cdcfe;">$h</span><span style="color: #d4d4d4;"> &lt; </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::MIN_HEIGHT) {</span></span>
<span class="line">            <span style="color: #dcdcaa;">imagedestroy</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$im</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">            <span style="color: #c586c0;">throw</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">RuntimeException</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"Image must be at least 200x200 for robust embedding."</span><span style="color: #d4d4d4;">);</span></span>
<span class="line"><span style="color: #d4d4d4;">        }</span></span>

<span class="line">        <span style="color: #6a9955;">// Convert RGB → Y</span></span>
<span class="line">        <span style="color: #9cdcfe;">$Y</span><span style="color: #d4d4d4;"> = </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::</span><span style="color: #dcdcaa;">rgbToY</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$im</span><span style="color: #d4d4d4;">);</span></span>

<span class="line">        <span style="color: #6a9955;">// Split into 8x8 blocks, apply forward DCT</span></span>
<span class="line">        <span style="color: #9cdcfe;">$blocks</span><span style="color: #d4d4d4;"> = </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::</span><span style="color: #dcdcaa;">forwardBlocks</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$Y</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$w</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$h</span><span style="color: #d4d4d4;">);</span></span>

<span class="line">        <span style="color: #6a9955;">// Embed the message into the DCT coefficients</span></span>
<span class="line">        <span style="color: #4ec9b0;">Watermarker</span><span style="color: #d4d4d4;">::</span><span style="color: #dcdcaa;">embed</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$blocks</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$message</span><span style="color: #d4d4d4;">);</span></span>

<span class="line">        <span style="color: #6a9955;">// Reconstruct the Y channel from the inverse DCT</span></span>
<span class="line">        <span style="color: #9cdcfe;">$Ywm</span><span style="color: #d4d4d4;"> = </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::</span><span style="color: #dcdcaa;">inverseBlocks</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$blocks</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$w</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$h</span><span style="color: #d4d4d4;">);</span></span>

<span class="line">        <span style="color: #6a9955;">// Merge the modified Y channel and save as JPEG</span></span>
<span class="line">        <span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::</span><span style="color: #dcdcaa;">mergeAndSave</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$im</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$Ywm</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$dest</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$quality</span><span style="color: #d4d4d4;">);</span></span>

<span class="line">        <span style="color: #dcdcaa;">imagedestroy</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$im</span><span style="color: #d4d4d4;">);</span></span>
<span class="line"><span style="color: #d4d4d4;">    }</span></span>

<span class="line">    <span style="color: #6a9955;">/**</span></span>
<span class="line"><span style="color: #6a9955;">     * Extract the watermark from a JPEG image.</span></span>
<span class="line"><span style="color: #6a9955;">     *</span></span>
<span class="line"><span style="color: #6a9955;">     * </span><span style="color: #569cd6;">@param</span> <span style="color: #569cd6;">string</span><span style="color: #6a9955;"> $src Path to the watermarked JPEG</span></span>
<span class="line"><span style="color: #6a9955;">     * </span><span style="color: #569cd6;">@return</span> <span style="color: #569cd6;">string</span><span style="color: #6a9955;">     The recovered ASCII message</span></span>
<span class="line"><span style="color: #6a9955;">     */</span></span>
<span class="line">    <span style="color: #569cd6;">public</span> <span style="color: #569cd6;">static</span> <span style="color: #569cd6;">function</span> <span style="color: #dcdcaa;">extract</span><span style="color: #d4d4d4;">(</span><span style="color: #569cd6;">string</span> <span style="color: #9cdcfe;">$src</span><span style="color: #d4d4d4;">): </span><span style="color: #569cd6;">string</span></span>
<span class="line"><span style="color: #d4d4d4;">    {</span></span>
<span class="line">        <span style="color: #9cdcfe;">$im</span><span style="color: #d4d4d4;"> = </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::</span><span style="color: #dcdcaa;">loadJpeg</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$src</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">        <span style="color: #9cdcfe;">$w</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">imagesx</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$im</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">        <span style="color: #9cdcfe;">$h</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">imagesy</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$im</span><span style="color: #d4d4d4;">);</span></span>

<span class="line">        <span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$w</span><span style="color: #d4d4d4;"> &lt; </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::MIN_WIDTH || </span><span style="color: #9cdcfe;">$h</span><span style="color: #d4d4d4;"> &lt; </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::MIN_HEIGHT) {</span></span>
<span class="line">            <span style="color: #dcdcaa;">imagedestroy</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$im</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">            <span style="color: #c586c0;">throw</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">RuntimeException</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"Image is too small (&lt;200x200). It may not contain a robust watermark."</span><span style="color: #d4d4d4;">);</span></span>
<span class="line"><span style="color: #d4d4d4;">        }</span></span>

<span class="line">        <span style="color: #6a9955;">// Convert to Y</span></span>
<span class="line">        <span style="color: #9cdcfe;">$Y</span><span style="color: #d4d4d4;"> = </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::</span><span style="color: #dcdcaa;">rgbToY</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$im</span><span style="color: #d4d4d4;">);</span></span>

<span class="line">        <span style="color: #6a9955;">// Apply DCT on 8x8 blocks</span></span>
<span class="line">        <span style="color: #9cdcfe;">$blocks</span><span style="color: #d4d4d4;"> = </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::</span><span style="color: #dcdcaa;">forwardBlocks</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$Y</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$w</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$h</span><span style="color: #d4d4d4;">);</span></span>

<span class="line">        <span style="color: #6a9955;">// Extract the message from the DCT blocks</span></span>
<span class="line">        <span style="color: #9cdcfe;">$msg</span><span style="color: #d4d4d4;"> = </span><span style="color: #4ec9b0;">Watermarker</span><span style="color: #d4d4d4;">::</span><span style="color: #dcdcaa;">extract</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$blocks</span><span style="color: #d4d4d4;">);</span></span>

<span class="line">        <span style="color: #dcdcaa;">imagedestroy</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$im</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">        <span style="color: #c586c0;">return</span> <span style="color: #9cdcfe;">$msg</span><span style="color: #d4d4d4;">;</span></span>
<span class="line"><span style="color: #d4d4d4;">    }</span></span>

<span class="line">    <span style="color: #6a9955;">/* -------------------------------------------------------------------------</span></span>
<span class="line"><span style="color: #6a9955;">       INTERNAL METHODS</span></span>
<span class="line"><span style="color: #6a9955;">    -------------------------------------------------------------------------- */</span></span>

<span class="line">    <span style="color: #6a9955;">/**</span></span>
<span class="line"><span style="color: #6a9955;">     * Loads a JPEG image from disk or throws an exception if invalid.</span></span>
<span class="line"><span style="color: #6a9955;">     */</span></span>
<span class="line">    <span style="color: #569cd6;">public</span> <span style="color: #569cd6;">static</span> <span style="color: #569cd6;">function</span> <span style="color: #dcdcaa;">loadJpeg</span><span style="color: #d4d4d4;">(</span><span style="color: #569cd6;">string</span> <span style="color: #9cdcfe;">$path</span><span style="color: #d4d4d4;">): </span><span style="color: #4ec9b0;">GdImage</span></span>
<span class="line"><span style="color: #d4d4d4;">    {</span></span>
<span class="line">        <span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> (!</span><span style="color: #dcdcaa;">is_file</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$path</span><span style="color: #d4d4d4;">)) {</span></span>
<span class="line">            <span style="color: #c586c0;">throw</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">RuntimeException</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"File not found: </span><span style="color: #9cdcfe;">$path</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">);</span></span>
<span class="line"><span style="color: #d4d4d4;">        }</span></span>
<span class="line">        <span style="color: #9cdcfe;">$im</span><span style="color: #d4d4d4;"> = @</span><span style="color: #dcdcaa;">imagecreatefromjpeg</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$path</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">        <span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> (!</span><span style="color: #9cdcfe;">$im</span><span style="color: #d4d4d4;">) {</span></span>
<span class="line">            <span style="color: #c586c0;">throw</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">RuntimeException</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">"Invalid JPEG: </span><span style="color: #9cdcfe;">$path</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">);</span></span>
<span class="line"><span style="color: #d4d4d4;">        }</span></span>

<span class="line">        <span style="color: #c586c0;">return</span> <span style="color: #9cdcfe;">$im</span><span style="color: #d4d4d4;">;</span></span>
<span class="line"><span style="color: #d4d4d4;">    }</span></span>

<span class="line">    <span style="color: #6a9955;">/**</span></span>
<span class="line"><span style="color: #6a9955;">     * Converts an RGB image to a 2D Y (luminance) matrix.</span></span>
<span class="line"><span style="color: #6a9955;">     */</span></span>
<span class="line">    <span style="color: #569cd6;">public</span> <span style="color: #569cd6;">static</span> <span style="color: #569cd6;">function</span> <span style="color: #dcdcaa;">rgbToY</span><span style="color: #d4d4d4;">(</span><span style="color: #4ec9b0;">GdImage</span> <span style="color: #9cdcfe;">$im</span><span style="color: #d4d4d4;">): </span><span style="color: #569cd6;">array</span></span>
<span class="line"><span style="color: #d4d4d4;">    {</span></span>
<span class="line">        <span style="color: #9cdcfe;">$w</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">imagesx</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$im</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">        <span style="color: #9cdcfe;">$h</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">imagesy</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$im</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">        <span style="color: #9cdcfe;">$Y</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">array_fill</span><span style="color: #d4d4d4;">(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$h</span><span style="color: #d4d4d4;">, </span><span style="color: #dcdcaa;">array_fill</span><span style="color: #d4d4d4;">(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$w</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">));</span></span>

<span class="line">        <span style="color: #c586c0;">for</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$y</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$y</span><span style="color: #d4d4d4;"> &lt; </span><span style="color: #9cdcfe;">$h</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$y</span><span style="color: #d4d4d4;">++) {</span></span>
<span class="line">            <span style="color: #c586c0;">for</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$x</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$x</span><span style="color: #d4d4d4;"> &lt; </span><span style="color: #9cdcfe;">$w</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$x</span><span style="color: #d4d4d4;">++) {</span></span>
<span class="line">                <span style="color: #9cdcfe;">$rgb</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">imagecolorat</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$im</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$x</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$y</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">                <span style="color: #9cdcfe;">$r</span><span style="color: #d4d4d4;"> = (</span><span style="color: #9cdcfe;">$rgb</span><span style="color: #d4d4d4;"> &gt;&gt; </span><span style="color: #b5cea8;">16</span><span style="color: #d4d4d4;">) &amp; </span><span style="color: #b5cea8;">0xFF</span><span style="color: #d4d4d4;">;</span></span>
<span class="line">                <span style="color: #9cdcfe;">$g</span><span style="color: #d4d4d4;"> = (</span><span style="color: #9cdcfe;">$rgb</span><span style="color: #d4d4d4;"> &gt;&gt; </span><span style="color: #b5cea8;">8</span><span style="color: #d4d4d4;">) &amp; </span><span style="color: #b5cea8;">0xFF</span><span style="color: #d4d4d4;">;</span></span>
<span class="line">                <span style="color: #9cdcfe;">$b</span><span style="color: #d4d4d4;"> = </span><span style="color: #9cdcfe;">$rgb</span><span style="color: #d4d4d4;"> &amp; </span><span style="color: #b5cea8;">0xFF</span><span style="color: #d4d4d4;">;</span></span>
<span class="line">                <span style="color: #6a9955;">// ITU-R BT.601 luma formula</span></span>
<span class="line">                <span style="color: #9cdcfe;">$luma</span><span style="color: #d4d4d4;"> = (</span><span style="color: #569cd6;">int</span><span style="color: #d4d4d4;">) </span><span style="color: #dcdcaa;">round</span><span style="color: #d4d4d4;">(</span><span style="color: #b5cea8;">0.299</span><span style="color: #d4d4d4;"> * </span><span style="color: #9cdcfe;">$r</span><span style="color: #d4d4d4;"> + </span><span style="color: #b5cea8;">0.587</span><span style="color: #d4d4d4;"> * </span><span style="color: #9cdcfe;">$g</span><span style="color: #d4d4d4;"> + </span><span style="color: #b5cea8;">0.114</span><span style="color: #d4d4d4;"> * </span><span style="color: #9cdcfe;">$b</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">                <span style="color: #9cdcfe;">$Y</span><span style="color: #d4d4d4;">[</span><span style="color: #9cdcfe;">$y</span><span style="color: #d4d4d4;">][</span><span style="color: #9cdcfe;">$x</span><span style="color: #d4d4d4;">] = </span><span style="color: #dcdcaa;">max</span><span style="color: #d4d4d4;">(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #dcdcaa;">min</span><span style="color: #d4d4d4;">(</span><span style="color: #b5cea8;">255</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$luma</span><span style="color: #d4d4d4;">));</span></span>
<span class="line"><span style="color: #d4d4d4;">            }</span></span>
<span class="line"><span style="color: #d4d4d4;">        }</span></span>

<span class="line">        <span style="color: #c586c0;">return</span> <span style="color: #9cdcfe;">$Y</span><span style="color: #d4d4d4;">;</span></span>
<span class="line"><span style="color: #d4d4d4;">    }</span></span>

<span class="line">    <span style="color: #6a9955;">/**</span></span>
<span class="line"><span style="color: #6a9955;">     * Splits the Y matrix into 8x8 blocks and applies forward DCT.</span></span>
<span class="line"><span style="color: #6a9955;">     */</span></span>
<span class="line">    <span style="color: #569cd6;">public</span> <span style="color: #569cd6;">static</span> <span style="color: #569cd6;">function</span> <span style="color: #dcdcaa;">forwardBlocks</span><span style="color: #d4d4d4;">(</span><span style="color: #569cd6;">array</span> <span style="color: #9cdcfe;">$Y</span><span style="color: #d4d4d4;">, </span><span style="color: #569cd6;">int</span> <span style="color: #9cdcfe;">$w</span><span style="color: #d4d4d4;">, </span><span style="color: #569cd6;">int</span> <span style="color: #9cdcfe;">$h</span><span style="color: #d4d4d4;">): </span><span style="color: #569cd6;">array</span></span>
<span class="line"><span style="color: #d4d4d4;">    {</span></span>
<span class="line">        <span style="color: #9cdcfe;">$blocks</span><span style="color: #d4d4d4;"> = [];</span></span>
<span class="line">        <span style="color: #c586c0;">for</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$by</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$by</span><span style="color: #d4d4d4;"> &lt; </span><span style="color: #9cdcfe;">$h</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$by</span><span style="color: #d4d4d4;"> += </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::BLOCK_SIZE) {</span></span>
<span class="line">            <span style="color: #c586c0;">for</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$bx</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$bx</span><span style="color: #d4d4d4;"> &lt; </span><span style="color: #9cdcfe;">$w</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$bx</span><span style="color: #d4d4d4;"> += </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::BLOCK_SIZE) {</span></span>
<span class="line">                <span style="color: #9cdcfe;">$block</span><span style="color: #d4d4d4;"> = [];</span></span>
<span class="line">                <span style="color: #c586c0;">for</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$i</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$i</span><span style="color: #d4d4d4;"> &lt; </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::BLOCK_SIZE; </span><span style="color: #9cdcfe;">$i</span><span style="color: #d4d4d4;">++) {</span></span>
<span class="line">                    <span style="color: #9cdcfe;">$row</span><span style="color: #d4d4d4;"> = [];</span></span>
<span class="line">                    <span style="color: #9cdcfe;">$srcY</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">min</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$by</span><span style="color: #d4d4d4;"> + </span><span style="color: #9cdcfe;">$i</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$h</span><span style="color: #d4d4d4;"> - </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">                    <span style="color: #c586c0;">for</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$j</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$j</span><span style="color: #d4d4d4;"> &lt; </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::BLOCK_SIZE; </span><span style="color: #9cdcfe;">$j</span><span style="color: #d4d4d4;">++) {</span></span>
<span class="line">                        <span style="color: #9cdcfe;">$srcX</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">min</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$bx</span><span style="color: #d4d4d4;"> + </span><span style="color: #9cdcfe;">$j</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$w</span><span style="color: #d4d4d4;"> - </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">                        <span style="color: #9cdcfe;">$row</span><span style="color: #d4d4d4;">[] = </span><span style="color: #9cdcfe;">$Y</span><span style="color: #d4d4d4;">[</span><span style="color: #9cdcfe;">$srcY</span><span style="color: #d4d4d4;">][</span><span style="color: #9cdcfe;">$srcX</span><span style="color: #d4d4d4;">];</span></span>
<span class="line"><span style="color: #d4d4d4;">                    }</span></span>
<span class="line">                    <span style="color: #9cdcfe;">$block</span><span style="color: #d4d4d4;">[] = </span><span style="color: #9cdcfe;">$row</span><span style="color: #d4d4d4;">;</span></span>
<span class="line"><span style="color: #d4d4d4;">                }</span></span>
<span class="line">                <span style="color: #9cdcfe;">$blocks</span><span style="color: #d4d4d4;">[] = </span><span style="color: #4ec9b0;">Dct</span><span style="color: #d4d4d4;">::</span><span style="color: #dcdcaa;">forward</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$block</span><span style="color: #d4d4d4;">);</span></span>
<span class="line"><span style="color: #d4d4d4;">            }</span></span>
<span class="line"><span style="color: #d4d4d4;">        }</span></span>
<span class="line">        <span style="color: #c586c0;">return</span> <span style="color: #9cdcfe;">$blocks</span><span style="color: #d4d4d4;">;</span></span>
<span class="line"><span style="color: #d4d4d4;">    }</span></span>

<span class="line">    <span style="color: #6a9955;">/**</span></span>
<span class="line"><span style="color: #6a9955;">     * Applies inverse DCT to each 8x8 block and rebuilds the Y matrix.</span></span>
<span class="line"><span style="color: #6a9955;">     */</span></span>
<span class="line">    <span style="color: #569cd6;">private</span> <span style="color: #569cd6;">static</span> <span style="color: #569cd6;">function</span> <span style="color: #dcdcaa;">inverseBlocks</span><span style="color: #d4d4d4;">(</span><span style="color: #569cd6;">array</span> <span style="color: #9cdcfe;">$blocks</span><span style="color: #d4d4d4;">, </span><span style="color: #569cd6;">int</span> <span style="color: #9cdcfe;">$w</span><span style="color: #d4d4d4;">, </span><span style="color: #569cd6;">int</span> <span style="color: #9cdcfe;">$h</span><span style="color: #d4d4d4;">): </span><span style="color: #569cd6;">array</span></span>
<span class="line"><span style="color: #d4d4d4;">    {</span></span>
<span class="line">        <span style="color: #9cdcfe;">$Y</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">array_fill</span><span style="color: #d4d4d4;">(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$h</span><span style="color: #d4d4d4;">, </span><span style="color: #dcdcaa;">array_fill</span><span style="color: #d4d4d4;">(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$w</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">));</span></span>
<span class="line">        <span style="color: #9cdcfe;">$idx</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">;</span></span>

<span class="line">        <span style="color: #c586c0;">for</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$by</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$by</span><span style="color: #d4d4d4;"> &lt; </span><span style="color: #9cdcfe;">$h</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$by</span><span style="color: #d4d4d4;"> += </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::BLOCK_SIZE) {</span></span>
<span class="line">            <span style="color: #c586c0;">for</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$bx</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$bx</span><span style="color: #d4d4d4;"> &lt; </span><span style="color: #9cdcfe;">$w</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$bx</span><span style="color: #d4d4d4;"> += </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::BLOCK_SIZE) {</span></span>
<span class="line">                <span style="color: #9cdcfe;">$coeffBlock</span><span style="color: #d4d4d4;"> = </span><span style="color: #9cdcfe;">$blocks</span><span style="color: #d4d4d4;">[</span><span style="color: #9cdcfe;">$idx</span><span style="color: #d4d4d4;">++];</span></span>
<span class="line">                <span style="color: #9cdcfe;">$pixBlock</span><span style="color: #d4d4d4;"> = </span><span style="color: #4ec9b0;">Dct</span><span style="color: #d4d4d4;">::</span><span style="color: #dcdcaa;">inverse</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$coeffBlock</span><span style="color: #d4d4d4;">);</span></span>

<span class="line">                <span style="color: #c586c0;">for</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$i</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$i</span><span style="color: #d4d4d4;"> &lt; </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::BLOCK_SIZE; </span><span style="color: #9cdcfe;">$i</span><span style="color: #d4d4d4;">++) {</span></span>
<span class="line">                    <span style="color: #9cdcfe;">$dstY</span><span style="color: #d4d4d4;"> = </span><span style="color: #9cdcfe;">$by</span><span style="color: #d4d4d4;"> + </span><span style="color: #9cdcfe;">$i</span><span style="color: #d4d4d4;">;</span></span>
<span class="line">                    <span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$dstY</span><span style="color: #d4d4d4;"> &gt;= </span><span style="color: #9cdcfe;">$h</span><span style="color: #d4d4d4;">) </span><span style="color: #c586c0;">break</span><span style="color: #d4d4d4;">;</span></span>
<span class="line">                    <span style="color: #c586c0;">for</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$j</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$j</span><span style="color: #d4d4d4;"> &lt; </span><span style="color: #569cd6;">self</span><span style="color: #d4d4d4;">::BLOCK_SIZE; </span><span style="color: #9cdcfe;">$j</span><span style="color: #d4d4d4;">++) {</span></span>
<span class="line">                        <span style="color: #9cdcfe;">$dstX</span><span style="color: #d4d4d4;"> = </span><span style="color: #9cdcfe;">$bx</span><span style="color: #d4d4d4;"> + </span><span style="color: #9cdcfe;">$j</span><span style="color: #d4d4d4;">;</span></span>
<span class="line">                        <span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$dstX</span><span style="color: #d4d4d4;"> &gt;= </span><span style="color: #9cdcfe;">$w</span><span style="color: #d4d4d4;">) </span><span style="color: #c586c0;">break</span><span style="color: #d4d4d4;">;</span></span>
<span class="line">                        <span style="color: #9cdcfe;">$Y</span><span style="color: #d4d4d4;">[</span><span style="color: #9cdcfe;">$dstY</span><span style="color: #d4d4d4;">][</span><span style="color: #9cdcfe;">$dstX</span><span style="color: #d4d4d4;">] = </span><span style="color: #9cdcfe;">$pixBlock</span><span style="color: #d4d4d4;">[</span><span style="color: #9cdcfe;">$i</span><span style="color: #d4d4d4;">][</span><span style="color: #9cdcfe;">$j</span><span style="color: #d4d4d4;">];</span></span>
<span class="line"><span style="color: #d4d4d4;">                    }</span></span>
<span class="line"><span style="color: #d4d4d4;">                }</span></span>
<span class="line"><span style="color: #d4d4d4;">            }</span></span>
<span class="line"><span style="color: #d4d4d4;">        }</span></span>

<span class="line">        <span style="color: #c586c0;">return</span> <span style="color: #9cdcfe;">$Y</span><span style="color: #d4d4d4;">;</span></span>
<span class="line"><span style="color: #d4d4d4;">    }</span></span>

<span class="line">    <span style="color: #6a9955;">/**</span></span>
<span class="line"><span style="color: #6a9955;">     * Merges the watermarked Y channel with the original CbCr, then saves the final JPEG.</span></span>
<span class="line"><span style="color: #6a9955;">     */</span></span>
<span class="line">    <span style="color: #569cd6;">private</span> <span style="color: #569cd6;">static</span> <span style="color: #569cd6;">function</span> <span style="color: #dcdcaa;">mergeAndSave</span><span style="color: #d4d4d4;">(</span><span style="color: #4ec9b0;">GdImage</span> <span style="color: #9cdcfe;">$orig</span><span style="color: #d4d4d4;">, </span><span style="color: #569cd6;">array</span> <span style="color: #9cdcfe;">$Ywm</span><span style="color: #d4d4d4;">, </span><span style="color: #569cd6;">string</span> <span style="color: #9cdcfe;">$dest</span><span style="color: #d4d4d4;">, </span><span style="color: #569cd6;">int</span> <span style="color: #9cdcfe;">$quality</span><span style="color: #d4d4d4;">): </span><span style="color: #569cd6;">void</span></span>
<span class="line"><span style="color: #d4d4d4;">    {</span></span>
<span class="line">        <span style="color: #9cdcfe;">$w</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">imagesx</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$orig</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">        <span style="color: #9cdcfe;">$h</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">imagesy</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$orig</span><span style="color: #d4d4d4;">);</span></span>

<span class="line">        <span style="color: #6a9955;">// 1) Overwrite the RGB pixels of the GDImage using the new Y</span></span>
<span class="line">        <span style="color: #c586c0;">for</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$y</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$y</span><span style="color: #d4d4d4;"> &lt; </span><span style="color: #9cdcfe;">$h</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$y</span><span style="color: #d4d4d4;">++) {</span></span>
<span class="line">            <span style="color: #c586c0;">for</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$x</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$x</span><span style="color: #d4d4d4;"> &lt; </span><span style="color: #9cdcfe;">$w</span><span style="color: #d4d4d4;">; </span><span style="color: #9cdcfe;">$x</span><span style="color: #d4d4d4;">++) {</span></span>
<span class="line">                <span style="color: #9cdcfe;">$rgb</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">imagecolorat</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$orig</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$x</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$y</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">                <span style="color: #9cdcfe;">$r</span><span style="color: #d4d4d4;"> = (</span><span style="color: #9cdcfe;">$rgb</span><span style="color: #d4d4d4;"> &gt;&gt; </span><span style="color: #b5cea8;">16</span><span style="color: #d4d4d4;">) &amp; </span><span style="color: #b5cea8;">0xFF</span><span style="color: #d4d4d4;">;</span></span>
<span class="line">                <span style="color: #9cdcfe;">$g</span><span style="color: #d4d4d4;"> = (</span><span style="color: #9cdcfe;">$rgb</span><span style="color: #d4d4d4;"> &gt;&gt; </span><span style="color: #b5cea8;">8</span><span style="color: #d4d4d4;">) &amp; </span><span style="color: #b5cea8;">0xFF</span><span style="color: #d4d4d4;">;</span></span>
<span class="line">                <span style="color: #9cdcfe;">$b</span><span style="color: #d4d4d4;"> = </span><span style="color: #9cdcfe;">$rgb</span><span style="color: #d4d4d4;"> &amp; </span><span style="color: #b5cea8;">0xFF</span><span style="color: #d4d4d4;">;</span></span>

<span class="line">                <span style="color: #9cdcfe;">$cb</span><span style="color: #d4d4d4;"> = -</span><span style="color: #b5cea8;">0.169</span><span style="color: #d4d4d4;"> * </span><span style="color: #9cdcfe;">$r</span><span style="color: #d4d4d4;"> - </span><span style="color: #b5cea8;">0.331</span><span style="color: #d4d4d4;"> * </span><span style="color: #9cdcfe;">$g</span><span style="color: #d4d4d4;"> + </span><span style="color: #b5cea8;">0.5</span><span style="color: #d4d4d4;"> * </span><span style="color: #9cdcfe;">$b</span><span style="color: #d4d4d4;"> + </span><span style="color: #b5cea8;">128</span><span style="color: #d4d4d4;">;</span></span>
<span class="line">                <span style="color: #9cdcfe;">$cr</span><span style="color: #d4d4d4;"> = </span><span style="color: #b5cea8;">0.5</span><span style="color: #d4d4d4;"> * </span><span style="color: #9cdcfe;">$r</span><span style="color: #d4d4d4;"> - </span><span style="color: #b5cea8;">0.419</span><span style="color: #d4d4d4;"> * </span><span style="color: #9cdcfe;">$g</span><span style="color: #d4d4d4;"> - </span><span style="color: #b5cea8;">0.081</span><span style="color: #d4d4d4;"> * </span><span style="color: #9cdcfe;">$b</span><span style="color: #d4d4d4;"> + </span><span style="color: #b5cea8;">128</span><span style="color: #d4d4d4;">;</span></span>

<span class="line">                <span style="color: #9cdcfe;">$Yval</span><span style="color: #d4d4d4;"> = </span><span style="color: #9cdcfe;">$Ywm</span><span style="color: #d4d4d4;">[</span><span style="color: #9cdcfe;">$y</span><span style="color: #d4d4d4;">][</span><span style="color: #9cdcfe;">$x</span><span style="color: #d4d4d4;">];</span></span>

<span class="line">                <span style="color: #9cdcfe;">$R</span><span style="color: #d4d4d4;"> = </span><span style="color: #9cdcfe;">$Yval</span><span style="color: #d4d4d4;"> + </span><span style="color: #b5cea8;">1.402</span><span style="color: #d4d4d4;"> * (</span><span style="color: #9cdcfe;">$cr</span><span style="color: #d4d4d4;"> - </span><span style="color: #b5cea8;">128</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">                <span style="color: #9cdcfe;">$G</span><span style="color: #d4d4d4;"> = </span><span style="color: #9cdcfe;">$Yval</span><span style="color: #d4d4d4;"> - </span><span style="color: #b5cea8;">0.344136</span><span style="color: #d4d4d4;"> * (</span><span style="color: #9cdcfe;">$cb</span><span style="color: #d4d4d4;"> - </span><span style="color: #b5cea8;">128</span><span style="color: #d4d4d4;">) - </span><span style="color: #b5cea8;">0.714136</span><span style="color: #d4d4d4;"> * (</span><span style="color: #9cdcfe;">$cr</span><span style="color: #d4d4d4;"> - </span><span style="color: #b5cea8;">128</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">                <span style="color: #9cdcfe;">$B</span><span style="color: #d4d4d4;"> = </span><span style="color: #9cdcfe;">$Yval</span><span style="color: #d4d4d4;"> + </span><span style="color: #b5cea8;">1.772</span><span style="color: #d4d4d4;"> * (</span><span style="color: #9cdcfe;">$cb</span><span style="color: #d4d4d4;"> - </span><span style="color: #b5cea8;">128</span><span style="color: #d4d4d4;">);</span></span>

<span class="line">                <span style="color: #9cdcfe;">$R</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">max</span><span style="color: #d4d4d4;">(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #dcdcaa;">min</span><span style="color: #d4d4d4;">(</span><span style="color: #b5cea8;">255</span><span style="color: #d4d4d4;">, (</span><span style="color: #569cd6;">int</span><span style="color: #d4d4d4;">) </span><span style="color: #dcdcaa;">round</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$R</span><span style="color: #d4d4d4;">)));</span></span>
<span class="line">                <span style="color: #9cdcfe;">$G</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">max</span><span style="color: #d4d4d4;">(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #dcdcaa;">min</span><span style="color: #d4d4d4;">(</span><span style="color: #b5cea8;">255</span><span style="color: #d4d4d4;">, (</span><span style="color: #569cd6;">int</span><span style="color: #d4d4d4;">) </span><span style="color: #dcdcaa;">round</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$G</span><span style="color: #d4d4d4;">)));</span></span>
<span class="line">                <span style="color: #9cdcfe;">$B</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">max</span><span style="color: #d4d4d4;">(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #dcdcaa;">min</span><span style="color: #d4d4d4;">(</span><span style="color: #b5cea8;">255</span><span style="color: #d4d4d4;">, (</span><span style="color: #569cd6;">int</span><span style="color: #d4d4d4;">) </span><span style="color: #dcdcaa;">round</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$B</span><span style="color: #d4d4d4;">)));</span></span>

<span class="line">                <span style="color: #9cdcfe;">$color</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">imagecolorallocate</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$orig</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$R</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$G</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$B</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">                <span style="color: #dcdcaa;">imagesetpixel</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$orig</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$x</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$y</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$color</span><span style="color: #d4d4d4;">);</span></span>
<span class="line"><span style="color: #d4d4d4;">            }</span></span>
<span class="line"><span style="color: #d4d4d4;">        }</span></span>

<span class="line">        <span style="color: #6a9955;">// 2) Convert GDImage to a PNG blob in memory (lossless)</span></span>
<span class="line">        <span style="color: #dcdcaa;">ob_start</span><span style="color: #d4d4d4;">();</span></span>
<span class="line">        <span style="color: #dcdcaa;">imagepng</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$orig</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">        <span style="color: #9cdcfe;">$pngData</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">ob_get_clean</span><span style="color: #d4d4d4;">();</span></span>

<span class="line">        <span style="color: #6a9955;">// 3) Create an Imagick object from the PNG blob</span></span>
<span class="line">        <span style="color: #9cdcfe;">$imagick</span><span style="color: #d4d4d4;"> = </span><span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">Imagick</span><span style="color: #d4d4d4;">();</span></span>
<span class="line">        <span style="color: #9cdcfe;">$imagick</span><span style="color: #d4d4d4;">-&gt;</span><span style="color: #dcdcaa;">readImageBlob</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$pngData</span><span style="color: #d4d4d4;">);</span></span>

<span class="line">        <span style="color: #6a9955;">// 4) Set desired JPEG parameters</span></span>
<span class="line">        <span style="color: #9cdcfe;">$imagick</span><span style="color: #d4d4d4;">-&gt;</span><span style="color: #dcdcaa;">setOption</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">'jpeg:sampling-factor'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'1x1'</span><span style="color: #d4d4d4;">); </span><span style="color: #6a9955;">// disable chroma subsampling</span></span>
<span class="line">        <span style="color: #9cdcfe;">$imagick</span><span style="color: #d4d4d4;">-&gt;</span><span style="color: #dcdcaa;">setImageCompressionQuality</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$quality</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">        <span style="color: #9cdcfe;">$imagick</span><span style="color: #d4d4d4;">-&gt;</span><span style="color: #dcdcaa;">setImageCompression</span><span style="color: #d4d4d4;">(</span><span style="color: #4ec9b0;">Imagick</span><span style="color: #d4d4d4;">::COMPRESSION_JPEG);</span></span>
<span class="line">        <span style="color: #9cdcfe;">$imagick</span><span style="color: #d4d4d4;">-&gt;</span><span style="color: #dcdcaa;">setImageFormat</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">'jpeg'</span><span style="color: #d4d4d4;">);</span></span>

<span class="line">        <span style="color: #6a9955;">// 5) Save the file</span></span>
<span class="line">        <span style="color: #9cdcfe;">$imagick</span><span style="color: #d4d4d4;">-&gt;</span><span style="color: #dcdcaa;">writeImage</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$dest</span><span style="color: #d4d4d4;">);</span></span>

<span class="line">        <span style="color: #6a9955;">// 6) Cleanup</span></span>
<span class="line">        <span style="color: #9cdcfe;">$imagick</span><span style="color: #d4d4d4;">-&gt;</span><span style="color: #dcdcaa;">destroy</span><span style="color: #d4d4d4;">();</span></span>
<span class="line"><span style="color: #d4d4d4;">    }</span></span>

<span class="line">    <span style="color: #6a9955;">/**</span></span>
<span class="line"><span style="color: #6a9955;">     * Prevents instantiation.</span></span>
<span class="line"><span style="color: #6a9955;">     */</span></span>
<span class="line">    <span style="color: #569cd6;">private</span> <span style="color: #569cd6;">function</span> <span style="color: #dcdcaa;">__construct</span><span style="color: #d4d4d4;">()</span></span>
<span class="line"><span style="color: #d4d4d4;">    {</span></span>
<span class="line"><span style="color: #d4d4d4;">    }</span></span>
<span class="line"><span style="color: #d4d4d4;">}</span></span></code></pre>
</div>


<p>Quando si invoca il metodo statico embed, il codice apre l’immagine con l’estensione GD e ricava la componente Y di luminanza — l’unica coinvolta nel watermarking — lasciando invariati i canali cromatici Cb e Cr per garantire che la firma resti invisibile all’occhio umano. La matrice Y viene poi suddivisa, riga per riga, in una griglia di blocchi 8×8. Ciascun blocco viene trasformato in frequenza con Dct::forward, così da permettere alla classe Watermarker di incidere i bit del messaggio <strong>forzando il segno</strong> di 8 coefficienti a <strong>bassa frequenza</strong> scelti appositamente.</p>
<p>Terminata l’incisione, ImageIO riconverte ogni blocco in pixel con Dct::inverse, ricostruendo una nuova matrice Y marcata. A questo punto la classe ricalcola i valori RGB combinando la luminanza modificata con le crominanze originali: per ogni pixel applica le formule inverse dello spazio colore secondo lo standard ITU-R BT.601, assicurandosi che i risultati restino nell’intervallo 0–255.</p>
<p>Il bitmap completo viene infine salvato in JPEG alla qualità desiderata, utilizzando Imagick per disattivare il subsampling cromatico e massimizzare la fedeltà. Il risultato è un file visivamente identico all’originale ma contenente, nei suoi coefficienti DCT, una firma testuale invisibile ma resistente.</p>
<p>Il percorso inverso, affidato al metodo extract, compie l’operazione opposta: ricarica l’immagine, isola la luminanza, la suddivide in blocchi, calcola la DCT di ciascuno e passa la sequenza di coefficienti a Watermarker::extract. Questo metodo legge il <strong>segno</strong> dei coefficienti, applica il voto di maggioranza tra le ripetizioni di ciascun bit, e ricostruisce la stringa originaria fino al carattere NUL di terminazione.</p>
<p>Tutta la logica di I/O, apertura dei file, conversione colore e salvataggio JPEG è confinata nella classe ImageIO, mentre Dct e Watermarker restano funzioni matematiche pure. Questa divisione chiara delle responsabilità favorisce la modularità del codice, la testabilità e l’integrazione in qualunque pipeline di elaborazione immagini.</p>
<h2 class="wp-block-heading">Test in CLI</h2>
<h3 class="wp-block-heading">/embed.php</h3>
<p>Inserimento di un watermark in un&#8217;immagine</p>


<div class="wp-block-kevinbatdorf-code-block-pro" style="font-size: .875rem; font-family: Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace; line-height: 1.25rem; --cbp-tab-width: 2; tab-size: var(--cbp-tab-width, 2);" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono">
<pre class="shiki dark-plus" style="background-color: #1e1e1e;" tabindex="0"><code><span class="line"><span style="color: #6a9955;">#!/usr/bin/env php</span></span>
<span class="line"><span style="color: #d4d4d4;">&lt;?php</span></span>

<span class="line"><span style="color: #6a9955;">// Set memory limit to 1GB</span></span>
<span class="line"><span style="color: #dcdcaa;">ini_set</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">'memory_limit'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'1024M'</span><span style="color: #d4d4d4;">);</span></span>
<span class="line"><span style="color: #c586c0;">require</span> <span style="color: #569cd6;">__DIR__</span> <span style="color: #d4d4d4;">.</span> <span style="color: #ce9178;">'/vendor/autoload.php'</span><span style="color: #d4d4d4;">;</span></span>

<span class="line"><span style="color: #569cd6;">use</span><span style="color: #d4d4d4;"> RenorWatermark</span><span style="color: #4ec9b0;">ImageIO</span><span style="color: #d4d4d4;">;</span></span>

<span class="line"><span style="color: #6a9955;">/**</span></span>
<span class="line"><span style="color: #6a9955;"> * This command-line script embeds a watermark (up to 10 characters)</span></span>
<span class="line"><span style="color: #6a9955;"> * into a JPEG and saves the result. By default, it uses quality=90.</span></span>
<span class="line"><span style="color: #6a9955;"> *</span></span>
<span class="line"><span style="color: #6a9955;"> * Example usage:</span></span>
<span class="line"><span style="color: #6a9955;"> *   php embed.php --src=original.jpg --out=watermarked.jpg --msg="HELLO" --q=95</span></span>
<span class="line"><span style="color: #6a9955;"> *</span></span>
<span class="line"><span style="color: #6a9955;"> * Or simply:</span></span>
<span class="line"><span style="color: #6a9955;"> *   php embed.php --src=original.jpg --out=watermarked.jpg</span></span>
<span class="line"><span style="color: #6a9955;"> *   (defaults to msg="DEFAULT" and quality=90)</span></span>
<span class="line"><span style="color: #6a9955;"> */</span></span>

<span class="line"><span style="color: #9cdcfe;">$options</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">getopt</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">''</span><span style="color: #d4d4d4;">, [</span><span style="color: #ce9178;">'src:'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'out:'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'msg::'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'q::'</span><span style="color: #d4d4d4;">]);</span></span>
<span class="line"><span style="color: #9cdcfe;">$src</span><span style="color: #d4d4d4;"> = </span><span style="color: #9cdcfe;">$options</span><span style="color: #d4d4d4;">[</span><span style="color: #ce9178;">'src'</span><span style="color: #d4d4d4;">] ?? </span><span style="color: #569cd6;">null</span><span style="color: #d4d4d4;">;</span></span>
<span class="line"><span style="color: #9cdcfe;">$out</span><span style="color: #d4d4d4;"> = </span><span style="color: #9cdcfe;">$options</span><span style="color: #d4d4d4;">[</span><span style="color: #ce9178;">'out'</span><span style="color: #d4d4d4;">] ?? </span><span style="color: #569cd6;">null</span><span style="color: #d4d4d4;">;</span></span>
<span class="line"><span style="color: #9cdcfe;">$msg</span><span style="color: #d4d4d4;"> = </span><span style="color: #9cdcfe;">$options</span><span style="color: #d4d4d4;">[</span><span style="color: #ce9178;">'msg'</span><span style="color: #d4d4d4;">] ?? </span><span style="color: #ce9178;">'DEFAULT'</span><span style="color: #d4d4d4;">;</span></span>
<span class="line"><span style="color: #9cdcfe;">$q</span><span style="color: #d4d4d4;">   = </span><span style="color: #dcdcaa;">isset</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$options</span><span style="color: #d4d4d4;">[</span><span style="color: #ce9178;">'q'</span><span style="color: #d4d4d4;">]) ? (</span><span style="color: #569cd6;">int</span><span style="color: #d4d4d4;">) </span><span style="color: #9cdcfe;">$options</span><span style="color: #d4d4d4;">[</span><span style="color: #ce9178;">'q'</span><span style="color: #d4d4d4;">] : </span><span style="color: #b5cea8;">90</span><span style="color: #d4d4d4;">;</span></span>

<span class="line"><span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> (!</span><span style="color: #9cdcfe;">$src</span><span style="color: #d4d4d4;"> || !</span><span style="color: #9cdcfe;">$out</span><span style="color: #d4d4d4;">) {</span></span>
<span class="line">    <span style="color: #dcdcaa;">fwrite</span><span style="color: #d4d4d4;">(STDERR, </span><span style="color: #ce9178;">"Usage: embed --src=INPUT.jpg --out=OUTPUT.jpg [--msg=STRING] [--q=90]</span><span style="color: #d7ba7d;">n</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">    <span style="color: #c586c0;">exit</span><span style="color: #d4d4d4;">(</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">);</span></span>
<span class="line"><span style="color: #d4d4d4;">}</span></span>

<span class="line"><span style="color: #c586c0;">try</span><span style="color: #d4d4d4;"> {</span></span>
<span class="line">    <span style="color: #4ec9b0;">ImageIO</span><span style="color: #d4d4d4;">::</span><span style="color: #dcdcaa;">embed</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$src</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$out</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$msg</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">$q</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">    <span style="color: #dcdcaa;">echo</span> <span style="color: #ce9178;">"Watermark </span><span style="color: #d7ba7d;">"</span><span style="color: #9cdcfe;">$msg</span><span style="color: #d7ba7d;">"</span><span style="color: #ce9178;"> embedded successfully into </span><span style="color: #9cdcfe;">$out</span><span style="color: #ce9178;"> at quality </span><span style="color: #9cdcfe;">$q</span><span style="color: #d7ba7d;">n</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">;</span></span>
<span class="line"><span style="color: #d4d4d4;">} </span><span style="color: #c586c0;">catch</span><span style="color: #d4d4d4;"> (</span><span style="color: #4ec9b0;">Exception</span> <span style="color: #9cdcfe;">$e</span><span style="color: #d4d4d4;">) {</span></span>
<span class="line">    <span style="color: #dcdcaa;">fwrite</span><span style="color: #d4d4d4;">(STDERR, </span><span style="color: #ce9178;">"Error: "</span> <span style="color: #d4d4d4;">.</span> <span style="color: #9cdcfe;">$e</span><span style="color: #d4d4d4;">-&gt;</span><span style="color: #dcdcaa;">getMessage</span><span style="color: #d4d4d4;">() </span><span style="color: #d4d4d4;">.</span> <span style="color: #ce9178;">"</span><span style="color: #d7ba7d;">n</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">    <span style="color: #c586c0;">exit</span><span style="color: #d4d4d4;">(</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">);</span></span>
<span class="line"><span style="color: #d4d4d4;">}</span></span></code></pre>
</div>


<p>In questo script CLI (embed.php) definiamo, attraverso l’opzione &#8211;msg, il messaggio da imprimere nell’immagine e invochiamo il metodo statico ImageIO::embed() della classe ImageIO. Lo script accetta i seguenti parametri:</p>
<p>&#8211;q (facoltativo): la qualità JPEG per il salvataggio (default: 90).</p>
<p>&#8211;src: il percorso dell’immagine JPEG sorgente (es. original.jpg);</p>
<p>&#8211;out: il percorso del file JPEG di destinazione che conterrà il watermark;</p>
<p>&#8211;msg (facoltativo): il testo ASCII da inserire (max 10 caratteri);</p>


<pre class="wp-block-code"><code>php embed.php --src=original.jpg --out=watermarked.jpg --msg="RENOR" --q=95</code></pre>


<p>ci tornerà:</p>


<pre class="wp-block-code"><code>Watermark "RENOR" embedded successfully into watermarked.jpg at quality 95</code></pre>


<h2 class="wp-block-heading">/extract.php</h2>
<p>Da qui estraiamo il testo del Watermark contenuto nell&#8217;immagine watermarked.jpg appena creata da embed.php</p>


<div class="wp-block-kevinbatdorf-code-block-pro" style="font-size: .875rem; font-family: Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace; line-height: 1.25rem; --cbp-tab-width: 2; tab-size: var(--cbp-tab-width, 2);" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono">
<pre class="shiki dark-plus" style="background-color: #1e1e1e;" tabindex="0"><code><span class="line"><span style="color: #6a9955;">#!/usr/bin/env php</span></span>
<span class="line"><span style="color: #d4d4d4;">&lt;?php</span></span>

<span class="line"><span style="color: #6a9955;">// Set memory limit to 1GB</span></span>
<span class="line"><span style="color: #dcdcaa;">ini_set</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">'memory_limit'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'1024M'</span><span style="color: #d4d4d4;">);</span></span>
<span class="line"><span style="color: #c586c0;">require</span> <span style="color: #569cd6;">__DIR__</span> <span style="color: #d4d4d4;">.</span> <span style="color: #ce9178;">'/vendor/autoload.php'</span><span style="color: #d4d4d4;">;</span></span>

<span class="line"><span style="color: #569cd6;">use</span><span style="color: #d4d4d4;"> RenorWatermark</span><span style="color: #4ec9b0;">ImageIO</span><span style="color: #d4d4d4;">;</span></span>

<span class="line"><span style="color: #6a9955;">/**</span></span>
<span class="line"><span style="color: #6a9955;"> * This command-line script extracts a watermark from a JPEG.</span></span>
<span class="line"><span style="color: #6a9955;"> *</span></span>
<span class="line"><span style="color: #6a9955;"> * Usage:</span></span>
<span class="line"><span style="color: #6a9955;"> *   php extract.php --src=watermarked.jpg</span></span>
<span class="line"><span style="color: #6a9955;"> */</span></span>

<span class="line"><span style="color: #9cdcfe;">$options</span><span style="color: #d4d4d4;"> = </span><span style="color: #dcdcaa;">getopt</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">''</span><span style="color: #d4d4d4;">, [</span><span style="color: #ce9178;">'src:'</span><span style="color: #d4d4d4;">]);</span></span>
<span class="line"><span style="color: #9cdcfe;">$src</span><span style="color: #d4d4d4;"> = </span><span style="color: #9cdcfe;">$options</span><span style="color: #d4d4d4;">[</span><span style="color: #ce9178;">'src'</span><span style="color: #d4d4d4;">] ?? </span><span style="color: #569cd6;">null</span><span style="color: #d4d4d4;">;</span></span>

<span class="line"><span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> (!</span><span style="color: #9cdcfe;">$src</span><span style="color: #d4d4d4;">) {</span></span>
<span class="line">    <span style="color: #dcdcaa;">fwrite</span><span style="color: #d4d4d4;">(STDERR, </span><span style="color: #ce9178;">"Usage: extract --src=IMAGE.jpg</span><span style="color: #d7ba7d;">n</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">    <span style="color: #c586c0;">exit</span><span style="color: #d4d4d4;">(</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">);</span></span>
<span class="line"><span style="color: #d4d4d4;">}</span></span>

<span class="line"><span style="color: #c586c0;">try</span><span style="color: #d4d4d4;"> {</span></span>
<span class="line">    <span style="color: #9cdcfe;">$msg</span><span style="color: #d4d4d4;"> = </span><span style="color: #4ec9b0;">ImageIO</span><span style="color: #d4d4d4;">::</span><span style="color: #dcdcaa;">extract</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">$src</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">    <span style="color: #dcdcaa;">echo</span> <span style="color: #ce9178;">"Recovered message: </span><span style="color: #d7ba7d;">"</span><span style="color: #9cdcfe;">$msg</span><span style="color: #d7ba7d;">"n</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">;</span></span>
<span class="line"><span style="color: #d4d4d4;">} </span><span style="color: #c586c0;">catch</span><span style="color: #d4d4d4;"> (</span><span style="color: #4ec9b0;">Exception</span> <span style="color: #9cdcfe;">$e</span><span style="color: #d4d4d4;">) {</span></span>
<span class="line">    <span style="color: #dcdcaa;">fwrite</span><span style="color: #d4d4d4;">(STDERR, </span><span style="color: #ce9178;">"Error: "</span> <span style="color: #d4d4d4;">.</span> <span style="color: #9cdcfe;">$e</span><span style="color: #d4d4d4;">-&gt;</span><span style="color: #dcdcaa;">getMessage</span><span style="color: #d4d4d4;">() </span><span style="color: #d4d4d4;">.</span> <span style="color: #ce9178;">"</span><span style="color: #d7ba7d;">n</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">);</span></span>
<span class="line">    <span style="color: #c586c0;">exit</span><span style="color: #d4d4d4;">(</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">);</span></span>
<span class="line"><span style="color: #d4d4d4;">}</span></span></code></pre>
</div>


<p>Lo script extract.php completa il ciclo di utilizzo del watermark invisibile permettendo l’estrazione del messaggio precedentemente incorporato. Si tratta di un semplice script da linea di comando che, dato un file JPEG marcato, ne legge il contenuto nascosto decodificando il segno di specifici coefficienti DCT modificati. Il messaggio, recuperato in modo robusto anche dopo compressioni JPEG con qualità medio-alta, viene ricostruito tramite voto di maggioranza sui bit ripetuti. Basta eseguire il comando con l’opzione &#8211;src per indicare l’immagine da analizzare, e lo script restituirà a terminale la stringa originariamente incorporata.</p>
<p>Possiamo landiare il comando:</p>


<pre class="wp-block-code"><code>php extract.php --src=watermarked.jpg  </code></pre>


<p>otterremo:</p>


<pre class="wp-block-code"><code>Recovered message: "RENOR"</code></pre>


<p>Vale il caso anche di analizzare le fotografie:</p>


<figure class="wp-block-image size-large"><img decoding="async" src="https://renor.it/wp-content/uploads/2025/05/test-1024x342.webp" alt="" class="wp-image-360"/></figure>


<p>A sinistra l&#8217;immagine originale, a destra quella watermarked.</p>
<p>Possiamo notare che effettivamente, come abbiamo ampiamente descritto non ci sono elementi visibili ad occhi nudo e riconducibili all&#8217;applicazione di un Watermark che resta tuttavia ancora decifrabile utilizzando l&#8217;extract. </p>
<p>Chiaramente il progetto è puramente dimostrativo ed utilizzarlo a livello enterprise o per progetti in produzione richiederebbe ulteriore raffinamento.</p>


<p><strong>[starbox]&nbsp;</strong></p>
<p>L'articolo <a href="https://renor.it/blog/sviluppo-software-programmazione/watermark-invisibile-in-jpeg-con-php-8/">Watermark Invisibile in JPEG con PHP 8</a> proviene da <a href="https://renor.it">RENOR &amp; Partners S.r.l.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Cosa diavolo cerchi? Un backend o un frontend?</title>
		<link>https://renor.it/blog/sviluppo-software-programmazione/cosa-diavolo-cerchi-un-backend-o-un-frontend/</link>
		
		<dc:creator><![CDATA[Simone Renzi]]></dc:creator>
		<pubDate>Wed, 12 Oct 2022 22:54:18 +0000</pubDate>
				<category><![CDATA[Sviluppo Software & Programmazione]]></category>
		<category><![CDATA[backend]]></category>
		<category><![CDATA[cosa cerchi]]></category>
		<category><![CDATA[cosa vuoi]]></category>
		<category><![CDATA[frontend]]></category>
		<category><![CDATA[fullstack]]></category>
		<guid isPermaLink="false">https://renor.it/?p=205</guid>

					<description><![CDATA[<p>Come al solito, entrerò a gamba tesa perché essere diretto è nelle mie corde&#8230; L&#8217;avrete intuito dal titolo ???? Partiamo dalla tipica domanda che si pongono ingegneri IT e sviluppatori quando leggono alcune offerte di lavoro: &#8220;Ma le aziende credono che sviluppatori e/o ingegneri in ambito IT siano tutti dei cretini? Oppure tutto questo è [&#8230;]</p>
<p>L'articolo <a href="https://renor.it/blog/sviluppo-software-programmazione/cosa-diavolo-cerchi-un-backend-o-un-frontend/">Cosa diavolo cerchi? Un backend o un frontend?</a> proviene da <a href="https://renor.it">RENOR &amp; Partners S.r.l.</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Come al solito, entrerò a gamba tesa perché essere diretto è nelle mie corde&#8230; L&#8217;avrete intuito dal titolo ????</p>
<p>Partiamo dalla tipica domanda che si pongono ingegneri IT e sviluppatori quando leggono alcune offerte di lavoro: &#8220;Ma le aziende credono che sviluppatori e/o ingegneri in ambito IT siano tutti dei cretini? Oppure tutto questo è dovuto al fatto che hanno personale ignorante come le capre, per usare un eufemismo alla Vittorio Sgarbi, tale per cui chi gli fa la selezione del personale non conosce la differenza tra un backend developer e un frontend developer?</p>
<p>Onestamente credo che la motivazione vada ricercata in altro&#8230; Perché poi la RAL offerta da pieno senso al ragionamento. E come dargli torto?</p>
<h2>Annunci assurdi</h2>
<p>Prendo un annuncio a caso fresco fresco ma vi garantisco che basta girare un po&#8217; per trovarne migliaia&#8230;</p>
<p class="t-24 t-bold jobs-unified-top-card__job-title">Titolo dell&#8217;annuncio: <strong>Sviluppatore PHP back-end</strong></p>
<blockquote><p><em><strong>REQUISITI RICHIESTI</strong></em></p>
<ul>
<li><em>Conoscenza dei principi dello sviluppo orientato agli oggetti</em></li>
<li><em>Esperienza nello sviluppo con il linguaggio PHP</em></li>
<li><em>Conoscenza dei principali framework</em></li>
<li><em>Conoscenza dei modelli e tool di versioning del software (GIT/SVN)</em></li>
<li><em>Conoscenza del linguaggio SQL</em></li>
<li><em>Conoscenza del web e dei suoi protocolli</em></li>
<li><em>Conoscenza di sistemi operativi quali Windows e Linux</em></li>
<li><em>Conoscenza di HTML, CSS, Javascript</em></li>
<li><em>Buona conoscenza della lingua inglese sia parlata che scritta</em></li>
<li><em>Comprovate capacità di problem solving relative al raggiungimento del risultato</em></li>
</ul>
<p><em><strong>REQUISITI PREFERENZIALI</strong></em></p>
<ul>
<li><em>Conoscenza dei principali CMS web: Drupal / WordPress / Magento /Adobe Commerce</em></li>
<li><em>Conoscenza ed esperienza nello sviluppo (full stack) di web applications</em></li>
<li><em>Certificazioni ottenute su prodotti enterprise. Esempio: Adobe Commerce</em></li>
<li><em>Esperienza in implementazione di pagamenti online e/o soluzioni e-commerce</em></li>
</ul>
</blockquote>
<h2>Confusione, ignoranza o paraculismo?</h2>
<p>Ora, anche un cieco qui vedrebbe che non stanno cercando uno sviluppatore Back-end, come suggerisce inequivocabilmente il titolo dell&#8217;annuncio&#8230; Qui si sta cercando un Full Stack e la cosa bella è che nei requisiti preferenziali lo asseriscono pure senza mezze misure!! &#8220;Conoscenza ed esperienza nello sviluppo (full stack) di Web Applications&#8221;. Oltre al fatto che in bella vista nei requisiti richiesti spunta chiara come l&#8217;acqua la conoscenza di HTML, CSS e JavaScript.</p>
<p>Personalmente non mi candiderei mai ad un annuncio del genere, perché lo ritengo un insulto alla mia intelligenza ma soprattutto a tutta la categoria.</p>
<p>Con tutto il rispetto possibile, queste non sono posizioni per cui si apre la ricerca a gente completamente ignorante dove puoi dargli a bere che gli asini volano. Queste sono posizioni dove chi si candida è una persona allenata ad utilizzare bene il cervello e non puoi pensare, con il fine ultimo di pagare una Ferrari 10 euro, di prendere in giro uno sviluppatore che al contrario di questi ciarlatani sa benissimo quali conoscenze comporti lo sviluppo front-end e quali il back-end; e cercare di mascherare il tutto facendo passare la ricerca di un Full Stack per un Back-end o di un Front-end per pagarlo di meno&#8230; Questo è un inganno ridicolo nel quale non cadrebbe neanche un bambino di 10 anni che deve finire!</p>
<p>Sarebbe più apprezzabile e onesto scrivere: &#8220;Cerchiamo uno sviluppatore Full Stack, offriamo questo compenso&#8221;.</p>
<p>In questo modo chiunque saprebbe quale figura cercano e, compatibilmente con l&#8217;offerta, potrebbe decidere se candidarsi oppure no, senza stare a perdere del tempo prezioso. In molti casi l&#8217;azienda non riceverebbe candidature, a quel punto forse, si renderebbe conto che è il caso di alzare il compenso!</p>
<h2>Offerte di lavoro di cui vergognarsi</h2>
<p>Per non parlare poi di tutte quelle offerte di lavoro dove richiedono competenze assurde che neanche chi lavora in NASA possiede a RAL ridicole&#8230; Ho letto annunci di aziende che cercavano competenze in ambito Back-end PHP, .net, JAVA, Python, sistemi di controllo del versioning (GIT, SVN, DevOps), sistemi di orchestrazione dei container (Kubernets, Docker), REST API e SOAP, vari framework PHP tra cui Symphony/Laravel, conoscenza degli ecosistemi e integrazione API dei servizi AWS, Google Cloud e Microsoft Azure. Per il front-end HTML, CSS, JavaScript, jQuery, React, Vue.js, Angular con requisito preferenziale nello sviluppo di App Ibride in Cordova, Flutter o Ionic&#8230; Il tutto per la modica cifra di 1200 euro signore e signori! Ma dico&#8230; Non vi vergognate neanche un po&#8217;? Mi viene da ridere ma qui ci sarebbe da piangere!</p>
<p>Non mi ci voglio mettere io di mezzo, che comunque in ambito IT non mi ritengo l&#8217;ultima ruota del carro, probabilmente la penultima ma non l&#8217;ultima, ma conosco personalmente delle figure con cui ho collaborato e collaboro, di altissimo livello in ambito informatico che forse arrivano con fatica ad avere neanche la metà di queste conoscenze&#8230; E per quali stipendi poi? 1200 euro al mese <a title="Face with Tears of Joy emoji" href="https://en.wikipedia.org/wiki/Face_with_Tears_of_Joy_emoji">????</a><a title="Face with Tears of Joy emoji" href="https://en.wikipedia.org/wiki/Face_with_Tears_of_Joy_emoji">????</a><a title="Face with Tears of Joy emoji" href="https://en.wikipedia.org/wiki/Face_with_Tears_of_Joy_emoji">????</a><a title="Face with Tears of Joy emoji" href="https://en.wikipedia.org/wiki/Face_with_Tears_of_Joy_emoji">????</a><a title="Face with Tears of Joy emoji" href="https://en.wikipedia.org/wiki/Face_with_Tears_of_Joy_emoji">????</a><a title="Face with Tears of Joy emoji" href="https://en.wikipedia.org/wiki/Face_with_Tears_of_Joy_emoji">????</a></p>
<p>Siete ridicoli!!! A tutti questi furbetti del quartierino, sfruttatori di persone in difficoltà dedico un sincero vaf@@@@lo.</p>
<h2>Le responsabilità degli imprenditori</h2>
<p>Io credo gli imprenditori debbano fermarsi a riflettere con grande senso autocritico ed entrare in un&#8217;ottica che ha a che fare con semplicissime leggi di mercato&#8230;</p>
<p>Ho un cugino che ha avuto il coraggio di abbandonare l&#8217;Italia ed è andato a lavorare in Francia. Cercavano una figura Front End: HTML, CSS, JavaScript, React.js e su Node.js se non lo conosci non c&#8217;è problema, investiamo noi sulla formazione&#8230; Fine! C&#8217;è da aggiungere altro? Ogni volta che lo sento mi fa sentire un idiota ad ostinarmi nel credere che anche qui in Italia si possano cambiare le cose e non fa altro che ripetermi: &#8220;una figura come la tua qui in Francia guadagnerebbe cifre da capogiro&#8221;.<br />
Stipendio mensile: 3800 euro al mese + bonus e che Dio glieli accresca. L&#8217;azienda offre formazione e aggiornamenti continui. È chiaro che in Francia gli imprenditori abbiano compreso che investire sul capitale umano, sui propri dipendenti, è cruciale per affrontare con successo le sfide del domani e continuare a crescere ed essere competitivi.</p>
<h2>Se non si cambia registro il fallimento è a un passo</h2>
<p>Veniamo in Italia&#8230;</p>
<p>Ci capita di fare appuntamenti con imprenditori &#8220;vecchio stampo&#8221;&#8230; Quando gli parli di automazione dei processi aziendali (cosa che gli farebbe risparmiare anche il 90% del loro attuale costo del lavoro perché devono sopportare costi assurdi per dipendenti che stanno tutto il giorno davanti a file excel e fanno in un giorno quello che un software farebbe in qualche decimo di secondo) ti guardano come se stessi venendo da Marte, perché di tutto quello che gli hai spiegato non ci hanno capito un fico secco, ma sono troppo pieni di sé per ammetterlo e per comprendere che se non si muoveranno in questo senso adesso, la strada futura potrà essere solo una: il fallimento!</p>
<p>Sì, parlo di fallimento, e lo urlo a tutti, senza temuta di smentita, conscio che non sto in alcun modo esasperando il discorso, perché ci capita di fare appuntamenti con imprenditori giovani che invece comprendono pienamente le potenzialità dell&#8217;automazione dei processi, comprendono che possono risparmiare tantissimo sul costo del lavoro e pertanto possono abbassare i costi dei loro servizi essendo più appetibili sul mercato e fanno numeri a colori, ed è qui che scatta la domanda di rito: &#8220;Un consumatore che trova un prodotto a 20€ e lo stesso identico prodotto, probabilmente anche con qualche difettuccio visto che tutto è frutto di &#8220;elaborazioni a mano&#8221; a 40€, secondo voi, <strong>ma </strong><strong>quale caspita di prodotto dovrebbe comprare tra i due?</strong>&#8220;</p>
<p>e come corollario ne scaturisce una seconda di domanda&#8230;</p>
<p>&#8220;Le persone che fino ad oggi hanno comprato il prodotto a 40€, quando si accorgono che un&#8217;altra azienda ne fornisce uno migliore a 20€, <strong>continueranno a comprare quello da 40?</strong>&#8220;. Sarebbe come credere che se domani uscisse una marca di TV Oled che producesse TV di qualità pari o addirittura migliore di quelli prodotti da marche blasonate, questi continuerebbero a comprare quelli che costano il doppio&#8230; Totalmente illogico!</p>
<p>Ecco come si va inesorabilmente verso il fallimento&#8230; La nuova generazione imprenditoriale sarà in grado di aggredire il mercato con prezzi più bassi perché riuscirà ad essere più performante avendo meno costi. E tutti i clienti delle old-company, quando vedranno che è possibile risparmiare tanto, avendo prodotti nati da processi automatizzati e quindi sicuramente più affidabili in termini di &#8220;errore umano&#8221; e di velocità di produzione, non ci metteranno molto a chiudere contratti da una parte e aprirli dall&#8217;altra; e questo discorso è tanto più valido quanto più è grande l&#8217;azienda. Su grandi imprese, se una rondella la trovo a 2 centesimi in meno con la stessa qualità di quella che ho utilizzato fino ad oggi, a fine anno potrei trovarmi con qualche milione di euro in più di liquidità.</p>
<h2>Idee obsolete e culto della persona</h2>
<p>È veramente molto difficile far capire a una persona che ha costruito la sua ricchezza nel 1980 seguendo una metodologia totalmente analogica, che non siamo più nel 1980. C&#8217;è chi, in modo intelligente si pone delle domande, si guarda intorno, comprende che il mondo è cambiato. Comprende che nel 1980 quello spray eccezionale per pulire i vetri che non lasciava aloni lo trovavi solo se prendevi la macchina e ti facevi 20km per andare in quel negozio dove eri certo di trovarlo, ora vai su amazon e il giorno dopo te lo trovi fuori al cancello di casa&#8230; Ma lui invece imperterrito continua a farsi 20km per uscire di casa spendendo benzina, sprecando tempo e magari esasperandosi in mezzo al traffico. C&#8217;è chi ha compreso che viviamo in un mondo molto diverso da quello c&#8217;era nel 1980. Questi sono i soggetti che si salvano e che anzi non faranno altro che veder salire i fatturati. Ma da qui a 5 anni, aziende che non si saranno mosse con l&#8217;informatizzazione dei processi e che si mettono ancora lì a scrivere con carta e penna o nel migliore dei casi con i fogli excel, faranno una bruttissima fine.</p>
<p>Molti lettori potranno dire: &#8220;Ok, ma così non hai più bisogno di tante persone, ne licenzieranno la metà&#8221;. Non è detto&#8230; Ma continuiamo invece a portare avanti un modello di business che tra un paio di anni farà il botto, almeno a casa ci andranno proprio tutti! Fermo restando che puoi comunque mantenere il livello occupazionale decidendo di investire delle risorse umane su altri comparti aziendali come ad esempio quello marketing in modo che aumenti il tuo livello di business, visto che ormai sul lato di elaborazione puoi scalare virtualmente all&#8217;infinito avendo investito in automazione.</p>
<p>Il mondo evolve e, specie nelle aziende private che sono il motore economico del sistema Italia, devi essere in grado di adattarti ai tempi e alle richieste del mercato. Solo in questo modo potrai avere una società solida. L&#8217;economia non funziona solo con la quantità di moneta che circola, è molto più importante la velocità con cui questa moneta passa di mano in mano e se l&#8217;andazzo sarà questo, presto avremo sempre meno aziende attive sul mercato e la ricchezza si concentrerà solo su pochi, perché quando gli altri si muoveranno sarà ormai troppo tardi.</p>
<h2>L&#8217;importanza di certe figure ed il loro riconoscimento</h2>
<p>Cominciamo con il dare valore alle persone&#8230; Uno sviluppatore Full Stack che ti risolve dei problemi nella tua impresa e ti fa risparmiare un milione di euro l&#8217;anno non puoi pagarlo 800 euro al mese. Imprenditore!?!?!? Sii coerente prima con te stesso e poi con gli altri!!! Ma come puoi presentarti con una briciola di pane da uno che ti ha fatto mantenere nella credenza 5000 pagnotte?</p>
<p>Uno che si è fatto un mazzo così a studiare libri su libri, fare nottate per preparare gli esami all&#8217;università, informarsi costantemente su ultime tecnologie (e ne escono un paio nuove ogni due mesi) mentre gli altri suoi coetanei erano a farsi le canne sul muretto della piazza del paese non esiste che venga a lavorare da te per farti guadagnare un milione di euro all&#8217;anno in cambio di 800 euro al mese, ovvero quello che prendono quelli che si facevano le canne; ma gliela vogliamo riconoscere una differenza o no?? E se pensi di essere intelligente a proporre offerte di lavoro di cui ci sarebbe solo da vergognarsi a pubblicarle, forse è il caso che ritorni sui tuoi passi&#8230; Perché se applichi questo ragionamento probabilmente non sei così intelligente.</p>


<p><strong>[starbox] </strong></p>
<p>L'articolo <a href="https://renor.it/blog/sviluppo-software-programmazione/cosa-diavolo-cerchi-un-backend-o-un-frontend/">Cosa diavolo cerchi? Un backend o un frontend?</a> proviene da <a href="https://renor.it">RENOR &amp; Partners S.r.l.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>WordPress o soluzione custom?</title>
		<link>https://renor.it/blog/sviluppo-software-programmazione/wordpress-o-custom/</link>
		
		<dc:creator><![CDATA[Simone Renzi]]></dc:creator>
		<pubDate>Fri, 19 Aug 2022 14:25:47 +0000</pubDate>
				<category><![CDATA[Sviluppo Software & Programmazione]]></category>
		<category><![CDATA[cms]]></category>
		<category><![CDATA[ottimizzazione immagini]]></category>
		<category><![CDATA[renor]]></category>
		<category><![CDATA[renor & partners]]></category>
		<category><![CDATA[SEO]]></category>
		<category><![CDATA[siti truffa]]></category>
		<category><![CDATA[truffe]]></category>
		<category><![CDATA[velocità di caricamento]]></category>
		<category><![CDATA[wordpress]]></category>
		<guid isPermaLink="false">https://renor.it/?p=89</guid>

					<description><![CDATA[<p>Non c&#8217;è dubbio che WordPress sia il CMS più utilizzato al mondo, la community dispone di più di 55000 plugin, è facile da installare, esistono plugin per customizzare i temi senza la necessità di dover scrivere una riga di codice, integra un blog sul quale è facile scrivere articoli, dispone di plugin per la cura [&#8230;]</p>
<p>L'articolo <a href="https://renor.it/blog/sviluppo-software-programmazione/wordpress-o-custom/">WordPress o soluzione custom?</a> proviene da <a href="https://renor.it">RENOR &amp; Partners S.r.l.</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Non c&#8217;è dubbio che <strong>WordPress</strong> sia il <strong>CMS più utilizzato al mondo</strong>, la community dispone di più di 55000 plugin, è facile da installare, esistono plugin per customizzare i temi senza la necessità di dover scrivere una riga di codice, integra un blog sul quale è facile scrivere articoli, dispone di plugin per la cura della SEO e chi più ne ha più ne metta. Se tutto è così bello perché ci sono ancora siti custom che non sfruttano i plugin?</p>
<h2>Alcune motivazioni</h2>
<p>Nonostante siano chiari i punti di forza di questo CMS, ci sono molte cose che l&#8217;utente medio non può conoscere non essendo un addetto ai lavori.</p>
<h3>C&#8217;è modo e modo di sviluppare siti web</h3>
<p>Quanti di voi sono caduti nella tentazione di farsi sviluppare un <strong>sito a 300 euro tutto incluso</strong>? Bene, vi do una notizia&#8230; <strong>Avete buttato i vostri soldi!</strong></p>
<p>Sviluppare un sito solo per dire &#8220;Hey amico! Ho fatto il sito&#8221;, è perfettamente inutile. Da che mondo è mondo, quando si decide di comprare un prodotto lo si fa perché <strong>deve essere utile a qualcosa</strong>. Un sito sviluppato con 300 euro è un <strong>sito inutile</strong>. C&#8217;è poco da girarci attorno. Con 300€ è impossibile mettere in atto tutte quelle pratiche virtuose dello sviluppo, necessarie a rendere il sito piacevole ai motori di ricerca.</p>
<h3>La velocità di caricamento</h3>
<p>Siti tirati su al costo di 300€ integrano sicuramente un <strong>plugin WYSIWYG</strong> (What You See Is What You Get &#8211; Quello che vedi è quello che ottieni). Questi plugin sono notoriamente molto pesanti, utilizzano tantissimo JavaScript e una miriade di CSS che per lo più non viene neanche utilizzato. Diciamolo: vengono usati principalmente da chi <strong>non sa sviluppare</strong> e vuole farsi un &#8220;sitarello per conto suo senza troppe pretese&#8221; o da chi, consapevole delle implicazioni sulla SEO, se ne sbatte solo per consegnare un lavoro prima e fare cassa.</p>
<p>Banco alle ciance! Numeri alla mano&#8230; In ambo i casi, <strong>il risultato sarà questo:</strong></p>
<p><img loading="lazy" decoding="async" class="size-full wp-image-91 aligncenter" src="https://renor.it/wp-content/uploads/2022/08/page-speed-troubles.jpg" alt="page speed" width="800" height="394" srcset="https://renor.it/wp-content/uploads/2022/08/page-speed-troubles.jpg 800w, https://renor.it/wp-content/uploads/2022/08/page-speed-troubles-300x148.jpg 300w, https://renor.it/wp-content/uploads/2022/08/page-speed-troubles-768x378.jpg 768w" sizes="auto, (max-width: 800px) 100vw, 800px" /></p>
<p>Un sito del genere sarà fortunato a comparire nella <strong>centesima pagina del motore di ricerca</strong>, a meno di non aver fatto un lavoro certosino sulla SEO studiando contenuti per parole chiave composte e più facilmente posizionabili, oppure avere un numero di articoli e contenuti che integrino tutta la conoscenza umana, allora si, si può chiudere un occhio sulla velocità di caricamento, ma se si è scesi a questi compromessi figuriamoci concentrare tutte queste attenzioni sulla SEO e i contenuti!</p>
<p>Pertanto: <strong>spesa inutile!</strong></p>
<h4>Ottimizzazione delle immagini</h4>
<p>Un sito ben fatto ha avuto dietro il lavoro di un webmaster che <strong>ha dedicato molto tempo all&#8217;ottimizzazione delle immagini</strong> per il Web.<br />Oggi ci sono formati pensati per favorire la <strong>velocità di caricamento</strong> del sito, formati che offrono una dimensione contenuta pur mantenendo una discreta qualità dell&#8217;immagine.</p>
<p>Oltretutto<strong> le immagini andrebbero ridimensionate</strong> per l&#8217;uso che se ne deve fare. Se necessito di riempire una piccola area di 300 x 200 pixel con un&#8217;immagine, è una bad-practice caricare un&#8217;immagine di 2400 x 1600 pixel.</p>
<p>A 300€ a sito questa cura <strong>non può esistere</strong> perché richiede tempo, pertanto si preferisce prendere e caricare l&#8217;immagine così com&#8217;è e ridimensionarla tramite CSS aggiungendo un paio di attributi al tag (operazione molto più veloce):</p>
<pre>[html]
&amp;amp;amp;amp;lt;img src=&amp;amp;amp;quot;./images/img01.jpg&amp;amp;amp;quot; width=&amp;amp;amp;quot;300px&amp;amp;amp;quot; height=&amp;amp;amp;quot;200px&amp;amp;amp;quot; alt=&amp;amp;amp;quot;&amp;amp;amp;quot; title=&amp;amp;amp;quot;&amp;amp;amp;quot; longdesc=&amp;amp;amp;quot;&amp;amp;amp;quot; /&amp;amp;amp;amp;gt;
[/html]</pre>
<p>Peccato che l&#8217;immagine pesi 4 Megabyte! In sostanza solo il caricamento di questa immagine, da mobile richiederebbe dai 4 ai 5 secondi con copertura 4G, contro i 20 Kb della stessa immagine ottimizzata, che verrebbe caricata in una 30ina di millisecondi.</p>
<p>Nella riga di codice suesposta spiccano anche <strong>tre attributi utili per la SEO</strong> e lasciati volutamente non compilati: ogni immagine dovrebbe avere un <strong>testo alternativo</strong>, un <strong>titolo</strong> e una <strong>descrizione lunga</strong>. Il motore di ricerca fa caso a questi dettagli perché sono utili ai <strong>non vedenti</strong> per interpretare il contenuto dell&#8217;immagine. Pertanto oltre a rappresentare delle ulteriori parole da catalogare ai fini SEO i motori tendono a premiare chi pensa anche ai meno fortunati. Naturalmente anche la compilazione di questi campi richiede del tempo e dubito che siti da 300€ abbiano fatto una preventiva <strong>ricerca delle parole chiave</strong> con cui potersi posizionare e si siano dati da fare per fornire descrizioni di tutte le immagini che inseriscono.</p>
<p>Tanto per darvi un&#8217;idea dei tempi, su questo blog esce <strong>un articolo al giorno</strong>&#8230; La motivazione non è dovuta alla scarsa voglia di scrivere o alla mancanza di fantasia: abbiamo centinaia di titoli di articoli da scrivere nel nostro piano editoriale, il problema è che tra analisi SEO e stesura, un articolo richiede <strong>mezza giornata</strong> di tempo per essere redatto&#8230; E vogliamo ancora perdere tempo a parlare di un sito a 300€?</p>
<h4>Rimandare CSS e JS che bloccano la visualizzazione</h4>
<p>Il browser quando riceve i dati del sito richiesti elabora CSS e JavaScript nell&#8217;ordine in cui sono scritti e <strong>blocca la visualizzazione</strong> della pagina fin tanto che tali file non siano stati completamente caricati. Questo allunga ulteriormente i tempi di interazione con il sito. Alcuni di questi file potrebbero non essere necessari nell&#8217;immediato e possono essere rimandati. In sostanza si deve semplicemente comunicare al browser che quei file possono essere caricati dopo il caricamento dei contenuti della pagina. Questa pratica da modo all&#8217;utente che sta aprendo il sito di poterlo visualizzare e poterci interagire molto prima. Tale pratica abbassa quella che i motori di ricerca chiamano <strong>frequenza di rimbalzo</strong>. In sostanza se un sito impiega 20 secondi per caricarsi a 6 secondi <strong>se ne sono già andati l&#8217;80% dei visitatori</strong>.</p>
<h4>Eliminare frammenti CSS e JavaScript non utilizzati</h4>
<p>Operazione sicuramente difficile da compiere su progetti che utilizzano framework CSS-JS! Lo sviluppatore dovrebbe accedere ai file CSS e JS del framework alla ricerca di tutte quelle classi (per il CSS) e di tutte le funzioni (per i JavaScript) che non vengono richiamate all&#8217;interno delle pagine del sito ed eliminarle. In condizioni normali non sarebbe la fine del mondo, ma non è facile trovare ciò che serve dentro un file dove il nome più <strong>user friendly</strong> per nominare una variabile è <em>&#8220;var k&#8221;</em>.</p>
<p>La pratica di sostituzione dei nomi delle variabili user friendly con variabili corte è messa in atto dagli sviluppatori del framework per <strong>ridurre la dimensione del file</strong> che il browser dovrà caricare, ma rende praticamente <strong>illegibile il codice JavaScript</strong> scritto al suo interno. In sostanza è una pratica non attuabile.</p>
<p>Un&#8217;operazione che sicuramente può essere fatta, riguarda la <strong>minificazione di tutti i CSS e i JS</strong>. Tutto il file viene scritto in un&#8217;unica riga eliminando le informazioni sugli &#8220;a capo&#8221; e gli spazi al suo interno. Questo permette di risparmiare una quantità di spazio che può andare mediamente dal 20 al 40% del peso originale. Tutto ciò si traduce in un<strong> tempo di caricamento inferiore</strong> a parità della velocità di download.</p>
<h3>Siti Web visivamente tutti uguali</h3>
<p>Quando si utilizzano queste scorciatoie si nota immediatamente una somiglianza tra tutti i siti. Vuoi o non vuoi, le classi CSS sempre quelle sono! Per quanto si possa poi personalizzare resta sempre un&#8217;impronta visiva molto simile tra siti che sono stati sviluppati utilizzando Plugin WYSIWYG o Framework.</p>
<h2>Stiamo dunque dicendo che WordPress è un CMS da evitare?</h2>
<p>Assolutamente no! WordPress è un ottimo CMS per creare siti web con blog e eCommerce, a patto che <strong>si sviluppino temi alla regola dell&#8217;arte</strong>, senza utilizzare questo genere di scorciatoie che rendono assolutamente inutile l&#8217;investimento fatto per avere un sito web online.</p>
<p>Anche questo sito è stato sviluppato con WordPress, ma non in mezza giornata&#8230; In un paio di mesi. Possiede uno stile che sarà impossibile trovare su Siti sviluppati con le scorciatoie di cui abbiamo parlato, ha immagini ottimizzate, SEO ottimizzata, Sitemap aggiornata in tempo reale e sicuramente il costo di sviluppo non è stato di 300€ a meno che non troviate uno sviluppatore che lavori per voi a <strong>150 euro al mese</strong>!</p>
<p>Del resto <strong>l&#8217;Audit parla da solo</strong>&#8230;</p>
<p><img loading="lazy" decoding="async" class="alignleft wp-image-95 size-medium" src="https://renor.it/wp-content/uploads/2022/08/Schermata-2022-08-19-alle-16.04.47-300x158.png" alt="audit renor.it" width="300" height="158" srcset="https://renor.it/wp-content/uploads/2022/08/Schermata-2022-08-19-alle-16.04.47-300x158.png 300w, https://renor.it/wp-content/uploads/2022/08/Schermata-2022-08-19-alle-16.04.47.png 692w" sizes="auto, (max-width: 300px) 100vw, 300px" /></p>
<p>Come avrete capito non si tratta di scegliere WordPress si o WordPress no; si tratta. come sempre, di valutare <strong>come</strong> si lavora su WordPress e fare tutte le valutazioni del caso se non si vogliono buttare via soldi.</p>
<p>Ahimé la maggior parte dei siti online vengono proprio sviluppati da <em>&#8220;fantomatiche agenzie web&#8221;<strong> </strong></em>proprio in questo modo, cercando di andare sulla velocità di sviluppo e sul numero di clienti intrappolati nella rete al pari dei pesci pescati in mare, rifilandogli dei siti web pagati poco, sviluppati tanto per far vedere il sito online al cliente e che risultano però essere completamente inutili ai fini per cui un sito web dovrebbe essere costruito: attirare nuovi clienti.</p>
<p>In Italia già da qualche anno si respira un&#8217;aria di &#8220;<em><strong>eccessiva faciloneria&#8221;</strong></em>. Si pensa che tutti possano fare tutto con poco sforzo. Mi dispiace, ma non è così! È giusto che qualcuno prima o poi si prenda la responsabilità di dire che se fai il Cuoco sarai bravissimo a fare il tuo lavoro ma non puoi sviluppare siti web.</p>
<h2>I contro di WordPress</h2>
<p>Essendo il CMS più gettonato al mondo è anche quello <strong>maggiormente preso d&#8217;assalto dalla comunità Hacker</strong>. Più di 16 milioni di siti online sono stati sviluppati utilizzando WordPress, il passo è breve: trovo una falla in WordPress e posso estenderla a 16 milioni di siti.</p>
<p>La community di WordPress è sempre al lavoro per <strong>migliorare il core del CMS</strong>, tanto è vero che di tanto in tanto vengono rilasciati aggiornamenti del Core, ma attenzione, ci sono anche i Plugin che sono spesso sviluppati da agenzie di terze parti,  e che potrebbero aver chiuso i battenti,  pertanto non rilasceranno più aggiornamenti e qualcuno prima o poi troverà una falla.</p>
<p>La maggior parte degli attacchi a WordPress proviene infatti proprio da <strong>falle nei plugin e nei temi</strong> (che non fanno eccezione). Sviluppare il proprio tema da zero permette di limitare gli attacchi sul fronte dei temi proprio in funzione del fatto che se il tema è vostro sarà univoco e non c&#8217;è Hacker al mondo disposto a trovare falle di sistema su un tema utilizzato da un singolo sito, a meno che non vi chiamiate INPS o Agenzia delle Entrate.</p>
<p>In buona sostanza dovete orientarvi preferibilmente su plugin che contino milioni di installazioni e che garantiscano che la Software House che li ha sviluppati resti in vita per garantire aggiornamenti del loro plugin nel tempo.</p>
<p>WordPress per essere sicuro necessita di essere <strong>aggiornato continuamente</strong>, non appena è disponibile un aggiornamento.</p>
<p>Dalle ultime versioni è possibile anche impostare gli aggiornamenti automatici, tanto per il core WordPress quanto per i plugin.</p>
<h2>Quando non è consigliabile utilizzare WordPress</h2>
<p>Non c&#8217;è una regola, ci si limita ad utilizzare il buon senso&#8230; Personalmente non utilizzerei WordPress per fini per cui non è stato concepito. Ricordiamoci che WordPress nasce come <strong>CMS per l&#8217;integrazione di Blog</strong>, per qualsiasi altro progetto ci penserei prima di utilizzare WordPress.</p>
<p>Non utilizzerei WordPress nel caso in cui la parola chiave del lavoro da svolgere fosse: performance. Non perché WordPress in sé sia lento ma perché, lo ricordo, per poter funzionare effettua chiamate che per alcuni progetti potrebbero essere totalmente superflue.</p>
<p>Strutturare un progetto Custom implica che si scriva <strong>solo ed esclusivamente il codice che serve</strong> ai fini per cui il sito o l&#8217;applicazione web è stata progettata. Allo stesso modo utilizzerei soluzioni Custom per non preoccuparmi di dover aggiornare temi e plugin, specie quando alcuni plugin non sono stati ancora aggiornati per il corretto funzionamento su un nuovo core di WordPress. Questo potrebbe generare problemi di utilizzo del sito.</p>
<p>In buona sostanza quando il budget lo permette sono sempre per l&#8217;implementazione di soluzioni custom: più affidabili, più performanti, più sicure&#8230; Naturalmente a patto di non pagarle 300€.</p>
<p> </p>


<p><strong>[starbox] </strong></p>
<p>L'articolo <a href="https://renor.it/blog/sviluppo-software-programmazione/wordpress-o-custom/">WordPress o soluzione custom?</a> proviene da <a href="https://renor.it">RENOR &amp; Partners S.r.l.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Prima i commenti, poi il codice</title>
		<link>https://renor.it/blog/sviluppo-software-programmazione/prima-i-commenti-poi-il-codice/</link>
		
		<dc:creator><![CDATA[Simone Renzi]]></dc:creator>
		<pubDate>Thu, 18 Aug 2022 19:57:25 +0000</pubDate>
				<category><![CDATA[Sviluppo Software & Programmazione]]></category>
		<category><![CDATA[commenti]]></category>
		<category><![CDATA[commenti al codice]]></category>
		<category><![CDATA[importanza dei commenti]]></category>
		<category><![CDATA[perché commentare il codice]]></category>
		<category><![CDATA[procedure]]></category>
		<category><![CDATA[programmazione procedurale]]></category>
		<category><![CDATA[renor]]></category>
		<category><![CDATA[renor & partners]]></category>
		<guid isPermaLink="false">https://renor.it/?p=71</guid>

					<description><![CDATA[<p>In questo articolo voglio parlare di un argomento che mi sta molto a cuore e che molti sviluppatori sembrano lasciare in secondo piano. Questo argomento rappresenta, oltre che una innegabile &#8220;best practice&#8221; nel modo di sviluppare codice, anche una forma di rispetto nei confronti della comunità di sviluppatori e ingegneri che dovranno rimettere mano al [&#8230;]</p>
<p>L'articolo <a href="https://renor.it/blog/sviluppo-software-programmazione/prima-i-commenti-poi-il-codice/">Prima i commenti, poi il codice</a> proviene da <a href="https://renor.it">RENOR &amp; Partners S.r.l.</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>In questo articolo voglio parlare di un argomento che mi sta molto a cuore e che molti sviluppatori sembrano lasciare in secondo piano. Questo argomento rappresenta, oltre che una innegabile &#8220;best practice&#8221; nel modo di sviluppare codice, anche una <strong>forma di rispetto</strong> nei confronti della comunità di sviluppatori e ingegneri che dovranno rimettere mano al codice scritto da qualcun altro: sto parlando dei <strong>commenti al codice</strong>.</p>
<p>Un mio carissimo Professore universitario, il Prof. Carlo Gaibisso, nelle sue lezioni di <em>&#8220;Programmazione strutturata&#8221;</em> in linguaggio C diceva sempre questa frase: <em>&#8220;Si scrivono prima i commenti e poi il codice&#8221;.</em></p>
<p>Come dargli torto?</p>
<p>Nonostante quella dei programmatori sia una classe di <em>&#8220;animali da palcoscenico&#8221;</em> nel mondo dell&#8217;informatica, e nonostante la preparazione che uno sviluppatore possa avere, è un dato di fatto che sia sempre più facile e immediato leggere qualcosa che appartenga al nostro <strong>comune modo di comunicare</strong>. È molto più semplice leggere in Italiano di cosa si occupa un algoritmo piuttosto di comprendere cosa faccia un algoritmo scritto in codice; diviene tanto più complesso comprenderlo quanto più è complesso l&#8217;algoritmo.</p>
<p>Anche la programmazione orientata agli oggetti congloba la programmazione procedurale. All&#8217;interno dei metodi di una classe si programma in modo procedurale.</p>
<p>La programmazione procedurale è così chiamata perché il codice viene eseguito secondo una <strong>procedura</strong>. Posso scrivere procedure per qualsiasi cosa&#8230; Ad esempio se devo calcolare la successione di Fibonacci so che per calcolare il numero al passo <em>n </em>devo prendere il numero precedente e sommarlo a quello ancora precedente.<br />
Pertanto la procedura sarà: partendo da S = 1 1,  <i>f</i><sub>3 </sub>-&gt; Prendo <i>f</i><sub>2</sub> lo sommo a <i>f</i><sub>1</sub>, e ottengo</p>
<p>S = 1 1 2</p>
<p>Prendo <i>f</i><sub>3</sub> = 2 lo sommo a <i>f</i><sub>2</sub> = 1 e ottengo</p>
<p>S = 1 1 2 3</p>
<p>Prendo <i>f</i><sub>4</sub> = 3 lo sommo a <i>f</i><sub>2</sub> = 2 e ottengo</p>
<p>S = 1 1 2 3 5</p>
<p>Comprenderete che è molto più facile capire istantaneamente la definizione: &#8220;Per calcolare il numero ennesimo della successione di Fibonacci sommo i due precedenti&#8221; rispetto a <i>f</i><sub>n</sub> = <i>f</i><sub>n-1</sub> + <i>f</i><sub>n-2</sub> specie se questa formula è scritta con variabili e cicli all&#8217;interno di un algoritmo informatico!</p>
<p>esempio in PHP per il calcolo della serie di Fibonacci senza commenti</p>
<p>[php]&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
class Fibonacci&lt;br /&gt;
{&lt;br /&gt;
    public static function calcolaSuccessione($n)&lt;br /&gt;
    {&lt;br /&gt;
        $n = $n &#8211; 2;&lt;br /&gt;
        $a = 1;&lt;br /&gt;
        $b = 1;&lt;br /&gt;
        echo $a . &amp;#039;&amp;lt;br&amp;gt;&amp;#039;;&lt;br /&gt;
        echo $b . &amp;#039;&amp;lt;br&amp;gt;&amp;#039;;&lt;br /&gt;
        for ($i = 0; $i &amp;lt; $n; $i++) {&lt;br /&gt;
            $c = $a + $b;&lt;br /&gt;
            echo $c . &amp;#039;&amp;lt;br&amp;gt;&amp;#039;;&lt;br /&gt;
            $a = $b;&lt;br /&gt;
            $b = $c;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;/p&gt;
&lt;p&gt;Fibonacci::calcolaSuccessione(1000);&lt;br /&gt;
[/php]</p>
<p>esempio in PHP con i commenti</p>
<p>[php]&lt;br /&gt;
&amp;lt;?php&lt;/p&gt;
&lt;p&gt;/**&lt;br /&gt;
 * Fibonacci è una classe per il calcolo della successione di Fibonacci&lt;br /&gt;
 * e la sua verifica&lt;br /&gt;
 *&lt;br /&gt;
 * Fibonacci è una classe per il calcolo della successione di Fibonacci&lt;br /&gt;
 * la verifica del rapporto aureo attraverso il rapporto tra termini successivi&lt;br /&gt;
 * la verifica tramite il triangolo di tartaglia e la verifica tramite&lt;br /&gt;
 * le uguaglianze di Cassini, Catalani e D&amp;#039;Ocagne&lt;br /&gt;
 *&lt;br /&gt;
 * @author Simone Renzi&lt;br /&gt;
 * @version 1.0&lt;br /&gt;
 * @access public&lt;br /&gt;
 * @see http://renor.it&lt;br /&gt;
 *&lt;br /&gt;
 **/&lt;br /&gt;
class Fibonacci&lt;br /&gt;
{&lt;br /&gt;
    /**&lt;br /&gt;
     * @method calcolaSuccessione &#8211; Metodo statico per calcolare la successione&lt;br /&gt;
     * di Fibonacci per $n termini&lt;br /&gt;
     * @param int $n &#8211; Il numero di iterazioni&lt;br /&gt;
     * @return void non ha valori di ritorno, stampa solamente a schermo i valori&lt;br /&gt;
     * della serie ad ogni iterazione&lt;br /&gt;
     * @access public&lt;br /&gt;
     **/&lt;/p&gt;
&lt;p&gt;    public static function calcolaSuccessione($n)&lt;br /&gt;
    {&lt;br /&gt;
        //Avendo due parametri fissi li rimuovo dalle iterazioni&lt;br /&gt;
        $n = $n &#8211; 2;&lt;br /&gt;
        $a = 1;&lt;br /&gt;
        $b = 1;&lt;br /&gt;
        //Stampo i due parametri di partenza&lt;br /&gt;
        echo $a . &amp;#039;&amp;lt;br&amp;gt;&amp;#039;;&lt;br /&gt;
        echo $b . &amp;#039;&amp;lt;br&amp;gt;&amp;#039;;&lt;br /&gt;
        //Ciclo per $n&lt;br /&gt;
        for ($i = 0; $i &amp;lt; $n; $i++) {&lt;br /&gt;
            //Calcolo il valore della successione al passo $n&lt;br /&gt;
            $c = $a + $b;&lt;br /&gt;
            //Stampo a schermo il valore&lt;br /&gt;
            echo $c . &amp;#039;&amp;lt;br&amp;gt;&amp;#039;;&lt;br /&gt;
            //sposto i valori delle variabili&lt;br /&gt;
            //f2 diventa f1&lt;br /&gt;
            $a = $b;&lt;br /&gt;
            //f3 diventa f2, nella prossima iterazione verrà calcolato il nuovo f3&lt;br /&gt;
            $b = $c;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;/p&gt;
&lt;p&gt;Fibonacci::calcolaSuccessione(1000);&lt;br /&gt;
[/php]</p>
<p>Posso continuare ad applicare la procedura di prendere i due numeri precedenti all&#8217;infinito. Ovviamente calcolare una serie infinita di numeri richiede un tempo infinito quindi si opta per trovare la successione al termine <em>n </em>dove <em>n </em>è il numero di iterazioni per cui l&#8217;algoritmo dovrà eseguire la procedura.</p>
<p>Dalle immagini si nota che scrivere codice commentato in modo virtuoso richiede molte più righe e quindi più tempo ma in caso di manutenzione diventerà tutto estremamente più facile e veloce. Investiamo una piccola parte  del nostro tempo prima per non dover perdere intere giornate dopo.</p>
<h2>Procedure</h2>
<p>Posso applicare una procedura in qualsiasi contesto, anche per cucinare. Una ricetta altro non è che una procedura: prendere un pentolino, mettere 2 cucchiai di olio extravergine di oliva, aggiungere uno spicchio di aglio, un peperoncino piccante, accendere il fornello e cuocere a fuoco lento, ecc.</p>
<p>Le procedure sono insite all&#8217;interno delle funzioni e possono essere combinate tra loro per risolvere problemi più grandi. Tornando all&#8217;esempio di Fibonacci, un altro algoritmo potrebbe verificare che il rapporto tra due numeri consecutivi della serie di fibonacci approssima al crescere di <em>n </em>sempre di più il rapporto aureo. Una ulteriore funzione potrebbe verificare che la successione ottenuta di <em>n</em> termini considerando la dei termini presenti su ciascuna diagonale del triangolo di tartaglia corrisponde alla successione di Fibonacci, ecc.</p>
<p>Al pari potrei dire che la procedura precedente utile per la preparazione del soffritto deve essere portata avanti al pari della procedure per cuocere la pasta.</p>
<p>Come avrete compreso una procedura si comprende meglio e in modo più veloce se ci sono commenti che ne descrivono i passi.</p>
<p>Si è soliti inserire prima i commenti e poi il codice perché questa pratica ci permette di scrivere codice molto velocemente senza dimenticarci nulla e lasciando agli altri programmatori che dovranno integrare altre funzioni di comprendere velocemente il famoso <em>&#8220;cosa serve a cosa&#8221;. </em></p>
<p>&nbsp;</p>


<p><strong>[starbox] </strong></p>
<p>L'articolo <a href="https://renor.it/blog/sviluppo-software-programmazione/prima-i-commenti-poi-il-codice/">Prima i commenti, poi il codice</a> proviene da <a href="https://renor.it">RENOR &amp; Partners S.r.l.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Programmazione: da procedurale verso l&#8217;OOP</title>
		<link>https://renor.it/blog/sviluppo-software-programmazione/programmazione-da-procedurale-verso-loop/</link>
		
		<dc:creator><![CDATA[Simone Renzi]]></dc:creator>
		<pubDate>Thu, 18 Aug 2022 11:55:27 +0000</pubDate>
				<category><![CDATA[Sviluppo Software & Programmazione]]></category>
		<category><![CDATA[come funziona la programmazione a oggetti]]></category>
		<category><![CDATA[funzionale]]></category>
		<category><![CDATA[OOP]]></category>
		<category><![CDATA[procedurale]]></category>
		<category><![CDATA[programmazione a oggetti]]></category>
		<category><![CDATA[programmazione funzionale]]></category>
		<category><![CDATA[programmazione procedurale]]></category>
		<category><![CDATA[renor]]></category>
		<category><![CDATA[renor & partners]]></category>
		<guid isPermaLink="false">https://renor.it/?p=66</guid>

					<description><![CDATA[<p>Ci sono molte persone a cui piacerebbe imparare a programmare per il Web ma non sanno da dove partire. Alcuni si affidano ai libri, altri a corsi online ma quello che mi capita di sentire è sempre la stessa frase: &#8220;ho iniziato a studiare un linguaggio di Backend su un corso online ma la programmazione [&#8230;]</p>
<p>L'articolo <a href="https://renor.it/blog/sviluppo-software-programmazione/programmazione-da-procedurale-verso-loop/">Programmazione: da procedurale verso l&#8217;OOP</a> proviene da <a href="https://renor.it">RENOR &amp; Partners S.r.l.</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Ci sono molte persone a cui piacerebbe imparare a programmare per il Web ma non sanno da dove partire. Alcuni si affidano ai libri, altri a corsi online ma quello che mi capita di sentire è sempre la stessa frase: &#8220;ho iniziato a studiare un linguaggio di Backend su un corso online ma la programmazione a oggetti è troppo difficile&#8221;.</p>
<p>Effettivamente, quasi tutti i libri di testo che ho letto nella vita sul paradigma OOP complicano un concetto che io definirei come la <strong>naturale evoluzione della programmazione procedurale e funzionale</strong>.</p>
<h2>Perché renderla complicata?</h2>
<p>Perché a mio avviso, tutti i libri e tutti i corsi vogliono per forza portare degli esempi paragonando la programmazione a oggetti alla vita reale. In realtà, chi ha imparato a programmare in modo procedurale, fino a quel momento ha visto unicamente esempi nel mondo dello sviluppo: cos&#8217;è una variabile, cos&#8217;è un array, come si cicla un array, come scrivere una condizione, come fare un diagramma di flusso, come scrivere una funzione; pertanto non ritengo siano abituati a cogliere degli esempi nel mondo reale in questa fase, ma dopo aver compreso cosa implica il paradigma a oggetti in ambito informatico. È un metodo di pensare al codice che diviene quasi necessario quando si passa dal piccolo progetto ad un progetto più grande o che dovrà essere manutenuto ed esteso nel futuro. Solo quando si avranno chiare queste basi, sarà possibile portare degli esempi nella vita reale.</p>
<h3>Complicare scoraggia</h3>
<p>Complicando in questo modo le cose i nuovi &#8220;adepti&#8221; del mondo della programmazione si scoraggiano. Il risultato è che alcuni di loro abbandonano l&#8217;interesse per la programmazione, altri restano fermi alla programmazione procedurale o funzionale senza sapere che da quella funzionale all&#8217;OOP il passo è veramente cortissimo.</p>
<h3>Chi sviluppa progetti procedurali può creare grattacapi a chi dovrà rimettere mano sul codice</h3>
<p>Il paradigma a oggetti non è solo un <em>&#8220;diverso modo di organizzare il codice&#8221;</em> ma fornisce delle regole e un modus operandi tale per cui chi dovrà mettere mano su codice sviluppato da qualcun altro lo potrà fare senza spaccarsi per troppo tempo la testa alla ricerca di <em>&#8220;cosa fa cosa&#8221;.</em></p>
<p>Inoltre nella programmazione a oggetti non bisognerebbe mai sottovalutare la potenza dell&#8217;UML.</p>
<p>In sostanza la programmazione a oggetti fornisce tutte le best practice che un buon sviluppatore dovrebbe mettere in atto per rendere il codice facilmente leggibile, strutturato come si deve, facilmente manutenibile e scalabile.</p>
<h2>Parlando di scalabilità</h2>
<p>Non solo il paradigma ad oggetti incanala lo sviluppatore in un&#8217;ottica di sviluppo virtuosa, ma permette anche di usufruire di tutta una serie di strumenti già pronti e facilmente integrabili. Git Hub pullula di Classi già implementate e facilmente integrabili, per lo più tramite <strong>Composer</strong>, che danno modo di risparmiare tantissimo tempo in fase di sviluppo. Esistono classi praticamente per qualsiasi cosa: la generazione di un PDF, l&#8217;invio delle eMail, l&#8217;integrazione con le API di eBay e Amazon per l&#8217;automazione di progetti eCommerce, la generazione di QR Code e Bar Code, il confronto tra immagini, il ridimensionamento di immagini, la compressione, ecc. ecc.</p>
<p>Questi strumenti sono sempre scritti con paradigma orientato agli oggetti e per sfruttarli e, in taluni casi, riadattarli alle nostre necessità, è necessario conoscere questa filosofia di scrittura del codice.</p>
<h3>Quando è difficile scalare?</h3>
<p>Svariate volte mi è capitato di rimettere mano su progetti scritti in ottica procedurale&#8230; Ho dovuto perdere letteralmente giorni solo per capire in che modo fosse organizzato il codice, dovendo leggermi file di decine di migliaia di righe di codice. Diviene molto difficile modificare anche una banalissima funzione perché è necessario andare alla ricerca, nelle migliaia di righe, di quella porzione di codice che si occupa di mettere in atto quell&#8217;azione, stare lì ore a debuggare&#8230; Un inferno.</p>
<p>In progetti orientati agli oggetti la cosa sarebbe stata di una banalità disarmante!</p>
<h2>Come funziona la programmazione a oggetti?</h2>
<p>Sono minuti che giriamo intorno all&#8217;argomento ma ancora non ne abbiamo dato una definizione che sia facilmente comprensibile.</p>
<p>Per imparare a programmare a oggetti non c&#8217;è altra strada che imparare prima la programmazione procedurale, poi quella funzionale ed in fine quella ad oggetti, perché una è la naturale evoluzione dell&#8217;altra.<br />
Programmare significa saper analizzare un grosso problema, scomporlo in problemi più piccoli e trovare delle soluzioni a ciascuno di questi piccoli problemi per poi ricomporne i pezzi e risolvere il problema principale. Per fare questo ci si avvale di funzioni.<br />
Il passaggio dalla programmazione procedurale alla funzionale è abbastanza semplice: le funzioni sono dei raccoglitori di codice procedurale specializzate nella risoluzione di un piccolo problema. Es. scrivere un programma che si occupi di cercare una stringa all&#8217;interno di un&#8217;altra stringa; se la trova restituire esito positivo altrimenti negativo. Questo programma posso scriverlo all&#8217;interno di una funzione. Le funzioni nella programmazione sono esattamente come le funzioni matematiche: si passano una o più variabili in ingresso dopodiché la funzione le elabora e restituisce un risultato, o se vogliamo dirlo in un modo più correttamente matematico. Data una funzione per ogni elemento del dominio della funzione ne corrisponde uno nel codominio. Nella programmazione le funzioni hanno dei parametri di ingresso che vengono elaborati e viene restituito un valore di uscita.</p>
<p>In questo modo si risolve un piccolo problema derivato dal problema principale e posso continuare a risolvere piccoli problemi con altre funzioni per poi metterle assieme e risolvere il problema principale.</p>
<p>All&#8217;atto in cui ho finito di preparare tutte queste funzioni, posso valutare quali siano risolutive di una <em>classe di problemi</em>. Poniamo ad esempio di dover calcolare il totale di n fatture ancora non pagate di un&#8217;azienda e di dover successivamente inviare questo totale tramite mail al cliente che ce le deve pagare.</p>
<p>Primo problema: prendere tutti i dati da un database</p>
<p>Secondo problema: esaminare le fatture</p>
<p>Terzo problema: sommare le fatture non pagate</p>
<p>Quarto problema: inviare il totale tramite email al cliente per il pagamento</p>
<h3>Come lo risolveremmo con la programmazione funzionale?</h3>
<p>Primo problema:</p>
<ul>
<li>scrivo una funzione che effettui la connessione al database</li>
<li>scrivo una funzione che faccia una query di SELECT nel database per recuperarne i dati</li>
<li>li inserisco in un array</li>
</ul>
<p>Secondo problema:</p>
<ul>
<li>scrivo una funzione che mi cicli l&#8217;array</li>
<li>scrivo una funzione che mi scarichi tutte le fatture</li>
<li>scrivo una funzione che inserisca le fatture non pagate in un altro array</li>
</ul>
<p>Terzo problema:</p>
<ul>
<li>scrivo una funzione che mi cicli l&#8217;array e mi sommi il totale delle fatture in una variabile di ritorno</li>
</ul>
<p>Quarto problema:</p>
<ul>
<li>scrivo una funzione che invii una mail al cliente passandogli il valore della variabile contenente il totale fattura</li>
<li>verifico il corretto invio della mail</li>
</ul>
<p>Come è possibile vedere abbiamo risolto il problema principale <em>&#8220;Calcolare il totale di n fatture ancora non pagate di un&#8217;azienda e inviare il totale tramite mail al cliente per il pagamento&#8221;</em> utilizzando delle funzioni che risolvono delle sottocategorie del problema principale semplicemente mettendole assieme in modo ordinato.</p>
<h3>Come portare tutto questo in un&#8217;ottica OOP?</h3>
<p>Si devono scindere gli attori in gioco per comprendere a quali <em>Classi di problemi</em> questi appartengano&#8230; Ad esempio la <strong>Connessione al database</strong> è una Classe di problemi che differisce da quella delle fatture, pertanto <strong>fatture</strong> potrebbe essere un&#8217;altra classe di problemi, così come l&#8217;<strong>invio della Mail</strong> è ulteriormente un&#8217;altra Classe di problema rispetto alla connessione al database e all&#8217;invio delle Mail.</p>
<p>Abbiamo quindi identificato delle Classi che altro non sono che raccoglitori di funzioni, le quali, al loro interno, assumono il nome di &#8220;Metodi&#8221;.</p>
<p>Per questo problema avremo la Classe <strong>Connection</strong> <em>per la connessione al database</em>, la Classe <strong>Fatture </strong><em>per tutte le funzioni che soddisfano il requisito di risolvere i sottoproblemi relativi alle fatture</em> e la Classe <strong>Mail </strong><em>che contiene i metodi relativi all&#8217;invio della posta elettronica.</em> <strong>Ogni classe raccoglie i metodi</strong> (o se vogliamo ancora utilizzare il termine &#8220;funzioni&#8221;) <strong>relativi alla loro classe risolutiva di problemi</strong>.</p>
<p>L&#8217;<strong>oggetto</strong> che viene <strong>istanziato</strong> (creato) dalla Classe, altro non è che un contenitore dei metodi che implementa la Classe (e di proprietà che altro non sono che le variabili in gioco all&#8217;interno della Classe)  che possono essere utilizzati per risolvere il problema principale.</p>
<p>Naturalmente questo è uno degli approcci, ma ognuno potrebbe trarre le sue conclusioni sugli attori come meglio crede che sia giusto e non ci sarebbe una versione corretta e una errata. Ad esempio uno sviluppatore avrebbe potuto integrare i metodi per l&#8217;invio delle Mail all&#8217;interno della Classe delle fatture visto che si riferiscono all&#8217;invio di posta elettronica contenente dati di fatturazione. Sarebbe stato un ragionamento che io non condivido ma non scorretto.</p>
<h3>Perché usando il paradigma ad oggetti il codice è più facilmente manutenibile?</h3>
<p>Semplice! Perché se domani mi chiedessero di integrare anche la media del totale delle fatture, tutto quello che dovrò fare sarà aggiungere un metodo alla Classe Fatture che si occupi di calcolare il valore medio delle fatture di quel Cliente, senza dover stare a ricercare in un file unico la parte di codice che si occupa della fattura.</p>
<p>La cosa importante da fare quando si programma a oggetti è dunque quella di capire quali sono i problemi in gioco, strutturare un diagramma che si chiama UML e raccogliere dentro ogni classe tutti i metodi risolutivi dei microproblemi adibiti a quella Classe di problemi. Una volta che il discorso filerà liscio su carta si potrà iniziare a scrivere i commenti (una best practice che puoi <strong><a href="https://renor.it/prima-i-commenti-poi-il-codice/">leggere in questo articolo</a></strong>) e per ultimo stendere il codice. L&#8217;operazione diventerà veramente banale perché il grosso del lavoro è stato fatto prima con carta e penna.</p>
<p>Naturalmente questo non è un corso di programmazione OOP, le regole della programmazione a oggetti sono molte e vanno comprese integralmente: ereditarietà di una classe figlia, classi astratte, interfacce, traits, ecc., ma una volta entrati nell&#8217;ottica di &#8220;Cosa è una Classe&#8221; e &#8220;Perché passare alla programmazione a oggetti&#8221; si tratterà solo di studiare queste regole ed applicarle programmando e continuando a programmare sempre di più. Ti accorgerai, una volta compresa, della facilità concettuale e di come torni veramente utile per organizzare il codice secondo regole facilmente interpretabili anche da un&#8217;altra persona che dovrà rimettere mano al tuo codice.</p>
<p>Come hai visto la programmazione a oggetti è molto più semplice di quello che si dice, non è assolutamente un concetto astratto e terribile che crea blocchi a chi vuole imparare a programmare&#8230; Tutto sta a prenderla nel modo giusto senza stare lì a parlare di Case, Automobili e Moto (questo è l&#8217;esempio più citato), Palazzi, Ricette e chi più ne ha più ne metta.</p>
<p>&nbsp;</p>


<p><strong>[starbox] </strong></p>
<p>L'articolo <a href="https://renor.it/blog/sviluppo-software-programmazione/programmazione-da-procedurale-verso-loop/">Programmazione: da procedurale verso l&#8217;OOP</a> proviene da <a href="https://renor.it">RENOR &amp; Partners S.r.l.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
