2012-05-01 7 views
5

私はPHP、DOM、およびPHP DOMの実装が新しくなっています。私がしようとしているのはDOMDocumentのルート要素を$_SESSION変数に保存することです。そのため、それにアクセスして以降のページの読み込み時に変更することができます。 DOMElementの

取得できませんでした:

警告:DOMNodeを::のappendChild()[domnode.appendchild] DOMElementの状態を保存するために$_SESSIONを使用した場合

しかし、私はPHPでエラーが出ます

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

DOMElement$_SESSION変数に保存しても同じことが当てはまるかどうかはわかりませんが、それは私が試していたものです。これを行うための私の理由は、1つの追加プロパティでDOMElementの拡張クラスを使用することです。 $ _SESSIONにルートDOMElementを保存することで、後で要素を取得してこの追加のプロパティを変更し、if(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のを使用して、コードのコメント行かどうかに応じて)次のエラーを与える:

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

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

これはDOMDocumentの場合と同じです。

代わりにXMLとして格納するか、シリアル化メカニズムをカプセル化します。

あなたは基本的にここに三つの問題に直面している

  • は、シリアライズをDOMDocument
  • FreezableDOMElement(あなたがこれを行う)シリアライズ
  • が文書でプライベートメンバFreezableDOMElement::$frozenしてください(あなたがこれを行います) 。

書いているとおり、シリアライズは利用できません。さらに、DOMDocumentは、偶数/無しのシリアル化を継続しません。

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はそう遠くないサポートsetUserData(DOMレベル3)の場合と同様に、一つの方法は、格納することができます:次の例では、インスタンスは自動的に保持されていない場合、デフォルト値FALSEが(Demo)が返されることを示しています名前空間の属性内の追加情報これは、オブジェクトをシリアライズするときにXML文字列を作成し、シリアル化を解除するときに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): %s\n", $newDoc->documentElement->getFrozen() ? 'YES' : 'NO'); 
printf("Child Element is frozen (should be): %s\n", $newDoc->getElementsByTagName('child')->item(0)->getFrozen() ? 'YES' : 'NO'); 

それは本当に完全にはなく作業のデモを備えていない:これは、すべての3つの問題(Demo)を解決します。追加の "フリーズ"データなしで完全なXMLを取得することは可能です。

+0

迅速な対応に感謝します。あなたが説明したソリューションを実装しようとしています。しかし、XML文字列がDOMDocumentに再ロードされたときに、拡張されたクラスプロパティにアクセスできないという問題に直面しているようです。 – neizan

+0

@neizan:ここで何を話していますか?独自のシリアライゼーションを行う場合は、探しているすべての可能性があるはずです。 XMLはシリアライゼーションの形式であるため、通常はDOMDocumentをシリアル化するのは意味をなさないことです。 – hakre

+0

私がシリアライゼーションを言ったとき、私は 'saveXML'メソッドの使用を指していました。混乱して申し訳ありません。私がちょうどあなたが言うことをしようと投稿した追加のサンプルコードを見てください。これは 'DOMElement'を使って動作しましたが、拡張クラス' FreezableDOMElement'では動作しませんでした。 – neizan

関連する問題