Elementy HTML

Klasa Nette\Utils\Html jest pomocnikiem do generowania kodu HTML, który zapobiega powstawaniu podatności Cross Site Scripting (XSS).

Działa tak, że jej obiekty reprezentują elementy HTML, którym ustawiamy parametry i pozwalamy je wyrenderować:

$el = Html::el('img');  // tworzy element <img>
$el->src = 'image.jpg'; // ustawia atrybut src
echo $el;               // wypisuje '<img src="image.jpg">'

Instalacja:

composer require nette/utils

Wszystkie przykłady zakładają utworzony alias:

use Nette\Utils\Html;

Tworzenie elementu HTML

Element tworzymy metodą Html::el():

$el = Html::el('img'); // tworzy element <img>

Oprócz nazwy możesz podać również inne atrybuty w składni HTML:

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

Lub przekazać je jako tablicę asocjacyjną drugim parametrem:

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

Zmiana i zwrócenie nazwy elementu:

$el->setName('img');
$el->getName(); // 'img'
$el->isEmpty(); // true, ponieważ <img> jest pustym elementem

Atrybuty HTML

Poszczególne atrybuty HTML możemy zmieniać i odczytywać na trzy sposoby, zależy od Ciebie, który bardziej Ci się spodoba. Pierwszy z nich to poprzez właściwości (properties):

$el->src = 'image.jpg'; // ustawia atrybut src

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

unset($el->src);  // usuwa atrybut
// lub $el->src = null;

Drugim sposobem jest wywoływanie metod, które w przeciwieństwie do ustawiania właściwości możemy łączyć w łańcuch:

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

$el->alt(null); // usunięcie atrybutu

A trzeci sposób jest najbardziej “gadatliwy”:

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

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

$el->removeAttribute('alt');

Zbiorczo można ustawić atrybuty za pomocą addAttributes(array $attrs) i usunąć za pomocą removeAttributes(array $attrNames).

Wartością atrybutu nie musi być tylko ciąg znaków, można używać również wartości logicznych dla atrybutów logicznych:

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

Atrybutem może być również tablica wartości, które zostaną wypisane oddzielone spacjami, co przydaje się na przykład dla klas CSS:

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

Alternatywą jest tablica asocjacyjna, gdzie wartości mówią, czy klucz ma być wypisany:

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

Style CSS można zapisywać w formie tablic asocjacyjnych:

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

Teraz używaliśmy właściwości, ale to samo można zapisać za pomocą metod:

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

Lub nawet najbardziej “gadatliwym” sposobem:

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

Jeszcze drobiazg na koniec: metoda href() potrafi ułatwić składanie parametrów query w URL:

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

Atrybuty danych (Data attributes)

Specjalne wsparcie mają atrybuty danych. Ponieważ ich nazwy zawierają myślniki, dostęp przez właściwości i metody nie jest tak elegancki, dlatego istnieje metoda data():

$el = Html::el('input');
$el->{'data-max-size'} = '500x300'; // nie jest tak eleganckie
$el->data('max-size', '500x300'); // jest eleganckie
echo $el; // '<input data-max-size="500x300">'

Jeśli wartością atrybutu danych jest tablica, automatycznie serializuje się do JSON:

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

Zawartość elementu

Wewnętrzną zawartość elementu ustawiamy metodami setHtml() lub setText(). Pierwszej z nich używaj tylko w przypadku, gdy wiesz, że w parametrze przekazujesz niezawodnie bezpieczny ciąg HTML.

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

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

I odwrotnie, wewnętrzną zawartość uzyskamy metodami getHtml() lub getText(). Druga z nich usuwa z wyniku znaczniki HTML i konwertuje encje HTML na znaki.

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

Węzły podrzędne

Wnętrze elementu może być również tablicą węzłów podrzędnych (children). Każdy z nich może być albo ciągiem znaków, albo kolejnym elementem Html. Wstawiamy je za pomocą addHtml() lub addText():

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

Inny sposób na utworzenie i wstawienie nowego węzła Html:

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

Z węzłami można pracować tak samo, jakby były tablicą. Czyli uzyskiwać dostęp do poszczególnych z nich za pomocą nawiasów kwadratowych, liczyć je za pomocą count() i iterować po nich:

$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

Nowy węzeł można wstawić w konkretne miejsce za pomocą insert(?int $index, $child, bool $replace = false). Jeśli $replace = false, wstawia element na pozycję $index i przesuwa pozostałe. Jeśli $index = null, dodaje element na koniec.

// wstawia element na pierwszą pozycję i przesuwa pozostałe
$el->insert(0, Html::el('span'));

Wszystkie węzły uzyskamy metodą getChildren() i usuniemy je metodą removeChildren().

Tworzenie fragmentu dokumentu

Jeśli chcemy pracować z tablicą węzłów i nie interesuje nas element otaczający, możemy utworzyć tzw. fragment dokumentu, przekazując null zamiast nazwy elementu:

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

Szybszy sposób tworzenia fragmentu oferują metody fromHtml() i fromText():

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

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

Generowanie wyniku HTML

Najprostszym sposobem na wypisanie elementu HTML jest użycie echo lub rzutowanie obiektu na (string). Można również osobno wypisać znaczniki otwierające lub zamykające oraz atrybuty:

$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"'

Ważną cechą jest automatyczna ochrona przed Cross Site Scripting (XSS). Wszystkie wartości atrybutów lub zawartość wstawiona przez setText() lub addText() są niezawodnie eskejpowane:

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

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

Konwersja HTML ↔ tekst

Do konwersji HTML na tekst możesz użyć statycznej metody htmlToText():

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

HtmlStringable

Obiekt Nette\Utils\Html implementuje interfejs Nette\HtmlStringable, za pomocą którego na przykład Latte lub formularze rozróżniają obiekty, które mają metodę __toString() zwracającą kod HTML. Dzięki temu nie dojdzie do podwójnego eskejpowania, jeśli na przykład wypiszemy obiekt w szablonie za pomocą {$el}.

wersja: 4.0