2012-05-01 24 views
5

Tôi khá mới với PHP, DOM và triển khai PHP DOM. Những gì tôi đang cố gắng thực hiện là lưu phần tử gốc của số DOMDocument vào biến số $_SESSION để tôi có thể truy cập và sửa đổi thành phần tải trang tiếp theo.

Nhưng tôi nhận được một lỗi trong PHP khi sử dụng $_SESSION để lưu trạng thái của DOMElement:

Cảnh báo: DOMNode :: appendChild() [domnode.appendchild]: Không thể lấy DOMElement

Tôi đã đọc rằng một đối tượng DOMDocument PHP không thể được lưu vào $_SESSION nguyên bản. Tuy nhiên, nó có thể được lưu bằng cách lưu tuần tự hóa của DOMDocument (ví dụ: $_SESSION['dom'] = $dom->saveXML()).

Tôi không biết điều tương tự có đúng với việc lưu một số DOMElement thành một biến số $_SESSION hay không, nhưng đó là những gì tôi đang cố gắng. Lý do tôi muốn làm điều này là sử dụng một lớp DOMElement mở rộng với một thuộc tính bổ sung. Tôi đã hy vọng rằng bằng cách lưu DOMElement gốc trong $ _SESSION mà sau này tôi có thể lấy phần tử và sửa đổi thuộc tính bổ sung này và thực hiện một phép thử như, nếu (addProperty === false) {làm điều gì đó; }. Tôi cũng đã đọc điều đó bằng cách lưu một DOMDocument và sau đó lấy nó, tất cả các phần tử được trả về như các đối tượng từ các lớp DOM nguyên gốc. Tức là, ngay cả khi tôi đã sử dụng một lớp mở rộng để tạo ra các phần tử, thì thuộc tính mà tôi sau đó cần sẽ không thể truy cập được, vì biến giữ tham chiếu đến đối tượng lớp mở rộng đã vượt quá phạm vi - đó là lý do tại sao tôi ' m cố gắng điều này khác. Tôi đã thử sử dụng lớp mở rộng (không bao gồm bên dưới) trước, nhưng có lỗi ... vì vậy tôi đã hoàn nguyên về việc sử dụng đối tượng DOMElement để xem đó có phải là sự cố hay không, nhưng tôi vẫn gặp lỗi tương tự. Dưới đây là các mã:

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

Mã này mang đến cho các lỗi sau đây (tuỳ thuộc vào việc tôi sử dụng appendChild trực tiếp hoặc dòng nhận xét của mã sử dụng 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

tôi có một số câu hỏi. Trước tiên, điều gì gây ra lỗi này và cách khắc phục sự cố? Ngoài ra, nếu những gì tôi đang cố gắng làm là không thể, sau đó làm thế nào tôi có thể thực hiện mục tiêu chung của tôi về tiết kiệm 'nhà nước' của một cây DOM trong khi sử dụng một thuộc tính tùy chỉnh cho mỗi phần tử? Lưu ý rằng thuộc tính bổ sung chỉ được sử dụng trong chương trình và không phải là thuộc tính được lưu trong tệp XML. Ngoài ra, tôi không thể chỉ lưu DOM trở lại tệp mỗi lần, vì DOMDocument, sau khi sửa đổi, có thể không hợp lệ theo lược đồ tôi đang sử dụng cho đến sau khi bổ sung modificaitons/bổ sung đã được thực hiện cho DOMDocument. Đó là lý do tại sao tôi cần phải lưu một DOMDocument tạm thời không hợp lệ. Cảm ơn lời khuyên nào!

CHỈNH SỬA: Sau khi thử giải pháp của hakre, mã đã hoạt động. Sau đó, tôi chuyển sang sử dụng một lớp DOMElement mở rộng, và, như tôi nghi ngờ, nó không hoạt động. Đây là mã mới:

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

Nó cho tôi lỗi Undefined property: DOMElement::$frozen. Như tôi đã đề cập trong bài đăng gốc của mình, sau saveXMLloadXML, một phần tử được khởi tạo ngay lập tức với FreezableDOMElement đang trả về loại DOMElement đó là lý do tại sao thuộc tính frozen không được nhận dạng. Có cách nào để khắc phục điều này?

Trả lời

4

Bạn không thể lưu trữ đối tượng DOMElement bên trong $_SESSION. Nó sẽ làm việc lúc đầu, nhưng với yêu cầu tiếp theo, nó sẽ không được thiết lập bởi vì nó không thể được tuần tự hóa.

Điều này tương tự như đối với DOMDocument khi bạn viết về câu hỏi của mình.

Lưu trữ dưới dạng XML thay thế hoặc đóng gói cơ chế tuần tự hóa.

cơ bản Bạn đang phải đối mặt với ba vấn đề ở đây:

  • Serialize các DOMDocument (bạn làm điều này để)
  • Serialize các FreezableDOMElement (bạn làm điều này để)
  • Giữ viên tin FreezableDOMElement::$frozen với tài liệu .

Như đã viết, việc tuần tự không có sẵn trong hộp. Ngoài ra, DOMDocument không còn giữ nguyên số FreezableDOMElement của bạn ngay cả khi được tuần tự hóa. Ví dụ sau đây cho thấy rằng các trường hợp không được tự động lưu giữ, giá trị mặc định FALSE được trả về (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) 

Như PHP không nên hỗ trợ xa setUserData (DOM Level 3), một trong những cách có thể để lưu trữ các thông tin bổ sung bên trong thuộc tính không có tên với phần tử. Điều này cũng có thể được tuần tự hóa bằng cách tạo chuỗi XML khi tuần tự hóa đối tượng và tải nó khi unserializing (xem Serializable). Điều này sau đó giải quyết tất cả ba vấn đề (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'); 

Nó không thực sự đầy đủ nhưng là một bản demo làm việc. Có thể lấy toàn bộ XML mà không cần thêm dữ liệu "đóng băng".

+0

Cảm ơn bạn đã phản hồi nhanh. Tôi sẽ cố gắng thực hiện giải pháp mà bạn mô tả. Tuy nhiên, có vẻ như tôi có thể sẽ vẫn gặp sự cố không có các thuộc tính lớp mở rộng có thể truy cập khi chuỗi XML được tải lại vào DOMDocument, đúng không? – neizan

+0

@neizan: Về những gì bạn nói ở đây? Nếu bạn chăm sóc serialization của riêng bạn, bạn nên có tất cả các khả năng bạn đang tìm kiếm. Nó chỉ là thường xuyên tuần tự hóa 'DOMDocument' làm cho không có ý nghĩa, như XML là một hình thức serialization đã. – hakre

+0

Phải, khi tôi nói serialization tôi đã đề cập đến bằng cách sử dụng phương pháp 'saveXML', xin lỗi vì sự nhầm lẫn. Vui lòng xem mã mẫu bổ sung mà tôi vừa đăng để cố gắng làm những gì bạn nói. Nó hoạt động bằng cách sử dụng 'DOMElement', nhưng không phải với lớp mở rộng' FreezableDOMElement'. – neizan

Các vấn đề liên quan