9 Mar 2009, 4:59 p.m.

Adding a Doctype Declaration to a DOMDocument in PHP

I've recently been spending quite a lot of time with PHP's DOM extension, which is extremely useful for both generating and parsing XML.

In this particular case, I'm generating XML, and it's imperative that the XML markup which the code is generating should contain a Doctype declaration (DTD). It isn't hard to do that using DOM, but it did take a little bit of hunting around in the manual and online, so here's a quick overview of how to add a Doctype declaration to a DOMDocument.

Of course, there isn't anything as handy as, say, $document->addDoctype('xhtml'), that would be too easy. Instead, the first step is to instance DOMImplementation, and have that create an instance of DOMDocumentType.

Once the DOMDocumentType object has been created, it can be passed as a parameter to DOMImplementation::createDocument(), which returns a DOMDocument that you can start working with.

That isn't terribly clear, so let's take a look at an example in action. In this case let's imagine that we're generating a WML page, and so our DTD will look like:

DOMImplementation::createDocumentType() takes three parameters: "the qualified name of the document type to create", "the external subset public identifier" and finally "the external subset system identifier". If that means as little to you as it did to me, don't worry: they can all be copied and pasted directly from an existing Doctype declaration. The PHP code is as follows:

createDocumentType('wml', '-//WAPFORUM//DTD WML 1.1//EN', 'http://www.wapforum.org/DTD/wml_1.1.xml'); $document = $implementation->createDocument('', '', $dtd);

From then on, you can work with $document exactly as with any other DOMDocument, for that is what it is.

For good measure, here's an example of creating an XHTML Mobile Profile (XHTML-MP) document:

createDocumentType('html', '-//WAPFORUM//DTD XHTML Mobile 1.1//EN', 'http://www.openmobilealliance.org/tech/DTD/xhtml-mobile11.dtd'); $document = $implementation->createDocument('', '', $dtd);

Once this is all in place, the Doctype declaration will automatically be added to the WML or XHTML generated by, for example, DOMDocument::saveXml().

Validation

One really nice side effect of specifying the DTD in a DOMDocument is that the document itself can subsequently be validated against the DTD very easily. It's as simple as:

validate();

That's a nice win, and a helpful addition to your test suite if you happen to be unit testing code which generates markup - which can otherwise be very fiddly indeed. DOMDocument::validate() returns a boolean true/false, so it should be trivial to integrate this into tests written against any testing framework.

Posted by Simon at 01:53:00 PM
6 Nov 2009, 4:03 a.m.

José

Hi, and thanks for its post, really help me so much to made something like that, but i have a doubt. How can insert a DocType to a DOMDocument that i already load from a XML file whit $domdocument->load('my.xml')?
Its file have not a doctype declared, but for make a validation i need insert it.
Can you give me some idea?

6 Nov 2009, 12:44 p.m.

Ciaran McNulty

@José - You could create a new document, then import the nodes from the existing document into it:

$child = $yourDom->importNode($simonsDom->documentElement);
$yourDom->documentElement->appendChild($child, true);

6 Nov 2009, 5:30 p.m.

José

Thanks again for the answer & time, i do this:
$node = $documentXML->importNode($loadedXML->documentElement, true);
//$documentXML is an empty XML Document $documentXML->appendChild($node);

but the result isn't the expected:
when echo $documentXML->saveXML() i see some changes in the structure of the XML's tree and its element's attributes and values; sorry for the necesary question. But, are you some idea why is it?

[comment snipped]

10 Nov 2009, 1:54 p.m.

Simon [ADMIN]

José -

I've snipped your later comments as they didn't display as I assume you hoped they might. They also contained your email address in plain text.

You'll probably have more luck asking questions like yours over at Stack Overflow - you'll find a much larger audience there, and many of the people there really have a lot of time on their hands, so you're sure to find help.

14 Apr 2010, 12:30 a.m.

Paulo Fonseca

Hi, i used your code in the following way:

<?php

$implementation = new DOMImplementation();

$dtd = $implementation->createDocumentType('ementa', '', 'ementa.dtd');

$document = $implementation->createDocument('', '', $dtd);

//$document = new DOMDocument('1.0', 'UTF-8');

$document->formatOutput = true;

//Cria a Root
$root = $document->createElement( "ementa" );
$document->appendChild( $root );

This will create the xml file with the correct DOCTYPE.

My question:

The 1st header generated is:

<?xml version="1.0"?>

How can i add encoding info to this header to make it like this:

<?xml version="1.0" encoding="UTF-8"?>

Many Thanks for your help!

1 Aug 2010, 6:07 p.m.

Windigo

Thanks so much; this was "less than obvious" in the PHP documentation at the best, and exactly what I was looking for!

20 Nov 2010, 12:42 p.m.

Terence Simpson

@Paulo
You can set the encoding after creating the document node with:
$document->encoding = 'utf-8';

3 Jun 2012, 11:19 a.m.

nickl-

After some deliberation and lots of persistence I managed to change the doctype on render this is what the render function looks like:


public function render($doctype='')
{
if ($doctype) {
$doc = new DOMDocument();
$doc->loadHTML($doctype);
$dt = $doc->doctype;
$di = new DOMImplementation();
$dt = $di->createDocumentType($dt->name, $dt->publicId, $dt->systemId);
$this->dom->replaceChild($dt, $this->dom->doctype);
}
return preg_replace('/<\?xml[\s\S]*\?>\n/', '', $this->dom->saveXML());
}
Hope that helps =)