<img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=1742770185958071&amp;ev=PageView&amp;noscript=1">

Inbound Marketing Blog

HTML-Generating Classes Using PHP's __toString() and ArrayAccess

One of PHP's strengths that I like the most is its versatility as a prototyping language. While the venerable MVC and friends are important in a large project with a lifespan measured in years, sometimes you just need to jot up a quick utility script or internal tool. In these cases, PHP's global context and loose typing really shine as you can mash everything into one sloppy file to start with, then gradually refactor logic out into functions and includes.

That said, I've never liked mixing languages in the same file. <h1><?php echo $header; ?></h1> scans pretty easily, but any more complicated than that and it gets pretty unreadable pretty quickly. It can also do a real number on syntax highlighters, which makes debugging a painful process.

For generating well-formed XML of any dialect, you can't get much more thorough than PHP's built-in DOM implementation but there's nothing concise about it, and I'm trying to be fast, here.

What I want is a way to quickly blast out some arbitrary (but valid!) HTML string in a keystroke-conscious fashion, so I turn to two of my favorite syntactic-sugar-enablers, __toString() and ArrayAccess.

I'm a big fan of PHP's magic __toString() method and use it liberally. Let's start our HTML toolkit at the bottom, with an Attribute class.

<span id="lnum1" style="color: #606060;"> 1:</span> namespace HTML;
<span id="lnum2" style="color: #606060;"> 2:</span>
<span id="lnum3" style="color: #606060;"> 3:</span> <span style="color: #0000ff;">class</span> InvalidAttributeNameException <span style="color: #0000ff;">extends</span> \InvalidArgumentException {}
<span id="lnum4" style="color: #606060;"> 4:</span> <span style="color: #0000ff;">class</span> InvalidAttributeValueException <span style="color: #0000ff;">extends</span> \InvalidArgumentException {}
<span id="lnum5" style="color: #606060;"> 5:</span>
<span id="lnum6" style="color: #606060;"> 6:</span> <span style="color: #0000ff;">class</span> Node {}
<span id="lnum7" style="color: #606060;"> 7:</span>
<span id="lnum8" style="color: #606060;"> 8:</span> <span style="color: #0000ff;">class</span> Attribute <span style="color: #0000ff;">extends</span> Node {
<span id="lnum9" style="color: #606060;"> 9:</span>     <span style="color: #0000ff;">protected</span> $name;
<span id="lnum10" style="color: #606060;"> 10:</span>     <span style="color: #0000ff;">protected</span> $value;
<span id="lnum11" style="color: #606060;"> 11:</span>     <span style="color: #0000ff;">protected</span> $delimiter = <span style="color: #006080;">'"'</span>;
<span id="lnum12" style="color: #606060;"> 12:</span>
<span id="lnum13" style="color: #606060;"> 13:</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> __construct($name, $value = <span style="color: #006080;">''</span>) {
<span id="lnum14" style="color: #606060;"> 14:</span>         <span style="color: #0000ff;">if</span> (!preg_match(<span style="color: #006080;">'/[a-zA-Z_:][-a-zA-Z0-9_:.]/'</span>, $name))
<span id="lnum15" style="color: #606060;"> 15:</span>             throw <span style="color: #0000ff;">new</span> InvalidAttributeNameException($name);
<span id="lnum16" style="color: #606060;"> 16:</span>
<span id="lnum17" style="color: #606060;"> 17:</span>         $this-&gt;name = $name;
<span id="lnum18" style="color: #606060;"> 18:</span>         $this-&gt;setValue($value);
<span id="lnum19" style="color: #606060;"> 19:</span>     }
<span id="lnum20" style="color: #606060;"> 20:</span>
<span id="lnum21" style="color: #606060;"> 21:</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> setValue($value = <span style="color: #006080;">''</span>) {
<span id="lnum22" style="color: #606060;"> 22:</span>         $this-&gt;delimiter = strpos($value, <span style="color: #006080;">'"'</span>) !== <span style="color: #0000ff;">false</span> ? <span style="color: #006080;">"'"</span> : <span style="color: #006080;">'"'</span>;
<span id="lnum23" style="color: #606060;"> 23:</span>         <span style="color: #0000ff;">if</span> (strpos($value, $this-&gt;delimiter))
<span id="lnum24" style="color: #606060;"> 24:</span>             throw <span style="color: #0000ff;">new</span> InvalidAttributeValueException(<span style="color: #006080;">"No valid delimiter for value $value"</span>);
<span id="lnum25" style="color: #606060;"> 25:</span>         $this-&gt;value = $value;
<span id="lnum26" style="color: #606060;"> 26:</span>     }
<span id="lnum27" style="color: #606060;"> 27:</span>
<span id="lnum28" style="color: #606060;"> 28:</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> __toString() {
<span id="lnum29" style="color: #606060;"> 29:</span>         <span style="color: #0000ff;">return</span> <span style="color: #006080;">"{$this-&gt;name}={$this-&gt;delimiter}{$this-&gt;value}{$this-&gt;delimiter}"</span>;
<span id="lnum30" style="color: #606060;"> 30:</span>     }
<span id="lnum31" style="color: #606060;"> 31:</span> }

 

The delimiter handling makes it a little hairy, but it's still pretty straightforward. Of course, HTML attributes by themselves are kind of worthless, so let's add a container class using SPL's ArrayObject:

<span id="lnum1" style="color: #606060;"> 1:</span> namespace HTML;
<span id="lnum2" style="color: #606060;"> 2:</span> <span style="color: #0000ff;">use</span> \ArrayObject;
<span id="lnum3" style="color: #606060;"> 3:</span>
<span id="lnum4" style="color: #606060;"> 4:</span> <span style="color: #0000ff;">class</span> AttributeList <span style="color: #0000ff;">extends</span> ArrayObject {
<span id="lnum5" style="color: #606060;"> 5:</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> __construct($a) {
<span id="lnum6" style="color: #606060;"> 6:</span>         <span style="color: #0000ff;">foreach</span>($a <span style="color: #0000ff;">as</span> $k =&gt; $v) {
<span id="lnum7" style="color: #606060;"> 7:</span>             $a[$k] = ($v instanceof Attribute ? $v : <span style="color: #0000ff;">new</span> Attribute($k,$v));
<span id="lnum8" style="color: #606060;"> 8:</span>         }
<span id="lnum9" style="color: #606060;"> 9:</span>         <span style="color: #0000ff;">parent</span>::__construct($a);
<span id="lnum10" style="color: #606060;"> 10:</span>     }
<span id="lnum11" style="color: #606060;"> 11:</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> offsetSet($k,$v) {
<span id="lnum12" style="color: #606060;"> 12:</span>         <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">parent</span>::offsetSet($k, ($v instanceof Attribute ? $v : <span style="color: #0000ff;">new</span> Attribute($k,$v)));
<span id="lnum13" style="color: #606060;"> 13:</span>     }
<span id="lnum14" style="color: #606060;"> 14:</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> __toString() {
<span id="lnum15" style="color: #606060;"> 15:</span>         <span style="color: #0000ff;">return</span> $this-&gt;count() ? <span style="color: #006080;">' '</span> . implode(<span style="color: #006080;">' '</span>, array_values($this-&gt;getArrayCopy())) : <span style="color: #006080;">''</span>;
<span id="lnum16" style="color: #606060;"> 16:</span>     }
<span id="lnum17" style="color: #606060;"> 17:</span> }

Okay, so this is a little cooler. Now we have a class that we can wrap around an array and get a properly-formatted HTML attribute string, like this:

<span id="lnum1" style="color: #606060;"> 1:</span> <span style="color: #0000ff;">use</span> HTML;
<span id="lnum2" style="color: #606060;"> 2:</span>
<span id="lnum3" style="color: #606060;"> 3:</span> <span style="color: #0000ff;">echo</span> <span style="color: #0000ff;">new</span> HTML\AttributeList(<span style="color: #0000ff;">array</span>(<span style="color: #006080;">"style"</span>=&gt;<span style="color: #006080;">"display:none"</span>, <span style="color: #0000ff;">class</span>=<span style="color: #006080;">"hiddenOnLoad"</span>));
<span id="lnum4" style="color: #606060;"> 4:</span> <span style="color: #008000;">// Output:</span>
<span id="lnum5" style="color: #606060;"> 5:</span> //  style=<span style="color: #006080;">"display:none"</span> <span style="color: #0000ff;">class</span>=<span style="color: #006080;">"hiddenOnLoad"</span>

Neat, but of course we have to take it all the way if we want it to be awesome:

<span id="lnum1" style="color: #606060;"> 1:</span> namespace HTML;
<span id="lnum2" style="color: #606060;"> 2:</span> <span style="color: #0000ff;">use</span> \ArrayObject, \ArrayAccess;
<span id="lnum3" style="color: #606060;"> 3:</span>
<span id="lnum4" style="color: #606060;"> 4:</span> <span style="color: #0000ff;">function</span> nullor(&amp;$val) { <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">isset</span>($val) ? $val : <span style="color: #0000ff;">null</span>; }
<span id="lnum5" style="color: #606060;"> 5:</span>
<span id="lnum6" style="color: #606060;"> 6:</span> <span style="color: #0000ff;">class</span> Element <span style="color: #0000ff;">extends</span> Node {}
<span id="lnum7" style="color: #606060;"> 7:</span>
<span id="lnum8" style="color: #606060;"> 8:</span> <span style="color: #0000ff;">abstract</span> <span style="color: #0000ff;">class</span> Tag <span style="color: #0000ff;">extends</span> Element <span style="color: #0000ff;">implements</span> ArrayAccess {
<span id="lnum9" style="color: #606060;"> 9:</span>     <span style="color: #0000ff;">protected</span> $attributes = <span style="color: #0000ff;">array</span>();
<span id="lnum10" style="color: #606060;"> 10:</span>     <span style="color: #0000ff;">protected</span> $content = <span style="color: #0000ff;">array</span>();
<span id="lnum11" style="color: #606060;"> 11:</span>
<span id="lnum12" style="color: #606060;"> 12:</span>     <span style="color: #0000ff;">abstract</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> getTagName();
<span id="lnum13" style="color: #606060;"> 13:</span>
<span id="lnum14" style="color: #606060;"> 14:</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> __construct($content = <span style="color: #0000ff;">null</span>, <span style="color: #0000ff;">array</span> $attributes = <span style="color: #0000ff;">array</span>()) {
<span id="lnum15" style="color: #606060;"> 15:</span>         <span style="color: #0000ff;">if</span> ($content)
<span id="lnum16" style="color: #606060;"> 16:</span>             $this-&gt;setContent($content);
<span id="lnum17" style="color: #606060;"> 17:</span>         $this-&gt;attributes = <span style="color: #0000ff;">new</span> AttributeList($attributes);
<span id="lnum18" style="color: #606060;"> 18:</span>     }
<span id="lnum19" style="color: #606060;"> 19:</span>
<span id="lnum20" style="color: #606060;"> 20:</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> __toString() {
<span id="lnum21" style="color: #606060;"> 21:</span>         $t = $this-&gt;getTagName();
<span id="lnum22" style="color: #606060;"> 22:</span>         $a = $this-&gt;getAttributeList();
<span id="lnum23" style="color: #606060;"> 23:</span>         $c = $this-&gt;getContent();
<span id="lnum24" style="color: #606060;"> 24:</span>         <span style="color: #0000ff;">return</span> <span style="color: #006080;">"&lt;$t$a&gt;$c&lt;/$t&gt;"</span>;
<span id="lnum25" style="color: #606060;"> 25:</span>     }
<span id="lnum26" style="color: #606060;"> 26:</span>
<span id="lnum27" style="color: #606060;"> 27:</span>     <span style="color: #008000;">// Content getters and setters</span>
<span id="lnum28" style="color: #606060;"> 28:</span>
<span id="lnum29" style="color: #606060;"> 29:</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> getContent() {
<span id="lnum30" style="color: #606060;"> 30:</span>         <span style="color: #0000ff;">return</span> is_array($this-&gt;content) ? implode(<span style="color: #006080;">''</span>, $this-&gt;content) : $this-&gt;content;
<span id="lnum31" style="color: #606060;"> 31:</span>     }
<span id="lnum32" style="color: #606060;"> 32:</span>
<span id="lnum33" style="color: #606060;"> 33:</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> setContent($content) {
<span id="lnum34" style="color: #606060;"> 34:</span>         $this-&gt;content = is_array($content) ? $content : <span style="color: #0000ff;">array</span>($content);
<span id="lnum35" style="color: #606060;"> 35:</span>     }
<span id="lnum36" style="color: #606060;"> 36:</span>
<span id="lnum37" style="color: #606060;"> 37:</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> appendContent($content) {
<span id="lnum38" style="color: #606060;"> 38:</span>         <span style="color: #0000ff;">if</span> (is_array($content))
<span id="lnum39" style="color: #606060;"> 39:</span>             <span style="color: #0000ff;">while</span> ($c = array_shift($content))
<span id="lnum40" style="color: #606060;"> 40:</span>                 $this-&gt;appendContent($c);
<span id="lnum41" style="color: #606060;"> 41:</span>         <span style="color: #0000ff;">else</span>
<span id="lnum42" style="color: #606060;"> 42:</span>             array_push($this-&gt;content, $content);
<span id="lnum43" style="color: #606060;"> 43:</span>     }
<span id="lnum44" style="color: #606060;"> 44:</span>
<span id="lnum45" style="color: #606060;"> 45:</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> prependContent($content) {
<span id="lnum46" style="color: #606060;"> 46:</span>         <span style="color: #0000ff;">if</span> (is_array($content))
<span id="lnum47" style="color: #606060;"> 47:</span>             <span style="color: #0000ff;">while</span> ($c = array_pop($content))
<span id="lnum48" style="color: #606060;"> 48:</span>                 $this-&gt;prependContent($c);
<span id="lnum49" style="color: #606060;"> 49:</span>         <span style="color: #0000ff;">else</span>
<span id="lnum50" style="color: #606060;"> 50:</span>             array_unshift($this-&gt;content, $content);
<span id="lnum51" style="color: #606060;"> 51:</span>     }
<span id="lnum52" style="color: #606060;"> 52:</span>
<span id="lnum53" style="color: #606060;"> 53:</span>     <span style="color: #008000;">// Attribute getters and setters</span>
<span id="lnum54" style="color: #606060;"> 54:</span>
<span id="lnum55" style="color: #606060;"> 55:</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> getAttribute($name) {
<span id="lnum56" style="color: #606060;"> 56:</span>         <span style="color: #0000ff;">return</span> nullor($this-&gt;attributes[$name]);
<span id="lnum57" style="color: #606060;"> 57:</span>     }
<span id="lnum58" style="color: #606060;"> 58:</span>
<span id="lnum59" style="color: #606060;"> 59:</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> getAttributeList() {
<span id="lnum60" style="color: #606060;"> 60:</span>         <span style="color: #0000ff;">return</span> $this-&gt;attributes;
<span id="lnum61" style="color: #606060;"> 61:</span>     }
<span id="lnum62" style="color: #606060;"> 62:</span>
<span id="lnum63" style="color: #606060;"> 63:</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> setAttribute($name, $value = <span style="color: #0000ff;">null</span>) {
<span id="lnum64" style="color: #606060;"> 64:</span>         <span style="color: #0000ff;">if</span> ($name instanceof Attribute)
<span id="lnum65" style="color: #606060;"> 65:</span>             $this-&gt;addAttributeNode($name);
<span id="lnum66" style="color: #606060;"> 66:</span>         <span style="color: #0000ff;">else</span>
<span id="lnum67" style="color: #606060;"> 67:</span>             $this-&gt;attributes[$name] = <span style="color: #0000ff;">new</span> Attribute($name, $value);
<span id="lnum68" style="color: #606060;"> 68:</span>     }
<span id="lnum69" style="color: #606060;"> 69:</span>
<span id="lnum70" style="color: #606060;"> 70:</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> setAttributeList(AttributeList $attributes) {
<span id="lnum71" style="color: #606060;"> 71:</span>         $this-&gt;attributes = $attributes;
<span id="lnum72" style="color: #606060;"> 72:</span>     }
<span id="lnum73" style="color: #606060;"> 73:</span>
<span id="lnum74" style="color: #606060;"> 74:</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> setAttributes(<span style="color: #0000ff;">array</span> $attributes) {
<span id="lnum75" style="color: #606060;"> 75:</span>         $this-&gt;setAttributeList(<span style="color: #0000ff;">new</span> AttributeList($attributes));
<span id="lnum76" style="color: #606060;"> 76:</span>     }
<span id="lnum77" style="color: #606060;"> 77:</span>
<span id="lnum78" style="color: #606060;"> 78:</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> addAttributeNode(Attribute $att) {
<span id="lnum79" style="color: #606060;"> 79:</span>         $this-&gt;attributes[$att-&gt;name] = $att;
<span id="lnum80" style="color: #606060;"> 80:</span>     }
<span id="lnum81" style="color: #606060;"> 81:</span>
<span id="lnum82" style="color: #606060;"> 82:</span>
<span id="lnum83" style="color: #606060;"> 83:</span>     <span style="color: #008000;">// ArrayAccess</span>
<span id="lnum84" style="color: #606060;"> 84:</span>
<span id="lnum85" style="color: #606060;"> 85:</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> offsetExists($k) {
<span id="lnum86" style="color: #606060;"> 86:</span>         <span style="color: #0000ff;">if</span> (is_numeric($k))
<span id="lnum87" style="color: #606060;"> 87:</span>             <span style="color: #0000ff;">return</span> array_key_exists($k, $this-&gt;content);
<span id="lnum88" style="color: #606060;"> 88:</span>         <span style="color: #0000ff;">else</span>
<span id="lnum89" style="color: #606060;"> 89:</span>             <span style="color: #0000ff;">return</span> array_key_exists($k, $this-&gt;attributes);
<span id="lnum90" style="color: #606060;"> 90:</span>     }
<span id="lnum91" style="color: #606060;"> 91:</span>
<span id="lnum92" style="color: #606060;"> 92:</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> offsetGet($k) {
<span id="lnum93" style="color: #606060;"> 93:</span>         <span style="color: #0000ff;">if</span> (!$this-&gt;offsetExists($k))
<span id="lnum94" style="color: #606060;"> 94:</span>             throw <span style="color: #0000ff;">new</span> \OutOfBoundsException($k);
<span id="lnum95" style="color: #606060;"> 95:</span>         <span style="color: #0000ff;">if</span> (is_numeric($k))
<span id="lnum96" style="color: #606060;"> 96:</span>             <span style="color: #0000ff;">return</span> $this-&gt;content[$k];
<span id="lnum97" style="color: #606060;"> 97:</span>         <span style="color: #0000ff;">else</span>
<span id="lnum98" style="color: #606060;"> 98:</span>             <span style="color: #0000ff;">return</span> $this-&gt;attributes[$k];
<span id="lnum99" style="color: #606060;"> 99:</span>     }
<span id="lnum100" style="color: #606060;"> 100:</span>
<span id="lnum101" style="color: #606060;"> 101:</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> offsetSet($k,$v) {
<span id="lnum102" style="color: #606060;"> 102:</span>         <span style="color: #0000ff;">if</span> (is_null($k))
<span id="lnum103" style="color: #606060;"> 103:</span>             <span style="color: #0000ff;">return</span> $this-&gt;appendContent($v);
<span id="lnum104" style="color: #606060;"> 104:</span>         <span style="color: #0000ff;">elseif</span> (is_numeric($k))
<span id="lnum105" style="color: #606060;"> 105:</span>             <span style="color: #0000ff;">return</span> $this-&gt;content[$k] = $v;
<span id="lnum106" style="color: #606060;"> 106:</span>         <span style="color: #0000ff;">else</span>
<span id="lnum107" style="color: #606060;"> 107:</span>             <span style="color: #0000ff;">return</span> $this-&gt;setAttribute($k, $v);
<span id="lnum108" style="color: #606060;"> 108:</span>     }
<span id="lnum109" style="color: #606060;"> 109:</span>
<span id="lnum110" style="color: #606060;"> 110:</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> offsetUnset($k) {
<span id="lnum111" style="color: #606060;"> 111:</span>         <span style="color: #0000ff;">if</span> (!$this-&gt;offsetExists($k))
<span id="lnum112" style="color: #606060;"> 112:</span>             throw <span style="color: #0000ff;">new</span> \OutOfBoundsException($k);
<span id="lnum113" style="color: #606060;"> 113:</span>         <span style="color: #0000ff;">if</span> (is_numeric($k))
<span id="lnum114" style="color: #606060;"> 114:</span>             <span style="color: #0000ff;">unset</span>($this-&gt;content[$k]);
<span id="lnum115" style="color: #606060;"> 115:</span>         <span style="color: #0000ff;">else</span>
<span id="lnum116" style="color: #606060;"> 116:</span>             <span style="color: #0000ff;">unset</span>($this-&gt;attributes[$k]);
<span id="lnum117" style="color: #606060;"> 117:</span>     }

I know it looks like a lot, but most of it is just setters and getters. We model the HTML tag as an object with a numerically-indexed array of content nodes and an associative array of Attributes. For extra fanciness, we implement ArrayAccess so we can access properties using nice concise array notation. All we need is a little late static binding magic to make it all sing:

<span id="lnum1" style="color: #606060;"> 1:</span> namespace HTML;
<span id="lnum2" style="color: #606060;"> 2:</span> <span style="color: #0000ff;">abstract</span> <span style="color: #0000ff;">class</span> NamedTag <span style="color: #0000ff;">extends</span> Tag {
<span id="lnum3" style="color: #606060;"> 3:</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> getTagName() {
<span id="lnum4" style="color: #606060;"> 4:</span>         $fqClassname = get_called_class();
<span id="lnum5" style="color: #606060;"> 5:</span>         $a = explode('\\', $fqClassname);  <span style="color: #008000;">// Trim off namespace</span>
<span id="lnum6" style="color: #606060;"> 6:</span>         <span style="color: #0000ff;">return</span> array_pop($a);
<span id="lnum7" style="color: #606060;"> 7:</span>     }
<span id="lnum8" style="color: #606060;"> 8:</span> }

Now all we have to do is extend NamedTag with classes named for the HTML tags we want to output:

<span id="lnum1" style="color: #606060;"> 1:</span> namespace HTML;
<span id="lnum2" style="color: #606060;"> 2:</span> <span style="color: #0000ff;">class</span> h1 <span style="color: #0000ff;">extends</span> NamedTag;
<span id="lnum3" style="color: #606060;"> 3:</span> <span style="color: #0000ff;">class</span> em <span style="color: #0000ff;">extends</span> NamedTag;
<span id="lnum4" style="color: #606060;"> 4:</span> <span style="color: #0000ff;">class</span> a <span style="color: #0000ff;">extends</span> NamedTag;

And tra-la, we now have objects representing HTML elements which can simply be echoed (conveniently, all existing HTML tags are valid PHP classnames, though I would use this solution with a namespace whenever possible to avoid future reserved word collisions; plus I like the type hint the namespace provides):

<span id="lnum1" style="color: #606060;"> 1:</span> <span style="color: #0000ff;">use</span> HTML;
<span id="lnum2" style="color: #606060;"> 2:</span>
<span id="lnum3" style="color: #606060;"> 3:</span> <span style="color: #0000ff;">echo</span> <span style="color: #0000ff;">new</span> HTML\h1(<span style="color: #006080;">'A simple header tag'</span>);
<span id="lnum4" style="color: #606060;"> 4:</span> <span style="color: #008000;">// Outputs</span>
<span id="lnum5" style="color: #606060;"> 5:</span> <span style="color: #008000;">// &lt;h1&gt;A simple header tag&lt;/h1&gt;</span>
<span id="lnum6" style="color: #606060;"> 6:</span>
<span id="lnum7" style="color: #606060;"> 7:</span> <span style="color: #0000ff;">echo</span> <span style="color: #0000ff;">new</span> HTML\em(<span style="color: #006080;">'An em tag with a class attribute'</span>, <span style="color: #0000ff;">array</span>(<span style="color: #006080;">'class'</span>=&gt;<span style="color: #006080;">'myClass'</span>));
<span id="lnum8" style="color: #606060;"> 8:</span> <span style="color: #008000;">// Outputs</span>
<span id="lnum9" style="color: #606060;"> 9:</span> // &lt;h1 <span style="color: #0000ff;">class</span>=<span style="color: #006080;">"myClass"</span>&gt;A simple header tag&lt;/h1&gt;

Pretty easy on the fingers! Because every class is just treated as a string, we can even nest our tags:

<span id="lnum1" style="color: #606060;"> 1:</span> <span style="color: #0000ff;">use</span> HTML;
<span id="lnum2" style="color: #606060;"> 2:</span>
<span id="lnum3" style="color: #606060;"> 3:</span> <span style="color: #0000ff;">echo</span> <span style="color: #0000ff;">new</span> HTML\h1(
<span id="lnum4" style="color: #606060;"> 4:</span>     <span style="color: #0000ff;">new</span> em(<span style="color: #006080;">'Emphasized text'</span>),
<span id="lnum5" style="color: #606060;"> 5:</span>     <span style="color: #0000ff;">array</span>(<span style="color: #006080;">'class'</span>=&gt;<span style="color: #006080;">'myHeaderClass'</span>)
<span id="lnum6" style="color: #606060;"> 6:</span>     );
<span id="lnum7" style="color: #606060;"> 7:</span> <span style="color: #008000;">// Outputs</span>
<span id="lnum8" style="color: #606060;"> 8:</span> <span style="color: #008000;">// &lt;h1 class="myClass"&gt;&lt;em&gt;Emphasized text&lt;/em&gt;&lt;/h1&gt;</span>
<span id="lnum9" style="color: #606060;"> 9:</span>
<span id="lnum10" style="color: #606060;"> 10:</span> <span style="color: #0000ff;">echo</span> <span style="color: #0000ff;">new</span> HTML\h1(
<span id="lnum11" style="color: #606060;"> 11:</span>     <span style="color: #0000ff;">new</span> HTML\em(<span style="color: #0000ff;">array</span>(
<span id="lnum12" style="color: #606060;"> 12:</span>         <span style="color: #006080;">'Emphasized text '</span>,
<span id="lnum13" style="color: #606060;"> 13:</span>         <span style="color: #0000ff;">new</span> HTML\a(
<span id="lnum14" style="color: #606060;"> 14:</span>             <span style="color: #006080;">'with a link!'</span>,
<span id="lnum15" style="color: #606060;"> 15:</span>             <span style="color: #0000ff;">array</span>(<span style="color: #006080;">'href'</span>=&gt;<span style="color: #006080;">'http://example.com'</span>)
<span id="lnum16" style="color: #606060;"> 16:</span>         )
<span id="lnum17" style="color: #606060;"> 17:</span>         )),
<span id="lnum18" style="color: #606060;"> 18:</span>         <span style="color: #0000ff;">array</span>(<span style="color: #006080;">'class'</span>=&gt;<span style="color: #006080;">'myHeaderClass'</span>)
<span id="lnum19" style="color: #606060;"> 19:</span> );
<span id="lnum20" style="color: #606060;"> 20:</span> <span style="color: #008000;">// Outputs</span>
<span id="lnum21" style="color: #606060;"> 21:</span> <span style="color: #008000;">// &lt;h1 class="myClass"&gt;&lt;em&gt;Emphasized text &lt;a href="http://example.com"&gt;with a link!&lt;/a&gt;&lt;/em&gt;&lt;/h1&gt;</span>

Or simply concatenate our objects as though they were strings:

<span id="lnum1" style="color: #606060;"> 1:</span> <span style="color: #0000ff;">use</span> HTML;
<span id="lnum2" style="color: #606060;"> 2:</span>
<span id="lnum3" style="color: #606060;"> 3:</span> <span style="color: #0000ff;">echo</span> <span style="color: #0000ff;">new</span> HTML\h1(
<span id="lnum4" style="color: #606060;"> 4:</span>     <span style="color: #0000ff;">new</span> HTML\em(
<span id="lnum5" style="color: #606060;"> 5:</span>         <span style="color: #006080;">'Emphasized text '</span> . <span style="color: #0000ff;">new</span> HTML\a(<span style="color: #006080;">'with a link!'</span>, <span style="color: #0000ff;">array</span>(<span style="color: #006080;">'href'</span>=&gt;<span style="color: #006080;">'http://example.com'</span>)),
<span id="lnum6" style="color: #606060;"> 6:</span>         <span style="color: #0000ff;">array</span>(<span style="color: #006080;">'class'</span>=&gt;<span style="color: #006080;">'myHeaderClass'</span>)
<span id="lnum7" style="color: #606060;"> 7:</span>     )
<span id="lnum8" style="color: #606060;"> 8:</span> );
<span id="lnum9" style="color: #606060;"> 9:</span> <span style="color: #008000;">// Also outputs</span>
<span id="lnum10" style="color: #606060;"> 10:</span> <span style="color: #008000;">// &lt;h1 class="myClass"&gt;&lt;em&gt;Emphasized text &lt;a href="http://example.com"&gt;with a link!&lt;/a&gt;&lt;/em&gt;&lt;/h1&gt;</span>

The ArrayAccess implementation allows us easy access to the object's properties, just like SimpleXML:

<span id="lnum1" style="color: #606060;"> 1:</span> <span style="color: #0000ff;">use</span> HTML;
<span id="lnum2" style="color: #606060;"> 2:</span>
<span id="lnum3" style="color: #606060;"> 3:</span> $header = <span style="color: #0000ff;">new</span> HTML\h1(<span style="color: #006080;">'A title'</span>);
<span id="lnum4" style="color: #606060;"> 4:</span>
<span id="lnum5" style="color: #606060;"> 5:</span> $header[] = <span style="color: #006080;">' for my document '</span>;  <span style="color: #008000;">// access content</span>
<span id="lnum6" style="color: #606060;"> 6:</span> $header[] = <span style="color: #0000ff;">new</span> HTML\a(<span style="color: #006080;">'with a link!'</span>,<span style="color: #0000ff;">array</span>(<span style="color: #006080;">'href'</span>=&gt;<span style="color: #006080;">'http://example.com'</span>));
<span id="lnum7" style="color: #606060;"> 7:</span> $header[<span style="color: #006080;">'class'</span>] = <span style="color: #006080;">'myHeaderClass'</span>;
<span id="lnum8" style="color: #606060;"> 8:</span> $header[2][<span style="color: #006080;">'class'</span>] = <span style="color: #006080;">'myLinkClass'</span>;
<span id="lnum9" style="color: #606060;"> 9:</span>
<span id="lnum10" style="color: #606060;"> 10:</span> <span style="color: #0000ff;">echo</span> $header;
<span id="lnum11" style="color: #606060;"> 11:</span> <span style="color: #008000;">// Outputs:</span>
<span id="lnum12" style="color: #606060;"> 12:</span> <span style="color: #008000;">// &lt;h1 class="myHeaderClass"&gt;A title for my document &lt;a href="http://example.com" class="myLinkClass"&gt;with a link!&lt;/a&gt;&lt;/h1&gt;</span>

Topics: Development

MINDSCAPE

Written by MINDSCAPE

We work with companies and organizations who want to get the most out of their digital marketing: more leads, more sales, more profit. Our success is measured by the hundreds of millions of dollars we help our clients generate each year.

Join the conversation

Subscribe to Email Updates

Let's Talk