Although the built-in DOM functions are great, since they're designed to support generic XML, generating HTML DOMs becomes particularly verbose. I ended up writing this function to drastically speed things up.
Instead of calling something like
<?php
$div = $dom->createElement("div");
$div->setAttribute("class","MyClass");
$div->setAttribute("id","MyID");
$someOtherDiv->appendChild($div);
?>
you can accomplish the same thing with:
<?php
$div = newElement("div", $someOtherDiv, "class=MyClass;id=MyID");
?>
The "key1=value;key2=value" syntax is really fast to use, but obviously doesn't hold up if your content has those characters in it. So, you can also pass it an array:
<?php
$div = newElement("div", $someOtherDiv, array("class","MyClass"));
?>
Or an array of arrays, representing different attributes:
<?php
$div = newElement("form", $someOtherDiv, array(array("method","get"), array("action","/refer/?id=5");
?>
Here's the function:
<?php
function newElement($type, $insertInto = NULL, $params=NULL, $content="")
{
$tempEl = $this->dom->createElement($type, $content);
if(gettype($params) == "string" && strlen($params) > 0)
{
$attributesCollection =split(";", $params);
foreach($attributesCollection as $attribute)
{
$keyvalue = split("=", $attribute);
$tempEl->setAttribute($keyvalue[0], $keyvalue[1]);
}
}
if(gettype($params) == "array")
{
if(gettype($params[0]) == "array")
{
foreach($params as $attribute)
{
$tempEl->setAttribute($attribute[0], $attribute[1]);
}
} else {
$tempEl->setAttribute($params[0], $params[1]);
}
}
?>
DOMDocument::createElement
(PHP 5)
DOMDocument::createElement — Crea un nuevo nodo elemento
Descripción
Esta función crea una nueva instancia de la clase DOMElement. Este nodo no se mostrará en el documento a no ser que sea insertado con (p.e.j.) DOMNode::appendChild().
Parámetros
-
name -
El nombre de etiqueta del elemento.
-
value -
El valor del elemento. De manera predeterminada se creará un elemento vacio. El valor también puede ser asignado más tarde con DOMElement::$nodeValue.
Valores devueltos
Devuelve una nueva instanca de la clase DOMElement o FALSE
si ha ocurrido un error.
Errores/Excepciones
-
DOM_INVALID_CHARACTER_ERR -
Lanzado si
namecontiene un carácter inválido.
Ejemplos
Ejemplo #1 Crear un nuevo elemento e insertarlo como raíz
<?php
$dom = new DOMDocument('1.0', 'utf-8');
$element = $dom->createElement('test', 'This is the root element!');
// Insertamos el nuevo elemento como raíz (hijo del documento)
$dom->appendChild($element);
echo $dom->saveXML();
?>
El resultado del ejemplo sería:
<?xml version="1.0" encoding="utf-8"?> <test>This is the root element!</test>
Notas
Nota:
El parámetro
valueno será escapado. Utilice DOMDocument::createTextNode() para crear un nodo de texto con soporte para escape de carácteres.
Ver también
- DOMNode::appendChild() - Añade un nuevo hijo al final de los hijos
- DOMDocument::createAttribute() - Crear nuevo attribute
- DOMDocument::createAttributeNS() - Crea un nuevo nodo atributo con un namespace asociado.
- DOMDocument::createCDATASection() - Crea un nuevo nodo cdata
- DOMDocument::createComment() - Crea un nuevo nodo de comentario
- DOMDocument::createDocumentFragment() - Crea un nuevo fragmento de documento
- DOMDocument::createElementNS() - Crea un nuevo nodo elemento con el nombre de espacio asociado
- DOMDocument::createEntityReference() - Create new entity reference node
- DOMDocument::createProcessingInstruction() - Crea un nuevo nodo PI
- DOMDocument::createTextNode() - Crea un nuevo nodo de texto
Note that the NUL character "\0" is not in the list of invalid characters for $name, so no error is triggered, but the tag name will be truncated at the null byte:
<?php
$dom = new DOMDocument('1.0', 'utf-8');
$el = $dom->createElement('foo' . "\0" . 'bar', 'Hello World');
echo $el->tagName; // outputs "foo"
?>
With regard to the note below about needing htmlentities to avoid warnings about unterminated entity references, I thought it worthwhile to mention that that you don't need to with createTextNode and DOMText::__construct. If you mix both methods of setting text nodes and do (or don't) apply htmlentities consistently to all data to be displayed, you'll get &s (or warnings and badly-formed xml).
It's probably in one's best interest to extend DOMElement and DOMDocument so that it creates a DOMText node and appends it, rather than passing it up to the DOMElement constructor. Otherwise, good luck using (or not using) htmlentities in all the right places in your code, especially as code changes get made.
<?php
class XDOMElement extends DOMElement {
function __construct($name, $value = null, $namespaceURI = null) {
parent::__construct($name, null, $namespaceURI);
}
}
class XDOMDocument extends DOMDocument {
function __construct($version = null, $encoding = null) {
parent::__construct($version, $encoding);
$this->registerNodeClass('DOMElement', 'XDOMElement');
}
function createElement($name, $value = null, $namespaceURI = null) {
$element = new XDOMElement($name, $value, $namespaceURI);
$element = $this->importNode($element);
if (!empty($value)) {
$element->appendChild(new DOMText($value));
}
return $element;
}
}
$doc1 = new XDOMDocument();
$doc1_e1 = $doc1->createElement('foo', 'bar & baz');
$doc1->appendChild($doc1_e1);
echo $doc1->saveXML();
$doc2 = new XDOMDocument();
$doc2_e1 = $doc2->createElement('foo');
$doc2->appendChild($doc2_e1);
$doc2_e1->appendChild($doc2->createTextNode('bar & baz'));
echo $doc2->saveXML();
?>
Text specified in createElement:
<?xml version=""?>
<foo>bar & baz</foo>
Text added via createTextNode:
<?xml version=""?>
<foo>bar & baz</foo>
You may think insertBefore and insertAfter is a direct alternative for appendChild, this is not the case.
<?php
$dom = new DOMDocument();
$dom->load($file);
$dom->appendChild($newNode); //Works fine
$dom->insertBefore($newNode, $refNode); //Will fail
$refNode->parentNode->insertBefore($newNode, $refNode); // thanx to yasindagli (first post)
?>
To create elements with attributes,
<?php
function createElement($domObj, $tag_name, $value = NULL, $attributes = NULL)
{
$element = ($value != NULL ) ? $domObj->createElement($tag_name, $value) : $domObj->createElement($tag_name);
if( $attributes != NULL )
{
foreach ($attributes as $attr=>$val)
{
$element->setAttribute($attr, $val);
}
}
return $element;
}
$dom = new DOMDocument('1.0', 'utf-8');
$elm = createElement($dom, 'foo', 'bar', array('attr_name'=>'attr_value'));
$dom->appendChild($elm);
echo $dom->saveXML();
?>
outputs :
<?xml version="1.0" encoding="utf-8"?>
<foo attr_name="attr_value">bar</foo>
In response to 'mikek dot nospam at nospam dot muonics dot com', here is a more correct version, as values have to be UTF-8 encoded, and there's absolutely no reason to create an extra DOMElement class.
<?php
class ADOMDocument extends DOMDocument {
function __construct($version = null, $encoding = null) {
parent::__construct($version, $encoding);
}
public function createElement($tagName, $value=null) {
$node=parent::createElement($tagName);
if (!is_null($value)) {
$node->appendChild(
$this->createTextNode(utf8_encode($value))
);
}
return $node;
}
public function createElementNS($namespaceURI, $qualifiedName, $value=null) {
$node=parent::createElementNS($namespaceURI, $qualifiedName);
if (!is_null($value)) {
$node->appendChild(
$this->createTextNode(utf8_encode($value))
);
}
return $node;
}
}
?>
Note that the second parameter (value), although convenient, is non-standard. You should create elements like this instead:
<?php
$doc = new DOMDocument('1.0', 'iso-8859-1');
$root = $doc->createElement('test');
$doc->appendChild($root);
$root_text = $doc->createTextNode('This is the root element!');
$root->appendChild($root_text);
print $doc->saveXML();
?>
Or, alternatively, extend the DOMDocument class and add your own custom, convenience method to avoid intruding on the standard:
<?php
class CustomDOMDocument extends DOMDocument {
function createElementWithText($name, $child_text) {
// Creates an element with a child text node
// @param string $name element tag name
// @param string $child_text child node text
// @return object new element
$element = $this->createElement($name);
$element_text = $this->createTextNode($child_text);
$element->appendChild($element_text);
return $element;
}
}
$doc = new CustomDOMDocument('1.0', 'iso-8859-1');
$root = $doc->createElementWithText('test', 'This is the root element!');
$doc->appendChild($root);
print $doc->saveXML();
?>
Also use caution with (or avoid) the 'DOMElement->nodeValue' property. It can return some unexpected values and changing its value will replace (remove) all descendants of the element with a single text node. It's also non-standard; according to the DOM spec it should return NULL.
To avoid warning message "unterminated entity reference" you may use htmlentities() for escaping supplied value:
<?php
//...
$dom->createElement('name', htmlentities($text))
//...
?>
