Elementi HTML
La classe Nette\Utils\Html è uno strumento di supporto per generare codice HTML che aiuta a prevenire le vulnerabilità Cross-Site Scripting (XSS).
Funziona così: i suoi oggetti rappresentano elementi HTML, ne impostate i parametri e poi li disegnate:
$el = Html::el('img'); // crea l'elemento <img>
$el->src = 'image.jpg'; // imposta l'attributo src
echo $el; // stampa '<img src="image.jpg">'
Potete riempire il corpo di un elemento con testo e altri elementi usando il metodo add(). Il
testo subisce automaticamente l'escaping, gli elementi vengono inseriti così come sono:
echo Html::el('div')->add(
'Hello ',
Html::el('b')->setText('world'),
);
// '<div>Hello <b>world</b></div>'
Installazione:
composer require nette/utils
Tutti gli esempi presuppongono che sia definito questo alias di classe:
use Nette\Utils\Html;
Creare un elemento HTML
Un elemento si crea con il metodo Html::el():
$el = Html::el('img'); // crea l'elemento <img>
Oltre al nome potete indicare anche altri attributi con la sintassi HTML:
$el = Html::el('input type=text class="red important"');
Oppure passarli come array associativo nel secondo parametro:
$el = Html::el('input', [
'type' => 'text',
'class' => 'important',
]);
Per cambiare e ottenere il nome dell'elemento:
$el->setName('img');
$el->getName(); // 'img'
$el->isEmpty(); // true, perché <img> è un elemento void
Attributi HTML
I singoli attributi HTML si possono impostare e leggere in tre modi; sta a voi scegliere quello che preferite. Il primo è tramite 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 è chiamare dei metodi che, a differenza dell'impostazione delle proprietà, si possono concatenare:
$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ù prolisso:
$el = Html::el('img')
->setAttribute('src', 'image.jpg')
->setAttribute('alt', 'photo');
echo $el->getAttribute('src'); // 'image.jpg'
$el->removeAttribute('alt');
Gli attributi si possono impostare in blocco con addAttributes(array $attrs) e rimuovere con
removeAttributes(array $attrNames).
Il valore di un attributo non deve essere per forza una stringa; per gli attributi booleani si possono usare valori booleani:
$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 valori, che vengono stampati separati da spazi. È utile per esempio per le classi CSS:
$el = Html::el('input');
$el->class[] = 'active';
$el->class[] = null; // null viene ignorato
$el->class[] = 'top';
echo $el; // '<input class="active top">'
Un'alternativa è un array associativo, in cui i valori indicano se la chiave va inclusa:
$el = Html::el('input');
$el->class['active'] = true;
$el->class['top'] = false;
echo $el; // '<input class="active">'
Gli stili CSS si possono scrivere come array associativi:
$el = Html::el('input');
$el->style['color'] = 'green';
$el->style['display'] = 'block';
echo $el; // '<input style="color:green;display:block">'
Finora abbiamo usato le proprietà, ma lo stesso risultato si ottiene con i metodi:
$el = Html::el('input');
$el->style('color', 'green');
$el->style('display', 'block');
echo $el; // '<input style="color:green;display:block">'
Oppure anche nel modo più prolisso:
$el = Html::el('input');
$el->appendAttribute('style', 'color', 'green');
$el->appendAttribute('style', 'display', 'block');
echo $el; // '<input style="color:green;display:block">'
Un ultimo dettaglio: il metodo href() può semplificare la composizione dei parametri della query di un URL:
echo Html::el('a')->href('index.php', [
'id' => 10,
'lang' => 'en',
]);
// '<a href="index.php?id=10&lang=en"></a>'
Attributi data
Gli attributi data hanno un supporto particolare. Poiché i loro nomi contengono trattini, accedervi tramite proprietà e
metodi non è altrettanto elegante, quindi esiste un metodo dedicato data():
$el = Html::el('input');
$el->{'data-max-size'} = '500x300'; // non altrettanto elegante
$el->data('max-size', '500x300'); // è elegante
echo $el; // '<input data-max-size="500x300">'
Se il valore di un attributo data è un array, viene serializzato automaticamente 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 si imposta con i metodi setHtml() o setText(). Usate il primo
solo se siete sicuri che il parametro contenga una stringa HTML affidabile e sicura.
echo Html::el('span')->setHtml('hello<br>');
// '<span>hello<br></span>'
echo Html::el('span')->setText('10 < 20');
// '<span>10 < 20</span>'
Al contrario, il contenuto interno si può leggere con i metodi getHtml() o getText(). Il secondo
rimuove i tag HTML dal contenuto e riconverte le entità HTML in caratteri.
echo $el->getHtml(); // '10 < 20'
echo $el->getText(); // '10 < 20'
Nodi figli
Il contenuto interno di un elemento può essere anche un array di nodi figli. Ogni figlio può essere una stringa oppure un
altro oggetto Html. Si aggiungono con addHtml() o addText():
$el = Html::el('span')
->addHtml('hello<br>')
->addText('10 < 20')
->addHtml( Html::el('br') );
// <span>hello<br>10 < 20<br></span>
Il metodo add() inserisce più figli in una volta sola. Le stringhe subiscono l'escaping come
con addText(), gli oggetti Html vengono inseriti così come sono e i valori null vengono
saltati, il che torna comodo per il contenuto condizionale. Racchiudete in Html::html() una stringa che sia HTML
affidabile e sicuro:
$el = Html::el('span')->add(
'10 < 20',
Html::el('br'),
Html::html('hello<br>'),
$showNote ? Html::el('small')->setText('note') : null,
);
// <span>10 < 20<br>hello<br><small>note</small></span>
Un altro modo di creare e inserire un nuovo nodo Html:
$ul = Html::el('ul');
$ul->create('li', ['class' => 'first'])
->setText('first');
// <ul><li class="first">first</li></ul>
Potete lavorare con i nodi come se fossero elementi di un array. Potete cioè accedere ai singoli nodi con le parentesi
quadre, contarli con count() e scorrerli:
$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 si può inserire in una posizione precisa con insert(?int $index, $child, bool $replace = false). Se
$replace = false, inserisce l'elemento nella posizione $index e sposta gli altri. Se
$index = null, aggiunge l'elemento alla fine.
// inserisce l'elemento nella prima posizione e sposta gli altri
$el->insert(0, Html::el('span'));
Tutti i nodi si possono ottenere con il metodo getChildren() e rimuovere con il metodo
removeChildren().
Creare un frammento di documento
Se volete lavorare con un insieme di nodi senza curarvi di un elemento che li racchiuda, potete creare un
frammento di documento. Disegna solo i propri figli, senza un tag proprio. Il metodo fragment() lo crea e
lo riempie di figli in un'unica chiamata, seguendo le stesse regole di add():
echo Html::fragment(
Html::el('strong')->setText('hello'),
'10 < 20',
Html::el('br'),
);
// <strong>hello</strong>10 < 20<br>
Un frammento con contenuto di solo testo o di solo HTML si crea con i metodi text() e
html():
echo Html::text('10 < 20'); // '10 < 20'
echo Html::html('hello<br>'); // 'hello<br>'
Se dovete supportare le versioni precedenti alla 4.1.5, create un frammento passando null al posto del nome
dell'elemento e riempitelo con addHtml() e addText(). Al posto di text() e
html(), queste versioni offrono i metodi fromText() e fromHtml(), che funzionano ancora ma
sono deprecati:
$el = Html::el(null)
->addHtml('hello<br>')
->addText('10 < 20');
// hello<br>10 < 20
echo Html::fromText('10 < 20'); // '10 < 20'
echo Html::fromHtml('hello<br>'); // 'hello<br>'
Generare l'output HTML
Il modo più semplice di stampare un elemento HTML è usare echo oppure convertire l'oggetto con
(string). Potete anche stampare separatamente il tag di apertura, quello di chiusura e gli attributi:
$el = Html::el('div class=header')->setText('hello');
echo $el; // '<div class="header">hello</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"'
Il metodo render(?int $indent = null) offre una stampa formattata. Se passate un livello di indentazione, l'output
viene indentato in modo ordinato su più righe:
echo $el->render(0); // restituisce l'HTML indentato
Una caratteristica importante è la protezione automatica contro il Cross-Site Scripting (XSS). Tutti i valori degli
attributi e il contenuto inserito con setText(), addText(), add() o fragment()
subiscono un escaping affidabile:
echo Html::el('div')
->title('" onmouseover="bad()')
->setText('<script>bad()</script>');
// <div title='" onmouseover="bad()'><script>bad()</script></div>
Conversione HTML ↔ testo
Per convertire l'HTML in testo potete usare il metodo statico htmlToText():
echo Html::htmlToText('<span>One & Two</span>'); // 'One & Two'
HtmlStringable
L'oggetto Nette\Utils\Html implementa l'interfaccia Nette\HtmlStringable. Latte e Forms usano questa
interfaccia, per esempio, per distinguere gli oggetti che hanno un metodo __toString() che restituisce codice HTML.
In questo modo si evita il doppio escaping se, per esempio, stampate l'oggetto in un template con {$el}.