UltraMega Blog
14Jul/099

Generating HTML5 Documents Using DOMDocument In PHP

PHP 5 includes a powerful set of DOM manipulation classes that gives you full control over HTML and XML documents. This functionality behaves very similar to JavaScript's DOM manipulation engine. In this tutorial, we'll explore the DOMDocument class by generating an entire HTML5 page without writing a single bit of raw markup. This may not be practical for most applications, but it should give you a good idea of how the basic DOMDocument methods work.

First, let's look at what the final output should look like. The generated version will have slightly different formatting and indentation, but the functionality will be the same.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!DOCTYPE html>
<html lang="en">
<head>
  <meta content="charset=utf-8">
  <title>DOMDocument</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body><div id="wrapper">
    <div id="header">
      <img src="header.gif" alt="Header" width="400" height="100">
    </div>
    <div id="nav">
      <ul>
        <li><a href="index.php">Home</a></li>
        <li><a href="download.php">Download</a></li>
        <li class="active"><a href="generate.php">Generate</a></li>
        <li><a href="about.php">About</a></li>
      </ul>
    </div>
    <div id="content">
      <h1>DOMDocument</h1>
      <p>This page was generated using PHP's <strong>DOMDocument</strong> class!</p>
    </div>
  </div>
</body>
</html>

As you can see, this is a very basic template with a header, navigation, and content sections. The header includes a single image, the navigation includes a list of links, and the content has a heading and any page content. All three sections are also wrapped in a "wrapper" div. Of course, all the page styling will be handled by the included CSS file in the header.

So now let's start writing code! Below each snippet is an explanation.

1
2
3
4
5
<?php
// Create document
$dom = new DOMImplementation;
$doctype = $dom->createDocumentType('html');
$document = $dom->createDocument(null, 'html', $doctype);

Here is where we setup the document, using a special class called DOMImplementation that allows us to easily create a document with the appropriate DOCTYPE declaration. The createDocumentType method stores the document type information in an object to be passes to the createDocument method, which actually creates the document. This document has the root <html> tag already created.

The createDocument class accepts the name of the root element (<html>) and the object created by createDocumentType as parameters.

7
8
// Create head element
$head = $document->createElement('head');

Here is an example of creating a simple element using the createElement method, in this case it is the head tag of the document. The createElement class accepts either one or two parameters. The first, and required, one is the name of the element to create. If a second parameter is supplied, it will create a text node of the supplied text and append it to the created element.

10
11
12
13
14
15
16
17
18
19
20
$metahttp = $document->createElement('meta');
$metahttp->setAttribute('content', 'charset=utf-8');
$head->appendChild($metahttp);
 
$title = $document->createElement('title', 'DOMDocument');
$head->appendChild($title);
 
$css = $document->createElement('link');
$css->setAttribute('rel', 'stylesheet');
$css->setAttribute('href', 'styles.css');
$head->appendChild($css);

More examples of creating elements, as well as actually adding them to the document with the DOMNode::appendChild method. This method can be called on any node created with createElement, and accepts the node that you want to add as its parameter. For example, here we created a $title element, which we added to the $head element.

This segment also demonstrates setting attributes with the DOMElement::setAttribute method, which simply accepts the attribute name and value as parameters.

22
23
24
25
26
27
// Create body element
$body = $document->createElement('body');
 
// Wrapper div
$wrapper = $document->createElement('div');
$wrapper->setAttribute('id', 'wrapper');

Here we created the body tag and the wrapper div.

29
30
31
32
33
34
35
36
37
38
39
40
// Header div
$header = $document->createElement('div');
$header->setAttribute('id', 'header');
$wrapper->appendChild($header);
 
// Header img
$himg = $document->createElement('img');
$himg->setAttribute('src', 'header.gif');
$himg->setAttribute('alt', 'Header');
$himg->setAttribute('width', '400');
$himg->setAttribute('height', '100');
$header->appendChild($himg);

Here, we created the header div and added the header image to it.

42
43
44
45
46
47
48
49
// Nav div
$nav = $document->createElement('div');
$nav->setAttribute('id', 'nav');
$wrapper->appendChild($nav);
 
// Nav ul
$navlist = $document->createElement('ul');
$nav->appendChild($navlist);

Here is where the nav div and list are created.

51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
$menuArray = array();
$menuArray['index.php'] = 'Home';
$menuArray['download.php'] = 'Download';
$menuArray['generate.php'] = 'Generate';
$menuArray['about.php'] = 'About';
 
$currentPage = basename($_SERVER['SCRIPT_FILENAME']);
 
foreach($menuArray as $page => $title) {
    $navitem = $document->createElement('li');
    if($page == $currentPage) {
        $navitem->setAttribute('class', 'active');
    }
 
    $navlink = $document->createElement('a', $title);
    $navlink->setAttribute('href', $page);
 
    $navitem->appendChild($navlink);
    $navlist->appendChild($navitem);
}

Here's the fun part! First, we set up an array of navigation links and figured out the file name of the current page. Then, we looped through the array to create and append the list items and links to the nav list. We also check to see if the item is the current page, and add the "active" class if it is.

72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
// Content div
$content = $document->createElement('div');
$content->setAttribute('id', 'content');
$wrapper->appendChild($content);
 
// Actual page content
$h1 = $document->createElement('h1', 'DOMDocument');
$content->appendChild($h1);</pre
>Here is where we start creating the content area. We created the content div and added a heading tag.
 
<pre lang="php" line="81">$p = $document->createElement('p');
$text = $document->createTextNode('This page was generated using PHP\'s ');
$p->appendChild($text);
$text = $document->createElement('strong', 'DOMDocument');
$p->appendChild($text);
$text = $document->createTextNode(' class!');
$p->appendChild($text);
$content->appendChild($p);

Here is where we create the paragraph element with text. In this case, since the text has a strong tag within the text, we need to break the text into pieces. First, we create the text before, then the strong text, and then the remaining text. I don't know of a better way to do this other than automating the process of breaking up text.

90
91
92
93
94
95
96
$body->appendChild($wrapper);
 
// Add head and body to document
$html = $document->getElementsByTagName('html')->item(0);
$html->setAttribute('lang', 'en');
$html->appendChild($head);
$html->appendChild($body);

Here, we actually add the wrapper div to the body, and the head and body elements to the document. We also set the language attribute while we have the root element. In order to access the root tag, we use getElementsByTagName and grab the first item (since there is only one, the first is the correct one). If you know of a better way to access the root element, please comment!

98
99
100
// Output document
$document->formatOutput = true;
echo $document->saveHTML();

Here is where we finally output the document in text form to the browser using the saveHTML method. I also set the formatOutput property to true so it indents the code nicely.

See the live demo page...

Posted by Steve

Tagged as: , , Leave a comment
Comments (9) Trackbacks (1)
  1. Hey Steve,
    I want to first thank you for the great example here! Documentation for DOMDocument is scarce and it’s really hard to find any conclusive articles on generating XHTML with it, so your article was very useful.
    I will note that the static calls to createDocument() and createElement() will trigger a Strict warning in PHP 5.3 since they aren’t static methods, but that’s an easy fix.

    The problem I’m having is when generating some form field types, mainly the textarea. textarea fields must have a closing tag “” and cannot be closed like some of the others. Unfortunately, it doesn’t seem saveXML() realizes this and therefore creates incorrect textarea tags. I haven’t tried any of the others, so I’m not sure, but “input” fields work fine and so does “form”.

    Do you have any ideas on this?
    Thanks.

    • I spoke too soon …
      After taking another look at it and looking at how the form element had proper tags, I had an idea and it worked. Here’s what I did:
      $textarea = $document->createElement(‘textarea’);
      $textarea->setAttribute(‘cols’, ’50’);
      $textarea->setAttribute(‘rows’, ‘5’);
      $textarea->setAttribute(‘name’, ‘comments’);
      $blank = $document->createTextNode(”);
      $textarea->appendChild($blank);

      By adding a blank textnode to it, it made it create both tags. Simple fix.

      Anyways, thanks again!

    • When you want to create an empty textarea, you just need to set its contents to an empty string. $textarea = $document->createElement('textarea', ''); should produce the correct results.

      Edit: Looks like we posted at the same time. What I posted is basically a shortcut to adding a text node.

  2. Hi Steve,

    good to see I’m not the only one trying this cool stuff 🙂

    Anyway, I had some problems with your example, running on PHP 5.3 with the error reporting cranked all the way up:

    Non-static method DOMImplementation::createDocumentType() should not be called statically, assuming $this from incompatible context

    Seems like the DomImplementation methods aren’t static methods, so you’ll need to create an instance first. This snippet works for me:

    // Create document
    $impl = new DOMImplementation();
    $doctype = $impl->createDocumentType(‘html’,
    ‘-//W3C//DTD XHTML 1.1//EN’,
    ‘http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd’);
    $document = $impl->createDocument(‘http://www.w3.org/1999/xhtml’,
    ‘html’, $doctype);

    Also, I’ve found that there’s an easy way to access the root “html” element: Just use the document itself. Since DOMDocument is a subclass of DOMNode, you can just add elements to it direclty:

    $document->appendChild($head);
    $document->appendChild($body);

  3. Woops, never mind the final remark in my previous comment: Adding children to the DomDocument object seems to work, but actually adds the head and body tags _after_ the html object, not inside.

    Anyway, I just noticed that DomDocument has a “documentElement” property, that does actually contain the HTML element. So the code would be:

    $html = $document->documentElement;
    $html->appendChild($head);
    $html->appendChild($body);

  4. I do consider all of the ideas you have introduced to your post.
    They’re very convincing and will certainly work. Nonetheless, the posts are very quick for newbies. Could you please lengthen them a bit from next time? Thank you for the post.

  5. amazing and super tuts with better explanation

  6. hi Steve i am a beginner,
    so how can i compiled your php script to create a html5 file?

  7. I will mention this post is very important.This system can be a really great option to consider.It is easy to process .Thanks


Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.