Preg_replace,请不要支持?

时间:2015-03-13 17:26:52

标签: php html-parsing preg-replace

所以我有这个preg_replace函数(来自别人写的脚本),它为所有链接添加了target="_blank"属性。但是,当我有一个已经具有target="_blank"属性的链接时,它会添加另一个属性。这会在链接中生成双target="_blank"属性。有没有办法在下面的preg_replace函数中解决这个问题?

$text = preg_replace('%(<a[^>]+)(href="https?://)((?:(?!(' . $host . '))[^"])+|(?:(?=(' . $host . '/' . $base_url . '/))[^"]+))"%i', '$1$2$3"target="_blank"', $text);

非常感谢!

1 个答案:

答案 0 :(得分:2)

正则表达式不是这种html操作的好方法和非常方便。首选方法是使用DOMDocument,它是一个使用libxml从HTML文档构建节点树(DOMNode实例)的工具。 DOMNode类有几个有用的方法和属性可以执行您想要的操作hasAttributesetAttribute

$dom = new DOMDocument;
$dom->loadHTMLFile('yourhtmlfile.html'); 
// or $dom->loadHTML($htmlContent); //if the html is already in a variable

// get all the link nodes
$linkNodeList = $dom->getElementsByTagName('a');

foreach($linkNodeList as $linkNode) {
    if (!$linkNode->hasAttribute('target'))
        $linkNode->setAttribute('target', '_blank');
}

$result = $dom->saveHTML();

注意:如果要在href属性中定位特定域和基本URL,可以将if语句更改为:

if ( $linkNode->hasAttribute('target')
  && strpos($host . '/' . $baseurl, $linkNode->getAttribute('href')) !== false)

或另一种方法是使用XPath查询立即定位您想要的链接:

$dom = new DOMDocument;
$dom->loadHTMLFile('yourhtmlfile.html'); 

$xp = new DOMXPath($dom);

$query = '//a[contains(@href, "' . $host . '/' . $baseurl . '") and not(@target)]';

$linkNodeList = $xp->query($query);

foreach ($linkNodeList as $linkNode) {
    $linkNode->setAttribute('target', '_blank');
}

$result = $dom->saveHTML(); 

注意:如果您正在使用部分html文档,DOMDocument会自动添加DTD并创建html和body标记。为了防止这种情况,有几种解决方法:

使用PHP&gt; = 5.4,您需要在加载文档时添加两个选项:

$dom->loadHTMLFile('yourhtmlfile.html', LIBXML_HTML_NODEFDTD | LIBXML_HTML_NOIMPLIED);

(有时出于不明原因,未定义常量LIBXML_HTML_NODEFDTDLIBXML_HTML_NOIMPLIED。在这种情况下,您可以将其替换为值48192或者在之前定义它们,或者直接使用8196的结果4 | 8192

使用PHP&gt; = 5.1,方法是使用saveXML逐个保存每个正文childNodes并连接字符串:

$result = '';
$bodyChildNodes = $dom->getElementsByTagName('body')->item(0)->childNodes;
foreach ($bodyChildNodes as $childNode) {
    $result .= $dom->saveXML($childNode);
}

对于较低的PHP版本,请使用字符串方法:

$result = preg_replace('~\A.*?<body>|</body></html>\z~s', '', $result);

$result = explode('<body>', $result, 2);
$result = substr($result[1], 0, -14); // 14 is the string length of "</body></html>"