首先我没有使用过 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) {}
我将简单地从这篇文章的 PHP 文档页面中取出 DOMDocument 的示例。
所以对于我的例子来说,我有这个 obj 开始。 (请注意我将 for 循环放在 cmets 中的位置,我将使用后者进行基准测试):
$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,所以我想我会提到它。
现在我不完全确定为什么在有限的测试中对性能的关注我冒昧地为 30,000 个节点(20,000 个轨道、10,000 个音符节点和 60,000 个属性)循环了 10,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 更好,它给我们的哈希值稍长,性能差异可以忽略不计。在这两种情况下,散列增加了大约百分之一秒,并让我们在比较、保存在数据库等时更容易查看。
-- 作为旁注,我喜欢散列,它就像环氧树脂胶水或胶带,它适用于所有东西。
所以我们只需对其进行哈希处理,将其保存到一个变量中并进行比较:
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
输出:/ 10,000 次迭代:
'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 个节点的节点提供了奇怪的计数,就像它计算打开和关闭标签以及编码标签,或者计算每个节点的嵌套子节点(通过在第二条轨道上添加一个音符节点来检查这一点,它没有,并且计数确实增加了 2 )关于这方面的任何更多信息都会有所帮助。
反正你知道这个方法的其余部分。
但是,在使用计数时,如果您要删除 1 个属性并添加另一个属性,则会错误地计算属性,这同样适用于节点(但计数很奇怪)。
但是,最终没有办法知道它是否改变了,如果不查看实际数据,如果一个节点的内容发生了变化怎么办?等等……
这(只是数数)可能足以满足您的需求
选择权在您手中,这实际上取决于您需要什么级别的详细信息,以及您愿意为该级别的详细信息承担多少性能损失。
我建议对每个步骤进行彻底的基准测试,然后确定哪个更能满足您的需求。
最后只是生成 XML 给了我以下时间/内存使用情况(记住保存和散列只有 0.11 秒):
'elapsedTime' => '21.16 seconds',
'elapsedMemory' => '0.61 KB'
我们在这里谈论性能,但没有给出数字,因此当我们根据性能做出决策时,我们确实需要将事情放在上下文中。
谢谢,