在PHP中重定向之前检查链接有效性的最快方法

时间:2011-05-16 12:50:25

标签: php redirect

我一直在我的ErrorDocument页面上运行一个脚本,当引荐者是我的网站时,该脚本会记录并通过电子邮件发送给我。最近我以为我可以使用相同的脚本来记录破碎的外部链接。我正在尝试检索外部页面标题,如果他们在我的网站上显示404留下错误消息,如果没有,则执行重定向。这是有效的,但即使网站可用,它也很慢。比直接访问外部页面慢得多。

我认为问题必然在于我如何检索标题(请参阅function is404)而不是错误报告或所有preg_replace,因为它在我使用它时工作得非常快在我的ErrorDocument中,但我会在下面发布所有代码,以防其他地方出现问题。

除了: 我还没有决定是否部署这个,显然它取决于速度问题的修复,但我想知道你对这种方法的意见,假设它的速度很快。我的网站的好处是明确的,它使用户保持在网站上,而不是倾倒到可能难以理解的404,它让我知道断开的链接。但是,如果外部页面具有自定义404,那么用户可能会更好地关注断开的链接。我试图通过在错误消息中包含404链接以及检查域的可用性并提供指向它的链接(如果可用)来抵消这一点。其他的东西也是可行的,例如链接到谷歌的结果。那么,如果你遇到这种情况,你会感到高兴,矛盾,有点生气或厌恶吗?

更新 我最终决定使用Mel建议的框架解决方案,我在iframe加载外部页面,并在顶部有一个小条,允许用户报告存在的断开链接。在这个解决方案中撒谎的问题是,如果我实际上没有重定向页面,如果用户希望将其加入书签,他们最终会得到一个指向我的重定向脚本的链接(除非引用者是我的网站,否则会被阻止;所以他们有一个禁止页面的链接)。这由一个简单的window.location=http://external.site修复,但问题是何时调用它。 iframe onload事件会触发页面是否为404,因此不合适并且使用简单的超时使得已明确加载的页面看起来很丑陋我的“有此页错误”消息最佳。我去了一个组合。当onload触发时,我正在对一个脚本进行Ajax调用,该脚本从我的服务器检查外部站点的状态。 (我使用的是与我下面相同的is404方法,但这次似乎运行得非常好。我想速度问题在其他地方,虽然我不确定在哪里。)我认为使用Ajax调用(超时)将阻止Adrian指出的站点阻塞问题。如果我的服务器认为该页面不是404,我会立即删除该横幅。如果它认为是我设置了超时并且让横幅(希望)足够长,以便想要点击它的用户。如答案中所指出的那样,我的404代码与用户看到的内容不匹配,但最糟糕的情况是错过了报告错误的机会或者在罚款中停留了一段时间的横幅页。希望大部分时间都可以解决。对于未启用javascript的用户,我只需使用meta refresh并立即转发到该链接。如果我在这里遇到问题或错过了其他重要的事情,我会非常感谢您的反馈意见。感谢。


以下是代码:

  //  /../php/urlfns.php

  1 <?php
  2 function hasProtocol($uri) {
  3     return preg_match('#^.+://#', $uri);
  4 }
  5 
  6 function getDomain($uri, $keepproto=false) {
  7     return preg_replace('#^((.+://)?(.+?))\/.*#', $keepproto?'${1}':'${3}', $uri.'/');
  8 }
  9 
 10 function getBaseDomain($uri, $keepproto=false) {
 11     $dom = getDomain($uri, $keepproto);
 12     if(!$keepproto) {
 13         return preg_replace('#.*\.(.+\..+)$#', '${1}', '.'.$dom);
 14     }
 15     else
 16     if(hasProtocol($dom)) {
 17         return preg_replace('#^(.+://).*\.(.+\..+)$#', '${1}${2}', $dom);
 18     }
 19     else {
 20         return preg_replace('#^.*\.(.+\..+)$#', '${1}', '.'.$dom);
 21     }
 22 }
 23 
 24 function isOnDomain($uri, $domain, $allowsubs=false) {
 25     $uridom = getDomain($uri);
 26 
 27     if(!$allowsubs) {
 28         return ($uridom===$domain);
 29     }
 30     else {
 31         $basedom = getBaseDomain($domain);
 32         return (strlen($uridom) - strlen($basedom) === strrpos($uridom, $basedom));
 33     }
 34 }
 35 
 36 function stripDomain($uri) {
 37     return preg_replace('#^(.+://)?.*?(/.*)#', '${2}', $uri);
 38 }
 39 
 40 function is404($uri) {
 41     stream_context_get_default(array(
 42         'http'=>array(
 43             'method' => 'HEAD'
 44         )
 45     ));
 46     $hds = @get_headers($uri);
 47     return (!$hds || strpos($hds[0], ' 404 ') !== false);
 48 }
 49 ?>

**

  //  /../php/badlink.php

  1 <?php
  2 require_once('urlfns.php');
  3 
  4 function reportbadlink($lntype='generic', $subdomains=false, $blcache='../badlinks/') {
  5 
  6     if(isset($_SERVER['HTTP_REFERER']) &&
  7         isOnDomain($_SERVER['HTTP_REFERER'], $_SERVER['SERVER_NAME'], $subdomains)) {
  8 
  9         $reffile = $_SERVER['DOCUMENT_ROOT'].'/'.stripDomain($_SERVER['HTTP_REFERER']);
 10 
 11         $blid  = md5($lntype.$_SERVER['REQUEST_URI'].$reffile.@filemtime($reffile));
 12         $blfil = dirname(__FILE__).'/'.$blcache.'/'.$blid;
 13 
 14         if(!file_exists($blfil)) {
 15             $report = '  On: '.$_SERVER['HTTP_REFERER']."\n".
 16                       '  To: '.$_SERVER['REQUEST_URI']."\n".
 17                       'Type: '.$lntype."\n".
 18                       '   #: '.$blid."\n\n";
 19 
 20             file_put_contents($blfil, $report);
 21             mail('webmaster@localhost', 'A broken link has been found.', $report);
 22         }
 23     }
 24 }
 25 ?>

**

  //  /ssi/extlink.php
  1 <?
  2     require_once('../../php/urlfns.php');
  3     $uri = $_SERVER['QUERY_STRING'];
  4 
  5     if(!hasProtocol($uri)) {
  6         $uri = 'http://'.$uri;
  7     }
  8     
  9     if(!is404($uri)) {
 10         header('Location: '.$uri);
 11         exit;
 12     }
 13         
 14     header("Cache-Control: no-cache, must-revalidate");
 15     header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");
 16 
 17     require_once('../../php/badlink.php');
 18     reportbadlink('external');
 19         
 20     $baseuri = getDomain($uri,true);
 21     if(preg_match('#^'.$baseuri.'/?$#', $uri) || is404($baseuri)) {
 22         $baseuri = null;
 23     }
 24                
 25     $errtype = 'External page not found';
 26     $errmesg = '<p><em><a href="'.$uri.'">'.$uri.'</a> was not found.</em></p>'.
 27                '<p>This may be because the site is temporarily unavailable, or it may be a broken link.</p>'.
 28                ($baseuri ? '<p><a href="'.$baseuri.'">'.$baseuri.'</a> appears to be available however and you may find '.
 29                ' what you are looking for there.</p>' : '');
 30 ?>
 31 <!DOCTYPE html>
 32 <html lang="en" dir="ltr">
    ...

4 个答案:

答案 0 :(得分:2)

我肯定不会部署这个,只是因为你的假设是,如果你能够访问该网站,那么你的用户也可以。但是,如果您不能,但是由于本地化路由问题,用户可以向他们发送红鲱鱼。

我宁愿部署一个框架式解决方案,在小型顶部框架中,您有一个按钮,可以将链接报告为已损坏。与Google图片的右侧窗格类似。然后,您可以在数据库中记录过去x天内报告链接被断开的次数,并将其显示在链接旁边。

对我来说,最终用户会好得多,然后有人断言该网站不适合我。 “我不相信你,你在那里做了什么阴暗的事情,我只需复制链接,将其粘贴在我的地址栏中,然后自己去!”。再说一遍,我是一个愤世嫉俗的人。

答案 1 :(得分:1)

我会有一个带有外部页面的SQL表,并定期检查带有cron任务的页面。这样人们点击几乎没有延迟。

答案 2 :(得分:1)

管理员应该在破碎的外部链接上收到通知的想法非常简洁,但作为用户,我不希望从您的网站获得该消息。我不会像真正的404那样信任它,所以我可能只是复制URL并将其粘贴到一个新标签中,只是为了检查。那会让我做更多的工作,而不是帮助我。另外,正如梅尔所说,如果你的支票错了怎么办?

如果您仍想检查外部链接的有效性,请确保它不会以任何方式影响用户。使用jodes提到的cron脚本似乎是一个好主意。

有点相关的故事,可能需要深思:

我已经使用PHP脚本来检查另一个域(我们的域,但在另一台服务器上......)上是否存在图像,如果检查失败,则将图像href替换为默认图像href。这个工作好了一个星期左右(虽然它使网站有点慢),但周一好一点我们在另一个域(电子商务服务器)上出现了巨大的流量高峰,该域名下降了,突然间我们有了加载时间在我工作的网站的会议记录中。

换句话说,那是 Really Bad Idea™

我不得不撕掉服务器端,快速检查以重新启动网站。后来我构建了一个小的javascript,它会将事件监听器附加到图像上,在加载时触发,并用默认图像替换返回404的任何图像。它工作正常,对于没有启用js的少数用户,它可以工作,即使它在资源管理器中看起来有些难看(破碎的图像图标)。

答案 3 :(得分:0)

您也可以使用fsockopen打开网址。如果返回的资源的 errno 为404,则其链接断开。保存所有正则表达式