mensbeam/html-parser

现代HTML文档的解析器和序列化器

1.4.0 2024-05-15 21:47 UTC

This package is auto-updated.

Last update: 2024-09-16 23:15:13 UTC


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,除非 reformatWhitespacetrue,否则没有效果。
  • indentWithSpaces (bool|null):是否使用空格或制表符进行缩进。默认为 true,除非 reformatWhitespacetrue,否则没有效果。
  • 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>&pi;</mi>", "UTF-8", $config);
    $mathFragment = Parser::parseFragment($math, 0, "<mi>&pi;</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,它不会在合理的时间内完成。