Create HTML Elements Using PHP: html_element Class
I love creating HTML elements using the MooTools JavaScript library. It's easy, fast, and the JavaScript code to create the element is beautiful. That got me thinking -- why don't I do this using PHP? When you build as many dynamic CMS-like websites as me, you end up having to manipulate single HTML element attributes with a bunch of if/else logic and the code can start looking ugly.
I took about a half hour to throw together the following PHP class. It's small, easy to use, and produces beautiful code--just like Moo!
The PHP
/* creates an html element, like in js */
class html_element
{
/* vars */
var $type;
var $attributes;
var $self_closers;
/* constructor */
function html_element($type,$self_closers = array('input','img','hr','br','meta','link'))
{
$this->type = strtolower($type);
$this->self_closers = $self_closers;
}
/* get */
function get($attribute)
{
return $this->attributes[$attribute];
}
/* set -- array or key,value */
function set($attribute,$value = '')
{
if(!is_array($attribute))
{
$this->attributes[$attribute] = $value;
}
else
{
$this->attributes = array_merge($this->attributes,$attribute);
}
}
/* remove an attribute */
function remove($att)
{
if(isset($this->attributes[$att]))
{
unset($this->attributes[$att]);
}
}
/* clear */
function clear()
{
$this->attributes = array();
}
/* inject */
function inject($object)
{
if(@get_class($object) == __class__)
{
$this->attributes['text'].= $object->build();
}
}
/* build */
function build()
{
//start
$build = '<'.$this->type;
//add attributes
if(count($this->attributes))
{
foreach($this->attributes as $key=>$value)
{
if($key != 'text') { $build.= ' '.$key.'="'.$value.'"'; }
}
}
//closing
if(!in_array($this->type,$this->self_closers))
{
$build.= '>'.$this->attributes['text'].'</'.$this->type.'>';
}
else
{
$build.= ' />';
}
//return it
return $build;
}
/* spit it out */
function output()
{
echo $this->build();
}
}
The class is pretty simple. When you instantiate the class, you feed it the element type. Once the element is created, you use get() to retrieve values and set() (key and value OR array key=>value) to set values. You can get rid of a specified attribute using remove() or all attribute key=>values using clear(). You can inject html_elements into other html_elements by using inject(). You can get the raw HTML of the element using build() or you can use the output() function to echo out the HTML. Important: use the "text" attribute to add text/HTML inside an element.
Example Uses
/* test case - simple link */
$my_anchor = new html_element('a');
$my_anchor->set('href','http://davidwalsh.name');
$my_anchor->set('title','David Walsh Blog');
$my_anchor->set('text','Click here!');
$my_anchor->output();
//<a href="http://davidwalsh.name" title="David Walsh Blog">Click here!</a>
/* test case - br tag */
echo '<pre>';
$my_anchor = new html_element('br');
$my_anchor->output();
//<br />
/* test case - sending an array to set */
echo '<pre>';
$my_anchor = new html_element('a');
$my_anchor->set('href','http://davidwalsh.name');
$my_anchor->set(array('href'=>'http://cnn.com','text'=>'CNN'));
$my_anchor->output();
//<a href="http://cnn.com">CNN</a>
/* test case - injecting another element */
echo '<pre>';
$my_image = new html_element('img');
$my_image->set('src','cnn-logo.jpg');
$my_image->set('border','0');
$my_anchor = new html_element('a');
$my_anchor->set(array('href'=>'http://cnn.com','title'=>'CNN'));
$my_anchor->inject($my_image);
$my_anchor->output();
//<a href="http://cnn.com" title="CNN"><img src="cnn-logo.jpg" border="0" /></a>
Have any ideas for this class? Share them!
Comments
Be Heard!
Share your thoughts without being a jerk! And wrap your code in <code> tags, f00!
Nice! Just a thought, shouldn’t it be:
$my_image->inject($my_anchor);
- or -
$my_anchor->grab($my_image)
@Richard: I don’t think so. I’m injecting the image into the link. If you say that because Moo does things that way, you’re right — I’ve never liked that part of Moo element handling. :)
Why not create functions for the elements instead of building the elements piece by piece, this requires a lot of code. And after all it only builds static html.
# $my_anchor = new html_element(‘br’);
# $my_anchor->output();
vs
<br>
This is pretty nice :)
I think that using something like this could be a life saver in the future, making it a lot easier to make the move from one standard to another, e.g. from html 4 to html 5, or to xhtml as another example – since one would only need to change the tag output one place.
One could also build helpers uppon this, to save some typing. An example would be from the CodeIgniter framework (http://codeigniter.com/user_guide/helpers/html_helper.html).
Of course you’d have the overhead from all the php, but there’s always caching :)
Anyway, I thought I’d just drop a few lines.. you got a really nice blog here, keep up the good work :)
Good work and useful. I try it as soon as. Thank you.
How is this any better than using php’s already built-in methods to output plain html?
@Keith: I don’t know if I’d call it “better.” It’s just a tool to keep code more organized.
Why not return the current instance of the class on the set function? Then you could chain the calls similar to mootools/jQuery (in PHP5, naturally):
$el = new html_element();
$el->set(‘href’,'http://…’)->set(‘title’,'…’)->set(‘text’,'Click here!’)->output();
@Mark: Great idea. I split them apart in my examples to make the idea seem easier, but that would definitely work.
You could also add a magic __toString method (php5) so you could just do `print $my_anchor`
It is a great way to build HTML elements, really simple code with a lot of power. The problem is in the principle, why are you generating HTML elements? This type of architecture would be better suited for dynamic FORM generation.
You can create dynamic forms with this. It’s just not limited to forms only. There may be many reasons that you need programmatic control over how HTML is built and this just provides a nice, simple method to do it.
@Richard,
Yes you could use the method provided for a lot of applications, I never said you could not. I was merely suggesting a practical use for it. In an enterprise level application, you would not want to have all your HTML generated from PHP, that would take millions of lines of code. You would want a strong template system.
@David,
I really like the way you structure your code. It reminds me of SimpleXML.
@Sam: Of course you wouldn’t want to do all of your XHTML like that. Forms would be a good usage. Thank you for the compliment!
Convenience functions could also be made to make it shorter for the most common ones:
$el = new html_element();
$el->href(‘http://…’)->title(,’…’)->text(‘Click here!’)->output();
Thanks for the class David! Here are my alterations… My goal was to make it a little more like Prototype JS element creation.
<?php
/**
* Create an element
*/
class Element {
private $type;
private $unaryTagArray = array(‘input’, ‘img’, ‘hr’, ‘br’, ‘meta’, ‘link’);
private $attributeArray;
private $innerHtml;
/**
* Constructor
*
* @param <type> $type
* @param <type> $attributeArray
* @param <type> $unaryTagArray
*/
public function __construct($type, $attributeArray = array()) {
$this->type = strtolower($type);
foreach($attributeArray as $attribute => $value) {
$this->setAttribute($attribute, $value);
}
return $this;
}
/**
* Get one of the element’s attributes
*
* @param <type> $attribute
* @return <type>
*/
public function getAttribute($attribute) {
return $this->attributeArray[$attribute];
}
/**
* Set an array, can pass an array or a key, value combination
*
* @param <type> $attribute
* @param <type> $value
*/
function setAttribute($attribute, $value = “”) {
if(!is_array($attribute)) {
$this->attributeArray[$attribute] = $value;
}
else {
$this->attributeArray = array_merge($this->attributeArray, $attribute);
}
return $this;
}
/**
* Remove an attribute from an element
*
* @param <type> $attribute
*/
function removeAttribute($attribute) {
if(isset($this->attributeArray[$attribute])) {
unset($this->attributeArray[$attribute]);
}
return $this;
}
/**
* Clear all of the element’s attributes
*/
function clearAttributes() {
$this->attributeArray = array();
return $this;
}
/**
* Insert an element into the current element
*
* @param <type> $object
*/
function insert($object) {
if(@get_class($object) == __class__) {
$this->innerHtml .= $object->build();
}
return $this;
}
/**
* Set the innerHtml of an element
*
* @param <type> $object
* @return <type>
*/
function update($object) {
$this->innerHtml = $object;
return $this;
}
/**
* Builds the element
*
* @return <type>
*/
function build() {
// Start the tag
$element = “<”.$this->type;
// Add attributes
if(count($this->attributeArray)) {
foreach($this->attributeArray as $key => $value) {
$element .= ” “.$key.”=\”".$value.”\”";
}
}
// Close the element
if(!in_array($this->type, $this->unaryTagArray)) {
$element.= “>\n”.$this->innerHtml.”\n</”.$this->type.”>\n”;
}
else {
$element.= ” />\n”;
}
return $element;
}
/**
* Echoes out the element
*
* @return <type>
*/
function __toString() {
return $this->build();
}
}
?>
;
This seems to be the sort of thing I am looking for. In my application I will have an html form into which users enter search parameters. php gets the parameters and uses them to query mysql. That’s the easy part, the tricky part is that I want php to present the data from the query within html tags, specifically I want to feed the information into java applet I suppose by using the tag within tags. It seems that php html_element class should make this possible, but I have a hard time wrapping my head around the concept.
I would check into using simple dom parser. Works for manipulating html/xhtml from a file or stored aready in a string. I use Simple DOM parser to manipulate and help clean up my template output before echo-ing out to the screen. It’s also great for quickly assembling screen scraping and scripts… aka, news, weather, etc.
I loved your class script but it’s a little bit outdated. I rewrote it for php 5 so that the set function would work. I also renamed some of the function names so that the lines are shorter. In addition I added two functions: re_set and dumpy (used for troubleshooting if needed). I also included Kirk Ouimet’s __tostring magic method and as an added bonus I created a nice external function test bed. Everything works beautifully and with far fewer lines of code to invoke the class. I Apologize if it looks like crap right now but if you copy and paste everything below between php tags it will (or should) look ordered. I am not sure how to use the code sample tag however please feel free to edit my entry if you see fit to do so. Again, David Walsh thank you for providing us with this fine foundation of a php class for writing html.
print(“code sample”);
testBed();
function testBed(){
$p = new Element(‘p’);
$br = new Element(‘br’);
echo ‘This sentence should break here’.$br.’if break element was created.’.$br.$br;
$img = new Element(‘img’,$atts=array(‘src’=>’../../images/test1.jpg’,'width’=>’250′,’height’=>’149′));
echo ‘A plain image should appear here: ‘.$img.$br.$br;
$atts=array(‘href’=>’http://www.dogpile.com/’,'title’=>’Can you guess?’,'text’=>’My favourite search engine. =)’);
$a = new Element(‘a’,$atts);
echo $a.$br.$br;
$a->re_set(‘href’,'http://www.google.com’);
$a->re_set(‘title’,”I’m now a sheep I in the doghouse. o_o”);
$a->re_set(‘text’,”I’m now an easily herded simple minded sheep like everyone else. =( “);
echo $a.$br.$br;
$a->remove(‘href’);
$a->re_set(‘text’,”I am now a dead link. =|”);
echo $a.$br.$br;
$a->remove(‘text’);
$a->set(‘href’,'http://www.mamma.com/’);
$a->re_set(‘title’,”You better good or mama will put you in the doghouse. o_~”);
$a->nest($img);
echo ‘Image link points to mother of all search engines: ‘.$a.$br.$br;
$a->clear();
echo ‘I am now a dead image-link: ‘.$a.$br.$br;
}
class Element{# creates a very user-friendly html element, just create new element and echo it
private $tag;# the html element to create
private $uni;# the remaining (ten) non-deprecated w3c recognized self-closing unitags
private $atts;# attributes are entered into an associative array
private $obj;# the object to be nested
public function __construct($tag,$atts=array(),$uni=array(‘meta’,'base’,'link’,'img’,'br’,'hr’,'param’,'input’,'option’,'col’)){
$this->tag = strtolower($tag); $this->atts = $atts; $this->uni = $uni;
if($atts) foreach($atts as $key => $val){$this->set($key,$val);}
}
public function __toString(){return $this->build();}# object emulates string after calling echo or print
public function get($key){# solicits a value for a given attribute
if(property_exists($this,$key)) return $this->atts[$key];
else echo “Cannot get attribute – \”$key\” is not a property of \”$this\”.”;
return $this;
}
public function set($key,$val=”){# sets an attribute value, can pass array or key
if(property_exists($this,$key)){echo ‘Cannot set an existing property – instead use re_set.’; return $this;}
if(!is_array($key)) $temp = array($key => $val);# make it into an array if its not an array
if($this->atts = array_merge($this->atts,$temp)){return $this;}
else echo “Cannot merge supplied parameters with existing object $this.\n”;
}
public function re_set($key,$val){# changes value of an attribute – not for resetting pointer
if($this->remove($key)) $this->set($key,$val);
else echo “Cannot change attribute, \”$key\” is not a property of \”$this\”.”;
return $this;
}
public function remove($key){# removes a single attribute
if(isset($this->atts[$key])){unset($this->atts[$key]);}return $this;
}
public function clear(){$this->atts = array();return $this;}# clears all attributes
public function nest($obj){# appends nested object
if(get_class($obj) == __class__){$this->obj .= $obj->build();}
return $this;
}
# build element and return text or but not text
private function build(){# and certainly not some text
$el = “\n\t\t\t<”.$this->tag;# tag opening
if(count($this->atts)){foreach($this->atts as $key=>$val){if($key!=’text’)$el.= ‘ ‘.$key.’=”‘.$val.’”‘;}}# attributes
if(in_array($this->tag,$this->uni)) return $el.= ” />\n”;# return self-closing tag
if(!$this->obj) return $el.= ‘>’.$this->atts['text'].’tag.’>’;# return tag with inserted text
else return $el.= ‘>’.$this->obj.’tag.’>’;# return paired tag with inserted object
}
public function dumpy(){echo ”; var_dump($this); echo ”; return;}# take a dump before pulling yer hair out
//public function update($obj){$this->obj = $obj; return $this;}# __constructor-like magic
}
Cool topic, I will try to use it my self
This is a very useful tool. But how would I go about creating a form and then adding inputs to that form?
I was searching for this code..
It is very useful for my WYSIWYG model content management system..
Thanks
Line 57 should be:
$this->set('text', $object->build());
As otherwise you risk an undefined index of the attributes array (text)
Line 55 should be:
$this->set('text', $object->build());
As otherwise you risk an undefined index of the attributes array (text)
Sorry, make that line 55 should be:
if(!isset($this->attributes['text'])){
$this->attributes['text'] = $object->build();
}else{
$this->attributes['text'].= $object->build();
}
Otherwise you could overwrite the previous elements text output
type = strtolower($type);
$this->self_closers = $self_closers;
$this->set(‘style’,”");
}
/* get */
function get($attribute)
{
return $this->attributes[$attribute];
}
/* set — array or key,value */
function set($attribute,$value = ”)
{
if(!is_array($attribute))
{
$this->attributes[$attribute] = $value;
}
else
{
$this->attributes = array_merge($this->attributes,$attribute);
}
}
/* remove an attribute */
function remove($att)
{
if(isset($this->attributes[$att]))
{
unset($this->attributes[$att]);
}
}
/* clear */
function clear()
{
$this->attributes = array();
}
/* inject */
function inject($object)
{
if(@get_class($object) == __class__)
{
$this->attributes['text'].= $object->build();
}
}
function setStyle($cssAt,$value){
$this->set(“style”,$this->get(“style”).”$cssAt:$value;”);
}
function appendChild($child){
$this->children[count($this->children)]= $child;
}
/* build */
function build()
{
//start
$build = ‘type;
$text=”";
//add attributes
if(count($this->attributes))
{
foreach($this->attributes as $key=>$value)
{
if($key != ‘text’) { $build.= ‘ ‘.$key.’=”‘.$value.’”‘; }
else{$text=$value;}
}
}
//closing
if(!in_array($this->type,$this->self_closers))
{
$build.= ‘>’;
$build.=$text;
foreach ($this->children as $child){
$build.=$child->build();
}
$build.=’type.’>’;
}
else
{
$build.= ‘ />’;
}
//return it
return $build;
}
/* spit it out */
function output()
{
echo $this->build();
}
}
?>
i have added appendChild ,setStyle function
When a value contains a quote sign, you end up with something like this:
That won’t work, so I’ve modified the set function of the original code with this:
function set($attribute,$value = ''){
if(!is_array($attribute))
{
$this->attributes[$attribute] = str_replace('"', '"', $value);
}
else
{
foreach($attribute as $key => $val){
$attribute[$key] = str_replace('"', '"', $val);
}
$this->attributes = array_merge($this->attributes,$attribute);
}
}
Correction:
When a value contains a quote sign, you end up with something like this:
<input value=”here “is a quote sign”>
gotta love your code, and thanks for it, but still think jquery is better than mootools. :)
I think a markup like this would be quicker and easier to use. I _just_ started working on this project, so if you guys have any ideas, please don’t hesitate to make contributions :)
"wrapper"), function(){
Html::h1("Hello, World", array("class" => "title"));
Html::ul(array("class" => "links", function(){
foreach(array(1,2,3) as $x)
Html::li(function() use($x) {
Html::a("Link {$x}", "#{$x}");
});
});
});
?>
Open source on Github at https://github.com/naomik/htmlgen
Well, the comment form butchered my code paste, but you can see it on github. Cheers :)
This code is great for what I am trying to do, but Im just learning this stuff as I go and I can’t figure out a way to wrap the output into a function, then call that functions through a jquery onclick event.
Any help would be great.
Thanks
Hey Ken,
This should get you started :)
HTML on your page
Click
The jQuery for that page
$(document).ready(function(){
$("#foo").click(function(){
$("#output").load("/getHtml.php");
return false;
});
});
getHtml.php
set('src' ,'foo.jpg');
$my_image->set('border', '0');
$my_image->output();
exit;
?>
Hope this helps :)
This guy really needs to fix his code snippets; they’re all getting massacred :(
tagnames should be validated, and text and attribute values MUST be escaped with htmlspecialchars().
Hello Gentlemen, ladies! Good Night;
How would I use this class to generate a
a
b
c
Please wait return!
thank you
Just a thought, the separate method calls for adding each attribute to the element seems superfluous. How about one __construct call…something like:
$a = new html_element(“img”, array( “src” => “http://blah”, “title” => “this”, “alt” => “that”) );
And perhaps a default for whether or not to output it or return it, which could be edited.
Hi there,
I just wrote some lines php to get all these stuff done…
http://code.google.com/p/phpcreatehtml/
This will give you the possibility to write html stuff just like this:
$div = html::tag("div")
->class("wrap");
$h1 = html::tag('h1')
->html("WORKS!");
$span = html::tag('span')
->html("Lars Echterhoff Medientechnik");
$out = html::tag('a')
->href("mailto:hallo@echterhoff.it")
->class("mailme")
->addClass("formore")
->html("Text")
->addhtml(" me!")
->append($h1)
->append($span)
->appendTo($div);
print_r($out->toArray());
print_r($out->toString());
toString() is also integrated as magic method.
Everything together wrapped in a nice small class ready to use.
If you have any ideas or bugs, feel free to post them at code.google.
Lars