PHP 日本研討會 2024

文件物件模型

新增註解

使用者貢獻的註解 38 則註解

tobiasz.cudnik[at]gmail.com
16 年前
如果您需要 DOM 的簡單介面,請查看 phpQuery - PHP 的 jQuery 移植版
http://code.google.com/p/phpquery/

它使用 CSS 選取器來擷取節點。
以下是如何運作的範例
<?php
// 只需要一個檔案來包含
require('phpQuery/phpQuery.php');

$html = '
<div>
mydiv
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
</div>'
;

// 從標記初始化新的 DOM
phpQuery::newDocument($markup)
->
find('ul > li')
->
addClass('my-new-class')
->
filter(':last')
->
addClass('last-li');

// 查詢最後使用的 DOM 中的所有無序清單
pq('ul')->insertAfter('div');

// 迭代最後使用的 DOM 中的所有 LI
foreach(pq('li') as $li) {
// 迭代傳回純 DOM 節點,而不是 phpQuery 物件
pq($li)->addClass('my-second-new-class');
}

// 與 pq('anything')->htmlOuter() 相同
// 但在文件根目錄上(傳回文件類型等)
print phpQuery::getDocument();
?>

它使用 DOM 擴展和 XPath,因此它僅在 PHP5 中有效。
Yanik <clonyara(at)ahoo(dot)com>
17 年前
我討厭 DOM 模型!
所以我寫了一個 dom2array 簡單函數 (簡單易用)

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;
}
super dot puma at gmail dot com
10 年前
如果您想要列印 DOM XML 檔案內容,您可以使用下一個程式碼

$doc = new DOMDocument();
$doc->load($xmlFileName);
echo "<br>" . $doc->documentURI;
$x = $doc->documentElement;
getNodeContent($x->childNodes, 0);

function getNodeContent($nodes, $level){
foreach ($nodes AS $item) {
// print "<br><br>TIPO: " . $item->nodeType ;
printValues($item, $level);
if ($item->nodeType == 1) { //DOMElement
foreach ($item->attributes AS $itemAtt) {
printValues($itemAtt, $level+3);
}
if($item->childNodes || $item->childNodes->lenth > 0) {
getNodeContent($item->childNodes, $level+5);
}
}
}
}

function printValues($item, $level){
if ($item->nodeType == 1) { //DOMElement
printLevel($level);
print $item->nodeName . " = " . $item->nodeValue;
}
if ($item->nodeType == 2) { //DOMAttr
printLevel($level);
print $item->name . " = " . $item->value ;
}
if ($item->nodeType == 3) { //DOMText
if ($item->isWhitespaceInElementContent() == false){
printLevel($level);
print $item->wholeText ;
}
}
}

function printLevel($level)
{
print "<br>";
if ($level == 0) {
print "<br>";
}
for($i=0; $i < $level; $i++) {
print "-";
}
}
pes_cz
19 年前
當我嘗試使用 DOM 擴展解析我的 XHTML Strict 檔案時,它無法理解 xhtml 實體 (例如 &copy;)。我在這裡找到一篇關於它的文章 (14-Jul-2005 09:05),其中建議新增 resolveExternals = true,但它非常慢。有一些關於 xml 目錄的簡短註解,但沒有任何關聯。這就是了

XML 目錄有點像快取。將所有需要的 dtd 下載到 /etc/xml,編輯檔案 /etc/xml/catalog 並新增這一行:<public publicId="-//W3C//DTD XHTML 1.0 Strict//EN" uri="file:///etc/xml/xhtml1-strict.dtd" />

就這樣了。感謝 http://www.whump.com/moreLikeThis/link/03815
simlee at indiana dot edu
18 年前
我目前正在進行的專案使用 XPath 來動態瀏覽 XML 檔案的區塊。我在網路上找不到任何會為我建立節點 XPath 的 PHP 程式碼,因此我編寫了自己的函數。結果發現它不像我想像的那麼難 (萬歲遞迴),儘管它確實需要使用一些 PHP 詭計...

希望它可以為其他人省去重新發明這個輪子的麻煩。

<?php
function getNodeXPath( $node ) {
// 請注意,XPATH 使用的是基於 1 而不是基於 0 的索引!!!

// 透過迴圈遍歷同層節點來取得當前節點的索引。
$parentNode = $node->parentNode;
if(
$parentNode != null ) {
$nodeIndex = 0;
do {
$testNode = $parentNode->childNodes->item( $nodeIndex );
$nodeName = $testNode->nodeName;
$nodeIndex++;

// PHP 的小技巧!這裡我們根據測試節點的節點名稱建立一個計數器,以便在 XPath 中使用。
if( !isset( $$nodeName ) ) $$nodeName = 1;
else $
$nodeName++;

// 保險起見的回傳值。
if( $nodeIndex > $parentNode->childNodes->length ) return( "/" );
} while( !
$node->isSameNode( $testNode ) );

// 遞迴取得父節點的 XPath。
return( getNodeXPath( $parentNode ) . "/{$node->nodeName}[{$$nodeName}]" );
} else {
// 遇到根節點!請注意,斜線是在建構 XPath 時加入的,所以我們只回傳空字串。
return( "" );
}
}
?>
johanwthijs-at-hotmail-dot-com
18 年前
身為一位經驗豐富的 ASP 開發人員,我一直在想如何取代節點的文字內容 (在 msxml 中,只需設定節點的 'text' 屬性即可輕鬆完成)。在受挫之下,我開始嘗試使用 SimpleXml,但我無法讓它與 xPath 結合使用。

我花了很多時間才找到方法,希望這對其他人有幫助

function replaceNodeText($objXml, $objNode, $strNewContent){
/*
此函式會以 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']";
$objNodeList = $objXpath ->query($strXpath);
foreach ($objNodeList as $objNode){
// 以參考傳遞節點
replaceNodeText($objXml, &$objNode, $strImportedValue);
}
cooper at asu dot ntu-kpi dot kiev dot ua
18 年前
如果您使用的是非物件導向的函式,且要更改它們需要花費太多時間 (或者您稍後會取代它們),那麼可以暫時使用這些模組

適用於 DOM XML
http://alexandre.alapetite.net/doc-alex/domxml-php4-php5/

適用於 XSLT
http://alexandre.alapetite.net/doc-alex/xslt-php4-php5/
aidan at php dot net
19 年前
在處理驗證或載入時,輸出的錯誤可能會很煩人。

PHP 5.1 引入了 libxml_get_errors()。

https://php.dev.org.tw/libxml_get_errors
aidan at php dot net
19 年前
從 PHP 5.1 開始,可以使用常數而不是使用專有的 DomDocument 屬性來設定 libxml 選項。

DomDocument->resolveExternals 等同於設定
LIBXML_DTDLOAD
LIBXML_DTDATTR

DomDocument->validateOnParse 等同於設定
LIBXML_DTDLOAD
LIBXML_DTDVALID

建議 PHP 5.1 使用者使用新的常數。

範例

DomDocument->load($file, LIBXML_DTDLOAD|LIBXML_DTDATTR);

DomDocument->load($file, LIBXML_DTDLOAD|LIBXML_DTDVALID);
toby at tobiasly dot com
19 年前
此模組在 CentOS 4 "centosplus" 儲存庫中也預設未包含。對於在 CentOS 4 上使用 PHP5 的使用者,只需簡單地執行 "yum --enablerepo=centosplus install php-xml" 即可 (這會安裝 XML 和 DOM 模組)。
sweisman at pobox dot com
15 年前
我在使用 "nospam at ya dot ru" 的 dom2array_full 函式時遇到問題。以下是我的函式,它在我的專案中運作正常,可能也適用於您的專案

<?php
function dom_to_array($root)
{
$result = array();

if (
$root->hasAttributes())
{
$attrs = $root->attributes;

foreach (
$attrs as $i => $attr)
$result[$attr->name] = $attr->value;
}

$children = $root->childNodes;

if (
$children->length == 1)
{
$child = $children->item(0);

if (
$child->nodeType == XML_TEXT_NODE)
{
$result['_value'] = $child->nodeValue;

if (
count($result) == 1)
return
$result['_value'];
else
return
$result;
}
}

$group = array();

for(
$i = 0; $i < $children->length; $i++)
{
$child = $children->item($i);

if (!isset(
$result[$child->nodeName]))
$result[$child->nodeName] = dom_to_array($child);
else
{
if (!isset(
$group[$child->nodeName]))
{
$tmp = $result[$child->nodeName];
$result[$child->nodeName] = array($tmp);
$group[$child->nodeName] = 1;
}

$result[$child->nodeName][] = dom_to_array($child);
}
}

return
$result;
}
?>
PHPdeveloper
17 年前
Yanik 的 dom2array() 函式 (於 2007 年 3 月 14 日 08:40 新增) 無法處理具有相同名稱的多個節點,例如:

<foo>
<name>aa</name>
<name>bb</name>
</foo>

它會覆寫先前的節點,而您的陣列只會包含最後一個節點 ("bb")
amir.laherATcomplinet.com
19 年前
這個特定的 W3C 頁面提供了 php5 中實作的 DOM 類別 (透過 libxml2) 的寶貴文件。它填補了 php.net 的許多空白

http://www.w3.org/TR/DOM-Level-2-Core/core.html

一些重要範例
* 類別階層的簡潔摘要 (1.1.1)
* 說明 DOM Level 2 不允許填入內部 DTD
* 解釋 DOMNode->normalize()
* 解釋 DOMImplementation 類別

介面是在 OMG 的介面定義語言中描述的
ohcc at 163 dot com
9 年前
<?php
// 這個筆記是關於如何取得 DOMNode 的 outerHTML 和 innerHTML。
$dom = new DOMDocument('1.0','UTF-8');
$dom->loadHTML('<html><body><div><p>p1</p><p>p2</p></div></body></html>');
$node = $dom->getElementsByTagName('div')->item(0);
$outerHTML = $node->ownerDocument->saveHTML($node);
$innerHTML = '';
foreach (
$node->childNodes as $childNode){
$innerHTML .= $childNode->ownerDocument->saveHTML($childNode);
}
echo
'<h2>outerHTML: </h2>';
echo
htmlspecialchars($outerHTML);
echo
'<h2>innerHTML: </h2>';
echo
htmlspecialchars($innerHTML);
?>
miguelangelhdz at NOSPAM dot com
16 年前
在搜尋如何擴充 DOMDocument 和 DOMElement 後,我在這個錯誤報告中找到了一個方法:http://bugs.php.net/bug.php?id=35104. 以下程式碼展示如何操作

<?php
class extDOMDocument extends DOMDocument {
public function
createElement($name, $value=null) {
$orphan = new extDOMElement($name, $value); // 新的子類別物件
$docFragment = $this->createDocumentFragment(); // 輕量級容器維護 "ownerDocument"
$docFragment->appendChild($orphan); // 附加
$ret = $docFragment->removeChild($orphan); // 移除
return $ret; // ownerDocument 已設定;不會在方法結束時被銷毀
}
// .. 更多類別定義
}

class
extDOMElement extends DOMElement {
function
__construct($name, $value='', $namespaceURI=null) {
parent::__construct($name, $value, $namespaceURI);
}
// ... 這裡有更多類別定義
}

$doc = new extDOMDocument('test');
$el = $doc->createElement('tagname');
$el->setAttribute("attr", "val");
$doc->appendChild($el);

// append 會捨棄 DOMDocumentFragment,只加入其子節點,但會保留 ownerDocument。
echo get_class($el)."<br/>";
echo
get_class($doc->documentElement)."<br/>";
echo
"<xmp>".$doc->saveXML()."</xmp>";
?>
emmanuellutula at hotmail dot com
6 年前
如果您想在 Symfony 控制器的 PHPUnit 測試中使用 DOMDocument(測試表單)!請像這樣使用

namespace Tests\YourBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use YourBundle\Controller\TextController;

class DefaultControllerTest extends WebTestCase
{
public function testIndex()
{
$client = static::createClient(array(), array());

$crawler = $client->request('GET', '/text/add');
$this->assertTrue($crawler->filter("form")->count() > 0, "文字表單存在!");

$form = $crawler->filter("form")->form();

$domDocument = new \DOMDocument;

$domInput = $domDocument->createElement('input');
$dom = $domDocument->appendChild($domInput);
$dom->setAttribute('slug', 'bloc');


$formInput = new \Symfony\Component\DomCrawler\Field\InputFormField($domInput);
$form->set($formInput);

$crawler = $client->submit($form);

if ($client->getResponse()->isRedirect())
{
$crawler = $client->followRedirect(false);
}

// $this->assertTrue($client->getResponse()->isSuccessful());
//$this->assertEquals(200, $client->getResponse()->getStatusCode(),
// "GET /backoffice/login 的 HTTP 狀態碼不符");

}
}
Drupella
13 年前
這裡有一個快速的 innerHTML 函數,它會回傳結果而無需迭代子節點。

<?php
function innerHTML($el) {
$doc = new DOMDocument();
$doc->appendChild($doc->importNode($el, TRUE));
$html = trim($doc->saveHTML());
$tag = $el->nodeName;
return
preg_replace('@^<' . $tag . '[^>]*>|</' . $tag . '>$@', '', $html);
}
?>

範例
<?php
$doc
= new DOMDocument();
// 一個損壞的 HTML 字串
$doc->loadHTML('<HTML><A HREF="ss">asd</A>');
$body = $doc->getElementsByTagName('body')->item(0);
print
htmlspecialchars(innerHTML($body));
// 印出 <a href="ss">asd</a>
?>
odessa131 at aol dot nospam dot com
15 年前
我花了很多時間更新一個複雜的 XML 文件。這是一個關於如何快速完成的範例。

<?php

// 從檔案載入 XML。
$xml = "a2062.xml"; // 這是一個先前未編碼且未解壓縮的 XFDL 表單。
$dom = new DOMDocument();
$dom->preserveWhiteSpace = false;
$dom->Load($xml);

// 建立 XPath 查詢。
// 注意:如果 XML 文件定義了命名空間,則必須定義命名空間。
$xpath = new DOMXPath($dom);
$xpath->registerNamespace('xfdl', "http://www.PureEdge.com/XFDL/6.5");

// 找出第一個項目描述欄位的值。
$query = "//xfdl:page/xfdl:field[@sid='ITEMDESA']/xfdl:value";
$nodeList = $xpath->query($query);

$nodeList->item(0)->nodeValue = "這是 DA 2062 PureEdge 表單中第一個項目描述欄位的值節點中的文字。";

$dom->save($xml);

?>

我希望這對某人有幫助。
Junior
15 年前
PHP DOM 中的 innerHTML

<?php
function DOMinnerHTML($element)
{
$innerHTML = "";
$children = $element->childNodes;
foreach (
$children as $child)
{
$tmp_dom = new DOMDocument();
$tmp_dom->appendChild($tmp_dom->importNode($child, true));
$innerHTML.=trim($tmp_dom->saveHTML());
}
return
$innerHTML;
}
?>

範例

<?php
$dom
= new DOMDocument();
$dom->load($html_string);
$dom->preserveWhiteSpace = false;

$domTable = $dom->getElementsByTagName("table");

foreach (
$domTable as $tables)
{
echo
DOMinnerHTML($tables);
}
?>
fantasyman3000 at gmail dot com
15 年前
回應 "simlee at indiana dot edu" 的提問,
- 首先,感謝您分享您的函式。
- 它對我不起作用,所以我用不同的方法從頭重寫了它。

這是新版本,希望對其他人有幫助

<?php
/**
* 結果範例:/html[1]/body[1]/span[1]/fieldset[1]/div[1]
* @return string
*/
function getNodeXPath( $node ) {
$result='';
while (
$parentNode = $node->parentNode) {
$nodeIndex=-1;
$nodeTagIndex=0;
do {
$nodeIndex++;
$testNode = $parentNode->childNodes->item( $nodeIndex );

if (
$testNode->nodeName==$node->nodeName and $testNode->parentNode->isSameNode($node->parentNode) and $testNode->childNodes->length>0) {
//echo "{$testNode->parentNode->nodeName}-{$testNode->nodeName}-{}<br/>";
$nodeTagIndex++;
}

} while (!
$node->isSameNode($testNode));

$result="/{$node->nodeName}[{$nodeTagIndex}]".$result;
$node=$parentNode;
};
return
$result;
}
?>

By Sina.Salek.ws
ben_demott at hotmail dot com
14 年前
這是用來解析 Google 搜尋結果頁面的其中一個函式,我之前寫的 - 自那時起 Google 可能已經更改了他們的網站,但我認為這對某些人可能會有幫助。

我正在移動伺服器,但我恢復後可能會將此發佈在我的部落格上。

<?php

function googleResult($listItem) {
// 給定一個 LIST ITEM 元素,這將驗證並返回該 LI 條目作為 Google 的內聯結果的陣列。
/*
* <li class='g w0'>
* <h3 class='r'>
* <a href='the URL' class='l'>
* Description <em>description</em>
* </a>
* </h3>
* </li>
*
UPDATE:
這個函式現在會尋找任何具有 href 的子容器,它不一定要是 H3
這將使它適用於更多格式化的搜尋結果。
*/

$listItem = $listItem->childNodes;
// 是的,我沒有使用 instanceof - 我想你必須接受。
foreach($listItem as $element) {
if(
is_object($element) && get_class($element) == 'DOMElement' && $element->hasChildNodes()) {
$hrefContainer = $element->childNodes;
foreach(
$hrefContainer as $element2) {
if(
is_object($element2) && get_class($element2) == 'DOMElement' && $element2->nodeName == 'a' && $element2->hasAttribute('href')) {
$anchor = $element2;
unset(
$h3);
unset(
$element2);
break;
} else {
//print __LINE__ ." :: Breaking out of loop (normal result) element is not an annchor Element='".$element2->nodeName."'\n";
}
}
unset(
$element);
unset(
$listItem);
break;
}
}
if(empty(
$anchor) || !is_object($anchor) || get_class($anchor) != 'DOMElement') {
//print __LINE__ ." :: Returning false, did not locate anchor through iteration...";
return false;
}
$href = $anchor->getAttribute('href');
if(empty(
$href)) {
//print __LINE__ ." :: Found anchor object, could not read href attribute / href is empty? href='$href'\n";
return false;
}
$description = $anchor->childNodes;
$urlDescription = '';
foreach(
$description as $words) {
$name = trim($words->nodeName);
if(
$name == 'em' || $name == '#text' || $name == 'b') {
if(!empty(
$words->nodeValue)) {
$text = trim($words->nodeValue);
$urlDescription = $urlDescription . $text . ' ';
}
}
}
$urlDescription = htmlspecialchars_decode($urlDescription, ENT_QUOTES);
$urlDescription = trim($urlDescription);
return array(
'description' => $urlDescription, 'href' => $href);
}
philipwaynerollins at gmail dot com
15 年前
您可以使用 nodeValue 取得 "innerHTML",因此

<?php
$doc
= new DOMDocument( );
$ele = $doc->createElement( 'p', 'Sensei Ninja' );
print
$ele->nodeValue;
?>

您甚至可以根據需要進行設定

<?php
$doc
= new DOMDocument( );
$ele = $doc->createElement( 'p' );
$ele->nodeValue = 'Sensei Ninja';
$doc->appendChild( $ele );
print
$doc->saveHTML( );
?>
sean at lookin3d dot com
18 年前
$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 的第一個子節點的節點名稱與最後一個子節點的節點名稱相同,並且 $n 的子節點數大於 1)
?$return[$nc 的節點名稱][] = xml2array($item)
:$return[$nc 的節點名稱] = xml2array($nc)
:$return = $nc 的節點值;
return $return;
}
nospam at ya dot ru
15 年前
<?PHP
function dom2array_full($node){
$result = array();
if(
$node->nodeType == XML_TEXT_NODE) {
$result = $node->nodeValue;
}
else {
if(
$node->hasAttributes()) {
$attributes = $node->attributes;
if(!
is_null($attributes))
foreach (
$attributes as $index=>$attr)
$result[$attr->name] = $attr->value;
}
if(
$node->hasChildNodes()){
$children = $node->childNodes;
for(
$i=0;$i<$children->length;$i++) {
$child = $children->item($i);
if(
$child->nodeName != '#text')
if(!isset(
$result[$child->nodeName]))
$result[$child->nodeName] = dom2array($child);
else {
$aux = $result[$child->nodeName];
$result[$child->nodeName] = array( $aux );
$result[$child->nodeName][] = dom2array($child);
}
}
}
}
return
$result;
}
?>
danf dot 1979 at []gmail[] dot com
16 年前
這是一對用於處理 yahoo yui 選單的類別。

/*
$menubar = new MenuBar();

$file = new Menu("檔案");
$file->setAttribute("href", "http://file.com");

$quit = new Menu("退出");
$quit->setAttribute("href", "http://quit.com");

$file->appendChild($quit);
$menubar->appendChild($file);

echo $menubar->grab();
*/

//
// 作者:Daniel Queirolo。
// LGPL
//

/** ---------------------------------
/** Class MenuBar()
/** 建立選單列並附加
/** yuimenubaritems 到其中。
/** ---------------------------------*/

class MenuBar extends DOMDocument
{

public $menuID = "nav_menu"; // 保留 javascript yui 選單程式碼應具有的 css id,以進行識別
private $UL; // 此節點保留每個選單,這是「主要」節點。

/** ---------------------------------
/** 建構子
/** 產生選單列骨架和 UL 節點
/** ---------------------------------*/

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");

// 所有 Menu() 實例都會在 <ul> 標籤內發生。

$this->UL = $bd->appendChild($ul);

}

/** ---------------------------------
/** appendChild()
/** 將新的 yuimenubaritem 附加到選單列 UL 節點。
/** 此函式會將 <li> 和 <a> 類別變更為 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()
/** 建立 yuimenuitem li 節點
/** ---------------------------------*/

class Menu extends DOMDocument {

public $LI; // 儲存將匯出到 MenuBar() 或用於 appendChild() 的 <li> 節點 (連結)

/** ---------------------------------
/** 建構子
/** 產生 yuimenuitem li 節點
/** 此處不會建立 yuimenubar 項目。MenuBar 會處理該項目。
/** ---------------------------------*/

public function __construct($link_name) {

parent::__construct();

$li = parent::createElement("li");
$li->setAttribute("class", "yuimenuitem");

// LI 節點會儲存「連結」。
// 如果使用 appendChild,則新的 (子) Menu() 會是 LI 節點子項。

$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
/** 將 (子) Menu() 附加至目前 Menu() 的 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 保留新子項的「連結」(來自子項的 __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);
}
}

}
Sanados at failure dot at
17 年前
附加到
brian dot reynolds at risaris dot com
2007 年 2 月 20 日 10:09

當您在開頭有可變節點時,您的陣列會失敗並遺失下方的節點。
計算發生次數的解決方案,但會耗用效能

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;
}
spammable69 at hotmail dot com
19 年前
我撰寫了一個框架,以實作 W3C 網站上指定的 StyleSheet 介面。程式碼是以 PHP 撰寫,而且不是完整的實作。請隨您喜歡的方式使用。我打算加入 CSSStyleSheet 介面。歡迎隨時提問。

<?
class StyleSheetList {
public length;
private self;

function __construct ( ) {
$this->self = array();
}

function __get($property, $&ret) {
if($property == 'length')
$ret = count($this->self);
return true;
}

function __set($property, $val) {
if($property == 'length')
return true;
}

function item( $index ) {
return $this->self[$index];
}
}

class MediaList extends StyleSheetList {

function appendMedium ( $newMedium ) {
array_push($this->self, $newMedium);
}

function deleteMedium ( $oldMedium ) {
foreach($this->self as $item) {
if( $item == $oldMedium ) {
$item = $this->self[ $this->length-1 ];
array_pop($this->self);
break;
}
}
}
}

class DocumentStyle {
public styleSheets;

function __construct ( ) {
$this->styleSheets = new StyleSheetList();
}

function __set($property, $val) {
if($property == 'styleSheets')
return true;
}
}

class LinkStyle {
public sheet;

function __construct () {
$this->sheet = new StyleSheet();
}

function __set($property, $val) {
if($property == 'sheet')
return true;
}
}

class StyleSheet {
public type;
public disabled;
public ownerNode;
public parentStyleSheet;
public href;
public title;
public media;

function __construct( $type, $disabled, $ownerNode, $parentStyleSheet, $href, $title){
$this->type = $type;
$this->disabled = $disabled;
$this->media = new MediaList();
$this->ownerNode = $ownerNode;
$this->parentStyleSheet = $parentStyleSheet;
$this->href = $href;
$this->title = $title;
}
}
?>

僅可透過 http://murpsoft.com/contact.html 聯絡
Nevyn at N dot O dot S dot P dot A dot M dot emai dot it
17 年前
我撰寫了一對函式來
- 從檔案建立 DOMDocument
- 剖析其中的命名空間
- 建立已註冊所有命名空間的 XPath 物件
- 載入綱要位置
- 驗證主要綱要 (不含前置詞的綱要) 上的檔案

這對我很有用,看看它是否對其他人也有用!

Giulio

function decodeNode($node)
{
$out = $node->ownerDocument->saveXML($node);
$re = "{^<((?:\\w*:)?\\w*)". //標籤名稱
"[\\s\n\r]*((?:[\\s\n\r]*".
"(?:\\w*:)?\\w+[\\s\n\r]*=[\\s\n\r]*". //可能的屬性名稱
"(?:\"[^\"]*\"|\'[^\']*\'))*)". //屬性值
"[\\s\n\r]*>[\r\n]*".
"((?:.*[\r\n]*)*)". //內容
"[\r\n]*</\\1>$}"; //關閉標籤
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;
}
mark at vectrex dot org dot uk
19 年前
請注意,這些 DOM 函式會預期(且推測會回傳)所有資料皆為 UTF-8 字元編碼,無論 PHP 目前的編碼為何。這表示文字節點、屬性值等應該為 utf8。

即使您產生最終不是 utf8 的 XML 文件,此規則也適用。

Mark
php at webdevelopers dot cz
19 年前
[編輯註記:如果您使用實體,別無選擇。XML 目錄可以加速 DTD 的解析。]

永遠不要使用

$dom->resolveExternals=true;

當解析具有 DOCTYPE 宣告且其中指定 DTD URL 的 XHTML 文件時。

否則,使用像這樣 DOCTYPE 的 XHTML 將會

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

會導致 PHP/DOM 在解析您的文件時,從 W3C 網站下載 DTD 檔案。這會為您的腳本增加額外的延遲 – 我曾經驗 $dom->load() 的總時間為 1 到 16 秒。

elixon
naudyj at aus3d.com
16 年前
以下程式碼可以取得 XML_TEXT_NODE 節點並以陣列形式回傳其內容。Yanick 的貢獻很棒 – 但
它會覆寫重複項目,僅保留最後一行
在回傳的陣列中。我從各種來源測試的所有其他函式都無法正確處理文字節點。希望這對某些人有所幫助。它是從本網站的程式碼改編而來。

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;
}
}

所以您可以使用

$nodes = $dom->getElementsByTagName("td");
if($nodes){
foreach ($nodes as $node){
$a = Array();
myTextNode($node, $a);
}
}
Francois Hill
17 年前
回應 lutfi at smartconsultant dot us 的問題

(請參閱我在
http://fr2.php.net/manual/en/
function.dom-domdocument-getelementsbytagname.php 的文章
)

使用我寫的這個類別

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);
}
}

(抱歉顯示成這樣... php.net 真的是一大阻礙!)
part of php.net !)

然後
從您的父節點建立一個新的 XPathableNode。
然後,您可以透過套用 xpath 從中擷取 DOMNodeList(因此能夠指定您想要的元素的深度和名稱)。
xpath (thus being able to specify the depth and name of
elements you want).

它幾次讓我擺脫了一些 (許多) DOM 的麻煩。

;o)
chealer at gmail dot com
6 年前
This extension allows editing an HTML snippet, for example removing an element
‌‌$dom = new DOMDocument();
‌‌$dom->loadHTML('keep<b id="simple">REMOVE</b>');
$‌‌remove = $dom->getElementById('simple');
‌‌$dom->removeChild($‌‌remove);
echo ‌‌$dom->saveHTML();
...would output "keep".

But it does not preserve formatting. If your code defines attributes using double quotes, saveHTML() may for instance return single quotes instead.
Anonymous
16 年前
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
16 年前
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.
Sven Arduwie
16 年前
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;
}
?>
jim.filter (at gmail.com)
17 年前
陣列轉 DOM

這是一個遞迴函式,可將多維陣列轉換為 XML 文件。它會處理相同標籤的多個元素,但每個父元素僅處理一個。例如

無法產生:可以產生
<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>

此外,此函式不會對您的陣列執行任何類型的錯誤檢查,如果您的陣列中使用的索引鍵值包含對適當的 DOM 標籤而言無效的字元,則會拋出 DOMException。此函式未針對「深層」多維陣列進行測試。

完整的程式碼,可直接執行,並附帶範例

<?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');
?>
To Top