我对php、dom和phpdom实现还很陌生。我要做的是将DOMDocument
的根元素保存在一个$_SESSION
变量中,这样我就可以访问它并在随后的页面加载中修改它。
但是当使用$_SESSION
保存domelement的状态时,php中出现了一个错误:
警告:domnode::appendchild()[domnode.appendchild]:无法获取domelement
我已经读到一个php domdocument对象不能以本机方式保存到$_SESSION
。但是,可以通过保存domdocument的序列化来保存它(例如$_SESSION['dom'] = $dom->saveXML()
)。
我不知道同样的方法是否也适用于将DOMElement
保存到$_SESSION
变量,但这正是我所尝试的。我想这样做的原因是使用一个带有一个额外属性的domeElement扩展类。我希望通过在$u会话中保存根domelement,以后可以检索该元素并修改此附加属性,然后执行类似if(additional property===false){do something;}的测试。我还通过保存一个domdocument并在稍后检索它,将所有元素作为对象从本地dom类返回。也就是说,即使我使用扩展类来创建元素,我随后需要的属性也将无法访问,因为保存对扩展类对象的引用的变量已经超出了范围——这就是我尝试其他方法的原因。我先尝试使用扩展类(不包括在下面),但得到了错误…所以我返回到使用domelement对象来查看这是否是问题所在,但仍然得到了相同的错误。代码如下:
<?php
session_start();
$rootTag = 'root';
$doc = new DOMDocument;
if (!isset($_SESSION[$rootTag])) {
$_SESSION[$rootTag] = new DOMElement($rootTag);
}
$root = $doc->appendChild($_SESSION[$rootTag]);
//$root = $doc->appendChild($doc->importNode($_SESSION[$rootTag], true));
$child = new DOMElement('child_element');
$n = $root->appendChild($child);
$ct = 0;
foreach ($root->childNodes as $ch) echo '<br/>'.$ch->tagName.' '.++$ct;
$_SESSION[$rootTag] = $doc->documentElement;
?>
此代码提供以下错误(取决于我是直接使用appendchild还是使用importnode注释的代码行):
Warning: DOMNode::appendChild() [domnode.appendchild]: Couldn't fetch DOMElement in C:\Program Files\wamp_server_2.2\www\test2.php on line 11
Warning: DOMDocument::importNode() [domdocument.importnode]: Couldn't fetch DOMElement in C:\Program Files\wamp_server_2.2\www\test2.php on line 12
我有几个问题。首先,是什么导致了这个错误,我该如何修复它?另外,如果我试图做的事情是不可能的,那么如何在为每个元素使用自定义属性的同时实现保存do m树的“state”的一般目标呢?注意,附加属性只在程序中使用,而不是要保存在xml文件中的属性。另外,我不能每次都将dom保存回文件,因为domdocument在修改之后,根据我正在使用的模式,可能是无效的,直到后来对domdocument执行了其他修改/添加操作。这就是为什么我需要保存一个临时无效的domdocument。谢谢你的建议!
编辑:
在尝试了Hakre的解决方案之后,代码工作了。然后我继续尝试使用一个扩展的domelement类,正如我所怀疑的那样,它不起作用。新代码如下:
<?php
session_start();
//$_SESSION = array();
$rootTag = 'root';
$doc = new DOMDocument;
if (!isset($_SESSION[$rootTag])) {
$root = new FreezableDOMElement($rootTag);
$doc->appendChild($root);
} else {
$doc->loadXML($_SESSION[$rootTag]);
$root = $doc->documentElement;
}
$child = new FreezableDOMElement('child_element');
$n = $root->appendChild($child);
$ct = 0;
foreach ($root->childNodes as $ch) {
$frozen = $ch->frozen ? 'is frozen' : 'is not frozen';
echo '<br/>'.$ch->tagName.' '.++$ct.': '.$frozen;
//echo '<br/>'.$ch->tagName.' '.++$ct;
}
$_SESSION[$rootTag] = $doc->saveXML();
/**********************************************************************************
* FreezableDOMElement class
*********************************************************************************/
class FreezableDOMElement extends DOMElement {
public $frozen; // boolean value
public function __construct($name) {
parent::__construct($name);
$this->frozen = false;
}
}
?>
它给了我一个错误
Undefined property: DOMElement::$frozen
。正如我在最初的文章中提到的,在saveXML
和loadXML
之后,最初用FreezableDOMElement
实例化的元素返回类型DOMElement
,这就是为什么不能识别frozen
属性的原因。有办法解决这个问题吗? 最佳答案
不能将DOMElement
对象存储在$_SESSION
中。它将首先工作,但在下一个请求中,它将被取消设置,因为它不能序列化。
就像你在问题中写的那样。
将其存储为XML,或者封装序列化机制。
你基本上面临三个问题:
序列化DOMDocument
(您这样做是为了)
序列化DOMDocument
(您这样做是为了)
在文档中保留私有成员FreezableDOMElement
。
如前所述,序列化不是现成的。此外,FreezableDOMElement::$frozen
甚至不支持序列化也不会持久化。下面的示例演示实例不是自动保留的,而是返回默认值DOMDocument
(Demo):
class FreezableDOMElement extends DOMElement
{
private $frozen = FALSE;
public function getFrozen()
{
return $this->frozen;
}
public function setFrozen($frozen)
{
$this->frozen = (bool)$frozen;
}
}
class FreezableDOMDocument extends DOMDocument
{
public function __construct()
{
parent::__construct();
$this->registerNodeClass('DOMElement', 'FreezableDOMElement');
}
}
$doc = new FreezableDOMDocument();
$doc->loadXML('<root><child></child></root>');
# own objects do not persist
$doc->documentElement->setFrozen(TRUE);
printf("Element is frozen (should): %d\n", $doc->documentElement->getFrozen()); # it is not (0)
由于php目前还不支持
FreezableDOMElement
(dom level 3),一种方法是将附加信息存储在带有元素的namespaced属性中。这也可以通过在序列化对象时创建xml字符串并在取消序列化时加载它来序列化(请参见FALSE
)。这就解决了所有三个问题(Demo):class FreezableDOMElement extends DOMElement
{
public function getFrozen()
{
return $this->getFrozenAttribute()->nodeValue === 'YES';
}
public function setFrozen($frozen)
{
$this->getFrozenAttribute()->nodeValue = $frozen ? 'YES' : 'NO';
}
private function getFrozenAttribute()
{
return $this->getSerializedAttribute('frozen');
}
protected function getSerializedAttribute($localName)
{
$namespaceURI = FreezableDOMDocument::NS_URI;
$prefix = FreezableDOMDocument::NS_PREFIX;
if ($this->hasAttributeNS($namespaceURI, $localName)) {
$attrib = $this->getAttributeNodeNS($namespaceURI, $localName);
} else {
$this->ownerDocument->documentElement->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:' . $prefix, $namespaceURI);
$attrib = $this->ownerDocument->createAttributeNS($namespaceURI, $prefix . ':' . $localName);
$attrib = $this->appendChild($attrib);
}
return $attrib;
}
}
class FreezableDOMDocument extends DOMDocument implements Serializable
{
const NS_URI = '/frozen.org/freeze/2';
const NS_PREFIX = 'freeze';
public function __construct()
{
parent::__construct();
$this->registerNodeClasses();
}
private function registerNodeClasses()
{
$this->registerNodeClass('DOMElement', 'FreezableDOMElement');
}
/**
* @return DOMNodeList
*/
private function getNodes()
{
$xp = new DOMXPath($this);
return $xp->query('//*');
}
public function serialize()
{
return parent::saveXML();
}
public function unserialize($serialized)
{
parent::__construct();
$this->registerNodeClasses();
$this->loadXML($serialized);
}
public function saveBareXML()
{
$doc = new DOMDocument();
$doc->loadXML(parent::saveXML());
$xp = new DOMXPath($doc);
foreach ($xp->query('//@*[namespace-uri()=\'' . self::NS_URI . '\']') as $attr) {
/* @var $attr DOMAttr */
$attr->parentNode->removeAttributeNode($attr);
}
$doc->documentElement->removeAttributeNS(self::NS_URI, self::NS_PREFIX);
return $doc->saveXML();
}
public function saveXMLDirect()
{
return parent::saveXML();
}
}
$doc = new FreezableDOMDocument();
$doc->loadXML('<root><child></child></root>');
$doc->documentElement->setFrozen(TRUE);
$child = $doc->getElementsByTagName('child')->item(0);
$child->setFrozen(TRUE);
echo "Plain XML:\n", $doc->saveXML(), "\n";
echo "Bare XML:\n", $doc->saveBareXML(), "\n";
$serialized = serialize($doc);
echo "Serialized:\n", $serialized, "\n";
$newDoc = unserialize($serialized);
printf("Document Element is frozen (should be): %s\n", $newDoc->documentElement->getFrozen() ? 'YES' : 'NO');
printf("Child Element is frozen (should be): %s\n", $newDoc->getElementsByTagName('child')->item(0)->getFrozen() ? 'YES' : 'NO');
它不是真正的功能完整,但一个工作演示。不需要额外的“冻结”数据就可以获得完整的xml。