Elementi HTML

La classe Nette\Utils\Html è un aiuto per la generazione di codice HTML che previene le vulnerabilità Cross Site Scripting (XSS).

Funziona in modo tale che i suoi oggetti rappresentano elementi HTML, si impostano i loro parametri e si lascia che vengano resi:

$el = Html::el('img'); // crea l'elemento <img>.
$el->src = 'image.jpg'; // imposta l'attributo src
echo $el; // stampa '<img src="immagine.jpg">'

Installazione:

composer require nette/utils

Tutti gli esempi presuppongono che sia definito il seguente alias di classe:

use Nette\Utils\Html;

Creazione di un elemento HTML

L'elemento viene creato con il metodo Html::el():

$el = Html::el('img'); // crea l'elemento <img

Oltre al nome, è possibile inserire altri attributi nella sintassi HTML:

$el = Html::el('input type=text class="red important"');

Oppure passarli come array associativo al secondo parametro:

$el = Html::el('input', [
	'type' => 'text',
	'class' => 'important',
]);

Per modificare e restituire il nome di un elemento:

$el->setName('img');
$el->getName(); // 'img'
$el->isEmpty(); // vero, poiché <img> è un elemento nullo

Attributi HTML

È possibile impostare e ottenere i singoli attributi HTML in tre modi, a seconda di quello che si preferisce. Il primo è attraverso le proprietà:

$el->src = 'image.jpg'; // imposta l'attributo src

echo $el->src; // 'image.jpg'

unset($el->src); // rimuove l'attributo
// oppure $el->src = null;

Il secondo modo è quello di chiamare metodi che, a differenza dell'impostazione delle proprietà, possono essere concatenati tra loro:

$el = Html::el('img')->src('image.jpg')->alt('photo');
// <img src="image.jpg" alt="photo">

$el->alt(null); // rimuove l'attributo

E il terzo modo è il più loquace:

$el = Html::el('img')
	->setAttribute('src', 'image.jpg')
	->setAttribute('alt', 'photo');

echo $el->getAttribute('src'); // 'image.jpg'

$el->removeAttribute('alt');

In blocco, gli attributi possono essere impostati con addAttributes(array $attrs) e cancellati con removeAttributes(array $attrNames).

Il valore di un attributo non deve essere necessariamente una stringa, ma si possono usare anche valori logici per gli attributi logici:

$checkbox = Html::el('input')->type('checkbox');
$checkbox->checked = true;  // <input type="checkbox" checked>
$checkbox->checked = false; // <input type="checkbox">

Un attributo può anche essere un array di token, separati da spazi, adatto per esempio alle classi CSS:

$el = Html::el('input');
$el->class[] = 'active';
$el->class[] = null; // null is ignored
$el->class[] = 'top';
echo $el; // '<input class="active top">'

Un'alternativa è un array associativo, in cui i valori dicono se la chiave deve essere elencata:

$el = Html::el('input');
$el->class['active'] = true;
$el->class['top'] = false;
echo $el; // '<input class="active">'

Gli stili CSS possono essere scritti sotto forma di array associativi:

$el = Html::el('input');
$el->style['color'] = 'green';
$el->style['display'] = 'block';
echo $el; // '<input style="color: green; display: block">'

Abbiamo utilizzato le proprietà, ma la stessa cosa può essere fatta utilizzando i metodi:

$el = Html::el('input');
$el->style('color', 'green');
$el->style('display', 'block');
echo $el; // '<input style="color: green; display: block">'

O anche nel modo più loquace:

$el = Html::el('input');
$el->appendAttribute('style', 'color', 'green');
$el->appendAttribute('style', 'display', 'block');
echo $el; // '<input style="color: green; display: block">'

Un'ultima cosa: il metodo href() può facilitare la composizione dei parametri di query in un URL:

echo Html::el('a')->href('index.php', [
	'id' => 10,
	'lang' => 'en',
]);
// '<a href="index.php?id=10&amp;lang=en"></a>'

Attributi dei dati

Gli attributi dei dati hanno un supporto speciale. Poiché i loro nomi contengono dei trattini, l'accesso tramite proprietà e metodi non è molto elegante, quindi esiste un metodo data():

$el = Html::el('input');
$el->{'data-max-size'} = '500x300'; // non è così elegante
$el->data('max-size', '500x300'); // è elegante
echo $el; // '<input data-max-size="500x300">'

Se il valore dell'attributo dati è un array, viene automaticamente serializzato in JSON:

$el = Html::el('input');
$el->data('items', [1,2,3]);
echo $el; // '<input data-items="[1,2,3]">'

Contenuto dell'elemento

Il contenuto interno dell'elemento viene impostato dai metodi setHtml() o setText(). Usare il primo solo se si sa che si sta passando in modo affidabile una stringa HTML sicura nel parametro.

echo Html::el('span')->setHtml('hello<br>');
// '<span>hello<br></span>'

echo Html::el('span')->setText('10 < 20');
// '<span>10 &lt; 20</span>'

Al contrario, il contenuto interno si ottiene con i metodi getHtml() o getText(). Il secondo rimuove i tag dall'output HTML e converte le entità HTML in caratteri.

echo $el->getHtml(); // '10 &lt; 20'
echo $el->getText(); // '10 < 20'

Nodi figli

Il contenuto interno di un elemento può anche essere un array di figli. Ognuno di essi può essere una stringa o un altro elemento Html. Vengono inseriti usando addHtml() o addText():

$el = Html::el('span')
	->addHtml('hello<br>')
	->addText('10 < 20')
	->addHtml( Html::el('br') );
// <span>hello<br>10 &lt; 20<br></span>

Un altro modo per creare e inserire un nuovo nodo Html:

$ul = Html::el('ul');
$ul->create('li', ['class' => 'first'])
	->setText('hello');
// <ul><li class="first">hello</li></ul>

È possibile lavorare con i nodi come se fossero elementi di un array. Si può quindi accedere ai singoli nodi usando le parentesi quadre, contarli con count() e iterare su di essi:

$el = Html::el('div');
$el[] = '<b>hello</b>';
$el[] = Html::el('span');
echo $el[1]; // '<span></span>'

foreach ($el as $child) { /* ... */ }

echo count($el); // 2

Un nuovo nodo può essere inserito in una posizione specifica usando insert(?int $index, $child, bool $replace = false). Se $replace = false, inserisce l'elemento nella posizione $index e sposta gli altri. Se $index = null, aggiunge un elemento alla fine.

// inserisce l'elemento nella prima posizione e fa avanzare gli altri
$el->insert(0, Html::el('span'));

Tutti i nodi vengono restituiti dal metodo getChildren() e rimossi dal metodo removeChildren().

Creare un frammento di documento

Se si vuole lavorare con una matrice di nodi e non si è interessati all'elemento che li avvolge, si può creare un cosiddetto frammento di documento passando null al posto del nome dell'elemento:

$el = Html::el(null)
	->addHtml('hello<br>')
	->addText('10 < 20')
	->addHtml( Html::el('br') );
// hello<br>10 &lt; 20<br>

I metodi fromHtml() e fromText() offrono un modo più rapido per creare un frammento:

$el = Html::fromHtml('hello<br>');
echo $el; // 'hello<br>'

$el = Html::fromText('10 < 20');
echo $el; // '10 &lt; 20'

Generare l'output HTML

Il modo più semplice per generare un elemento HTML è usare echo o lanciare un oggetto in (string). È anche possibile stampare separatamente i tag e gli attributi di apertura o chiusura:

$el = Html::el('div class=header')->setText('hello');

echo $el;               // '<div class="header"></div>'
$s = (string) $el;      // '<div class="header">hello</div>'
$s = $el->toHtml();     // '<div class="header">hello</div>'
$s = $el->toText();     // 'hello'
echo $el->startTag();   // '<div class="header">'
echo $el->endTag();     // '</div>'
echo $el->attributes(); // 'class="header"'

Una caratteristica importante è la protezione automatica contro il Cross Site Scripting (XSS). Tutti i valori degli attributi o i contenuti inseriti con setText() o addText() vengono evasi in modo affidabile:

echo Html::el('div')
	->title('" onmouseover="bad()')
	->setText('<script>bad()</script>');

// <div title='" onmouseover="bad()'>&lt;script&gt;bad()&lt;/script&gt;</div>

Conversione HTML ↔ Testo

È possibile utilizzare il metodo statico htmlToText() per convertire l'HTML in testo:

echo Html::htmlToText('<span>One &amp; Two</span>'); // 'One & Two'

HtmlStringable

L'oggetto Nette\Utils\Html implementa l'interfaccia Nette\HtmlStringable, che, ad esempio, Latte o i form utilizzano per distinguere gli oggetti che hanno un metodo __toString() che restituisce codice HTML. Quindi il doppio escape non si verifica se, ad esempio, si stampa l'oggetto nel modello usando {$el}.

versione: 4.0