lyte/xml

更安全、更简单的PHP XML处理

1.0.0 2017-06-21 09:32 UTC

This package is not auto-updated.

Last update: 2024-09-29 02:34:08 UTC


README

Build Status Test Coverage Code Climate Issue Count

PHP中的XML基础类有一些小问题让我感到非常烦恼,这是一个针对这些烦恼的简单修复集合。

我试图放入的一些内容将是纯实验性的,所以请自行承担风险 :)

示例

XMLWriter中的嵌套CDATA

有一个相当知名的方法可以解决XML实际上不允许在另一个CDATA标签内嵌套CDATA标签的事实,但XMLWriter没有为你应用这个修复。如果你是在传输包含在另一个XML格式中的HTML片段,这会成为一个问题。

使用 XMLWriter

$writer = new \XMLWriter();
$writer->openMemory();
$writer->writeCData('<![CDATA[a little bit of cdata]]>');
echo $writer->flush()."\n";

将导致

<![CDATA[<![CDATA[a little bit of cdata]]>]]>

这不是有效的XML!

请使用 Lyte\XML\XMLWriter 代替,这样你将得到你期望的结果

use Lyte\XML\XMLWriter;
$writer = new XMLWriter();
$writer->openMemory();
$writer->writeCData('<![CDATA[a little bit of cdata]]>');
echo $writer->flush()."\n";

将导致

<![CDATA[<![CDATA[a little bit of cdata]]]]><![CDATA[>]]>

从XMLReader展开到DOMNode

使用原生的 XMLReader,如果你调用 expand(),你会得到一个 DOMNode,其 ownerDocument 属性设置为 null,这使得使用 DOMXPath 或将其保存为XML字符串片段变得相当困难。

例如,使用原生的 XMLReader

$reader = new \XMLReader();
$reader->xml('<foo>bar</foo>');
$reader->read();
$node = $reader->expand();
echo $node->ownerDocument->saveXML();

导致

PHP Fatal error:  Call to a member function saveXML() on a non-object in - on line 6

... 哎呀!

使用 Lyte\XML\XMLReader,如果你展开一个节点,它将为你创建 ownerDocument

use Lyte\XML\XMLReader;
$reader = new XMLReader();
$reader->xml('<foo>bar</foo>');
$reader->read();
$node = $reader->expand();
echo $node->ownerDocument->saveXML();

这次成功了

<?xml version="1.0"?>
<foo>bar</foo>

懒XPaths

PHP在 DOMXPath 的形式下提供了相当可靠的 XPath 支持,但它并没有直接附加到任何东西上,破坏了你的OO上下文,因为你现在需要传递两个对象或不断重新实例化你的 DOMXPath 对象。

Lyte\XML\DOMDocument 将会懒加载一个 DOMXPath 对象以供使用,如果你只是请求它,例如使用原生的 DOMDocument

$doc = new \DOMDocument();
$doc->load('<foo/>');
$xpath = new \DOMXPath($doc);
// and now I've got to pass around $doc and $xpath or recreate $xpath many times

使用 Lyte\XML\DOMDocument

use Lyte\XML\DOMDocument;
$doc = new DOMDocument();
$doc->loadXML('<foo/>');
// now I can just use the xpath (the xpath property gets instantiated to a Lyte\XML\DOMXPath as it's requested)
$nodes = $doc->xpath->query('/foo');

上下文化的DOMNode XPath函数

通常,要在一个特定的上下文中运行XPath,你必须做一些设置,例如

$doc = new \DOMDocument();
$doc->loadXML('<root><foo>one</foo><foo>two</foo></root>');
$xpath = new \DOMXPath($doc);
$node = $doc->firstChild;
$nodes = $xpath->query('foo/text()', $node);

但是,Lyte\XML\DOMNode 提供了直接上下文化的XPath函数

use Lyte\XML\DOMDocument;
$doc = new DOMDocument();
$doc->loadXML('<root><foo>one</foo><foo>two</foo></root>');
$nodes = $doc->firstChild->xPathQuery('foo/text()');

还有一个 Lyte\XML\DOMNode::xPathEvaluate() 函数,它与 DOMXPath::evaluate() 同义,上下文已经填充。

键/值对迭代器

我似乎经常需要用键对解析XML,例如

<root>
	<key1>value1</key1>
	<key2>value2</key2>
	...
	<key3>value3</key3>
</root>

使用 Lyte\XML\DOMNodeList,我提供了一个 toPairs() 函数来简化此操作

// once you have a node with the key/pairs in it:
$node = ...;
// you can just iterate over it:
foreach ($node->childNodes->toPairs() as $k => $v) {
	...
}

在任意位置使用saveXML()

有一个常见的技巧可以获取XML DOM的子树的XML,使用 ownerDocument 如此

$xml = $node->ownerDocument->saveXML($node);

使用一个 Lyte\XML\DOMNode,你只需请求它直接保存XML即可

$xml = $node->saveXML();

即时转换为UTF8

Lyte\XML\XMLWriter

只需声明源编码,并在即时转换,例如

use Lyte\XML\XMLWriter;
$writer = new Lyte\XML\XMLWriter();
$writer->openMemory();
$writer->setSourceCharacterEncoding('Windows-1252');
$writer->text("Don\x92t you hate word quotes?\n");
echo $writer->flush();

生成

Don’t you hate word quotes?

Lyte\XML\DOMDocument::loadHTML()

从任意字符集加载HTML

use Lyte\XML\DOMDocument;
$dom = new DOMDocument();
$html = "<p>\x93bendy quotes\x94</p>";
$encoding = 'Windows-1252';
$dom->loadHTML($html, $encoding);

要求

PHP 5.4+ 或 HHVM。

注意事项

我创建的大部分类都不是直接从XML类继承的,例如:new Lyte\XML\DOMDocument() instanceof \DOMDocument 返回 false。我目前这样做是为了避免在各个地方重复使用内存并重新序列化过多的XML,我非常需要使用装饰器模式,但即使使用了PHP的魔术方法,我也找不到同时继承和装饰对象的方法。我还查看了使用反射API来遍历上游类并选择性地eval一个新的类来存在,但遇到了许多公共属性在DOM基类中在奇怪的时间被更新的问题。

最终结果是许多对象看起来像鸭子,说话像鸭子,但在一些奇怪的情况下,你可能难以说服PHP它们是鸭子,但如果你遇到问题,请仍然向我发送任何错误。

如果有人能解决这个问题,请提交一个问题:)