如何以 $_SESSION 为单位序列化/保存 DOMElement



我对PHP,DOM和PHP DOM实现很陌生。 我正在尝试做的是将DOMDocument的根元素保存在$_SESSION变量中,以便我可以访问它并在后续页面加载时对其进行修改。

但是当使用$_SESSION保存 DOMElement 的状态时,我在 PHP 中出现错误:

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

我读过PHP DOMDocument对象不能保存到本机$_SESSION。但是,可以通过保存 DOMDocument 的序列化来保存它(例如 $_SESSION['dom'] = $dom->saveXML()(。

我不知道将DOMElement保存到$_SESSION变量是否也是如此,但这就是我正在尝试的。 我想要这样做的原因是使用带有一个附加属性的扩展类 DOMElement。 我希望通过将根 DOMElement 保存在 $_SESSION 中,我以后可以检索元素并修改此附加属性并执行测试,例如 if (extraalProperty === 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 Fileswamp_server_2.2wwwtest2.php on line 11

Warning: DOMDocument::importNode() [domdocument.importnode]: Couldn't fetch DOMElement in C:Program Fileswamp_server_2.2wwwtest2.php on line 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 ? '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. 就像我在原始帖子中提到的,在 saveXMLloadXML 之后,最初用 FreezableDOMElement 实例化的元素返回类型 DOMElement 这就是为什么无法识别 frozen 属性的原因。 有什么办法吗?

不能将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>');
# own objects do not persist
$doc->documentElement->setFrozen(TRUE);
printf("Element is frozen (should): %dn", $doc->documentElement->getFrozen()); # it is not (0)

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

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): %sn", $newDoc->documentElement->getFrozen() ? 'YES' : 'NO');
printf("Child Element is frozen (should be): %sn", $newDoc->getElementsByTagName('child')->item(0)->getFrozen() ? 'YES' : 'NO');

它并不是真正的功能完整,而是一个工作演示。可以在没有额外"冻结"数据的情况下获取完整的 XML。

最新更新