只允许特定网站的iframe加载

3

有没有办法在PHP中,将一段文本中的iframe删除,如果它们不在白名单数组或黑名单数组中的域名中,那么我可以允许像YouTube、Facebook这样的网站的iframe,而不是每个网站。


不确定您需要这个的原因,但请注意这可能不是100%有效的解决方案。例如,看看这个链接 - http://www.facebook.com/l.php?u=http%3A%2F%2Fwww.google.com%2F&h=f9234 - 看起来像Facebook,对吧?但实际上它是到Facebook重定向器的链接,然后将您发送到Google。相同的代码可以修改为将人们发送到任何地方,但对此URL的任何测试都会说它是Facebook URL(当然,在重定向之前)。 - Luke Stevenson
@Keverw 为什么要悬赏?您能否添加一条评论告诉我为什么我的答案不起作用,我会进行修改以适应。 - alex
你的代码运行成功了。我想知道其他人怎么看。你的代码没有注释,所以很难学习。也许你可以更新一下?比如说,我不确定“Strip www”是什么意思。 - Keverw
@Keverw 好的,我会添加一些注释,现在请检查答案 :) - alex
能否将$iframe->parentNode->removeChild($iframe)编辑为替换iframe?在我的真实应用程序中,我将删除它,但我有另一个用途,即将YouTube的Flash嵌入代码升级为Html5 iframe代码。我有点反感Flash趋势。 - Keverw
显示剩余4条评论
2个回答

5

输入

<h3>Allowed</h3>
<iframe src="http://youtube.com" ></iframe>
<iframe src="http://www.facebook.com" ></iframe>
<iframe src="http://google.com" ></iframe>

<h3>Banned</h3>
<iframe src="http://example.com" ></iframe>
<iframe src="http://alexanderdickson.com" ></iframe>

PHP

// Make a list of allows hosts.
$allowedHosts = array(
  'youtube.com',
  'facebook.com',
  'google.com'
);

$dom = new DOMDocument;
$dom->loadHTML($str);

// Get all iframes in the document.
$iframes = $dom->getElementsByTagName('iframe');
$iframesLength = $iframes->length;

// Iterate over all iframes.
while ($iframesLength--) {
     $iframe = $iframes->item($iframesLength);
     if ($iframe->hasAttribute('src')) {

         // Get the src attribute of the iframe.
         $src = $iframe->getAttribute('src');

         // Get the host of this iframe, to compare with our allowed hosts.
         $host = parse_url($src, PHP_URL_HOST);

         // If not host, then skip this iframe.
         if ($host === NULL) {
             continue;
         }

         // Strip www. because otherwise it may be 'www.facebook.com` and we have only
         // banned `facebook.com`.
         $host = preg_replace('/^www\./', '', $host);


         // If this host is not in our allowed list, remove it from the document.
         if ( ! in_array($host, $allowedHosts)) {
             $iframe->parentNode->removeChild($iframe);
         }
     }
}
echo $dom->saveHTML();

CodePad.

Output

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> 
<html><body> 
<h3>Allowed</h3> 
<iframe src="http://youtube.com"></iframe> 
<iframe src="http://www.facebook.com"></iframe> 
<iframe src="http://google.com"></iframe> 

<h3>Banned</h3> 

</body></html> 

如果你不想让返回的 HTML 包含所有的 htmlbody 等标签,那么请在代码末尾运行以下代码...
$html = '';
foreach($dom->getElementsByTagName('body')->item(0)->childNodes as $node) {
   $html .= $dom->saveXML($node, LIBXML_NOEMPTYTAG);
}

如果您的PHP版本>=5.3.6,请将上面的saveXML()替换为saveHTML()

更新

是否可以编辑$iframe->parentNode->removeChild($iframe);以替换iframe

是的,请使用以下内容替换整个块...

// Create video element
$video = $dom->createElement('video');

// Attach whatever you need to...
$video->setAttribute('src', 'whatever');

// Get a reference to the parent of the iframe
$parent = $iframe->parentNode;

// Insert the video element before the iframe
$parent->insertBefore($video, $iframe);

// Remove the iframe
$parent->removeChild($iframe);

@Keverw 我以前从未见过这种情况。看起来它正在编码换行符。你使用的 PHP 版本是什么? - alex
PHP版本为5.3.2-1ubuntu4.7,我在测试中发现它与我在上一条评论中提到的CodePad表现相同。 - Keverw
@Keverw 好的,saveHTML() 函数接受一个节点作为参数,需要支持 PHP 5.3.6 或更高版本,但你可以尝试一下 :) - alex
"DOMDocument::saveHTML()函数期望恰好有0个参数" $html .= $dom->saveHTML($node, LIBXML_NOEMPTYTAG); 这就是代码。所以我猜不行?我也试过 $html .= $dom->saveHTML($node); ... 没关系,我不急,也许我应该等到Ubuntu的仓库更新。在那之前,我可以处理项目的其他部分。希望不要太久。 - Keverw
@Keverw 是的,我就知道会是这样。有什么其他可以帮助你的吗? - alex
显示剩余2条评论

0
我会使用像PhPQuery这样的HTML DOM解析器,遍历所有iframe标签,并删除任何src不以youtube开头且不在您白名单中的站点。然后只需打印出结果HTML即可。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接