htmltag.module: html tag attributes as a PHP object.

Events happening in the community are now at Drupal community events on www.drupal.org.
donquixote's picture

I am using this quite a lot on custom modules already.

<?php
$li_attr
= htmltag_attributes();
$li_attr->addClass('menu-item');
$li_html = $li_attr->LI($link_text);
$ul_attr = htmltag_attributes();
$ul_attr->addClass('menu');
$ul_html = $ul_attr->UL($li_html);
// or using __toString()
$ul_html = "<ul $ul_attr>$li_html</ul>";
?>

What is the benefit compared to string html definition?
- automated drupal_attributes() and check_plain().
- no more explode(' ') and implode(' ') to deal with classes.
- dedicated methods for classes, inline styles, etc. (ok, I don't care much about styles)
- looks nicer in code that is not a template file.
- attribute objects can be passed around to other functions. The attribute values can be defined in a different place, than the output.
- separate attributes assembly from DOM structure and content assembly

Some design considerations:

Attributes object vs tag object.

The object stores a collection of attributes. It does not store the tag name or the html contents.
This allows to render the same attributes either as a 'LI' or as a 'TR', for instance. The tag name is chosen by the function that assembles the html, not by the code that assembles the attributes.
The same for the html contents of a tag: Typically, these are only determined shortly before the tag is to be rendered, so there is no real benefit passing it around with the attribute values.

Special attributes

For most attributes, a generic setAttr() and getAttr() (or just jquery-style attr()) should be enough. Internally these will be represented as simple strings.
However, some attributes have a special 'type':

  • class: This has a special behavior with addClass() and removeClass(), hasClass(). Internally, best stored as a key/value map (value being identical with the key).
  • style: Internally to be stored as a key/value map. To be modified jQuery-style with $attr->css($key, $value);.
  • src, href, action: these behave like urls. They can have an array of query params, a hash, they can have a drupal system path and a language. Should this be supported in the attributes object, or is the usual setAttr() enough?

I found that the most relevant and useful is really just the classes attribute.
So, is it worth the trouble and bloat, to support special behaviors for src, href, action and style?
Should we provide an api for custom attribute types?

Modular architecture?

If we limit it to setAttr() and addClass(), this will all fit quite well into a single class.
If we want to extend this to src, href, action, style and more, we will quickly get to a quite bloated and unpleasant class.

Is there a way to make this extensible?
If the addClass() behavior is provided by a plugin, then should this remain to be a method on the attributes object, or should we have sth like this instead?

<?php
$attr
->plugin('class')->addClass('menu-item');
?>

Can we afford (performance-wise) to hide this with magic methods?
Or is Stapler the solution?
(this allows to "bake" one big class out of different components, but it is quite experimental, and also quite inflexible if we go beyond one linear stack of components)

Atm, I tend to support just class as a special attribute, and treat anything else as normal strings.

Comments

Module name?

donquixote's picture

Also, I am still undecided about the module name for releasing this.

"Empty text" feature.

donquixote's picture

Example:

<?php
function test_ul($items_html) {
 
// if $items_html is empty string, 'no items' is shown instead.
 
return htmltag()->UL($items_html, '<p>no items</p>');
}

print
test_ul('<li>FIRST</li><li>SECOND</li>') . "\n";
print
test_ul('');
?>

This would print:

<ul><li>FIRST</li><li>SECOND</li></ul>
<p>no items</p>

Any opinions on this?
Should the empty text be '' by default, or should the behavior be disabled by default?

Proper escaping?

donquixote's picture

I also wonder what is the correct way to escape attribute values.
In my current implementation, attribute-setting functions receive a $raw parameter.
If $raw = FALSE, it will use check_plain on the attribute value.
If $raw = TRUE, it will assume the attribute is already escaped, and use the literal value.

What should be the default behavior? Escape, or not escape?
And, is check_plain the right thing, or should we use addslashes?
Should we use other escape functions for styles and classes?

Contributed Module Ideas

Group organizers

Group notifications

This group offers an RSS feed. Or subscribe to these personalized, sitewide feeds: