如何使用PHP DOM扩展(或必要时的其他扩展或库)找到特定节点或属性的偏移量。
例如,假设我有这个HTML文档:
<html><a href="/foo">bar</a></html>
使用以下代码(进行适当修改):
$dom = new DOMDocument;
$dom->loadHTML($html);
$xpath = new DOMXPath($dom);
$nodes = $xpath->query('//a/@href');
foreach($nodes as $href) {
// Find start of $href attribute here
echo $href->something;
}
我希望看到输出15或其他东西,以表明属性从字符15开始进入文档。
似乎有方法DOMNode::getLineNo()
返回行号 - 这与我想要的类似,但我找不到文本中一般偏移的替代方法。
答案 0 :(得分:2)
找到你想要的属性后,
$html = <<<HTML
<html><a href="/foo">bar</a></html>
HTML;
$dom = new DOMDocument;
$dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
$xpath = new DOMXPath($dom);
$nodes = $xpath->query('//a/@href');
$mySecretId = 'abc123';
foreach($nodes as $href) {
$href->value = $mySecretId;
}
$html = $dom->saveHTML();
echo strpos($html, $mySecretId) . "\n";
“ strpos ”会为您提供第一次出现的替换值,即您想要的位置。
注意标记“LIBXML_HTML_NOIMPLIED”和“LIBXML_HTML_NODEFDTD”,更多here。
如果要查找匹配元素的所有位置,请执行:
foreach($nodes as $href) {
$previousValue = $href->value;
$href->value = $mySecretId;
$html = $dom->saveHTML();
echo strpos($html, $mySecretId) . "\n";
$href->value = $previousValue;
}
答案 1 :(得分:1)
以下是基于一些假设:
a.href
属性是唯一应该处理的候选者 - 如果使用的正则表达式模式可能会变得更复杂a.href
属性始终封装在双引号"
中,属性节点的值不能为空a.href
属性在同一节点中多次出现,则最后一次出现优先preg_match_all
使用offset-capture <?php
// define some HTML, could be retrieved by e.g. file_get_contents() as well
$html = <<< HTML
<!DOCTYPE html>
<html lang="en">
<body>
<a href="https://google.com/">Google</a><div><a href=
"https://stackoverflow.com/">StackOverflow</a></div>
<A HREF="https://google.com/" href="https://goo.gl/">
Google URL</a>
</body>
</html>
HTML;
// search href attributes in anchor tags (case insensitive & multi-line)
preg_match_all(
'#<a[^>]*\s+href\s*=\s*"(?P<value>[^"]*)"[^>]*>#mis',
$html,
$matches,
PREG_OFFSET_CAPTURE
);
$positions = array_map(
function (array $match) {
$length = mb_strlen($match[0]);
return [
'value' => $match[0],
'length' => $length,
'start' => $match[1],
'end' => $match[1] + $length,
];
},
$matches['value']
);
var_dump($positions);
将输出位置信息,如下所示(注意:最后一个位置使用第二个href
属性,该属性已为同一个锚标记定义了两次)
array(3) {
[0] => array(4) {
'value' => string(19) "https://google.com/"
'length' => int(19)
'start' => int(49)
'end' => int(68)
}
[1] => array(4) {
'value' => string(26) "https://stackoverflow.com/"
'length' => int(26)
'start' => int(95)
'end' => int(121)
}
[2] => array(4) {
'value' => string(15) "https://goo.gl/"
'length' => int(15)
'start' => int(183)
'end' => int(198)
}
}