5

私は、PHP、DOM、および PHP DOM の実装にかなり慣れていません。私がやろうとしているのは、のルート要素を変数に保存してDOMDocument$_SESSION後続のページの読み込み時にアクセスして変更できるようにすることです。

しかし$_SESSION、DOMElement の状態を保存するために使用すると、PHP でエラーが発生します。

警告: DOMNode::appendChild() [domnode.appendchild]: DOMElement を取得できませんでした

$_SESSIONPHP DOMDocument オブジェクトをネイティブに保存できないことを読みました。ただし、DOMDocument のシリアル化を保存することで保存できます (例: $_SESSION['dom'] = $dom->saveXML())。

DOMElementaを aに保存する場合も同じことが当てはまるかどうかはわかりません$_SESSION変数もありますが、それが私が試みていたことです。これを行う理由は、DOMElement の拡張クラスに 1 つの追加プロパティを使用するためです。ルート DOMElement を $_SESSION に保存することで、後で要素を取得してこの追加のプロパティを変更し、次のようなテストを実行できることを期待していました。}。また、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

いくつか質問があります。まず、このエラーの原因と修正方法を教えてください。また、私がやろうとしていることが不可能な場合、各要素のカスタム プロパティを使用しながら、DOM ツリーの「状態」を保存するという一般的な目的をどのように達成できますか? 追加のプロパティはプログラムでのみ使用され、XML ファイルに保存される属性ではないことに注意してください。また、DOMDocument は、変更後、追加の変更/追加が DOMDocument に対して実行されるまで、使用しているスキーマに従って有効ではない可能性があるため、毎回 DOM をファイルに保存することはできません。そのため、一時的に無効な DOMDocument を保存する必要があります。アドバイスをありがとう!

EDITED: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プロパティが認識されません。これを回避する方法はありますか?

4

1 に答える 1

4

DOMElement内にオブジェクトを格納することはできません$_SESSION。最初は動作しますが、次のリクエストではシリアル化できないため設定が解除されます。

DOMDocumentそれはあなたが質問で書いているのと同じです。

代わりに XML として保存するか、シリアライゼーション メカニズムをカプセル化します。

基本的に、ここで 3 つの問題に直面しています。

  • シリアル化し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): %d\n", $doc->documentElement->getFrozen()); # it is not (0)

PHP はこれまで (DOM レベル 3) をサポートしていないためsetUserData、1 つの方法として、要素を使用して名前空間属性内に追加情報を格納することができます。これは、オブジェクトをシリアライズするときに XML 文字列を作成し、アンシリアライズするときにそれをロードすることによってシリアライズすることもできます (「 」を参照Serializable)。これにより、3 つの問題がすべて解決されます ( 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 を取得することは可能です。

于 2012-05-01T13:20:35.497 に答える