HTML елементи
Клас Nette\Utils\Html є помічником для генерації HTML-коду, який запобігає виникненню вразливості Cross Site Scripting (XSS).
Він працює так, що його об'єкти представляють HTML-елементи, яким ми встановлюємо параметри та дозволяємо їх відобразити:
$el = Html::el('img'); // створює елемент <img>
$el->src = 'image.jpg'; // встановлює атрибут src
echo $el; // виводить '<img src="image.jpg">'
Встановлення:
composer require nette/utils
Усі приклади передбачають створений псевдонім:
use Nette\Utils\Html;
Створення HTML-елемента
Елемент створюємо методом Html::el()
:
$el = Html::el('img'); // створює елемент <img>
Крім назви, ви можете вказати й інші атрибути в HTML-синтаксисі:
$el = Html::el('input type=text class="red important"');
Або передати їх як асоціативний масив другим параметром:
$el = Html::el('input', [
'type' => 'text',
'class' => 'important',
]);
Зміна та повернення назви елемента:
$el->setName('img');
$el->getName(); // 'img'
$el->isEmpty(); // true, оскільки <img> є порожнім елементом
HTML атрибути
Окремі HTML-атрибути ми можемо змінювати та читати трьома способами, залежно від того, який вам більше сподобається. Перший з них — через властивості:
$el->src = 'image.jpg'; // встановлює атрибут src
echo $el->src; // 'image.jpg'
unset($el->src); // скасовує атрибут
// або $el->src = null;
Другий шлях — виклик методів, які, на відміну від встановлення властивостей, можна ланцюжком викликати один за одним:
$el = Html::el('img')->src('image.jpg')->alt('photo');
// <img src="image.jpg" alt="photo">
$el->alt(null); // скасування атрибута
А третій спосіб — найбільш багатослівний:
$el = Html::el('img')
->setAttribute('src', 'image.jpg')
->setAttribute('alt', 'photo');
echo $el->getAttribute('src'); // 'image.jpg'
$el->removeAttribute('alt');
Масово атрибути можна встановити за допомогою addAttributes(array $attrs)
та видалити за допомогою removeAttributes(array $attrNames)
.
Значенням атрибута не обов'язково має бути рядок, можна використовувати й логічні значення для логічних атрибутів:
$checkbox = Html::el('input')->type('checkbox');
$checkbox->checked = true; // <input type="checkbox" checked>
$checkbox->checked = false; // <input type="checkbox">
Атрибутом може бути й масив значень, які будуть виведені, розділені пробілами, що зручно, наприклад, для CSS-класів:
$el = Html::el('input');
$el->class[] = 'active';
$el->class[] = null; // null ігнорується
$el->class[] = 'top';
echo $el; // '<input class="active top">'
Альтернативою є асоціативний масив, де значення вказують, чи має бути виведений ключ:
$el = Html::el('input');
$el->class['active'] = true;
$el->class['top'] = false;
echo $el; // '<input class="active">'
CSS-стилі можна записувати у формі асоціативних масивів:
$el = Html::el('input');
$el->style['color'] = 'green';
$el->style['display'] = 'block';
echo $el; // '<input style="color: green; display: block">'
Зараз ми використовували властивості, але те саме можна записати за допомогою методів:
$el = Html::el('input');
$el->style('color', 'green');
$el->style('display', 'block');
echo $el; // '<input style="color: green; display: block">'
Або навіть найбільш багатослівним способом:
$el = Html::el('input');
$el->appendAttribute('style', 'color', 'green');
$el->appendAttribute('style', 'display', 'block');
echo $el; // '<input style="color: green; display: block">'
Ще одна дрібниця на завершення: метод href()
може полегшити
складання параметрів запиту в URL:
echo Html::el('a')->href('index.php', [
'id' => 10,
'lang' => 'en',
]);
// '<a href="index.php?id=10&lang=en"></a>'
Data атрибути
Спеціальну підтримку мають data-атрибути. Оскільки їхні назви містять
дефіси, доступ через властивості та методи не такий елегантний, тому
існує метод data()
:
$el = Html::el('input');
$el->{'data-max-size'} = '500x300'; // не так елегантно
$el->data('max-size', '500x300'); // елегантно
echo $el; // '<input data-max-size="500x300">'
Якщо значенням data-атрибута є масив, він автоматично серіалізується в JSON:
$el = Html::el('input');
$el->data('items', [1,2,3]);
echo $el; // '<input data-items="[1,2,3]">'
Вміст елемента
Внутрішній вміст елемента встановлюємо методами setHtml()
чи
setText()
. Перший з них використовуйте лише в тому випадку, якщо ви
знаєте, що в параметрі передаєте надійно безпечний HTML-рядок.
echo Html::el('span')->setHtml('hello<br>');
// '<span>hello<br></span>'
echo Html::el('span')->setText('10 < 20');
// '<span>10 < 20</span>'
І навпаки, внутрішній вміст отримуємо методами getHtml()
чи
getText()
. Другий з них видаляє з виводу HTML-теги та перетворює
HTML-сутності на символи.
echo $el->getHtml(); // '10 < 20'
echo $el->getText(); // '10 < 20'
Дочірні вузли
Внутрішній вміст елемента може бути також масивом дочірніх (children)
вузлів. Кожен з них може бути або рядком, або іншим Html
елементом.
Вставляємо їх за допомогою addHtml()
чи addText()
:
$el = Html::el('span')
->addHtml('hello<br>')
->addText('10 < 20')
->addHtml( Html::el('br') );
// <span>hello<br>10 < 20<br></span>
Інший спосіб для створення та вставки нового Html
вузла:
$ul = Html::el('ul');
$ul->create('li', ['class' => 'first'])
->setText('перший');
// <ul><li class="first">перший</li></ul>
З вузлами можна працювати так само, якби це був масив. Тобто
звертатися до окремих з них за допомогою квадратних дужок,
підраховувати їх за допомогою count()
та ітерувати по них:
$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
Новий вузол можна вставити на конкретне місце за допомогою
insert(?int $index, $child, bool $replace = false)
. Якщо $replace = false
, він вставить
елемент на позицію $index
та зсуне інші. Якщо $index = null
,
додасть елемент в кінець.
// вставить елемент на першу позицію та зсуне інші
$el->insert(0, Html::el('span'));
Усі вузли отримуємо методом getChildren()
та видаляємо їх методом
removeChildren()
.
Створення фрагмента документа
Якщо ми хочемо працювати з масивом вузлів і нас не цікавить
обгортаючий елемент, ми можемо створити так званий document fragment,
передавши null
замість імені елемента:
$el = Html::el(null)
->addHtml('hello<br>')
->addText('10 < 20')
->addHtml( Html::el('br') );
// hello<br>10 < 20<br>
Швидший спосіб створення фрагмента пропонують методи fromHtml()
та fromText()
:
$el = Html::fromHtml('hello<br>');
echo $el; // 'hello<br>'
$el = Html::fromText('10 < 20');
echo $el; // '10 < 20'
Генерація HTML-виводу
Найпростішим способом вивести HTML-елемент є використання echo
або перетворення об'єкта на (string)
. Можна також окремо вивести
відкриваючі або закриваючі теги та атрибути:
$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"'
Важливою рисою є автоматичний захист від Cross Site Scripting (XSS). Усі значення
атрибутів або вміст, вставлений через setText()
чи addText()
,
надійно екрануються:
echo Html::el('div')
->title('" onmouseover="bad()')
->setText('<script>bad()</script>');
// <div title='" onmouseover="bad()'><script>bad()</script></div>
Перетворення HTML ↔ текст
Для перетворення HTML на текст ви можете використати статичний метод
htmlToText()
:
echo Html::htmlToText('<span>One & Two</span>'); // 'One & Two'
HtmlStringable
Об'єкт Nette\Utils\Html
реалізує інтерфейс Nette\HtmlStringable
, за
допомогою якого, наприклад, Latte або форми розрізняють об'єкти, що мають
метод __toString()
, який повертає HTML-код. Таким чином, не відбувається
подвійного екранування, якщо, наприклад, ми виводимо об'єкт у шаблоні
за допомогою {$el}
.