Я довольно новичок в PHP, DOM и реализации PHP DOM. То, что я пытаюсь сделать, это сохранить корневой элемент DOMDocument
в переменной $_SESSION
чтобы я мог получить к нему доступ и изменить его при последующих загрузках страниц.
Но я получаю ошибку в PHP при использовании $_SESSION
для сохранения состояния DOMElement:
Предупреждение: DOMNode :: appendChild () [domnode.appendchild]: Не удалось получить DOMElement
Я прочитал, что объект PHP DOMDocument не может быть сохранен в $_SESSION
изначально. Однако его можно сохранить, сохранив сериализацию DOMDocument (например, $_SESSION['dom'] = $dom->saveXML()
).
Я не знаю, верно ли то же самое для сохранения DOMElement
в переменной $_SESSION
, но это то, что я пытался. Моей причиной для этого является использование расширенного класса DOMElement с одним дополнительным свойством. Я надеялся, что, сохранив корневой DOMElement в $ _SESSION, я мог бы позже извлечь элемент и изменить это дополнительное свойство и выполнить тест, например if (extraProperty === false) {сделать что-то; }. Я также прочитал, что, сохраняя DOMDocument, а затем получая его, все элементы возвращаются как объекты из родных классов DOM. То есть, даже если я использовал расширенный класс для создания элементов, то свойство, которое мне впоследствии понадобится, не будет доступно, потому что ссылка, содержащая ссылку на объект расширенного класса, вышла за пределы области действия – вот почему я ' m пробует эту другую вещь. Сначала я попытался использовать расширенный класс (не включен ниже), но получил ошибки … поэтому я вернулся к использованию объекта 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. Кроме того, я не могу просто сохранять DOM обратно в файл каждый раз, потому что DOMDocument после модификации может быть недействительным в соответствии со схемой, которую я использую до тех пор, пока в DOMDocument не будут выполнены дополнительные модификации / дополнения. Вот почему мне нужно сохранить временно недопустимый 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
. Как я уже упоминал в своем оригинальном посте, после saveXML
и loadXML
, элемент, первоначально loadXML
с помощью 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): %d\n", $doc->documentElement->getFrozen()); # it is not (0)
Поскольку PHP до сих пор не поддерживает setUserData
(DOM Level 3), одним из способов может быть сохранение дополнительной информации внутри атрибута с пространством имен с элементом. Это также можно сериализовать, создав XML-строку при сериализации объекта и загрузке его при несериализации (см. Serializable
). Затем это решает все три проблемы ( 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 без дополнительных данных о «замораживании».