Elementos HTML
A classe Nette\Utils\Html é um auxiliar para gerar código HTML que previne a vulnerabilidade de Cross Site Scripting (XSS).
Funciona de tal forma que seus objetos representam elementos HTML, aos quais definimos parâmetros e os deixamos renderizar:
$el = Html::el('img'); // cria o elemento <img>
$el->src = 'image.jpg'; // define o atributo src
echo $el; // imprime '<img src="image.jpg">'
Instalação:
composer require nette/utils
Todos os exemplos pressupõem a criação de um alias:
use Nette\Utils\Html;
Criação de elementos HTML
Criamos um elemento usando o método Html::el()
:
$el = Html::el('img'); // cria o elemento <img>
Além do nome, você também pode especificar outros atributos na sintaxe HTML:
$el = Html::el('input type=text class="red important"');
Ou passá-los como um array associativo no segundo parâmetro:
$el = Html::el('input', [
'type' => 'text',
'class' => 'important',
]);
Alterando e retornando o nome do elemento:
$el->setName('img');
$el->getName(); // 'img'
$el->isEmpty(); // true, pois <img> é um elemento vazio
Atributos HTML
Podemos alterar e ler atributos HTML individuais de três maneiras; depende de você qual delas prefere. A primeira é através de propriedades:
$el->src = 'image.jpg'; // define o atributo src
echo $el->src; // 'image.jpg'
unset($el->src); // remove o atributo
// ou $el->src = null;
A segunda maneira é chamando métodos, que, ao contrário da definição de propriedades, podemos encadear:
$el = Html::el('img')->src('image.jpg')->alt('photo');
// <img src="image.jpg" alt="photo">
$el->alt(null); // remoção do atributo
E a terceira maneira é a mais verbosa:
$el = Html::el('img')
->setAttribute('src', 'image.jpg')
->setAttribute('alt', 'photo');
echo $el->getAttribute('src'); // 'image.jpg'
$el->removeAttribute('alt');
Atributos podem ser definidos em massa usando addAttributes(array $attrs)
e removidos usando
removeAttributes(array $attrNames)
.
O valor de um atributo não precisa ser apenas uma string; valores booleanos também podem ser usados para atributos lógicos:
$checkbox = Html::el('input')->type('checkbox');
$checkbox->checked = true; // <input type="checkbox" checked>
$checkbox->checked = false; // <input type="checkbox">
Um atributo também pode ser um array de valores, que serão impressos separados por espaços, o que é útil, por exemplo, para classes CSS:
$el = Html::el('input');
$el->class[] = 'active';
$el->class[] = null; // null é ignorado
$el->class[] = 'top';
echo $el; // '<input class="active top">'
Uma alternativa é um array associativo, onde os valores indicam se a chave deve ser impressa:
$el = Html::el('input');
$el->class['active'] = true;
$el->class['top'] = false;
echo $el; // '<input class="active">'
Estilos CSS podem ser escritos na forma de arrays associativos:
$el = Html::el('input');
$el->style['color'] = 'green';
$el->style['display'] = 'block';
echo $el; // '<input style="color: green; display: block">'
Até agora usamos propriedades, mas o mesmo pode ser escrito usando métodos:
$el = Html::el('input');
$el->style('color', 'green');
$el->style('display', 'block');
echo $el; // '<input style="color: green; display: block">'
Ou mesmo da maneira mais verbosa:
$el = Html::el('input');
$el->appendAttribute('style', 'color', 'green');
$el->appendAttribute('style', 'display', 'block');
echo $el; // '<input style="color: green; display: block">'
Um pequeno detalhe para concluir: o método href()
pode facilitar a composição de parâmetros de consulta
na URL:
echo Html::el('a')->href('index.php', [
'id' => 10,
'lang' => 'en',
]);
// '<a href="index.php?id=10&lang=en"></a>'
Atributos de dados
Atributos de dados têm suporte especial. Como seus nomes contêm hífens, o acesso via propriedades e métodos não é tão
elegante, por isso existe o método data()
:
$el = Html::el('input');
$el->{'data-max-size'} = '500x300'; // não é tão elegante
$el->data('max-size', '500x300'); // é elegante
echo $el; // '<input data-max-size="500x300">'
Se o valor de um atributo de dados for um array, ele é automaticamente serializado para JSON:
$el = Html::el('input');
$el->data('items', [1,2,3]);
echo $el; // '<input data-items="[1,2,3]">'
Conteúdo do elemento
Definimos o conteúdo interno do elemento usando os métodos setHtml()
ou setText()
. Use o primeiro
apenas se souber que está passando uma string HTML confiavelmente segura no parâmetro.
echo Html::el('span')->setHtml('hello<br>');
// '<span>hello<br></span>'
echo Html::el('span')->setText('10 < 20');
// '<span>10 < 20</span>'
E, inversamente, obtemos o conteúdo interno usando os métodos getHtml()
ou getText()
. O segundo
remove tags HTML da saída e converte entidades HTML em caracteres.
echo $el->getHtml(); // '10 < 20'
echo $el->getText(); // '10 < 20'
Nós filhos
O interior de um elemento também pode ser um array de nós filhos (children). Cada um deles pode ser uma string ou outro
elemento Html
. Nós os inserimos usando addHtml()
ou addText()
:
$el = Html::el('span')
->addHtml('hello<br>')
->addText('10 < 20')
->addHtml( Html::el('br') );
// <span>hello<br>10 < 20<br></span>
Outra maneira de criar e inserir um novo nó Html
:
$ul = Html::el('ul');
$ul->create('li', ['class' => 'first'])
->setText('primeiro');
// <ul><li class="first">primeiro</li></ul>
É possível trabalhar com nós da mesma forma que com arrays. Ou seja, acessar nós individuais usando colchetes, contá-los
usando count()
e iterar sobre eles:
$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
Um novo nó pode ser inserido em uma posição específica usando
insert(?int $index, $child, bool $replace = false)
. Se $replace = false
, ele insere o elemento na
posição $index
e desloca os outros. Se $index = null
, ele adiciona o elemento ao final.
// insere o elemento na primeira posição e desloca os outros
$el->insert(0, Html::el('span'));
Obtemos todos os nós usando o método getChildren()
e os removemos usando o método
removeChildren()
.
Criação de fragmento de documento
Se quisermos trabalhar com um array de nós e não nos importarmos com o elemento envolvente, podemos criar um chamado
fragmento de documento passando null
em vez do nome do elemento:
$el = Html::el(null)
->addHtml('hello<br>')
->addText('10 < 20')
->addHtml( Html::el('br') );
// hello<br>10 < 20<br>
Uma maneira mais rápida de criar um fragmento é oferecida pelos métodos fromHtml()
e
fromText()
:
$el = Html::fromHtml('hello<br>');
echo $el; // 'hello<br>'
$el = Html::fromText('10 < 20');
echo $el; // '10 < 20'
Geração de saída HTML
A maneira mais simples de imprimir um elemento HTML é usar echo
ou converter o objeto para
(string)
. Também é possível imprimir separadamente as tags de abertura ou fechamento e os atributos:
$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"'
Uma característica importante é a proteção automática contra Cross Site Scripting (XSS). Todos os valores de
atributos ou conteúdo inserido via setText()
ou addText()
são escapados de forma confiável:
echo Html::el('div')
->title('" onmouseover="bad()')
->setText('<script>bad()</script>');
// <div title='" onmouseover="bad()'><script>bad()</script></div>
Conversão HTML ↔ texto
Para converter HTML em texto, você pode usar o método estático htmlToText()
:
echo Html::htmlToText('<span>One & Two</span>'); // 'One & Two'
HtmlStringable
O objeto Nette\Utils\Html
implementa a interface Nette\HtmlStringable
, que, por exemplo, o Latte ou
os formulários usam para distinguir objetos que possuem um método __toString()
retornando código HTML. Assim, não
ocorrerá dupla escapagem se, por exemplo, imprimirmos o objeto em um template usando {$el}
.