Elementy HTML
Klasa Nette\Utils\Html jest pomocnikiem do generowania kodu HTML, który nie pozwala na wykorzystanie luk Cross Site Scripting (XSS).
Sposób, w jaki działa, polega na tym, że jego obiekty są elementami HTML, do których ustawiamy parametry i pozwalamy im renderować:
$el = Html::el('img'); // tworzy element <img>
$el->src = 'image.jpg'; // ustawia atrybut src
echo $el; // drukuje '<img src="image.jpg">'
Instalacja:
composer require nette/utils
Wszystkie przykłady zakładają, że alias został utworzony:
use Nette\Utils\Html;
Tworzenie elementu HTML
Utwórz element za pomocą metody Html::el()
:
$el = Html::el('img'); // tworzy element <img>
Oprócz nazwy można określić inne atrybuty w składni HTML:
$el = Html::el('input type=text class="red important"');
Lub przekazać je jako pole asocjacyjne z drugim parametrem:
$el = Html::el('input', [
'type' => 'text',
'class' => 'important',
]);
Zmień i zwróć nazwę elementu:
$el->setName('img');
$el->getName(); // 'img'
$el->isEmpty(); // prawda, ponieważ <img> jest pustym elementem
Atrybuty HTML
Istnieją trzy sposoby zmiany i odczytu poszczególnych atrybutów HTML, to od Ciebie zależy, który z nich bardziej Ci się podoba. Pierwszy z nich odbywa się za pośrednictwem nieruchomości:
$el->src = 'image.jpg'; // ustawia atrybut src
echo $el->src; // 'image.jpg'
unset($el->src); // unset atrybut
// lub $el->src = null;
Drugim sposobem jest wywołanie metod, które w przeciwieństwie do ustawiania właściwości można łączyć w łańcuchy:
$el = Html::el('img')->src('image.jpg')->alt('photo');
// <img src="image.jpg" alt="photo">
$el->alt(null); // anulowanie atrybutu
A trzeci sposób jest najbardziej dosłowny:
$el = Html::el('img')
->setAttribute('src', 'image.jpg')
->setAttribute('alt', 'photo');
echo $el->getAttribute('src'); // 'image.jpg'
$el->removeAttribute('alt');
Atrybuty mogą być ustawiane masowo za pomocą addAttributes(array $attrs)
i usuwane za pomocą
removeAttributes(array $attrNames)
.
Wartość atrybutu nie musi być ciągiem znaków, możesz również użyć 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">
Atrybut może być również tablicą wartości, które są wypisane oddzielone spacjami, co jest przydatne na przykład dla klas CSS:
$el = Html::el('input');
$el->class[] = 'active';
$el->class[] = null; // null se ignoruje
$el->class[] = 'top';
echo $el; // '<input class="active top">'
Alternatywą jest tablica asocjacyjna, gdzie wartości mówią, czy klucz powinien być wyprowadzony:
$el = Html::el('input');
$el->class['active'] = true;
$el->class['top'] = false;
echo $el; // '<input class="active">'
Style CSS mogą być zapisane jako pola asocjacyjne:
$el = Html::el('input');
$el->style['color'] = 'green';
$el->style['display'] = 'block';
echo $el; // '<input style="color: green; display: block">'
Teraz użyliśmy właściwości, ale to samo można napisać używając metod:
$el = Html::el('input');
$el->style('color', 'green');
$el->style('display', 'block');
echo $el; // '<input style="color: green; display: block">'
Albo nawet w najbardziej dosadny sposób:
$el = Html::el('input');
$el->appendAttribute('style', 'color', 'green');
$el->appendAttribute('style', 'display', 'block');
echo $el; // '<input style="color: green; display: block">'
Jeden ostatni szczegół: metoda href()
może ułatwić komponowanie parametrów zapytania w adresach URL:
echo Html::el('a')->href('index.php', [
'id' => 10,
'lang' => 'en',
]);
// '<a href="index.php?id=10&lang=en"></a>'
Atrybuty danych
Atrybuty danych mają specjalne wsparcie. Ponieważ ich nazwy zawierają myślniki, dostęp poprzez właściwości i metody
nie jest tak elegancki, więc istnieje metoda data()
:
$el = Html::el('input');
$el->{'data-max-size'} = '500x300'; // nie tak elegancko
$el->data('max-size', '500x300'); // jest elegancko
echo $el; // '<input data-max-size="500x300">'
Jeśli wartość atrybutu danych jest tablicą, jest ona automatycznie serializowana do JSON:
$el = Html::el('input');
$el->data('items', [1,2,3]);
echo $el; // '<input data-items="[1,2,3]">'
Zawartość elementu
Ustawia wewnętrzną zawartość elementu metodami setHtml()
lub setText()
. Użyj tego pierwszego
tylko wtedy, gdy wiesz, że przekazujesz niezawodnie bezpieczny ciąg HTML w parametrze.
echo Html::el('span')->setHtml('hello<br>');
// '<span>hello<br></span>'
echo Html::el('span')->setText('10 < 20');
// '<span>10 < 20</span>'
I odwrotnie, użyj metod getHtml()
lub getText()
, aby uzyskać wewnętrzną zawartość. Ta ostatnia
metoda usuwa znaczniki HTML z wyjścia i konwertuje encje HTML na znaki.
echo $el->getHtml(); // '10 < 20'
echo $el->getText(); // '10 < 20'
Węzły dzieci
Wnętrze elementu może być również tablicą węzłów dziecięcych. Każdy z nich może być ciągiem znaków lub innym
elementem Html
. Wstawia się je za pomocą strony addHtml()
lub addText()
:
$el = Html::el('span')
->addHtml('hello<br>')
->addText('10 < 20')
->addHtml( Html::el('br') );
// <span>hello<br>10 < 20<br></span>
Inny sposób tworzenia i wstawiania nowego węzła Html
:
$ul = Html::el('ul');
$ul->create('li', ['class' => 'first'])
->setText('hello');
// <ul><li class="first">hello</li></ul>
Możesz pracować z węzłami tak, jakby były tablicami. To znaczy, uzyskać dostęp do każdego z nich za pomocą nawiasów
kwadratowych, policzyć je za pomocą count()
i iterować nad nimi:
$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że być wstawiony w określonym miejscu za pomocą
insert(?int $index, $child, bool $replace = false)
. Jeśli $replace = false
, to wstawi element na
pozycji $index
i przesunie pozostałe. Jeśli jest to $index = null
, to dodaje element jako
ostatni.
// wstawia element na pierwszej pozycji i przesuwa pozostałe
$el->insert(0, Html::el('span'));
Wszystkie węzły są pobierane za pomocą metody getChildren()
i usuwane za pomocą metody
removeChildren()
.
Tworzenie fragmentu dokumentu
Jeśli chcemy pracować z tablicą węzłów i nie interesuje nas element wrapper, możemy stworzyć 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 < 20<br>
Metody fromHtml()
i fromText()
oferują szybszy sposób tworzenia fragmentu:
$el = Html::fromHtml('hello<br>');
echo $el; // 'hello<br>'
$el = Html::fromText('10 < 20');
echo $el; // '10 < 20'
Generowanie danych wyjściowych HTML
Najprostszym sposobem wyprowadzenia elementu HTML jest użycie echo
lub przepisanie obiektu na
(string)
. Można również wyprowadzić osobno 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). Wszelkie wartości atrybutów lub treści wstawiane za pośrednictwem setText()
lub
addText()
są niezawodnie unikane:
echo Html::el('div')
->title('" onmouseover="bad()')
->setText('<script>bad()</script>');
// <div title='" onmouseover="bad()'><script>bad()</script></div>
Konwersja HTML ↔ tekst
Do konwersji HTML na tekst można użyć statycznej metody htmlToText()
:
echo Html::htmlToText('<span>One & Two</span>'); // 'One & Two'
HtmlStringable
Obiekt Nette\Utils\Html
implementuje interfejs Nette\HtmlStringable
, który jest używany np. przez
Latte lub formularze do rozróżniania obiektów posiadających metodę __toString()
zwracającą kod HTML. Tak więc
nie będzie podwójnego escapingu, jeśli na przykład obiekt zostanie wypisany w szablonie za pomocą {$el}
.