Траблы с Simple XML

alexn

Guest
Траблы с Simple XML

Привет всем!
Я в одном своем проекте решил воспользоваться SimpleXML (на свою голову) - думал будет меньше ресурсов "кушать", быстрее ворочатся и т.п.

Все шло нормально, пока не возникла необходимость создавать новые узлы.
Нашел класс, расширяющий SimpleXML, который позволяет это делать: http://www.php.net/manual/ru/ref.simplexml.php#54486
Но у меня он вызывает Exception в $parent->appendChild($node); с кодом 4 "Wrong Document Error".

Непонятно, то-ли автор не проверил свой класс, прежде чем выложить его на общее обозрение, то-ли я что-то не так делаю.

Скачать файл для тестирования можно здесь:
http://www.axis.com.ua/alex/extendedSimpleXML.tgz
test1.php - это работа с классом в практически не измененном состоянии (добавлены только try и catch);
test2.php - я пробовал различные варианты изменения этого класса.

Мне очень хочется понять причину этого Exception! :confused:

P.S.: Не хочется мне сейчас отказываться от SImplXML и переводить все на DOM. Во-первых DOM - "тяжелее для сервера" (больше ресурсов требует), а во-вторых уже много сделано под SimplXML. Но если другого выхода не будет - придется перейти на DOM. :(
 

slach

Новичок
PHP:
<?
/**
*   конвертация SimpleXMLElement в DOMDocument
*   @param object ссылка на  SimpleXMLElement
*   @return object DOMDocument
*/
    function simplexml_to_domxml(SimpleXMLElement $simplexml) {
        $dom = new DOMDocument('1.0');
        $domnode_imported = dom_import_simplexml($simplexml);
        if (!$domnode_imported) trigger_error(_('simplexml_to_domxml Ошибка конвертации XML'), E_USER_WARNING);
        try {
            $domnode = $dom->importNode($domnode_imported, true);
        } catch(DOMException $e) {
            if (!$domnode) trigger_error(sprintf(_('simplexml_to_domxml Ошибка конвертации XML, DOMException %s'),$e->getMessage()), E_USER_WARNING);
        }

        if ($domnode) {
            $dom->appendChild($domnode);
            return $dom;
        } else {
            return false;
        }
    }

/**
*   добавление дочерней ноды в simpleXML документ, взято из {@link [url]http://www.php.net/manual/en/ref.simplexml.php#46256[/url]}
*
*   пример использования:
*   <code>
*   $xml = simplexml_load_string( '<?xml version="1.0" standalone="yes"?><root><parenttag></parenttag></root>' );
*   $xml = simplexml_addchild( $xml, "//parenttag", 'childtag', 'xxxyyy' );
*   echo $xml->asXml();
*   </code>
*   @param SimpleXMLElement $simplexml документ к которому добавляется нода
*   @param string $xpathParentNode путь до родительской ноды
*   @param string $childNodeName имя дочерней ноды
*   @param string $childNodeValue значение дочерней ноды
*   @param array $childNodeAttrs ассоциативный массив атрибутов (attrName=>attrValue)
*/
    function simplexml_addchild(SimpleXMLElement $simplexml, $xpathParentNode='/', $childNodeName, $childNodeValue='',$childNodeAttrs=NULL) {
        if (!$childNodeName) trigger_error(_('simplexml_addchild ошибка, не задано $childNodeName'),E_USER_ERROR);
        $dom = simplexml_to_domxml($simplexml);
        $xpath = new DOMXPath($dom);
        $parent = $xpath->query($xpathParentNode);
        if ($parent instanceof DOMNodeList && $parent->length>0) {
            for($i=0;$i<$parent->length;$i++) {
                $node  = $dom->createElement('testNode', 'testValue');
                if (is_array($childNodeAttrs)) {
                  foreach($childNodeAttrs as $attrName=>$attrValue) {
                    $node->setAttribute($attrName,$attrValue);
                  }
                }
                $parent->item($i)->appendChild($node);
            }
            $simplexml = simplexml_import_dom($dom);
        }
        return $simplexml;
    }

/**
*   удаление дочерней ноды или списка нод из simpleXML документа, по условию
*
*   пример использования:
*   <code>
*   $xml = simplexml_load_string( '<?xml version="1.0" standalone="yes"?><root><sometag></sometag></root>' );
*   $xml = simplexml_removechild( $xml, "//sometag");
*   echo $xml->asXml();
*   </code>
*/
    function simplexml_removechild(SimpleXMLElement $simplexml, $xpathRemove='/') {
        $dom = simplexml_to_domxml($simplexml);
        $xpath = new DOMXPath($dom);
        $nodelist = $xpath->query($xpathRemove);
        for ($i=0;$i<$nodelist->length;$i++) {
          $nodelist->item($i)->parentNode->removeChild($nodelist->item($i));
        }
        if ($dom->documentElement instanceof DOMNode) {
            $simplexml = simplexml_import_dom( $dom );
        } else {
            trigger_error(_('simplexml_removechild удалены все узлы в документе включая корневой, SimpleXMLElement не может работать без корневого узла'),E_USER_WARNING);
            $simplexml = false;
        }
        return $simplexml;
    }
?>
 

alexn

Guest
slach
Спасибо за предложенный вариант. Кое какие идеи я у Вас позаимствую. :)

Но это немного не то, что мне нужно.
Отдельные узлы у меня получалось добавлять немного модифицировав тот класс, что я привел в примере. Но мне нужно вставлять целые ветки: грубо говоря - подставить один xml-файл в определенный узел другого xml.

У меня уже кое-что в этом направлении начало получаться. Попробую добить этот вопрос самостоятельно! :)
 

slach

Новичок
ну вообще =) добавлять ветки можно ТОЛЬКО через DOM документ
importNode

Imho вы неверно выбрали интерфейс для доступа
особенно если вам требуется частая модификация дерева
 

alexn

Guest
slach
Hу вообще-то я именно так и хотел. Я хотел сделать работоспособным тот класс, на который я давал ссылку. По идее, он должен при помощи DOM позволять делать то, что я хочу.
Вообще я уже почти все в своем проекте перевожу на DOM, хоть меня и смущает его "тяжеловесность".

Хотя модификация дерева мне нужна не часто (максимум 1-2 раза, за один запрос), но модификации специфические - именно так как я сказал: одно дерево вкладывается в другое.

У меня есть попутно еще один вопрос (пусть простят меня модераторы, что не выделяю его в отдельную тему, но завязка именно в том, что я с самого начала не хотел брать DOM из-за его ресурсоемкости). Так вот вопрос вот в чем: может быть есть какие-то способы кэширования DOM-xml, чтобы php не делал каждый раз синтаксический разбор xml-файла, а брал его из кэша "тепленьким" (в виде готового сформированного объекта).
 

[DAN]

Старожил PHPClub
Стандартные средства php не позволяют кешировать DOM-документы.
Смотри в сторону сторонних продуктов.
Встречный вопрос: а в чем проявляется "тяжеловесность" твоих DOM-документов?
 

Profic

just Profic (PHP5 BetaTeam)
alexn
Внутри php, что dom дерево, что simplexml дерево представляются одинаково. И то, и другое работает через libxml2. И все те прелести, которые дает SimpleXML, достигаются на стороне php.

А насчет кеша. Эти php-объекты внутри хранят c-"объекты", потому кеша не будет.
 

[DAN]

Старожил PHPClub
Profic, не совсем правильно говоришь.
Модуль SimpleXml не работает с DOM-объектами непосредственно. Есть возможность работать с DOM-документом используя API SimpleXML, но это немного не то.
Сам модуль скорее реализует SAX-модель обработки XML-данных.

А кешировать объекты вполне можно. В конце концов класс DomDocument имплементирован в PHP по-умолчанию, и объекты этого класса можно кешировать также, как и объекты других классов, используя сторонние продукты.
 

slach

Новичок
DAN ?? какие сторонние продукты имеются ввиду ?? =)
уже не memcached ? или APC ?? или SRM ???

я что то пока не видел РЕАЛЬНОГО инструмента для именно хранения "распарсенного" php5 DOM документа =(
 

[DAN]

Старожил PHPClub
slach, хотя бы тот же SRM.
Чем принципиально отличается объект самописного класса от объекта DomDocument?
Честно говоря, в глубоко в задачу я не вдавался, но моя логика такова:
если можно кешировать объекты в PHP, почему бы этого не делать с DOM-объектами.

P.S. речь идет о PHP5.
 

slach

Новичок
=) спешу огорчить, SRM не собирается вместе с php5 у меня по крайней мере =) не говоря уже про то, что SRM это вообще уже не КЭШ объектов
это фактически, отдельный ДЕМОН, в котором есть энное кол-во call-backs, которые вызываются через $srm->callBackName();...
т.е. фактически это Application Level объекты
хотя конечно при грамотной реализации из них можно сделать Session Level объекты

кеширование через СЕРИАЛИЗАЦИЮ не проканывает
через MEMCACHED тоже (там фактически таже сериализация)

так что, пока IMHO ничего кроме статических кусков XML в файлах реализовывать не получается
что тоже не так уж плохо
 
Сверху