Nette\Utils\Html – coder's little helper

16 years ago by David Grudl  

A small class for generating secure HTML code in PHP.

There are many ways to generate page code in PHP. From simply calling the echo command, to using templates, to using DOM functions. The search for the best method leads to a paradox. The more experienced a programmer is, the more they gravitate towards cleaner solutions, ideally based on the XML DOM. However, he starts to encounter new obstacles and constraints and spends more time solving and circumventing them. This makes it gravitate more towards pragmatic solutions such as echo calling.

But judging the suitability of each method is beyond the scope of this article.

Meet Nette\Utils\Html

Generating HTML tags “on your knee” leads to cluttered code and thus more error-prone code. Let's take a simple example:

echo '<select name="person"',
	($required ? ' class="required"' : ''), '>';
$res = dibi::query('SELECT * FROM [persons] WHERE [group]=%s', $group);
while ($rec = $res->fetch())
	echo '<option value="', $rec['id'], '"',
		($active == $rec['id'] ? ' selected="selected"' : ''),
		'>', htmlSpecialChars($rec['name']),
		'</option>';

echo '</select>';

Not much, right? The notation can be greatly simplified using the Nette\Utils\Html helper. It is a small library that is designed to object-encapsulate HTML elements and, most importantly, offer the programmer an intuitive and handy interface.

First, a small taste:

use Nette\Utils\Html;

//  we create an element
$el = Html::el('img');

// set attributes
$el->src = 'image.gif';
$el->alt = $text;

// and print it
echo $el; // <img src="image.gif" alt="...">

Attributes can also be set by calling overloaded methods, for example $el->src('image.gif'). Here, chain calls (fluent interfaces) can be used advantageously:

$params['id'] = 10;
echo Html::el('a')->href('https://nette.org', $params)->setText('Click here');
// vypíše <a href="https://nette.org?id=10">Click here</a>

echo Html::el('img')->src($url)->alt($alt);

echo Html::el('input')->type($secret ? 'password' : 'text');

However, an attribute need not be just a string:

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

// attribute is cleared by value null
$el->type = null;	// <input />

// even an array can be used
$el->class[] = $active ? 'active' : null; // null is ignored
$el->class[] = 'top';

$el->style['color'] = 'green';
$el->style['display'] = 'block';

echo $el;
// <input class="active top" style="color: green; display: block" />

Sometimes I prefer to list the start and end tags separately instead of the whole element:

$el = Html::el('div');
echo $el->startTag();
...
... we are now printing the contents
...
echo $el->endTag();

// or set the content of the element directly
$el->setText('Barnes & Noble');
echo $el; // <div>Barnes &amp; Noble</div>

// or HTML content
$el->setHtml('Barnes &amp; Noble');
echo $el; // <div>Barnes &amp; Noble</div>

Children and grandchildren

The family is the basis of the HTML state and therefore each Nette\Utils\Html node can have children:

$el = Html::el();
// note $el is now a "container", i.e. without an element name

// and create a descendant, element strong
$strong = $el->create('strong');
$strong->setText('La Trine');
// or you can write:
// $el->create('strong', 'La Trine');

// existing Html nodes can be added
$br = Html::el('br');
$el->add($br);

echo $el; //  <strong>La Trine</strong><br />

// node can also be text
$el->add('Yes!'); // obdoba setText

// descendants can be accessed directly via ArrayAccess
$el[] = 'Hello!';

if ($el->count()) ...

And back to the example

Finally, I'll rewrite the introductory example in a clearer form using Nette\Utils\Html:

$el = Html::el('select')->name('person')->class($required ? 'required' : null)

$res = dibi::query('SELECT * FROM [persons] WHERE [group]=%s', $group);
while ($rec = $res->fetch())
	$el->create('option')
		->value($rec['id'])
		->selected($active == $rec['id'])
		->setText($rec['name']);

echo $el;