I needed a function that quickly converts an xml with key/value pairs to an array.
for example:
<?xml version="1.0" encoding="UTF-8"?>
<test>
<key id="Array key #1" value="Value #1"/>
<key id="Array key #2" value="Value #2"/>
<key id="Dupe" value="Duplicate keys"/>
<key id="Dupe" value="create numeric arrays"/>
<key id="And another key">
<![CDATA[
Multiline
data
works
just
as
well.
]]>
</key>
<nested>
<key id="Nested key" value="Nested data works as well, but it still results in a 1 dimensional array."/>
</nested>
</test>
Results in the following array:
array(5) {
["Array key #1"]=>
string(8) "Value #1"
["Array key #2"]=>
string(8) "Value #2"
["Dupe"]=>
array(2) {
[0]=>
string(14) "Duplicate keys"
[1]=>
string(21) "create numeric arrays"
}
["And another key"]=>
string(49) "Multiline
data
works
just
as
well."
["Nested key"]=>
string(73) "Nested data works as well, but it still results in a 1 dimensional array."
}
Here's the code:
<?php
function xml2array($xml) {
$domDocument = new DOMDocument;
$domDocument->loadXML($xml);
$domXPath = new DOMXPath($domDocument);
$array = array();
foreach ($domXPath->query('//key') as $keyDOM) {
$id = $keyDOM->getAttribute('id');
$value = $keyDOM->hasAttribute('value') ? $keyDOM->getAttribute('value') : trim($keyDOM->textContent);
if (array_key_exists($id, $array)) {
if (is_array($array[$id])) {
$array[$id][] = $value;
} else {
$array[$id] = array($array[$id]);
$array[$id][] = $value;
}
} else {
$array[$id] = $value;
}
}
return $array;
}
?>
Document Object Model
- 導入
- インストール/設定
- 定義済み定数
- DOMAttr — DOMAttr クラス
- DOMAttr::__construct — 新しい DOMAttr オブジェクトを作成する
- DOMAttr::isId — 属性が定義済みの ID かどうかを調べる
- DOMCharacterData — DOMCharacterData クラス
- DOMCharacterData::appendData — ノードの文字データの最後に文字列を追加する
- DOMCharacterData::deleteData — 指定した範囲の文字列をノードから削除する
- DOMCharacterData::insertData — 指定した 16 ビット単位のオフセットに、文字列を挿入する
- DOMCharacterData::replaceData — DOMCharacterData ノードの文字列の一部を置換する
- DOMCharacterData::substringData — ノードから指定した範囲のデータを抽出する
- DOMComment — DOMComment クラス
- DOMComment::__construct — 新しい DOMComment オブジェクトを作成する
- DOMDocument — DOMDocument クラス
- DOMDocument::__construct — 新しい DOMDocument オブジェクトを作成する
- DOMDocument::createAttribute — 新しい属性を作成する
- DOMDocument::createAttributeNS — 関連付けられた名前空間に新しい属性を作成する
- DOMDocument::createCDATASection — 新しい cdata ノードを作成する
- DOMDocument::createComment — 新しい comment ノードを作成する
- DOMDocument::createDocumentFragment — 新しい文書片を作成する
- DOMDocument::createElement — 新しい要素ノードを作成する
- DOMDocument::createElementNS — 関連付けられた名前空間に新しい要素を作成する
- DOMDocument::createEntityReference — 新しいエンティティ参照ノードを作成する
- DOMDocument::createProcessingInstruction — 新しい PI ノードを作成する
- DOMDocument::createTextNode — 新しいテキストノードを作成する
- DOMDocument::getElementById — id に対応する要素を検索する
- DOMDocument::getElementsByTagName — 指定したタグ名に対応するすべての要素を検索する
- DOMDocument::getElementsByTagNameNS — 指定した名前空間で、タグ名に対応するすべての要素を検索する
- DOMDocument::importNode — 現在のドキュメントにノードをインポートする
- DOMDocument::load — ファイルから XML を読み込む
- DOMDocument::loadHTML — 文字列から HTML を読み込む
- DOMDocument::loadHTMLFile — ファイルから HTML を読み込む
- DOMDocument::loadXML — 文字列から XML を読み込む
- DOMDocument::normalizeDocument — ドキュメントを正規化する
- DOMDocument::registerNodeClass — 基底ノード型を作成する際に使用する拡張クラスを登録する
- DOMDocument::relaxNGValidate — ドキュメントを relaxNG で検証する
- DOMDocument::relaxNGValidateSource — ドキュメントを relaxNG で検証する
- DOMDocument::save — 内部の XML ツリーをファイルに出力する
- DOMDocument::saveHTML — 内部のドキュメントを HTML 形式の文字列として出力する
- DOMDocument::saveHTMLFile — 内部のドキュメントを HTML 形式でファイルに出力する
- DOMDocument::saveXML — 内部の XML ツリーを文字列として出力する
- DOMDocument::schemaValidate — スキーマに基づいてドキュメントを検証する
- DOMDocument::schemaValidateSource — スキーマに基づいてドキュメントを検証する
- DOMDocument::validate — DTD に基づいてドキュメントを検証する
- DOMDocument::xinclude — DOMDocument オブジェクト内の XIncludes を置換する
- DOMDocumentFragment — DOMDocumentFragment クラス
- DOMDocumentFragment::appendXML — 生の XML データを追加する
- DOMDocumentType — DOMDocumentType クラス
- DOMElement — DOMElement クラス
- DOMElement::__construct — 新しい DOMElement オブジェクトを作成する
- DOMElement::getAttribute — 属性の値を返す
- DOMElement::getAttributeNode — 属性ノードを返す
- DOMElement::getAttributeNodeNS — 属性ノードを返す
- DOMElement::getAttributeNS — 属性の値を返す
- DOMElement::getElementsByTagName — タグ名から要素を取得する
- DOMElement::getElementsByTagNameNS — 名前空間 URI とローカル名から要素を取得する
- DOMElement::hasAttribute — 属性が存在するかどうかを調べる
- DOMElement::hasAttributeNS — 属性が存在するかどうかを調べる
- DOMElement::removeAttribute — 属性を削除する
- DOMElement::removeAttributeNode — 属性を削除する
- DOMElement::removeAttributeNS — 属性を削除する
- DOMElement::setAttribute — 新しい属性を追加する
- DOMElement::setAttributeNode — 新しい属性ノードを要素に追加する
- DOMElement::setAttributeNodeNS — 新しい属性ノードを要素に追加する
- DOMElement::setAttributeNS — 新しい属性を追加する
- DOMElement::setIdAttribute — ID 型の属性を名前で宣言する
- DOMElement::setIdAttributeNode — ID 型の属性をノードで宣言する
- DOMElement::setIdAttributeNS — ID 型の属性をローカル名および名前空間 URI で宣言する
- DOMEntity — DOMEntity クラス
- DOMEntityReference — DOMEntityReference クラス
- DOMEntityReference::__construct — 新しい DOMEntityReference オブジェクトを作成する
- DOMException — DOMException クラス
- DOMImplementation — DOMImplementation クラス
- DOMImplementation::__construct — 新しい DOMImplementation オブジェクトを作成する
- DOMImplementation::createDocument — 指定した型とドキュメント要素の DOMDocument オブジェクトを作成する
- DOMImplementation::createDocumentType — 空の DOMDocumentType オブジェクトを作成する
- DOMImplementation::hasFeature — DOM 実装が、指定した機能を実装しているかどうかを調べる
- DOMNamedNodeMap — DOMNamedNodeMap クラス
- DOMNamedNodeMap::getNamedItem — 名前で指定されたノードを取得する
- DOMNamedNodeMap::getNamedItemNS — ローカル名および名前空間 URI で指定したノードを取得する
- DOMNamedNodeMap::item — インデックスで指定したノードを取得する
- DOMNode — DOMNode クラス
- DOMNode::appendChild — 子要素群の最後に新しい子要素を追加する
- DOMNode::cloneNode — ノードを複製する
- DOMNode::hasAttributes — ノードが属性を保持しているかどうかを調べる
- DOMNode::hasChildNodes — ノードが子を保持しているかどうかを調べる
- DOMNode::insertBefore — 参照しているノードの前に新しい子を追加する
- DOMNode::isDefaultNamespace — 指定した namespaceURI がデフォルトの名前空間かどうかを調べる
- DOMNode::isSameNode — 2 つのノードが等しいかどうかを調べる
- DOMNode::isSupported — 指定したバージョンで機能がサポートされているかどうかを調べる
- DOMNode::lookupNamespaceURI — プレフィックスに基づいて、ノードの名前空間 URI を取得する
- DOMNode::lookupPrefix — 名前空間 URI に基づいて、ノードの名前空間プレフィックスを取得する
- DOMNode::normalize — ノードを正規化する
- DOMNode::removeChild — 子要素群から子要素を削除する
- DOMNode::replaceChild — 子を置き換える
- DOMNodeList — DOMNodeList クラス
- DOMNodelist::item — インデックスで指定したノードを取得する
- DOMNotation — DOMNotation クラス
- DOMProcessingInstruction — DOMProcessingInstruction クラス
- DOMProcessingInstruction::__construct — 新しい DOMProcessingInstruction オブジェクトを作成する
- DOMText — DOMText クラス
- DOMText::__construct — 新しい DOMText オブジェクトを作成する
- DOMText::isWhitespaceInElementContent — このテキストノードが空白を含むかどうかを示す
- DOMText::splitText — 指定したオフセットでノードを 2 つに分割する
- DOMXPath — DOMXPath クラス
- DOMXPath::__construct — 新しい DOMXPath オブジェクトを作成する
- DOMXPath::evaluate — 与えられた XPath 式を評価し、可能であれば結果を返す
- DOMXPath::query — 与えられた XPath 式を評価する
- DOMXPath::registerNamespace — DOMXPath オブジェクトの名前空間を登録する
- DOM 関数
- dom_import_simplexml — SimpleXMLElement オブジェクトから DOMElement オブジェクトを取得する
DOM
Sven Arduwie
17-Mar-2008 03:28
17-Mar-2008 03:28
danf dot 1979 at []gmail[] dot com
24-Feb-2008 04:47
24-Feb-2008 04:47
This is a couple of classes to deal with yahoo yui menu.
/*
$menubar = new MenuBar();
$file = new Menu("File");
$file->setAttribute("href", "http://file.com");
$quit = new Menu("Quit");
$quit->setAttribute("href", "http://quit.com");
$file->appendChild($quit);
$menubar->appendChild($file);
echo $menubar->grab();
*/
//
// Author: Daniel Queirolo.
// LGPL
//
/** ---------------------------------
/** Class MenuBar()
/** Creates a the menubar and appends
/** yuimenubaritems to it.
/** ---------------------------------*/
class MenuBar extends DOMDocument
{
public $menuID = "nav_menu"; // holds the css id that javascript yui menu code should have to recognize
private $UL; // This node holds every menu, This is THE node.
/** ---------------------------------
/** Constructor
/** Generates a menubar skeleton and the UL node
/** ---------------------------------*/
public function __construct() {
parent::__construct();
$rootdiv = parent::createElement("div");
$rootdiv->setAttribute("class", "yui-skin-sam");
parent::appendChild($rootdiv);
$yui_menubar = parent::createElement("div");
$yui_menubar->setAttribute("id", $this->menuID);
$yui_menubar->setAttribute("class", "yuimenubar");
$rootdiv->appendChild($yui_menubar);
$bd = parent::createElement("div");
$bd->setAttribute("class", "bd");
$yui_menubar->appendChild($bd);
$ul = parent::createElement("ul");
$ul->setAttribute("class", "first-of-type");
// ALL Menu() instances ocurr inside an <ul> tag.
$this->UL = $bd->appendChild($ul);
}
/** ---------------------------------
/** appendChild()
/** Appends a new yuimenubaritem to the menubar UL node.
/** This function changes <li> and <a> classes to yuiMENUBARsomething
/** ---------------------------------*/
public function appendChild($child) {
$li = parent::importNode($child->LI, true);
$li->setAttribute("class", "yuimenubaritem");
$li->getElementsByTagName("a")->item(0)->setAttribute("class", "yuimenubaritemlabel");
$this->UL->appendChild($li);
}
public function grab() {
return parent::saveHTML();
}
}
/** ---------------------------------
/** Class Menu()
/** Creates a yuimenuitem li node
/** ---------------------------------*/
class Menu extends DOMDocument {
public $LI; // stores the <li> node (THE link) that will be exported to MenuBar() or used on appendChild()
/** ---------------------------------
/** Constructor
/** Generates a yuimenuitem li node
/** No yuimenubar items are created here. MenuBar handles that.
/** ---------------------------------*/
public function __construct($link_name) {
parent::__construct();
$li = parent::createElement("li");
$li->setAttribute("class", "yuimenuitem");
// LI node stores THE link.
// if appendChild is used, the new (sub) Menu() would be LI node child.
$this->LI = parent::appendChild($li);
$a = parent::createElement("a", $link_name);
$a->setAttribute("class", "yuimenuitemlabel");
$li->appendChild($a);
$this->li = $li;
$this->a = $a;
}
/** ---------------------------------
/** appendChild
/** Appends a (sub) Menu() to current Menu() in LI
/** ---------------------------------*/
public function appendChild($child) {
$yuimenu = parent::createElement("div");
$yuimenu->setAttribute("class", "yuimenu");
$this->LI->appendChild($yuimenu);
$bd = parent::createElement("div");
$bd->setAttribute("class", "bd");
$yuimenu->appendChild($bd);
$ul = parent::createElement("ul");
$bd->appendChild($ul);
// child->NODE holds THE link from the new child (from child's __construct())
$ul->appendChild(parent::importNode($child->LI, true));
}
public function setAttribute($name, $value, $node="a") {
if ($node == "a") {
$this->a->setAttribute($name, $value);
}
else {
$this->li->setAttribute($name, $value);
}
}
}
ptichy at pobox dot sk
09-Feb-2008 11:40
09-Feb-2008 11:40
How to get contents of node with all tags by PHP Dom:
<?php
// XML data
$xml_string = "<?xml version='1.0' ?>
<sentence>
How to get contents of node with all tags by PHP Dom: <br/>
<b>cabbages</b>, <i>tomatoes</i>,
<i>apples</i>, <font color='purple'>aubergines</font>
<ol>
<li> row 1</li>
<li> row 2 </li>
<li> row 3 </li>
</ol>
<table border = '1'>
<tbody>
<tr>
<td>I am best</td>
</tr>
<tr>
<td>Programmer</td>
</tr>
</tbody>
</table>
You see my projects at:
<strong><a href='http://www.itdeveloping.eu' >www.itdeveloping.eu</a></strong>
</sentence>";
// parse it
$doc= new DOMDocument();
if (!$doc->loadXML($xml_string))
{
echo ("Error in XML document");
}
$nody = $doc->getElementsByTagName('sentence'); // gets NodeList
$nod=$nody->item(0);//Node
getContent($Content,$nod);
echo $Content;
function getContent(&$NodeContent="",$nod)
{ $NodList=$nod->childNodes;
for( $j=0 ; $j < $NodList->length; $j++ )
{ $nod2=$NodList->item($j);//Node j
$nodemane=$nod2->nodeName;
$nodevalue=$nod2->nodeValue;
if($nod2->nodeType == XML_TEXT_NODE)
$NodeContent .= $nodevalue;
else
{ $NodeContent .= "<$nodemane ";
$attAre=$nod2->attributes;
foreach ($attAre as $value)
$NodeContent .="{$value->nodeName}='{$value->nodeValue}'" ;
$NodeContent .=">";
getContent($NodeContent,$nod2);
$NodeContent .= "</$nodemane>";
}
}
}
?>
Anonymous
30-Jan-2008 08:37
30-Jan-2008 08:37
In response to...
"If you create your own custom element extending DOMElement and append him in place of the document element, you cannot access to any new members newly defined in your custom class via DOMDocument::$documentElement."
... it is not a bug, it is a feature. The DOMDocument::$documentElement property name may be misleading but according to the DOM Level 2 Core specification it is a convenience attribute meant to access the root element of your DOMDocument. See here: http://www.w3.org/TR/DOM-Level-2-Core/core.html#i-Document
ryoroxdahouse at hotmail dot com
25-Jan-2008 09:14
25-Jan-2008 09:14
I use DOM to generate dynamically XHTML document.
When trying to extend the DOMDocument and DOMElement classes, I found a very annoying bug concerning DOMDocument::$documentElement.
If you create your own custom element extending DOMElement and append him in place of the document element, you cannot access to any new members newly defined in your custom class via DOMDocument::$documentElement.
In my situation, I cannot use DOMDocument::registerNodeClass() because the document element is not necessarily the base class for all the elements in my document.
*******
problem
*******
See bellow for the repro step:
<?php
class MyElement extends DOMElement{
public $myProp="myProp";
public function myMethod(){
return 'myMethod()';
}
}
$myDocument=new DOMDocument();
$myDocument->appendChild(new MyElement('myElement','myElement'));
echo ('$myElement->myProp :'.$myDocument->documentElement->myProp.'<br />');
echo ('$myElement->myMethod :'.$myDocument->documentElement->myMethod().'<br />');
?>
will output:
Notice: Undefined property: DOMElement::$myProp in C:\Program Files\EasyPHP 2.0b1\www\testDOMBug\test2.php on line 11
$myElement->myProp :
Fatal error: Call to undefined method DOMElement::myMethod() in C:\Program Files\EasyPHP 2.0b1\www\testDOMBug\test2.php on line 12
*******
solution
*******
After searching around, I found a pretty odd way to fix this problem. It seems that you have to stock a reference to your appended document element in an user-defined (and persistent) variable (in other words, not only in DOMDocument::$documentElement). See below:
<?php
class MyElement extends DOMElement{
public $myProp="myProp";
public function myMethod(){
return 'myMethod()';
}
}
$myDocument=new DOMDocument();
$mydocumentElement=$myDocument->appendChild(new MyElement('myElement','myElement')); //here is the hack
echo ('$myElement->myProp :'.$myDocument->documentElement->myProp.'<br />');
echo ('$myElement->myMethod :'.$myDocument->documentElement->myMethod().'<br />');
?>
will output:
$myElement->myProp :myProp
$myElement->myMethod :myMethod()
Hope it will help.
moshedolev at gmail dot com
08-Jan-2008 07:15
08-Jan-2008 07:15
I developed a group of functions that make it very easy to extract any information you want from any page you load from the internet. All based on the DOMDocument object.
You can read the entire documentation and download the source code here:
http://www.tintetoner-shop.de/DomUtilities/
Enjoy !!
naudyj at aus3d.com
04-Jan-2008 02:06
04-Jan-2008 02:06
The following can take a XML_TEXT_NODE node and return the contents in an array. Yanick's contribution rocks - but
it overwrote with duplicates only keeping the last line
in the returned array. All the other functions i tested from various sources failed to handle text nodes correctly. Hope this helps someone. It is adapted from code on this site.
function myTextNode($n, &$a)
{
static $depth = 0;
static $sz = '';
if ($cn = $n->firstChild)
{
while ($cn)
{
if ($cn->nodeType == XML_TEXT_NODE)
{
$sz .= $cn->nodeValue;
}
elseif ($cn->nodeType == XML_ELEMENT_NODE)
{
$b = 1;
if ($cn->hasChildNodes())
{
$depth++;
if ($this->myHeadings($cn, $a))
{
if ($sz){
array_push($a, $sz);
$sz = '';
}
}
$depth--;
}
}
$cn = $cn->nextSibling;
}
return $b;
}
}
so you could use:
$nodes = $dom->getElementsByTagName("td");
if($nodes){
foreach ($nodes as $node){
$a = Array();
myTextNode($node, $a);
}
}
Joe dot Cattlet at msn dot com
13-Nov-2007 03:56
13-Nov-2007 03:56
In response to TrollBoy:
You might want to look a little harder before you go and write new code, someone posted a function that does exactly what your class does about a week before you. Also their function allows you to seed the document with a root so it could technically work for HTML documents as well.
trollboy at shoggoth dot net
13-Nov-2007 12:12
13-Nov-2007 12:12
I wrote a simple class to turn an associative array into xml, as this seems like something fairly simple, but nothing out there does it that I know of. That said, enjoy.
<?php
/**
* basic class for converting an array to xml.
* @author Matt Wiseman (trollboy at shoggoth.net)
*
*/
class array2xml {
public $data;
public $dom_tree;
/**
* basic constructor
*
* @param array $array
*/
public function __construct($array){
if(!is_array($array)){
throw new Exception('array2xml requires an array', 1);
unset($this);
}
if(!count($array)){
throw new Exception('array is empty', 2);
unset($this);
}
$this->data = new DOMDocument('1.0');
$this->dom_tree = $this->data->createElement('result');
$this->data->appendChild($this->dom_tree);
$this->recurse_node($array, $this->dom_tree);
}
/**
* recurse a nested array and return dom back
*
* @param array $data
* @param dom element $obj
*/
private function recurse_node($data, $obj){
$i = 0;
foreach($data as $key=>$value){
if(is_array($value)){
//recurse if neccisary
$sub_obj[$i] = $this->data->createElement($key);
$obj->appendChild($sub_obj[$i]);
$this->recurse_node($value, $sub_obj[$i]);
} elseif(is_object($value)) {
//no object support so just say what it is
$sub_obj[$i] = $this->data->createElement($key, 'Object: "' . $key . '" type: "' . get_class($value) . '"');
$obj->appendChild($sub_obj[$i]);
} else {
//straight up data, no weirdness
$sub_obj[$i] = $this->data->createElement($key, $value);
$obj->appendChild($sub_obj[$i]);
}
$i++;
}
}
/**
* get the finished xml
*
* @return string
*/
public function saveXML(){
return $this->data->saveXML();
}
}
$test = array(
'cat'=>'animal',
'container'=> array(
'thing'=>'stuff',
'thing2'=>'stuff2',
'thing3'=>3,
'thing4'=>4,
'wtf'=> new stdClass(),
),
'foo'=>'bar',
);
var_dump($test);
try {
$o_test = new array2xml($test);
} catch (Exception $e) {
echo $e->getMessage();
}
echo $o_test->saveXML();
?>
jim.filter (at gmail.com)
03-Nov-2007 04:42
03-Nov-2007 04:42
Array to DOM
Here is a recursive function to turn a multidimensional array into an XML document. It will handle multiple elements of the same tag, but only one per parent element. IE:
Can't generate: Can generate:
<root> <root>
<sub1>data1</sub1> <subs1>
<sub1>data2</sub1> <value>data1</value>
<sub2>data1</sub2> <value>data2</value>
<sub2>data2</sub2> </subs1>
</root> <subs2>
<value>data1</value>
<value>data2</value>
<subs2>
</root>
Also, the function performs no type of error checking on your array and will throw a DOMException if a key value you used in your array contains invalid characters for a proper DOM tag. This function is untested for "deep" multidimensional arrays.
Complete code ready to run with example:
<?PHP
function AtoX($array, $DOM=null, $root=null){
if($DOM == null){$DOM = new DOMDocument('1.0', 'iso-8859-1');}
if($root == null){$root = $DOM->appendChild($DOM->createElement('root'));}
$name = $array['#MULTIPLE_ELEMENT_NAME'];
foreach($array as $key => $value){
if(is_int($key) && $name != null){
if(is_array($value)){
$subroot = $root->appendChild($DOM->createElement($name));
AtoX($value, $DOM, $subroot);
}
else if(is_scalar($value)){
$root->appendChild($DOM->createElement($name, $value));
}
}
else if(is_string($key) && $key != '#MULTIPLE_ELEMENT_NAME'){
if(is_array($value)){
$subroot = $root->appendChild($DOM->createElement($key));
AtoX($value, $DOM, $subroot);
}
else if(is_scalar($value)){
$root->appendChild($DOM->createElement($key, $value));
}
}
}
return $DOM;
}
$array = array(
'#MULTIPLE_ELEMENT_NAME' => 'GenericDatas',
'Date' => 'November 03, 2007',
'Company' => 'Facility One',
'Field' => 'Facility Management Software',
'Employees' => array(
'#MULTIPLE_ELEMENT_NAME' => 'Employee',
'Cindy',
'Sean',
'Joe',
'Owen',
'Jim',
'Dale',
'Kelly',
'Ryan',
'Johnathan',
'Robin',
'William Marcus',
'NewCoops' => array(
'#MULTIPLE_ELEMENT_NAME' => 'Coop',
'John',
'Tyler',
'Ray',
'Dawn'
)
),
'Datas',
'DATAS',
'OtherDatas'
);
$DOM = new DOMDocument('1.0', 'iso-8859-1');
$root = $DOM->appendChild($DOM->createElement('CompanyData'));
$DOM = AtoX($array, $DOM, $root);
$DOM->save('C:\test.xml');
?>
freyjkell at gmail dot com
02-Nov-2007 07:02
02-Nov-2007 07:02
In order to REALLY well handle XHTML entities with DOM, you can do following things:
1. Add this DOCTYPE to your documents
<!DOCTYPE xhtmlentities PUBLIC "-//W3C//ENTITIES XHTML Character Entities 1.0//EN" "/xhtml11.ent">
2. Copy http://freyjkell.ovh.org/xhtml11.ent into your document root.
3. In your PHP:
<?php
$dom=new DOMDocument();
$dom->load('file.xhtml',LIBXML_DTDLOAD); // NOT resolveExternals - it needs true doctype, and includes crap code
// some DOM operations
$doctype=DOMImplementation::createDocumentType("html","-//W3C//DTD XHTML 1.1//EN","http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"); // creating real doctype
$output=DOMImplementation::createDocument('','',$doctype);
$output->appendChild($output->importNode($dom->documentElement,true));
$output->encoding='utf-8';
$output=$output->saveXML();
$xhtml=preg_match(
'/application\/xhtml\+xml(?![+a-z])'.
'(;q=(0\.\d{1,3}|[01]))?/i',
$_SERVER['HTTP_ACCEPT'],$xhtml) &&
(isset($xhtml[2])?$xhtml[2]:1) > 0 ||
strpos($_SERVER["HTTP_USER_AGENT"],
"W3C_Validator")!==false ||
strpos($_SERVER["HTTP_USER_AGENT"],
"WebKit")!==false; // XHTML Content-Negotiation
header('Content-Type: '.($xhtml?'application/xhtml+xml':'text/html').'; charset=utf-8');
print $output;
?>
PHPdeveloper
06-Sep-2007 11:46
06-Sep-2007 11:46
The Yanik's dom2array() function (added on 14-Mar-2007 08:40) does not handle multiple nodes with the same name, i.e.:
<foo>
<name>aa</name>
<name>bb</name>
</foo>
It will overwrite former and your array will contain just the last one ("bb")
Francois Hill
01-Aug-2007 08:21
01-Aug-2007 08:21
In response to lutfi at smartconsultant dot us :
(see my post on
http://fr2.php.net/manual/en/
function.dom-domdocument-getelementsbytagname.php
)
Use this class I wrote:
class XPathableNode extends DOMNode
{
protected $Node;
protected $DOMDocument_from_node;
protected $DOMXpath_for_node;
public function __construct(/* DOMNode */ $node)
{
$this->Node=$node;
$this->DOMDocument_from_node=new
DomDocument();
$domNode=$this->DOMDocument_from_node
->importNode($this->Node, true);
$this->DOMDocument_from_node
->appendChild($domNode);
$this->DomXpath_for_node =
new Domxpath($this->
DOMDocument_from_node);
}
public function convertHTML()
{ return $this->DOMDocument_from_node
->saveHTML();
}
public /*DomNodeList*/ function applyXpath($xpath)
{ return $this->DomXpath_for_node
->query($xpath);
}
}
(sorry for the display... What a terrible hinderance on the
part of php.net !)
Then :
Make a new XPathableNode out of your parent node.
You may then retrieve a DOMNodeList from it by applying a
xpath (thus being able to specify the depth and name of
elements you want).
Has got me around some (of the many) DOM awkwardnesses a few times.
;o)
lpetrov AT axisvista.com
21-Jul-2007 02:41
21-Jul-2007 02:41
Basicly there are alot of problems on dynamic namespaces registering and other maybe 'not well' documented parts of DOM.
Here is an article covering some of the problems that our company web developers found while we were developing a template engine for our new framework.
The link:
http://blog.axisvista.com/?p=35
lutfi at smartconsultant dot us
20-Jul-2007 06:06
20-Jul-2007 06:06
i have some problem parsing recurred xml tree here:
<menu name='parent1' >
<submenu name='file' >
<submenu name='open' >Open file</submenu>
<submenu name='close' >Close file</submenu>
</submenu>
<submenu name='edit' >
<submenu name='cut' >Cut Clipboards</submenu>
<submenu name='copy' >Copy Clipboards</submenu>
<submenu name='paste' >Paste Clipboards</submenu>
</submenu>
</menu>
with getElementsByTagName all submenu is on the same level.
i want it to be structured like tree list, but not change the 'submenu' tag
Sanados at failure dot at
05-Jun-2007 11:45
05-Jun-2007 11:45
appended to
brian dot reynolds at risaris dot com
20-Feb-2007 10:09
when you got variable nodes at start you array fails and looses nodes beneath.
solution that counts occurance though eats up performance:
function xmlToArray($n)
{
$xml_array = array();
$occurance = array();
foreach($n->childNodes as $nc)
{
$occurance[$nc->nodeName]++;
}
foreach($n->childNodes as $nc){
if( $nc->hasChildNodes() )
{
if($occurance[$nc->nodeName] > 1)
{
$xml_array[$nc->nodeName][] = xmlToArray($nc);
}
else
{
$xml_array[$nc->nodeName] = xmlToArray($nc);
}
}
else
{
return utf8_decode($nc->nodeValue);
}
}
return $xml_array;
}
Yanik <clonyara(at)ahoo(dot)com>
14-Mar-2007 08:40
14-Mar-2007 08:40
I hate DOM model !
so I wrote dom2array simple function (simple for use):
function dom2array($node) {
$res = array();
print $node->nodeType.'<br/>';
if($node->nodeType == XML_TEXT_NODE){
$res = $node->nodeValue;
}
else{
if($node->hasAttributes()){
$attributes = $node->attributes;
if(!is_null($attributes)){
$res['@attributes'] = array();
foreach ($attributes as $index=>$attr) {
$res['@attributes'][$attr->name] = $attr->value;
}
}
}
if($node->hasChildNodes()){
$children = $node->childNodes;
for($i=0;$i<$children->length;$i++){
$child = $children->item($i);
$res[$child->nodeName] = dom2array($child);
}
}
}
return $res;
}
Nevyn at N dot O dot S dot P dot A dot M dot emai dot it
10-Mar-2007 04:35
10-Mar-2007 04:35
I wrote a couple of functions to:
- create a DOMDocument from a file
- parse the namespaces in it
- create a XPath object with all the namespaces registered
- load the schemalocations
- validate the file on the main schema (the one without prefix)
It is useful for me, see if it is also for someone else!!
Giulio
function decodeNode($node)
{
$out = $node->ownerDocument->saveXML($node);
$re = "{^<((?:\\w*:)?\\w*)". //the tag name
"[\\s\n\r]*((?:[\\s\n\r]*".
"(?:\\w*:)?\\w+[\\s\n\r]*=[\\s\n\r]*". //possible attribute name
"(?:\"[^\"]*\"|\'[^\']*\'))*)". //attribute value
"[\\s\n\r]*>[\r\n]*".
"((?:.*[\r\n]*)*)". //content
"[\r\n]*</\\1>$}"; //closing tag
preg_match($re, $out, $mat);
return $mat;
}
function innerXml($node)
{
$mat = decodeNode($node);
return $mat[3];
}
function getnodeAttributes($node)
{
$mat = decodeNode($node);
$txt = $mat[2];
$re = "{((?:\\w*:)?\\w+)[\\s\n\r]*=[\\s\n\r]*(\"[^\"]*\"|\'[^\']*\')}";
preg_match_all($re, $txt, $mat);
$att = array();
for ($i=0; $i<count($mat[0]); $i++)
{
$value = $mat[2][$i];
if ($value[0] == "\'" || $value[0] == "\"")
{
$len = strlen($value);
$value = substr($value, 1, strlen($value)-2);
}
$att[ $mat[1][$i] ] = $value;
}
return $att;
}
function loadXml($file)
{
$doc = new DOMDocument();
$doc->load($file);
//cerca l'attributo xmlns
$xsi = false;
$doc->namespaces = array();
$doc->xpath = new DOMXPath($doc);
$attr = getnodeAttributes($doc->documentElement);
foreach ($attr as $name => $value)
{
if (substr($name,0,5) == "xmlns")
{
$uri = $value;
$pre = $doc->documentElement->lookupPrefix($uri);
if ($uri == "http://www.w3.org/2001/XMLSchema-instance")
$xsi = $pre;
$doc->namespaces[$pre] = $uri;
if ($pre == "")
$pre = "noname";
$doc->xpath->registerNamespace($pre, $uri);
}
}
if ($xsi)
{
$doc->schemaLocations = array();
$lst = $doc->xpath->query("//@$xsi:schemaLocation");
foreach($lst as $el)
{
$re = "{[\\s\n\r]*([^\\s\n\r]+)[\\s\n\r]*([^\\s\n\r]+)}";
preg_match_all($re, $el->nodeValue, $mat);
for ($i=0; $i<count($mat[0]); $i++)
{
$value = $mat[2][$i];
$doc->schemaLocations[ $mat[1][$i] ] = $value;
}
}
$olddir = getcwd();
chdir(dirname($file));
$schema = $doc->schemaLocations[$doc->namespaces[""]];
if (substr($schema,0,7) == "file://")
{
$schema = substr($value,7);
}
if (!$doc->schemaValidate($schema))
dbg()->err("Invalid file");
chdir($olddir);
}
return $doc;
}
brian dot reynolds at risaris dot com
20-Feb-2007 10:09
20-Feb-2007 10:09
I found the xml2array function below very useful, but there seems to be a bug in it. The $item variable was never getting set. I've expanded this out to be a bit more readable, and the corrected code is :
function xmlToArray($n)
{
$return=array();
foreach($n->childNodes as $nc){
if( $nc->hasChildNodes() ){
if( $n->firstChild->nodeName== $n->lastChild->nodeName&&$n->childNodes->length>1){
$item = $n->firstChild;
$return[$nc->nodeName][]=$this->xmlToArray($item);
}
else{
$return[$nc->nodeName]=$this->xmlToArray($nc);
}
}
else{
$return=$nc->nodeValue;
}
}
return $return;
}
cooper at asu dot ntu-kpi dot kiev dot ua
22-Nov-2006 11:32
22-Nov-2006 11:32
If you are using not object-oriented functions and it takes too much time to change them all (or you'll be replacing them later) then as a temporary decision can be used this modules:
For DOM XML:
http://alexandre.alapetite.net/doc-alex/domxml-php4-php5/
For XSLT:
http://alexandre.alapetite.net/doc-alex/xslt-php4-php5/
cormac at idreamproducts dot com
17-Oct-2006 12:48
17-Oct-2006 12:48
Most email clients ignore stylesheets in HTML formatted emails. The best way to ensure your HTML is formatted correctly by a broad spectrum of email clients, including webmail implementations as Gmail, is to use inline style attributes. The following function uses DOM to parse an inline stylesheet, and will replace element class and id attributes with inline style attributes, and add inline style attributes for generic tag stylesheet rules. It will remove the stylesheet and any used class and id attributes as these are defunct for most email clients. It is a fairly lightweight function and does not support CSS inheritance, but will work for simple stylesheets e.g.:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>HTML EMAIL</title>
<style type="text/css">
body {
margin: 10px 10px;
font: 8pt arial;
background: #fff;
color: #000;
}
p {
margin: 0 0 10px;
line-height: 1.2em;
text-align: justify;
}
p.centered {
text-align: centre;
}
p#right {
text-align: right;
}
</style>
</head>
<body>
<p>Sample text justified</p>
<p class="centered">Centered text here</p>
<p id="right">Right-aligned text</p>
</body>
</html>
Here's the function:
<?php
function parseStyleSheetfor Email($html)
{
$doc = new DOMDocument;
$doc->loadHTML($html);
// grab inline stylesheet as DOM object
$oStyle = $doc->getElementsByTagName('style')->item(0);
// grab rule identifiers and rules
preg_match_all('/^([-#._a-z0-9]+) ?\{(.*?)\}/ims', $oStyle->nodeValue, $aMatches, PREG_SET_ORDER);
foreach ($aMatches as $aRule) {
$rule_id = $aRule[1];
// clean up rules
$rule = str_replace(array("\r", "\n", ' ', '; '), array('', '', ' ', ';'), $aRule[2]);
$rule = preg_replace(array('/^ /', '/;$/'), '', $rule);
// generic rules
if (!strstr($rule_id, '.') && !strstr($rule_id, '#')) {
$items = $doc->getElementsByTagName($rule_id);
// set style attribute equal to rule from stylesheet
foreach ($items as $item) {
// if there is already inline style append it to end of stylesheet rule
$current_style = $item->getAttribute('style');
if (!empty($current_style)) {
$item->setAttribute('style', $rule . ';' . $current_style);
} else {
$item->setAttribute('style', $rule);
}
}
// classes
} elseif (strstr($rule_id, '.')) {
list($rule_tag, $rule_class) = explode('.', $rule_id);
$items = $doc->getElementsByTagName($rule_tag);
foreach ($items as $item) {
$class = $item->getAttribute('class');
if ($class == $rule_class) {
// if there is already inline style append it to end of stylesheet rule
$current_style = $item->getAttribute('style');
if (!empty($current_style)) {
$item->setAttribute('style', $current_style . ';' . $rule);
} else {
$item->setAttribute('style', $rule);
}
// remove class as it won't be used now
$item->removeAttribute('class');
}
}
// ids
} elseif (strstr($rule_id, '#')) {
list($rule_tag, $id) = explode('#', $rule_id);
$item = $doc->getElementById($id);
$current_style = $item->getAttribute('style');
if (!empty($current_style)) {
$item->setAttribute('style', $current_style . ';' . $rule);
} else {
$item->setAttribute('style', $rule);
}
// remove class as it won't be used now
$item->removeAttribute('id');
}
}
// remove inline stylesheet
$oStyle->parentNode->removeChild($oStyle);
return $doc->saveHTML();
}
?>
sean at lookin3d dot com
17-Aug-2006 07:21
17-Aug-2006 07:21
$xmlDoc=<<<XML
<?xml version="1.0"?>
<methodCall>
<methodName>examples.getStateName</methodName>
<params>
<param>
<value><i4>41</i4></value>
</param>
</params>
</methodCall>
XML;
$xml= new DOMDocument();
$xml->preserveWhiteSpace=false;
$xml->loadXML($xmlDoc);
print_r(xml2array($xml));
function xml2array($n)
{
$return=array();
foreach($n->childNodes as $nc)
($nc->hasChildNodes())
?($n->firstChild->nodeName== $n->lastChild->nodeName&&$n->childNodes->length>1)
?$return[$nc->nodeName][]=xml2array($item)
:$return[$nc->nodeName]=xml2array($nc)
:$return=$nc->nodeValue;
return $return;
}
massimo dot scamarcia at gmail dot com
20-Feb-2006 11:56
20-Feb-2006 11:56
If you're moving from PHP4 to PHP5, you can keep your scripts untouched using this:
http://alexandre.alapetite.net/doc-alex/domxml-php4-php5/index.en.html
simlee at indiana dot edu
29-Dec-2005 09:16
29-Dec-2005 09:16
The project I'm currently working on uses XPaths to dynamically navigate through chunks of an XML file. I couldn't find any PHP code on the net that would build the XPath to a node for me, so I wrote my own function. Turns out it wasn't as hard as I thought it might be (yay recursion), though it does entail using some PHP shenanigans...
Hopefully it'll save someone else the trouble of reinventing this wheel.
<?php
function getNodeXPath( $node ) {
// REMEMBER THAT XPATHS USE BASE-1 INSTEAD OF BASE-0!!!
// Get the index for the current node by looping through the siblings.
$parentNode = $node->parentNode;
if( $parentNode != null ) {
$nodeIndex = 0;
do {
$testNode = $parentNode->childNodes->item( $nodeIndex );
$nodeName = $testNode->nodeName;
$nodeIndex++;
// PHP trickery! Here we create a counter based on the node
// name of the test node to use in the XPath.
if( !isset( $$nodeName ) ) $$nodeName = 1;
else $$nodeName++;
// Failsafe return value.
if( $nodeIndex > $parentNode->childNodes->length ) return( "/" );
} while( !$node->isSameNode( $testNode ) );
// Recursively get the XPath for the parent.
return( getNodeXPath( $parentNode ) . "/{$node->nodeName}[{$$nodeName}]" );
} else {
// Hit the root node! Note that the slash is added when
// building the XPath, so we return just an empty string.
return( "" );
}
}
?>
johanwthijs-at-hotmail-dot-com
14-Dec-2005 03:25
14-Dec-2005 03:25
Being an experienced ASP developer I was wondering how to replace textual content of a node (with msxml this is simply acheived by setting the 'text' property of a node). Out of frustration I started to play around with SimpleXml but I could not get it to work in combination with xPath.
I took me a lot of time to find out so I hope this helps others:
function replaceNodeText($objXml, $objNode, $strNewContent){
/*
This function replaces a node's string content with strNewContent
*/
$objNodeListNested = &$objNode->childNodes;
foreach ( $objNodeListNested as $objNodeNested ){
if ($objNodeNested->nodeType == XML_TEXT_NODE)$objNode->removeChild ($objNodeNested);
}
$objNode->appendChild($objXml->createTextNode($strNewContent));
}
$objXml= new DOMDocument();
$objXml->loadXML('<root><node id="1">bla</note></root>');
$objXpath = new domxpath($objXml);
$strXpath="/root/node[@id='1']";
$o