Как проверить, был ли DomDocument изменен простым и быстрым сравнением?

Мне нужно только сравнить тот же объект DomDocument до и после некоторой операции, «быстрая проверка», если операция изменила объект … Мне нужен некоторый serialize_DomDocument() ( существует что-то вроде? ), Который может использоваться в следующем контексте:

  1. $obj – объект DomDocumemt («состояние объекта» – это значение всех свойств и всего содержимого documentElement).

  2. $dump = serialize_DomDocument($obj) Как это сделать? Как сбросить состояние объекта? … Не переводя все в XML методом saveXML() , а только (FASTER!), Справляя все двоичные представления (объекта, на который указывает $obj ) на $dump .

  3. выполнить некоторую операцию (например, удалить узел или изменить атрибут или «ничего не делать»)

  4. if ($dump != serialize_DomDocument($obj)) или что-то вроде этого, проверяя, изменился ли $obj . Быстрое сравнение. Операция была «ничего не делать» или «что-то делать»?

Альтернативное решение…

Не идеальный, но разрешите некоторые случаи … Есть некоторые операции (например, только appendChild или only removeChild ), где изменение всегда влияет на количество узлов, поэтому для такого рода операций, чтобы проверить общее количество узлов или общей длины .

Как проверить быстрее, чем saveXML() ?


ЗАМЕТКИ

ПРИМЕЧАНИЕ-1 : как это помнит этот пост , вы не можете сериализовать объект DomDocument.

ПРИМЕЧАНИЕ 2 : это не проблема сравнения двух разных объектов DOM , но что-то более простое, потому что не изменять идентификаторы и т. Д. Не нужно каноническое представление (!), А только доступ к внутреннему представлению.


ИЗМЕНИТЬ ПОСЛЕ БОУТНИ

Этот вопрос касается не того, «как изменяет controll или флаг изменения» , пожалуйста, прочитайте с вниманием вопрос.

Этот вопрос не является запросом теоретических обзоров .

EDIT2

Возможно, решение касается «низкого уровня» … Я не понимаю, является ли «двоичное представление» «дампом» или имеет другое имя, см.

  • libxml xmlNodePtr для необработанной строки XML?
  • http://xmlsoft.org/html/libxml-tree.html#xmlBufNodeDump

Solutions Collecting From Web of "Как проверить, был ли DomDocument изменен простым и быстрым сравнением?"

Во-первых, я долгое время не использовал DOMDocument, но я отдам ему (и, пожалуйста, прочитайте полный пост).

Вы можете использовать метод C14N (), это плохо документировано, но из моей IDE я получаю следующее:

 /** * Canonicalize nodes to a string * @link http://www.php.net/manual/en/domnode.c14n.php * @param exclusive bool[optional] <p> * Enable exclusive parsing of only the nodes matched by the provided * xpath or namespace prefixes. * </p> * @param with_comments bool[optional] <p> * Retain comments in output. * </p> * @param xpath array[optional] <p> * An array of xpaths to filter the nodes by. * </p> * @param ns_prefixes array[optional] <p> * An array of namespace prefixes to filter the nodes by. * </p> * @return string canonicalized nodes as a string&return.falseforfailure; */ public function C14N ($exclusive = null, $with_comments = null, array $xpath = null, array $ns_prefixes = null) {} 

Я просто возьму пример DOMDocument со страницы документации PHP для этого сообщения.

Так что, для моих примеров, у меня есть этот объект для начала. (обратите внимание, где я помещаю цикл for в комментарии, я буду использовать это последнее для бенчмаркинга):

  $xml = new \DOMDocument( "1.0", "ISO-8859-15" ); $xml_album = $xml->createElement( "Album" ); //---- for( $i=0; $i < 10000; $i++ ){ //for benchmarks I will be adding 30,000 nodes, to get something worth measuring performance on. // Create some elements. $xml_track = $xml->createElement( "Track", "The ninth symphony" ); // Set the attributes. $xml_track->setAttribute( "length", "0:01:15" ); $xml_track->setAttribute( "bitrate", "64kb/s" ); $xml_track->setAttribute( "channels", "2" ); // Create another element, just to show you can add any (realistic to computer) number of sublevels. $xml_note = $xml->createElement( "Note", "The last symphony composed by Ludwig van Beethoven." ); // Append the whole bunch. $xml_track->appendChild( $xml_note ); $xml_album->appendChild( $xml_track ); // Repeat the above with some different values.. $xml_track = $xml->createElement( "Track", "Highway Blues" ); $xml_track->setAttribute( "length", "0:01:33" ); $xml_track->setAttribute( "bitrate", "64kb/s" ); $xml_track->setAttribute( "channels", "2" ); $xml_album->appendChild( $xml_track ); $xml->appendChild( $xml_album ); //----- } //end for loop // Parse the XML. print $xml->saveXML(); 

Или, грубо говоря, когда мы кодируем его с помощью htmlspecialchars и немного табуляции:

 <?xml version="1.0" encoding="ISO-8859-15"?> <Album> <Track length="0:01:15" bitrate="64kb/s" channels="2">The ninth symphony <Note>The last symphony composed by Ludwig van Beethoven.</Note> </Track> <Track length="0:01:33" bitrate="64kb/s" channels="2">Highway Blues</Track> </Album> 

До сих пор хорошо использовать (плохо документированный C14N ()) дает нам это (минус хороший отступ и т. Д.), Обратите внимание, что они почти одинаковы, но порядок отличается, и мы минус бит кодирования, поэтому мы не захотим сравнить их друг с другом:

 <Album> <Track bitrate="64kb/s" channels="2" length="0:01:15">The ninth symphony <Note>The last symphony composed by Ludwig van Beethoven.</Note> </Track> <Track bitrate="64kb/s" channels="2" length="0:01:33">Highway Blues</Track> </Album> 

Теперь, как правило, это похоже на только файл saveXML, но у него есть еще несколько параметров фильтрации выходных данных, чем просто saveXML, поэтому я подумал, что хочу упомянуть об этом.

Теперь я не совсем уверен, почему забота о производительности, как в моем ограниченном тестировании, я взял на себя смелость зацикливать ее на 10 000 раз для 30 000 узлов (20 000 треков, 10 000 нот и 60 000 атрибутов), и даже тогда производительность была довольно хорошей, давая мне эти результаты (только для вызовов функций, показанных ниже, а не для создания содержимого DOM, поскольку это отдельная проблема):

  $xml->saveXML(); 'elapsedTime' => '0.10 seconds', 'elapsedMemory' => '0.39 KB' $xml->C14N(); 'elapsedTime' => '0.15 seconds', 'elapsedMemory' => '0.3 KB' /// outputting to the screen should not be tracked - as I show below this will have a slight, but non-zero impact on the performance benchmarks. echo $xml->saveXML() 'elapsedTime' => '0.16 seconds', //+0.06 seconds 'elapsedMemory' => '0.3 KB' echo $xml->C14N(); 'elapsedTime' => '0.21 seconds', //+0.06 seconds again 'elapsedMemory' => '0.3 KB' 

Таким образом, производительность немного меньше, чем у saveXML, но в обоих случаях я бы сказал, что для количества узлов, которые я использую, очень разумно.

Итак, мы можем с легкостью использовать saveXML или C14N, как мы можем сравнивать изменения с такой большой строкой? Мы, как все должны знать, вы это делаете. Теперь сразу можно подумать о md5, но sha1 на самом деле лучше здесь, он дает нам немного более длинный хеш, и разница в производительности незначительна. В обоих случаях хеширование добавляет около 1 сотки секунды и дает нам что-то более удобное в сравнении при сравнении, сохранении в БД и т. Д.

– В качестве побочного примечания я люблю хеширование, это как эпоксидный клей или клейкая лента, он просто работает на все.

Таким образом, мы просто хэш, сохраняем его в переменной и сравниваем все, что хотим:

  print md5( $xml->saveXML() ); '19edc177072416b7bbf88ea0a240be73' 'elapsedTime' => '0.11 seconds', 'elapsedMemory' => '0.39 KB' print sha1( $xml->saveXML() ); '7c644c6e1630ffde15eee64643779e415a1746b7' 'elapsedTime' => '0.11 seconds', 'elapsedMemory' => '0.3 KB' 

Теперь мне, вероятно, придется постучать за использование saveXML () (и / или C14N ()), но в конечном итоге то, к чему это сводится, – это. Даже считая атрибуты, которые могут быть сделаны таким образом (только для того, чтобы покрыть мои базы):

  $old_xp = new \DOMXpath($xml); $old_a = $old_xp->evaluate('count( //@* )'); $old_n = $old_xp->evaluate('count( //node() )'); print 'Attributes: '.$old_a.'<br>'; print 'Nodes: '.$old_n.'<br>'; print 'Total: '.($old_a + $old_n).'<br>'; 

выходы: / 1 итерации (отметьте вышеописанный xml):

  Attributes: 6 Nodes: 7 //expected 4 nodes Total: 13 

выходы: / 10000 итераций:

 'elapsedTime' => '0.02 seconds', 'elapsedMemory' => '0.5 KB' Attributes: 60000 Nodes: 60001 //expected 30,001 nodes ( +2 tracks, +1 note, node per loop and one album node )? Total: 120001 

Поскольку вы видите, что время выполняется быстрее, но поскольку мы создаем DOMXpath здесь, что может не повлиять на вас, если у вас уже есть экземпляр, потребление памяти почти вдвое.

– как побочная заметка, похоже, что $old_xp->evaluate('count( //node() )') дает странные подсчеты для узлов, которые я ожидал 4 узла, и получил 7, например, он считает как теги open, так и close и тег кодирования, или подсчитывает вложенность для каждого дочернего узла (проверили это, добавив узел заметок на второй трек, который не имел ни одного, а счет действительно увеличился на 2), более подробная информация об этом будет полезна.

В любом случае, вы знаете все остальное для этого метода.

Однако при использовании счетчиков, если вы должны удалить 1 атрибут и добавить другой, он будет неправильно подсчитывать атрибуты, то же самое относится и к узлам (запрещающий его странный подсчет).

Но, в конечном счете, нет способа узнать, изменилось ли это, не глядя на фактические данные, что, если содержимое узла изменится? и т.д…

И это (просто подсчет) может быть достаточно хорошим для ваших нужд

Выбор за вами, и это действительно зависит от того, какой уровень детализации вам нужен, и сколько потери производительности вы готовы принять за этот уровень детализации.

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

Наконец, просто генерация XML дала мне следующее время / использование памяти (помните, что сохранение и хеширование было всего 0,11 секунды):

 'elapsedTime' => '21.16 seconds', 'elapsedMemory' => '0.61 KB' 

Мы говорим о производительности здесь, но не было дано никаких цифр, поэтому нам действительно нужно вставлять вещи в контекст, когда принимаем решения на основе производительности.

Благодаря,

Здесь «решение второго класса», надеясь , что здесь «первый класс» .

… в отсутствие прямого решения есть « Альтернативные решения », как я выразился в тексте вопроса … И я так делаю сегодня в своем заявлении:

с count(//node()) XPath count(//node()) мы можем проверить, было ли изменено , быстрее, чем saveXML() , но если DOM имеет одинаковое количество узлов (например, значение атрибута изменения), нам нужно проверить saveXML , потратить память и / или время процессора для генерации и сравнения XML.

 // $old_dom is the original document, $old_xp = new DOMXpath($old_dom); // ... holding $old_n (and perhaps $old_xml) in a cache $old_n = $old_xp->evaluate('count(//node())'); $old_xml = $old_dom->saveXML(); ... // $new_dom is the modified document, must tested at each "black box" change. $new_xp = new DOMXpath($new_dom); $new_n = $new_xp->evaluate('count(//node())'); if ($new_n!=$old_n) { // OK, fast!! //.. OK! do something because changed! } // else, check with more detail if changed, elseif ($new_dom->saveXML()!=$old_xml){ // memory and CPU-time consuming here! //.. OK! do something because changed! } 

Как предложил @ArtisiticPhoenix, давайте сравним!

BENCHMARKING с помощью простых простых и реальных XML-документов PubMed Central :

  $xp = new DOMXpath($dom); $test = ( $xp->evaluate('count(//node())') == 123 ); // Execution total time of 10000 loops: 0.582 seconds // Averaged execution time of each loop: 5.8152985572815E-5 seconds $xp = new DOMXpath($dom); $test = ( $xp->evaluate('count(//*)') == 123 ); // Execution total time of 10000 loops: 0.462 seconds // Averaged execution time of each loop: 4.6241903305054E-5 seconds 

~ 10 раз лучше по сравнению с другими решениями,

  $test = ( md5( $dom->saveXML() ) == 'ff1afedc7127eb221050fa48eee5153a'); // Execution total time of 10000 loops: 3.877 seconds // Averaged execution time of each loop: 0.00038769061565399 seconds $test = ( $dom->saveXML() == $oldXml ); // comparing long strings // Execution total time of 10000 loops: 3.168 seconds // Averaged execution time of each loop: 0.00031677310466766 seconds $test = ( $dom->C14N() == $oldXml ); // comparing long strings // Execution total time of 10000 loops: 8.874 seconds // Averaged execution time of each loop: 0.00088736770153046 seconds 

ПРИМЕЧАНИЕ. Прямое сравнение longString быстрее, чем shortString по md5 . Подтверждено,

  $test = ( $dom->saveXML() == 'xxx' ); //Execution total time of 10000 loops: 3.14 seconds //Averaged execution time of each loop: 0.00031396999359131 seconds 

Длительное / короткое изменение, даже в случае wrost (сравнивая ту же строку), не изменяет «0,31 микросекунды» для выполнения прямых, лучше коротких + MD5 «0,39 микросекунды».


Что я ищу в этом «альтернативном решении» …

… является внутренним свойством DomDocument, которое возвращает «DOM length» или «DOM number of nodes», без необходимости процедуры подсчета XPath.

PS: для такого ответа есть щедрость за «альтернативное решение».

Существует три возможных решения этой общей проблемы:

1) Сохраните копию (или сериализованную копию), как вы предложили. Это решение может быть выполнено полностью извне (предполагается, что клонирование или сериализация существует). Однако, как вы уже упоминали, это может быть чрезвычайно медленным для сложных объектов.

2) Предоставить услугу (обнаружение изменений) как функцию класса DomDocument (или DomNode). Это решение требует от вас доступа к определению DomDocument, поэтому вы, вероятно, не хотите его рассматривать. Тем не менее, это «правильное» место для повышения эффективности службы, поскольку сам класс лучше знает, как определить, приведет ли какое-либо действие к изменению DOM.

3) Предоставьте услугу (запись изменений) в качестве функции этапа 3. выше. Это означает, что код «выполняет некоторую операцию» отслеживает, было ли произведено изменение. Для некоторых операций это может потребовать сохранения локальной копии (или сериализованной копии) небольшой локализованной части DOM. Или может потребоваться проверка текущего состояния некоторых узлов (узлов) перед выполнением операции, чтобы определить, произошло ли изменение. Однако для большинства операций ответ должен быть достаточно очевиден.

Хм, я посмотрел на структуру объектов DOMDocument . Похоже, нет простого способа сделать это. Моим первым решением было бы сравнить результаты метода saveXML . Но это не то, что вы хотите. Давайте сделаем это быстрее:

Вариант A:

Используйте внутреннюю функцию serialize php: http://php.net/manual/de/function.serialize.php . Это также сравнило бы весь документ, но в сериализованной форме. Может быть немного быстрее, чем сравнение результатов saveXML.

Вариант B:

Интересен первый параметр метода saveXML . Если вы знаете, какие узлы могли быть изменены, вы можете уменьшить свой вывод только на них (см. http://php.net/manual/de/domdocument.savexml.php ). Таким образом, вам не нужно сравнивать весь документ.

Вариант C:

Если объект DOMDocument не может определить изменения в собственном, давайте научим его делать это! Это будет выглядеть так (не проверял его на синтаксис, но это важная идея):

 use DOMDocument; use DOMNode; class AlterationSensitiveDOMDocument extends DOMDocument { /** * @var bool * Determines whether the DOMDocument was altered or not. */ protected $isAltered = false; /** * @param DOMNode $newnode * * @return DOMNode|void */ public function appendChild(DOMNode $newnode) { $this->isAltered = true; return parent::appendChild($newnode); } // More overrides (The ones you need) /** * @return boolean */ public function isAltered() { return $this->isAltered; } } 

Используя новый метод AlterationSensitiveDOMDocument вместо DOMDocument, вы получите полный доступ к параметрам и методам DOMDocument. PLUS некоторые из методов изменения (те, которые вы переопределите) задают состояние «isAltered» равным true, поэтому вы можете видеть, что объект DOMDocument был изменен или нет.

Это решение пахнет хаком, но должно сделать трюк.