如何在$ _SESSION中序列化/保存DOMElement? [英] How to serialize/save a DOMElement in $_SESSION?

查看:144
本文介绍了如何在$ _SESSION中序列化/保存DOMElement?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我非常喜欢PHP,DOM和PHP DOM实现。我想要做的是将 DOMDocument 的根元素保存在 $ _ SESSION 变量中,以便我可以访问但是,当使用 $ _ SESSION 保存时,我收到一个错误,但是在后续的页面加载中修改它。



状态DOMElement:


警告:DOMNode :: appendChild()[domnode.appendchild]:无法获取DOMElement


我已经读到PHP DOMDocument对象无法保存到 $ _ SESSION 。然而,可以通过保存DOMDocument的序列化(例如 $ _ SESSION ['dom'] = $ dom-> saveXML())来保存它。



我不知道如果将 DOMElement 保存到 $ _SESSION 变量,但这是我正在尝试。我想要这样做的原因是使用一个扩展类别的DOMElement和一个额外的属性。我希望通过保存$ _SESSION中的根DOMElement,我可以稍后检索元素并修改此附加属性并执行一个测试,如果(additionalProperty === false){做某事; }。我还通过保存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):



警告:DOMNode :: appendChild()[domnode.appendchild]:无法在C:\Program文件中获取DOMElement \wamp_server_2.2\www\test2.php第11行



警告:DOMDocument :: importNode ()[domdocument.importnode]:无法在C中获取DOMElement C:\Program Files\wamp_server_2.2\www\test2.php第12行



我有几个问题。首先,是什么导致这个错误,我该如何解决?另外,如果我想要做的是不可能的话,那么如何在为每个元素使用自定义属性时完成保存DOM树状态的一般目标?请注意,附加属性仅在程序中使用,而不是要保存在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? '被冻结':'不冻结';
echo'< br />'$ ch-> tagName。''。++ $ ct。':$。
// echo'< br />'。$ ch-> tagName。''。++ $ ct;
}

$ _SESSION [$ rootTag] = $ doc-> saveXML();

/ ******************************************* *****************************************
* FreezableDOMElement类
************************************************** ******************************* /
class FreezableDOMElement extends DOMElement {
public $ frozen ; // boolean value

public function __construct($ name){
parent :: __ construct($ name);
$ this-> frozen = false;
}
}
?>

它给我错误未定义的属性:DOMElement :: $ frozen 。就像我原来的帖子中提到的,在 saveXML loadXML 之后,一个元素最初用 FreezableDOMElement 正在返回类型 DOMElement ,这就是为什么不能识别冻结属性的原因。有没有办法?

解决方案

您不能存储 DOMElement 对象内部 $ _ SESSION 。它将首先工作,但是随着下一个请求,它将被取消设置,因为它不能被序列化。



DOMDocument一样



将其存储为XML,或封装序列化机制。



您基本上面临三个问题:




  • 序列化 DOMDocument (你这样做)

  • 序列化 FreezableDOMElement (你这样做)

  • 使用文件保留私人会员 FreezableDOMElement :: $ frozen



正如书面所述,序列化不是开箱即用的。另外,即使没有序列化, DOMDocument 也不会保留您的 FreezableDOMElement 。以下示例演示了实例不会自动保留,返回默认值 FALSE 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>');

#自己的对象不坚持
$ doc-> documentElement-> setFrozen(TRUE);
printf(元素被冻结(应该):%d\\\
,$ doc-> documentElement-> getFrozen()); #不是(0)

由于PHP不支持 setUserData (DOM Level 3),一种方法可以将附加信息存储在带有元素的命名空间属性中。这也可以通过在序列化对象并在反序列化时加载它来创建XML字符串来进行序列化(请参阅 可序列化 )。然后解决所有三个问题(演示):

  class FreezableDOMElement extends DOMElement 
{
public function getFrozen()
{
return $ this-> getFrozenAttribute() - > nodeValue = =='YES'
}

public function setFrozen($ frozen)
{
$ this-> getFrozenAttribute() - > nodeValue = $ frozen? '是':'不'
}

私有函数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();
}

私有函数registerNodeClasses()
{
$ this-> registerNodeClass('DOMElement','FreezableDOMElement');
}

/ **
* @return DOMNodeList
* /
私有函数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纯XML:\\\
,$ doc-> saveXML(),\\\
;
echoBare XML:\\\
,$ doc-> saveBareXML(),\\\
;

$ serialized = serialize($ doc);

echoSerialized:\\\
,$ serialized,\\\
;

$ newDoc = unserialize($ serialized);

printf(Document Element is frozen(should be):%s\\\
,$ newDoc-> documentElement-> getFrozen()?'YES':'NO');
printf(Child Element is frozen(should be):%s\\\
,$ newDoc-> getElementsByTagName('child') - > item(0) - > getFrozen()? ':'否');

这不是真正的功能完整,而是一个工作的演示。可以获得完整的XML,而不需要额外的冻结数据。


I'm pretty new to PHP, DOM, and the PHP DOM implementation. What I'm trying to do is save the root element of the DOMDocument in a $_SESSION variable so I can access it and modify it on subsequent page loads.

But I get an error in PHP when using $_SESSION to save state of DOMElement:

Warning: DOMNode::appendChild() [domnode.appendchild]: Couldn't fetch DOMElement

I have read that a PHP DOMDocument object cannot be saved to $_SESSION natively. However it can be saved by saving the serialization of the DOMDocument (e.g. $_SESSION['dom'] = $dom->saveXML()).

I don't know if the same holds true for saving a DOMElement to a $_SESSION variable as well, but that's what I was trying. My reason for wanting to do this is to use an extended class of DOMElement with one additional property. I was hoping that by saving the root DOMElement in $_SESSION that I could later retrieve the element and modify this additional property and perform a test like, if (additionalProperty === false) { do something; }. I've also read that by saving a DOMDocument, and later retrieving it, all elements are returned as objects from native DOM classes. That is to say, even if I used an extended class to create elements, the property that I subsequently need will not be accessible, because the variable holding reference to the extended-class object has gone out of scope--which is why I'm trying this other thing. I tried using the extended class (not included below) first, but got errors...so I reverted to using a DOMElement object to see if that was the problem, but I'm still getting the same errors. Here's the code:

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

This code gives the following errors (depending on whether I use appendChild directly or the commented line of code using 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

I have several questions. First, what is causing this error and how do I fix it? Also, if what I'm trying to do isn't possible, then how can I accomplish my general objective of saving the 'state' of a DOM tree while using a custom property for each element? Note that the additional property is only used in the program and is not an attribute to be saved in the XML file. Also, I can't just save the DOM back to file each time, because the DOMDocument, after a modification, may not be valid according to a schema I'm using until later when additional modificaitons/additions have been performed to the DOMDocument. That's why I need to save a temporarily invalid DOMDocument. Thanks for any advice!

EDITED: After trying hakre's solution, the code worked. Then I moved on to trying to use an extended class of DOMElement, and, as I suspected, it did not work. Here's the new code:

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

It gives me the error Undefined property: DOMElement::$frozen. Like I mentioned in my original post, after saveXML and loadXML, an element originally instantiated with FreezableDOMElement is returning type DOMElement which is why the frozen property is not recognized. Is there any way around this?

解决方案

You can not store a DOMElement object inside $_SESSION. It will work at first, but with the next request, it will be unset because it can not be serialized.

That's the same like for DOMDocument as you write about in your question.

Store it as XML instead or encapsulate the serialization mechanism.

You are basically facing three problems here:

  • Serialize the DOMDocument (you do this to)
  • Serialize the FreezableDOMElement (you do this to)
  • Keep the private member FreezableDOMElement::$frozen with the document.

As written, serialization is not available out of the box. Additionally, DOMDocument does not persist your FreezableDOMElement even w/o serialization. The following example demonstrates that the instance is not automatically kept, the default value FALSE is returned (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)

As PHP does not so far support setUserData (DOM Level 3), one way could be to store the additional information inside a namespaced attribute with the element. This can also be serialized by creating the XML string when serializing the object and loading it when unserializing (see Serializable). This then solves all three problems (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');

It's not really feature complete but a working demo. It's possible to obtain the full XML without the additional "freeze" data.

这篇关于如何在$ _SESSION中序列化/保存DOMElement?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆