mensbeam / html-parser
现代HTML文档的解析器和序列化器
Requires
- php: >=7.1
- ext-dom: *
- mensbeam/intl: >=0.9.1
- mensbeam/mimesniff: >=0.2.0
Requires (Dev)
Suggests
- ext-ctype: Improved performance
README
PHP的现代、准确的HTML解析器和序列化器。
使用方法
解析文档
public static MensBeam\HTML\Parser::parse( string $data, ?string $encodingOrContentType = null, ?MensBeam\HTML\Parser\Config $config = null ): MensBeam\HTML\Parser\Output
MensBeam\HTML\Parser::parse
静态方法用于解析文档。输入是任意字符串和可选的编码,输出是 MensBeam\HTML\Parser\Output
对象。该 Output
对象具有以下属性
document
: 表示解析文档的DOMDocument
对象encoding
: 文档的原始字符编码,由用户提供或在解析过程中检测到quirksMode
: 检测到的文档的“怪癖模式”属性。这将是从Parser::NO_QURIKS_MODE
(0
)、Parser::QUIRKS_MODE
(1
) 或Parser::LIMITED_QUIRKS_MODE
(2
) 中选择的一个errors
: 如果启用了解析错误报告,则包含在处理过程中发出的解析错误列表的数组,否则为null
可以通过传递一个 MensBeam\HTML\Parser\Config
对象作为最后一个 $config
参数来向解析器提供额外的配置参数。有关更多详细信息,请参阅下面的 配置 部分。
使用 DOMParser
进行解析
从版本 1.3.0 开始,该库还提供了一个 DOMParser
接口 的实现。
class MensBeam\HTML\DOMParser { public function parseFromString( string $string, string $type ): \DOMDocument }
与标准接口一样,它将解析 HTML 或 XML 文档。然而,该实现有以下不同之处
- 任何 XML MIME 内容类型(例如,
application/rss+xml
)都是可接受的,而不仅仅是接口要求的受限列表 - MIME 内容类型可以包含一个
charset
参数来指定文档的权威编码 - 如果没有提供
charset
,则将从文档提示中检测编码;HTML 的默认编码为windows-1252
,XML 的默认编码为UTF-8
- 在适当的位置抛出
InvalidArgumentException
而不是 JavaScript 的TypeError
将解析到现有文档中
public static MensBeam\HTML\Parser::parseInto( string $data, \DOMDocument $document, ?string $encodingOrContentType = null, ?MensBeam\HTML\Parser\Config $config = null ): MensBeam\HTML\Parser\Output
使用 MensBeam\HTML\Parser::parseInto
静态方法将解析到现有文档中。提供的文档必须是 \DOMDocument
的实例或其派生实例,并且必须为空。所有其他参数与正常解析文档时使用的参数相同。
注意: 当使用此方法时,documentClass
配置选项无效。
解析片段
public static MensBeam\HTML\Parser::parse( DOMElement $contextElement, int $quirksMode, string $data, ?string $encodingOrContentType = null, ?MensBeam\HTML\Parser\Config $config = null ): DOMDocumentFragment
使用 MensBeam\HTML\Parser::parseFragment
静态方法来解析文档片段。此方法的主要用途是在实现 HTML 元素的 innerHTML
设置器时。因此需要上下文元素以及上下文元素的文档的“怪癖模式”属性(这必须是 Parser::NO_QUIRKS_MODE
(0
)、Parser::QUIRKS_MODE
(1
) 或 Parser::LIMITED_QUIRKS_MODE
(2
) 之一)。其他参数与解析文档时使用的参数相同。
如果不知道文档的“怪癖模式”属性,通常使用 Parser::NO_QUIRKS_MODE
(0
) 是最佳选择。
与 parse()
方法不同,parseFragment()
方法返回属于 $contextElement
所拥有文档的 DOMDocumentFragment
对象。
序列化节点
public static MensBeam\HTML\Parser::serialize( DOMNode $node, array $config = [] ): string
public static MensBeam\HTML\Parser::serializeInner( DOMNode $node, array $config = [] ): string
MensBeam\HTML\Parser::serialize
方法可以将大多数 DOMNode
对象转换为字符串,使用 HTML 规范中定义的基本算法。以下类型的节点可以成功序列化:
DOMDocument
DOMElement
DOMText
DOMComment
DOMDocumentFragment
DOMDocumentType
DOMProcessingInstruction
同样,MensBeam\HTML\Parser::serializeInner
方法可以使用 HTML 规范中定义的基本算法将非叶 DOMNode
对象的子节点转换为字符串。以下类型的节点的子节点可以成功序列化:
DOMDocument
DOMElement
DOMDocumentFragment
序列化方法使用关联数组进行配置,可能的键和值类型如下:
booleanAttributeValues
(bool|null
):在序列化过程中是否包含 HTML 元素上布尔属性的值。按照标准,默认为true
。foreignVoidEndTags
(bool|null
):是否打印外部的 void 元素的结束标签而不是自闭合其开始标签。按照标准,默认为true
。groupElements
(bool|null
):将类似“块”的元素分组,并在组之间插入额外的换行符。indentStep
(int|null
):每个步骤缩进的空间或制表符(取决于缩进步骤的设置)的数量。默认为1
,除非reformatWhitespace
为true
,否则没有效果。indentWithSpaces
(bool|null
):是否使用空格或制表符进行缩进。默认为true
,除非reformatWhitespace
为true
,否则没有效果。reformatWhitespace
(bool|null
):是否重新格式化空白(美化打印)。默认为false
。
示例
-
解析未知编码的文档
use MensBeam\HTML\Parser; echo Parser::parse('<!DOCTYPE html><b>Hello world!</b>')->encoding; // prints "windows-1252" echo Parser::parse('<!DOCTYPE html><meta charset="UTF-8"><b>Hello world!</b>')->encoding; // prints "UTF-8"
-
解析已知编码的文档
use MensBeam\HTML\Parser; echo Parser::parse("<!DOCTYPE html>\u{3088}", "UTF-8") ->document ->getElementsByTagName("body")[0] ->textContent; // prints "よ" echo Parser::parse("<!DOCTYPE html>\u{3088}", "text/html; charset=utf-8") ->document ->getElementsByTagName("body")[0] ->textContent; // also prints "よ"
-
解析不同默认编码的文档
use MensBeam\HTML\Parser; use MensBeam\HTML\Parser\Config; $config = new Config; $config->encodingFallback = "Shift_JIS"; echo Parser::parse("<!DOCTYPE html>\x82\xE6", null, $config) ->document ->getElementsByTagName("body")[0] ->textContent; // also also prints "よ"
-
解析文档片段
use MensBeam\HTML\Parser; use MensBeam\HTML\Parser\Config; $config = new Config; $config->htmlNamespace = true; // set up two context nodes $document = Parser::parse("<!DOCTYPE html><math></math>", "UTF-8", $config)->document; $body = $document->getElementsByTagName("body")[0]; $math = $document->getElementsByTagName("math")[0]; echo $body->namespaceURI; // prints "http://www.w3.org/1999/xhtml" echo $math->namespaceURI; // prints "http://www.w3.org/1998/Math/MathML" // parse two identical fragments using different context elements $htmlFragment = Parser::parseFragment($body, 0, "<mi>π</mi>", "UTF-8", $config); $mathFragment = Parser::parseFragment($math, 0, "<mi>π</mi>", "UTF-8", $config); echo $htmlFragment->firstChild->namespaceURI; // prints "http://www.w3.org/1999/xhtml" echo $mathFragment->firstChild->namespaceURI; // prints "http://www.w3.org/1998/Math/MathML"
-
序列化文档和元素
use MensBeam\HTML\Parser; $document = Parser::parse("<!DOCTYPE html><a>Ook<p>Eek</a>")->document; $body = $document->getElementsByTagName("body")[0]; echo Parser::serialize($document->documentElement); // prints "<html><head></head><body><a>Ook</a><p><a>Eek</a></p></body></html> echo Parser::serializeInner($body); // prints "<a>Ook</a><p><a>Eek</a></p>
配置
MensBeam\HTML\Parser\Config
类用于作为解析器的配置参数容器。我们已尽量使用合理的默认值,但某些参数仍然可配置
documentClass
:构建文档对象时使用的 PHP 类。此类必须是DOMDocument
的子类。默认使用DOMDocument
。使用其他类可能会影响性能,尤其是在处理大型文档时;建议用户进行自己的基准测试。encodingFallback
:当没有提供给解析器,并且无法检测到编码时使用的默认编码。默认使用windows-1252
编码,但根据区域设置或环境,可能需要使用其他编码。有关可能的值,请参阅编码规范。encodingPrescanBytes
:在解析之前检查的字节数(默认为1024
),以确定未提供时文档的字符编码。通常不需要更改此设置。使用0
将禁用编码预扫描。errorCollection
:一个布尔值,指示是否将解析错误收集到Output
对象的errors
数组中。出于性能原因,通常应将其保留在默认的false
。目前errors
数组的内容被认为是实现细节,可能会在没有通知的情况下更改。htmlNamespace
:一个布尔值,指示是否在 HTML 命名空间中创建 HTML 元素,即http://www.w3.org/1999/xhtml
而不是null
命名空间。尽管使用 HTML 命名空间是正确的行为,但出于性能和兼容性的原因,默认使用null
命名空间。processingInstructions
:一个布尔值,表示是否在解析文档时保留处理指令。默认情况下,根据规范,处理指令被解析为注释。注意,如果设置为true
,解析器将插入HTML处理指令,这些指令以第一个>
字符结束,而不是以?>
结束的XML处理指令。
限制
此库的主要目标是准确性。如果文档对象与规范要求的不同,这可能是错误。然而,我们也被PHP所限制,它施加了各种限制。如下所述
- 由于PHP的DOM是为XML 1.0第二版设计的,因此元素和属性名在XML 1.0第二版中是非法的,它们被破坏,如规范建议。
- PHP的DOM没有特别理解HTML的
<template>
元素。因此,模板内容与其他元素的子元素一样被对待。 - PHP的DOM特别处理
xmlns
属性。因此,会改变元素或前缀的命名空间URI的属性被丢弃。 - 由于PHP的一个严重降低大文档性能的bug,并且考虑到现有的PHP软件,HTML元素默认放在null命名空间中,而不是HTML命名空间中。
- PHP的DOM不允许没有名称的DOCTYPE(即
<!DOCTYPE >
而不是<!DOCTYPE html>
);在这种情况下,解析器将创建一个DOCTYPE,其名称为一个单一的U+0020 SPACE
字符。
与masterminds/html5
的比较
此库和masterminds/html5具有类似的功能。一般来说,我们更准确,但它们更快。以下表格总结了主要的功能差异。
* 例如:<svg xmlns:xlink='http://www.w3.org/1999/xhtml' xlink:href='http://example.com/'/>
。正确的行为尚不清楚,但我们认为我们的行为更符合规范的本意。
† 禁用HTML命名空间。启用HTML命名空间时,由于PHP的bug,它不会在合理的时间内完成。